/*
 * Deskwork's design tokens: the one file the desk, its setup page, its sign-in pages and the
 * marketing site read (gs-s4pr). Every value the product draws with belongs here, named.
 *
 * Two layers, in this order:
 *
 *   1. SEMANTIC tokens. A name says what the value is FOR, not what it looks like: `--canvas` is
 *      the page, `--surface` a card on it, `--text-muted` the quietest legible text, `--accent` the
 *      brand's darkened taupe, `--danger` a removal or a refusal. Then the scales — type, space,
 *      weight, shadow, motion — that every sheet picks its numbers from.
 *   2. The OLD names (`--ink`, `--paper`, `--cream`, `--rust`, `--moss`, …), kept as aliases of
 *      layer 1. They came from a warm palette that is long gone: `--rust` is a taupe-brown, `--cream`
 *      is white, `--rust-soft` is a cool grey and `--moss` is grey. Hundreds of rules still ask for
 *      them, so they stay until each sheet moves over (gs-s4pr step 3). New code uses layer 1.
 *
 * Literals belong in this file only. `tools/check-css-tokens.mjs` (part of `npm run verify`) counts
 * colour literals, raw font sizes, off-scale spacing, off-scale weights, literal shadows and literal
 * durations in every other sheet against `tools/css-token-baseline.json`, and fails when a count
 * goes up. It never has to go up: add the token here, then use it.
 *
 * Loaded first, before the desk's other shared sheets (`src/shared/desk-stylesheets.ts`), by
 * `office.html`, `setup.html` and the sign-in pages (`src/server/auth-pages.ts`). Moving the tokens
 * out of what was then one `office.css` changed no pixel: every name
 * resolves to the value it had.
 *
 * The site is deployed from `site/` alone, so it carries a copy, `site/tokens.css`, linked before
 * `site.css`; `npm run site:check` fails if the copy differs from this file by a byte. Edit this
 * one and copy it over. `site.css` declares no colour of its own: it reads these, and adds only its
 * system-font stack and its one-size-up display scale (gs-ujdd).
 */
