/* ============================================================
   Chorus — marketing page
   Brand tokens follow WEB-01: midnight navy, mint three-band C,
   ivory wordmark, tagline exactly "Many minds. One mission."
   ============================================================ */

:root {
  color-scheme: dark;

  /* palette */
  --navy-900: #040C1F;
  --navy:     #071735;
  --navy-700: #0B2048;
  --navy-600: #123068;
  --mint:     #49F5A2;
  --mint-dim: #2BC77F;
  --ivory:    #FFF8E8;
  --amber:    #F0B429;

  --ink:      var(--ivory);
  --ink-soft: color-mix(in srgb, var(--ivory) 68%, transparent);
  /* 62% is well clear of WCAG AA 4.5:1 for small print, not at the floor —
     solving for the floor over the surfaces text actually sits on gives 46.35%
     over --navy-900 (the page), 46.90% over --navy, 46.46% over the terminal
     body and 48.35% over --navy-700 (cards and terminal frames). So 62% carries
     ~14 points of headroom and measures 6.53:1 at its tightest. The headroom is
     deliberate at 11px, but anyone who needs to move this token has room; an
     earlier version of this comment called 62% the minimum, which was wrong.
     Not --navy-600, which is only a gradient stop in the hero glow and never a
     text background. */
  --ink-dim:  color-mix(in srgb, var(--ivory) 62%, transparent);
  --line:     color-mix(in srgb, var(--mint) 18%, transparent);
  /* --line draws frames: card edges, panel edges, the terminal's chrome. WCAG
     1.4.11 does not govern those — a heading or a caption identifies the thing
     inside, and the frame is decoration. --edge draws the boundary of something
     you can operate, where the line *is* the affordance, and that has to reach
     3:1. Solving over each surface these sit on: ivory needs 35% against the page
     behind the ghost button, 35% inside a terminal, 35% inside the pyramid's
     button. 40% clears all three with headroom (3.3:1 at the tightest). Kept as
     its own token so toning the frames down later cannot quietly take a control's
     only affordance below the floor. */
  --edge:     color-mix(in srgb, var(--ivory) 40%, transparent);
  /* The pill ring: one value for the whole family — the tag in a terminal bar, the
     agent status pill, the dashboard's status pills, the active tab, the planning
     chip. All of them were a 12% fill inside a 26-35% ring and all of them measured
     2.02 to 2.21:1. They sit on five different surfaces, and the worst of the five
     needs mint at 43% and amber at 50% for 3:1, so 48% and 55% carry every one of
     them past 3.3:1. It matters most for the active tab, where the ring and the
     tint are the only things saying which tab is current; on the pills a dot and a
     word say it too, and they share the value because a page where one pill has a
     crisper ring than the next looks like a mistake. */
  --ring-mint:  color-mix(in srgb, var(--mint) 48%, transparent);
  --ring-amber: color-mix(in srgb, var(--amber) 55%, transparent);

  /* type */
  --sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* motion */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --dur: .8s;

  /* scroll progress, written by main.js */
  --p: 0;      /* hero progress 0 -> 1 */
  --scroll: 0; /* whole-page progress 0 -> 1 */

  --pad: clamp(1.25rem, 5vw, 6rem);
  --maxw: 1200px;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* stop iOS inflating type in landscape, which would break the mono
     terminal columns */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--navy-900);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(1rem, .55vw + .9rem, 1.125rem);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  -webkit-tap-highlight-color: color-mix(in srgb, var(--mint) 22%, transparent);
}

h1, h2, h3 { line-height: 1.08; letter-spacing: -.03em; margin: 0 0 .6em; font-weight: 700; }
h2 { font-size: clamp(1.9rem, 3.6vw, 3.2rem); }
h3 { font-size: clamp(1.05rem, 1.2vw, 1.25rem); letter-spacing: -.015em; }
p  { margin: 0 0 1.1em; }
em { color: var(--mint); font-style: normal; }
a  { color: var(--mint); text-decoration-thickness: 1px; text-underline-offset: 3px; }

.note { color: var(--ink-dim); font-size: .9rem; line-height: 1.55; }

.eyebrow {
  font: 600 .78rem/1 var(--mono);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--mint);
  margin-bottom: 1.25rem;
}

.skip {
  position: fixed; left: .5rem; top: .5rem; z-index: 99;
  background: var(--mint); color: var(--navy); padding: .6rem 1rem;
  border-radius: .5rem; font-weight: 600; text-decoration: none;
  translate: 0 -250%;
  transition: translate .2s var(--ease);
}
.skip:focus { translate: none; }

:where(a, button):focus-visible {
  outline: 2px solid var(--mint);
  outline-offset: 3px;
  border-radius: .35rem;
}
/* The transcripts are tab stops because they scroll horizontally and a keyboard
   visitor needs to reach them (axe: scrollable-region-focusable). The ring goes
   inside, since an offset ring would sit outside the terminal frame. */
.term__body:focus-visible { outline: 2px solid var(--mint); outline-offset: -2px; }

/* ---------- buttons ---------- */
.btn {
  display: inline-block;
  background: var(--mint); color: #04203A;
  font-weight: 650; text-decoration: none;
  padding: .8rem 1.5rem; border-radius: 999px;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 10px 30px -8px var(--mint-dim); }
.btn--ghost {
  background: transparent; color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--edge);
}
.btn--ghost:hover { box-shadow: inset 0 0 0 1px var(--mint); }
.btn--sm { padding: .5rem 1rem; font-size: .875rem; }

.chip {
  display: inline-block;
  font: 600 .75rem/1 var(--mono);
  letter-spacing: .06em;
  padding: .5rem .85rem; border-radius: 999px;
  color: var(--amber);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  box-shadow: inset 0 0 0 1px var(--ring-amber);
}
/* keep the planning-stage chip on one line on narrow phones */
@media (max-width: 430px) { .chip { font-size: .66rem; padding: .45rem .7rem; } }

