/*
 * motion.css — site-wide reveal system + reduced-motion guard.
 * Spec: docs/REDESIGN-PROPOSAL.md §2.5 (R1–R5), §2.7 (R6′, R7, R8, R9, R11).
 * Tokens (--dur-*, --ease-*, --stagger) live in base.css; this sheet only
 * consumes them. Loaded right after base.css (partials/_head_common.php).
 *
 * Contract with js/reveal.js:
 *   - Markup:  data-reveal="up|fade|scale|draw|lift" on the element;
 *              data-reveal-group on a parent to stagger its items (--i, cap 8).
 *   - Hidden state exists ONLY under html.lp-reveal-ready AND on elements the
 *     script registered (.is-pending). No JS, a script error, reduced motion,
 *     or an element inserted later and never registered = fully visible
 *     (R3, R9). Crawlers always get every word at full opacity.
 *   - On entry the script swaps .is-pending → .is-in, then adds .is-settled
 *     once the entrance is over (drops the stagger delay for later hovers).
 *
 * Variants:
 *   up     translateY(16px) → 0 + fade      below-the-fold blocks only
 *   fade   opacity 0 → 1                     below-the-fold blocks only
 *   scale  scale(.98) → 1 + fade             cards, tiles
 *   draw   element stays visible; its .lp-draw strokes are drawn
 *          (each stroke needs pathLength="1")
 *   lift   translateY(12px) → 0, opacity stays 1 — the ONLY variant allowed
 *          on LCP candidates and text that must never be hidden (R7, R9)
 *
 * R1: transform + opacity only (stroke-dashoffset for draw is the exception).
 * R2: every motion rule sits inside (prefers-reduced-motion: no-preference).
 * R6′: ambient budget — see the "Ambient loops" section below.
 * R8: hover-only motion lives inside @media (hover: hover) and (pointer: fine);
 *     touch / coarse pointers get :active press states only. Shared hooks:
 *       .lp-hover-lift   lift on hover (--lp-lift, default -2px)
 *       .btn--sheen      one facet sheen sweep per hover
 *       .lp-press        press state for coarse pointers
 *     Page sheets that add their own hover motion (tilt, fan-out, heading
 *     anchors…) must use the same media query.
 */

/* ── Reveal system (R3, R7, R9) ───────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
    .lp-reveal-ready [data-reveal] {
        transition:
            opacity var(--dur-reveal) var(--ease-out),
            transform var(--dur-reveal) var(--ease-out);
        transition-delay: calc(min(var(--i, 0), 8) * var(--stagger));
    }
    .lp-reveal-ready [data-reveal].is-settled { transition-delay: 0s; }

    /* Pre-entrance states. */
    .lp-reveal-ready [data-reveal="up"].is-pending    { opacity: 0; transform: translateY(16px); }
    .lp-reveal-ready [data-reveal="fade"].is-pending  { opacity: 0; }
    .lp-reveal-ready [data-reveal="scale"].is-pending { opacity: 0; transform: scale(.98); }
    /* LCP-safe: never starts transparent (R7). */
    .lp-reveal-ready [data-reveal="lift"].is-pending  { transform: translateY(12px); }

    /* "draw": the element stays visible, its strokes are drawn. */
    .lp-reveal-ready [data-reveal="draw"].is-pending .lp-draw,
    .lp-reveal-ready [data-reveal="draw"].is-in .lp-draw {
        stroke-dasharray: 1;
        transition: stroke-dashoffset 900ms var(--ease-out);
        transition-delay: inherit;
    }
    .lp-reveal-ready [data-reveal="draw"].is-pending .lp-draw { stroke-dashoffset: 1; }
}

/* ── Ambient loops (R4, R6′) ──────────────────────────────────────────── */
/* A CSS ambient loop is declared by [data-ambient] on its root (facet field:
   partials/facet_field.php). js/ambient.js toggles .is-offscreen on it.

   R6′ budget: the home hero may run TWO loops (facet field + real-asset
   turntable); every other screen at most ONE per viewport. The guard below
   enforces it between sibling loops: from the 2nd [data-ambient] of a parent
   on, loops are paused, unless the parent declares data-ambient-budget="2"
   (home hero only), where the 3rd on is paused. It never sets `running`, so
   .is-offscreen and reduced motion always win. Loops in different sections
   sharing a viewport, and WebGL loops (turntable: AssetCardViewer), are not
   visible to CSS — the budget is a review rule for those. */
.is-offscreen,
.is-offscreen * { animation-play-state: paused !important; }

:not([data-ambient-budget="2"]) > [data-ambient] ~ [data-ambient],
:not([data-ambient-budget="2"]) > [data-ambient] ~ [data-ambient] *,
[data-ambient] ~ [data-ambient] ~ [data-ambient],
[data-ambient] ~ [data-ambient] ~ [data-ambient] * { animation-play-state: paused !important; }

/* ── Hover motion: fine pointers only (R8) ────────────────────────────── */
.btn--sheen { position: relative; overflow: hidden; }
.btn--sheen::after {
    content: "";
    position: absolute;
    inset: -1px;
    pointer-events: none;
    background: linear-gradient(105deg, transparent 30%, rgba(255, 255, 255, .35) 50%, transparent 70%);
    transform: translateX(-120%);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
    .lp-hover-lift { transition: transform var(--dur-ui) var(--ease-out); }
    .lp-hover-lift:hover { transform: translateY(var(--lp-lift, -2px)); }
    /* One sweep per hover; returns instantly so the next hover sweeps again. */
    .btn--sheen:hover::after {
        transform: translateX(120%);
        transition: transform var(--dur-reveal) var(--ease-out);
    }
}

/* Touch / coarse pointers: no hover motion (a tap leaves :hover stuck), a
   press state instead. Overrides base.css `.btn:hover` lift — this sheet is
   read right after base.css at the same specificity. */
@media (hover: none), (pointer: coarse) {
    .btn:hover,
    .lp-hover-lift:hover { transform: none; }
    .btn:active:not(:disabled),
    .lp-press:active { transform: scale(.98); }
}

/* ── Page transitions (R11) ───────────────────────────────────────────── */
/* Cross-document View Transitions, progressive: browsers without support
   ignore every rule here and navigate exactly as before. Opt-in only under
   no-preference, so reduced motion gets plain navigations (no crossfade, no
   morph). Both pages of a navigation must opt in — this sheet loads site-wide.
   Card → detail morph: .assets-card-thumb (assets/_card.php) and
   #lp-asset-viewer-root (assets/show.php) share `view-transition-name:
   asset-{id}` + `view-transition-class: lp-asset-poster`, set inline and
   issued once per page by Lowpoly\Services\ViewTransitionName. */
@media (prefers-reduced-motion: no-preference) {
    @view-transition { navigation: auto; }

    ::view-transition-group(root) { animation-duration: var(--dur-ui); }

    ::view-transition-group(.lp-asset-poster) {
        animation-duration: var(--dur-panel);
        animation-timing-function: var(--ease-out);
    }
    /* 4:3 card box ↔ 16:10 stage: letterbox both snapshots in the moving box
       instead of the default width-fit, which would overflow it. */
    ::view-transition-old(.lp-asset-poster),
    ::view-transition-new(.lp-asset-poster) {
        height: 100%;
        object-fit: contain;
    }
}

/* ── Global reduced-motion guard (R2) ─────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .01ms !important;
        animation-delay: 0s !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        transition-delay: 0s !important;
        scroll-behavior: auto !important;
    }
    /* Loops stop and show their static first frame (R2, R6′). */
    [data-ambient],
    [data-ambient] * { animation: none !important; }
}
