/*!
 * style.css
 *
 * Design thesis: the page is a document — ink on paper, monochrome, hairline
 * rules, no shadows. Saturated colour appears in exactly one place: inside the
 * code panes. Colour means code. That is the product in one sentence, so the
 * page is built to say it before a word is read.
 *
 * Code panes stay VS Code Dark+ in both light and dark page themes, because
 * "looks like your editor" is the promise, not a preference.
 *
 * No webfonts: every byte of font is already on the device. Type personality
 * comes from role separation instead — monospace is the interface voice
 * (labels, nav, buttons, metadata), the system sans is the reading voice.
 */

/* ============================ Tokens ============================ */

:root {
  /* Paper + ink. Neutrals carry a faint warm bias so they read as chosen. */
  --paper:        #f7f7f5;
  --paper-raised: #ffffff;
  --paper-sunk:   #efefec;
  --ink:          #17171a;
  --ink-soft:     #4a4b52;
  --ink-faint:    #74757e;
  --rule:         #e2e2dd;
  --rule-strong:  #cfcfc8;
  /* The viewer sits a shade off the page so its edges are legible. */
  --viewer-bg:    #ffffff;
  --viewer-edge:  #dcdcd6;

  /* One accent, taken from the VS Code Dark+ teal and darkened for paper. */
  --accent:       #0f7a68;
  --accent-hover: #0b5c4e;
  --accent-wash:  #e3f1ee;
  --accent-ink:   #0a4f43;
  --on-accent:    #ffffff;

  --danger:       #a3342c;
  --danger-wash:  #f7e8e6;

  /* Format buttons: the conventional red/blue of the two file types. */
  --pdf:        #b3261e;
  --pdf-hover:  #8f1d17;
  --word:       #1d4ed8;
  --word-hover: #1739a8;

  /* VS Code Dark+ — fixed in both page themes.
     Two surfaces, deliberately different: the editor is a dark blue, the
     terminal is near-black. The colour is the cue for which one you're
     looking at, before you read a character of it. */
  --vs-bg:      #1e2433;
  --vs-chrome:  #262d3d;
  --vs-edge:    #333c50;
  --vs-fg:      #d4d4d4;
  --vs-gutter:  #6b7689;
  --vs-gutter-edge: #2b2b2b;

  --term-bg:     #0c0c0c;
  --term-chrome: #171717;
  --term-edge:   #2b2b2b;
  --term-fg:     #e8e8e8;
  --term-gutter: #6a6a6a;
  /* Dark+ puts keywords on a mid-blue (#569cd6). Against this pane's dark blue
     that is blue on blue: legible at 5.25:1, but it reads as murk. Keywords take
     the light blue instead and identifiers fall back to the plain foreground —
     the pairing classic Visual Studio Dark uses, so nothing here is invented. */
  --vs-keyword: #9cdcfe;
  --vs-control: #c586c0;
  --vs-string:  #ce9178;
  --vs-number:  #b5cea8;
  --vs-func:    #dcdcaa;
  --vs-type:    #4ec9b0;
  --vs-comment: #6a9955;
  --vs-var:     #d4d4d4;
  --vs-punct:   #d4d4d4;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", "Cascadia Code", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --fs-xs:   0.75rem;
  --fs-sm:   0.875rem;
  --fs-base: 1.0625rem;
  --fs-lg:   1.25rem;
  --fs-xl:   1.625rem;
  --fs-2xl:  2.125rem;
  --fs-3xl:  clamp(2.25rem, 1.4rem + 3.2vw, 3.25rem);
  --fs-4xl:  clamp(2.5rem, 1.5rem + 3.9vw, 3.9rem);

  /* One hard-edged shadow, used only on the editor window. It is the single
     place on the site where something is meant to sit *above* the page rather
     than on it, so it is the single place that casts anything. */
  --lift: 0 1px 0 var(--rule-strong), 0 14px 34px -18px rgba(12, 14, 20, 0.45);

  /* Reading measure for running text only. Tables, code panes and figures
     deliberately break out past this to use the full container. */
  --measure: 80ch;
  --gutter: 1.5rem;
  --radius: 6px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:        #16171a;
    --paper-raised: #1c1d21;
    --paper-sunk:   #202126;
    --ink:          #e9e9e6;
    --ink-soft:     #b0b1b8;
    --ink-faint:    #85868f;
    --rule:         #2b2c32;
    --rule-strong:  #3a3b43;
    --viewer-bg:    #1f2025;
    --viewer-edge:  #34353d;

    --accent:       #4ec9b0;
    --accent-hover: #6fdcc6;
    --accent-wash:  rgba(78, 201, 176, 0.12);
    --accent-ink:   #7fe0cb;
    --on-accent:    #06231d;

    --danger:       #e7827a;
    --danger-wash:  rgba(231, 130, 122, 0.12);
    --pdf:        #d1483d;
    --pdf-hover:  #e0655a;
    --word:       #3b74e8;
    --word-hover: #5a8cf0;
  }
}

:root[data-theme="dark"] {
  --paper: #16171a; --paper-raised: #1c1d21; --paper-sunk: #202126;
  --ink: #e9e9e6; --ink-soft: #b0b1b8; --ink-faint: #85868f;
  --rule: #2b2c32; --rule-strong: #3a3b43;
  --viewer-bg: #1f2025; --viewer-edge: #34353d;
  --accent: #4ec9b0; --accent-hover: #6fdcc6;
  --accent-wash: rgba(78, 201, 176, 0.12); --accent-ink: #7fe0cb; --on-accent: #06231d;
  --danger: #e7827a; --danger-wash: rgba(231, 130, 122, 0.12);
  --pdf: #d1483d; --pdf-hover: #e0655a; --word: #3b74e8; --word-hover: #5a8cf0;
}

:root[data-theme="light"] {
  --paper: #f7f7f5; --paper-raised: #ffffff; --paper-sunk: #efefec;
  --viewer-bg: #ffffff; --viewer-edge: #dcdcd6;
  --ink: #17171a; --ink-soft: #4a4b52; --ink-faint: #74757e;
  --rule: #e2e2dd; --rule-strong: #cfcfc8;
  --accent: #0f7a68; --accent-hover: #0b5c4e;
  --accent-wash: #e3f1ee; --accent-ink: #0a4f43; --on-accent: #ffffff;
  --danger: #a3342c; --danger-wash: #f7e8e6;
  --pdf: #b3261e; --pdf-hover: #8f1d17; --word: #1d4ed8; --word-hover: #1739a8;
}