:root {
  /*
   * ---- colour: the semantic layer -------------------------------------------------------------
   *
   * BrokerageBox's palette, measured off the live account rather than guessed at. See
   * docs/BROKERAGEBOX_UI_MATCH.md for where each number came from and for the three places we
   * deliberately do not match them. The desk's page is their cool grey.
   *
   *   --canvas          the page behind everything
   *   --surface         a card, a panel, a dialog, a field: the lightest surface
   *   --surface-sunken  a quiet fill inside a surface: a hovered row, a note, a field's fill
   *   --surface-strong  the darkest neutral fill: a block's heading band, a neutral badge
   *   --text            body text and headings
   *   --text-soft       the second voice: table cells, values under a label
   *   --text-muted      the quietest text that still clears 4.5:1 on every fill above
   *   --accent          the brand's taupe darkened until it can carry a word: links, the focus
   *                     ring, quiet actions
   *   --border          a rule: between rows, around a card. Decoration, not a boundary
   *   --border-strong   the edge of something you type in or press: at least 3:1 everywhere
   *   --neutral-mark    a neutral rule or dot that marks a row; never type
   */
  --canvas: #edf0f2;
  --surface: #ffffff;
  --surface-sunken: #f6f8f9;
  --surface-strong: #dde1e3;
  --text: #212529;
  --text-soft: #49545a;
  /*
   * Their neutral text is #6C757D and their meta grey is #9AA5AC. On their own page grey
   * (#EDF0F2) those are 4.14:1 and 2.3:1 — both under the 4.5:1 floor, and a walk of every text
   * node on all ten screens at 1440 and 390 found #6C757D failing in 110 places and nothing else
   * failing at all. So --text-muted is their neutral darkened until it clears.
   * --neutral-mark keeps the literal #6C757D because it is used for fills and rules, never for type.
   *
   * It has since been darkened one more step. #5c666e cleared on every surface the first walk
   * reached, but the record drawer is two clicks deep and that walk never opened it: inside it,
   * `.queue-block-head` is --surface-strong #DDE1E3, the darkest fill in the product, and #5c666e
   * is 4.46:1 there — a miss by four hundredths. #58626a is the same cool grey with real headroom:
   * 6.2:1 on white, 5.8:1 on --surface-sunken, 5.4:1 on the page, 4.73:1 on --surface-strong.
   * Contrast is part of the acceptance gate now, so this is checked on every screen rather than
   * remembered.
   */
  --text-muted: #58626a;
  /*
   * Placeholder text, and nothing else. Their meta grey, used as-is and never for type.
   *
   * Every ::placeholder rule in the office used to be the muted grey, only a step lighter than
   * --text. So an empty box with "80/20 Transaction Plan", "Rae Lindqvist" or "999.00" in it
   * looked filled in: the plan editor said "2 things to settle" under a form that looked complete.
   * A placeholder is an example, not an answer, and it has to look like one at a glance. The
   * example placeholders also say "e.g.", so the difference is not carried by colour alone.
   *
   * It was #9aa5ac, 2.51:1 on white — but a placeholder carries information ("Street, agent, client
   * or MLS number" is the only description of what Find a file searches), so it is text and owes
   * 4.5:1 (a11y audit 2026-10-03 M6). #687179 is 4.97:1 on white and 4.66:1 on --surface-sunken,
   * the fills a box has, and still visibly lighter than --text-muted.
   */
  --text-placeholder: #687179;
  /*
   * Graystone's taupe (`--brand-taupe` below) is 2.8:1 on white, which is almost certainly why
   * BrokerageBox sets its own page titles in a darkened version of the same hue. This is that
   * darkened value: 6.0:1 on white.
   */
  --accent: #6e6157;
  --border: #dee2e6;
  /*
   * Their control edge, one step darker than the rule. BrokerageBox rules a table with #DEE2E6 and
   * edges a *control* with #CED4DA, but an edge is the only thing that says where an empty box is,
   * and #CED4DA was 1.49:1 on white and 1.31:1 on the page — under the 3:1 a control's boundary
   * needs (WCAG 1.4.11, a11y audit 2026-10-03 S2). #7D878E is the same cool grey taken down to
   * 3.67:1 on white, 3.44:1 on --surface-sunken and 3.2:1 on the page. `--border` stays light: it
   * rules tables, and a rule is decoration, not a control.
   */
  --border-strong: #7d878e;
  --neutral-mark: #6c757d;

  /*
   * ---- colour: states and tones ---------------------------------------------------------------
   *
   * The badge, the callout and the danger button draw from these and nothing else. Each ink is
   * measured against its own fill at 11px/800 (the badge), which is the hardest case: neutral
   * 5.9:1, info 6.6:1, success 5.4:1, warning 5.6:1, danger 5.1:1. `--warning-ink` is the darkened
   * ochre the old `.stat-warn` used (#8a6d1f), one step darker so it clears on --surface-sunken too.
   *
   * `--danger-strong` is the danger ink for words set ON a tinted fill — a refusal's message in a
   * callout — where `--danger` is the rule, the edge and the badge. Seven sheets typed it as a
   * literal (#8a2f22) beside a hand-typed `--warning-soft`; it is named here so they can stop.
   */
  --success: #356849;
  --success-strong: #2b4f38;
  --danger: #a34435;
  --danger-strong: #8a2f22;
  --neutral-soft: var(--surface-strong);
  --neutral-ink: var(--text-soft);
  --info-soft: #e5edf4;
  --info-ink: #2f5577;
  --success-soft: #e4ebe1;
  --success-ink: var(--success);
  --warning-soft: #fdf3d4;
  --warning-ink: #7a5d12;
  --danger-soft: #f8e7e4;
  --danger-ink: var(--danger);

  /*
   * ---- colour: near-twins, named so that moving a sheet onto tokens changes no pixel ------------
   *
   * Each of these is a hand-typed neighbour of a tone above, kept at its own value because gs-s4pr
   * step 3 moves literals without moving pixels. Collapsing one onto its twin is a visible change
   * and its own decision; until then it is named here and nowhere else.
   *
   *   --success-faint / -ink   the Checklists "saved" notice; twin of --success-soft and -strong.
   *                            9.5:1
   *   --warning-faint          the paler warning fill on Checklists ("what is wrong", the files a
   *                            change touches, its confirm box); twin of --warning-soft
   *   --warning-line           the 1px edge of a --warning-faint block. Decoration, not an edge
   *   --warning-ink-light      the old `.stat-warn` ochre that --warning-ink replaced: 4.9:1 on
   *                            white, 4.6:1 on --surface-sunken, so never on anything darker
   *   --caution-soft / -ink    "files with no dates at all" on the desk home (the calendar's twin,
   *                            `.cal-gaps-bare`, draws the same two). Amber, not red: a file
   *                            with no dates is not late. 7.4:1. The ink is also the words of
   *                            the accent-edged notes on Closing and Plans that are deliberately
   *                            not red (fee disclosures, cap reached, a locked plan, the plan's
   *                            summary); 7.7:1 on --surface-sunken
   *   --note-ink               the words of a quiet note on an --accent edge (year to date, no
   *                            referrals yet): the old rust ink. 7.6:1 on white
   *   --info-quiet             the product's blue-grey: a statement's "shared with" line, an
   *                            in-flight card's stripe; twin of --info-ink. 7.9:1 on white
   *   --border-faint           the rule between money lines; a step lighter than --border
   *   --border-hairline        the rule between a closing's money lines (Closing's settlement
   *                            sheet); a step lighter again, 1.13:1 on white. Decoration only
   *   --accent-faint           the warm fill behind an --accent-edged flag on a plan card
   *                            (somebody on it pays the office nothing); --text is 13.8:1 on it
   *   --danger-faint           the paler danger fill behind a --danger-edged flag on a plan card
   *                            (agents with no cap year, so none of their deals can be priced);
   *                            twin of --danger-soft
   */
  --success-faint: #eef3ee;
  --success-faint-ink: #24452f;
  --warning-faint: #fefaf0;
  --warning-line: #e8dca8;
  --warning-ink-light: #8a6d1f;
  --caution-soft: #fbf3e4;
  --caution-ink: #7a3f26;
  --note-ink: #7a4632;
  --info-quiet: #3c5464;
  --border-faint: #e2e6eb;
  --border-hairline: #eef1f3;
  --accent-faint: #f9f1ea;
  --danger-faint: #faf0ee;

  /*
   * ---- colour: how soon a file with missing dates closes ---------------------------------------
   *
   * The stripe on a row in "Missing dates" (calendar-risk.ts: urgent, soon, later, undated). Warm to
   * quiet, and deliberately not red: red is spoken for by a date that has already passed. Marks
   * only, never type. The calendar's rows (`.cal-gap-row` in calendar.css) draw the same two.
   */
  --urgent-mark: #b5651d;
  --soon-mark: #c68a3e;

  /*
   * The success tone's two extra steps, the green twins of `--danger-strong` (gs-vu03):
   * `--success-strong` (beside `--success` above) is green words set ON `--success-soft` (a "saved"
   * notice's sentence, the notes on a paid referral's figure), `--success-edge` the hairline round
   * that fill. Six sheets
   * typed the first (#2b4f38); the edge was typed two ways (#a9bda9, #b6c6b4) and is one now.
   *
   * `--partial-soft` / `--partial-ink` are the slate tone for "some, not all": a notification
   * group with some of its rules on. Between the green of "all" and the grey of "none", and not a
   * warning.
   */
  --success-edge: #b6c6b4;
  --partial-soft: #e2e6eb;
  --partial-ink: #465a6e;

  /*
   * The office-fee status tags on Plans (gs-n4y0): paid, part paid, overpaid. Unpaid is the
   * neutral tag. Paid is the success tone with a slightly greener edge than --success-edge; part
   * paid is ochre, not the warning yellow, because a part payment is progress rather than a
   * problem; overpaid is violet so it can never be read as either. Each ink on its own fill:
   * paid 7.6:1, part paid 6.8:1, overpaid 9.0:1. The edges are decoration, the words carry it.
   */
  --fee-paid-edge: #b9cdb9;
  --fee-part-soft: #f6eeda;
  --fee-part-ink: #6a4c14;
  --fee-part-edge: #d9c6a4;
  --fee-over-soft: #efe7f5;
  --fee-over-ink: #4c3163;
  --fee-over-edge: #cfc0dd;

  /*
   * `--rule-quiet` is the left rule of a quiet, tinted notice that is not a problem: the Tracker's
   * "not connected" line and the queue's unfiled line and its slot buttons. Those rules asked
   * for a `--line-strong` that was never defined, so this warm grey, their fallback, is what they
   * have always drawn (gs-vu03).
   * `--scrim` covers the page behind the phone rail's "More" sheet: lighter than a dialog's.
   */
  --rule-quiet: #c9c3b8;
  --scrim: rgba(24, 27, 25, 0.4);
  /*
   * ---- colour: office.css's own (gs-q3g4) -----------------------------------------------------
   *
   * The shared sheet's hand-typed colours, named where they are used so moving office.css onto
   * tokens changed no pixel (it was one sheet then; since gs-s8in they sit in the sheets it was cut
   * into). Most are leftovers of the old warm palette on one control each; collapsing one onto its
   * nearest tone above is a visible change and its own decision.
   *
   *   --neutral-mark-hover   the header's Sign in pill under the pointer: --neutral-mark a step darker
   *   --danger-wash          the fill under a hovered Sign out; a step off --danger-faint
   *   --danger-tint          the fill behind a commission's error line (left rule --danger)
   *   --draft-fill           the "you have a draft" banner's green wash (edge: --success at 24%)
   *   --field-hover-edge     a New file field's edge under the pointer
   *   --preview-edge / -fill the found-property card on New file: a pale green block
   *   --mark-empty           a document slot's dot when nothing is filed yet (hollow when optional)
   *   --mark-edge            the ring of a checklist preview's empty box; a hair off --mark-empty
   *   --mark-review          a document slot's dot when a filed document waits on the office
   *   --approve-ink          the words on a slot's Approve; a deeper --success. 6.6:1 on white
   *   --next-ink             the "Next office action" label in the record drawer. 6.1:1 on white
   *   --attention-ink        the small print under an attention check. 6.3:1 on white
   *   --drop-edge / -fill    the dashed edge and the warm wash of a drop-a-file box
   *   --caution-edge         the edge of a file in "files with no dates at all" (on --caution-soft)
   */
  --neutral-mark-hover: #5c646b;
  --danger-wash: #fbeeea;
  --danger-tint: #f6e4dc;
  --draft-fill: rgba(225, 235, 223, 0.72);
  --field-hover-edge: #aaa294;
  --preview-edge: #bdcbbb;
  --preview-fill: #edf2e9;
  --mark-empty: #bab4aa;
  --mark-edge: #b9b2a6;
  --mark-review: #d69a55;
  --approve-ink: #1f6b3a;
  --next-ink: #855d49;
  --attention-ink: #7e6253;
  --drop-edge: #bcb3a4;
  --drop-fill: rgba(244, 240, 231, 0.62);
  --caution-edge: #e8dcc2;
  /*
   * The Listings screen (`.idx-*`): a photo-led page with its own small palette. `--photo-ink` is
   * the near-black every caption, badge and close button over a photo is drawn in, at the opacity
   * the rule says. The map is a drawn stand-in, not tiles: a sage ground, a grid, one ring.
   *
   *   --photo-fallback        the far corner of the gradient a card shows before its photo loads
   *   --card-hover-edge       a listing card's edge under the pointer
   *   --listing-pending-ink / --listing-open-ink   a card's "Pending" and "Open house" words
   *   --listing-text          a listing's description in its detail panel
   *   --map-ground / -canvas / -grid / -ring / -label   the drawn map
   *   --marker-hover / --marker-pending   a map pin under the pointer, and a pending listing's pin
   */
  --photo-ink: #202321;
  --photo-fallback: #d6ddcf;
  --card-hover-edge: #bdb3a3;
  --listing-pending-ink: #8b4b31;
  --listing-open-ink: #365977;
  --listing-text: #5f615c;
  --map-ground: #dfe5d9;
  --map-canvas: #e3e8dd;
  --map-grid: rgba(100, 124, 96, 0.12);
  --map-ring: rgba(75, 99, 75, 0.14);
  --map-label: rgba(60, 75, 60, 0.22);
  --marker-hover: #59504a;
  --marker-pending: #9c5e43;
  /*
   * The calendar's six date types (`--cal-accent` in calendar.css): six hue families, each clearing
   * 4.5:1 for the white initial set on it. Closing is NOT --danger, though it is near it: red is
   * spoken for by a date already missed, and Late has to stay the one marker that reads as red.
   * `--today-fill` and `--closing-fill` are Closing's tint (today's cell, a closing's chip), and
   * `--heat-1..4` the Year view's four steps from a quiet month to a busy one, in the same hue.
   */
  --date-closing: #9c5334;
  --date-listing: #2f6b45;
  --date-possession: #2f5a8a;
  --date-acceptance: #6f3f8a;
  --date-inspection: #0f6a70;
  --date-offer: #8a5a00;
  --today-fill: #fdf2eb;
  --closing-fill: #f8e9df;
  --heat-1: #f8efe9;
  --heat-2: #f2dfd2;
  --heat-3: #ebcdb9;
  --heat-4: #e3bba0;
  /*
   * `--alarm-*` is the sync bar's orange: the one alarm in the product (somebody's work is about to
   * be lost), on a palette with no alarm colour. Near --danger but not it; collapsing the two is a
   * visible change and its own decision. --alarm-ink on --alarm-soft is 8.3:1.
   * `--dialog-scrim` covers the page behind a drawer, a confirm and the command palette.
   * `--mask-opaque` is not a colour anyone sees: it is the solid end of an alpha mask (a chip row
   * that fades out at its edge), where only the opacity counts.
   */
  --alarm-edge: #b54708;
  --alarm-ink: #7a2e0e;
  --alarm-soft: #fff4ed;
  --alarm-hover: #ffe9db;
  --dialog-scrim: rgba(33, 37, 41, 0.45);
  --mask-opaque: #000;

  /*
   * ---- the primary action ---------------------------------------------------------------------
   *
   * The one thing a panel is for: ink fill, white type, the softer ink on hover (`.btn-primary` in
   * components.css). Named so the site, the sign-up pages and /setup can all draw the same one
   * (gs-s4pr step 5) instead of three different ones.
   */
  --primary: var(--text);
  --primary-ink: var(--surface);
  --primary-hover: var(--text-soft);

  /*
   * ---- shadows: four, and nothing between them ------------------------------------------------
   *
   *   --shadow-sm      a card on the page. Theirs is a close, tight card shadow, not a deep
   *                    page-level one.
   *   --shadow-md      something raised off a card: a hovered tile, a tray, a sticky bar.
   *   --shadow-lg      something that opens over the page beside what opened it: a menu, a
   *                    popover, a suggestion list.
   *   --shadow-dialog  a card that floats over the page in the middle of the window: the shortcuts
   *                    sheet, My email, a confirm.
   *
   * `--control-shadow` (below) is not a level: it is part of a control's edge.
   */
  --shadow-sm: 0 2px 3px rgba(55, 63, 67, 0.16);
  --shadow-md: 0 4px 12px rgba(55, 63, 67, 0.12);
  --shadow-lg: 0 8px 24px rgba(55, 63, 67, 0.16);
  --shadow-dialog: 0 18px 48px rgba(33, 37, 41, 0.28);

  /*
   * ---- spacing: a 4px scale -------------------------------------------------------------------
   *
   * Padding, margins and gaps come from these ten steps. A walk of the sheets found 43 distinct
   * pixel values in them — 10, 11, 13, 14, 15, 18 beside 8, 12, 16 — which is why two panels that
   * should line up never quite do. Sheets snap onto the scale one at a time (gs-s4pr step 4).
   */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 32px;
  --space-8: 40px;
  --space-9: 48px;
  --space-10: 64px;

  /*
   * ---- weights: four ---------------------------------------------------------------------------
   *
   * The sheets used eight (400, 500, 600, 650, 700, 750, 800, 850). Raleway draws 650 and 750 as
   * the nearest real weight anyway, so the in-between values only made two screens disagree.
   */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  /*
   * ---- motion ---------------------------------------------------------------------------------
   *
   * Three durations and two curves. `--dur-fast` for a colour or border change under the pointer,
   * `--dur` for something that moves or fades in, `--dur-slow` for something that crosses the
   * screen. Reduced motion is handled once, in base.css, by turning transitions off.
   */
  --dur-fast: 120ms;
  --dur: 180ms;
  --dur-slow: 280ms;
  --ease: ease;
  --ease-out: ease-out;

  /*
   * ---- the old names --------------------------------------------------------------------------
   *
   * Aliases of the semantic layer, for the rules that still ask for them. Same values; a re-skin
   * (or a dark theme) changes the layer above and these follow.
   */
  --ink: var(--text);
  --ink-soft: var(--text-soft);
  --muted: var(--text-muted);
  --placeholder: var(--text-placeholder);
  --paper: var(--canvas);
  --paper-deep: var(--surface-strong);
  --cream: var(--surface);
  --rust: var(--accent);
  --rust-soft: var(--surface-sunken);
  --moss: var(--neutral-mark);
  --line: var(--border);
  --control-line: var(--border-strong);
  --error: var(--danger);
  --error-soft: var(--danger-soft);
  --shadow: var(--shadow-sm);
  --dialog-shadow: var(--shadow-dialog);

  /*
   * ---- type faces -----------------------------------------------------------------------------
   *
   * They run one family for everything. So do we now — the Georgia headings were the single loudest
   * signal that this was not the product she uses. `--serif` keeps its name because ninety rules
   * ask for it; it is no longer a serif.
   */
  --serif:
    Raleway, "Source Sans Pro", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --sans:
    Raleway, "Source Sans Pro", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;

  /*
   * Graystone's own, sampled from the logo she sent rather than guessed at. The taupe is the brand
   * colour; it is only 2.8:1 on white, which is almost certainly why BrokerageBox sets its own page
   * titles in a darkened version of the same hue — that darkened value is `--accent` above. So the
   * taupe is for rules, fills and marks, never for text.
   */
  --brand-charcoal: #444544;
  --brand-taupe: #9d8b7d;
  --brand-grey: #808283;
  /*
   * The taupe one step darker, for a mark that has to be SEEN rather than decorate: the site's list
   * bullets. The taupe itself is 2.86:1 on the page, under the 3:1 of WCAG 1.4.11; this is 3.69:1
   * on the page and 4.22:1 on white, and at bullet size it reads as the same colour.
   */
  --brand-taupe-ink: #8a7869;
  /*
   * Type on the charcoal band (deskworkapp.com's privacy card): the paragraph, the small print and
   * the numbered marks, light enough to read on --brand-charcoal and nowhere else.
   */
  --on-charcoal-soft: #d8dcda;
  --on-charcoal-muted: #cbd0cd;
  --on-charcoal-taupe: #e1d2c6;
  /*
   * The threshold: the warm paper of deskworkapp.com's hero (`site/site.css`, `.landing .hero`), for
   * the pages somebody sees before they sign in (`setup.css`, "The sign-in page"), so the walk from
   * the product site to a desk's sign-in card reads as one product (gs-r54k). The desk itself stays
   * on its cool grey. --text-muted is 5.1:1 on the deepest of the three, --accent 4.9:1.
   */
  --threshold-light: #fbfaf8;
  --threshold: #f5f2ee;
  --threshold-deep: #eee8e2;
  /* Rules on the threshold: between rows of a list (`--threshold-rule`) and round a card on it
   * (`--threshold-edge`). The warm counterparts of --border. */
  --threshold-rule: #d5cec8;
  --threshold-edge: #ded6d0;
  /*
   * Google's own sign-in button, light theme, from its branding guidelines: the outline (4.6:1 on
   * white, so the button's edge clears 3:1) and the label. Used by that one button and nothing else.
   */
  --google-stroke: #747775;
  --google-ink: #1f1f1f;
  /*
   * Shapes by role (docs/DESIGN.md "Shapes"). The 160px pill was the universal radius, so a text
   * box, a status tag and a button were all the same lozenge: a 92px textarea turned into an oval,
   * an 800px name box on Plans read as a button, and a grey tag looked like something to press.
   * One shape per role now, and the shape says what a thing is:
   *
   *   --pill          something you PRESS: a button, a filter chip, a segment. Nothing else.
   *   --radius-field  something you TYPE or CHOOSE in: input, select, textarea, money box.
   *   --radius-tag    something you READ: a status or metadata tag. Square-ish, so it stops
   *                   looking clickable.
   *   --card-radius   a card or a panel; --block-radius a small block inside one.
   */
  --pill: 160px;
  --radius-field: 8px;
  --radius-tag: 4px;
  --card-radius: 16px;

  /*
   * Their control shadow, measured off the live account on 2026-09-14 and re-measured on the
   * roster screen: every select, search field and button BrokerageBox draws carries
   * `#DDE1E3 0 1px 2px`, the same close shadow as the counter lozenges. Ours had the pill shape but
   * neither their darker edge (`--border-strong`) nor this, which is why a row of our controls read
   * as flatter and more washed-out than theirs at a glance. Part of a control's edge, not a shadow
   * level.
   */
  --control-shadow: 0 1px 2px var(--surface-strong);
  /*
   * The lift under the primary pill on the pages before sign-in (deskworkapp.com's calls to act and
   * the sign-in and signup cards' `.auth-pill`): the same pill, raised the same amount on every door.
   * Part of that control, not a shadow level.
   */
  --cta-shadow: 0 6px 18px color-mix(in srgb, var(--brand-charcoal) 13%, transparent);
  /*
   * Two edges that are drawn with box-shadow but are not shadow levels either. `--start-rule`: the
   * rust rule down a row's first edge that marks it (a line still owed on a file just filed, a
   * chosen row); inset, so it takes no room. `--bleed`: a spread that paints a sticky bar's ground
   * out to the window's edges without making the page wider (`box-shadow: var(--bleed) <colour>`
   * with `clip-path: inset(0 -100vmax)` so it stays the bar's height).
   */
  --start-rule: inset 3px 0 0 var(--rust);
  --bleed: 0 0 0 100vmax;

  /* The switch's knob: a white disc lifted off its track. Part of the control, like
   * `--control-shadow`, not a shadow level (gs-vu03). */
  --knob-shadow: 0 1px 3px rgba(55, 63, 67, 0.35);

  /* The shadow a docked bar casts UP onto the page it sits under (Closing's totals bar, gs-n4y0).
   * Every level above falls downward, so none of them fits; part of the dock, not a level. */
  --dock-shadow: 0 -8px 20px rgba(55, 63, 67, 0.08);

  /*
   * Edges, rings and rules drawn with box-shadow in the shared sheets (gs-q3g4). None is a shadow level:
   * each is part of the thing it outlines, and takes no room.
   *
   *   --warning-outline   the outline tag (`.badge-warning-outline`)
   *   --selected-ring     a pressed counter tile
   *   --ink-ring          the calendar's today and chosen day
   *   --selected-rule     the rule down a chosen palette row's first edge (`--start-rule` in ink)
   *   --focus-edge        a money box's or a New file field's focused edge, one step past its border
   *   --focus-underline   the command palette's search line when it has focus
   *   --avatar-ring       the white gap round an avatar in a stack of faces
   *   --late-ring         the halo round a late date's dot: --danger at 34%
   *   --live-halo         the halo round the Listings "live" dot: --success at 10%
   *   --edge-top / --edge-bottom          a rule just above / below a block, outside it
   *   --edge-bottom-inset                 a rule along a sticky table heading's foot, inside it
   *   --pin-bleed / --pin-bleed-narrow    the record drawer's pinned head: its white ground and its
   *                                       foot rule carried out over the drawer's side padding
   *                                       (42px, 20px on a phone)
   */
  --warning-outline: inset 0 0 0 1px var(--warning-ink);
  --selected-ring: inset 0 0 0 1px var(--selected-line);
  --ink-ring: inset 0 0 0 1px var(--ink);
  --selected-rule: inset 3px 0 0 var(--selected-line);
  --focus-edge: 0 0 0 1px var(--focus-color);
  --focus-underline: inset 0 -2px 0 var(--focus-color);
  --avatar-ring: 0 0 0 2px var(--surface);
  --late-ring: 0 0 0 2px color-mix(in srgb, var(--danger) 34%, transparent);
  --live-halo: 0 0 0 4px color-mix(in srgb, var(--success) 10%, transparent);
  --edge-top: 0 -1px 0 var(--border);
  --edge-bottom: 0 1px 0 var(--border);
  --edge-bottom-inset: inset 0 -1px 0 var(--border);
  --pin-bleed:
    -42px 0 var(--surface), 42px 0 var(--surface), 0 1px var(--border), -42px 1px var(--border),
    42px 1px var(--border);
  --pin-bleed-narrow:
    -20px 0 var(--surface), 20px 0 var(--surface), 0 1px var(--border), -20px 1px var(--border),
    20px 1px var(--border);

  /*
   * Soft drops in the shared sheets that sit between the levels and are not within a hair of any, kept at
   * their own values (gs-q3g4): collapsing one is a visible change. Each is a long, faint fall
   * under a panel that rests on the page.
   *
   *   --form-shadow      a New file step and its documents column
   *   --suggest-shadow   the address suggestions under New file's address box
   *   --tray-shadow      the Listings advanced-filters tray
   *   --listing-shadow   a listing card at rest (it takes --shadow-sm under the pointer)
   *   --drawer-shadow    a side drawer, cast to its left over the page
   *   --marker-shadow    a map pin, in --photo-ink
   *   --photo-badge-shadow   a badge on a listing photo: --shadow-md's fall, in --photo-ink
   *   --chip-hover-shadow    a calendar chip under the pointer: --shadow-md's fall, a little fainter
   *   --palette-shadow   the command palette: deeper and softer than --shadow-dialog
   */
  --form-shadow: 0 12px 35px rgba(55, 63, 67, 0.04);
  --suggest-shadow: 0 12px 25px rgba(55, 63, 67, 0.08);
  --tray-shadow: 0 12px 28px rgba(55, 63, 67, 0.05);
  --listing-shadow: 0 12px 30px rgba(55, 63, 67, 0.045);
  --drawer-shadow: -18px 0 48px rgba(33, 37, 41, 0.18);
  --marker-shadow: 0 4px 12px color-mix(in srgb, var(--photo-ink) 25%, transparent);
  --photo-badge-shadow: 0 4px 12px color-mix(in srgb, var(--photo-ink) 12%, transparent);
  --chip-hover-shadow: 0 4px 12px rgba(55, 63, 67, 0.09);
  --palette-shadow: 0 24px 64px rgba(33, 37, 41, 0.24);

  /*
   * Their block radius. A walk of every painted element on their roster screen returns exactly
   * nine radii, and only four of them are shapes rather than circles or split-pill halves: 160px
   * for a control, 99px for a nav pill, 16px for a card — and **6px, which is by far the most
   * used of the lot (55 elements against the card radius's one)**. It is their badge radius, which
   * this project had already matched, but it is also every small block, note and inner tile they
   * draw.
   *
   * Ours had fourteen distinct radii scattered across the twelve sheets — 4, 5, 6, 7, 8, 9, 10,
   * 11, 12, 13, 14 — which is the kind of thing no single screen shows you and every pair of
   * screens does. Collapsing them onto their vocabulary is most of what makes two screenshots look
   * like one product.
   */
  --block-radius: 6px;

  /*
   * ---- their data grid -----------------------------------------------------------------------
   *
   * BrokerageBox is a dense grid, not a wall of cards, and these are the numbers that make a list
   * read as one. Measured off the live account: heading row 12px / 700 / uppercase /
   * letter-spacing .15px over a 1px #DEE2E6 underline; cells 14px #49545A; rows separated by the
   * same rule with no gutter between them.
   *
   * One deliberate departure. Their heading ink is #797979, which is 4.35:1 on white and misses
   * the 4.5:1 floor this project holds — at 12px it does not qualify for the large-text
   * exception either. So the *treatment* is theirs and the ink is --text-muted, which clears on every
   * surface in the office. Recognisable at a glance, and legible on inspection.
   */
  --grid-head-size: var(--fs-small);
  --grid-head-track: 0.15px;
  /* Their row rhythm. `--row-pad-y` is what turns our cards-with-air into their grid: at 14px
   * type it puts an ordinary one-line row at 50px, against the 78-84px ours used to sit at. */
  --row-size: var(--fs-body);
  --row-pad-y: 9px;
  /* A queue cell's ink: their table text, the same value as --text-soft on screen, but its own name
   * because print darkens --text-soft and has never darkened the queue's cells (gs-q3g4). */
  --grid-cell: #49545a;
  --row-pad-x: 16px;

  /*
   * ---- the type scale ------------------------------------------------------------------------
   *
   * Ten steps, and nothing off them.
   *
   * These are not invented. A walk of every painted text node on every screen at 1440 returned
   * twenty distinct sizes, and the counts say plainly which ones are the product and which ones are
   * somebody's afternoon: 11px (271 declarations), 12px (171), 13px (108), 14px (42), 15px (22),
   * 21px (13), 17px (6), 19px (8), 26px (8), 30px (2) — against 14.4px used once, 14.5px once,
   * 13.5px twice, 12.5px seven times, 11.5px four times, and 22/23/24/25/28/29/34/38 one to three
   * times each. The second list is the same handful of sizes as the first, missed by a pixel or
   * two, on a different screen each time.
   *
   * A pixel is not visible on one line and is very visible in a column of them: it is why a heading
   * on one screen and the same heading on the next never quite sat together. Every off-scale value
   * has been snapped to its nearest step here — downward wherever the choice was even, so nothing
   * grew into a cell it used to fit.
   *
   * The floor is `--fs-meta`. Nothing in this product is set below 11px, on any screen, ever; the
   * acceptance gate fails a screen that tries.
   */
  --fs-meta: 11px; /* badges, provenance lines, the smallest legible thing here */
  --fs-small: 12px; /* grid headings, the densest meta lines */
  --fs-label: 13px; /* a file row's facts line, a field's sentence-case label (gs-p54j) */
  /*
   * Prose, and so 14px — the same number as --fs-body since the 2026-10-03 UI audit, which measured
   * 68% of the words on the tracker and 76% on Listings at 12px or smaller. A lead paragraph is
   * something somebody reads, not a label, and reading is what 14px is for. The name stays because
   * a hundred and twenty rules ask for it; its role is "prose inside a panel", not "one step down".
   * 11px and 12px are for labels, badges and meta lines only (docs/DESIGN.md).
   */
  --fs-sub: var(--fs-body); /* a value under a label, a lead paragraph inside a panel */
  --fs-body: 14px; /* body text and table cells — the same number as --row-size */
  --fs-lead: 15px; /* the emphasised value in a row */
  --fs-panel: 17px; /* a panel's own title */
  --fs-block: 19px; /* a block heading inside a panel */
  --fs-figure: 21px; /* a total, a number meant to be read across the room */
  --fs-stat: 28px; /* the figure in a counter tile (`.stat-value`), and nothing else */
  --fs-title: 26px; /* a screen title */
  --fs-display: 30px; /* the one number a screen exists to show */
  /*
   * A screen title, once.
   *
   * Nine stylesheets each wrote `clamp(22px, 2vw, 26px)` for their own `h1`, which is nine places
   * for the product's largest recurring line to drift. The bounds are now scale steps rather than
   * 22, which was on no scale at either end of it.
   */
  --fs-screen-title: clamp(var(--fs-figure), 2vw, var(--fs-title));
  /* Section headings inside a panel. App scale — the 25px panel titles were display scale for a
   * label that sits above a list of fifty rows. Kept under its old name because ninety rules ask
   * for it; it is the scale's `--fs-panel` step and nothing else. */
  --panel-title-size: var(--fs-panel);
  /*
   * An eyebrow's tracking, once.
   *
   * `.rst-eyebrow`, `.pln-eyebrow`, `.tpl-eyebrow`, `.trk-eyebrow`, `.ref-eyebrow`, `.cls-eyebrow`
   * and `.dsh-eyebrow` each declared 0.16em; the shared `.eyebrow` in office.css declared 0.18em,
   * and both the calendar and the notifications screen then carried a rule whose entire job was to
   * put the shared one back to 0.16em on that screen. Two of the nine had already noticed. This is
   * the number all nine were reaching for.
   */
  --eyebrow-track: 0.16em;
  /*
   * The other uppercase label: the quiet one.
   *
   * `.rail-group`, `.hdr-menu-label` and the header's brokerage divider are the same thing as
   * each other — muted, 11px, 700, uppercase — and were already agreed at 0.12em on all of them. The token does not change any of them; it means the fifth one is written down
   * somewhere instead of guessed at, which is how the eyebrow above ended up with five values.
   */
  --label-track: 0.12em;
  /*
   * How far a header menu's contents sit in from its edge: the label over a menu, and the bell's
   * tabs, rows and buttons (gs-55gi). 10px, as `.hdr-menu-label` and `.hdr-alert` already draw it,
   * so the inbox lines up with them.
   */
  --menu-inset-x: 10px;
  /*
   * And the third: the small uppercase label that is not a heading at all.
   *
   * Badges, statuses, column tags, the word over a figure — one component, drawn on
   * every screen, and a walk of the twelve stylesheets found it tracking at 0.03em, 0.04em, 0.05em,
   * 0.06em, 0.07em, 0.08em, 0.1em and 0.13em. Eight values, none of them ever chosen against the
   * others, spread so that no two screens set a badge the same way. 0.05em is the one thirteen of
   * them had already agreed on, so it is the one kept.
   */
  --caps-track: 0.05em;

  /*
   * ---- the spacing this product repeats -------------------------------------------------------
   *
   * Three components are drawn on nearly every screen, and each had been given its padding
   * separately on each of them. Measured in the browser across twenty-three screens: badges came
   * back with nine different paddings (5px 10px, 5px 11px, 6px 10px, 6px 8px, 5px 7px, 6px 11px,
   * 6px 12px, 4px 11px, 5px 6px), screen buttons with six (11px 16px on 149 of them, then 11px
   * 20px, 10px 16px, 11px 15px, 10px 13px, 6px 12px), and text fields with five (11px 12px on 36,
   * then 10px 12px, 10px 13px, 13px 14px, 10px 14px).
   *
   * In each case the most-used value is the one kept, so the token is what most of the product was
   * already doing rather than a new opinion about it.
   */
  --pad-badge: 5px 10px;
  --pad-button: 11px 16px;
  /* The end inset of a field. A select widens it (components.css) so its disclosure
   * arrow, drawn inset from the edge, does not cover the last letters. */
  --pad-field-end: 12px;
  --pad-field: 11px var(--pad-field-end) 11px 12px;

  /*
   * How tall a control is (gs-y1gg). Every button and chip was a 44px pill at every width, so a
   * document row with four of them read as a toolbar. 44px is the touch floor, and a desk is not
   * touched: a button is 36px there, a small one (an action beside a row) 32px, and both are 44px
   * at 640px and under. The type inside is `--fs-label`.
   */
  --control-h: 36px;
  --control-h-sm: 32px;
  --control-h-touch: 44px;

  /*
   * ---- interaction states, once (docs/DESIGN.md) ----------------------------------------------
   *
   * The 2026-10-03 audit found the global focus ring at `3px rgba(110,97,87,.35)` — about 1.6:1
   * against the page, which fails WCAG 1.4.11 — while other screens drew 2px rust, 2px ink or
   * nothing. And "selected" was drawn four ways: a black chip, a brown segment, a white pill and a
   * dark card border. One of each now, named:
   *
   *   --focus-ring    2px solid --accent: 6.0:1 on white, 5.2:1 on the page, 4.5:1 on --surface-strong.
   *                   Drawn OUTSIDE the control (`--focus-offset`), so it is measured against the
   *                   surface behind it, never against a dark button's own fill.
   *   --selected-*    the toggle that is on: ink fill, cream text. Announced by `aria-pressed` /
   *                   `aria-selected`, never by a class alone — `markPressed` in `ui.ts` sets the right one.
   */
  --focus-color: var(--accent);
  --focus-ring: 2px solid var(--focus-color);
  --focus-offset: 2px;
  --selected-bg: var(--text);
  --selected-ink: var(--surface);
  --selected-line: var(--text);
  /* An unavailable control is the same control, faded: see "One disabled look" in components.css. Ink
   * text at this opacity is about 4.2:1 on white — still readable, plainly not live. */
  --disabled-opacity: 0.6;
  /* The width of a badge in a column of them (`.badge-col`): "Closing soon" and "Needs review"
   * fit at 11px/800 with room; "Paid & archived" is allowed past it. */
  --badge-col-w: 120px;

  /*
   * Numbers. Money and counts are set in tabular (equal-width) figures so a column of dollars lines
   * up digit for digit, and never in monospace — the tracker had eleven rules doing that, which put
   * a typewriter face beside every other figure in the product. `--mono` is for identifiers a person
   * might copy (an MLS number, a file id), not for amounts.
   */
  --num: tabular-nums;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /*
   * ---- the screen rhythm ----------------------------------------------------------------------
   *
   * Three distances decide whether two screens look like the same product before a single word is
   * read: the air above a screen's heading block, the air under it before the first panel, and
   * where a screen's content stops at the bottom. All three were per-screen literals.
   *
   * Measured: the air above the heading was 40px on the Roster, the Dashboard, Closing, Checklists,
   * Plans, Referrals, the Tracker and Notifications (an 18px mount padding plus a 22px heading
   * margin), 24px on Transactions and the Calendar, 36px on the listing search and 24px on intake.
   * The broker-owner's words for the 40px version were "too much padding above it" — he was
   * comparing it, without knowing the numbers, against the 24px he sees on Transactions all day.
   *
   * Under the heading it was the mirror image: 14px on eight screens, 16px on the listing search,
   * 18px on the Tracker — and 14px is not enough air to separate a screen's title from the first
   * row of its data, which is the other half of the same complaint ("the section below needs more
   * padding").
   *
   * So: 24px above (18 + 6, which is what Transactions already does), 22px below, everywhere. The
   * heading block moves up and the content moves down, which is the change he asked for, made once
   * for every screen rather than once for his.
   */
  --screen-pad-top: 18px;
  --screen-pad-bottom: 56px;
  --heading-mt: 6px;
  --heading-mb: 22px;
  /*
   * One left edge inside a panel.
   *
   * Measured on every panel on eleven screens, in the browser, as "how many different x positions
   * does this panel's own content start at": nearly all of them answered **17 and 19**. 19 is a
   * panel heading — 18px of padding over the 1px border — and 17 is the row beneath it, which takes
   * `--row-pad-x` at 16px. Two pixels, on every panel in the product, between the title of a list
   * and the list. Nothing overlaps and nothing is cut off; it simply never quite lines up, which is
   * the thing the broker-owner was looking at when he said the roster's rows needed better
   * alignment with each other.
   *
   * It is `--row-pad-x` and not 18px because 16 is the number that was measured off the live
   * BrokerageBox account for a row, and because moving the headings in gives the content four more
   * pixels rather than taking four away — the direction that cannot introduce a truncation.
   */
  --panel-pad-x: var(--row-pad-x);

  /*
   * ---- the application chrome -----------------------------------------------------------------
   *
   * The header is `position: sticky` and the rail is `position: fixed`, so the height the rail
   * starts below is not something the two can each guess at: the rail's first 20px were sitting
   * underneath the header because 72 and 92 were written in different files. One number, named,
   * used by both — and by every screen that needs to scroll something under the chrome.
   */
  --header-h: 72px;
  /*
   * Everything stuck to the top of the window, together.
   *
   * On a wide screen that is the header alone, because the rail runs down the left. On a phone the
   * rail lies down into a strip directly beneath the header, so the chrome is twice as deep — and
   * anything that needs to clear the chrome (scrolling something into view, parking a sticky panel
   * heading) has to ask for the whole of it, not for the header. `rail.css` raises this where the
   * rail lies down, next to the rule that makes it happen.
   */
  --chrome-h: var(--header-h);
  /* Their sidebar is 250px, re-measured today off the live roster screen. Ours had been trimmed to
   * 236 on the grounds that our labels are shorter — but the nav item's own inset is 16px, theirs,
   * and taking 14px off the rail while giving 8px back to the padding left the longest labels
   * ("Find a listing", "Notifications") closer to eliding than hers ever get. It is also simply
   * the number they use. It is the rail's width plus the page gutter that `main` has to clear, so
   * both come from here. */
  --rail-w: 250px;
  --gutter: 24px;
}

