/* BurnSpan UI.
 *
 * The tokens below are the BurnSpan Design System v0.1
 * (design/burnspan-design-system.md, D-250). Components read the semantic
 * layer only, never a primitive or a hex, so a theme is one block of values.
 *
 * Colour means something (P1). Ink is for action, signal blue for what can be
 * clicked or is selected, the heat scale for how long the money lasts, and
 * nothing else gets a hue. Status is four colours with a word beside each.
 */

:root {
  color-scheme: light;

  /* -- primitives (never themed): the ramps the light semantics are built from (D-269).
     Three ramps anchored on the logo: navy (its #0B2A4C is dark enough to be
     step 800), blue (the bright #1A7AD0 at 600, the mid #1F5D95 at 700) and
     slate, the neutral (the logo's #9AACBC at 300 and #66778B at 500). -- */
  --color-navy-50: #EEF3F8; --color-navy-100: #D8E2EE; --color-navy-200: #B3C5DA; --color-navy-300: #86A3C2;
  --color-navy-400: #5B80A6; --color-navy-500: #3A5F88; --color-navy-600: #28486C; --color-navy-700: #1A385C;
  --color-navy-800: #0B2A4C; --color-navy-900: #082039; --color-navy-950: #051426;
  --color-blue-50: #EAF3FC; --color-blue-100: #D2E6F8; --color-blue-200: #A9CDF1; --color-blue-300: #7AB1E8;
  --color-blue-400: #4D96DE; --color-blue-500: #2F86D7; --color-blue-600: #1A7AD0; --color-blue-700: #1F5D95;
  --color-blue-800: #194A77; --color-blue-900: #133758; --color-blue-950: #0C2338;
  --color-slate-0: #FFFFFF; --color-slate-50: #F3F5F8; --color-slate-100: #E8ECF0; --color-slate-200: #CDD6DE;
  --color-slate-300: #9AACBC; --color-slate-400: #7F91A3; --color-slate-500: #66778B; --color-slate-600: #4F5F71;
  --color-slate-700: #3C4A5A; --color-slate-800: #28333F; --color-slate-900: #1A222C; --color-slate-950: #0F151C;
  --color-ember-50: #FDEAE2; --color-ember-500: #EF5A24; --color-ember-700: #9E3410;
  /* The brand's primary, by the name a design handoff uses. */
  --color-primary-500: var(--color-navy-800);

  /* -- semantic: start -- */
  --bg-canvas: var(--color-slate-50);
  --bg-surface: var(--color-slate-0);
  --bg-raised: var(--color-slate-0);
  --bg-sunken: var(--color-slate-100);
  --bg-subtle: var(--color-slate-50);  /* table head, row hover */
  --bg-nav: var(--color-navy-900);     /* no reader today: kept for a marketing header (D-271) */
  --border-default: var(--color-slate-200);
  --border-strong: var(--color-slate-300);
  --border-subtle: var(--color-slate-100);  /* row dividers */
  --text-primary: var(--color-slate-950);   /* 18.4:1 on surface */
  --text-secondary: var(--color-slate-700); /* 9.1:1 */
  --text-tertiary: #5B6B7E;                 /* a tuned slate-500: 5.5:1 on surface, 4.6:1 on sunken */
  --text-disabled: var(--color-slate-400);
  --action-primary: var(--color-navy-800);  /* the one button: the brand navy; white on it 14.5:1 */
  --action-primary-hover: var(--color-navy-700);
  --action-primary-pressed: var(--color-navy-900);
  --action-on-primary: #FFFFFF;
  --signal: #1670C4;                        /* links and focus: a tuned blue-600 — the logo's #1A7AD0 is 4.4:1 on white, this is 5.1:1 */
  --signal-hover: var(--color-blue-700);
  --signal-subtle: var(--color-blue-50);
  --signal-on-subtle: var(--color-blue-700);
  --signal-halo: var(--color-blue-100);
  --danger: #B8331F;
  --danger-hover: #8F1D13;         /* proposed */
  --danger-on: #FFFFFF;            /* proposed */
  --row-flagged: #FDF5F4;          /* proposed */
  --switch-knob: #FFFFFF;          /* proposed */
  --brand-navy: #0B2A4C;           /* the gauge mark (D-268): the logo's own colours; */
  --brand-blue: #1F5D95;           /* tools/brand/build_brand.py PALETTE holds the same */
  --brand-grey: #9AACBC;
  --brand-bright: #1A7AD0;
  --brand-bars: #A2B2C2;
  --brand-slate: #66778B;          /* "Span" in the lockup */
  --accent: #EF5A24;               /* ember: the needle and a few chrome moments, never a status (D-268) */
  --accent-subtle: #FDEAE2;
  --accent-on-subtle: #9E3410;
  --hatch-a: var(--color-slate-400);  /* the span's covered stretch (D-254) */
  --hatch-b: var(--color-slate-200);
  /* The names a handoff asks for, as aliases: success/warning take the heat
     scale's healthy and watch, error the danger pair, info the signal pair.
     Status itself is unchanged (D-269): its meanings must not move. */
  --success: var(--status-healthy);       --success-text: var(--status-healthy-text);   --success-tint: var(--status-healthy-tint);
  --warning: var(--status-watch);         --warning-text: var(--status-watch-text);     --warning-tint: var(--status-watch-tint);
  --error: var(--danger);                 --error-text: var(--status-critical-text);    --error-tint: var(--status-critical-tint);
  --info: var(--signal);                  --info-text: var(--signal-on-subtle);         --info-tint: var(--signal-subtle);

  /* The heat scale: how short the span is. Solid for dots, bars and fills;
     text on tint uses the text step. Watch's solid is faint on white and
     never carries text. */
  --status-healthy: #1F8A70;     --status-healthy-text: #11624F;     --status-healthy-tint: #E2F3EE;
  --status-watch: #D99B00;       --status-watch-text: #7A5600;       --status-watch-tint: #FCF1D6;
  --status-risk: #E8641E;        --status-risk-text: #9C3A0B;        --status-risk-tint: #FDE8DC;
  --status-critical: #C42B1C;    --status-critical-text: #8F1D13;    --status-critical-tint: #FBE3E0;
  --status-provisional: #7464B0; --status-provisional-text: #4B3F86; --status-provisional-tint: #EEEBF8;
  --status-closed: #8C95A1;      --status-closed-text: #4A5462;      --status-closed-tint: #ECEEF1;

  /* Categorical series, in this order. The first three are what the app
     draws (rate-trend pools, the bid); validated as adjacent line pairs on
     white, 2026-10-09. */
  --cat-1: #3E4C9E; --cat-2: #3C8DBC; --cat-3: #8B4D8F; --cat-4: #A88450; --cat-5: #5E6B78; --cat-other: #B8BFC8;

  --e1: 0 1px 2px rgba(5, 20, 38, .06), 0 1px 1px rgba(5, 20, 38, .04);
  --e2: 0 8px 24px -4px rgba(5, 20, 38, .16), 0 2px 6px rgba(5, 20, 38, .06);
  --e3: 0 24px 48px -12px rgba(5, 20, 38, .28), 0 4px 12px rgba(5, 20, 38, .08);
  --scrim: rgba(5, 20, 38, .48);
  /* -- semantic: end -- */

  /* -- derived: follow the semantic layer, so a theme never restates them -- */
  /* Chart roles (BurnSpan §3): ink for actual and EAC, signal for funding,
     a quiet dotted grey for the value ceiling, the critical solid for a
     runout. Charts read these through svgEl, so a theme recolours them live. */
  --chart-actual: var(--text-primary);
  --chart-forecast-fill: var(--bg-sunken);
  --chart-forecast-edge: var(--text-tertiary);
  --chart-bar-tint: var(--border-strong);
  --chart-funding: var(--signal);
  --chart-ceiling: var(--text-tertiary);
  --chart-bid: var(--cat-3);
  --chart-scenario: var(--cat-4);  /* D-371: the what-if line, told from the EAC by colour */
  --chart-today: var(--border-strong);
  --chart-runout: var(--status-critical);
  --chart-grid: var(--border-subtle);
  --chart-axis: var(--border-default);
  --chart-label: var(--text-secondary);
  --chart-muted: var(--text-tertiary);
  --chart-band: var(--bg-subtle);
  --chart-halo: var(--bg-surface);
  /* D-268: the gauge's needle is ember — a speedometer's redline. One line
     puts it back to the logo's navy: var(--brand-navy). */
  --brand-needle: var(--accent);

  /* -- structure (never themed) -- */
  --font-display: "Montserrat", "Helvetica Neue", Arial, sans-serif;
  --font-sans: "IBM Plex Sans", system-ui, "Segoe UI", "Helvetica Neue", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
  /* D-296: what people write to each other may carry emoji; the chat's text
     names the system's colour emoji faces after the body face. */
  --font-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
  /* The type scale (D-269). Sizes and their line heights; the density
     aliases below pick the body pair, so a screen reads --fs-body as before. */
  --text-xs: 11px; --lh-xs: 16px;      /* labels, small caps */
  --text-sm: 13px; --lh-sm: 18px;      /* body, compact */
  --text-base: 14px; --lh-base: 20px;  /* body, comfortable; controls */
  --text-lg: 17px; --lh-lg: 24px;      /* h3, card titles */
  --text-xl: 20px; --lh-xl: 26px;      /* h2, display */
  --text-2xl: 28px; --lh-2xl: 34px;    /* h1, display */
  /* Spacing on a 4px base. New rules read these; the literal paddings of
     older rules are not migrated (D-070's lesson: a half-adopted scale is
     worse than none). */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 64px;
  --r-0: 0; --r-1: 3px; --r-2: 6px; --r-3: 8px; --r-4: 12px; --r-full: 999px;
  --motion-fast: 120ms; --motion-base: 200ms; --motion-slow: 320ms;
  --ease-out: ease-out; --ease-standard: cubic-bezier(.2, 0, 0, 1); --ease-exit: cubic-bezier(.4, 0, 1, 1);
  --topbar-h: 56px;
  /* D-255: where the bar ends. A phone drawn to its notch (viewport-fit=cover)
     starts the bar below the status area; in a browser tab the inset is 0. */
  --topbar-offset: calc(var(--topbar-h) + env(safe-area-inset-top));
  /* D-255: the page gutter and the row padding of nav links and menu items,
     tokens so a band or a touch screen can change them in one place. */
  --page-x: var(--space-6); --page-y: var(--space-5); --nav-py: var(--space-2); --menu-item-py: var(--space-2);

  /* -- density: compact is the product default (the owner, 2026-10-09) -- */
  --fs-body: var(--text-sm); --lh-body: var(--lh-sm); --fs-label: var(--text-xs); --cell-px: 12px; --cell-py: 9px;
  --control-h: 36px; --control-h-sm: 32px;
}
/* D-253: the dark theme. A full theme, not an inversion (BurnSpan §3): the
   document's dark values for every semantic token, so the set below is the
   semantic block's set exactly (tests/test_design_tokens.py holds it). Shadows
   drop out; a raised surface steps up in colour and keeps a hairline. */
[data-theme="dark"] {
  color-scheme: dark;
  --bg-canvas: #0B1119;
  --bg-surface: #121A23;
  --bg-raised: #1A2430;
  --bg-sunken: #0E141B;
  --bg-subtle: #182029;
  --bg-nav: #07111E;
  --border-default: #2B3644;
  --border-strong: #3B4858;
  --border-subtle: #1F2833;
  --text-primary: #E6EBF0;
  --text-secondary: #AEBBC8;
  --text-tertiary: #8596A8;
  --text-disabled: #5D6B7A;
  --action-primary: #D8E2EE;        /* navy-100: the one button, pale on a dark page; navy-950 on it 14:1 */
  --action-primary-hover: #EEF3F8;
  --action-primary-pressed: #B3C5DA;
  --action-on-primary: #051426;
  --signal: #7AB1E8;                /* blue-300: 7.8:1 on surface */
  --signal-hover: #A9CDF1;
  --signal-subtle: #14263B;
  --signal-on-subtle: #A9CDF1;
  --signal-halo: #1E3A5C;
  --danger: #FF6B5E;
  --danger-hover: #FF8A80;
  --danger-on: #051426;
  --row-flagged: #221413;
  --switch-knob: #E6E9EE;
  --brand-navy: #C9D6E4;           /* the dark colourway: the navy segment, hub and "Burn" lifted */
  --brand-blue: #3F82C4;
  --brand-grey: #9AACBC;
  --brand-bright: #1A7AD0;
  --brand-bars: #5F7186;
  --brand-slate: #9AACBC;
  --accent: #FF7A47;
  --accent-subtle: #3A1A10;
  --accent-on-subtle: #FFB596;
  --hatch-a: #5D6B7A;
  --hatch-b: #2B3644;
  --success: var(--status-healthy);       --success-text: var(--status-healthy-text);   --success-tint: var(--status-healthy-tint);
  --warning: var(--status-watch);         --warning-text: var(--status-watch-text);     --warning-tint: var(--status-watch-tint);
  --error: var(--danger);                 --error-text: var(--status-critical-text);    --error-tint: var(--status-critical-tint);
  --info: var(--signal);                  --info-text: var(--signal-on-subtle);         --info-tint: var(--signal-subtle);

  --status-healthy: #3BBF9A;     --status-healthy-text: #9BE3CD;     --status-healthy-tint: #0F2C25;
  --status-watch: #F2C04A;       --status-watch-text: #F7D98C;       --status-watch-tint: #33280C;
  --status-risk: #FF8A4C;        --status-risk-text: #FFC09C;        --status-risk-tint: #3A1E10;
  --status-critical: #FF6B5E;    --status-critical-text: #FFB4AC;    --status-critical-tint: #3A1714;
  --status-provisional: #A697E6; --status-provisional-text: #CFC6F5; --status-provisional-tint: #231E3A;
  --status-closed: #5D6B7A;      --status-closed-text: #AEBBC8;      --status-closed-tint: #1A2430;

  /* The same three categorical roles, stepped for the dark ground and
     validated as adjacent line pairs on #12161C (2026-10-09); the plum bid
     clears dark funding too. Slots 4 and 5 are lifted to match, unused. */
  --cat-1: #9085E9; --cat-2: #1B8C70; --cat-3: #C96BBE; --cat-4: #C9A46A; --cat-5: #8A96A3; --cat-other: #5C6672;

  --e1: none;
  --e2: 0 0 0 1px #3B4858;
  --e3: 0 0 0 1px #3B4858;
  --scrim: rgba(0, 0, 0, .6);
}

/* Comfortable is reserved for a later per-person switch; nothing sets it yet. */
[data-density="comfortable"] {
  --fs-body: var(--text-base); --lh-body: var(--lh-base); --fs-label: var(--text-sm); --cell-px: 16px; --cell-py: 14px; --control-h: 40px;
}
/* Touch (BurnSpan §5 Density, §7; D-255): 44px targets on a coarse pointer, in
   either density. Inside the token block on purpose — components keep reading
   --control-h and never learn about touch. A laptop with a trackpad reports a
   fine primary pointer and keeps the desktop sizes. */
@media (pointer: coarse) {
  :root, [data-density="comfortable"] {
    --control-h: 44px; --control-h-sm: 44px; --cell-py: 13px; --nav-py: 14px; --menu-item-py: 14px;
  }
}
/* -- tokens: end -- */

/* -- layout bands (BurnSpan §5, D-255). Fixed px: a custom property cannot
   drive @media. `VIEWPORT_BANDS` in app.js mirrors them and tests/test_responsive.py
   holds the two together. Every width query in this file uses one of these
   boundaries (1280 is the one exception: the room the open assistant needs).
     sm   (max-width: 639px)                          phone: one Menu, cards, one tile column
     md   (min-width: 640px) and (max-width: 1023px)  tablet: tiles two up, panels full width
     lg   (min-width: 1024px) and (max-width: 1439px) the full shell
     xl   (min-width: 1440px)                         the full shell; the content keeps the whole width (D-262)
   Touch is (pointer: coarse), whatever the band: 44px targets, through the
   tokens above. Shell rules by band live in the responsive section at the end
   of this file. A container query is for a scroller that already has
   `container-type` (the People table, D-171) and nothing that holds a
   fixed-position child: inline-size containment brings layout containment,
   which pins a fixed tip or menu to the container instead of the window. -- */

/* Type (BurnSpan §4). Archivo Expanded for page titles, section heads and
   tile figures, never below 17px; IBM Plex Sans for every other word and
   every number in a table; IBM Plex Mono only for identifiers. */
.t-xs { font-size: var(--text-xs); }
.t-11 { font-size: var(--text-sm); }
.t-12 { font-size: var(--text-sm); }
.t-13 { font-size: var(--text-sm); }
.w-500 { font-weight: 500; }
.w-600 { font-weight: 600; }
.meta { font-size: var(--text-sm); color: var(--text-tertiary); }
.lead { font-weight: 600; color: var(--text-primary); }
.c-watch { color: var(--status-watch-text); }
.c-risk { color: var(--status-critical-text); }
/* Labels: caps, tracked, tertiary ink. 11px in compact density so a fixed
   column's header still fits its width; 12px in comfortable. */
.microlabel, th, .legend__label, .stat__label, .meter__name, .fresh__label, .chart__sub, .chips__label {
  font-size: var(--fs-label); line-height: 16px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary);
}

/* Layout utilities that retire inline styles. */
.cluster { display: flex; align-items: center; gap: 8px; }
.cluster--wrap { flex-wrap: wrap; }
.cluster--between { justify-content: space-between; }
/* A control beside a two-line label: the two line up at the top, not on the
   centre of a block whose height depends on whether it has a second line. */
.cluster--top { align-items: flex-start; gap: 4px; }
/* Base above its modifiers: equal specificity, so source order decides. */
.stack { display: flex; flex-direction: column; gap: 2px; }
.stack--loose { gap: 6px; }
.stack--roomy { gap: 10px; }
.stack--section { gap: 16px; }
.mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 12px; }
.mb-2 { margin-top: 0; margin-bottom: 8px; }
.m-0 { margin: 0; }
.pagehead__text { display: flex; flex-direction: column; gap: 6px; }
.rowactions--spaced { margin-top: 12px; }
.note { font-size: var(--text-sm); color: var(--text-secondary); line-height: 1.55; max-width: 72ch; }

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg-canvas);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  -webkit-font-smoothing: antialiased;
  /* D-255: a double tap is two taps, not a zoom; pinch zoom stays (§7). */
  touch-action: manipulation;
}
button, a, summary { -webkit-tap-highlight-color: transparent; }
/* A sheet or a modal owns the screen: the page behind it does not scroll. */
body:has(dialog.sheet[open]), body:has(dialog.modal[open]) { overflow: hidden; }

a { color: var(--signal); text-decoration: none; }
a:hover { color: var(--signal-hover); text-decoration: underline; }

:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; border-radius: 2px; }

/* ------------------------------------------------ native controls (D-266) */
/* The ground every box, list and tick stands on. Zero specificity (:where),
   so each component rule below still wins on anything it declares; this
   fills only what a bare control left to the browser. A chained :not() alone
   would rank (0,4,1) and re-border every styled input. No height or padding
   on boxes: a min-height would grow the 30px rule and fee boxes. Chrome paints
   a select's popup from the select's own background and each row from
   `option`, so both are tokens: a see-through select pops a white list on a
   dark page. */
:where(input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="submit"]):not([type="button"]):not([type="reset"]), select, textarea) {
  background: var(--bg-surface); color: var(--text-primary);
  border: 1px solid var(--border-strong); border-radius: var(--r-2); font: inherit;
}
:where(select) { cursor: pointer; }
:where(textarea) { padding: 8px 10px; line-height: 1.45; resize: vertical; }
:where(input, textarea)::placeholder { color: var(--text-tertiary); }
/* The list a select opens: raised, as the app's own menus are. */
option, optgroup { background: var(--bg-raised); color: var(--text-primary); }
:where(input[type="checkbox"], input[type="radio"]) { accent-color: var(--action-primary); }
/* A file box's own button reads as a quiet button beside it. */
input::file-selector-button { font: inherit; font-size: var(--text-sm); margin-right: 10px; padding: 2px 10px; cursor: pointer; background: var(--bg-subtle); color: var(--text-primary); border: 1px solid var(--border-strong); border-radius: var(--r-1); }
/* Chrome's remembered sign-in paints its own tint; keep the box's ground. */
input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus { -webkit-text-fill-color: var(--text-primary); caret-color: var(--text-primary); box-shadow: 0 0 0 1000px var(--bg-surface) inset; }

/* Every number in the product: Plex Sans, tabular and lining, so columns of
   figures line up (BurnSpan §4). Mono is for identifiers, not amounts. */
.num {
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---------------------------------------------------------------- sign in */

/* A centred card that says nothing. The screen used to be a split layout with a
   dark brand panel carrying the product pitch; with the pitch gone there is
   nothing for the second half to hold, and an unauthenticated visitor should
   not be able to tell from this page what the system is for. */

.signin {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 32px 20px; background: var(--bg-canvas);
}
.signin__panel { width: 100%; max-width: 400px; }
.signin__card {
  background: var(--bg-raised); border: 1px solid var(--border-default); border-radius: var(--r-4);
  padding: 36px 36px 32px; display: flex; flex-direction: column; gap: 24px;
  box-shadow: var(--e2); position: relative; overflow: hidden;
}
/* D-271: a 3px ember bar along the card's top edge, clipped by its corners
   — the one page with no data on it. */
.signin__card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--accent); }
/* D-268: the lockup — the gauge mark, then BurnSpan in Montserrat, "Burn"
   in the navy and "Span" in the slate, as the logo has it. The gap is 0.4 of
   the mark's height. */
.wordmark { display: flex; align-items: center; gap: calc(var(--mark-size) * 0.4); }
.wordmark--bar { --mark-size: 24px; }
.wordmark--card { --mark-size: 32px; }
.wordmark__mark { display: inline-flex; flex: none; }
/* The gauge is 100 wide by 57 tall (tools/brand/build_brand.py). */
.wordmark__mark svg { display: block; width: calc(var(--mark-size) * 100 / 57); height: var(--mark-size); }
.wordmark__name {
  font-family: var(--font-display); font-size: var(--text-lg); line-height: 1; font-weight: 800;
  letter-spacing: -0.02em; color: var(--brand-navy); white-space: nowrap;
}
.wordmark--card .wordmark__name { font-size: 22px /* size: wordmark, drawn to the mark */; }
.wordmark__accent { color: var(--brand-slate); }
.mark__navy { fill: var(--brand-navy); }
.mark__blue { fill: var(--brand-blue); }
.mark__grey { fill: var(--brand-grey); }
.mark__bright { fill: var(--brand-bright); }
.mark__bars { fill: var(--brand-bars); }
.mark__needle { fill: var(--brand-needle); }

.signin__form { display: flex; flex-direction: column; gap: 24px; }
/* An error slot with nothing in it takes no gap. */
.signin__form > div:empty { display: none; }
.signin__head { display: flex; flex-direction: column; gap: 6px; }
.signin__head h2 {
  font-family: var(--font-display); font-size: var(--text-xl); line-height: 26px;
  font-weight: 650; margin: 0;
}
.signin__head p { font-size: var(--text-sm); color: var(--text-secondary); margin: 0; line-height: 1.55; }
.signin__fields { display: flex; flex-direction: column; gap: 16px; }
.signin__actions { display: flex; flex-direction: column; gap: 16px; }
.signin__help { font-size: var(--text-sm); color: var(--text-tertiary); margin: 0; text-align: center; }

.field { display: flex; flex-direction: column; gap: 8px; }
.field > label { font-size: var(--text-sm); font-weight: 600; color: var(--text-secondary); }
/* A group of choices under one label (D-182: the organizations to follow)
   reads like any other field: no browser border, the legend as its label. */
fieldset.field { border: 0; padding: 0; margin: 0; min-width: 0; }
fieldset.field > legend { padding: 0; margin-bottom: 6px; font-size: var(--text-sm); font-weight: 600; color: var(--text-secondary); }
fieldset.field .cluster { font-size: var(--text-base); }
.field input {
  height: var(--control-h); border: 1px solid var(--border-strong); border-radius: var(--r-2);
  padding: 0 12px; font: inherit; font-size: var(--text-base); color: var(--text-primary);
  background: var(--bg-surface); width: 100%;
}
.field input:focus { border-color: var(--signal); outline: none; box-shadow: 0 0 0 3px var(--signal-halo); }
/* D-266: a remembered sign-in keeps both the focus ring and the box's ground;
   the rule above would otherwise drop the base rule's inset on focus. */
