/* intro.css — First-visit intro overlay ("Marquee"), home page only.
   Loaded SYNCHRONOUSLY right after tokens.css (which it consumes: --void,
   --font-*, --accent-*, --border*, --surface-translucent, --muted*), so the
   closed doors paint on the very first frame and there is no critical-CSS
   duplicate to keep in sync. home.css (async) carries no intro rules.

   Monolith's kinetic type rhythm, then the ™ dot stretches into a beam of
   light and the void splits open along it onto the page that was alive
   underneath the whole time. The page renders normally from frame one; the
   two opaque door panels inside this overlay are what cover it.

   State model (classes on <html>, driven by the gate script in <head> and
   the completion script at end of body):
     intro-active  — overlay shown (doors closed). Set pre-paint. Structural
                     rules only key on this, so the cover needs no fonts.
     intro-play    — choreography runs. Added once Fredoka is loaded (or an
                     800ms timeout passes) so the slams never play in a
                     fallback face and reflow mid-show. Every animation keys
                     on this class.
     intro-done    — 0.45s dissolve. Teardown is two-phase: JS adds this
                     first and removes intro-active/intro-play ~500ms later,
                     so display stays block and every held animation frame
                     stays put while the fade runs.

   Master timeline (~5.5s from intro-play):
     0.12–1.92  three word slams with light wakes + mono captions
     1.92–2.42  YOURBOT™ slams center, lock wake
     2.40–3.10  hold: tracking tightens, glow pulse, glyph + tagline
     3.06–3.44  the ™ dot detaches, drops to the baseline
     3.40–3.72  dot whip-stretches into a full-width hairline beam,
                hard flare + lens streak at center
     3.70–5.12  the void splits along the beam (door easing); the wordmark
                rides the top panel and dissolves as the real nav brand
                ignites (relay at 4.6s/4.94s); the tagline rides the bottom
                panel away over its identical hero eyebrow
     3.55–5.50  seam light, settle veil and ambient boost decay into the
                hero glow; overlay pointer-events release at 4.6s

   Units: vertical positions that must line up with the door seam use % of
   the overlay (not vh — mobile dynamic toolbars make 100vh and the fixed
   overlay's height disagree and the seam would drift). All wordmark-derived
   geometry (dot start, travel, size) is expressed in multiples of --iwm so
   the mobile type scale moves the whole bridge with it. */

html.intro-active { scrollbar-gutter: stable; }
html.intro-active body { overflow: hidden; }
.intro {
  position: fixed; inset: 0; z-index: 1000;
  display: none;
  overflow: hidden;
  background: transparent; /* the doors are the cover */
  pointer-events: auto;
  --ie1: cubic-bezier(.4, 0, .2, 1);
  --ie2: cubic-bezier(.22, .61, .36, 1);
  --iein: cubic-bezier(.55, .06, .68, .19);
  --iedoor: cubic-bezier(.72, 0, .14, 1);
  /* Wordmark scale — every piece of dot/beam geometry derives from this,
     so the mobile override rescales the whole bridge coherently. */
  --iwm: 11vw;
  /* Theme-flipping light values. Dark: white-hot core over blue. Light:
     the same choreography in deep vivid blue so it reads on bright doors. */
  --ib-ink: #f2f4ff;
  --ib-core: #ffffff;
  --ib-flare-core: rgba(255, 255, 255, 0.95);
  --ib-seam-core: rgba(234, 243, 255, 0.75);
  --ib-hi: 116, 179, 255;
  --ib-ac: 59, 130, 246;
  --ib-cy: 52, 196, 244;
  --ib-blend: screen;
  --ib-door-a: #030409;
  --ib-door-b: #060811;
  --ib-door-c: #0b1020;
  --ib-glyph: clamp(3.2rem, 7vw, 7.5rem);
}
html[data-theme="light"] .intro {
  --ib-ink: #141c33;
  --ib-core: #1d4ed8;
  --ib-flare-core: rgba(37, 99, 235, 0.55);
  --ib-seam-core: rgba(37, 99, 235, 0.50);
  --ib-hi: 59, 130, 246;
  --ib-ac: 37, 99, 235;
  --ib-cy: 31, 143, 196;
  --ib-blend: normal;
  --ib-door-a: #d5dae9;
  --ib-door-b: #e4e7f2;
  --ib-door-c: #dbe3f6;
}
html.intro-active .intro { display: block; }
html.intro-done .intro {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* Natural end: doors are open and every layer has decayed, so this is
     invisible. Skip mid-play: a soft dissolve instead of a hard cut. */
  transition: opacity 0.45s cubic-bezier(.4, 0, .2, 1), visibility 0s linear 0.45s;
}
/* The overlay stops eating clicks once the doors look open (~4.6s) — the
   revealed CTAs are real from that moment. The skip pill keeps its own
   pointer-events so the visible skip mechanism stays alive to the end. */
