/* The container is tall; the stage inside it is one viewport and sticks.
   Scroll distance through the container becomes --p, 0 to 1, and every beat
   below is a pure function of it. JS computes one number; CSS does the rest. */
/* The flying pieces travel via transform to well past the viewport edge
   (--dx up to 85vw) before their beat starts; clip that at the container so
   it never contributes to the document's scrollable width. overflow-x, not
   overflow, and clip rather than hidden, so the y-axis and position: sticky
   inside are untouched. */
/* Pulled up under the sticky header. Without this the stage begins one header
   height down the page and has to travel that far before it pins, so the first
   heading visibly slides up by --nav-h before the sequence starts. */
.intro { height: 400svh; overflow-x: clip; margin-top: calc(-1 * var(--nav-h)); }

.beat {
  grid-area: 1 / 1;                 /* all beats stack in the stage's one cell */
  margin: 0; text-align: center;
  will-change: opacity, transform;
}

.beat-start { display: grid; gap: 0.5rem; }
.beat-kicker {
  display: block; color: var(--red-text);
  font-size: clamp(1rem, 2.5vw, 1.5rem); letter-spacing: 0.18em;
}
.beat-headline {
  display: block; color: var(--fg);
  font-size: clamp(2.5rem, 9vw, 6rem); letter-spacing: 0.04em;
}
.beat-welcome {
  color: var(--fg);
  font-size: clamp(1.5rem, 6vw, 4rem); letter-spacing: 0.06em;
}

/* The beats overlap continuously: every one starts before its predecessor has
   finished, so any scroll movement changes something on screen. Earlier timings
   left three dead stretches — 0-.20, .35-.55 and .85-1 — where scrolling did
   nothing at all, which read as the page having stalled. */

/* Beat 1: dissolving from the very first pixel of scroll. */
.beat-start {
  opacity: clamp(0, calc((0.20 - var(--p)) / 0.20), 1);
  filter: blur(calc(clamp(0, calc(var(--p) / 0.20), 1) * 6px));
}

/* Beat 2: in over .10-.30, out over .34-.54 — deliberately overlapping beat 1,
   as the design shows both on screen at once. */
.beat-welcome {
  opacity: clamp(0, min(calc((var(--p) - 0.10) / 0.20),
                        calc((0.54 - var(--p)) / 0.20)), 1);
}

/* Beat 3: the pieces fly in over .32-.92, starting while the welcome is still
   fading. --settle keeps the logo converging through the last stretch, so the
   tail of the sequence is still motion rather than a held frame. */
.beat-logo {
  --q: clamp(0, calc((var(--p) - 0.32) / 0.60), 1);
  --settle: clamp(0, calc((var(--p) - 0.86) / 0.14), 1);
  opacity: clamp(0, calc(var(--q) * 4), 1);
  transform: scale(calc(1.05 - 0.05 * var(--settle)));
}
.logo-piece {
  transform: translate(calc((1 - var(--q)) * var(--dx, 0px)),
                       calc((1 - var(--q)) * var(--dy, 0px)));
}
.logo-mark     { --dy: -75svh; }
.logo-arc      { --dx: 85vw; }
.logo-wordmark { --dx: -85vw; }
.logo-banner   { --dy: 75svh; }

.scroll-hint {
  position: absolute; right: var(--gutter); bottom: 2rem;
  width: 1.6rem; height: 2.6rem;
  border: 2px solid var(--fg); border-radius: 1rem;
  opacity: clamp(0, calc((0.12 - var(--p)) / 0.12), 1);
}
.scroll-hint::after {
  content: ''; position: absolute; left: 50%; top: 0.45rem;
  width: 3px; height: 0.6rem; margin-left: -1.5px;
  background: var(--fg); border-radius: 2px;
}

/* Content sections fade up once as they arrive. */
.reveal { opacity: 0; transform: translateY(1.5rem); transition: opacity 0.6s ease, transform 0.6s ease; }
.reveal.is-visible { opacity: 1; transform: none; }

/* No JS, or motion suppressed: no pinning, no animation. The stage's default
   --p of 1 already resolves every beat to its final state, so the assembled
   logo is simply there. */
@media (prefers-reduced-motion: reduce) {
  .intro { height: auto; }
  .intro-stage { position: static; height: 100svh; }
  .scroll-hint { display: none; }
  .reveal { opacity: 1; transform: none; transition: none; }
  /* Same reasoning as .nav-brand below: with no intro to assemble it, the
     brand lockup should just be there, and this should not depend on JS
     having run far enough to add .is-visible. */
  .nav-brand { opacity: 1; }
}
html:not(.js) .intro { height: auto; }
html:not(.js) .intro-stage { position: static; height: 100svh; }
html:not(.js) .scroll-hint { display: none; }
html:not(.js) .reveal { opacity: 1; transform: none; }
/* The brand lockup is only hidden pending the intro's reveal; with no JS
   there is no intro to spoil, so it should be visible from the start. */
html:not(.js) .nav-brand { opacity: 1; }

/* Half the scroll distance on phones, and shorter flight paths, so a visitor
   checking training times is not scrolling through four screens of animation. */
@media (max-width: 43.999rem) {
  .intro { height: 200svh; }
  .logo-mark     { --dy: -45svh; }
  .logo-arc      { --dx: 60vw; }
  .logo-wordmark { --dx: -60vw; }
  .logo-banner   { --dy: 45svh; }
}
