  /* Light is the DEFAULT for every visitor, on purpose. There is no `prefers-color-scheme`
     rule anywhere on this site: the OS setting is ignored, and dark applies only to someone who
     clicks the toggle, which persists in localStorage under `opyt-theme`. Re-adding a
     `prefers-color-scheme` block would silently give dark-OS visitors dark again. */
  :root {
    --bg: #ffffff;
    --surface: #f4f4f4;
    --panel: #fafafa;
    --text: #141414;
    --muted: #5c5c5c;
    --faint: #8f8f8f;
    --accent: #dd3418;
    --accent-strong: #b32a12;
    --line: #e3e3e3;
    /* transcript palette: light theme reads as paper, not as an inverted terminal */
    --code-bg: #f7f7f5;
    --code-text: #1a1a1a;
    --code-dim: #63635e;
    --code-line: #e0e0dc;
    --code-accent: #c22d10;
    --card: #ffffff;
    color-scheme: light;
  }
  :root[data-theme="dark"] {
    --bg: #0c0c0c;
    --surface: #171717;
    --panel: #101010;
    --text: #ececec;
    --muted: #9c9c9c;
    --faint: #6d6d6d;
    --accent: #ff5a33;
    --accent-strong: #ff7a55;
    --line: #262626;
    --code-bg: #0b0b0b;
    --code-text: #e4e4e4;
    --code-dim: #7c7c7c;
    --code-line: #242424;
    --code-accent: #ff5a33;
    --card: #1b1b1b;
    color-scheme: dark;
  }

  * { box-sizing: border-box; }
  [hidden] { display: none !important; }
  body {
    background: var(--bg);
    color: var(--text);
    font-family: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 15px;
    line-height: 1.7;
    margin: 0;
    /* a URL, a package spec or a handle is one unbreakable token, and a 320px screen is
       narrower than several of them: the install command's astral.sh URL, the hit rows in an
       artifact panel. Break inside a token only when it would otherwise overflow — the break
       is soft, so a copied command still comes out on one line.

       `anywhere`, not `break-word`. The two differ in one place that matters here: only
       `anywhere` reduces an element's MIN-CONTENT size, and min-content is what sizes a grid
       track and a flex item's automatic minimum. Under `break-word` the `.hits` grid and the
       `details.t-tool` summary were still sized to their longest URL and could not shrink, so
       the rule did not mean what it said. `min-width: 0` on those items does not fix it —
       measured 2026-08-31, the summary's overlong run is an ANONYMOUS flex item that no
       selector reaches. Cost of `anywhere`: a future grid or flex track that wants to hold a
       token intact will collapse instead; undo it there with `overflow-wrap: normal`. */
    overflow-wrap: anywhere;
  }
  ::selection { background: var(--accent); color: var(--bg); }

  a { color: var(--text); text-decoration-color: var(--faint); text-underline-offset: 3px; }
  a:hover { text-decoration-color: var(--accent); }
  a:focus-visible, button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  /* ---------- the sheet ---------- */
  .sheet {
    max-width: 1120px; margin: 0 auto;
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
    min-height: 100vh;
  }

  /* ---------- the tile mark ---------- */
  /* sharp-cornered terminal tile: tile takes the text color, glyph punches through
     to the page ground, cursor is the accent, so one SVG works in both themes */
  .tile .tile-bg { fill: currentColor; }
  .tile .tile-glyph { stroke: var(--bg); }
  .tile .tile-cursor { fill: var(--accent); }
  /* only the hero mark blinks; the manbar tile and the footer wordmark stay static */
  .hero-mark .tile-cursor { animation: blink 1.1s steps(1) infinite; }
  @keyframes blink { 50% { opacity: 0; } }

  /* ---------- man header ---------- */
  .manbar {
    display: flex; align-items: center; gap: 18px;
    padding: 14px 24px;
    border-bottom: 1px solid var(--line);
    font-size: 13px;
  }
  .brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
  .brand .tile { width: 30px; height: 30px; display: block; }
  .brand .name {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-weight: 700; letter-spacing: 0.02em;
  }
  .manbar .links { margin-left: auto; display: flex; gap: 20px; align-items: center; }
  .manbar .links a { text-decoration: none; color: var(--muted); }
  .manbar .links a:hover { color: var(--text); }
  .manbar .go {
    background: var(--accent); color: #fff !important;
    padding: 5px 14px; font-weight: 600;
  }
  .manbar .go:hover { background: var(--accent-strong); }
  /* Star CTA. Deliberately NOT github/buttons.js: that widget renders a live count, and the
     repo's count is 0, which reads worse than no number at all. It is also a third-party
     script on every page. Revisit the widget only once the count argues for us. */
  .manbar .links .star-btn {
    display: flex; align-items: center; gap: 6px;
    border: 1px solid var(--line); padding: 4px 11px;
    color: var(--muted);
  }
  .manbar .links .star-btn:hover { border-color: var(--faint); color: var(--text); }
  .manbar .links .star-btn svg { width: 13px; height: 13px; display: block; }
  .manbar .links .star-btn:hover svg { stroke: var(--accent); }
  .theme-btn {
    font-family: inherit; font-size: 13px;
    background: none; border: none; cursor: pointer;
    color: var(--muted); padding: 5px 0;
    display: flex; align-items: center; gap: 7px;
  }
  .theme-btn:hover { color: var(--text); }
  .theme-btn svg { width: 14px; height: 14px; display: block; }
  /* one glyph, rotated by theme: the filled half reads as the mode you switch to */
  :root[data-theme="dark"] .theme-btn svg { transform: rotate(180deg); }

  /* ---------- sections ---------- */
  .sec { border-bottom: 1px solid var(--line); }
  .sec:last-of-type { border-bottom: none; }
  /* Sections are separated by one hairline, so the air around it is what actually reads as a
     break. The hero keeps the old rhythm — it is centered against a viewport height, and more
     padding there just shrinks the first thing you see. */
  .con { padding: 132px 56px 144px; min-width: 0; }
  .sec.hero .con { padding: 92px 56px 104px; }
  .sec.also .con { padding: 48px 56px 44px; }

  /* section label: a landmark the eye can catch while scrolling */
  .kicker {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 10.5px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--accent); margin: 0 0 18px;
  }

  h2 {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 27px; font-weight: 700; line-height: 1.32;
    margin: 0 0 20px; letter-spacing: -0.01em;
    max-width: 32ch;
  }
  h2 .dot { color: var(--accent); }
  /* The lede belongs to the heading above it, so this gap groups rather than separates:
     tighter than the step-to-step break below it, not equal to it. */
  .lede { color: var(--muted); margin: 0 0 56px; max-width: 70ch; }
  .lede strong { color: var(--text); font-weight: 600; }

  /* ---------- NAME / hero ---------- */
  h1 {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: clamp(24px, 4.8vw, 56px);
    font-weight: 780;
    line-height: 1.18;
    letter-spacing: -0.015em;
    text-transform: uppercase;
    margin: 0 0 26px;
    max-width: 21ch;
  }
  h1 .dot { color: var(--accent); }
  .hero-sub { color: var(--muted); margin: 0 0 36px; font-size: 16px; max-width: 64ch; }
  .hero-sub strong { color: var(--text); font-weight: 600; }

  .install-line {
    display: flex; align-items: center; max-width: 860px;
    background: var(--code-bg); border: 1px solid var(--line);
    overflow: hidden;
  }
  .install-line code {
    font-size: 13.5px; color: var(--code-text);
    padding: 13px 16px; flex: 1; white-space: nowrap; overflow-x: auto;
  }
  .install-line code .p { color: var(--code-dim); user-select: none; }
  .copy-btn {
    font-family: inherit; font-size: 12px; letter-spacing: 0.05em;
    background: none; border: none; border-left: 1px solid var(--code-line);
    color: var(--code-dim); padding: 13px 16px; cursor: pointer; flex-shrink: 0;
  }
  .copy-btn:hover { color: var(--code-text); }

  .hero-ctas { display: flex; gap: 16px; margin-top: 20px; font-size: 13.5px; flex-wrap: wrap; }
  .hero-ctas a { text-decoration: none; padding: 10px 18px; }
  .cta-primary { background: var(--accent); color: #fff; font-weight: 600; }
  .cta-primary:hover { background: var(--accent-strong); }
  .cta-secondary { border: 1px solid var(--line); color: var(--text); }
  .cta-secondary:hover { border-color: var(--faint); }
  .cta-secondary::before { content: "[ "; color: var(--faint); }
  .cta-secondary::after { content: " ]"; color: var(--faint); }

  /* the star ask, above the footer on index.html */
  .star-card {
    display: flex; align-items: center; justify-content: space-between;
    gap: 24px; flex-wrap: wrap;
    border: 1px solid var(--line); background: var(--panel);
    padding: 22px 24px;
  }
  .star-card h3 { font-size: 15px; margin: 0 0 6px; }
  .star-card p { margin: 0; color: var(--muted); font-size: 13.5px; max-width: 62ch; }
  .star-card .go {
    display: flex; align-items: center; gap: 8px; white-space: nowrap;
    background: var(--accent); color: #fff; text-decoration: none;
    padding: 10px 18px; font-weight: 600; font-size: 13.5px;
  }
  .star-card .go:hover { background: var(--accent-strong); }
  .star-card .go svg { width: 15px; height: 15px; display: block; }

  /* the hero owns the first screen: viewport less the manbar (61px), less a 90px
     strip so the next section peeks above the fold and reads as scrollable */
  .sec.hero { display: flex; min-height: calc(100vh - 151px); }
  @supports (height: 100svh) { .sec.hero { min-height: calc(100svh - 151px); } }
  .name-con { display: flex; gap: 48px; align-items: center; flex: 1; }
  .name-main { flex: 1; min-width: 0; }
  .hero-mark { width: 240px; height: 240px; flex-shrink: 0; display: block; }

  /* ---------- transcripts ----------
     One transcript body, four presentations. Each section of the page uses exactly
     one of them, so a reader scrolling past can tell which group they are in by
     shape alone. The previous layout repeated a single card ten times. */
  .t-body {
    padding: 22px 26px;
    font-size: 13.5px; line-height: 1.7; color: var(--code-text);
    overflow-x: auto;
  }
  .t-body p { margin: 0 0 14px; white-space: pre-wrap; }
  .t-body p:last-child { margin-bottom: 0; }
  /* a closing note inside a transcript is PROSE, so it opts out of the pre-wrap the transcript
     lines need — otherwise its source indentation renders as a hanging indent */
  .t-body .art-foot { white-space: normal; }
  .t-you b, .t-model b { color: var(--code-accent); font-weight: 600; }
  details.t-tool { margin: 0 0 14px; }
  /* Block, not flex. A flex container turns each run of text into its own item, so the gap
     landed between the linked tool NAME and its argument list and the row read `search (...)`
     as two tokens. Inline layout keeps the call one phrase, wraps it at its own spaces, and
     lets the SHOW affordance drop to a second line when the panel is narrow — which is the
     only thing the old `flex-wrap: wrap` was there to do. */
  details.t-tool > summary {
    color: var(--code-dim); cursor: pointer; list-style: none;
  }
  details.t-tool > summary::-webkit-details-marker { display: none; }
  details.t-tool > summary::after {
    content: "show"; margin-left: 10px; white-space: nowrap;
    font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--code-dim); border-bottom: 1px dashed var(--code-line);
  }
  details.t-tool[open] > summary::after { content: "hide"; }
  details.t-tool > summary:hover, details.t-tool[open] > summary { color: var(--code-text); }
  details.t-tool > summary:hover::after { color: var(--code-text); border-bottom-color: var(--code-dim); }
  .t-out { white-space: pre-wrap; color: var(--code-dim); margin-top: 6px; }
  .t-cite { color: var(--code-dim); }
  /* a tool call with nothing hidden behind it: in 06 and 07 the artifact below IS the return
     value, so the line names the call and links it, with no disclosure that would open onto
     nothing. `details.t-tool` stays the form for calls whose raw output is worth folding away.
     Laid out inline, NOT as a flex row like that summary: a gap between the linked tool name
     and its argument list reads as two separate tokens rather than one call. */
  .t-call { color: var(--code-dim); margin: 0 0 18px; }
  /* consecutive calls are one fan-out, not one exchange each, so they close up and the group
     keeps the single gap a lone `.t-call` has */
  .t-calls { margin: 0 0 18px; }
  .t-calls .t-call { margin: 0 0 5px; }
  .t-calls .t-call:last-child { margin-bottom: 0; }

  /* 05 to 08 put transcript lines inside an artifact frame rather than a `.t-body`, so the
     size, color and measure `.t-body` would have given them are set here instead. Same voice
     and same reading measure as 01's chain: the two forms are one grammar.
     Descendant, not child: 08 nests its turns one level down inside a `.lens-panel`. */
  .art .t-you, .art .t-model {
    margin: 0 0 20px; font-size: 13.5px; line-height: 1.7; color: var(--code-text);
    max-width: 660px;
  }

  /* the benefit line that leads a step or a panel — the forms that carry a claim (01-04) */
  .claim {
    font-size: 16px; line-height: 1.55; color: var(--text);
    font-weight: 500; margin: 0 0 22px;
    /* the same 660px the artifact below uses, so the claim and the evidence it
       introduces share a right edge instead of breaking early against nothing */
    max-width: 660px;
  }

  /* ---------- form A, the chain (sources: 01 curate → 02 screen → 03 track) ----------
     A sequence, so it is drawn as one: a dashed spine down a label column, and the
     transcript floating on a tint with no outline. */
  .chain { list-style: none; padding: 0; margin: 0; }
  .step { display: grid; grid-template-columns: 168px 1fr; gap: 44px; }
  .step-mark {
    border-right: 1px dashed var(--line);
    padding: 0 26px 72px 0; text-align: right;
  }
  .step:last-child .step-mark { border-right: none; padding-bottom: 0; }
  .step:last-child .step-body { padding-bottom: 0; }
  .step-body { padding-bottom: 116px; min-width: 0; }
  .step-n {
    display: block;
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 30px; font-weight: 700; color: var(--accent); line-height: 1;
  }
  .step-verb {
    display: block; margin-top: 10px;
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 13px; font-weight: 700; color: var(--text);
  }
  .step-tool {
    display: block; margin-top: 6px;
    font-size: 12px; color: var(--faint);
  }
  .step .t-body { background: var(--code-bg); padding: 24px 28px; }

  /* ---------- form B, the panel + pair (growth: 04 alone, then 05 · 06) ----------
     04 is the one thing that happens with nobody typing, so it gets the full width
     and a filled ground; the two it feeds sit under it as a pair. */
  .panel {
    background: var(--surface);
    display: grid; grid-template-columns: 190px 1fr; gap: 44px;
    padding: 40px 44px; margin: 0 0 76px;
  }
  .panel-n {
    display: block;
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 30px; font-weight: 700; color: var(--accent); line-height: 1;
  }
  .panel-verb {
    display: block; margin-top: 10px;
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 13px; font-weight: 700; color: var(--text);
  }
  /* same offset as `.step-tool`: the mark reads 04 / grow / sitting, like the chain's steps */
  .panel-tool { display: block; margin-top: 6px; font-size: 12px; color: var(--faint); }
  .panel-main { min-width: 0; }
  .panel .t-body, .panel .art { background: var(--bg); border: 1px solid var(--line); }

  /* 05 and 06 stack full-width: 05's week ledger needs the horizontal room for a day column
     beside full artifact titles, and a half-width 06 left alone reads as a stray column.
     The gap matches `.xchg`'s 76px: two bordered artifact cards in sequence are the same
     relationship in both forms, and the page should not answer it twice. */
  .stack { display: grid; grid-template-columns: 1fr; gap: 76px; }
  .mini {
    background: var(--code-bg); border: 1px solid var(--line);
    display: flex; flex-direction: column; min-width: 0;
  }
  .mini-head {
    display: flex; justify-content: space-between; gap: 14px;
    border-bottom: 1px solid var(--code-line);
    padding: 10px 22px;
    font-size: 12px; letter-spacing: 0.04em; color: var(--code-dim);
  }
  .mini-head b { color: var(--code-text); font-weight: 600; }
  .mini-head .n { color: var(--code-accent); margin-right: 8px; }
  .mini .t-body, .mini .art { flex: 1; }
  /* the mini already carries a head bar, so the artifact's caption drops its rule */
  .mini .art-head { border-bottom: none; padding-bottom: 0; margin-bottom: 14px; }
  .mini-note {
    border-top: 1px dashed var(--code-line);
    padding: 16px 22px; margin: 0;
    font-size: 12.5px; line-height: 1.6; color: var(--code-dim);
  }
  /* full-width variant: the 660px payload cap exists to hold a READING measure, so prose keeps
     it and the figures (fan, join, ledger, bars) drop it. */
  .wide .ledger, .wide .hits { max-width: none; }
  /* in 10 every hit carries the same `kb:` stamp, so pushing it to the right edge stacks the
     three stamps into a column: the point of the card is that all of it came from one store */
  .wide .hit.slim { justify-content: space-between; }
  /* the bars stop short of the full frame: a 96 that runs the whole card puts its own number
     an inch from its label, and the row stops reading as one row. */
  .wide .bars { max-width: 880px; }
  .wide .art-head { max-width: none; }
  .wide .mini-head { padding: 9px 24px; }
  .wide .art-flush { padding: 20px 24px 18px; }
  /* `.mini.wide`, not `.wide`: the half-width `.mini .ledger` rules below carry the same
     specificity and win on order, so the wide card has to out-weigh them rather than restate. */
  .mini.wide .ledger .row { grid-template-columns: 18px minmax(0, 1fr) max-content; padding: 8px 0; }
  .mini.wide .ledger .sig { grid-column: 3; text-align: right; }
  .loop-btn {
    font: inherit; font-size: 11.5px; color: var(--code-dim);
    background: none; border: 1px solid var(--code-line); cursor: pointer;
    padding: 1px 9px; margin-right: 14px; min-width: 54px;
  }
  .loop-btn:hover { color: var(--code-text); border-color: var(--code-dim); }

  /* ---------- form C, the wide card with a margin note (retrieval: 07 · 08 · 09) ----------
     The old page-wide default, now used in one section only, so it reads as chosen. */
  .xchg {
    background: var(--code-bg); border: 1px solid var(--line);
    margin: 0 0 76px;
    display: grid;
    grid-template-columns: 1fr minmax(210px, 300px);
    grid-template-areas: "head head" "body note";
  }
  .xchg:last-child { margin-bottom: 0; }
  .xchg-head {
    grid-area: head;
    display: flex; justify-content: space-between; gap: 16px;
    border-bottom: 1px solid var(--code-line);
    padding: 10px 22px;
    font-size: 12px; letter-spacing: 0.04em; color: var(--code-dim);
  }
  .xchg-head b { color: var(--code-text); font-weight: 600; }
  .xchg-head .n { color: var(--code-accent); margin-right: 8px; }
  .xchg .t-body, .xchg .art { grid-area: body; padding: 26px 28px; }
  .xchg .art { border: none; background: none; }
  .t-note {
    grid-area: note;
    color: var(--code-dim);
    border-left: 1px dashed var(--code-line);
    padding: 26px 26px; margin: 0;
    font-size: 12.5px; line-height: 1.65;
  }
  .t-note b { color: var(--code-text); font-weight: 500; }
  /* No margin note: the card drops the second column and the payload spreads into it. Kept as a
     modifier rather than changed on `.xchg` itself, because use-cases.html still carries notes
     substantial enough to earn their own column. The FOOT keeps the 660px cap — it is prose, and
     a reading measure is what that cap is for. */
  .xchg.no-note { grid-template-columns: 1fr; grid-template-areas: "head" "body"; }
  .xchg.no-note .art-head, .xchg.no-note .hits, .xchg.no-note .claims { max-width: none; }
  /* the labeled rows are ONE prose column, not two, so releasing the cap entirely would set
     them at 110 characters. 148 (the label) + 24 (the gap) + 660 (the measure) is the width at
     which the prose reads the same as everywhere else on the page. */
  .xchg.no-note .lens-rows { max-width: 832px; }
  /* A foot's RULE should span the whole card while its PROSE keeps a reading measure. Padding
     holds the text back instead of width, because `border-top` is drawn on the border box —
     so the line runs the full width and the words still wrap at 660px. Collapses to 0 on a
     card narrower than the measure. */
  .xchg.no-note .art-foot, .mini.wide .art-foot {
    max-width: none; padding-right: max(0px, 100% - 660px);
  }

  /* Form D (the offset: a claim on the left, its artifact held off to the right) was deleted
     2026-08-30 with the claim it existed to carry. 10 now uses the head-bar card, which is the
     rule the page already follows: a section with a claim gets a mark column, a section that is
     pure artifact gets a head bar. */

  .t-body.play > *, .art.play > * { animation: line-in 0.28s ease-out both; }
  .t-body.play > *:nth-child(1), .art.play > *:nth-child(1) { animation-delay: 0.05s; }
  .t-body.play > *:nth-child(2), .art.play > *:nth-child(2) { animation-delay: 0.35s; }
  .t-body.play > *:nth-child(3), .art.play > *:nth-child(3) { animation-delay: 0.65s; }
  .t-body.play > *:nth-child(4), .art.play > *:nth-child(4) { animation-delay: 1s; }
  .t-body.play > *:nth-child(5), .art.play > *:nth-child(5) { animation-delay: 1.4s; }
  .t-body.play > *:nth-child(6) { animation-delay: 1.75s; }
  @keyframes line-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) {
    .t-body.play > *, .art.play > * { animation: none; }
    .hero-mark .tile-cursor, .wm-tile .tile-cursor { animation: none; }
    .lens-panel.swap, .client-panel.swap, .aud-panel.swap { animation: none; }
  }

  /* ---------- SOURCES table ---------- */

  /* ---------- artifacts: what a tool hands back, rendered as itself ----------
     Nine identical transcripts read as one shape, so the eye skips them. Each section
     now carries a different KIND of evidence, and these are the shared primitives:
     `.art` is the paper frame, and .ledger / .figs / .bars / .hits / .claims
     are the payloads that sit in it. */
  .art {
    background: var(--code-bg); border: 1px solid var(--code-line);
    padding: 22px 26px 24px; font-size: 13px; min-width: 0;
  }
  .art-loose { margin-top: 44px; }
  /* in the chain 01 is a transcript and 02-03 are artifacts: same frame, or the list
     reads as three unrelated boxes */
  .step .art { border: none; padding: 24px 28px; }
  /* The chain's column is ~794px but every payload is capped at 660, which left ~106px of
     dead ground inside each box, with the caption's right label floating out over it.
     660 + the 28px padding on both sides = 716: the frame now ends where the content does. */
  .step .t-body, .step .art { max-width: 716px; }
  .art-flush { border: none; background: none; padding: 22px 24px 20px; }
  /* ONE right edge for every part of an artifact. A `ch` cap would differ per element,
     because the head, the payload and the foot are set at three sizes. */
  .art-head, .art-foot, .ledger, .figs, .bars, .hits, .claims,
  .lens-rows { max-width: 660px; }
  .art-head {
    display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
    border-bottom: 1px dashed var(--code-line);
    padding-bottom: 10px; margin-bottom: 18px;
    font-size: 11.5px; letter-spacing: 0.04em; color: var(--code-dim);
  }
  .art-head span:first-child { color: var(--code-text); }
  .art-foot {
    color: var(--code-dim); border-top: 1px dashed var(--code-line);
    margin: 18px 0 0; padding-top: 12px; font-size: 12.5px; line-height: 1.65;
  }

  /* a ranked list with a marker column: the 02 shortlist, the 05 queue */
  .ledger .row {
    display: grid; grid-template-columns: 18px minmax(0, 13em) minmax(0, 1fr);
    gap: 16px; align-items: baseline;
    padding: 9px 0; border-top: 1px dashed var(--code-line);
  }
  .ledger .row:first-child { border-top: none; padding-top: 0; }
  .ledger .mk { color: var(--code-accent); }
  .ledger .nm { color: var(--code-text); }
  .ledger .sig { color: var(--code-dim); font-size: 12px; }
  .ledger .row.out .mk, .ledger .row.out .nm { color: var(--code-dim); }
  .ledger.queue .mk { color: var(--code-dim); }
  /* half-width inside the duo: the signal wraps under the name instead of squashing it */
  .mini .ledger .row { grid-template-columns: 18px minmax(0, 1fr); }
  .mini .ledger .sig { grid-column: 2; text-align: left; }

  /* ---------- the 05 week ledger ----------
     TWO exchanges three days apart, in the same you/claude transcript grammar as 01, with a
     dated divider between them. The gap between the turns is the claim, so it is drawn as a
     labeled rule rather than described. Set roomier than the other artifacts on purpose:
     the elapsed days are the content, so the rows get air. */
  .wide .conv-art { padding: 32px 32px 28px; }
  /* the transcript lines match 01's size exactly — same voice, same grammar. They sit outside
     a `.t-body`, so its font-size, color and spacing are restated here rather than inherited. */
  .wk-when {
    display: flex; align-items: center; gap: 14px;
    margin: 0 0 18px;
    font-size: 11.5px; letter-spacing: 0.04em; color: var(--code-dim);
  }
  .wk-when::after { content: ""; flex: 1; border-top: 1px dashed var(--code-line); }
  .wk-when.gap { margin: 34px 0 18px; }
  .wk-qs { margin: 0 0 32px; }
  .wk-qs .q {
    display: block; font-style: italic; font-size: 13px; line-height: 1.6;
    color: var(--code-text);
    margin-top: 12px; padding-left: 16px; border-left: 1px solid var(--code-line);
  }
  .week {
    display: grid; grid-template-columns: 48px minmax(0, 1fr);
    column-gap: 26px; row-gap: 22px; align-items: baseline;
    font-size: 13px; line-height: 1.55;
  }
  .wk-day { font-size: 12px; color: var(--code-dim); }
  .wk-it {
    display: grid; grid-template-columns: 18px minmax(0, 1fr) max-content; gap: 4px 14px;
    align-items: baseline; padding: 0 19px;
  }
  .wk-it .mk { color: var(--code-dim); font-weight: 400; }
  .wk-it .nm { color: var(--code-text); }
  .wk-it .sig { color: var(--code-dim); font-size: 12px; text-align: right; }
  /* rank 1 is the payoff and gets the only box; the ranks below pad to its inner edges
     (18px padding + 1px border) so the marker column stays one straight line */
  .wk-it.hit { background: var(--bg); border: 1px solid var(--code-line); padding: 14px 18px; }
  .wk-it.hit .mk { color: var(--code-accent); }
  /* 19px pad + 18px marker + 14px gap: the tail starts where the names above start */
  .wk-it.more { display: block; color: var(--code-dim); padding: 0 19px 0 51px; }

  /* the card keeps the page's one-time fade-in, one block at a time; the generic `.art.play`
     cascade only delays children 1-3, and this card has eight */
  .conv-art.play > *:nth-child(4) { animation-delay: 0.90s; }
  .conv-art.play > *:nth-child(5) { animation-delay: 1.15s; }
  .conv-art.play > *:nth-child(6) { animation-delay: 1.40s; }
  .conv-art.play > *:nth-child(7) { animation-delay: 1.65s; }
  .conv-art.play > *:nth-child(8) { animation-delay: 1.90s; }

  /* measured numbers, at the size of the claim they carry: 03 */
  .figs { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 24px 26px; }
  .fig-n {
    display: block; font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 26px; font-weight: 700; color: var(--code-text); line-height: 1.1;
  }
  .fig-l { display: block; margin-top: 7px; font-size: 12px; color: var(--code-dim); line-height: 1.5; }

  /* proportion shown as proportion: the store inventory, and 06 */
  .bars { display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content; gap: 11px 16px; align-items: center; }
  .b-l { color: var(--code-text); font-size: 12.5px; }
  .b-t { display: block; }
  .bar { display: block; height: 9px; min-width: 2px; background: var(--code-accent); }
  .bar.dim { background: var(--code-dim); }
  .b-n { color: var(--code-dim); font-size: 12.5px; text-align: right; font-variant-numeric: tabular-nums; }

  /* the card `search` actually returns: 07 */
  .hits { display: grid; gap: 10px; }
  .hit { background: var(--card); border: 1px solid var(--code-line); padding: 14px 16px; }
  /* Every hit is a link to its own source: the section's whole claim is that an answer is
     traceable back to the thing you saved, so the row itself is the affordance rather than a
     "view" control bolted onto it. `.hit.slim` outranks this on specificity and keeps its flex. */
  a.hit { display: block; color: inherit; text-decoration: none; }
  a.hit:hover { border-color: var(--code-dim); }
  a.hit:hover .hit-src, a.hit:focus-visible .hit-src { color: var(--code-text); }
  /* the URL carries the underline, not the provenance note beside it: only one of them is
     the address you would land on. `--faint` rather than `--code-line`, which is the divider
     tint and disappears against the dark card — this is the same affordance `a.tt` uses. */
  .hit-url { border-bottom: 1px dotted var(--faint); }
  a.hit:hover .hit-url { border-bottom-color: var(--accent); }
  .hit-head { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12px; color: var(--code-dim); }
  .hit-score { color: var(--code-accent); font-weight: 700; font-variant-numeric: tabular-nums; }
  .hit-who { color: var(--code-text); }
  .hit-src { color: var(--code-dim); font-size: 11.5px; margin: 0; }
  p.hit-src { margin-top: 9px; }
  .hit.slim {
    display: flex; gap: 20px; flex-wrap: wrap;
    font-size: 12px; color: var(--code-dim);
  }

  /* claim beside what would break it: 08 */
  .claims .row {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px 28px;
    padding: 12px 0; border-top: 1px dashed var(--code-line);
  }
  .claims .row:first-child { border-top: none; padding-top: 0; }
  .claims .row.head { font-size: 11.5px; letter-spacing: 0.04em; color: var(--code-dim); }
  .claims .c { color: var(--code-text); line-height: 1.6; }
  .claims .d { color: var(--code-dim); line-height: 1.6; }

  /* ---------- 08's lens picker ----------
     One region read five ways. The head above the switch — the topic and its size — never
     changes, and that fixity IS the claim the card makes: the same 47 documents, a different
     reading. Only the panel under the switch is swapped.

     Every string lives in the markup, including each panel's call line and foot, so the
     script does nothing but toggle `hidden` and `aria-selected`. Nothing is written from
     JavaScript, and a copy edit is never made in two places.

     Four of the five lenses return PROSE from the model, so four of them share `.lens-rows`
     and differ in what the left column anchors each line to — a heading, a date range, or the
     atom being cited. Giving them four invented shapes would misrepresent the tool. `claims`
     is the one that returns a structured list, and it keeps its own two-column table. */
  .lens-switch {
    display: flex; flex-wrap: wrap; width: max-content; max-width: 100%;
    border: 1px solid var(--code-line); margin: 0 0 20px;
  }
  .lens-switch button {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 11.5px; font-weight: 600; letter-spacing: 0.04em;
    background: none; border: none; cursor: pointer;
    color: var(--code-dim); padding: 8px 15px;
  }
  .lens-switch button + button { border-left: 1px solid var(--code-line); }
  .lens-switch button:hover { color: var(--code-text); }
  .lens-switch button[aria-selected="true"] { background: var(--code-text); color: var(--code-bg); }
  .lens-rows .row {
    display: grid; grid-template-columns: 148px minmax(0, 1fr); gap: 8px 24px;
    padding: 12px 0; border-top: 1px dashed var(--code-line);
  }
  .lens-rows .row:first-child { border-top: none; padding-top: 0; }
  .lens-rows .k { color: var(--code-dim); font-size: 12px; line-height: 1.6; }
  .lens-rows .v { color: var(--code-text); line-height: 1.6; }
  /* the swapped-in panel fades once. `.art.play` only fires on scroll-in, so without this a
     tab click would replace the content with no signal that anything happened. */
  .lens-panel.swap { animation: line-in 0.24s ease-out both; }
  /* The tab swap owns a panel's animation, and has to say so explicitly: the panels are direct
     children of `.art`, so the scroll-reveal cascade's `.art.play > *:nth-child(n)` delays match
     them too and out-specify the rule above. Without this a swapped-in panel inherits the
     reveal delay and holds at opacity 0 — `both` fill — for up to 1.4s after the click. Four
     classes, so it wins on specificity rather than on source order. */
  .art.play > .lens-panel.swap { animation-delay: 0s; }


  /* the questions nobody typed, and what each one has caught: 04 */
  /* ---------- the growth loop (04) ----------
     The same you/claude transcript the rest of the page uses, bent to fit a story that spans
     six months and contains exactly ONE thing the reader did. Two devices carry that:

     A dated divider opens each block, so the three-month emptiness between the May turn and
     the August one is drawn rather than asserted.

     The August turn has NO `you ›` line. That is the claim, not an omission: nobody asked for
     it, the question written in May was still running. A transcript with one prompt and two
     answers is the shortest true statement of "it gets better on the days you never open it".
     Setting it as five evenly-weighted stages, which is what this replaced, gave the one act
     and the four unattended steps identical visual weight and so argued the opposite. */
  .t-ctx {
    margin: 0 0 20px; max-width: 660px;
    font-size: 13px; line-height: 1.7; color: var(--code-dim);
  }
  /* this card states its own reveal: the shared `.art.play` cascade only reaches child 5 and
     there are nine here, and the unlisted ones would hold at opacity 0 and then land first. */
  .grow-art.play > *:nth-child(4) { animation-delay: 0.90s; }
  .grow-art.play > *:nth-child(5) { animation-delay: 1.10s; }
  .grow-art.play > *:nth-child(6) { animation-delay: 1.30s; }
  .grow-art.play > *:nth-child(7) { animation-delay: 1.50s; }
  .grow-art.play > *:nth-child(8) { animation-delay: 1.75s; }
  .grow-art.play > *:nth-child(9) { animation-delay: 1.95s; }

  .srcs-wrap { overflow-x: auto; }
  .srcs { border-collapse: collapse; width: 100%; font-size: 13.5px; }
  .srcs th, .srcs td {
    text-align: left; vertical-align: top;
    border-top: 1px dashed var(--line);
    padding: 11px 18px 11px 0; font-weight: 400;
  }
  .srcs tr:last-child th, .srcs tr:last-child td { border-bottom: 1px dashed var(--line); }
  .srcs th {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 12px; font-weight: 700; width: 172px; color: var(--text);
  }
  .srcs td:nth-child(2) { color: var(--text); }
  .srcs td:last-child { color: var(--faint); width: 190px; }
  /* a source that is not live yet: dimmed, with its status where the access column sits */
  .srcs tr.soon th, .srcs tr.soon td { color: var(--faint); }
  .srcs tr.soon td:last-child { color: var(--accent); }
  .srcs-note { color: var(--muted); font-size: 13px; margin: 22px 0 0; max-width: 78ch; }
  /* a note that follows a block rather than a table it belongs to */
  .srcs-note.spaced { margin-top: 30px; }

  /* ---------- INSTALLATION: one pane per client ----------
     The switch used to pick an AUDIENCE (humans / agents). It picks a CLIENT now: with the
     package on PyPI the agent instruction is one sentence for every client, so it earns a copy
     box above the strip rather than a tab of its own, and the tabs go to the axis that actually
     varies. Same visual form as `.lens-switch` and the same script drives both. */
  .install-agent-lede { color: var(--muted); font-size: 13.5px; margin: 0 0 14px; max-width: 78ch; }

  /* The OUTER switch: agent or human. Its humans pane holds a second strip, so it is set heavier
     than that one — same form, larger type and padding — or two identical strips stacked read as
     one row of eight. `.install-or` between them carries the rest of the separation. */
  .aud-switch { display: flex; margin: 0 0 34px; border: 1px solid var(--line); width: max-content; }
  .aud-switch button {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 13px; font-weight: 700; letter-spacing: 0.1em;
    background: none; border: none; cursor: pointer;
    color: var(--muted); padding: 13px 26px;
  }
  .aud-switch button + button { border-left: 1px solid var(--line); }
  .aud-switch button:hover { color: var(--text); }
  .aud-switch button[aria-selected="true"] { background: var(--text); color: var(--bg); }
  .aud-panel.swap { animation: line-in 0.24s ease-out both; }

  .install-or {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 13px; font-weight: 700; color: var(--text);
    margin: 40px 0 18px;
  }
  .install-or .dot { color: var(--accent); }

  .client-switch { display: flex; gap: 0; margin: 0 0 24px; border: 1px solid var(--line); width: max-content; }
  .client-switch button {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 12px; font-weight: 600; letter-spacing: 0.08em;
    background: none; border: none; cursor: pointer;
    color: var(--muted); padding: 10px 20px;
  }
  .client-switch button + button { border-left: 1px solid var(--line); }
  .client-switch button:hover { color: var(--text); }
  .client-switch button[aria-selected="true"] { background: var(--text); color: var(--bg); }
  /* its own rule rather than a shared `.swap`: the lens panels animate inside a `.art` that
     staggers its children, and carry a delay override this one must not inherit. */
  .client-panel.swap { animation: line-in 0.24s ease-out both; }
  /* Six tabs measure 818px, and .con adds 112px of padding, so the strip stops fitting below
     930px — not below 860px, where the phone rules start. In that 70px band `width:
     max-content` pushed the page sideways rather than wrapping. It wraps into chips from here
     down instead: a wrapped SHARED border would leave the second row's first button with no
     left edge, so every button carries its own. */
  @media (max-width: 929px) {
    .client-switch { width: auto; border: none; gap: 6px; flex-wrap: wrap; }
    .client-switch button, .client-switch button + button {
      border: 1px solid var(--line); padding: 9px 14px; font-size: 11px;
    }
  }
  /* every pane's last child carries no bottom margin, so the panel sets none of its own */
  .route-head { color: var(--text); font-size: 13.5px; margin: 0 0 14px; max-width: 78ch; }
  /* Only the Desktop pane has two routes, so `.sep` marks the second one and is styled as the
     SECONDARY route: muted body text under the primary route's button. Two routes at equal
     weight made the pane read as a wall with no entry point. */
  .route-head.sep {
    margin-top: 34px; padding-top: 30px; border-top: 1px dashed var(--line);
    color: var(--muted);
  }
  .route-head b { font-weight: 600; color: var(--text); }
  /* The .mcpb download IS the install for anyone who does not open a terminal, so it carries the
     same weight as the code block below it instead of sitting inline in a sentence, where it
     read as a footnote. */
  .dl-btn {
    display: inline-flex; align-items: center; gap: 10px;
    background: var(--accent); color: #fff; text-decoration: none;
    padding: 13px 22px; font-weight: 600; font-size: 14px;
  }
  .dl-btn:hover { background: var(--accent-strong); }
  .dl-btn svg { width: 15px; height: 15px; display: block; }
  /* the string literals in the JSON block, which is the one install block that is not shell */
  .install-block .s { color: var(--code-accent); }
  /* the shared tail: the three steps that are the same whichever pane you came from. No rule of
     its own — the first `li` already draws one, and two rules 4px apart read as a smudge. */
  /* ONE numbered list runs through the whole install, across two `ol` elements: the pane's own
     step, then the three shared tail steps. The contract that makes the count work is that
     EVERY pane holds exactly one step — including Desktop, whose two routes are alternatives
     inside step 1, not steps 1 and 2. So the tail always starts at 2, and `counter-reset: step
     1` is that contract, not a magic number. Add a second step to any pane and this number is
     wrong; put the second thing inside the existing step instead. */
  .pane-steps { margin-bottom: 0; }
  .steps.tail { margin-top: 0; counter-reset: step 1; }
  /* the pane step's own rule would double the tail's first rule if the two lists met with a gap */
  .steps.tail li:first-child { border-top: 1px dashed var(--line); }
  /* a step that carries a block or a route indents its body under the number, so the block does
     not start at the digit's left edge and break the column the step text sets */
  .steps .install-block, .steps .dl-btn { margin-top: 12px; }
  .steps .route-head { margin: 14px 0 0; }
  /* inside a numbered step the dashed rule between the two routes competes with the list's own
     rules, so the second route separates on space and its bold lead-in alone */
  .steps .route-head.sep { margin-top: 24px; padding-top: 0; border-top: none; }
  /* the one pane that installs nothing. Marked so it does not read as a recipe the eye can
     follow: an accent rule down the side, and the verdict as the first line rather than a step. */
  .no-go { border-left: 2px solid var(--accent); padding-left: 22px; }
  .no-go-head {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 14px; font-weight: 700; color: var(--text);
    margin: 0 0 16px;
  }
  /* `onboard` and `oracle` are what you do AFTER an install, so they disappear with the pane that
     has no install. `:has()` rather than a flag through the tab script, which stays generic. */
  .con:has(#pane-web:not([hidden])) #install-tail { display: none; }
  .steps { list-style: none; counter-reset: step; padding: 0; margin: 0 0 32px; max-width: 82ch; }
  .steps li {
    counter-increment: step;
    padding: 12px 0; border-top: 1px dashed var(--line);
    color: var(--muted); font-size: 13.5px;
  }
  .steps li::before {
    content: counter(step) "  ";
    font-family: "Martian Mono", ui-monospace, monospace;
    font-weight: 700; color: var(--accent);
  }
  .steps li strong { color: var(--text); font-weight: 600; }
  .install-block {
    background: var(--code-bg); border: 1px solid var(--line);
    padding: 20px 24px; overflow-x: auto;
    white-space: pre;
    font-size: 13.5px; line-height: 1.9;
    color: var(--code-text);
  }
  .install-block .c { color: var(--code-dim); }
  .after-install { color: var(--muted); margin: 20px 0 0; font-size: 13.5px; max-width: 78ch; }
  .after-install code, .steps code, .filerow code, .route-head code {
    font-size: 0.9em;
    background: var(--surface); border: 1px solid var(--line);
    padding: 1px 6px; color: var(--text);
  }

  /* ---------- SEE ALSO ---------- */
  .filerow {
    display: grid; grid-template-columns: 220px 1fr; gap: 18px;
    border-top: 1px dashed var(--line);
    padding: 10px 0;
    font-size: 13.5px;
  }
  .filerow:last-child { border-bottom: 1px dashed var(--line); }
  .filerow .t { color: var(--text); }
  .filerow .d { color: var(--muted); }
  /* three columns, then the wordmark full-bleed under them */
  .footbar {
    display: grid; grid-template-columns: 1fr auto 1fr;
    align-items: center; gap: 20px;
    border-top: 1px dashed var(--line);
    margin-top: 34px; padding-top: 24px;
    font-size: 12.5px; color: var(--faint);
  }
  .footbar .mid { text-align: center; }
  .footbar .end { display: flex; gap: 18px; justify-content: flex-end; }
  .footbar a { color: var(--faint); text-decoration: none; }
  .footbar a:hover { color: var(--text); }

  /* the wordmark is drawn, not set: dashed outline for the name, the cursor solid.
     the same two parts as the tile mark, at page scale */
  .wordmark { padding: 8px 24px 0; }
  .wordmark svg { display: block; width: 100%; height: auto; }
  .wm-text {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-weight: 800;
    fill: none;
    stroke: var(--faint);
    stroke-width: 2;
    stroke-dasharray: 7 6;
  }
  /* the footer mark blinks too: it is the same cursor as the hero, at page scale */
  .wm-tile .tile-cursor { animation: blink 1.1s steps(1) infinite; }


  @media (max-width: 860px) {
    .con { padding: 84px 20px 92px; }
    .sec.hero .con { padding: 56px 20px 64px; }
    /* every artifact drops to one column; nothing gets squashed into a word per line */
    .claims .row { grid-template-columns: 1fr; gap: 5px; }
    .claims .row.head { display: none; }
    .claims .d::before { content: "disproved by · "; color: var(--code-accent); }
    .ledger .row, .mini.wide .ledger .row { grid-template-columns: 18px minmax(0, 1fr); }
    .ledger .sig, .mini.wide .ledger .sig { grid-column: 2; text-align: left; }
    /* the queue narrows: the signal wraps under the name, and the box pads lighter */
    .week { grid-template-columns: 40px minmax(0, 1fr); column-gap: 14px; }
    .wk-it { grid-template-columns: 18px minmax(0, 1fr); padding: 0; }
    .wk-it .sig { grid-column: 2; text-align: left; }
    .wk-it.hit { padding: 12px 14px; }
    .wk-it.more { padding: 0 0 0 32px; }
    .lens-rows .row { grid-template-columns: 1fr; gap: 4px; }
    /* the segmented control wraps to two rows here, and a shared outer border leaves the
       second row's first button without a left edge. Separate chips instead. */
    .lens-switch { width: auto; border: none; gap: 6px; }
    .lens-switch button, .lens-switch button + button { border: 1px solid var(--code-line); }
    /* FOR HUMANS / FOR AGENTS: `width: max-content` can neither shrink nor wrap, so on a
       320px screen the two buttons were what pushed the whole page sideways. Chips, for the
       same reason the client switch below uses them. */
    .aud-switch { width: auto; border: none; gap: 6px; flex-wrap: wrap; }
    .aud-switch button, .aud-switch button + button {
      border: 1px solid var(--line); padding: 11px 20px; font-size: 12px;
    }
    .sec.also .con { padding: 36px 20px 48px; }
    h2 { font-size: 22px; max-width: none; }
    .lede { margin-bottom: 44px; }
    .xchg { grid-template-columns: 1fr; grid-template-areas: "head" "body" "note"; }
    .t-note { border-left: none; border-top: 1px dashed var(--code-line); padding: 16px 22px 20px; }
    /* the chain drops its spine: the label sits above its own step */
    .step { grid-template-columns: 1fr; gap: 14px; }
    .step-mark { border-right: none; padding: 0; text-align: left; }
    .step-body { padding-bottom: 84px; }
    .step-n, .panel-n { font-size: 24px; }
    .panel { grid-template-columns: 1fr; gap: 20px; padding: 28px 22px; }
    .filerow { grid-template-columns: 1fr; gap: 2px; }
    /* three columns do not fit: stack them, all left-aligned */
    .footbar { grid-template-columns: 1fr; gap: 10px; }
    .footbar .mid { text-align: left; }
    .footbar .end { justify-content: flex-start; }
    .wordmark { padding: 4px 16px 0; }
    /* six links do not fit a phone on one line, and not wrapping made the whole page
       scroll sideways. The links drop to their own row under the brand instead. */
    .manbar { padding: 12px 16px; flex-wrap: wrap; row-gap: 12px; }
    .manbar .links { gap: 8px 16px; flex-wrap: wrap; }
    /* a 22px-tall link is a coin-flip under a thumb; the extra padding is vertical only, so
       the star button and the install button keep the horizontal shape they were drawn with */
    .manbar .links a, .manbar .links .star-btn,
    .manbar .links .theme-btn { padding-top: 8px; padding-bottom: 8px; }
    .hero-mark { display: none; }
    /* the hero reserves a viewport minus 151px for a manbar that is 61px tall on a desktop.
       Wrapped onto three rows a phone's manbar is over twice that, so the reserve overshot
       and the next section peeked by 11px instead of the ~90px the rule exists to leave.
       On a phone the hero's own content already fills most of the screen: no reserve. */
    .sec.hero { min-height: 0; }
    /* the transcripts are hard-wrapped in the source for a 660px column and rendered
       pre-wrap, so every one of those lines wrapped a second time here and the source's
       continuation indent landed mid-sentence. Prose reflows to the phone's measure instead;
       .t-out keeps pre-wrap, because its line breaks are the artifact. */
    .t-body p { white-space: normal; }
    /* the install command is what this page is for, and it ran off the right edge */
    .install-block { white-space: pre-wrap; }
  }

  /* ---------- tool-name tooltips ----------
     The definitions live in tooltip.js. The link keeps the color of whatever it sits in
     (prose, a transcript, a table cell) so it never reads as a foreign element; only the
     dotted underline marks it. The card inverts the page, so one rule serves both themes. */
  a.tt {
    color: inherit; text-decoration: none;
    border-bottom: 1px dotted var(--faint);
    cursor: help;
  }
  a.tt:hover, a.tt:focus-visible { color: var(--accent); border-bottom-color: var(--accent); }
  .tipcard {
    position: fixed; z-index: 40; max-width: 320px;
    background: var(--text); color: var(--bg);
    padding: 9px 13px;
    font-size: 12.5px; line-height: 1.5;
    pointer-events: none;
  }

  /* ---------- invite page ----------
     The one page on this site that is not read but ACTED ON: a recipient has to copy a line
     into their client, and nothing else on the page matters until they have. So it drops the
     landing rhythm every other page uses — no full-viewport hero, no 132/144px section
     padding — and fits one screen instead. Measured before the change, at 1440x820: the
     document ran 1685px and the accept line sat at y=1002, 182px under the fold. The first
     recipient read the headline and stopped.
     It is a floor, not a cage: min-height, never height, and no overflow rule anywhere, so a
     short window or a phone scrolls normally rather than clipping the instruction. */
  .sheet.invite .sec {
    border-bottom: none;
    min-height: calc(100vh - 61px);   /* 61px is the manbar */
    display: flex; flex-direction: column; justify-content: center;
  }
  @supports (height: 100svh) {
    .sheet.invite .sec { min-height: calc(100svh - 61px); }
  }
  /* Tight on purpose, and it does NOT read as cramped: the section centers against its
     min-height, so on a tall window the slack comes back as equal air above and below. Padding
     only has to be small enough that a short window stops overflowing on air alone. */
  .sheet.invite .con { padding: 28px 56px 20px; }
  /* 56px would take three lines here and cost more height than the routes below it */
  .sheet.invite h1 { font-size: clamp(22px, 3vw, 34px); max-width: 30ch; margin: 0 0 14px; }
  .sheet.invite .hero-sub { margin: 0 0 28px; font-size: 15px; }
  .sheet.invite .kicker { margin: 0 0 14px; }

  /* Two routes abreast. Stacking them is what pushed the second one off the screen, and the
     horizontal room was sitting unused: the column caps at 82ch on a 1440px page. */
  .sheet.invite .routes {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    border-top: 1px dashed var(--line);
  }
  .sheet.invite .route { padding: 20px 32px 0 0; min-width: 0; }
  .sheet.invite .route + .route {
    padding: 20px 0 0 32px; border-left: 1px dashed var(--line);
  }
  .sheet.invite .route .install-block { margin-top: 12px; }
  .sheet.invite .route .after-install { margin-top: 14px; }
  /* Both blocks WRAP here. A half-width column is narrower than `claude mcp add Opyt -- uvx
     --from opyt==0.1.0a4 opyt-mcp`, and the shared rule (`white-space: pre; overflow-x: auto`)
     turned that into a sideways scroll inside the box — trading the fold this page exists to
     fix for a second, less visible one. The accept line breaks anywhere, because it is one
     unbroken URL; the shell block breaks only between words, so no command gains a space. */
  .sheet.invite .install-block { white-space: pre-wrap; }
  .sheet.invite #accept-line { word-break: break-all; }
  /* Everywhere else `.copy-btn` sits INSIDE a bordered block and borrows its edge. Here it sits
     under one, where that edge is gone — and this is the page's primary action, so it draws its
     own rather than reading as a caption. */
  .sheet.invite .copy-btn {
    border: 1px solid var(--line); margin-top: 10px; padding: 8px 18px;
    color: var(--text); background: var(--surface);
  }
  .sheet.invite .copy-btn:hover { border-color: var(--accent); color: var(--accent); }

  @media (max-width: 860px) {
    /* a phone cannot hold two code blocks and a headline on one screen, and pretending
       otherwise would shrink them to fit. The routes stack and the page scrolls. */
    .sheet.invite .sec { min-height: 0; display: block; }
    .sheet.invite .con { padding: 32px 20px 40px; }
    .sheet.invite .routes { grid-template-columns: 1fr; }
    .sheet.invite .route { padding: 20px 0 0; }
    .sheet.invite .route + .route {
      padding: 20px 0 0; border-left: none; border-top: 1px dashed var(--line);
    }
  }

  /* ---------- docs page ---------- */
  /* The docs page carries a rail the other pages do not, so it takes some extra width —
     but not enough to pay for the rail outright. The reading column is deliberately
     narrower here than on the other pages: a 90-character line was the crowding. */
  .sheet.docs { max-width: 1200px; }
  .docgrid { display: grid; grid-template-columns: minmax(0, 1fr) 236px; }
  .docmain { min-width: 0; border-right: 1px solid var(--line); }
  /* the same section rhythm as every other page: the air around a hairline is what reads
     as the break, so cutting it here made the page feel packed */

  /* The rail stretches to the height of .docmain, which is what gives the inner block
     something to stick against for the whole scroll. */
  .docnav-inner {
    position: sticky; top: 0;
    max-height: 100vh; overflow-y: auto;
    padding: 44px 0 40px 26px;
  }
  .docnav nav { display: flex; flex-direction: column; border-left: 1px solid var(--line); }
  .docnav a {
    text-decoration: none; color: var(--muted);
    font-size: 12.5px; line-height: 1.45;
    padding: 6px 12px; margin-left: -1px;
    border-left: 2px solid transparent;
  }
  .docnav a:hover { color: var(--text); }
  .docnav a.sub {
    padding-left: 24px; font-size: 12px;
    font-family: "Martian Mono", ui-monospace, monospace;
  }
  /* the marker the reader tracks: accent text plus the rule going solid at that entry */
  .docnav a.here { color: var(--accent); border-left-color: var(--accent); }
  @media (max-width: 1180px) {
    .docgrid { grid-template-columns: minmax(0, 1fr); }
    .docmain { border-right: none; }
    .docnav { display: none; }
  }

  .doc-intro { color: var(--muted); margin: 0 0 30px; max-width: 78ch; }
  .doc-intro strong { color: var(--text); font-weight: 600; }

  /* inline code, everywhere on the docs page EXCEPT inside .sigblock (which paints its own
     ground — a boxed <code> inside a boxed <pre> reads as two nested panels) */
  .doc-intro code, .flow code, .tool-sum code, .acts code, .pdesc code, .param-head code,
  .toolidx code, .notes code, .srcs-note code {
    font-size: 0.9em; background: var(--surface); border: 1px solid var(--line);
    padding: 1px 6px; color: var(--text);
  }

  /* the index table: one row per tool, jumping to its entry below */
  .toolidx { border-collapse: collapse; width: 100%; font-size: 13.5px; }
  .toolidx th, .toolidx td {
    text-align: left; vertical-align: top;
    border-top: 1px dashed var(--line);
    padding: 11px 18px 11px 0; font-weight: 400; color: var(--muted);
  }
  .toolidx thead th {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--faint); border-top: none; padding-top: 0;
  }
  .toolidx tbody th {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 12.5px; font-weight: 700; width: 150px;
  }
  .toolidx tbody th a { color: var(--accent); text-decoration: none; }
  .toolidx tbody th a:hover { text-decoration: underline; }
  .toolidx td.eff { color: var(--faint); font-size: 12.5px; width: 178px; }

  /* the entries sit in the same section as the index above them: one hairline between an
     index and the thing it indexes read as two unrelated sections */
  .tools-list { margin-top: 56px; }

  /* one tool entry: head + summary + signature + parameter/return fields + notes */
  .tool { border-top: 1px solid var(--line); padding: 40px 0 44px; scroll-margin-top: 20px; }
  .tool:last-of-type { border-bottom: 1px solid var(--line); }
  .tool-head {
    display: flex; align-items: baseline; flex-wrap: wrap;
    gap: 10px 16px; margin: 0 0 14px;
  }
  .tool-name {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 17px; font-weight: 700; color: var(--text); margin: 0;
  }
  .tags { display: flex; flex-wrap: wrap; gap: 6px; }
  .tag {
    font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
    padding: 2px 8px; border: 1px solid var(--line); color: var(--faint);
    white-space: nowrap;
  }
  /* the write/spend badges take the accent: they are the ones worth reading before calling */
  .tag.w { border-color: var(--accent); color: var(--accent); }
  .tool-sum { color: var(--muted); font-size: 13.5px; margin: 0 0 22px; max-width: 78ch; }
  .tool-sum b { color: var(--text); font-weight: 600; }
  .sigblock {
    background: var(--code-bg); border: 1px solid var(--code-line); color: var(--code-text);
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-size: 12.5px; line-height: 1.65;
    padding: 14px 16px; margin: 0 0 24px;
    overflow-x: auto; white-space: pre;
  }
  .tool .block { margin: 0 0 32px; }
  .tool .block:last-child { margin-bottom: 0; }
  .tool h4 {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--text); margin: 0 0 12px;
  }
  /* ---------- a parameter or a returned field ----------
     Stacked, not tabled. A three-column table parks a 90-character description a fixed
     270px right of the name it belongs to; stacking puts the name directly above its own
     text and lets the measure be capped. Same shape Stripe and Mintlify use. */
  .params { display: flex; flex-direction: column; }
  .param { border-top: 1px dashed var(--line); padding: 13px 0 14px; }
  .param-head {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: 5px 10px;
    margin: 0 0 5px;
  }
  .param-head .pn {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 12.5px; font-weight: 700; color: var(--text);
  }
  .param-head .pt { color: var(--faint); font-size: 12px; }
  .pflag {
    font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
    padding: 1px 7px; border: 1px solid var(--line); color: var(--faint);
    white-space: nowrap;
  }
  /* required is the one flag that changes whether the call works at all */
  .pflag.req { border-color: var(--accent); color: var(--accent); }
  .pflag.def { text-transform: none; letter-spacing: 0.02em; border-style: dashed; }
  .pflag code { font-size: 11px; background: none; border: none; padding: 0; color: inherit; }
  .pdesc { color: var(--muted); font-size: 13px; line-height: 1.65; max-width: 80ch; }
  .pdesc b { color: var(--text); font-weight: 600; }

  /* ---------- the action table ----------
     Actions are a fixed set of alternatives compared across the same two columns, which is
     what a table is for. It stays a table so it never reads as another parameter list. */
  .acts { border-collapse: collapse; width: 100%; font-size: 13px; }
  .acts th, .acts td {
    text-align: left; vertical-align: top;
    border-top: 1px dashed var(--line);
    padding: 11px 20px 12px 0; font-weight: 400; color: var(--muted);
  }
  .acts thead th {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--faint); border-top: none; padding-top: 0;
  }
  .acts td.pn {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 12.5px; font-weight: 700; color: var(--accent); width: 132px;
  }
  .acts td.pt { color: var(--faint); font-size: 12px; width: 150px; }
  .acts b { color: var(--text); font-weight: 600; }
  .notes { list-style: none; padding: 0; margin: 0; }
  .notes li {
    position: relative; color: var(--muted); font-size: 13px;
    padding: 9px 0 9px 20px; border-top: 1px dashed var(--line);
  }
  .notes li::before { content: "—"; position: absolute; left: 0; color: var(--accent); }
  .notes b { color: var(--text); font-weight: 600; }

  .flow { list-style: none; counter-reset: f; padding: 0; margin: 0 0 10px; }
  .flow li {
    counter-increment: f; padding: 12px 0;
    border-top: 1px dashed var(--line); color: var(--muted); font-size: 13.5px;
  }
  .flow li::before {
    content: counter(f) "  ";
    font-family: "Martian Mono", ui-monospace, monospace; font-weight: 700; color: var(--accent);
  }
  .flow li b { color: var(--text); font-weight: 600; }
  @media (max-width: 860px) {
    /* .toolidx and .acts stack here too — the rule is shared with .srcs and .cmp and lives
       in the first 860px block, so this one holds only what is specific to the docs page */
    .tool { padding: 26px 0 30px; }
    .tool-name { font-size: 16px; }
    /* a signature wraps rather than scrolling: the argument list is what the entry is for,
       and half of it was off the right edge with no scrollbar drawn to say so */
    .sigblock { white-space: pre-wrap; }
  }

  /* ---------- COMPARE pages ----------
     compare.html plus vs/*.html are generated from compare_data.py by build_compare.py.
     These rules are shared by all fifteen pages, so a change here lands on every one. */
  .cmp-scroll { overflow-x: auto; }
  .cmp { border-collapse: collapse; width: 100%; font-size: 13.5px; min-width: 620px; }
  .cmp th, .cmp td {
    text-align: left; vertical-align: top;
    border-top: 1px dashed var(--line);
    padding: 12px 18px 12px 0; font-weight: 400;
  }
  .cmp thead th {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--faint); border-top: none; padding-top: 0;
  }
  .cmp thead th.mine { color: var(--accent); }
  .cmp tbody th {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 12px; font-weight: 700; color: var(--text); width: 200px;
  }
  .cmp td { color: var(--muted); width: 34%; }
  .cmp td.mine { color: var(--text); }
  .cmp tr:last-child th, .cmp tr:last-child td { border-bottom: 1px dashed var(--line); }

  /* the two verdict lists: a minus for their advantages, a plus for ours */
  .cmp-list { list-style: none; padding: 0; margin: 0; max-width: 82ch; }
  .cmp-list li {
    border-top: 1px dashed var(--line);
    padding: 13px 0 13px 26px; position: relative;
    color: var(--muted); font-size: 14px;
  }
  .cmp-list li:last-child { border-bottom: 1px dashed var(--line); }
  .cmp-list li::before {
    position: absolute; left: 0; top: 12px;
    font-family: "Martian Mono", ui-monospace, monospace; font-weight: 700; font-size: 13px;
  }
  .cmp-list.them li::before { content: "−"; color: var(--faint); }
  .cmp-list.mine li::before { content: "+"; color: var(--accent); }

  .cmp-pick {
    display: grid; grid-template-columns: 1fr 1fr;
    border: 1px solid var(--line); margin-bottom: 34px;
  }
  .cmp-pick .pick { padding: 22px 24px; }
  .cmp-pick .pick + .pick { border-left: 1px solid var(--line); }
  .cmp-pick .pick.own { background: var(--panel); }
  .cmp-pick h3 {
    font-family: "Martian Mono", ui-monospace, monospace;
    font-size: 13px; font-weight: 700; margin: 0 0 12px;
  }
  .cmp-pick .own h3 { color: var(--accent); }
  .cmp-pick ul { list-style: none; margin: 0; padding: 0; }
  .cmp-pick li {
    color: var(--muted); font-size: 13.5px;
    padding: 5px 0 5px 16px; position: relative;
  }
  .cmp-pick li::before { content: "·"; position: absolute; left: 2px; color: var(--faint); }

  .cmp-verdict {
    max-width: 78ch; font-size: 15px; color: var(--text);
    border-left: 2px solid var(--accent); padding: 2px 0 2px 18px; margin: 0 0 30px;
  }
  .cmp-cta { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin: 0; }
  .cmp-cta .go {
    background: var(--accent); color: #fff; text-decoration: none;
    font-weight: 600; padding: 9px 20px; display: inline-block;
  }
  .cmp-cta .go:hover { background: var(--accent-strong); }
  .cmp-cta span { color: var(--faint); font-size: 13px; }
  .cmp-also { color: var(--faint); font-size: 12.5px; margin: 16px 0 0; }
  .cmp-also:empty { display: none; }

  @media (max-width: 860px) {
    .cmp-pick { grid-template-columns: 1fr; }
    .cmp-pick .pick + .pick { border-left: none; border-top: 1px solid var(--line); }
  }


  /* ---------- WIDE TABLES ON A PHONE: they stack, they do not scroll ----------
     .srcs, .toolidx, .acts and .cmp are all three columns and all wanted 540–620px inside a
     wrapper set to scroll. A 390px phone gives the content column 348, so a third of every row
     sat off the right edge behind a scrollbar a phone does not draw: the access column on
     .srcs, which is where "coming soon" lives, and on every vs/ page the competitor's whole
     column — a comparison table showing one side of the comparison. Each row becomes a block
     instead, one cell per line, and the horizontal scroll goes away rather than being given an
     affordance: two cells you can only see one at a time are not a comparison.

     THIS BLOCK MUST STAY LAST IN THE FILE. It overrides four tables whose own rules run from
     .srcs at the index page down to .cmp at the very bottom, and a media query adds no
     specificity — only source order decides. Anything appended after this wins over it. */
  @media (max-width: 860px) {
    .srcs-wrap, .cmp-scroll { overflow-x: visible; }
    .srcs, .toolidx, .acts, .cmp { display: block; min-width: 0; }
    .srcs tbody, .toolidx tbody, .acts tbody, .cmp tbody { display: block; }
    .toolidx thead, .acts thead, .cmp thead { display: none; }
    .srcs tr, .toolidx tr, .acts tr, .cmp tr {
      display: block; padding: 13px 0;
      border-top: 1px dashed var(--line);
    }
    .srcs tr:last-child, .toolidx tr:last-child,
    .acts tr:last-child, .cmp tr:last-child { border-bottom: 1px dashed var(--line); }
    /* the row owns the rule now, so the cells drop the borders and the column gutter */
    .srcs th, .srcs td, .toolidx th, .toolidx td, .acts th, .acts td, .cmp th, .cmp td {
      display: block; width: auto; padding: 0 0 5px;
      border-top: none; border-bottom: none;
    }
    .srcs tr:last-child th, .srcs tr:last-child td,
    .cmp tr:last-child th, .cmp tr:last-child td { border-bottom: none; }
    .srcs tr > :last-child, .toolidx tr > :last-child,
    .acts tr > :last-child, .cmp tr > :last-child { padding-bottom: 0; }
    /* the fixed column widths the side-by-side layout needed; a stacked cell takes the line */
    .srcs th, .srcs td:last-child, .toolidx tbody th, .toolidx td.eff,
    .acts td.pn, .acts td.pt, .cmp tbody th, .cmp td { width: auto; }
    /* stacked, .cmp's two answers are two paragraphs with nothing saying whose is whose, and
       the header row that used to say it is hidden. Each cell carries its own column name
       instead — build_compare.py writes it into data-col, so the name lives in one place. */
    .cmp td::before {
      content: attr(data-col); display: block; margin-bottom: 3px;
      font-family: "Martian Mono", ui-monospace, monospace;
      font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
      color: var(--faint);
    }
    .cmp td.mine::before { color: var(--accent); }
  }
