/* ── the design system ──────────────────────────────────────────────────────────────────────────
   One scale for the whole site, added here before anything is moved onto it. Nothing below
   --fs-xs renders: the sweep this exists to serve found 57 of 231 font-size declarations under
   12px (9.5 twice, 10 eight times, 10.5 twelve, 11 eighteen, 11.5 seventeen) and 24 labels in
   tracked capitals, which is the same complaint written twice — the smallest text on the page was
   also the hardest to skim. The four ink steps each clear 4.5:1 on both surfaces, so a step down
   means less emphasis and never less legibility, and --ink-4 is the floor: nothing dimmer is
   ever text.

   Two colour families and only two, because the page had neither: one hex did duty for a past-due marker, an
   off-chart account, a sync diff and three pages' waiting icon. Rust is severity — four steps, which is what
   the paper has, in the two registers that are one family (the aging buckets, and the submitted file's three
   actions, which are the same partition in another vocabulary and therefore wear the same ink) — and it is a
   FILL ramp and never a text ramp: the light end is 2.28:1, so every severity *word* is
   --rust-ink and the tier is carried by a mark. Green is money and the brand, and appears on no
   scale at all, which is the only way it can mean dollars alone.

   The fact colour sits off the ramp on purpose. A bill the paper has already answered at its own mark is not
   the darkest step of anybody's scale; it is a different kind of statement, and it is the one thing on this
   page no level word is allowed to describe — which is why the row that carries one shows a flag in
   --fact-ink beside an estimate that keeps its own colour.
   ─────────────────────────────────────────────────────────────────────────────────────────────── */
:root{
  --font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --paper: Georgia, "Times New Roman", serif;              /* a sheet is a document, nothing else */
  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;  /* invoice ids and refs, never a label */

  --fs-xs:12px; --fs-sm:14px; --fs-md:17px; --fs-lg:20px;
  --fs-xl:24px; --fs-2xl:28px; --fs-3xl:34px; --fs-4xl:40px;
  --lh-display:1.08; --lh-tight:1.22; --lh-body:1.55;
  --w-regular:400; --w-medium:550; --w-semibold:650; --w-bold:700;

  --ink-1:#16241e; --ink-2:#2a3a33; --ink-3:#526458; --ink-4:#69756d;
  --surface:#ffffff; --surface-sunk:#f6f8f6; --line:#e3e7e3; --line-strong:#c6cfc8;

  --money:#286244; --money-mid:#387554; --money-deep:#1b493b; --money-track:#eaf3ee;
  --sev-1:#f69e7f; --sev-2:#cd785a; --sev-3:#a65438; --sev-4:#823417;
  --sev-none:#8d9993; --sev-track:#f6e8df;
  --rust-ink:#8a4a30; --fact-ink:#5b2512;
  /* The four steps above are the ramp, and the validator says so: run through
     `validate_palette.js --ordinal` on the adjacency the page actually draws, they come out monotone in
     lightness, every ΔL at or above .06, one hue (spread 1°), light end 2.02:1 against the surface. The
     same run over the five-stop *bar* — which puts `--sev-none` at the ramp's end — FAILS the ordinal
     checks, and that is the point: the grey is not a fifth step, it is the dollars nobody has good cause to
     chase yet, so it is textured rather than stepped, and `--fact-ink` sits off the ramp at the other end
     because a bill the paper has answered is not the darkest forecast. Below the ramp, the same family at a
     paler step:
     a tint washes a panel, a track is the ground a meter is measured against, and the pale end of the ramp
     still needs an edge on its own. One rule the walk taught me about the four ink steps: --ink-4 is the
     floor on the two surfaces it was measured against — the white (4.81) and the sunk grey (4.51) — but a
     family *tint* is a third ground, and the same ink falls under 4.5 on all five of them: 4.41 on the
     paper band, 4.25 on the money track and the severity wash, 4.01 on `--sev-track`, the darkest ground
     the site paints. So the floor is a pair-and-its-grounds, not a property of the colour, and the rule is
     stated where it can be tested: --ink-4 lives on the neutral pair, and on a tint the step is --ink-3,
     which clears 4.5 everywhere — its worst is 5.28, on that same `--sev-track`. */
  --sev-tint:#fbefe4; --sev-line:#e8c8a8;
  /* Green carries no scale, so it needs only the three steps a page can ask of a brand colour, plus the
     line and the track. `--focus` is the one place a green is allowed to be bright — and the ring it
     replaces was 2.88:1 against white, under the 3:1 a focus indicator has to clear, which is why the
     ring now wears `--money-mid`'s step rather than a lighter one of its own. */
  --money-line:#b9d6c4; --focus:#387554;
  /* The ledger's own two columns, in the two colours no scale on this site uses: an entry is a debit or a
     credit, and a purple that meant only that has to be named rather than folded into severity. */
  --credit:#5a4a7a;
  /* The facsimile of a printed sheet keeps its own paper and its own two inks, because that is the one
     place the site imitates a document instead of speaking. The beige family goes background → band →
     rule, and the muted ink is the lightest warm grey that still clears 4.5:1 on the sheet's own ground:
     the #8a8069 it replaces was 3.84, and 3.84 is not a printed form, it is a disclaimer. */
  --paper-bg:#fffdf6; --paper-band:#fdf4e7; --paper-line:#e3d3b8;
  --paper-ink:#24352f; --paper-muted:#6f6547;

  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px;
  --sp-6:32px; --sp-7:48px; --sp-8:64px;
  --r-sm:4px; --r-md:8px; --r-lg:12px; --r-pill:20px;
}

/* The document's own defaults. They lived in a second `:root` beside a typeface that was named and never
   loaded — `Inter` appears nowhere in this repo and the site makes no external request, so the whole site
   has been rendering in the system face while saying otherwise. Saying what renders is the fix;
   `font-synthesis:none` stays, because a weight the face does not have is precisely the thing a browser
   will otherwise fake, and the scale above asks for 550 and 650. The three tokens this line retires had
   no meaning that the families above do not already carry: `--green` was a fourth spelling of money,
   `--muted` a fifth grey, and `--orange` was `#aa5b2c`, which did duty for seven unrelated things on four
   pages. The page ground is `--surface-sunk` one step under the white every panel wears, which is what
   `#f5f7f7` always was. */
