/* ═══════════════════════════════════════════════════════════════════════════════
   START Analyst — the owner surface. Visual direction "Broadsheet" (v1.0 visual spec):
   aged manila paper light, aubergine-black dark; Newsreader for the greeting and the
   lead, Public Sans for everything else, IBM Plex Mono for the Checked line alone;
   tabular figures everywhere; plum for the rules, the mark and the owner's own words;
   ledger red only on a bad-news figure. No gradients, no shadows, no spinners.

   TWO ink tones and no third. --ink is anything the owner reads; --ink-mid is anything
   that names or stamps it. Hierarchy is carried by face, size and weight — never by
   fading text out, which is what made evidence prose read as a footnote.
   ═══════════════════════════════════════════════════════════════════════════════ */

/* Faces are linked from index.html, not @imported: an @import is a second serial
   round-trip and the landing has to paint at 0 ms. */

:root {
  /* paper — aged manila, the same stock as the marketing site's "The Tape" */
  --paper:     #F3EEE3;   /* page */
  --paper-2:   #EDE6D7;   /* second surface: artifacts, tables, cards */
  --paper-3:   #E5DCC9;   /* wells: bar tracks */
  /* TWO ink tones for text, and no third.
     --ink      anything that IS the content: prose, figures, table rows, entries, chips.
     --ink-mid  anything that NAMES or stamps the content: column headers, gutter dates,
                the meta row, the Checked line, labels, placeholders.
     There is no "quiet body text". Evidence prose set a step lighter than the lead reads
     as a footnote, which is what the owner test found — the hierarchy is carried by face,
     size and weight instead, which is where it belongs. */
  --ink:       #1E1A15;
  --ink-mid:   #5C5449;
  --rule:      #D6CCB8;   /* the spine, borders, dividers */
  --rule-soft: #E3DACA;   /* row dividers inside artifacts */
  --plum:      #432D63;   /* three jobs only: the rules, the mark, the owner's own words */
  --plum-soft: #6B5390;
  --red:       #BE2E1E;   /* ledger red — bad-news FIGURES only, never chrome */
  /* the heatmap's sequential ramp (Phase B): plum over the artifact paper at 48/61/74/87/100%,
     one hue light→dark, validated with the dataviz ordinal check in both themes */
  --seq-1: #9B8D9F; --seq-2: #857590; --seq-3: #6F5D81; --seq-4: #594572; --seq-5: #432D63;
  --radius:    2px;
  --gutter:    72px;
  /* Width of a classic scrollbar, measured once at boot in main.js. 0 where the OS
     uses overlay scrollbars. The composer subtracts it so it centres inside the same
     usable width as the thread, which sits inside a permanently-reserved scroller. */
  --sbw:       0px;

  --serif: 'Newsreader', 'Iowan Old Style', Georgia, serif;                /* display + lead only */
  --sans:  'Public Sans', -apple-system, 'Segoe UI', system-ui, sans-serif; /* everything else */
  --mono:  'IBM Plex Mono', ui-monospace, 'Cascadia Mono', monospace;      /* the Checked line and the sheet's figures */

  --col: 860px;
  --rail: 260px;
  --rail-strip: 56px;
  --drawer: 440px;
  color-scheme: light;

}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #17141A; --paper-2: #1E1A25; --paper-3: #2A2333;   /* aubergine-black, not charcoal */
    --ink: #EBE3D6; --ink-mid: #A79C8E;
    --rule: #322A3D; --rule-soft: #262032;
    --plum: #B79CE0; --plum-soft: #8E76BA; --red: #E0705E; --seq-1: #67587F; --seq-2: #7B6997; --seq-3: #8F7AAF; --seq-4: #A38BC8; --seq-5: #B79CE0;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --paper: #17141A; --paper-2: #1E1A25; --paper-3: #2A2333;
  --ink: #EBE3D6; --ink-mid: #A79C8E;
  --rule: #322A3D; --rule-soft: #262032;
  --plum: #B79CE0; --plum-soft: #8E76BA; --red: #E0705E; --seq-1: #67587F; --seq-2: #7B6997; --seq-3: #8F7AAF; --seq-4: #A38BC8; --seq-5: #B79CE0;
  color-scheme: dark;
}

/* ── base ─────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
/* Every numeral in the product, without exception (visual spec §3). Public Sans is the
   USWDS fork of Libre Franklin precisely because Libre Franklin ships no `tnum` and this
   line would silently do nothing — see scripts/qa/tabularProbe.ts. */
html { font-variant-numeric: tabular-nums lining-nums; -webkit-text-size-adjust: 100%; }
body { margin: 0; background: var(--paper); color: var(--ink); font-family: var(--sans); font-size: 15.5px; line-height: 1.55; -webkit-font-smoothing: antialiased; overflow-x: hidden; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 2px solid var(--plum); outline-offset: 2px; }
/* The scrollbars are the product's, not the browser's: a thin thumb in the rule colour on
   no track, darker under the pointer. `scrollbar-color` inherits, so it is set once at the
   root; `scrollbar-width` does not, so every scroller names it — and so does the probe
   main.js measures `--sbw` with, or the composer would centre against the wrong width. */
:root { scrollbar-color: var(--rule) transparent; }
.thread-scroll, .rail-body, .book-body, .drawer-body, .field { scrollbar-width: thin; }
.thread-scroll:hover, .book-body:hover, .drawer-body:hover { scrollbar-color: var(--ink-mid) transparent; }
.sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.muted { color: var(--ink-mid); }
.mono { font-family: var(--mono); font-size: 12.5px; }
.num { font-variant-numeric: tabular-nums; }
code { font-family: var(--mono); font-size: .9em; background: var(--paper-2); padding: 1px 5px; border-radius: var(--radius); }

/* ── motion (§8) ──────────────────────────────────────────────────────────────
   Motion means something arrived or changed state. Nothing moves for decoration.
   Four verbs and no others: Arrive, Tick, Open/close, Pulse — plus the entry sheet's
   one gesture. Banned and absent: overshoot, scale-in, shimmer skeletons, chip
   hover-lift, spring physics, and count-up animation of any kind. A figure appears at
   its value, instantly, always: animating a figure counting up contradicts the trust
   invariant perceptually, which is the whole product. */

/* Arrive — answers, artifacts, the change sentence; the owner's note and the pending turn
   the moment they send; the landing's pass 1 behind a greeting that already painted.
   Opacity and 6px of travel, no scale. */
