/* ============================================================================
 * S8 Insights — Public motion system  ·  single source of truth
 * ============================================================================
 * A restrained, premium motion layer for the public marketing pages. Motion is
 * governed by tokens the same way colour is: two durations, two easings, one
 * stagger interval. Everything here animates ONLY transform/opacity (GPU-safe,
 * no layout shift) and is fully disabled under prefers-reduced-motion.
 *
 * Pair with shared/public-motion.js (IntersectionObserver reveals + count-up).
 *
 *   Entrance reveal:  <div data-reveal> ... </div>
 *   Staggered group:  <div data-reveal-group> <child/> <child/> ... </div>
 *   Count-up number:  <span data-countup>547</span>  (or data-countup="547")
 *   Card lift:        class="mo-lift"
 *   Press feedback:   class="mo-press"   (CTAs, buttons, pressable cards)
 * ============================================================================ */

:root {
  /* ── Motion tokens ── */
  --mo-fast:    150ms;   /* hovers, presses, colour changes */
  --mo-slow:    520ms;   /* entrances, reveals */
  --mo-ease-out: cubic-bezier(0.23, 1, 0.32, 1);   /* entering elements */
  --mo-ease:     cubic-bezier(0.4, 0, 0.2, 1);     /* hover / on-screen morph */
  --mo-stagger: 64ms;    /* delay between staggered siblings */
}

/* ── Scroll-triggered entrance reveal ──────────────────────────────────────
   Elements start slightly down + transparent, settle into place when scrolled
   into view (JS adds .is-in). Translate is small (premium, not theatrical).

   SAFETY: the hidden initial state is gated behind html.s8-motion, which is set
   by a tiny render-blocking inline script ONLY when JS is available. If JS is
   off or the engine fails to load, nothing is ever hidden — content renders
   normally. No blank-page failure mode. */
html.s8-motion [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity   var(--mo-slow) var(--mo-ease-out),
    transform var(--mo-slow) var(--mo-ease-out);
  will-change: opacity, transform;
}
html.s8-motion [data-reveal].is-in {
  opacity: 1;
  transform: none;
}
/* Once settled, drop the GPU hint so we don't hold layers forever. */
html.s8-motion [data-reveal].is-done { will-change: auto; }

/* Staggered children: JS sets --mo-i (0,1,2,...) on each direct child. */
html.s8-motion [data-reveal-group] > * {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity   var(--mo-slow) var(--mo-ease-out),
    transform var(--mo-slow) var(--mo-ease-out);
  transition-delay: calc(var(--mo-i, 0) * var(--mo-stagger));
  will-change: opacity, transform;
}
html.s8-motion [data-reveal-group].is-in > * {
  opacity: 1;
  transform: none;
}

/* ── Hover lift (cards) ────────────────────────────────────────────────────
   Gated behind real hover pointers so touch taps never trigger a stuck state. */
.mo-lift {
  transition:
    transform   var(--mo-fast) var(--mo-ease),
    border-color var(--mo-fast) var(--mo-ease),
    box-shadow  var(--mo-fast) var(--mo-ease);
}
@media (hover: hover) and (pointer: fine) {
  .mo-lift:hover { transform: translateY(-4px); }
}

/* ── Press feedback (CTAs, buttons, pressable cards) ────────────────────────
   Instant tactile confirmation that the interface heard the tap/click. */
.mo-press { transition: transform var(--mo-fast) var(--mo-ease-out); }
.mo-press:active { transform: scale(0.97); }

/* Count-up: hold the slot so digits filling in never cause a layout jump. */
[data-countup] { font-variant-numeric: tabular-nums; }

/* ── prefers-reduced-motion: disable all non-essential motion ───────────────
   Content appears immediately and in place. No transforms, no count-up. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal-group] > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .mo-lift,
  .mo-press { transition: none !important; }
  .mo-lift:hover { transform: none !important; }
  .mo-press:active { transform: none !important; }
}