/* ============================ Base ============================= */

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

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { line-height: 1.2; text-wrap: balance; margin: 0; font-weight: 700; }
h1 { font-size: var(--fs-3xl); letter-spacing: -0.022em; }
h2 { font-size: var(--fs-2xl); letter-spacing: -0.018em; }
h3 { font-size: var(--fs-xl); letter-spacing: -0.012em; }
h4 { font-size: var(--fs-lg); letter-spacing: -0.008em; }

p { margin: 0; }
img { max-width: 100%; height: auto; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-hover); }

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: #fff; padding: 0.75rem 1.25rem;
  font-family: var(--font-mono); font-size: var(--fs-sm);
}
.skip-link:focus { left: 0; }

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

/* Every component below sets its own display, which silently outranks the UA
   stylesheet's [hidden] rule. Saying it once here is the difference between
   "hidden" meaning hidden and meaning nothing. */
[hidden] { display: none !important; }

/* ========================== Structure ========================== */

.wrap { width: 100%; max-width: 82rem; margin-inline: auto; padding-inline: var(--gutter); }
.wrap-narrow { width: 100%; max-width: 68rem; margin-inline: auto; padding-inline: var(--gutter); }

/* The monospace interface voice: every label, tag, and control. */
.mono-label {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ========================== Header ============================= */

.site-header {
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
  position: sticky; top: 0; z-index: 20;
}
.site-header .wrap {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; min-height: 4rem;
}
.brand {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-mono); font-weight: 700; font-size: 1rem;
  letter-spacing: -0.02em; color: var(--ink); text-decoration: none;
  flex: none;
}
.brand:hover { color: var(--ink); }
.brand-mark { width: 26px; height: 26px; flex: none; display: block; }
/* The wordmark carries a sliver of the highlighter under it, so the name and
   the thing the tool does are the same gesture. */
.brand-word { position: relative; }
.brand-word::after {
  content: ""; position: absolute; left: -0.1em; right: -0.1em; bottom: 0.06em;
  height: 0.34em; background: var(--accent); opacity: 0.22; border-radius: 2px;
  z-index: -1;
}

/* Three bars until there is room for the whole nav. */
.nav-toggle {
  display: none; flex-direction: column; justify-content: center; gap: 4px;
  width: 2.4rem; height: 2.2rem; padding: 0 0.5rem;
  background: none; border: 1px solid var(--rule); border-radius: var(--radius);
  cursor: pointer;
}
.nav-toggle span { display: block; height: 2px; background: var(--ink-soft); border-radius: 1px; }
.nav-toggle[aria-expanded="true"] { border-color: var(--rule-strong); }

.site-nav { display: flex; align-items: center; gap: 0.15rem; flex-wrap: wrap; }
.site-nav a {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--ink-soft); text-decoration: none;
  padding: 0.4rem 0.6rem; border-radius: var(--radius);
}
.site-nav a:hover { color: var(--ink); background: var(--paper-sunk); }
.site-nav a[aria-current="page"] { color: var(--accent); }

/* An icon rather than the word "Dark", because the word was doing two jobs
   badly: it named a state some readers took for the current one and a state
   others took for the destination. A sun and a moon have no such ambiguity.
   The button shows the mode it will switch you *to*, which is what the word
   meant as well.

   Three states have to be covered, not two: an explicit choice stamps
   data-theme on the root, and the default "system" setting stamps nothing, so
   only prefers-color-scheme separates light from dark there. Same guard the
   colour tokens above use, for the same reason. */
.theme-toggle {
  display: inline-grid; place-items: center;
  width: 2.15rem; height: 2.15rem; padding: 0;
  background: none; border: 1px solid var(--rule); color: var(--ink-soft);
  border-radius: var(--radius); cursor: pointer; line-height: 0;
  transition: border-color 0.14s ease, color 0.14s ease, background-color 0.14s ease;
}
.theme-toggle:hover {
  border-color: var(--rule-strong); color: var(--accent); background: var(--paper-sunk);
}
.theme-toggle svg { width: 1.05rem; height: 1.05rem; display: block; }

/* Light is the default, so the moon shows: pressing it takes you to dark. */
.theme-toggle .icon-sun  { display: none; }
.theme-toggle .icon-moon { display: block; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun  { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
}
:root[data-theme="dark"]  .theme-toggle .icon-sun  { display: block; }
:root[data-theme="dark"]  .theme-toggle .icon-moon { display: none; }
:root[data-theme="light"] .theme-toggle .icon-sun  { display: none; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: block; }
.theme-toggle { margin-left: 0.4rem; }

@media (max-width: 68rem) {
  .site-header .wrap { flex-wrap: wrap; }
  .nav-toggle { display: flex; }
  .site-nav {
    flex-basis: 100%; display: none; flex-direction: column; align-items: stretch;
    gap: 0; padding-bottom: 0.75rem;
  }
  .site-nav.is-open { display: flex; }
  .site-nav a { padding: 0.6rem 0.5rem; border-top: 1px solid var(--rule); }
  .theme-toggle { margin: 0.75rem 0 0; align-self: flex-start; }
}

/* =========================== Hero ============================== */

/* Faint ruled paper behind the opening, so the first thing on screen is a
   document rather than a landing page. The rules are pinned to the same
   rhythm as body text, which is why they read as ruling and not as stripes. */
.hero {
  padding-block: clamp(2.75rem, 5vw, 4.5rem) 1.75rem;
  position: relative; overflow: hidden;
}
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: repeating-linear-gradient(
    to bottom, transparent 0 1.55rem, var(--rule) 1.55rem 1.6rem);
  opacity: 0.7;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 78%);
          mask-image: linear-gradient(to bottom, #000 0%, transparent 78%);
}
.hero > .wrap { position: relative; z-index: 1; }
.hero-inner { max-width: 54rem; }
.hero h1 { margin-bottom: 1.15rem; font-size: var(--fs-4xl, var(--fs-3xl)); }

/* The signature: a marker stroke behind the words, drawn slightly askew and
   with soft ends so it reads as a hand rather than as a background colour. */
