/* Organic — design-system tokens and component classes. This file is the source of truth for the system's look; retune it here and see readme.md. */
/* Fonts are SELF-HOSTED, deliberately. This was an `@import` from
   fonts.googleapis.com until 2026-08-12, when Google's own css2 stylesheet
   began handing out DM Sans v17 file URLs that 404 on fonts.gstatic.com (a
   Google-side version skew: the JetBrains Mono URLs in the same response
   resolved fine). The failure surfaced as a console error on a different
   docs page each run — whichever page happened to need the broken subset —
   and the browser smoke suite fails a page with any console error, so a
   third party's cache state could redden CI at any time. A docs site does
   not need a runtime dependency on another origin to render its own text.

   The files under fonts/ are the upstream Google Fonts binaries, taken from
   the @fontsource-variable packages (byte-identical to what gstatic serves
   for the same subset), latin + latin-ext only: the site is English, and
   every other subset was already unused. Both faces are VARIABLE fonts —
   DM Sans carries opsz 9..40 + wght, JetBrains Mono wght — so one file per
   subset covers every weight the pages ask for.

   The same file is declared at 400, 500 and 600 rather than as one
   `font-weight: 400 600` range ON PURPOSE: it reproduces exactly the face
   list the Google stylesheet used to publish. Pages ask for weights it
   never offered (520, 620, 650, 680, 700), and CSS font matching snapped
   those to the nearest declared face. A range would instead render each one
   at its literal weight and quietly restyle the site. Duplicating the
   declaration keeps the type identical; the browser still fetches each URL
   once. The unicode-range values are Google's, so a page still downloads
   latin-ext only when it actually contains such a character. */
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/dm-sans-latin.woff2) format('woff2');
  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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 500; font-display: swap;
  src: url(fonts/dm-sans-latin.woff2) format('woff2');
  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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/dm-sans-latin.woff2) format('woff2');
  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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 500; font-display: swap;
  src: url(fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/jetbrains-mono-latin.woff2) format('woff2');
  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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono'; font-style: normal; font-weight: 500; font-display: swap;
  src: url(fonts/jetbrains-mono-latin.woff2) format('woff2');
  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+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono'; font-style: normal; font-weight: 500; font-display: swap;
  src: url(fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --color-bg: #f5ead8;
  --color-surface: #ebddc5;
  --color-text: #201e1d;
  --color-accent: #c67139;
  --color-accent-2: #7a8a5e;
  /* Hairlines are alpha over the page, never a solid grey: they then sit
     correctly on the card, the header and the page at once, and they darken
     rather than disappear when something is layered over them. */
  --color-divider: rgba(32, 30, 29, 0.12);
  --color-divider-strong: rgba(32, 30, 29, 0.2);
  --quiet-hover: rgba(32, 30, 29, 0.04);
  --btn-primary-hover: #000;
  --card-sheen: rgba(255, 255, 255, 0.5);

  /* Tonal ramps — generated in OKLCH on one shared lightness scale, so the
     same step of any role matches the others in visual value. */
  --color-neutral-100: #f9f4ed;
  --color-neutral-200: #eee7db;
  --color-neutral-300: #dcd3c4;
  --color-neutral-400: #c0b6a5;
  --color-neutral-500: #a19786;
  --color-neutral-600: #82796a;
  --color-neutral-700: #645c50;
  --color-neutral-800: #474238;
  --color-neutral-900: #2e2b25;

  --color-accent-100: #fff2eb;
  --color-accent-200: #ffe1d0;
  --color-accent-300: #ffc6a5;
  --color-accent-400: #f6a06b;
  --color-accent-500: #d67f48;
  --color-accent-600: #b2622d;
  --color-accent-700: #8c491a;
  --color-accent-800: #643312;
  --color-accent-900: #402310;

  --color-accent-2-100: #f0fae1;
  --color-accent-2-200: #e1eecc;
  --color-accent-2-300: #ccdbb2;
  --color-accent-2-400: #aebf92;
  --color-accent-2-500: #8fa073;
  --color-accent-2-600: #728157;
  --color-accent-2-700: #56633f;
  --color-accent-2-800: #3d472b;
  --color-accent-2-900: #272e1b;

  /* One clean geometric sans carries everything — display, prose and UI — and
     one mono carries code and every micro-label. Display type is weight 500
     with negative tracking; micro-labels invert that with positive tracking on
     the mono face. That opposition is the whole typographic idea; there is no
     third family and no bold display weight. */
  --font-sans: "DM Sans", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
  /* --font-heading/--font-body are the older spellings, kept so pages written
     against them keep working. They now name the same face. */
  --font-heading: var(--font-sans);
  --font-heading-weight: 500;
  --font-body: var(--font-sans);

  --space-1: 4.4px;
  --space-2: 8.8px;
  --space-3: 13.2px;
  --space-4: 17.6px;
  --space-6: 26.4px;
  --space-8: 35.2px;

  --radius-sm: 8px;
  --radius-md: 16px;
  --radius-lg: 28px;

  /* Elevation — derived from the ground: soft ink-tinted shadows on a
     light theme, a hairline edge + ambient darkness on a dark one. */
  --shadow-sm: 0 1px 2px color-mix(in srgb, #2e2b25 14%, transparent);
  --shadow-md: 0 3px 10px color-mix(in srgb, #2e2b25 16%, transparent);
  --shadow-lg: 0 12px 32px color-mix(in srgb, #2e2b25 22%, transparent);
}

body {
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-feature-settings: "ss01", "ss02";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4 { font-family: var(--font-sans); font-weight: var(--font-heading-weight); }

.washed{filter:saturate(0.6) contrast(0.85) brightness(1.1) opacity(0.94)}

/* ══════════════════════════════════════════════════════════════════════════
   Components — built with the tokens above. Plain CSS
   on plain HTML: no JavaScript, no build step. Each class is documented in
   readme.md and demonstrated in foundations/ and components/.
   ══════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-size: 15px; line-height: 1.5; font-weight: 400; }
.skip-link {
  position: fixed; z-index: 10000; top: 10px; left: 10px;
  padding: 9px 13px; border-radius: var(--radius-sm); background: var(--color-text);
  color: var(--color-bg); font-weight: 700; transform: translateY(-180%);
}
.skip-link:focus { transform: translateY(0); }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 4px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important; animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important; transition-duration: 0.01ms !important;
  }
}
/* Set by theme.js for one frame around a light/dark swap. Hover transitions
   are for hovering; a theme change must land instantly and completely, or the
   page spends a quarter-second in colours belonging to neither theme. */
[data-theme-switching] *, [data-theme-switching] *::before, [data-theme-switching] *::after {
  transition: none !important;
}
/* Display type: weight 500, never bold, tight negative tracking, and a line
   height that closes up as the size grows. Sizes are fluid so the same scale
   serves a 390px phone and a 1280px desktop without a second set of rules —
   the clamp floors are chosen so the longest heading on the site still fits
   inside 390px minus the page gutters. */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-sans); font-weight: var(--font-heading-weight);
  line-height: 1.1; letter-spacing: -0.03em; margin: 0 0 var(--space-2);
  overflow-wrap: break-word;
}
h1 { font-size: clamp(2.15rem, 5.2vw, 3rem); letter-spacing: -0.035em; line-height: 1.04; }
h2 { font-size: clamp(1.6rem, 3.4vw, 2.1rem); letter-spacing: -0.035em; line-height: 1.08; }
h3 { font-size: 1.2rem; letter-spacing: -0.02em; }
h4 { font-size: 1rem; font-weight: 600; letter-spacing: -0.015em; }
h5 { font-size: 0.94rem; font-weight: 600; letter-spacing: -0.015em; }
/* h6 is a micro-label, so it flips to the mono face and positive tracking. */
h6, .eyebrow {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; line-height: 1.5;
  color: var(--color-neutral-600);
}
p { margin: 0 0 var(--space-3); }
/* Links carry no decoration by default — see the "links" section at the foot of
   this file, which adds an underline back inside prose only. */
a { color: var(--color-text); text-decoration: none; }
img { display: block; max-width: 100%; }
figure { margin: 0; }
.toc-sub, .ck-rail a span:first-child { color: var(--color-neutral-700) !important; }
figcaption {
  font-size: 11px; margin-top: var(--space-1);
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.text-muted { color: color-mix(in srgb, var(--color-text) 55%, transparent); }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 4px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

/* — rules — */
.hr {
  height: 1px; border: 0; margin: var(--space-4) 0;
  background: var(--color-divider);
}

/* — buttons —
   The primary call to action is the page's own foreground INVERTED — dark text
   on a light slab in the dark theme, light text on a dark slab in the light
   one. It is never the accent: the accent is reserved for state and detail
   (list markers, the `$` prompt, focus rings, the active sidebar bar), so the
   one loud element on a page is the thing you are meant to press, and the
   accent keeps meaning "look here" rather than "this is a button".
   The lift on hover is 1px — enough to feel live, small enough not to reflow. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  font-family: var(--font-sans); font-weight: 500;
  font-size: 14px; line-height: 1.2; letter-spacing: -0.01em;
  color: var(--color-text);
  background: transparent; border: 1px solid transparent;
  padding: 10px 18px;
  border-radius: 8px;
  transition: transform 0.18s ease, background 0.18s ease,
    border-color 0.18s ease, color 0.18s ease;
}
.btn svg { display: block; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary {
  background: var(--color-text); color: var(--color-bg); font-weight: 600;
  border-color: transparent;
}
.btn-primary:hover { transform: translateY(-1px); background: var(--btn-primary-hover); }
.btn-primary:active { transform: translateY(0); }
.btn-secondary {
  color: var(--color-neutral-700);
  border-color: var(--color-divider);
  background: transparent;
}
.btn-secondary:hover {
  color: var(--color-text);
  border-color: var(--color-divider-strong);
  background: var(--quiet-hover);
}
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 10%, transparent); }
.btn-ghost {
  color: var(--color-neutral-700);
  border-color: transparent; padding-inline: var(--space-2);
}
.btn-ghost:hover { color: var(--color-text); background: var(--quiet-hover); }
.btn-ghost:active { background: color-mix(in srgb, var(--color-text) 10%, transparent); }
.btn-lg { padding: 14px 22px; font-size: 15px; border-radius: 10px; }
.btn-icon { width: 36px; height: 36px; padding: 0; }
.btn-block { width: 100%; margin-top: var(--space-2); }
@media (prefers-reduced-motion: reduce) { .btn:hover { transform: none; } }

/* — forms — */
.field > label {
  display: block; font-size: 12px; margin-bottom: 5px;
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
.input {
  width: 100%; min-height: 36px; padding: 6px 10px; font: inherit;
  font-size: 14px; color: var(--color-text); caret-color: var(--color-accent);
  background: var(--color-surface);
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible { border-color: var(--color-accent); outline-offset: 0; }
textarea.input { min-height: 90px; resize: vertical; }
.radio { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; }
.radio input, .seg-opt input {
  position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.radio .dot {
  width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 1.5px solid var(--color-divider);
}
.radio:hover .dot { border-color: var(--color-accent); }
.radio input:checked + .dot {
  border-color: var(--color-accent); background: var(--color-accent);
  box-shadow: inset 0 0 0 4px var(--color-bg);
}
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.seg {
  display: inline-flex; overflow: hidden;
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.seg-opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; font-size: 13px; cursor: pointer;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt:has(input:checked) { background: var(--color-accent); color: var(--color-bg); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

/* — cards — */
/* A card is a 1px hairline and a barely-there top sheen — no drop shadow. The
   edge is what separates it from the page; the gradient is what stops the edge
   from reading as a drawn box. */
.card {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3); border-radius: var(--radius-md);
  border: 1px solid var(--color-divider);
  background-color: var(--color-surface);
  background-image: linear-gradient(180deg, var(--card-sheen), transparent 60%);
}
.card-kicker {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-accent);
}
.card-title {
  font-family: var(--font-sans); font-weight: 500;
  font-size: 17px; line-height: 1.25; letter-spacing: -0.02em;
}
.card-body { margin: 0; font-size: 13px; opacity: 0.8; flex: 1; }
.card-meta {
  display: flex; align-items: center; gap: 6px; font-size: 11px;
  color: color-mix(in srgb, var(--color-text) 50%, transparent);
}
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

/* — tags — */
.tag {
  display: inline-flex; align-items: center;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.06em; padding: 4px 11px;
  border-radius: calc(var(--radius-md) * 0.75);
}
.tag-accent { background: var(--color-accent-100); color: var(--color-accent-800); }
.tag-accent-2 { background: var(--color-accent-2-100); color: var(--color-accent-2-800); }
.tag-neutral { background: var(--color-neutral-100); color: var(--color-neutral-800); }
.tag-outline { border: 1px solid var(--color-accent); color: var(--color-accent); }

/* — navigation — */
.nav {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: none;
}
.nav-brand {
  font-family: var(--font-sans); font-weight: 650;
  font-size: 18px; letter-spacing: -0.025em; margin-right: auto;
}
.nav a { color: inherit; text-decoration: none; font-size: 14px; }
.nav a:hover, .nav a[aria-current='page'] { color: var(--color-accent); }

/* — tables — */
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
  text-align: left; font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-neutral-600);
  padding: var(--space-2); border-bottom: 1px solid var(--color-divider);
}
.table td {
  padding: var(--space-2);
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
}
.table tbody tr:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }

/* — dialog — */
.dialog-backdrop {
  position: fixed; inset: 0; display: grid; place-items: center;
  padding: var(--space-4);
  background: color-mix(in srgb, var(--color-neutral-900) 50%, transparent);
}
.dialog {
  width: min(440px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4); border-radius: var(--radius-lg);
  background: var(--color-surface); box-shadow: var(--shadow-lg);
}
.dialog-title {
  font-family: var(--font-sans); font-weight: 500;
  font-size: 20px; letter-spacing: -0.025em;
}
.dialog-body { font-size: 14px; opacity: 0.85; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }

/* — rounded frame: everything softens, small controls go pill —
   Buttons are deliberately NOT in that list: they keep the 8px radius set
   above. A pill reads as a chip; the square-ish slab is what makes the primary
   action look pressable rather than decorative. */
.card, .dialog { border-radius: calc(var(--radius-lg) * 1.15); }
.tag, .seg, .input { border-radius: 999px; }
.input { padding-inline: 14px; }


/* ══════════════════════════════════════════════════════════════════════════
   Site theming — the two axes, in ONE place
   ──────────────────────────────────────────────────────────────────────────
   Every docs page (hand-written and generated) links this file, so the palette
   lives here and nowhere else. Two independent axes compose:

     data-palette  "organic" (the default, warm) or "slate" (the shipped site)
     data-theme    "light" or "dark", owned by theme.js

   These blocks were previously copy-pasted verbatim into three page <style>
   blocks and paraphrased a fourth time inside tools/make-docs-pages.py. They
   are now declared once. Order matters: `:root` above has the same specificity
   as `[data-palette]`, so these must stay BELOW it.
   ══════════════════════════════════════════════════════════════════════ */

[data-theme="dark"] {
  --color-bg: #211E1B; --color-surface: #2A2622; --color-text: #F5EAD8;
  --color-divider: rgba(255, 255, 255, 0.09);
  --color-divider-strong: rgba(255, 255, 255, 0.16);
  --quiet-hover: rgba(255, 255, 255, 0.03);
  --btn-primary-hover: #fff;
  --card-sheen: rgba(255, 255, 255, 0.015);
  --color-accent: #D98A52; --color-accent-2: #97A876;
  --color-neutral-100: #332E29; --color-neutral-200: #3A342E; --color-neutral-300: #46403A;
  --color-neutral-400: #5C554C; --color-neutral-500: #847767; --color-neutral-600: #A89B87;
  --color-neutral-700: #C9BCA7; --color-neutral-800: #DACDB8; --color-neutral-900: #EFE6D4;
  --color-accent-100: #3A2B1D; --color-accent-200: #4A3625; --color-accent-300: #5C4229;
  --color-accent-600: #E09B66; --color-accent-700: #E4A878; --color-accent-800: #EDBF97;
  --color-accent-900: #F4D5B8;
  --color-accent-2-100: #2A3122; --color-accent-2-200: #353D2B; --color-accent-2-700: #B5C69A;
  --color-accent-2-800: #C8D6B0; --color-accent-2-900: #DBE5C9;
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
  --shadow-md: 0 3px 10px rgba(0,0,0,0.45);
  --shadow-lg: 0 12px 32px rgba(0,0,0,0.55);
}

/* Terminal chrome. A terminal reads as a terminal in BOTH themes, so these
   stay dark either way — which also means their contrast is verified once,
   not twice. Keep every foreground here at 4.5:1 or better on --term-bg. */
[data-palette] {
  --term-bg: #26211C; --term-hd: #1C1815; --term-btn: #38312A; --term-bd: #463D34;
  --term-muted: #A5988A; --term-text: #E6DCC9;
  --term-ok: #9DB380; --term-bad: #D97A62; --term-warn: #DDA84E;
}

[data-palette="slate"] {
  --term-bg: #14181C; --term-hd: #0E1114; --term-btn: #23282E; --term-bd: #2E353C;
  --term-muted: #97A1AD; --term-text: #D7DEE4;
  --term-ok: #7CC08B; --term-bad: #E5786B; --term-warn: #E0B04A;

  --color-bg: #FBFAF7; --color-surface: #FFFFFF; --color-text: #1B1F24;
  --color-divider: rgba(27, 31, 36, 0.12);
  --color-divider-strong: rgba(27, 31, 36, 0.2);
  --quiet-hover: rgba(27, 31, 36, 0.04);
  --btn-primary-hover: #000;
  --card-sheen: rgba(255, 255, 255, 0.5);
  /* Depth on this site comes from the hairline, not from a drop shadow. */
  --shadow-sm: none; --shadow-md: none; --shadow-lg: none;
  --color-accent: #B4540A; --color-accent-2: #2A6F4E;
  --color-neutral-100: #F3F0EA; --color-neutral-200: #E7E2D9; --color-neutral-300: #D8D2C7;
  --color-neutral-400: #B9B3A8; --color-neutral-500: #7E858D; --color-neutral-600: #5B6570;
  --color-neutral-700: #3A424B; --color-neutral-800: #2B3138; --color-neutral-900: #1B1F24;
  --color-accent-100: #FBF1E7; --color-accent-200: #F5DFC8; --color-accent-300: #EECDA9;
  --color-accent-600: #9C4A08; --color-accent-700: #8A3F06; --color-accent-800: #6F3305;
  --color-accent-900: #542704;
  --color-accent-2-100: #EAF4EC; --color-accent-2-200: #D3E7D8; --color-accent-2-700: #1F5238;
  --color-accent-2-800: #183F2B; --color-accent-2-900: #112D1F;
}

[data-palette="slate"][data-theme="dark"] {
  --color-bg: #14171B; --color-surface: #1A1E23; --color-text: #E9E7E2;
  --color-divider: rgba(255, 255, 255, 0.08);
  --color-divider-strong: rgba(255, 255, 255, 0.14);
  --quiet-hover: rgba(255, 255, 255, 0.03);
  --btn-primary-hover: #fff;
  --card-sheen: rgba(255, 255, 255, 0.015);
  --color-accent: #F0975A; --color-accent-2: #7CC08B;
  --color-neutral-100: #1E2329; --color-neutral-200: #292E34; --color-neutral-300: #343B43;
  --color-neutral-400: #454D57; --color-neutral-500: #69727C; --color-neutral-600: #9AA3AC;
  --color-neutral-700: #C3C9CF; --color-neutral-800: #D7DEE4; --color-neutral-900: #EDF1F4;
  --color-accent-100: #2B2018; --color-accent-200: #3A2A1C; --color-accent-300: #4A3520;
  --color-accent-600: #F2A470; --color-accent-700: #F5B183; --color-accent-800: #F8C7A3;
  --color-accent-900: #FBDCC5;
  --color-accent-2-100: #16281C; --color-accent-2-200: #1E3626; --color-accent-2-700: #A8D4B3;
  --color-accent-2-800: #C2E2CA; --color-accent-2-900: #DCEFE1;
  --shadow-sm: none; --shadow-md: none; --shadow-lg: none;
}

/* ── One vocabulary, two spellings ─────────────────────────────────────────
   The generated docs pages were written against a second, shorter token set
   (--paper/--ink/--line/…). Rather than keep two palettes in step by hand,
   the short names are now ALIASES of the --color-* ramp above. A colour
   therefore changes in exactly one place. Aliases resolve at use time against
   whichever palette/theme block won on :root, so one declaration covers all
   four combinations. */
:root {
  --paper: var(--color-bg);
  --surface: var(--color-surface);
  --ink: var(--color-text);
  --muted: var(--color-neutral-600);
  /* Running prose sits one step back from the page's foreground, with <strong>
     promoted to the full foreground — the same split t3code uses. This is the
     step most likely to fail a contrast check, so it is pinned to the ramp's
     600 level, which measures 7.0:1 on the dark page and 5.7:1 on the light
     one. tools/site-smoke.mjs runs axe in BOTH themes and will say so if a
     retune breaks it. */
  --prose: var(--color-neutral-600);
  --accent: var(--color-accent);
  --accent-ink: var(--color-bg);
  --accent-soft: color-mix(in srgb, var(--color-accent) 10%, transparent);
  --accent-line: color-mix(in srgb, var(--color-accent) 28%, transparent);
  --line: var(--color-divider);
  --line-soft: color-mix(in srgb, var(--color-divider) 60%, transparent);
  --line-strong: var(--color-divider-strong);
  --code-bg: var(--color-neutral-100);
  --mono: var(--font-mono);
  --sans: var(--font-sans);
}
/* --accent-ink is text ON the accent; in the light palettes white beats the
   page background against that mid-tone orange. */
[data-palette="slate"] { --accent-ink: #FFFFFF; }
[data-theme="dark"] { --accent-ink: #211406; }
[data-palette="slate"][data-theme="dark"] { --accent-ink: #1A1206; }

.nav a.btn-primary, .nav a.btn-primary:hover { color: var(--color-bg); }
.card { border-radius: var(--radius-md, 16px); }

/* ── links ────────────────────────────────────────────────────────────────
   Underlines are earned, not universal. `a` above is undecorated, so nav
   items, sidebar entries, footers, cards and buttons are plain and shift
   colour on hover. Only links inside running prose get an underline, and it
   is a soft accent-tinted rule under body-coloured text — hover brightens the
   RULE, never the metrics: changing text-decoration-thickness on hover reflows
   the line and makes the text shimmer.

   `:where()` keeps the whole prose-link selector at single-element
   specificity, so the ONE exception below — `.navlist`, a list of nothing but
   links, which is navigation rather than prose — can switch the underline off
   with an ordinary class instead of an escalation war. */
:where(main, [role="main"]) :where(p, li, td, dd, dt, blockquote, figcaption)
  a:where(:not(.btn):not(.hlink)) {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--color-accent) 55%, transparent);
  text-underline-offset: 4px;
}
:where(main, [role="main"]) :where(p, li, td, dd, dt, blockquote, figcaption)
  a:where(:not(.btn):not(.hlink)):hover {
  text-decoration-color: var(--color-accent);
}
/* A contents list — every item is a bare link and nothing else — is a table of
   contents, not running prose. tools/make-docs-pages.py marks such lists
   structurally, at build time, from that same "link-only" property; this is
   the only place the class is consumed. */
:where(main, [role="main"]) .navlist a,
:where(main, [role="main"]) .navlist a:hover { text-decoration: none; }
:where(main, [role="main"]) .navlist a:hover { color: var(--color-accent); }
a:not([class]):hover, .nav a:hover { color: var(--color-accent); }

/* ── code blocks and terminals ────────────────────────────────────────────
   One component, used by the generated pages (emitted by
   tools/make-docs-pages.py) and by the hand-written pages alike.

     .cb            the card: hairline border, 12px radius, clipped corners
     .cb-bar        the header strip — language label, and dots on a terminal
     .cb > pre      the only scroller; theme.js gives it tabindex="0" when it
                    actually overflows, so a wide block scrolls inside itself
                    and never widens the document
     .cb-term       the terminal variant: always-dark chrome
       .pr          a shell prompt — accent-coloured and unselectable, so
                    copying a session yields runnable text
       .out         program output — dimmed, which is what separates what you
                    typed from what came back
       .cm          a comment — dimmed the same way
   ══════════════════════════════════════════════════════════════════════ */
.cb {
  margin: 1rem 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  background: var(--code-bg);
}
.cb-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--color-text) 5%, var(--code-bg));
}
.cb-lang {
  font-family: var(--mono); font-size: 11px; line-height: 1.6;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--color-neutral-700);
}
.cb-dots { display: inline-flex; gap: 6px; flex: none; }
.cb-dots span {
  display: block; width: 9px; height: 9px; border-radius: 999px;
  background: var(--term-btn);
}
.cb > pre {
  margin: 0; border: 0; border-radius: 0; background: none;
  padding: 0.85rem 1rem;
  overflow-x: auto;
  font-family: var(--mono); font-size: 0.82rem; line-height: 1.6;
  scrollbar-width: thin;
}
.cb > pre > code {
  background: none; border: 0; padding: 0; font-size: 1em; font-family: inherit;
}
.cb-term { background: var(--term-bg); border-color: var(--term-bd); }
.cb-term .cb-bar { background: var(--term-hd); border-bottom-color: var(--term-bd); }
.cb-term .cb-lang { color: var(--term-muted); }
.cb-term > pre { color: var(--term-text); }
.cb-term .pr { color: var(--term-warn); user-select: none; -webkit-user-select: none; }
.cb-term .out, .cb-term .cm { color: var(--term-muted); }
.cb-term .ok { color: var(--term-ok); }
.cb-term .bad { color: var(--term-bad); }

