/* StudioZIO — Core Design System v1
   Single source of truth for the hub, Mastering Suite and Tempo Delay.
   Tokens are copied verbatim from 00-core-system.md: do not re-derive,
   lighten, or convert to hex. */

/* ---------- fonts (self-hosted, OFL — licences in assets/fonts/) -------- */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/space-grotesk-600.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/space-grotesk-700.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Tight";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/inter-tight-400.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Tight";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/inter-tight-500.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-500.woff2") format("woff2");
}

/* ---------- tokens (verbatim from 00-core-system.md) -------------------- */
:root {
  /* surface ramp — each step is a visible +0.045 L lift */
  --background:      oklch(0.155 0.006 236);
  --surface-raised:  oklch(0.202 0.008 236);
  --surface-overlay: oklch(0.248 0.009 236);
  --surface-control: oklch(0.295 0.011 236);

  --foreground:       oklch(0.945 0.004 236);
  --muted-foreground: oklch(0.672 0.014 236);

  /* deep instrument cyan — final darkened value, shared by all three sites */
  --primary:            oklch(0.578 0.086 218);
  --primary-foreground: oklch(0.96  0.008 218);
  --primary-deep:       oklch(0.385 0.068 220);

  /* Derived accent-text step. --primary above is the brand accent and stays
     exactly as specified; at small sizes it measures 4.3-4.7:1, which sits on
     the WCAG AA floor. Small accent TEXT uses this lighter step of the same
     hue and chroma; --primary keeps every large, bold and background use. */
  --primary-text:       oklch(0.700 0.086 218);

  --destructive: oklch(0.598 0.185 22);

  --border:   oklch(1 0 0 / 9%);
  --hairline: oklch(1 0 0 / 6%);
  --input:    oklch(1 0 0 / 12%);
  --ring:     oklch(0.578 0.086 218 / 55%);

  /* elevation */
  --shadow-panel:  0 1px 0 0 oklch(1 0 0 / 5%) inset, 0 18px 40px -24px oklch(0 0 0 / 90%);
  --shadow-float:  0 1px 0 0 oklch(1 0 0 / 6%) inset, 0 34px 70px -32px oklch(0 0 0 / 95%);
  --glow-primary:  0 0 0 1px oklch(0.578 0.086 218 / 28%), 0 6px 18px -12px oklch(0 0 0 / 80%);

  /* type */
  --font-display: "Space Grotesk", "Segoe UI", system-ui, sans-serif;
  --font-body: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  --measure: 62ch;

  /* layout + motion */
  --shell: 72rem;
  --gutter: 1.5rem;
  --dur-fast: 140ms;
  --dur-base: 260ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: dark;
}
@media (min-width: 900px) {
  :root { --gutter: 2rem; }
}

/* ---------- base -------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { min-width: 0; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.025em;
  text-wrap: balance;
  hyphens: none;          /* required — fixes the legacy hero word break */
  overflow-wrap: normal;
}
h1 { font-size: clamp(2.6rem, 6.2vw, 4.6rem); }
h2 { font-size: clamp(1.9rem, 3.6vw, 2.9rem); }
h3 { font-size: 1.125rem; font-weight: 600; letter-spacing: -0.01em; }
h4 { font-size: 1rem; font-weight: 600; }

p { margin: 0 0 1rem; max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-decoration: none; }
/* ...except in body copy. `color: inherit` with no underline is right for the
   navigation, the cards and the buttons, which all carry their own
   affordance; inside a sentence it left links completely indistinguishable
   from the text around them -- same colour, same weight, no underline -- so
   the contact page's link to the catalogue, and every cross-link between the
   notes, were invisible to a reader and only worked for a crawler. Scoped to
   paragraphs, list items and definition values so nothing that already looks
   like a control is touched. */
main p a:not(.btn),
main li a:not(.btn),
main dd a:not(.btn) {
  color: var(--primary-text);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in oklch, var(--primary-text) 45%, transparent);
  transition: text-decoration-color var(--dur-base) var(--ease-out);
}
main p a:not(.btn):hover,
main li a:not(.btn):hover,
main dd a:not(.btn):hover {
  text-decoration-color: currentColor;
}
img, svg { display: block; max-width: 100%; }
ul, ol { margin: 0; padding: 0; list-style: none; }

:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--primary-deep); color: var(--foreground); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 80;
  background: var(--primary);
  color: var(--primary-foreground);
  padding: 0.7rem 1.1rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ---------- shell ------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--primary-text);
  margin: 0 0 1.25rem;
}
.eyebrow--muted { color: var(--muted-foreground); }

.lede {
  color: var(--muted-foreground);
  font-size: 1.0625rem;
  max-width: var(--measure);
}

/* ---------- header ------------------------------------------------------ */
.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  border-bottom: 1px solid var(--hairline);
  background: color-mix(in oklab, var(--background) 85%, transparent);
  backdrop-filter: blur(20px);
}
.site-header .bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: 60px;
  padding-block: 0.6rem;
}

/* logo: waveform mark tile + Studio/ZIO wordmark + optional product suffix */
.logo { display: inline-flex; align-items: center; gap: 0.6rem; min-height: 44px; }
.logo-mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-overlay);
}
.logo-mark svg { width: 20px; height: 20px; }
.logo-word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1875rem;
  letter-spacing: -0.02em;
  color: var(--foreground);
}
.logo-word b { font-weight: 700; color: var(--primary); }
.logo-suffix {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  padding-left: 0.15rem;
}

