.wordmark {
  display: inline-flex;
  align-items: center;
  gap: .48em;
  min-height: 44px;
  font-size: 1.04rem;
  font-weight: 700;
  letter-spacing: -.04em;
  text-decoration: none;
}

/* The mark takes currentColor rather than a value of its own, so it is
   white on the dark header and black under the print stylesheet without
   either being written down twice. Sized in em: it tracks the wordmark. */
.wordmark svg {
  width: 1.25em;
  height: 1.25em;
  flex: none;
  fill: currentColor;
}

.site-header nav a {
  display: inline-flex;
  min-height: 48px;
  align-items: center;
  color: var(--inverse-soft);
  font-size: .94rem;
  text-decoration: none;
  white-space: nowrap;
  transition: color 180ms ease;
}

.header-actions a {
  color: var(--inverse-soft);
  font-size: .94rem;
  text-decoration: none;
  white-space: nowrap;
}

/* Discord's own blurple, so it reads as the thing it is rather than as
   another of our controls. The only colour in this bar from outside the
   palette, and it stays the mark alone at every width: with the word it
   measured 100px against Download's 93 for a shorter label, which made the
   community link the widest control in a bar whose one action is Download.
   A circle the height of the pills reads as a peer, not a competitor.
   Its name comes from aria-label; there is no text to remove. */
.header-actions .header-discord {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: #fff;
  background: #5865f2;
  border-radius: 999px;
  transition: background 180ms ease;
}

/* Buy is a text link between two 44px controls and it measured 24x21 — the
   smallest thing in the group carrying the same 14px of air as the biggest,
   and a tap target under the 44px floor. The padding is the target, not
   decoration; the type size is unchanged. */
.header-actions .header-buy {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-1);
  margin-right: var(--space-3);
}

h3 {
  margin-bottom: var(--space-2);
  font-size: clamp(1.45rem, 2.2vw, 2rem);
  font-weight: 500;
  letter-spacing: -.035em;
  line-height: 1.08;
}

.button-row { display: flex; flex-wrap: wrap; gap: .75rem; }

.button-secondary { background: transparent; color: inherit; }

.button-unavailable { background: transparent; color: var(--inverse-soft); cursor: default; }

/* Tux is full colour and lives in its own file: hidden plus lazy means
   the Windows and macOS visitors who never see him never fetch him.
   He answers to the label, not to the 1.15rem platform squares he is
   never shown beside. */
.hero-actions img[data-primary-icon="linux"] { width: auto; height: 1.45rem; }

.hero {
  position: relative;
  isolation: isolate;
  background: var(--inverse);
  color: #f7f7f3;
}

.hero-aurora {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

@media print {
  .hero-aurora { display: none; }
}

.hero-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  gap: clamp(3rem, 5vw, 4.5rem);
  padding-block: clamp(5rem, 9vw, 8rem) 0;
}

.hero-copy {
  position: relative;
  z-index: 1;
  width: min(100%, 46rem);
  margin-inline: auto;
  text-align: center;
}

.hero-copy h1 { animation: hero-enter 720ms cubic-bezier(.22, .8, .28, 1) both; }

.hero-actions { animation: hero-enter 720ms 90ms cubic-bezier(.22, .8, .28, 1) both; }

/* Release the opacity layer after entry so the paragraph can blur the aurora. */
.hero-demo-details { animation: hero-enter 720ms 210ms cubic-bezier(.22, .8, .28, 1) backwards; }

/* Last in the hero's own order. It is below the fold's copy but inside the
   hero, so the scroll observer would have fired for it before anyone could
   see it move -- it belongs to the load choreography, not to the reveal. */
.hero-facts-band { animation: hero-enter 720ms 270ms cubic-bezier(.22, .8, .28, 1) both; }

.hero h1 {
  max-width: 17ch;
  margin: 0 auto var(--space-3);
  font-size: clamp(2.55rem, 4.2vw, 4.2rem);
  line-height: 1.02;
}

.hero .lede { max-width: 33rem; margin-bottom: var(--space-4); color: var(--inverse-soft); }

.hero-actions { align-items: center; justify-content: center; }

.hero-actions .button {
  min-height: 3.25rem;
  gap: .6rem;
  padding: .75rem 1.25rem;
  border-radius: .6rem;
}

.hero-actions .button-secondary {
  border-color: rgb(247 247 243 / .28);
  background: rgb(247 247 243 / .04);
}

.hero-actions svg {
  width: 1rem;
  height: 1rem;
  flex: none;
  fill: currentColor;
}

.hero-assurance {
  margin: var(--space-2) auto 0;
  color: var(--inverse-soft);
  font-size: .82rem;
}

.hero-demo-details {
  max-width: 32rem;
  margin-top: .25rem;
  margin-inline: auto;
  color: var(--inverse-soft);
  font-size: .82rem;
}

.hero-demo-details summary {
  padding-block: .8rem;
  margin-block: -.4rem;
  width: max-content;
  max-width: 100%;
  margin-inline: auto;
  color: #f7f7f3;
  cursor: pointer;
}

.hero-demo-details summary:focus { outline: none; }

.hero-demo-details summary:focus-visible {
  text-decoration: underline;
  text-decoration-color: var(--inverse-soft);
  text-decoration-thickness: 1px;
  text-underline-offset: .25em;
}

.hero-demo-details ul {
  margin: .65rem 0 0;
  padding: var(--space-2) var(--space-3) var(--space-2) var(--space-4);
  text-align: left;
  color: #e1e1db;
  background: rgb(18 17 16 / .86);
  border: 1px solid rgb(247 247 243 / .08);
  border-radius: var(--radius);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.hero-demo-details li::marker { content: "−  "; }

.hero-visual {
  position: relative;
  z-index: 1;
  isolation: isolate;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: visible;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: clamp(1.25rem, 4vw, 4rem);
  background: var(--inverse);
  border-radius: var(--radius) var(--radius) 0 0;
}

.hero-visual::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 10% 14% 4%;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(0, 0, 0, .64), transparent 68%);
  filter: blur(1.4rem);
  opacity: .52;
  pointer-events: none;
  transform: translateY(.15rem);
  transition: opacity 120ms cubic-bezier(.22, .8, .28, 1), transform 120ms cubic-bezier(.22, .8, .28, 1);
}

.hero-product {
  position: relative;
  z-index: 2;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  margin-inline: auto;
  object-fit: contain;
  outline: 1px solid rgba(255, 255, 255, .08);
  box-shadow: 0 1.35rem 3rem rgba(0, 0, 0, .42), 0 .25rem .8rem rgba(0, 0, 0, .3);
  transform: translateY(0) scale(1);
  transform-origin: center;
  transition: transform 260ms cubic-bezier(.22, .8, .28, 1),
              box-shadow 260ms cubic-bezier(.22, .8, .28, 1),
              outline-color 260ms ease;
}