.sweep { position: relative; white-space: nowrap; }
.sweep::before {
  content: ""; position: absolute; z-index: -1;
  left: -0.18em; right: -0.18em; top: 0.14em; bottom: 0.02em;
  background: var(--accent); opacity: 0.24;
  border-radius: 0.7em 0.35em 0.6em 0.4em / 0.5em;
  transform: rotate(-0.75deg);
}
@media (max-width: 40rem) { .sweep { white-space: normal; } }
.hero-sub {
  font-size: var(--fs-lg); color: var(--ink-soft);
  max-width: var(--measure); margin-bottom: 1.25rem;
}
.hero-sub strong { color: var(--ink); font-weight: 600; }

.hero-facts {
  display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none;
  padding: 0; margin: 0.35rem 0 0.5rem;
}
/* Opaque rather than transparent: these sit over the hero's ruled paper, and
   letting the rules run through them made them read as printing rather than
   as chips. */
.hero-facts li {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-mono); font-size: var(--fs-xs); line-height: 1;
  color: var(--ink); background: var(--paper-raised);
  border: 1px solid var(--rule); border-radius: 100px;
  padding: 0.45rem 0.9rem 0.45rem 0.7rem;
}
/* Each fact gets the icon of the thing it is claiming, in the one accent the
   page is allowed. Four identical ticks said nothing four times. No hover
   state: there is nothing here to click, and an affordance that leads nowhere
   is a small lie. */
.hero-facts li svg {
  width: 15px; height: 15px; flex: none; display: block; color: var(--accent);
}

/* ========================== The tool =========================== */

.tool { padding-bottom: 3rem; }

.dropzone {
  position: relative;
  border: 2px dashed var(--rule-strong);
  border-radius: 10px;
  background: var(--paper-raised);
  padding: clamp(1.75rem, 4vw, 2.75rem) var(--gutter);
  text-align: center;
  min-height: 15rem;                 /* reserved: nothing shifts on load */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.85rem;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.dropzone.is-over { border-color: var(--accent); background: var(--accent-wash); }
.dropzone.is-busy { opacity: 0.75; }

.dropzone-icon { width: 40px; height: 40px; color: var(--ink-faint); }
.dropzone-title { font-size: var(--fs-lg); font-weight: 650; }
.dropzone-hint { color: var(--ink-soft); font-size: var(--fs-sm); max-width: 40ch; margin-inline: auto; }

.dropzone-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; justify-content: center; }

.btn {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  border-radius: var(--radius); padding: 0.6rem 1.1rem;
  border: 1px solid transparent; cursor: pointer; line-height: 1.2;
  text-decoration: none; display: inline-flex; align-items: center; gap: 0.45rem;
}
.btn-primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); }

/* The two document formats carry their conventional colours so the pair is
   scannable at a glance; HTML and copy stay neutral because they are secondary. */
