/* ===========================================================================
   franken_ocr — franken-ocr.com
   Hand-written design system. No framework, no build step, no CDN.
   Self-hosted variable fonts (Inter + JetBrains Mono, both SIL OFL 1.1).
   =========================================================================== */

@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-latin-wght-normal.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("../fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2-variations");
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ------------------------------------------------------------------ tokens */
:root {
  /* Surfaces: near-black with a faint green cast, so emerald reads as light
     rather than as paint. */
  --bg: #060b09;
  --bg-deep: #030706;
  --surface: rgba(255, 255, 255, 0.022);
  --surface-strong: rgba(3, 7, 6, 0.72);
  --inset: rgba(2, 6, 5, 0.66);

  /* Ink */
  --fg: #e8eef2;
  --fg-mid: #cbd5e1;
  --fg-dim: #94a3b8;
  /* #64748b measured 4.16:1 on this background — below AA for body text.
     #748496 is 5.18:1 and still reads as the quietest voice on the page. */
  --fg-faint: #748496;

  /* Semantics — fixed meanings, used identically in CSS and in every viz. */
  --accent: #34d399;   /* active / ours / success */
  --accent-deep: #059669;
  --accent-ink: #04140d;
  --amber: #fbbf24;    /* warning / threshold / "here is the tail" */
  --red: #f87171;      /* failure / skipped / refused */
  --ref: #7e8e9f;      /* the reference we are measured against */
  --violet: #a78bfa;   /* structure / shapes / metadata */

  --line: rgba(255, 255, 255, 0.07);
  --line-strong: rgba(52, 211, 153, 0.26);

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    Consolas, monospace;

  --r-sm: 8px;
  --r: 12px;
  --r-lg: 18px;
  --r-pill: 9999px;

  --shadow: 0 14px 44px rgba(0, 0, 0, 0.45);
  --glow: 0 0 30px rgba(16, 185, 129, 0.09);

  --wrap: 1180px;
  --header-h: 68px;
}

* { box-sizing: border-box; }

/* app.js drives the whole playground with the `hidden` attribute, and several
   elements below are given an explicit `display` (grid/inline-flex). A later
   display rule beats the UA's `[hidden] { display: none }`, so without this the
   consent box, the progress bar, the cancel button and the empty result panel
   would all be permanently on screen. This line is load-bearing. */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  /* Orbs and wide viz frames must never widen the document. */
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  overflow-x: hidden;
  background: var(--bg);
  color: var(--fg);
  font: 400 16px/1.65 var(--sans);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

/* A steady base wash so the page never looks like a flat rectangle even with
   JS off — the orbs below are decoration on top of this, not instead of it. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(1100px 620px at 12% -6%, rgba(16, 185, 129, 0.13), transparent 68%),
    radial-gradient(900px 700px at 96% 22%, rgba(4, 66, 37, 0.34), transparent 70%),
    radial-gradient(760px 560px at 40% 108%, rgba(251, 191, 36, 0.05), transparent 70%);
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

:focus-visible {
  outline: 2px solid rgba(52, 211, 153, 0.75);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection { background: rgba(52, 211, 153, 0.28); color: #fff; }

code {
  font-family: var(--mono);
  font-size: 0.88em;
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 0.08em 0.34em;
  color: var(--fg-mid);
}
pre code { background: none; border: 0; padding: 0; }

h1, h2, h3 { font-weight: 800; letter-spacing: -0.02em; margin: 0; }

/* Spacing utilities — the only two the page needs. */
.mt { margin-top: 1.5rem; }
.viz-intro { margin: 0.9rem 0 1.5rem; }
.viz-foot { margin-top: 1.3rem; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 800;
  padding: 0.7rem 1.2rem;
  border-radius: 0 0 var(--r) 0;
}
.skip-link:focus { left: 0; text-decoration: none; }

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