html.intro-play .intro { animation: introPtrOff 0.01s linear 4.6s both; }

/* ── Nav-brand relay — the riding wordmark/glyph hand off to the real nav
     brand on the same clock the couriers expire (text 4.6s, icon 4.94s).
     :not(.intro-done) means a mid-play skip restores the brand instantly. ── */
html.intro-active:not(.intro-done) .nav-brand-icon,
html.intro-active:not(.intro-done) .nav-brand-text { opacity: 0; }
html.intro-play:not(.intro-done) .nav-brand-text {
  animation: introFadeIn 0.3s cubic-bezier(.22, .61, .36, 1) 4.6s both;
}
html.intro-play:not(.intro-done) .nav-brand-icon {
  animation: introFadeIn 0.3s cubic-bezier(.22, .61, .36, 1) 4.94s both;
}

/* ── Under-door light: seam glow, settle veil, ambient boost — revealed as
     the doors part, they decay into the hero's own ambient radial. ── */
.intro-seam {
  position: absolute; left: -10vw; right: -10vw; top: 36%; height: 28%;
  z-index: 0; opacity: 0; transform: scaleY(0.08); transform-origin: center;
  background: radial-gradient(ellipse 55% 50% at center,
    var(--ib-seam-core), rgba(var(--ib-hi), 0.45) 26%,
    rgba(var(--ib-ac), 0.22) 52%, transparent 74%);
  mix-blend-mode: var(--ib-blend);
}
html.intro-play .intro-seam { animation: introSeam 1.8s linear 3.55s both; }
/* uniform exposure wash: the page reads as flooded by the beam's light,
   then settles to its final grade (the mockup's m6Settle, as an overlay) */
.intro-veil {
  position: absolute; inset: 0; z-index: 0; opacity: 0; pointer-events: none;
  background: rgba(var(--ib-hi), 0.14);
  mix-blend-mode: var(--ib-blend);
}
html.intro-play .intro-veil { animation: introVeil 1.7s cubic-bezier(.22, .61, .36, 1) 3.75s both; }
.intro-boost {
  position: absolute; inset: 0; z-index: 0; opacity: 0; pointer-events: none;
  background: radial-gradient(ellipse 70% 40% at 46% 51%,
    rgba(var(--ib-hi), 0.22), rgba(var(--ib-ac), 0.10) 40%, transparent 70%);
  mix-blend-mode: var(--ib-blend);
}
html.intro-play .intro-boost { animation: introBoost 1.6s ease-in-out 3.9s both; }

/* ── The two halves of the darkness — the stage the words play on ── */
.intro-door {
  position: absolute; left: 0; right: 0; z-index: 1;
  will-change: transform;
}
.intro-door-t {
  top: 0; height: 50.06%;
  background: linear-gradient(180deg, var(--ib-door-a) 0%, var(--ib-door-b) 72%, var(--ib-door-c) 100%);
}
.intro-door-b {
  bottom: 0; height: 50.06%;
  background: linear-gradient(0deg, var(--ib-door-a) 0%, var(--ib-door-b) 72%, var(--ib-door-c) 100%);
}
html.intro-play .intro-door-t { animation: introDoorT 1.42s var(--iedoor) 3.7s both; }
html.intro-play .intro-door-b { animation: introDoorB 1.42s var(--iedoor) 3.7s both; }
/* faint texture so the mass of the panels is legible in motion */
.intro-door::before {
  content: ''; position: absolute; inset: 0;
  background:
    repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.014) 0 1px, transparent 1px 4px),
    radial-gradient(ellipse 90% 80% at 50% 50%, transparent 40%, rgba(0, 0, 0, 0.25) 100%);
}
/* seam-edge rims: the beam's light catching the panel edges */
.intro-door-t::after,
.intro-door-b::after {
  content: ''; position: absolute; left: 0; right: 0; height: 0.45vh; opacity: 0;
  background: linear-gradient(90deg, transparent 4%, rgba(var(--ib-hi), 0.85) 38%, var(--ib-core) 50%, rgba(var(--ib-hi), 0.85) 62%, transparent 96%);
  filter: blur(0.5px);
}
.intro-door-t::after { bottom: 0; }
.intro-door-b::after { top: 0; }
html.intro-play .intro-door-t::after,
html.intro-play .intro-door-b::after { animation: introRim 1.7s linear 3.42s both; }