.btn-pdf  { background: var(--pdf); color: #fff; border-color: var(--pdf); }
.btn-pdf:hover  { background: var(--pdf-hover); border-color: var(--pdf-hover); color: #fff; }
.btn-word { background: var(--word); color: #fff; border-color: var(--word); }
.btn-word:hover { background: var(--word-hover); border-color: var(--word-hover); color: #fff; }

.btn-ghost { background: transparent; color: var(--ink); border-color: var(--rule-strong); }
.btn-ghost:hover { background: var(--paper-sunk); color: var(--ink); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* A download that is switched off keeps its shape and loses its colour, so
   the pair still reads as a pair and the missing one is legible as
   unavailable rather than as absent. Its title says why. */
.btn-pdf:disabled, .btn-word:disabled {
  background: var(--paper-sunk); color: var(--ink-faint);
  border-color: var(--rule); opacity: 1;
}
.btn-sm { font-size: var(--fs-xs); padding: 0.35rem 0.7rem; }

/* Line-number switch. Sits with the export controls because it changes what
   the exported file contains, not just what the screen shows. */
.toggle {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-family: var(--font-mono); font-size: var(--fs-sm);
  color: var(--ink-soft); cursor: pointer;
  padding: 0.6rem 0.2rem; line-height: 1.2;
}
.toggle input { accent-color: var(--accent); width: 15px; height: 15px; cursor: pointer; }
.toggle:hover { color: var(--ink); }

/* Hiding the gutter must also reclaim its padding, or the code sits inset. */
.output.no-line-numbers .codeblock-gutter { display: none; }
.output.no-line-numbers .codeblock-code { padding-inline: 1.1rem 1.25rem; }

.result-note {
  flex-basis: 100%;
  font-size: var(--fs-sm);
  color: var(--ink-faint);
  max-width: var(--measure);
  margin-top: 0.15rem;
}

/* ---------------------------- equations ---------------------------- *
 * Deliberately the inverse of the code panes: white paper, black ink. An
 * equation is not code and should not be dressed as it, and every printed
 * formula the reader has ever seen was black on white.
 *
 * The body is MathML, typeset by the browser itself — real fraction bars and
 * radicals, no library and no webfont. Where MathML is unsupported the plain
 * form shows through and stays readable.
 * ------------------------------------------------------------------- */

.equation {
  margin: 1.25rem 0; border: 1px solid var(--rule-strong);
  border-radius: 8px; overflow: hidden;
  background: #ffffff;                 /* fixed: paper, in either page theme */
}
.equation-bar {
  display: flex; align-items: center; gap: 0.75rem;
  background: #f2f2ef; border-bottom: 1px solid #e2e2dd;
  padding: 0.4rem 0.85rem;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: 0.06em; text-transform: uppercase; color: #55565e;
}
.equation-body { padding: 0.35rem 0; }

.eq-row {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.45rem 0.85rem;
  color: #17171a;                      /* black ink, whatever the page theme */
  scroll-margin-block: 8rem;
}
.eq-conf {
  flex: none; width: 3rem; text-align: right;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums; color: #74757e;
  -webkit-user-select: none; user-select: none;
}
.eq-math { flex: 1 1 auto; min-width: 0; overflow-x: auto; }
.eq-math math { font-size: 1.25em; }

/* The fallback, and the LaTeX source shown when it cannot be typeset. */
.eq-plain {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--font-mono); font-size: 0.9375rem;
  color: #17171a; white-space: pre-wrap; word-break: break-word;
}
.eq-unconvertible { color: #6b6c74; font-style: italic; }
.eq-note {
  flex: none; font-family: var(--font-mono); font-size: var(--fs-xs);
  color: #a3342c;
}

/* Found by the low-confidence walker, on white rather than the page accent. */
.eq-row.is-found { outline: 2px solid var(--accent); outline-offset: -2px; }
.eq-row.is-ai .eq-conf { color: var(--word); font-weight: 700; }

/* ------------------- the document as review surface ------------------- *
 * There is no list beside the result any more: the result *is* the list.
 * Every line carries its confidence in a left column, selecting text offers
 * to reclassify it, and a sticky bar walks the doubtful lines. The columns are
 * user-select:none throughout so that copying a passage yields the document's
 * words and not a spreadsheet of percentages.
 * ---------------------------------------------------------------------- */

/* ===================== the editor window =======================
 * The result is somebody's file, so it is framed as a file open in an
 * application: a title bar with the document's name, a toolbar of the controls
 * that act on it, and the page itself below. The chrome takes its colours from
 * the selected code theme, which is also what makes the theme picker visibly
 * do something the instant it is used.
 * ---------------------------------------------------------------- */

.editor {
  border: 1px solid var(--vs-edge);
  border-radius: 10px;
  background: var(--vs-chrome);
  box-shadow: var(--lift);
  overflow: hidden;
  margin-top: 0.25rem;
}

.editor-chrome {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.5rem 0.85rem;
  background: var(--vs-chrome);
  border-bottom: 1px solid var(--vs-edge);
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--vs-gutter);
}
/* Two lights, in the theme's own accents rather than the usual red and
   green: this is not pretending to be somebody else's window. They are real
   buttons -- the first fills the browser window, the second goes to full
   screen. The glyph on each rests faint, comes up on its own when the
   pointer is over it, and is solid only while it is in effect. */
.editor-dots { display: inline-flex; gap: 6px; flex: none; }
.editor-dot {
  width: 12px; height: 12px; padding: 0; border: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  color: var(--vs-chrome);
}
.editor-dot:nth-child(1) { background: var(--vs-control); }
.editor-dot:nth-child(2) { background: var(--vs-func); }
/* The light itself never changes; it is the glyph on it that does. Faint
   at rest, stronger under the pointer, solid while in effect. */
.editor-dot svg {
  width: 9px; height: 9px; opacity: 0.6;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
  transition: opacity 120ms;
}
.editor-dot:hover svg, .editor-dot:focus-visible svg { opacity: 0.85; }
.editor-dot[aria-pressed="true"] svg { opacity: 1; }
.editor-dot:focus-visible { outline: 2px solid var(--vs-fg); outline-offset: 2px; }

/* Filling the browser window: the editor leaves the page and takes the
   viewport, and the document scrolls inside it rather than the page. Full
   screen is the same layout with the browser's own chrome gone too. */
.editor.is-expanded,
.editor:fullscreen {
  position: fixed; inset: 0; z-index: 60;
  border-radius: 0; border: 0; margin: 0;
  display: flex; flex-direction: column;
  background: var(--vs-chrome);
}
.editor.is-expanded .doc-viewer,
.editor:fullscreen .doc-viewer { flex: 1; max-height: none; }
.editor.is-expanded .editor-chrome,
.editor.is-expanded .editor-toolbar,
.editor:fullscreen .editor-chrome,
.editor:fullscreen .editor-toolbar { flex: none; }
body.editor-expanded { overflow: hidden; }

.editor-tab {
  color: var(--vs-fg); background: var(--vs-bg);
  border: 1px solid var(--vs-edge); border-bottom-color: transparent;
  border-radius: 5px 5px 0 0;
  padding: 0.3rem 0.75rem; margin-bottom: -0.55rem;
  max-width: 24rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.editor-count {
  margin-left: auto; text-align: right;
  letter-spacing: 0.05em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.editor-toolbar {
  display: flex; align-items: flex-end; gap: 0.9rem 1.1rem; flex-wrap: wrap;
  padding: 0.6rem 0.85rem;
  background: var(--vs-chrome);
  border-bottom: 1px solid var(--vs-edge);
}
.tb-group { display: flex; align-items: flex-end; gap: 0.75rem; flex-wrap: wrap; }
.tb-right { margin-left: auto; align-items: center; gap: 0.45rem; }

.tb-field { display: flex; flex-direction: column; gap: 0.15rem; }
.tb-field .k {
  font-family: var(--font-mono); font-size: 0.6875rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--vs-gutter);
}
.tb-field select {
  font-family: var(--code-font, var(--font-mono)); font-size: var(--fs-xs);
  color: var(--vs-fg); background: var(--vs-bg);
  border: 1px solid var(--vs-edge); border-radius: 5px;
  padding: 0.3rem 0.45rem; max-width: 13rem; cursor: pointer;
}
.tb-field select:hover { border-color: var(--vs-type); }
.tb-field option { color: #17171a; background: #fff; }
.tb-field option[data-missing] { opacity: 0.55; }
.tb-file-row { display: inline-flex; align-items: center; gap: 0.4rem; }
.tb-file input[type="file"] {
  font-family: var(--font-mono); font-size: 0.6875rem;
  color: var(--vs-gutter); max-width: 11rem;
}
.tb-file input[type="file"]::file-selector-button {
  font: inherit; color: var(--vs-fg); background: var(--vs-bg);
  border: 1px solid var(--vs-edge); border-radius: 5px;
  padding: 0.25rem 0.5rem; margin-right: 0.45rem; cursor: pointer;
}
.tb-file input[type="file"]::file-selector-button:hover { border-color: var(--vs-type); }
.btn-clear {
  font-family: var(--font-mono); font-size: 0.6875rem;
  background: none; border: 0; color: var(--vs-gutter);
  text-decoration: underline; cursor: pointer; padding: 0.15rem 0;
}
.btn-clear:hover { color: var(--vs-fg); }

/* The line-number switch lives in the toolbar now, so it takes the toolbar's
   colours rather than the page's. */
.editor-toolbar .toggle { color: var(--vs-gutter); font-size: var(--fs-xs); padding: 0.3rem 0; }
.editor-toolbar .toggle:hover { color: var(--vs-fg); }
.editor-toolbar .toggle input { accent-color: var(--vs-type); }
.editor-toolbar .btn-ghost {
  color: var(--vs-fg); border-color: var(--vs-edge); background: var(--vs-bg);
}
.editor-toolbar .btn-ghost:hover { background: var(--vs-edge); color: var(--vs-fg); }

/* Short by design: it sits over the document, so every extra character of it
   is a character of somebody's file it is covering up. */
.doc-find {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-family: var(--font-mono); font-size: 0.6875rem;
  color: var(--vs-gutter);
  padding: 0.2rem 0.25rem 0.2rem 0.5rem;
  border: 1px solid var(--vs-edge); border-radius: 5px;
  background: var(--vs-bg);
}
.doc-find-cap { text-transform: uppercase; letter-spacing: 0.05em; }
.doc-find-label {
  min-width: 2.6rem; text-align: right; font-variant-numeric: tabular-nums;
  color: var(--vs-fg);
}
.doc-find button {
  font: inherit; line-height: 1; cursor: pointer;
  border: 1px solid var(--vs-edge); background: transparent; color: var(--vs-fg);
  border-radius: 3px; padding: 0.18rem 0.32rem;
}
.doc-find button:hover {
  background: var(--vs-type); color: var(--vs-bg); border-color: var(--vs-type);
}

/* The long explanation used to sit across the top of the document. It is one
   sentence you read once, so it lives behind a mark you can ignore. */
.tb-info {
  position: relative; flex: none; cursor: help;
  width: 1.35rem; height: 1.35rem; border-radius: 50%;
  display: inline-grid; place-items: center;
  border: 1px solid var(--vs-edge); background: var(--vs-bg);
  color: var(--vs-gutter);
  font-family: var(--font-mono); font-size: 0.75rem; font-style: italic;
  line-height: 1;
}
.tb-info:hover, .tb-info:focus-visible { color: var(--vs-fg); border-color: var(--vs-type); }
.tb-tip {
  position: absolute; right: 0; top: calc(100% + 0.5rem); z-index: 30;
  width: min(24rem, 70vw);
  padding: 0.65rem 0.8rem;
  background: var(--vs-bg); color: var(--vs-fg);
  border: 1px solid var(--vs-edge); border-radius: var(--radius);
  box-shadow: 0 10px 26px -12px rgba(0, 0, 0, 0.6);
  font-family: var(--font-sans); font-size: var(--fs-sm); font-style: normal;
  line-height: 1.45; text-align: left;
  opacity: 0; visibility: hidden; transform: translateY(-3px);
  transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s;
}
.tb-info:hover .tb-tip, .tb-info:focus-visible .tb-tip, .tb-info:focus .tb-tip {
  opacity: 1; visibility: visible; transform: none;
}

@media (max-width: 52rem) {
  .tb-right { margin-left: 0; flex-basis: 100%; justify-content: flex-end; }
}

/* ---- prose rows ---- */

.doc-line {
  display: flex; gap: 0.75rem; align-items: baseline;
  padding: 0.1rem 0.35rem; border-radius: 3px;
  scroll-margin-block: 8rem;      /* clears the sticky header and toolbar */
  /* No measure limit and no colour of our own: the line is set in whatever
     the document asked for, and a width cap here would re-wrap a paragraph
     the author had already laid out. */
  max-width: none; color: var(--ink);
}
.doc-text {
  flex: 1 1 auto; min-width: 0;
  white-space: pre-wrap; word-break: break-word;
  /* The parser writes font-family, size, weight, indent, alignment and
     leading onto this element as inline styles, taken from the source
     document. Anything set here is only the fallback for a line that
     carried no formatting of its own. */
  font-family: Georgia, "Times New Roman", serif;
  line-height: 1.5;
}
.doc-gap { height: 0.65rem; }

/* The confidence column, in both prose rows and code panes. */
.doc-conf, .codeblock-conf {
  flex: none; width: 3rem; text-align: right;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  -webkit-user-select: none; user-select: none;
}
.doc-line[data-conf="low"] .doc-conf { color: var(--danger); font-weight: 700; }
.doc-line[data-conf="low"] { border-left: 3px solid var(--danger); padding-left: 0; }
.doc-line[data-conf="mid"] { border-left: 3px solid var(--rule-strong); padding-left: 0; }
.doc-line[data-conf="low"] .doc-conf,
.doc-line[data-conf="mid"] .doc-conf { margin-left: 0.35rem; }

/* ---- code rows ---- */

.codeblock-conf {
  /* Same face as the code beside it: the columns are aligned by line height,
     and two different fonts in one pane is exactly how that alignment slips. */
  font-family: var(--code-font, var(--font-mono));
  width: auto; text-align: right;
  padding: 0.9rem 0.6rem 0.9rem 0.9rem;
  margin: 0; color: var(--vs-gutter); background: var(--vs-bg);
  border-right: 1px solid var(--vs-edge);
  line-height: 1.55; font-size: 0.8125rem;
}
.output.no-line-numbers .codeblock-conf { padding-right: 0.9rem; }
.codeblock.is-terminal .codeblock-conf { background: var(--term-bg); color: var(--term-gutter); }
.cb-line { display: inline; }

/* ---- the line the find control jumped to ---- */

.doc-line.is-found, .cb-line.is-found {
  outline: 2px solid var(--accent); outline-offset: 2px;
  background: var(--accent-wash);
}
.cb-line.is-found { border-radius: 2px; }

/* ---- lines the AI pass changed ---- */

.doc-line.is-ai .doc-conf { color: var(--word); font-weight: 700; }
.doc-line.is-ai .doc-conf::after {
  content: "AI"; font-size: 0.72em; letter-spacing: 0.06em;
  border: 1px solid var(--word); border-radius: 3px;
  padding: 0.05em 0.22em; margin-left: 0.3em;
}
.doc-line.is-ai .doc-conf { width: 5rem; }
.cb-line.is-ai { border-bottom: 1px dotted var(--word); }

/* ---- the floating mark menu ---- */

.mark-menu {
  position: fixed; z-index: 60;
  display: flex; flex-direction: column; min-width: 12rem;
  padding: 0.25rem; border: 1px solid var(--rule-strong);
  border-radius: var(--radius); background: var(--paper-raised);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
}
.mark-menu[hidden] { display: none; }
.mark-menu button {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  text-align: left; cursor: pointer;
  padding: 0.45rem 0.6rem; border: 0; border-radius: 3px;
  background: transparent; color: var(--ink);
  /* The icon sits at the right edge, so the labels stay left-aligned and
     the icons line up in a column of their own however long a label is. */
  display: flex; align-items: center; gap: 1.25rem;
}
.mark-label { flex: 1 1 auto; }
/* Drawn rather than lettered: a glyph would pick up the reader's emoji font
   and arrive in colour at a size we do not control. Stroked in currentColor
   so it follows the text through hover, the dark theme and the disabled
   state without a second rule for each. */
.mark-icon {
  flex: none; width: 1.05em; height: 1.05em;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.75;
}
.mark-menu button:hover:not(:disabled) .mark-icon { opacity: 1; }
.mark-menu button:hover:not(:disabled) { background: var(--accent); color: var(--on-accent); }
/* Shown greyed rather than hidden: the option keeps its place in the menu so
   it does not shuffle the others around as the selection changes. */
.mark-menu button:disabled { color: var(--ink-faint); cursor: default; opacity: 0.65; }
.mark-menu button[data-mark="text-all"] {
  border-top: 1px solid var(--rule); margin-top: 0.2rem; padding-top: 0.5rem;
}

/* ---------------------- AI refinement ---------------------- *
 * The button is deliberately large and deliberately dull until pressed.
 * Grey means nothing has left the device; colour means it has. That is the
 * only state in this tool where the distinction matters, so it gets the
 * loudest signal available rather than a line of small print.
 * ----------------------------------------------------------- */

.ai-panel { margin: 0 0 1.25rem; }

.ai-btn {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 1rem 1.25rem; border-radius: var(--radius);
  border: 2px dashed var(--rule-strong);
  background: var(--paper-sunk); color: var(--ink-faint);
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.ai-btn:hover:not(:disabled) { border-color: var(--word); color: var(--ink); }

.ai-btn-main {
  display: block; font-family: var(--font-mono); font-weight: 700;
  font-size: var(--fs-base); letter-spacing: -0.01em;
}
.ai-btn-sub {
  display: block; margin-top: 0.3rem;
  font-size: var(--fs-sm); line-height: 1.45; color: var(--ink-faint);
}

/* Pressed and applied: solid, coloured, no longer a dashed invitation. */
.ai-btn.is-done {
  border-style: solid; border-color: var(--word);
  background: var(--word); color: var(--on-accent);
}
.ai-btn.is-done .ai-btn-sub { color: var(--on-accent); opacity: 0.9; }
.ai-btn.is-done .ai-btn-main::after { content: " ✓"; }

.ai-btn:disabled { cursor: progress; opacity: 0.7; }

/* The busy state stands in for the output, so it occupies real space
   rather than floating over a result the user should not be reading yet. */
.ai-busy {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.6rem; min-height: 16rem; margin-top: 1.5rem;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--paper-raised); text-align: center; padding: 2rem 1.25rem;
}
.ai-spinner {
  width: 2.75rem; height: 2.75rem; border-radius: 50%;
  border: 3px solid var(--rule-strong); border-top-color: var(--word);
  animation: ai-spin 0.8s linear infinite;
}
@keyframes ai-spin { to { transform: rotate(360deg); } }

.ai-busy-main {
  font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-base);
  color: var(--ink);
}
.ai-busy-step { font-size: var(--fs-sm); color: var(--ink-faint); }

/* Reduced motion: pulse instead of spin, so there is still a sign of life. */
@media (prefers-reduced-motion: reduce) {
  .ai-spinner { animation: ai-pulse 1.4s ease-in-out infinite; border-top-color: var(--word); }
  @keyframes ai-pulse { 50% { opacity: 0.35; } }
}

/* Everything the AI pass is about to change is hidden while it runs. */
.is-hidden-busy { display: none !important; }

/* How far the review has got. The number behind it is real - the pass sends
   its batches one at a time and reports each one - so this is a measurement,
   not a reassurance. */
.ai-progress {
  width: min(28rem, 100%);
  height: 6px;
  margin-top: 0.9rem;
  border-radius: 100px;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  overflow: hidden;
}
.ai-progress-fill {
  height: 100%; width: 0;
  background: var(--accent);
  border-radius: inherit;
  transition: width 0.35s ease;
}
/* Before the batches have been counted there is no position to report, so the
   track sweeps rather than sitting at zero pretending to be one. A bar parked
   at 0% and a bar that has genuinely hung look identical. */
.ai-progress.is-indeterminate .ai-progress-fill {
  width: 35%;
  transition: none;
  animation: ai-sweep 1.1s ease-in-out infinite;
}
@keyframes ai-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(320%); }
}
.ai-progress-count {
  margin-top: 0.4rem; min-height: 1rem;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--ink-faint); font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .ai-progress.is-indeterminate .ai-progress-fill { animation: none; width: 100%; opacity: 0.4; }
  .ai-progress-fill { transition: none; }
}

/* The "AI" badge, both as a row marker and inline in prose. */
.ai-badge {
  font-family: var(--font-mono); font-size: 0.7em; font-weight: 700;
  letter-spacing: 0.08em; vertical-align: 0.15em;
  border: 1px solid var(--word); color: var(--word);
  border-radius: 3px; padding: 0.1em 0.32em; white-space: nowrap;
}

/* A verdict the AI pass overturned. Marked rather than silent: if the model
   got it wrong, the user needs to be able to see which one to put back. */

/* Takes the dropzone's slot once a file is in. Same footprint, same corner
   radius, same dashed-to-solid family, so the intake area reads as one thing
   in two states rather than as two things stacked. */
.loaded-card {
  display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap;
  border: 1px solid var(--rule-strong); border-radius: 10px;
  background: var(--paper-raised);
  padding: 1.15rem 1.35rem;
  position: relative; overflow: hidden;
}
/* A highlighter stripe down the edge: the file has been through the tool. */
.loaded-card::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--accent);
}
.loaded-icon {
  flex: none; width: 2.6rem; height: 2.6rem; display: grid; place-items: center;
  border-radius: 8px; background: var(--accent-wash); color: var(--accent-ink);
}
.loaded-icon svg { width: 1.4rem; height: 1.4rem; }
.loaded-body { min-width: 12rem; flex: 1 1 16rem; }
.loaded-body .mono-label { display: block; margin-bottom: 0.1rem; }
.loaded-name {
  font-weight: 650; font-size: var(--fs-lg); word-break: break-all;
  line-height: 1.3;
}
.loaded-meta {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--ink-faint); margin-top: 0.2rem;
}
.loaded-side {
  display: flex; flex-direction: column; align-items: flex-end; gap: 0.5rem;
  margin-left: auto; text-align: right;
}
.loaded-hint { color: var(--ink-soft); font-size: var(--fs-sm); }
@media (max-width: 44rem) {
  .loaded-side { align-items: flex-start; text-align: left; margin-left: 0; flex-basis: 100%; }
}