.field input:-webkit-autofill:focus { box-shadow: 0 0 0 3px var(--signal-halo), 0 0 0 1000px var(--bg-surface) inset; }
/* A note box in a form matches the boxes beside it (not .field input: that
   would give it a box's 36px height). */
.field textarea { width: 100%; font-size: var(--text-base); }
.field textarea:focus { border-color: var(--signal); outline: none; box-shadow: 0 0 0 3px var(--signal-halo); }
/* D-141 amended: the invite form fills work email and name from Unanet and
   does not let them be edited. A disabled field has to read as "this is
   settled elsewhere", not as a broken input, so it keeps its border and
   loses only its white ground and the text cursor. */
.field input:disabled { background: var(--bg-subtle); color: var(--text-secondary); cursor: default; }
/* D-119: the rule above is for text inputs. A checkbox inside a .field was
   inheriting its 42px height and full width — .grants input reset only margin
   and accent-color, and a tie in specificity leaves every property it does not
   declare in force. That is why Project access drew rows of huge squares. */
.field input[type="checkbox"] {
  height: auto; width: auto; padding: 0; border: 0; background: none;
  accent-color: var(--action-primary);
}

/* Primary: ink, one per view (BurnSpan §6). */
.btn {
  height: var(--control-h); padding: 0 16px; border: 0; border-radius: var(--r-2); background: var(--action-primary);
  color: var(--action-on-primary); font: inherit; font-size: var(--text-base); font-weight: 500; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: background var(--motion-fast) var(--ease-out), border-color var(--motion-fast) var(--ease-out);
}
.btn:hover:not(:disabled) { background: var(--action-primary-hover); }
.btn:active:not(:disabled) { background: var(--action-primary-pressed); }
.btn:disabled { opacity: 0.55; cursor: progress; }

/* D-069: navigation in a head wears the button shape; it stays an anchor. */
a.btn, a.btn:hover { text-decoration: none; }
/* The spec's Secondary button. The class keeps its older name: it is on
   every head control in the app. */
.btn--ghost {
  height: var(--control-h); padding: 0 14px; background: var(--bg-surface); color: var(--text-primary);
  border: 1px solid var(--border-strong); font-size: var(--text-sm); font-weight: 500;
}
.btn--ghost:hover:not(:disabled) { background: var(--bg-subtle); border-color: var(--text-disabled); color: var(--text-primary); }
.btn--ghost:active:not(:disabled) { background: var(--bg-sunken); }
/* D-271: the true ghost — no ground, no border; for a quiet action beside a
   primary or a secondary. The design document's "Ghost". */
.btn--plain { background: none; color: var(--text-secondary); border: 0; padding: 0 10px; }
.btn--plain:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text-primary); }
.btn--plain:active:not(:disabled) { background: var(--bg-sunken); }

.alert {
  padding: 12px 16px; border-radius: var(--r-3); font-size: var(--text-sm); line-height: 1.5;
  background: var(--status-critical-tint); color: var(--status-critical-text);
}

/* Same shape as .alert, for an outcome that succeeded — e.g. a connection test
   that reached Unanet. */
.alert--ok {
  background: var(--status-healthy-tint); color: var(--status-healthy-text);
}

/* ------------------------------------------------------------------ shell */

.topbar {
  height: var(--topbar-offset); background: var(--bg-surface); border-bottom: 1px solid var(--border-default);
  display: flex; align-items: center; gap: 32px; padding: env(safe-area-inset-top) 32px 0;
  position: sticky; top: 0; z-index: 10;
}
/* D-255: the one Menu a phone or a tablet shows in place of the bar's tabs
   and menus (the responsive section shows it under 1024px). D-258: Back,
   for the app installed on a home screen, where the browser's is gone. */
.topbar__who > details.menu--all, .topbar__back { display: none; }
.topbar__menulabel { font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary); }
.topbar__back {
  width: var(--control-h-sm); height: var(--control-h-sm); margin-right: -8px; padding: 0 0 2px;
  border: 0; border-radius: var(--r-2); background: none; color: var(--text-secondary);
  font: 500 22px/1 var(--font-sans) /* size: glyph */; cursor: pointer; align-items: center; justify-content: center; flex: none;
}
.topbar__back:hover { background: var(--bg-subtle); color: var(--text-primary); }
@media (display-mode: standalone) { .topbar__back { display: inline-flex; } }
.menu__group { display: flex; flex-direction: column; gap: 1px; padding: 2px 0; }
.menu__label { font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-tertiary); padding: 8px 10px 4px; }
.menu__who { color: var(--text-primary); font-weight: 600; font-size: var(--text-sm); }
.menu__group--ask .topbar__ask { width: 100%; justify-content: flex-start; height: var(--control-h); border: 0; padding: 0 10px; }
.menu__group--ask .topbar__asklabel { display: inline; }
.menu__group--ask .topbar__askslot:empty { display: none; }
/* D-223: the product, then the place — the organization, or Platform. */
.topbar__brand { display: flex; align-items: center; gap: 16px; flex: none; }
.topbar__nav { display: flex; align-items: center; gap: 4px; flex-grow: 1; }
.topbar__nav a {
  font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary);
  padding: var(--nav-py) 12px; border-radius: 4px;
}
.topbar__nav a:hover { color: var(--text-primary); text-decoration: none; background: var(--bg-subtle); }
/* D-271: the current page carries the one ember line the chrome allows — the
   accent never sits beside a status, and the bar holds none. */
.topbar__nav a[aria-current="page"] { font-weight: 600; color: var(--text-primary); background: var(--bg-sunken); box-shadow: inset 0 -2px var(--accent); }
.topbar__who { display: flex; align-items: center; gap: 16px; }
.topbar__tenant { font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary); white-space: nowrap; }
.topbar__sep { width: 1px; height: 20px; background: var(--border-default); }
.topbar__user { display: flex; align-items: center; gap: 10px; }
.topbar__avatar {
  width: 28px; height: 28px; border-radius: 50%; background: var(--action-primary); color: var(--action-on-primary);
  font-size: var(--text-xs); font-weight: 600; display: flex; align-items: center; justify-content: center;
  flex: none;
}
/* D-122: who is signed in, over what they are signed in as. Two lines fit the
   56px bar at these sizes; the role is the quieter of the two. */
.topbar__id { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.25; }
.topbar__name { font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary); }
.topbar__role { font-size: var(--text-xs); color: var(--text-tertiary); }
/* Topbar dropdowns on <details>/<summary> (D-042). Outside-click and Escape
   dismissal are wired once at boot; navigation re-mounts the shell, which
   closes any open menu by construction. */
details.menu { position: relative; }
details.menu > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 6px;
}
details.menu > summary::-webkit-details-marker { display: none; }
/* A menu that is a destination wears the tab's look: Admin in the nav, Global
   Admin at the right (D-223). The person's own menu keeps its own. */
.topbar__nav details.menu > summary,
.topbar__who details.menu > summary:not(.topbar__user) {
  font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary); padding: var(--nav-py) 12px; border-radius: 4px;
}
.topbar__nav details.menu > summary:hover,
.topbar__who details.menu > summary:not(.topbar__user):hover { color: var(--text-primary); background: var(--bg-subtle); }
.topbar__nav details.menu > summary[aria-current="page"],
.topbar__who details.menu > summary:not(.topbar__user)[aria-current="page"] {
  font-weight: 600; color: var(--text-primary); background: var(--bg-sunken);
}
/* D-270: icons sit on the text's baseline and take its colour. */
.menu__caret { display: inline-flex; color: var(--text-disabled); }
.menu__caret svg, .btn__icon svg, .steps__tick svg, .steps__dot svg { display: block; }
.btn__icon { display: inline-flex; vertical-align: -2px; }
.steps__tick { display: inline-flex; width: 16px; justify-content: center; align-self: center; }
.menu__panel {
  position: absolute; right: 0; top: calc(100% + 6px); min-width: 200px; z-index: 20;
  padding: 6px; display: flex; flex-direction: column; gap: 1px;
  background: var(--bg-raised); border: 1px solid var(--border-default); border-radius: var(--r-3);
  box-shadow: var(--e2);
}
.menu__panel--left { left: 0; right: auto; }
.menu__item {
  display: block; text-align: left; font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary);
  padding: var(--menu-item-py) 10px; border-radius: var(--r-2); background: none; border: 0;
  cursor: pointer; font-family: inherit; width: 100%;
}
.menu__item:hover { color: var(--text-primary); background: var(--bg-subtle); text-decoration: none; }
.menu__item[aria-current="page"] { font-weight: 600; color: var(--text-primary); background: var(--bg-sunken); }
/* D-352: Tickets carries the chat's blue count bubble while a reply is unread. */
.menu__item--count { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.menu__item--count[data-unread] { font-weight: 600; color: var(--text-primary); }
.menu__count[hidden] { display: none; }
.menu__rule { height: 1px; background: var(--border-subtle); margin: 5px 2px; }
/* D-144: named column presets across the top of a picker. */
.menu__meta {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 10px 6px;
  font-size: var(--text-sm); color: var(--text-tertiary);
}
/* D-253: Light · Dark · System, under the person's name. */
.menu__appearance { display: flex; flex-direction: column; gap: 6px; padding: 6px 10px 8px; }
.menu__appearance .seg { min-width: 220px; }

/* D-255: the gutters are tokens (a band changes them). Not a container:
   containment would make every fixed-position thing inside the page — a
   tip, a menu risen from the foot — position against the page, not the
   window. */
.page { padding: var(--page-y) var(--page-x) 48px; display: flex; flex-direction: column; gap: 20px; }
/* D-428: where the floating chat button is drawn, the page ends below it,
   so the last row is never under the button. */

.pagehead { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.pagehead h1 {
  font-family: var(--font-display); font-size: var(--text-2xl); line-height: 34px;
  font-weight: 650; margin: 0;
}
.pagehead p { font-size: var(--text-sm); color: var(--text-secondary); margin: 0; }
.pagehead strong { font-weight: 600; color: var(--text-primary); }

.breadcrumb { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--text-tertiary); }
.breadcrumb span { color: var(--text-secondary); }

/* ---------------------------------------------------------------- filters */

.filters { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* D-256: the phone's Sort control, where the column headers are not shown. */
.sortbar { display: none; }
.filters__search {
  display: flex; align-items: center; gap: 8px; height: var(--control-h); padding: 0 12px;
  background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--r-2); width: 268px;
}
.filters__search input { border: 0; outline: none; font: inherit; font-size: var(--text-sm); width: 100%; background: none; color: var(--text-primary); }
.filters__search input::placeholder { color: var(--text-tertiary); }
.filters__select {
  display: flex; align-items: center; gap: 8px; height: var(--control-h); padding: 0 12px;
  background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--r-2);
  font-size: var(--text-sm); color: var(--text-secondary);
}
/* D-266: the pill's own ground, never see-through: Chrome paints the list
   the select opens from it, and on a dark page a see-through select opened
   a white one. */
.filters__select select {
  border: 0; outline: none; background: var(--bg-surface); font: inherit; font-size: var(--text-sm);
  font-weight: 500; color: var(--text-primary); cursor: pointer;
}
/* D-213: a pill whose choices are long names (a project with its code) stops
   at a readable width instead of following its longest option off the page. */
.filters__select--fit select { max-width: 48ch; text-overflow: ellipsis; }
.filters__select--fit > span { white-space: nowrap; }

/* ----------------------------------------------------------------- panels */

.panel { background: var(--bg-surface); border: 1px solid var(--border-default); border-radius: var(--r-3); overflow: hidden; }
.panel__head {
  padding: 16px 24px 14px; display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; border-bottom: 1px solid var(--border-default);
}
.panel__head h2 { font-size: var(--text-lg); line-height: 22px; font-weight: 600; margin: 0; }
/* D-159: a report's head carries the layout switch and the Columns menu; on
   a phone they wrap under the title rather than run off the panel. */
.panel__head--wrap { flex-wrap: wrap; align-items: center; }
/* D-160: a saved report's head carries up to five controls; they wrap as a
   group on a phone, each label on one line. */
.pagehead__actions .btn, .pagehead__actions summary { white-space: nowrap; }
.panel--primary .panel__head h2 {
  font-family: var(--font-display); font-size: var(--text-xl); line-height: 26px; font-weight: 650;
}
/* The subtitle, not the pills: a pill carries its own colours, and this rule
   used to outrank them (grey on red, D-106). */
.panel__head span:not(.pill) { font-size: var(--text-sm); color: var(--text-tertiary); }
.panel__scroll { overflow-x: auto; overscroll-behavior-x: contain; }
/* D-257: a preview that scrolls vertically inside its sheet keeps its header
   row in view (a sticky header works in a vertical scroller, not a page). */
.panel__scroll--tall { overflow-y: auto; }
.panel__scroll--tall thead th { position: sticky; top: 0; z-index: 4; background: var(--bg-subtle); }
/* D-263: frozen panes (the Portfolio). A sticky header cannot stick to the
   page from inside a sideways scroller — the scroller is its container on
   both axes — so the scroller gets a height and its own vertical scroll,
   and the header row and the first column stick inside it. The height here
   is the cap until frozenScroller (app.js) measures the box and ends it at
   the foot of the window. A list
   that fits is unchanged (a max, not a height); one that runs out hands the
   wheel back to the page. The cells carry the backgrounds and the rules,
   since a row's background and collapsed border stay behind when a cell
   sticks. The first column is the Project column (`always`, D-147). */
.panel__scroll--frozen { overflow: auto; max-height: calc(100dvh - var(--topbar-offset) - 16px); }
/* The head sticks as one, so its cells layer among themselves as they do in
   any table: a column grip (z 1) overhangs the next cell, which a stack of
   sticky cells, each its own layer, would paint over. */
.panel__scroll--frozen > table > thead { position: sticky; top: 0; z-index: 4; }
.panel__scroll--frozen > table > thead > tr > th { background: var(--bg-subtle); box-shadow: inset 0 -1px var(--border-default); }
.panel__scroll--frozen > table > tbody > tr > :first-child:not([colspan]) {
  position: sticky; left: 0; z-index: 2; background: var(--bg-surface);
  box-shadow: inset -1px 0 var(--border-default);
}
.panel__scroll--frozen > table > tbody > tr.row--flagged > :first-child { background: var(--row-flagged); }
/* D-428: a total or group row is one grey band, its frozen name cell too. */
.panel__scroll--frozen > table > tbody > tr.row--total > :first-child:not([colspan]) { background: var(--bg-subtle); }
/* A header cell showing its tip rises over the corner, which the tip may
   cross once its column has scrolled to the left. */
.panel__scroll--frozen > table > thead > tr > th:has(.info--open),
.panel__scroll--frozen > table > thead > tr > th:has(.info--focus),
.panel__scroll--frozen > table > thead > tr > th:has(.info:hover) { z-index: 6; }
/* The corner sits over the head's other cells as they scroll under it; named
   in full to outrank the tablet's D-257 pin. */
.panel__scroll.panel__scroll--frozen:not(.grid):not(.panel__scroll--free) > table > thead > tr > :first-child:not([colspan]) {
  position: sticky; left: 0; z-index: 5; background: var(--bg-subtle);
  box-shadow: inset -1px 0 var(--border-default), inset 0 -1px var(--border-default);
}
/* D-326: the columns fixed beside Project (the Portfolio's Start date and
   End date) freeze with it while the frozen block fits (pinnedLefts).
   pinCells marks them and sets each one's `left`; the edge rule moves from
   Project to the last of them. Head cells stack downward from the corner so
   each one's resize grip stays above its neighbour (D-263's grip gotcha);
   a cell showing a tip still rises over them all. Never on a phone, where
   every table is cards (labelCells marks them all). */
@media (min-width: 640px) {
  .panel__scroll--frozen > table.table--pinned > tbody > tr > .cell--pin,
  .panel__scroll--frozen > table.table--pinned > tfoot > tr > .cell--pin {
    position: sticky; z-index: 2; background: var(--bg-surface);
  }
  .panel__scroll--frozen > table.table--pinned > thead > tr > th.cell--pin {
    position: sticky; z-index: 3; background: var(--bg-subtle);
  }
  .panel__scroll--frozen > table.table--pinned > thead > tr > th.cell--pin[data-pin="1"] { z-index: 4; }
  .panel__scroll--frozen > table.table--pinned > thead > tr > th.cell--pin:has(.info--open),
  .panel__scroll--frozen > table.table--pinned > thead > tr > th.cell--pin:has(.info--focus),
  .panel__scroll--frozen > table.table--pinned > thead > tr > th.cell--pin:has(.info:hover) { z-index: 6; }
  .panel__scroll--frozen > table.table--pinned > tbody > tr.row--flagged > .cell--pin { background: var(--row-flagged); }
  .panel__scroll--frozen > table.table--pinned > tbody > tr.row--total > .cell--pin,
  .panel__scroll--frozen > table.table--pinned > tfoot > tr.row--total > .cell--pin { background: var(--bg-subtle); }
  .panel__scroll--frozen > table.table--pinned > tbody > tr > .cell--pin-edge { box-shadow: inset -1px 0 var(--border-default); }
  .panel__scroll--frozen > table.table--pinned > thead > tr > th.cell--pin-edge {
    box-shadow: inset -1px 0 var(--border-default), inset 0 -1px var(--border-default);
  }
  .panel__scroll.panel__scroll--frozen:not(.grid):not(.panel__scroll--free) > table.table--pinned > tbody > tr > :first-child:not([colspan]) { box-shadow: none; }
  .panel__scroll.panel__scroll--frozen:not(.grid):not(.panel__scroll--free) > table.table--pinned > thead > tr > :first-child:not([colspan]) {
    box-shadow: inset 0 -1px var(--border-default);
  }
}
/* D-330: the month picker — a what-if line's months, the escalation's start.
   A row menu whose summary is the field's box, so a drawer's table lets it
   overflow and a phone gets the bottom sheet. The grid is a fiscal year,
   four months a row; the chosen stretch is tinted, its ends filled, months
   before the open month greyed. No accent here: the signal blue is the
   choice, as in every field. */
.field > details.monthpick { display: block; width: 100%; }
.field > details.monthpick > summary.monthpick__trigger {
  width: 100%; justify-content: space-between; font-size: var(--text-base); font-weight: 400; padding: 0 12px;
}
.field > details.monthpick > summary.monthpick__trigger.is-empty { color: var(--text-tertiary); }
.monthpick__words { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu__panel--months { min-width: 340px; max-width: 440px; padding: 12px; gap: 10px; }
/* Its own width wherever it hangs: an inline box's panel would otherwise
   shrink to the box (D-331, the report bar at a tablet's width). On a
   phone the sheet rule below takes the width back. */
@media (min-width: 640px) { .menu__panel--months { width: 340px; } }
.monthpick__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.monthpick__step { padding: 0 10px; min-width: var(--control-h-sm); font-size: 16px /* size: glyph */; }
.monthpick__label { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }
.monthpick__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px 0; }
.monthpick__month {
  position: relative; height: max(40px, var(--control-h-sm)); border: 0; border-radius: var(--r-2); background: none; font: inherit;
  font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); cursor: pointer; display: flex;
  flex-direction: column; align-items: center; justify-content: center; line-height: 1.1; gap: 1px;
  transition: background var(--motion-fast) var(--ease-out);
}
.monthpick__month:hover { background: var(--bg-subtle); }
.monthpick__month:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--signal); }
.monthpick__year { font-size: var(--text-xs); font-weight: 400; color: var(--text-tertiary); }
.monthpick__month.is-past { color: var(--text-disabled); }
.monthpick__month.is-past .monthpick__year { color: var(--text-disabled); }
.monthpick__month.is-between { background: var(--signal-subtle); border-radius: 0; color: var(--signal-on-subtle); }
.monthpick__month.is-between .monthpick__year { color: var(--signal-on-subtle); }
.monthpick__month.is-start, .monthpick__month.is-end { background: var(--signal); color: var(--action-on-primary); }
.monthpick__month.is-start .monthpick__year, .monthpick__month.is-end .monthpick__year { color: var(--action-on-primary); opacity: .85; }
.monthpick__month.is-start:not(.is-end) { border-radius: var(--r-2) 0 0 var(--r-2); }
.monthpick__month.is-end:not(.is-start) { border-radius: 0 var(--r-2) var(--r-2) 0; }
/* The open month wears a dot; the period's last month an underline. */
.monthpick__month.is-open::after {
  content: ''; position: absolute; bottom: 4px; left: 50%; width: 4px; height: 4px; margin-left: -2px;
  border-radius: 50%; background: currentColor;
}
.monthpick__month.is-end-of-period { box-shadow: inset 0 -2px 0 var(--status-watch); }
.monthpick__month.is-end-of-period:focus-visible { box-shadow: inset 0 0 0 2px var(--signal), inset 0 -2px 0 var(--status-watch); }
.monthpick__quick { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: var(--text-sm); }
/* A touch size pads a link 15px each way with matching negative margins
   (the coarse-pointer rule); wrapped chips would overlap, so here the
   padding stands on its own and the rows clear each other. */
.monthpick__quick .btn--link { margin-top: 0; margin-bottom: 0; }
.monthpick__legend { font-size: var(--text-sm); color: var(--text-tertiary); }
.monthpick__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: var(--text-sm); color: var(--text-secondary); }
.monthpick__reading.c-watch { color: var(--status-watch-text); }
/* D-431: the day picker: the month picker's panel, a week to a row. */
.daypick__dow, .daypick__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.daypick__dow span { text-align: center; font-size: var(--text-xs); font-weight: 600; color: var(--text-tertiary); padding: 2px 0; }
.daypick__grid { gap: 2px 0; }
.daypick__day {
  position: relative; height: max(36px, var(--control-h-sm)); border: 0; border-radius: var(--r-2); background: none; font: inherit;
  font-size: var(--text-sm); font-variant-numeric: tabular-nums; color: var(--text-primary); cursor: pointer;
  transition: background var(--motion-fast) var(--ease-out);
}
.daypick__day:hover { background: var(--bg-subtle); }
.daypick__day:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--signal); }
.daypick__day.is-start { background: var(--signal); color: var(--action-on-primary); }
.daypick__day.is-open::after {
  content: ''; position: absolute; bottom: 3px; left: 50%; width: 4px; height: 4px; margin-left: -2px;
  border-radius: 50%; background: currentColor;
}
.daypick__panel .monthpick__head .cluster .monthpick__step { padding: 0 6px; }
@media (pointer: coarse) { .daypick__day { height: 44px; } }
/* D-331: the picker in a row of words (an award fee's Fill, a report's
   parameters, a sheet's tools): its own width, the row's height. */
details.monthpick--inline { display: inline-block; width: auto; }
details.monthpick--inline > summary.monthpick__trigger { width: auto; justify-content: space-between; font-size: var(--text-sm); font-weight: 400; padding: 0 10px; gap: 8px; }
details.monthpick--inline > summary.monthpick__trigger.is-empty { color: var(--text-tertiary); }
.fee__fill details.monthpick--inline > summary.monthpick__trigger { height: 30px; font-size: var(--text-sm); padding: 0 8px; }
.filters__select--months { padding: 0; border: 0; background: none; gap: 0; }
.filters__select--months > span { padding: 0 8px 0 12px; height: var(--control-h); display: inline-flex; align-items: center; border: 1px solid var(--border-strong); border-right: 0; border-radius: var(--r-2) 0 0 var(--r-2); background: var(--bg-surface); }
.filters__select--months details.monthpick--inline > summary.monthpick__trigger { border-radius: 0 var(--r-2) var(--r-2) 0; }
/* A panel that folds (D-071): its head is the summary; a caret says so. */
details.fold > summary { list-style: none; cursor: pointer; }
details.fold > summary::-webkit-details-marker { display: none; }
details.fold > summary h2::before { content: '▸'; display: inline-block; width: 14px; font-size: var(--text-xs); color: var(--text-tertiary); }
details.fold[open] > summary h2::before { content: '▾'; }
details.fold:not([open]) > summary { border-bottom: 0; }
/* D-074: a fold's summary is one line — its title, then its meta — with the
   marker on the first thing in it, whatever element that is. A fold that is a
   panel keeps the panel head's type size; one inside a panel is a row. */