.nav-links { display: none; }
.nav-links ul { display: flex; align-items: center; gap: 2rem; }
.nav-links a {
  display: block;
  padding-block: 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-links a:hover, .nav-links a:focus-visible { color: var(--foreground); }
.nav-links a[aria-current="page"] { color: var(--foreground); }

/* compact nav: native disclosure, works without JavaScript */

/* The search box that sits in the header. It carries what is typed to the
   hub's search page and does nothing else; the estate's index lives there.
   Enter rather than a <form>, because form-action is 'none'. Two copies
   ship, one in the row and one in the compact menu, so the box is there on a
   phone as well. */
.header-search { flex: none; width: 150px; }
.header-search .header-search-field {
  min-height: 38px;
  padding: 0.4rem 0.7rem;
  font-size: 0.8125rem;
}
.header-search .header-search-field::placeholder {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.header-search--panel { width: auto; padding: 0.85rem; border-bottom: 1px solid var(--hairline); }
.header-search--bar { display: none; }

/* A search result is one link, not a card with a link in it: the whole block
   is the target, it says so with the cursor, and the keyboard's current
   result wears the same border the pointer's hover does. */
.search-hit { display: flex; text-decoration: none; color: inherit; cursor: pointer; }
.search-hit h3 { color: var(--foreground); }
.search-hit strong { color: var(--primary-text); font-weight: 500; }
.search-hit:hover, .search-hit:focus-visible, .search-hit[aria-selected="true"] {
  border-color: var(--primary);
  background: var(--surface-overlay);
}
.search-hit:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

.nav-compact { position: relative; }
.nav-compact > summary {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-raised);
  cursor: pointer;
  list-style: none;
  color: var(--foreground);
  font-size: 1rem;
  line-height: 1;
}
.nav-compact > summary::-webkit-details-marker { display: none; }
.nav-compact > summary:hover { border-color: var(--primary); }
.nav-compact > summary .shut { display: none; }
.nav-compact[open] > summary .open { display: none; }
.nav-compact[open] > summary .shut { display: block; }
.nav-compact .panel {
  position: absolute;
  right: 0;
  top: calc(100% + 10px);
  min-width: 15rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-raised);
  box-shadow: var(--shadow-float);
  z-index: 65;
}
.nav-compact .panel li + li { border-top: 1px solid var(--hairline); }
.nav-compact .panel a {
  display: block;
  padding: 0.95rem 1.15rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.nav-compact .panel a:hover, .nav-compact .panel a[aria-current="page"] { color: var(--foreground); }
@media (min-width: 940px) {
  .nav-links { display: block; }
  .header-search--bar { display: block; }
  .nav-compact { display: none; }
}

/* ---------- sections ---------------------------------------------------- */
.section { border-bottom: 1px solid var(--hairline); padding-block: 5rem; }
.section--tight { padding-block: 3.5rem; }
.section-head { margin-bottom: 2.5rem; }
.section-head h2 { margin-bottom: 0.9rem; }

/* hero: compact, never min-h-screen */
.hero {
  position: relative;
  border-bottom: 1px solid var(--hairline);
  padding-top: 4rem;
  padding-bottom: 3.5rem;
  overflow: hidden;
}
.hero-grid { display: grid; gap: 3rem; align-items: center; }
@media (min-width: 1000px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: 4rem; }
  .hero--stacked .hero-grid { grid-template-columns: minmax(0, 1fr); }
}
.hero h1 { max-width: 22ch; }
.hero h1 .accent { color: var(--primary); display: block; }
.hero .lede { margin-top: 1.6rem; }
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 2.25rem;
}

/* 56px technical grid — hero backdrops only */
.tech-grid::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, oklch(1 0 0 / 3.5%) 1px, transparent 1px),
    linear-gradient(to bottom, oklch(1 0 0 / 3.5%) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: linear-gradient(to bottom, oklch(0 0 0) 55%, transparent);
}
.tech-grid > * { position: relative; }

/* ---------- buttons ----------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 44px;
  padding: 0.75rem 1.35rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--foreground);
  font-family: var(--font-body);
  font-size: 1.1875rem;   /* large-text scale: keeps the specified fill/label pair AA-compliant */
  font-weight: 700;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-out),
              background var(--dur-base) var(--ease-out),
              filter var(--dur-base) var(--ease-out);
}
.btn:hover, .btn:focus-visible { border-color: var(--primary); }
.btn-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-foreground);
  box-shadow: var(--glow-primary);   /* ring + tight shadow, never a bloom */
}
.btn-primary:hover, .btn-primary:focus-visible { filter: brightness(1.1); border-color: var(--primary); }

/* ---------- chips ------------------------------------------------------- */
.chip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface-raised);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.chip--flag { color: var(--foreground); border-color: var(--border); }
.chip--destructive { color: var(--destructive); border-color: oklch(0.598 0.185 22 / 30%); }
.chip--bare { border: 0; background: none; padding-inline: 0; }
.chip .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.chip--flag .dot { background: var(--primary); }
.chip--live .dot { background: var(--primary); animation: pulse-dot 2.4s ease-in-out infinite; }
.chip--destructive .dot { background: var(--destructive); }
/* A defect that is fixed and needs nothing from the reader. It takes the
   instrument cyan rather than --destructive: red on a closed entry reads as
   "this is still broken" to anyone scanning the page, which is the opposite
   of what it records. Text uses the lighter --primary-text step because chips
   set 10px type, where --primary itself only just clears AA. */
.chip--closed { color: var(--primary-text); border-color: var(--ring); }
.chip--closed .dot { background: var(--primary); }

/* ---------- panels ------------------------------------------------------ */
.panel {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-raised);
  box-shadow: var(--shadow-panel);
}
.panel-float {
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface-raised);
  box-shadow: var(--shadow-float);
}

/* product card: panel + live mock + body; hover lifts the surface only */
.card-grid { display: grid; gap: 1.5rem; }
@media (min-width: 900px) { .card-grid--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 760px) { .card-grid--3 { grid-template-columns: repeat(3, 1fr); } }

.product-card { display: flex; flex-direction: column; overflow: hidden; transition: background var(--dur-base) var(--ease-out); }
.product-card:hover { background: var(--surface-overlay); }
.product-card .card-body { padding: 1.5rem; display: flex; flex-direction: column; gap: 0.9rem; flex: 1; }
.product-card .card-title-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.product-card p { color: var(--muted-foreground); font-size: 0.9375rem; }
.product-card .btn { align-self: flex-start; margin-top: 0.25rem; }

/* module card: mono index, display title, one muted sentence */
.module-card { padding: 1.5rem; display: flex; flex-direction: column; gap: 0.6rem; transition: background var(--dur-base) var(--ease-out); }
.module-card:hover { background: var(--surface-overlay); }
.module-card .idx {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  color: var(--primary-text);
}
.module-card p { color: var(--muted-foreground); font-size: 0.9375rem; margin: 0; }