html{font-family:var(--font);font-size:var(--fs-md);color:var(--ink-2);background:var(--surface-sunk);
     font-synthesis:none;-webkit-text-size-adjust:100%}*{box-sizing:border-box}body{margin:0}button,input{font:inherit}button{cursor:pointer}button:focus-visible,a:focus-visible,input:focus-visible{outline:3px solid var(--focus);outline-offset:3px}button:disabled{cursor:default;opacity:.55}button{transition:background .15s,box-shadow .15s}button:hover:not(:disabled){filter:brightness(.97)}.topbar{height:90px;background:var(--surface);border-bottom:1px solid var(--line);padding:0 5%;display:flex;align-items:center;justify-content:flex-start}.brand{display:flex;gap:12px;align-items:center;text-decoration:none;color:inherit;font-size:var(--fs-lg);font-weight:650;letter-spacing:-.4px}.brand small{display:block;font-size:var(--fs-xs);letter-spacing:2.3px;font-weight:600;margin-top:5px;color:var(--ink-4)}.mark{position:relative;display:grid;place-items:center;background:var(--money-deep);color:var(--surface);border-radius:10px;width:44px;height:44px;font:var(--fs-2xl)/1 var(--paper)}.mark span{position:absolute;font:var(--fs-md) var(--font);right:5px;top:4px}.demo-label{display:flex;gap:24px;align-items:center;font-size:var(--fs-sm);color:var(--ink-4)}.tag{border:1px solid var(--line);border-radius:6px;padding:7px 10px;color:var(--ink-3);font-size:var(--fs-xs)}main{max-width:1510px;margin:auto;padding:28px 5% var(--sp-5)}.intro{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--sp-5)}.eyebrow{font-size:var(--fs-sm);font-weight:var(--w-medium);color:var(--ink-3)}h1{font-size:var(--fs-4xl);margin:10px 0}p{line-height:1.5}.intro p{margin:0;color:var(--ink-4);font-size:var(--fs-md)}.reset{padding:9px 14px;border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--ink-3);font-size:var(--fs-sm);display:flex;gap:8px;align-items:center}.toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:17px;font-size:var(--fs-sm)}.segmented{display:flex;padding:3px;background:var(--money-track);border-radius:8px}.segmented button{background:transparent;border:0;padding:7px 15px;color:var(--ink-3);border-radius:5px;font-size:var(--fs-sm)}.segmented button[aria-pressed=true]{background:var(--surface);box-shadow:0 1px 4px #17352316;color:var(--money);font-weight:600}.simulation-date{display:flex;align-items:center;gap:8px;color:var(--ink-4);font-size:var(--fs-xs)}.status-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--money-deep)}.workspace{display:grid;grid-template-columns:minmax(310px,.78fr) minmax(530px,1.7fr);gap:22px;align-items:stretch}.panel{border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:0 4px 18px #1d3c2b03;overflow:hidden}.conversation{display:flex;flex-direction:column;min-height:663px}.panel-heading{display:flex;align-items:center;gap:11px;padding:21px 20px;border-bottom:1px solid var(--line)}.avatar{display:grid;place-items:center;flex-shrink:0;width:35px;height:35px;border-radius:9px}.agent-avatar{background:var(--money-track);color:var(--money);font-size:var(--fs-2xl)}.panel-heading h2{font-size:var(--fs-xl);line-height:var(--lh-tight);margin:0}.panel-heading p{font-size:var(--fs-sm);color:var(--ink-4);margin:var(--sp-1) 0 0}.small-tag{margin-left:auto;font-size:var(--fs-xs);font-weight:600;color:var(--ink-4);}.scenario{margin:18px 18px 0;padding:12px;background:var(--surface-sunk);border:1px solid var(--line);border-radius:7px;display:flex;gap:10px}.scenario-icon{color:var(--ink-4);font-size:var(--fs-xl)}.scenario strong{font-size:var(--fs-sm);font-weight:600}.scenario p{font-size:var(--fs-xs);margin:4px 0 0;color:var(--ink-4)}.messages{padding:22px 18px 4px;display:flex;flex-direction:column;gap:18px;max-height:340px;min-height:210px;overflow:auto;flex:1;scroll-behavior:smooth}.message{max-width:94%;font-size:var(--fs-sm);line-height:1.6}.message-name{font-size:var(--fs-xs);color:var(--ink-4);margin-bottom:5px}.bubble{padding:12px 14px;border-radius:0 10px 10px 10px;background:var(--surface-sunk);color:var(--ink-2)}.message.user{align-self:flex-end}.user .message-name{text-align:right}.user .bubble{background:var(--money-deep);color:var(--surface);border-radius:10px 0 10px 10px}.choices{padding:10px 18px 18px;display:flex;flex-wrap:wrap;gap:8px}.choice{font-size:var(--fs-sm);text-align:left;padding:10px 12px;background:var(--surface);border:1px solid var(--money-line);color:var(--money);border-radius:7px}.choice.selected{background:var(--money-track);border-color:var(--money-deep)}.choice.full{width:100%;display:flex;justify-content:space-between;align-items:center}.primary{background:var(--money-deep);border:1px solid var(--money-deep);border-radius:7px;color:var(--surface);padding:11px 15px;font-size:var(--fs-sm);text-decoration:none}.choices .primary{width:100%}.composer{margin:0 18px;display:flex;gap:8px;padding:7px;border:1px solid var(--line);border-radius:8px}.composer input{border:0;outline:none!important;min-width:0;flex:1;padding:7px;background:none;font-size:var(--fs-sm)}.composer:focus-within{outline:2px solid var(--focus);outline-offset:2px}.composer button{background:var(--money-deep);border:0;color:var(--surface);border-radius:5px;width:33px;height:33px;font-size:var(--fs-xl)}.prototype-note{text-align:center;color:var(--ink-4);font-size:var(--fs-xs);padding:13px 8px 15px}.company-heading{padding:22px 24px 18px;display:flex;align-items:center;justify-content:space-between}.workspace-label{display:flex;align-items:center;gap:7px;color:var(--ink-4);font-size:var(--fs-xs)}.calendar-toolbar{padding:0 24px 19px;display:flex;align-items:center;justify-content:space-between}.calendar-toolbar strong{font-size:var(--fs-sm);font-weight:600}.calendar-sub{display:block;font-size:var(--fs-xs);color:var(--ink-4);margin-top:5px}.week-tag{padding:7px 11px;border:1px solid var(--line);border-radius:5px;font-size:var(--fs-xs);color:var(--ink-4)}.calendar-scroll{overflow-x:auto;padding:0 24px}.calendar{display:grid;grid-template-columns:43px repeat(5,minmax(83px,1fr));grid-template-rows:49px repeat(16,26px);position:relative;min-width:500px}.day{grid-row:1;text-align:center;border-bottom:1px solid var(--line);font-size:var(--fs-xs);color:var(--ink-4)}.day strong{display:block;font-size:var(--fs-md);font-weight:500;color:var(--ink-3);margin-top:6px}.day.today strong{background:var(--money-deep);color:var(--surface);border-radius:50%;width:25px;height:25px;display:grid;place-items:center;margin:3px auto 0}.time{font-size:var(--fs-xs);color:var(--ink-4);transform:translateY(-6px);align-self:start}.cell{border-top:1px solid var(--line);border-left:1px solid var(--line)}.event{margin:2px 3px 2px 4px;padding:6px 7px;border:0;border-left:2px solid var(--line-strong);border-radius:4px;background:var(--surface-sunk);z-index:1;text-align:left;color:var(--ink-1);min-width:0;overflow:hidden;align-self:stretch}.event strong,.event span{display:block;font-size:var(--fs-xs);line-height:1.4}.event strong{font-weight:600}.event span{opacity:.85;font-size:var(--fs-xs)}.event.new-event{border-left-color:var(--money-deep);background:var(--money-track);color:var(--money);animation:appear .4s ease}.event.proposed{background:var(--money-track);border:1px dashed var(--money-mid);color:var(--ink-3)}.calendar-legend{display:flex;align-items:center;gap:18px;padding:15px 24px 20px;font-size:var(--fs-xs);color:var(--ink-4)}.calendar-legend>span{display:flex;align-items:center;gap:5px}.legend-square{width:7px;height:7px;border-radius:2px}/* An existing job is furniture, not a warning: the calendar has no severity to borrow and the ramp is reserved for the book, so the neutral pair is what says "already here" beside the green that says "the agent just did this". */
.legend-square.existing{background:var(--line-strong)}.legend-square.new{background:var(--money-mid)}.calendar-legend .calendar-status{margin-left:auto;color:var(--ink-4)}.activity{padding:18px 24px 20px;border-top:1px solid var(--line);background:var(--surface);min-height:116px}.activity-header{display:flex;justify-content:space-between;align-items:center}.activity h3{font-size:var(--fs-xs);font-weight:600;margin:0}.activity-header>span{font-size:var(--fs-xs);color:var(--ink-4)}.activity-empty{font-size:var(--fs-xs);color:var(--ink-4);margin:18px 0 0}.activity-row{display:flex;align-items:flex-start;gap:9px;font-size:var(--fs-xs);color:var(--ink-4);margin-top:13px;animation:appear .2s ease}.activity-row i{font-style:normal;color:var(--money-mid);background:var(--money-track);display:grid;place-items:center;width:16px;height:16px;border-radius:50%;font-size:var(--fs-xs);flex-shrink:0}.activity-row span{flex:1}.activity-row small{font-size:var(--fs-xs);color:var(--ink-4)}.activity-row a{color:inherit}footer{display:flex;justify-content:space-between;gap:20px;font-size:var(--fs-xs);line-height:1.6}footer strong{font-weight:500;color:var(--ink-2)}dialog{border:1px solid var(--line);border-radius:15px;padding:25px;width:min(450px,90vw);color:var(--ink-1);box-shadow:0 20px 100px #203d3430}dialog::backdrop{background:#182d2455;backdrop-filter:blur(3px)}.dialog-top{display:flex;align-items:center;justify-content:space-between}.dialog-top button{border:0;background:none;font-size:var(--fs-2xl);color:var(--ink-4)}dialog h2{font-size:var(--fs-xl)}dialog p{font-size:var(--fs-sm);color:var(--ink-4)}dialog .primary{width:100%;margin-top:15px}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}@keyframes appear{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@media(min-width:1600px){main{padding-left:35px;padding-right:35px}}@media(max-width:1050px){main{padding:28px 3%}.topbar{padding:0 3%}.workspace{grid-template-columns:minmax(290px,.85fr) minmax(430px,1.4fr);gap:15px}.calendar-scroll{padding:0 15px}.calendar{min-width:420px;grid-template-columns:32px repeat(5,minmax(73px,1fr))}.calendar-legend{gap:10px;padding-left:15px;padding-right:15px}.calendar-status{display:none!important}.workspace-label{display:none}.company-heading,.calendar-toolbar,.activity{padding-left:18px;padding-right:18px}}@media(max-width:760px){.brand{font-size:var(--fs-md)}.demo-label .tag{font-size:var(--fs-xs)}main{padding:26px 16px}.intro{align-items:flex-start;margin-bottom:24px;gap:10px}.intro h1{font-size:var(--fs-2xl);line-height:1.2}.reset{padding:8px;font-size:var(--fs-lg);margin-top:25px}.reset span{display:none}.toolbar{align-items:flex-start;gap:15px;flex-direction:column}.simulation-date{font-size:var(--fs-xs)}.workspace{grid-template-columns:minmax(0,1fr)}.conversation{min-height:480px}.messages{max-height:290px}.company-heading{padding-top:23px}.calendar{min-width:475px}.calendar-legend{font-size:var(--fs-xs);flex-wrap:wrap}footer{flex-direction:column;gap:7px}footer .copyright{margin-left:0}.desktop-only{display:none}.messages .message{font-size:var(--fs-md)}.calendar-scroll{padding-bottom:4px}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;scroll-behavior:auto!important;transition:none!important}}

/* Readable working controls and half-hour calendar geometry. */
.message{font-size:var(--fs-md)}.choice,.composer input,.scenario strong{font-size:var(--fs-sm)}
.scenario p,.calendar-sub,.workspace-label,.activity-empty{font-size:var(--fs-xs)}
.activity h3,.activity-row,.calendar-toolbar strong{font-size:var(--fs-sm)}
.event strong{font-size:var(--fs-xs)}.event span{font-size:var(--fs-xs)}.event{padding:4px 7px}
.event.new-event,.event.proposed{padding:2px 6px;white-space:nowrap;text-overflow:ellipsis}
.event.new-event strong,.event.proposed strong{overflow:hidden;text-overflow:ellipsis}
.event.new-event span,.event.proposed span{display:none}
.calendar-legend{flex-wrap:wrap;font-size:var(--fs-xs)}.calendar-status{font-size:var(--fs-xs)}
.day,.time{font-size:var(--fs-xs)}.day{padding-bottom:5px}
.prototype-note{font-size:var(--fs-xs)}.prototype-note{color:var(--ink-4)}
.message-name,.calendar-sub,.time,.day,.activity-header>span{color:var(--ink-4)}
@media(max-width:760px){.messages .message{font-size:var(--fs-md)}.calendar{min-width:600px}.calendar-legend{font-size:var(--fs-xs)}}

.details-form{width:100%;display:grid;gap:12px}.details-form label{font-size:var(--fs-sm);color:var(--ink-3);display:grid;gap:6px}.details-form input{font:inherit;font-size:var(--fs-md);width:100%;min-width:0;border:1px solid var(--line-strong);border-radius:6px;padding:10px;color:var(--ink-1);background:var(--surface)}.review-card{width:100%;background:var(--surface-sunk);border:1px solid var(--line);border-radius:8px;padding:15px;font-size:var(--fs-sm);overflow-wrap:anywhere}.review-card p{margin:8px 0}.review-card p span:first-child{font-weight:600}.request-error{margin:12px 18px 0;padding:12px;border:1px solid var(--sev-line);background:var(--sev-tint);border-radius:7px;color:var(--rust-ink);font-size:var(--fs-sm)}.slot-choice{flex:1 1 45%;font-size:var(--fs-sm)}#flow-status{display:block;margin-top:8px;font-size:var(--fs-xs);color:var(--money)}.conversation{max-height:1100px}.messages{min-height:140px;max-height:260px}.choices{max-height:560px;overflow:auto}.small-tag{font-size:var(--fs-xs)}.activity{max-height:340px;overflow:auto}.bubble{overflow-wrap:anywhere}.event strong{overflow:hidden;text-overflow:ellipsis}.workspace{align-items:start}.company{min-height:760px}.conversation{min-height:760px}@media(max-width:760px){.conversation{min-height:600px}.choices{max-height:none}.conversation{max-height:none}.company{min-height:0}.small-tag{display:none}}

.details-form select{font:inherit;font-size:var(--fs-md);width:100%;border:1px solid var(--line-strong);border-radius:6px;padding:10px;color:var(--ink-1);background:var(--surface)}
/* The contact page's one free-text field lives in the same `.details-form` the booking form uses, whose
   rules cover `input`, `label` and `select` and never a `textarea` — an unstyled one arrives with the
   browser's own font, which is not `font:inherit`, so it would sit beside the name and email boxes in a
   different typeface at a different size. Same tokens as `.details-form input`, and `resize:vertical` as
   `.decision textarea` has it, so a visitor can lengthen the box but never break the page's measure by
   dragging it sideways. */
.details-form textarea{font:inherit;font-size:var(--fs-md);width:100%;min-width:0;border:1px solid var(--line-strong);border-radius:6px;padding:10px;color:var(--ink-1);background:var(--surface);resize:vertical}

.event.availability-block { background: var(--line); color: var(--ink-3); border: 1px dashed var(--line-strong); opacity: .85; z-index: 1; }
.event:not(.availability-block) { z-index: 2; }

/* Showcase site: shared nav, landing cards, triage workspace. */
/* The nav is the list of destinations, and since 2026-10-19 it shares the header's first row with the brand —
   the ask one day after the same office asked for a centred row of its own, which is exactly what the
   `width:100%;order:3` pair did and what came out of this rule for it. A wrapping flex line packs greedily in
   visual order, so on a shared row the links join the brand and it is the *corner* that drops when the row
   runs short: brand, pages, then the action. That is the fold worth having — the button is one action, the
   nav is where a reader goes.
   `justify-content:center` centres the links in the nav's own box, and `flex-wrap` is what turns a narrow
   screen into a taller bar instead of a wider page: six links hold one line from 1425 of client width down to
   665, then split 3+3 through 365 and three rows of two below 345, with zero sideways overflow and no element
   hanging off the viewport at any of the 57 widths walked on 2026-10-19. The eight-link chrome that shared
   the first row overflowed from 1100 down (scrollWidth 1003 against a client width of 885 at 900px, the
   consultation button's right edge 21px past the screen) — a horizontal scrollbar on a page that has no
   reason to have one, and the reason `flex-wrap` stays whatever the row order.
   What a shared row cannot buy is the page's own axis. The links' centre sits 37px left of it across the
   whole one-row band, constant, and it is exactly half the difference between the two ends it is centred
   between: corner 260, brand 185. Centring them on the *row* — the thing a reader sees — is the claim this
   rule can keep; centring them on the page while two unequal ends share it would need a magic `min-width` on
   the brand, which is a second layout pretending to be the first.
   `.brand` cannot shrink, because its two-line wordmark is what identifies the site and it is the one child a
   squeezed header must not abbreviate. The grow is scoped to `min-width:1250` because flex growth resolves
   *before* main-axis alignment: an un-scoped `flex-grow:1` centres the links in the leftovers of a row the
   corner has already abandoned, which measures a flat +102px off the axis at every width from 1225 down to
   945 (forced on the shipped sheet to check), against the −36 to +100 drift they hold here — and buys nothing
   above 1250, where the row holds all three and the scoped/unscoped pair differ by 1px at 1244. */
.topnav{display:flex;gap:4px;align-items:center;flex-wrap:wrap;flex:0 1 auto;justify-content:center}
.brand{flex-shrink:0}
@media(min-width:1250px){.topnav{flex-grow:1}}
.topnav a{text-decoration:none;color:var(--ink-4);font-size:var(--fs-sm);font-weight:500;padding:8px 12px;border-radius:6px}
.topnav a:hover{background:var(--surface-sunk);color:var(--money)}
.topnav a.active{background:var(--money-track);color:var(--money);font-weight:600}
/* The company that built this demo signs the chrome; the client whose records it runs on stays in the
   content, and the two names never meet in one sentence. What used to sit in the header's right hand —
   `Maple & Stone · Business sandbox` — repeated a brand mark three inches away and said nothing about who
   made the thing. Green is the step `.topnav a:hover` already spends on an anchor that answers, so the
   byline reads as a link in this header's own vocabulary rather than a second label competing with the tag,
   and it is `nowrap` because the string is a proper noun: the walk caught the header breaking it as
   `The Model / Advantage`, which is not how anyone's company name should be set. The UA underline is taken
   back off it — nothing else in this header is underlined, the green is the link cue, and `:hover` puts the
   line back where the nav's own hover puts the colour. */
.demo-label .byline{color:var(--money);white-space:nowrap;text-decoration:none}
.demo-label .byline:hover{text-decoration:underline}
/* Three rules the consultation button forced, each re-measured the day the nav moved back onto the brand's
   row and the corner became a column to make room.
   The gap: with the links inline, the nav's right edge used to land exactly on the label's left edge, and the
   corner is a filled green box that needs air on both sides. 18px holds on either side of the nav at every
   width where all three share a row.
   The wrap: the first row is brand, links and corner, and they hold it from 1244 of viewport width up —
   brand 185, links 625, corner 260 and two 18px gaps sum to 1106 against the 1121 the bar has inside its own
   5% padding at 1245 of client width, 15px of slack to its name. At 1229 the two numbers meet exactly, and
   then 1px of bold decides a page: the three pages whose active link is `Home`, `About` or `Contact` (624-625
   of links) still hold the row at 1244, while the four desk pages, whose active link is `font-weight:600` and
   measures 626-627, need 1248. The bar therefore stops fixing its
   height and keeps 90px as a floor purely to hold the corner's own two-line stack, and `margin-left:auto`
   holds the corner at the right edge of whatever line it ends up on — `space-between` would otherwise drop a
   lone item left, which the walk measured as +239px of drift. The bar's own `scrollWidth` cannot answer a
   demand question at all: it counted the padding and still landed 72px under its own children, so the numbers
   above are added up from the parts.
   What the fold costs, walked at all 57 widths from 1440 to 320 on 2026-10-19 (viewport widths; client width
   is 15px less with the scrollbar): 90px from 1440 down to 1244, one row of brand, six links and the corner;
   145px from 1240 to 860, the corner on a second row of its own, flush right; 196px from 840 to 780, three
   rows — brand, links, corner; 165px from 760 to 680, where the phone rules have folded the byline and the
   corner is down to its 231px button; 206px from 660 to 380, the links on two rows of three; 247px from 360
   down, three rows of two. The button stays one line at every width and flush with the bar's inner right edge
   at all of them, nothing spills sideways, and nothing hangs past the bottom border.
   Against the centred-row pass this replaces — 120px from 1440 down to 765 of *client* width, then
   113/154/206/247 — the trade is one row wherever all three fit, paid for in the middle: 90 against 120 at
   1425, but 145 against 120 at 1225 and 196 against 120 at 765, because the corner is a two-line column and
   the row it drops to is therefore taller than the row it left. That is what the shared row cost, and the
   number is worth keeping where the change is.
   (`ar.html` still scrolls sideways at 320, inside its own table scroller: measured byte-identical with
   this chrome removed, so it predates the button and this rule neither caused nor fixed it.) */
.topbar{gap:18px;flex-wrap:wrap;height:auto;min-height:90px;padding-top:12px;padding-bottom:12px}
/* The right hand holds the action *over* the byline, and stacking them is what bought the shared row. Side by
   side the pair demands 501px (byline 260 + button 223 + the bar's 18px gap), which with the brand and the six
   links sums to 1347 against the 1283 the bar has inside its own padding at 1440 — 64px short of the ask, and
   still about 10px short after every tightening that was measured (gutter 5% → 3%, gaps 18 → 12, which
   recovers some 54px and spends the header's air to do it). Stacked, the corner is 260px wide — its own
   widest child, the byline, the button being 223 — the row's demand falls to 1106, and all three fit from
   1244 of viewport width up. A column flex container takes its width from its widest child, which is the whole
   trick: the pair stopped competing for the row's length by giving up its share of it.
   `align-items:flex-end` pulls the byline onto the button's right edge so the two read as one block rather
   than as a stray sentence, `gap:6px` is the line-height of that block, and `margin-left:auto` keeps it flush
   right on whichever row it lands on. Below the byline's own 760 fold the corner is a single 231px line, which
   is why the bar gets shorter there and not taller. */
.demo-label{margin-left:auto;flex-direction:column;align-items:flex-end;gap:6px}
/* The consultation button is the header's action, so it sits at the right edge rather than at the end of the
   list of pages, and it carries its own padding, radius and text-decoration because none of those reach it
   from `.topnav a` any more — a rule that lived inside a container it has left. It stays qualified through
   `.demo-label` because that is the trap it already fell into once in the nav (`.topnav a` is (0,1,1), a lone
   class is (0,1,0), and the button arrived painted in the nav's grey with no static read of the stylesheet
   showing why), and borrows `.primary`'s ink-on-green pair rather than inventing a fourth ground the contrast
   sweep has never measured. Hover deepens the same green instead of lifting it, because a lighter ground is a
   weaker one under white ink. */
.demo-label a.header-cta{background:var(--money-deep);border:1px solid var(--money-deep);color:var(--surface);font-size:var(--fs-sm);font-weight:600;padding:8px 12px;border-radius:7px;white-space:nowrap;text-decoration:none}
.demo-label a.header-cta:hover{filter:brightness(.85);color:var(--surface)}
/* A link in a sentence, which the site had none of until the landing page named who built it: without this
   the browser's own blue arrives in a paragraph about the company, `rgb(0,0,238)` underlined, on a page whose
   palette is ink and green. It is `main a` (0,0,2) on purpose — two type selectors, the cheapest rule that
   still reaches an unclassed anchor — so any link that carries a class outranks it and only prose picks it
   up. The footer's credit used to be the one link inside `main` with no class of its own; it left `main` on
   2026-10-19 to become a full-width band, so this rule no longer reaches it and `footer a` is now the ONLY thing
   standing between those anchors and the browser's own blue — an unclassed `<a>` with nothing above it but an
   inherited `color` takes `-webkit-link`, and a future edit that deletes the footer rule as "redundant with the
   prose rule" would paint it blue on white. The prediction was tested the same day it was written: the landing
   page's band dropped to one line with the maker's link inside the copyright, the class-scoped version of the
   rule did not reach it, and the screenshot came back blue. It is drawn as a credit rather than as a sentence
   link, through its own span — (0,1,2) — which was about beating the prose rule and is now about replacing it. */
main a{color:var(--money);text-decoration:underline;text-underline-offset:2px}
/* The credit is the one footer item allowed to leave the demo, so it is drawn as a link and not as prose — and
   since the landing page's band is a single line with the maker's home page linked *inside* the copyright, that
   is now two anchors in the band and one rule for both. On the white ground a link takes the same green the
   prose links take — 7.19:1 — and deepens toward the logo's own green on hover (10.18:1), because on a light
   ground a link has somewhere to get darker; on yesterday's dark band the only direction was up, which is why it
   was `--money-line` then and is `--money` now. Keyed to `footer a` and not to `.made-by a` for exactly the
   reason the comment above `main a` gives: the class-scoped version left the landing's unclassed anchor wearing
   `-webkit-link`, blue on white, in the one line that names who built this. */
footer a{color:var(--money);text-decoration:underline;text-underline-offset:2px}
footer a:hover{color:var(--money-deep)}
/* The footer's ownership line, and the two rules that keep it readable. The copyright is 313px of text on one
   line, and the footer is a flex row that wraps: given a fourth item, a row that does not wrap squeezes the new
   one against its siblings rather than handing it a line of its own. Re-measured today by forcing
   `flex-wrap:nowrap` back on inside a live page — on the bookkeeping page, whose footer carries the longest
   prose, that squeeze sets the line at 191x38 (two lines) at 1440, 136x58 (three) at 1060, 118x58 at 900 and
   100x96 (five) at 780, a sentence nobody chose to compose in a 100px column. (Yesterday's numbers were
   185/133/117/100 wide against the inset band's 1234px row; the band's own gutter replaced that row's 24px of
   side padding, so the squeeze is 48px gentler at the wide end and the same in kind.)
   `flex-wrap` costs the band rows — 52px tall where all three items share one row (index, ar, contact at 1440),
   91 on about/scan/triage and 111 here — and buys the copyright back as one unbroken 313x19 line at every width
   from 1440 down to 390 on all seven pages, breaking to two lines only at 320, where the band's row is 265px
   against 313px of text. No page scrolls sideways at any width walked. The auto margin then holds it flush
   against the band's right gutter on whichever row it lands on — 52/91/111px-tall rows, each ending at the same
   x the content column ends at, because the band's gutter is now the column's inset rather than the header's
   (the rules and the arithmetic are below).
   One page has no row to be flush with: the landing's band is a single line, and `margin-left:auto` on an only
   child is not an alignment, it is a shove to the far edge — measured before this rule, its first word sat at
   x=1041 on a 1425px row while the cards above it start at 71, and at x=1497 on a 1905px row against cards that
   start at 233. So a lone copyright starts at the start of the row, like every other first line on the site.
   Position-based (`:only-child`) and not a class, because the office cut that band's other two items by hand and
   the next page that drops to one item gets the same answer without anyone editing a stylesheet.
   Below 760 the footer is a column, where a cross-axis auto margin both defeats the stretch and hangs the
   line at the right edge of a left-aligned stack, so the media query zeroes it — and has to say
   `footer .copyright` to be heard, because this sheet keeps its media queries *above* its component rules, where
   a lone `.copyright` written later wins the tie on order alone. */
footer{flex-wrap:wrap}
.copyright{margin-left:auto}
footer .copyright:only-child{margin-left:0}
/* The footer is a section, and since 2026-10-19 it is the page's *other header*: the office asked for the band
   to span the whole width "like header" and then, when shown the numbers, for the header's own ground rather
   than the green it wore for a day. So it is `--surface` with a 1px `--line` rule across its top, exactly the
   header's pairing turned upside down — the header is the same fill with the same rule beneath it.
   Say the arithmetic out loud, because it is the honest part: white on this page's ground (`--surface-sunk`)
   measures 1.07:1, and the rule itself 1.25:1 against white, so nothing about this band separates by *colour*.
   What separates it is that it spans edge to edge, that its ground changes from the sunk page to the raised
   surface, and that a hairline runs across it — which is precisely how the header has always read, and is why
   the two now match. The inks are the header's own: `--ink-4` prose (4.81:1 at the 12px this block is set at,
   the same step the byline wears above) and `--ink-2` for the strong sentence (11.99:1), so the loudest words
   in the band are not printed in its faintest rung.
   The structure is what does the width, and it is the part not to undo: `<footer>` is a direct child of
   `<body>`, the sibling `<main>` closes after, which is where `<header class="topbar">` has always sat. It used
   to sit *inside* `main`, and that is why it could not span: `main` is `max-width:1510px;margin:auto` with 5% of
   padding, so a child of it is two constraints from the viewport edge. The usual breakout — `width:100vw` with a
   negative margin — counts a scrollbar the page has, and a footer that overflows sideways is a horizontal
   scrollbar on a page with no reason to have one; a corrected `calc(100vw - <scrollbar>)` needs a script or an
   inline style, both of which this site bans. Leaving `main` is the only version with no magic number in it.
   What the band's own gutter is changed on 2026-10-19, when the office asked for the footer "to the left of the
   cards". It used to borrow the header's `5%` — the *viewport's* gutter — while the cards sit inside `main`,
   which is capped and centred. Under the cap the two are one number (71px at 1440, 31 at 1044, which is why the
   old claim looked true on a laptop); above it they are not: at 1920 the header's gutter is 95px against cards
   that start at 233, at 2560 it is 127 against 553. So the band pays the column's inset now, and that inset is
   `main`'s padding PLUS half of whatever the viewport has beyond the cap — `main`'s own centring margin, which a
   sibling cannot inherit and so has to be spelled out (`--col-center`). The 1510px is not a second layout
   pretending to be the first; it is the number `main` already wears, one rule up the file, and the cap rule below
   exists for the same reason: `main`'s padding stops being `5%` and becomes `35px` at a 1600px viewport, so the
   band has to follow it there or the two columns part again. Below 1050 the viewport can never reach the cap, so
   the two narrow folds carry the plain value. And the phone fold now reads `16px` where the band used to read the
   header's `20px`: aligned to the cards costs 4px against the brand's first word below 760, and the cards won.
   The radius is gone with the inset: a rounded corner on a band that touches both edges paints the page ground
   into the corner instead of drawing an object.
   Being last in the document is the other half of "like header". The office then asked for the half it had not
   been given: the ground to reach the bottom of the window. So `body` is a flex column and `main` grows
   (`flex:1 0 auto`), which puts the band on the viewport floor whenever the window is taller than the page and
   changes nothing when it is not. Measured today across 168 samples (seven pages, eight widths, three window
   heights), 11 of them used to print page ground under the band — 124px on `contact.html` and up to 293px on
   `triage.html` at a 1400px-tall window — and none of them does now. The floor case is a desktop case and never
   a phone one: the shortest document on the site is `triage.html` at 1146px (1728 wide and up, 1210 at 1440),
   while the shortest page at 390px is `contact.html` at 1991px against a 900px viewport, so `min-height` cannot
   bite on a phone, which is the reason yesterday's note declined it (`vh` is the *large* viewport on mobile
   Safari). Writing `100vh` first and `100dvh` after it is the order that keeps that reason honest: the dynamic
   viewport wins where the browser knows one, and a browser that does not gets the value it can resolve.
   One trap sits inside that growth rather than under it: `main` wears `margin:auto`, and in a flex column an auto
   margin on the main axis eats free space, which would hang a short page in the middle of its window with the
   band already at the floor. It does not, because `flex-grow` is applied before the margins are resolved —
   verified in the same walk, where `main`'s box begins at the header's bottom (zero gap, every sample) and the
   slack lands below the band and not above the page.
   Moving the gutter in from the viewport costs the band rows where its line no longer fits the narrower row: at
   1728 and up the band is one row taller on four pages (`about`, `scan` and `triage` 52px → 91/92, `books`
   53 → 92 at 2560) and one row shorter at 390 (`about` 162 → 143, `books` 208 → 189) where the gutter came in by
   4px. Nothing about the content column moved: `main` measures the same box at every width it did before, and no
   page scrolls sideways at any width walked.
   One guard moved with the ground, the same one that moved yesterday: the ink/ground scan merges each
   selector's declarations and reads its LAST `color` against the LAST `background` written for it, so the
   pairing has to be stated here and not inherited — which is what `--ink-4` on `--surface` is doing in this rule
   instead of back in the grouped `.message-name,…,footer` ladder line it was taken out of. */
footer{--col-center:max(0px,(100% - 1510px)/2);background:var(--surface);border-top:1px solid var(--line);color:var(--ink-4);padding:var(--sp-4) calc(5% + var(--col-center))}
/* The three gutter rules, written HERE and not up in the big `@media` blocks with the page's other responsive
   rules, because this sheet keeps its media queries *above* its component rules and a lone `footer` selector
   written later wins the tie on order alone — the same trap `footer .copyright` had to be written around. A
   `footer{padding:…}` up there would be dead bytes: read back, not overridden.
   Each one pairs `footer` with the `main{padding:…}` rule it mirrors, at the same breakpoint and the same value:
   `5%` then `3%` then `16px` in from the viewport, `35px` once the 1510px cap is reached, plus the centring
   margin the cap leaves behind. The 1600px rule is the cap's own — `main` up there is 1510px wide and centred,
   so half the leftover is content inset and the band has to add it by hand. */
@media(min-width:1600px){footer{padding-inline:calc(35px + var(--col-center))}}
@media(max-width:1050px){footer{padding:var(--sp-4) 3%}}
@media(max-width:760px){footer{padding:var(--sp-4) 16px}}
/* The page is a column that fills its window, so the band is on the floor and not where the content stops.
   `flex:1 0 auto` and not `flex:1`: a growing item must not shrink (the workspace grids already own their own
   minimums), and it takes no basis beyond its content. The two `min-height` values and the auto-margin question
   are the comment above the band rule.
   `width:100%` is the load-bearing part, and it arrived here the hard way. A flex item's cross size is
   *content-based* — the container's width is not one of the inputs — and `margin:auto` makes `main` a
   non-stretched item, so putting `main` in a flex column handed its width to its own content: 1012px on the
   about page and 919 on the contact page at 1920 (their widest paragraph, which wants one line and gets
   measured anyway), and 1059px on the aging page at 390 — wider than the phone, because the book's table has
   that minimum and nothing clipped it any more. `width:100%` says fill the container, which is what `width:auto`
   meant when `main` was a block, and `max-width:1510px` with the auto margins still cap and centre it inside
   that: every width walked now measures 375/745/1029/1425/1510/1510/1510 across 390/760/1044/1440/1560/1728/1920,
   the same numbers the page measured before the body was a column, and no page scrolls sideways at any width. */
body{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh}
main{width:100%;flex:1 0 auto}
.landing-intro{align-items:flex-start}.maker-eyebrow{display:flex;align-items:center;gap:7px;margin:0 0 10px;font-size:var(--fs-xs);font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}.maker-eyebrow a{color:var(--money-deep);text-decoration:underline;text-underline-offset:2px}.maker-eyebrow a:hover{color:var(--money-mid)}
.landing-intro p{max-width:520px}
.case-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:6px}
.case-card{display:flex;flex-direction:column;gap:10px;padding:20px 20px 18px;border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:0 4px 18px #1d3c2b03;text-decoration:none;color:inherit;transition:transform .15s,box-shadow .15s}
a.case-card:hover{transform:translateY(-2px);box-shadow:0 8px 26px #1d3c2b14}
.case-top{display:flex;justify-content:space-between;align-items:center}
.case-badge{font-size:var(--fs-xs);font-weight:650;color:var(--ink-4);border:1px solid var(--line);border-radius:20px;padding:4px 10px}
.case-badge.live{color:var(--money);border-color:var(--money-line);background:var(--money-track)}
.case-icon{font-size:var(--fs-xl)}
.case-card h2{font-size:var(--fs-lg);line-height:var(--lh-tight);margin:0}
.case-card p{font-size:var(--fs-sm);color:var(--ink-4);margin:0;line-height:1.5}
.case-cta{margin-top:auto;font-size:var(--fs-sm);font-weight:600;color:var(--money)}
/* One component, two appearances, measured on the landing page: four of these are `<span>`s inside a card
   and the fifth (`The facts behind every answer`) is a real anchor, so `main a` underlined it and nothing
   underlined its siblings. `a.case-cta` and not `.case-cta`, because `main a` is (0,1,1) and a bare class
   would lose to it — the same specificity trap the header CTA had to be written around. The arrow already
   says it leads somewhere, and this line is what `.case-card` does for the same reason one rule above. */
a.case-cta{text-decoration:none}
/* The landing page splits by who the demo is for, not by technology: two headings, the
   same card grid under each, so both halves of the business are drawn with the one card.
   A group pays nothing to start and 24px only after another group: the block above the first one — a prose
   section — already carries its 24px of bottom padding, and a blanket `margin-top:34px` bought a second
   payment on top of it, which is the 66px of nothing the 2026-10-18 rhythm pass was asked to remove. */
.case-group{margin-top:0}.case-group+.case-group{margin-top:var(--sp-5)}
.group-title{margin:0;font-size:var(--fs-xl);line-height:var(--lh-tight)}
.group-note{margin:6px 0 0;font-size:var(--fs-sm);color:var(--ink-4);line-height:1.5;max-width:640px}
.case-group .case-grid{margin-top:16px}
.triage-workspace{align-items:start}
.status-chip{grid-row:1/3;align-self:center;font-size:var(--fs-xs);font-weight:650;border-radius:20px;padding:4px 9px;background:var(--surface-sunk);color:var(--ink-4)}
.status-chip.hold{background:var(--sev-tint);color:var(--rust-ink)}
.status-chip.resolved{background:var(--money-track);color:var(--money)}
.review-panel{display:flex;flex-direction:column}
.item-body{padding:18px 18px 4px;display:flex;flex-direction:column;gap:12px;max-height:760px;overflow:auto}
.item-hint{font-size:var(--fs-sm);color:var(--ink-4);margin:6px 0}
.item-task{font-size:var(--fs-sm);margin:0;color:var(--money);background:var(--surface-sunk);border:1px solid var(--line);border-radius:7px;padding:11px 13px}
.doc-card{border:1px solid var(--line);border-radius:9px;background:var(--surface);overflow:hidden}
.doc-head{display:flex;align-items:center;gap:10px;padding:11px 13px;border-bottom:1px solid var(--line);background:var(--surface-sunk)}
.doc-head strong{font-size:var(--fs-sm);font-weight:600;color:var(--ink-1);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.doc-meta{font-size:var(--fs-xs);color:var(--ink-4);white-space:nowrap}
.cite-btn{font-size:var(--fs-xs);font-weight:600;padding:6px 11px;border:1px solid var(--money-line);border-radius:6px;background:var(--surface);color:var(--money);white-space:nowrap}
.cite-btn:hover{background:var(--money-track)}
.doc-text{font-family:var(--mono);font-size:var(--fs-xs);line-height:1.55;color:var(--ink-3);margin:0;padding:12px 13px;white-space:pre-wrap;overflow-wrap:anywhere;max-height:190px;overflow:auto}
/* One treatment for every heading, and only the size differs per voice. Before this the h1 wore one
   combination of weight, tracking and colour and each block name another, and `.section-title` — the thing
   whose job is to say where a block begins — was 12px bold grey on all five pages, *smaller* than the prose
   and the table under it. Every heading now takes the darkest ink, the semibold cut and the negative
   tracking the approved demo set, on the display line-height; a voice that sits over its own text takes
   `--lh-tight` instead, so a title does not look detached from what follows it. The sizes are the ladder's
   heading steps: 40 the h1, 24 a block name (`.section-title`, `.panel-heading h2`, the landing's group
   titles), 20 one nested inside a named block or inside the collapsed panel. Two headings are left at the
   dense voice on purpose because neither opens a block: the appointment page's activity caption sits over a
   list of 14px rows, and the scan gallery names a thumbnail. Both still get the shared ink and tracking.
   The row drawer's identity line is an `h4` at 17 — the ladder's prose step, because it is a data string
   ("INV-022-03 · residential, size tier 2, crew Green") under the label "This invoice", not a title.
   A bare `h2`/`h3` gets no size from this rule by design: a voice is what makes a heading, so a heading
   that renders at the prose size is a heading someone forgot to give a voice, and it is findable. */
h1,h2,h3{color:var(--ink-1);font-weight:var(--w-semibold);letter-spacing:-.02em;line-height:var(--lh-display)}
/* The note that shares a head row with a title stays `--fs-sm` and quiet, so the pair reads as title and
   gloss rather than as two headings competing. */
.section-title{font-size:var(--fs-xl);line-height:var(--lh-tight);margin:var(--sp-5) 0 0}
/* A block name *inside* another named block is a step down. The office record's rail and the two panels
   `demo.js` builds — the trial balance, the accounting system — each sit under a heading of their own, and
   at 24 the pair read as one sentence broken in half. Same step inside the collapsed panel: a sub-heading
   of a block a reader opened is not a section of the page. */
.record-section>.section-title{font-size:var(--fs-lg);margin-top:0}
.related-list{display:flex;flex-direction:column;gap:6px}
.related-row{display:flex;align-items:center;gap:10px;padding:8px 11px;border:1px solid var(--line);border-radius:7px;background:var(--surface)}
.related-label{font-size:var(--fs-sm);color:var(--ink-3);flex:1;min-width:0}
.citation-manager{border:1px solid var(--line);border-radius:9px;padding:14px 14px 16px;background:var(--surface)}
.citation-current{display:flex;flex-wrap:wrap;gap:7px;min-height:26px;margin-bottom:10px}
.citation-chip{display:inline-flex;align-items:center;gap:7px;font-size:var(--fs-sm);font-weight:600;color:var(--money);background:var(--money-track);border:1px solid var(--money-line);border-radius:20px;padding:4px 6px 4px 11px}
.chip-remove{border:0;background:none;color:var(--money);font-size:var(--fs-sm);line-height:1;padding:2px 6px;border-radius:50%}
.chip-remove:hover{background:var(--money-track)}
.citation-search{display:grid;gap:8px}
.citation-search input{font:inherit;font-size:var(--fs-sm);width:100%;border:1px solid var(--line-strong);border-radius:6px;padding:9px 11px;color:var(--ink-1);background:var(--surface)}
.search-results{display:flex;flex-direction:column;gap:6px}
.search-hit{display:flex;align-items:center;gap:10px;padding:8px 11px;border:1px solid var(--line);border-radius:7px;background:var(--surface)}
.muted-text{font-size:var(--fs-sm);color:var(--ink-4)}
.decision{border:1px solid var(--line);border-radius:9px;padding:14px 14px 16px;background:var(--surface)}
.assessment-label{display:block;font-size:var(--fs-sm);color:var(--ink-3)}
.decision textarea{font:inherit;font-size:var(--fs-sm);width:100%;margin-top:7px;border:1px solid var(--line-strong);border-radius:6px;padding:10px 11px;color:var(--ink-1);background:var(--surface);resize:vertical}
.decision-actions{display:flex;gap:8px;margin-top:12px}
.decision-actions .choice{flex:1;text-align:center;font-weight:600}
/* Below 1050 the landing page's cards go two across and the nav's links take their second padding step:
   625 of links measures 589 at 1025 of client width, which is what lets six of them still hold one line all
   the way down to 665. Nothing here touches the header's arrangement any more — the byline used to fold at
   this width, and it moved to 760 on 2026-10-18, the same day the row it was competing with went away.
   The bar's own `height:auto` used to live in this query, which is where the walk caught it failing — at 780
   and 800 the wrapped nav hung 9px past the bottom border and drew the header's rule through the middle of the
   consultation button. It is a base rule now, so no width has to remember to ask for it. */
@media(max-width:1050px){.case-grid{grid-template-columns:repeat(2,1fr)}.topnav a{padding:8px 9px}}
/* The nav is the site's only way between the desks, and until 2026-10-13 it was `display:none` below 760px:
   a phone got one page and no way off it, which is a dead demo rather than a small one. The topbar already
   wraps at this width, so the links take lines of their own between the brand and the corner instead of
   disappearing. Walked on the shipped build at 1440, 760, 420 and 320 on 2026-10-19: one line at 760 (the bar
   165px, three rows), two at 420 (3+3, 206px), three at 320 (2+2+2, 247px), and the document spills sideways
   at none of them. Every figure this comment used to carry came from an older walk and three of them were
   false: it claimed the button was 37px tall here against 35px on a desktop, four lines and a 196px bar at 420
   and a 276px bar at 320. What the button is here is 231×45, and that is the rule at the bottom of this
   query's doing — a consultation button a desk can only hit with a stylus is a button that is not there — at
   760, 420 and 320 alike, right edge flush with the bar's own 20px padding (740, 400, 300), one line, above
   the fold, with no invisible tab stop left behind it by the folded byline (`display:none` takes an anchor out
   of the order, which is why the ordinal is not restated here: the nav's link count moved every count of it in
   the old text). What does overflow is the aging table, and only inside its own `.ar-scroll`: 1024px of table
   in a 286px window at 320px, which is the wrapper doing the job it was built for rather than a page that
   spills. */
@media(max-width:760px){.case-grid{grid-template-columns:1fr}.topbar{height:auto;padding:12px 20px;flex-wrap:wrap;gap:8px}
  .topnav{gap:6px}.topnav a{padding:9px 11px}
  .demo-label a.header-cta{padding:13px 16px}}

/* Business operations desk (triage.html) */
.ops-workspace{grid-template-columns:minmax(280px,.8fr) minmax(430px,1.5fr) minmax(300px,1fr)}
.channel-tabs{display:flex;gap:6px;margin-bottom:12px}
.channel-tabs button{flex:1;font:inherit;font-size:var(--fs-sm);font-weight:600;padding:9px 10px;border:1px solid var(--line-strong);border-radius:7px;background:var(--surface);color:var(--ink-3);cursor:pointer}
.channel-tabs button[aria-selected="true"]{background:var(--money-deep);border-color:var(--money-deep);color:var(--surface)}
.channel-tabs button:disabled{opacity:.55;cursor:default}
.trigger-list{display:flex;flex-direction:column;gap:7px;margin-bottom:12px}
.trigger-list .muted-text{margin:0 0 2px}
.trigger{display:flex;flex-direction:column;align-items:flex-start;gap:2px;font:inherit;text-align:left;padding:9px 11px;border:1px solid var(--line-strong);border-radius:7px;background:var(--surface);color:var(--ink-1);cursor:pointer}
.trigger strong{font-size:var(--fs-sm);font-weight:600}
.trigger-sub{font-size:var(--fs-xs);color:var(--ink-4)}
.trigger:hover{border-color:var(--money-deep);background:var(--surface-sunk)}
.trigger:disabled,.choice:disabled,.mini-btn:disabled,.slot:disabled,.todo-toggle:disabled{opacity:.55;cursor:default}
.flow-card{border:1px dashed var(--money-line);border-radius:9px;background:var(--surface-sunk);padding:11px 13px;margin:0 0 10px}
.flow-card h3{font-size:var(--fs-xs);font-weight:700;color:var(--money-mid);margin:0 0 8px}
.flow-steps-list{display:flex;flex-direction:column;gap:5px}
.flow-step{display:flex;align-items:center;gap:8px;font-size:var(--fs-sm);color:var(--money)}
.flow-icon{width:18px;text-align:center;font-size:var(--fs-sm)}
.flow-step.done .flow-icon{color:var(--money-mid)}
.flow-step.waiting .flow-icon{color:var(--rust-ink)}
.flow-detail{font-size:var(--fs-xs);color:var(--ink-4)}
.citation-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.citation-row .citation-chip{font-size:var(--fs-xs);padding:3px 5px 3px 10px}
.review-rows{display:flex;flex-direction:column;gap:5px;margin:9px 0}
.review-row{display:flex;gap:10px;font-size:var(--fs-sm);color:var(--money)}
.review-label{flex:0 0 120px;font-size:var(--fs-xs);font-weight:600;color:var(--ink-4);padding-top:1px}
.record-panel{display:flex;flex-direction:column}
.office-record{display:flex;flex-direction:column;gap:4px;overflow-y:auto;max-height:430px;padding:2px}
.record-section{margin:0 0 var(--sp-4)}
.record-row{display:flex;flex-direction:column;gap:1px;padding:9px 11px;border:1px solid var(--line);border-radius:7px;background:var(--surface);font-size:var(--fs-sm);color:var(--money)}
.record-row strong{font-size:var(--fs-sm);color:var(--ink-1)}
.record-sub{font-size:var(--fs-xs);color:var(--ink-4)}
.tb-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:6px 0 0}
/* One table voice for the whole site, and it is the body's: a journal line, a trial-balance account and a
   receivable row are all *content a reader acts on*, and 12px is what made three of the five pages read as a
   printout. The column names stay a step smaller and a step quieter — 12px at `--ink-3`, not bold grey — which
   is the aging page's arrangement, measured there first. */
.trial-balance{border-collapse:collapse;background:var(--surface);border:1px solid var(--line);border-radius:7px;font-size:var(--fs-sm)}
.trial-balance th,.trial-balance td{padding:5px 7px;border-bottom:1px solid var(--line);text-align:left;font-weight:400;color:var(--money)}
.trial-balance thead th{font-size:var(--fs-xs);font-weight:var(--w-medium);color:var(--ink-3);background:var(--surface-sunk);border-bottom:1px solid var(--line)}
.tb-account{white-space:nowrap}
.tb-num{text-align:right;font-family:var(--mono);font-size:var(--fs-sm);white-space:nowrap;color:var(--ink-1)}
/* `demo.js` puts this class on a trial-balance cell whose figure is the other sign — a credit where the
   column is debits — and no scale on this site means that, so it gets its own colour rather than a
   step of severity or a shade of ink. Parentheses already say it; a hue saying it a third time is the
   one redundancy a ledger is allowed. */
.tb-credit{color:var(--credit)}
.tb-totals th,.tb-totals td{border-bottom:0;font-weight:700;color:var(--ink-1);background:var(--surface-sunk)}
.tb-offchart .tb-account{color:var(--rust-ink)}
/* The trial balance's five columns of exact cents need 597px, measured as the table's `scrollWidth`
   against its scroller's `clientWidth`. The rail is 660px on a desktop — 603 of scroller, so the name
   and the three columns of figures sit side by side and the book reads as the table the bookkeeping
   desk shows it. That is what the width was chosen for, and it is not every width: the mid-window step
   gives the rail 460 and the scroller 403, and a phone, where the rail goes full width, gives it 388
   and the scroller 346. Four of five columns are off the page at those two, and a horizontal scrollbar
   in a panel that narrow is a control most visitors never find. The figures may not be abbreviated or
   rounded (that is how a demo gets audited), so below the width the table needs, each row turns into the
   account's own small block: the name on its line, then the figures with the column each one came from
   printed beside it. Those names are on the cells already — `demo.js` puts them there from this table's
   own header row — so a renamed column cannot leave a stale label under a figure. The switch is measured
   against the scroller rather than a viewport because the rail's width is set two rules away in
   `.chat-workspace`, where no media query can see it, and 600px is the width the 597 fits inside. A
   browser without container queries ignores the block and gets the scroll — which is what every narrow
   rail on this page looked like before the desktop one was widened. */
/* One gutter for the rail's three blocks. The record's own box carried `padding:2px`, so "Books",
   "Trial balance" and "Your accounting system" started 18px left of the inset the panel heading above
   them uses and ran to the border, while the activity list underneath started 4px right of it — one
   panel, three left edges. 20px is what `.panel-heading` already uses, so the section names line up
   with the block they belong under instead of inventing a fourth number; the `max-width:1050px` query
   that takes `.activity` to 18px is outranked here because a panel's edge does not move with the
   viewport. Choosing the heading's inset over `--sp-5` also costs the tables 4px less: the trial balance
   wants 597px and the desktop rail gives its scroller 603, so those 4px are the difference between the
   book as a table and the book as a stack of blocks. */
.rail-panel .office-record,.rail-panel .activity{padding-left:20px;padding-right:20px}
.rail-panel .tb-scroll{container-type:inline-size}
@container (max-width:600px){
  .rail-panel .trial-balance thead{display:none}
  .rail-panel .trial-balance tr{display:flex;flex-wrap:wrap;align-items:baseline;
    gap:var(--sp-1) var(--sp-5);padding:var(--sp-2) var(--sp-1);border-bottom:1px solid var(--line)}
  .rail-panel .trial-balance tr:last-child{border-bottom:0}
  .rail-panel .trial-balance th,.rail-panel .trial-balance td{border-bottom:0;padding:0}
  .rail-panel .trial-balance th{white-space:normal}
  .rail-panel .trial-balance .tb-account{flex:1 1 100%}
  .rail-panel .trial-balance td{flex:0 0 auto;display:flex;gap:var(--sp-2)}
  .rail-panel .trial-balance td[data-label]::before{content:attr(data-label);font-family:var(--font);
    font-size:var(--fs-xs);color:var(--ink-3)}
  .rail-panel .trial-balance .tb-totals{background:var(--surface-sunk)}
  .rail-panel .trial-balance .tb-totals th,.rail-panel .trial-balance .tb-totals td{background:none}
}
.tb-foot{margin:7px 0 0;padding:7px 9px;border:1px solid var(--money-line);background:var(--money-track);border-radius:7px;font-size:var(--fs-xs);font-weight:600;color:var(--money)}
.tb-unbalanced{border-color:var(--sev-line);background:var(--sev-tint);color:var(--rust-ink)}
.tb-slice{margin:6px 0 0;font-size:var(--fs-xs);line-height:1.45;color:var(--ink-4)}
.sync-head{display:flex;align-items:center;gap:8px;margin:6px 0 0}
.sync-head strong{font-size:var(--fs-sm);color:var(--ink-1)}
.sync-notice{margin:5px 0 0;font-size:var(--fs-xs);font-weight:600;color:var(--rust-ink);background:var(--sev-tint);border:1px solid var(--sev-line);border-radius:6px;padding:6px 8px;line-height:1.4}
.sync-actions{display:flex;flex-wrap:wrap;gap:7px;margin:8px 0 0}
.mini-btn.sync-send{background:var(--money-deep);border-color:var(--money-deep);color:var(--surface)}
.mini-btn.sync-send:hover:not(:disabled){background:var(--money-deep)}
.sync-standing{margin:7px 0 0;font-size:var(--fs-xs);color:var(--ink-4)}
.sync-diff{color:var(--rust-ink);font-weight:600}
.sync-caption{margin:5px 0 0;font-size:var(--fs-xs);color:var(--ink-4)}
.sync-receipt{display:flex;flex-wrap:wrap;align-items:center;gap:7px;padding:7px 9px;border:1px solid var(--line);border-radius:7px;background:var(--surface);font-size:var(--fs-xs);margin:6px 0 0}
.sync-ref{font-family:var(--mono);font-size:var(--fs-xs);font-weight:600;color:var(--money)}
.sync-receipt-body{flex:1;min-width:0;color:var(--money);overflow-wrap:anywhere}
.sync-reason{flex:0 0 100%;font-size:var(--fs-xs);color:var(--rust-ink)}
.sync-open{margin:6px 0 0;font-size:var(--fs-xs);color:var(--money)}
.todo-row{display:flex;align-items:center;gap:9px;padding:7px 11px;border:1px solid var(--line);border-radius:7px;background:var(--surface);font-size:var(--fs-sm);color:var(--money)}
.todo-toggle{flex:0 0 22px;height:22px;border:1px solid var(--money-line);border-radius:50%;background:var(--surface);color:var(--money);font-size:var(--fs-xs);line-height:1;cursor:pointer;padding:0}
.todo-toggle:hover{background:var(--money-track)}
.todo-row.done .todo-text{text-decoration:line-through;color:var(--ink-4)}
.draft-card{border:1px solid var(--line);border-radius:9px;background:var(--surface);padding:10px 12px}
.draft-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}
.draft-head strong{font-size:var(--fs-sm);color:var(--ink-1)}
.draft-body{font-size:var(--fs-sm);line-height:1.5;color:var(--money);margin:0 0 8px;white-space:pre-wrap;overflow-wrap:anywhere}
.draft-foot{display:flex;flex-wrap:wrap;align-items:center;gap:6px}
.sent-tag{font-size:var(--fs-xs);font-weight:600;color:var(--money-mid)}
.mini-btn{font:inherit;font-size:var(--fs-sm);font-weight:600;padding:5px 11px;border:1px solid var(--money-line);border-radius:6px;background:var(--surface);color:var(--money);cursor:pointer;white-space:nowrap}
.mini-btn:hover{background:var(--money-track)}
.mini-btn.danger{border-color:var(--sev-1);color:var(--rust-ink)}
.mini-btn.danger:hover{background:var(--sev-tint)}
.appt-panel{border-top:1px solid var(--line);background:var(--surface);padding-top:4px}
.appt-body{display:flex;flex-direction:column;gap:8px;overflow-y:auto;max-height:380px;padding:2px}
.appt-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:9px 11px;border:1px solid var(--line);border-radius:7px;background:var(--surface)}
.appt-row.result{border-color:var(--money-line);background:var(--surface-sunk)}
.appt-main{display:flex;flex-direction:column;gap:1px;min-width:0}
.appt-main strong{font-size:var(--fs-sm);color:var(--ink-1)}
.appt-main span{font-size:var(--fs-sm);color:var(--ink-3)}
.appt-actions{display:flex;gap:6px;flex:0 0 auto}
.slot-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:6px}
.slot{font:inherit;font-size:var(--fs-sm);font-weight:600;padding:8px 6px;border:1px solid var(--line-strong);border-radius:7px;background:var(--surface);color:var(--money);cursor:pointer}
.slot:hover{border-color:var(--money-deep);background:var(--surface-sunk)}
.slot.selected{background:var(--money-deep);border-color:var(--money-deep);color:var(--surface)}
@media(max-width:1250px){.ops-workspace{grid-template-columns:minmax(260px,1fr) minmax(380px,1.6fr)}.record-panel{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:14px}.record-panel .panel-heading{grid-column:1/-1}.office-record{max-height:340px}.appt-body{max-height:340px}}
@media(max-width:760px){.ops-workspace{grid-template-columns:1fr}.record-panel{grid-template-columns:1fr}.slot-grid{grid-template-columns:1fr}}