.hero-facts-band { background: var(--flow-black, #121110); }

.hero-facts {
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  width: min(100%, calc(var(--content) + (2 * var(--gutter))));
  margin-inline: auto;
  margin-block: 0;
}

.hero-facts div { padding: 1.1rem var(--gutter); }

.hero-facts div:nth-child(2) { text-align: center; }

.hero-facts div:nth-child(3) { text-align: right; }

.hero-facts dt { margin-bottom: .25rem; color: #f7f7f3; font-size: .78rem; font-weight: 700; }

.hero-facts dd { margin: 0; color: var(--inverse-soft); font-size: .85rem; }

.proof-visual {
  position: relative;
  z-index: 1;
  width: min(calc(100% - (2 * var(--gutter))), var(--content));
  aspect-ratio: 5 / 1;
  margin: 0 auto clamp(3rem, 6vw, 5rem);
  overflow: hidden;
  border-radius: var(--radius);
  background: #0a0908;
}

.proof-visual img,
.proof-visual video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 35%;
}

/* The dry take is the floor and never fades. Cross-fading BOTH layers dips
   dark at the midpoint -- two half-opacity images over the dark box
   composite darker than either one -- so only the wet take fades, over an
   already-opaque picture. These 220 ms are also the cover the player script
   hands decoding over under, which is why that timing is read there. */
.proof-visual .proof-take { position: absolute; inset: 0; }

/* Each take is two copies of the same clip. The renderer eases the motion to
   a stop at both ends and the two ends are not quite the same pose, so a
   plain loop reads as freeze-bump-freeze. Crossfading one copy into the
   other across that near-static region hides the join completely. */
.proof-visual .proof-take video {
  position: absolute;
  inset: 0;
  opacity: 0;
}

.proof-visual .proof-take video:first-child { opacity: 1; }

/* The still is the floor: it shows until the takes are playing, and stays
   put for reduced-motion and for anything that cannot decode the video. */
.proof-visual .proof-still { position: relative; }

.proof-aurora {
  position: absolute;
  inset: 0;
  z-index: 4;
  width: 100%;
  height: 100%;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 220ms ease;
}

.proof-aurora[data-renderer="vgpu"] { opacity: .42; }

.proof-visual[data-audio-playing="true"] .proof-aurora[data-renderer="vgpu"] { opacity: .72; }

@media (prefers-reduced-motion: reduce) {
  .proof-visual .proof-take { display: none; }
  .proof-visual .proof-still { opacity: 1; }
  .proof-aurora { display: none; }
}

.proof { padding-block: clamp(4.5rem, 8vw, 7rem); background: var(--paper-bright); }

.proof-inner { max-width: 70rem; }

.proof-heading {
  max-width: 54rem;
  margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
}

.proof-heading h2 {
  max-width: 14ch;
  margin-bottom: var(--space-3);
  font-size: clamp(2.75rem, 4.5vw, 4.15rem);
}

.proof-heading > p { max-width: 28rem; margin-bottom: 0; color: var(--ink-soft); font-size: 1.05rem; }

.comparison-player {
  display: grid;
  gap: 1.25rem;
}

.comparison-head {
  display: grid;
  justify-items: center;
  gap: .35rem;
}

.comparison-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  /* A column: the switch on its own line, the transport centered under it.
     Volume left this row on 2026-08-20 and lives on the position row, so
     nothing here fights for width any more and the old wrap guard (which
     existed because a range input will not shrink) went with it. */
  flex-direction: column;
  gap: .9rem;
  width: min(100%, 30rem);
}

/* Always visible since it moved to the position row (2026-08-20). It used
   to fade in on hover while holding its 4.5rem of space, which was the
   right call beside the switch and the wrong one here: the reserved width
   pushed the duration in off the right edge and left a gap nothing filled. */
.transport-volume {
  display: flex;
  align-items: center;
  gap: .35rem;
  flex: 0 0 auto;
  min-width: 0;
}

.volume-toggle {
  display: grid;
  place-items: center;
  width: 2.2rem;
  height: 2.2rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 120ms linear;
}

.volume-toggle svg { width: 1.25rem; height: 1.25rem; }

.volume-toggle .volume-cross { opacity: 0; }

.transport-volume[data-muted="true"] .volume-wave { opacity: 0; }

.transport-volume[data-muted="true"] .volume-cross { opacity: 1; }

.transport-volume[data-muted="true"] .volume-toggle { color: var(--ink); }

.volume-slider {
  width: 4.5rem;
  height: 44px;
  margin: 0;
  accent-color: var(--ink);
  cursor: pointer;
  opacity: 1;
}

/* The centering correction that used to live here is gone with the reason
   for it. Volume shared the control row, was invisible until hover, and
   still took 4.5rem out of the measurement, so the three visible controls
   sat half a slider left of center and a min-width:44rem rule pushed them
   back. On the position row none of that applies. */
.transport-volume { margin-left: .35rem; }

.volume-toggle { display: inline-flex; }

.volume-toggle svg { width: 1.05rem; height: 1.05rem; }

.audio-switch {
  position: relative;
  display: inline-grid;
  grid-template-columns: 1fr 1fr;
  flex: 0 0 auto;
  width: min(100%, 24rem);
  min-width: 0;
  padding: .34rem;
  background: var(--line);
  border-radius: 999px;
}

.audio-switch::before {
  content: "";
  position: absolute;
  top: .3rem;
  bottom: .3rem;
  left: .3rem;
  width: calc(50% - .3rem);
  background: var(--ink);
  border-radius: 999px;
  box-shadow: 0 .35rem 1rem rgba(0, 0, 0, .16);
  transform: translateX(0);
  transition: transform 140ms cubic-bezier(.2, .85, .3, 1);
}

.audio-switch button {
  position: relative;
  z-index: 1;
  min-height: 3.2rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: -.015em;
  transition: color 80ms linear, transform 180ms ease;
}

.audio-switch button[aria-pressed="true"] {
  background: transparent;
  color: #fff;
  box-shadow: none;
}

.comparison-caption { max-width: 26rem; margin: 0; color: var(--ink-soft); font-size: .74rem; text-align: center; opacity: .8; }

.comparison-caption kbd {
  display: inline-grid;
  min-width: 1.45rem;
  min-height: 1.45rem;
  place-items: center;
  padding-inline: .35rem;
  border: 1px solid var(--line);
  border-radius: .3rem;
  background: var(--paper-bright);
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  box-shadow: 0 .12rem 0 var(--line);
}

.transport { display: block; }

.transport-play {
  width: 2.8rem;
  min-width: 2.8rem;
  aspect-ratio: 1;
  border: 1px solid var(--ink);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  font-size: .8rem;
  transition: transform 180ms ease, background-color 180ms ease, color 180ms ease, box-shadow 180ms ease;
}

.transport-play[data-playing="true"] { background: var(--ink); color: #fff; }

/* Was the character U+25B6, which iOS renders with EMOJI presentation: a
   blue rounded play button on iPhone, a plain triangle everywhere else.
   A path is the same shape on every platform and inherits currentColor,
   so it inverts with the button when playing. */
.play-glyph {
  width: 1.25rem; height: 1.25rem;
  fill: currentColor;
  display: block; margin-inline: auto;
}

.transport-timeline { display: grid; gap: .15rem; min-width: 0; }

.transport-timeline input {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  /* 44px stays for the touch target; the margin pulls back the part of it
     the track does not draw, which was reading as blank space. */
  height: 44px;
  margin-block: -13px;
  margin-inline: 0;
  background: transparent;
  accent-color: var(--ink);
  cursor: pointer;
}

.transport-timeline input::-webkit-slider-runnable-track {
  height: 4px; border-radius: 999px; background: var(--line);
}

.transport-timeline input::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; margin-top: -4.5px;
  border: 0; border-radius: 50%; background: var(--ink);
}

.transport-timeline input::-moz-range-track {
  height: 4px; border-radius: 999px; background: var(--line);
}

.transport-timeline input::-moz-range-thumb {
  width: 13px; height: 13px; border: 0; border-radius: 50%; background: var(--ink);
}

/* Indicate keyboard focus on the knob, not the full invisible hit area. */
.transport-timeline input:focus-visible { outline: none; }

.transport-timeline input:focus-visible::-webkit-slider-thumb {
  outline: .12rem solid var(--ink); outline-offset: .15rem;
}

.transport-timeline input:focus-visible::-moz-range-thumb {
  outline: .12rem solid var(--ink); outline-offset: .15rem;
}

.transport-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink-soft);
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
}

.transport-meta [data-current-time] { margin-right: auto; }