/* claim + readout: a plain-language claim with the value the plugin actually
   reports directly beneath it. The products are measurement-first, so the
   pages argue the way they do -- never a claim without its number. */
.claims { display: grid; gap: 2.25rem; }
@media (min-width: 820px) { .claims { grid-template-columns: 1fr 1fr; gap: 2.75rem 3.5rem; } }
/* the claim is a column so the readout can sit at the bottom of it: that
   lines the hairline rules up across a row instead of letting each one follow
   the length of its own paragraph. The margin-bottom keeps the gap honest
   when a claim is tall enough that there is no free space to push with. */
.claim { display: flex; flex-direction: column; }
.claim h3 { font-size: 1.1875rem; letter-spacing: -0.012em; }
.claim p { margin: 0.7rem 0 1rem; color: var(--muted-foreground); font-size: 0.9375rem; }
.claim-readout {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.9rem;
  margin: auto 0 0;
  padding-top: 0.7rem;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}
.claim-readout dt {
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
/* amber is data: these values are measured, so they read as measurements */
.claim-readout dd { margin: 0; color: var(--primary-text); }

/* spec grid: 1px gaps over --border so dividers read as hairlines */
.spec-grid {
  display: grid;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  margin: 0;
}
@media (min-width: 620px) { .spec-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .spec-grid { grid-template-columns: repeat(3, 1fr); } }
.spec-grid > div { background: var(--surface-raised); padding: 1.35rem 1.5rem; }
.spec-grid dt {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.spec-grid dd { margin: 0.5rem 0 0; font-size: 0.9375rem; }

/* download row */
.download-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 2rem;
}
.download-row .actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }

/* ---------- plugin mocks (flat, alive, no glow) ------------------------- */
.mock { border-bottom: 1px solid var(--hairline); background: var(--surface-raised); }
.mock-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-overlay);
}
.mock-title {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--foreground);
}
.mock-title .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--primary); animation: pulse-dot 2.4s ease-in-out infinite; }
.mock-body { padding: 1.1rem; display: grid; gap: 0.9rem; }

/* The rail is the plug-in's own organising idea: every stage in the order the
   audio reaches it. A cyan head arms one stage at a time, left to right, so
   the motion states the layout rather than decorating it. Stage count drives
   the stagger, which is why the delays are keyed per rail size. */
.rail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.5312rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.rail { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 4px; }
.rail .st {
  border: 1px solid var(--hairline);
  border-top: 2px solid transparent;
  border-radius: 5px;
  background: var(--surface-control);
  padding: 0.4rem 0.36rem;
  display: grid;
  gap: 0.1rem;
  min-width: 0;
  animation: stage-arm var(--rail-cycle) linear infinite;
}
.rail .st .k {
  font-family: var(--font-mono);
  font-size: 0.4688rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rail .st .v {
  font-family: var(--font-mono);
  font-size: 0.6562rem;
  color: var(--foreground);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The rack: the same cadence as the rail, but each cell carries a module name
   and the group it belongs to, because that is what the plug-in's own preview
   shows on screen. The IN and OUT nodes bracket the eight so the order reads
   as a signal path rather than as a list. */
.rack--8 { --rail-cycle: 4.8s; }
.rack--8 .slot:nth-child(2) { animation-delay: 0.0s; }
.rack--8 .slot:nth-child(3) { animation-delay: 0.28s; }
.rack--8 .slot:nth-child(4) { animation-delay: 0.56s; }
.rack--8 .slot:nth-child(5) { animation-delay: 0.84s; }
.rack--8 .slot:nth-child(6) { animation-delay: 1.12s; }
.rack--8 .slot:nth-child(7) { animation-delay: 1.4s; }
.rack--8 .slot:nth-child(8) { animation-delay: 1.68s; }
.rack--8 .slot:nth-child(9) { animation-delay: 1.96s; }
/* Eight equal tenths of a card cannot hold "Transient Shaper" on one line,
   and no way of breaking it reads as anything but a fault. So every cell is
   the width of the longest name instead -- all eight identical, none broken --
   and the row wraps on a narrow screen the way a line of text does, IN still
   first, OUT still last, order intact. */
.rack {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 2px;
}
.rack-node {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  min-width: 0;
  padding: 0.4rem 0.16rem;
  border: 1px dashed var(--hairline);
  border-radius: 5px;
  font-family: var(--font-mono);
  font-size: 0.4688rem;
  letter-spacing: 0.14em;
  color: var(--muted-foreground);
}
.rack .slot {
  flex: 0 0 auto;
  border: 1px solid var(--hairline);
  border-top: 2px solid transparent;
  border-radius: 5px;
  background: var(--surface-control);
  padding: 0.5rem 0.45rem;
  display: grid;
  align-content: center;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 0.5312rem;
  line-height: 1.25;
  color: var(--foreground);
  text-align: left;
  white-space: nowrap;
  /* The face is monospaced, so one measure in ch holds every name and makes
     every cell the same width without a magic pixel number. "Transient
     Shaper" is the longest at sixteen. */
  /* border-box, so the measure has to carry the padding and the border with
     it or the longest name pushes its own cell two pixels wider than the
     rest. */
  min-width: calc(16ch + 0.9rem + 2px);
  cursor: grab;
  touch-action: none;
  animation: stage-arm var(--rail-cycle) linear infinite;
}
.rack .slot:hover { border-color: var(--primary); }
.rack .slot:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
/* The one being carried leaves the sweep behind: a module that is moving
   should not also be lighting up as though the signal were passing through
   it. */
.rack .slot[data-dragging="true"] {
  cursor: grabbing;
  border-color: var(--primary);
  background: var(--surface-overlay);
  animation: none;
}
.rack[data-dragging="true"] .slot { transition: none; }
/* An instruction, not a label: the mono capitals the rest of the mock uses
   for its headings would shout it, and it is the quietest thing here. */
.rack-hint {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--muted-foreground);
}

/* Where a module landed is obvious on screen, so printing it under the rack
   was repeating what the reader had just watched happen. It is not obvious to
   anyone reordering with the arrow keys and a screen reader, though, so the
   announcement stays and only the sight of it goes. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Output meters, the pair the window shows. Decorative, like the bands and
   the correlation bar on the other two mocks. */
.meters { display: grid; gap: 0.3rem; }
.meters-head {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.5312rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.meters-row { display: grid; gap: 4px; }
.meters-row i {
  display: block;
  height: 6px;
  border-radius: 3px;
  background: color-mix(in oklab, var(--primary) 55%, transparent);
  transform-origin: left center;
  animation: meter-ride 3.2s ease-in-out infinite;
}
.meters-row i:nth-child(2) { animation-delay: 0.45s; }
@keyframes meter-ride {
  0%, 100% { transform: scaleX(0.42); }
  30%      { transform: scaleX(0.86); }
  55%      { transform: scaleX(0.63); }
  78%      { transform: scaleX(0.91); }
}

.rail--8 { --rail-cycle: 4.8s; }
.rail--8 .st:nth-child(1) { animation-delay: 0s; }
.rail--8 .st:nth-child(2) { animation-delay: 0.28s; }
.rail--8 .st:nth-child(3) { animation-delay: 0.56s; }
.rail--8 .st:nth-child(4) { animation-delay: 0.84s; }
.rail--8 .st:nth-child(5) { animation-delay: 1.12s; }
.rail--8 .st:nth-child(6) { animation-delay: 1.4s; }
.rail--8 .st:nth-child(7) { animation-delay: 1.68s; }
.rail--8 .st:nth-child(8) { animation-delay: 1.96s; }
.rail--9 { --rail-cycle: 5.4s; }
.rail--9 .st:nth-child(1) { animation-delay: 0s; }
.rail--9 .st:nth-child(2) { animation-delay: 0.26s; }
.rail--9 .st:nth-child(3) { animation-delay: 0.52s; }
.rail--9 .st:nth-child(4) { animation-delay: 0.78s; }
.rail--9 .st:nth-child(5) { animation-delay: 1.04s; }
.rail--9 .st:nth-child(6) { animation-delay: 1.3s; }
.rail--9 .st:nth-child(7) { animation-delay: 1.56s; }
.rail--9 .st:nth-child(8) { animation-delay: 1.82s; }
.rail--9 .st:nth-child(9) { animation-delay: 2.08s; }

/* Stereo echo field — the delay's own right-hand column. L and R are told
   apart by their labels, exactly as the plug-in does it; the correlation bar
   settles rather than sweeping, because that is what correlation does. */
.echo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.9rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface-control);
  padding: 0.65rem 0.8rem;
}
.echo-corr { display: grid; gap: 0.32rem; min-width: 0; }
.echo-label {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.5312rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.echo-bar { position: relative; height: 6px; border-radius: 3px; background: color-mix(in oklab, var(--foreground) 8%, transparent); }
.echo-bar i { position: absolute; top: 0; bottom: 0; left: 50%; border-radius: 3px; background: var(--primary); animation: corr-settle 5.2s ease-in-out infinite; }
.echo-big {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.45rem;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--foreground);
}
.echo-big small {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  color: var(--muted-foreground);
}

