:root {
  --ground: #14171c; /* Rail       */
  --raised: #1a1e25; /* Chrome     */
  --doc: #242a33; /* Doc        */
  --card: #303949; /* Card       */
  --strip: #2b323c; /* Strip      */
  --control: #404a59; /* Control    */
  --warn: #ef86b2; /* Warn       */
  --bar: #0f1217; /* StatusBar  */
  --ink: #d6d3cc; /* Text       */
  --dim: #8b8a85; /* Dim        */
  --rose: #ef86b2; /* Brand + Heading */
  --azure: #6189e8; /* Focus      */
  --amber: #e2a356; /* Accent     */
  --ok: #84c084; /* OK         */
  --badge: #d9b34a; /* Badge      */
  --code: #d4b87e; /* Code       */
  --codebg: #1b2129; /* CodeBg     */
  --focus: #6189e8;

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

  --measure: 63ch;
  --page: 60rem;
  --gutter: 2.2ch;
  --rail: 2ch;

  color-scheme: dark;
}

/* Zone names in the comments map to draftplane-tui/theme/theme.go's Theme
     struct, so a preset change there can be re-applied here by name.

     The VALUES are the client's, verbatim; the ROLES are chosen for a page
     rather than copied. Rail is a one-cell border in the TUI and never a
     large field — assigning it to --ground worked in dark by luck (any near
     black reads fine at any size) and was oppressive in light, where Rail is
     the darkest value in the preset. The rule instead: the page takes the
     zone furthest from the text (Rail in dark, Doc in light), and frames step
     one surface toward the middle — lighter than the page in dark, darker in
     light, which is how panels read in each.

     No prefers-color-scheme override on purpose: the client's own default
     preset is `dark` and the product is a terminal tool, so the site commits
     to dark and light is reached deliberately through the toggle, which
     persists in localStorage. */

:root[data-theme="light"] {
  --ground: #f9f2e0; /* Doc       — the page IS the reading surface */
  --doc: #f1e9d5; /* Card      — frame body, one step in from the page */
  --raised: #f0e7d2; /* Chrome    — cmd blocks, chat, the beta box */
  --card: #ede4d0; /* Strip     — confirm panels, the banded row */
  --strip: #e0d3b4; /* Rail      — hairlines */
  --control: #e0d3b4; /* Control   — button grounds */
  --warn: #a8386c; /* Warn      — centred panel heads */
  --bar: #e0d3b4; /* StatusBar — frame title and status bars */
  --ink: #2e2a21; /* Text      */
  --dim: #6f6a5c; /* Dim       */
  --rose: #00699f; /* Brand + Heading */
  --azure: #00699f; /* Focus     */
  --amber: #a8386c; /* Accent    */
  --ok: #00699f; /* OK        */
  --badge: #6f6a5c; /* Badge     */
  --code: #5f4a12; /* Code      */
  --codebg: #f9f2e0; /* CodeBg    */
  --focus: #00699f;
  color-scheme: light;
}

:root[data-theme="dark"] {
  --ground: #14171c;
  --raised: #1a1e25;
  --doc: #242a33;
  --card: #303949;
  --strip: #2b323c;
  --bar: #0f1217;
  --ink: #d6d3cc;
  --dim: #8b8a85;
  --rose: #ef86b2;
  --azure: #6189e8;
  --amber: #e2a356;
  --ok: #84c084;
  --badge: #d9b34a;
  --code: #d4b87e;
  --codebg: #1b2129;
  --focus: #6189e8;
  color-scheme: dark;
  --control: #404a59;
  --warn: #ef86b2;
}

* {
  box-sizing: border-box;
}

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

/* A column at least one viewport tall, so a short page (access, 404) puts
     its footer at the bottom of the screen rather than mid-screen. footer's
     margin-top: auto takes the slack; on a long page there is none and this
     does nothing. svh rather than vh so the footer is not tucked under a
     mobile browser's toolbar. */
.wrap {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 1.5rem;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
}

/* ---------- masthead ---------- */