/* ---------- scroll spine (logo bands as progress thread) ---------- */
.spine {
  /* inset from the nav and the bottom edge so it reads as a gauge,
     not as a stray mark in the corner */
  position: fixed; inset: 96px auto 32px 0;
  width: 12px; z-index: 5;
  opacity: calc(var(--p) * .9);
  pointer-events: none;
}
.spine svg { width: 100%; height: 100%; }
.spine__line {
  fill: none; stroke: var(--mint); stroke-width: 1.5;
  stroke-dasharray: 100; stroke-linecap: round;
  stroke-dashoffset: calc(100 - (var(--scroll) * 100));
}
.spine__line:nth-child(2) { opacity: .55; stroke-dashoffset: calc(100 - (var(--scroll) * 92)); }
.spine__line:nth-child(3) { opacity: .32; stroke-dashoffset: calc(100 - (var(--scroll) * 84)); }

/* ---------- nav ---------- */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 20;
  display: flex; align-items: center; gap: 1.5rem;
  padding: .85rem var(--pad);
  background: color-mix(in srgb, var(--navy-900) 78%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  /* hidden until the hero has opened */
  opacity: 0; transform: translateY(-100%);
  transition: opacity .45s var(--ease), transform .45s var(--ease), border-color .45s;
}
.nav.is-visible { opacity: 1; transform: none; border-bottom-color: var(--line); }
.nav__brand {
  display: flex; align-items: center; gap: .6rem;
  text-decoration: none; color: var(--ink); font-weight: 700;
  letter-spacing: -.04em; font-size: 1.15rem;
  /* the text is 30px tall; the padding is what makes it a 44px thumb target */
  padding-block: .45rem;
}
.nav__mark { width: 26px; height: 26px; flex: none; }
.nav__bands path { fill: none; stroke: var(--mint); stroke-width: 26; stroke-linecap: round; }
.nav__bands path:nth-child(2) { opacity: .8; }
.nav__bands path:nth-child(3) { opacity: .6; }
.nav__links { display: flex; gap: 1.4rem; margin-left: auto; }
.nav__links a {
  color: var(--ink-soft); text-decoration: none; font-size: .9rem; font-weight: 500;
  transition: color .2s;
  /* same reason: 24px of text is not a touch target on a tablet */
  padding-block: .6rem;
}
.nav__links a:hover { color: var(--mint); }
@media (max-width: 720px) { .nav__links { display: none; } .nav .btn--sm { margin-left: auto; } }

/* ============================================================
   HERO — the mark opens the site
   Tall section + sticky stage gives us scroll distance without
   hijacking the scrollbar.
   ============================================================ */
/* Mobile browsers report 100vh as the tallest possible viewport, so with the
   URL bar showing, a 100vh sticky stage is taller than the visible area and
   the lockup sits under the chrome. svh is the *small* viewport — the one you
   can actually see — with the vh line kept first as the fallback. */
.hero { height: 178vh; height: 178svh; position: relative; }

.hero__stage {
  position: sticky; top: 0;
  height: 100vh;
  height: 100svh;
  display: grid; place-content: center; justify-items: center;
  text-align: center;
  padding: 0 var(--pad);
  overflow: hidden;
}

.hero__glow {
  position: absolute; left: 50%; top: 42%;
  width: min(1100px, 130vw); aspect-ratio: 1;
  translate: -50% -50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--mint) 16%, transparent) 0%,
    color-mix(in srgb, var(--navy-600) 30%, transparent) 42%,
    transparent 68%);
  opacity: calc(.65 + var(--p) * .5);
  scale: calc(1 + var(--p) * 1.6);
  filter: blur(10px);
}

/* --- horizontal lockup: bands beside the wordmark --- */
.hero__lockup {
  display: flex; align-items: center;
  gap: clamp(.25rem, 1.5vw, 1.25rem);
  margin-bottom: clamp(1rem, 3vh, 2rem);
}
.hero__words { text-align: left; }
.hero__emblem {
  position: relative;
  width: clamp(172px, 23vw, 280px); aspect-ratio: 1; flex: none;
}
.hero__emblem .hero__mark { display: block; width: 100%; height: 100%; }
.hero__canvas {
  position: fixed; inset: 0; z-index: 21;
  width: 100%; height: 100%; pointer-events: none;
}
/* One transparent canvas renders all visible marks with clipped viewports.
   Inline SVGs retain layout and accessible meaning until each view is drawn. */
.logo3d { display: inline-block; aspect-ratio: 1; color: var(--mint); vertical-align: middle; }
.logo3d > svg { display: block; width: 100%; height: 100%; }
.logo3d.is-3d > svg { opacity: 0; }
.foot__mark { width: 52px; margin-bottom: .85rem; }
.brand-motion {
  position: fixed; right: max(1rem, env(safe-area-inset-right));
  bottom: max(1rem, env(safe-area-inset-bottom)); z-index: 22;
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .7rem .9rem; min-height: 44px;
  border: 1px solid var(--edge); border-radius: 999px;
  background: var(--navy-900); color: var(--ink);
  font: 500 .7rem/1 var(--mono); cursor: pointer;
  box-shadow: 0 5px 24px #0003;
}
.brand-motion[hidden] { display: none; }
.brand-motion:hover { border-color: var(--mint); color: var(--mint); }
.brand-motion__icon { min-width: .8rem; text-align: center; }
@media (max-width: 620px) { body .foot { padding-bottom: 6rem; } }
.hero__lockup, .hero__sub, .hero .chip, .hero .scrollcue, .hero__next { z-index: 1; }
.hero.has-3d .hero__mark { opacity: 0; }
.hero.has-3d .hero__burst { display: none; }
@media (max-width: 620px) {
  .hero__lockup { flex-direction: column; gap: .75rem; }
  .hero__words { text-align: center; }
  .hero__emblem { width: clamp(152px, 42vw, 210px); }
}

/* --- the resting mark: lifts and dissolves, handing off to the burst --- */
.hero__mark {
  width: clamp(104px, 17vw, 200px); aspect-ratio: 1; flex: none;
  rotate: calc(var(--p) * 45deg);
  scale: calc(1 + var(--p) * .55);
  opacity: calc(1 - var(--p) * 3);
  transform-origin: 50% 50%;
  will-change: rotate, scale, opacity;
}