@keyframes arrive { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.arrive { animation: arrive 200ms cubic-bezier(.2,0,0,1) both; }
/* The change sentence has further to come and takes a moment longer. */
.change.arrive { animation: change-arrive 240ms cubic-bezier(.2,0,0,1) both; }
@keyframes change-arrive { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
/* Ranked rows stagger 20ms, capped at six — 120ms in total, and never longer. */
.arrive > .rk > .bar-row:nth-child(1) { animation-delay: 0ms; }
.arrive > .rk > .bar-row:nth-child(2) { animation-delay: 20ms; }
.arrive > .rk > .bar-row:nth-child(3) { animation-delay: 40ms; }
.arrive > .rk > .bar-row:nth-child(4) { animation-delay: 60ms; }
.arrive > .rk > .bar-row:nth-child(5) { animation-delay: 80ms; }
.arrive > .rk > .bar-row:nth-child(6) { animation-delay: 100ms; }
/* Only the rows on show arrive. A row inside the closed "Show all N" disclosure is in
   content the browser never ticks: its animation would sit at "running" for ever (and did —
   it hung the axe gate's wait for the resting state, 2026-09-18). */
.arrive > .rk > .bar-row { animation: arrive 200ms cubic-bezier(.2,0,0,1) both; }

/* Tick — ZERO movement. The glyph cross-fades and the line's colour ramps; the glyph
   column is a fixed width so nothing reflows. */
.step { transition: color 120ms linear; }
.step .tick { transition: color 120ms linear, opacity 120ms linear; }

/* Open / close — the disclosures. Open is quicker in, slower out. */
.numbers > summary::before, .art-more > summary::before, .checked > summary .tick { transition: transform 180ms cubic-bezier(.2,0,0,1); }
.numbers:not([open]) > summary::before, .art-more:not([open]) > summary::before { transition-duration: 140ms; transition-timing-function: cubic-bezier(.4,0,1,1); }

/* Chip selection does not animate the chip: the row fades and the question appears
   where questions appear. */
.chips.chosen { animation: chip-fade 120ms linear both; pointer-events: none; }
@keyframes chip-fade { from { opacity: 1; } to { opacity: 0; } }

/* The landing's chips arrive a beat apart: 20ms, capped at four — 80ms, never longer. */
.landing-chips.arrive .chip { animation: arrive 200ms cubic-bezier(.2,0,0,1) both; }
.landing-chips.arrive .chip:nth-child(2) { animation-delay: 20ms; }
.landing-chips.arrive .chip:nth-child(3) { animation-delay: 40ms; }
.landing-chips.arrive .chip:nth-child(4) { animation-delay: 60ms; }

/* Open / close, the content. A disclosure's body sits in a `.fold` — a one-row grid whose
   row goes 0fr → 1fr under the chevron, on the chevron's timings. Closing is held for the
   fold by foldable() in fold.js, because removing `open` would hide the body in the same
   frame. The working list leaves the same way (`.leaving`, via foldAway) so the figures
   below glide up instead of jumping when the receipt line replaces it. */
.fold { display: grid; }
.fold > * { min-height: 0; overflow: hidden; }
@keyframes unfold { from { grid-template-rows: 0fr; opacity: 0; } to { grid-template-rows: 1fr; opacity: 1; } }
@keyframes fold   { from { grid-template-rows: 1fr; opacity: 1; } to { grid-template-rows: 0fr; opacity: 0; } }
details[open] > .fold { animation: unfold 180ms cubic-bezier(.2,0,0,1) both; }
details.closing > .fold, .fold.leaving { animation: fold 140ms cubic-bezier(.4,0,1,1) both; }

/* The mark. Placeholder ✳ — it collides with Claude's logo and the avatar replaces it;
   what must survive the swap is the thinking hook, so it lives on `html.thinking`.
   The pulse is the ONLY loop in the product and it runs only while something is pending.
   Everything pending breathes on the same beat: the mark, the running step's glyph, and
   the hairline holding the lead's place. One keyframe, one period, nothing moves. */
.mark { display: inline-block; color: var(--plum); font-family: var(--serif); font-weight: 500; line-height: 1; }
html.thinking .mark,
html.thinking .step.run .tick,
html.thinking .lead-slot i { animation: pulse 1000ms ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
/* Reduced motion removes MOVEMENT, never information: the mark stops pulsing and
   changes colour instead, so a thinking state is still visible (§8). The glyph and the
   hairline do the same. */
@media (prefers-reduced-motion: reduce) {
  html.thinking .mark { animation: none; color: var(--ink-mid); }
  html.thinking .step.run .tick { animation: none; color: var(--plum); }
  html.thinking .lead-slot i { animation: none; background: var(--plum-soft); }
  * { transition-duration: 0ms !important; animation-duration: 0ms !important; }
}

/* ── app frame ─────────────────────────────────────────────────────────────── */
.app { display: grid; grid-template-columns: var(--rail-strip) minmax(0, 1fr); min-height: 100dvh; }
html.rail-open .app { grid-template-columns: var(--rail) minmax(0, 1fr); }
html.book-open .app { grid-template-columns: var(--rail-strip) minmax(0, 1fr) minmax(0, 45%); }
html.book-open.rail-open .app { grid-template-columns: var(--rail) minmax(0, 1fr) minmax(0, 45%); }
/* The rail's push eases (§8 Open/close): 200ms on the column. The rail itself is a fixed
   width (see `.rail.open`), so the widening column REVEALS it from behind `.rail-wrap`'s
   overflow instead of reflowing its text — and nothing fades: an opacity fade is what axe
   sees mid-animation, and the gate runs axe the instant the rail is clicked. It is still a
   push, not an overlay — that is the open review question; this only stops the reflow
   being a jolt. Below 1024 the rail slides over the page instead. */
.app { transition: grid-template-columns 200ms cubic-bezier(.2,0,0,1); }
.main { display: flex; flex-direction: column; min-height: 100dvh; min-width: 0; }
.thread-host { flex: 1 1 auto; position: relative; min-height: 0; }
/* The scrollbar is load-bearing for alignment, so it is reserved ALWAYS.
   `.thread` centres itself inside this scroller; the composer centres itself outside it.
   With `overflow-y: auto`, the moment an answer made the thread overflow a scrollbar
   appeared, this box got 17px narrower, and the centred thread slid 8.5px left while the
   composer did not move — the spine and the composer's border came apart by half a
   scrollbar mid-conversation and snapped back when the thread was short again. That is
   exactly the drift Philip saw when an artifact rendered.
   `scrollbar-gutter: stable` reserves the bar's width on the inline-end edge whether or
   not it is showing, so this box never changes width, and `--sbw` — measured once at
   boot, because CSS cannot ask how wide a scrollbar is — takes the same width off the
   composer. Both then centre inside the same usable width when the viewport is wider
   than `--col`, and both fill it identically below that, where nothing is centred.
   Two alternatives were measured and rejected: `both-edges` reserves on both sides
   unconditionally, which is correct while centred and 17px out as soon as the thread
   just fills; `overflow-y: scroll` is stable but parks a dead scrollbar track on a
   surface whose whole point is the absence of chrome. */
.thread-scroll { position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden; padding: 28px 0 0; scrollbar-gutter: stable; }
.new-answer { position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); border: 1px solid var(--rule); background: var(--paper); color: var(--ink); border-radius: var(--radius); padding: 7px 14px; font-size: 13px; }
.composer-host { flex: none; }

/* ── the spine (§6) ────────────────────────────────────────────────────────────
   One continuous rule down the WHOLE thread, not per item, with the date in the
   gutter beside it. This is the move that makes the product a book rather than a
   transcript, so everything else sits inside it: rows, the greeting, the composer's
   input border, all on the same x. */
/* The spine is as long as the book is written. It grows with the thread — a page that
   fills as you use it — which is the behaviour Philip asked to keep. The 8px that used
   to sit on `.thread-scroll`'s padding-bottom lives here instead: it is the same
   breathing room under the last row, but now it is INSIDE the thread's border box, so
   the spine covers it and lands exactly on the composer's rule once the thread is full.
   That 8px was the whole defect — a content-length spine could never reach the composer,
   so it always stopped just short and read as a broken corner. */
.thread { position: relative; max-width: var(--col); margin: 0 auto; padding-right: 32px; padding-bottom: 8px; }
.thread::before { content: ''; position: absolute; left: var(--gutter); top: 0; bottom: 0; width: 1px; background: var(--rule); }
.entry-days { display: contents; }
.row { display: grid; grid-template-columns: var(--gutter) 1fr; align-items: start; }
.gut { padding: 3px 16px 0 0; text-align: right; min-width: 0; }
/* The date marker sticks while its day is on screen; every other gutter is empty. */
.gut-mark { position: sticky; top: 12px; font-family: var(--sans); font-size: 11px; font-weight: 500; letter-spacing: .04em; line-height: 1.5; color: var(--ink-mid); text-transform: uppercase; }
.gut-date { display: block; }
.gut-sub { display: block; font-weight: 400; letter-spacing: .02em; text-transform: none; }
.body { padding-left: 28px; min-width: 0; }

/* a day of the book already written */
.entry-row .body { padding-top: 7px; padding-bottom: 7px; }
.entry { margin: 0; font-size: 14px; line-height: 1.5; }
.entry .amt { font-variant-numeric: tabular-nums lining-nums; }
.landing-row .body { padding-top: 40px; }

/* ── rail (§7, §11) ──────────────────────────────────────────────────────────
   56 px: the mark and a `+`. Opened it carries Start a new page · Your book ·
   Earlier · light/dark, and nothing else. The five view panels are deleted — each was
   the answer to a question, so each is asked as a question now. */
.rail-wrap { position: sticky; top: 0; height: 100dvh; border-right: 1px solid var(--rule); background: var(--paper); display: flex; overflow: hidden; }
.rail-strip { width: var(--rail-strip); flex: 0 0 var(--rail-strip); display: flex; flex-direction: column; align-items: center; padding: 16px 0; gap: 18px; }
.rail-toggle { width: 32px; height: 32px; border: 0; background: none; font-size: 17px; display: grid; place-items: center; padding: 0; }
.rail-plus { width: 32px; height: 32px; display: grid; place-items: center; border: 1px solid var(--rule); border-radius: var(--radius); background: none; color: var(--ink-mid); font-size: 15px; padding: 0; }
.rail-plus:hover { color: var(--ink); border-color: var(--ink-mid); }
.rail { display: none; flex-direction: column; min-height: 0; flex: 1; min-width: 0; }
/* Fixed at its open width, not flex: 1, so the column's 200ms ease uncovers it rather
   than squeezing its lines through every width in between. (Below 1024 the rail is
   position: fixed and sizes itself; this rule is inert there.) */
.rail.open { display: flex; flex: 0 0 calc(var(--rail) - var(--rail-strip)); }
.rail-body { padding: 16px 12px; overflow-y: auto; flex: 1; }
.rail-head { font-size: 11px; font-weight: 500; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); margin: 22px 8px 6px; }
.rail-group { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-mid); margin: 14px 8px 4px; }
.rail-item { display: block; width: 100%; text-align: left; border: 0; background: none; padding: 7px 8px; border-radius: var(--radius); font-size: 14px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The wordmark. It only ever appeared at ≤1023px, so the product had no name on a
   desktop — the mark alone carried it, and the mark is still a placeholder. */
.rail-word { font-family: var(--serif); font-size: 17px; font-weight: 500; letter-spacing: .01em; color: var(--ink); margin: 2px 8px 18px; }

/* A recent page is a label and a figure. The label may be clipped; the FIGURE MAY NOT —
   a rail full of "Aug 6–31 sales · ₱16…" is a row of cut numbers, which is worse than
   showing no number at all. */
.rail-item.recent { display: flex; align-items: baseline; gap: 8px; }
.rail-item.recent .r-lab { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-item.recent .r-amt { flex: 0 0 auto; font-size: 12.5px; color: var(--ink-mid); font-variant-numeric: tabular-nums lining-nums; }
.rail-item:hover { background: var(--paper-2); }
.rail-item.new, .rail-item.book { color: var(--ink); }
.rail-item.new:hover, .rail-item.book:hover { color: var(--plum); }
.rail-item.older, .rail-item.theme { font-size: 13px; }
.rail-item.theme { margin-top: 22px; }
.rail-item.dataset { display: block; padding: 0 4px; }
.rail-item.dataset select { width: 100%; border: 1px solid var(--rule); border-radius: var(--radius); background: none; color: var(--ink-mid); font-size: 13px; padding: 6px; }
.rail-item.dev { font-size: 13px; }
.rail-item.dev[aria-pressed="true"] { color: var(--plum); }
.rail-empty { color: var(--ink-mid); font-size: 13px; padding: 4px 8px; margin: 0; }

/* Earlier: delete on hover, no confirm, five seconds of inline Undo (§11). The Undo
   replaces the row in place, so nothing below it moves while they decide. */
.rail-row { display: flex; align-items: center; }
.rail-row.current .rail-item { color: var(--plum); }
.rail-row .rail-item { flex: 1; min-width: 0; }
.rail-del { border: 0; background: none; color: var(--ink-mid); font-size: 15px; line-height: 1; padding: 4px 8px; opacity: 0; }
.rail-row:hover .rail-del, .rail-row:focus-within .rail-del { opacity: 1; }
.rail-del:hover { color: var(--ink); }
.rail-undo { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 7px 8px; font-size: 13px; color: var(--ink-mid); }
.rail-undo-btn { border: 0; background: none; color: var(--plum); font-size: 13px; padding: 0; }
.rail-scrim { display: none; }

/* ── Your book (§11) ──────────────────────────────────────────────────────────
   Not a view: the record the owner owns, showing exactly the rows the tools read,
   so every figure in every answer can be checked by hand. */
.book-surface { position: sticky; top: 0; height: 100dvh; border-left: 1px solid var(--rule); background: var(--paper); display: flex; flex-direction: column; min-width: 0; }
.book-head { display: flex; align-items: baseline; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--rule); }
.book-head h2 { font-family: var(--serif); font-weight: 500; font-size: 21px; margin: 0; }
.book-count { flex: 1; font-size: 12px; }
.book-body { overflow: auto; padding: 0 20px 40px; }
.book-note { font-size: 13px; padding: 16px 0; }
.book-table { border-collapse: collapse; width: 100%; font-size: 13.5px; }
.book-table th { position: sticky; top: 0; background: var(--paper); text-align: left; padding: 0; border-bottom: 1px solid var(--rule); }
.book-table th.num { text-align: right; }
.book-sort { border: 0; background: none; width: 100%; text-align: inherit; padding: 10px 10px 10px 0; font-size: 11px; font-weight: 500; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); }
.book-sort.on { color: var(--ink); }
.book-table td { padding: 7px 10px 7px 0; border-bottom: 1px solid var(--rule-soft); white-space: nowrap; }
.book-table td.num { text-align: right; font-weight: 500; }