/* Wraps rather than squeezing: the wordmark and all three links are wider
     than a 320px phone even with no gaps, so there the links drop to a row of
     their own instead of pushing the theme toggle off-screen. */
.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding: 1.6rem 0 0;
  font-family: var(--mono);
  font-size: 0.82rem;
}
.wordmark {
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ink);
  font-size: 0.95rem;
  text-decoration: none; /* it is an <a> on subpages, a <span> on the index */
  white-space: nowrap;
}
.wordmark::before {
  content: "§ ";
  color: var(--rose);
}
.mast-links {
  display: flex;
  gap: 1.4rem;
  color: var(--dim);
}
.mast-links a {
  color: inherit;
  text-decoration: none;
}
.mast-links a:hover {
  color: var(--azure);
}
.theme-toggle {
  background: none;
  border: 0;
  padding: 0;
  color: var(--dim);
  font-family: var(--mono);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
}
.theme-toggle:hover {
  color: var(--amber);
}

/* ---------- type ---------- */

h1 {
  font-family: var(--mono);
  font-weight: 700;
  font-size: clamp(2rem, 5.4vw, 3.35rem);
  line-height: 1.06;
  letter-spacing: -0.035em;
  text-wrap: balance;
  margin: 0;
  max-width: 20ch;
}

h2 {
  font-family: var(--mono);
  font-weight: 700;
  font-size: clamp(1.35rem, 2.8vw, 1.75rem);
  line-height: 1.2;
  letter-spacing: -0.025em;
  text-wrap: balance;
  margin: 0;
  max-width: 26ch;
}
h2::before {
  content: "§ ";
  color: var(--rose);
  font-weight: 400;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  color: var(--dim);
  margin: 0;
}

p {
  margin: 0;
  max-width: var(--measure);
}
.lede {
  font-size: clamp(1.02rem, 1.9vw, 1.18rem);
  color: var(--ink);
  max-width: 58ch;
}
.dim {
  color: var(--dim);
}

code {
  font-family: var(--mono);
  font-size: 0.9em;
  color: var(--code);
}

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

/* ---------- sections ---------- */

section {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: 4.5rem 0;
  border-top: 1px solid var(--strip);
}
section.hero {
  border-top: 0;
  padding: 3.5rem 0 4.5rem;
}
.prose {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.head {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}

/* ---------- command block ---------- */

.cmds {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  font-family: var(--mono);
  font-size: 0.92rem;
  align-items: flex-start;
}
.cmd {
  display: flex;
  align-items: center;
  gap: 0.7ch;
  background: var(--raised);
  border: 1px solid var(--strip);
  border-radius: 3px;
  padding: 0.5rem 0.85rem;
  max-width: 100%;
  overflow-x: auto;
  white-space: nowrap;
}
.cmd .sigil {
  color: var(--rose);
  user-select: none;
}
.cmd .txt {
  color: var(--ink);
}
.copy {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: none;
  border: 0;
  color: var(--dim);
  cursor: pointer;
  padding: 0 0 0 1.2ch;
  margin-left: auto;
}
.copy:hover {
  color: var(--amber);
}
.cmd-note {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--dim);
  letter-spacing: 0.02em;
}

/* ---------- private beta signup ---------- */

.beta {
  background: var(--raised);
  border: 1px solid var(--strip);
  border-radius: 4px;
  padding: 1.15rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  max-width: var(--measure);
}
.beta-tag {
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  color: var(--amber);
}
.beta p {
  font-size: 0.95rem;
}

.beta-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: stretch;
}
.beta-form input[type="email"] {
  flex: 1 1 18ch;
  min-width: 0;
  font-family: var(--mono);
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--ground);
  border: 1px solid var(--strip);
  border-radius: 3px;
  padding: 0.55rem 0.8rem;
}
.beta-form input[type="email"]::placeholder {
  color: var(--dim);
}
.beta-form input[type="email"]:focus-visible {
  border-color: var(--focus);
}
.beta-form button {
  font-family: var(--mono);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--ground);
  background: var(--amber);
  border: 1px solid var(--amber);
  border-radius: 3px;
  padding: 0.55rem 1.1rem;
  cursor: pointer;
  white-space: nowrap;
}
.beta-form button:hover {
  filter: brightness(1.08);
}