/* The switch already names the take; a second readout under the scrubber
   said it again. Kept in the DOM because it carries aria-live. */
.comparison-state {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -4px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.latency { padding-block: clamp(4rem, 7vw, 6rem); }

.latency-head {
  display: grid;
  grid-template-columns: minmax(0, .8fr) minmax(18rem, .6fr);
  gap: var(--space-5);
  align-items: end;
  margin-bottom: var(--space-4);
}

.latency-head p { max-width: 30rem; margin-bottom: .4rem; color: var(--ink-soft); }

.mode-families { display: grid; }

.mode-family-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-1);
  color: var(--ink-soft);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .12em;
  line-height: 1.3;
  text-transform: uppercase;
}

.mode-family-title::after {
  content: "";
  flex: 1;
  min-width: var(--space-3);
  height: 1px;
  background: var(--line);
}

.mode-family + .mode-family { margin-top: var(--space-4); }

.mode-family-new .mode-family-title { color: var(--ink); }

.mode-family-new .mode-family-title::before {
  content: "";
  width: .45rem;
  height: .45rem;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--signal);
}

.mode-list { margin: 0; }

.latency-more { margin: clamp(2rem, 3.5vw, 3rem) 0 .1rem; max-width: 34rem; color: var(--ink-soft); }

.latency-cta {
  margin-top: clamp(1.1rem, 2vw, 1.5rem);
  padding-inline: 1.6rem;
  border-radius: .6rem;
  /* The one green FIELD on the site. Green is an accent everywhere else;
     owner's call 2026-08-26, on a single button rather than a surface. */
  border-color: var(--signal);
  background: var(--signal);
  color: #173222;
  box-shadow: 0 .35rem .9rem rgb(71 112 81 / .28);
}

.cta-arrow {
  width: 1rem;
  height: 1rem;
  margin-left: .7rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 180ms ease;
}

.latency-cta:hover .cta-arrow,
.latency-cta:focus-visible .cta-arrow { transform: translateX(.2rem); }

.latency-cta:hover, .latency-cta:focus-visible { transform: translateY(-1px); box-shadow: 0 .6rem 1.4rem rgb(71 112 81 / .38); }

/* The same knob mark the controls page uses for its five knobs. */
.cta-knob {
  width: 1.3rem;
  height: 1.3rem;
  margin-right: .7rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
}

.cta-knob-pointer {
  transform-box: view-box;
  transform-origin: 6px 6px;
  transition: transform 180ms ease;
}

.latency-cta:hover .cta-knob-pointer,
.latency-cta:focus-visible .cta-knob-pointer { transform: rotate(72deg); }

@media (prefers-reduced-motion: reduce) {
  .cta-knob-pointer, .cta-arrow { transition: none; }
  .latency-cta:hover .cta-knob-pointer,
  .latency-cta:focus-visible .cta-knob-pointer,
  .latency-cta:hover .cta-arrow,
  .latency-cta:focus-visible .cta-arrow { transform: none; }
}

.mode-list > div {
  display: grid;
  grid-template-columns: minmax(7rem, .6fr) minmax(8rem, 1fr) minmax(9rem, 1.3fr);
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  padding-block: clamp(1rem, 1.8vw, 1.5rem);
}

.mode-list dt { font-size: clamp(1.35rem, 2vw, 1.8rem); }

.mode-list dd { margin: 0; }

.mode-ms { font-size: clamp(2.5rem, 5vw, 5rem); letter-spacing: -.06em; line-height: .8; }

.mode-role { color: var(--ink-soft); }

/* Bar widths are latency / 40 ms. The scale is the argument, so it gets
   drawn rather than left to the reader to infer from four numbers. */
.mode-bar {
  grid-column: 1 / -1;
  align-self: center;
  height: 3px;
  margin-top: clamp(.6rem, 1vw, .85rem);
  background: var(--line);
}

.mode-bar i { display: block; height: 100%; background: var(--ink); }

.walkthrough {
  --walk-radius: clamp(.5rem, 1vw, .8rem);
  background: var(--inverse);
  color: #f7f7f3;
}

.walk-intro { padding-top: var(--section); }

.walk-intro p { max-width: 30rem; color: var(--inverse-soft); }

.walk-run { position: relative; padding-block: var(--space-5) var(--section); }

.walk-stage { display: block; }

.walk-visual {
  position: relative;
  width: min(100%, calc(84vh * 1350 / 1110));
  margin-inline: auto;
}

.walk-frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1350 / 1110;
  background: #100f0e;
  border: 1px solid var(--line-dark);
  border-radius: var(--walk-radius);
  box-shadow: 0 2rem 5rem rgba(0, 0, 0, .5);
}

.walk-base,
.walk-sharp {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

html.js-walk .walk-base {
  transition: filter 260ms cubic-bezier(.32, .9, .3, 1);
}

/* The step before the first one: the editor as it actually looks, held
   until the scroll moves inside the section. Dimming only reads as a
   spotlight to someone who has seen the thing undimmed -- with no such
   state, the resting frame read as an image that had failed to load. The
   narrow layout drops the spotlight entirely, so it never enters this
   state and the steps below stay visible there. */
html.js-walk .walk-visual.is-full .walk-base { filter: none; }

html.js-walk .walk-visual.is-full .walk-focus { clip-path: inset(0); }

html.js-walk .walk-visual.is-full .walk-box { opacity: 0; }

html.js-walk .walk-visual.is-full .walk-copy,
html.js-walk .walk-visual.is-full .walk-count { opacity: 0; }

.walk-focus,
.walk-box { display: none; }

html.js-walk .walk-box {
  display: block;
  position: absolute;
  left: 5%;
  top: 6%;
  width: 90%;
  height: 8%;
  border: 1px solid #f7f7f3;
  border-radius: calc(var(--walk-radius) * .65);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .65), 0 1rem 2.5rem rgba(0, 0, 0, .48);
  transition: left 260ms cubic-bezier(.32, .9, .3, 1),
              top 260ms cubic-bezier(.32, .9, .3, 1),
              width 260ms cubic-bezier(.32, .9, .3, 1),
              height 260ms cubic-bezier(.32, .9, .3, 1),
              opacity 200ms ease;
  pointer-events: none;
}

.walk-count { display: none; }

.walk-copy { padding-top: var(--space-4); }

html.js-walk .walk-copy {
  --walk-copy-inset: clamp(.75rem, 2.4vw, 1.5rem);
  --walk-copy-width: min(28rem, calc(100% - var(--walk-copy-inset) - var(--walk-copy-inset)));
  position: absolute;
  z-index: 1;
  left: clamp(
    var(--walk-copy-inset),
    var(--copy-left, var(--walk-copy-inset)),
    calc(100% - var(--walk-copy-width) - var(--walk-copy-inset))
  );
  top: var(--copy-y, 18%);
  width: var(--walk-copy-width);
  max-width: 100%;
  padding: 0;
  transform: translateY(var(--copy-shift, 0%));
  transition: left 260ms cubic-bezier(.32, .9, .3, 1),
              top 260ms cubic-bezier(.32, .9, .3, 1),
              transform 260ms cubic-bezier(.32, .9, .3, 1),
              opacity 200ms ease;
}

.walk-copy-panel {
  padding: clamp(1.15rem, 2vw, 1.6rem);
  background: rgba(17, 17, 17, .7);
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: var(--walk-radius);
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, .28);
  backdrop-filter: blur(1rem) saturate(.7);
  -webkit-backdrop-filter: blur(1rem) saturate(.7);
}

.walk-steps { display: block; }

.walk-step {
  margin-bottom: var(--space-4);
}

.walk-step:last-child { margin-bottom: 0; }