/* Progress + status */
.tool-status {
  margin-top: 1rem; min-height: 1.5rem;
  font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--ink-soft);
}
.tool-status[data-state="error"] {
  color: var(--danger); background: var(--danger-wash);
  padding: 0.75rem 1rem; border-radius: var(--radius);
  border: 1px solid var(--danger);
}

/* Everything that only means something once a document is loaded. One
   container, so "is there a file?" is answered in exactly one place instead
   of on each control separately. */
.workbench { margin-top: 1.5rem; }

/* Results summary strip */
.summary-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1.5rem;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  padding: 0.9rem 0; margin: 0 0 1.35rem;
}
.summary { display: flex; flex-wrap: wrap; gap: 1.75rem; }
.summary div { display: flex; flex-direction: column; gap: 0.1rem; }
.summary dt, .summary .k {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint);
}
.summary dd, .summary .v {
  margin: 0; font-size: var(--fs-lg); font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.result-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-bottom: 1.25rem; }

/* The page inside the window. It follows the site's light/dark setting rather
   than the code theme, because it is the reader's own document and not a code
   pane — and because a document whose runs were left at automatic black is
   readable either way. The prose inside carries the typeface, size, weight,
   indent and spacing the document itself specified; those arrive as inline
   styles from the parser. It scrolls inside the window, which is what keeps a
   three-hundred-page file from turning the page into a mile of scrollbar. */