/* --- the burst: centred, sweeps outward past the viewport --- */
.hero__burst {
  position: absolute; left: 50%; top: 50%;
  width: min(380px, 64vw); aspect-ratio: 1;
  translate: -50% -50%;
  rotate: calc(var(--p) * 185deg);
  scale: calc(.55 + var(--p) * var(--p) * 20);
  /* triangular curve: rises in, peaks mid-scroll, gone before the page lands */
  opacity: calc(min(var(--p) * 3.4, max(0, (.82 - var(--p)) * 4.6)));
  will-change: rotate, scale, opacity;
  pointer-events: none;
}
.burst__bands path {
  fill: none; stroke: var(--mint); stroke-linecap: round; stroke-width: 22;
}
.burst__bands path:nth-child(2) { opacity: .72; }
.burst__bands path:nth-child(3) { opacity: .5; }
.band {
  fill: none; stroke: var(--mint); stroke-linecap: round; stroke-width: 10;
  transform-box: fill-box; transform-origin: center;
}
/* Arc lengths, measured from the brand paths, used for the draw-in. */
.band--1 { --len: 151; stroke-dasharray: var(--len); }
.band--2 { --len: 65; stroke-dasharray: var(--len); }
.band--3 { --len: 53; stroke-dasharray: var(--len); }

/* --draw defaults to 1 (fully drawn) so the mark is VISIBLE even where
   animating a custom property is unsupported. The animation only ever
   takes it away and puts it back. */
@property --draw { syntax: "<number>"; inherits: true; initial-value: 1; }
@keyframes draw { from { --draw: 0; } to { --draw: 1; } }

.js .band {
  /* draw-in progress, plus scroll prising the dash gap open */
  stroke-dashoffset: calc(
    var(--len) * (1 - var(--draw)) + var(--len) * var(--p) * .55
  );
  animation: draw 1.5s var(--ease) both;
}
.js .band--2 { animation-delay: .13s; }
.js .band--3 { animation-delay: .26s; }

/* wordmark + copy recede as the bands open */
.hero__word {
  font-size: clamp(2.9rem, 9.5vw, 7rem);
  letter-spacing: -.055em; margin: 0;
  color: var(--ivory);
  scale: calc(1 - var(--p) * .3);
  translate: 0 calc(var(--p) * -3vh);
  opacity: calc(1 - var(--p) * 2.9);
  transform-origin: left center;
}
.hero__tagline {
  font-size: clamp(.95rem, 1.7vw, 1.3rem);
  letter-spacing: .11em; color: var(--ink);
  margin: .35rem 0 0;
  opacity: calc(1 - var(--p) * 2.4);
}
.hero__sub {
  max-width: 40ch; color: var(--ink-soft);
  opacity: calc(1 - var(--p) * 3);
}
.hero .chip { opacity: calc(1 - var(--p) * 3.4); }

.scrollcue {
  margin-top: clamp(1.5rem, 4vh, 2.5rem);
  display: grid; justify-items: center; gap: .5rem;
  color: var(--ink-dim); text-decoration: none;
  font: 600 .7rem/1 var(--mono); letter-spacing: .2em; text-transform: uppercase;
  opacity: calc(1 - var(--p) * 4);
  transition: color .2s;
}
.scrollcue:hover { color: var(--mint); }
.scrollcue svg { width: 20px; height: 34px; fill: none; stroke: currentColor; stroke-width: 1.5; }
/* Two iterations, not infinite, and the count is the whole point: WCAG 2.2.2 (Pause, Stop,
   Hide) governs automatic motion that runs longer than five seconds, and this dot was the
   one loop on the page with no mechanism to stop it — every other loop sits inside a
   terminal or the dashboard, each of which has its own Play/Pause. Two passes of 1.9s is
   3.8s: the invitation to scroll still animates on arrival, then rests, and the criterion
   no longer applies rather than applying and being unmet. `--loop` stays so a hidden tab
   does not spend the two iterations unwatched. */
.scrollcue__dot { fill: var(--mint); stroke: none; animation: cue 1.9s var(--ease) 2; animation-play-state: var(--loop, running); }
/* A phone turned sideways still needs the whole invitation in its sticky stage. */
@media (max-height: 500px) and (min-width: 621px) {
  .hero__emblem { width: 140px; }
  .hero__word { font-size: 4rem; }
  .hero__lockup { margin-bottom: 1rem; }
  .hero__sub { font-size: .95rem; line-height: 1.45; margin-bottom: .65rem; }
  .hero .chip { margin-bottom: .5rem; }
  .scrollcue { margin-top: .5rem; }
  .scrollcue svg { height: 24px; }
}
@keyframes cue {
  0%, 100% { transform: translateY(0); opacity: 1; }
  55%      { transform: translateY(14px); opacity: .25; }
}

/* ---------- the line the bands open onto ---------- */
.hero__next {
  position: absolute; inset: 0;
  display: grid; place-content: center;
  padding: 0 var(--pad);
  pointer-events: none;
  /* fades up as the bands sweep away */
  opacity: calc((var(--p) - .48) * 2.8);
}
.lead {
  font-size: clamp(1.4rem, 3.2vw, 2.5rem);
  line-height: 1.28; letter-spacing: -.025em;
  max-width: 30ch; margin: 0 auto; color: var(--ink);
  text-wrap: balance;
}

/* ---------- scenes ---------- */
.scene { padding: clamp(4rem, 12vh, 9rem) var(--pad); position: relative; }
.scene--alt { background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--navy) 70%, transparent) 30%, transparent); }
.scene__grid {
  max-width: var(--maxw); margin-inline: auto;
  display: grid; gap: clamp(2rem, 6vw, 5rem);
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 900px) { .scene__grid { grid-template-columns: 1fr 1fr; } }
.scene__copy p:last-child { margin-bottom: 0; }
.scene__viz { display: grid; gap: .9rem; }