/*
 * Less air above the title on a phone.
 *
 * Up to 780px a screen's heading used to take a 28px top margin instead of 6
 * (`--heading-mt-narrow`, and 20-34px of a screen's own on the intake and the listing search), for
 * a top bar that wrapped onto two lines over the title. Nothing above a title wraps any more — the
 * screen names are the bar at the bottom (rail.css) — but the margin stayed: the title sat 52px
 * under the header at 390, a tenth of the first screen spent on nothing, and the first record went
 * below the fold. On a phone the title now sits 18px under the header (12px of mount padding plus
 * the title's own 6px margin); on a desktop it is 30, unchanged.
 */
@media (max-width: 780px) {
  :root {
    --screen-pad-top: 12px;
    --heading-mt: 0px;
  }
}

/* ---------------------------------------------------------------- *
 * The stacking order, named once
 *
 * Four things on this page overlap, and three of them used to pick a number locally: the rail chose
 * 30, the header 40, the record drawer 30, the listing overlay 20. So a modal scrim covered the
 * page and NOTHING ELSE — the header and the whole left rail floated above it, lit, while the page
 * behind them went dark. On the client's own screen that read as a rendering fault: every rail label
 * greyed out against the scrim while the active pill, which has an opaque white background, stayed a
 * bright lozenge in the middle of it.
 *
 * A modal is modal or it is not. These four tokens are the whole order, and a new overlay takes one
 * rather than inventing a fifth number:
 *
 *   rail     30   the fixed left navigation
 *   header   40   sticky, and above the rail where the two meet
 *   modal    50   a scrim that must cover BOTH of the above, or it is not a scrim
 *   popover  60   the header's own account menu, which belongs to the header and outranks it
 * ---------------------------------------------------------------- */
:root {
  --z-rail: 30;
  --z-header: 40;
  --z-modal: 50;
  --z-popover: 60;
}
