/* ─── Scroll-Reveal Orchestrator (.hzo-reveal--*) ──────────────────────────
   W-020 · spec §8.2. The SUPERSET reveal engine reachable via [hzo_reveal]
   and reveal="effect": fade / slide-* / scale-in / blur-in / mask-up /
   clip-wipe, group stagger, order, and a native animation-timeline:view()
   scrub fast path — all reduced-motion, forced-colors, print, and no-JS safe.

   ADDITIVE — DOES NOT TOUCH the bare .hzo-reveal legacy path. The always-on
   fade+rise (assets/js/main.js + components/_base/scroll-reveal.css) keeps
   owning bare class="hzo-reveal" byte-for-byte. This orchestrator only ever
   claims elements carrying an orchestrator marker (.hzo-reveal--root / the
   JS-applied .hzo-reveal--item), NEVER the standalone .hzo-reveal token, so
   the two systems never fight over the same node during the coexistence
   window. Every class here still contains the "hzo-reveal" substring, so the
   gate marker still enqueues these bytes.

   FAIL-VISIBLE BY CONSTRUCTION: the armed (hidden) leg is gated on the
   JS-set html.hzo-reveal-ready flag. No JS, a thrown init, reduced-motion,
   forced-colors, or print → the flag / armed class is absent → content is
   fully visible. Content is hidden ONLY with opacity/transform/filter/
   clip-path — NEVER display:none / visibility:hidden / pointer-events:none —
   so AT can read and keyboards can reach an unrevealed node (§8.2 A11y).
   ─────────────────────────────────────────────────────────────────────── */

/* The root wrapper is a passive container in group mode and the item itself
   in single mode; it sets no visual state of its own. Items (the root in
   single mode, or the resolved children in group mode) are marked
   .hzo-reveal--item by reveal.js. */
.hzo-reveal--root { }

/* ── Motion tokens (author-overridable via inline custom properties the
      handler sets from [hzo_reveal] options). Defaults reproduce the shipped
      legacy timing: 600ms, ease-out, 20px rise. ── */
.hzo-reveal--item {
    --hzo-reveal-dur: 600ms;
    --hzo-reveal-ease: var(--ease-out);
    --hzo-reveal-delay: 0ms;
    --hzo-reveal-distance: 20px;
    --hzo-reveal-scale-from: 0.92;
    --hzo-reveal-blur: 8px;
}

/* ── Armed (hidden) leg — ONLY under the JS-ready flag. Each effect declares
      its start visual via the shared --hzo-rv-* custom properties so a single
      transform/opacity/filter keyframe covers them all. ── */
.hzo-reveal-ready .hzo-reveal--item.hzo-reveal--init {
    opacity: 0;
    transform: translate3d(var(--hzo-rv-x, 0), var(--hzo-rv-y, 0), 0) scale(var(--hzo-rv-s, 1));
    filter: var(--hzo-rv-filter, none);
    will-change: opacity, transform;
}

/* Per-effect start values (custom props consumed by the armed leg + keyframe). */
.hzo-reveal--fade        { }                                                   /* opacity only */
.hzo-reveal--slide-up    { --hzo-rv-y: var(--hzo-reveal-distance, 20px); }     /* starts low, rises */
.hzo-reveal--slide-down  { --hzo-rv-y: calc(-1 * var(--hzo-reveal-distance, 20px)); }
.hzo-reveal--slide-left  { --hzo-rv-x: var(--hzo-reveal-distance, 20px); }     /* JS flips sign for RTL */
.hzo-reveal--slide-right { --hzo-rv-x: calc(-1 * var(--hzo-reveal-distance, 20px)); }
.hzo-reveal--scale-in    { --hzo-rv-s: var(--hzo-reveal-scale-from, 0.92); }
.hzo-reveal--blur-in     { --hzo-rv-filter: blur(var(--hzo-reveal-blur, 8px)); }

/* Revealed leg — the discrete IO/JS path. `backwards` fill holds the start
   visual through any stagger delay so a delayed item never flashes visible
   first. Fires only while .hzo-reveal--init is still present; reveal.js drops
   --init on settle, leaving a plain visible .hzo-reveal--item.hzo-reveal--in
   (no animation, no will-change) so later :hover/:focus carry no stale delay. */