details.fold > summary { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
details.panel.fold > summary { padding: 14px 24px; }
details.panel.fold > summary h2 { font-size: var(--text-lg); line-height: 22px; font-weight: 600; margin: 0; }
details.fold > summary > span:first-child::before { content: '▸'; display: inline-block; width: 14px; font-size: var(--text-xs); color: var(--text-tertiary); }
details.fold[open] > summary > span:first-child::before { content: '▾'; }
.panel > .note { padding: 12px 24px 0; }
/* A row menu on the last row must not be clipped by the table's scroll box. */
.panel__scroll:has(details.menu--row[open]), .panel:has(details.menu--row[open]) { overflow: visible; }
.table--sized td:has(details.row-move[open]) { overflow: visible; }
/* A panel clips its overflow; an open ⓘ tip must hang past its edge. */
.panel:has(.info--open), .panel:has(.info--focus), .panel:has(.info:hover) { overflow: visible; }
/* D-156: so must the table's scroll box and the sized header cell the tip
   hangs from — "you can't read what they say. The text box is hidden". Both
   clip (`.table--sized th` for its ellipsis, `.panel__scroll` for its
   scrollbar); each is released only while a tip is showing, the trade the
   row-menu rule above already makes. */
.panel__scroll:has(.info--open), .panel__scroll:has(.info--focus), .panel__scroll:has(.info:hover) { overflow: visible; }
.table--sized th:has(.info--open), .table--sized th:has(.info--focus), .table--sized th:has(.info:hover) { overflow: visible; }
/* D-263: never the frozen scroller. Let go, its rows would spill and its
   scroll position go; under 1024 the band's `auto hidden` would end its
   vertical scroll. Its tips hang from the window instead (infoTip), and a
   row menu scrolls into view with the rows. */
.panel__scroll.panel__scroll--frozen:has(details.menu--row[open]),
.panel__scroll.panel__scroll--frozen:has(.info--open),
.panel__scroll.panel__scroll--frozen:has(.info--focus),
.panel__scroll.panel__scroll--frozen:has(.info:hover) { overflow: auto; }
/* The hidden attribute wins over every display rule, whatever the class. */
[hidden] { display: none !important; }
/* The chart's Options menu holds controls, so it must not close on click. */
.chart__options-panel { min-width: 260px; padding: 10px; gap: 10px; }
.chart__options-panel .chart__bid { padding: 2px 0; }

/* D-129: the Portfolio's Columns picker holds checkboxes, for the same
   reason — a click inside it chooses, it does not dismiss. */
.menu__panel--picker { min-width: 220px; padding: 6px; }
/* D-313: the picker is a search box, a scrolling list and a foot; the list
   scrolls so a long one (the Portfolio's) never runs off the window, and the
   columns not showing sit in sections under a rule and a label. */
.menu__list { display: flex; flex-direction: column; gap: 1px; max-height: min(480px, 60vh); overflow-y: auto; }
.menu__section, .menu__foot { display: flex; flex-direction: column; gap: 1px; }
.menu__panel .menu__search { flex: none; width: auto; margin: 2px 2px 6px; }   /* flex: none — the phone's sheet is a flex column that scrolls, and its own .filters__search rule would let it shrink */
.menu__check {
  display: flex; align-items: center; gap: 8px; padding: 6px 10px;
  font-size: var(--text-sm); color: var(--text-secondary); border-radius: var(--r-2);
  cursor: pointer; text-align: left;
}
.menu__check:hover { color: var(--text-primary); background: var(--bg-subtle); }
.menu__check input { margin: 0; }
/* The "always shown" hint sits at the end of its row and never wraps. */
.menu__check .muted { margin-left: auto; font-size: var(--text-xs); white-space: nowrap; }
/* The panel hangs off its right edge; on a narrow screen the menu has
   wrapped to the left of the page, so it must open the other way. */
/* D-164: a row of the Columns menu being carried, and where it will land.
   D-281: the handle at the row's left takes the drag, as every arranged
   row's does; the row itself still scrolls under a finger. */
.menu__check--movable { position: relative; }
.menu__check.is-moving { opacity: .55; }
.menu__check.is-drop-before::before, .menu__check.is-drop-after::after {
  content: ''; position: absolute; left: 6px; right: 6px; height: 2px; background: var(--signal);
}
.menu__check.is-drop-before::before { top: -1px; }
.menu__check.is-drop-after::after { bottom: -1px; }
.menu__hint { font-size: var(--text-xs); color: var(--text-tertiary); margin: 6px 10px 4px; line-height: 1.45; max-width: 220px; }
@media (prefers-reduced-motion: reduce) { .th-move { transition: none; } }

/* ----------------------------------------------------------------- tables */

table { border-collapse: collapse; width: 100%; }
thead tr { background: var(--bg-subtle); border-bottom: 1px solid var(--border-default); }
th { text-align: left; padding: 10px var(--cell-px); white-space: nowrap; }
/* D-428: a wide table whose headers may take two lines to fit its panel. */
.table--wrapheads th { white-space: normal; vertical-align: bottom; }
th:first-child, td:first-child { padding-left: 20px; }
th:last-child, td:last-child { padding-right: 20px; }
/* A sortable header is a button that looks exactly like the header. */
.th-sort {
  all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: 4px;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit;
}
.th-sort:hover, .th-sort.is-active { color: var(--text-primary); }
.th-sort:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; border-radius: 2px; }
/* D-133: a column stays where it is put. Fixed layout is what makes a width
   a width rather than a suggestion; the last column carries no width and so
   takes the slack, which keeps the table filling its panel. */
.table--sized { table-layout: fixed; }
/* D-428: a header is read whole. It wraps onto a second line rather than
   being cut to an ellipsis when its column is narrower than its words; the
   default widths are set so that it seldom has to. */
.table--sized th { position: relative; white-space: normal; vertical-align: bottom; }
.table--sized .th-sort { max-width: 100%; overflow-wrap: break-word; }
/* The grip straddles the column boundary, and is a good deal wider than the
   line it draws — a 1px target is a target nobody hits. */
.th-grip {
  position: absolute; top: 0; right: -4px; width: 9px; height: 100%;
  cursor: col-resize; touch-action: none; z-index: 1;
}
.th-grip::after {
  content: ''; position: absolute; top: 20%; bottom: 20%; left: 4px; width: 1px;
  background: var(--border-default); opacity: 0; transition: opacity var(--motion-fast) var(--ease-out);
}
thead:hover .th-grip::after { opacity: 1; }
.th-grip:hover::after, .th-grip.is-dragging::after, .th-grip:focus-visible::after {
  opacity: 1; background: var(--signal); width: 2px; top: 0; bottom: 0;
}
.th-grip:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; border-radius: 2px; }
/* D-147: a header the reader is carrying somewhere else, and the boundary it
   would land on. The bar is drawn INSIDE the cell: `.table--sized th` hides
   its overflow (until D-428), so an outside one would be clipped away. */
/* D-164: the cue that a header can be carried. A grip at the header's left
   edge shows while the head row is hovered, with a grab cursor of its own;
   the sort button keeps its pointer, because a click still sorts. A
   sideways finger-drag on the header moves the column; a vertical one still
   scrolls (pan-y). */
th.th--movable { touch-action: pan-y; }
.th-move {
  position: absolute; left: 3px; top: 50%; transform: translateY(-50%);
  font-size: var(--text-xs); line-height: 1; color: var(--text-tertiary); cursor: grab;
  opacity: 0; transition: opacity var(--motion-fast) var(--ease-out);
}
/* D-428: only the header under the pointer shows its handle. Shown on the
   whole row, each handle sat against the previous column's right-aligned
   label and read as a stray mark after it ("ACTUAL HOURS·"). */
th.th--movable:hover .th-move, th:focus-within .th-move { opacity: .8; }
th.is-moving { opacity: .55; }
th.is-drop-before::before, th.is-drop-after::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 2px; background: var(--signal);
}
th.is-drop-before::before { left: 0; }
th.is-drop-after::after { right: 0; }
/* While a column is being carried the pointer owns the page: no text
   selection anywhere, and the cursor says what is happening over every part
   of it. */
body.is-col-drag, body.is-col-drag * { cursor: grabbing !important; user-select: none; }
/* D-230: a task carried by its handle (Ceilings by task). The handle sits in
   the first cell's left padding, shown on the row's hover or when focused;
   it owns the touch (the drag is vertical), the rest of the row scrolls.
   The landing bar is drawn on the cells, since a row has no box of its own. */
td.cell--movable { position: relative; }
.row-move {
  position: absolute; left: 3px; top: 50%; transform: translateY(-50%);
  width: 14px; text-align: center; font-size: var(--text-xs); line-height: 1; color: var(--text-tertiary);
  cursor: grab; touch-action: none; border-radius: 3px; opacity: 0; transition: opacity var(--motion-fast) var(--ease-out);
}
/* D-256: the handle is a menu too (a tap opens Move up / down / to top / to
   bottom); its summary wears the glyph and takes the drag. */
details.row-move > summary { list-style: none; cursor: grab; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; padding: 0; }
details.row-move > summary::-webkit-details-marker { display: none; }
details.row-move > .menu__panel { top: 100%; left: 0; right: auto; min-width: 150px; transform: none; }
details.row-move[open] { opacity: 1; z-index: 6; }
/* D-261: the handle is a details.menu (D-256), and `details.menu
   { position: relative }` outranks a single class — so its placement is
   stated here against the cell it sits in. Left, in the cell's padding,
   centred on the row. */
td.cell--movable > details.row-move { position: absolute; top: 50%; transform: translateY(-50%); }
tr:hover .row-move, tr:focus-within .row-move { opacity: .8; }
.row-move > summary:focus-visible { opacity: 1; outline: 2px solid var(--signal); outline-offset: 1px; border-radius: 3px; }
tr.is-moving { opacity: .55; }
/* D-231: a project under a contract sits indented; its handle sits in the indent. */
.row--child .row-move { left: 14px; }
tr.is-drop-before > td { box-shadow: inset 0 2px 0 var(--signal); }
tr.is-drop-after > td { box-shadow: inset 0 -2px 0 var(--signal); }
.order-line { display: flex; flex-direction: column; gap: 8px; padding: 14px 24px 10px; }
/* D-446: a setting that saves itself says so where its Save was. */
.autosave { font-size: var(--text-sm); color: var(--text-tertiary); }
.autosave:empty { display: none; }
.autosave--ok { color: var(--status-healthy-text); }
.autosave--error { color: var(--status-critical-text); }
.order-line__words { margin: 0; font-size: var(--text-sm); color: var(--text-tertiary); }
/* D-314: the table's own controls in the panel's top row — the Default ·
   Dollars · Hours switch and Customize, left-aligned so the menu opens
   rightward from its button. D-437: the fold links, when two or more rows
   fold, sit at the row's far end. */
.tablebar { padding: 12px 24px; gap: 12px; }
.tablebar__folds { margin-left: auto; }
.tablebar__folds:empty { display: none; }
.tablebar .menu__panel { left: 0; right: auto; }
/* D-337: Ceilings by task's row sits under its search, which already
   leaves the gap above it. */
.tablebar--ledger { padding-top: 0; }
@media (prefers-reduced-motion: reduce) { .row-move { transition: none; } }
/* While a boundary is being dragged the pointer owns it: no text selection,
   and no hover states flickering down the table. */
.th-grip.is-dragging { user-select: none; }
@media (prefers-reduced-motion: reduce) { .th-grip::after { transition: none; } }
.th-sort__dir { font-size: var(--text-xs); min-width: 8px; }
th.right .th-sort { flex-direction: row-reverse; }
td { padding: var(--cell-py) var(--cell-px); font-size: var(--fs-body); line-height: var(--lh-body); vertical-align: top; }
tbody tr { border-bottom: 1px solid var(--border-subtle); }
tbody tr:last-child { border-bottom: 0; }
.right { text-align: right; }
.stack__lead { font-size: var(--text-sm); font-weight: 600; }
.stack__sub { font-size: var(--text-sm); color: var(--text-tertiary); }
.muted { color: var(--text-tertiary); }
.row--flagged { background: var(--row-flagged); }
.row--skip td { background: var(--status-critical-tint); }
.row--total { background: var(--bg-subtle); font-weight: 600; }
/* D-137: a task summarising the ceilings beneath it. It carries NO background
   of its own: a subtotal row can also be flagged, and a tint on the cell would
   paint over the risk row underneath it. The weight and its own sub-line —
   "2 ceilings beneath" — are what say it is a sum rather than a ceiling. */
.row--subtotal td { font-weight: 500; color: var(--text-secondary); }
/* D-139: a projection zeroed because its task has finished. Dotted, not
   muted: the figure is exact, it is the REASON that needs explaining, and
   the title says it. */
td.is-closedout { text-decoration: underline dotted var(--status-closed); text-underline-offset: 3px; cursor: help; }
.row--total td { padding-top: 13px; padding-bottom: 13px; font-weight: 600; }
.rowlink { color: inherit; font-weight: 600; }
.rowlink:hover { color: var(--signal); }

.pos { color: var(--status-healthy-text); font-weight: 500; }
.neg { color: var(--status-critical-text); font-weight: 500; }
.neg-soft { color: var(--status-watch-text); font-weight: 500; }

/* ------------------------------------------------------------ status pills */

.pill {
  display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px;
  border-radius: var(--r-full); font-size: var(--text-sm); font-weight: 600; line-height: 1;
  white-space: nowrap;
}
/* The heat scale (D-250), worn by two vocabularies: a project's status and
   the Alerts page's own critical/high/medium/low (D-132). Tint behind, the
   text step in front, and the tone's shape beside the word in its solid
   (D-251): hue, shape and word carry the reading together, so At risk and
   Critical no longer need to share a colour (D-072 superseded). The metadata
   keys and Low share one neutral look; "no ceilings" reads as watch. */
.pill--shaped { padding: 0 10px 0 8px; }
.pill__glyph { display: inline-flex; flex: none; width: 10px; height: 10px; }
.pill__glyph svg { display: block; width: 100%; height: 100%; }
.pill--critical { background: var(--status-critical-tint); color: var(--status-critical-text); }
.pill--critical .pill__glyph { color: var(--status-critical); }
.pill--action .pill__glyph, .pill--high .pill__glyph { color: var(--status-risk); }
.pill--watch .pill__glyph, .pill--medium .pill__glyph { color: var(--status-watch); }
.pill--healthy .pill__glyph { color: var(--status-healthy); }
/* Provisional: figures not yet checked. A dashed edge as well as the shape. */
.pill--provisional {
  background: var(--status-provisional-tint); color: var(--status-provisional-text);
  border: 1px dashed var(--status-provisional);
}
.pill--provisional .pill__glyph { color: var(--status-provisional); }
/* Closed: a fact about the project, not a warning. */
.pill--closed { background: var(--status-closed-tint); color: var(--status-closed-text); }
.pill--closed .pill__glyph { color: var(--status-closed); }
.pill--action, .pill--high { background: var(--status-risk-tint); color: var(--status-risk-text); }
.pill--watch, .pill--medium { background: var(--status-watch-tint); color: var(--status-watch-text); }
.pill--healthy { background: var(--status-healthy-tint); color: var(--status-healthy-text); }
.pill--unmapped, .pill--off, .pill--info, .pill--low { background: var(--status-closed-tint); color: var(--status-closed-text); }
.pill--unbounded { background: var(--status-watch-tint); color: var(--status-watch-text); }
/* A contract type (T&M, CPFF, FFP) is a tag, not a status: an outline. */
.pill--type {
  height: 20px; padding: 0 7px; border-radius: var(--r-1); font-size: var(--text-sm); font-weight: 500;
  background: transparent; color: var(--text-secondary); border: 1px solid var(--border-strong);
}
/* D-236: a billing type beside a name — the Portfolio's project, a task on
   Ceilings by task. The name keeps its line; the pill sits on it. */
.cluster--name { gap: 6px; flex-wrap: wrap; }
.pill--sm { height: 20px; font-size: var(--text-sm); padding: 0 8px; }
.pill--sm.pill--shaped { padding: 0 8px 0 6px; }
.pill--sm .pill__glyph { width: 8px; height: 8px; }


/* ------------------------------------------------------------ tickets (D-345) */

/* A ticket page: the crumb line above the head, the thread as a column of
   chat messages (the chat's own classes), and the foot with the reporter's
   words and the composer. BurnSpan's avatar wears the signal so a reply
   from support reads apart from a colleague's at a glance. */
.ticket > .breadcrumb, .page > .breadcrumb + .ticket { margin-top: 0; }
.ticket .pagehead__actions { align-self: flex-start; }
.ticket .pagehead__text .cluster { margin: 6px 0; }
.ticket__thread { display: flex; flex-direction: column; padding: 8px 0; }
.ticket__comment { padding: 12px 24px; }
.ticket__comment + .ticket__comment { border-top: 1px solid var(--border-subtle); }
.ticket__comment--opening .chat__text { font-size: var(--text-base); }
/* D-409: a blank line in a ticket reads as one — a paragraph, a list or a
   quote stands a line apart, not the chat's 6px. */
.ticket__comment .chat__text > * + *, .ticket__suggestion .chat__text > * + * { margin-top: 0.85em; }
/* The Aa toggle and the hint beside it, under a ticket's box. */
.ticket__tools { gap: 4px 8px; align-items: center; }
.ticket__files { margin-top: 8px; align-items: flex-start; }
.ticket__note { color: var(--text-secondary); font-weight: 500; }
/* D-395: a status change without words, between the comments. */
.ticket__event { display: flex; align-items: center; gap: 12px; padding: 6px 24px; font-size: var(--text-sm); color: var(--text-secondary); }
.ticket__event::before, .ticket__event::after { content: ''; flex: 1; border-top: 1px solid var(--border-subtle); }
.ticket__event time { color: var(--text-tertiary); }
.chat__avatar--burnspan { background: var(--signal); color: var(--action-on-primary); }
.ticket__foot { border-top: 1px solid var(--border-subtle); }
.ticket__composer textarea { width: 100%; font-size: var(--text-base); }
.ticket__controls { padding-top: 6px; border-top: 1px solid var(--border-subtle); }
.ticket__controls select, .ticket__composer select { min-height: 32px; font-size: var(--text-sm); }
@media (pointer: coarse) { .ticket__controls select, .ticket__composer select { min-height: var(--control-h-sm); } }
.ticket__row--waiting td:first-child { box-shadow: inset 3px 0 0 var(--status-watch); }
/* D-347: the assistant's draft for BurnSpan, the links a reply rests on,
   and the four figures. */
.ticket__suggestion { padding: 14px 16px; border: 1px dashed var(--border-strong); border-radius: var(--r-2); background: var(--bg-subtle); }
.ticket__cites { margin-top: 4px; }
.ticket__cites a { color: var(--signal-on-subtle); }
.ticket__metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px 24px; }
.ticket__metric { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ticket__figure { font-family: var(--font-mono); font-size: var(--text-xl); font-weight: 500; color: var(--text-primary); }
/* D-348: the GitHub panel's parts, and a secret shown once. */
.ticket__rule { border-top: 1px solid var(--border-subtle); }
/* D-349: a reply on the reader's own ticket, not yet looked at. The dot is
   the signal blue the chat's count wears, never the ember accent (the bar
   holds no status). */
.topbar__avatar { position: relative; }
.topbar__dot { position: absolute; top: -2px; right: -2px; width: 10px; height: 10px; border-radius: 50%; background: var(--signal); box-shadow: 0 0 0 2px var(--bg-canvas); }
.topbar__dot[hidden] { display: none; }
.ticket__unread { font-weight: 600; }
.alert code { display: block; font-family: var(--font-mono); font-size: var(--text-sm); overflow-wrap: anywhere; user-select: all; }

/* --------------------------------------------------------- position strip */

.strip { display: flex; flex-direction: column; gap: 16px; padding: 20px 24px; }
.strip__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 24px; }
/* D-241: four to a row on a desktop (the lg rule in the responsive section),
   so seven or eight tiles make even rows. */
.stat { display: flex; flex-direction: column; gap: 4px; }
.stat__value {
  font-family: var(--font-mono); font-size: var(--text-xl); line-height: 26px; font-weight: 650; font-variant-numeric: tabular-nums;
}
/* D-428: a state in a figure's place ("Needs ceilings", "Off") reads in the
   body face, on the figure's line, so a row of tiles keeps its rhythm. */
.stat__value--words { font-family: var(--font-sans); font-size: var(--text-base); font-weight: 600; line-height: 26px; }
.stat__value--muted { color: var(--text-secondary); }
.stat__value--watch { color: var(--status-watch-text); }
.stat__value--neg { color: var(--status-critical-text); }
.stat__value--pos { color: var(--status-healthy-text); }
.stat__sub { font-size: var(--text-sm); color: var(--text-tertiary); min-height: 1em; }
.strip__rule { height: 1px; background: var(--border-subtle); }

/* A simple progress bar (the connection wizard's Landed): a track and its
   fill. The position strip's meters became spans (D-254). */
.meter { display: flex; flex-direction: column; gap: 6px; }
.meter__caption { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.meter__figures { display: flex; gap: 16px; flex-wrap: wrap; font-size: var(--text-sm); color: var(--text-tertiary); }
.meter__figures .num { color: var(--text-secondary); }
.meter__track { position: relative; height: 10px; border-radius: var(--r-1); background: var(--bg-sunken); }
.meter__fill { position: absolute; top: 0; bottom: 0; border-radius: var(--r-1); background: var(--chart-actual); }

/* D-254: the span (BurnSpan §6). One bar per ceiling across the period of
   performance: ink to today, hatched to the runout (what the ceiling still
   covers), then the gap to the period's end in the status tint with a dashed
   edge — the size of the modification needed. The runout is a rule in the
   status colour with its date above; the early-warning point a tick with its
   date below. */
.spans { display: flex; flex-direction: column; gap: 18px; }
.span { display: flex; flex-direction: column; gap: 2px; }
.span--critical { --span-solid: var(--status-critical); --span-text: var(--status-critical-text); --span-tint: var(--status-critical-tint); }
.span--action { --span-solid: var(--status-risk); --span-text: var(--status-risk-text); --span-tint: var(--status-risk-tint); }
.span--watch { --span-solid: var(--status-watch); --span-text: var(--status-watch-text); --span-tint: var(--status-watch-tint); }
.span--healthy { --span-solid: var(--status-healthy); --span-text: var(--status-healthy-text); --span-tint: var(--status-healthy-tint); }
.span__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.span__binds { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }
.span__figures { margin-left: auto; display: flex; gap: 14px; flex-wrap: wrap; font-size: var(--text-sm); color: var(--text-tertiary); }
.span__figures .num { color: var(--text-secondary); }
.span__track { position: relative; height: 12px; margin: 8px 0; border-radius: 2px; background: var(--bg-sunken); }
/* Room for a runout date above and an early-warning date below, only where there is one. */
.span--dated .span__track { margin-top: 24px; }
.span--marked .span__track { margin-bottom: 22px; }
.span__track:focus-visible { outline: 2px solid var(--signal); outline-offset: 6px; }
.span__elapsed, .span__covered, .span__gap { position: absolute; top: 0; bottom: 0; }
.span__elapsed { background: var(--text-primary); border-radius: 2px 0 0 2px; }
.span__covered { background: repeating-linear-gradient(135deg, var(--hatch-a) 0 2px, var(--hatch-b) 2px 5px); }
.span__gap { background: var(--span-tint); border: 1.5px dashed var(--span-solid); border-radius: 0 2px 2px 0; }
.span__runout { position: absolute; top: -5px; bottom: -5px; width: 2px; margin-left: -1px; background: var(--span-solid); }
.span__date { position: absolute; bottom: calc(100% + 6px); font-size: var(--text-sm); font-weight: 600; color: var(--span-text); white-space: nowrap; }
.span__mark { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--text-tertiary); box-shadow: 0 0 0 1px var(--bg-surface); }
.span__marklabel { position: absolute; top: calc(100% + 6px); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-tertiary); white-space: nowrap; }
/* D-284: the dates carry weight — they are what the bar is drawn across. */
.span__scale { display: flex; justify-content: space-between; font-size: var(--text-sm); font-weight: 500; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.panel__head .pop__dates, .band__words .pop__dates { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }

/* D-274: the gauge — the mark's own dial, read against a goal — and the
   Position panel's bands. Zones wear the status tints with a thin edge in
   the solid; the needle is ink (the logo as supplied; the ember needle
   stays on the mark). The dial sits beside its words so a band of gauges
   is no taller than a row of tiles. */
.band { display: flex; flex-direction: column; gap: 12px; }
.band__label { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.band__words { font-size: var(--text-sm); color: var(--text-tertiary); }
.gauges { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 24px; }
.gauge { display: flex; align-items: center; gap: 18px; min-width: 0; }
.gauge__dialwrap { display: flex; flex-direction: column; gap: 4px; flex: none; width: 150px; }
.gauge__dial { display: block; width: 100%; height: auto; }
/* D-436: a gauge with nothing to read against is its figure and words, and
   how to set the target; no dial. */
.gauge--nodial { align-items: flex-start; }
.gauge__set { font-size: var(--text-sm); }
.gauge__dial:focus-visible { outline: 2px solid var(--signal); outline-offset: 4px; }
.gauge__text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.gauge__figure { font-family: var(--font-mono); font-size: var(--text-xl); line-height: 26px; font-weight: 650; font-variant-numeric: tabular-nums; }
.gauge__word { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); font-weight: 600; color: var(--text-tertiary); }
.gauge__word--healthy { color: var(--status-healthy-text); }
.gauge__word--watch { color: var(--status-watch-text); }
.gauge__word--critical { color: var(--status-critical-text); }
.gauge__glyph { display: inline-flex; line-height: 0; }
.gauge__glyph svg { width: 10px; height: 10px; }
.gauge__sub { font-size: var(--text-sm); line-height: 16px; color: var(--text-tertiary); }
.gauge__marks { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: var(--text-xs); line-height: 16px; color: var(--text-secondary); }
.gauge__marks i { font-style: normal; display: inline-flex; align-items: center; gap: 4px; }
.gauge__marks i::before { content: ""; display: inline-block; width: 2px; height: 10px; background: var(--text-secondary); }
.gauge__zone--healthy { fill: var(--status-healthy-tint); }
.gauge__zone--watch { fill: var(--status-watch-tint); }
.gauge__zone--critical { fill: var(--status-critical-tint); }
.gauge__edge--healthy { fill: var(--status-healthy); }
.gauge__edge--watch { fill: var(--status-watch); }
.gauge__edge--critical { fill: var(--status-critical); }
.gauge__mark { stroke: var(--text-secondary); stroke-width: 1.4; stroke-linecap: round; }
.gauge__end { font-family: var(--font-sans); font-size: 5.5px /* size: svg units, the dial is a 100-wide viewBox */; font-weight: 500; fill: var(--text-tertiary); }
.gauge__needle { fill: var(--text-primary); }
/* D-312: the needle is drawn at the dial's left end and turned to its
   reading about the hub (50,50 in the dial's own units), so a new reading
   sweeps it there; the hub circle shares the colour and never moves. The
   words beneath fade in when they change. Reduced motion stops both, with
   every other transition on the page. */
path.gauge__needle--pointer { transform-box: view-box; transform-origin: 50px 50px; transition: transform var(--motion-slow) var(--ease-standard); }
.gauge__needle--none { visibility: hidden; }
@keyframes gauge-swap { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: none; } }
.gauge__word.is-swapped, .gauge__sub.is-swapped { animation: gauge-swap var(--motion-base) var(--ease-standard); }
/* D-278: the Goals panel — the company's years as fields in a row, the
   project grid with its names held still while the years scroll. */