/* ── Word slams (Monolith rhythm) ── */
.intro-words { position: absolute; inset: 0; z-index: 3; }
.intro-word { position: absolute; inset: 0; display: grid; place-items: center; }
.intro-w1 { --wd: 0.12s; }
.intro-w2 { --wd: 0.72s; }
.intro-w3 { --wd: 1.32s; }
.intro-wtext {
  grid-area: 1 / 1; font-family: var(--font-display); font-weight: 700;
  color: var(--ib-ink); letter-spacing: 0.01em; white-space: nowrap; opacity: 0;
  text-shadow: 0 0 3vw rgba(var(--ib-ac), 0.22);
  will-change: transform, opacity, filter;
}
.intro-w1 .intro-wtext { font-size: 8.4vw; }
.intro-w2 .intro-wtext { font-size: 11vw; }
.intro-w3 .intro-wtext { font-size: 12.5vw; }
.intro-wake {
  grid-area: 1 / 1; width: 86vw; height: 26vh; border-radius: 50%; opacity: 0;
  background: radial-gradient(closest-side, rgba(var(--ib-ac), 0.34), rgba(var(--ib-ac), 0.1) 55%, transparent 75%);
  will-change: transform, opacity;
}
.intro-cap {
  grid-area: 1 / 1; align-self: center; margin-top: 26vh; opacity: 0;
  font-family: var(--font-mono); font-size: clamp(0.62rem, 0.95vw, 1rem);
  letter-spacing: 0.34em; text-transform: uppercase; color: var(--accent-light);
}
html.intro-play .intro-wtext { animation: introSlam 0.6s var(--ie1) var(--wd) both; }
/* the shockwave fires ~70ms AFTER its word so it reads as consequence */
html.intro-play .intro-wake { animation: introWakeA 0.6s var(--ie2) calc(var(--wd) + 0.07s) both; }
html.intro-play .intro-cap { animation: introCapF 0.6s linear var(--wd) both; }

/* ── The YOURBOT™ lockup: lands center, holds, then rides the top panel
     (same distance, easing and clock) and dissolves into the nav relay. ── */
.intro-ride { position: absolute; inset: 0; z-index: 3; will-change: transform, opacity, filter; }
html.intro-play .intro-ride {
  animation: introRideUp 1.42s var(--iedoor) 3.7s both, introWordOut 0.32s linear 4.5s both;
}
.intro-lockgrid { position: absolute; inset: 0; display: grid; place-items: center; }
/* lifts the word center to ~43% so its baseline sits on the door seam */
.intro-lock { position: relative; top: -7%; }
.intro-slam { position: relative; opacity: 0; will-change: transform, opacity; }
html.intro-play .intro-slam { animation: introLockIn 0.5s var(--ie1) 1.92s both; }
.intro-lockwake {
  position: absolute; left: 50%; top: 50%; width: 92vw; height: 30vh;
  transform: translate(-50%, -50%); border-radius: 50%; opacity: 0;
  background: radial-gradient(closest-side, rgba(var(--ib-ac), 0.4), rgba(var(--ib-cy), 0.08) 55%, transparent 75%);
}
html.intro-play .intro-lockwake { animation: introWakeB 0.8s var(--ie2) 1.92s both; }
.intro-lockword {
  position: relative; z-index: 1; display: inline-block;
  font-family: var(--font-display); font-weight: 700; font-size: var(--iwm);
  letter-spacing: 0.04em; color: var(--ib-ink); white-space: nowrap;
}
html.intro-play .intro-lockword { animation: introGlowP 0.7s var(--ie2) 2.4s both; }
.intro-lt { display: inline-block; transform: translateX(var(--sp)); will-change: transform; }
html.intro-play .intro-lt { animation: introTighten 0.5s var(--ie2) 2.4s both; }
.intro-tm {
  display: inline-block; font-size: 0.26em; vertical-align: 0.16em; margin-left: 0.14em;
  color: var(--accent-light); transform: translateX(var(--sp));
}
/* the ™ fades the instant the dot ignites in its place */
html.intro-play .intro-tm { animation: introTighten 0.5s var(--ie2) 2.4s both, introOut 0.14s linear 3.06s both; }