/* Chat-first business operations (triage.html): one conversation, a slim live rail. */
/* The chat/email mode switch toggles the hidden attribute; author display rules
   (.composer, .composer-bar, .email-compose) would otherwise beat the UA's
   [hidden]{display:none}, so reassert it here. */
[hidden]{display:none!important}
.chat-intro{align-items:flex-start}
.chat-intro p{max-width:560px}
.intro-actions{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
/* The rail carries the office record, the trial balance and the hand-off, and 340px was narrower
   than the trial balance's five columns of exact cents (597px measured): every figure arrived
   stacked. 660px leaves 603 of scroller once the 40px of gutter and the record's own scrollbar are off
   it, so the table is a table at a normal window — and at 1440px the chat still gets 614px of the 1296
   the page allows. A mid window is the trade the other way: below 1320px the rail takes 460, the
   scroller comes to 403, and the table goes back to its stacked reading, which is what the block under
   the container query is for. */
.chat-workspace{grid-template-columns:minmax(0,1fr) 660px;align-items:start}
.chat-panel .messages{max-height:560px;min-height:320px}
.rail-panel{display:flex;flex-direction:column}
.suggestions{display:flex;flex-wrap:wrap;gap:8px;padding:0 18px 10px}
.composer-bar{display:flex;align-items:center;gap:12px;padding:0 18px 8px}
/* Pin the composer to the viewport bottom so it stays reachable as the thread grows. */
.chat-panel .composer-dock{position:sticky;bottom:0;background:var(--surface);padding-bottom:10px}
#review-area,#appt-area{padding:0 18px}
#review-area{display:flex;flex-direction:column;gap:8px}
#review-area .review-card{margin:10px 0}
#review-area .primary{width:100%}
#review-area .choice{width:100%;text-align:center}
#appt-area .appt-panel{display:flex;flex-direction:column;gap:8px;background:var(--surface);border:1px solid var(--line);border-radius:9px;padding:10px 12px;margin:10px 0}
/* Fixed, equal length for the two desk panels. On the two-column layout the chat box was sized
   to its own content (align-items:start) and swelled ~520px the moment the booking desk opened
   (measured 894 -> 1417px) while the office rail sat at 259px. Both panels now share one fixed
   height and let their own region scroll, so the length stops changing — for the chat panel and
   for both office-record tabs alike. Gated to the two-column fold: below 1100px the panels stack
   full-width and a fixed height would just become a wall of scrolling. */
@media(min-width:1101px){
  .chat-workspace{--chat-panel-h:1000px;align-items:stretch}
  .chat-workspace .chat-panel,.chat-workspace .rail-panel{height:var(--chat-panel-h)}
  .chat-panel{display:flex;flex-direction:column}
  .chat-panel .panel-heading{flex:0 0 auto}
  .chat-panel .messages{flex:1 1 auto;min-height:0;max-height:none}
  .chat-panel #appt-area,.chat-panel #review-area,.chat-panel .suggestions{flex:0 0 auto}
  .chat-panel .composer-dock{position:static;flex:0 0 auto}
  .rail-panel{display:flex;flex-direction:column}
  .rail-panel .panel-heading,.rail-panel .rail-tabs{flex:0 0 auto}
  .rail-panel .rail-pane{max-height:none}
}
.answer-chips{display:flex;flex-wrap:wrap;gap:8px;max-width:94%}
/* One chip carries a whole sentence rather than an answer — the "or say it yourself" row on the
   bookkeeping desk, whose first suggestion is the desk's own hardest example. `nowrap` is right for a
   two-word reply and wrong for a sentence: on a phone the pill outgrew the panel and the panel clips, so
   the sentence was cut mid-word and the press was a mystery. It wraps, and stays a chip. */
#try-row button{white-space:normal;text-align:left;max-width:100%}
.bubble-text{white-space:pre-wrap}
/* Demo hint: a demo aid rendered under the agent's message, never part of it. */
.bubble-hint{margin-top:9px;padding:7px 10px;font-size:var(--fs-xs);font-style:italic;color:var(--ink-4);background:var(--surface-sunk);border-left:3px solid var(--sev-1);border-radius:0 6px 6px 0;line-height:1.5}
.bubble-hint .hint-label{display:block;font-style:normal;font-size:var(--fs-xs);font-weight:700;color:var(--rust-ink);margin-bottom:2px}
/* "Behind the scenes": a floating notification window in the lower-right corner. */
.scene-window{position:fixed;right:20px;bottom:20px;z-index:60;width:380px;max-width:calc(100vw - 32px);background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 16px 48px #1d3c2b33;overflow:hidden;animation:scene-in .25s ease}
@keyframes scene-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
.scene-head{display:flex;align-items:center;gap:8px;padding:11px 10px 11px 14px;background:var(--surface-sunk);border-bottom:1px solid var(--line)}
.scene-title{font-size:var(--fs-xs);font-weight:700;color:var(--money-mid)}
.scene-head .work-tag{margin-left:auto}
#work-area{padding:11px 14px 13px;display:flex;flex-direction:column;gap:6px}
.work-tag{font-size:var(--fs-xs);font-weight:700;border-radius:20px;padding:3px 9px;background:var(--money-track);color:var(--money);border:1px solid var(--money-line);white-space:nowrap}
.scene-window.active .work-tag{background:var(--sev-tint);color:var(--rust-ink);border-color:var(--sev-line)}
.scene-window.dismissed #work-area,.scene-window.ended #work-area,.scene-window.superseded #work-area{opacity:.7}
.work-step{display:flex;align-items:baseline;gap:8px;font-size:var(--fs-sm);color:var(--money)}
.work-icon{flex:0 0 16px;width:16px;text-align:center;font-size:var(--fs-xs)}
.work-step.done .work-icon{color:var(--money-mid)}
.work-step.active .work-icon{color:var(--money)}
.work-step.waiting .work-icon{color:var(--rust-ink)}
.work-step.todo .work-icon{color:var(--ink-4)}
.work-step.todo .work-label{color:var(--ink-4)}
.work-label{font-weight:550}
.work-detail{font-size:var(--fs-sm);color:var(--ink-4)}
/* A confirmed quote is a deliverable: it sits in the thread as its own card. */
.quote-card{max-width:560px;background:var(--surface);border:1px solid var(--money-line);border-left:4px solid var(--money-deep);border-radius:10px;padding:12px 16px;margin:10px 0}
.quote-head{font-size:var(--fs-xs);font-weight:700;color:var(--money-mid);margin-bottom:4px}
.quote-card .review-rows{margin:6px 0 2px}
/* Email client view: in email mode the main panel is the inbox — thread cards
   with From / To / Subject / Body mail blocks and the flow's process inline. */
.mode-picker button{padding:5px 12px;font-size:var(--fs-sm)}
.email-compose{margin:0 18px;display:flex;flex-direction:column;gap:9px;padding:12px 14px;border:1px solid var(--line);border-radius:8px;background:var(--surface)}
.compose-heading{font-size:var(--fs-xs);font-weight:700;color:var(--money-mid)}
.compose-fields{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.compose-fields label{display:grid;gap:4px;font-size:var(--fs-xs);font-weight:600;color:var(--ink-3)}
.compose-fields input,.compose-fields textarea{font:inherit;font-size:var(--fs-sm);font-weight:400;line-height:1.45;border:1px solid var(--line-strong);border-radius:6px;padding:7px 9px;background:var(--surface);color:var(--ink-1)}
.compose-fields input:focus,.compose-fields textarea:focus{outline:2px solid var(--focus);outline-offset:1px}
.compose-fields input[readonly]{background:var(--surface-sunk);color:var(--ink-4)}
.compose-full{grid-column:1/-1}
.compose-fields textarea{resize:vertical;min-height:52px;max-height:110px}
.compose-hint{margin:0}
.email-compose .primary{align-self:flex-start}
.email-thread{display:flex;flex-direction:column;gap:8px;padding:12px 14px;border:1px solid var(--paper-line);border-radius:10px;background:var(--paper-bg)}
.email-thread.review{border-color:var(--paper-line);background:var(--paper-band)}
.email-thread.input{border-color:var(--paper-line);background:var(--paper-bg)}
.email-thread-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.email-thread-from{font-size:var(--fs-sm);font-weight:650;color:var(--paper-ink);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.email-status{font-size:var(--fs-xs);font-weight:700;color:var(--ink-4);white-space:nowrap}
.email-thread.review .email-status{color:var(--paper-muted)}
.email-thread.input .email-status{color:var(--paper-muted)}
.mail-block{padding:10px 12px;border:1px solid var(--line);border-radius:8px;background:var(--surface)}
.mail-block.from-customer{background:var(--surface-sunk);border-color:var(--money-line)}
.mail-meta{display:flex;flex-direction:column;gap:2px;margin-bottom:7px}
.mail-meta-row{display:flex;gap:8px;font-size:var(--fs-xs);color:var(--ink-4);overflow-wrap:anywhere}
.mail-meta-row strong{flex:0 0 52px;font-size:var(--fs-xs);font-weight:700;color:var(--ink-4);padding-top:1px}
.mail-body{font-size:var(--fs-sm);line-height:1.5;color:var(--money);white-space:pre-wrap;overflow-wrap:anywhere}
.mail-note{margin-top:9px;padding:6px 9px;font-size:var(--fs-xs);font-style:italic;color:var(--ink-4);background:var(--surface-sunk);border-left:3px solid var(--sev-1);border-radius:0 6px 6px 0;line-height:1.45}
.mail-note .hint-label{display:block;font-style:normal;font-size:var(--fs-xs);font-weight:700;color:var(--rust-ink);margin-bottom:1px}
/* The desk's own copy of the exchange: what it said about the customer, kept in
   the thread as the office's trail and deliberately never sent. */
.thread-internal{padding:8px 11px;border:1px dashed var(--line-strong);border-radius:8px;background:var(--surface)}
.internal-label{display:block;font-size:var(--fs-xs);font-weight:700;color:var(--ink-3);margin-bottom:3px}
/* The one label on the chrome side of this file that used to be set in tracked capitals: "Office note ·
   not sent" is a UI label, not a line on a vendor's form, so it takes the sentence case the rest of the
   site's labels take. Its ink moved from the paper's grey to `--ink-3` with it, because the grey it
   borrowed belongs to the facsimile above and meant "printed", not "quiet". */
.internal-body{font-size:var(--fs-sm);line-height:1.45;color:var(--paper-muted);font-style:italic}
/* The email channel shows the exchange, not the pipeline: the desk's internal steps
   have no card here — the customer's own mail is what meets its gates. */
.thread-actions{display:flex;gap:8px}
.thread-actions .primary{flex:1}
.thread-actions .choice{flex:1;text-align:center}
.email-thread .quote-card{max-width:none;margin:0}
.email-trigger{display:flex;flex-direction:column;align-items:flex-start;gap:2px;font:inherit;text-align:left;padding:9px 11px;border:1px dashed var(--line-strong);border-radius:7px;background:var(--surface);color:var(--ink-1);cursor:pointer}
.email-trigger strong{font-size:var(--fs-sm);font-weight:600}
.email-trigger:hover{border-color:var(--money-deep);background:var(--surface-sunk)}
/* Invoice scan. A sheet is a document, not a chat line: it renders as paper (serif face,
   ruled line items, the synthetic banner printed on it) so every figure on the panel
   can be read against the line it was taken from. What this card shows is the office's
   filed copy; the paper the reading model is handed is the PDF the desk renders from
   these same bytes, and a reading is believed only where this copy agrees with it.
   Three rules in this block set their text in capitals — `.sheet-banner`, `.sheet-key`,
   `.sheet-table th` — and after a sweep of every rendered page in Chrome they are the site's ONLY
   remaining `text-transform`, left deliberately.
   Everywhere else a capital was a UI label shouting, which is why the rest went to sentence case; here
   it is a vendor's form doing what forms do, and a facsimile that said "Bill to:" in sentence case
   would be a worse imitation of paper. All three sit inside `.paper-sheet`, which is the boundary:
   a capital on that side of it is mimesis, and a capital on this side of it was the thing to fix. */
.paper-sheet{align-self:flex-start;max-width:660px;background:var(--paper-bg);border:1px solid var(--paper-line);border-radius:4px;box-shadow:0 6px 18px #4c422b14;overflow:hidden}
.sheet-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:10px 14px 8px;border-bottom:1px solid var(--paper-line)}
.sheet-doc{font-family:var(--mono);font-size:var(--fs-xs);font-weight:700;color:var(--paper-muted)}
.sheet-title{font-size:var(--fs-xs);color:var(--paper-muted);text-align:right}
.sheet-banner{padding:5px 14px;font-size:var(--fs-xs);font-weight:700;letter-spacing:.9px;text-transform:uppercase;color:var(--paper-muted);background:var(--paper-band);border-bottom:1px dashed var(--paper-line)}
.sheet-body{padding:11px 14px 14px;font-family:var(--paper);font-size:var(--fs-sm);line-height:1.62;color:var(--paper-ink)}
.sheet-line{display:flex;gap:8px;align-items:baseline}
.sheet-key{flex:0 0 116px;font-family:var(--font);font-size:var(--fs-xs);font-weight:600;letter-spacing:.5px;text-transform:uppercase;color:var(--paper-muted)}
.sheet-value{min-width:0;overflow-wrap:anywhere}
.sheet-table{width:100%;border-collapse:collapse;margin:9px 0 7px;font-size:var(--fs-sm)}
.sheet-table th,.sheet-table td{padding:4px 6px;border-bottom:1px solid var(--paper-line);text-align:left}
.sheet-table th{border-bottom:1.5px solid var(--paper-line);font-family:var(--font);font-size:var(--fs-xs);font-weight:600;letter-spacing:.5px;text-transform:uppercase;color:var(--paper-muted)}
.sheet-table tr>*:not(:first-child){text-align:right;font-variant-numeric:tabular-nums}
/* A retype of a sheet, sent from the viewer on the intake page: the only paper a visitor can
   type, and the desk checks it against the copy on file rather than believing it. The chat's
   own tray of sheet buttons and its paste drawer left with them — paper arrives on that page. */
.paste-form{display:flex;flex-direction:column;gap:9px;padding:0 12px 12px}
.paste-form textarea{font:var(--fs-sm)/1.5 var(--mono);border:1px solid var(--paper-line);border-radius:6px;padding:8px 10px;background:var(--surface);color:var(--paper-ink);resize:vertical;min-height:96px;max-height:240px}
.paste-form textarea:focus{outline:2px solid var(--focus);outline-offset:1px}
.paste-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:var(--fs-xs)}
/* Two steps down for the rail, not one: 660 is what the trial balance needs to stay a table, and a
   window that cannot give it 660 without crushing the chat gets 460 and the stacked reading instead.
   The chat stays the wider of the two all the way down — 706px beside a 460px rail at 1320 — and at
   1100 the pair becomes one column and the rail gets the whole page. */
@media(max-width:1320px){.chat-workspace{grid-template-columns:minmax(0,1fr) 460px}}
@media(max-width:1100px){.chat-workspace{grid-template-columns:1fr}.chat-panel .messages{max-height:420px}.scene-window{right:16px;bottom:16px;width:calc(100vw - 32px)}}

/* Invoice scan (scan.html): an intake form on the left, the desk at work on the right.
   The paper palette is the gallery's — these cards are sheets, so they look like the sheet
   renderer's own paper rather than like chat furniture. */
.scan-workspace,.books-workspace{grid-template-columns:minmax(330px,.85fr) minmax(470px,1.45fr);align-items:start}
.intake-panel,.events-panel,.process-panel{display:flex;flex-direction:column}
.intake-body,.events-body{padding:18px;display:flex;flex-direction:column;gap:11px}
.attach-zone{display:flex;flex-direction:column;align-items:flex-start;gap:5px;width:100%;font:inherit;text-align:left;padding:15px 16px;border:1.5px dashed var(--money-line);border-radius:10px;background:var(--surface);color:var(--ink-1);cursor:pointer;transition:border-color .15s,background .15s}
.attach-zone:hover:not(:disabled),.attach-zone.over{border-color:var(--money-deep);background:var(--surface-sunk)}
.attach-zone:disabled{opacity:.55;cursor:default}
.attach-icon{font-size:var(--fs-lg);line-height:1}
.attach-title{font-size:var(--fs-sm);font-weight:650}
.attach-hint{font-size:var(--fs-xs);line-height:1.5;color:var(--ink-4)}
.attach-menu{display:flex;flex-direction:column;gap:5px;padding:8px;border:1px solid var(--line);border-radius:9px;background:var(--surface);box-shadow:0 10px 28px #1d3c2b1f}
.attach-option{display:flex;align-items:baseline;justify-content:space-between;gap:9px;font:inherit;font-size:var(--fs-sm);text-align:left;padding:7px 9px;border:1px solid transparent;border-radius:6px;background:var(--surface);color:var(--ink-1);cursor:pointer}
.attach-option:hover{border-color:var(--money-line);background:var(--surface-sunk)}
.attach-option strong{font-family:var(--mono);font-size:var(--fs-xs);white-space:nowrap}
.attach-option span{color:var(--ink-4);font-size:var(--fs-xs);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.attach-chip-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.attach-chip-row:empty{display:none}
.attach-chip{display:inline-flex;align-items:center;gap:7px;font-size:var(--fs-sm);font-weight:600;color:var(--money);background:var(--money-track);border:1px solid var(--money-line);border-radius:20px;padding:4px 6px 4px 11px}
.attach-chip-name{font-family:var(--mono)}
.submit-sheet{width:100%}
#submit-hint{margin:0}
.gallery-note{margin:2px 0 0}
.gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(228px,1fr));gap:10px}
.gallery-card{display:flex;flex-direction:column;gap:5px;padding:11px 12px;border:1px dashed var(--paper-line);border-radius:8px;background:var(--paper-bg);cursor:pointer;transition:border-color .15s,background .15s}
.gallery-card:hover{border-color:var(--money-deep);background:var(--paper-band)}
.gallery-card.attached{border-style:solid;border-color:var(--money-deep);background:var(--paper-bg)}
.gallery-top{display:flex;align-items:center;justify-content:space-between;gap:8px}
.gallery-type{font-size:var(--fs-xs);font-weight:700;color:var(--paper-muted);background:var(--paper-band);border:1px solid var(--paper-line);border-radius:20px;padding:2px 8px}
.gallery-id{font-family:var(--mono);font-size:var(--fs-xs);color:var(--paper-muted);white-space:nowrap}
.gallery-title{margin:0;font-size:var(--fs-sm);font-weight:600;line-height:1.35;color:var(--paper-ink)}
.gallery-party{margin:0;font-size:var(--fs-sm);color:var(--paper-muted)}
.gallery-figure{margin:2px 0 0;display:flex;flex-direction:column;gap:1px}
.gallery-figure strong{font-size:var(--fs-md);font-weight:650;color:var(--paper-ink);font-variant-numeric:tabular-nums}
.gallery-figure span{font-size:var(--fs-xs);color:var(--paper-muted)}
/* The page speaking *about* the sheet, not another line printed on it. A card is built to look
   like the paper — dashed border, beige ground, the same facsimile the viewer opens — so a note
   styled like its neighbours reads as the vendor's own words, which is the one thing it is not.
   The hairline above it says whose sentence this is, the job a footnote rule does on a printed
   page. Full ink rather than `--paper-muted`: this line is why the card is in the gallery, and
   the muted step is reserved for what the sheet states about itself. */
.gallery-shows{margin:0;padding-top:var(--sp-2);border-top:1px solid var(--paper-line);font-size:var(--fs-sm);line-height:1.35;color:var(--paper-ink)}
/* The two buttons in a card's action row are `white-space:nowrap` on purpose — a verb does not get
   broken in half — and "View the paper" beside "Attached ✓" measures 233px of button against a gallery
   track that bottoms out at 228. At an 820px viewport the second button's right edge sat 13px past the
   card's own border. The row wraps instead of the card lying about its edge: the sheet's confirmation
   keeps its whole sentence, and the card keeps its box. */
.gallery-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
/* Four stages over the desk's own eight step rows. The stage heading is the viewer's word for
   a run of the desk's steps; the rows underneath are the desk's, verbatim, details and all. */
.process-body{display:flex;flex-direction:column}
.stage-list{padding:14px 18px;display:flex;flex-direction:column;gap:8px}
.stage{border:1px solid var(--line);border-radius:9px;background:var(--surface);overflow:hidden}
.stage.done{border-color:var(--money-line)}
.stage.waiting{border-color:var(--sev-line);background:var(--surface-sunk)}
.stage-head{display:flex;align-items:center;gap:9px;width:100%;font:inherit;font-size:var(--fs-sm);font-weight:600;text-align:left;padding:9px 11px;border:0;background:none;color:var(--ink-1);cursor:pointer}
.stage-head:hover{background:var(--surface-sunk)}
.stage-icon{flex:0 0 16px;width:16px;text-align:center;font-size:var(--fs-xs);color:var(--ink-4)}
.stage-name{flex:1;min-width:0}
.stage-count{font-size:var(--fs-xs);font-weight:600;color:var(--ink-4);white-space:nowrap}
.stage.done .stage-icon{color:var(--money-mid)}
.stage.active .stage-icon{color:var(--money)}
.stage.waiting .stage-icon{color:var(--rust-ink)}
.stage.waiting .stage-name{color:var(--rust-ink)}
.stage.todo .stage-name{color:var(--ink-4)}
.stage-body{display:flex;flex-direction:column;gap:5px;padding:8px 11px 10px 36px;border-top:1px dashed var(--line)}
@keyframes step-in{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
.work-step.revealing{animation:step-in .3s ease}
.panel-region{padding:14px 18px;border-top:1px solid var(--line)}
.panel-region .section-title{margin:0 0 var(--sp-2)}
.panel-region .review-rows{margin:0}
.process-body #review-area{padding:0}
.process-body #proof-area,.process-body #books-area{display:flex;flex-direction:column;gap:7px}
.process-body .request-error{margin:14px 18px}
.desk-words{margin:0;font-size:var(--fs-sm);line-height:1.6;color:var(--money);white-space:pre-wrap;overflow-wrap:anywhere}
.proof-line{margin:0;padding:8px 10px;border-radius:7px;font-size:var(--fs-sm);font-weight:600}
.proof-line.moved{background:var(--money-track);border:1px solid var(--money-line);color:var(--money)}
.proof-line.held{background:var(--surface-sunk);border:1px solid var(--line);color:var(--ink-3)}
/* The viewer, and it shows one sheet twice: the PDF the desk renders and reads, then the
   office's text copy under it — the standard a reading is checked against, and the box a retype
   is typed into. Wider than a chat card, because the paper is the point. */
.sheet-dialog{width:min(760px,94vw);max-height:88vh;overflow:auto}
.sheet-dialog .dialog-top strong{font-size:var(--fs-md)}
.viewer-note{margin:8px 0 0}
.viewer-label,.retype-label,.ask-label{font-size:var(--fs-xs);font-weight:700;color:var(--ink-4)}
.viewer-label{margin:16px 0 0}
.viewer-file{display:flex;flex-direction:column;gap:7px;margin:6px 0 0}
/* The file itself, at a height a presenter can read a sheet at without scrolling the dialog.
   The background is what shows while the browser's own viewer paints. */
.viewer-pdf{width:100%;height:60vh;min-height:340px;border:1px solid var(--line);border-radius:8px;background:var(--surface-sunk)}
.viewer-file-row{margin:0;font-size:var(--fs-xs)}
.viewer-file-link{color:var(--money)}
.viewer-no-file{margin:0;padding:11px 13px;border:1px solid var(--sev-line);background:var(--sev-tint);border-radius:7px;color:var(--rust-ink);font-size:var(--fs-sm)}
.viewer-paper{display:flex;margin:6px 0 0}
.viewer-paper .paper-sheet{align-self:stretch;max-width:none;width:100%}
.sheet-dialog .paste-form{padding:14px 0 0;margin-top:16px;border-top:1px solid var(--line)}
.sheet-dialog .paste-actions{align-items:flex-start;flex-wrap:wrap}
/* The base dialog rule makes every .primary the dialog's full-width action; the retype row is a button beside its own warning line. */
.sheet-dialog .paste-actions .primary{width:auto;margin-top:0;flex:0 0 auto}

/* The bookkeeping desk (books.html): the office's own sentences on the left, the desk working on
   the right. Everything on that right half — the stage rows, the gate, the entry lines, both tables,
   the hand-off panel — is the intake page's furniture, already styled above. What is new here is a
   list of things to say, and the words printed before they are sent. */
.events-body p{margin:0}
.event-list{display:flex;flex-direction:column;gap:9px}
.event-card{display:flex;flex-direction:column;align-items:flex-start;gap:7px;width:100%;font:inherit;text-align:left;padding:12px 14px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:var(--ink-1);transition:border-color .15s,background .15s}
.event-card:hover:not(:disabled){border-color:var(--money-line);background:var(--surface-sunk)}
.event-top{display:flex;align-items:baseline;gap:9px;width:100%}
.event-index{flex-shrink:0;display:grid;place-items:center;width:19px;height:19px;border-radius:50%;background:var(--money-track);color:var(--money);font-size:var(--fs-xs);font-weight:700}
.event-name{font-size:var(--fs-sm);font-weight:650}
.event-claim{margin-left:auto;font-size:var(--fs-xs);font-weight:700;color:var(--ink-4);white-space:nowrap}
/* The payload, in the face the box uses: what is printed on the card is, byte for byte, the sentence
   the desk is handed, so a visitor can check the claim against the words rather than against a
   summary of them. */
.event-words{font:var(--fs-sm)/1.5 var(--mono);color:var(--money);background:var(--surface-sunk);border:1px solid var(--line);border-radius:6px;padding:7px 9px;overflow-wrap:anywhere}
/* An event that is a press rather than a sentence has no words to show, and says so in prose —
   never in the mono face, which would print a lie about what was sent. */
.event-aside{font-size:var(--fs-xs);font-style:italic;color:var(--ink-4)}
.event-proves{font-size:var(--fs-xs);line-height:1.5;color:var(--ink-4)}
.pending-note{padding:9px 11px;border:1px solid var(--sev-line);background:var(--sev-tint);border-radius:7px;color:var(--rust-ink);font-size:var(--fs-sm);line-height:1.5}
/* The three paragraphs that say what the two panels are, for a visitor who has never had a set of
   books described to them. Darker and a half-step larger than the page's other muted prose on
   purpose: the event cards and the hints are skimmable furniture, and this is the part someone has
   to actually read. Its bold leads are the two record names, so they scan as sub-headings. */
/* Where the two records change hands. `.panel-region` already gives it the padding and the rule that
   divides the panel's regions; this only keeps it quieter than the desk's own prose, so a line of
   orientation does not compete with the numbers it is orienting. */
.seam-note{margin:0;font-size:var(--fs-xs);line-height:1.5;color:var(--ink-4)}
.seam-note strong{font-weight:650;color:var(--ink-3)}
/* `.paste-form` carries the chat tray's own inset padding; in this panel the form is the column, so
   the column's padding is the only one it needs. */
.ask-form{padding:0}
.ask-form .paste-actions{align-items:flex-start;flex-wrap:wrap;gap:8px}
.ask-hint{flex:1 1 170px;line-height:1.45}
/* AR aging (ar.html): a ledger header of four figures, the same open dollars cut three ways, the
   worklist, and one collapsed panel of proof. Most of the furniture is already above — the record rows,
   the table and its scroller, the foot that says whether a claim held. What is new here is the aging bar,
   the step bar drawn beside it, the legend each one carries, and a row that is itself the control. */
/* The book is not one card any more. The four figures stand on the page's own ground as a ledger header,
   each cut is a panel, and the worklist is the product rather than a box inside a box. Dropping the
   ancestor's chrome is also what makes the sticky header real: *any* scroll container becomes the scrollport
   for a sticky descendant, so `overflow:hidden` on the panel would have the column names sticking to a box
   that never scrolls and riding out of view with the page instead. */
/* The head: the label, the h1, and the one paragraph that says what the page does. The name is the shared
   one — the other four pages take this class in the commit that cuts their intro paragraphs, because
   `--fs-lg` is a size a lede has to earn and three of the four are still essays. Its eyebrow rule used to
   live here and is now the base `.eyebrow`, since every page's label wants the same voice. */
.page-head h1{margin:var(--sp-2) 0 0}
.page-head p{font-size:var(--fs-lg);line-height:var(--lh-tight);color:var(--ink-2);max-width:68ch;
             margin:var(--sp-3) 0 0}
.book-panel{display:block;background:none;border:0;border-radius:0;box-shadow:none;overflow:visible;padding:0}
.book-panel>.panel-heading{padding:0 0 var(--sp-2);border-bottom:0}
.book-body{padding:0;display:flex;flex-direction:column;gap:var(--sp-6)}
.book-body p{margin:0}
.book-body .answer-chips{max-width:none}
.book-body .request-error{margin:0}
.book-body .tb-scroll{margin:0}
.aging-block{display:flex;flex-direction:column;gap:var(--sp-3)}
/* A cut is a panel; the worklist is not, because its own scroller already draws the box. */
.chart-grid>.aging-block{background:var(--surface);border:1px solid var(--line);
                         border-radius:var(--r-lg);padding:var(--sp-5)}
.aging-block .section-title{margin:0}
.model-question{font-size:var(--fs-md);line-height:var(--lh-body);color:var(--money);font-weight:600;max-width:68ch}
.provenance-list{margin:0;padding-left:19px;display:flex;flex-direction:column;gap:7px;font-size:var(--fs-sm);line-height:1.55;color:var(--ink-3)}
.provenance-list li::marker{color:var(--money-mid)}
/* The four figures across the top of the aging page: a **ledger header**, not four cards. Hairline rules
   between them do the separating and a 2px rule over the top says this is the book's own masthead, so the
   row reads as one statement about the book rather than four objects that happen to be near each other.
   None is a button: a card that presses nothing is honest, and this page asks nothing of a model. Three
   lines each — what is counted, the figure, and one qualifier of eight words or less — because a figure
   that needs four sentences to be read correctly is a figure that has not been worked out yet, and these
   have been, in Python, over the rows under them. The disclaimers the qualifiers used to carry are in the
   panel, one block per card.

   What keeps the forecast/fact distinction on the *surface* is shape: figures one and two are the money
   family, figure three the severity family's, and figure four is a fact — `--fact-ink`, deliberately off
   the ramp, on the one ground of its own, because "already gone unpaid past the day we ask about" is a
   different instruction from "the odds it will" and the two must not read as one figure shown twice. */
.stat-grid{display:grid;gap:9px}
.ar-cards{grid-template-columns:repeat(4,minmax(0,1fr));gap:0;
          border-top:2px solid var(--ink-1);border-bottom:1px solid var(--line);
          padding-block:var(--sp-5)}
.stat-card{display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-2);width:100%;
           text-align:left;font:inherit;padding:0 var(--sp-5);border-left:1px solid var(--line);color:var(--ink-1)}
.stat-card:first-child{border-left:0;padding-left:0}
.money-stat .stat-figure{color:var(--money)}
/* The figure about risk used to wear the money green, which is the one colour this page reserves for
   dollars the company expects. A level is a judgement about a bill, not a balance. */
.risk-stat .stat-figure{color:var(--rust-ink)}
/* A row the filed year has already answered is not the darkest step of the estimate's scale, so it is not
   another rung of the ramp: it gets the tint. The tint may not move the figure it marks, so its vertical
   padding is cancelled by a negative
   margin and all four labels, figures and qualifiers still sit on one line — a box that shifted the fourth
   figure out of the grid would be a card again, which is the thing this row stopped being. */
.confirmed-stat{background:var(--sev-tint);border-radius:var(--r-md);padding:var(--sp-3) var(--sp-5);
                margin-block:calc(-1 * var(--sp-3)) 0}
.confirmed-stat .stat-figure{color:var(--fact-ink)}
.stat-label{font-size:var(--fs-sm);font-weight:var(--w-semibold);color:var(--ink-3)}
.stat-figure{font-size:var(--fs-3xl);font-weight:var(--w-semibold);letter-spacing:-.4px;line-height:1.05;
             color:var(--ink-1);font-variant-numeric:tabular-nums}
.stat-qual{font-size:var(--fs-sm);line-height:1.4;color:var(--ink-3)}
/* The aging bar: each bucket's own cents as its `flex-grow`, so the bar is the book's proportions and
   no percentage is computed in the browser that could disagree with the table under it.

   The 2px separator between segments is a shadow painted *inside* the slice on the right, not a `gap`
   and not a border, and the arithmetic is why. A flex item's outer hypothetical size includes its
   border, so a 2px border on each of three slices leaves each filling `W·share − 2px`: the boxes go
   from proportional to `cents − constant`, and the error lands worst on the smallest segment. A `gap`
   and a `margin` both take their pixels out of the pool *before* the distribution, so they keep the
   ratios honest and the track short by 8px. A `box-shadow` takes no part in the distribution at all,
   so the boxes sum to the track exactly and each one is its own dollar share exactly. What it costs is
   2px of painted fill at each boundary — a constant, not a scale factor, and the same price the other
   two arrangements pay. Measured on this book's real cents over a 407px track: the gap-and-stub version
   drew 51.0 / 27.5 / 16.5 percent for 53.7 / 29.0 / 17.4 of the dollars, and this draws 53.7 / 29.0 /
   17.4. */
.aging-bar{display:flex;height:28px;border:1px solid var(--line);border-radius:6px;overflow:hidden;background:var(--surface-sunk)}
.aging-slice{flex:1 1 0;min-width:0;background:currentColor}
.aging-slice + .aging-slice{box-shadow:inset 2px 0 0 var(--surface)}
/* A bucket with no dollars draws nothing at all. It used to keep a 6px stub, on the reasoning that
   "nothing over 90 days" is a finding rather than an absence — but on this book two such stubs each
   drew 1.5% of the track for $0.00, which is the page spending ink to look fuller than the money is,
   and a reader cannot tell a 6px finding from a 6px rounding error. The bucket keeps its row in the
   legend at $0.00 · 0 invoices, where the finding is actually stated. */
.slice-empty{flex:0 0 0;background:none}
/* Each colour is declared once, as the element's own `color`, and everything that draws from it says so
   itself — a swatch in a legend (`i{background:currentColor}`), a bar's slice (`.aging-slice`), a chip's
   border — so one word has one value everywhere it appears and none of them can drift.

   What these classes deliberately do *not* carry any more is a `background`. They name a tier, and they
   are handed to three different kinds of element: the slice that must be filled, the `i` that must be
   painted, and the container of *words* — `.chart-key`, which `ar.js` gives the same class it gives a
   slice, and a row's action chip. Declaring the fill here meant every one of those painted itself, so a
   legend row arrived as a 253-to-318px block of its own series colour with the page's dark ink sitting
   directly on it. Ten of those, on a page whose whole point is being readable across a room. The colour
   stays — a class carrying no `color` is an invisible square — and the fill moves to the drawing. */
.slice-current{color:var(--sev-none)}
.slice-1-30{color:var(--sev-1)}
.slice-31-60{color:var(--sev-2)}
.slice-61-90{color:var(--sev-3)}
.slice-over-90{color:var(--sev-4)}
/* The three actions the submitted file can suggest, in the file's own words, in the order the file puts them
   in. One word wears one colour in three places: the action chart's slice, its legend swatch, and the chip in
   the row — and the classes are derived on the server from the CSV's own spelling, so renaming an action there
   breaks `tests/test_ar_page.py`, which reads this file for a class beside every word the file can print.
   The colours are the aging buckets' own, deliberately: the action is a pure function of the days a bill has
   stood past its due date, so `Standard payment terms` *is* the current bucket, `Automated reminder` *is*
   1–30 and `Routine collections follow-up` *is* 31–60. Two charts that are one partition should not be
   allowed to look like two findings, and matching ink is what makes the restatement visible. */
.action-standard-payment-terms{color:var(--sev-none)}
.action-automated-reminder{color:var(--sev-1)}
.action-routine-collections-follow-up{color:var(--sev-2)}
/* One grey for the money nobody has good cause to chase yet, and it is never a fourth rung: `current` on the
   age chart and `Standard payment terms` on the action chart are the same $22,240.00 in two registers — a bill
   whose due date has not arrived — so they share a colour, and a flat pale fill in the same place as the
   ramp's pale end is what a reader would grade instead of read. They carry a texture. Compound selectors
   only: a mark that draws takes the pattern, and a container of words never does, which is the defect the
   last commit removed — a pattern laid over `background:currentColor` is no pattern at all, since the
   shorthand resets `background-image`. */
.aging-slice.slice-current,.aging-slice.action-standard-payment-terms,
.chart-key.slice-current i,.chart-key.action-standard-payment-terms i{
  background-image:repeating-linear-gradient(135deg,currentColor 0 2px,transparent 2px 7px);
  background-color:var(--surface-sunk)}
.bucket-empty .tb-num,.bucket-empty .tb-account{color:var(--ink-4)}
/* The aging table. `.trial-balance` gives the rules and the figures; what is added is the row's own
   anatomy — an id over the account it belongs to, a day count over why, a risk cell whose meter takes this
   family's ink and whose words stay the page's own rust, a dollars column that is a product of the two, and a
   chip wearing the colour of the file's own action string. */
.ar-table{min-width:100%}
/* Density. The size is `.trial-balance`'s now — 14px for every table on the site, which is what this row
   asked for first — so what is added here is the padding on the row's own cells, and the dollars leaving the
   mono family for tabular figures, because a worklist row is read for its shape and not aligned against a
   printed form. Padding on the cell rather than the table is what keeps every class that names a smaller
   voice (`.ar-cust`, `.ar-days small`, the head's `.th-note`) the size it chose: a declaration on a
   cell always beats an inherited one, and a descendant selector beats both.
   What the row is *wide* is not declared here, and that is worth the note because the guess was wrong twice.
   The fill comes from `.ar-table{min-width:100%}` above; the pan at a phone width comes from the table's own
   min-content, re-measured in Chrome on 2026-10-16 at **1025px** with seven columns and all 71 rows drawn (the
   sixth column's own number was 1024, so the file's dollars at risk cost one pixel of a row's minimum — which
   is the whole reason the breakpoint below did not move and gets written down rather than assumed). The long
   account names set to 181px inside the 210px cap on `.ar-cust` below, so nothing ellipsizes, and no cell on
   any row clipped at 760, 780, 900, 1024, 1100, 1157, 1200, 1220, 1290, 1360 or 1430. A `min-width:1024px`
   here would have matched that earlier measurement exactly and still been wrong, because min-width is one
   property: it would have replaced the 100%
   and left the worklist drawn 1024px inside a 1281px frame, its rules stopping short of the box around them.
   A floor is only ever needed *below* a table's natural width; above it, the same number is a ceiling in disguise. */
.ar-main-table .ar-row>td,.ar-main-table .ar-row>th{padding:var(--sp-3)}
.ar-main-table .tb-num{font-family:var(--font);font-variant-numeric:tabular-nums}
/* The worklist is the product, so it is not a window onto itself. Capped at 560px, an opened row's evidence
   — the invoice, the addition, the account's own paper — rendered below a *second* scrollbar and a reader had
   to hunt inside a box for the proof they had clicked for. So no vertical cap. On a wide screen the table
   needs no horizontal scroll either, which is also what lets the sticky header reach the page: a scroll
   container becomes a sticky descendant's scrollport, and a box that never scrolls vertically would pin the
   column names to itself while the page carried them away. On a narrow one it takes `overflow-x` and the
   header rides the box.
   The breakpoint is arithmetic, not taste: `main` is 1510px wide at most with 5% padding either side, so the box
   has 0.9 × (viewport − 15) − 2 px in it, and the table needs 1025. That holds from 1157px up — measured, pan 0,
   the box 1028 at that width and 1029 at 1160; 1220 is taken so
   the box still fits the table with 6% to spare for a *different* system font (the box there is 1085 against a
   1025 minimum) — this page has no webfont, so the
   widths below are SF Pro's and a Linux screenshot renders them with Roboto's. Below the breakpoint a spill would
   put the table past its own frame: `overflow:visible` draws what does not fit rather than hiding it, which is
   how a 1000px window ended up scrolling sideways by 54px for a header that was not sticking anyway. */
.ar-scroll{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);
           overflow-x:auto;-webkit-overflow-scrolling:touch}
.ar-scroll>.trial-balance{border:0;border-radius:0;background:none}
@media(min-width:1220px){.ar-scroll{overflow:visible}}
/* The header is `position:sticky` inside a scroller and had no background of its own, so 71 rows
   scrolled *through* the words that name their columns — the walk caught it by reading
   `getComputedStyle(th).backgroundColor` back to the page ground. A sticky thing paints or it does
   not work; `--surface` is the panel it belongs to. */
.ar-main-table thead th{position:sticky;top:0;z-index:2;background:var(--surface);padding:var(--sp-3);
                   box-shadow:inset 0 -1px 0 var(--line-strong)}
.ar-row:hover{background:var(--surface-sunk)}
.ar-row-open{background:var(--surface-sunk)}
/* The name leads and the reference sits under it, which is the order an office reads a worklist in: the
   call is placed with a person, and the invoice number is what you quote once you have them. They were
   the other way round — a bold green id over a 12px grey name — so the most prominent thing on every row
   was a string nobody telephones. The id keeps the mono family (it is a reference, and it is set like one
   on the invoice in the drawer) and gives up the colour: `--money` is this page's affordance for *money*,
   and the green on 71 row heads competed with the four figures above it rather than pointing at anything.
   The caret is what says the line opens, and the underline on hover says the number is a document. */
.ar-invoice{display:block;font-family:var(--mono);font-size:var(--fs-xs);color:var(--ink-2);white-space:nowrap}
.ar-cust{display:block;font-size:var(--fs-sm);font-weight:var(--w-medium);color:var(--ink-1);
         white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:210px}
.ar-dates{white-space:nowrap;font-size:var(--fs-sm);color:var(--ink-2);font-variant-numeric:tabular-nums}
.ar-days span{font-variant-numeric:tabular-nums;color:var(--ink-1)}
.ar-days small{display:block;font-size:var(--fs-xs);font-weight:400}
.ar-late{color:var(--rust-ink)}
.ar-soon{color:var(--ink-4)}
/* `.ar-expected` went with the cash card: this page no longer discounts a receivable into an expected
   arrival, because the file it reads does not offer a date and the desk would be inventing one. */
.ar-risk{white-space:nowrap}
/* The cell's colour is the meter's and nothing else: a row whose mark has already gone by is not drawn
   differently here, because the estimate beside it is still the file's estimate and what changed is that the
   filed year has since spoken — which `.risk-flag` says in its own words, in `--fact-ink`, the darkest ink in
   this family. On the book this file joins there are no such rows: the earliest mark falls after the desk's own
   date, and the flag is a definition rather than today's finding. The colour is `--sev-3` because the track it
   draws on is the ramp's own pale ground, and `--sev-1` at 8px on `--sev-track` measured as a wash rather than
   as a bar. */
.risk-cell{display:flex;align-items:center;gap:7px;color:var(--sev-3)}
/* A `<span>` asked to draw a bar has to say so itself. Inside `.risk-cell` the meter is a flex item, so
   `flex-basis` sized it and the browser blockified it for free — but a meter once drawn in a plain `<td>`,
   with no flex container anywhere near, kept `display:inline`, ignored `width` and `height`, and rendered
   **0×0 for a whole rebuild**: a stylesheet test reads declarations and the walk measured the stacked bars, so
   nothing caught it until the page was looked at. Hence `display:inline-block` and a `width` next to the
   `flex-basis` — the first is what makes it draw anywhere, the second is what makes it the right size when
   nothing is laying it out. The drawer's interval table is gone with the curve, and this rule stays for the
   meter's own sake. */
.risk-meter{display:inline-block;vertical-align:middle;flex:0 0 64px;width:64px;height:8px;
            border-radius:var(--r-pill);background:var(--sev-track);overflow:hidden}
.risk-meter i{display:block;height:100%;border-radius:var(--r-pill);background:currentColor}
/* The estimate's words are the number a reader is comparing across 71 rows, so they are ink and not a tier:
   this column has no tiers, and the only other thing in the cell is a bar that is not the same number as the
   word beside it. Every row wears the same colour now, deliberately — the ramp belongs to the action chip,
   which is the file's vocabulary of three, and a meter that also carried severity would be the page
   decorating rather than saying. */
.risk-words{font-size:var(--fs-sm);font-weight:650;font-variant-numeric:tabular-nums;color:var(--rust-ink)}
.risk-flag{font-size:var(--fs-xs);font-weight:700;color:var(--fact-ink);background:var(--sev-tint);border:1px solid var(--sev-line);border-radius:20px;padding:2px 7px;white-space:nowrap}
.ar-action{white-space:nowrap}
.ar-exposure{font-variant-numeric:tabular-nums;color:var(--rust-ink)}
/* The row's accessible name and focus ring, and nothing more: a bordered button in the first cell made
   every row of the worklist look like a control that was already pressed, and the row itself is the
   control. It sizes nothing — `font:inherit` and no `font-size`, because the three things inside it are
   three different voices (a reference, a name, a mark) and each declares its own. One rule, not two:
   this element carried `.row-toggle` *and* `.ar-open-btn`, and the pair of rules a hundred lines apart
   both set `font`, `color`, `padding` and `border`, so which one won was a question about file position. */
.row-toggle{display:flex;flex-direction:column;align-items:flex-start;gap:1px;width:100%;
            font:inherit;text-align:left;padding:0;border:0;background:none;cursor:pointer}
.row-toggle:hover .ar-invoice{text-decoration:underline}
/* A ring for what this page draws and a keyboard reaches. The stylesheet's only other `:focus` rules are
   two textareas, so every button on every page here has been tabbing through invisible — and this page
   puts a pressable control on every row of the book and a scrollable region on a table, where a visitor
   who cannot see where they stand has no way to know the book continues. The cards are not in the list
   below because nothing on them is pressable any more: they are three figures, not three questions. Scoped to the classes this page uses
   rather than a global `*:focus`, because the older pages are not being restyled in a commit about a new
   one. The scroller is focusable on purpose: a table you can only move with a mouse is a table that
   stops being readable at 1000px. */
.row-toggle:focus-visible,.mini-btn:focus-visible,.ar-scroll:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* The two folded regions on this page are `<summary>` elements, which the stylesheet's global `:focus`
   rules do not reach — a control a keyboard can operate but nobody can see is a control that only
   works by luck. */
summary:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
.ar-scroll:focus-visible{outline-offset:-2px}
.row-action{margin-left:6px}
/* The opened row: the account's own facts, the reason lines the desk reads aloud, and the addition a
   reader can check. It is a row inside the table so the figures above it stay in place while you read
   them. */
/* The opened row is the reason the row was clicked, so it gets the measure: a sunk ground (the row above it
   is sunk too, which is what makes the two read as one opened thing), the page's own gutters, and its two
   columns allowed to stand at the body's 14px. The size goes on each inner table rather than on their cells,
   for the same reason as the worklist: a class that names a smaller voice keeps it. */
.ar-detail-row>td{padding:var(--sp-5) var(--sp-4) var(--sp-6);background:var(--surface-sunk);
                  border-bottom:1px solid var(--line-strong)}
.ar-detail-row .trial-balance{background:none}
.ar-detail-row .trial-balance th,.ar-detail-row .trial-balance td{padding:var(--sp-1) var(--sp-2)}
.detail-title{margin:0;font-size:var(--fs-md);font-weight:650;color:var(--ink-1);letter-spacing:-.2px}
.detail-label{font-size:var(--fs-xs);font-weight:700;color:var(--ink-4)}
.detail-why{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:var(--fs-sm);line-height:1.5;color:var(--money)}
/* The separator leads the item it belongs to rather than trailing the one before it, because these
   sentences are long and at a desk width the first fills its line: a trailing dot then sat alone at the end
   of a paragraph, which reads as a typo rather than as the join it is. The server's statements are written
   to follow a middot — the desk reads the same strings aloud — so the join stays and only its side moves. */
.detail-why span+span::before{content:'· ';color:var(--money-mid)}
.detail-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:var(--sp-2) var(--sp-4)}
.fact{display:flex;flex-direction:column;gap:1px;font-size:var(--fs-sm);color:var(--ink-2);overflow-wrap:anywhere}
.fact strong{font-size:var(--fs-xs);font-weight:700;color:var(--ink-4)}
.detail-note{font-size:var(--fs-sm);line-height:1.5;color:var(--ink-3);max-width:68ch}
/* The two halves of a drawer are `.detail-half` each and they are laid out by `.detail-cols` below; the notes
   inside them are `p`s, and `p{margin:0}` means the order between a table and its note has to come from the
   gap rather than from a default nobody chose. */
/* `on time` in the account's own history, the counterpart of `.ar-late` two lines of code away. It used to
   be called `.contrib-plus` because it was born in the fit's weights table, and that table is gone: a class
   named for a thing the page no longer draws is a second place a colour can be defined that nothing reads.
   The win/lose/baseline helpers went with the weights table for the same reason. */
.ar-ontime{color:var(--money-mid)}
/* The cuts sit side by side while the viewport can hold them: age, the file's action and the month the money
   falls due are the same open dollars three times, and holding them across one row is the point of putting
   them next to each other. The due-month cut is a list rather than a third bar — a phone wants a list, not a
   fourth bar chart — and it needs the full width to read its own cumulative shape, so it takes both columns. */
.chart-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-5)}
.chart-grid>.aging-block:last-child{grid-column:1/-1}
.chart-bar{height:28px}
.chart-legend{display:flex;flex-direction:column;gap:var(--sp-1)}
/* One head per block. The title names the cut and the note beside it is a proof rather than a paragraph —
   the dollars adding back to the book, the due-month lines being cumulative — because both figures are the
   derivations' own, and a reader can check a chart against the card above it without reading prose. The
   caption paragraphs that used to sit here are in the panel. */