/* ------------------------------------------------------------------ header */
.top {
  position: fixed;
  top: 0.7rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 50;
  width: min(96%, var(--wrap));
  height: var(--header-h);
  padding: 0 0.75rem 0 1rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  transition: background 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease,
    backdrop-filter 0.35s ease;
}
.top.stuck {
  background: rgba(3, 7, 6, 0.76);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  border-color: rgba(52, 211, 153, 0.2);
  box-shadow: var(--shadow), var(--glow);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--fg);
  flex: 0 0 auto;
}
.brand:hover { text-decoration: none; }
.brand-mark {
  position: relative;
  width: 38px; height: 38px;
  flex: 0 0 38px;
  border-radius: 11px;
  background: linear-gradient(140deg, #04351f, #34d399);
  display: grid; place-items: center;
  font: 800 19px/1 var(--mono);
  color: #04140d;
  box-shadow: 0 0 22px rgba(16, 185, 129, 0.28);
}
/* Two stitched studs — the family mark. */
.brand-mark::before, .brand-mark::after {
  content: "";
  position: absolute;
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background:
    linear-gradient(45deg, transparent 43%, #0f172a 43%, #0f172a 57%, transparent 57%),
    linear-gradient(-45deg, transparent 43%, #0f172a 43%, #0f172a 57%, transparent 57%),
    radial-gradient(circle at 34% 30%, #64748b, #020617 72%);
}
.brand-mark::before { left: -4px; top: -4px; }
.brand-mark::after { right: -4px; bottom: -4px; }
.brand-text { line-height: 1.1; }
.brand-name { font: 800 15px/1 var(--mono); letter-spacing: -0.01em; }
.brand-tag {
  margin-top: 4px;
  font: 800 8px/1 var(--sans);
  letter-spacing: 0.22em;
  color: #10b981;
  text-transform: uppercase;
}

.top nav { display: none; gap: 0.1rem; align-items: center; }
.top nav a {
  padding: 0.5rem 0.7rem;
  border-radius: var(--r-pill);
  font: 800 10px/1 var(--sans);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-dim);
  transition: color 0.18s, background 0.18s;
}
.top nav a:hover { color: var(--accent); background: rgba(16, 185, 129, 0.1); text-decoration: none; }
.top nav a[aria-current="true"] { color: var(--accent); background: rgba(16, 185, 129, 0.13); }
@media (min-width: 1024px) { .top nav { display: flex; } }

/* -------------------------------------------------------------- primitives */
.wrap { width: min(100% - 2rem, var(--wrap)); margin-inline: auto; }
.wrap-narrow { width: min(100% - 2rem, 780px); margin-inline: auto; }

section { padding: 5rem 0; scroll-margin-top: 96px; position: relative; }
section:first-of-type { padding-top: 0; }
/* A hairline rule between sections: the page is one long dark column, and
   without a beat every panel edge reads as the same weight. */
main section + section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(100% - 2rem, var(--wrap));
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line) 22%, var(--line) 78%, transparent);
}
footer { scroll-margin-top: 92px; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  font: 800 10px/1 var(--sans);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: #10b981;
  margin-bottom: 1rem;
}
.eyebrow::before, .eyebrow::after {
  content: "";
  height: 1px; width: 30px;
  background: rgba(52, 211, 153, 0.4);
}

.section-head { text-align: center; margin-bottom: 3rem; }
.section-head h2 {
  font-size: clamp(2rem, 5.2vw, 3.4rem);
  line-height: 1.06;
  margin-bottom: 1.1rem;
}
.section-head h2 em { color: var(--accent); font-style: normal; }
.section-head .lede {
  color: var(--fg-dim);
  font-size: clamp(1rem, 1.6vw, 1.09rem);
  max-width: 46rem;
  margin: 0 auto;
}
.section-head .lede strong { color: var(--fg-mid); font-weight: 600; }

.note { color: var(--fg-dim); font-size: 0.95rem; line-height: 1.68; margin: 0; }
.note + .note { margin-top: 0.85rem; }
.note strong { color: var(--fg-mid); font-weight: 600; }
.note em { color: var(--fg-mid); font-style: italic; }
.micro { color: var(--fg-faint); font-size: 0.82rem; line-height: 1.6; margin: 0; }
.mono { font-family: var(--mono); }
.hl { color: var(--accent); }