/* Learned correction — Pink Match reports a figure per band, so the bars run
   a learn pass and settle instead of drifting at random. */
.bands { border: 1px solid var(--hairline); border-radius: 8px; background: var(--surface-control); padding: 0.7rem 0.8rem; display: grid; gap: 0.45rem; }
.bands-head {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.5312rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.bands-row { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 5px; align-items: center; height: 46px; }
.bands-row i { display: block; height: 3px; border-radius: 2px; background: var(--primary); transform-origin: center; animation: band-learn 6s ease-in-out infinite; }
.bands-row i:nth-child(1) { animation-delay: 0s; }
.bands-row i:nth-child(2) { animation-delay: 0.12s; }
.bands-row i:nth-child(3) { animation-delay: 0.24s; }
.bands-row i:nth-child(4) { animation-delay: 0.36s; }
.bands-row i:nth-child(5) { animation-delay: 0.48s; }
.bands-row i:nth-child(6) { animation-delay: 0.6s; }
.bands-row i:nth-child(7) { animation-delay: 0.72s; }
.bands-row i:nth-child(8) { animation-delay: 0.84s; }
.bands-row i:nth-child(9) { animation-delay: 0.96s; }
.bands-row i:nth-child(10) { animation-delay: 1.08s; }
.bands-scale {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 0.4688rem;
  color: var(--muted-foreground);
  text-align: center;
}



/* ---------- footer ------------------------------------------------------ */
.site-footer { border-top: 1px solid var(--hairline); padding-block: 2.5rem; }
.site-footer .inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.5rem; }
.footer-brand { display: inline-flex; align-items: center; gap: 0.8rem; }
.footer-tools { display: inline-flex; align-items: center; gap: 0.45rem; }
.social-link, .kvr-link {
  display: inline-grid;
  place-items: center;
  height: 36px;
  color: var(--muted-foreground);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.social-link { width: 36px; }
.kvr-link { width: 42px; }
.social-link svg { width: 19px; height: 19px; }
.kvr-link svg { width: 28px; height: 18px; }
.social-link:hover, .social-link:focus-visible, .kvr-link:hover, .kvr-link:focus-visible { color: var(--foreground); border-color: var(--border); }
.site-footer nav ul { display: flex; flex-wrap: wrap; gap: 1rem; }
.site-footer nav a {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  display: inline-block;
  padding-block: 0.6rem;
}
.site-footer nav a:hover, .site-footer nav a:focus-visible { color: var(--foreground); }
.site-footer .copy { font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.1em; color: var(--muted-foreground); }

/* ---------- CSP-safe helpers -------------------------------------------
   The site is served under `style-src 'self'` with no 'unsafe-inline', so the
   browser drops every style="" attribute in the markup. Anything that would
   otherwise have been an inline style has to live here instead. The build
   validator asserts that no page ships an inline style attribute, because a
   dropped one fails silently — the page still renders, just wrongly. */

/* logo mark: the SVG paints with currentColor */
.logo-mark svg { color: var(--primary); }


/* spacing steps between blocks inside a section */
.mt-sm { margin-top: 1rem; }
.mt-md { margin-top: 1.5rem; }
.mt-lg { margin-top: 1.75rem; }
.mt-xl { margin-top: 2rem; }
.mt-xxl { margin-top: 3rem; }

.sha { font-family: var(--font-mono); font-size: 0.6875rem; overflow-wrap: anywhere; }
.accent-text { color: var(--primary-text); }

/* system page: colour swatches */
.swatch { overflow: hidden; }
.swatch-sample { display: block; height: 74px; border-bottom: 1px solid var(--hairline); }
.swatch-body { display: flex; }
.swatch-body .idx { color: var(--foreground); }
.swatch-note { color: var(--muted-foreground); font-size: 0.875rem; }
.swatch--background { background: var(--background); }
.swatch--surface-raised { background: var(--surface-raised); }
.swatch--surface-overlay { background: var(--surface-overlay); }
.swatch--surface-control { background: var(--surface-control); }
.swatch--primary { background: var(--primary); }
.swatch--primary-deep { background: var(--primary-deep); }
.swatch--destructive { background: var(--destructive); }

/* ---------- motion ------------------------------------------------------ */
/* ---------- the per-stage oversampling figure ---------------------------
   A figure the reader can move rather than one they watch. The dial is the
   single global factor most plug-ins offer; the four rows are the stages the
   note names. A row below its rate is wrong in the way that matters, so it is
   the one colour on this site that means trouble; a row above it is merely
   wasteful and stays quiet. */
.osfig {
  margin: 2rem 0 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-raised);
  padding: 1.5rem;
  display: grid;
  gap: 1.1rem;
}
.osfig-head {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--muted-foreground);
}
.osfig-dial, .figdial { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.osfig-pick, .figdial-pick {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  min-height: 40px;
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-control);
  color: var(--muted-foreground);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
.osfig-pick:hover, .figdial-pick:hover { border-color: var(--primary); color: var(--foreground); }
.osfig-pick:focus-visible, .figdial-pick:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.osfig-pick[aria-pressed="true"], .figdial-pick[aria-pressed="true"] {
  border-color: var(--primary);
  background: var(--surface-overlay);
  color: var(--foreground);
}
.osfig-pick--shipped { margin-left: auto; text-transform: uppercase; }

.osfig-stages { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.osfig-stage {
  display: grid;
  grid-template-columns: minmax(0, 10rem) minmax(0, 1fr) minmax(3.5rem, auto);
  grid-template-areas: "k lane rate" "detail detail verdict";
  align-items: center;
  gap: 0.35rem 0.9rem;
  border: 1px solid var(--hairline);
  border-left: 2px solid transparent;
  border-radius: 8px;
  background: var(--surface-control);
  padding: 0.7rem 0.85rem;
  transition: border-color var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out);
}
.osfig-k { grid-area: k; font-size: 0.9375rem; color: var(--foreground); }
.osfig-rate {
  grid-area: rate;
  font-family: var(--font-mono);
  font-size: 0.875rem;
  color: var(--foreground);
  text-align: right;
}
.osfig-detail {
  grid-area: detail;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.osfig-verdict {
  grid-area: verdict;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  text-align: right;
  white-space: nowrap;
}

/* The lane is the running rate drawn as sample ticks: the point of the figure
   is that 16x is four times denser than 4x, not that it is a bigger number.
   The tick spacing animates between choices, so the density is something the
   reader watches change rather than has to compare from memory. */
.osfig-lane {
  grid-area: lane;
  height: 22px;
  border-radius: 4px;
  background-image: linear-gradient(to right, var(--muted-foreground) 1px, transparent 1px);
  background-repeat: repeat-x;
  background-position: left center;
  background-size: 50% 14px;
  opacity: 0.55;
  transition: background-size var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
}
.osfig-lane--1 { background-size: 100% 14px; }
.osfig-lane--2 { background-size: 50% 14px; }
.osfig-lane--4 { background-size: 25% 14px; }
.osfig-lane--8 { background-size: 12.5% 14px; }
.osfig-lane--16 { background-size: 6.25% 14px; }

.osfig-stage[data-state="ok"] { border-left-color: var(--primary); }
.osfig-stage[data-state="ok"] .osfig-verdict { color: var(--primary-text); }
.osfig-stage[data-state="ok"] .osfig-lane { opacity: 1; background-image: linear-gradient(to right, var(--primary) 1px, transparent 1px); }
.osfig-stage[data-state="low"] { border-left-color: var(--destructive); background: var(--surface-overlay); }
.osfig-stage[data-state="low"] .osfig-verdict { color: var(--destructive); }
.osfig-stage[data-state="high"] .osfig-verdict { color: var(--muted-foreground); }
.osfig-stage[data-state="high"] .osfig-lane { opacity: 0.35; }

.osfig-summary { margin: 0; font-size: 0.9375rem; color: var(--muted-foreground); }

@media (max-width: 640px) {
  /* On a phone the verdict takes its own line rather than sharing one with
     the stage's detail: "more than needed" and "polyBLAMP transition
     correction" do not fit on 400 pixels together, and a row that scrolls
     sideways is not a row anyone reads. */
  .osfig-stage {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "k rate" "lane lane" "detail detail" "verdict verdict";
  }
  .osfig-verdict { text-align: left; white-space: normal; }
  .osfig-pick--shipped { margin-left: 0; }
}

/* ---------- what EXPECTED TP is derived from ---------------------------- */
.tpfig, .latfig {
  margin: 2rem 0 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-raised);
  padding: 1.5rem;
  display: grid;
  gap: 1.1rem;
}
.tpfig-dials { display: grid; gap: 1rem; }
@media (min-width: 720px) { .tpfig-dials { grid-template-columns: 1fr 1fr; } }
.tpfig-dial { display: grid; gap: 0.5rem; align-content: start; }
.tpfig-legend {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.tpfig-sum { margin: 0; display: grid; gap: 0.4rem; }
.tpfig-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  border: 1px solid var(--hairline);
  border-left: 2px solid transparent;
  border-radius: 8px;
  background: var(--surface-control);
  padding: 0.65rem 0.85rem;
}
.tpfig-row dt { font-size: 0.9375rem; color: var(--muted-foreground); }
.tpfig-row dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.875rem;
  color: var(--foreground);
  text-align: right;
}
/* The derived readout is the one the note is about, so it is the one the eye
   lands on; the row underneath is the thing this figure cannot tell you, and
   it stays deliberately blank-faced. */
