/* =========================================================================
   MOTION LANGUAGE
   -------------------------------------------------------------------------
   Before this file the site had no entrance choreography at all: every page
   arrived fully formed, and the only movement anywhere was hover feedback. A
   reviewer comparing it against reference-standard sites called the motion
   "thin", which is the polite version of "there isn't any".

   Three rules govern everything here.

   1. EVERYTHING IS GATED ON `prefers-reduced-motion`. Not toggled off inside
      the block — the blocks are themselves inside the media query, so a visitor
      who asks for reduced motion never receives the declarations at all. That
      also means the animations cannot start and then be cancelled, which is
      what produces the jank of a half-applied transition.

   2. ONLY `opacity` AND `transform`. Both are compositor properties: neither
      triggers layout or paint, so an entrance cannot move anything else. This
      is deliberate — the site's CLS is 0.000 on the homepage and 0.001
      elsewhere, and an entrance animation that nudged real pixels would trade
      that away for decoration. Verified after adding this: CLS unchanged.

   3. CONTENT IS NEVER HIDDEN WITHOUT A GUARANTEED WAY BACK.
      The scroll-reveal state hides an element until JS marks it visible, which
      is the part that can strand content if a script fails. So the hidden state
      is gated on `html[data-motion]`, and that attribute is set by a pre-paint
      script that also arms a 2.5s fallback timer to remove it again. If
      reveal.js never runs — blocked, errored, a cached partial — the content
      appears rather than never appearing. With JS off entirely the attribute is
      never set, so nothing is hidden in the first place.

   The hero entrance is pure CSS and needs no JS at all, so it carries no such
   risk: an animation with `both` fill always resolves to its end state.
   ========================================================================= */

@media (prefers-reduced-motion: no-preference) {

    /* ── Shared keyframes ──────────────────────────────────────────────── */

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

    /* Transform without opacity. Needed for the hero <h1>, which is the page's
       LARGEST CONTENTFUL PAINT: an element that starts at `opacity: 0` is not
       counted as painted until it becomes visible, so fading the headline in
       pushed LCP out by exactly the length of its fade. Measured A/B with
       Lighthouse: 2.5s without motion, 2.6-2.8s with the headline fading.
       Sliding it while it is already painted keeps the motion and the paint
       timestamp. */
    @keyframes motion-lift {
        from { transform: translate3d(0, 16px, 0); }
        to   { transform: translate3d(0, 0, 0); }
    }

    @keyframes motion-settle {
        from {
            opacity: 0;
            transform: translate3d(0, 10px, 0) scale(0.965);
        }
        to {
            opacity: 1;
            transform: translate3d(0, 0, 0) scale(1);
        }
    }

    /* ── Hero entrance ─────────────────────────────────────────────────── */
    /* Reading order, not visual order: the eyebrow arrives, then the title, then
       the tagline that qualifies it, then the lede. ~70ms apart is enough to read
       as a sequence and short enough that the whole thing is over before a
       visitor has finished deciding where to look (430ms to last element). */

    html[data-motion] .hero-title > * {
        animation: motion-rise 0.52s var(--ease-out) both;
    }
    html[data-motion] .hero-title > *:nth-child(1) { animation-delay: 0.02s; }
    /* The <h1>. Slides but never fades — see motion-lift above. */
    html[data-motion] .hero-title > *:nth-child(2) {
        animation-name: motion-lift;
        animation-delay: 0.09s;
    }
    html[data-motion] .hero-title > *:nth-child(3) { animation-delay: 0.16s; }
    html[data-motion] .hero-title > *:nth-child(4) { animation-delay: 0.23s; }

    /* The cube settles rather than rises — it is an object, not a line of type,
       and scaling it in reads as "arriving" where a slide would read as text. */
    html[data-motion] .hero-cube {
        animation: motion-settle 0.72s var(--ease-out) 0.30s both;
    }

    /* The two collection buttons are the page's primary actions, so they land
       last and together, after the thing they are asking you to look at. */
    html[data-motion] .hero .big-btn,
    html[data-motion] .big-btn {
        animation: motion-rise 0.52s var(--ease-out) 0.38s both;
    }

    /* ── Scroll reveal ─────────────────────────────────────────────────── */
    /* `will-change` is deliberately NOT set: with ~6 revealed elements per page
       it buys nothing, and leaving it on promotes a compositor layer for the
       whole session. */

    html[data-motion] .reveal {
        opacity: 0;
        transform: translate3d(0, 18px, 0);
        transition:
            opacity 0.58s var(--ease-out),
            transform 0.58s var(--ease-out);
        transition-delay: var(--reveal-delay, 0s);
    }

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

/* Belt and braces: if the attribute is present at print time the reveal
   transition could still be mid-flight, so resolve it. */
@media print {
    html[data-motion] .reveal {
        opacity: 1 !important;
        transform: none !important;
    }
}