.chart-head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-2) var(--sp-4);flex-wrap:wrap}
.chart-note{font-size:var(--fs-sm);line-height:1.4;color:var(--ink-3);font-variant-numeric:tabular-nums;text-align:right}
/* Three columns — swatch, name, figures — and no row of the legend is ever a bar. It used to be two,
   with the key column `minmax(0,1fr)`: the coloured box inside it took the whole leftover, so the ten
   legend rows measured 253 / 253 / 268 / 318 / 318px — a length that tracks the *label's* character
   count and moves inversely to the figure beside it. Five painted boxes whose width correlates with
   nothing is precisely what reads as "the bar length does not align with the number", and it was the
   more visible of the two bar defects because it sat directly under the one the reader was comparing.
   `.chart-key` becomes `display:contents` so its swatch and its word can take columns one and two
   without the markup changing: the key is still one element carrying one tier, and the row is still
   three things in a row. */
/* A legend row is the chart read as a sentence, so it takes the table's own voice: 14px, and the swatch
   aligns to the middle of the line rather than its baseline. */
.chart-line{display:grid;grid-template-columns:min-content minmax(0,1fr) auto;gap:var(--sp-1) var(--sp-3);align-items:center;font-size:var(--fs-sm);color:var(--ink-2)}
.chart-key{display:contents}
/* The swatch takes the colour of the bucket or the file's action named beside it — the same class the row's
   action chip wears and the same ink the bar above it was cut with, so one word has one colour everywhere on
   this page. The risk cell is deliberately *not* in that list: a meter is one estimate on a scale the file
   sets, and the page already says in the column's own head what the scale is. */