.tpfig-row--out { border-left-color: var(--primary); }
.tpfig-row--out dd { color: var(--primary-text); }
.tpfig-row--unknown dd { color: var(--muted-foreground); font-family: var(--font-body); }

/* ---------- what a reported latency makes the host do ------------------- */
.latfig-tracks { display: grid; gap: 0.5rem; }
.latfig-track {
  display: grid;
  grid-template-columns: minmax(0, 11rem) minmax(0, 1fr) minmax(8rem, auto);
  align-items: center;
  gap: 0.9rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface-control);
  padding: 0.7rem 0.85rem;
}
.latfig-name { font-size: 0.9375rem; color: var(--foreground); }
.latfig-shift {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  text-align: right;
}
.latfig-lane {
  position: relative;
  display: block;
  height: 26px;
  border-radius: 4px;
  background: var(--surface-overlay);
  overflow: hidden;
}
.latfig-block {
  position: absolute;
  inset: 4px auto 4px 0;
  width: 34%;
  border-radius: 3px;
  background: color-mix(in oklab, var(--primary) 55%, transparent);
  transition: transform var(--dur-base) var(--ease-out);
}
.latfig-block--fixed { background: color-mix(in oklab, var(--primary) 35%, transparent); }
/* The shift is drawn to the same scale as the number: 2048 samples is the
   full lane, so 512 moves a quarter of it. */