/* ── turns ─────────────────────────────────────────────────────────────────── */
.turn-row .body { padding-top: 18px; padding-bottom: 26px; }
.turn.assistant { padding-top: 2px; position: relative; }

/* The owner's turn is a marginal note, not a bubble (§7): flush right, no fill, no
   border, their own words in plum — the one place besides the rules that plum is used. */
.turn.owner .note { text-align: right; padding: 6px 0 4px; }
.note-rule { display: inline-block; width: 24px; height: 1px; background: var(--plum-soft); opacity: .6; margin-bottom: 10px; }
.note .q { margin: 0; font-size: 15px; line-height: 1.5; color: var(--plum); white-space: pre-wrap; }
.owner-actions { display: flex; gap: 8px; align-items: center; justify-content: flex-end; min-height: 18px; margin-top: 5px; }
.owner-actions .edit { opacity: 0; }
.turn.owner:hover .owner-actions .edit, .turn.owner:focus-within .owner-actions .edit { opacity: 1; }
.unanswered-tag { font-size: 12px; color: var(--ink-mid); font-style: italic; }
.quiet { border: 0; background: none; color: var(--ink-mid); font-size: 12px; padding: 2px 4px; border-radius: var(--radius); }
.quiet:hover { color: var(--ink); }
/* The selected-turn marker sits in the body's left padding, NOT inside the turn box.
   As an inset shadow it was drawn on the turn's own left edge, which has no left
   padding — so the rule ran flush against the prose and read as clipping into it.
   Absolute keeps it out of flow, so selecting a turn still shifts nothing. */
.turn.assistant.selected::before {
  content: ''; position: absolute; left: -14px; top: 0; bottom: 0;
  width: 2px; background: var(--plum-soft);
}

/* ── landing (§5.3) ───────────────────────────────────────────────────────────
   Greeting, then the deterministic state line, then a slot the change sentence
   appends into. The state line is never replaced and never moves. */
.greeting { font-family: var(--serif); font-size: 30px; font-weight: 500; line-height: 1.2; margin: 0 0 16px; letter-spacing: -.01em; }
.greeting .mark { font-size: 26px; margin-right: 4px; }
.state, .change { font-family: var(--serif); font-size: 19px; font-weight: 400; line-height: 1.55; margin: 0; color: var(--ink); }
.change { margin-top: 6px; }
.capability { font-size: 13.5px; line-height: 1.6; margin: 16px 0 0; max-width: 520px; }
.landing-chips { margin-top: 18px; }

/* ── the wait (§7) ────────────────────────────────────────────────────────────
   The Checked line and the loading state are the SAME component: it renders early
   from what is deterministically known and fills in as the plan resolves. There is
   no separate spinner anywhere in the product. Mono appears here and nowhere else. */
.work { min-height: 0; }
.checked-line, .checked > summary {
  font-family: var(--mono); font-size: 12.5px; line-height: 1.5; letter-spacing: -.01em;
  color: var(--ink-mid); margin: 0; padding: 2px 0 12px;
}
.checked-line .tick, .checked > summary .tick { color: var(--plum-soft); }