.chart-key i{width:10px;height:10px;border-radius:var(--r-sm);background:currentColor;flex-shrink:0}
/* The name is a label, not a heading: at 650 the label column out-shouted the figures it exists to point
   at, and a legend is read by matching a number to the length of the bar above it. */
.chart-key strong{font-weight:var(--w-regular);color:var(--ink-2);font-size:var(--fs-sm)}
.chart-fig{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--ink-3)}
.chart-fig b{color:var(--money);font-weight:var(--w-medium)}
.chart-sub{grid-column:1/-1;font-size:var(--fs-xs);color:var(--ink-4);line-height:1.4}
/* The third cut, and the one on this page that needs no estimate behind it: the month an invoice prints as
   its due date. Rows rather than columns, because three months under three bars wastes a third of the grid and
   a desk scanning this wants the month and the figure on the same line as the shape. Each bar is the point's
   own `cumulative_share` as a width — cumulative, so the line above it is always shorter and the last line is
   the book. Green, because every dollar on it is the paper's own and none of it is a model's number: the
   severity ramp is the risk file's vocabulary, and this chart must not be allowed to look like it belongs to
   that column. */
.due-list{display:flex;flex-direction:column;gap:var(--sp-2)}
.due-line{display:grid;grid-template-columns:84px minmax(0,1fr) auto;gap:var(--sp-3);align-items:center;font-size:var(--fs-sm)}
.due-month{color:var(--ink-3);font-weight:650;white-space:nowrap;font-variant-numeric:tabular-nums}
.due-track{height:14px;border-radius:var(--r-sm);background:var(--money-track);overflow:hidden}
.due-track i{display:block;height:100%;border-radius:var(--r-sm);background:var(--money-mid)}
.due-fig{font-variant-numeric:tabular-nums;color:var(--money);white-space:nowrap}
/* The head's second line: what scale the column under it is drawn on. `small` is the one element this
   stylesheet cannot leave alone — the browser sizes it to five-sixths of its parent, which lands under the
   floor for a page that promises 12px minimum — so it says its own size here, like every other `small` on
   these pages. */
