/* motion.css — the motion contract, in CSS.
   Rule 1: nothing is hidden unless BOTH JavaScript is running (.js on <html>) AND the user
   has not asked for reduced motion. So no-JS users and reduced-motion users always render
   the designed FINAL state — totals at value, rules drawn, highlighter applied, stamps
   placed. GSAP then animates from these hidden states to the same final state.
   Rule 2: an unconditional reduced-motion override collapses every transition/animation,
   a second line of defence independent of GSAP's matchMedia branch.
   Initial states below only ever touch transform/opacity — zero layout shift. */

/* Register --draw as a typed custom property so the highlighter/underline "draw" interpolates
   robustly, defaulting to fully-drawn for no-JS / reduced-motion (matches var(--draw, 1)). */
@property --draw {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

/* Structural clip masks (needed regardless of motion so lines/terms can ride behind them) */
.mask { display: block; overflow: hidden; }

/* ---------------------------------------------------------------------------
   INITIAL HIDDEN STATES — applied only when JS is present and motion is welcome
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  /* Fade/rise reveals (and hero-staging elements) */
  .js [data-reveal],
  .js [data-stage] { opacity: 0; }

  /* Hero + header line masks: the line sits below its clip window */
  .js [data-line] { transform: translateY(115%); }

  /* Equation factors sit below their clip window, ready to snap up */
  .js [data-term] { transform: translateY(110%); }

  /* Anything that draws (extends) starts at zero length */
  .js .rule-off,
  .js .rule-draw,
  .js .pull-quote__underline { transform: scaleX(0); }
  /* Highlighter smears draw via a CSS variable so GSAP can animate the pseudo-element */
  .js .hl,
  .js .place { --draw: 0; }

  /* 404 struck title: clip it away so full motion can wipe the whole struck line in
     left-to-right (the strike is a real line-through, not an animatable bar). No-JS /
     reduced-motion never hide it, so the struck final state always shows. */
  .js [data-strike] { clip-path: inset(0 100% 0 0); }

  /* Stamps drop in: start lifted + oversized, land at −4° */
  .js [data-stamp] { opacity: 0; transform: rotate(-4deg) scale(1.35); }
  /* Ticket buttons press in to full opacity (no rotation) */
  .js [data-press] { opacity: 0; transform: scale(1.2); }

  /* The annotation ring draws its stroke */
  .js .annotate__ring path { stroke-dasharray: 1; stroke-dashoffset: 1; }

  /* Seeded figures (running totals + count-ups) carry their FINAL text in the HTML so no-JS
     and reduced-motion render correctly. Under JS + motion they'd otherwise flash that final
     value for one frame before JS zeroes them — so pre-hide them here. JS reveals each the
     moment it writes its starting value; visibility keeps the box (no layout shift), and the
     GSAP-absent failsafe drops .js, which un-hides them. Mirrors the [data-term] pre-hide. */
  .js [data-total],
  .js [data-count] { visibility: hidden; }
}

/* ---------------------------------------------------------------------------
   REDUCED MOTION — unconditional collapse (independent of the GSAP branch)
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  /* The measuring caret stops blinking */
  .blink::after { animation: none; content: ""; }
  /* Progress rails settle rather than scrub */
  .header-progress, .mobile-progress { transition: none !important; }
}