/* plan lines — zero movement on tick (§8): the glyph cross-fades, the text ramps. */
.steps { list-style: none; margin: 2px 0 0; padding: 0; }
.step { display: flex; gap: 10px; align-items: baseline; padding: 4px 0; font-size: 13.5px; line-height: 1.55; color: var(--ink-mid); }
.step .tick { font-family: var(--mono); font-size: 12px; width: 12px; flex: 0 0 12px; }
.step.done, .step.run { color: var(--ink); }
.step.done .tick { color: var(--plum-soft); }
.step.declined { color: var(--ink-mid); }
.declined-note { font-style: italic; }

.checked { margin: 0; }
.checked > summary { cursor: pointer; list-style: none; }
.checked > summary::-webkit-details-marker { display: none; }
/* The tick turns down when the work is open. Line 112 has always declared the
   transition for this; the rule that actually moves it was missing, so the
   disclosure was silently inert — it opened and never said so. */
.checked > summary .tick { display: inline-block; }   /* transforms do not apply to inline boxes */
.checked[open] > summary .tick { transform: rotate(90deg); }
/* And it has to look clickable before it is clicked: cursor:pointer alone is not an
   affordance for the one control that proves the numbers were computed, not written. */
.checked > summary:hover { color: var(--ink); }
.checked > summary:hover .tick { color: var(--plum); }
.checked .done-list { margin: 0 0 12px 1em; font-family: var(--sans); }

/* The scope line under each step — what was actually read, so the owner can check the
   assumption rather than trust it. Owner language only: no tool names, no tiers, no ISO
   dates, no field names. Those live in the developer drawer, which answers a different
   question (how it ran, not what it read). */
.step-scope { display: block; margin: 1px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--ink-mid); font-variant-numeric: tabular-nums lining-nums; }

/* ── the answer (§3, type scale §5) ───────────────────────────────────────────
   The lead is the serif line the answer turns on; everything after it is body sans
   at 14.5/1.65 and deliberately quieter, so the eye lands on the finding first. */
.answer p.lead { font-family: var(--serif); font-size: 21px; font-weight: 400; line-height: 1.5; letter-spacing: -.005em; margin: 0 0 4px; }
.answer p { font-size: 14.5px; line-height: 1.65; margin: 14px 0 0; max-width: 600px; }
.answer strong { font-weight: 600; color: inherit; }
.answer ul, .answer ol { font-size: 14.5px; line-height: 1.65; margin: 12px 0 0; padding-left: 20px; max-width: 600px; }
.answer li { margin: 4px 0; }
.answer .table-wrap, .numbers .table-wrap { overflow-x: auto; margin: 14px 0 0; }
.answer table, .numbers table { border-collapse: collapse; width: 100%; font-size: 13.5px; }
.answer th, .numbers th { text-align: left; font-weight: 500; color: var(--ink-mid); padding: 6px 10px 6px 0; border-bottom: 1px solid var(--rule); font-size: 11px; text-transform: uppercase; letter-spacing: .10em; }
.answer td, .numbers td { padding: 7px 10px 7px 0; border-bottom: 1px solid var(--rule-soft); vertical-align: top; }
.answer th.num, .answer td.num, .numbers th.num, .numbers td.num { text-align: right; font-weight: 500; }

/* A figure inside a serif line is the largest thing on the screen (§5). It NEVER
   animates its value — it appears at what it is, instantly, always. */
.fig { font-size: 1.36em; font-weight: 600; letter-spacing: -.015em; }

/* The lead's slot, held open while the sentence is still being written (§9). Two serif
   lines tall — the length the answer contract's lead almost always is — with a hairline
   drawn in it, so the prose lands in a space that already existed instead of shoving
   the artifact down the page. */
.lead-slot { min-height: calc(21px * 1.5 * 2); display: flex; align-items: center; }
.lead-slot i { display: block; width: 180px; height: 1px; background: var(--rule); }

.view-link .link { border: 0; background: none; color: var(--plum); font-size: 13px; padding: 0; }
.certainty { margin: 12px 0 0; }
.tag { display: inline-block; font-size: 11px; color: var(--ink-mid); border: 1px solid var(--rule); border-radius: var(--radius); padding: 1px 7px; letter-spacing: .04em; text-transform: uppercase; }

/* ── artifacts — the second surface (§7) ──────────────────────────────────────
   Everything computed sits on --paper-2 inside a hairline. That tone is the whole
   distinction between what the tools worked out and what was said about it. */
/* The artifact is data, not reading text, so it is allowed to be wider than the 600px
   prose measure — the page has the room and a truncated product name in a product
   ranking is the one thing this column cannot afford. */
.art { background: var(--paper-2); border: 1px solid var(--rule-soft); border-radius: var(--radius); padding: 16px 18px; margin: 20px 0 0; max-width: 680px; }
.art-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 11px; font-weight: 500; line-height: 1; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); padding-bottom: 10px; border-bottom: 1px solid var(--rule); }
/* The view switcher (Phase B): text labels in a segmented control on the head's right,
   the pressed view in plum. 44px targets at the phone width (below). */
.art-right { display: inline-flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.views { display: inline-flex; border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden; text-transform: none; letter-spacing: 0; }
.view { border: 0; background: none; color: var(--ink-mid); font-size: 12px; line-height: 1; padding: 7px 10px; min-height: 28px; cursor: pointer; }
.view + .view { border-left: 1px solid var(--rule); }
.view:hover { background: var(--paper-3); }
.view[aria-pressed="true"] { color: var(--plum); background: var(--paper); font-weight: 500; }
.view:focus-visible { outline: 2px solid var(--plum); outline-offset: -2px; }
.art-more { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--rule); }
.art-more > summary { cursor: pointer; list-style: none; font-size: 12.5px; line-height: 1; color: var(--ink-mid); }
.art-more > summary::-webkit-details-marker { display: none; }
.art-more > summary::before { content: '▶'; display: inline-block; width: 1.2em; font-size: 9px; }
.art-more[open] > summary::before { transform: rotate(90deg); }