/* ============================================================
   TERMINAL DEMOS
   Every line of the transcript is real DOM text, always present.
   The typing animation only reveals what is already there, so a
   screen reader, a no-JS visitor and a reduced-motion visitor all
   get the complete transcript.
   ============================================================ */
.term {
  margin: 0;
  border-radius: .9rem;
  overflow: hidden;
  background: color-mix(in srgb, #01060F 88%, var(--navy));
  box-shadow: inset 0 0 0 1px var(--line), 0 24px 60px -30px #000;
}
.term--wide { max-width: var(--maxw); margin: clamp(2rem, 5vw, 3.5rem) auto 0; }

.term__bar {
  display: flex; align-items: center; gap: .7rem;
  padding: .6rem .85rem;
  background: color-mix(in srgb, var(--navy-700) 55%, transparent);
  border-bottom: 1px solid var(--line);
  font: 500 .78rem/1.3 var(--mono);
  color: var(--ink-soft);
}
.term__dots { display: flex; gap: .35rem; flex: none; }
.term__dots i { width: 9px; height: 9px; border-radius: 50%; background: color-mix(in srgb, var(--ivory) 22%, transparent); }
.term__dots i:first-child { background: color-mix(in srgb, var(--amber) 70%, transparent); }
.term__dots i:last-child  { background: color-mix(in srgb, var(--mint) 60%, transparent); }
/* The title wraps rather than truncating. It used to be nowrap + ellipsis, the way
   a real terminal's title bar behaves, and at 320px that lost up to 256px of it:
   "chorus · the same mission, from the operator's chair" arrived as "chorus · the
   same mission, from the operator". The tail is where the meaning is, the id here
   is what labels the transcript region, and a sighted visitor at 320px has no way
   to recover it. Wrapping costs nothing at any width where it already fitted. */
.term__title { min-width: 0; overflow-wrap: break-word; }
.term__tag {
  margin-left: auto; flex: none;
  font: 600 .66rem/1 var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--amber);
  padding: .3rem .5rem; border-radius: 999px;
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  box-shadow: inset 0 0 0 1px var(--ring-amber);
}

.term__body {
  /* positioned so it is the offsetParent for the typed span — Terminal.follow()
     measures against it to keep the caret in view on narrow screens */
  position: relative;
  padding: 1rem .95rem 1.1rem;
  font: 400 clamp(.74rem, .42vw + .62rem, .82rem)/1.85 var(--mono);
  color: var(--ink-soft);
  /* a terminal may scroll sideways inside its own frame; it must never
     make the PAGE scroll sideways */
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--mint) 45%, transparent) transparent;
}
/* a visible thumb is the only affordance that long lines can be scrolled */
.term__body::-webkit-scrollbar { height: 7px; }
.term__body::-webkit-scrollbar-track { background: transparent; }
.term__body::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: color-mix(in srgb, var(--mint) 40%, transparent);
}
.term__body::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--mint) 65%, transparent); }
/* lines are blocks with preserved internal spacing, so the whitespace
   between the tags in the source collapses away instead of adding
   blank rows the way a <pre> would */
.term__line { display: block; white-space: pre; }
.term__cmd { color: var(--ink); }
.term__p { color: var(--mint); user-select: none; }
.term__t { color: var(--ink); }
.term__out b { color: var(--mint); font-weight: 600; }
.term__out--ok { color: var(--mint); }
.term__out--ok b { color: var(--ink); }
.term__out--warn { color: var(--amber); }
.term__out--warn b { color: var(--ink); }
.term__out--arrow { color: var(--mint); }
.term__out--head { color: var(--ink-dim); letter-spacing: .04em; }

/* One control per demo, four labels: Play / Pause / Resume / Replay. It is
   visible from the start rather than appearing at the end, because a demo that
   starts by itself and runs past five seconds needs a way to stop it (WCAG
   2.2.2), and because a control nobody can see is not a mechanism. */
.term__ctl {
  display: block; width: 100%;
  /* --edge, not --line: this border is the whole visible boundary of the play
     control, and at --line (mint 18%) it measured 1.51:1 against the button's
     own fill — a control a low-vision visitor could not find the edge of. */
  border: 0; border-top: 1px solid var(--edge);
  background: color-mix(in srgb, var(--navy-700) 35%, transparent);
  color: var(--ink-soft);
  font: 600 .72rem/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
  padding: .7rem; cursor: pointer;
  transition: color .2s, background .2s;
}
/* `display: block` above would otherwise beat the UA rule for [hidden] */
.term__ctl[hidden] { display: none; }
.term__ctl:hover { color: var(--mint); background: color-mix(in srgb, var(--mint) 8%, transparent); }
/* Paused is a state worth seeing at a glance, not just reading */
.term__ctl[data-state='paused'] { color: var(--amber); }

.mission__foot { max-width: 60ch; margin: clamp(1.5rem, 4vw, 2.5rem) auto 0; text-align: center; }

/* --- the typing animation, only once JS has taken over a terminal --- */
/* Scoped to a line that has started, because `--n` is 0 until then and a
   zero-width clipped span is content a screen reader cannot reach. */
.js .term--playing .term__cmd.is-started .term__t {
  display: inline-block; vertical-align: bottom;
  width: calc(var(--n, 0) * 1ch);
  overflow: hidden; white-space: pre;
}
/* The clamp comes off the moment a line has finished typing. `1ch` is the advance
   of "0" and excludes letter-spacing, so a visitor who applies the WCAG 1.4.12
   spacing (0.12em per character) makes every command wider than the box revealing
   it, and the tail is clipped for good — 52 to 105px of it, measured, which is
   several words of a command. The finished state is the one that persists and has
   to be complete; a frame mid-typing running a character behind is what typing
   looks like. In a monospace font at default spacing `auto` and `--n * 1ch` are
   the same width, so nothing moves. */