.goals__company { display: flex; flex-wrap: wrap; gap: 12px 24px; }
/* The goal cells are controls a finger presses: the control height, which
   the touch band raises to 44 px (BurnSpan §5). */
.goals__company .fee__input, .goals__grid .fee__input, .fee__grid--down .fee__input { height: var(--control-h); }
/* D-285: a year whose project goals, added, run past the company's. */
.goals__flag { display: inline-flex; margin-left: 8px; vertical-align: middle; }
.goals__over { color: var(--status-watch-text); }
.goals__group { display: flex; flex-direction: column; gap: 8px; }
/* D-316: the heading line holds Expand all / Collapse all; a child's name is
   indented under its parent (the first cell here is a sticky th, not a td). */
.goals__headline { justify-content: space-between; }
.goals__headline h3 { margin: 0; }
.goals__grid tr.row--child th.measure { padding-left: 34px; }
.goals__block--child { margin-left: 16px; }
/* A covered cell: the other side of its family holds the year's target. */
.fee__input:disabled, .fee__input--covered {
  background: var(--bg-subtle); color: var(--text-secondary); border-color: var(--border-default); cursor: default;
}
.goals__grid th.measure { position: sticky; left: 0; background: var(--bg-surface); z-index: 1; text-align: left; font-weight: 500; white-space: nowrap; }
/* D-426: a row's name reads as a cell, not as a column header's capitals. */
.goals__grid tbody th.measure { text-transform: none; letter-spacing: 0; font-size: var(--text-sm); color: var(--text-primary); }
.goals__grid td.measure { position: sticky; left: 0; background: var(--bg-surface); z-index: 1; font-weight: 600; }
/* D-276: the Company panel on the Portfolio — the same band, larger. */
.company { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 24px; padding: 20px 24px; }
.company .gauge__dialwrap { width: 200px; }
/* D-312: the head carries the Forecast · Actuals switch at its right; the
   count takes the room between, and on a phone the switch drops under. */
.company__head { align-items: center; }
.company__head > span:not(.pill) { flex: 1 1 auto; min-width: 0; }
/* D-333: the Targets and runout panel — the Scenario and Task choices on a
   row of their own under the head, at the left, above the gauges they move;
   the chart's own controls on their row above the drawings, at the right. */
.targets__pick { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 0 24px 4px; }
.strip--gauges { padding-top: 12px; }
.chart__head--tools { justify-content: flex-end; }

/* ------------------------------------------------------------------ chart */

.chart { padding: 18px 24px 20px; display: flex; flex-direction: column; gap: 16px; }
.chart__head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.chart__head h2 { font-size: var(--text-lg); line-height: 22px; font-weight: 600; margin: 0; }
.chart__head span { font-size: var(--text-sm); color: var(--text-tertiary); }
.chart__legend { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.chart__key { display: flex; align-items: center; gap: 6px; font-size: var(--text-sm); color: var(--text-secondary); }
/* D-233: the task picker's checklist — "Several tasks…" — hangs under it. */
.chart__pickwrap { position: relative; }
.chart__pick {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; width: min(380px, 90vw);
  display: flex; flex-direction: column; gap: 10px; padding: 12px 14px;
}
.chart__pick[hidden] { display: none; }
.chart__pick-list { display: flex; flex-direction: column; gap: 4px; max-height: 280px; overflow-y: auto; }
.chart__pick-row { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--text-secondary); }
.chart__pick-row input { margin: 0; }
/* The stage holds the tooltip so it can hang beside the plot without being
   clipped by the frame's scroll box. */
.chart__stage { position: relative; }
.chart__frame { overflow-x: auto; }
/* D-257: the chart is drawn at the frame's width (flirChart measures it),
   so the floor is where the frame starts to scroll instead. */
.chart__frame svg { display: block; min-width: 320px; width: 100%; height: auto; }
.chart__controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.chart__scope {
  padding: 6px 10px; border: 1px solid var(--border-strong); border-radius: var(--r-2);
  background: var(--bg-surface); font: inherit; font-size: var(--text-sm); color: var(--text-secondary);
  max-width: 260px;
}
/* The bid is opt-in: it is drawn on request and never sets the scale. */
.chart__bid {
  display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); color: var(--text-secondary);
  cursor: pointer;
}
.chart__bid input { margin: 0; }
/* Names the lower panel without competing with the chart's own heading. */
.chart__sub { border-top: 1px solid var(--border-default); padding-top: 12px; }
/* Hover readout. Anchored above the hovered month and nudged left of its own
   centre, so it never covers the marks it explains. */
.chart__tip {
  position: absolute; pointer-events: none;
  background: var(--bg-raised); border: 1px solid var(--border-default); border-radius: var(--r-3);
  box-shadow: var(--e2); padding: 8px 10px; z-index: 4;
  display: flex; flex-direction: column; gap: 3px; font-size: var(--text-sm); color: var(--text-secondary);
  white-space: nowrap;
}
.chart__tip strong { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }
.chart__tip .num { font-weight: 500; color: var(--text-primary); }
/* The modification block (D-068): a ruled header, then rows that wrap —
   descriptions are sentences — while the figure rows above stay on one line. */
.chart__tip .chart__tip-sub { margin-top: 3px; padding-top: 5px; border-top: 1px solid var(--border-default); }
.chart__tip .chart__mod { white-space: normal; max-width: 300px; line-height: 1.4; display: flex; flex-direction: column; }
.chart__tip .chart__mod b { font-weight: 600; color: var(--text-primary); }
.chart__tip .muted { color: var(--text-tertiary); }
.chart__dot { transition: r var(--motion-fast) var(--ease-out); }

/* ------------------------------------------------------- states & loading */

.state {
  padding: 48px 24px; text-align: center; color: var(--text-tertiary); font-size: var(--text-base);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.state strong { color: var(--text-primary); font-weight: 600; font-size: var(--text-base); }
/* D-271: the lost page shows the mark, needle and all — no data sits near it. */
.state__mark svg { display: block; width: 64px; height: auto; margin-bottom: 4px; }

.banner {
  display: flex; align-items: flex-start; gap: 10px; padding: 12px 16px;
  border-radius: var(--r-3); font-size: var(--text-sm); line-height: 1.55;
  background: var(--status-watch-tint); color: var(--status-watch-text);
}
.banner strong { font-weight: 600; }
/* BurnSpan's provisional surface: its tint, a fine hatch and a dashed edge
   (D-251). Said with the word, never with the look alone. */
.banner--provisional {
  align-items: center;
  background-color: var(--status-provisional-tint); color: var(--status-provisional-text);
  background-image: repeating-linear-gradient(135deg, transparent 0 8px,
    color-mix(in srgb, var(--status-provisional) 8%, transparent) 8px 11px);
  border: 1px dashed var(--status-provisional);
}

/* D-130: the rules as they will read, and what they would do to today's
   portfolio, as one quiet readout. Neutral rather than toned — it is not a
   warning, it is the consequence of the controls above it, and it has to be
   legible while someone is still deciding. */
.rulesout {
  border: 1px solid var(--border-default); border-radius: var(--r-3);
  background: var(--bg-subtle); padding: 13px 16px;
  display: flex; flex-direction: column; gap: 6px;
}
.rulesout__says { font-size: var(--text-sm); line-height: 1.55; color: var(--text-primary); }
.rulesout__count { font-size: var(--text-sm); line-height: 1.55; color: var(--text-secondary); }

/* An explanatory note behind an ⓘ (D-099). Hover shows it, so does keyboard
   focus, and a tap toggles it — the button is the control, the tip is text
   only. Nothing load-bearing goes here (D-070): the print beside it names
   the thing; this says why. */
.info { position: relative; display: inline-flex; align-items: center; }
.info__btn {
  width: 16px; height: 16px; padding: 0; border-radius: 50%;
  border: 1px solid var(--text-tertiary); background: transparent; color: var(--text-tertiary);
  font: 600 var(--text-xs)/1 var(--font-sans); font-style: italic; cursor: help;
  display: inline-flex; align-items: center; justify-content: center;
}
.info__btn:hover, .info__btn:focus-visible, .info--open .info__btn { color: var(--text-primary); border-color: var(--text-secondary); }
.info__tip {
  display: none; position: absolute; top: calc(100% + 6px); left: 0; z-index: 5;
  width: max-content; max-width: min(360px, calc(100vw - 32px)); padding: 10px 12px;
  font-size: var(--text-sm); font-weight: 400; line-height: 1.55; color: var(--text-secondary); text-align: left;
  /* A tip inside a table header would otherwise inherit its small caps and
     letter-spacing: the tip is a sentence, not a label (D-130). */
  text-transform: none; letter-spacing: normal; white-space: normal;
  background: var(--bg-raised); border: 1px solid var(--border-default); border-radius: var(--r-3);
  box-shadow: var(--e2);
}
/* D-130: on the last column a left-anchored tip runs off the table, the same
   way a row menu's panel does (D-062). Anchoring it to the right edge keeps
   it on screen; the modifier is on the root so the button stays put. */
.info--right .info__tip { left: auto; right: 0; }
.info:hover .info__tip, .info--focus .info__tip, .info--open .info__tip { display: block; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ------------------------------------------------------------------- users */

.drawer { background: var(--bg-subtle); border-bottom: 1px solid var(--border-default); }
.drawer__inner { padding: 20px 24px; display: flex; flex-direction: column; gap: 18px; }
/* D-105: the column owns the rhythm. Its children bring no margins of their
   own — they had been keeping the browser's, stacked on top of the gap — and
   a slot with nothing in it takes no room, so an empty error box no longer
   opens a hole above the content it belongs to. */
.drawer__inner h3, .drawer__inner p { margin: 0; }
.drawer__inner > :empty { display: none; }
/* A panel heading is 15px (D-070). `.drawer h3` below only ever matched the
   row drawers, so these fell back to the browser's 1.17em; row drawers keep
   their 13px, their column being no direct child of a panel or sheet. */
.panel > .drawer__inner h3, .sheet__scroll > .drawer__inner h3 { font-size: var(--text-lg); font-weight: 600; }
.drawer__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
/* D-088: two rows of one form read as a form only when their columns
   line up, so the what-if line drawer fixes the track count. */
.drawer__grid--four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.drawer__grid--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* D-117: City · State · ZIP. Not three equal tracks — the city is the long one
   and a state is two characters, so equal columns would waste the row. */
.drawer__grid--addr { grid-template-columns: 2fr 1fr 1fr; }
/* D-094: the three ceilings one modification can move, side by side and
   equal — none of them is the default. */
.drawer__amounts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.drawer__actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.drawer h3 { font-size: var(--text-sm); font-weight: 600; margin: 0; }

.field select {
  height: var(--control-h); border: 1px solid var(--border-strong); border-radius: var(--r-2);
  padding: 0 10px; font: inherit; font-size: var(--text-sm); background: var(--bg-surface); color: var(--text-primary);
  /* D-318: a select named after a project never widens its field or the page. */
  max-width: 100%;
}
.drawer__grid > .field { min-width: 0; }
.field__hint { font-size: var(--text-sm); color: var(--text-tertiary); line-height: 1.5; }

.access__note { font-size: var(--text-sm); color: var(--text-secondary); padding: 10px 0; }

/* ------------------------------------------------------- on/off switch */
.switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.switch input {
  position: absolute; opacity: 0; width: 0; height: 0;   /* keyboard-reachable, not drawn */
}
.switch__track {
  position: relative; flex: none; width: 36px; height: 20px; border-radius: var(--r-full);
  background: var(--border-strong); border: 1px solid var(--border-strong);
  transition: background var(--motion-fast) var(--ease-out), border-color var(--motion-fast) var(--ease-out);
}
.switch__knob {
  position: absolute; top: 1px; left: 1px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--switch-knob); box-shadow: var(--e1);
  transition: transform var(--motion-fast) var(--ease-out);
}
.switch input:checked + .switch__track { background: var(--action-primary); border-color: var(--action-primary); }
.switch input:checked + .switch__track .switch__knob { transform: translateX(16px); background: var(--action-on-primary); }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--signal); outline-offset: 2px; }
/* Busy is a pulse, not a fade: a half-opacity switch just looks pale, which
   is what made a slow save read as a dead page. */
.switch[aria-busy="true"] .switch__track { animation: switch-busy 1.1s ease-in-out infinite; }
@keyframes switch-busy { 50% { opacity: .5; } }
.switch:has(input:disabled) { cursor: progress; }
/* Wide enough for the longest thing it says, so the column cannot jitter. */
.switch__text { font-size: var(--text-sm); color: var(--text-tertiary); min-width: 82px; }
.switch[aria-busy="true"] .switch__text { font-style: italic; }

@media (prefers-reduced-motion: reduce) {
  .switch__track, .switch__knob { transition: none; }
  .switch[aria-busy="true"] .switch__track { animation: none; opacity: .6; }
}

/* ------------------------------------------------- families (D-058) */
/* A child row is indented, not boxed: the parent above it is the frame. */
.row--child td:first-child { padding-left: 34px; }
/* D-173: a program's projects sit one step in, their people two. */
.row--nested td:first-child { padding-left: 34px; }
.row--grandchild td:first-child { padding-left: 58px; }
/* D-235: a person under an organization, under a project, under a program. */
.row--depth3 td:first-child { padding-left: 82px; }
/* D-402: a task under its parent tasks, in a report — one step a level. */
.row--depth4 td:first-child { padding-left: 106px; }
.row--depth5 td:first-child { padding-left: 130px; }
.row--depth6 td:first-child { padding-left: 154px; }
.disclosure__row { display: inline-flex; align-items: center; gap: 4px; margin-left: -4px; }
.disclosure {
  /* flex: none — a longer line beside it (D-408's notes) must never squeeze
     the chevron, or its 44px touch target with it. */
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 20px; height: 20px; padding: 0; border: 0; border-radius: 4px;
  background: transparent; cursor: pointer; color: inherit;
}
.disclosure:hover { background: var(--bg-subtle); }
/* The room a chevron would take, for a row that has none (D-137). */
.disclosure__gap { display: inline-block; flex: 0 0 20px; width: 20px; }
.disclosure:focus-visible { outline: 2px solid var(--signal); outline-offset: 1px; }
.disclosure svg { transition: transform var(--motion-fast) var(--ease-out); }
.disclosure[aria-expanded="true"] svg { transform: rotate(90deg); }
.family__line { display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline; }

/* D-119: the grant picker's table. Denser than a page table — it is a form
   control — and it borrows .row--child, .disclosure and .disclosure__row from
   the families above, so a granted family reads the way the Sync page draws
   the same one. */
.access {
  border: 1px solid var(--border-strong); border-radius: var(--r-2);
  background: var(--bg-surface); max-height: 300px; overflow-y: auto;
}
.access td { padding: 6px 10px; vertical-align: middle; }
.access td:first-child { width: 1%; white-space: nowrap; padding-left: 12px; }
.access .row--child td:first-child { padding-left: 34px; }
.access label { cursor: pointer; }
/* Holds the chevron's place on a row that has none, so every box sits at one x. */
.access__spacer { display: inline-block; width: 20px; }
.children__tick { display: inline-flex; align-items: center; }
/* D-218: Projects under this contract. The grant table's frame, taller in a
   sheet, and a brand edge on every row the Save would change, so the change
   is visible while scrolling. The search keeps its size inside the sheet's
   flex column (the D-098 squeeze). */
.access.children { max-height: min(520px, 56vh); }
.access.children thead th { position: sticky; top: 0; background: var(--bg-surface); font-size: var(--text-xs); text-align: left; padding: 6px 10px; }
.access .children__row--changing td:first-child { box-shadow: inset 3px 0 0 var(--signal); }
.access .children__row--changing { background: var(--signal-subtle); }
.children__search { flex: 1 1 auto; }
@media (prefers-reduced-motion: reduce) { .disclosure svg { transition: none; } }

/* ------------------------------------------------- connection wizard (D-038) */
.steps { display: flex; align-items: center; list-style: none; margin: 0 0 4px; padding: 0; }
.steps__item { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--text-tertiary); }
.steps__item + .steps__item::before {
  content: ""; width: 24px; height: 1px; background: var(--border-default); margin: 0 14px;
}
.steps__dot {
  width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; border: 1px solid var(--border-strong); background: var(--bg-surface);
  font-size: var(--text-xs); font-weight: 600;
}
.steps__item--current { color: var(--text-primary); font-weight: 600; }
.steps__item--current .steps__dot { background: var(--action-primary); border-color: var(--action-primary); color: var(--action-on-primary); }
.steps__item--done .steps__dot { background: var(--signal-subtle); border-color: var(--signal); color: var(--signal); }
.steps__item--done .btn--link { display: flex; align-items: center; gap: 8px; color: var(--text-tertiary); }

.wizard__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
/* D-104: a field is a control, not a banner. The connection fields — in the
   wizard and on the settings page — hold to a readable measure; the grid
   inside pairs them up. D-105 extends it to the wizard's whole step body,
   which is why the name is no longer about fields. Deliberately not
   app-wide (the owner's call). */
.measure { max-width: 640px; }
.picker__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: var(--text-sm); margin-bottom: 8px; }
.picker__head label { display: flex; align-items: center; gap: 8px; cursor: pointer; font-weight: 600; }
.picker {
  border: 1px solid var(--border-strong); border-radius: var(--r-2); background: var(--bg-surface);
  max-height: 340px; overflow-y: auto; padding: 2px 12px;
}
.picker__row { display: flex; align-items: center; gap: 10px; padding: 8px 0; font-size: var(--text-sm); cursor: pointer; }
.picker__row + .picker__row { border-top: 1px solid var(--border-default); }
.picker__row input { margin: 0; accent-color: var(--action-primary); }
.picker__meta { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.btn--sm { height: var(--control-h-sm); padding: 0 12px; font-size: var(--text-sm); }
.btn--link {
  background: none; border: 0; color: var(--signal); font: inherit; font-size: var(--text-sm);
  cursor: pointer; padding: 3px 0; text-align: left;
}
.btn--link:hover { color: var(--signal-hover); text-decoration: underline; }
.btn--danger { color: var(--danger); }
/* A destructive PRIMARY action — deleting an organization is the only one
   (D-050). Its disabled state says "blocked", not the base button's
   "working": the button is dark until the identifier has been typed out. */
.btn.btn--danger { background: var(--danger); color: var(--danger-on); }
.btn.btn--danger:hover:not(:disabled) { background: var(--danger-hover); }
.btn.btn--danger:disabled { cursor: not-allowed; }
.rowactions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.tokenbox {
  display: flex; flex-direction: column; gap: 8px; padding: 14px 16px;
  background: var(--bg-sunken); border: 1px solid var(--border-default); border-radius: var(--r-3);
}
.tokenbox__label { font-size: var(--text-sm); font-weight: 600; color: var(--text-secondary); }
.tokenbox__row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tokenbox input {
  flex: 1 1 340px; min-width: 0; height: 34px; padding: 0 10px; font-size: var(--text-sm);
  font-family: var(--font-mono);
  border: 1px solid var(--border-strong); border-radius: var(--r-2); background: var(--bg-surface); color: var(--text-primary);
}
.tokenbox__note { font-size: var(--text-sm); color: var(--text-secondary); line-height: 1.5; margin: 0; }

/* ------------------------------------------------ two-factor set-up (D-178) */
.enroll { display: flex; flex-direction: column; gap: 16px; }
.enroll__steps { margin: 0; padding-left: 20px; font-size: var(--text-sm); color: var(--text-secondary); line-height: 1.55; }
.enroll__steps li + li { margin-top: 4px; }
/* Dark on white whatever the theme: scanners read a QR code that way round. */
.enroll__qr { display: flex; }
.qr {
  width: 184px; height: 184px; color: #0E1116; background: #FFFFFF;
  border: 1px solid var(--border-default); border-radius: var(--r-2);
}
/* The set-up screen carries a QR code and a secret key: a little more room. */
.signin__panel:has(.enroll) { max-width: 480px; }

/* ----------------------------------------------------- contract modifications */

/* Right-docked sheet on the native <dialog> top layer. display goes on [open]
   only — an unconditional display:flex would override the UA's display:none
   and leave closed dialogs permanently visible. min(…, 100%) makes the sheet
   full-width on narrow viewports without a media query. */
dialog.sheet {
  position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0;
  width: min(460px, 100%); max-width: 100%; height: 100%; max-height: none;
  border: 0; border-left: 1px solid var(--border-default); background: var(--bg-raised);
  color: var(--text-primary);
  box-shadow: var(--e3);
}
dialog.sheet[open] { display: flex; flex-direction: column; animation: sheet-in var(--motion-slow) var(--ease-standard); }
/* The literal first: ::backdrop reads custom properties only in recent
   browsers (Chrome 122, Safari 17.4). */
dialog.sheet::backdrop { background: rgba(5, 20, 38, .48); background: var(--scrim); }
@keyframes sheet-in { from { transform: translateX(24px); opacity: 0; } }
/* `dialog.sheet` above sets the width at specificity (0,1,1), so the modifier
   has to match it or it silently loses — which is what it had been doing. */
dialog.sheet--wide { width: min(760px, 100%); }
/* D-115: the record sheet holds a task tree with six levels of indent beside
   four figure columns; the owner allowed the width. */
dialog.sheet--record { width: min(880px, 100%); }
/* A centred modal for the chart (D-067): the page stays where it is behind
   it, and Close or Escape returns to it. Same top-layer rules as the sheet. */
dialog.modal {
  position: fixed; inset: 0; margin: auto; padding: 0;
  width: min(1240px, calc(100% - 32px)); max-width: calc(100% - 32px);
  max-height: calc(100% - 32px); height: auto;
  border: 1px solid var(--border-default); border-radius: var(--r-4); background: var(--bg-raised); color: var(--text-primary);
  box-shadow: var(--e3);
}
dialog.modal[open] { display: flex; flex-direction: column; animation: modal-in var(--motion-base) var(--ease-standard); }
dialog.modal::backdrop { background: rgba(5, 20, 38, .48); background: var(--scrim); }
/* A chart in the modal sits on the raised ground: its label halos follow. */
dialog.modal .chart { --chart-halo: var(--bg-raised); }
@keyframes modal-in { from { transform: translateY(8px); opacity: 0; } }
.sheet__head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 24px; border-bottom: 1px solid var(--border-default); flex: none;
}
.sheet__head h2 { font-size: var(--text-lg); line-height: 22px; font-weight: 600; margin: 0; }
.sheet__scroll { overflow-y: auto; flex: 1 1 auto; }
/* Two forms in one sheet (My account, D-122) are two sections, not one long
   one: the rule says where the first stops answering for the second. */