/* ranked list: name · bar · amount · share (§5.8) */
.rk { list-style: none; margin: 0; padding: 0; }
.bar-row, .art-total { display: grid; grid-template-columns: 176px minmax(0, 1fr) 92px 44px; gap: 12px; align-items: center; }
.bar-row { padding: 8px 0; }
.bar-row + .bar-row { border-top: 1px solid var(--rule-soft); }
.bar-name { font-size: 13.5px; line-height: 1.3; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar-track { height: 9px; background: var(--paper-3); border-radius: 1px; overflow: hidden; }
.bar-fill { display: block; height: 100%; background: var(--plum); opacity: .72; }
.bar-amt { text-align: right; font-size: 13.5px; font-weight: 500; line-height: 1.3; color: var(--ink); }
.bar-pct { text-align: right; font-size: 12.5px; line-height: 1.3; }
/* The accounting rule above a sum: 2px, not a hairline. */
.art-total { margin-top: 6px; padding-top: 9px; border-top: 2px solid var(--rule); }
.art-total .bar-name { font-weight: 500; }
/* The Total has no bar. An empty track beside a full figure reads as zero, which is the
   opposite of what the row says — so the well is not drawn at all, and the column keeps
   its width so the figures above stay in line. */
.art-total .bar-track { background: none; }

/* trend */
.trend svg { width: 100%; height: auto; display: block; margin-top: 10px; }
.trend .grid { stroke: var(--rule); stroke-width: 1; }
.trend .line { fill: none; stroke: var(--plum); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.trend .band { fill: var(--plum); opacity: .14; }
.trend .marked { fill: var(--paper-2); stroke: var(--plum); stroke-width: 2; vector-effect: non-scaling-stroke; }
.trend text { font-family: var(--sans); font-size: 12px; fill: var(--ink-mid); font-variant-numeric: tabular-nums lining-nums;
  /* A figure must be legible wherever it lands: paint the paper behind the glyphs so a
     label that falls on the line or the axis still reads. */
  paint-order: stroke fill; stroke: var(--paper-2); stroke-width: 3px; stroke-linejoin: round; }
.trend .marked-label, .trend .marked-axis { fill: var(--ink); }
/* columns by day (Phase B): the same plum as the bar list's fill; the marked day at full strength */
.trend .col { fill: var(--plum); opacity: .72; }
.trend .col.marked { opacity: 1; }
.trend .axis.wd { font-size: 10px; }

/* comparison card (§5.9): three cells on a hairline grid, every one carrying its span */
.cmp-card .cmp { margin-top: 12px; border: 1px solid var(--rule-soft); background: var(--rule-soft); }
.cmp { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; }
.cmp.cmp-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cmp-cell { background: var(--paper-2); padding: 13px 15px; min-width: 0; }
.cmp-lab { font-size: 10.5px; font-weight: 500; line-height: 1.3; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-mid); }
.cmp-span { font-size: 11.5px; line-height: 1.4; color: var(--ink-mid); margin-top: 3px; }
.cmp-val { font-size: 19px; font-weight: 600; line-height: 1.2; letter-spacing: -.01em; margin-top: 8px; }
/* Ledger red lands on the figure and nowhere else — never a border, never a label. */
.cmp-val.neg { color: var(--red); }
.cmp-sub { font-size: 12px; line-height: 1.4; margin-top: 4px; }

/* data sheet (Phase A, 2026-09-17): more than five rows of period data scroll INSIDE the
   artifact instead of down the thread. About six rows show; the column header sticks to
   the top of the scroll and the tool's own total sticks to the bottom. Figures in mono,
   tabular, right-aligned. The fade says "there is more below" and leaves at the end. */
.datasheet { margin-top: 10px; }
.art-more .datasheet { margin-top: 14px; }
.datasheet-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 11px; font-weight: 500; line-height: 1; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); padding-bottom: 8px; }
.datasheet-view { position: relative; }
.datasheet-scroll { max-height: 236px; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; border-radius: 1px; }
.datasheet-scroll:focus-visible { outline: 2px solid var(--plum); outline-offset: 2px; }
.datasheet table { border-collapse: separate; border-spacing: 0; width: 100%; font-size: 13.5px; margin: 0; }
.datasheet th { position: sticky; top: 0; z-index: 1; background: var(--paper-2); text-align: left; font-weight: 500; color: var(--ink-mid); padding: 4px 10px 6px 0; border-bottom: 1px solid var(--rule); font-size: 11px; text-transform: uppercase; letter-spacing: .10em; }
.datasheet td { padding: 7px 10px 7px 0; border-bottom: 1px solid var(--rule-soft); vertical-align: top; white-space: nowrap; }
.datasheet th.num, .datasheet td.num { text-align: right; }
.datasheet td.num { font-family: var(--mono); font-size: 12.5px; font-weight: 500; font-variant-numeric: tabular-nums lining-nums; }
.datasheet td:last-child, .datasheet th:last-child { padding-right: 0; }
/* The accounting rule above a sum: 2px, not a hairline. */
.datasheet tfoot td { position: sticky; bottom: 0; z-index: 1; background: var(--paper-2); border-top: 2px solid var(--rule); border-bottom: 0; font-weight: 500; }
.datasheet tr.gap td { color: var(--ink-mid); }
.datasheet tr.gap td.num { font-family: var(--sans); font-weight: 400; font-size: 13px; }
/* The same mark the trend draws on the named day: a plum ring beside the date. */
.datasheet tr.named td:first-child, .datasheet tr.flagged td:first-child { padding-left: 14px; }
.datasheet .row-mark { display: inline-block; width: 8px; height: 8px; border: 2px solid var(--plum); border-radius: 50%; box-sizing: border-box; margin: 0 6px 1px -14px; vertical-align: middle; }
.datasheet tr.flagged .row-mark { border-radius: 0; transform: rotate(45deg); width: 7px; height: 7px; }
.datasheet tr.named td:first-child { color: var(--ink); font-weight: 500; }
.datasheet-view::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 28px; background: linear-gradient(to bottom, transparent, var(--paper-2)); pointer-events: none; opacity: 1; transition: opacity 180ms cubic-bezier(.2,0,0,1); }
.datasheet-view.has-total::after { bottom: var(--ds-foot, 36px); }
.datasheet-view.at-end::after { opacity: 0; }

/* paired bars (Phase B): the comparison's second view. Bars by length on the same track
   as the ranked list; the change figure is the only thing that can wear ledger red. */
.pairs { margin-top: 14px; display: grid; gap: 10px; }
.pair-row, .pair-change { display: grid; grid-template-columns: 176px minmax(0, 1fr) 120px; gap: 12px; align-items: center; }
.pair-lab { font-size: 13.5px; line-height: 1.3; color: var(--ink); }
.pair-span, .pair-sub { display: block; font-size: 11.5px; line-height: 1.4; color: var(--ink-mid); font-weight: 400; }
.pair-amt { text-align: right; font-size: 13.5px; font-weight: 500; line-height: 1.3; }
.pair-row.empty .bar-fill { width: 0; }
.pair-change { border-top: 1px solid var(--rule); padding-top: 10px; align-items: baseline; }
.pair-change .pair-val { grid-column: 3; text-align: right; font-size: 19px; font-weight: 600; line-height: 1.2; letter-spacing: -.01em; }
.pair-change .pair-sub { grid-column: 3; text-align: right; }
.pair-change.neg .pair-val { color: var(--red); }

/* share strip (Phase B): one stacked bar, one hue in four tone steps, a 2px surface gap,
   and the key list carrying every name, amount and share — no legend box, no pie. */
.strip { display: flex; gap: 2px; height: 18px; margin-top: 14px; border-radius: 1px; }
.seg { display: block; flex: 0 0 auto; background: var(--plum); border-radius: 1px; min-width: 2px; }
.seg[data-tone="1"], .swatch[data-tone="1"] { opacity: .9; }
.seg[data-tone="2"], .swatch[data-tone="2"] { opacity: .68; }
.seg[data-tone="3"], .swatch[data-tone="3"] { opacity: .5; }
.seg[data-tone="4"], .swatch[data-tone="4"] { opacity: .34; }
.seg.rest, .swatch.rest { background: var(--paper-3); opacity: 1; }
.strip-key { list-style: none; margin: 10px 0 0; padding: 0; }
.strip-key li { display: grid; grid-template-columns: 12px minmax(0, 1fr) 92px 44px; gap: 12px; align-items: center; padding: 6px 0; border-top: 1px solid var(--rule-soft); font-size: 13.5px; }
.strip-key li:first-child { border-top: 0; }
.strip-key .swatch { display: block; width: 10px; height: 10px; background: var(--plum); border-radius: 1px; }
.strip-key .key-amt, .strip-key .key-pct { text-align: right; }
.strip-key .key-amt { font-weight: 500; }
.strip-key li.rest .key-name { color: var(--ink-mid); }

/* target vs actual (Phase B): a bullet — the fill is what came in, the ticks are the pace
   and the target; the gap figure alone may wear ledger red. */
.bullet { margin-top: 16px; }
.bullet-track { position: relative; height: 14px; background: var(--paper-3); border-radius: 1px; }
.bullet-fill { position: absolute; left: 0; top: 0; bottom: 0; background: var(--plum); opacity: .72; border-radius: 1px; }
.bullet-tick { position: absolute; top: -5px; bottom: -5px; width: 2px; margin-left: -1px; background: var(--ink); }
.bullet-tick.pace { background: var(--plum); }
.bullet-legend { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px; font-size: 12.5px; color: var(--ink-mid); }
.bullet-legend b { font-weight: 500; color: var(--ink); }
.bullet-target { margin-left: auto; }
.bullet-gap { margin: 12px 0 0; font-size: 15px; }
.bullet-gap .fig { font-weight: 600; }
.bullet-gap.neg .fig { color: var(--red); }
.bullet-need { color: var(--ink-mid); font-size: 13px; }

/* gross-profit waterfall (Phase B): three floating bars on one track */
.wf { margin-top: 14px; display: grid; gap: 10px; }
.wf-row { display: grid; grid-template-columns: 120px minmax(0, 1fr) 100px; gap: 12px; align-items: center; font-size: 13.5px; }
.wf-track { position: relative; display: block; height: 14px; background: var(--paper-3); border-radius: 1px; }
.wf-bar { position: absolute; top: 0; bottom: 0; background: var(--plum); border-radius: 1px; }
.wf-bar.sales { opacity: .72; }
.wf-bar.cost { opacity: .34; }
.wf-bar.profit { opacity: 1; }
.wf-amt { text-align: right; font-weight: 500; }
.wf-bar.expenses { opacity: .34; }
.wf-bar.net { opacity: 1; }
.wf-amt.neg { color: var(--red); }