.walk-step h3 {
  margin-bottom: var(--space-2);
  font-size: clamp(1.25rem, 1.8vw, 1.65rem);
}

.walk-step p {
  max-width: 27rem;
  margin-bottom: 0;
  color: var(--inverse-soft);
  line-height: 1.45;
}

.walk-marks { display: none; }

.walk-mark { height: 28vh; }

/* Warm paper, not a green wash. Green tinting a whole full-height section
   is what made the accent read as the brand color; it is an accent again,
   carried here by the wire diagram and the lane tags only. */
.ear {
  --ear-bg: var(--flow-paper);
  padding-block: var(--section);
  background: var(--ear-bg);
  color: var(--ink);
}

/* The one boundary on the page that cannot take the standard bottom
   padding: this section ends on a folded one-line disclosure, so
   var(--section) under it left a screen of paper after a single sentence.
   It follows the panel -- tighter shut, more air once the table is out --
   and moves with the same easing, so the gap and the panel read as one
   gesture rather than the page jumping under the click. */
.ear { transition: padding-bottom 320ms cubic-bezier(.32, .9, .3, 1); }

.ear:has(.bench:not([open])) { padding-bottom: clamp(2.5rem, 5vw, 4rem); }

/* Keyed on the closing class, not on [open] alone. The attribute cannot
   come off until the panel has finished collapsing, so keying the gap to
   it left the section holding its open height and then dropping 24px in
   one frame after the panel was already gone -- the snap at the end. The
   class lands on the click, so both move together. */
.ear:has(.bench[open]) { padding-bottom: clamp(3.5rem, 7vw, 5.5rem); }

.ear.is-closing:has(.bench[open]) { padding-bottom: clamp(2.5rem, 5vw, 4rem); }

.ear .eyebrow { display: inline-flex; align-items: center; gap: .5rem; }

.ear-icon {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.04;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ear-intro {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(16rem, 1fr);
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
}

.ear-photo {
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--radius);
  background: #0b0a09;
}

.ear-photo img { width: 100%; height: 100%; object-fit: cover; }

.ear-diagram {
  max-width: 70rem;
  margin: clamp(3.5rem, 7vw, 6rem) auto 0;
}

.ear-copy { padding-top: var(--space-2); }

.ear-copy h2 { max-width: 13ch; }

.ear-copy p { max-width: var(--reading); color: var(--ink-soft); }

.ear-copy .offer-line {
  padding-left: var(--space-2);
  border-left: .16rem solid var(--ink);
  color: var(--ink);
  font-weight: 700;
}

/* Session map: three track lanes, one key wire in the right gutter.
   Every connector piece is positioned as a percentage of its OWN gutter
   cell, so row height and chip width are free to change. */
.mix-wrap { min-width: 0; }

.mix-map {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 4rem;
}

.lane {
  display: grid;
  grid-template-columns: minmax(5.5rem, auto) 1fr auto;
  align-items: center;
  gap: .75rem;
  margin-block: .3rem;
  padding: .8rem 1rem;
  border: 1px solid rgba(71, 112, 81,.18);
  border-radius: var(--radius);
  background: rgba(255,255,255,.4);
}

.lane-name {
  font-size: .75rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink);
}

.lane-tag {
  display: flex; align-items: center; gap: .75rem;
  font-size: .62rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-soft);
}

.lane-tag::before {
  content: ""; flex: 1;
  border-top: 1px dotted rgba(71, 112, 81,.32);
}

.lane-tag-rec { color: var(--signal-ink); }

.chip {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .45rem .85rem; border: 1px solid var(--line-dark);
  border-radius: 999px; background: var(--paper-bright);
  font-size: .8rem; font-weight: 700; line-height: 1.2;
  color: var(--ink); white-space: nowrap;
}

.chip .ear-icon { width: 1rem; height: 1rem; }

.chip-ghost { border-style: dashed; border-color: rgba(90, 89, 85, .5); background: transparent; color: var(--ink-soft); }

.wire { position: relative; }

.wire::before {
  content: ""; position: absolute; left: 50%; width: 2px; margin-left: -1px;
  background: repeating-linear-gradient(to bottom, var(--signal-ink) 0 6px, transparent 6px 11px);
}

.wire-in::before  { top: 50%; bottom: 0; }

.wire-run::before { top: 0; bottom: 0; }

.wire-out::before { top: 0; bottom: 50%; }

.wire-in::after,
.wire-out::after {
  content: ""; position: absolute; top: 50%; left: 0; width: 50%; height: 2px; margin-top: -1px;
  background: repeating-linear-gradient(90deg, var(--signal-ink) 0 6px, transparent 6px 11px);
}

.tip { position: absolute; left: 0; top: 50%; }

.tip-arrow {
  margin: -.3rem 0 0 -.34rem;
  border: .3rem solid transparent;
  border-right: .34rem solid var(--signal-ink);
}

.tip-dot {
  width: .4rem; height: .4rem; margin: -.2rem 0 0 -.2rem;
  border-radius: 50%; background: var(--signal-ink);
}

.wire-label {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  /* A haze rather than a solid chip. The dashes ran through the glyphs at
     the old .2rem padding, and a hard rectangle would have read as a gap
     cut in the wire; fading the field out lets the line pass behind the
     word instead of stopping at it. */
  padding: .75rem .7rem;
  /* Fallback because --ear-bg is defined on .ear, not here: with an
     undefined custom property the whole gradient is invalid and the
     background silently drops to none, which is a hard failure to see. */
  background: radial-gradient(ellipse 82% 62% at 50% 50%,
                              var(--ear-bg, var(--flow-paper)) 0 62%,
                              transparent 100%);
  color: var(--signal-ink);
  font-size: .62rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}

.mix-note { margin: 1.1rem 0 0; font-size: .78rem; color: var(--ink-soft); }

/* Folded shut by default. The numbers are the honest backing for the claim
   above, not the claim itself, and a reader who wants them will open it.
   Styled for the light Ear field, unlike the two dark-field disclosures. */
/* Left edge shared with the diagram it annotates, rather than centred
   under it: 46rem of reading width centred inside a 63rem column started a
   third left edge in one section, which is what made the open state read
   as floating. Spacing below is deliberately uneven -- title and lede as
   one thing, a wider run to the evidence, a shorter one back to the
   reading of it, and the sourcing fenced off behind a rule. */
.bench {
  width: min(100%, 46rem);
  margin: clamp(2.5rem, 5vw, 4rem) 0 0;
}

.bench summary {
  padding-block: .5rem;
  margin-block: -.35rem;
  color: var(--ink);
  font-weight: 700;
  cursor: pointer;
}

/* The gap under the summary lives inside the collapsing track, not on the
   summary: as a margin it belonged to an element that is never collapsing,
   so it appeared and vanished in one frame at each end of the animation. */
/* 0fr to 1fr rather than a height, because the panel's height depends on
   how the table wraps and nothing here should have to know that number.
   The inner element is the one that clips; the outer one is the track. */
.bench-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 320ms cubic-bezier(.32, .9, .3, 1);
}

.bench[open] .bench-body { grid-template-rows: 1fr; }

/* Padding here, not on this element: padding is part of the box and a box
   cannot shrink below it, so .55rem of padding-top left a 9px sliver
   standing after the track reached zero, which then vanished in one frame
   with the attribute. A child margin is content, and content is what the
   track clips. */
.bench-inner { min-height: 0; overflow: hidden; }

.bench-inner > :first-child { margin-top: .55rem; }

.bench-lede { max-width: 34rem; margin: 0; color: var(--ink-soft); }

/* Belt and braces only: below 35rem the table stops being a table (see the
   stacked rules further down), so this never actually scrolls. It stays as
   the guard against a future column making the row wider than the page. */
.bench-scroll { overflow-x: auto; }