/* glyph: fades in above the lockup, then rides up-left toward the nav
   corner and dissolves as the nav relay ignites the real brand icon */
.intro-glyph-wrap {
  position: absolute; z-index: 3;
  width: var(--ib-glyph); height: var(--ib-glyph);
  left: calc(50vw - var(--ib-glyph) / 2); top: calc(23% - var(--ib-glyph) / 2);
  opacity: 0;
  will-change: transform, opacity;
}
.intro-glyph-wrap::before {
  content: ''; position: absolute; inset: -60%; z-index: -1;
  background: radial-gradient(circle, rgba(var(--ib-ac), 0.38), rgba(var(--ib-cy), 0.12) 45%, transparent 70%);
}
.intro-glyph {
  display: block; width: 100%; height: 100%; object-fit: contain;
  filter: drop-shadow(0 0 1.4vw rgba(var(--ib-ac), 0.55));
}
html.intro-play .intro-glyph-wrap {
  animation:
    introFadeIn 0.45s var(--ie2) 2.5s both,
    introGlyphRide 1.42s var(--iedoor) 3.7s both,
    introOut 0.28s linear 4.92s both;
}
html.intro-play .intro-glyph-wrap::before { animation: introHalo 2.4s ease-in-out 2.6s 1 both; }

/* mono tagline beneath the lockup; rides the bottom panel away while the
   page's identical hero eyebrow is revealed beneath it */
.intro-tagline { position: absolute; inset: 0; z-index: 3; will-change: transform, opacity; }
.intro-tag-pos { position: absolute; left: 0; right: 0; top: 58%; text-align: center; }
.intro-tag-i {
  display: inline-block; font-family: var(--font-mono);
  font-size: clamp(0.66rem, 1.35vw, 1.35rem);
  letter-spacing: 0.24em; text-transform: uppercase; color: var(--accent-light); opacity: 0;
}
html.intro-play .intro-tag-i { animation: introTagIn 0.45s var(--ie2) 2.62s both; }
html.intro-play .intro-tagline {
  animation: introTagRide 1.42s var(--iedoor) 3.7s both, introOut 0.35s linear 3.92s both;
}

/* ── THE BRIDGE: the ™ dot becomes the light the doors open along ──
   Column wrapper spans the overlay height so the drop can land exactly
   on the 50% seam (top animates; the element is tiny, layout cost nil).
   All geometry in multiples of --iwm: at 11vw the dot starts 2.2 wordmark
   units right of center (the ™ position) and travels 2.268 back. */
