/* ─── motion ─────────────────────────────────────────────────────────────
   One orchestrated load, then quiet scroll reveals. Every scrubbed effect
   collapses to its finished state under prefers-reduced-motion.
   ───────────────────────────────────────────────────────────────────────── */

/* ── the load: the wordmark wipes up letter by letter ── */
.wordmark span {
  transform: translateY(0.16em);
  opacity: 0;
  animation: strike-up 1.1s var(--ease) forwards;
  animation-delay: calc(var(--i) * 55ms + 150ms);
}

@keyframes strike-up {
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* everything else on the first screen fades in behind the wordmark */
.hero__eyebrow,
.hero__strip,
.topbar {
  opacity: 0;
  animation: fade-in 1s var(--ease) forwards;
  animation-delay: 700ms;
}

.hero__strip {
  animation-delay: 850ms;
}

@keyframes fade-in {
  to {
    opacity: 1;
  }
}

/* ── scroll reveals ──
   .rv elements start displaced and clipped; .in releases them. --d staggers
   siblings without a keyframe per child.

   The hidden state is gated on html.js — set by an inline script in <head> —
   so that if scripting is off, blocked, or slow, the content is simply
   visible rather than permanently invisible. A reveal must never be load
   bearing for reading the page. */
.js .rv {
  opacity: 0;
  transform: translateY(2.2rem);
  clip-path: inset(0 0 100% 0);
  transition:
    opacity 0.9s var(--ease) var(--d, 0ms),
    transform 1s var(--ease) var(--d, 0ms),
    clip-path 1.1s var(--ease) var(--d, 0ms);
}

/* The finished clip is deliberately negative on the top edge. Humane's ink
   overhangs its line box by roughly 0.05em at the line-heights used here, so
   a clip of `inset(0 ...)` shaves the tops off every display heading. */
.js .rv.in {
  opacity: 1;
  transform: none;
  clip-path: inset(-30% 0 -25% 0);
}

/* a variant that wipes horizontally — used on the numbered section heads */
.js .rv-x {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 1.1s var(--ease) var(--d, 0ms);
}

.js .rv-x.in {
  clip-path: inset(-30% 0 -25% 0);
}

/* ── the count-up numbers hold tabular width so they don't jitter ── */
.num b {
  transition: none;
}

/* ── reduced motion: keep the design, drop the movement ── */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .wordmark span,
  .hero__eyebrow,
  .hero__strip,
  .topbar {
    opacity: 1;
    transform: none;
  }

  .js .rv,
  .js .rv-x {
    opacity: 1;
    transform: none;
    clip-path: none;
  }

  /* the rail stops being a scrubbed stage and becomes an ordinary grid */
  .rail {
    height: auto;
  }

  /* The stage is a flex row, which would leave the track sized to its own
     content and defeat the wrap — so it becomes a plain block and the track
     becomes an ordinary responsive grid of cards. */
  .rail__stage {
    display: block;
    position: static;
    height: auto;
    overflow: visible;
    padding: 3rem var(--gut);
  }

  .rail__track {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    padding-inline: 0;
    transform: none !important;
  }

  .rail__card {
    width: auto;
  }

  .rail__cap {
    position: static;
    display: block;
    margin-top: 2rem;
  }

  /* a scrub progress bar means nothing when there is no scrub */
  .rail__prog {
    display: none;
  }

  .marquee__row {
    transform: none !important;
  }

  .rung::after {
    transform: scaleX(1);
  }

  .card[data-tier="legendary"] .card__frame {
    animation: none;
  }
}