.doc-viewer {
  background: var(--viewer-bg);
  padding: 1.6rem 1.35rem;
  max-height: 78vh; overflow-y: auto;
  scrollbar-color: var(--rule-strong) transparent;
}
.doc-viewer:has(.output:empty) { display: none; }

/* Rendered document output */
.output { margin-top: 0; }
.output:empty { display: none; }
.output > p {
  max-width: var(--measure); margin-bottom: 1rem; color: var(--ink-soft);
}

/* ======================= Code panes (Dark+) ==================== */

.codeblock {
  margin: 1.5rem 0;
  background: var(--vs-bg);
  border: 1px solid var(--vs-edge);
  border-radius: 8px;
  overflow: hidden;
}
.codeblock-bar {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  background: var(--vs-chrome); border-bottom: 1px solid var(--vs-edge);
  padding: 0.5rem 0.85rem;
  font-family: var(--font-mono); font-size: var(--fs-xs);
}
.codeblock-lang {
  color: var(--vs-fg); font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
}
.codeblock-why { color: var(--vs-gutter); flex: 1 1 12rem; min-width: 0; }
.codeblock-copy {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  background: transparent; border: 1px solid var(--vs-edge); color: var(--vs-gutter);
  border-radius: 4px; padding: 0.25rem 0.6rem; cursor: pointer; line-height: 1.3;
}
.codeblock-copy:hover {
  background: var(--vs-chrome); color: var(--vs-fg); border-color: var(--vs-gutter);
}
.codeblock-copy:focus-visible { outline: 2px solid var(--vs-keyword); outline-offset: 1px; }