.intro-dot {
  position: absolute; z-index: 4; left: calc(50vw + 2.2 * var(--iwm)); top: 0; bottom: 0;
  width: calc(0.136 * var(--iwm)); pointer-events: none;
}
html.intro-play .intro-dot { animation: introDotX 0.32s var(--ie1) 3.08s both; }
.intro-dot-core {
  position: absolute; left: 0; width: calc(0.136 * var(--iwm)); height: calc(0.136 * var(--iwm));
  top: calc(43% - 0.382 * var(--iwm)); border-radius: 50%; opacity: 0;
  background: radial-gradient(circle, var(--ib-core), rgba(var(--ib-hi), 0.9) 30%, rgba(var(--ib-ac), 0.4) 55%, transparent 75%);
  box-shadow: 0 0 1.2vw rgba(var(--ib-hi), 0.9), 0 0 3vw rgba(var(--ib-ac), 0.55);
  will-change: transform, opacity;
}
html.intro-play .intro-dot-core {
  animation: introDotY 0.32s var(--iein) 3.08s both, introDotOut 0.16s linear 3.4s both;
}
/* the hairline beam at the wordmark's baseline: the dot, stretched */
.intro-beamline {
  position: absolute; z-index: 4; left: 0; right: 0; top: calc(50% - 0.3vh); height: 0.56vh;
  opacity: 0; transform: scaleX(0.012); transform-origin: center;
  border-radius: 0.28vh;
  background: linear-gradient(90deg, transparent 0%, rgba(var(--ib-hi), 0.85) 8%, var(--ib-core) 50%, rgba(var(--ib-hi), 0.85) 92%, transparent 100%);
  box-shadow: 0 0 1.2vw rgba(var(--ib-hi), 0.9), 0 0 3vw rgba(var(--ib-ac), 0.6), 0 0 7vw rgba(var(--ib-cy), 0.3);
  will-change: transform, opacity;
}
html.intro-play .intro-beamline { animation: introBeam 1s linear 3.4s both; }
/* horizontal glow envelope around the beam */
.intro-glowh {
  position: absolute; z-index: 4; left: -8vw; right: -8vw; top: 40%; height: 20%;
  opacity: 0; transform: scaleY(0.25); transform-origin: center;
  background: radial-gradient(ellipse 50% 50% at center,
    rgba(var(--ib-ac), 0.34), rgba(var(--ib-ac), 0.13) 45%, transparent 72%);
  mix-blend-mode: var(--ib-blend);
}
html.intro-play .intro-glowh { animation: introGlowH 1.1s linear 3.42s both; }
/* hard center flare with restrained chromatic fringe */
.intro-flare {
  position: absolute; z-index: 4; left: 36vw; top: calc(50% - 14vw);
  width: 28vw; height: 28vw;
  opacity: 0; transform: scale(0.55); transform-origin: center;
  background: radial-gradient(circle, var(--ib-flare-core), rgba(var(--ib-hi), 0.5) 22%, rgba(var(--ib-ac), 0.2) 45%, transparent 70%);
  mix-blend-mode: var(--ib-blend);
}
.intro-flare::before,
.intro-flare::after { content: ''; position: absolute; inset: 0; }
.intro-flare::before {
  background: radial-gradient(circle, transparent 28%, rgba(var(--ib-cy), 0.22) 40%, transparent 56%);
  transform: translateX(0.6vw);
}
.intro-flare::after {
  background: radial-gradient(circle, transparent 28%, rgba(var(--ib-ac), 0.25) 40%, transparent 56%);
  transform: translateX(-0.6vw);
}
html.intro-play .intro-flare { animation: introFlare 0.9s linear 3.42s both; }
/* horizontal lens streak at the flare peak */
.intro-streak {
  position: absolute; z-index: 2; left: -4vw; right: -4vw; top: calc(50% - 1.1vh); height: 2.2vh;
  opacity: 0; transform: scaleX(0.25); transform-origin: center;
  background: linear-gradient(90deg, transparent, rgba(var(--ib-hi), 0.55) 18%, var(--ib-core) 50%, rgba(var(--ib-hi), 0.55) 82%, transparent);
  filter: blur(0.35vw);
  mix-blend-mode: var(--ib-blend);
}
html.intro-play .intro-streak { animation: introStreak 0.8s linear 3.44s both; }

/* ── Skip — hidden and unhittable until it fades in at 0.6s, then stays
     visible to the very end (WCAG 2.2.2: the stop mechanism must remain
     perceivable while the motion runs). Keeps its own pointer-events so it
     survives the overlay's 4.6s pointer release. ── */