/* Off-screen rather than display:none — a field a bot fills but a person
     never sees. See the note in index.html about what this can and cannot
     catch when the POST goes straight to a third party. */
.beta-form .decoy {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Swapped out once the signup lands, so the confirmation replaces the field
     rather than sitting under a form you could submit twice. */
.beta-form.sent {
  display: none;
}

.beta-status {
  font-family: var(--mono);
  font-size: 0.84rem;
  color: var(--dim);
  min-height: 1.3em;
}
.beta-status:empty {
  display: none;
}
.beta-status.ok {
  color: var(--ok);
}
.beta-status.err {
  color: var(--amber);
}

.beta-fine {
  font-size: 0.82rem;
  color: var(--dim);
  max-width: 58ch;
}

/* ---------- agent exchange ---------- */

/* Your agent's pane, not Draftplane's. Deliberately a different surface
     from .frame — flat, no title bar — so the two read as two applications
     rather than one. ❯ is your turn, ✻ the agent's. */
.chat {
  background: var(--raised);
  border: 1px solid var(--strip);
  border-left: 2px solid var(--strip);
  border-radius: 4px;
  padding: 0.85rem 0.95rem;
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.6;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.turn {
  display: flex;
  gap: 1.2ch;
}
.turn .glyph {
  flex: 0 0 1ch;
  user-select: none;
}
.turn .said-to {
  flex: 1 1 auto;
  min-width: 0;
}
.turn.you .glyph {
  color: var(--rose);
}
.turn.you .said-to {
  color: var(--ink);
}
.turn.agent .glyph {
  color: var(--amber);
}
.turn.agent .said-to {
  color: var(--dim);
}

/* ---------- terminal frame ---------- */

.frame {
  background: var(--doc);
  border: 1px solid var(--strip);
  border-radius: 4px;
  overflow: hidden;
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.55;
}
.frame-cmd {
  background: var(--bar);
  color: var(--dim);
  padding: 0.45rem 0.9rem;
  border-bottom: 1px solid var(--strip);
  white-space: nowrap;
  overflow-x: auto;
}
.frame-cmd .sigil {
  color: var(--rose);
}
.screen {
  padding: 0.9rem 0.9rem 1rem;
  display: flex;
  flex-direction: column;
}
.ln {
  display: flex;
  align-items: flex-start;
}

/* ---------- the review view's zones ----------

     .screen--doc marks a frame that depicts the REVIEW VIEW, which is the
     only view with these. A plain .screen is CLI output (the login frame) and
     has no rail and no zones; .screen--cols is the plan list, which has its
     own geometry. Scoping matters: applied to every .screen, the rail would
     appear beside `draftplane login`, where nothing draws one.

     Two zones, both from renderDocPainted:

     THE RAIL. A 2-cell margin on BOTH sides, painted in Rail. The LEFT one
     turns Brand on the cursor block's own rows and nowhere else — a blank
     separator is wrap(false, ...), so it never carries focus even directly
     beneath the cursor.

     THE CARD. A block that carries ANY thread is painted in Card, and so are
     its comment rows, so the block and its conversation read as one region
     and an annotated line is visible as annotated without hunting for the
     gutter mark. The rule is onCardAt: `len(views[idx]) > 0 && Kind !=
     KindCode` — RESOLVED counts, and COLLAPSED counts. A block whose only
     thread is resolved and closed still sits on Card with a '○' beside it.
     Code blocks are the exception and keep CodeBg.

     The trailing blank BELOW a carded region is Doc, not Card: blankRow
     renders from the outer st, never the block's rebased one. That dark row
     under the last comment is what ends the region. */
/* THE RAIL IS PAINTED ON THE CONTAINER, NOT PER ROW, so it runs unbroken
     from the top of the screen to the bottom — through the blank separators
     AND through the padding above the first row and below the last, which is
     what it does in the terminal. Drawn as two background gradients pinned to
     the left and right edges at --rail wide and full height.

     The rows still carry --rail borders, but TRANSPARENT ones: they reserve
     the same two columns so text lands where it did, and they let the
     container's rail show through. The cursor row then just gives its left
     border a colour, which paints over the gradient for exactly those rows —
     which is the whole reason the rail is not simply a border on .screen.

     background-clip: padding-box on the rows is what keeps a Card row's
     background out of those transparent borders. Without it the card colour
     paints under its own border by default (border-box) and the card bleeds
     across the rail into the frame edge. */
.screen--doc {
  padding-left: 0;
  padding-right: 0;
  background-color: var(--doc);
  background-image:
    linear-gradient(var(--ground), var(--ground)), linear-gradient(var(--ground), var(--ground));
  background-repeat: no-repeat, no-repeat;
  background-size:
    var(--rail) 100%,
    var(--rail) 100%;
  background-position:
    left top,
    right top;
}
.screen--doc .ln,
.screen--doc .blank {
  border-left: var(--rail) solid transparent;
  border-right: var(--rail) solid transparent;
  padding-left: 0.9rem;
  padding-right: 0.9rem;
  background-clip: padding-box;
}
.screen--doc .ln.cur {
  border-left-color: var(--rose);
}
/* --card, the client's own value, unchanged. It was lifted to a brighter
     #38445a for one round on the belief that the step was too subtle against
     a frame that is itself a raised panel. That belief was wrong, and the
     evidence for it was an artefact: the card was not too dim, it was not
     being SERVED — style.css was stale in the browser (see `make serve`'s
     no-store note). A contrast judgement taken against a stylesheet that is
     not the one on disk is not a judgement about contrast. The step it
     produced measured 20,26,39 against the list's 12,15,22, which is nearly
     double the banded row everyone agreed already read correctly. */
.screen--doc .ln.card {
  background-color: var(--card);
}

/* background-COLOR, never the `background` shorthand. The shorthand resets
     every background-* longhand it does not name, which silently includes the
     `background-clip: padding-box` set on .ln above — so the card painted
     under its own transparent border and straight over the rail, leaving a
     122px hole in a column that is supposed to be unbroken. It looked like
     the gradient failing; it was one property quietly reset by another rule.
     Found by measuring the rail column pixel by pixel, not by looking. */

/* .card is the ZONE and .cmt is the ROW KIND, and they are not the same
     question — the annotated BLOCK is on Card too, and it must not take this
     indent. Conflating them shifted the commented block two cells right of
     the paragraph above it, which no client row does.

     A comment row is gutter(2) + docCardIndent(2) + the card's own indent(2)
     + text, so its '|' sits TWO cells right of the block text above it — and
     a body line two further right again, which is the &nbsp;&nbsp; in the
     markup. A block row is gutter(2) + mark(2) + text. .gut + .mk carry the
     first four cells of both; this is the third pair, and only a comment row
     has it. */
.screen--doc .ln.cmt .txt {
  padding-left: var(--rail);
}

/* Plan list. Real grid columns rather than &nbsp; padding — hand-counted
     padding drifts the moment any cell's text length changes, which is what
     it did. Widths mirror app/list.go: cursor, glyph 2, NAME flexible,
     COMMENTS 6 + a 3-cell gap, then the section's trailing region. NAME takes
     minmax(8ch, 1fr) so it is the column that gives up room and ellipsizes,
     exactly as ansi.Truncate does in the client. */
.screen--cols .ln {
  display: grid;
  /* cursor 1.5 · glyph 2 · NAME flexible · COMMENTS 6 + 3 gap · ORG 14 + 2
       gap · AUTHOR 21 — the client's regionSplit at a 120-cell row, whose 37
       cells are also what SOURCE and OPENED span.

       The trailing region is two tracks because Shared splits it into ORG
       then AUTHOR (the client's regionSplit); a row with one trailing value
       — SOURCE in Your plans, OPENED in Recently opened — spans both, below.
       Both are minmax(0, …) so on a narrow screen they shrink alongside NAME
       instead of pushing the row out of a frame that clips.

       NO max-width here. Rows fill the frame, because a row's background IS
       the cursor band — cap the row and the band stops mid-frame with a
       ragged edge, which is not something the client can draw. The width cap
       belongs to the frame instead, below. */
  grid-template-columns: 1.5ch 2ch minmax(8ch, 1fr) 9ch minmax(0, 16ch) minmax(0, 21ch);
  align-items: baseline;
}
.screen--cols .ln > :nth-child(5):last-child {
  grid-column: 5 / -1;
}
/* ORG's 2-cell gap before AUTHOR (listOrgGapWidth), kept even when a narrow
     screen ellipsizes it. */
.screen--cols .ln > :nth-child(5):not(:last-child) {
  padding-right: 2ch;
}
.screen--cols .ln > * {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* A 120-cell row plus ~4ch of frame padding. 120 is where NAME reaches its
     listMaxTitleWidth ceiling of 68 (app/list.go), leaving the 37-cell
     trailing region the grid above mirrors; past it the client only lengthens
     that region, which a fixed frame has no need to show. It used to be the
     client's listMaxRowWidth, a hard cap on the row that increment 35 removed.

     It was 84ch, from an 80-column body, and the reasoning was that "a list
     frame is only as wide as the terminal it depicts". True, but 80 is not
     the width it depicts: the review frames on this page have no cap and run
     the full column, so the list sat visibly narrower than every frame around
     it for a reason none of them shared. 124 is close enough to --page that
     the list lines up with its neighbours instead of stopping short. */
.frame--list {
  max-width: 124ch;
}
/* The masthead is a rounded box around "§ Draftplane", in Brand and not
     bold, with "Signed into {org}" under it only when signed into an org
     (app/list.go's mastheadBoxContent and mastheadCaptionRows). The box starts
     in the glyph column so it and the glyphs line up — a choice, not a copy:
     the client draws the box one cell left of the glyph (mastheadIndent is 1,
     the glyph 2), and this grid's cursor column is 1.5ch rather than 2 anyway.
     The caption starts one cell further in, just inside the box's left edge:
     a box-drawing vertical sits mid-cell, so a caption in the box's own cell
     starts left of the line. The client drew it there too and is being fixed
     to match (2026-09-27). The box rows run at the font's own line height
     rather than the frame's 1.55, or the box-drawing verticals break into
     dashes between rows. */
.screen--cols .ln.mast > * {
  grid-column: 2 / -1;
}
.screen--cols .ln.mast:has(.mast-box) {
  line-height: 1.2;
}
.screen--cols .ln > .mast-caption {
  color: var(--dim);
  padding-left: 1ch;
}
.screen--cols .ln > .mast-box {
  color: var(--rose);
  white-space: pre;
}

/* A section's name starts in the NAME column, over the column labels and the
     titles beneath it. The client once put a ▸ in the glyph column of the
     section holding the cursor, and the name began after it; 9198833 removed
     the mark (▸ is every file tree's disclosure triangle, and the band has
     nothing to expand) but kept its two cells as a constant indent, since
     that is what lines the name up. The name is Accent and bold: three
     sections on one screen made it something a reader has to find. */
.screen--cols .ln.band > .band-title {
  grid-column: 3 / -1;
  color: var(--amber);
  font-weight: 700;
}
/* THE REVIEW VIEW'S GUTTER IS TWO COLUMNS, NOT ONE, and it is drawn that
     way here because the client draws it that way: renderDocPainted composes
     each row as `gutter + mark + text` (ui/painted.go), where gutter is the
     2-cell cursor column and mark is the 2-cell annotation column. A single
     span could not show a block that is BOTH under the cursor AND carries a
     thread, which is the ordinary case — it is the block you are about to
     comment on.

     .gut  — cursor column. '┃' in Brand, from cursorGlyph.
     .mk   — mark column. '※' open / '○' all-resolved, in Accent and BOLD,
             from gutterMarkGlyph. Bold because the client sets it: without
             it the mark reads dimmer than the card it refers to, which looks
             like a colour difference and is not one.

     '┃' is the REVIEW view. The plan LIST's cursor is '▌' (listCursorGlyph,
     app/list.go) and the two are not interchangeable — every review frame on
     this site drew '▌' until 2026-08-23. */
.gut {
  flex: 0 0 var(--gutter);
  color: var(--rose);
  user-select: none;
}
.mk {
  flex: 0 0 var(--gutter);
  color: var(--amber);
  font-weight: 700;
  user-select: none;
}
.gut.cursor {
  color: var(--rose);
}

/* A TEXT CARET IS NOT A GUTTER. The compose and confirm panels draw a '▌'
     at the end of an input line, and it rode on .gut purely because that
     class happened to be the right colour. When .gut became the review view's
     CURSOR column it turned rose, and three carets on workflows.html changed
     colour with it -- a class doing two unrelated jobs, found the moment one
     of them moved. */
.caret {
  color: var(--amber);
  user-select: none;
}
.txt {
  flex: 1 1 auto;
  min-width: 0;
}
.blank {
  height: 0.75em;
}

.h2line {
  color: var(--rose);
  font-weight: 700;
}
.h2line .mark {
  color: var(--dim);
  font-weight: 400;
}
.body {
  color: var(--ink);
}

.thread {
  display: flex;
  flex-direction: column;
}
.who {
  color: var(--amber);
  font-weight: 700;
}
.said {
  color: var(--ink);
  opacity: 0.88;
  font-style: italic;
}

/* A confirm/compose panel: the TUI floats these above the status bar,
     owning key handling until they resolve. Distinct from .screen so a frame
     can show the document dimmed behind one. */
.panel {
  border-top: 1px solid var(--strip);
  background: var(--card);
  padding: 0.7rem 0.9rem;
  color: var(--ink);
}
.panel .q {
  color: var(--amber);
  font-weight: 700;
}

/* ---------- centred panel ----------
     drawsCentredPanel (app/painted.go) is a CLOSED SET: publish confirm, the
     publish result and the "?" keys panel float in a bordered box; approve,
     export, unlink-confirm and REPUBLISH still draw the full-width .panel
     strip above. Do not promote a mode into this shape without checking that
     list -- workflows.html's republish panel is deliberately still a strip.

     centredBox's geometry, which is sized off the LINES and not the terminal:
     two cells of padding and one border cell each side, one blank row inside
     the border above the body and below it. Hence fit-content plus auto
     margins rather than a full-width band.

     The border is Chrome's style -- background Chrome, foreground Text -- and
     deliberately NOT the head row's colour: "a border shouting with the
     headline leaves nothing quieter for it to be louder THAN."

     The head line is WarnHeader, and --warn is a token of its own rather than
     --rose: the two share a value in dark and diverge in light. */
.cpanel {
  width: fit-content;
  max-width: 100%;
  margin: 0.4rem auto;
  background: var(--raised);
  color: var(--ink);
  border: 2px solid var(--ink);
  padding: 1.05rem 2ch;
}
.cpanel .q {
  color: var(--warn);
  font-weight: 700;
}

/* ---------- compose buttons ----------
     The compose panel's hint strip is GONE from the client (increment 32,
     Task 3): the words that named post and cancel were replaced by the button
     row itself, so the help bar under it now says only "esc cancel" and these
     are the control rather than a second spelling of a key.

     Both sit on Control here because composeFocus opens at composeFocusEditor
     -- the reader is typing, and neither button is focused. A focused one
     takes the Chip ground, which is the same value the palette already calls
     --focus in both presets, so it needs no token of its own.

     ⚠️ NO LEAD INDENT, AND THAT IS THE FAITHFUL CHOICE HERE RATHER THAN A
     DEPARTURE. composeButtonLead is composeHeaderRule's width ("── ", three
     cells), and it exists so the buttons line up with the header's WORDS
     rather than with the rule drawn in front of them. These frames do not
     draw that rule, so reproducing the indent measured from it pushed the
     buttons three cells right of the very text they are supposed to align
     with. The alignment is the invariant; the number was only ever how the
     client reaches it. */
.btnrow {
  display: flex;
  gap: 2ch;
  margin-top: 0.55rem;
}
.btn {
  background: var(--control);
  color: var(--ink);
}
/* The document dims behind a confirm panel. Transitioned so the frame can
     play the keypress rather than only showing its result. */
.screen {
  transition: opacity 300ms ease;
}
/* Dimming dark-on-light costs far more legibility than dimming
     light-on-dark, so light backs off less. */
.screen.behind {
  opacity: 0.45;
}
:root[data-theme="light"] .screen.behind {
  opacity: 0.68;
}

/* The cursor's row is banded as well as barred. */
.screen--cols .ln.on {
  background: var(--card);
}

.statusbar.split {
  display: flex;
  justify-content: space-between;
  gap: 2ch;
}
.statusbar .right {
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* NO border-top, and the asymmetry with .frame-cmd's border-bottom is the
     point rather than an oversight.

     .frame-cmd is the invoking shell command — genuinely outside the running
     program — so a hairline there marks a real boundary, and it is correct
     that it crosses the rail: the rail does not exist above it.

     The status bar is not outside anything. In the terminal it is simply the
     bottom row of the same screen, in its own zone, with no rule above it. A
     1px --strip line here is LIGHTER than the rail it crosses, so it capped
     the rail column with a bright tick and made the band look like it stopped
     short of the bottom. --bar is several steps darker than both --doc and
     --card in either theme, so the zone change is the boundary and the line
     was never doing any work. */
.statusbar {
  background: var(--bar);
  color: var(--dim);
  padding: 0.35rem 0.9rem;
  white-space: nowrap;
  overflow-x: auto;
}
.statusbar .ok {
  color: var(--ok);
}
.helpbar {
  color: var(--dim);
  opacity: 0.75;
  padding: 0.35rem 0.9rem 0.6rem;
  font-size: 0.76rem;
  white-space: nowrap;
  overflow-x: auto;
}

/* ---------- animation ---------- */

.reveal {
  opacity: 0;
  transform: translateY(-0.25em);
  transition:
    opacity 420ms ease,
    transform 420ms ease;
}
.reveal.on {
  opacity: 1;
  transform: none;
}
.swap {
  transition: opacity 300ms ease;
}
.swap.out {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .swap {
    transition: none;
  }
}

/* A CONFIG BLOCK IS NOT A COMMAND, which is why it is not a .cmd. .cmd is
     one copyable line with a $ sigil and a copy button; this is the contents
     of somebody else's config file.

     No copy button, deliberately: you cannot paste this whole into most
     clients, you merge it into an `mcpServers` object that already exists.
     A button offering the whole blob would be promising something that does
     not work. */
.conf {
  background: var(--raised);
  border: 1px solid var(--strip);
  border-radius: 4px;
  padding: 0.7rem 0.9rem;
  margin: 0;
  font-family: var(--mono);
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--ink);
  white-space: pre;
  overflow-x: auto;
  max-width: 100%;
}

/* ---------- diagrams ----------

     Inline SVG rather than an image, for the same reason the terminal frames
     are hand-built HTML: crisp at any DPI, theme-aware because it takes the
     palette from the same custom properties, and correctable when the product
     moves. Shape colours are presentation attributes using var() directly;
     only text needs classes, because font-family and size are not worth
     repeating on every element.

     The container scrolls rather than shrinking. A diagram that scales to a
     narrow phone is a diagram nobody can read — the site already answers this
     the same way for .frame-cmd, .statusbar and .helpbar. */
.dgm {
  overflow-x: auto;
  max-width: 100%;
}
.dgm svg {
  display: block;
  width: 100%;
  min-width: 34rem;
  height: auto;
}
.dgm-cap {
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 0.5rem;
}
.dgm-node-title {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 700;
  fill: var(--ink);
}
.dgm-node-sub {
  font-family: var(--mono);
  font-size: 12px;
  fill: var(--dim);
}
.dgm-edge-label {
  font-family: var(--mono);
  font-size: 11.5px;
  fill: var(--dim);
}
.dgm-scope {
  font-family: var(--mono);
  font-size: 11.5px;
  fill: var(--dim);
}
.dgm-scope-dp {
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 700;
  fill: var(--rose);
}

/* ---------- negative space list ---------- */

.nots {
  display: flex;
  flex-direction: column;
  gap: 1.05rem;
  max-width: var(--measure);
  margin: 0;
  padding: 0;
  list-style: none;
}
.nots li {
  display: grid;
  grid-template-columns: 2.4ch 1fr;
  gap: 0.4ch;
}
.nots .x {
  font-family: var(--mono);
  color: var(--rose);
  line-height: 1.62;
}
.nots b {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 0.94em;
  letter-spacing: -0.01em;
}

/* ---------- prose lists ----------
     A bulleted list is the continuation of the line that introduces it, not
     a block set apart from it, so it pulls back against the gap its parent
     sets and sits just under that line. Items sit closer together than
     paragraphs do: one thought each, not one paragraph each. .nots is the
     landing page's negative-space list and owns its own rhythm above, so it
     is excluded rather than re-tuned here. */
.prose ul,
section > ul:not(.nots) {
  margin: 0;
  padding-left: 2.4ch;
  max-width: var(--measure);
}
.prose li + li,
section > ul:not(.nots) > li + li {
  margin-top: 0.3rem;
}
.prose li::marker,
section > ul:not(.nots) > li::marker {
  color: var(--rose);
}
/* The pull-back is the parent's gap (1.1rem in .prose, 1.5rem in a section)
   less the ~0.35rem that should actually remain. */
.prose > :not(ul) + ul {
  margin-top: -0.75rem;
}
section > :not(ul) + ul:not(.nots) {
  margin-top: -1.15rem;
}

/* ---------- changelog rhythm ----------
     Every section here is a heading and a handful of lines, so the site's
     4.5rem section padding reads as emptiness rather than air. A quarter
     off it closes the page up without breaking step with the rest. */
body.log section {
  padding: 3.4rem 0;
}
body.log section.hero {
  padding: 3.5rem 0 3.4rem;
}

/* ---------- legal pages ----------
     Terms and privacy read as documents rather than as the site: no rule
     between sections, and headings in plain bold body text with no §. The
     section padding shrinks with the rules, because 9rem of air between a
     heading and the paragraph above it reads as a page break. Heading to its
     text is tighter than paragraph to paragraph, so a heading visibly belongs
     to what follows it. */
body.legal section {
  border-top: 0;
  padding: 0 0 2rem;
  gap: 0.5rem;
}
body.legal section.hero {
  padding: 3.5rem 0 2.5rem;
}
body.legal h2 {
  font-family: var(--sans);
  font-size: 1em;
  line-height: inherit;
  letter-spacing: normal;
  max-width: none;
}
body.legal h2::before {
  content: none;
}

/* ---------- closer ---------- */

.closer {
  align-items: flex-start;
}
footer {
  margin-top: auto;
  border-top: 1px solid var(--strip);
  padding: 2rem 0 3rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--dim);
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem;
  justify-content: space-between;
}
footer a {
  color: var(--dim);
  text-decoration: none;
}
footer a:hover {
  color: var(--azure);
}
.foot-links {
  display: flex;
  gap: 1.4rem;
  flex-wrap: wrap;
}

@media (max-width: 34rem) {
  body {
    font-size: 16px;
  }
  .frame {
    font-size: 0.72rem;
  }
  :root {
    --gutter: 2ch;
  }
}
/* reset — supplied by the artifact host previously; owned here now */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
}
img,
svg {
  max-width: 100%;
  height: auto;
  display: block;
}
button {
  font: inherit;
  color: inherit;
}