.bench-table {
  min-width: 27rem;
  width: 100%;
  margin: 1.9rem 0 1.1rem;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.bench-table th, .bench-table td { padding: .6rem .5rem; text-align: right; }

.bench-table th:first-child,
.bench-table tbody th { text-align: left; font-weight: 400; }

.bench-table thead th {
  border-bottom: 1px solid var(--line);
  font-size: .68rem; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-soft);
}

.bench-table tbody tr + tr th,
.bench-table tbody tr + tr td { border-top: 1px solid var(--line); }

/* The superseded column reads as a footnote, not as a second result. */
.bench-table td:last-child { color: var(--ink-soft); }

.bench-note { margin: 0; max-width: 40rem; font-size: .85rem; }

.bench-credit {
  margin: 1.4rem 0 0;
  padding-top: .85rem;
  border-top: 1px solid var(--line);
  max-width: 40rem;
  font-size: .72rem;
  color: var(--ink-soft);
}

/* Narrow: the table stacks into one block per corpus rather than scrolling
   sideways or dropping columns. Every value keeps its own label, so a phone
   reader gets all six numbers, which is the whole point of the group. */
@media (max-width: 35rem) {
  .bench-table { min-width: 0; font-size: .9rem; }
  /* Kept for assistive tech, out of the visual flow. */
  .bench-table thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .bench-table tr { display: block; padding: .85rem 0; }
  .bench-table tbody tr + tr th,
  .bench-table tbody tr + tr td { border-top: 0; }
  .bench-table tbody tr + tr { border-top: 1px solid var(--line); }
  .bench-table tbody th {
    display: block; padding: 0 0 .45rem; font-weight: 700;
  }
  .bench-table td {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 1rem; padding: .2rem 0;
  }
  .bench-table td::before {
    content: attr(data-label);
    color: var(--ink-soft);
    font-size: .68rem; font-weight: 700;
    letter-spacing: .09em; text-transform: uppercase;
  }
}

.fit { padding-block: clamp(3.5rem, 5vw, 4.5rem); }

.fit-grid {
  display: grid;
  grid-template-columns: minmax(18rem, .7fr) minmax(0, 1.3fr);
  gap: clamp(2rem, 4vw, 3rem);
  align-items: start;
}

.fit-copy p { max-width: 28rem; color: var(--ink-soft); }

.fit-list { margin: 0; }

.fit-list div {
  display: grid;
  grid-template-columns: minmax(6rem, .45fr) minmax(0, 1.55fr);
  gap: var(--space-2) var(--space-3);
  padding-block: 1.5rem;
}

.fit-list div + div { border-top: 1px solid var(--line); }

.fit-list dt { font-weight: 700; }

.fit-list dd { margin: 0; color: var(--ink-soft); }

.maker {
  display: grid;
  grid-template-columns: minmax(8rem, .4fr) minmax(0, 1.6fr);
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
}

.maker strong { font-size: .78rem; text-transform: uppercase; letter-spacing: .08em; }

.maker p { max-width: 44rem; margin-bottom: 0; }

.interlude { padding-block: var(--section); text-align: center; }

.interlude-title {
  max-width: 20ch;
  margin: 0 auto var(--space-2);
  font-size: clamp(1.6rem, 2.6vw, 2.4rem);
}

.interlude-lede { max-width: 30ch; margin: 0 auto; text-align: center; }

.interlude-visual {
  width: min(80vw, 76rem);
  aspect-ratio: 3 / 2;
  margin: clamp(3rem, 6vw, 5rem) auto 0;
  overflow: hidden;
  border-radius: var(--radius);
  background: #0a0908;
}

.interlude-visual img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 32% 28%;
}

/* One purchase moment: price, one button, then the terms as fine print.
   The page already asks three times above this and need not ask twice
   more on the way out. */
/* The hero's own ground, so the page reads as two surfaces, dark and
   paper, rather than three. It used to be its own green-black. */

/* :Community — the Discord ask, grouped by spacing alone. No panel and no
   rule: the band reads as part of the page the way the interlude does, and
   the blurple pill is the only colour on it, so it is the only thing to
   look at. It sits before the offer because it is the cheaper ask of the
   two. The mark is the header's, at the header's blurple. */
.discord-band { padding-block: clamp(4rem, 9vw, 6.5rem); text-align: center; }

.discord-shell { width: min(100%, 40rem); margin-inline: auto; padding-inline: var(--gutter); }

.discord-title { margin: 0 0 var(--space-2); font-size: clamp(1.6rem, 2.6vw, 2.4rem); letter-spacing: -.02em; }

.discord-lede { max-width: 30rem; margin: 0 auto var(--space-4); color: var(--ink-soft); }

.discord-btn {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  min-height: 52px;
  padding: .75rem 1.5rem;
  background: #5865f2;
  color: #fff;
  border-radius: 999px;
  font-weight: 700;
  text-decoration: none;
  transition: background 180ms ease, transform 180ms ease, box-shadow 180ms ease;
}

.discord-btn svg { width: 1.25rem; height: 1.25rem; flex: none; fill: currentColor; }