.js .term--playing .term__cmd.is-started:not(.is-typing) .term__t {
  width: auto; overflow: visible;
}
/* Lines that have not run yet are transparent rather than removed from flow.
   They used to be `display: none`, which reads well — output appends downwards
   the way a real terminal does, no stray `$` prompts floating in gaps — but it
   also took them out of the accessibility tree: 214 non-empty StaticText nodes
   before a demo plays, 177 mid-play. A screen reader arriving in the middle of a
   demo read an incomplete transcript and had to wait out the animation.

   Transparent costs nothing visually. Every row is `white-space: pre` at a fixed
   line height, so reserving each line's height puts the played ones in exactly
   the positions they occupied before, and the reserved total now equals the
   min-height JS already pinned to the finished transcript. */
.js .term--playing .term__line { opacity: 0; }
.js .term--playing .term__cmd.is-started,
.js .term--playing .term__out.is-shown { opacity: 1; }
.js .term--playing .term__out.is-shown { animation: termline .2s var(--ease) both; }
@keyframes termline { from { opacity: 0; translate: 0 -2px; } to { opacity: 1; translate: none; } }
/* the caret sits on whichever command line is currently being typed */
.js .term--playing .term__cmd.is-typing::after,
.js .term--playing .term__cmd.is-entering::after {
  content: ''; display: inline-block; vertical-align: -2px;
  width: 1ch; height: 1.05em;
  background: var(--mint);
  animation: caret .9s steps(1, end) infinite; animation-play-state: var(--loop, running);
}
.js .term--playing .term__cmd.is-entering::after { animation: none; }
@keyframes caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
/* the flash when enter is pressed */
.js .term--playing .term__cmd.is-entering { background: color-mix(in srgb, var(--mint) 14%, transparent); }
/* One inherited switch, not six overrides. `is-paused` means "offscreen or in a
   hidden tab, not worth animating"; `is-held` means "the visitor pressed Pause".
   They are separate flags so the visibility observer cannot clear a visitor's
   hold. Both are read here rather than in six `.is-paused .thing` rules, because
   those lost on specificity: every base rule above uses the `animation`
   shorthand, which resets animation-play-state to running, and
   `.js .term--playing .term__cmd.is-typing::after` outranks
   `.is-paused .term__cmd.is-typing::after`. The caret never actually stopped.
   An inherited custom property does not compete on specificity at all. */
:is(.is-paused, .is-held) { --loop: paused; }
/* Paused loops should land on the solid state, not freeze mid-fade. `animation-play-state:
   paused` holds whichever frame the animation was on, and for these two dots that is a
   35%-opacity trough as often as not — a *state* indicator dimmed below the contrast it
   was measured at, and a visitor who pressed Pause has no way to nudge it brighter.
   Reduced motion already resolves them with `animation: none; opacity: 1`; a held loop is
   the same situation arriving by a different route, so it gets the same answer. Carets and
   the spinner glyph are left to freeze: they carry no state, and stopping mid-blink is
   what "stopped" looks like for a cursor. */
:is(.is-paused, .is-held) .term__state i { animation: none; opacity: 1; }

/* ============================================================
   ORCHESTRATION PYRAMID
   Your terminal on top, three agent programs below, a delivery wire between.
   The wire is its own grid row rather than an absolute overlay, so it
   stays aligned no matter how tall the transcripts get.
   ============================================================ */
/* Spacing here is deliberately tight for a full-page section: the head and all
   four terminals have to land inside one 900px-tall viewport, or the payoff
   (three agents taking a part each at once) happens off screen. */
.orch { padding: clamp(3rem, 8vh, 6rem) var(--pad) clamp(3rem, 8vh, 6rem); }
.orch__head { max-width: 64ch; margin: 0 auto clamp(1.8rem, 4vw, 2.8rem); text-align: center; }
/* one step down from the page's h2 scale — this head has to share the fold
   with three terminals and a bracket */
.orch__head h2 { font-size: clamp(1.7rem, 2.8vw, 2.5rem); margin-bottom: .5rem; }
.orch__head p:last-child { font-size: .98rem; margin-bottom: 0; }
.orch__head .eyebrow { margin-bottom: .9rem; }
.orch__foot { max-width: 62ch; margin: clamp(1.5rem, 4vw, 2.5rem) auto 0; text-align: center; }

.pyramid {
  --pgap: clamp(.9rem, 2vw, 1.4rem);
  max-width: var(--maxw); margin-inline: auto;
  display: grid; gap: var(--pgap);
  grid-template-columns: 1fr;
}
.pyramid .term { position: relative; z-index: 1; }
.term--lead { width: 100%; }

.pyramid__wire { display: none; }

/* 900px, not 860: three columns need 300px each before the transcripts start
   scrolling sideways more than they read. Below it the four terminals stack,
   which is the same one-column reading order screen readers always get. */
@media (min-width: 900px) {
  .pyramid { grid-template-columns: repeat(3, 1fr); }
  /* lead centred over the three */
  .term--lead { grid-column: 1 / -1; max-width: 640px; margin-inline: auto; }
  .pyramid__wire {
    display: block; position: relative;
    grid-column: 1 / -1; grid-row: 2;
    width: 100%; height: clamp(30px, 3.4vw, 44px);
  }
}
/* The drops sit over each agent's centre. With three 1fr columns and a gap of g,
   those centres are at 16.667% - g/3, a flat 50%, and 83.333% + g/3 — the outer
   two are pulled in by the gaps the percentage alone does not account for. The
   middle one needs no correction because it is the axis the gaps are symmetric
   about, which is also why the stem can drop straight through it. */
.wire {
  position: absolute; background: var(--mint);
  border-radius: 1px; opacity: .3;
}
.wire--stem { left: 50%; top: 0; width: 2px; height: 50%; margin-left: -1px; transform-origin: top; }
.wire--bar  { top: calc(50% - 1px); height: 2px;
              left: calc(16.667% - var(--pgap) / 3); right: calc(16.667% - var(--pgap) / 3); }