.intro-skip {
  position: absolute;
  top: max(1.25rem, env(safe-area-inset-top, 0px));
  right: max(1.5rem, env(safe-area-inset-right, 0px));
  z-index: 6;
  background: var(--surface-translucent);
  border: 1px solid var(--border2);
  color: var(--muted);
  padding: 0.45rem 0.95rem;
  border-radius: 99px;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  pointer-events: auto;
  transition: color 0.2s, border-color 0.2s;
}
html.intro-play .intro-skip { animation: introSkipIn 0.4s var(--ie2) 0.6s both; }
.intro-skip:hover { color: var(--accent-light); border-color: var(--accent-dim); }
.intro-skip:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ── Keyframes ── */
@keyframes introFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes introOut { to { opacity: 0; } }
@keyframes introPtrOff { to { pointer-events: none; } }
@keyframes introSkipIn {
  0%   { opacity: 0; visibility: hidden; }
  1%   { opacity: 0; visibility: visible; }
  100% { opacity: 1; visibility: visible; }
}

/* impact grammar: blur-in arrival, chromatic kick on the hit frame,
   compression undershoot, recover, hold, blur-out */
@keyframes introSlam {
  0%   { opacity: 0; transform: scale(1.16); filter: blur(8px);
         text-shadow: 0.35vw 0 0 rgba(var(--ib-cy), 0.4), -0.35vw 0 0 rgba(var(--ib-ac), 0.4), 0 0 3vw rgba(var(--ib-ac), 0.22); }
  7%   { opacity: 1; transform: scale(1.1); filter: blur(0);
         text-shadow: 0.3vw 0 0 rgba(var(--ib-cy), 0.35), -0.3vw 0 0 rgba(var(--ib-ac), 0.35), 0 0 3vw rgba(var(--ib-ac), 0.22); }
  22%  { transform: scale(0.985);
         text-shadow: 0 0 0 rgba(var(--ib-cy), 0), 0 0 0 rgba(var(--ib-ac), 0), 0 0 3vw rgba(var(--ib-ac), 0.22); }
  34%  { transform: scale(1.005); }
  46%  { transform: scale(1); }
  82%  { opacity: 1; transform: scale(1); filter: blur(0); }
  100% { opacity: 0; transform: scale(0.965); filter: blur(7px); }
}
@keyframes introWakeA {
  0%   { opacity: 0; transform: scale(0.55, 0.6); }
  10%  { opacity: 0.55; transform: scale(0.9, 0.85); }
  100% { opacity: 0; transform: scale(1.28, 1.12); }
}
@keyframes introCapF {
  0%, 9% { opacity: 0; } 13% { opacity: 0.9; } 17% { opacity: 0.15; } 23% { opacity: 1; }
  80% { opacity: 1; } 100% { opacity: 0; }
}
@keyframes introLockIn {
  0%   { opacity: 0; transform: scale(1.16); filter: blur(8px); }
  14%  { opacity: 1; transform: scale(1.08); filter: blur(0); }
  38%  { transform: scale(0.99); }
  62%  { transform: scale(1.003); }
  100% { opacity: 1; transform: scale(1); filter: blur(0); }
}
@keyframes introWakeB {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.55, 0.6); }
  12%  { opacity: 0.7; transform: translate(-50%, -50%) scale(0.95, 0.9); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.35, 1.18); }
}
@keyframes introTighten { from { transform: translateX(var(--sp)); } to { transform: translateX(0); } }
@keyframes introGlowP {
  0%   { filter: drop-shadow(0 0 0 transparent); }
  35%  { filter: drop-shadow(0 0 3.4vw rgba(var(--ib-ac), 0.85)); }
  100% { filter: drop-shadow(0 0 1.1vw rgba(var(--ib-ac), 0.35)); }
}
@keyframes introTagIn { from { opacity: 0; transform: translateY(1.4vh); } to { opacity: 1; transform: translateY(0); } }
@keyframes introHalo {
  0%, 100% { transform: scale(1); opacity: 0.8; }
  50%      { transform: scale(1.12); opacity: 1; }
}

/* dot detach: x eases toward center, y accelerates down onto the seam */
@keyframes introDotX { to { transform: translateX(calc(-2.268 * var(--iwm))); } }
@keyframes introDotY {
  0%   { opacity: 0; transform: scale(0.4); }
  18%  { opacity: 1; transform: scale(1.12); }
  100% { opacity: 1; top: calc(50% - 0.068 * var(--iwm)); transform: scale(1.28); }
}
@keyframes introDotOut { to { opacity: 0; top: calc(50% - 0.068 * var(--iwm)); transform: scale(2.4); } }