.hzo-reveal-ready .hzo-reveal--item.hzo-reveal--init.hzo-reveal--in {
    animation: hzo-reveal-in var(--hzo-reveal-dur, 600ms)
               var(--hzo-reveal-ease, var(--ease-out, ease))
               var(--hzo-reveal-delay, 0ms) backwards;
}
@keyframes hzo-reveal-in {
    from {
        opacity: 0;
        transform: translate3d(var(--hzo-rv-x, 0), var(--hzo-rv-y, 0), 0) scale(var(--hzo-rv-s, 1));
        filter: var(--hzo-rv-filter, none);
    }
    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

/* ── Clip / mask effects: clip-path is not smoothly interpolable to a keyword,
      so these get dedicated keyframes that animate inset()→inset(0). reveal.js
      sets --hzo-rv-clip per `origin` (RTL-aware for the inline axis). ── */
.hzo-reveal--clip-wipe,
.hzo-reveal--mask-up {
    --hzo-rv-clip: inset(100% 0 0 0);   /* default origin=up: reveals bottom → top */
}
.hzo-reveal--mask-up { --hzo-rv-y: var(--hzo-reveal-distance, 24px); }
.hzo-reveal-ready .hzo-reveal--item.hzo-reveal--clip-wipe.hzo-reveal--init,
.hzo-reveal-ready .hzo-reveal--item.hzo-reveal--mask-up.hzo-reveal--init {
    clip-path: var(--hzo-rv-clip, inset(100% 0 0 0));
}
.hzo-reveal-ready .hzo-reveal--item.hzo-reveal--clip-wipe.hzo-reveal--init.hzo-reveal--in,
.hzo-reveal-ready .hzo-reveal--item.hzo-reveal--mask-up.hzo-reveal--init.hzo-reveal--in {
    animation: hzo-reveal-clip var(--hzo-reveal-dur, 600ms)
               var(--hzo-reveal-ease, var(--ease-out, ease))
               var(--hzo-reveal-delay, 0ms) backwards;
}
@keyframes hzo-reveal-clip {
    from {
        opacity: 1;
        clip-path: var(--hzo-rv-clip, inset(100% 0 0 0));
        transform: translate3d(var(--hzo-rv-x, 0), var(--hzo-rv-y, 0), 0) scale(var(--hzo-rv-s, 1));
    }
    to {
        opacity: 1;
        clip-path: inset(0 0 0 0);
        transform: none;
    }
}

/* ═══ Native scroll-driven fast path (main-thread-free) ════════════════════
   repeat="every"/"scrub" ride animation-timeline: view() where supported —
   no IntersectionObserver, no JS per frame. reveal.js marks these
   .hzo-reveal--native and does NOT arm/observe them. The armed leg still
   holds the start visual (from the effect custom props) until the view
   timeline scrubs it in. */
@supports (animation-timeline: view()) {
    .hzo-reveal-ready .hzo-reveal--item.hzo-reveal--native {
        opacity: 0;
        transform: translate3d(var(--hzo-rv-x, 0), var(--hzo-rv-y, 0), 0) scale(var(--hzo-rv-s, 1));
        filter: var(--hzo-rv-filter, none);
        animation: hzo-reveal-in var(--hzo-reveal-dur, 600ms) var(--hzo-reveal-ease, var(--ease-out, ease)) both;
        animation-timeline: view();
        animation-range: entry 0% cover 35%;
    }
    /* scrub re-runs across the whole view pass; every settles once in view */
    .hzo-reveal-ready .hzo-reveal--item.hzo-reveal--native.hzo-reveal--scrub {
        animation-range: entry 0% exit 100%;
    }
}

/* ═══ Fail-visible environment branches (spec §8.2 Environment) ════════════ */

/* reduced-motion [PRESERVE-5 · G13/G60]: ambient motion fully STOPS, content
   visible — never stranded at opacity:0. reveal.js also force-reveals in JS;
   this is the CSS belt to its suspenders (and covers the native path). */
@media (prefers-reduced-motion: reduce) {
    .hzo-reveal--item,
    .hzo-reveal-ready .hzo-reveal--item.hzo-reveal--init,
    .hzo-reveal-ready .hzo-reveal--item.hzo-reveal--native {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        clip-path: none !important;
        animation: none !important;
        will-change: auto !important;
    }
}

/* forced-colors [G60]: opacity/blur intermediates are unreliable in High
   Contrast — collapse to show-instant. */
@media (forced-colors: active) {
    .hzo-reveal--item,
    .hzo-reveal-ready .hzo-reveal--item.hzo-reveal--init,
    .hzo-reveal-ready .hzo-reveal--item.hzo-reveal--native {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        clip-path: none !important;
        animation: none !important;
    }
}

/* @media print [G13 — reveal is the named culprit in §12.1]: print-without-
   scrolling must NEVER blank a still-armed timeline/steps/bento/feature/logo
   section. Force every armed node visible; the beforeprint JS handler
   additionally flushes the native scroll-driven path CSS alone can't unfreeze. */
@media print {
    .hzo-reveal--item,
    .hzo-reveal--init,
    .hzo-reveal--native {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        clip-path: none !important;
        animation: none !important;
    }
}
