/* ================================================================
   Kudbee shared motion engine — CSS half (DBZ-051, north-star Phase A).

   Progressive enhancement: this stylesheet is linked from index.html with a
   plain <link rel="stylesheet">. If it 404s, every selector below simply
   never applies — [data-rv] elements render as normal static content (no
   hidden-forever state), marquees sit still, magnetic/tilt targets stay put.
   Nothing here is required for the page to be readable or functional.

   Motion tokens (--mo-dur-*, --mo-ease-*, --mo-stagger-*, --mo-loop-*) are
   NOT defined in this file — they live in index.html's own inline <style>
   so the host page's own transitions/animations keep their exact timing
   even when this file fails to load. This file only *consumes* those
   tokens; a page that wants to reuse it must define the same custom
   property names in its own stylesheet (see index.html's :root block for
   the reference set).
   ================================================================ */

/* ---------------- Reveal + stagger primitive ----------------
   Fires once per element via IntersectionObserver (kudbee-motion.js) and
   never re-hides — matches the exact behavior of the pre-DBZ-051 inline
   version. --rvd (per-element stagger delay) is authored inline on each
   element, e.g. style="--rvd:0.16s"; --mo-stagger-1..3 are the canonical
   base units available for any new stagger group. */
html.js [data-rv] { opacity: 0; transform: translateY(34px); filter: blur(6px); }
html.js [data-rv].in {
    opacity: 1; transform: none; filter: none;
    transition: opacity var(--mo-dur-4) var(--mo-ease-out),
                transform var(--mo-dur-4) var(--mo-ease-out),
                filter var(--mo-dur-4) var(--mo-ease-out);
    transition-delay: var(--rvd, 0s);
}
html.js [data-rv="left"] { transform: translateX(-44px); }
html.js [data-rv="right"] { transform: translateX(44px); }
html.js [data-rv="scale"] { transform: scale(0.92); }
@media (prefers-reduced-motion: reduce) {
    html.js [data-rv] { opacity: 1; transform: none; filter: none; transition: none; }
}

/* animation-timeline: view() progressive enhancement — where supported,
   the entrance motion is driven natively by scroll position (smoother,
   compositor-only) instead of waiting on the IntersectionObserver callback.
   The IO in kudbee-motion.js still runs unconditionally and still adds
   `.in`; because `.in` here freezes the state (animation: none + explicit
   end values) at higher specificity, the "reveal once, stay revealed"
   contract is unchanged — scrolling back up never re-hides content, exactly
   like the plain-transition version above. The animation-range is set short
   so the natively-driven entrance has normally already settled at its end
   state by the time IO's threshold (0.14, rootMargin -4%) fires — that
   keeps the hand-off invisible instead of a visible snap. */
@supports (animation-timeline: view()) {
    @keyframes kmo-reveal-in {
        from { opacity: 0; transform: translateY(34px); filter: blur(6px); }
        to   { opacity: 1; transform: none; filter: none; }
    }
    @keyframes kmo-reveal-in-left {
        from { opacity: 0; transform: translateX(-44px); filter: blur(6px); }
        to   { opacity: 1; transform: none; filter: none; }
    }
    @keyframes kmo-reveal-in-right {
        from { opacity: 0; transform: translateX(44px); filter: blur(6px); }
        to   { opacity: 1; transform: none; filter: none; }
    }
    @keyframes kmo-reveal-in-scale {
        from { opacity: 0; transform: scale(0.92); filter: blur(6px); }
        to   { opacity: 1; transform: none; filter: none; }
    }
    html.js [data-rv] {
        animation: kmo-reveal-in var(--mo-ease-out) both;
        animation-timeline: view();
        animation-range: entry 0% entry 30%;
    }
    html.js [data-rv="left"] { animation-name: kmo-reveal-in-left; }
    html.js [data-rv="right"] { animation-name: kmo-reveal-in-right; }
    html.js [data-rv="scale"] { animation-name: kmo-reveal-in-scale; }
    html.js [data-rv].in {
        animation: none;
        opacity: 1; transform: none; filter: none;
    }
    @media (prefers-reduced-motion: reduce) {
        html.js [data-rv] { animation: none; opacity: 1; transform: none; filter: none; }
    }
}

/* ---------------- Marquee primitive ----------------
   Layout rules (.ribbon .track / .lab-track flex/gap/etc.) stay in
   index.html since they're structural chrome, not motion. Only the
   animation + hover/focus pause + @keyframes live here. Track content is
   duplicated once by kudbee-motion.js for a seamless loop; if that JS never
   runs (file 404s) the track still renders — just the first copy, sitting
   still, since the animation below wouldn't apply either. */
@media (prefers-reduced-motion: no-preference) {
    .ribbon .track { animation: kmo-marquee var(--mo-loop-marquee-1) linear infinite; }
    .lab-track { animation: kmo-marquee var(--mo-loop-marquee-2) linear infinite; }
}
.ribbon:hover .track { animation-play-state: paused; }
.lab-marquee:hover .lab-track, .lab-marquee:focus-within .lab-track { animation-play-state: paused; }
@keyframes kmo-marquee { to { transform: translateX(-50%); } }

/* ---------------- Tilt primitive (new, DBZ-051 infra) ----------------
   Not applied to any element in this lane — new visual effects are lane 05
   / lane 11 scope. Defined here so those lanes can opt in with
   data-tilt="max-degrees" (default 8) without re-deriving the mechanics;
   wiring it up is a visual change and is deliberately left undone here to
   keep this lane's behavior change at zero. kudbee-motion.js sets
   --kmo-tilt-x/--kmo-tilt-y per pointer position; this rule just applies
   them, gated the same way as every other motion primitive. */
@media (prefers-reduced-motion: no-preference) {
    [data-tilt] {
        transform: perspective(900px)
                   rotateX(var(--kmo-tilt-x, 0deg))
                   rotateY(var(--kmo-tilt-y, 0deg));
        transition: transform var(--mo-dur-2) var(--mo-ease-out);
        will-change: transform;
    }
}