.chips { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: 1px solid rgba(52, 211, 153, 0.2);
  border-radius: var(--r-pill);
  background: rgba(16, 185, 129, 0.05);
  color: var(--fg-dim);
  font: 700 0.74rem/1 var(--mono);
  padding: 0.42rem 0.85rem;
}
.chip b { color: var(--accent); font-weight: 800; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.025);
  color: var(--fg-dim);
  font: 800 0.76rem/1 var(--sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.68rem 1.2rem;
  transition: color 0.2s, border-color 0.2s, background 0.2s, transform 0.2s;
}
.btn:hover { color: var(--accent); border-color: rgba(52, 211, 153, 0.42); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.38; cursor: not-allowed; }
.btn:disabled:hover { color: var(--fg-dim); border-color: rgba(255, 255, 255, 0.1); }
.btn.primary {
  background: linear-gradient(135deg, #04351f, var(--accent-deep));
  border-color: rgba(52, 211, 153, 0.38);
  color: #eafff5;
}
.btn.primary:hover { color: #fff; border-color: var(--accent); }
.btn.subtle {
  background: transparent;
  border-color: transparent;
  color: var(--fg-faint);
  padding-inline: 0.6rem;
  text-transform: none;
  letter-spacing: 0.01em;
  font-weight: 600;
}
.btn.subtle:hover { color: var(--accent); background: rgba(16, 185, 129, 0.07); }
.btn.danger { border-color: rgba(248, 113, 113, 0.32); color: var(--red); }
.btn.danger:hover { color: var(--red); border-color: rgba(248, 113, 113, 0.6); background: rgba(248, 113, 113, 0.06); }

select.btn {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  font-size: 0.86rem;
  font-family: var(--sans);
  color: var(--fg-mid);
  border-radius: var(--r);
  padding-right: 2.1rem;
  max-width: 100%;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--fg-dim) 50%),
    linear-gradient(135deg, var(--fg-dim) 50%, transparent 50%);
  background-position: right 1.1rem top 52%, right 0.75rem top 52%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
select.btn option { background: #0b1512; color: var(--fg); }
@media (max-width: 560px) {
  /* The model names are long; on a phone the select owns its own line. */
  select.btn { flex: 1 1 100%; width: 100%; }
  .row > .btn { flex: 1 1 auto; }
}

/* Panels — the recurring surface for every visualization and the playground. */
.panel {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-strong);
  padding: clamp(1.1rem, 2.6vw, 2rem);
}
.panel::before, .panel::after {
  content: "";
  position: absolute;
  width: 13px; height: 13px;
  border-radius: 50%;
  z-index: 3;
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 0 9px rgba(16, 185, 129, 0.22);
  background:
    linear-gradient(45deg, transparent 43%, #1e293b 43%, #1e293b 57%, transparent 57%),
    linear-gradient(-45deg, transparent 43%, #1e293b 43%, #1e293b 57%, transparent 57%),
    radial-gradient(circle at 34% 30%, #475569, #0b1220 72%);
}
.panel::before { left: -6px; top: -6px; }
.panel::after { right: -6px; bottom: -6px; }

.card {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-strong);
  padding: 1.5rem;
  transition: transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
.card:hover {
  transform: translateY(-3px);
  border-color: rgba(52, 211, 153, 0.28);
  box-shadow: var(--shadow), var(--glow);
}
.card.accent { border-color: rgba(52, 211, 153, 0.26); box-shadow: inset 0 0 46px rgba(16, 185, 129, 0.06); }
.card-num {
  position: absolute;
  top: 0.2rem; right: 0.8rem;
  font: 800 2.7rem/1 var(--mono);
  color: rgba(52, 211, 153, 0.1);
  pointer-events: none;
}
.card h3 { font-size: 1.02rem; margin-bottom: 0.6rem; color: var(--fg); }
.card.stat-card { text-align: center; display: grid; align-content: start; }
.card.stat-card .label { margin-bottom: 0.7rem; }

.label {
  font: 800 0.68rem/1 var(--sans);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #10b981;
}
.label + * { margin-top: 0.8rem; }

.grid { display: grid; gap: 1.2rem; }
.grid > * { min-width: 0; }
.grid-2, .grid-3 { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 680px) { .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 940px) { .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.stack { display: grid; gap: 1.2rem; }
.stack > * { min-width: 0; }
.row { display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; min-width: 0; }
.row > * { min-width: 0; }
.row.spread { justify-content: space-between; }

/* Scroll reveals — the no-JS/no-motion fallback is "fully visible". */
.reveal { opacity: 1; }
html.js .reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s cubic-bezier(0.22, 0.8, 0.2, 1),
    transform 0.7s cubic-bezier(0.22, 0.8, 0.2, 1);
}
html.js .reveal.revealed { opacity: 1; transform: none; }

/* --------------------------------------------------------------------- hero */
/* `section.hero`, not `.hero`: the hero IS the first section, so the
   `section:first-of-type { padding-top: 0 }` reset above outranks a bare class
   and the eyebrow + first H1 line end up underneath the fixed pill header.
   The two-class selector wins, and the value clears the header's own offset
   (0.7rem) plus its height plus real breathing room, at both widths. */
section.hero {
  position: relative;
  padding: calc(var(--header-h) + 5.2rem) 0 3rem;
  text-align: center;
}
.hero h1 {
  font-size: clamp(2.5rem, 7.4vw, 5.1rem);
  line-height: 1.02;
  margin-bottom: 1.4rem;
}
.hero h1 em { color: var(--accent); font-style: normal; }
.hero .lede {
  color: var(--fg-dim);
  font-size: clamp(1rem, 1.7vw, 1.14rem);
  max-width: 44rem;
  margin: 0 auto 1.9rem;
}
.hero .lede strong { color: var(--fg-mid); font-weight: 600; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 0.7rem; justify-content: center; margin-bottom: 1.8rem; }
.hero-cta .btn { padding: 0.85rem 1.6rem; font-size: 0.8rem; }
.hero-art {
  position: relative;
  margin: 2.6rem auto 0;
  max-width: 760px;
  border-radius: var(--r-lg);
  border: 1px solid rgba(52, 211, 153, 0.13);
  overflow: hidden;
  box-shadow: 0 0 70px rgba(16, 185, 129, 0.13);
}
.hero-art img { display: block; width: 100%; height: auto; }

/* --------------------------------------------------------------- playground */
.pg {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 1000px) { .pg { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.pg-col { display: grid; gap: 1.1rem; align-content: start; min-width: 0; }
.pg-step { display: grid; gap: 0.75rem; min-width: 0; }

.kv { color: var(--fg-dim); font-size: 0.83rem; font-family: var(--mono); }
.kv code { color: var(--accent); background: none; border: 0; padding: 0; }

.status {
  font: 600 0.88rem/1.5 var(--mono);
  color: var(--fg-dim);
  border-left: 2px solid var(--line);
  padding: 0.15rem 0 0.15rem 0.7rem;
  overflow-wrap: anywhere;
}
.status.ok { color: var(--accent); border-left-color: var(--accent); }
.status.warn { color: var(--amber); border-left-color: var(--amber); }
.status.err { color: var(--red); border-left-color: var(--red); }

.consent {
  border: 1px solid rgba(251, 191, 36, 0.4);
  background: rgba(251, 191, 36, 0.05);
  border-radius: var(--r);
  padding: 1rem 1.1rem;
  display: grid;
  gap: 0.85rem;
  justify-items: start;
}
.consent p { margin: 0; color: var(--fg-mid); font-size: 0.92rem; line-height: 1.6; }

/* The download bar and the recognize bar are the same two elements; they never
   run at the same time. The inset well makes the bar read as part of the panel
   rather than a floating strip, and the stage line under it is monospace so a
   ticking token count does not shuffle the text sideways. */
.progress {
  height: 9px;
  border-radius: 6px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--line);
}
#progress-bar {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--accent-deep), var(--accent));
  transition: width 0.25s ease;
}
#progress-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--inset);
  padding: 0.8rem 0.9rem;
}
#progress-text {
  font-variant-numeric: tabular-nums;
  color: var(--fg-mid);
  overflow-wrap: anywhere;
}

/* A free-text prompt for the one lane that takes one. */
.text-input {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--inset);
  color: var(--fg);
  font: 400 0.92rem/1.5 var(--sans);
  padding: 0.65rem 0.85rem;
  min-height: 44px;
}
.text-input::placeholder { color: var(--fg-faint); }
.text-input:focus { border-color: rgba(52, 211, 153, 0.42); outline-offset: 1px; }
#question-wrap { display: grid; gap: 0.5rem; }

/* Segmented Rendered / Source control. */
.seg {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.025);
  padding: 2px;
  flex: 0 0 auto;
}
.seg-btn {
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: var(--r-pill);
  color: var(--fg-faint);
  font: 800 0.68rem/1 var(--sans);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  padding: 0.5rem 0.85rem;
  transition: color 0.18s, background 0.18s;
}
.seg-btn:hover { color: var(--accent); }
.seg-btn[aria-pressed="true"] {
  background: rgba(16, 185, 129, 0.14);
  color: var(--accent);
}
@media (pointer: coarse) { .seg-btn { min-height: 44px; } }