.latfig[data-latency="0"] .latfig-block--shifted { transform: none; }
.latfig[data-latency="64"] .latfig-block--shifted { transform: translateX(2%); }
.latfig[data-latency="512"] .latfig-block--shifted { transform: translateX(15.5%); }
.latfig[data-latency="2048"] .latfig-block--shifted { transform: translateX(62%); }

@media (max-width: 640px) {
  .latfig-track {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.4rem;
  }
  .latfig-shift { text-align: left; }
  .tpfig-row { flex-direction: column; align-items: flex-start; gap: 0.25rem; }
  .tpfig-row dd { text-align: left; }
}

/* ---------- two delay lines ---------------------------------------------
   Two lanes, one window: the window is however long the two divisions take to
   meet again, so the taps always fill the width and the eye compares patterns
   rather than lengths. */
.dlfig, .archfig {
  margin: 2rem 0 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-raised);
  padding: 1.5rem;
  display: grid;
  gap: 1.1rem;
}
/* Seven divisions do not fit in half a column, and a dial that wraps onto a
   second row stops reading as one row of choices. Each side gets the full
   width instead. */
.dlfig-dials { display: grid; gap: 1rem; }
.dlfig-plot {
  display: grid;
  gap: 6px;
  border-radius: 8px;
  background: var(--surface-control);
  padding: 0.85rem;
}
.dlfig-lane {
  position: relative;
  display: block;
  height: 34px;
  border-radius: 4px;
  background: var(--surface-overlay);
}
.dlfig-tap {
  position: absolute;
  top: 6px;
  bottom: 6px;
  left: var(--at);
  width: 3px;
  border-radius: 2px;
  background: var(--primary);
}
.dlfig-lane--right .dlfig-tap { background: color-mix(in oklab, var(--primary) 55%, transparent); }
/* Locked to one time, the two lanes are the same lane twice, and saying so
   quietly is more honest than drawing a difference that is not there. */
.dlfig[data-mode="one"] .dlfig-lane--right .dlfig-tap { background: color-mix(in oklab, var(--primary) 30%, transparent); }
.dlfig-readout {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

/* ---------- what is inside the binary ---------------------------------- */
.archfig-products { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.archfig-product {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, auto) minmax(0, 1fr);
  grid-template-areas: "name slices verdict" "kind kind verdict";
  align-items: center;
  gap: 0.3rem 0.9rem;
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--primary);
  border-radius: 8px;
  background: var(--surface-control);
  padding: 0.7rem 0.85rem;
  transition: border-color var(--dur-base) var(--ease-out);
}
.archfig-name { grid-area: name; font-size: 0.9375rem; color: var(--foreground); }
.archfig-slices { grid-area: slices; display: flex; gap: 0.35rem; }
.archfig-slice {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  border: 1px solid var(--hairline);
  border-radius: 4px;
  padding: 0.15rem 0.45rem;
  color: var(--primary-text);
}
.archfig-kind {
  grid-area: kind;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.archfig-verdict {
  grid-area: verdict;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--primary-text);
  text-align: right;
}
/* A product that cannot load is the one fact a reader on an Intel Mac needs
   before they download anything, so it is the one that is allowed to shout. */
.archfig-product[data-verdict="blocked"] { border-left-color: var(--destructive); }
.archfig-product[data-verdict="blocked"] .archfig-verdict { color: var(--destructive); }
.archfig-product[data-verdict="blocked"] .archfig-slice { color: var(--muted-foreground); }

@media (max-width: 640px) {
  .archfig-product {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "name" "slices" "kind" "verdict";
  }
  .archfig-verdict { text-align: left; }
}

/* ---------- what lives inside the feedback loop -------------------------
   One row per repeat, and a mark for every pass through the filter. The rows
   are the argument: after the delay they are all one mark long, inside the
   loop they grow, and the growth is the thing the note is describing. */