.sheet__scroll > form + form,
.sheet__scroll > form + section,
.sheet__scroll > section + section { border-top: 1px solid var(--border-subtle); }
.sheet__foot { padding: 14px 24px; border-top: 1px solid var(--border-default); flex: none; }

/* A full-height sheet earns a taller task tree than the old 380px rail did. */
/* Labels collide across scopes in the real corpus, so the path is not optional
   decoration — it is the only thing that disambiguates two identically named
   nodes. Never hide it behind a hover or a tooltip. */
.tree__path {
  font-family: var(--font-mono); font-size: var(--text-xs); color:var(--text-tertiary);
  overflow-wrap:anywhere;
}

/* Rows that hold the money read as the ones to pick; the rest are reporting
   scopes beneath them. A left rule, not a fill — the tree already tints the
   selected row and two backgrounds would fight. */
.tree__holds, .tree__reporting {
  margin-left:7px; padding:1px 5px; border-radius:var(--r-1);
  font-size: var(--text-xs); font-weight:600; letter-spacing:0.03em; text-transform:uppercase;
  vertical-align:middle;
}
.tree__holds { background:var(--signal-subtle); color:var(--signal-on-subtle); }
.tree__reporting { background:var(--bg-subtle); color:var(--text-tertiary); border:1px solid var(--border-default); }
tr.row--holds td { background:var(--signal-subtle); }
/* D-063: the grid is the picker. While a head button waits for a task, rows
   are clickable; the row with the drawer open is marked. */
tr.row--choose { cursor: pointer; }
tr.row--choose:hover td, tr.row--choose:focus-visible td { background: var(--bg-subtle); }
tr.row--holds.row--choose:hover td { background: var(--signal-subtle); filter: brightness(.97); }
tr.row--recording td:first-child { box-shadow: inset 3px 0 0 var(--signal); }
.banner--choose { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin: 0 20px 10px; }

/* D-098: the ledger read as modifications — a row per modification, its
   ceiling lines in a nested table beneath, and a filter bar that repaints
   the table alone. */
.filters--ledger { padding: 0 24px 14px; }
/* D-240: Cost by category's task picker, under its summary. */
.filters--costs { padding: 0 24px 12px; }
.filters--costs .chart__pickwrap { position: relative; }
.panel[aria-busy="true"] .panel__scroll { opacity: .55; transition: opacity var(--motion-fast); }
/* Pushed to the far end of the filters bar: a control over the whole table,
   not another filter on it (D-137). */
.filters__end { margin-left: auto; }
/* D-431: the Effective range's two day pickers sit inside the filter's own box. */
.filters__select--days details.monthpick--inline > summary.monthpick__trigger { border: 0; background: none; padding: 0 6px; height: var(--control-h); }
.filters--ledger label.filters__select { cursor: pointer; }
/* D-159: a report's parameters, the question above its table. The page's
   own gap spaces it; the inputs read like the selects beside them. */
.filters--report .filters__select input { border: 0; outline: none; background: none; font: inherit; font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); }
.filters--report .filters__select input[type="number"] { width: 6em; }
.filters--report label.filters__select { cursor: pointer; }
.filters--ledger .filters__select input[type="checkbox"] { margin: 0; }
tr.row--mod td { vertical-align: top; }
tr.row--lines > td { padding: 0 0 6px 40px; background: var(--bg-subtle); }
.lines table { margin: 0; }
.lines th { font-size: var(--text-xs); }
.lines td { background: var(--bg-subtle); }

/* D-098 step 2: the task picker — a combobox over the tree. (`.picker` is the
   connection wizard's project list; this is a different thing.) */
.taskpick { position: relative; display: flex; flex-direction: column; gap: 6px; }
.taskpick input[role="combobox"] {
  height: var(--control-h); min-height: var(--control-h); flex: 0 0 auto;
  padding: 0 12px; border: 1px solid var(--border-strong); border-radius: var(--r-2);
  font: inherit; font-size: var(--text-sm); color: var(--text-primary); background: var(--bg-surface); width: 100%;
}
/* Neither the input nor the list may be squeezed by the flex columns above
   them when the list is tall; measured at 370 tasks, the input collapsed to
   16px until told not to shrink. */
.taskpick__list { flex: 0 0 auto; }

.taskpick input[role="combobox"]:focus { border-color: var(--signal); outline: none; box-shadow: 0 0 0 3px var(--signal-halo); }
.taskpick__list {
  max-height: min(600px, 62vh); overflow-y: auto;
  padding: 6px; display: flex; flex-direction: column; gap: 1px;
  background: var(--bg-raised); border: 1px solid var(--border-default); border-radius: var(--r-3);
  box-shadow: var(--e2);
}
.taskpick__option { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 16px; align-items: center; padding: 8px 10px; border-radius: var(--r-2); cursor: pointer; }
.taskpick__option--line { grid-template-columns: minmax(0, 1fr); }
.taskpick__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* The grid's four figure columns, right-aligned and tabular, under one head. */
.taskpick__figures { display: grid; grid-template-columns: repeat(4, minmax(96px, auto)); gap: 12px; font-size: var(--text-sm); text-align: right; white-space: nowrap; }
.taskpick__head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 16px; padding: 4px 10px 6px; font-size: var(--text-xs); letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-tertiary); border-bottom: 1px solid var(--border-subtle); margin-bottom: 4px; }
.taskpick__head .taskpick__figures { font-size: var(--text-xs); }
.taskpick__line { font-size: var(--text-xs); color: var(--text-secondary); }
.taskpick__option[aria-selected="true"] { background: var(--bg-subtle); }
.taskpick__option--holds .taskpick__label { font-weight: 600; }
/* D-115: an ancestor shown under a search only for its place in the tree. */
.taskpick__option--context .taskpick__label { color: var(--text-tertiary); font-weight: 400; }
.taskpick__label .disclosure { flex: none; margin-left: -4px; }
.taskpick__label { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--text-primary); }
.taskpick__path { font-size: var(--text-xs); color: var(--text-tertiary); font-family: var(--font-mono); }
.taskpick__empty { padding: 8px 10px; font-size: var(--text-sm); color: var(--text-tertiary); }
.taskpick__more { padding: 8px 10px; text-align: left; }
.taskpick--overlay { position: relative; }
.taskpick--overlay .taskpick__list { position: absolute; left: 0; right: auto; top: 40px; z-index: 30; width: min(560px, 90vw); }
td.cell--picker { min-width: 260px; }

/* Segmented control (BurnSpan §6): a sunken track, the chosen segment raised. */
.seg { display:flex; gap:2px; padding:3px; background:var(--bg-sunken); border-radius:var(--r-3); }
.seg button {
  flex:1; padding:6px 10px; border:0; border-radius:var(--r-2); background:transparent; font:inherit; font-size: var(--text-sm);
  font-weight:500; color:var(--text-secondary); cursor:pointer;
}
.seg button:hover { color:var(--text-primary); }
.seg button[aria-pressed="true"] { background:var(--bg-raised); color:var(--text-primary); box-shadow:var(--e1); }


/* What this modification is about to do. Recessive by default — it explains,
   it never blocks; the flag is the only part that raises its voice. */
.preview {
  display:flex; flex-direction:column; gap:6px;
  padding:11px 13px; border:1px solid var(--border-default); border-radius:var(--r-2);
  background:var(--bg-subtle);
}
.preview__row { display:flex; align-items:baseline; justify-content:space-between; gap:14px; }
.preview__label { font-size: var(--text-xs); font-weight:600; color:var(--text-secondary); }
.preview__flow { display:inline-flex; align-items:baseline; gap:8px; font-size: var(--text-sm); }
.preview__arrow { color:var(--text-disabled); }
.preview__to { font-weight:600; color:var(--text-primary); }
.preview__by { font-size: var(--text-xs); color:var(--text-tertiary); }
.preview__flag {
  display:flex; flex-direction:column; gap:3px; padding:8px 10px; border-radius:var(--r-1);
  background:var(--status-watch-tint); color:var(--status-watch-text);
  font-size: var(--text-sm); line-height:1.5;
}
.preview__flag strong { font-weight:600; }

/* -------------------------------------------------------------- freshness */

.fresh {
  display: flex; align-items: stretch; gap: 0; flex-wrap: wrap;
  background: var(--bg-surface); border: 1px solid var(--border-default); border-radius: var(--r-3);
  overflow: hidden;
}
.fresh__item { display: flex; align-items: flex-start; gap: 10px; padding: 12px 18px; flex: 1 1 220px; min-width: 0; }
.fresh__item + .fresh__item { border-left: 1px solid var(--border-default); }
.fresh__dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; flex-shrink: 0; }
.fresh__dot--ok { background: var(--status-healthy); }
.fresh__dot--warn { background: var(--status-watch); }
.fresh__dot--bad { background: var(--status-critical); }
.fresh__dot--none { background: var(--text-disabled); }
.fresh__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.fresh__value { font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); }
.fresh__sub { font-size: var(--text-xs); color: var(--text-tertiary); }
.fresh__more { margin-left: auto; display: flex; align-items: center; padding: 12px 18px; }
/* D-110: the Portfolio's one-line form — a dot, the last sync, its note and
   the refresh, in the page head beside its control. No cards. */
.fresh--compact {
  background: none; border: 0; border-radius: 0; overflow: visible;
  align-items: center; gap: 8px; font-size: var(--text-sm); flex-wrap: wrap;
}
.fresh--compact .fresh__dot { margin-top: 0; }
.fresh--compact .fresh__value { font-size: var(--text-sm); }
.fresh--compact .fresh__sub::before { content: '· '; }

/* ----------------------------------------------------------- monthly grid */

/* The small segmented toggle is the .seg control at table size (D-070). */
.seg--sm button { padding:4px 12px; font-size: var(--text-sm); white-space:nowrap; }
/* D-327: a switch whose buttons carry counts (Compare with Unanet's kinds)
   wraps inside a narrow sheet rather than pushing it past the window. */
.seg--wrap { flex-wrap: wrap; max-width: 100%; }
.seg--wrap button { flex: 1 1 auto; }

/* Section tabs on a cost-structure card (D-056). An underline, not the dark
   segmented toggle: tabs change what you are looking at, the toggle changes
   how you look at it, and the two must not read as the same control. */
.tabs { display:flex; gap:2px; padding:0 24px; border-bottom:1px solid var(--border-default); }
.tabs [role="tab"] {
  background:none; border:0; border-bottom:2px solid transparent; margin-bottom:-1px;
  padding:calc(var(--nav-py) + 2px) 12px; font:inherit; font-size: var(--text-sm); font-weight:500; color:var(--text-secondary); cursor:pointer;
}
.tabs [role="tab"]:hover { color:var(--text-primary); }
.tabs [role="tab"][aria-selected="true"] { color:var(--text-primary); font-weight:600; border-bottom-color:var(--action-primary); }
/* D-440: a project's pages as tabs, under its breadcrumb on every one of
   them — the in-page tabs' look, as links. */
.projecthead { display: flex; flex-direction: column; gap: 12px; }
.pagetabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border-default); }
.pagetabs__tab {
  border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap;
  padding: calc(var(--nav-py) + 2px) 12px; font-size: var(--text-sm); font-weight: 500;
  color: var(--text-secondary); text-decoration: none;
}
.pagetabs__tab:first-child { padding-left: 0; }
.pagetabs__tab:hover { color: var(--text-primary); }
.pagetabs__tab[aria-current="page"] { color: var(--text-primary); font-weight: 600; border-bottom-color: var(--action-primary); }
/* D-174: five tabs do not fit a phone; the row scrolls sideways under 1024
   (the responsive section) instead of squeezing each label onto two lines. */

/* The measure name has to stay readable while months scroll under it. */
.grid table { min-width:max-content; }
.grid th.measure, .grid td.measure {
  position:sticky; left:0; z-index:2; background:var(--bg-surface);
  border-right:1px solid var(--border-default); white-space:nowrap; text-align:left;
}
.grid thead th.measure { background:var(--bg-subtle); z-index:3; }
.grid tbody tr:hover td { background:var(--bg-subtle); }
.grid tbody tr:hover td.measure { background:var(--bg-subtle); }
/* D-125: a person's working sits above a month grid that is legitimately
   wide and scrolls sideways. A sentence must never ask for that, so it stays
   put while the months move under it — the same sticky trick the measure
   column uses — and holds to a readable width on a phone. */
.people__working {
  position: sticky; left: 0;
  /* 640px is the reading measure; on a narrow screen it is instead what the
     panel actually shows — the viewport less the page gutters (32px each),
     the panel's borders, and the drawer's own padding (24px each). D-171:
     measured against the table's scroller where the browser can, which is
     right with the assistant open too. */
  max-width: min(640px, calc(100vw - 64px - 2px - 48px));
  max-width: min(640px, calc(100cqw - 48px));
}
/* D-171: "I can't scroll." A person's months sit in a row drawer inside the
 * People table, whose width is the table's, not the panel's; the months'
 * scroller ran past the panel's edge and its scrollbar with it. It is clamped
 * to what the panel shows and held in view as the table scrolls, and a
 * sideways scroll that runs out stays in it. */
.people__scroll { container-type: inline-size; }
.people__months {
  position: sticky; left: 0; overflow-x: auto; overscroll-behavior-x: contain;
  max-width: calc(100vw - 64px - 2px - 48px);
  max-width: calc(100cqw - 48px);
}
/* D-309: a person's calculation. Each line is its figure, with how it was
   made beneath it; a section's head carries the chevron, and its body — the
   rest of the figures and the section's own months table — sits past it.
   Both sticky blocks inside a body give up the indent they sit behind. */
.calc__row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.calc__why { font-size: var(--text-sm); color: var(--text-secondary); margin-top: 2px; }
.calc__why--head, .calc__body { margin-left: 28px; }
.calc__body { display: flex; flex-direction: column; gap: 14px; margin-top: 12px; }
.calc__body .people__working { max-width: min(640px, calc(100cqw - 76px)); }
.calc__body .people__months { max-width: calc(100cqw - 76px); }
.grid th.fy .disclosure { vertical-align: middle; margin-right: 4px; color: inherit; }
.grid td.month--sum, .grid th.month--sum { background: var(--bg-subtle); font-weight: 600; }
.grid td.month--total, .grid th.month--total { border-left: 1px solid var(--border-default); font-weight: 600; }

.grid th.fy {
  text-align:center; border-left:1px solid var(--border-default); background:var(--bg-sunken);
  color:var(--text-secondary); letter-spacing:.08em;
}
.grid td.month, .grid th.month {
  text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums lining-nums;
}
.grid td.month { font-size: var(--text-sm); }
.grid th.month { font-size: var(--text-xs); }
.grid td.zero { color:var(--text-disabled); }
.grid tr.grid__rule td { border-top:1px solid var(--border-default); }

.grid__note { font-size: var(--text-sm); color:var(--text-secondary); margin:10px 0 0; line-height:1.55; max-width:72ch; }
/* A note that closes a panel wears the panel's own gutter. */
.panel > .grid__note { padding:0 24px 18px; max-width:calc(72ch + 48px); }

/* The fiscal year containing today, on the cost-structure matrix — same
   treatment as the GFY group header above, a step stronger. */
.grid th.fy-now { background:var(--bg-sunken); color:var(--text-primary); }
.grid td.fy-now { background:var(--bg-subtle); }
/* Months the forecast covers, in the wash the chart already uses for ETC. */
.grid th.month--etc, .grid td.month--etc { background:var(--chart-forecast-fill); }

/* Wrapping chip rows (cost elements, labor categories). */
.chips { display:flex; flex-wrap:wrap; gap:6px; }

/* --------------------------------------------------------- data quality */

/* D-116: the severity pill and the project each hold a column that takes
   its own width, so the headline starts at one x on every row. */
th.sev, td.sev { width: 1%; white-space: nowrap; }
th.proj, td.proj { width: 1%; white-space: nowrap; }
.dq__what { font-size: var(--text-sm); font-weight:600; color:var(--text-primary); }
/* D-062: an acknowledged alert stays listed, quietly. */
.alert-row--muted td { opacity: .62; }
/* The decision column holds one primary button and a More menu: it takes
   exactly its width and never wraps, so the controls line up down the page. */
th.decision, td.decision { white-space: nowrap; width: 1%; }
td.decision .stack__sub { white-space: normal; max-width: 36ch; text-align: right; }
/* D-064: a tile's second figure, the matrix's change marks, the diverging banner. */
.stat__second { font-size: var(--text-sm); color: var(--text-secondary); margin-top: 2px; }
.stat__second-label { font-size: var(--text-sm); color: var(--text-tertiary); }
.stat__delta { font-size: var(--text-sm); }
.stat__delta.pos { color: var(--status-healthy-text); }
.grid td.changed { box-shadow: inset 0 -2px 0 var(--signal); }
.banner--diverge { display: flex; gap: 10px; align-items: baseline; }
.rowactions--tight { flex-wrap: nowrap; gap: 8px; justify-content: flex-end; }
details.menu--row > summary.btn { display: inline-flex; }
details.menu--row .menu__panel { min-width: 170px; }
.alert-row--muted:hover td { opacity: 1; }
/* D-168: a project's open alerts, in its head. The pill sits in a column of
 * its own so every line's text starts at the same place (D-116's rule). */
.pagehead--project { align-items: flex-start; }
/* D-310: the name's line runs the full width, so Project settings at its end
   sits at the far right of the page; the destinations wrap beneath. */
.pagehead--project > .stack { flex: 1 1 100%; min-width: 0; }
/* D-442: a row naming several projects lists them in its Details. */
.alert-members { margin: 0; padding-left: 18px; font-size: var(--text-sm); display: grid; gap: 2px; }
.alerts-inline { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; max-width: 96ch; }
.alerts-inline__item { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: 0 10px; align-items: baseline; }
.alerts-inline__body { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; font-size: var(--text-sm); color: var(--text-primary); }
.alerts-inline__what { font-weight: 600; }
.alerts-inline__fix { color: var(--text-secondary); }
/* D-435: the why opens under its line from Details, as the Alerts page's
   drawer does; closed, the head is one line per alert. */
.alerts-inline__why { flex-basis: 100%; display: flex; flex-direction: column; gap: 2px; font-size: var(--text-sm); color: var(--text-secondary); }
.alerts-inline__why[hidden] { display: none; }
.alerts-inline__item--muted > * { opacity: .62; }
.pagehead p.alerts-inline__none { font-size: var(--text-sm); color: var(--text-tertiary); }

/* D-073: a fold inside a card (the scenario's lines) keeps the card's inset. */
.panel > details.fold > summary { padding: 12px 24px; border-top: 1px solid var(--border-subtle); }

/* A ceilings row drives the chart (D-075): it reads as a control on hover and
   takes focus like one, without becoming a button in the middle of a table. */
.row--pick { cursor: pointer; }
.row--pick:hover td { background: var(--bg-subtle); }
.row--pick:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
/* D-241: the row the chart and Cost by category follow. */
.row--pick.is-selected td { background: var(--signal-subtle); }
.row--pick.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--signal); }

/* A task order sits under its contract in the import picker (D-075), the way
   the Sync page nests a family; the closed and administrative fold away
   beneath it, so the list stops being "the entire list of everything". */
.picker__row--child { padding-left: 28px; }
/* D-317: a project's fold inside a contract's Ceilings by task panel — a
   direct child of the panel, so its summary wears the fold-in-a-card inset
   above; its body gives the Open link the same inset, and the family bar
   under the panel's own summary or table keeps a little air. */
.fold--project__body > .note { padding: 10px 24px 0; margin: 0; }
.fold--project__body > .fold--project__slot > .meta { display: block; padding: 8px 24px 16px; }
.filters--family { padding-top: 12px; }

/* A disclosure inside a form or a panel body, rather than one that IS a panel:
   its summary is a quiet line of text, not a heading. */
.fold--inline { margin-top: 8px; }
.fold--inline > summary {
  cursor: pointer; font-size: var(--text-sm); color: var(--text-secondary);
  padding: 6px 2px; list-style: none; display: flex; align-items: center; gap: 6px;
}
.fold--inline > summary::-webkit-details-marker { display: none; }
.fold--inline > summary::before { content: '▸'; font-size: var(--text-xs); }
.fold--inline[open] > summary::before { content: '▾'; }
.fold--inline > summary:hover { color: var(--text-primary); }

/* D-163: the dictionary — a category rail beside the terms; on a phone the
   rail wraps above them. Terms sit inside category panels, separated by the
   panel's own rule, and a jumped-to term flashes once. */
.dict { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 24px; align-items: start; }
.dict__body { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.dict__term { padding: 16px 24px 18px; border-top: 1px solid var(--border-subtle); scroll-margin-top: calc(var(--topbar-offset) + 16px); transition: background var(--motion-slow); }
.dict__term h3 { font-size: var(--text-lg); font-weight: 600; margin: 0 0 4px; }
.dict__anchor { color: inherit; text-decoration: none; }
.dict__anchor:hover { text-decoration: underline; }
.dict__term p, .dict__term ul { font-size: var(--text-sm); line-height: 1.6; color: var(--text-secondary); margin: 6px 0 0; max-width: 78ch; }
.dict__term ul { padding-left: 20px; }
.dict__term code { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-primary); }
.dict__term .dict__meta { font-size: var(--text-sm); color: var(--text-tertiary); margin-top: 2px; }
.dict__term .dict__calc, .dict__term .dict__example { background: var(--bg-subtle); border-radius: var(--r-2); padding: 8px 12px; margin-top: 10px; color: var(--text-primary); }
.dict__term--target { background: var(--signal-subtle); }
/* D-321: the help pages — their own rail of sections down the left (the
   D-223 rail, with several titles), a page or the Glossary beside it. D-323:
   pages are read top to bottom, so they take a reading size (15/1.65, the
   primary colour) rather than the app's compact body, wrap at a wide measure
   (the owner's choice over no cap), and give a screenshot the whole width. */
.rail--help .rail__group + .rail__group { margin-top: 14px; }
/* The pages sit in from their section's title by the width of its marker
   and gap (the owner's ask, 2026-10-25), so a section reads as a fold. */
.rail--help .rail__pages { display: flex; flex-direction: column; gap: 2px; padding-left: 16px; }
.rail--help summary.rail__title { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 6px; border-radius: var(--r-2); }
.rail--help summary.rail__title::-webkit-details-marker { display: none; }
.rail--help summary.rail__title::before { content: '▸'; font-size: var(--text-xs); width: 10px; }
.rail--help .rail__group[open] > summary.rail__title::before { content: '▾'; }
.rail--help summary.rail__title:hover { color: var(--text-secondary); }
.rail--help summary.rail__title:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.rail__mark { font-size: var(--text-xs); color: var(--text-tertiary); margin-left: 6px; vertical-align: 1px; }
.help__article { padding: 24px 28px 28px; }
.help__article h2 { font-family: var(--font-display); font-size: var(--text-xl); line-height: 26px; font-weight: 700; margin: 30px 0 8px; }
.help__article h2:first-child, .help__article .microlabel + h2 { margin-top: 0; }
.help__article h3 { font-size: var(--text-lg); line-height: 24px; font-weight: 600; margin: 22px 0 6px; }
.help__article p, .help__article ul, .help__article ol { font-size: var(--text-base); line-height: 1.65; color: var(--text-primary); margin: 8px 0 0; max-width: 110ch; }
.help__article ul, .help__article ol { padding-left: 22px; }
.help__article li + li { margin-top: 6px; }
.help__article code { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-primary); }
.help__article strong { color: var(--text-primary); font-weight: 600; }
.help__article > .microlabel { display: block; margin-bottom: 8px; }
.help__figure { margin: 18px 0 4px; }
/* A screenshot is never drawn larger than its own pixels, and a tall one is
   held to most of the viewport's height: a wide capture fills the panel, a
   portrait one sits at about half the width, and the zoom shows either large. */