.ar-main-table thead .th-note{display:block;font-size:var(--fs-xs);font-weight:400;color:var(--ink-4)}
/* A row is the control, and the handler is on the row itself (`ar.js`), not on an overlay: a pseudo-element
   stretched over a `position:relative` row stops being sized against it as soon as a sibling `colspan` row
   changes the table's width, which left the middle of an opened row answering nothing. The button still
   carries the accessible name and the focus ring; this file's job is to say that the whole line is live. */
.ar-row{position:relative;cursor:pointer}
/* The button's own rules live with `.row-toggle`, the class the keyboard and the tests know it by; the
   markup carries both names and the file gives it exactly one declaration block. The mark is the page's
   one green promise that a line opens: the id is a reference and the account is the subject, and a row
   that is merely clickable says so with a hover ground and this caret. */
.ar-caret{font-size:var(--fs-sm);color:var(--money)}
.action-chip{display:inline-block;font-size:var(--fs-sm);font-weight:600;padding:var(--sp-1) var(--sp-3);border:1px solid currentColor;border-radius:var(--r-pill);background:var(--surface);white-space:nowrap}
/* The chip's *border* is the step and its word is not. The class naming the tier has to stay on the
   element carrying the border, and `currentColor` resolves against that element's own `color` — so
   putting the ink on `.action-chip` itself would have painted the border grey and thrown the tier away
   with the bug. The word is a child span, and the child is what stops wearing it: with no `color`
   anywhere, all 71 rows printed their action *in* its severity — "Call" at 3.44:1 on white, "1-30" at
   2.46:1 — in the one column a reader is being asked to act on. */