.codeblock-body { display: flex; overflow-x: auto; }
/* --code-font is set on :root by the font picker; the built-in stack is the
   fallback, so an uninstalled choice degrades instead of breaking. */
.codeblock-gutter,
.codeblock-code {
  margin: 0; padding: 0.85rem 0;
  font-family: var(--code-font, var(--font-mono));
  font-size: 0.8125rem; line-height: 1.65;
  tab-size: 4;
}
.codeblock-gutter {
  flex: none; padding-inline: 0.85rem 0.6rem;
  color: var(--vs-gutter); text-align: right; user-select: none;
  border-right: 1px solid var(--vs-gutter-edge); background: var(--vs-bg);
  font-variant-numeric: tabular-nums;
}
.codeblock-code {
  flex: 1 1 auto; padding-inline: 0.9rem 1.25rem;
  color: var(--vs-fg); white-space: pre; min-width: 0;
}
.codeblock-code code { font: inherit; color: inherit; background: none; padding: 0; }

.t-keyword   { color: var(--vs-keyword); }
.t-control   { color: var(--vs-control); }
.t-string    { color: var(--vs-string); }
.t-number    { color: var(--vs-number); }
.t-function  { color: var(--vs-func); }
.t-type      { color: var(--vs-type); }
.t-comment   { color: var(--vs-comment); font-style: italic; }
.t-variable  { color: var(--vs-var); }
.t-decorator { color: var(--vs-func); }
.t-preproc   { color: var(--vs-control); }
.t-operator  { color: var(--vs-fg); }
.t-punct     { color: var(--vs-punct); }

/* Terminal: near-black surface, no line gutter, no syntax colour. A shell
   transcript has no line numbers, and colouring its words would imply a
   structure that is not there. */
.codeblock.is-terminal { background: var(--term-bg); border-color: var(--term-edge); }
.codeblock.is-terminal .codeblock-bar { background: var(--term-chrome); border-bottom-color: var(--term-edge); }
.codeblock.is-terminal .codeblock-code { color: var(--term-fg); }
/* A transcript and a listing are both dark panes, and at a glance they read
   as the same thing. The distinction is what the reader is actually trying to
   see, so it is stated rather than implied: a listing is named by its
   language and numbered down the side; a transcript is labelled as output,
   has no line numbers to give, and carries a prompt mark on its bar. */
.codeblock.is-terminal .codeblock-lang { color: var(--term-fg); }
.codeblock.is-terminal .codeblock-why { color: var(--term-gutter); }
.codeblock.is-terminal .codeblock-copy {
  border-color: var(--term-edge); color: var(--term-gutter);
}
.codeblock.is-terminal .codeblock-copy:hover {
  background: var(--term-chrome); color: var(--term-fg); border-color: var(--term-gutter);
}
.codeblock.is-terminal .codeblock-lang::before {
  content: "› ";        /* a shell prompt, before the word */
  color: var(--term-gutter); font-weight: 700;
}
.codeblock:not(.is-terminal) .codeblock-lang::before {
  content: "■ ";        /* a filled square, as an editor tab marker */
  color: var(--vs-keyword); font-weight: 700;
}
.codeblock.is-terminal .codeblock-gutter { display: none; }
.codeblock.is-terminal .codeblock-code { padding-inline: 1.1rem 1.25rem; }

/* ======================== Prose content ======================== */

/* The measure applies to running text, not to the whole column: constraining
   the container as well would cap tables and code panes at reading width too,
   which wastes the screen and makes wide content scroll for no reason. */