/* The rendered view is a sandboxed iframe (no scripts, no same-origin), so the
   page cannot measure its content height. It gets a fixed frame with its own
   scroll, matching the source <pre> it replaces. */
.rendered {
  width: 100%;
  height: 24rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--inset);
  display: block;
}

.drop {
  border: 2px dashed rgba(255, 255, 255, 0.13);
  border-radius: var(--r);
  min-height: 9.5rem;
  padding: 1.1rem;
  display: grid;
  place-items: center;
  text-align: center;
  color: var(--fg-dim);
  font-size: 0.92rem;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}
.drop:hover { border-color: rgba(52, 211, 153, 0.38); }
.drop.armed { border-color: rgba(52, 211, 153, 0.42); }
.drop.over { border-color: var(--accent); background: rgba(52, 211, 153, 0.07); }
#drop-hint { display: block; max-width: 30rem; }
.preview-stack { position: relative; max-width: 100%; line-height: 0; }
.preview-stack img { max-width: 100%; height: auto; display: block; border-radius: var(--r-sm); }
.preview-stack canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }

.output {
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 1rem;
  margin: 0;
  max-height: 24rem;
  overflow: auto;
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1.6;
  color: var(--fg-mid);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.warnings { color: var(--amber); font-size: 0.83rem; margin: 0; padding-left: 1.15rem; display: grid; gap: 0.3rem; }
.warnings:empty { display: none; }
#license { font-size: 0.76rem; color: var(--fg-faint); font-family: var(--mono); }
#result-wrap { display: grid; gap: 0.9rem; }
/* The placeholder explains what will appear in the empty right-hand column, and
   retires itself the moment app.js unhides the real result. */
.pg-col:has(#result-wrap:not([hidden])) #result-placeholder { display: none; }
#progress-wrap { display: grid; gap: 0.5rem; }

.legend { display: flex; flex-wrap: wrap; gap: 1rem; font-size: 0.8rem; color: var(--fg-dim); }
.legend span { display: inline-flex; align-items: center; gap: 0.42rem; }
.legend i {
  width: 11px; height: 11px; border-radius: 3px; display: inline-block;
  border: 1.5px solid currentColor;
}
.legend .sw-ok { color: var(--accent); }
.legend .sw-skip { color: var(--red); }
.legend .sw-warn { color: var(--amber); }

/* ------------------------------------------------------------ visualizations */
/* Wide diagrams scroll inside their own box rather than widening the page. The
   right-edge fade is the affordance that says so on a phone, where there is no
   scrollbar to see. */
.viz-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.viz-wide {
  position: relative;
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 34px), transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 34px), transparent 100%);
}
@media (min-width: 1100px) {
  /* At full width nothing is clipped, so the fade would be a lie. */
  .viz-wide { -webkit-mask-image: none; mask-image: none; }
}
.viz-scroll > svg { display: block; width: 100%; height: auto; }
.viz-wide > svg { min-width: 720px; }
.viz-caption { color: var(--fg-dim); font-size: 0.86rem; text-align: center; margin: 1rem auto 0; max-width: 46rem; }