.offer { padding-block: clamp(3rem, 5vw, 4.5rem); background: var(--flow-black); color: #f7f7f3; }

.offer-shell { width: min(100%, 48rem); margin-inline: auto; text-align: center; }

.offer h2 { margin-bottom: var(--space-3); font-size: clamp(3.4rem, 7vw, 5.5rem); }

/* Brighter than the list text, not a different hue: the tick still reads
   as affirmative and still separates from the body, without color doing
   the work. */
.term-list li::before { content: "✓"; color: #f7f7f3; }

.license-details {
  width: min(100%, 32rem);
  margin: var(--space-2) auto 0;
  color: var(--inverse-soft);
  font-size: .88rem;
  text-align: center;
}

footer a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: #a7a7a1;
  text-decoration: none;
}

/* The mark draws itself tail to head, along the direction its own taper
   grows. The clip is a rotated band swept across the glyph; the end state
   is deliberately larger than the box so the wipe finishes clear of the
   head instead of easing to a stop on top of it. */
@keyframes mark-draw {
  from { clip-path: polygon(0 100%, 0 100%, 0 100%, 0 100%); }
  to   { clip-path: polygon(-40% 140%, 140% -40%, 200% 20%, 20% 200%); }
}

.wordmark svg { animation: mark-draw 900ms 200ms cubic-bezier(.3, .75, .35, 1) both; }

@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* The buy block arrives in order rather than all at once. The container's
   own reveal is handed to its children so nothing animates twice; without
   js-reveal the container reveal still applies exactly as before, which is
   also the no-JS and reduced-motion path.

   Total is 820ms. An earlier cut ran 1420ms and the section sat visibly
   empty for the first 800 of them. */
html.js-reveal .offer-shell.motion-reveal { opacity: 1; transform: none; }

html.js-reveal .mix-map.motion-reveal { opacity: 1; transform: none; }

html.js-reveal .offer-shell.motion-reveal > :not(.term-list):not(.license-details),
html.js-reveal .offer-shell.motion-reveal .term-list li { opacity: 0; }

html.js-reveal .offer-shell.motion-visible > .eyebrow   { animation: rise 380ms   0ms cubic-bezier(.22,.8,.28,1) both; }

html.js-reveal .offer-shell.motion-visible > h2         { animation: rise 460ms  60ms cubic-bezier(.22,.8,.28,1) both; }

html.js-reveal .offer-shell.motion-visible > .lede      { animation: rise 380ms 150ms cubic-bezier(.22,.8,.28,1) both; }

html.js-reveal .offer-shell.motion-visible > .offer-buy { animation: rise 380ms 220ms cubic-bezier(.22,.8,.28,1) both; }

html.js-reveal .offer-shell.motion-visible > .offer-license-note { animation: rise 320ms 580ms cubic-bezier(.22,.8,.28,1) both; }

html.js-reveal .offer-shell.motion-visible .term-list li {
  animation: rise 320ms cubic-bezier(.22,.8,.28,1) both;
  animation-delay: calc(300ms + (var(--i) * 40ms));
}

html.js-reveal .offer-shell.motion-visible .term-list li:nth-child(1) { --i: 0; }

html.js-reveal .offer-shell.motion-visible .term-list li:nth-child(2) { --i: 1; }

html.js-reveal .offer-shell.motion-visible .term-list li:nth-child(3) { --i: 2; }

html.js-reveal .offer-shell.motion-visible .term-list li:nth-child(4) { --i: 3; }

html.js-reveal .offer-shell.motion-visible .term-list li:nth-child(5) { --i: 4; }

html.js-reveal .offer-shell.motion-visible .term-list li:nth-child(6) { --i: 5; }

/* Signal moving down the routing diagram. The wires are already dashes cut
   from a repeating gradient on an 11px period, so marching the gradient one
   period is a seamless loop and needs no extra element.

   It starts only once the diagram has been scrolled to, and then runs on:
   the reveal observer unobserves after the first intersection, so there is
   no hook to stop it on the way out. It is a 2px line, animating one
   compositable property. */
@keyframes wire-flow-up    { to { background-position-y: -11px; } }

@keyframes wire-flow-right { to { background-position-x:  11px; } }

@keyframes wire-flow-left  { to { background-position-x: -11px; } }

/* Direction is per segment, and it is not decoration: the path runs dot ->
   right -> up -> left -> arrow, so .wire-out (the dot, the source) marches
   right, the verticals march up, and .wire-in (the arrow, the destination)
   marches left. Marching them all one way read as the key flowing OUT of
   the plugin, which is the opposite of what Ear does. */
html.js-reveal .mix-map.motion-visible .wire::before    { animation: wire-flow-up    900ms linear infinite; }

html.js-reveal .mix-map.motion-visible .wire-out::after { animation: wire-flow-right 900ms linear infinite; }

html.js-reveal .mix-map.motion-visible .wire-in::after  { animation: wire-flow-left  900ms linear infinite; }

/* What Ear is doing while the wire carries the key: audio arriving,
   confidence climbing, the readout resolving from a question mark to a
   key, then falling back and going again. One 6s loop, and every part of
   it is on that one clock so the confidence bar and the readout cannot
   disagree about when Ear became sure. */
.ear-readout {
  /* Both columns. On `grid-column: 1` it consumed the first cell and every
     lane auto-placed into the 4rem wire column instead. */
  grid-column: 1 / -1;
  display: flex; align-items: center; justify-content: center; gap: .7rem;
  /* No frame. Boxed, it spanned the full width to hold four short things,
     so it read as a container with nothing in it rather than as a readout
     above the lanes. The lanes below are the boxes here. */
  margin-bottom: 1.1rem;
  font-size: .68rem; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-soft);
}

.er-label { font-weight: 700; white-space: nowrap; }

.er-meter { display: inline-flex; align-items: flex-end; gap: 2px; height: .85rem; }

.er-meter i { width: 3px; height: 22%; background: var(--signal-ink); opacity: .55; border-radius: 1px; }

/* Fixed rather than flex:1 now the row is centered: a growing bar would
   push the readout off center as the section width changes. */
.er-conf {
  position: relative; width: 7rem; flex: none; height: 3px;
  border-radius: 2px; background: rgba(71, 112, 81, .18); overflow: hidden;
}

.er-conf i { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--signal-ink); }

/* Reserves the width of the longer of the two readouts, so the row does
   not resize every time the question mark resolves. */
.er-key { position: relative; min-width: 4.4rem; height: 1em; color: var(--ink); }

.er-key b { position: absolute; left: 0; top: 0; font-weight: 700; }

.er-val { opacity: 0; }

@keyframes er-bar  { 0%, 100% { height: 22%; } 50% { height: 100%; } }

@keyframes er-fill { 0% { width: 0; } 40% { width: 100%; } 80% { width: 100%; } 100% { width: 0; } }

@keyframes er-q    { 0%, 36% { opacity: 1; } 44%, 80% { opacity: 0; } 88%, 100% { opacity: 1; } }

@keyframes er-val  { 0%, 36% { opacity: 0; } 44%, 80% { opacity: 1; } 88%, 100% { opacity: 0; } }

html.js-reveal .mix-map.motion-visible .er-conf i { animation: er-fill 6s cubic-bezier(.3,.7,.4,1) infinite; }

html.js-reveal .mix-map.motion-visible .er-q      { animation: er-q    6s linear infinite; }

html.js-reveal .mix-map.motion-visible .er-val    { animation: er-val  6s linear infinite; }

/* Uneven durations on purpose: five bars on one period read as a bar chart
   stepping, not as a level meter. */
html.js-reveal .mix-map.motion-visible .er-meter i { animation: er-bar 420ms ease-in-out infinite; }

html.js-reveal .mix-map.motion-visible .er-meter i:nth-child(2) { animation-duration: 300ms; }

html.js-reveal .mix-map.motion-visible .er-meter i:nth-child(3) { animation-duration: 530ms; }

html.js-reveal .mix-map.motion-visible .er-meter i:nth-child(4) { animation-duration: 360ms; }

html.js-reveal .mix-map.motion-visible .er-meter i:nth-child(5) { animation-duration: 470ms; }