.help__figure img, .help__figure video { display: block; width: auto; max-width: 100%; max-height: 70vh; height: auto; border: 1px solid var(--border-default); border-radius: var(--r-2); background: var(--bg-subtle); }
.help__figure figcaption { font-size: var(--text-sm); color: var(--text-tertiary); margin-top: 8px; }
.help__zoom { display: inline-block; max-width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; border-radius: var(--r-2); }
.help__zoom:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }
.help__missing { font-size: var(--text-sm); color: var(--text-tertiary); padding: 12px 14px; border: 1px dashed var(--border-default); border-radius: var(--r-2); }
/* The Glossary's terms in the help area read at the same size. */
.help__terms .dict__term h3 { font-size: var(--text-lg); line-height: 24px; }
.help__terms .dict__term p, .help__terms .dict__term ul { font-size: var(--text-base); line-height: 1.65; color: var(--text-primary); max-width: 110ch; }
.help__terms .dict__term .dict__meta { font-size: var(--text-sm); color: var(--text-tertiary); }
.help__terms .dict__term code { font-size: var(--text-sm); }
/* D-323: the editor beside its live preview; one above the other under 1024. */
.help__split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; align-items: start; }
.help__side { position: sticky; top: calc(var(--topbar-offset) + 16px); display: flex; flex-direction: column; gap: 12px; }
.help__preview { max-height: calc(100vh - var(--topbar-offset) - 32px); overflow: auto; }
.help__editor { width: 100%; min-height: 60vh; font-family: var(--font-mono); font-size: var(--text-sm); line-height: 1.5; resize: vertical; margin-top: 10px; }
.help__problems { margin: 0; padding: 12px 16px 12px 32px; font-size: var(--text-sm); line-height: 1.5; color: var(--text-primary); background: var(--bg-surface); border: 1px solid var(--danger); border-radius: var(--r-2); }
.help__problems li + li { margin-top: 4px; }
/* D-323: a screenshot, large: most of the viewport, the page behind it. */
dialog.lightbox {
  position: fixed; inset: 0; margin: auto; padding: 0;
  width: auto; max-width: calc(100% - 32px); max-height: calc(100% - 32px);
  border: 1px solid var(--border-default); border-radius: var(--r-4); background: var(--bg-raised); color: var(--text-primary);
  box-shadow: var(--e3); cursor: zoom-out;
}
dialog.lightbox[open] { display: flex; flex-direction: column; animation: modal-in var(--motion-base) var(--ease-standard); }
dialog.lightbox::backdrop { background: rgba(5, 20, 38, .48); background: var(--scrim); }
.lightbox__bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px 8px 8px 16px; border-bottom: 1px solid var(--border-subtle); cursor: default; }
.lightbox__caption { font-size: var(--text-sm); color: var(--text-secondary); }
.lightbox__img { display: block; max-width: calc(100vw - 34px); max-height: calc(100vh - 92px); width: auto; height: auto; object-fit: contain; cursor: default; }
.banner--help { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.diff { font-family: var(--font-mono); font-size: var(--text-sm); line-height: 1.5; white-space: pre-wrap; word-break: break-word; margin: 0; background: var(--bg-surface); }
.diff__line { padding: 0 8px; border-radius: 2px; }
.diff__line--add { background: var(--signal-subtle); color: var(--text-primary); }
.diff__line--del { background: var(--bg-sunken); color: var(--text-tertiary); text-decoration: line-through; }
.diff__legend { font-size: var(--text-sm); color: var(--text-tertiary); margin: 0 0 10px; }
/* Global Admin › Change log (D-222): a panel per day, each change a fold that
   shows its title, summary, where and who while closed. */
/* D-443: the change log's newest changes, then this button for the rest. */
.log__more { display: flex; justify-content: center; padding-top: 4px; }
.log { display: flex; flex-direction: column; gap: 20px; }
.log__entry { border-top: 1px solid var(--border-subtle); scroll-margin-top: calc(var(--topbar-offset) + 16px); }
.log__summary { list-style: none; cursor: pointer; padding: 14px 24px 14px 44px; position: relative; }
.log__summary::-webkit-details-marker { display: none; }
.log__summary::before { content: '▸'; position: absolute; left: 22px; top: 15px; font-size: var(--text-xs); color: var(--text-tertiary); }
.log__entry[open] > .log__summary::before { content: '▾'; }
.log__summary:hover h3 { text-decoration: underline; }
.log__summary h3 { font-size: var(--text-lg); font-weight: 600; margin: 0 0 4px; }
.log__summary p { font-size: var(--text-sm); line-height: 1.6; color: var(--text-secondary); margin: 0; max-width: 78ch; }
.log__summary .log__meta { font-size: var(--text-sm); color: var(--text-tertiary); margin-top: 4px; }
.log__body { padding: 0 24px 18px 44px; }
.log__body p, .log__body ul { font-size: var(--text-sm); line-height: 1.6; color: var(--text-secondary); margin: 6px 0 0; max-width: 78ch; }
.log__body ul { padding-left: 20px; }
.log__body li + li { margin-top: 4px; }
.log__body code { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-primary); }
.log__body strong, .log__summary strong { color: var(--text-primary); font-weight: 600; }

/* ------------------------------------------------------------ assistant */

/* D-165: Ask. The slot is empty until the organization says the assistant
   is on, and an empty slot takes no room in the topbar's gap. */
.topbar__askslot:empty { display: none; }
/* Ask (D-165, D-250): a secondary button. Colour means something in
   BurnSpan, so the assistant has no accent of its own. */
.topbar__ask {
  height: var(--control-h-sm); padding: 0 12px 0 10px; border-radius: var(--r-2);
  border: 1px solid var(--border-strong); background: var(--bg-surface); color: var(--text-primary);
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font: inherit; font-size: var(--text-sm); font-weight: 500;
  transition: background var(--motion-fast) var(--ease-out), border-color var(--motion-fast) var(--ease-out);
}
.topbar__ask:hover { background: var(--bg-subtle); border-color: var(--text-disabled); }
.topbar__ask[aria-expanded="true"] { background: var(--bg-sunken); }
.ai-spark { display: inline-flex; color: var(--text-secondary); }

/* Docked under the topbar and NOT modal: the page stays live beside it, so a
   reader can select a figure, ask, and follow a link it read. Below the
   topbar's layer (10) so the topbar's menus drop over it; above anything the
   page raises (5 and under). */
