/* ─────────────────────────────────────────────────────────────────────────
   Orcrist — motion.

   The old page applied one gesture (24px fade-up) to every element via a
   fixed data-delay="1..4" ladder. Here the gesture is chosen per element
   type, and the stagger is a CSS custom property set per instance:

       <div data-reveal style="--delay: .12s">

   ...which is unbounded and needs no new CSS rule per step.

   Deliberately restrained: this is clinical software, so there is no
   ambient drift or perpetual movement in the content. The one continuous
   animation on the site is the structures marquee, which is genuinely
   conveying "there are more of these than fit on screen".
   ───────────────────────────────────────────────────────────────────────── */

/* ── Reveal gestures ────────────────────────────────────────────────────
   [data-reveal] takes an optional value naming the gesture. JS adds
   .is-visible when the element scrolls into view.

   FAIL-VISIBLE: the hidden state is applied only once JS has confirmed it
   can drive the reveal, by setting .js-reveal on <html>. Without JS — or if
   the script errors before it runs — content simply renders normally rather
   than being stranded at opacity 0. */
.js-reveal [data-reveal] {
  opacity: 0;
  transition:
    opacity var(--dur-slow) var(--ease-out) var(--delay),
    transform var(--dur-slow) var(--ease-out) var(--delay);
  will-change: opacity, transform;
}
.js-reveal [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* Default: a short rise. For body copy and general blocks. */
.js-reveal [data-reveal],
.js-reveal [data-reveal="rise"] {
  transform: translateY(18px);
}

/* Headings settle from slightly further, slightly slower — they should
   arrive before the content they introduce. */
.js-reveal [data-reveal="settle"] {
  transform: translateY(26px);
  transition-duration: 0.85s;
}

/* Cards and panels scale up a hair as they fade, which reads as the object
   coming forward rather than sliding. */
.js-reveal [data-reveal="lift"] {
  transform: translateY(14px) scale(0.985);
}

/* Timeline steps enter from the rail side, reinforcing the left-to-right
   reading of the sequence. */
.js-reveal [data-reveal="slide"] {
  transform: translateX(-16px);
}

/* Figures/statistics simply fade — no movement, so numbers feel stable. */
.js-reveal [data-reveal="fade"] {
  transform: none;
}

/* ── Hero entrance ──────────────────────────────────────────────────────
   Runs once on load rather than on scroll, staggered so the eye is led
   through eyebrow → headline → lede → actions at reading pace. */
@keyframes hero-in {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.hero-inner > * {
  animation: hero-in 0.9s var(--ease-out) both;
  animation-delay: var(--delay, 0s);
}

/* ── Count-up ───────────────────────────────────────────────────────────
   The number itself is animated in JS; this only holds tabular figures so
   the width does not jitter while counting. */
[data-count] {
  font-variant-numeric: tabular-nums;
}

/* ── Reduced motion ─────────────────────────────────────────────────────
   A hard kill, not a softening: every transition, transform and continuous
   animation stops, and the marquee is swapped for a static wrapped list.
   JS reads the same media query and skips the count-up entirely. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .js-reveal [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
  }
  .hero-inner > * {
    animation: none !important;
    opacity: 1;
  }
  .marquee {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .marquee-track {
    display: none;
  }
  .marquee-static {
    display: flex;
  }
  .compare-new:hover,
  .team-card:hover,
  .btn-primary:hover {
    transform: none;
  }
}