/* A standalone terminal on a hand-written page: same chrome, content authored
   inline rather than compiled from Markdown. */
.term {
  border-radius: var(--radius-md, 16px);
  overflow: hidden;
  border: 1px solid var(--term-bd);
  background: var(--term-bg);
}
.term-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px;
  background: var(--term-hd);
  border-bottom: 1px solid var(--term-bd);
}
.term-title {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em;
  color: var(--term-muted);
}
.term-body {
  padding: 14px 18px;
  font-family: var(--mono); font-size: 0.8rem; line-height: 1.9;
  color: var(--term-text);
  overflow-x: auto;
  scrollbar-width: thin;
}
.term-body pre { margin: 0; font: inherit; }
.term-row { display: flex; gap: 12px; }
.term-row > .cmd { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* A terminal with no window chrome — the body's look on a bare element, for
   panes that are one command deep or are filled in by page script. */
.term-flat {
  border: 1px solid var(--term-bd);
  border-radius: var(--radius-md, 16px);
  background: var(--term-bg);
  color: var(--term-text);
  padding: 14px 18px;
  font-family: var(--mono); font-size: 0.82rem; line-height: 1.8;
  overflow-x: auto; scrollbar-width: thin;
}
:is(.term, .term-flat) .pr { color: var(--term-warn); user-select: none; -webkit-user-select: none; }
:is(.term, .term-flat) :is(.out, .cm) { color: var(--term-muted); }
:is(.term, .term-flat) .ok { color: var(--term-ok); }
:is(.term, .term-flat) .bad { color: var(--term-bad); }
/* Multi-line authored content that keeps its own whitespace (trees, captured
   output). A <div>, not a <pre>: the CLI-surface test in
   crates/cli/tests/docs_commands.rs treats <pre>/<code> as "a command shown to
   a reader", and these panes are illustrations of output.

   ONE BLOCK PER CAPTURED LINE (`.tl`). The whole point of these panes is that
   a reader can read the command, so a line wider than the pane has to wrap —
   the earlier `white-space: pre` on the container let every transcript run
   off its own right edge, readable only by scrolling a control that gave no
   sign it was scrollable. Wrapping needs a per-line box because `text-indent`
   applies to the first line of a BLOCK, not to each preserved newline inside
   one; with a block per line, `padding-left` plus a matching negative
   `text-indent` hangs every continuation 4ch in from where its line started,
   so a wrap is visible as a wrap and never mistaken for the next line.

   Whitespace WITHIN a line is still preserved (`pre-wrap`) — the tree glyphs
   and the column alignment of trailing comments are part of the capture. The
   newlines BETWEEN the blocks are ordinary markup indentation, which is why
   the container is `white-space: normal`: under `pre` they would each render
   as an extra blank line. All content must therefore sit inside a `.tl`. */
.term-lines { white-space: normal; }
.term-lines > .tl {
  display: block;
  white-space: pre-wrap;
  padding-left: 4ch;
  text-indent: -4ch;
}