@media (hover: hover) {
  .button:hover { transform: translateY(-.12rem); box-shadow: 0 .7rem 1.8rem rgba(0, 0, 0, .18); }
  .header-discord:hover { background: #4752c4; color: #fff; }
  .discord-btn:hover { background: #4752c4; transform: translateY(-.12rem); box-shadow: 0 .7rem 1.8rem rgba(0, 0, 0, .18); }
  /* Paper, not the signal colour. Green is what the Ear diagram uses to
     mean a key travelling between plug-ins; spending it on a hover state
     and on three stat labels made it decoration up here and semantics
     down there, and the mint read as an alert either way. */
  .site-header nav a:hover { color: #f7f7f3; }
  footer a:hover { color: #f7f7f3; text-decoration: underline; text-underline-offset: .18em; }
  .language-control:hover { color: #f7f7f3; background: rgba(247, 247, 243, .08); }
  .button-secondary:hover { border-color: #f7f7f3; background: rgba(247, 247, 243, .1); }
  .button:active { transform: scale(.98); }
  .transport-play:hover { transform: scale(1.07); box-shadow: 0 .65rem 1.5rem rgba(0, 0, 0, .18); }
  .transport-play:active { transform: scale(.96); }
  .audio-switch button:hover:not([aria-pressed="true"]) { color: var(--ink); transform: scale(.985); }
  .hero-visual.hero-product-hovered::after { opacity: .72; transform: translateY(.45rem) scale(.94); }
  .hero-visual.hero-product-hovered .hero-product {
    transform: translateY(-.35rem) scale(1.015);
    outline-color: rgba(255, 255, 255, .2);
    box-shadow: 0 2.2rem 5rem rgba(0, 0, 0, .58), 0 .45rem 1.15rem rgba(0, 0, 0, .38);
  }
}

@media (max-width: 76rem) {
  .site-header { grid-template-columns: minmax(0, 1fr) auto; }
  .site-header nav,
  .header-actions .header-buy { display: none; }
}

@media (hover: none) {
  /* Hover-to-reveal has no gesture here, so the slider just stays up. */
  .volume-slider { opacity: 1; }
}

@media (max-width: 58rem) {
  .site-header { grid-template-columns: minmax(0, 1fr) auto; }
  .site-header nav { display: none; }
  .header-actions .header-buy { display: none; }
  .hero-grid,
  .proof-heading,
  .audio-stage,
  .latency-head,
  .ear-intro,
  .fit-grid { grid-template-columns: minmax(0, 1fr); }
  .hero-grid { min-height: auto; }
  .hero-product { width: 100%; max-width: 100%; }
  .proof-heading, .latency-head { align-items: start; }
  .latency-head { gap: var(--space-3); }
  .latency-head h2 { margin-bottom: 0; }
  .walk-run { padding-top: var(--space-4); }
  html.js-walk .walk-stage { position: static; height: auto; display: block; }
  .walk-visual { width: 100%; }
  html.js-walk .walk-focus,
  html.js-walk .walk-box,
  html.js-walk .walk-count,
  html.js-walk .walk-marks { display: none; }
  /* No spotlight here means no full-view state to return from; the steps
     are the section on a narrow screen and must never fade out. */
  html.js-walk .walk-visual.is-full .walk-copy { opacity: 1; }
  html.js-walk .walk-copy {
    position: static;
    width: min(100%, var(--reading));
    max-width: var(--reading);
    margin-inline: auto;
    padding: var(--space-4) 0 0;
    background: transparent;
    transform: none;
  }
  html.js-walk .walk-steps { display: block; }
  html.js-walk .walk-step {
    display: grid;
    grid-template-columns: minmax(13rem, .8fr) minmax(0, 1.2fr);
    gap: var(--space-4);
    align-items: start;
    margin-bottom: 0;
    padding-block: var(--space-3);
    opacity: 1;
    pointer-events: auto;
    transform: none;
    border-bottom: 0;
  }
  html.js-walk .walk-step + .walk-step { border-top: 1px solid var(--line-dark); }
  html.js-walk .walk-step h3 { margin-bottom: 0; }
  html.js-walk .walk-step p { max-width: none; }
}

@media (max-width: 35rem) {
  .latency { padding-block: 3rem; }
  .latency-head { margin-bottom: var(--space-2); }
  .walk-intro { padding-top: 4rem; }
  .walk-frame { display: none; }
  html.js-walk .walk-copy { padding-top: var(--space-2); }
  html.js-walk .walk-step {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-1);
    margin-bottom: 0;
    padding-block: 1rem;
  }
  .fit { padding-block: 3rem; }
  .fit-list div {
    grid-template-columns: 5.5rem minmax(0, 1fr);
    padding-block: 1rem;
    font-size: .9rem;
  }
  .maker { grid-template-columns: minmax(0, 1fr); margin-top: var(--space-3); }
  .button-row .button { width: 100%; }
  .discord-btn { width: 100%; justify-content: center; }
  .comparison-controls { width: 100%; }
  .audio-switch { width: 100%; }
  .audio-switch button { min-height: 2.9rem; font-size: .95rem; }
  .comparison-caption { max-width: none; }
  .transport-play { width: 3.25rem; min-width: 3.25rem; }
  .term-list { grid-template-columns: minmax(0, 1fr); }
  .hero-actions { display: grid; grid-template-columns: minmax(0, 1fr); }
  .hero-actions .button { width: 100%; }
  .hero-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .hero-facts div { padding: var(--space-3) var(--space-1); }
  .hero-facts dt { font-size: .68rem; line-height: 1.15; }
  .hero-facts dd { font-size: .72rem; line-height: 1.25; }
  .mix-map { grid-template-columns: minmax(0, 1fr) 2.25rem; }
  .lane {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: .25rem;
    margin-block: .15rem;
    padding: .5rem .7rem;
  }
  .lane-name { grid-column: 1 / -1; }
  .lane-tag::before { display: none; }
  .chip { padding: .35rem .65rem; font-size: .75rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-copy h1,
  .hero-actions,
  .hero-demo-details,
  .hero-facts-band,
  .hero-visual { animation: none !important; }
  .bench-body,
  .ear { transition: none !important; }
  .hero-visual::after { transition: none !important; }
  .motion-reveal { opacity: 1 !important; transform: none !important; }
  /* This block enumerates rather than blanket-killing, so anything added
     above has to be named here too or it keeps moving. */
  .wordmark svg,
  .offer-shell > *,
  .term-list li,
  .wire::before,
  .wire-in::after,
  .wire-out::after,
  .er-meter i,
  .er-conf i,
  .er-q,
  .er-val { animation: none !important; }
  /* Settled on the answer rather than the question: with no motion, the
     readout has to show the state Ear ends in. */
  .er-conf i { width: 100% !important; }
  .er-q { opacity: 0 !important; }
  .er-val { opacity: 1 !important; }
  .er-meter i { height: 60% !important; }
  .offer-shell > *,
  .term-list li { opacity: 1 !important; transform: none !important; }
  .wordmark svg { clip-path: none !important; }
  .button,
  .transport-play,
  .audio-switch button,
  .hero-product,
  .walk-focus,
  .walk-box,
  .walk-copy,
  .walk-step { transition: none !important; }
}

/* HERO — product first. No illustrative backdrop. */
.hero { background: var(--flow-black); }

.hero-grid {
  gap: clamp(2.5rem, 4vw, 4rem);
  padding-top: clamp(4.5rem, 8vw, 7.5rem);
}

.hero-copy { width: min(100%, 44rem); }

.hero h1 {
  max-width: 15ch;
  font-size: clamp(3rem, 5vw, 5.4rem);
  letter-spacing: -.072em;
  line-height: .94;
  text-wrap: balance;
}

/* The frame states its height and the render states its own. No
   percentage survives anywhere in this chain, and that is the whole point:
   WebKit does not resolve a percentage height against a height it derived
   itself, so with aspect-ratio: 16 / 8.7 here and max-height: 90% on the
   render, Safari dropped the cap, fell back to the 57rem width cap, and
   drew the render 694px tall inside a 411px box. The overflow below cut
   the title bar off the top and the keyboard off the bottom.

   Stating the frame's height outright was not enough. Measured on the
   load timeline in Safari 26.5, render against a 1184x644 frame: 912x750
   at DOMContentLoaded, at load, at +100ms and at +600ms, then 522x429 by
   +2500ms, and correct immediately after any style change -- which is why
   hovering the render appeared to fix it. WebKit resolves the percentage
   on a later pass, not on the first layout, so the percentage has to go.

   The render's height is the frame's content box -- its own height less
   the 1px border and the padding -- times the .9 that used to be
   max-height: 90%. Width follows from the 1350x1110 intrinsic ratio.
   @Robustness: 100vw counts a classic scrollbar where 100% does not, so
   between roughly 930 and 1264px without overlay scrollbars the frame can
   run 8px off the ratio, and Chromium draws the render 1px shorter than
   the percentage did. Both are invisible against a 449px render. */
.hero-visual {
  --hero-w: min(100vw - (2 * var(--gutter)), 74rem);
  --hero-pad: clamp(2rem, 5vw, 5.5rem);
  width: min(100%, var(--hero-w));
  margin-inline: auto;
  height: calc(var(--hero-w) * 8.7 / 16);
  overflow: hidden;
  padding: var(--hero-pad);
  background:
    radial-gradient(ellipse at 50% 52%, rgba(255,255,255,.055) 0, rgba(255,255,255,.018) 35%, transparent 67%),
    linear-gradient(180deg, #161514 0%, #121110 100%);
  border: 1px solid rgba(255,255,255,.055);
  border-bottom: 0;
}

/* Painted canvas behind the render. Held back with a scrim so the plugin
   stays the brightest thing in the frame. */
.hero-visual::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(18,17,16,.62) 0%, rgba(18,17,16,.78) 100%),
    url("assets/hero-canvas.webp") center / cover no-repeat;
  pointer-events: none;
}

.hero-visual::after {
  inset: 18% 19% 6%;
  background: radial-gradient(ellipse, rgba(0,0,0,.42), transparent 70%);
  filter: blur(1.8rem);
  opacity: .55;
}

.hero-product {
  height: calc((var(--hero-w) * 8.7 / 16 - 2px - (2 * var(--hero-pad))) * .9);
  width: auto;
  max-width: min(100%, 57rem);
  max-height: none;
  outline-color: rgba(255,255,255,.065);
  box-shadow: 0 2.5rem 5.5rem rgba(0,0,0,.5), 0 .25rem 1rem rgba(0,0,0,.32);
}

/* Continuous with the hero rather than a band of its own: --flow-black is
   the exact value the hero visual's gradient ends on, so the strip reads
   as the bottom of the hero and only the hairline separates them. It was
   a filled green (#162219), which spent the signal color on a whole
   surface -- the accent stops meaning anything once it is also a
   background, and the strip read as a foreign object under a near-black
   hero. The green now survives where it is doing work: the three labels. */
.hero-facts-band {
  border-top: 1px solid rgba(255, 255, 255, .07);
  background: var(--flow-black);
}

/* HUMAN MOMENT — the image gets a whole breath of its own. */
.proof {
  padding-top: clamp(1.25rem, 3vw, 2.5rem);
  padding-bottom: clamp(6rem, 10vw, 10rem);
  background: var(--flow-paper-bright);
}

/* Sized so the picture and the Dry / Corrected switch share one screen --
   her face has to be visible at the moment you hit the toggle, otherwise the
   reaction the shot exists for happens off-screen. */
.proof-visual {
  width: min(calc(100% - (2 * var(--gutter))), 64rem);
  height: min(42vh, 26rem);
  aspect-ratio: auto;
  margin: 0 auto clamp(1.5rem, 3vw, 2.5rem);
  border-radius: var(--radius);
  background: #0b0a09;
  box-shadow: 0 2rem 6rem rgba(24,24,24,.08);
}

.proof-visual img,
.proof-visual video {
  object-position: 50% 33%;
}

.proof-inner { max-width: 64rem; }

.proof-heading {
  margin-bottom: clamp(1.25rem, 2.5vw, 2rem);
}

.proof-heading h2 { max-width: 12ch; }

/* The audio proof itself should feel like one focused instrument. */
.comparison-player {
  width: min(100%, 46rem);
  margin-inline: auto;
}

.transport-timeline { margin-top: 0; }

/* ENGINEERING — intentionally quieter after the human image. */
.latency {
  background: var(--flow-paper);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.mode-list > div {
  grid-template-columns: minmax(7rem,.52fr) minmax(9rem,.9fr) minmax(12rem,1.35fr);
}

.mode-ms { font-variant-numeric: tabular-nums; }

/* PRODUCT WALKTHROUGH — deep focus, no extra decoration. */
.walkthrough { background: #121110; }

.walk-frame {
  border-color: rgba(255,255,255,.075);
  box-shadow: 0 2.25rem 6rem rgba(0,0,0,.46);
}

.walk-copy-panel {
  background: rgba(18,17,16,.78);
  border-color: rgba(255,255,255,.075);
  box-shadow: 0 1rem 3rem rgba(0,0,0,.24);
}

.offer h2 { letter-spacing: -.065em; }

@media (max-width: 58rem) {
  /* The desktop overlay paint is declared immediately above. Narrow uses
     the copy as the section itself, so its panel must be cleared here,
     after that desktop rule, or the rectangle and shadow come back. */
  .walk-copy-panel {
    padding: 0;
    background: transparent;
    border: 0;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* The framed canvas is a desktop device. Narrow, the plugin shrinks but
     the frame does not, so the painted rectangle becomes the biggest thing
     on screen -- exactly what the hero is not allowed to be. Drop the card
     and let the render sit on the page at full width; it also buys the UI
     back the ~90px of padding it could not spare at this size. */
  .hero-visual {
    height: auto;
    aspect-ratio: auto;
    width: calc(100% + (2 * var(--gutter)));
    margin-inline: calc(-1 * var(--gutter));
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
  }
  .hero-visual::before,
  .hero-visual::after { display: none; }
  .hero-product {
    height: auto;
    max-width: 100%;
    max-height: none;
    outline: 0;
    box-shadow: none;
  }
  /* 4/5 is sized for a phone and keeps scaling with the viewport, so by
     900px these were 1106px tall -- taller than the screen. The cap holds
     them to one comfortable band; object-fit crops the rest. Phones are
     unchanged: 30rem is already shorter than 4/5 of a 390px viewport. */
  .proof-visual,
  .interlude-visual,
  .ear-photo { max-height: min(60vh, 30rem); }
  /* 4/5 made this 480px tall on a 390px phone, and image-top to
     player-bottom spanned 1032px against about 699px of usable height in
     Safari once its chrome is counted. Her face and the Dry/Corrected
     switch could not be on screen at the same time, which is the entire
     point of the section: you scrolled her away to reach the control.
     16/9 lands near the source frame's own 1.75:1, so it also crops less
     than the portrait box did. */
  .proof-visual {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    margin: 0 0 clamp(1.25rem, 4vw, 2rem);
    border-radius: 0;
    box-shadow: none;
  }
  /* Once the visuals run edge to edge they read as the section's own
     opening, and the section padding above them reads as a hole. Flush. */
  .proof { padding-top: 0; }
  .ear { padding-top: 0; }
  .proof-visual img,
  .proof-visual video {
    object-position: 50% 32%;
  }
  /* The heading sits between the picture and the player, so its margins
     are part of the same budget. */
  .proof-heading { margin-bottom: clamp(.75rem, 3vw, 1.25rem); }
  .proof-heading > p { margin-bottom: 0; }
  /* Part of the same budget as the picture above it: at 1.5rem the
     section ran 21px past what Safari leaves visible with its URL
     bar showing. */
  .interlude-visual {
    width: 100%;
    aspect-ratio: 4 / 5;
    margin-top: clamp(2rem, 8vw, 3rem);
    border-radius: 0;
  }
  .ear-photo {
    aspect-ratio: 4 / 5;
    border-radius: 0;
    margin: 0 calc(-1 * var(--gutter));
    width: calc(100% + (2 * var(--gutter)));
  }
  .ear-intro { row-gap: clamp(1.5rem, 5vw, 2.5rem); }
  /* 68% pinned her against the left edge and spent the right half of
     the frame on the amp and the laptop. The box is 390x480 and the
     source is 1536x1024, so cover scales it to 720x480 and crops 330px
     horizontally; at 68% that showed 31-85% of the frame. 35% keeps
     her, the notebook and the writing hand. */
  .ear-photo img { object-position: 35% 30%; }
  .interlude-visual img { object-position: 50% 22%; }
  /* Narrow drops the spotlight, so the dimming that made it readable has
     nothing left to point at. Declared after the filter above, never
     before it, or specificity ties and source order undo this. */
  html.js-walk .walk-base { filter: none; }
}

@media (max-width: 35rem) {
  .hero h1 { font-size: clamp(2.7rem, 14vw, 4.2rem); }
  .mode-list > div {
    grid-template-columns: minmax(0, 1fr) auto;
    padding-block: 1rem;
  }
  .mode-ms { grid-column: 2; grid-row: 1 / 3; align-self: center; margin: 0; }
  .mode-role { grid-column: 1; }
  .mode-bar { grid-row: 3; }
  .ear-photo { aspect-ratio: 3 / 2; max-height: none; }
  .ear-intro { row-gap: var(--space-4); }
  .ear-diagram { margin-top: var(--space-4); }
  .site-header { gap: var(--space-2); }
}
