/* ═══════════════════════════════════════════════════════════════════════════
   10 - MOTION
   Loaded last so the reduced-motion escape hatch outranks everything.

   Doctrine (§4.3): slow, weighted, never bouncy. An alap, not a tihai.
   Reveals are opacity 0→1 + translateY(24px→0) + blur(6px→0) over --dur-slow
   with --ease-out, children staggered 70ms.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer motion {

  /* ── Scroll reveal ──────────────────────────────────────────────────── */

  /* Only armed once JS confirms it can observe; without it the content is
     simply present, which is also what noscript enforces. */
  .js [data-reveal] {
    opacity: 0;
    transform: translateY(24px);
    filter: blur(6px);
    transition:
      opacity var(--dur-slow) var(--ease-out),
      transform var(--dur-slow) var(--ease-out),
      filter var(--dur-slow) var(--ease-out);
    transition-delay: calc(var(--i, 0) * var(--stagger));
    will-change: opacity, transform;
  }

  .js [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
    filter: none;
  }

  /* Once a reveal has run, drop the compositing hint. */
  .js [data-reveal].is-revealed.is-settled { will-change: auto; }

  /* ── The per-word mask reveal (§8 H1) ───────────────────────────────── */

  .js [data-split-words] .mask > span {
    transform: translateY(110%);
    transition: transform var(--dur-hero) var(--ease-out);
    transition-delay: calc(var(--i, 0) * 60ms);
  }

  .js [data-split-words].is-revealed .mask > span { transform: translateY(0); }

  /* ── Preloader line drawing (§7.1) ──────────────────────────────────── */

  .preloader__stroke {
    stroke-dasharray: 100;
    stroke-dashoffset: 100;
    animation: draw 1.1s var(--ease-out) forwards;
  }

  .preloader__stroke:nth-child(2) { animation-delay: 80ms; }
  .preloader__stroke:nth-child(3) { animation-delay: 200ms; }
  .preloader__stroke:nth-child(4) { animation-delay: 260ms; }
  .preloader__stroke:nth-child(5) { animation-delay: 420ms; }
  .preloader__stroke:nth-child(6),
  .preloader__stroke:nth-child(7) { animation-delay: 560ms; }
  .preloader__stroke:nth-child(8) { animation-delay: 640ms; }

  @keyframes draw {
    to { stroke-dashoffset: 0; }
  }

  /* ── Parallax ───────────────────────────────────────────────────────── */

  /* parallax.js writes --py; the element only reads it. Nothing animates in
     CSS here, so a paused scroll leaves the page composed (§15.9). */
  [data-parallax] {
    transform: translate3d(0, var(--py, 0px), 0);
    will-change: transform;
  }

  .ghost-word { transform: translate3d(0, var(--py, 0px), 0); }

  .ghost-word--center { transform: translate(-50%, calc(-50% + var(--py, 0px))); }

  /* ── Page-transition safety ─────────────────────────────────────────── */

  /* If a navigation is cancelled, the curtain must not stay up. */
  .curtain:not(.is-in):not(.is-out) { transform: translateY(100%); }

  /* ═════════════════════════════════════════════════════════════════════
     REDUCED MOTION (§4.3, §14)

     Everything decorative stops. Nothing disappears: reveals resolve to
     their finished state rather than staying hidden, and the JS modules
     (marquee, parallax, counters, cursor, transitions) check
     matchMedia('(prefers-reduced-motion: reduce)') and no-op.
     ═════════════════════════════════════════════════════════════════════ */

  @media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
      animation-duration: 1ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 1ms !important;
      transition-delay: 0ms !important;
      scroll-behavior: auto !important;
    }

    .js [data-reveal],
    .js [data-split-words] .mask > span {
      opacity: 1 !important;
      transform: none !important;
      filter: none !important;
    }

    .preloader__stroke { stroke-dashoffset: 0; animation: none; }

    [data-parallax],
    .ghost-word { transform: none !important; }

    .ghost-word--center { transform: translate(-50%, -50%) !important; }

    .play-trigger--pulse::before,
    .play-trigger--pulse::after { display: none; }

    .hero__scroll-line::after { animation: none; transform: translateY(50%); }

    .cursor, .cursor-ring, .curtain { display: none !important; }
  }

  @media print {
    [data-reveal],
    [data-split-words] .mask > span {
      opacity: 1 !important;
      transform: none !important;
      filter: none !important;
    }
  }
}