.action-chip span{color:var(--ink-1)}
/* The opened row: the invoice on one side, the account's whole paper on the other. */
.detail-cols{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-6)}
.detail-half{display:flex;flex-direction:column;gap:var(--sp-2);min-width:0}
/* The one sentence a reader is being asked to check the number against: it is set at body size, not in a
   box, because a framed quotation on a sunk ground reads as somebody else's words rather than the page's
   own arithmetic. */
.detail-sum{font-size:var(--fs-md);line-height:var(--lh-tight);color:var(--ink-1);margin:0;
           font-variant-numeric:tabular-nums;max-width:68ch}
/* Six columns of dates is a narrow table: its own min-content measures 558px on this book (re-read for all
   71 rows on 2026-10-16, with both halves pinned to 200px so `scrollWidth` could only report the content's
   demand), because only the last column can wrap. It was 510px when this note was first written and grew
   when the outcome column took its gloss as a second line. The same reason the worklist's has no number
   either: a floor here would replace the fill below its own width and become a ceiling above it. */
.acct-table{min-width:100%}
.acct-this>th,.acct-this>td{background:var(--surface)}
/* The outcome words take the drawer's own 14px and only the gloss under them is a smaller voice — and a
   `<small>` is the one element this file cannot leave alone: the browser sizes it to five-sixths of its
   parent, which is how a gloss under a 12px reference rendered at 10px. That is below the floor and
   invisible to the test which reads the stylesheet for sub-floor `font-size` declarations, because no
   declaration named 10px. So every `small` this page draws says its own size.
   The gloss is a line of its own rather than a tail on the word, because `white-space:nowrap` on this cell is
   what makes the account's paper wider than the half holding it: put the declaration back and the table's own
   minimum measures **650px** against the 604px half it sits in at 1430px (re-measured 2026-10-16 by reading
   the width back with the nowrap re-applied, not reasoned out). The consequence today would be a pan, not a
   cut — the table lives in a `.tb-scroll`, and 88px of sideways scrollbar appeared inside the drawer under
   that experiment — which is the same failure in a louder dress: the evidence a reader clicked a row for
   would arrive needing its own scroll, next to a page that removed the drawer's second scrollbar on purpose. */