/* the statement artifact (Phase C): a real table, right-aligned Plex Mono figures — the
   sheet's own tokens — sales/gross/net/left carry the weight, a rule sits above gross
   and above whichever of net/left is the bottom line, cogs and each expense line read
   as a deduction (a leading minus, never a colour), and the expenses subtotal is muted.
   Ledger red belongs to the bottom-line figure alone, only when it is a loss. */
.stmt-table { border-collapse: separate; border-spacing: 0; width: 100%; font-size: 13.5px; margin-top: 6px; }
.stmt-table th, .stmt-table td { padding: 7px 0; border-bottom: 1px solid var(--rule-soft); text-align: left; font-weight: 400; }
.stmt-table th[scope="row"] { color: var(--ink); }
.stmt-table td.num { font-family: var(--mono); font-size: 13px; font-weight: 500; font-variant-numeric: tabular-nums lining-nums; text-align: right; white-space: nowrap; }
.stmt-table tr.emph th, .stmt-table tr.emph td { font-weight: 600; }
.stmt-table tr.rule-above th, .stmt-table tr.rule-above td { border-top: 1px solid var(--rule); }
.stmt-table tr.subtotal th, .stmt-table tr.subtotal td { color: var(--ink-mid); font-weight: 400; }
.stmt-table td.num.neg { color: var(--red); }
.stmt-scope { margin: 0 0 8px; font-size: 12.5px; color: var(--ink-mid); }
.stmt-note, .stmt-foot { margin: 8px 0 0; font-size: 12.5px; }
.stmt .datasheet { margin-top: 4px; }

/* the decline contract: can't → why → the unlock → instead, in that order, never a
   bullet list. The unlock is a real button, plum on the paper tone, with the same
   :focus-visible ring as everything else (§4) — no extra rule needed for that. */
.decline { margin-top: 6px; }
.decline-cant { margin: 0; font-size: 15px; }
.decline-why { margin: 4px 0 0; color: var(--ink-mid); font-size: 13.5px; }
.decline-unlock { margin: 12px 0 0; border: 1px solid var(--plum-soft); background: transparent; color: var(--plum); border-radius: var(--radius); padding: 9px 14px; font-size: 13.5px; font-weight: 500; }
.decline-unlock:hover { background: var(--paper-2); border-color: var(--plum); }
.decline-instead { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--rule-soft); }
.stmt-instead-lead { margin: 0 0 8px; font-size: 13.5px; color: var(--ink-mid); }

/* heatmap (Phase B): five steps of one hue (--seq-*, validated); empty cells outlined;
   the two named cells ringed; no text on a cell — the figures live in the Sheet view */
.hm { width: 100%; height: auto; display: block; margin-top: 8px; }
.hm .axis { font-family: var(--sans); font-size: 11px; fill: var(--ink-mid); }
.hm .cell.s1, .hm-key .s1 { fill: var(--seq-1); background: var(--seq-1); }
.hm .cell.s2, .hm-key .s2 { fill: var(--seq-2); background: var(--seq-2); }
.hm .cell.s3, .hm-key .s3 { fill: var(--seq-3); background: var(--seq-3); }
.hm .cell.s4, .hm-key .s4 { fill: var(--seq-4); background: var(--seq-4); }
.hm .cell.s5, .hm-key .s5 { fill: var(--seq-5); background: var(--seq-5); }
.hm .cell.none { fill: none; stroke: var(--rule); stroke-width: 1; stroke-dasharray: 3 3; }
.hm .cell.strongest, .hm .cell.weakest { stroke: var(--ink); stroke-width: 2; }
.hm-key { display: flex; align-items: center; gap: 4px; margin: 8px 0 0; font-size: 11.5px; color: var(--ink-mid); }
.hm-key i { display: inline-block; width: 16px; height: 10px; border-radius: 1px; }
.hm-key span:first-child { margin-right: 4px; }
.hm-key .hm-none { margin-left: auto; }
.hm-key .hm-none i { border: 1px dashed var(--rule); background: none; vertical-align: middle; margin-right: 4px; }

/* ── disclosures (§7) ─────────────────────────────────────────────────────────
   `› See the numbers`, and on a decline `› What's missing` — a decline has no facts to
   disclose but it does have a gap. No rule above either: the answer is one column. */
.numbers { margin: 18px 0 0; }
.numbers > summary { cursor: pointer; list-style: none; font-size: 13px; line-height: 1; color: var(--ink-mid); }
.numbers > summary::-webkit-details-marker { display: none; }
.numbers > summary::before { content: '›'; display: inline-block; width: 1em; color: var(--plum-soft); transition: transform .18s cubic-bezier(.2,0,0,1); }
.numbers[open] > summary::before { transform: rotate(90deg); }
.numbers-block { margin: 12px 0 0; font-size: 13px; }
.numbers-block h4 { margin: 0 0 6px; font-weight: 500; font-size: 11px; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); }
.numbers-block .facts { display: grid; grid-template-columns: 1fr; gap: 0; margin: 0 0 8px; }
.numbers-block .facts div { display: flex; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--rule-soft); padding: 4px 0; }
.numbers-block .facts dt { color: var(--ink-mid); font-weight: 400; }
.numbers-block .facts dd { margin: 0; font-weight: 500; }
.numbers-block .claims { margin: 6px 0 0; padding-left: 18px; line-height: 1.6; }
.numbers-block .flag { margin: 0 0 8px; }
.numbers table th, .numbers table td { font-size: 13px; }

/* Chips (§7): a hairline rectangle, never a pill, never filled. The recommended
   action is plum-lettered — it is not a button colour, it is the owner's own next move. */
.chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 18px 0 0; }
.chip { border: 1px solid var(--rule); background: transparent; color: var(--ink); border-radius: var(--radius); padding: 9px 12px; font-size: 13px; line-height: 1; text-align: left; }
.chip:hover { background: var(--paper-2); border-color: var(--ink-mid); }
.chip.act { color: var(--plum); border-color: var(--plum-soft); }
.chip.act:hover { color: var(--plum); border-color: var(--plum); }
.actions { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; margin-top: 16px; min-height: 22px; }
/* Meta row: Copy · Something off? · time. The time is always there — mono is reserved
   for the Checked line and never used for a timestamp (§5). */
/* Spec §7 asks for the timestamp ON HOVER; the build shipped it permanently, which put
   the same time on screen twice — once in the gutter, where it is what makes an answer
   findable later, and again here. The gutter keeps it; this one appears when reached.
   Opacity, not display, so it stays in the accessibility tree. */
.actions .time { font-size: 12px; line-height: 1; color: var(--ink-mid); opacity: 0; transition: opacity 120ms linear; }
.turn.assistant:hover .actions .time, .turn.assistant:focus-within .actions .time { opacity: 1; }
.off-form { display: flex; gap: 6px; width: 100%; }
.off-form input { flex: 1; border: 1px solid var(--rule); background: var(--paper); border-radius: var(--radius); padding: 6px 9px; font-size: 14px; }
.off-done { font-size: 13px; color: var(--ink-mid); }
/* Not red: ledger red belongs to a bad-news figure, never to error chrome (§4). */
.recovery { color: var(--ink); margin: 0 0 8px; font-family: var(--serif); font-size: 21px; line-height: 1.5; }
.recovery-actions { display: flex; gap: 8px; }

/* ── composer — a rule, not a card (§7) ───────────────────────────────────────
   One hairline across the foot of the page. `+ Add data` sits in the gutter column
   with the dates; the input's left border continues the spine to the bottom of the
   page. No box, no radius, no shadow, and no circular arrow. */
/* padding-right = the scroller's reserved scrollbar, so `.composer-in` and `.thread`
   centre inside identical widths. The border-top stays full-bleed. */
