/* ============================================================================
   CODE QUEST — animations.css
   The motion library: keyframes plus the utility classes that use them.

   Principles applied throughout:
   - only transform / opacity / filter are animated, so everything composites
   - entrances are expo-out (fast start, soft landing); nothing linear
   - anything that loops is slow (3s+) and low-amplitude, so it reads as
     "alive" rather than "busy"
   - every class here is disabled by prefers-reduced-motion at the bottom
   ========================================================================== */

/* ============================================================================
   1. KEYFRAMES — primitives
   ========================================================================== */

@keyframes cq-spin {
  to { transform: rotate(360deg); }
}

@keyframes cq-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

@keyframes cq-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes cq-rise {
  from { opacity: 0; transform: translate3d(0, 18px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes cq-rise-sm {
  from { opacity: 0; transform: translate3d(0, 8px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes cq-drop {
  from { opacity: 0; transform: translate3d(0, -16px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes cq-slide-in-left {
  from { opacity: 0; transform: translate3d(-26px, 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes cq-slide-in-right {
  from { opacity: 0; transform: translate3d(26px, 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes cq-pop {
  0%   { opacity: 0; transform: scale(0.9); }
  60%  { opacity: 1; transform: scale(1.025); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes cq-pop-lg {
  0%   { opacity: 0; transform: scale(0.76); filter: blur(6px); }
  55%  { opacity: 1; transform: scale(1.05); filter: blur(0); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes cq-zoom-out {
  from { opacity: 0; transform: scale(1.12); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes cq-shake {
  0%, 100% { transform: translate3d(0, 0, 0); }
  15%      { transform: translate3d(-8px, 0, 0); }
  32%      { transform: translate3d(7px, 0, 0); }
  49%      { transform: translate3d(-5px, 0, 0); }
  66%      { transform: translate3d(4px, 0, 0); }
  83%      { transform: translate3d(-2px, 0, 0); }
}

@keyframes cq-nudge-x {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(5px, 0, 0); }
}

/* ============================================================================
   2. KEYFRAMES — ambient loops
   ========================================================================== */

/* The signature CTA motion: a slow rise-and-fall. Amplitude is small on
   purpose — enough to catch the eye, not enough to feel unstable. */
@keyframes cq-float {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(0, -7px, 0); }
}

@keyframes cq-float-sm {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(0, -4px, 0); }
}

@keyframes cq-float-rotate {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(-1.2deg); }
  50%      { transform: translate3d(0, -9px, 0) rotate(1.2deg); }
}

@keyframes cq-pulse-cyan {
  0%, 100% { box-shadow: var(--glow-cyan-sm); }
  50%      { box-shadow: var(--glow-cyan-md); }
}

@keyframes cq-pulse-gold {
  0%, 100% { box-shadow: var(--glow-gold-sm); }
  50%      { box-shadow: var(--glow-gold-md); }
}

@keyframes cq-pulse-ring {
  0%   { box-shadow: 0 0 0 0 rgba(63, 201, 247, 0.42); }
  70%  { box-shadow: 0 0 0 16px rgba(63, 201, 247, 0); }
  100% { box-shadow: 0 0 0 0 rgba(63, 201, 247, 0); }
}

@keyframes cq-pulse-ring-gold {
  0%   { box-shadow: 0 0 0 0 rgba(239, 189, 87, 0.45); }
  70%  { box-shadow: 0 0 0 18px rgba(239, 189, 87, 0); }
  100% { box-shadow: 0 0 0 0 rgba(239, 189, 87, 0); }
}

@keyframes cq-breathe-opacity {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}

@keyframes cq-orbit {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* ============================================================================
   3. KEYFRAMES — reward & reveal moments
   ========================================================================== */

/* A core spinning up into existence. */
@keyframes cq-core-charge {
  0%   { opacity: 0; transform: scale(0.45) rotate(-140deg); filter: blur(10px); }
  55%  { opacity: 1; transform: scale(1.14) rotate(18deg);  filter: blur(0); }
  75%  { transform: scale(0.97) rotate(-5deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}

/* An expanding energy ring, used once on reward reveal. */
@keyframes cq-shockwave {
  0%   { opacity: 0.85; transform: scale(0.2); border-width: 3px; }
  100% { opacity: 0;    transform: scale(2.6); border-width: 0.5px; }
}

/* Counting-up numbers get a small scale kick as they settle. */
@keyframes cq-count-settle {
  0%   { transform: scale(1.3); opacity: 0.4; }
  60%  { transform: scale(0.98); opacity: 1; }
  100% { transform: scale(1); }
}

/* A star landing in place. */
@keyframes cq-star-land {
  0%   { opacity: 0; transform: scale(2.2) rotate(-50deg); }
  55%  { opacity: 1; transform: scale(0.88) rotate(8deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}

/* A bar filling from the left. --fill-to is set inline by JS. */
@keyframes cq-bar-fill {
  from { width: 0; }
  to   { width: var(--fill-to, 100%); }
}

/* Text arriving with a soft focus pull — for cinematic titles. */
@keyframes cq-focus-in {
  0%   { opacity: 0; letter-spacing: 0.4em; filter: blur(12px); }
  100% { opacity: 1; letter-spacing: inherit; filter: blur(0); }
}

/* ============================================================================
   4. KEYFRAMES — feedback
   ========================================================================== */

@keyframes cq-flash-correct {
  0%   { background-color: rgba(70, 227, 168, 0); }
  25%  { background-color: rgba(70, 227, 168, 0.22); }
  100% { background-color: var(--success-soft); }
}

@keyframes cq-flash-wrong {
  0%   { background-color: rgba(255, 111, 134, 0); }
  25%  { background-color: rgba(255, 111, 134, 0.22); }
  100% { background-color: var(--danger-soft); }
}

@keyframes cq-tick-draw {
  from { stroke-dashoffset: 32; }
  to   { stroke-dashoffset: 0; }
}

/* ============================================================================
   5. UTILITY CLASSES — entrances
   ========================================================================== */

.cq-anim-fade   { animation: cq-fade-in var(--dur-slow) var(--ease-out-expo) both; }
.cq-anim-rise   { animation: cq-rise var(--dur-slow) var(--ease-out-expo) both; }
.cq-anim-rise-sm{ animation: cq-rise-sm var(--dur-mid) var(--ease-out-expo) both; }
.cq-anim-drop   { animation: cq-drop var(--dur-slow) var(--ease-out-expo) both; }
.cq-anim-left   { animation: cq-slide-in-left var(--dur-slow) var(--ease-out-expo) both; }
.cq-anim-right  { animation: cq-slide-in-right var(--dur-slow) var(--ease-out-expo) both; }
.cq-anim-pop    { animation: cq-pop var(--dur-slow) var(--ease-out-back) both; }
.cq-anim-pop-lg { animation: cq-pop-lg var(--dur-slower) var(--ease-out-back) both; }
.cq-anim-zoom   { animation: cq-zoom-out var(--dur-cinematic) var(--ease-out-expo) both; }
.cq-anim-focus  { animation: cq-focus-in var(--dur-slower) var(--ease-out-expo) both; }

/* ---------- Stagger: set --i on each child (or let JS do it) ---------- */

.cq-stagger > * {
  animation: cq-rise var(--dur-slow) var(--ease-out-expo) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}

.cq-stagger--fast > * {
  animation-delay: calc(var(--i, 0) * 45ms);
}

.cq-stagger--slow > * {
  animation-delay: calc(var(--i, 0) * 110ms);
}

/* ============================================================================
   6. UTILITY CLASSES — ambient loops
   ========================================================================== */

.cq-anim-float        { animation: cq-float 4.2s var(--ease-in-out-soft) infinite; }
.cq-anim-float-sm     { animation: cq-float-sm 3.6s var(--ease-in-out-soft) infinite; }
.cq-anim-float-rotate { animation: cq-float-rotate 6s var(--ease-in-out-soft) infinite; }
.cq-anim-glow         { animation: cq-pulse-cyan 3s var(--ease-in-out-soft) infinite; }
.cq-anim-glow-gold    { animation: cq-pulse-gold 3s var(--ease-in-out-soft) infinite; }
.cq-anim-ring         { animation: cq-pulse-ring 2.4s var(--ease-out-expo) infinite; }
.cq-anim-ring-gold    { animation: cq-pulse-ring-gold 2.4s var(--ease-out-expo) infinite; }
.cq-anim-breathe      { animation: cq-breathe-opacity 3.4s var(--ease-in-out-soft) infinite; }
.cq-anim-spin-slow    { animation: cq-orbit 26s linear infinite; }

/* ============================================================================
   7. UTILITY CLASSES — moments
   ========================================================================== */

.cq-anim-shake  { animation: cq-shake 500ms var(--ease-in-out-soft); }
.cq-anim-nudge  { animation: cq-nudge-x 900ms var(--ease-in-out-soft) infinite; }
.cq-anim-core   { animation: cq-core-charge 1.1s var(--ease-out-back) both; }
.cq-anim-star   { animation: cq-star-land 620ms var(--ease-out-back) both; }
.cq-anim-count  { animation: cq-count-settle 520ms var(--ease-out-back) both; }

/* A one-shot expanding ring. Placed absolutely by whatever spawns it. */
.cq-shockwave {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 120px;
  height: 120px;
  border: 3px solid var(--cyan-300);
  border-radius: 50%;
  pointer-events: none;
  animation: cq-shockwave 1.15s var(--ease-out-expo) forwards;
}

.cq-shockwave--gold {
  border-color: var(--gold-300);
}

/* ============================================================================
   8. VIEW TRANSITIONS — driven by js/core/transition.js
   The page fades up on first paint; sections cross-fade when swapped.
   ========================================================================== */

/* Set on <html> before first paint, removed once booted, so there is never a
   flash of unstyled dark content. */
html.cq-booting body {
  opacity: 0;
}

body {
  transition: opacity var(--dur-slow) var(--ease-out-expo);
}

/* A view that is entering. */
.cq-view-enter {
  animation: cq-rise var(--dur-slower) var(--ease-out-expo) both;
}

/* A view being replaced — JS waits for this before swapping content. */
.cq-view-leave {
  animation: cq-view-out var(--dur-mid) var(--ease-in-out-soft) both;
}

@keyframes cq-view-out {
  from { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
  to   { opacity: 0; transform: translate3d(0, -10px, 0) scale(0.985); }
}

/* Question-to-question swap inside a test: a short horizontal slide. */
.cq-swap-next {
  animation: cq-swap-in-right var(--dur-slow) var(--ease-out-expo) both;
}

.cq-swap-prev {
  animation: cq-swap-in-left var(--dur-slow) var(--ease-out-expo) both;
}

@keyframes cq-swap-in-right {
  from { opacity: 0; transform: translate3d(28px, 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes cq-swap-in-left {
  from { opacity: 0; transform: translate3d(-28px, 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* ============================================================================
   9. SCROLL REVEAL — js/core/transition.js toggles .is-in
   ========================================================================== */

[data-reveal] {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity var(--dur-slower) var(--ease-out-expo),
    transform var(--dur-slower) var(--ease-out-expo);
  transition-delay: var(--reveal-delay, 0ms);
}

[data-reveal].is-in {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

[data-reveal='scale'] {
  transform: scale(0.95);
}

[data-reveal='scale'].is-in {
  transform: scale(1);
}

[data-reveal='left'] {
  transform: translate3d(-26px, 0, 0);
}

[data-reveal='right'] {
  transform: translate3d(26px, 0, 0);
}

/* ============================================================================
   10. REDUCED MOTION
   Entrances collapse to "already arrived"; loops stop; transitions shorten to
   nothing. Meaning is preserved — only movement is removed.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  [class*='cq-anim-'],
  .cq-stagger > *,
  .cq-view-enter,
  .cq-view-leave,
  .cq-swap-next,
  .cq-swap-prev {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }

  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
  }

  .cq-shockwave {
    display: none !important;
  }

  html.cq-booting body {
    opacity: 1;
  }
}

html[data-reduced-motion='true'] [class*='cq-anim-'],
html[data-reduced-motion='true'] .cq-stagger > *,
html[data-reduced-motion='true'] .cq-view-enter,
html[data-reduced-motion='true'] .cq-swap-next,
html[data-reduced-motion='true'] .cq-swap-prev {
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
}

html[data-reduced-motion='true'] [data-reveal] {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

html[data-reduced-motion='true'] .cq-shockwave {
  display: none !important;
}