.loopfig {
  margin: 2rem 0 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-raised);
  padding: 1.5rem;
  display: grid;
  gap: 1.1rem;
}
.loopfig-dials { display: grid; gap: 0.75rem; }
.loopfig-repeats { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.loopfig-repeat {
  display: grid;
  grid-template-columns: minmax(0, 6rem) minmax(0, 1fr) minmax(0, auto);
  align-items: center;
  gap: 0.9rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface-control);
  padding: 0.55rem 0.85rem;
}
.loopfig-n { font-family: var(--font-mono); font-size: 0.75rem; color: var(--foreground); }
.loopfig-marks { display: flex; flex-wrap: wrap; gap: 4px; min-width: 0; }
.loopfig-mark {
  display: block;
  width: 10px;
  height: 18px;
  border-radius: 2px;
  background: color-mix(in oklab, var(--primary) 60%, transparent);
}
.loopfig-passes {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  text-align: right;
  white-space: nowrap;
}

@media (max-width: 640px) {
  .loopfig-repeat {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.35rem;
  }
  .loopfig-passes { text-align: left; white-space: normal; }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}
@keyframes pulse-dot {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}
@keyframes stage-arm {
  0%, 100% { border-top-color: transparent; background: var(--surface-control); }
  4%       { border-top-color: var(--primary); background: var(--surface-overlay); }
  13%      { border-top-color: color-mix(in oklab, var(--primary) 45%, transparent); background: var(--surface-overlay); }
  22%      { border-top-color: transparent; background: var(--surface-control); }
}
@keyframes band-learn {
  0%, 100% { transform: translateY(0) scaleX(0.35); opacity: 0.4; }
  18%      { transform: translateY(-9px) scaleX(1); opacity: 1; }
  55%      { transform: translateY(-3px) scaleX(1); opacity: 1; }
  82%      { transform: translateY(0) scaleX(1); opacity: 0.75; }
}
@keyframes corr-settle {
  0%, 100% { right: 6%; }
  45%      { right: 2%; }
  70%      { right: 11%; }
}
.rise { animation: rise 0.7s cubic-bezier(0.16, 1, 0.3, 1) both; }
.rise-1 { animation-delay: 60ms; }
.rise-2 { animation-delay: 120ms; }
.rise-3 { animation-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  /* The rail and the band read as a still frame: every stage armed, every
     band at its learned position, nothing moving. */
  .rail .st, .rack .slot { border-top-color: color-mix(in oklab, var(--primary) 45%, transparent); }
  .meters-row i { transform: scaleX(0.7); }
  /* The oversampling figure is driven by the reader, not by a timer, so it
     keeps working here; only the easing between states goes. */
  .osfig-lane, .osfig-stage, .osfig-pick,
  .figdial-pick, .latfig-block, .archfig-product { transition-duration: 0.001ms !important; }
  .bands-row i { transform: scaleX(1); opacity: 1; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---------- forms -------------------------------------------------------
   The site's input surfaces: the support form, the MixRack release notice,
   and the MixRack tester-interest form. Controls inherit the panel/token set
   rather than introducing a second visual language: same radius, same
   hairline, same focus ring as the buttons beside them. All three forms
   share the shell so none can drift into a look of its own. */
.support-form,
.notify-form,
.tester-form { display: grid; gap: 1.5rem; padding: 1.75rem; }
/* One short field does not want the full 1100px shell; the support form and
   the tester form have a grid of them and do. */
.notify-form { max-width: 34rem; }
.form-grid { display: grid; gap: 1.25rem; }
@media (min-width: 700px) {
  .form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.form-row { display: grid; gap: 0.45rem; align-content: start; }
.form-label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.form-label .req { color: var(--primary-text); margin-left: 0.4rem; }
.field {
  width: 100%;
  min-height: 44px;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--input);
  border-radius: 8px;
  background: var(--surface-control);
  color: var(--foreground);
  font-family: var(--font-body);
  font-size: 1rem;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.field::placeholder { color: var(--muted-foreground); }
.field:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; border-color: var(--primary); }
.field-mono { font-family: var(--font-mono); font-size: 0.875rem; }
.field-area { min-height: 160px; resize: vertical; line-height: 1.6; }
.form-hint { margin: 0; font-size: 0.8125rem; color: var(--muted-foreground); }
.form-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }

/* Empty until the submit says something, so it never reserves a blank line. */
.form-status { margin: 0; font-size: 0.9375rem; color: var(--muted-foreground); }
.form-status:empty { display: none; }
.form-status--sent { color: var(--primary-text); }
.form-status--error { color: var(--destructive); }

.form-note { margin: 1.25rem 0 0; font-size: 0.9375rem; color: var(--muted-foreground); }

/* The honeypot: reachable by a bot reading the DOM, never by a person.
   Moved out of view rather than display:none, which some bots skip. */
.form-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- consent banner ----------------------------------------------
   Drawn by consent.js only for visitors who have not chosen yet. It sits
   above the page rather than pushing it, and uses the raised surface so it
   reads as a control layer, not as content. The buttons reuse .btn and
   .btn-primary unchanged: the design system sizes those for AA contrast at
   the accent fill, and shrinking them here would quietly break it. */
.consent-banner {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 40;
  background: var(--surface-raised);
  border-top: 1px solid var(--border);
  box-shadow: var(--shadow-float);
}
.consent-inner {
  max-width: var(--shell);
  margin-inline: auto;
  padding: 1.25rem var(--gutter);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.consent-text {
  margin: 0;
  max-width: 52ch;
  font-size: 0.9375rem;
  color: var(--muted-foreground);
}
.consent-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.consent-btn { flex: 0 0 auto; }

/* The withdraw control joins the footer navigation, so it inherits that
   list's mono/uppercase treatment rather than looking like a stray button. */
.consent-reopen {
  appearance: none;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  padding: 0.6rem 0;
}
.consent-reopen:hover, .consent-reopen:focus-visible { color: var(--foreground); }

/* ---------- A/B listening -----------------------------------------------
   Two renders of one passage, matched in loudness, switched instantly. The
   card is a panel with four bands: title, the plug-in capture, the transport,
   and the note. The capture is a real screenshot cropped to the plug-in's own
   window, so the only frame around it is this one.

   Both readouts start empty. They are written by src/ab.js on every animation
   frame while a passage plays, and a bar with no declared width would sit at
   full scale reporting a signal that is not there. */
.ab-card { overflow: hidden; display: flex; flex-direction: column; }
.ab-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.7rem 1.15rem;
  border-bottom: 1px solid var(--hairline);
  background: color-mix(in oklab, var(--surface-overlay) 60%, transparent);
}
.ab-title, .ab-flag {
  font-family: var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.ab-title { font-size: 0.6875rem; color: var(--foreground); }
.ab-flag { font-size: 0.625rem; color: var(--muted-foreground); }
.ab-shot { display: block; width: 100%; height: auto; background: var(--background); }
.ab-transport {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  padding: 1.15rem;
}
.ab-play { flex: none; }
.ab-takes {
  display: flex;
  flex: none;
  gap: 0.25rem;
  padding: 0.25rem;
  border: 1px solid var(--border);
  border-radius: 0.6rem;
  background: var(--background);
}
.ab-takes button {
  border: 0;
  border-radius: 0.4rem;
  padding: 0.4rem 0.85rem;
  background: none;
  color: var(--muted-foreground);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.ab-takes button[aria-pressed="true"] { background: var(--surface-control); color: var(--foreground); }
.ab-takes button:hover { color: var(--foreground); }
.ab-readout { flex: 1 1 12rem; min-width: 0; }
.ab-meter {
  height: 8px;
  border-radius: 999px;
  background: var(--surface-control);
  overflow: hidden;
}
.ab-meter-fill {
  display: block;
  width: 0;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
  transition: width 75ms linear;
}
.ab-scale {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 0.5rem;
}
.ab-key {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.ab-progress { flex: 1 1 0; max-width: 9rem; height: 1px; background: var(--border); }
.ab-progress-fill { display: block; width: 0; height: 1px; background: var(--primary); }
.ab-foot {
  margin: 0;
  padding: 0.85rem 1.15rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.75rem;
  color: var(--muted-foreground);
  max-width: none;
}
/* Playback needs a gesture and can still be refused. Say so rather than
   leaving a dead button. */
.ab-card.is-blocked .ab-flag::after { content: " · playback blocked"; }
@media (prefers-reduced-motion: reduce) {
  .ab-meter-fill, .ab-progress-fill { transition: none; }
}

/* ---------- community --------------------------------------------------- */
.community-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  margin-top: 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.05em;
}
.community-nav a {
  color: var(--muted-foreground);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}
.community-nav a:hover,
.community-nav a:focus-visible {
  color: var(--foreground);
}
.community-nav a[aria-current="page"] {
  color: var(--primary-text);
  font-weight: 500;
}

.code-block {
  margin: 0.75rem 0;
  padding: 0.85rem 1.15rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-raised);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--foreground);
  overflow-x: auto;
}
.code-block code {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}
.mono-note {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--muted-foreground);
  line-height: 1.5;
}