.viz-controls {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  flex-wrap: wrap;
  margin-top: 1.1rem;
}
.slider { flex: 1 1 200px; min-width: 170px; accent-color: var(--accent); height: 1.6rem; cursor: pointer; }
.readout { display: flex; align-items: baseline; gap: 0.5rem; white-space: nowrap; }
.readout-big { font: 800 1.7rem/1 var(--mono); color: var(--accent); }
.readout-dim { color: var(--fg-faint); font: 700 0.95rem/1 var(--mono); }

/* Stage / step chrome shared by the pipeline and walls steppers */
.step-head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.step-count { font: 700 0.82rem/1 var(--mono); color: var(--fg-faint); }
.step-title { font: 800 1.25rem/1.2 var(--sans); color: var(--fg); }
.step-stat { font: 700 0.9rem/1 var(--mono); color: var(--accent); margin-left: auto; }
.step-body { min-height: 11.5em; }
.stepper .step-body { min-height: 7.5em; }
.step-row { display: flex; gap: 0.9rem; margin-bottom: 0.85rem; align-items: baseline; }
.step-row:last-child { margin-bottom: 0; }
.step-kind {
  flex: 0 0 6.4rem;
  text-align: right;
  font: 800 0.66rem/1.5 var(--sans);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.step-kind.wall { color: var(--red); }
.step-kind.why { color: var(--amber); }
.step-kind.fix { color: var(--accent); }
.step-nav { display: flex; align-items: center; gap: 0.8rem; margin-top: 0.4rem; }
.step-dots { display: flex; gap: 0.45rem; margin: 0 auto; }
.step-dot {
  width: 26px; height: 26px; padding: 0;
  border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.step-dot::after {
  content: "";
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1px solid rgba(52, 211, 153, 0.45);
  transition: background 0.2s, transform 0.2s;
}
.step-dot[aria-current="true"]::after { background: var(--accent); transform: scale(1.18); }
.step-dot:hover::after { background: rgba(52, 211, 153, 0.4); }

/* Bar charts (byte budget, CER, parity) */
.bars { display: grid; gap: 0.55rem; }
/* A reference bar and its measured partner read as one unit. */
.bar-pair { display: grid; gap: 0.3rem; margin-bottom: 0.85rem; }
.bar-pair:last-child { margin-bottom: 0; }
.bar-row { display: flex; align-items: center; gap: 0.85rem; }
.bar-name {
  flex: 0 0 8.5rem;
  text-align: right;
  color: var(--fg-dim);
  font: 700 0.78rem/1.35 var(--mono);
  overflow-wrap: anywhere;
}
.bar-track {
  position: relative;
  flex: 1 1 auto;
  min-width: 60px;
  height: 15px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  overflow: hidden;
}
.bar-fill {
  height: 100%;
  width: 0;
  border-radius: 7px 0 0 7px;
  background: linear-gradient(90deg, var(--accent-deep), var(--accent));
}
.bar-fill.ref { background: linear-gradient(90deg, #334155, var(--ref)); }
.bar-fill.amber { background: linear-gradient(90deg, #b45309, var(--amber)); }
.bar-val {
  flex: 0 0 6.4rem;
  color: var(--fg-mid);
  font: 700 0.78rem/1.35 var(--mono);
}
.bar-val small { color: var(--fg-faint); font-weight: 500; }
/* An exact zero: an empty track with a start tick, so "we measured it and it
   was nothing" looks different from "we measured it and it was very small". */
.bar-track.is-zero::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: rgba(52, 211, 153, 0.55);
}

.stat { font: 800 clamp(2.1rem, 5vw, 2.9rem)/1.08 var(--mono); color: var(--accent); margin-bottom: 0.45rem; }
.stat-dim { color: var(--fg-faint); font-size: 0.55em; font-weight: 700; }

/* ------------------------------------------------------------------- tables */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--r);
  /* The caption below sizes itself to the VISIBLE box, not the table. */
  container-type: inline-size;
}
@media (max-width: 760px) {
  /* Same affordance as the wide diagrams: a fading right edge is the only
     "there is more over here" signal a phone gets. */
  .table-scroll {
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
  }
}
table { width: 100%; border-collapse: collapse; font-size: 0.88rem; min-width: 460px; }
#models table { min-width: 760px; }
/* A caption is laid out at the TABLE's width, so inside a horizontal scroller a
   long one runs off the right edge and gets clipped with no way to reach it.
   Pinning it to the scroller's left edge and sizing it to the container's
   inline size makes it wrap where the reader can actually see it. */
caption {
  text-align: left;
  color: var(--fg-faint);
  font-size: 0.8rem;
  padding-bottom: 0.8rem;
  line-height: 1.5;
  position: sticky;
  left: 0;
  width: 100cqw;
}
/* The browser column is a verdict, not prose: it must not wrap mid-phrase. */
td.lane { white-space: nowrap; }
th, td { text-align: left; padding: 0.72rem 0.8rem; border-bottom: 1px solid var(--line); vertical-align: top; }
thead th {
  color: #10b981;
  font: 800 0.66rem/1.4 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-bottom-color: rgba(52, 211, 153, 0.24);
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: rgba(255, 255, 255, 0.018); }
td strong { color: var(--fg); }
td.num { font-family: var(--mono); font-size: 0.82rem; white-space: nowrap; }
.yes { color: var(--accent); font-weight: 700; }
.no { color: var(--fg-faint); }
.bad { color: var(--red); font-weight: 600; }

/* --------------------------------------------------------------------- code */
pre.code {
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 1.2rem 1.3rem;
  margin: 0;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 0.83rem;
  line-height: 1.85;
  color: var(--fg-mid);
  text-align: left;
}
pre.code .c { color: var(--fg-faint); }

/* ------------------------------------------------------------------- footer */
footer {
  border-top: 1px solid var(--line);
  margin-top: 3rem;
  padding: 3rem 0 4rem;
}
.footer-grid { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 780px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.footer-head {
  font: 800 0.68rem/1 var(--sans);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #10b981;
  margin-bottom: 0.9rem;
}
.footer-grid ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.footer-grid li { font-size: 0.87rem; color: var(--fg-dim); }
.footer-bottom {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  color: var(--fg-faint);
  font-size: 0.8rem;
  line-height: 1.7;
}

/* ------------------------------------------------------- motion preferences */
@media (pointer: coarse) {
  /* 44px minimum touch target on anything tappable. */
  .btn { min-height: 44px; }
  .step-dot { width: 44px; height: 44px; }
  .top nav a { min-height: 44px; display: inline-flex; align-items: center; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  html.js .reveal { opacity: 1; transform: none; }
  .card:hover { transform: none; }
}

/* --------------------------------------------------------------- small screens */
@media (max-width: 560px) {
  /* The decorative rules either side of an eyebrow push a long one onto three
     ragged lines on a phone. The words are the content; the rules are not. */
  .eyebrow::before, .eyebrow::after { display: none; }
  .eyebrow { gap: 0; }
}

@media (max-width: 640px) {
  :root { --header-h: 60px; }
  section { padding: 3.2rem 0; }
  .panel::before, .panel::after { display: none; }
  .step-row { display: block; }
  .step-kind { display: block; text-align: left; margin-bottom: 0.15rem; }
  .step-body, .stepper .step-body { min-height: 0; }
  .step-stat { margin-left: 0; flex-basis: 100%; }
  /* Prev / dots / Next will not fit one 390px line; the dots take their own row
     so neither button is clipped off the edge. */
  .step-nav { flex-wrap: wrap; justify-content: space-between; }
  /* The five-wall rail is unreadable at 390px and the step dots already carry
     the same "which one am I on" signal, so it goes rather than shrinks. */
  .walls-rail { display: none; }
  .step-dots { order: -1; flex-basis: 100%; margin: 0 0 0.6rem; justify-content: center; }
  .bar-row { flex-wrap: wrap; }
  .bar-name { flex-basis: 100%; text-align: left; }
  .bar-val { flex: 0 0 5.6rem; }
  .card-num { font-size: 2.2rem; }
}