dialog.assist {
  position: fixed; top: var(--topbar-offset); right: 0; bottom: 0; left: auto; margin: 0; padding: 0;
  width: min(420px, 100%); max-width: 100%; height: auto; max-height: none; z-index: 9;
  border: 0; border-left: 1px solid var(--border-default); background: var(--bg-raised); color: var(--text-primary);
  box-shadow: var(--e3);
}
dialog.assist[open] { display: flex; flex-direction: column; animation: sheet-in var(--motion-slow) var(--ease-standard); }
.assist__head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 20px; border-bottom: 1px solid var(--border-default); flex: none;
}
.assist__head h2 { font-size: var(--text-lg); font-weight: 600; margin: 0; }
.assist__scroll {
  overflow-y: auto; flex: 1 1 auto; padding: 16px 20px 20px;
  display: flex; flex-direction: column; gap: 22px;
}
.assist__empty { display: flex; flex-direction: column; gap: 8px; }
.assist__empty p { margin: 0; }
.assist__turn { display: flex; flex-direction: column; gap: 8px; }
.assist__q {
  font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); line-height: 1.45;
  border-left: 3px solid var(--border-strong); padding: 2px 0 2px 10px; white-space: pre-wrap;
}
.assist__a { font-size: var(--text-sm); line-height: 1.6; color: var(--text-primary); display: flex; flex-direction: column; gap: 8px; }
.assist__a p, .assist__a ul { margin: 0; }
.assist__a ul { padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.assist__read { border-top: 1px solid var(--border-subtle); padding-top: 8px; }
.assist__read ul { margin: 4px 0 0; padding-left: 16px; font-size: var(--text-sm); color: var(--text-secondary); display: flex; flex-direction: column; gap: 2px; }
.assist__foot {
  border-top: 1px solid var(--border-default); padding: 12px 20px 16px; flex: none;
  display: flex; flex-direction: column; gap: 8px; background: var(--bg-raised);
}
.assist__foot textarea {
  width: 100%; min-height: 64px; resize: vertical; font: inherit; font-size: var(--text-base); line-height: 1.45;
  border: 1px solid var(--border-strong); border-radius: var(--r-2); padding: 8px 10px;
  background: var(--bg-surface); color: var(--text-primary);
}
.assist__foot textarea:focus { outline: none; box-shadow: 0 0 0 3px var(--signal-halo); border-color: var(--signal); }
.assist__foot .meta { line-height: 1.45; }
.assist__chip:empty { display: none; }
.assist__selected { font-style: italic; }
/* D-390: the answer card. A figure that opens a trace is dotted-underlined
   (the design system's rule); the tag beside a source says where it came
   from — Unanet on the sunken ground, Recorded in the signal's tint. */
.assist__meta { font-size: var(--text-xs); }
.assist__readline { font-size: var(--text-sm); }
.assist__head .cluster { gap: 14px; }
.assist__new[hidden] { display: none; }
.assist .mono { font-family: var(--font-mono); font-size: var(--text-sm); }
.fig {
  background: none; border: 0; padding: 0 1px; margin: 0; font: inherit; color: inherit; cursor: pointer;
  font-variant-numeric: tabular-nums; border-radius: var(--r-1);
  text-decoration: underline dotted var(--text-tertiary); text-decoration-thickness: 1.5px; text-underline-offset: 3px;
}
.fig:hover, .fig[aria-expanded="true"] { background: var(--bg-subtle); text-decoration-color: var(--text-secondary); }
.fig:focus-visible { outline: 2px solid var(--signal); outline-offset: 1px; }
.fig--unverified { text-decoration-style: wavy; text-decoration-color: var(--status-watch); }
.tag--unanet { background: var(--bg-sunken); color: var(--text-secondary); }
.tag--recorded { background: var(--signal-subtle); color: var(--signal-on-subtle); border-color: transparent; }
.tag--none { border-style: dashed; }
.assist__card {
  border: 1px solid var(--border-default); border-radius: var(--r-3); background: var(--bg-surface);
  padding: 10px 14px 12px; display: flex; flex-direction: column; gap: 8px;
}
.assist__card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.assist__card-head h3 { font-size: var(--text-sm); font-weight: 600; margin: 0; }
.assist__card .meta { margin: 0; line-height: 1.5; }
.assist__formula {
  font-family: var(--font-mono); font-size: var(--text-sm); line-height: 20px; font-variant-numeric: tabular-nums;
  display: flex; flex-direction: column; overflow-x: auto;
}
.assist__formula-line { white-space: pre-wrap; overflow-wrap: anywhere; }
.assist__formula-line--result { border-top: 1px solid var(--text-primary); margin-top: 2px; padding-top: 2px; }
.assist__card--table { padding: 0; overflow-x: auto; }
.assist__table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.assist__table caption { text-align: left; font-weight: 600; padding: 8px 12px 4px; color: var(--text-primary); }
.assist__table th, .assist__table td { padding: 6px 12px; border-top: 1px solid var(--border-subtle); vertical-align: top; }
.assist__table thead th { border-top: 0; }
.assist__table tbody tr:first-child td { border-top: 1px solid var(--border-default); }
.assist__table th.num, .assist__table td.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.assist__provisional {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: var(--text-sm); line-height: 1.5;
  background: var(--status-provisional-tint); color: var(--status-provisional-text);
  border: 1px dashed var(--status-provisional); border-radius: var(--r-2); padding: 8px 10px;
}
.assist__sources { display: flex; flex-direction: column; gap: 6px; font-size: var(--text-sm); }
.assist__source { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 8px; }
.assist__source-label { font-weight: 600; color: var(--text-primary); }
.assist__source .meta { flex: 1 1 200px; }
.assist__actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.assist__next-block { display: flex; flex-direction: column; gap: 6px; }
.assist__next { display: flex; flex-wrap: wrap; gap: 6px; }
.assist__next-q, .assist__starter {
  font: inherit; font-size: var(--text-sm); text-align: left; color: var(--text-primary); background: var(--bg-surface);
  border: 1px solid var(--border-strong); border-radius: var(--r-2); padding: 6px 10px; cursor: pointer;
}
.assist__next-q:hover, .assist__starter:hover { background: var(--bg-subtle); }
.assist__unverified { color: var(--status-watch); }
.assist__pop {
  display: flex; flex-direction: column; gap: 8px; font-size: var(--text-sm); line-height: 1.5;
  background: var(--bg-raised); border: 1px solid var(--border-default); border-radius: var(--r-3);
  box-shadow: var(--e2); padding: 10px 12px; align-items: flex-start;
}
.assist__pop--unverified { border-color: var(--status-watch); }
.assist__pop-head { display: flex; align-items: center; gap: 8px; width: 100%; }
.assist__pop-head .btn--link { margin-left: auto; }
.assist__pop-fig { font-family: var(--font-mono); font-weight: 600; color: var(--text-primary); }
.assist__pop-lines { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 2px 10px; margin: 0; }
.assist__pop-lines dt { color: var(--text-tertiary); }
.assist__pop-lines dd { margin: 0; overflow-wrap: anywhere; }
.assist__trace { display: flex; flex-direction: column; gap: 6px; }
.assist__trace .assist__card-head h3 { font-size: var(--text-sm); }
.assist__trace-table td { font-size: var(--text-sm); }
.assist__trace-table td.mono { overflow-wrap: anywhere; }
/* D-391: the opening words, the questions to start with, and the chips —
   the figure selected (its label and value) and the project named. */
.assist__intro { margin: 0; font-size: var(--text-sm); line-height: 1.55; color: var(--text-primary); }
.assist__block { display: flex; flex-direction: column; gap: 8px; }
.assist__starters { display: flex; flex-direction: column; gap: 6px; }
.assist__starter { display: block; width: 100%; font-size: var(--text-sm); padding: 10px 12px; }
.assist__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.assist__chips:empty { display: none; }
.assist__chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%; font-size: var(--text-sm); line-height: 1.3;
  border: 1px solid var(--border-default); border-radius: var(--r-2); background: var(--bg-subtle);
  color: var(--text-primary); padding: 4px 4px 4px 8px;
}
.assist__chip b { font-weight: 600; }
.assist__chip-icon { display: inline-flex; color: var(--text-secondary); }
.assist__chip-words { font-style: italic; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.assist__chip-x {
  background: none; border: 0; padding: 2px; margin: 0; display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-secondary); cursor: pointer; border-radius: var(--r-1); line-height: 0;
}
.assist__chip-x:hover { background: var(--bg-sunken); color: var(--text-primary); }
.assist__projchip { background: var(--bg-subtle); border: 1px solid var(--border-default); border-radius: var(--r-1); padding: 0 5px; }
.assist__composer { position: relative; }
.assist__composer .chat__mentions { bottom: 100%; top: auto; }
.assist__mention-code { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-primary); }
.assist__tools { align-items: center; justify-content: flex-end; }
/* D-392: the reader's earlier questions, each a row with when it was asked. */
.assist__earlier { display: flex; flex-direction: column; }
.assist__earlier-row {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px; width: 100%; text-align: left;
  background: none; border: 0; border-top: 1px solid var(--border-subtle); padding: 9px 0; font: inherit; font-size: var(--text-sm);
  color: var(--text-primary); cursor: pointer;
}
.assist__earlier-row:first-child { border-top: 0; }
.assist__earlier-row:hover .assist__earlier-q { text-decoration: underline; }
.assist__earlier-q { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.assist__earlier-row .meta { flex: none; white-space: nowrap; }
/* D-393: report a wrong answer; once filed, the ticket's line takes its place. */
.assist__report { margin-left: auto; }
.assist__reported { margin-left: auto; }
.assist__report-q { border-left: 3px solid var(--border-strong); padding-left: 10px; }
/* ------------------------------------------------------------ project chat */

/* D-445: Chat is a labelled control in the top bar, beside Ask, on every
   page of an organization — the window it opens sits bottom right on the
   Ask dock's layer (9): under the topbar's menus, over anything the page
   raises. The window is not modal: the page stays live beside it. */
.topbar__chat {
  height: var(--control-h-sm); padding: 0 10px; border-radius: var(--r-2);
  border: 1px solid var(--border-strong); background: var(--bg-surface); color: var(--text-primary);
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font: inherit; font-size: var(--text-sm); font-weight: 500;
  transition: background var(--motion-fast) var(--ease-out), border-color var(--motion-fast) var(--ease-out);
}
.topbar__chat:hover { background: var(--bg-subtle); border-color: var(--text-disabled); }
.topbar__chat[aria-expanded="true"] { background: var(--bg-sunken); }
.topbar__chaticon { display: inline-flex; color: var(--text-secondary); }
.topbar__chaticon svg { display: block; width: 16px; height: 16px; }
.topbar__chatcount[hidden] { display: none; }
.chatwin__count, .menu__count {
  min-width: 20px; height: 20px; padding: 0 6px; border-radius: var(--r-full);
  background: var(--signal); color: var(--action-on-primary); font-size: var(--text-xs); font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; line-height: 1; flex: none;
}
dialog.chatwin {
  position: fixed; right: 20px; bottom: 20px; left: auto; top: auto; margin: 0; padding: 0;
  /* D-361: the usual size; the corner grip sets a kept one inline. */
  --chatwin-w: 680px; --chatwin-h: 640px;
  width: min(var(--chatwin-w), 100vw - 32px); height: min(var(--chatwin-h), 100vh - var(--topbar-offset) - 100px);
  max-width: none; max-height: none; z-index: 9; overflow: hidden;
  border: 1px solid var(--border-default); border-radius: var(--r-3);
  background: var(--bg-raised); color: var(--text-primary); box-shadow: var(--e3);
}
dialog.chatwin[open] { display: grid; grid-template-columns: 220px minmax(0, 1fr); animation: sheet-in var(--motion-slow) var(--ease-standard); }
/* D-361: the corner grip, top left because the window is pinned bottom right.
   A computer only: a touch screen gets none, and a phone's window is full
   screen (below). */
.chatwin__grip {
  position: absolute; top: 0; left: 0; width: 18px; height: 18px; z-index: 1;
  cursor: nwse-resize; touch-action: none; border-radius: var(--r-3) 0 0 0;
}
.chatwin__grip::before {
  content: ''; position: absolute; top: 4px; left: 4px; width: 8px; height: 8px;
  border-top: 2px solid var(--border-strong); border-left: 2px solid var(--border-strong); border-top-left-radius: 3px;
  transition: border-color var(--motion-fast) var(--ease-out);
}
.chatwin__grip:hover::before, .chatwin.is-resizing .chatwin__grip::before, .chatwin__grip:focus-visible::before { border-color: var(--signal); }
.chatwin__grip:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
dialog.chatwin.is-resizing { user-select: none; }
@media (pointer: coarse) { .chatwin__grip { display: none; } }
.chatwin__side { display: flex; flex-direction: column; min-height: 0; border-right: 1px solid var(--border-default); background: var(--bg-surface); }
.chatwin__sidehead, .chatwin__head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 16px; border-bottom: 1px solid var(--border-default); flex: none; min-height: 52px;
}
.chatwin__sidehead h2, .chatwin__title { font-size: var(--text-base); font-weight: 600; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chatwin__titles { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.chatwin__filter { padding: 8px 12px 0; flex: none; display: flex; flex-direction: column; gap: 8px; }
.chatwin__filter .seg button { flex: 1 1 0; }
.chatwin__filter .filters__search { width: 100%; height: var(--control-h-sm); }
.chatwin__rooms { overflow-y: auto; flex: 1 1 auto; padding: 8px; display: flex; flex-direction: column; gap: 2px; }
.chatwin__room {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
  padding: 8px 10px; border: 0; border-radius: var(--r-2); background: none; color: var(--text-secondary);
  font: inherit; font-size: var(--text-sm); cursor: pointer;
}
.chatwin__room:hover { background: var(--bg-subtle); color: var(--text-primary); }
.chatwin__room[aria-current="true"] { background: var(--bg-sunken); color: var(--text-primary); font-weight: 600; }
.chatwin__roomname { display: flex; gap: 6px; min-width: 0; align-items: baseline; }
/* D-298: a family folds under its head; the chevron is the Sync page's. */
.chatwin__family { display: flex; flex-direction: column; gap: 2px; }
.chatwin__headrow { display: flex; align-items: flex-start; gap: 2px; }
.chatwin__headrow > .disclosure { flex: none; margin-top: 7px; }
.chatwin__headrow > .chatwin__room { flex: 1 1 auto; min-width: 0; }
.chatwin__kids { display: flex; flex-direction: column; gap: 2px; padding-left: 22px; }
.chatwin__kids[hidden] { display: none; }
.chatwin__roomtitle { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.chatwin__roommeta { display: flex; justify-content: space-between; align-items: center; gap: 8px; min-height: 16px; }
.chatwin__main { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.chatwin__head .filters__search { flex: 1 1 auto; width: auto; height: var(--control-h-sm); }
.chatwin__tools { display: flex; align-items: center; gap: 8px; flex: none; }
.chatwin__searchbtn { display: inline-flex; align-items: center; }
.chatwin__back { white-space: nowrap; flex: none; }
.chatwin__scroll { overflow-y: auto; flex: 1 1 auto; min-height: 0; padding: 8px 0 12px; display: flex; flex-direction: column; }
.chatwin__picker { flex: none; border-top: 1px solid var(--border-subtle); background: var(--bg-surface); }
.chatwin__picker:empty { display: none; }
.chatwin__pickerhead { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px 0; font-size: var(--text-sm); }
.chatwin__foot { flex: none; border-top: 1px solid var(--border-default); padding: 10px 16px 12px; background: var(--bg-raised); }
.chatwin__foot[hidden] { display: none; }
.chat__composer { display: flex; flex-direction: column; gap: 6px; position: relative; }
.chat__composer textarea, .chat__edit textarea {
  width: 100%; min-height: 44px; resize: vertical; font: inherit; font-size: var(--text-base); line-height: 1.45;
  border: 1px solid var(--border-strong); border-radius: var(--r-2); padding: 8px 10px;
  background: var(--bg-surface); color: var(--text-primary);
}
.chat__composer textarea:focus, .chat__edit textarea:focus { outline: none; box-shadow: 0 0 0 3px var(--signal-halo); border-color: var(--signal); }
.chat__tools { display: flex; align-items: center; gap: 6px; }
.chat__hint { flex: 1 1 auto; min-width: 0; margin-left: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat__tools .btn { white-space: nowrap; flex: none; }
/* D-315: the tool buttons — formatting, emoji, mention — are square icons;
   the formatting row sits above the box while its Aa button is pressed. */
.chat__tool { width: var(--control-h-sm); padding: 0; display: inline-flex; align-items: center; justify-content: center; color: var(--text-secondary); }
.chat__tool[aria-pressed="true"] { background: var(--bg-sunken); color: var(--text-primary); }
.chat__fmtbar { display: flex; flex-wrap: wrap; gap: 2px; }
.chat__fmtbar[hidden] { display: none; }
.chat__text, .chat__composer textarea, .chat__edit textarea, .emoji, .reaction, .chat__mentions { font-family: var(--font-sans), var(--font-emoji); }
.chat__mentions {
  position: absolute; left: 0; right: 0; bottom: 100%; margin-bottom: 6px; z-index: 30;
  max-height: 240px; overflow-y: auto; padding: 6px; display: flex; flex-direction: column; gap: 1px;
  background: var(--bg-raised); border: 1px solid var(--border-default); border-radius: var(--r-3); box-shadow: var(--e2);
}
.chat__mentions[hidden] { display: none; }
.chat__edit { position: relative; display: flex; flex-direction: column; gap: 6px; }
.chat__mentions__opt { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--r-2); cursor: pointer; font-size: var(--text-sm); }
.chat__mentions__opt[aria-selected="true"] { background: var(--bg-subtle); }
.chat__mentions__name { flex: 1 1 auto; color: var(--text-primary); }
.chat__day, .chat__new { display: flex; align-items: center; gap: 12px; padding: 8px 16px; font-size: var(--text-xs); letter-spacing: .06em; text-transform: uppercase; color: var(--text-tertiary); }
.chat__day::before, .chat__day::after, .chat__new::before, .chat__new::after { content: ''; flex: 1; border-top: 1px solid var(--border-subtle); }
.chat__new { color: var(--signal-on-subtle); }
.chat__new::before, .chat__new::after { border-color: var(--signal); }
.chat__msg { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 10px; padding: 8px 16px; }
.chat__msg--reply { padding-left: 32px; }
.chat__msg--pending { opacity: .62; }
.chat__msg--failed { opacity: 1; }
.chat__msg--target { background: var(--signal-subtle); }
.chat__msg--compact { padding: 6px 0; }
.chat__replies { border-top: 1px solid var(--border-subtle); margin-top: 4px; padding-top: 4px; }
.chat__noreplies { padding: 8px 16px 8px 32px; }
.chat__avatar {
  width: 28px; height: 28px; border-radius: 50%; background: var(--action-primary); color: var(--action-on-primary);
  font-size: var(--text-xs); font-weight: 600; display: flex; align-items: center; justify-content: center; flex: none;
}
.chat__body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.chat__meta { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: var(--text-xs); color: var(--text-tertiary); }
.chat__meta strong { color: var(--text-primary); font-size: var(--text-sm); }
/* D-398: a message the reader keeps unread wears its tag, in the signal
   colour every unread cue uses. */
.chat__mark { color: var(--signal-on-subtle); background: var(--signal-subtle); border-radius: var(--r-full); padding: 1px 7px; font-size: var(--text-xs); font-weight: 600; line-height: 16px; }
.chat__msg--marked { box-shadow: inset 3px 0 0 var(--signal); }
.chat__meta .menu--row { margin-left: auto; }
.chat__meta .menu--row > summary { height: 24px; padding: 0 8px; font-size: var(--text-xs); }
.chat__text { white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--text-primary); }
.chat__removed { color: var(--text-tertiary); }
/* D-315: the blocks and marks a message carries. */
.chat__text p { margin: 0; }
.chat__text > * + * { margin-top: 6px; }
.chat__text code { font-family: var(--font-mono); font-size: var(--text-sm); background: var(--bg-subtle); border-radius: var(--r-1); padding: 0 3px; }
.chat__text pre { margin: 0; padding: 8px 10px; background: var(--bg-subtle); border-radius: var(--r-2); overflow-x: auto; white-space: pre-wrap; }
.chat__text pre code { padding: 0; background: none; font-size: var(--text-sm); }
.chat__text blockquote { margin: 0; padding: 2px 0 2px 10px; border-left: 3px solid var(--border-strong); color: var(--text-secondary); }
.chat__text ul, .chat__text ol { margin: 0; padding-left: 1.4em; }
.chat__text s { color: var(--text-secondary); }
.chat__mention { color: var(--signal-on-subtle); background: var(--signal-subtle); border-radius: var(--r-1); padding: 0 3px; font-weight: 500; }
.chat__foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.chat__reactions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.reaction {
  height: 24px; padding: 0 8px; border-radius: var(--r-full); border: 1px solid var(--border-default);
  background: var(--bg-surface); color: var(--text-secondary); font: inherit; font-size: var(--text-sm); cursor: pointer;
}
.reaction[aria-pressed="true"] { border-color: var(--signal); background: var(--signal-subtle); color: var(--signal-on-subtle); }
.chat__opener, .chat__react { font-size: var(--text-sm); }
.chat__confirm { display: flex; flex-direction: column; gap: 6px; padding: 6px 0 2px; }
.chat__editnote:empty { display: none; }
.chat__result { cursor: pointer; padding: 0 16px; }
.chat__result:hover { background: var(--bg-subtle); }
.chat__resulthead { padding: 4px 16px 8px; }
.chat__earlier { display: flex; justify-content: center; padding: 4px 16px 8px; }
.chat__jump {
  position: sticky; bottom: 8px; align-self: center; z-index: 2; margin-top: 8px;
  height: var(--control-h-sm); padding: 0 14px; border-radius: var(--r-full); border: 1px solid var(--border-default);
  background: var(--bg-raised); color: var(--text-primary); box-shadow: var(--e2); font: inherit; font-size: var(--text-sm); font-weight: 500; cursor: pointer;
}
.chat__jump[hidden] { display: none; }
.emoji { display: grid; grid-template-columns: repeat(auto-fill, 32px); gap: 2px; padding: 6px 12px 10px; }
.emoji__btn { width: 32px; height: 32px; border: 0; background: none; border-radius: var(--r-2); font-size: 18px /* size: glyph */; line-height: 1; cursor: pointer; padding: 0; }
.emoji__btn:hover { background: var(--bg-subtle); }
.emoji__quick { border-bottom: 1px solid var(--border-subtle); }
/* D-223: a Global Admin page — its siblings down the left, the page beside. */
.page--rail { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 32px; align-items: start; }
.rail { position: sticky; top: calc(var(--topbar-offset) + 24px); display: flex; flex-direction: column; gap: 2px; font-size: var(--text-sm); }
.rail__title { font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-tertiary); padding: 6px 10px 8px; }
.rail a { display: block; padding: var(--nav-py) 10px; border-radius: var(--r-2); color: var(--text-secondary); text-decoration: none; }
.rail a:hover { background: var(--bg-subtle); color: var(--text-primary); }
.rail a[aria-current="page"] { background: var(--bg-sunken); color: var(--text-primary); font-weight: 600; }
.rail__body { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
/* A wide screen makes room rather than covering the right of the page. Not a
   band boundary on purpose: 1280 is lg plus the 420px the panel takes. */
@media (min-width: 1280px) { body.assist-open .page { margin-right: 420px; } }

/* D-180: a report figure the report assumed rather than read (the waterfall's
   run rate). Italic on screen as in the exported file; the legend says why. */
.table--sized td.assumed { font-style: italic; }
/* D-211: a figure someone typed — italic like an assumption, underlined so a bold row still shows it. */
.table--sized td.typed { text-decoration: underline dotted; text-underline-offset: 3px; }

/* D-183: Global Admin › Testing. A message as its recipient gets it: who and
   the subject, then the email itself in a sandboxed frame at a mail
   client's width, and its plain text. */
.mailhead { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; margin: 0; font-size: var(--text-sm); }
.mailhead dt { color: var(--text-tertiary); }
.mailhead dd { margin: 0; color: var(--text-primary); overflow-wrap: anywhere; }
/* An email as its recipient gets it: the message's own light page in every
   theme, so the colour stays a literal. */
.mailpreview { width: 100%; max-width: 640px; height: 560px; border: 1px solid var(--border-default); border-radius: var(--r-3); background: #F4F5F7; }
.mailtext { margin: 8px 0 0; padding: 12px 14px; max-width: 640px; white-space: pre-wrap; overflow-wrap: anywhere;
  font-size: var(--text-sm); line-height: 1.5; background: var(--bg-subtle); border: 1px solid var(--border-default); border-radius: var(--r-3); }

/* ---------------------------------------------- Configure alerts (D-185) */
/* Read by assistive technology, not drawn. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* The head carries a title, three lines and Close; Close stays at the top. */
.sheet__head--top { align-items: flex-start; }
.rules__who { gap: 8px; margin-top: 6px; }
/* A positioning context: the switches' hidden inputs and the screen-reader
   spans are absolutely positioned, and without one they measure against the
   dialog, making the sheet itself scroll and leaving a band under its foot. */
.rules { display: flex; flex-direction: column; position: relative; }
.rules__section { padding: 20px 24px; display: flex; flex-direction: column; gap: 10px; }
.rules__section + .rules__section { border-top: 1px solid var(--border-subtle); }
.rules__section h3 { font-size: var(--text-lg); font-weight: 600; margin: 0; gap: 10px; }
.rules__note { font-size: var(--text-sm); color: var(--text-secondary); line-height: 1.5; margin: 0; }
.rules__tag {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--text-xs); font-weight: 500; color: var(--text-tertiary);
}
/* One alert per row: its switch, what it catches, its severity. The
   columns are fixed so every severity select lines up down the sheet. */
.rule {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) 132px; gap: 14px;
  align-items: start; padding: 12px 0; border-top: 1px solid var(--border-subtle);
}
.rule__lead { padding-top: 2px; }
.rule__lock { display: inline-flex; color: var(--text-tertiary); padding: 3px 0 0 10px; }
.rule__title { margin: 0; font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }
.rule__sub { margin: 4px 0 0; font-size: var(--text-sm); color: var(--text-secondary); line-height: 1.8; }
.rule__hint { margin: 4px 0 0; font-size: var(--text-sm); color: var(--status-watch-text); }
.rule__hint:empty { display: none; }
.rule__state { font-size: var(--text-sm); color: var(--text-secondary); }
.rule__end { justify-self: end; }
.rule__sev, .ladder__cap select {
  height: 34px; border: 1px solid var(--border-strong); border-radius: var(--r-2);
  padding: 0 10px; font: inherit; font-size: var(--text-sm); background: var(--bg-surface); color: var(--text-primary);
}
.rule__sev { width: 132px; }
.rule__num {
  width: 64px; height: 30px; margin: 0 4px; padding: 0 8px; text-align: center;
  border: 1px solid var(--border-strong); border-radius: var(--r-2); font: inherit; font-size: var(--text-sm);
}
/* Off reads as off without hiding what it would do if turned back on. */
.rule--off .rule__title, .rule--off .rule__sub { color: var(--text-tertiary); }
/* The runout ladder. */
table.ladder { width: 100%; border-collapse: collapse; }
.ladder th, .ladder td { padding: 8px 6px; text-align: left; font-size: var(--text-sm); }
.ladder thead th { font-size: var(--text-xs); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); font-weight: 600; }
.ladder tbody th { font-weight: 500; white-space: nowrap; text-transform: none; letter-spacing: 0; font-size: var(--text-sm); color: var(--text-primary); }
.ladder tbody tr + tr > * { border-top: 1px solid var(--border-subtle); }
.ladder__window { white-space: nowrap; text-transform: none !important; letter-spacing: 0 !important; font-size: var(--text-sm) !important; }
.ladder__edit { gap: 2px; align-items: center; }
.ladder__edit .rule__num { width: 58px; }
.ladder__cap .stack { gap: 4px; }
/* The foot: what saving would change today, then the buttons. */
.rules__foot { display: flex; flex-direction: column; gap: 10px; background: var(--bg-subtle); position: relative; }
.rules__preview:empty { display: none; }
.rules__preview { display: flex; flex-direction: column; gap: 6px; }
.rules__count { margin: 0; font-size: var(--text-sm); font-weight: 600; }
.rules__problem { margin: 0; font-size: var(--text-sm); color: var(--status-critical-text); }
.rules__changes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.rules__changes li {
  display: grid; grid-template-columns: minmax(0, 11rem) minmax(0, 1fr) auto; gap: 12px;
  align-items: center; font-size: var(--text-sm);
}
.rules__proj { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rules__what { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rules__move { display: inline-flex; align-items: center; gap: 6px; color: var(--text-tertiary); }
.rules__closes { font-size: var(--text-xs); font-weight: 600; color: var(--text-secondary); }
.rules__actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
.rules__reset { margin-right: auto; }

/* ------------------------------------------- award fee forecast (D-187) */
/* A slot that a save repaints. No box of its own, so an empty one takes no
   gap in the page's column. */
.slot { display: contents; }
/* D-241: the links under the project head. */
.sectionnav { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; font-size: var(--text-sm); margin-top: -8px; }
.sectionnav a { color: var(--text-secondary); text-decoration: none; }
.sectionnav a:hover, .sectionnav a:focus-visible { color: var(--signal); text-decoration: underline; }
.sectionnav__sep { color: var(--border-default); }
/* D-280: the project page is a dashboard the person composes. A panel and
   a widget wear the ⠿ handle a task row wears (D-230): in the head's left
   padding for a panel, at a corner for a tile, gauge, span or drawing of
   the chart; shown on hover or focus, always on a touch screen (the coarse
   rule below). `details.menu { position: relative }` outranks a class, so
   each place is stated against what holds it, as D-261 did for the cell. */
.panel--arranged > .panel__head, .panel--arranged > summary, .panel--arranged > .chart > .chart__head { position: relative; }
.panel--arranged > .panel__head > details.row-move--panel, .panel--arranged > summary > details.row-move--panel,
.panel--arranged > .chart > .chart__head > details.row-move--panel { position: absolute; left: 5px; top: 50%; transform: translateY(-50%); }
.panel--arranged:hover .row-move--panel, .panel--arranged:focus-within .row-move--panel { opacity: .8; }
.panel--arranged.is-moving { opacity: .55; }
.panel--arranged.is-drop-before { box-shadow: 0 -11px 0 -8px var(--signal); }
.panel--arranged.is-drop-after { box-shadow: 0 11px 0 -8px var(--signal); }
.widget { position: relative; }
.widget > .row-move--widget { position: absolute; left: auto; right: -6px; top: -4px; transform: none; width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; }
/* A span and a drawing run the panel's width; their handle sits in the left padding. */
.spans > .widget > .row-move--widget, .chart__part > .row-move--widget { right: auto; left: -20px; top: 1px; }
.widget:hover > .row-move--widget, .widget:focus-within > .row-move--widget { opacity: .8; }
.widget.is-moving { opacity: .55; }
.widget.is-drop-before::before, .widget.is-drop-after::after { content: ''; position: absolute; background: var(--signal); border-radius: 2px; }
/* Tiles and gauges sit side by side: the landing bar stands beside one;
   spans and drawings stack: it lies above or below. */
.strip__grid > .widget.is-drop-before::before, .gauges > .widget.is-drop-before::before { left: -13px; top: 0; bottom: 0; width: 3px; }
.strip__grid > .widget.is-drop-after::after, .gauges > .widget.is-drop-after::after { right: -13px; top: 0; bottom: 0; width: 3px; }
.spans > .widget.is-drop-before::before, .chart__part.is-drop-before::before { left: 0; right: 0; top: -10px; height: 3px; }
.spans > .widget.is-drop-after::after, .chart__part.is-drop-after::after { left: 0; right: 0; bottom: -10px; height: 3px; }
.chart__part:first-child > .chart__sub { border-top: 0; padding-top: 0; }
/* D-283: a panel's own menu, behind the sliders icon at the end of its
   head — the chart's among its controls, a fold's at the right of its
   summary, where a click must not open or close the fold. The handle at a
   row's left, where the task row has it. */
details.menu--row .menu__panel--tools { min-width: 260px; }
.paneltools__btn { width: var(--control-h-sm); padding: 0; color: var(--text-tertiary); }
.paneltools[open] > .paneltools__btn, .paneltools__btn:hover { background: var(--bg-sunken); color: var(--text-primary); }
/* Open, above the next panel's head: the transform below makes a stacking context, as the handle's rule (D-256) found. */
.paneltools[open] { z-index: 6; }
.panel--arranged > .panel__head > h2 { margin-right: auto; }
.panel--arranged > .panel__head > .paneltools { flex: none; margin-left: 12px; }
details.panel.fold > summary > .paneltools { position: absolute; right: 16px; top: 50%; transform: translateY(-50%); }
details.panel.fold > summary:has(> .paneltools) { padding-right: 68px; }
/* D-286: the catalog sheet — a group label, then a row per figure with its
   first dictionary sentence; one the project cannot draw is greyed. */
.catalog { display: flex; flex-direction: column; gap: 16px; }
.catalog__group { display: flex; flex-direction: column; gap: 2px; }
/* D-449: the column sheet — the Customize list in a side sheet, each row a
   column and its one line from the dictionary, as Add a metric reads. */
.menu__list--sheet { max-height: none; }
.sheet .menu__search { flex: none; width: auto; height: var(--control-h); margin: 0 0 8px; }
.sheet .menu__search input { height: 100%; }
.menu__pickwords { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.menu__def { font-size: var(--text-sm); line-height: 16px; color: var(--text-tertiary); max-width: 60ch; white-space: normal; }
.catalog__row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 10px; border-radius: var(--r-2); cursor: pointer; }
.catalog__row:hover { background: var(--bg-subtle); }
.catalog__row input { margin: 3px 0 0; }
.catalog__row--off { color: var(--text-tertiary); cursor: default; }
.catalog__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.catalog__name { font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); }
.catalog__row--off .catalog__name { color: var(--text-tertiary); }
.catalog__def { font-size: var(--text-sm); line-height: 16px; color: var(--text-tertiary); max-width: 60ch; }
/* D-283: the way back — the hidden panels and Reset, at the page's foot. */
.layoutfoot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; font-size: var(--text-sm); color: var(--text-secondary); }
.layoutfoot__sep { color: var(--border-strong); }
.menu__check > .menu__pick { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; cursor: pointer; }
.menu__check > .menu__pick > input { margin: 0; }
.menu__check > .menu__pick > span { white-space: nowrap; }
.menu__check > .row-move, .menu__check > details.row-move { position: static; transform: none; opacity: .85; width: 14px; height: 18px; flex: none; display: inline-flex; align-items: center; justify-content: center; }
.menu__check > .row-move--none { opacity: 0; }
.menu__check > details.row-move > .menu__panel { top: 100%; left: 0; }
.fee { gap: 14px; }
.fee__pool { display: grid; grid-template-columns: repeat(4, minmax(120px, 1fr)); gap: 20px; max-width: 720px; }
.fee__pool .stat__label { font-size: var(--text-xs); color: var(--text-tertiary); }
.fee__grid td.month { padding: 6px 8px; }
.fee__input {
  width: 92px; height: 30px; border: 1px solid var(--border-strong); border-radius: var(--r-2);
  padding: 0 8px; font: inherit; font-size: var(--text-sm); text-align: right; background: var(--bg-surface); color: var(--text-primary);
}
.fee__input--typed { border-color: var(--signal); }
.fee__input::placeholder { color: var(--text-disabled); }
.fee__input:focus { outline: none; box-shadow: 0 0 0 3px var(--signal-halo); border-color: var(--signal); }
.fee__tools { gap: 16px; align-items: center; }
.fee__fill { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--text-secondary); flex-wrap: wrap; }
.fee__fill input {
  height: 30px; border: 1px solid var(--border-strong); border-radius: var(--r-2);
  padding: 0 8px; font: inherit; font-size: var(--text-sm); background: var(--bg-surface); color: var(--text-primary);
}
.fee__fill-amount { width: 100px; text-align: right; }
.fee__hint { font-size: var(--text-xs); color: var(--text-tertiary); margin: 0; }
/* D-415: a task's award-fee schedule, one row per period, above its months. */
.fee__schedule-block { display: flex; flex-direction: column; gap: 8px; }
/* Positioned, so the header's hidden label stays inside the scroll box. */
.fee__schedule-block > .panel__scroll { position: relative; }
.fee__schedule { border-collapse: collapse; font-size: var(--text-sm); width: auto; }
.fee__schedule th { font-size: var(--text-xs); font-weight: 500; color: var(--text-tertiary); text-align: left; padding: 4px 8px; white-space: nowrap; }
.fee__schedule td { padding: 4px 8px; white-space: nowrap; vertical-align: middle; }
.fee__schedule .num { text-align: right; }
.fee__input--num { width: 48px; text-align: left; }
.fee__input--pct { width: 64px; }
/* D-229: one block per task the fee is tracked on, and the picker in
   Project settings that chooses them. */
.fee__block { display: flex; flex-direction: column; gap: 12px; padding-top: 12px; border-top: 1px solid var(--border-subtle); }
.fee__task { font-size: var(--text-sm); font-weight: 600; margin: 0; }
.fee__tasks { display: flex; flex-direction: column; gap: 4px; margin: 8px 0; max-height: 260px; overflow-y: auto; }
.fee__task-row { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); }
.fee__task-row input { margin: 0; }
/* The award fee line under Revenue: a part of the line above, not a line. */
.grid td.measure--sub { padding-left: 28px; color: var(--text-secondary); font-weight: 400; }
.grid tr.grid__sub td.month { color: var(--text-secondary); }

/* ---------------------------------- uploading a prime's sheet (D-232) */
/* A dropzone: drop a file on it or choose one; it reads at once. The file
   input hides inside it, so the zone is its positioning context (D-185). */