.actionable-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-top: 1rem;
}
.actionable-list li {
  position: relative;
  padding-left: 1.25rem;
  color: var(--foreground);
  font-size: 1rem;
  line-height: 1.5;
}
.actionable-list li::before {
  content: "—";
  position: absolute;
  left: 0;
  color: var(--muted-foreground);
}

.product-spec-card {
  padding: 1.75rem;
}
.product-spec-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 1rem;
}
.product-spec-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.25rem;
}
.spec-full {
  grid-column: 1 / -1;
}
.host-card {
  padding: 1.5rem 1.75rem;
}
.host-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.host-card-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.125rem;
}
.host-card p {
  margin: 0.75rem 0 0;
  color: var(--muted-foreground);
  font-size: 0.875rem;
  line-height: 1.5;
}

.issue-card {
  padding: 1.5rem 1.75rem;
}
.issue-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}
.issue-card-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.125rem;
}
.issue-card > p {
  margin: 0.75rem 0 0;
  color: var(--muted-foreground);
  font-size: 0.9375rem;
  line-height: 1.5;
}
.issue-workaround {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--border);
}
.issue-workaround .issue-label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.issue-workaround p {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--foreground);
}

.roadmap-card {
  padding: 1.5rem 1.75rem;
}
.roadmap-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.roadmap-card-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.125rem;
}
.roadmap-card p {
  margin: 0.75rem 0 0;
  color: var(--muted-foreground);
  font-size: 0.9375rem;
  line-height: 1.5;
}

/* MixRack preview video. Until the visitor presses play the card holds only a
   poster inside a button; src/video.js swaps in the <video> on click. Same-origin,
   so media-src inherits default-src 'self'. Width and height on the image and the
   player, plus the aspect ratio here, reserve the frame before any byte arrives. */
.video-card { margin: 0; overflow: hidden; }
.video-facade { position: relative; display: block; width: 100%; padding: 0; border: 0; background: #0a0d0e; cursor: pointer; color: inherit; font: inherit; }
.video-poster, .video-player { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; background: #0a0d0e; }
.video-play {
  position: absolute; left: 50%; bottom: 14%; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 0.65rem; padding: 0.7rem 1.15rem 0.7rem 0.8rem;
  border-radius: 999px; border: 1px solid var(--border); background: var(--surface-overlay);
  font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 0.8125rem; letter-spacing: 0.06em; white-space: nowrap;
}
.video-play-icon { width: 1.9rem; height: 1.9rem; border-radius: 50%; background: var(--primary); position: relative; flex: none; }
.video-play-icon::after { content: ""; position: absolute; left: 0.72rem; top: 0.55rem; border-style: solid; border-width: 0.4rem 0 0.4rem 0.64rem; border-color: transparent transparent transparent var(--primary-foreground, #ecf3f5); }
.video-facade:hover .video-play, .video-facade:focus-visible .video-play { border-color: var(--primary); }
.video-facade:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.video-caption { margin: 0; padding: 0.85rem 1.15rem; font-size: 0.8125rem; color: var(--muted-foreground); border-top: 1px solid var(--border); }
@media (max-width: 560px) {
  .video-play { bottom: 8%; gap: 0.45rem; padding: 0.35rem 0.75rem 0.35rem 0.4rem; font-size: 0.75rem; }
  .video-play-label { display: none; }
  .video-play-icon { width: 1.5rem; height: 1.5rem; }
  .video-play-icon::after { left: 0.56rem; top: 0.42rem; border-width: 0.32rem 0 0.32rem 0.52rem; }
}