.composer { border-top: 1px solid var(--rule); background: var(--paper); padding-right: var(--sbw); }
.composer-in { max-width: var(--col); margin: 0 auto; padding-right: 32px; display: grid; grid-template-columns: var(--gutter) minmax(0, 1fr) auto; align-items: center; }
.add { justify-self: end; margin-right: 16px; border: 0; background: none; padding: 0; white-space: nowrap; font-size: 13px; line-height: 1; color: var(--ink-mid); }
.add b { font-weight: 400; color: var(--plum); font-size: 14px; margin-right: 4px; }
.add:hover { color: var(--ink); }
.field { display: block; width: 100%; border: 0; border-left: 1px solid var(--rule); background: none; resize: none; outline: none; font-size: 15px; line-height: 24px; padding: 22px 0 22px 28px; max-height: 168px; }
.field::placeholder { color: var(--ink-mid); }
.ask { border: 0; background: none; color: var(--ink-mid); font-size: 13.5px; font-weight: 500; letter-spacing: .02em; padding: 10px 0 10px 20px; }
.ask.on { color: var(--plum); }
.stop { border: 1px solid var(--rule); border-radius: var(--radius); background: none; color: var(--ink-mid); font-size: 12px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase; padding: 9px 12px; margin-left: 20px; }
.stop:hover { color: var(--ink); border-color: var(--ink-mid); }

/* ── developer drawer (§5.4) ───────────────────────────────────────────────── */
.drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(var(--drawer), 100vw); background: var(--paper); border-left: 1px solid var(--rule); display: flex; flex-direction: column; z-index: 20; font-size: 13px; }
.drawer-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--rule); }
.drawer-head h2 { font-size: 15px; margin: 0; flex: 1; font-weight: 600; }
.drawer-head .hint { color: var(--ink-mid); font-family: var(--mono); font-size: 12px; }
.drawer-body { overflow: auto; padding: 12px 16px; flex: 1; }
.drawer-body section { margin-bottom: 16px; }
.drawer-body h3 { font-size: 12px; text-transform: none; color: var(--ink-mid); margin: 0 0 6px; font-weight: 600; }
.drawer-body pre { margin: 0; background: var(--paper-2); border-radius: var(--radius); padding: 8px; overflow-x: auto; font-family: var(--mono); font-size: 11.5px; line-height: 1.4; white-space: pre-wrap; word-break: break-word; }
.drawer-body .kv div { display: grid; grid-template-columns: 90px 1fr; gap: 8px; padding: 3px 0; border-bottom: 1px dotted var(--rule); }
.drawer-body .kv dt { color: var(--ink-mid); } .drawer-body .kv dd { margin: 0; word-break: break-word; }
.drawer-body .dev-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.drawer-body .dev-table th, .drawer-body .dev-table td { text-align: left; padding: 4px 6px 4px 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
.drawer-body .small { font-size: 11px; }
.drawer-body details summary { cursor: pointer; padding: 4px 0; }
.drawer-foot { border-top: 1px solid var(--rule); padding: 8px 16px; color: var(--ink-mid); }

/* ── entry sheet (§5.12) ──────────────────────────────────────────────────────
   A bottom sheet, and the ONE place radius 2 is broken: 8 px on the top corners,
   because it is a physical drawer rather than a card. */
.sheet-scrim { position: fixed; inset: 0; background: rgba(30, 26, 21, .28); z-index: 29; }
.sheet {
  position: fixed; left: 50%; bottom: 0; transform: translateX(-50%);
  width: min(560px, 100vw); background: var(--paper);
  border: 1px solid var(--rule); border-bottom: 0;
  border-radius: 8px 8px 0 0;
  padding: 18px 20px 24px; z-index: 30;
  animation: sheet-in 240ms cubic-bezier(.2,0,0,1);
}
@keyframes sheet-in { from { transform: translate(-50%, 100%); } to { transform: translate(-50%, 0); } }
.sheet-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 14px; }
.sheet-head h2 { font-family: var(--serif); font-weight: 500; font-size: 21px; margin: 0; }
.sheet-saved:not(:empty) { margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--rule-soft); }
.sheet-line { margin: 0 0 3px; font-size: 13px; }
.sheet-form { display: grid; gap: 14px; }
.sheet-field { display: grid; gap: 5px; }
.sheet-field > span { font-size: 11px; font-weight: 500; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); }
.sheet-field input { border: 0; border-bottom: 1px solid var(--rule); background: none; padding: 6px 0; font-size: 16px; outline: none; }
.sheet-field input:focus { border-bottom-color: var(--plum-soft); }
.sheet-field.amount input { font-size: 28px; font-weight: 600; letter-spacing: -.015em; }
.sheet-days { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.day-btn { border: 1px solid var(--rule); border-radius: var(--radius); background: none; color: var(--ink-mid); font-size: 13px; line-height: 1; padding: 9px 12px; }
.day-btn.on { color: var(--plum); border-color: var(--plum-soft); }
.sheet-days input[type="date"] { border: 1px solid var(--rule); border-radius: var(--radius); background: none; color: var(--ink-mid); font-size: 13px; padding: 8px; }
.sheet-save { border: 1px solid var(--plum-soft); border-radius: var(--radius); background: none; color: var(--plum); font-size: 13px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase; padding: 12px; }
.sheet-save:hover { background: var(--paper-2); }
.sheet-save[disabled] { opacity: .5; }
.sheet-error { margin: 0; font-size: 13px; }

/* ── entry sheet: Sale · Expense (Phase C card 1) ────────────────────────────
   The segmented switch and the category row reuse the sheet's own tokens; no new
   colour is introduced. Buttons keep a 44px touch height at every width, not only
   the phone breakpoint, since the sheet itself is width-capped and centred. */
.mode-switch { display: flex; gap: 8px; margin-bottom: 14px; }
.mode-btn {
  flex: 1; min-height: 44px; border: 1px solid var(--rule); border-radius: var(--radius);
  background: none; color: var(--ink-mid); font-size: 13px; font-weight: 500;
  letter-spacing: .04em; text-transform: uppercase; padding: 10px 12px;
}
.mode-btn.on { color: var(--plum); border-color: var(--plum-soft); background: var(--paper-2); }
.mode-btn:focus-visible, .cat-btn:focus-visible, .day-btn:focus-visible { outline: 2px solid var(--plum-soft); outline-offset: 2px; }
.cat-group { display: flex; flex-wrap: wrap; gap: 8px; }
.cat-btn {
  min-height: 44px; border: 1px solid var(--rule); border-radius: var(--radius);
  background: none; color: var(--ink-mid); font-size: 13px; padding: 9px 12px;
}
.cat-btn.on { color: var(--plum); border-color: var(--plum-soft); background: var(--paper-2); }
.sheet-more { border-top: 1px solid var(--rule-soft); padding-top: 10px; }
.sheet-more summary { cursor: pointer; font-size: 12px; font-weight: 500; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-mid); }
.sheet-more .sheet-field { margin-top: 12px; }
.receipt-btn { min-height: 44px; border: 1px dashed var(--rule); border-radius: var(--radius); text-align: left; width: fit-content; }
.receipt-btn[disabled] { opacity: .6; }
.receipt-status { margin: 0; font-size: 12px; color: var(--ink-mid); }