.dropzone {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 26px 20px; border: 1.5px dashed var(--border-strong); border-radius: var(--r-3);
  background: var(--bg-subtle); text-align: center;
  transition: border-color var(--motion-fast) var(--ease-out), background var(--motion-fast) var(--ease-out);
}
.dropzone.is-over { border-color: var(--signal); background: var(--signal-subtle); }
.dropzone[aria-busy="true"] { opacity: .75; }
.dropzone__icon { color: var(--text-secondary); line-height: 0; }
.dropzone__title { font-size: var(--text-base); font-weight: 600; color: var(--text-primary); }
.dropzone__or { font-size: var(--text-xs); color: var(--text-tertiary); }
.dropzone__hint { font-size: var(--text-sm); color: var(--text-tertiary); margin-top: 2px; }
.dropzone input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.state__zone { width: min(440px, 100%); margin-top: 8px; }
/* D-438: the same zone as one row, above a page that already has a sheet. */
.dropzone--compact { flex-direction: row; flex-wrap: wrap; justify-content: center; padding: 12px 16px; gap: 6px 12px; }
.dropzone--compact .dropzone__hint { margin-top: 0; }
@media (prefers-reduced-motion: reduce) { .dropzone { transition: none; } }
/* The file once read: its name, what was read, and the way to change it. */
.filechip {
  display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  border: 1px solid var(--border-default); border-radius: var(--r-2); background: var(--bg-surface);
}
.filechip > .stack { flex: 1 1 auto; min-width: 0; }
.filechip__icon { color: var(--text-secondary); line-height: 0; }
.filechip__name { font-size: var(--text-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filechip__meta { font-size: var(--text-sm); color: var(--text-tertiary); }
/* A select that keeps to a readable width, and cuts a long option short. */
select.select--fit {
  max-width: 34ch; width: 100%; height: 32px; border: 1px solid var(--border-strong); border-radius: var(--r-2);
  padding: 0 8px; font: inherit; font-size: var(--text-sm); background: var(--bg-surface); color: var(--text-primary); text-overflow: ellipsis;
}
/* Match the columns: a field per row, then the first rows of the sheet. */
.colmap { display: flex; flex-direction: column; gap: 12px; padding: 16px; border: 1px solid var(--border-default);
  border-radius: var(--r-3); background: var(--bg-subtle); }
.colmap h3 { margin: 0; font-size: var(--text-base); }
.colmap__fields td { padding: 6px 8px 6px 0; border: 0; vertical-align: middle; }
.colmap__fields td:first-child { padding-left: 0; width: 40%; }
.colmap__samples th { font-size: var(--text-xs); }
.colmap__as { font-size: var(--text-xs); font-weight: 600; color: var(--text-primary); text-transform: none; letter-spacing: 0; }
.colmap__as--none { color: var(--text-tertiary); font-weight: 400; }
.tag { display: inline-block; font-size: var(--text-xs); color: var(--text-tertiary); background: var(--bg-surface);
  border: 1px solid var(--border-default); border-radius: 999px; padding: 1px 8px; white-space: nowrap; }
.layoutline { gap: 12px; align-items: flex-start; }
.layoutline .btn--link { white-space: nowrap; flex: none; }
/* People on the sheet: the person column takes the room, the select fits it. */
.people-map td:last-child { min-width: 260px; }

/* ------------------------------------------------------------ responsive */
/* D-255: every rule by band, in one place, each inside a media or container
   query — nothing here reaches the desktop layout (tests/test_responsive.py
   holds that). Boundaries are the bands in the comment after the tokens. */

/* lg and up */
@media (min-width: 1024px) {
  /* D-241: four tiles to a row, so seven or eight make even rows. */
  .strip__grid--four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* md and down: a tablet, or a desktop window narrowed */
@media (max-width: 1023px) {
  /* D-088 / D-117 / D-094: fixed form tracks fall back to as many as fit. */
  .drawer__grid--four, .drawer__grid--three { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
  .drawer__grid--addr, .drawer__amounts { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
  /* D-174: a tab row scrolls sideways rather than wrapping its labels. */
  .tabs { overflow-x: auto; scrollbar-width: none; padding: 0 16px; }
  /* D-440: under 1024 the project's tabs scroll sideways inside their row,
     never the page. */
  .pagetabs { overflow-x: auto; scrollbar-width: none; }
  .pagetabs__tab { flex: none; margin-bottom: 0; }
  .tabs [role="tab"] { flex: none; white-space: nowrap; margin-bottom: 0; }
  /* D-163: the dictionary's rail wraps above the terms. */
  .dict { grid-template-columns: minmax(0, 1fr); }
  /* D-255: the bar folds. Tabs stay; Admin, Global Admin, Ask and the
     person's menu become one Menu at the right. The hidden copies leave the
     accessibility tree with display:none. */
  .topbar { gap: 16px; padding-left: 16px; padding-right: 16px; }
  .topbar__tenant { overflow: hidden; text-overflow: ellipsis; max-width: 24ch; }
  .topbar__nav > details.menu, .topbar__who > details.menu:not(.menu--all), .topbar__who > .topbar__askslot { display: none; }
  .topbar__who > details.menu--all { display: block; }
  .menu__panel--all .menu__group--primary { display: none; }
  .menu__panel--all { max-width: calc(100vw - 24px); }
  /* D-223: the Global Admin rail is a row of tabs above the page. */
  .page--rail { display: flex; flex-direction: column; align-items: stretch; gap: 16px; }
  .rail {
    position: static; flex-direction: row; gap: 2px; overflow-x: auto; scrollbar-width: none;
    margin: 0 calc(-1 * var(--page-x)); padding: 0 var(--page-x); border-bottom: 1px solid var(--border-default);
  }
  .rail__title { display: none; }
  /* D-323: the editor and its preview stack; the preview is not sticky. */
  .help__split { grid-template-columns: minmax(0, 1fr); }
  .help__side { position: static; }
  .help__preview { max-height: none; }
  .help__editor { min-height: 40vh; }
  /* D-321: the help rail keeps its group names, as separators in the row;
     a group is a fold laid along the row, its pages beside its name. */
  .rail--help .rail__group { display: flex; flex-direction: row; align-items: stretch; flex: none; }
  .rail--help .rail__group + .rail__group { margin-top: 0; margin-left: 8px; }
  .rail--help .rail__pages { flex-direction: row; gap: 2px; padding-left: 0; }
  .rail--help summary.rail__title { display: flex; flex: none; align-self: center; padding: 0 6px 0 4px; min-height: 44px; }
  .rail a { flex: none; white-space: nowrap; border-radius: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; padding: calc(var(--nav-py) + 2px) 12px; }
  .rail a[aria-current="page"] { background: none; border-bottom-color: var(--action-primary); }
  /* Chrome by band: a narrower gutter, two tiles to a row, panels full width. */
  .page { --page-x: 24px; }
  .strip__grid, .strip__grid--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  dialog.sheet--wide, dialog.sheet--record { width: 100%; }
  .menu__panel { max-width: calc(100vw - 24px); }
  /* A tip is a card at the foot of the screen, where a finger can read it
     and where it cannot push the page wider from a cell at the right edge. */
  .info__tip, .info--right .info__tip { position: fixed; left: 12px; right: 12px; top: auto; bottom: max(12px, env(safe-area-inset-bottom)); width: auto; max-width: none; }
  /* The tip no longer hangs from its cell, so the table's scroll box has no
     reason to let go of its overflow (D-156) — released, a wide table would
     push the page out sideways. */
  .panel__scroll:has(.info--open), .panel__scroll:has(.info--focus), .panel__scroll:has(.info:hover) { overflow: auto hidden; }
  /* D-257: a table in a scroller keeps its first column in view as the rest
     scrolls under it — the name is what the figures are about. Every table,
     unless it opts out with panel__scroll--free; month grids pin their own
     .measure cell already. A cell spanning the row (a drawer, a note) is
     not a first column. Row tints are repeated on the pinned cell, which
     carries a surface of its own so the scrolling cells pass beneath it. */
  .panel__scroll:not(.grid):not(.panel__scroll--free) > table > thead > tr > :first-child:not([colspan]),
  .panel__scroll:not(.grid):not(.panel__scroll--free) > table > tbody > tr > :first-child:not([colspan]),
  .panel__scroll:not(.grid):not(.panel__scroll--free) > table > tfoot > tr > :first-child:not([colspan]) {
    position: sticky; left: 0; z-index: 2; background: var(--bg-surface);
    box-shadow: inset -1px 0 var(--border-default);
  }
  .panel__scroll:not(.grid):not(.panel__scroll--free) > table > thead > tr > :first-child:not([colspan]) { z-index: 3; background: var(--bg-subtle); }
  .panel__scroll > table > tbody > tr.row--flagged > :first-child { background: var(--row-flagged); }
  .panel__scroll > table > tbody > tr.row--skip > :first-child { background: var(--status-critical-tint); }
  .panel__scroll > table > tbody > tr.row--total > :first-child,
  .panel__scroll > table > tfoot > tr.row--total > :first-child,
  .panel__scroll > table > tbody > tr.row--pick:hover > :first-child,
  .panel__scroll > table > tbody > tr.row--choose:hover > :first-child { background: var(--bg-subtle); }
  .panel__scroll > table > tbody > tr.row--holds > :first-child,
  .panel__scroll > table > tbody > tr.row--pick.is-selected > :first-child { background: var(--signal-subtle); }
  .panel__scroll > table > tbody > tr.lines > :first-child { background: var(--bg-subtle); }
  /* A form in a row drawer travels with the columns it sits under (D-171's
     fix for the People drawer, for every drawer): held at the scroller's
     left and no wider than what the panel shows. */
  .panel__scroll tr.drawer .drawer__inner { position: sticky; left: 0; max-width: calc(100vw - 2 * var(--page-x) - 2px); }
}

/* sm: a phone */
@media (max-width: 639px) {
  /* D-323: the reading size holds on a phone; the article's gutter tightens. */
  .help__article { padding: 16px 16px 20px; }
  /* D-283: a panel's menu icon sits top right of its head on a phone. */
  .panel--arranged > .panel__head:has(> .paneltools) { position: relative; padding-right: 56px; }
  .panel--arranged > .panel__head > .paneltools { position: absolute; right: 10px; top: 10px; margin-left: 0; }
  /* The bar: the mark, the organization, Back (installed) and Menu. */
  .topbar { padding-left: max(12px, env(safe-area-inset-left)); padding-right: max(12px, env(safe-area-inset-right)); gap: 10px; }
  .topbar .wordmark__name, .topbar__nav { display: none; }
  .topbar__brand { gap: 10px; min-width: 0; flex: 1 1 auto; }
  .topbar__tenant { max-width: none; flex: 1 1 auto; min-width: 0; }
  .topbar__who { flex: none; }
  .topbar__menulabel { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .menu__panel--all .menu__group--primary { display: flex; }
  /* The Menu is a sheet from the bar down, not a dropdown: nothing hangs off
     screen, and the page beneath is covered rather than scrolled. */
  .topbar__who > details.menu--all > .menu__panel--all {
    position: fixed; top: var(--topbar-offset); right: 0; left: 0; bottom: 0; max-width: none;
    min-width: 0; border-radius: 0; border-width: 0 0 1px; overflow-y: auto;
    padding: 8px 12px max(16px, env(safe-area-inset-bottom));
  }
  /* Every other menu rises from the bottom, behind a scrim; the details owns
     the scrim, so a tap on it reaches the boot-time dismissal. */
  details.menu[open]:not(.menu--all) > .menu__panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; max-height: 70vh; overflow-y: auto;
    min-width: 0; max-width: none; border-radius: var(--r-4) var(--r-4) 0 0;
    padding: 10px 12px max(12px, env(safe-area-inset-bottom)); z-index: 20;
  }
  details.menu[open]:not(.menu--all)::before { content: ''; position: fixed; inset: 0; background: var(--scrim); z-index: 19; }
  .menu__hint { max-width: none; }
  .menu__list { max-height: none; }   /* the sheet is the one scroller */
  .tablebar { padding: 12px 16px; }
  /* The chrome. */
  .page { --page-x: 12px; --page-y: 16px; gap: 16px; padding-left: max(var(--page-x), env(safe-area-inset-left)); padding-right: max(var(--page-x), env(safe-area-inset-right)); }
  .pagehead { flex-direction: column; align-items: stretch; gap: 12px; }
  .pagehead h1 { font-size: var(--text-xl); line-height: 28px; }
  .pagehead__actions { display: flex; flex-wrap: wrap; gap: 8px; }
  .pagehead__actions .btn, .pagehead__actions summary { flex: 1 1 auto; justify-content: center; }
  .breadcrumb { flex-wrap: wrap; row-gap: 4px; }
  .filters__search { width: 100%; flex: 1 1 100%; }
  .filters__select { flex: 1 1 auto; min-width: 0; }
  .filters__select select { min-width: 0; flex: 1 1 auto; }
  .filters__select--fit select { max-width: 100%; }
  .panel__head { flex-wrap: wrap; padding: 12px 16px; }
  /* D-428: a panel's controls wrap under its title rather than running past
     the panel's edge (Mid-month's Columns). */
  .panel__head .filters__end.cluster { flex-wrap: wrap; justify-content: flex-end; }
  .strip { padding: 16px 12px; }
  .strip__grid, .strip__grid--four { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .drawer__grid, .drawer__grid--four, .drawer__grid--three, .drawer__grid--addr, .drawer__amounts { grid-template-columns: minmax(0, 1fr); }
  .drawer__inner { padding: 16px; }
  /* A sheet, a modal and the assistant take the whole screen. */
  dialog.sheet, dialog.sheet--wide, dialog.sheet--record { width: 100%; border-left: 0; }
  .sheet__head, .sheet__foot { padding: 12px 16px; }
  .sheet__foot { padding-bottom: max(12px, env(safe-area-inset-bottom)); }
  dialog.modal { inset: 0; width: 100%; max-width: 100%; height: 100%; max-height: 100%; border: 0; border-radius: 0; }
  dialog.assist { left: 0; width: 100%; border-left: 0; }
  .assist__foot { padding-bottom: max(16px, env(safe-area-inset-bottom)); }
  /* D-390: a figure's popover sits at the foot of the screen, inside its sides. */
  .assist__pop { position: fixed; left: 12px; right: 12px; bottom: max(12px, env(safe-area-inset-bottom)); max-height: 60vh; overflow: auto; z-index: 11; }
  .assist__actions .btn { flex: 1 1 auto; justify-content: center; }
  /* D-296: the chat window takes the screen; the projects are a view of their
     own, and the button hides while the window is open. */
  dialog.chatwin { top: var(--topbar-offset); right: 0; bottom: 0; left: 0; width: 100%; height: auto; border: 0; border-radius: 0; box-shadow: none; }
  dialog.chatwin[open] { grid-template-columns: minmax(0, 1fr); }
  .chatwin__grip { display: none; }
  .chatwin__side { border-right: 0; }
  .chatwin:not(.chatwin--rooms) .chatwin__side, .chatwin--rooms .chatwin__main { display: none; }
  body:has(dialog.chatwin[open]) { overflow: hidden; }
  .chatwin__room { min-height: var(--control-h); }
  .chatwin__foot { padding-bottom: max(12px, env(safe-area-inset-bottom)); }
  /* D-315: three finger-sized tool buttons and Post leave the hint no honest
     room, and eight such buttons fill the row only without a gap. */
  .chat__hint { display: none; }
  .chat__fmtbar { gap: 0; }
  /* D-256: a list table reads as a stack of cards. The table is the one
     source: its rows become cards, its cells the card's parts, from the
     labels labelCells stamped on them. The pinned first column (D-257) has
     no meaning in a card and is let go. */
  .sortbar { display: flex; align-items: center; gap: 8px; flex: 1 1 100%; }
  .sortbar .filters__select { flex: 1 1 auto; }
  .sortslot:empty { display: none; }
  .fresh--compact { flex-basis: 100%; }
  .filters__select { flex: 1 1 calc(50% - 5px); }
  .table--cards, .table--cards > tbody, .table--cards > tfoot { display: block; width: auto !important; }
  .table--cards > colgroup, .table--cards > thead { display: none; }
  .table--cards > tbody > tr, .table--cards > tfoot > tr {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-auto-flow: row dense; gap: 8px 12px;
    padding: 12px 16px; border-bottom: 1px solid var(--border-subtle);
  }
  .table--cards td { display: block; padding: 0; grid-column: 1 / -1; min-width: 0; text-align: left; }
  /* D-263: cards are the page's to scroll, not a box's inside it. */
  .panel__scroll--frozen { max-height: none; }
  /* The pinned first column (D-257) is let go here; its rule outranks a
     single class, so this one names the same chain. */
  .panel__scroll:not(.grid):not(.panel__scroll--free) > .table--cards > tbody > tr > td:first-child:not([colspan]),
  .panel__scroll:not(.grid):not(.panel__scroll--free) > .table--cards > tfoot > tr > td:first-child:not([colspan]),
  .table--cards td { position: static; box-shadow: none; background: none; }
  .table--cards td[data-label]:not([data-label=""])::before {
    content: attr(data-label); display: block; font-size: var(--fs-label); line-height: 16px;
    font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-tertiary);
  }
  .table--cards td[data-card="row"] { display: flex; justify-content: space-between; align-items: baseline; gap: 4px 12px; flex-wrap: wrap; }
  /* D-428: a value too long for its label's line drops under it, kept right. */
  .table--cards td[data-card="row"] > * { margin-left: auto; }
  .table--cards td[data-card="row"]::before { display: inline; flex: none; }
  .table--cards td[data-card="head"] { order: -4; min-height: var(--control-h-sm); }
  .table--cards td[data-card="tag"] { order: -3; grid-column: 2; justify-self: end; }
  .table--cards tr:has(> td[data-card="tag"]) > td[data-card="head"] { grid-column: 1; }
  .table--cards td[data-card="lead"] { order: -2; grid-column: auto; }
  .table--cards td[data-card="lead"].num, .table--cards td[data-card="lead"] .num { font-size: var(--text-base); font-weight: 600; }
  .table--cards td[data-card="head"][data-label]::before, .table--cards td[data-card="tag"][data-label]::before,
  .table--cards td[data-card="body"][data-label]::before, .table--cards td[data-card="actions"][data-label]::before { content: none; }
  .table--cards td[data-card="actions"] { order: 1; }
  .table--cards td[data-card="actions"] .rowactions, .table--cards td[data-card="actions"] .rowactions--tight { flex-wrap: wrap; justify-content: stretch; gap: 8px; }
  .table--cards td[data-card="actions"] .rowactions > * { flex: 1 1 100%; }
  .table--cards td[data-card="actions"] > .btn, .table--cards td[data-card="actions"] .rowactions > .btn,
  .table--cards td[data-card="actions"] details.menu--row, .table--cards td[data-card="actions"] summary.btn { width: 100%; justify-content: center; }
  .table--cards td:empty { display: none; }
  /* A column sized to its content on a desktop (the decision column's 1%)
     is a full-width card line here. */
  .table--cards td.decision, .table--cards td.sev, .table--cards td.proj { width: auto; white-space: normal; }
  .table--cards td.decision .stack__sub { max-width: none; text-align: left; }
  .table--cards > tbody > tr.drawer, .table--cards > tbody > tr.row--lines { display: block; padding: 0; }
  .table--cards tr.drawer .drawer__inner { position: static; max-width: none; padding: 16px; }
  .table--cards > tbody > tr.row--child { margin-left: 12px; border-left: 2px solid var(--border-default); }
  .table--cards > tbody > tr.row--total, .table--cards > tfoot > tr.row--total { background: var(--bg-subtle); }
  /* D-256: Ceilings by task as cards. A picked task tints its whole card
     and keeps the chart's bar at its left; a subtotal reads quieter. */
  .table--cards > tbody > tr.row--pick.is-selected { background: var(--signal-subtle); box-shadow: inset 3px 0 0 var(--signal); }
  .table--cards > tbody > tr.row--pick:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
  .table--cards > tbody > tr.row--flagged { background: var(--row-flagged); }
  .table--cards td.cell--movable { padding-left: 0; }
  .table--cards td[data-card="head"]:has(.row-move) { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
  .table--cards .row-move, .table--cards td.cell--movable > details.row-move { position: static; transform: none; order: 1; opacity: .8; width: var(--control-h-sm); height: var(--control-h-sm); display: inline-flex; align-items: center; justify-content: center; flex: none; }
  /* D-256 part 3: the project page. Six destinations in two columns, the
     section links a row that scrolls, a span's figures on their own line. */
  .pagehead--project .rowactions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; width: 100%; }
  .pagehead--project .rowactions .btn { justify-content: center; }
  .sectionnav { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin-left: calc(-1 * var(--page-x)); margin-right: calc(-1 * var(--page-x)); padding: 0 var(--page-x); }
  .sectionnav a { white-space: nowrap; padding: 12px 0; }
  .sectionnav__sep { display: none; }
  .span__figures { flex-basis: 100%; margin-left: 0; gap: 10px; }
  .alerts-inline__item { grid-template-columns: 5rem minmax(0, 1fr); }
  /* Sign in: the card keeps more of the screen. */
  .signin { padding: 16px 12px; }
  .signin__card { padding: 24px 20px 20px; }
  .enroll__qr { flex-direction: column; }
  /* Documentation's categories as a row of chips; the Change log's folds
     without the desktop's left margin. */
  .dict__term { padding: 14px 16px; }
  .log__summary { padding: 14px 16px 14px 32px; }
  .log__summary::before { left: 12px; }
  .log__body { padding: 0 16px 16px 32px; }
  /* D-257 part 3: the editing flows at a phone's width. A sheet's form grid
     is one column (D-255); what follows is each flow's own shape. */
  .filters--report { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .filters--report .filters__select { width: 100%; }
  .filters--report .filters__select select, .filters--report .filters__select input { flex: 1 1 auto; min-width: 0; }
  .filters--report .filters__select--fit select { max-width: none; }
  /* D-428: a before → after pair stays on one line; its table scrolls. */
  .preview__flow { display: inline-flex; flex-wrap: nowrap; white-space: nowrap; }
  .fee__grid--down .fee__input { width: 100%; min-width: 72px; }
  .fee__pool { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .fee__fill input { width: 100%; }
  .mailpreview { height: 60vh; }
  .rule { grid-template-columns: 44px minmax(0, 1fr); }
  .rule__end { grid-column: 2; justify-self: start; }
  .steps__item:not(.steps__item--current) .steps__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .colmap__fields tr { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; padding: 8px 0; border-top: 1px solid var(--border-subtle); }
  .colmap__fields td { display: block; padding: 0; width: auto; }
  .colmap__fields select { width: 100%; max-width: none; }
  .people-map td:last-child { min-width: 0; }
  .people-map select { width: 100%; max-width: none; }
  dialog.sheet select { max-width: 100%; }
  .taskpick--overlay .taskpick__list { width: min(560px, calc(100vw - 24px)); }
  /* D-257: the chart on a phone. Its controls in two columns, the task
     picker across both; the readout wraps and sits under the plot. */
  .chart { padding: 14px 12px 16px; }
  .chart__controls { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); width: 100%; }
  .chart__controls .chart__pickwrap, .chart__controls > .chart__scope:first-child { grid-column: 1 / -1; }
  .chart__controls .chart__scope { max-width: none; width: 100%; }
  /* D-333: the Scenario and Task choices, each across the panel. */
  .targets__pick { display: grid; grid-template-columns: minmax(0, 1fr); padding: 0 12px 4px; }
  .targets__pick .chart__scope { max-width: none; width: 100%; }
  .chart__pick { width: min(380px, calc(100vw - 32px)); }
  .chart__tip { white-space: normal; max-width: calc(100% - 16px); }
  .chart__tip .chart__mod { max-width: none; }
}

/* A touch screen, any width: what the tokens do not reach grows here. A
   text-shaped control keeps its place in the line and gains its height from
   padding that a negative margin gives back. */
@media (pointer: coarse) {
  .topbar__user { min-height: var(--control-h-sm); min-width: var(--control-h-sm); padding: 0 4px; }
  .btn--link, .th-sort { padding-top: 15px; padding-bottom: 15px; margin-top: -15px; margin-bottom: -15px; min-width: var(--control-h-sm); }
  /* D-331: an award fee's typed months and its Fill row — the boxes and the
     months picker — are finger-sized; found when the walk first saw the
     fee on. */
  .fee__fill input, .fee__input, .fee__fill details.monthpick--inline > summary.monthpick__trigger { height: var(--control-h-sm); }
  .seg button { min-height: var(--control-h-sm); }
  /* A small glyph keeps its size and gains a 44px hit area around it. */
  .info__btn, .disclosure { position: relative; }
  .info__btn::before { content: ''; position: absolute; inset: -14px; }
  .disclosure::before { content: ''; position: absolute; inset: -15px; }
  details.fold > summary { min-height: var(--control-h-sm); align-items: center; }
  .switch { min-height: var(--control-h-sm); }
  .chart__scope { min-height: var(--control-h-sm); }
  /* D-257: the editing flows' rows and options are finger-sized. */
  .taskpick__option { min-height: var(--control-h-sm); }
  /* D-296: the chat's small controls are finger-sized. */
  .reaction, .emoji__btn, .chat__mentions__opt, .chatwin__room, .chat__tool, .chatwin__searchbtn, .chat__jump { min-height: var(--control-h-sm); }
  .reaction, .chat__tool, .chatwin__searchbtn { min-width: var(--control-h-sm); }
  .emoji__btn { width: var(--control-h-sm); }
  .emoji { grid-template-columns: repeat(auto-fill, var(--control-h-sm)); }
  .chat__meta .menu--row > summary { height: var(--control-h-sm); }
  /* D-390: a figure in an answer keeps its size in the line and gains a
     finger-sized hit area; the chips and the questions to ask next grow. */
  .fig { position: relative; }
  .fig::before { content: ''; position: absolute; inset: -13px -14px; }
  .assist__next-q, .assist__starter, .assist__pop .btn--link { min-height: var(--control-h-sm); }
  .assist__chip-x { min-width: var(--control-h-sm); min-height: var(--control-h-sm); margin: -10px -4px -10px -2px; }
  .assist__earlier-row { min-height: var(--control-h-sm); }
  .picker__row { min-height: var(--control-h-sm); }
  .picker__row input, .access input[type="checkbox"] { width: 20px; height: 20px; }
  .access label { min-height: var(--control-h-sm); display: inline-flex; align-items: center; gap: 10px; }
  .children__tick { min-width: var(--control-h-sm); justify-content: center; margin: -8px -12px; }
  .rule__sev, .ladder__cap select, .rule__num { height: var(--control-h-sm); }
  /* D-257: the header sorts, the panel scrolls, the Columns menu moves and
     the stored widths stand — no finger resize, no header drag. */
  th.th--movable { touch-action: auto; }
  .th-move { display: none; }
  .th-grip { pointer-events: none; }
  .th-grip::after, thead:hover .th-grip::after { opacity: 0; }
  .menu__check { min-height: var(--control-h-sm); }
  /* D-230: the row's handle is always there to be seen, and wide enough. */
  td.cell--movable { padding-left: 52px; }
  .row-move { opacity: .8; left: 2px; width: var(--control-h-sm); height: var(--control-h-sm); display: inline-flex; align-items: center; justify-content: center; }
  .row--child .row-move { left: 10px; }
  /* D-280: a widget's and a menu row's handle grow to the touch size too. */
  .widget > .row-move--widget { width: var(--control-h-sm); height: var(--control-h-sm); top: -10px; right: -10px; }
  .spans > .widget > .row-move--widget, .chart__part > .row-move--widget { right: auto; left: -8px; top: -8px; }
  .menu__check > .row-move, .menu__check > details.row-move { width: var(--control-h-sm); height: var(--control-h-sm); }
  label:has(> input[type="checkbox"]), label:has(> input[type="radio"]) { min-height: var(--control-h-sm); align-items: center; }
}

/* A touch screen at phone width: iOS zooms into a field under 16px. */
@media (max-width: 639px) and (pointer: coarse) {
  input, select, textarea { font-size: 16px /* size: iOS zooms a field set below 16px */; }
}