.wire--drop { top: 50%; width: 2px; height: 50%; margin-left: -1px; transform-origin: top; }
.wire--drop-l { left: calc(16.667% - var(--pgap) / 3); }
.wire--drop-m { left: 50%; }
.wire--drop-r { left: calc(83.333% + var(--pgap) / 3); }

/* Drawn only when the directive is actually sent. Where `scale` is unsupported
   the lines simply fade in at full length instead of drawing. */
.js .pyramid .wire {
  opacity: 0;
  transition: opacity .3s var(--ease), scale .45s var(--ease);
}
.js .pyramid .wire--stem, .js .pyramid .wire--drop { scale: 1 0; }
.js .pyramid .wire--bar { scale: 0 1; }
.js .pyramid.is-directing .wire { opacity: .34; scale: 1 1; }
.js .pyramid.is-directing .wire--bar { transition-delay: .34s; }
.js .pyramid.is-directing .wire--drop-l { transition-delay: .6s; }
.js .pyramid.is-directing .wire--drop-m { transition-delay: .66s; }
.js .pyramid.is-directing .wire--drop-r { transition-delay: .72s; }

/* The lead's idle prompt: shown only while it waits to be typed into, and taken
   out of flow to do it. The transcript below now reserves every line's height
   from the start, so a row in flow here would make the frame one line taller
   than the height JS pinned. top/left match .term__body's padding. */
.term__idle { display: none; }
.js .term--playing.is-waiting .term__idle {
  display: block; opacity: 1;
  position: absolute; top: 1rem; left: .95rem;
}
.js .term--playing.is-waiting .term__idle::after {
  content: ''; display: inline-block; vertical-align: -2px;
  width: 1ch; height: 1.05em; background: var(--mint);
  animation: caret .9s steps(1, end) infinite; animation-play-state: var(--loop, running);
}

/* agent status pill in the title bar */
.term__state {
  margin-left: auto; flex: none;
  display: inline-flex; align-items: center; gap: .4rem;
  font: 600 .66rem/1 var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--mint);
  padding: .3rem .5rem; border-radius: 999px;
  background: color-mix(in srgb, var(--mint) 12%, transparent);
  box-shadow: inset 0 0 0 1px var(--ring-mint);
}
.term__state i {
  width: 6px; height: 6px; border-radius: 50%; background: currentColor;
  animation: statedot 1.4s ease-in-out infinite; animation-play-state: var(--loop, running);
}
@keyframes statedot { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* Four words in the markup, one shown at a time. `delivered` is the DEFAULT —
   the state the pill has with no class on it at all — because the markup holds
   the finished scene: a visitor with no JS, or one whose JS failed after the
   watchdog reverted the page, is reading three complete transcripts, and any
   other word beside a complete transcript is a lie. The three in-flight words
   are opt-in, each keyed to a class only the player adds. */
.st { display: none; }
.st--ok { display: inline; }
.term--agent.is-pending .st--ok,
.term--agent.is-live .st--ok,
.term--agent.is-adjusting .st--ok { display: none; }
.term--agent.is-pending .st--off,
.term--agent.is-live .st--on,
.term--agent.is-adjusting .st--run { display: inline; }

/* offline: no colour, and no pulse — a dot that pulses reads as a heartbeat,
   which is the opposite of what an empty seat is saying */
.term--agent.is-pending .term__state {
  color: var(--ink-soft);
  background: color-mix(in srgb, var(--ivory) 7%, transparent);
  box-shadow: inset 0 0 0 1px var(--line);
}
.term--agent.is-pending .term__state i { animation: none; opacity: .45; }

/* working: amber, matching the illustrative tag and the adjusting spinner */
.term--agent.is-adjusting .term__state {
  color: var(--amber);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  box-shadow: inset 0 0 0 1px var(--ring-amber);
}

/* The subagent spinner. Parked by default and spun only while the agent is
   actually working, for the same reason `delivered` is the default word above:
   with no class on it this line has already been merged, and that is what an
   idle section, a no-JS page and a reverted page are all showing. Inverting it
   this way also means it is the default that survives — the two blocks that
   used to have to park it (.no-js, and reduced motion) now agree with it. */
.term__spin::after { content: '·'; margin-left: .6ch; color: var(--mint); opacity: .5; }
.term--agent.is-adjusting .term__spin::after {
  content: '⠋'; opacity: 1;
  animation: spinframes .72s steps(1, end) infinite; animation-play-state: var(--loop, running);
}
@keyframes spinframes {
  0%   { content: '⠋'; } 12.5% { content: '⠙'; } 25%  { content: '⠹'; }
  37.5%{ content: '⠸'; } 50%   { content: '⠼'; } 62.5%{ content: '⠴'; }
  75%  { content: '⠦'; } 87.5% { content: '⠧'; }
}

.term__out--in { color: var(--mint); }
.term__out--in b { color: var(--ink); }

.pyramid__ctl {
  grid-column: 1 / -1;
  justify-self: center;
  margin-top: .4rem;
  border: 0; border-radius: 999px;
  background: color-mix(in srgb, var(--navy-700) 60%, transparent);
  box-shadow: inset 0 0 0 1px var(--edge);   /* 1.53:1 at --line; see .term__ctl */
  color: var(--ink-soft);
  font: 600 .72rem/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
  padding: .85rem 1.4rem; cursor: pointer;
  transition: color .2s, background .2s;
}
.pyramid__ctl[hidden] { display: none; }
.pyramid__ctl:hover { color: var(--mint); background: color-mix(in srgb, var(--mint) 10%, transparent); }
.pyramid__ctl[data-state='paused'] { color: var(--amber); }

/* ============================================================
   DASHBOARD
   The real operator dashboard (packages/dashboard), built into
   /dashboard/ at deploy time and embedded in a frame. Nothing here
   styles the dashboard itself — it brings its own stylesheet.
   ============================================================ */
.dash { padding: clamp(4rem, 12vh, 9rem) var(--pad); }
.dash__foot { max-width: 64ch; margin: clamp(1.5rem, 4vw, 2.5rem) auto 0; text-align: center; }

/* A border, not the inset box-shadow the terminals use: the iframe paints over
   its parent's content box, so an inset shadow would be hidden under it on
   every side but the title bar's. Forced colours repaint a border in
   CanvasText, so the frame keeps its edge there with no extra rule. */
.dashframe {
  max-width: var(--maxw); margin: 0 auto;
  border: 1px solid var(--line); border-radius: 1rem; overflow: hidden;
  background: color-mix(in srgb, #01060F 82%, var(--navy));
  box-shadow: 0 30px 80px -40px #000;
}
.dashframe__bar {
  display: flex; align-items: center; gap: .7rem;
  padding: .6rem .85rem;
  background: color-mix(in srgb, var(--navy-700) 55%, transparent);
  border-bottom: 1px solid var(--line);
  font: 500 .78rem/1.3 var(--mono); color: var(--ink-soft);
}
/* The one control in the bar. Padded out to the page's own target floor
   rather than sitting at the bar's text height. */
.dashframe__open {
  margin-left: auto; padding: .55rem .75rem; border-radius: .5rem;
  color: var(--mint); text-decoration: none; white-space: nowrap;
}
.dashframe__open:hover { background: color-mix(in srgb, var(--mint) 10%, transparent); }
/* The height is fixed before the frame loads, so a lazy load cannot shift the
   page. The dashboard scrolls inside it. */
.dashframe__view {
  display: block; width: 100%; border: 0;
  height: clamp(560px, 82vh, 880px);
  background: #030b1c;
}

@media (max-width: 560px) {
  /* Below this the title has to wrap to survive, and wrapping it inside a single
     flex row leaves it a ~180px column three or four lines deep. Giving it its own
     full-width row underneath the dots and the tag reads as a title bar still. */
  .term__bar, .dashframe__bar { flex-wrap: wrap; }
  .term__title { order: 3; flex: 1 0 100%; }
  .dashframe__view { height: 78vh; }
}

/* Text for assistive tech only. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---------- mission steps ---------- */
.mission, .caps { padding: clamp(5rem, 14vh, 10rem) var(--pad); }
.mission__head { max-width: 52ch; margin: 0 auto clamp(2.5rem, 6vw, 4.5rem); text-align: center; }
.mission__head h2 { margin-bottom: .5rem; }
.steps {
  list-style: none; margin: 0 auto; padding: 0; max-width: var(--maxw);
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
  counter-reset: s;
}
/* six steps read best as 3 + 3 rather than a ragged 4 + 2 */
@media (min-width: 1000px) { .steps { grid-template-columns: repeat(3, 1fr); } }
.step {
  position: relative; padding: 1.6rem 1.4rem 1.4rem;
  border-radius: 1rem;
  background: color-mix(in srgb, var(--navy-700) 55%, transparent);
  box-shadow: inset 0 0 0 1px var(--line);
}
.step__n {
  display: grid; place-content: center;
  width: 2rem; height: 2rem; border-radius: 50%;
  font: 700 .85rem/1 var(--mono);
  color: var(--navy); background: var(--mint);
  margin-bottom: .9rem;
}
.step p { margin: 0; color: var(--ink-soft); font-size: .92rem; }

/* ---------- capabilities ---------- */
.caps__grid {
  list-style: none; margin: 0 auto; padding: 0; max-width: var(--maxw);
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
}
.cap {
  padding: 1.4rem;
  border-radius: 1rem;
  background: color-mix(in srgb, var(--navy) 60%, transparent);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: box-shadow .3s var(--ease), transform .3s var(--ease);
}
.cap:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--mint) 45%, transparent); transform: translateY(-3px); }
.cap h3 { color: var(--mint); }
.cap p { margin: 0; color: var(--ink-soft); font-size: .9rem; }