/* ── phone (§S8: verified at 390, zero horizontal overflow) ────────────────── */
@media (max-width: 1023px) {
  .app, html.rail-open .app, html.book-open .app, html.book-open.rail-open .app { grid-template-columns: minmax(0, 1fr); }
  /* The mark is a REAL 44px bar the thread scrolls under, not a floating control
     sitting on top of the date gutter (§10, and the owner-test overlap at 390). */
  .rail-wrap { position: static; height: auto; border: 0; background: none; overflow: visible; display: contents; }
  .rail-strip { position: fixed; left: 0; right: 0; top: 0; z-index: 17; width: auto; height: 44px; flex-direction: row; align-items: center; gap: 12px; padding: 0 14px; background: var(--paper); border-bottom: 1px solid var(--rule); }
  .rail-strip::after { content: 'START'; font-size: 13px; font-weight: 500; letter-spacing: .02em; color: var(--ink-mid); }
  .rail-toggle { width: 28px; height: 28px; }
  .rail-plus { display: none; }   /* the `+` lives in the composer at this width */
  .rail { position: fixed; left: 0; top: 0; bottom: 0; width: min(var(--rail), 86vw); transform: translateX(-100%); transition: transform .18s cubic-bezier(.2,0,0,1); z-index: 18; background: var(--paper); border-right: 1px solid var(--rule); display: flex; }
  .rail.open { transform: none; }
  .rail-body { padding-top: 52px; }
  .rail-scrim { display: block; position: fixed; inset: 0; background: rgba(30, 26, 21, .28); z-index: 17; }
  .rail-scrim[hidden] { display: none; }
  .thread-scroll { padding: 60px 0 0; }
  .thread { padding-right: 16px; padding-bottom: 12px; }
  .body { padding-left: 16px; }
  /* The gutter is a desktop luxury. At 390 it was holding 72px — a fifth of the screen —
     for a date, while product names truncated in the artifact beside it. It keeps the
     date and gives the rest back; the spine and the composer's `+` follow it, because
     both are positioned from this same token. */
  :root { --gutter: 44px; }
  .gut { padding-right: 10px; }
  .gut-mark { font-size: 10.5px; }
  .rail-word { display: none; }   /* the 44px bar already carries the wordmark here */
  /* `+` only, and it never scrolls away — it is the entry point, not a toolbar item. */
  .composer-in { padding-right: 14px; }
  .add { margin-right: 10px; font-size: 0; }
  .add b { font-size: 19px; margin: 0; }
  .field { padding: 18px 0 18px 16px; }
  .ask { padding-left: 14px; }
  .stop { margin-left: 14px; }
  .book-surface { position: fixed; inset: 0; z-index: 19; height: 100dvh; }
  .greeting { font-size: 25px; }
  .state, .change { font-size: 17px; }
  .answer p.lead { font-size: 18.5px; }
  .lead-slot { min-height: calc(18.5px * 1.5 * 2); }
  /* Bars are dropped below 1024 (§8) — figures and shares stay, and they are the point. */
  .art { padding: 13px 14px; }
  .datasheet-scroll { max-height: 224px; }
  .art-head { flex-wrap: wrap; row-gap: 8px; }
  .pair-row, .pair-change { grid-template-columns: minmax(0, 1fr) 120px; }
  .wf-row { grid-template-columns: 96px minmax(0, 1fr) 84px; }
  .pair-row .bar-track { grid-column: 1 / -1; display: block; }
  .pair-change .pair-val, .pair-change .pair-sub { grid-column: 2; }
  .view { min-height: 44px; padding: 0 12px; }
  .datasheet td { padding-right: 8px; }
  .bar-row, .art-total { grid-template-columns: minmax(0, 1fr) 96px 46px; align-items: baseline; }
  .bar-track { display: none; }
  /* At this width a long name cannot be made to fit on one line, and the name is the
     content of a product ranking — so it wraps rather than truncating. Two short lines
     beat "Ham & Ch…". */
  .bar-name { white-space: normal; overflow: visible; text-overflow: clip; }
  /* The body's left padding is 16px here, so the marker moves in to stay clear of both
     the spine and the prose. */
  .turn.assistant.selected::before { left: -9px; }
  .cmp, .cmp.cmp-2 { grid-template-columns: 1fr; }
  .drawer { width: 100vw; }
}

/* ── sign in · plan locked (platform foundation, Card 4b) ─────────────────────
   The first screen an owner ever sees, so it is the same paper, the same two inks
   and the same rule as everything else — one column, centred, no card, no shadow.
   Existing tokens only; not one value above is changed. It fits inside 390 without
   a horizontal scrollbar because the column is capped in `min()` against the
   viewport, not in fixed pixels. */
.auth { min-height: 100dvh; display: grid; place-items: center; padding: 32px 20px; }
.auth-form { width: min(360px, 100%); display: grid; gap: 4px; }
.auth-title { font-family: var(--serif); font-size: 27px; font-weight: 400; letter-spacing: -.01em; margin: 0; color: var(--ink); }
.auth-sub { margin: 2px 0 22px; font-size: 15px; color: var(--ink-mid); }
.auth-field { margin-top: 14px; }
.auth-field > span { font-size: 11px; font-weight: 500; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); }
.auth-input { width: 100%; border: 0; border-bottom: 1px solid var(--rule); background: none; padding: 7px 0; font-size: 16px; outline: none; color: var(--ink); }
.auth-input:focus { border-bottom-color: var(--plum-soft); }
.auth-error { margin: 14px 0 0; min-height: 1.55em; font-size: 13.5px; color: var(--red); }
.auth-submit { margin-top: 10px; border: 1px solid var(--plum-soft); border-radius: var(--radius); background: none; color: var(--plum); font-size: 13px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase; padding: 12px; }
.auth-submit:hover { background: var(--paper-2); }
.auth-submit[disabled] { opacity: .5; }
/* The locked state: one sentence, and a way out. */
.auth-locked { margin: 18px 0 0; font-family: var(--serif); font-size: 19px; line-height: 1.5; color: var(--ink); }
.auth-link { margin-top: 18px; justify-self: start; border: 0; background: none; padding: 0; font-size: 14px; color: var(--plum); text-decoration: underline; text-underline-offset: 3px; }

/* ── Account (platform foundation, Card 5b) ──────────────────────────────────
   Mounted the way "Your book" is (`book.js` / `.book-surface`), but an overlay rather
   than a pushed grid column: Account is opened rarely, from the rail, and does not want
   a permanent share of `.app`'s width the way the book does. Existing tokens only; no
   value changed anywhere above this line (the palette is locked, both themes). */
.account-scrim { position: fixed; inset: 0; z-index: 19; background: rgba(30, 26, 21, .28); }
.account-scrim[hidden] { display: none; }
.account-surface {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 100vw); z-index: 20;
  background: var(--paper); border-left: 1px solid var(--rule);
  display: flex; flex-direction: column; min-width: 0;
  transform: translateX(100%); transition: transform 200ms cubic-bezier(.2,0,0,1);
}
html.account-open .account-surface { transform: none; }
.account-head { display: flex; align-items: baseline; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--rule); }
.account-head h2 { font-family: var(--serif); font-weight: 500; font-size: 21px; margin: 0; flex: 1; }
.account-body { overflow: auto; padding: 4px 20px 40px; }
.account-note { font-size: 13px; padding: 16px 0; color: var(--ink-mid); }
.account-plan { font-family: var(--serif); font-size: 17px; margin: 14px 0 4px; color: var(--ink); }
.account-head3 { font-size: 11px; font-weight: 500; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); margin: 22px 0 8px; }
.account-head4 { font-size: 13px; font-weight: 500; margin: 0 0 4px; color: var(--ink); }
.account-stores { list-style: none; margin: 0; padding: 0; font-size: 13.5px; }
.account-stores li { padding: 5px 0; border-bottom: 1px solid var(--rule-soft); }
.account-team { list-style: none; margin: 0 0 20px; padding: 0; }
.account-row { padding: 10px 0; border-bottom: 1px solid var(--rule-soft); display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; }
.account-row-main { flex: 1 1 auto; min-width: 0; }
.account-row-name { font-size: 14px; color: var(--ink); }
.account-row-meta { display: block; font-size: 12.5px; color: var(--ink-mid); }
.account-row-actions { display: flex; gap: 10px; flex: none; }
.account-form { display: grid; gap: 4px; margin-top: 8px; }
.account-reset-form { width: 100%; }
.account-field { margin-top: 12px; }
.account-field > span { font-size: 11px; font-weight: 500; letter-spacing: .10em; text-transform: uppercase; color: var(--ink-mid); }
.account-input { width: 100%; border: 0; border-bottom: 1px solid var(--rule); background: none; padding: 7px 0; font-size: 15px; outline: none; color: var(--ink); box-sizing: border-box; }
.account-input:focus { border-bottom-color: var(--plum-soft); }
.account-error:empty { display: none; }
.account-error { margin: 10px 0 0; font-size: 13px; color: var(--red); }
.account-submit { margin-top: 10px; border: 1px solid var(--plum-soft); border-radius: var(--radius); background: none; color: var(--plum); font-size: 13px; font-weight: 500; padding: 10px 14px; justify-self: start; }
.account-submit:hover { background: var(--paper-2); }
.account-submit[disabled] { opacity: .5; }
@media (max-width: 1023px) {
  .account-surface { width: 100vw; }
}