.prose { max-width: none; }
.prose > p,
.prose > ul,
.prose > ol,
.prose > h2,
.prose > h3,
.prose > h4,
.prose > blockquote,
.prose > .lede { max-width: var(--measure); }
.prose > .table-scroll,
.prose > .codeblock,
.prose > figure { max-width: none; }
.prose > * + * { margin-top: 1.1rem; }
.prose h2 { margin-top: 2.75rem; padding-top: 1.25rem; border-top: 1px solid var(--rule); }
.prose h3 { margin-top: 2rem; }
.prose h2 + p, .prose h3 + p { margin-top: 0.75rem; }
.prose ul, .prose ol { padding-left: 1.35rem; margin-block: 1rem; }
.prose li + li { margin-top: 0.4rem; }
.prose strong { font-weight: 650; }

.prose code, .inline-code {
  font-family: var(--font-mono); font-size: 0.875em;
  background: var(--paper-sunk); border: 1px solid var(--rule);
  padding: 0.05em 0.35em; border-radius: 4px;
}
.prose .codeblock code { background: none; border: 0; padding: 0; font-size: inherit; }

.prose blockquote {
  margin-inline: 0; padding-left: 1.1rem;
  border-left: 2px solid var(--accent); color: var(--ink-soft);
}

.lede { font-size: var(--fs-lg); color: var(--ink-soft); }

/* A boxed summary, marked down its edge the way you would mark a passage in a
   printed page. Used where a long argument deserves a two-sentence version. */
.callout {
  border: 1px solid var(--rule); border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-wash);
  padding: 1.1rem 1.25rem;
  margin-block: 1.75rem;
  max-width: var(--measure);
}
.callout .mono-label { display: block; margin-bottom: 0.45rem; color: var(--accent-ink); }
.callout p:last-child { margin: 0; }
.prose > .callout { max-width: var(--measure); }

/* Tables scroll inside themselves; the page never scrolls sideways. */
.table-scroll { overflow-x: auto; border: 1px solid var(--rule); border-radius: var(--radius); margin-block: 1.5rem; }
table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
th, td { text-align: left; padding: 0.6rem 0.85rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
thead th {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-faint); background: var(--paper-sunk); white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
td.pos { color: var(--accent); font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
td.neg { color: var(--danger); font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Page intro block */
.page-head { padding-block: clamp(2rem, 4vw, 3rem) 1.25rem; border-bottom: 1px solid var(--rule); }
.page-head h1 { font-size: var(--fs-2xl); margin-bottom: 0.65rem; max-width: var(--measure); }
.page-head .lede { max-width: var(--measure); }

.breadcrumbs { margin-bottom: 0.9rem; }
.breadcrumbs ol {
  list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem;
  padding: 0; margin: 0;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: 0.05em; text-transform: uppercase;
}
.breadcrumbs li::after { content: "/"; color: var(--ink-faint); margin-left: 0.4rem; }
.breadcrumbs li:last-child::after { content: ""; }
.breadcrumbs a { color: var(--ink-faint); text-decoration: none; }
.breadcrumbs a:hover { color: var(--accent); }
.breadcrumbs [aria-current="page"] { color: var(--ink-soft); }

section.band { padding-block: clamp(2.5rem, 5vw, 4rem); }
section.band + section.band { border-top: 1px solid var(--rule); }

/* Card grid used for "next steps" / related links */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; margin-top: 1.5rem; }
.card {
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 1.1rem; background: var(--paper-raised);
  display: flex; flex-direction: column; gap: 0.4rem;
}
.card h3 { font-size: 1.05rem; }
.card a { text-decoration: none; }
.card a::after { content: ""; position: absolute; inset: 0; }
.card { position: relative; }
.card { transition: border-color 0.14s ease, transform 0.14s ease; }
.card::before {
  content: ""; position: absolute; left: -1px; right: -1px; top: -1px; height: 3px;
  background: var(--accent); opacity: 0; border-radius: var(--radius) var(--radius) 0 0;
  transition: opacity 0.14s ease;
}
.card:hover { border-color: var(--rule-strong); transform: translateY(-1px); }
.card:hover::before { opacity: 1; }
.card p { font-size: var(--fs-sm); color: var(--ink-soft); }

/* Before/after demo used on the home page */
.demo-split { display: grid; gap: 1rem; grid-template-columns: 1fr; margin-top: 1.5rem; }
@media (min-width: 60rem) { .demo-split { grid-template-columns: 1fr 1fr; align-items: start; } }
.demo-pane { border: 1px solid var(--rule); border-radius: 8px; overflow: hidden; background: var(--paper-raised); }
.demo-pane > .mono-label {
  display: block; padding: 0.5rem 0.85rem;
  border-bottom: 1px solid var(--rule); background: var(--paper-sunk);
}
.demo-doc {
  margin: 0; padding: 0.85rem 1rem;
  font-family: var(--font-mono); font-size: 0.8125rem; line-height: 1.65;
  color: var(--ink-soft); white-space: pre-wrap; overflow-x: auto;
}
.demo-pane .codeblock { margin: 0; border: 0; border-radius: 0; }

/* Email capture */
.signup { border: 1px solid var(--rule); border-radius: 8px; padding: 1.5rem; background: var(--paper-raised); }
.signup form { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 0.9rem; }
.signup input[type="email"] {
  flex: 1 1 16rem; min-width: 0;
  font-family: var(--font-mono); font-size: var(--fs-sm);
  padding: 0.6rem 0.8rem; border: 1px solid var(--rule-strong);
  border-radius: var(--radius); background: var(--paper); color: var(--ink);
}
.signup input[type="email"]::placeholder { color: var(--ink-faint); }

/* =========================== Footer ============================ */

.site-footer { border-top: 1px solid var(--rule); margin-top: 4rem; padding-block: 2.5rem 3rem; }
.footer-grid { display: grid; gap: 1.75rem; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.footer-grid h2 { font-size: var(--fs-xs); font-family: var(--font-mono); letter-spacing: 0.09em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; margin-bottom: 0.6rem; }
.footer-grid ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.footer-grid a { font-size: var(--fs-sm); color: var(--ink-soft); text-decoration: none; }
.footer-grid a:hover { color: var(--accent); }
.footer-note { margin-top: 2rem; padding-top: 1.25rem; border-top: 1px solid var(--rule); font-size: var(--fs-sm); color: var(--ink-faint); }
.footer-note a { color: var(--ink-soft); }

/* ============================ 404 ============================== */

.error-page { padding-block: 5rem; text-align: center; }
.error-page .mono-label { display: block; margin-bottom: 0.75rem; }
.error-page p { max-width: 40ch; margin: 1rem auto 1.75rem; color: var(--ink-soft); }