/* ---------- CTA ---------- */
.cta {
  position: relative; text-align: center;
  padding: clamp(6rem, 18vh, 12rem) var(--pad);
  display: grid; justify-items: center;
}
.cta__mark { width: min(200px, 40vw); aspect-ratio: 1; margin-bottom: 1.5rem; }
.cta__bands path { fill: none; stroke: var(--mint); stroke-width: 22; stroke-linecap: round; }
.cta__bands path:nth-child(2) { opacity: .85; }
.cta__bands path:nth-child(3) { opacity: .7; }
.cta p { max-width: 44ch; color: var(--ink-soft); }
.cta__row { display: flex; flex-wrap: wrap; gap: .8rem; justify-content: center; }

/* ---------- footer ---------- */
.foot {
  padding: 3rem var(--pad) 4rem;
  border-top: 1px solid var(--line);
  text-align: center;
}
.foot p { margin: 0 0 .5rem; }
.foot b { letter-spacing: -.03em; }
.foot .note { max-width: 60ch; margin-inline: auto; }

/* ============================================================
   Reveal-on-scroll.
   Only ever hides content when JS is running AND motion is allowed,
   so a no-JS or reduced-motion visitor gets the complete page.
   ============================================================ */
.js .reveal {
  opacity: 0;
  translate: 0 22px;
  transition: opacity .7s var(--ease), translate .7s var(--ease);
}
.js .reveal.is-in { opacity: 1; translate: none; }

/* pause decorative loops when offscreen or the tab is hidden */

/* ============================================================
   No JavaScript — flatten the hero, show everything
   ============================================================ */
.no-js .hero { height: auto; }
.no-js .hero__stage { position: static; height: auto; padding: 22vh var(--pad) 12vh; }
.no-js .hero__mark { rotate: none; scale: 1; opacity: 1; }
.no-js .hero__canvas, .no-js .brand-motion { display: none !important; }
.no-js .logo3d > svg { opacity: 1; }
.no-js .hero.has-3d .hero__mark { opacity: 1; }
.no-js .band { stroke-dashoffset: 0; }
.no-js .hero__word, .no-js .hero__tagline, .no-js .hero__sub,
.no-js .hero .chip, .no-js .scrollcue { opacity: 1; scale: 1; translate: none; }
.no-js .nav { position: static; opacity: 1; transform: none; border-bottom-color: var(--line); }
.no-js .spine, .no-js .hero__burst { display: none; }
.no-js .hero__next {
  position: static; inset: auto; opacity: 1; pointer-events: auto;
  padding: 0; margin-top: clamp(3rem, 8vh, 6rem);
}
/* the pyramid is already complete in the markup; without JS
   they simply stand still. The replay controls would do nothing, so they go. */