/* dot -> beam: flicker-ignite, whip-stretch to full width, hold, hand off */
@keyframes introBeam {
  0%   { opacity: 0; transform: scaleX(0.012); animation-timing-function: cubic-bezier(.2, .7, .2, 1); }
  6%   { opacity: 1; }
  10%  { opacity: 0.45; }
  14%  { opacity: 1; }
  32%  { transform: scaleX(1); }
  55%  { opacity: 0.95; transform: scaleX(1); }
  100% { opacity: 0; transform: scaleX(1); }
}
@keyframes introGlowH {
  0%   { opacity: 0; transform: scaleY(0.25); }
  22%  { opacity: 0.9; transform: scaleY(0.6); }
  60%  { opacity: 0.6; transform: scaleY(1.05); }
  100% { opacity: 0; transform: scaleY(1.7); }
}
@keyframes introFlare {
  0%   { opacity: 0; transform: scale(0.55); }
  22%  { opacity: 1; transform: scale(1.12); }
  55%  { opacity: 0.45; }
  100% { opacity: 0; transform: scale(1.6); }
}
@keyframes introStreak {
  0%   { opacity: 0; transform: scaleX(0.25); }
  25%  { opacity: 0.95; transform: scaleX(1.02); }
  70%  { opacity: 0.3; }
  100% { opacity: 0; transform: scaleX(1.2); }
}
@keyframes introSeam {
  0%   { opacity: 0; transform: scaleY(0.08); }
  10%  { opacity: 0.95; transform: scaleY(0.2); }
  32%  { opacity: 0.8; transform: scaleY(0.65); }
  70%  { opacity: 0.45; transform: scaleY(1.35); }
  100% { opacity: 0; transform: scaleY(2.4); }
}
@keyframes introRim { 0% { opacity: 0; } 8% { opacity: 1; } 100% { opacity: 0.12; } }
@keyframes introDoorT { to { transform: translateY(-102%); } }
@keyframes introDoorB { to { transform: translateY(102%); } }
@keyframes introVeil { 0% { opacity: 0.9; } 100% { opacity: 0; } }
@keyframes introBoost { 0% { opacity: 0; } 45% { opacity: 0.5; } 100% { opacity: 0; } }

/* the wordmark/tagline ride their panels: full-height wrappers, so the
   percentage travel matches the doors' own-height travel exactly */
@keyframes introRideUp { to { transform: translateY(-51%); } }
@keyframes introWordOut { to { opacity: 0; filter: blur(0.5vw); } }
@keyframes introTagRide { to { transform: translateY(51%); } }
@keyframes introGlyphRide {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(-45vw, -19vh) scale(0.27); }
}

/* ── Mobile pass ─────────────────────────────────────────────────────────
   Bare vw type reads "giant" on desktop but modest on a 390px phone; scale
   the whole show up. --iwm moves the dot/beam geometry automatically. */
@media (max-width: 700px) {
  .intro { --iwm: 15vw; }
  /* Sizes budgeted so each nowrap string fits inside 100vw at the slam's
     1.1 hit-frame scale (see the width-budget lint test before retuning). */
  .intro-w1 .intro-wtext { font-size: 9.5vw; }
  .intro-w2 .intro-wtext { font-size: 13.5vw; }
  .intro-w3 .intro-wtext { font-size: 18vw; }
  .intro-cap { letter-spacing: 0.26em; }
}
/* Touch devices: 44px skip target, and trade the blend-mode luminosity for
   frame rate — five viewport-scale screen-blend layers animating at the
   door split are a known iOS Safari compositing cliff. The normal-blend
   rendering already ships as the light theme, so it is a designed look. */
@media (pointer: coarse) {
  .intro { --ib-blend: normal; }
  .intro-streak { filter: none; }
  .intro-skip { padding: 0.7rem 1.2rem; min-height: 44px; min-width: 44px; }
}

/* ── Backstops — the gate script never starts the intro for these users,
     but if the classes ever appear anyway, the page must win. ── */
@media (prefers-reduced-motion: reduce) {
  html.intro-active .intro { display: none !important; }
}
@media (forced-colors: active) {
  html.intro-active .intro { display: none !important; }
}
