/* ==========================================================================
   motion.css — scroll-triggered entrances
   --------------------------------------------------------------------------
   The motion thesis is inspection-line discipline. The visual world is the
   spec sheet and the inspection tag, so motion borrows that world's behaviour:
   content settles into place in short, measured steps, the way a gauge comes
   to rest — never sliding around for effect.

   One authored moment exists: the evidence ledger. Its oversized maroon
   numerals start lower and smaller than their claim and land a beat later, so
   the numbers read as a sequence being counted off rather than as four blocks
   fading in. Everywhere else the entrance is one quiet rise per section
   heading, and a capped sibling stagger for card, step and spec collections.

   Rules this file obeys:
   - transform, opacity and visibility only. Nothing here triggers layout, so
     there is no cumulative layout shift from an entrance.
   - The hidden state exists ONLY when scripting is on (`.js`, set inline in
     the document head) AND the OS has not asked for reduced motion. No
     script, or reduced motion, means every element renders finished — content
     is never trapped behind a script that failed to load.
   - Entrances are CSS animations, not transitions. A transition declared here
     would overwrite a component's own transition and slow its hover feedback
     (`.product-card`, `.btn`); an animation leaves those transitions intact.
     `backwards` fill holds the hidden state through the stagger delay and
     releases the element afterwards, so no forced `transform` lingers.
   - The hidden state is opacity and transform ONLY. It deliberately does NOT
     set `visibility: hidden`: that would remove every unrevealed target from
     the accessibility tree and from the tab order, so a screen reader or a
     keyboard user could never reach the ledger, the steps or the FAQ at all —
     the browser cannot scroll to an element that is not focusable, so the
     reveal could never fire for them. Because the hidden state is not
     `visibility: hidden`, tabbing to an off-screen control scrolls it into
     view, which fires the observer and reveals it. Painting is still cheap:
     an element at `opacity: 0` is painted but not visible, and the whole
     system is gated on `.js` + `prefers-reduced-motion: no-preference`.
   - Covered by a print override: a printed page has no scroll, so no reveal.

   Vocabulary (the names a WordPress template would print):
     [data-reveal]           one element that rises into place.
     [data-reveal="fade"]    rises without travel (mastheads, dark bands).
     [data-reveal="left"]    enters from the left.
     [data-reveal="right"]   enters from the right.
     [data-reveal="scale"]   a near-imperceptible settle in size.
     [data-reveal-stagger]   a container whose direct children rise in turn;
                             reveal.js writes each child's --reveal-delay.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {

  /* ------------------------------------------------------------------------
     Hidden state.
     `:where()` pins this rule's specificity to (0,1,0) so it never outranks a
     component's own rules. Standalone targets and the children of a stagger
     group both start displaced, transparent, and out of the tab order.
     ------------------------------------------------------------------------ */
  .js :where([data-reveal]:not(.is-revealed)),
  .js :where([data-reveal-stagger]) > :where(:not(.is-revealed)) {
    opacity: 0;
    transform: translate3d(
        var(--reveal-x, 0),
        var(--reveal-y, var(--reveal-shift, 18px)),
        0
      )
      scale(var(--reveal-scale, 1));
  }

  /* Direction and shape variants for standalone targets. They set custom
     properties only; the transform above reads them. */
  .js :where([data-reveal="fade"])   { --reveal-y: 0px; }
  .js :where([data-reveal="left"])   { --reveal-x: -24px; --reveal-y: 0px; }
  .js :where([data-reveal="right"])  { --reveal-x: 24px;  --reveal-y: 0px; }
  .js :where([data-reveal="scale"])  { --reveal-y: 14px;  --reveal-scale: 0.97; }

  /* ------------------------------------------------------------------------
     Revealed state. The animation carries the entrance; the plain
     declarations that follow it carry the steady state, so once the animation
     releases, the element simply sits at its natural position and a component
     hover transform (0,2,0) is free to win over this `transform: none`.
     ------------------------------------------------------------------------ */
  .js :where([data-reveal].is-revealed),
  .js :where([data-reveal-stagger]) > :where(.is-revealed) {
    opacity: 1;
    transform: none;
    animation: hjt-reveal-rise var(--dur-reveal) var(--ease-out)
               var(--reveal-delay, 0ms) backwards;
  }

  @keyframes hjt-reveal-rise {
    from {
      opacity: 0;
      transform: translate3d(
          var(--reveal-x, 0),
          var(--reveal-y, var(--reveal-shift, 18px)),
          0
        )
        scale(var(--reveal-scale, 1));
    }
    to {
      opacity: 1;
      transform: none;
    }
  }

  /* ------------------------------------------------------------------------
     The authored moment — the evidence ledger.
     The claim rises on the item's beat; the maroon numeral follows 70ms
     later, travelling further and settling from a smaller size. The delay is
     added to the child's own --reveal-delay so the whole ledger stays in
     sequence rather than every numeral snapping together.
     ------------------------------------------------------------------------ */
  .js :where([data-reveal-stagger]) > :where(.ledger__item.is-revealed) .ledger__num {
    animation: hjt-reveal-num 680ms var(--ease-out)
               calc(var(--reveal-delay, 0ms) + 70ms) backwards;
  }

  @keyframes hjt-reveal-num {
    from { transform: translate3d(0, 12px, 0) scale(0.92); }
    to   { transform: none; }
  }
}

/* --------------------------------------------------------------------------
   Reduced motion. The block above never applies, so every target is already
   finished; this only guarantees the same for a browser that somehow reached
   the hidden state anyway. Feedback that confirms an action — hover, focus,
   the drawer — is untouched, because that is not motion for its own sake.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-reveal-stagger] > *,
  .js [data-reveal-stagger] > * .ledger__num {
    opacity: 1;
    visibility: visible;
    transform: none;
    animation: none;
  }
}

/* A printed page never scrolls, so no reveal ever fires. Render every target
   finished rather than shipping a print-out of the hidden state. */
@media print {
  .js [data-reveal],
  .js [data-reveal-stagger] > *,
  .js [data-reveal-stagger] > * .ledger__num {
    opacity: 1;
    visibility: visible;
    transform: none;
    animation: none;
  }
}