.no-js .pyramid__ctl, .no-js .term__ctl { display: none; }
.no-js .term__state i { animation: none; }
.no-js .term__spin::after { content: '·'; animation: none; opacity: .5; }

/* ============================================================
   Reduced motion — WEB-04: replace movement with complete,
   readable static scenes. No transforms, no loops, no pinning.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .hero__canvas, .brand-motion { display: none !important; }
  .logo3d > svg { opacity: 1; }
  .hero.has-3d .hero__mark { opacity: 1; }
  html { scroll-behavior: auto; }

  .hero { height: auto; }
  .hero__stage { position: static; height: auto; padding: 18vh var(--pad) 10vh; overflow: visible; }
  .hero__mark { rotate: none !important; scale: 1 !important; opacity: 1 !important; animation: none; }
  .band { stroke-dashoffset: 0 !important; animation: none !important; }
  .hero__word, .hero__tagline, .hero__sub, .hero .chip, .scrollcue {
    opacity: 1 !important; scale: 1 !important; translate: none !important;
  }
  .hero__glow { opacity: .6; scale: 1; }

  .nav { position: sticky; opacity: 1; transform: none; border-bottom-color: var(--line); }
  .spine, .hero__burst { display: none; }

  .hero__next {
    position: static; inset: auto; opacity: 1 !important; pointer-events: auto;
    padding: 0; margin-top: clamp(3rem, 8vh, 6rem);
  }

  .js .reveal, .js .reveal.is-in {
    opacity: 1 !important; translate: none !important; transition: none !important;
  }

  /* terminals show their complete transcript, no typing, no caret */
  /* !important because the typing clamp above is scoped to `.is-started`, which
     outranks any two-class selector here */
  .js .term--playing .term__t {
    display: inline !important; width: auto !important; overflow: visible !important;
  }
  .js .term--playing .term__line { display: block; animation: none !important; opacity: 1 !important; }
  .js .term--playing .term__cmd::after { content: none !important; }
  .term__ctl, .pyramid__ctl { display: none; }

  /* pyramid: four complete transcripts, the wire drawn, nothing pulsing. The
     status pills need no rule here — "delivered" is their default state, so a
     player that never runs leaves each one already agreeing with its transcript. */
  .term__idle, .js .term--playing.is-waiting .term__idle { display: none; }
  .wire { opacity: .3 !important; scale: none !important; transition: none !important; }
  .term__state i { animation: none !important; opacity: 1; }
  .term__spin::after { content: '·' !important; animation: none !important; opacity: .5; }

  .scrollcue__dot { animation: none !important; }
  .btn:hover, .cap:hover { transform: none; }
  /* The catch-all, and it has to name animations as well as transitions.
     `transition-duration` alone was a rule that looked like it covered everything
     and covered no keyframe animation at all — they are separate properties, and
     the six `animation: none` rules above were carrying the whole block. One
     animation had already escaped: the since-removed dashboard mockup re-ran
     `termline` on a state pill at every scene change regardless of the motion
     preference, so a reduced-motion visitor stepping it by hand saw it play. Neutralising the property rather than the
     instance means the next animation added to this file is covered on arrival.
     .01ms rather than 0 keeps `animationend` firing, and every animation here uses
     `both` or ends on its resting state, so collapsing the duration lands on the
     final frame instead of skipping it. */
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ============================================================
   Forced colours — Windows High Contrast and the like.
   ============================================================
   Measured rather than assumed. Of eight ways this page puts
   colour on screen, forced-colors mode:
     - flattens background-color, background-image gradients,
       border colour and text colour to Canvas / CanvasText;
     - DROPS box-shadow entirely, inset and outer alike;
     - repaints border and outline in CanvasText;
     - leaves SVG `fill` and `stroke` exactly as authored.
   This page sits on both of the last two. Every brand mark is an
   SVG stroke, so the mint survives: on the light forced palette
   that is #4ff5a5 on #ffffff, 1.41:1, a logo that is effectively
   gone. And all twenty of its 1px frames are inset box-shadows,
   so every terminal, card, tile and pill loses its edge and the
   page collapses into undifferentiated text.
   Author declarations of system colours inside this block do
   paint — verified the same way — so both are recoverable. */
@media (forced-colors: active) {
  /* Brand geometry follows the palette instead of keeping mint. */
  .band, .nav__bands path, .cta__bands path, .spine__line { stroke: CanvasText; }
  .scrollcue__dot { fill: CanvasText; }
  /* The three bands read as depth through opacity; at a single colour that is
     just a fade, and .82 of CanvasText is a grey with less contrast than the
     palette promised. */
  .band--2, .band--3 { opacity: 1; }

  /* Frames come back as outlines, not borders: outline is repainted in
     CanvasText and costs no layout, so the edge lands exactly where the inset
     shadow was instead of growing every box by 2px. */
  .term, .cap, .step, .chip, .btn--ghost,
  .term__state, .pyramid__ctl, .term__ctl,
  .step__n, .term__dots i {
    outline: 1px solid CanvasText; outline-offset: -1px;
  }

  /* Focus has to stay louder than the frames now sharing its mechanism:
     Highlight, thicker, and outside the box where the frame outline is inside
     it. Two 1px CanvasText lines a pixel apart would be no indicator at all. */
  :where(a, button):focus-visible { outline: 3px solid Highlight; outline-offset: 2px; }
  .term__body:focus-visible { outline: 3px solid Highlight; outline-offset: -3px; }

  /* The hero glow and the burst are pure decoration and forced colours has
     already flattened them to Canvas; drop them so they cannot fight the
     palette if either ever gains an SVG or an image. */
  .hero__glow, .hero__burst { display: none; }
}