.acct-outcome small,.ar-gloss{display:block;font-size:var(--fs-xs);color:var(--ink-4);font-weight:400}
/* The account's paper is a list of bills, so its first column keeps the worklist's weight; the reference
   under it stays the quieter voice it is in the row above. */
.ar-detail-row .ar-invoice{font-size:var(--fs-sm);color:var(--ink-1)}
/* The drawer's interval table went with the fit that drew it: a row now opens on the invoice, on the file's
   three numbers and on the account's own paper, and none of those is a curve read off a survival function.
   What used to live here was the meters, the support counts and the two marked horizons — a whole family of
   classes named for a thing this repository no longer has. `.risk-meter` survives because the worklist still
   draws one, and it draws it in the row. */
/* The folded evidence, outside the panel: the page stays short, and nothing about the fit is deleted. */
/* One panel for everything the surface stopped saying, opened on purpose. It is a rule rather than a box,
   because a bordered card around the honesty block makes the disclaimer the loudest thing on the page; prose
   needs a measure a reader can come back to, so the body is capped at 88ch and each paragraph inside it at
   68ch. The two mounts let somebody reach the note about a figure, or the fit itself, without scrolling past
   the whole honesty block to find the one they wanted. */
.how-panel{margin-top:var(--sp-6);border-top:1px solid var(--line);padding:0}
.how-panel>summary{font-size:var(--fs-md);font-weight:var(--w-semibold);color:var(--ink-1);padding:var(--sp-4) 0;cursor:pointer;list-style:none}
.how-panel>summary:hover{color:var(--money-mid)}
.how-panel>summary::before{content:'▸ ';color:var(--ink-4)}
.how-panel[open]>summary::before{content:'▾ ';color:var(--ink-4)}
/* The page's vertical rhythm, reset on 2026-10-18 when the landing page's sections were measured too far
   apart. Every gap between top-level blocks on a served page is now one token, `--sp-5` (24px), and the
   rules that hold it are spread over this file: `main`'s top padding (37 → 28), `.intro`'s margin-bottom
   (30 → 24), this block's own `padding-bottom` (32 → 24), `.case-group`'s margin-top (34 → 24, and 0 on a
   first group, which sits under a block that has already paid). The rung above the footer moved on 2026-10-19
   when the band left `main`: it was `footer`'s own `margin-top` (23 → 24 in the pass below), and it is now
   `main`'s `padding-bottom` (20 → 24) — the same token paid by the block that ends the page rather than by the
   band that starts below it, because a margin on a sibling of `main` and a padding on `main` are the same one
   break, and paying both is the 66px mistake again. The two narrower `main{padding:…}` overrides above the
   component rules reset the shorthand, so the gap reads 24 at 1051 and up, 28 from 1050 to 781, 26 at 760 and
   below — measured, not inherited from the sentence before it.
   Measured on `index.html` at 1440 as the whitespace a reader actually sees between consecutive blocks: it
   was 30 / 32 / 66 / 34 / 23 px and is now 24 in every one of those five places (the 66 was a 32px padding
   under a 34px margin, both paid for the same break, and the box-edge gap read 34), under an opening that
   went 37 → 28. The page stood 1737px against 1903 — the same content 166px shorter. It measures 1720 today,
   because the band left `main` on 2026-10-19 and the page ground that used to trail *below* it inside `main`
   went with it: the band is now the last thing in the document.
   `margin:0` on the paragraphs is the bigger half of that. `.how-body` is a flex column, and in a flex
   container margins do not collapse, so the UA's `p{margin:1em 0}` stacked with the `gap` instead of
   merging into it: a heading-to-paragraph break printed 29px and a paragraph-to-paragraph break 46px where
   the gap alone says 16. Inside a flex column the gap is the rhythm; a paragraph carrying its own margin
   too is two systems answering one question. */
.how-body{display:flex;flex-direction:column;gap:var(--sp-4);max-width:88ch;padding-bottom:var(--sp-5)}
.how-body p{max-width:68ch;margin:0}
/* The panel is where the page's claims live, not where they go to be dimmed: body size, body ink, on the
   measure. `--ink-4` is the metadata step, and the sentences that say which column is a fit and which is a
   fact are the least metadata-bearing things on the page. */
/* One emphasis voice inside a folded panel, on either page that has one. The bookkeeping desk used to
   set its two record names in `--money`, which made green mean "this word is important" as well as "this
   is money"; inside a panel whose whole job is to be checked, emphasis is ink, not a second palette. */
.how-body strong{font-weight:var(--w-semibold);color:var(--ink-1)}
.how-body .muted-text{font-size:var(--fs-md);line-height:var(--lh-body);color:var(--ink-2)}
.how-body .provenance-list{font-size:var(--fs-md);line-height:var(--lh-body);color:var(--ink-2)}
.how-body .tb-foot{font-size:var(--fs-sm);color:var(--ink-2)}
/* The step-down belongs to the panel, not to the body class. It used to read `.how-body .section-title`,
   and `about.html` uses `.how-body` for its whole page body — six top-level sections, none of them inside
   a heading a reader opened — so all six of its block names printed at 20px, one step under the landing
   page's four group titles at 24, while this file's own ladder comment said 24 is the block-name step.
   Measured on the served page at 1440x900: about's six `.section-title` headings at 20px, the landing's
   `.group-title` at 24px, `.case-card h2` at 20px (correct, those are nested in a card). Keyed to
   `.how-panel` the rule still reaches both panels that need it, on `ar.html` and `books.html`, and about
   takes the block-name step the rest of the site gives a section. */
.how-panel .section-title{font-size:var(--fs-lg)}
.how-part{display:flex;flex-direction:column;gap:var(--sp-3)}
/* What the fit is, as a label beside a value: the same content in six paragraphs reads as a defence, and
   in a spec table it reads as rigour. The terms are the page's own words for the payload's fields, so no
   figure here is one the endpoint did not send. */
.model-spec{display:grid;grid-template-columns:minmax(0,190px) minmax(0,1fr);gap:0 var(--sp-5);
           margin:var(--sp-3) 0 0;border-top:1px solid var(--line)}
.model-spec dt{font-size:var(--fs-sm);font-weight:var(--w-semibold);color:var(--ink-3);
               padding:var(--sp-2) 0;border-bottom:1px solid var(--line)}
.model-spec dd{margin:0;font-size:var(--fs-sm);line-height:1.55;color:var(--ink-1);
               padding:var(--sp-2) 0;border-bottom:1px solid var(--line)}
summary::-webkit-details-marker{display:none}
/* Side by side is what the drawer was drawn for — the bill beside the account's whole paper, one read
   against the other — and only one of the two halves cannot be squeezed: the invoice card is prose and a
   grid of `minmax(170px,1fr)` cells, so it reflows, while the account's paper is a table. Its own
   min-content, measured on 2026-10-16 by pinning both halves to 200px and reading the table's `scrollWidth`
   on every one of the 71 rows, is **558px**, and it is the same 558 on every row because the head row and
   the "the row you opened" gloss set it, not the dollars.
   The 1.15fr this tier carried was measured against the interval table that used to sit in the left half —
   a floor of 639px, now gone with the fit that drew it — and left the paper 562px at 1430px against its own
   558: four pixels. An even split gives each half 604px at 1430px, 608 at 1440px, 641 at 1512px and 687 at
   1728px, so the table clears its floor by 46px at the tier's own edge, which is roughly the 6-8% the
   worklist's breakpoint keeps for a different system font (this is SF Pro's arithmetic; a Linux screenshot
   renders Roboto's). Below 1430px the halves stack and each takes the whole drawer — 1123px of it at
   1300px, measured, with nothing overflowing. What an overflow would cost is not a painting-over: the paper
   table sits in a `.tb-scroll`, so it pans, and 88px of a horizontal scrollbar was measured inside the
   drawer with the outcome cell's nowrap put back. That is the real reason the gloss stays on its own line —
   a reader who clicked a row for evidence should not have to scroll sideways inside the evidence — and the
   drawer's own table therefore never clips (`unpaid` arrived whole at every width tested, 0 of 71 rows
   panning). */
@media(min-width:1430px){.detail-cols{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:1100px){.scan-workspace,.books-workspace{grid-template-columns:minmax(0,1fr)}}
/* The ledger header is four figures until the row cannot hold them, and then it is two and one; the rule
   over the top and the hairline under it stay, so it is still one statement about the book rather than a
   stack of boxes. The separators move with the wrap: the first figure of each line loses its left rule. */
@media(max-width:1020px){.ar-cards{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-5) 0}
  .stat-card{padding-right:var(--sp-4)}
  .stat-card:nth-child(3){border-left:0;padding-left:0}
  .chart-grid{grid-template-columns:minmax(0,1fr)}}
@media(max-width:760px){.intake-body,.events-body{padding:14px}.stage-list,.panel-region{padding:12px 14px}.process-body .request-error{margin:12px 14px}.gallery{grid-template-columns:1fr}.sheet-dialog{width:96vw;padding:18px}.ar-cards{grid-template-columns:minmax(0,1fr)}.stat-card{border-left:0;padding:0}.stat-card+.stat-card{margin-top:var(--sp-5)}.book-body{gap:var(--sp-5)}.detail-cols{gap:var(--sp-5)}}
@media(max-width:640px){.chart-grid>.aging-block,.ar-scroll{border-radius:var(--r-md)}}
@media(prefers-reduced-motion:reduce){.work-step.revealing{animation:none}.attach-zone,.gallery-card,.event-card,.row-toggle{transition:none}}
/* Office rail tabs: a pill bar under the rail heading that switches between the brief
   activity log (default) and the full books. Reuses the .segmented pill geometry already
   on the page so it reads as the same control family as the chat/email mode picker. */
/* Office rail tabs: a two-stop strip under the rail heading that switches between the brief
   activity log (default) and the full books. Two options, so a pill would sit unbalanced; instead
   the active tab is marked by a 2px brand-green underline on a shared hairline — no background fill,
   so neither tab looks heavier. The base .segmented geometry is overridden for this rail only. */
.rail-tabs{display:flex;margin:0 20px 10px;background:none;border-radius:0;border-bottom:1px solid var(--line);padding:0}
.rail-tabs button{background:transparent;border:0;padding:8px 4px;margin:0 14px 0 0;color:var(--ink-3);font-size:var(--fs-sm);border-radius:0;border-bottom:2px solid transparent;margin-bottom:-1px}
.rail-tabs button:last-child{margin-right:0}
.rail-tabs button:hover:not(:disabled){filter:none;color:var(--ink-2)}
.rail-tabs button[aria-pressed=true]{background:transparent;box-shadow:none;color:var(--money-deep);font-weight:600;border-bottom-color:var(--money-deep)}
.rail-pane{flex:1 1 auto;min-height:116px;overflow-y:auto}
.rail-pane[hidden]{display:none}
.rail-panel .rail-pane.activity{padding-left:20px;padding-right:20px;border-top:0}
.rail-panel .rail-pane.office-record{max-height:none;flex:1 1 auto;overflow-y:auto;padding-left:20px;padding-right:20px}
#activity-docs{margin-top:var(--sp-2)}
