*{box-sizing:border-box;margin:0;padding:0}
:root{
  /* ---- BlackStone design system. Brand: six colours, nothing else. ---- */
  --green:#83BF40;      --brunswick:#1C4837;  --black:#000000;
  --white:#FFFFFF;      --platinum:#E0E0E0;   --blue:#298FC2;

  /* Grounds */
  --surface:#FFFFFF;    --surface-quiet:#F4F6F3;  --surface-strong:#1C4837;

  /* Ink */
  --ink:#1A1A1A;        --ink-muted:#6B6B6B;  --ink-inverse:#FFFFFF;
  --heading:#1C4837;    --accent-ink:#1C4837; --accent-light:#A8D96A;
  --on-accent:#000000;  /* text on a green fill is black, never white */

  /* Rules and focus */
  --rule:#D8D8D8;       --rule-strong:#1C4837;  --rule-accent:#83BF40;
  --focus:#1C4837;      --focus-inverse:#83BF40;

  /* Status: four pairs, an ink and a tint. Reserved - never a decoration. */
  /*
   * DEVIATION FROM THE DESIGN SYSTEM - status-caution, light theme.
   *
   * The BlackStone design system gives caution as #B26B00. Against its own tint
   * (#F4EADB) that is 3.5:1, which fails WCAG AA for normal text - and the tint
   * exists precisely to be the ground for caution-ink copy: the REVIEW pill
   * (10px bold), .rpt-warning, the pending-sync banner and the worksheet notice
   * all put the two together. So the documented pair could not be used as
   * documented.
   *
   * Darkened one step to #965800:
   *     on status-caution-tint  4.8:1   (was 3.5:1 - failed)
   *     on surface              5.7:1
   *     on surface-quiet        5.2:1
   *
   * Knock-on: a SOLID caution ground now takes ink-inverse, not black. Black on
   * #965800 is 3.7:1; white is 5.7:1. That is why .nb-w and .filters-active read
   * white where they used to read black.
   *
   * The dark theme is untouched - #E2A33F on #38290F is already 6.4:1.
   *
   * TODO: fold #965800 back into the design system artifact so the doc and the
   * code agree. Until then the doc is right about intent and this file is right
   * about the value. - 17 Sep 2026
   */
  /*
   * COLOURS PASS, 17 Oct 2026 (Gord: green, amber and red read too close together). The three inks were all dark and near each other in
   * lightness (Brunswick #1C4837, brown #965800, brick #9B2C1F). Now:
   *   TEXT inks, 4.5:1 or more on surface, surface-quiet and their own tint:
   *     --status-good     #1C4837   10.3:1 surface  9.5:1 quiet  8.7:1 on good-tint  UNCHANGED, Brunswick on purpose (Gord, 17 Oct: it only needs to
   *                                 draw attention when things are not right: calm when fine, loud when not)
   *     --status-caution  #965800   unchanged: a more orange value fails 4.5:1 on the tint
   *     --status-problem  #B91C1C   6.5:1 surface  6.0:1 quiet  5.1:1 on problem-tint  (was #9B2C1F, a brick that read brown)
   *   MARKS, 3:1 or more on surface AND surface-quiet, for graphics that carry no text: dots, the left border of a cell or tile, swatches,
   *   progress fills. They are lighter and more saturated than the inks, which is what pulls the three apart. Never use a mark for text.
   *     --status-good-mark     #1C4837  10.3:1 surface  9.5:1 quiet  (Brunswick: quiet and dark; only caution and problem are bright)
   *     --status-caution-mark  #D06F00  3.5:1 surface  3.2:1 quiet
   *     --status-problem-mark  #DC2626  4.8:1 surface  4.4:1 quiet
   *     --status-unknown-mark  #737373  4.7:1 surface  4.4:1 quiet
   * The dots also carry a glyph and a word (statusDotHtml in app.js), so colour is never the only cue. There is no dark theme to redefine
   * these in (the app has none); --status-problem-light stays the only ink for dark grounds. tools/lib/colors-cases.mjs recomputes every
   * ratio above from this file.
   */
  --status-good:#1C4837;     --status-good-tint:#EEF6E4;
  --status-caution:#965800;  --status-caution-tint:#F4EADB;
  --status-problem:#B91C1C;  --status-problem-tint:#F1E1E0;
  /* The Sheetmetal colour on the schedule map (site-map.js TRADE_PALETTE) is a TRADE colour, not a status: it stays the old brick so the map does not move. */
  --trade-sheetmetal:#9B2C1F;
  --status-good-mark:#1C4837;  --status-caution-mark:#D06F00;  --status-problem-mark:#DC2626;  --status-unknown-mark:#737373;
  --status-info:#1F6E97;     --status-info-tint:#E1EFF6;
  --status-problem-light:#F08A7C;  /* the problem ink for dark grounds only */

  /* Chart series, used in this order, always */
  --chart-1:#83BF40; --chart-2:#1C4837; --chart-3:#298FC2;
  /* Stacked cost chart. Greyscale is deliberate - these print and get marked up - but a mark that
     IS the data must clear 3:1 on white or it reads fainter than the gridlines. The old ramp had
     materials at #C9C9C9 (1.6:1) and the profit cap at #E8E8E8 (1.2:1); both failed. Darkened
     19 Sep. The profit cap also takes a hairline stroke so a light fill still reads. */
  --cost-labor:#4D4D4D;      /* 7.5:1 */
  --cost-subs:#6B6B6B;       /* 4.9:1 */
  --cost-materials:#8A8A8A;  /* 3.5:1 */
  --cost-equipment:#9E9E9E;  /* 3.0:1 */
  --cost-profit:#C9C9C9;     /* the remainder cap, not a cost category - strokes its own edge */
  --chart-4:#6B6B6B; --chart-5:#E0E0E0;
  --ramp-1:#EDF5E3; --ramp-2:#C7E0A0; --ramp-3:#83BF40;
  --ramp-4:#4C8A3E; --ramp-5:#1C4837;

  /* Type: Barlow for everything on screen (brand doc; Gordon, 1 Oct 2026). No Barlow Condensed.
     --font-serif is kept as a token for future print prose, but Libre Caslon is no longer
     downloaded (index.html) because nothing on screen uses it, so it resolves to Georgia.
     --font-mono is for IDs and logs only. */
  --font-sans:'Barlow',system-ui,sans-serif;
  --font-serif:'Libre Caslon Text',Georgia,serif;
  --font-mono:Consolas,'Courier New',monospace;

  /* Spacing */
  --space-1:4px;  --space-2:8px;  --space-3:12px; --space-4:16px;
  --space-6:24px; --space-8:32px; --space-12:48px; --space-16:64px;

  /* Field widths (OP-19 PR C): narrow = a number or a percent, medium = a PO# or an invoice #, wide = a search or free-text box */
  --field-w-narrow:52px; --field-w-medium:110px; --field-w-wide:260px;

  /* Radius: square by default; 2px only on buttons, inputs and chips */
  --radius-none:0; --radius-sm:2px;

  /* On a black ground. ink-muted (#6B6B6B) is only 3.9:1 there, so it is not reused. */
  --surface-black:#000000;  --surface-black-raised:#111111;  --rule-inverse:#222222;
  /* A button on a dark ground (a modal header, the bulk bar) is .btn-inverse (OP-19 PR D): white at low alpha, so it reads on Brunswick or black. */
  --inverse-fill:rgba(255,255,255,0.1);  --inverse-fill-hover:rgba(255,255,255,0.2);  --inverse-rule:rgba(255,255,255,0.3);
  /* A raised surface's shadow (the toasts). Was referenced with a fallback and never defined. */
  --shadow-raised:0 4px 12px rgba(0,0,0,.18);
  /* Microsoft's own brand blue: the one place off-palette colour is correct (.btn-m365, .m365-mark). */
  --m365-blue:#0078D4;  --m365-blue-hover:#006CBE;
  /* Print windows (the T&M sheet, the GC letter, the pull-plan cards, the share Gantt) are separate documents that cannot read these
     variables, so app.js PRINT_TOKENS carries the VALUES of the tokens they use. Change a colour here, change it there: a regression
     case (tools/lib/ui-tokens-cases.mjs) fails while the two disagree. */
  --ink-muted-inverse:#AAAAAA;   /* nav items, 9.0:1 on black */
  --ink-faint-inverse:#8A8A8A;   /* section labels, 5.9:1 on black - was var(--ink-faint-inverse) at 1.7:1 */

  /* ---- Legacy names, so every existing rule keeps working ---- */
  --g:var(--green);        --brn:var(--brunswick);   --blk:var(--black);
  --wht:var(--white);      --plat:var(--platinum);
  --surface2:var(--surface-quiet);  --border:var(--rule);
  --text:var(--ink);       --muted:var(--ink-muted);
  --red:var(--status-problem);  --amber:var(--status-caution);
  --teal:var(--status-good);
  --r:var(--radius-sm);    --rl:var(--radius-none);
}
html,body{height:100%;font-family:var(--font-sans);background:var(--surface);color:var(--text);font-size:14px}
/* --ui-zoom is set by applyTextScale (app.js). The topbar and sidebar are zoomed, so the grid track
   that holds them must grow by the same factor — otherwise the sidebar labels clip and the topbar
   text is cut off at the top, which is exactly what the old fixed 56px/220px tracks did at L and XL. */
.app{display:grid;grid-template-rows:calc(56px * var(--ui-zoom,1)) 1fr;grid-template-columns:calc(220px * var(--ui-zoom,1)) 1fr;height:100vh;overflow:hidden}
.topbar{grid-column:1/-1;background:var(--brn);display:flex;align-items:center;justify-content:space-between;padding:0 20px;border-bottom:3px solid var(--g)}
.sidebar{background:var(--blk);overflow-y:auto;display:flex;flex-direction:column}
.main{overflow-y:auto;padding:24px}
.brand{display:flex;align-items:center;gap:10px}
.brand-sub{font-family:var(--font-sans);font-size:10px;color:var(--g);letter-spacing:3px}
.dl-chip{display:flex;align-items:center;gap:10px}
.dl-label{font-family:var(--font-sans);font-size:11px;color:var(--plat);letter-spacing:0.5px}
.dl-date{font-family:var(--font-sans);font-weight:700;font-size:14px;color:var(--g)}
.dl-date.urgent{color:var(--status-problem-light)}
.dl-days{font-size:11px;color:var(--plat)}
.tb-right{display:flex;align-items:center;gap:10px}
.sdot{width:8px;height:8px;border-radius:50%;background:var(--ink-faint-inverse)}
.sdot.ok{background:var(--g)}
.sdot.warn{background:var(--amber)}
.stxt{font-size:11px;color:var(--plat)}
.sep{width:1px;height:18px;background:rgba(255,255,255,0.15)}
.nsec{padding:16px 12px 4px;font-family:var(--font-sans);font-size:10px;letter-spacing:2px;color:var(--ink-faint-inverse);text-transform:uppercase}
.ni{display:flex;align-items:center;gap:10px;padding:10px 16px;cursor:pointer;border-left:3px solid transparent;color:var(--ink-muted-inverse);font-size:13px;font-weight:600;letter-spacing:0.3px;transition:all 0.12s}
.ni:hover{background:var(--surface-black-raised);color:var(--wht)}
.ni.active{background:var(--surface-black-raised);color:var(--g);border-left-color:var(--g)}
.ni svg{width:16px;height:16px;flex-shrink:0;opacity:0.7}
.ni.active svg,.ni:hover svg{opacity:1}
/* Billing Worksheets (17 Oct 2026): the parent row, then its two pages indented under it. Always visible, so narrow screens need no rule. */
.ni-parent{padding-bottom:4px}
.ni-child{padding-left:34px;font-size:12.5px}
.nb{margin-left:auto;font-size:10px;font-weight:700;padding:1px 6px;border-radius:var(--radius-sm)}
.nb-w{background:var(--amber);color:var(--ink-inverse)}.nb-r{background:var(--red);color:#fff}
.sb-bot{margin-top:auto;padding:14px;border-top:1px solid var(--rule-inverse)}
.panel{display:none}.panel.active{display:block}.panel.active.flex-panel{display:flex;flex-direction:column;overflow:hidden}
.ph{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:20px}
.pt{font-family:var(--font-sans);font-weight:700;font-size:22px;color:var(--brn)}
.ps{font-size:12px;color:var(--muted);margin-top:2px}
.pa{display:flex;gap:8px;align-items:center;flex-shrink:0}
.sl{font-family:var(--font-sans);font-weight:700;font-size:11px;letter-spacing:1.5px;color:var(--muted);text-transform:uppercase;padding-bottom:6px;border-bottom:2px solid var(--g);margin-bottom:12px}
.metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:20px}
/* position:relative is load-bearing, not cosmetic. Every ⓘ data-source icon is placed with
   position:absolute in its tile's corner, and an absolutely-positioned element anchors to its
   nearest POSITIONED ancestor — so without this it escaped the tile and landed in the corner of
   whatever container happened to be positioned further up, or of the page. Twenty call sites
   worked around it with an inline style="position:relative"; the four Billing History cost tiles
   did not, and their icons rendered outside the tile. Declaring it once here is the fix, and it
   lets those inline overrides go. */
.metric{position:relative;background:var(--wht);border:1px solid var(--border);border-radius:var(--r);padding:14px 16px}
.ml{font-size:11px;color:var(--muted);margin-bottom:4px;font-family:var(--font-sans);font-weight:600;text-transform:uppercase}
.mv{font-family:var(--font-sans);font-weight:700;font-size:26px;color:var(--text)}
.mv.green{color:var(--teal)}.mv.blue{color:var(--blue)}.mv.amber{color:var(--amber)}
.ms{font-size:11px;color:var(--muted);margin-top:2px}

/* ── Dashboard hierarchy ───────────────────────────────────────────────────────
   Ten tiles of equal weight is ten things with an equal claim on your attention,
   which in practice is none of them. A PM opens this page during a billing period
   asking three questions - what are we invoicing, are we making money on it, and
   what is still to come - so those three carry the weight. Everything else either
   supports one of them or is an exception waiting to be cleared.

   Nothing here changes a figure or a drill-down; this is weight and grouping only. */
.metrics--lead{grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:0}
.metric--lead{padding:18px 20px;border-top:3px solid var(--rule-accent)}
.metric--lead .mv{font-size:38px;line-height:1.05}
.metric--lead .ms{font-size:11px;margin-top:6px}

.metrics--support{grid-template-columns:repeat(5,1fr);gap:8px;margin:8px 0 0}
.metric--support{padding:10px 12px}
.metric--support .ml{font-size:10px;letter-spacing:0.3px;margin-bottom:2px}
.metric--support .mv{font-size:19px}
.metric--support .ms{font-size:10px;line-height:1.35;margin-top:3px}

/* Exceptions are a count of things to clear, not a measure of the business, so they
   read as one quiet line rather than as two more tiles competing with the figures. */
.metrics--exceptions{grid-template-columns:repeat(2,1fr);gap:8px;margin:8px 0 20px}
.metric--exception{background:var(--surface-quiet);padding:9px 30px 9px 14px;display:flex;align-items:baseline;gap:10px}
/* the 30px right pad is the drill-down glyph's gutter - it is absolutely positioned at
   right:8px, and on a flex row the sub-text and any button run straight underneath it. */
.metric--exception .ml{margin-bottom:0;flex:0 0 auto}
.metric--exception .mv{font-size:20px;line-height:1}
.metric--exception .ms{margin-top:0;margin-left:auto;text-align:right;max-width:58%}

@media (max-width:1100px){
  .metrics--lead{grid-template-columns:repeat(3,1fr)}
  .metrics--support{grid-template-columns:repeat(3,1fr)}
  .metric--lead .mv{font-size:30px}
}
@media (max-width:760px){
  .metrics--lead,.metrics--support,.metrics--exceptions{grid-template-columns:1fr 1fr}
}
.tw{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);overflow:hidden;margin-bottom:16px}
table{width:100%;border-collapse:collapse}
th{background:var(--brn);color:var(--wht);font-family:var(--font-sans);font-weight:700;font-size:11px;letter-spacing:1px;text-align:left;padding:9px 10px;white-space:nowrap}
td{padding:7px 10px;border-bottom:1px solid var(--border);font-size:12px;vertical-align:middle}
tr:last-child td{border-bottom:none}
tr:hover td{background:var(--surface)}
.num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--font-sans);font-weight:600}
.bold{font-weight:700;color:var(--brn)}
.pill{display:inline-block;font-family:var(--font-sans);font-weight:700;font-size:10px;letter-spacing:0.5px;padding:2px 7px;border:1px solid transparent;border-radius:var(--radius-sm);white-space:nowrap}
/* Status pills: four reserved pairs. The word carries the meaning; colour supports it. */
.p-ready{background:var(--status-good-tint);color:var(--status-good);border-color:var(--status-good)}
.p-rev{background:var(--status-caution-tint);color:var(--status-caution);border-color:var(--status-caution)}
.p-prog{background:var(--status-info-tint);color:var(--status-info);border-color:var(--status-info)}
.p-none{background:var(--status-problem-tint);color:var(--status-problem);border-color:var(--status-problem)}
.p-nobill,.p-closed{background:var(--surface-quiet);color:var(--ink-muted);border-color:var(--rule)}
/* Portal pills name a category, not a state, so they must not wear a status colour. */
.p-tex,.p-gc,.p-pro{background:var(--white);color:var(--ink-muted);border-color:var(--rule)}
/* A step still to do in the billing flow (fcWorkflowHtml): neutral, no status colour. */
.p-todo{background:var(--surface);color:var(--ink);border-color:var(--rule)}
.pw{display:flex;align-items:center;gap:5px}
.pb{width:55px;height:5px;background:var(--border);border-radius:var(--radius-sm);overflow:hidden}
.pf{height:5px;background:var(--g);border-radius:var(--radius-sm)}
.pf.warn{background:var(--status-caution-mark)}.pf.over{background:var(--status-problem-mark)}
.pp{font-family:var(--font-sans);font-size:11px;font-weight:600;color:var(--muted)}
/* Buttons (UI audit 1 Oct 2026, A15/A16/A17). Until then .btn and .btn-p were the same solid
   Brunswick block, so every button read as the main action, and the outline variants (.btn-s,
   .btn-d, .btn-i) set only a border and text colour - so they sat on .btn's Brunswick fill:
   "Confirm Invoice" was Brunswick on Brunswick (1.0:1) and every red action 1.37:1.

   Now: .btn is the SECONDARY button (white ground, Brunswick label and border). .btn-p is the
   PRIMARY action (solid Brunswick). .btn-g is the green call-to-action. .btn-d is DANGER, .btn-s
   is "confirm/good", .btn-i is info; each declares its own background so none of them can inherit
   a dark fill again. Every variant also declares its own :hover, because `.btn:hover` (two
   classes' worth of specificity) otherwise beats a one-class variant and repaints it green.
   A button that sits on a dark ground (modal headers, the bulk bar) is .btn-inverse, below. */
.btn{font-family:var(--font-sans);font-weight:600;font-size:12px;padding:7px 13px;border-radius:var(--r);border:1px solid var(--brn);background:var(--surface);color:var(--brn);cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:all 0.12s;white-space:nowrap}
.btn:hover{background:var(--surface-quiet);color:var(--brn);border-color:var(--brn)}
.btn:disabled{opacity:0.45;cursor:not-allowed}
.btn-p{background:var(--brn);color:var(--ink-inverse);border-color:var(--brn)}
.btn-p:hover{background:var(--g);color:var(--brn);border-color:var(--g)}
.btn-g{background:var(--g);color:var(--brn);border-color:var(--g);font-weight:700}
.btn-g:hover{background:var(--brunswick);color:var(--ink-inverse);border-color:var(--brunswick)}
.btn-sm{font-size:11px;padding:4px 8px}
.btn-d{background:var(--surface);border-color:var(--status-problem);color:var(--status-problem)}
.btn-d:hover{background:var(--status-problem);border-color:var(--status-problem);color:var(--ink-inverse)}
.btn-s{background:var(--status-good-tint);border-color:var(--status-good);color:var(--status-good)}
.btn-s:hover{background:var(--status-good);border-color:var(--status-good);color:var(--ink-inverse)}
.btn-i{background:var(--status-info-tint);border-color:var(--status-info);color:var(--status-info)}
.btn-i:hover{background:var(--status-info);border-color:var(--status-info);color:var(--ink-inverse)}
/* THE save-in-progress indicator (22 Oct 2026, busybtn: busyButton in app.js). One look for every button that saves or runs: working = the info pair and the .spin,
   then Saved (good pair) / Partly done (caution pair: a partial result is a warning, rule 16) / Not saved (problem pair). The words carry it, colour is never the only cue.
   The doubled class beats .btn-p and .btn:disabled; each state declares its own :hover (.btn:hover would otherwise repaint it). */
.btn.btn-busy,.btn.btn-busy:hover,.btn.btn-busy:disabled{background:var(--status-info-tint);border-color:var(--status-info);color:var(--status-info);opacity:1;cursor:progress}
.btn.btn-done,.btn.btn-done:hover,.btn.btn-done:disabled{background:var(--status-good-tint);border-color:var(--status-good);color:var(--status-good);opacity:1;cursor:default}
.btn.btn-warn,.btn.btn-warn:hover,.btn.btn-warn:disabled{background:var(--status-caution-tint);border-color:var(--status-caution);color:var(--status-caution);opacity:1}
.btn.btn-bad,.btn.btn-bad:hover,.btn.btn-bad:disabled{background:var(--status-problem-tint);border-color:var(--status-problem);color:var(--status-problem);opacity:1}
.btn.btn-busy .spin{width:11px;height:11px;border-color:var(--status-info-tint);border-top-color:var(--status-info)}
.busy-note{display:inline-block;margin-left:8px;font-size:11px;line-height:1.4;vertical-align:middle;overflow-wrap:anywhere}
.busy-note-ok{color:var(--status-good)}
.busy-note-warn{color:var(--status-caution)}
.busy-note-bad{color:var(--status-problem)}
/* The same indicator for a write with no button of its own (saveIndBegin in app.js): one note at the bottom left, the same three results on the same token pairs. */
.save-ind{position:fixed;left:16px;bottom:16px;z-index:9000;max-width:min(420px,calc(100vw - 32px));padding:7px 12px;border:1px solid var(--rule);border-radius:6px;font-size:12px;line-height:1.4;overflow-wrap:anywhere;box-shadow:0 2px 8px rgba(0,0,0,.12)}
.save-ind[hidden]{display:none}
.save-ind-run{background:var(--status-info-tint);border-color:var(--status-info);color:var(--status-info)}
.save-ind-ok{background:var(--status-good-tint);border-color:var(--status-good);color:var(--status-good)}
.save-ind-warn{background:var(--status-caution-tint);border-color:var(--status-caution);color:var(--status-caution)}
.save-ind-bad{background:var(--status-problem-tint);border-color:var(--status-problem);color:var(--status-problem)}
.save-ind .spin{width:11px;height:11px;border-color:var(--status-info-tint);border-top-color:var(--status-info)}
@media (prefers-reduced-motion:reduce){.btn.btn-busy .spin{animation-duration:2.4s}}
/* Quiet: a secondary button that is not the next step (the billing flow's Record as billed before any claim is sent). Still clickable. */
.btn-quiet{background:var(--surface);border-color:var(--rule);color:var(--ink-muted)}
.btn-quiet:hover{background:var(--surface-quiet);border-color:var(--brn);color:var(--brn)}
/* Microsoft's own brand blue. A sign-in button has to look like the thing it
   signs you in to, so this is the one place off-palette colour is correct. */
.btn-m365{background:var(--m365-blue);color:var(--ink-inverse);border-color:var(--m365-blue);font-weight:700}
.btn-m365:hover{background:var(--m365-blue-hover);border-color:var(--m365-blue-hover);color:var(--ink-inverse)}
.m365-mark{background:var(--m365-blue);color:var(--ink-inverse)}
/* Inverse (OP-19 PR D): a button on a dark ground - the close/cancel in a modal header (.ah), Clear in the bulk bar. Replaces the inline
   rgba(255,255,255,.1) / #fff each one carried. Declares its own :hover for the same reason as the variants above. */
.btn-inverse{background:var(--inverse-fill);color:var(--ink-inverse);border-color:var(--inverse-rule)}
.btn-inverse:hover{background:var(--inverse-fill-hover);color:var(--ink-inverse);border-color:var(--ink-inverse)}
/* Q26. The import-review filter tabs (All / New / Changed / Removed) add and remove .active on a
   .btn, and no rule existed for it -- so all four looked identical while a PM chose which subset of
   an estimate import to approve. Same treatment as .fchk.active, which is the app's existing way of
   saying 'this filter is the one in force'. */
.btn.active{background:var(--brn);color:var(--wht);border-color:var(--brn)}
.card{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);padding:16px;margin-bottom:12px}
.ct{font-family:var(--font-sans);font-weight:700;font-size:14px;color:var(--brn);margin-bottom:10px}
.alert{padding:10px 14px;border-radius:var(--r);margin-bottom:12px;font-size:12px;display:flex;align-items:flex-start;gap:8px;border-left:4px solid;line-height:1.5}
.a-w{background:var(--status-caution-tint);border-color:var(--status-caution-mark);color:var(--status-caution)}
.a-i{background:var(--status-info-tint);border-color:var(--blue);color:var(--status-info)}
.a-s{background:var(--status-good-tint);border-color:var(--status-good-mark);color:var(--status-good)}
.a-d{background:var(--status-problem-tint);border-color:var(--status-problem-mark);color:var(--status-problem)}
.fg{display:flex;flex-direction:column;gap:4px;margin-bottom:12px}
label.lbl{font-size:11px;font-weight:700;color:var(--muted);letter-spacing:0.3px;font-family:var(--font-sans);text-transform:uppercase}
input,select,textarea{font-family:var(--font-sans);font-size:13px;padding:7px 10px;border:1px solid var(--border);border-radius:var(--r);background:var(--wht);color:var(--text);width:100%}
input:focus,select:focus,textarea:focus{border-color:var(--brn)}
/* Field widths (OP-19 PR C). An input or select is 100% wide by default (above). A text box marked wide is AT LEAST wide and still fills its row;
   a select marked wide is AT MOST wide, so a long option cannot push the layout out. */
.fw-narrow{width:var(--field-w-narrow)}
.fw-medium{width:var(--field-w-medium)}
.fw-wide{min-width:min(var(--field-w-wide),100%)}
select.fw-wide{min-width:0;max-width:var(--field-w-wide)}
/* "How this works": the longer explanation under a card's one-line description (Data Setup, AI Assistant, Settings; OP-19 PR C) */
.ds-how{margin-top:4px}
.ds-how>summary{cursor:pointer;color:var(--status-info);font-size:12px;width:max-content}
.ds-how[open]>summary{margin-bottom:4px}
/* Data Setup launcher (11 Oct 2026): small tiles, one panel for the open tool (beside the tiles on a wide screen, under them on a narrow one), and
   the tool-run console at the foot of that panel. #ds-store keeps every card in its group and is hidden only once app.js has drawn the tiles. */
#ds-launcher{display:none}
#panel-data.ds-tiles-on #ds-launcher{display:block}
#panel-data.ds-tiles-on #ds-store{display:none}
.ds-bar{margin:0 0 10px}
.ds-wrap{display:block}
/* Gord 13 Oct: with a tool open the tiles take a third of the width or less (two compact tiles per row); the panel takes the rest. No tool open: full width. */
.ds-wrap.ds-open{display:grid;grid-template-columns:minmax(260px,1fr) minmax(0,2fr);gap:14px;align-items:start}
.ds-wrap.ds-open .ds-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}
.ds-wrap.ds-open .ds-tile{padding:6px 8px}
.ds-gh{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);margin:10px 0 6px}
.ds-fold{display:block;background:none;border:0;padding:0;cursor:pointer;font-family:inherit;text-align:left}
.ds-fold:hover{color:var(--brn)}
.ds-fold-note{font-size:11px;color:var(--muted);margin:-2px 0 6px}
.ds-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:8px}
.ds-tile{display:flex;flex-direction:column;gap:2px;text-align:left;background:var(--wht);border:1px solid var(--border);border-radius:var(--r);padding:8px 10px;cursor:pointer;color:var(--text);min-width:0;font-size:12px}
.ds-tile:hover{border-color:var(--brn)}
.ds-tile.ds-on{border-color:var(--brn);box-shadow:inset 3px 0 0 var(--brn)}
.ds-tile-t{font-weight:700;font-size:12.5px;display:flex;align-items:center;gap:6px;min-width:0}
.ds-tile-t span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ds-tile-p{color:var(--muted);font-size:11px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}   /* up to two lines (21 Oct 2026): a phone has no hover, so the title attribute is out of reach */
.ds-attn{border:1px solid var(--border);border-radius:var(--r);padding:8px 10px;margin:6px 0 10px;background:var(--wht)}
.ds-attn-list{display:flex;flex-direction:column;gap:4px}
.ds-attn-i{display:flex;align-items:center;gap:8px;flex-wrap:wrap;text-align:left;border:1px solid var(--border);border-radius:var(--r);padding:6px 8px;font-size:12px;cursor:pointer;color:var(--text);background:var(--wht)}
.ds-attn-i:hover{border-color:var(--brn)}
.ds-attn-bad{background:var(--status-problem-tint)}
.ds-attn-warn{background:var(--status-caution-tint)}
.ds-attn-t{font-weight:700}
.ds-attn-s{color:var(--muted);min-width:0}
.ds-tile-s{font-size:11px;font-weight:600}
.ds-dot{flex-shrink:0}
.ds-runall{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:8px;font-size:12px}
/* The status dot (statusDotHtml): a 12px mark-coloured circle with a glyph, so colour is never the only cue. The glyph is 9px text, so it clears 4.5:1 on its mark: inverse ink on good (Brunswick, 10.3:1), problem (4.8:1) and unknown (4.7:1), ink on caution (4.9:1: white on #D06F00 would be only 3.5:1, so the mark stays and the glyph ink changes). */
.st-dot{display:inline-flex;align-items:center;justify-content:center;width:12px;height:12px;border-radius:50%;font-size:9px;font-weight:800;line-height:1;color:var(--ink-inverse);flex:none;font-family:var(--font-sans);vertical-align:middle}
.st-caution{color:var(--ink)}
.st-good{background:var(--status-good-mark)}.st-caution{background:var(--status-caution-mark)}.st-problem{background:var(--status-problem-mark)}.st-unknown{background:var(--status-unknown-mark)}
#ds-drawer[hidden]{display:none}
/* One scroll (3 Nov 2026, Gord: "boxes that scroll, then i have to scroll the page, but just the right half"): the panel has no height cap and no scroll of
   its own, for every tool; the page scrolls once. The tile column is NOT sticky: a sticky column taller than the screen hides its lower tiles, and
   capping it would bring back an inner scroll box. */
#ds-drawer{margin:0}
/* Full width (3 Nov 2026, Gord's answer to Q1): QuickBooks check, QuickBooks sync, Previously billed and Contract and change orders (dsWideToolIds) open
   across the page; the tiles fold away and "All tools" at the top of the panel brings them back (it closes the tool). Close stays for the two-pane tools. */
.ds-wrap.ds-open.ds-wide{display:block}
.ds-wrap.ds-wide #ds-tiles{display:none}
#ds-all-tools,.ds-tools-wrap{display:none}
.ds-wrap.ds-wide #ds-all-tools{display:inline-flex}
.ds-wrap.ds-wide .ds-tools-wrap{display:inline-block;position:relative}
/* A slim bar that stays at the top while a long full-width tool scrolls under it (the page scrolls once: this is sticky, not a scroll box). */
.ds-wrap.ds-wide .ds-drawer-head{justify-content:flex-start;position:sticky;top:0;z-index:20;background:var(--wht);padding:8px 0;border-bottom:1px solid var(--border)}
.ds-wrap.ds-wide #ds-drawer-title{flex:1;min-width:0}
#ds-tools-menu{position:absolute;left:0;top:100%;margin-top:4px;min-width:240px;max-height:70vh;overflow-y:auto;background:var(--wht);border:1px solid var(--border);border-radius:var(--r);box-shadow:0 6px 18px rgba(0,0,0,.18);padding:4px;z-index:30}
#ds-tools-menu[hidden]{display:none}
.ds-menu-g{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);padding:6px 8px 2px}
.ds-menu-i{display:block;width:100%;text-align:left;background:transparent;border:none;border-radius:var(--r);padding:6px 8px;font-size:12.5px;color:var(--text);cursor:pointer}
.ds-menu-i:hover{background:var(--surface)}
.ds-menu-i.ds-on{font-weight:700;box-shadow:inset 3px 0 0 var(--brn)}
.ds-wrap.ds-wide #ds-drawer-close{display:none}
/* No scroll box inside the page (3 Nov 2026, step 2): a long QuickBooks check table shows its first ten rows; "Show all N" (qbShowAll) adds qb-all and the rest show. */
.qb-first:not(.qb-all) tr[data-qbmore]{display:none}
.qb-showall{margin-top:4px}
/* The QuickBooks check's titled sections (3 Nov 2026, step 3): one numbered title each, a short muted note, the daily and monthly ones open. */
.qb-sec{border-top:1px solid var(--border);padding:6px 0;margin:0}
.qb-sec>summary.qb-sec-h{cursor:pointer;font-weight:700;font-size:var(--ds-fs-sub,13px);color:var(--brn);padding:4px 0}
.qb-sec[open]>summary.qb-sec-h{margin-bottom:6px}
.qb-sec-note{font-weight:400;color:var(--muted);font-size:var(--ds-fs-help,11px)}
.qb-sec-n{font-weight:700}
.qb-headline{border:1px solid var(--border);border-left-width:4px;border-radius:6px;padding:8px 10px;margin:4px 0 8px}
.qb-headline-t{font-weight:700;font-size:13px}
.qb-headline-times{font-size:12px;color:var(--muted);margin-top:2px}
.qbp-full{margin-top:4px;font-size:11px;color:var(--muted)}
.ds-drawer-head{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:8px}
#ds-drawer-body>.card{border:none;padding:0;margin-bottom:8px}
.ds-console{border-top:1px solid var(--border);margin-top:10px;padding-top:8px;font-size:12px}
.ds-run{border:1px solid var(--border);border-radius:var(--r);padding:6px 8px;margin-top:6px}
/* THE result block of every Admin Settings tool (commit B, 21 Oct 2026: dsResultHtml). running = the info pair with a spinner, ok = the good pair, warn = the caution pair, bad = the problem pair
   (rule 16: a partial success is drawn as the worst of its parts). The words say it too, so colour is never the only cue. */
.ds-result{display:flex;align-items:flex-start;gap:8px;border:1px solid var(--border);border-left-width:4px;border-radius:var(--r);padding:8px 10px;margin:6px 0;font-size:var(--ds-fs-body,12px);line-height:1.45;overflow-wrap:anywhere}
.ds-result .spin{flex:none;margin-top:2px}
.ds-r-body{min-width:0;flex:1}
.ds-r-run{background:var(--status-info-tint);border-color:var(--status-info);color:var(--status-info)}
.ds-r-ok{background:var(--status-good-tint);border-color:var(--status-good);color:var(--status-good)}
.ds-r-warn{background:var(--status-caution-tint);border-color:var(--status-caution);color:var(--status-caution)}
.ds-r-bad{background:var(--status-problem-tint);border-color:var(--status-problem);color:var(--status-problem)}
/* Phone (commit B): the Company grid and the Add New User form stack to one column; button rows already wrap. */
@media (max-width:600px){.ds-cogrid,.ds-form3{grid-template-columns:1fr!important}}
@media (max-width:1100px){.ds-wrap.ds-open{display:block}#ds-drawer{position:static;max-height:none;margin-top:12px}}
/* Keyboard focus ring (OP-19, 11 Oct 2026): every input, select, button and .btn shows where focus is. :focus-visible keeps it off a mouse click on a button. */
input:focus-visible,select:focus-visible,textarea:focus-visible,button:focus-visible,.btn:focus-visible,a:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
/* Q25. `button` is deliberately a SEPARATE rule and must stay one. The rule above also sets
   width:100%, which on a button means every button in the app stretches to fill its container --
   so adding `button` to that selector list looks like the tidier fix and wrecks the layout. 15
   controls carried no .btn class and rendered in the browser's system font, most visibly the
   invoice sort row. Lower specificity than .btn, so it changes nothing that already has a class. */
button{font-family:var(--font-sans)}
/* checks */
.checks{display:flex;gap:3px;align-items:center}
.chk{width:19px;height:19px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;cursor:pointer;flex-shrink:0;border:1.5px solid;position:relative}
.chk-ok{background:var(--status-good-tint);border-color:var(--status-good-mark);color:var(--teal)}
.chk-miss{background:var(--surface2);border-color:var(--border);color:var(--muted)}
.chk-unk{background:var(--surface2);border-color:var(--border);border-style:dashed;color:var(--muted)}
.chk:hover::after{content:attr(title);position:absolute;bottom:120%;left:50%;transform:translateX(-50%);background:var(--surface-strong);color:var(--ink-inverse);font-size:10px;padding:3px 7px;border-radius:var(--radius-sm);white-space:normal;width:max-content;max-width:280px;z-index:20;pointer-events:none;font-family:var(--font-sans)}
/* invoice */
.ii{display:grid;grid-template-columns:1fr auto;border-bottom:1px solid var(--border);transition:background 0.1s}
.ii:last-child{border-bottom:none}
.ii:hover{background:var(--surface)}
.is{width:5px}
/* The Invoices lists are dtTable tables (OP-19): a row keeps the card look. */
.dt.inv-tbl tr.ii{display:table-row;transition:background 0.1s}
.dt.inv-tbl tr.ii:hover{background:var(--surface)}
.dt.inv-tbl td{vertical-align:top}
.dt.inv-tbl td .ir{display:flex}
.ib{padding:9px 12px;min-width:0}
.ifr{font-weight:700;font-size:12px;display:flex;align-items:center;gap:5px;flex-wrap:wrap}
.isub{font-size:11px;color:var(--muted);margin:2px 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.idet{font-size:11px;color:var(--blue)}
.ir{padding:9px 10px;display:flex;flex-direction:column;gap:4px;align-items:flex-end;justify-content:center;min-width:200px}
.iam{font-family:var(--font-sans);font-weight:700;font-size:13px;color:var(--brn)}
/* forecast */
.fg-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px}
.fr{display:flex;justify-content:space-between;align-items:center;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px}
.fr:last-child{border-bottom:none;padding-top:8px}
.fr label{color:var(--muted);font-size:11px;font-family:var(--font-sans);font-weight:600;text-transform:uppercase}
.fr span{font-family:var(--font-sans);font-weight:700;font-size:12px}
.fr.tot span{color:var(--brn);font-size:14px}
.wi{width:55px;font-family:var(--font-sans);font-weight:700;font-size:13px;padding:3px 6px;text-align:center;border:1px solid var(--border);border-radius:var(--r);background:var(--surface2)}
/* connect card */
.cc{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);padding:12px 16px;display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
.ci{display:flex;align-items:center;gap:12px}
.cion{width:36px;height:36px;border-radius:var(--r);display:flex;align-items:center;justify-content:center;font-size:17px;flex-shrink:0}
.cn{font-weight:700;font-size:13px}
.cd{font-size:11px;color:var(--muted);margin-top:2px}
.tag{font-family:var(--font-sans);font-size:10px;font-weight:700;letter-spacing:0.5px;padding:3px 8px;border-radius:var(--radius-sm)}
.t-ok{background:var(--status-good-tint);color:var(--status-good)}.t-pe{background:var(--status-caution-tint);color:var(--status-caution)}.t-er{background:var(--status-problem-tint);color:var(--status-problem)}.t-mu{background:var(--surface-quiet);color:var(--ink-muted)}
/* chat */
/* 100vh inside the zoomed .main is scaled by the zoom too, so divide it back out — at 125% the
   undivided version put the AI Assistant's input 317px below the bottom of the screen (28 Sep). */
.cw{display:flex;flex-direction:column;height:calc(100vh / var(--ui-zoom,1) - 175px)}
.cm{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:10px;background:var(--surface);border:1px solid var(--border);border-radius:var(--rl);margin-bottom:10px}
.msg{max-width:82%;padding:9px 13px;border-radius:var(--rl);font-size:13px;line-height:1.5;white-space:pre-wrap}
.msg-a{background:var(--wht);color:var(--text);align-self:flex-start;border:1px solid var(--border);border-bottom-left-radius:3px}
.msg-a.ld{color:var(--muted)}
.cir{display:flex;gap:8px}
.ci-inp{flex:1;font-family:var(--font-sans);font-size:13px;padding:9px 13px;border:1px solid var(--border);border-radius:var(--rl);background:var(--wht);resize:none}
.ci-inp:focus{border-color:var(--brn)}
/* holidays */
.hg{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.hc{background:var(--wht);border:1px solid var(--border);border-radius:var(--r);padding:10px 12px}
.hn{font-weight:700;font-size:12px;color:var(--brn)}
.hdate{font-size:11px;color:var(--muted);margin:2px 0}
.hb{font-family:var(--font-sans);font-size:11px;font-weight:700;color:var(--blue)}
/* audit */
.ao{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.65);z-index:1000;align-items:center;justify-content:center}
.ao.open{display:flex}
.am{background:var(--wht);border-radius:var(--rl);width:94vw;max-width:1100px;max-height:90vh;display:flex;flex-direction:column;overflow:hidden;border-top:4px solid var(--g)}
.ah{background:var(--brn);color:var(--wht);padding:16px 20px;display:flex;align-items:center;justify-content:space-between}
.at{font-family:var(--font-sans);font-weight:700;font-size:18px}
.as2{font-size:12px;color:rgba(255,255,255,0.7);margin-top:2px}
.ab{flex:1;overflow-y:auto;padding:16px 20px}
.asum{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px}
.ast{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);padding:12px;text-align:center}
.asv{font-family:var(--font-sans);font-weight:700;font-size:24px}
.asv.add{color:var(--teal)}.asv.rem{color:var(--red)}.asv.chg{color:var(--amber)}.asv.unc{color:var(--muted)}
.asl{font-size:11px;color:var(--muted);margin-top:2px;font-family:var(--font-sans);font-weight:600;text-transform:uppercase}
.dt{width:100%;border-collapse:collapse;font-size:12px}
.dt th{background:var(--brn);color:var(--wht);font-family:var(--font-sans);font-weight:700;font-size:11px;letter-spacing:0.8px;padding:8px 10px;text-align:left}
.dt td{padding:7px 10px;border-bottom:1px solid var(--border);vertical-align:top}
.dt tr:last-child td{border-bottom:none}
.dr-rem td{background:var(--status-problem-tint);text-decoration:line-through;color:var(--muted)}
.dr-unc td{color:var(--muted)}
.db{font-family:var(--font-sans);font-size:10px;font-weight:700;padding:2px 6px;border-radius:var(--radius-sm)}
.db-rem{background:var(--status-problem-tint);color:var(--status-problem)}
.db-unc{background:var(--surface2);color:var(--muted)}
.dc{font-size:11px;color:var(--muted)}.dc .old{color:var(--red)}.dc .nw{color:var(--teal)}
.af{padding:14px 20px;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;background:var(--surface)}
.afl{font-size:12px;color:var(--muted)}
/* reports */
.rg{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px}
.rc{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);padding:16px;display:flex;flex-direction:column;gap:10px}
.rn{font-family:var(--font-sans);font-weight:700;font-size:14px;color:var(--brn)}
.rd{font-size:12px;color:var(--muted);flex:1}
.pb2{height:3px;background:repeating-linear-gradient(90deg,var(--g) 0,var(--g) 8px,var(--brn) 8px,var(--brn) 16px);margin-bottom:20px}
.spin{display:inline-block;width:13px;height:13px;border:2px solid var(--border);border-top-color:var(--g);border-radius:50%;animation:sp 0.7s linear infinite}
@keyframes sp{to{transform:rotate(360deg)}}
.lr{display:flex;align-items:center;gap:8px;padding:16px;color:var(--muted);font-size:12px}
.qb-tbl{width:100%;border-collapse:collapse;font-size:12px;margin-bottom:14px}
.qb-tbl th{background:var(--brn);color:var(--wht);font-family:var(--font-sans);font-weight:700;font-size:11px;letter-spacing:0.8px;padding:8px 10px;text-align:left}
.qb-tbl td{padding:8px 10px;border-bottom:1px solid var(--border)}
.qb-tbl td:first-child{font-weight:700;color:var(--brn);background:var(--surface2);white-space:nowrap}
.qb-tbl tr:last-child td{border-bottom:none}


/* ── INVOICE REWORK ── */
.inv-panel-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:8px;flex-wrap:wrap}
.inv-filter-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;padding:8px 0;border-bottom:1px solid var(--border);margin-bottom:10px}
.inv-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:14px}
.inv-stat{background:var(--wht);border:1px solid var(--border);border-radius:var(--r);padding:10px 12px;text-align:center}
.inv-stat-val{font-family:var(--font-sans);font-weight:700;font-size:20px}
.inv-stat-label{font-size:10px;color:var(--muted);font-family:var(--font-sans);font-weight:600;text-transform:uppercase;letter-spacing:0.5px}
/* Bulk action bar */
.bulk-bar{display:none;align-items:center;gap:8px;padding:8px 12px;background:var(--brn);border-radius:var(--r);margin-bottom:10px;flex-wrap:wrap}
.bulk-bar.visible{display:flex}
.bulk-count{font-family:var(--font-sans);font-weight:700;font-size:13px;color:var(--wht)}
/* Searchable project select */
.proj-search-input{font-size:11px;padding:4px 8px;border:1px solid var(--border);border-radius:var(--r);background:var(--wht);width:100%;height:28px}
.proj-search-input:focus{border-color:var(--brn)}
.proj-dropdown{display:none;position:absolute;top:100%;left:0;right:0;background:var(--wht);border:1px solid var(--brn);border-radius:0 0 var(--r) var(--r);max-height:180px;overflow-y:auto;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.1)}
.proj-dropdown.open{display:block}
.proj-option{padding:6px 10px;font-size:11px;cursor:pointer;border-bottom:1px solid var(--border)}
.proj-option:last-child{border-bottom:none}
.proj-option:hover{background:var(--surface2)}
.proj-option.top-match{background:var(--ramp-1);font-weight:700;color:var(--brn)}
.proj-option .proj-pm{font-size:10px;color:var(--muted);margin-left:4px}
/* Flag reasons */
.flag-menu{display:none;position:absolute;right:0;top:100%;background:var(--wht);border:1px solid var(--border);border-radius:var(--r);box-shadow:0 4px 12px rgba(0,0,0,0.1);z-index:100;min-width:220px}
.flag-menu.open{display:block}
.flag-option{padding:8px 12px;font-size:12px;cursor:pointer;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--border)}
.flag-option:last-child{border-bottom:none}
.flag-option:hover{background:var(--surface2)}
.flag-option input[type=checkbox]{accent-color:var(--red)}
/* Action items */
.action-item{background:var(--status-info-tint);border:1px solid var(--status-info);border-radius:var(--r);padding:10px 12px;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between;gap:10px}

/* ── PHASE 1 & 2 UI ── */
.filter-bar{display:flex;gap:6px;flex-wrap:wrap;align-items:center;padding:10px 0;margin-bottom:8px;border-bottom:1px solid var(--border)}
.filter-group{display:flex;align-items:center;gap:5px}
.filter-label{font-family:var(--font-sans);font-size:10px;font-weight:700;letter-spacing:1px;color:var(--muted);text-transform:uppercase;white-space:nowrap}
.filter-sep{width:1px;height:24px;background:var(--border);margin:0 4px}
.search-wrap{position:relative;flex:1;min-width:180px;max-width:280px}
.search-wrap input{padding-left:28px;font-size:12px;height:30px}
.search-icon{position:absolute;left:8px;top:50%;transform:translateY(-50%);color:var(--muted);pointer-events:none;display:flex}
.search-icon svg{width:13px;height:13px}
.fsel{font-family:var(--font-sans);font-size:12px;padding:4px 8px;border:1px solid var(--border);border-radius:var(--r);background:var(--wht);color:var(--text);height:30px;cursor:pointer}
.fsel:focus{border-color:var(--brn)}
.fchk{display:flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);cursor:pointer;padding:4px 8px;border:1px solid var(--border);border-radius:var(--r);background:var(--wht);white-space:nowrap;height:30px;transition:all 0.12s}
.fchk:hover{border-color:var(--brn);color:var(--text)}
.fchk.active{background:var(--brn);color:var(--wht);border-color:var(--brn)}
.fchk input{display:none}
.clear-filters{font-family:var(--font-sans);font-size:11px;color:var(--muted);cursor:pointer;padding:4px 8px;border:none;background:transparent;text-decoration:underline}
.clear-filters:hover{color:var(--brn)}
/* .stat-sm: the four QB-data counts on the Data Setup card were bare divs in a grid. Same surface
   treatment as .metric, at the smaller scale the card is built at. */
.stat-sm{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);padding:8px 10px}
/* sortable headers */
th.sortable{cursor:pointer;user-select:none}
th.sortable:hover{background:var(--ramp-4)}
.sort-indicator{margin-left:4px;opacity:0.5;font-size:10px}
th.sort-asc .sort-indicator::after{content:'▲'}
th.sort-desc .sort-indicator::after{content:'▼'}
th:not(.sort-asc):not(.sort-desc) .sort-indicator::after{content:'⇅';opacity:0.3}
tr.no-bill td{opacity:0.55}
/* result count */
.result-count{font-family:var(--font-sans);font-size:11px;color:var(--muted);letter-spacing:0.3px}
/* vendor memory */
.vm-suggestion{display:flex;align-items:center;gap:6px;padding:4px 8px;background:var(--ramp-1);border:1px solid var(--g);border-radius:var(--r);font-size:11px;color:var(--brn);cursor:pointer;margin-top:4px}
.vm-suggestion:hover{background:var(--status-good-tint)}
/* pm routing */
.pm-route{display:flex;align-items:center;gap:4px;margin-top:3px}
.pm-route select{font-size:10px;padding:2px 4px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface2);height:22px}
/* filter active indicator */
.filters-active{font-family:var(--font-sans);font-size:10px;font-weight:700;background:var(--amber);color:var(--ink-inverse);padding:2px 7px;border-radius:var(--radius-sm);margin-left:6px}


/* ── FORECAST PAGE ── */
.fc-layout{display:grid;grid-template-columns:300px 1fr;gap:16px;flex:1;min-height:0;overflow:hidden}
.fc-sidebar{display:flex;flex-direction:column;gap:12px;overflow-y:auto}
.fc-main{overflow-y:auto;display:flex;flex-direction:column;gap:14px;padding-bottom:32px;min-height:0}
.project-selector{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);padding:14px}
.ps-title{font-family:var(--font-sans);font-weight:700;font-size:12px;color:var(--brn);text-transform:uppercase;letter-spacing:1px;margin-bottom:8px}
.pm-tabs{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:10px}
.pm-tab{font-size:11px;padding:3px 9px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--surface);cursor:pointer;font-family:var(--font-sans);font-weight:700;transition:all 0.12s}
.pm-tab:hover{border-color:var(--brn)}
.pm-tab.active{background:var(--brn);color:var(--wht);border-color:var(--brn)}
.proj-list{display:flex;flex-direction:column;gap:4px;max-height:320px;overflow-y:auto}
.proj-list-item{padding:8px 10px;border-radius:var(--r);cursor:pointer;border:1px solid transparent;font-size:12px;transition:all 0.1s}
.proj-list-item:hover{background:var(--surface2);border-color:var(--border)}
.proj-list-item.active{background:var(--surface2);border-color:var(--g);border-left:3px solid var(--g)}
.proj-list-name{font-weight:700;font-size:12px;color:var(--brn);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.proj-list-meta{font-size:10px;color:var(--muted);margin-top:1px}
/* GP cards */
.gp-cards{display:grid;grid-template-columns:1fr 1fr;gap:10px}
/* Same reason as .metric above — these four sidebar cards now carry ⓘ icons too. */
.gp-card{position:relative;background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);padding:14px}
.gp-label{font-family:var(--font-sans);font-weight:700;font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--muted);margin-bottom:4px}
.gp-value{font-family:var(--font-sans);font-weight:700;font-size:22px}
.gp-value.positive{color:var(--teal)}
.gp-value.negative{color:var(--red)}
.gp-sub{font-size:11px;color:var(--muted);margin-top:2px}
/* Billing worksheet */
.bw-wrap{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);overflow:hidden}
.bw-cat-header{background:var(--brn);color:var(--wht);padding:8px 12px;display:flex;align-items:center;justify-content:space-between;cursor:pointer}
.bw-cat-name{font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-sub,13px);letter-spacing:0.5px}
.bw-cat-total{font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-sub,13px);font-variant-numeric:tabular-nums}
.bw-section-header{background:var(--surface2);padding:5px 12px;font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-sec,11px);color:var(--muted);text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid var(--border)}
.bw-row{display:grid;grid-template-columns:1fr 80px 80px 70px 70px 90px;gap:0;border-bottom:1px solid var(--border);align-items:center}
.bw-row:last-child{border-bottom:none}
.bw-row:hover{background:var(--surface)}
.bw-row.flagged{background:var(--status-caution-tint)}
.bw-row.complete{background:var(--ramp-1);opacity:0.75}
.bw-cell{padding:6px 10px;font-size:var(--ds-fs-td,12px)}
.bw-desc{color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bw-num{text-align:right;font-family:var(--font-sans);font-weight:600;color:var(--muted);font-variant-numeric:tabular-nums}
.bw-budget{text-align:right;font-family:var(--font-sans);font-weight:700;color:var(--text);font-variant-numeric:tabular-nums}
.bw-ctd-wrap{display:flex;align-items:center;justify-content:center;gap:2px}
.bw-ctd{width:var(--field-w-narrow);font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-field,12px);font-variant-numeric:tabular-nums;padding:2px 4px;text-align:center;border:1px solid var(--border);border-radius:var(--r);background:var(--surface2)}
.bw-ctd:focus{border-color:var(--brn);background:#fff}
.bw-ctd.flagged{border-color:var(--status-caution-mark);background:var(--status-caution-tint)}
.bw-this-month{text-align:right;font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-td,12px);color:var(--brn);font-variant-numeric:tabular-nums}
.bw-col-headers{display:grid;grid-template-columns:1fr 80px 80px 70px 70px 90px;gap:0;background:var(--brn);color:var(--ink-inverse);font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-th,11px);letter-spacing:0.8px;text-transform:uppercase}
/* The Committed Subcontracts header is the SAME header, with its seven columns (wssum, 20 Oct 2026): one class for every column-header row on the worksheet. */
.bw-col-headers-subs{grid-template-columns:1fr 90px 90px 90px 70px 70px 90px}
.bw-col-h{padding:5px 10px;text-align:right}
.bw-col-h:first-child{text-align:left}
.bw-subtotal{display:grid;grid-template-columns:1fr 80px 80px 70px 70px 90px;background:var(--surface2);border-top:2px solid var(--border)}
.bw-subtotal-cell{padding:6px 10px;font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-td,12px);font-variant-numeric:tabular-nums;text-align:right;color:var(--brn)}
.bw-subtotal-cell:first-child{text-align:left;color:var(--muted);font-size:var(--ds-fs-sec,11px);text-transform:uppercase;letter-spacing:0.5px}
.bw-materials-row{background:var(--ramp-1);border-bottom:1px solid var(--border)}
/* AI insights */
.insights-card{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);padding:14px}
.insight-item{display:flex;gap:8px;padding:8px 0;border-bottom:1px solid var(--border);font-size:12px;line-height:1.4}
.insight-item:last-child{border-bottom:none}
.insight-text{flex:1;color:var(--text)}
.insight-text strong{color:var(--brn)}
/* Chart */
.chart-wrap{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);padding:14px}
.chart-area{height:140px;position:relative;margin-top:8px}
.chart-bar-wrap{display:flex;align-items:flex-end;gap:3px;height:100%;padding:0 4px}
.chart-bar{border-radius:var(--radius-sm) var(--radius-sm) 0 0;flex:1;min-width:8px;transition:height 0.3s;cursor:pointer;position:relative}
.chart-bar:hover::after{content:attr(title);position:absolute;bottom:105%;left:50%;transform:translateX(-50%);background:var(--surface-strong);color:var(--ink-inverse);font-size:10px;padding:2px 6px;border-radius:var(--radius-sm);white-space:nowrap;z-index:10}
/* estimate upload prompt */
.est-prompt{background:var(--surface2);border:2px dashed var(--border);border-radius:var(--rl);padding:20px;text-align:center;cursor:pointer;transition:all 0.15s}
.est-prompt:hover{border-color:var(--g);background:var(--ramp-1)}
/* Change order */
.co-row{display:flex;gap:8px;align-items:center;padding:6px 10px;border-bottom:1px solid var(--border);background:var(--status-info-tint)}
.co-desc{flex:1;font-size:var(--ds-fs-td,12px);color:var(--text)}
.co-val{font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-td,12px);font-variant-numeric:tabular-nums;color:var(--status-info)}
.co-group-header{padding:4px 10px;font-family:var(--font-sans);font-weight:700;font-size:var(--ds-fs-sec,11px);letter-spacing:0.5px;color:var(--status-info);background:var(--status-info-tint);text-transform:uppercase}


/* ── ESTIMATE REVIEW MODAL ── */
.est-review-cat{margin-bottom:12px;border:1px solid var(--border);border-radius:var(--r);overflow:hidden}
.est-review-cat-hdr{background:var(--brn);color:#fff;padding:8px 12px;display:flex;justify-content:space-between;align-items:center;font-family:var(--font-sans);font-weight:700;font-size:13px}
.est-review-item{display:grid;grid-template-columns:1fr 60px 90px;gap:0;border-bottom:1px solid var(--border);padding:5px 10px;font-size:11px;align-items:center}
.est-review-item:last-child{border-bottom:none}
.est-review-item:hover{background:var(--surface)}
.est-review-hrs{text-align:right;color:var(--muted);font-family:var(--font-sans)}
.est-review-val{text-align:right;font-family:var(--font-sans);font-weight:700;color:var(--brn)}
.est-summary-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px}
.est-summary-card{background:var(--surface2);border:1px solid var(--border);border-radius:var(--r);padding:10px 12px}
.est-summary-label{font-family:var(--font-sans);font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--muted);margin-bottom:2px}
.est-summary-val{font-family:var(--font-sans);font-weight:700;font-size:18px;color:var(--brn)}


/* ── REPORTS PAGE ── */
.rpt-layout{display:grid;grid-template-columns:280px 1fr;gap:16px;flex:1;min-height:0;overflow:hidden}
.rpt-sidebar{display:flex;flex-direction:column;gap:10px;overflow-y:auto}
.rpt-main{overflow-y:auto}
.rpt-main-col{display:flex;flex-direction:column;min-height:0;min-width:0}
.rpt-main-col>.rpt-main{flex:1;min-height:0}
.rpt-period-bar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:10px}
.rpt-proj-item{padding:8px 10px;border-radius:var(--r);cursor:pointer;border:1px solid transparent;font-size:12px;transition:all 0.1s}
.rpt-proj-item:hover{background:var(--surface2);border-color:var(--border)}
.rpt-proj-item.active{background:var(--surface2);border-color:var(--g);border-left:3px solid var(--g)}
.rpt-proj-name{font-weight:700;font-size:12px;color:var(--brn);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rpt-proj-meta{font-size:10px;color:var(--muted);margin-top:1px}
/* SOV editor */
.sov-row{display:grid;grid-template-columns:1fr 110px 110px 28px;gap:4px;align-items:center;padding:4px 0;border-bottom:1px solid var(--border)}
.sov-row:last-child{border-bottom:none}
.sov-desc{font-size:12px;padding:4px 8px;border:1px solid var(--border);border-radius:var(--r);width:100%}
.sov-val{font-size:12px;padding:4px 8px;border:1px solid var(--border);border-radius:var(--r);width:100%;text-align:right}
.sov-del{background:none;border:none;cursor:pointer;color:var(--red);font-size:14px;padding:2px 4px}
.sov-del:hover{background:var(--surface2);border-radius:var(--r)}
/* Report preview */
.rpt-preview{background:var(--wht);border:1px solid var(--border);border-radius:var(--rl);overflow:hidden}
.rpt-preview-hdr{background:var(--brn);color:var(--wht);padding:16px 20px;display:flex;align-items:center;justify-content:space-between}
.rpt-preview-title{font-family:var(--font-sans);font-weight:700;font-size:18px;letter-spacing:0.5px}
.rpt-preview-sub{font-size:11px;color:rgba(255,255,255,0.7);margin-top:2px}
.rpt-tbl{width:100%;border-collapse:collapse;font-size:11px}
.rpt-tbl th{background:var(--brn);color:#fff;padding:6px 10px;text-align:right;font-family:var(--font-sans);font-size:10px;letter-spacing:0.8px;white-space:nowrap}
.rpt-tbl th:first-child{text-align:left}
.rpt-tbl td{padding:6px 10px;border-bottom:1px solid var(--border);text-align:right;font-variant-numeric:tabular-nums}
.rpt-tbl td:first-child{text-align:left}
.rpt-tbl tr:hover td{background:var(--surface)}
.rpt-tbl .section-hdr td{background:var(--surface2);font-family:var(--font-sans);font-weight:700;font-size:11px;color:var(--brn);text-transform:uppercase;letter-spacing:0.5px}
.rpt-tbl .total-row td{background:var(--brn);color:#fff;font-weight:700;font-family:var(--font-sans)}
.rpt-tbl .subtotal-row td{background:var(--surface2);font-weight:700;font-family:var(--font-sans);border-top:2px solid var(--border)}
.rpt-warning{background:var(--status-caution-tint);border:1px solid var(--status-caution-mark);border-radius:var(--r);padding:10px 14px;font-size:12px;color:var(--status-caution);display:flex;gap:8px;align-items:flex-start;margin-bottom:10px}

.sub-cost-row{background:var(--status-info-tint)!important}.sub-cost-row .bw-budget,.sub-cost-row .est-review-val{font-weight:700}
/* Headings (brand doc, decided by Gordon 1 Oct 2026): uppercase Barlow Bold, tracked 25
   (0.025em), no Barlow Condensed. Page, modal and card titles, plus the three section titles that
   already had their own tracking. Kept at the END of the file on purpose: several of these classes
   are declared earlier with a letter-spacing of their own, and this rule has to win over them.
   The muted explanatory span some card titles carry after the name ("QuickBooks Reports - Source
   of truth...") is not part of the heading, so it keeps its own case. */
.pt,.at,.ct,.rpt-preview-title,.ps-title,.bw-cat-name{font-family:var(--font-sans);font-weight:700;text-transform:uppercase;letter-spacing:0.025em}
.ct span[style*="font-weight:400"]{text-transform:none;letter-spacing:normal}
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:var(--radius-sm)}

/* Role screen rules (ROLE_CAPS / applyRoleUI in app.js). Body classes rather than per-element
   inline styles, so a control a renderer draws AFTER the role loads is covered too, and a script
   that later sets style.display on it cannot bring it back. Screen rules only, not a lock. */
body.bsm-nowrite .edit-only:not([data-list]) { display: none !important; }
.edit-only.bsm-list-denied { display: none !important; }
body.bsm-nofin .fin-only { display: none !important; }

/* Billing Worksheet (1 Oct 2026, ROADMAP section 4): the title row carrying Save Billing / Log Billing
   to Tool / Export used to scroll off the top of a long worksheet. It now stays pinned to the top of
   the scrolling .main area. It takes its own space in the layout and has an opaque background, so
   rows scroll UNDER it rather than being hidden behind it. (.flex-panel, the older plan for this,
   stays unused: it needs a fixed panel height and would give the worksheet its own inner scrollbar.) */
#panel-forecast > .ph{position:sticky;top:0;z-index:30;background:var(--surface);margin-bottom:12px;padding:8px 0 10px;border-bottom:1px solid var(--border)}

/* Pinned save bars on the other long pages (2 Oct 2026, ROADMAP section 4). A commit button at the
   END of a long list - Lock Schedule of Values and Save Mapping on the GC Billing Report, Save ticked
   changes on the Project Hub's intake review - scrolled out of reach just as the worksheet's did. It
   is now sticky to the BOTTOM of the scrolling .main area, opaque, and takes its own space, so rows
   scroll under it and the last row is still reachable.
   Sticky binds to the nearest ancestor with any overflow other than visible/clip, and .fc-layout,
   .fc-main, .rpt-layout, .rpt-main and .rpt-preview all set one (none of them actually scrolls: the
   panel has no fixed height, so .main does all the scrolling). Left alone, the bar would pin itself
   to a box that never moves and do nothing. So, ONLY on a page currently showing a bar, those
   boxes switch to overflow:clip (clips the same, but is not a scroll container) or visible with
   min-width:0 (keeps the grid column the width it was). A browser without :has() just shows the bar
   where it always was. The worksheet keeps its own top-pinned title row above. */
.bsm-actionbar{position:sticky;bottom:0;z-index:20;background:var(--surface);border-top:1px solid var(--border);padding:10px 0}
.fc-layout:has(.bsm-actionbar),.rpt-layout:has(.bsm-actionbar),.rpt-preview:has(.bsm-actionbar){overflow:clip}
.fc-main:has(.bsm-actionbar),.rpt-main:has(.bsm-actionbar){overflow:visible;min-width:0}
/* On paper nothing scrolls, so nothing is pinned: every bar prints where it sits in the page. */
@media print{#panel-forecast > .ph,.bsm-actionbar{position:static}}
/* Company Schedule, People view (9 Oct 2026): the page-wide input rule (width:100%) must not stretch the grid's tick boxes or the toolbar. */
#sched-people-body input[type=checkbox]{margin:0 4px 0 0;vertical-align:middle}
#sched-people-body .sched-tools input,#sched-people-body .sched-tools select{width:auto;padding:4px 6px;font-size:12px}
#sched-people-body label{white-space:nowrap}
/* Company Schedule, Map view (11 Oct 2026, Gord: "this page is straying from our branding colors"). The page-wide input rule (padding, border, width:100%)
   was landing on the day slider and the tick boxes, which then drew in the browser's own blue. Tick boxes and the slider take the brand's accent ink
   (Brunswick, 10:1 on white; BlackStone Green is 2.2:1, too faint for a control), and any link on the map (Leaflet's attribution and zoom buttons, or an
   address a browser turns into a link) takes the same ink with a green underline instead of the default link blue. The buttons are the portal's .btn. */
#sched-map-body input[type=checkbox],#sched-map-body input[type=range],#sched-people-body input[type=checkbox]{accent-color:var(--accent-ink)}
#sched-map-body input[type=checkbox]{margin:0;vertical-align:middle}
#sched-map-body input[type=range]{padding:0;border:0;background:transparent}
#sched-map-body a,#sched-map-body a[x-apple-data-detectors]{color:var(--accent-ink);text-decoration-color:var(--rule-accent)}
/* Company Schedule, People and Jobs views in brand colours (11 Oct 2026, Gord: "yes update these, we need to maintain consistency"). Every colour below is
   an app.css :root token, or a tint of one mixed with white (color-mix), never a hex of its own; tools/lib/schedule-brand-cases.mjs fails otherwise.
   - Highlight (focus, crosshair, selection, the readout, the entry box): Brunswick, the brand's focus ink (--focus), where it was a bright blue.
     The crosshair and the selection are drawn as Brunswick LINES, not a wash over the cell: a wash darkens the cell, and "Time off" in caution amber
     then drops under 4.5:1 on a job colour. Lines leave every text colour exactly as readable as it is at rest.
   - Job colours (a crew on one job reads as one block): six brand tints (11 Oct 2026, Gord: "we probably need a few more tints to help differentiate
     job colors"; were three). Green (light green, strong and pale), Blue (light and strong), BlackStone black (a grey) and the green ramp's sage, ordered
     so the next index is always another family (consecutive PO numbers take consecutive indices, schedJobHue). Every text colour in a cell (ink, the
     caution amber of "Time off", the Brunswick of "School" and a shift) reads at 4.5:1 or more on each. A planned day takes the full tint, a day worked
     with nothing planned the paler one. Six is the ceiling: a tint must stay pale for that text, and only so many pale colours stay apart for a
     colour-blind viewer. Two jobs can still share one; the job number is always written in the cell, so the colour is never the only label. No amber
     or brick here: on this page those mean a status (Late, Time off, the school load), so a job never wears them.
   - Inside a day cell the muted grey is a step darker (half ink-muted, half ink), and so is the caution amber of "Time off" / "Sick" (three parts
     caution, one part ink; 7.7:1 on white): the stronger tints are too dark for the plain caution amber (5.7:1 on white) to keep 4.5:1. It is still the
     caution colour, only deeper; the Late tag keeps its own amber on its own ground.
   - Hover (or keyboard focus, or a long press / a tap on the job number on a touch screen) on a job's cell: that job's cells are outlined in Brunswick
     and every other job's cells fade (schedJobHighlight: one class on the grid, one on each of that job's cells; nothing is redrawn). */
#sched-people-body{
  --sched-job-0:color-mix(in srgb,var(--accent-light) 94%,var(--white));  --sched-job-0-worked:color-mix(in srgb,var(--accent-light) 47%,var(--white));
  /* 9 Nov 2026 (Gord): the Job Board colours. Job tints are soft greens, sage and a pale yellow and mint, so none competes with an absence colour
     (red off, blue school, purple sick, orange away, grey stat, pink parental) and none can read as school: no blue, red, purple, orange, pink or grey. */
  --sched-job-1:color-mix(in srgb,var(--sched-c5) 26%,var(--white));      --sched-job-1-worked:color-mix(in srgb,var(--sched-c5) 13%,var(--white));
  --sched-job-2:color-mix(in srgb,var(--ramp-4) 24%,var(--white));        --sched-job-2-worked:color-mix(in srgb,var(--ramp-4) 12%,var(--white));
  --sched-job-3:color-mix(in srgb,var(--sched-c3) 28%,var(--white));      --sched-job-3-worked:color-mix(in srgb,var(--sched-c3) 14%,var(--white));
  --sched-job-4:color-mix(in srgb,var(--accent-light) 34%,var(--white));  --sched-job-4-worked:color-mix(in srgb,var(--accent-light) 17%,var(--white));
  --sched-job-5:color-mix(in srgb,var(--ramp-4) 40%,var(--white));        --sched-job-5-worked:color-mix(in srgb,var(--ramp-4) 20%,var(--white));
  --sched-rowhead:color-mix(in srgb,var(--focus) 9%,var(--white));
}
#sched-grid-wrap td.sg-c{--muted:color-mix(in srgb,var(--ink-muted) 50%,var(--ink));--amber:color-mix(in srgb,var(--status-caution) 75%,var(--ink))}
#sched-grid-wrap.sg-hl td.sg-c[data-job]:not(.sg-hl-on){opacity:.45}
#sched-grid-wrap.sg-hl td.sg-c.sg-hl-on:not(.sg-focus){outline:2px solid var(--focus);outline-offset:-2px}
/* Jobs view: the job name links in the brand's accent ink with a green underline (was the browser's link blue), as on the Map. */
#sched-co-body a,#sched-co-body a[x-apple-data-detectors]{color:var(--accent-ink);text-decoration-color:var(--rule-accent)}
/* People grid as a spreadsheet (10 Oct 2026): one focused cell with its person's row and its day's column marked (a crosshair), a selected block,
   and the readout naming who and which day, large enough to read on the meeting-room TV. Job colours are set inline per cell (schedCellHue: the tints --sched-job-0 to 5 above). */
#sched-grid-wrap:focus{outline:2px solid var(--focus);outline-offset:1px}
#sched-grid-wrap td.sg-c{user-select:none}
/* The Job Board colours (9 Nov 2026, Gord): red = time off, blue = school, purple = sick, orange = away, grey = STAT, pink = parental leave. A pale tint is the cell
   fill; the -strong colour is the stripe, the dashed outline, the legend edge and the Attendance chart's bar. The word (STO, School, Sick, Away, STAT) always stays
   in the cell, so colour is never the only label. Light theme: tints mixed with white, dark ink. Dark theme (data-theme="dark"): deeper tints, light ink. */
:root{
  --sched-sto:color-mix(in srgb,var(--status-problem-mark) 24%,var(--white));   --sched-sto-strong:var(--status-problem-mark);
  --sched-school:color-mix(in srgb,var(--blue) 26%,var(--white));               --sched-school-strong:var(--blue);
  --sched-sick:color-mix(in srgb,var(--sched-c8) 24%,var(--white));             --sched-sick-strong:var(--sched-c8);
  --sched-away:color-mix(in srgb,var(--status-caution-mark) 30%,var(--white));  --sched-away-strong:var(--status-caution-mark);
  --sched-stat:color-mix(in srgb,var(--black) 18%,var(--white));                --sched-stat-strong:var(--ink-muted);
  --sched-parental:color-mix(in srgb,var(--sched-c4) 32%,var(--white));         --sched-parental-strong:var(--sched-c4);
  --sched-late-strong:var(--ink-muted);
  --sched-abs-ink:var(--ink);
  --rest:var(--surface);   /* a half-day cell's other half; the grid sets it per cell */
}
:root[data-theme="dark"]{
  --sched-sto:color-mix(in srgb,var(--status-problem-mark) 55%,var(--surface-black-raised));   --sched-sto-strong:var(--status-problem-light);
  --sched-school:color-mix(in srgb,var(--blue) 50%,var(--surface-black-raised));               --sched-school-strong:var(--blue);
  --sched-sick:color-mix(in srgb,var(--sched-c8) 60%,var(--surface-black-raised));             --sched-sick-strong:var(--sched-c8);
  --sched-away:color-mix(in srgb,var(--status-caution-mark) 50%,var(--surface-black-raised));  --sched-away-strong:var(--status-caution-mark);
  --sched-stat:color-mix(in srgb,var(--white) 28%,var(--surface-black-raised));                --sched-stat-strong:var(--platinum);
  --sched-parental:color-mix(in srgb,var(--sched-c4) 50%,var(--surface-black-raised));         --sched-parental-strong:var(--sched-c4);
  --sched-late-strong:var(--platinum);
  --sched-abs-ink:var(--ink-inverse);
}
.sg-abs-sto{--abs:var(--sched-sto);--abs-line:var(--sched-sto-strong)}
.sg-abs-school{--abs:var(--sched-school);--abs-line:var(--sched-school-strong)}
.sg-abs-sick{--abs:var(--sched-sick);--abs-line:var(--sched-sick-strong)}
.sg-abs-away{--abs:var(--sched-away);--abs-line:var(--sched-away-strong)}
.sg-abs-stat{--abs:var(--sched-stat);--abs-line:var(--sched-stat-strong)}
.sg-abs-parental{--abs:var(--sched-parental);--abs-line:var(--sched-parental-strong)}
/* An absence cell: full fill; a half day (.sg-half-l = morning, .sg-half-r = afternoon) fills half and leaves the rest as the cell was (--rest, set inline);
   requested = diagonal stripes over the fill and a dashed outline (:where keeps the focus outline stronger). */
#sched-grid-wrap td.sg-abs{background-color:var(--abs)}
#sched-grid-wrap td.sg-abs .sg-away{color:var(--sched-abs-ink)}
#sched-grid-wrap td.sg-abs.sg-half{background-color:var(--rest,var(--surface))}
#sched-grid-wrap td.sg-abs.sg-half-l{background-image:linear-gradient(90deg,var(--abs) 50%,transparent 50%)}
#sched-grid-wrap td.sg-abs.sg-half-r{background-image:linear-gradient(270deg,var(--abs) 50%,transparent 50%)}
#sched-grid-wrap td:where(.sg-abs.sg-req){outline:2px dashed var(--abs-line);outline-offset:-3px}
#sched-grid-wrap td.sg-abs.sg-req{background-image:repeating-linear-gradient(135deg,color-mix(in srgb,var(--abs-line) 38%,transparent) 0 2px,transparent 2px 7px)}
#sched-grid-wrap td.sg-abs.sg-req.sg-half-l{background-image:repeating-linear-gradient(135deg,color-mix(in srgb,var(--abs-line) 38%,transparent) 0 2px,transparent 2px 7px),linear-gradient(90deg,var(--abs) 50%,transparent 50%);background-size:50% 100%,100% 100%;background-position:left top,0 0;background-repeat:no-repeat}
#sched-grid-wrap td.sg-abs.sg-req.sg-half-r{background-image:repeating-linear-gradient(135deg,color-mix(in srgb,var(--abs-line) 38%,transparent) 0 2px,transparent 2px 7px),linear-gradient(270deg,var(--abs) 50%,transparent 50%);background-size:50% 100%,100% 100%;background-position:right top,0 0;background-repeat:no-repeat}
/* A STAT holiday: the whole day column is grey, header and cells (a booking that day keeps its own colour). */
#sched-grid-wrap td.sg-statcell{background:var(--sched-stat)}
#sched-grid-wrap th.sg-statcol{background:var(--sched-stat);color:var(--sched-abs-ink)}
#sched-grid-wrap .sg-away{color:var(--amber)}
/* The legend above the grid. */
#sched-people-body .sg-legend{display:flex;gap:10px;flex-wrap:wrap;align-items:center;font-size:11px;margin-bottom:6px}
#sched-people-body .sg-leg{display:inline-flex;align-items:center;gap:4px}
#sched-people-body .sg-sw{display:inline-block;width:14px;height:14px;background:var(--abs);box-shadow:inset 0 0 0 1px var(--abs-line)}
#sched-people-body .sg-sw-req{background-image:repeating-linear-gradient(135deg,color-mix(in srgb,var(--abs-line) 38%,transparent) 0 2px,transparent 2px 5px);outline:1px dashed var(--abs-line);outline-offset:-1px}
#sched-people-body .sg-sw-half{background-image:linear-gradient(90deg,var(--abs) 50%,transparent 50%);background-color:var(--surface)}
/* A- / A+ (schedZoom) sets the grid table's font size; the site-wide td/th sizes (12px / 11px) would otherwise win (Gord, 10 Oct). */
#sched-grid-wrap td,#sched-grid-wrap th{font-size:inherit}
/* Group headers (10 Oct 2026): the label is pinned to the left edge so it stays readable when the grid is scrolled sideways; the fold button scales with A- / A+. */
#sched-grid-wrap .sg-glabel{position:sticky;left:0;display:inline-block;max-width:90vw;box-sizing:border-box;padding:6px 6px 2px;font-weight:700;white-space:normal;background:var(--wht)}
/* A heavy rule where each week begins (Gord, 10 Oct), readable on the TV. The header (dark) gets a white inset line instead: a collapsed border does not travel with a sticky cell. */
#sched-grid-wrap td.sg-wk{border-left:3px solid var(--text)}
#sched-grid-wrap th.sg-wk{box-shadow:inset 3px 0 0 var(--ink-inverse)}
#sched-grid-wrap .sg-fold{font:inherit;width:auto;min-width:1.6em;padding:0 4px;margin:0 4px 0 0;border:1px solid var(--border);border-radius:var(--r);background:var(--wht);color:inherit;cursor:pointer;line-height:1.2}
#sched-people-body .sched-filter input[type=search]{min-width:200px}
#sched-grid-wrap .sg-job{font-size:1.12em;font-weight:800;color:var(--ink)}
/* The crosshair: the focused day's column between two Brunswick lines, the focused person's row (the row holding the focused cell, :has) between two
   more. A browser without :has() draws the row like the column, still a crosshair. A selected block: every cell boxed in Brunswick. */
#sched-grid-wrap td.sg-cross{box-shadow:inset 2px 0 0 var(--focus),inset -2px 0 0 var(--focus)}
#sched-grid-wrap tr:has(td.sg-focus) td.sg-cross{box-shadow:inset 0 2px 0 var(--focus),inset 0 -2px 0 var(--focus)}
#sched-grid-wrap td.sg-c.sg-sel{box-shadow:inset 0 0 0 2px var(--focus)}
#sched-grid-wrap td.sg-focus{outline:4px solid var(--focus);outline-offset:-4px}
#sched-grid-wrap td.sg-saving{opacity:.5;font-style:italic}
/* The focused day's header turns white with Brunswick ink and a Brunswick frame (the other headers are Brunswick, today's is green); the person's name
   cell a pale Brunswick tint with a Brunswick bar. */
#sched-grid-wrap th.sg-colhead{background:var(--surface) !important;color:var(--accent-ink);box-shadow:inset 0 0 0 2px var(--accent-ink)}
#sched-grid-wrap th.sg-wk.sg-colhead{box-shadow:inset 3px 0 0 var(--accent-ink),inset 0 0 0 2px var(--accent-ink)}
#sched-grid-wrap td.sg-name.sg-rowhead{background:var(--sched-rowhead) !important;font-weight:700;box-shadow:inset 4px 0 0 var(--focus)}
#sched-people-body .sg-readout{position:sticky;top:0;z-index:6;display:flex;gap:14px;align-items:center;flex-wrap:wrap;min-height:2.6em;background:var(--wht);border:2px solid var(--accent-ink);border-radius:var(--r);padding:6px 12px;margin-bottom:6px}
#sched-people-body .sg-who{font-size:1.6em;font-weight:800;color:var(--heading);white-space:nowrap}
#sched-people-body .sg-what{font-size:1.05em}
#sched-people-body .sg-range{font-weight:700;background:var(--sched-rowhead);box-shadow:inset 0 0 0 1px var(--focus);padding:2px 8px;border-radius:var(--r)}
#sched-people-body .sg-busy{color:var(--amber);font-weight:700}
#sched-people-body .sg-hint{color:var(--muted)}
#sched-people-body .sg-entry{position:fixed;display:flex;flex-direction:column;z-index:20;background:var(--wht);border:3px solid var(--accent-ink);box-shadow:0 8px 24px rgba(0,0,0,.25);border-radius:var(--r);font-size:inherit}
#sched-people-body .sg-entry.up{flex-direction:column-reverse}
#sched-people-body .sg-entry input{width:100%;font-size:1.15em;font-weight:700;padding:6px 8px;border:0;outline:none;box-sizing:border-box}
#sched-people-body .sg-sug{padding:5px 10px;cursor:pointer;white-space:nowrap;display:flex;align-items:center;gap:8px}
#sched-people-body .sg-sug.on{background:var(--accent-ink);color:var(--ink-inverse)}
/* A job's swatch carries a hairline so a pale tint still reads as a box (3:1) on the white list and on the Brunswick row. */
#sched-people-body .sg-sug-sw{display:inline-block;width:12px;height:12px;border-radius:2px;flex:none;box-shadow:inset 0 0 0 1px var(--ink-muted)}
#sched-people-body .sg-sug-note{padding:5px 10px;color:var(--muted);font-size:.9em;white-space:normal}
#sched-people-body .sg-sug-head{padding:4px 10px 2px;color:var(--muted);font-size:.8em;font-weight:700;text-transform:uppercase;letter-spacing:.04em}
#sched-people-body .sg-mark{display:inline-block;margin-top:2px;padding:0 5px;border-radius:3px;font-size:.85em;font-weight:600;color:var(--status-caution);background:var(--status-caution-tint);border:1px solid var(--status-caution)}
#sched-people-body .sg-statday{color:var(--muted);font-weight:600;letter-spacing:.04em}
#sched-people-body .sg-stat{display:inline-block;margin-top:2px;padding:0 5px;border-radius:3px;font-size:.85em;color:var(--muted);border:1px solid var(--border)}
/* Equipment need-by card (11 Oct 2026): the page-wide input rule (width:100%) stretched each install-task tick box across the whole column and pushed the task names into the On site column (Gord, 2498). */
#pj-needby input[type=checkbox]{vertical-align:middle}
/* To-do board (13 Oct 2026, Gord: "break this into categories with list underneath them"). Summary tiles at the top, one folding category per kind of work,
   one tidy row per item. Urgency colours are the status pairs only: overdue = problem, due this week = caution, otherwise neutral. */
.todo-strip{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:0 0 var(--space-3)}
.todo-tile{font-family:var(--font-sans);display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:132px;padding:var(--space-2) var(--space-3);border:1px solid var(--rule);border-left-width:4px;border-radius:var(--r);background:var(--surface);color:var(--ink);cursor:pointer;text-align:left}
.todo-tile:hover{background:var(--surface-quiet)}
.todo-tile:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.todo-tile-n{font-size:20px;font-weight:700;line-height:1.1}
.todo-tile-l{font-size:12px;font-weight:600}
.todo-tile.todo-overdue{border-color:var(--status-problem-mark);background:var(--status-problem-tint)}.todo-tile.todo-overdue .todo-tile-n{color:var(--status-problem)}
.todo-tile.todo-soon{border-color:var(--status-caution-mark);background:var(--status-caution-tint)}.todo-tile.todo-soon .todo-tile-n{color:var(--status-caution)}
.todo-tile.todo-quiet{border-left-color:var(--rule-strong)}
.todo-tile.todo-nc{border-color:var(--status-caution);border-style:dashed;border-left-style:solid}.todo-tile.todo-nc .todo-tile-n{color:var(--status-caution)}
.todo-cat{border:1px solid var(--rule);border-left:4px solid var(--rule-strong);border-radius:var(--r);margin:0 0 var(--space-2);background:var(--surface)}
.todo-cat.todo-overdue{border-left-color:var(--status-problem-mark)}.todo-cat.todo-soon{border-left-color:var(--status-caution-mark)}
.todo-cat>summary{display:flex;align-items:baseline;gap:var(--space-2);flex-wrap:wrap;padding:var(--space-2) var(--space-3);cursor:pointer;background:var(--surface-quiet);list-style:none}
.todo-cat>summary::-webkit-details-marker{display:none}
.todo-cat>summary::before{content:'\25B8';color:var(--ink-muted);font-size:12px;width:12px}
.todo-cat[open]>summary::before{content:'\25BE'}
.todo-cat>summary:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.todo-cat-l{font-size:15px;font-weight:700;color:var(--heading)}
.todo-chip{display:inline-block;padding:1px 7px;border-radius:var(--r);font-size:12px;font-weight:600;white-space:nowrap;border:1px solid var(--rule);color:var(--ink-muted);background:var(--surface)}
.todo-chip.todo-overdue{color:var(--status-problem);background:var(--status-problem-tint);border-color:var(--status-problem)}
.todo-chip.todo-soon{color:var(--status-caution);background:var(--status-caution-tint);border-color:var(--status-caution)}
.todo-sub{font-size:12px;color:var(--ink-muted);font-weight:400}
.todo-list{width:100%;border-collapse:collapse;table-layout:fixed;font-size:13px}
.todo-list td{padding:var(--space-2) var(--space-3);border-top:1px solid var(--rule);vertical-align:top}
.todo-c-when{width:118px}
.todo-c-job{width:160px}.todo-job{overflow-wrap:anywhere}
.todo-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.todo-c-act{width:200px}.todo-act{text-align:right}
.todo-title{font-weight:600;color:var(--ink)}
.todo-why{font-size:12px;color:var(--ink-muted);margin-top:2px}
.todo-onit{font-size:12px;color:var(--status-good);margin-top:2px;font-weight:600}
.todo-links{display:flex;gap:var(--space-3);justify-content:flex-end;margin-top:4px}
.todo-link{font-family:var(--font-sans);font-size:12px;background:none;border:0;padding:0;color:var(--accent-ink);text-decoration:underline;cursor:pointer}
.todo-link:hover{color:var(--ink)}
.todo-snoozed{opacity:.6}
.todo-jobhead td{background:var(--surface);padding-top:var(--space-3);font-size:13px}
.todo-jobhead + .todo-row td{border-top:0}
@media (max-width:720px){
  .todo-list,.todo-list tbody,.todo-list tr,.todo-list td{display:block;width:auto}.todo-list colgroup{display:none}
  .todo-list tr.todo-row{border-top:1px solid var(--rule);padding:var(--space-2) 0}
  .todo-list td{border-top:0;padding:2px var(--space-3)}
  .todo-act{text-align:left}.todo-links{justify-content:flex-start}
  .todo-tile{min-width:0;flex:1 1 40%}
}
/* Admin Settings type hierarchy and form rows (13 Oct 2026, Gord: "make sure all the fonts are consistently sized for the hierarchy ... things are getting squeezed").
   One scale for every card in the right-hand panel (and the cards while they sit in #ds-store): title 14 (.ct), subhead 13, section heading 11 caps,
   field label 11 caps, body 12, help 11, table head 11, table cell 12, button 11, field 12. The classes below replace the inline font-size the cards used to carry. */
#panel-data{--ds-fs-title:14px;--ds-fs-sub:13px;--ds-fs-sec:11px;--ds-fs-label:11px;--ds-fs-body:12px;--ds-fs-help:11px;--ds-fs-th:11px;--ds-fs-td:12px;--ds-fs-btn:11px;--ds-fs-field:12px;--ds-field-h:34px}
#panel-data .ds-sub{font-size:var(--ds-fs-sub)}
#panel-data .ds-body{font-size:var(--ds-fs-body)}
#panel-data .ds-help{font-size:var(--ds-fs-help)}
#panel-data .ds-subhead{cursor:pointer;font-weight:700;font-size:var(--ds-fs-sub);color:var(--brn);padding:8px 2px}
#panel-data .ds-sec{font-family:var(--font-sans);font-size:var(--ds-fs-sec);font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);margin-bottom:8px}
#panel-data .ct{font-size:var(--ds-fs-title)}
#panel-data .ct span[style*="font-size"]{font-size:var(--ds-fs-help)!important}
#panel-data label.lbl{font-size:var(--ds-fs-label)}
#panel-data th{font-size:var(--ds-fs-th)}
#panel-data td{font-size:var(--ds-fs-td)}
#panel-data .btn{font-size:var(--ds-fs-btn);white-space:nowrap}
#panel-data input,#panel-data select,#panel-data textarea,#panel-data option{font-size:var(--ds-fs-field)}
#panel-data .alert{font-size:var(--ds-fs-body)}
/* Text the JS draws with its own inline size: pulled onto the scale (10px and 9px were below the help size; 13px above body). KPI numerals (16px) are left alone. */
#panel-data [style*="font-size:10px"],#panel-data [style*="font-size:9px"],#panel-data [style*="font-size:10.5px"]{font-size:var(--ds-fs-help)!important}
#panel-data [style*="font-size:13px"]:not(.ds-subhead):not(.ds-sub):not(.btn):not(input):not(select):not(textarea){font-size:var(--ds-fs-body)!important}
#panel-data button.btn[style*="font-size"]{font-size:var(--ds-fs-btn)!important}
/* Form rows. A checkbox is not a 100%-wide field (the global input rule squeezed it, and its label wrapped), and a checkbox label stays on one line. */
#panel-data input[type=checkbox],#panel-data input[type=radio]{margin:0}
#panel-data label:has(>input[type=checkbox]),#panel-data label:has(>input[type=radio]),#panel-data .ds-chk{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;font-size:var(--ds-fs-body);flex:none}
/* Text fields and selects share one height, and a button that sits at the end of a field row lines up with the fields. */
#panel-data input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=hidden]),#panel-data select{min-height:var(--ds-field-h)}
#panel-data select[style*="height:38px"]{height:var(--ds-field-h)!important}
#panel-data .fg+.btn,#panel-data .fg+button{align-self:flex-end;min-height:var(--ds-field-h)}
#panel-data button.btn[style*="height:38px"]{height:var(--ds-field-h)!important}
/* The same scale on the HR page and the School tab (15 Oct 2026): the paragraph above a table was 14px, louder than the table. */
#panel-hr,#sched-school-body{--ds-fs-title:14px;--ds-fs-sub:13px;--ds-fs-sec:11px;--ds-fs-label:11px;--ds-fs-body:12px;--ds-fs-help:11px;--ds-fs-th:11px;--ds-fs-td:12px;--ds-fs-btn:11px;--ds-fs-field:12px;--ds-field-h:34px}
#panel-hr .ds-sub{font-size:var(--ds-fs-sub)}
#sched-school-body .ds-sub{font-size:var(--ds-fs-sub)}
#panel-hr .ds-body{font-size:var(--ds-fs-body)}
#sched-school-body .ds-body{font-size:var(--ds-fs-body)}
#panel-hr .ds-help{font-size:var(--ds-fs-help)}
#sched-school-body .ds-help{font-size:var(--ds-fs-help)}
#panel-hr .ds-subhead{cursor:pointer;font-weight:700;font-size:var(--ds-fs-sub);color:var(--brn);padding:8px 2px}
#sched-school-body .ds-subhead{cursor:pointer;font-weight:700;font-size:var(--ds-fs-sub);color:var(--brn);padding:8px 2px}
#panel-hr .ds-sec{font-family:var(--font-sans);font-size:var(--ds-fs-sec);font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);margin-bottom:8px}
#sched-school-body .ds-sec{font-family:var(--font-sans);font-size:var(--ds-fs-sec);font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);margin-bottom:8px}
#panel-hr .ct{font-size:var(--ds-fs-title)}
#sched-school-body .ct{font-size:var(--ds-fs-title)}
#panel-hr .ct span[style*="font-size"]{font-size:var(--ds-fs-help)!important}
#sched-school-body .ct span[style*="font-size"]{font-size:var(--ds-fs-help)!important}
#panel-hr label.lbl{font-size:var(--ds-fs-label)}
#sched-school-body label.lbl{font-size:var(--ds-fs-label)}
#panel-hr th{font-size:var(--ds-fs-th)}
#sched-school-body th{font-size:var(--ds-fs-th)}
#panel-hr td{font-size:var(--ds-fs-td)}
#sched-school-body td{font-size:var(--ds-fs-td)}
#panel-hr .btn{font-size:var(--ds-fs-btn);white-space:nowrap}
#sched-school-body .btn{font-size:var(--ds-fs-btn);white-space:nowrap}
#panel-hr input,#panel-hr select,#panel-hr textarea,#panel-hr option{font-size:var(--ds-fs-field)}
#sched-school-body input,#sched-school-body select,#sched-school-body textarea,#sched-school-body option{font-size:var(--ds-fs-field)}
#panel-hr .alert{font-size:var(--ds-fs-body)}
#sched-school-body .alert{font-size:var(--ds-fs-body)}
#panel-hr [style*="font-size:10px"],#panel-hr [style*="font-size:9px"],#panel-hr [style*="font-size:10.5px"]{font-size:var(--ds-fs-help)!important}
#sched-school-body [style*="font-size:10px"],#sched-school-body [style*="font-size:9px"],#sched-school-body [style*="font-size:10.5px"]{font-size:var(--ds-fs-help)!important}
#panel-hr [style*="font-size:13px"]:not(.ds-subhead):not(.ds-sub):not(.btn):not(input):not(select):not(textarea){font-size:var(--ds-fs-body)!important}
#sched-school-body [style*="font-size:13px"]:not(.ds-subhead):not(.ds-sub):not(.btn):not(input):not(select):not(textarea){font-size:var(--ds-fs-body)!important}
#panel-hr button.btn[style*="font-size"]{font-size:var(--ds-fs-btn)!important}
#sched-school-body button.btn[style*="font-size"]{font-size:var(--ds-fs-btn)!important}
#panel-hr input[type=checkbox],#panel-hr input[type=radio]{margin:0}
#sched-school-body input[type=checkbox],#sched-school-body input[type=radio]{margin:0}
.lc-toggle label{display:inline-flex;align-items:center;gap:6px}
.lc-toggle input[type=checkbox]{margin:0;accent-color:var(--accent-ink)}
#panel-hr label:has(>input[type=checkbox]),#panel-hr label:has(>input[type=radio]),#panel-hr .ds-chk{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;font-size:var(--ds-fs-body);flex:none}
#sched-school-body label:has(>input[type=checkbox]),#sched-school-body label:has(>input[type=radio]),#sched-school-body .ds-chk{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;font-size:var(--ds-fs-body);flex:none}
#panel-hr input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=hidden]),#panel-hr select{min-height:var(--ds-field-h)}
#sched-school-body input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=hidden]),#sched-school-body select{min-height:var(--ds-field-h)}
#panel-hr select[style*="height:38px"]{height:var(--ds-field-h)!important}
#sched-school-body select[style*="height:38px"]{height:var(--ds-field-h)!important}
#panel-hr .fg+.btn,#panel-hr .fg+button{align-self:flex-end;min-height:var(--ds-field-h)}
#sched-school-body .fg+.btn,#sched-school-body .fg+button{align-self:flex-end;min-height:var(--ds-field-h)}
#panel-hr button.btn[style*="height:38px"]{height:var(--ds-field-h)!important}
#sched-school-body button.btn[style*="height:38px"]{height:var(--ds-field-h)!important}
/* Grant access (Field Portal job access): three pickers wrap as a group instead of squeezing, the picked chips sit directly under their picker,
   and the checkbox, the Grant button and the status read as one row. */
#panel-data .ds-grant{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:8px 10px;align-items:start}
#panel-data .ds-grant .fg{min-width:0}
#panel-data .ds-grant [id$="-chips"]{display:flex;flex-wrap:wrap;gap:4px;margin-top:2px}
#panel-data .ds-actrow{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;margin-top:10px}
#panel-data .ds-actrow .ds-body,#panel-data #field-access-status{font-size:var(--ds-fs-body)}
/* A row of buttons or fields drawn with an inline display:flex wraps as a group when the panel is narrow, rather than running out of the card (Database tools, Sign-ins, Trade roster). */
#panel-data [style*="display:flex"]:not([style*="flex-direction"]):not([style*="nowrap"]):not([style*="inline-flex"]):not(td):not(th){flex-wrap:wrap}
/* The shared table helper (dtTable): one top row (title, search, filters, count, actions on the right), the pager under the table. */
.dt-bar{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px}
.dt-bar .dt-title{margin:0}
.dt-bar input.dt-search{width:var(--field-w-wide);max-width:100%}   /* the search box over every shared table is the standard wide field (OP-19), not the 110px of fw-medium */
.dt-bar select.dt-filter{width:auto;max-width:200px;height:30px}
.dt-count{font-size:11px;color:var(--muted)}
.dt-actions{margin-left:auto;display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.dt-pager{display:flex;gap:8px;align-items:center;margin-top:6px}
/* A textarea that carries .fsel kept the single-line field height (30px), so the email wording was a one-line slit (15 Oct 2026 review). */
textarea.fsel{height:auto;min-height:14em;cursor:text;line-height:1.45;resize:vertical;padding:6px 8px}
/* The HR tiles in groups, each under its small caption. */
.hr-groups{display:flex;flex-wrap:wrap;gap:6px 22px;margin-bottom:10px}
.hr-group .ds-grid{display:flex;flex-wrap:wrap;gap:6px}
.hr-group .ds-tile{width:200px}
.hr-firstrun{border-color:var(--amber)}
/* With a person open beside the list on a medium screen, the list drops its two least-needed columns so a name never wraps to two lines. */
@media (max-width:1500px){.hr-wrap.hr-open .dt .dt-opt{display:none}}
/* Wide tables scroll inside their own box on a narrow panel instead of being clipped; status pills use the help size. */
#panel-data #user-mgmt-table,#panel-data #field-access-table{overflow-x:auto!important}
#panel-data .tag{font-size:var(--ds-fs-help)}

/* Sticky table headers (Gord, 13 Oct 2026: "when you scroll this box can the column names be frozen?"). One rule for every table that scrolls inside its own box:
   the header row stays at the top of the box. The scroll boxes are drawn with an inline max-height plus overflow:auto, so they are matched on that, and the
   admin tables by id. A box with overflow-x:auto only (no max-height) is not matched: it never scrolls up and down, so there is nothing to freeze there.
   Sticky pins to the NEAREST scrolling ancestor, so these boxes (which scroll both ways) work as they are. The header needs a solid background so rows do not
   show through; tables whose headers are drawn transparent get the white card colour instead. */
.tw[style*="max-height"] > table > thead > tr > th,
div[style*="max-height"][style*="overflow:auto"] > table > thead > tr > th,
div[style*="max-height"][style*="overflow-y:auto"] > table > thead > tr > th,
#audit-log-content > table > thead > tr > th,
#signin-lastseen-table > table > thead > tr > th,
#signin-recent-table > table > thead > tr > th,
.sticky-head > table > thead > tr > th{position:sticky;top:0;z-index:2;background-clip:padding-box;box-shadow:0 1px 0 var(--border),0 2px 4px rgba(0,0,0,0.12)}
.tw[style*="max-height"] > table > thead > tr > th[style*="background:transparent"],
div[style*="max-height"][style*="overflow:auto"] > table > thead > tr > th[style*="background:transparent"],
div[style*="max-height"][style*="overflow-y:auto"] > table > thead > tr > th[style*="background:transparent"]{background:var(--wht)!important}

/* HR page (OP-58, 14 Oct 2026): the people table and, with a person open, their panel beside it (under it on a narrow screen). */
.hr-wrap{display:block}
.hr-wrap.hr-open{display:grid;grid-template-columns:minmax(0,2fr) minmax(280px,1fr);gap:14px;align-items:start}
.hr-wrap.hr-open #hr-panel{max-height:calc(100vh - 90px);overflow:auto;margin:0}
.hr-sel>td{box-shadow:inset 0 0 0 9999px rgba(0,0,0,.04)}
@media (max-width:1100px){.hr-wrap.hr-open{display:block}.hr-wrap.hr-open #hr-panel{max-height:none;margin-top:12px}}
/* HR person panel, new hire, end of employment, raise letter (15 Oct 2026): sections, two-column fields, Save apart from End employment, groups in columns, a send bar that stays in view. */
.hr-sec{margin:14px 0 6px;padding-top:8px;border-top:1px solid var(--border)}
.hr-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:6px 10px}
.hr-grid .fsel,.hr-grid select,.hr-grid input{width:100%}
.hr-grid2{grid-template-columns:repeat(2,minmax(0,1fr))}
.hr-wide{grid-column:1/-1}
.hr-savebar{margin:12px 0 0;display:flex;gap:6px}
.hr-endbar{display:flex;gap:6px;margin-top:22px;padding-top:10px;border-top:1px dashed var(--border);justify-content:flex-end}
.btn.hr-danger{background:transparent;color:var(--red);border-color:var(--red);font-weight:400}
.hr-tick{display:flex;gap:8px;align-items:center;justify-content:space-between;margin-top:4px}
.hr-seg{display:inline-flex;gap:2px;flex:none}
.hr-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:0 22px}
.hr-actbar{display:flex;gap:6px;padding:8px 0;margin:6px 0;border-bottom:1px solid var(--border)}
@media (max-width:700px){.hr-grid2{grid-template-columns:1fr}}
/* ── Vehicles (16 Oct 2026). The chart series are the dataviz reference hues, validated with its palette checker (all pairs, light and dark
   surfaces): four class hues for the age/km scatter, which ALSO differ by shape; status colours always sit beside their words. Ink, grid and
   surface come from the page's own tokens, so the charts follow the theme. Dark values apply only if the page is ever given data-theme="dark". */
.veh-viz{--veh-s1:#2a78d6;--veh-s2:#eb6834;--veh-s3:#1baf7a;--veh-s4:#4a3aa7;--veh-good:var(--status-good-mark);--veh-warn:var(--status-caution-mark);--veh-crit:var(--status-problem-mark);--veh-none:var(--status-unknown-mark);margin-top:10px}
:root[data-theme="dark"] .veh-viz{--veh-s1:#3987e5;--veh-s2:#d95926;--veh-s3:#199e70;--veh-s4:#e87ba4}
.veh-tabs{display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap}
.veh-chart{max-width:100%;height:auto;display:block;font-family:var(--font-sans)}
.veh-chart .veh-grid{stroke:var(--rule);stroke-width:1}
.veh-chart .veh-base{stroke:var(--ink-muted);stroke-width:1}
.veh-chart .veh-axis{fill:var(--ink-muted);font-size:11px}
.veh-chart .veh-mark-label,.veh-chart .veh-end-label{fill:var(--ink);font-size:11px;paint-order:stroke;stroke:var(--surface);stroke-width:3px;stroke-linejoin:round}
.veh-chart .veh-l1,.veh-legend .veh-l1{fill:none;stroke:var(--veh-s1);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.veh-chart .veh-l2,.veh-legend .veh-l2{fill:none;stroke:var(--veh-s2);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.veh-chart .veh-l3,.veh-legend .veh-l3{fill:none;stroke:var(--veh-s3);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.veh-chart .veh-pt1{fill:var(--veh-s1);stroke:var(--surface);stroke-width:2}
.veh-chart .veh-eq-up{fill:var(--veh-s3);opacity:.14}
.veh-chart .veh-eq-down{fill:var(--ink-muted);opacity:.14}
.veh-chart .veh-band{fill:var(--ink);opacity:.06}
.veh-chart .veh-today{stroke:var(--ink);stroke-width:1.5}
.veh-chart .veh-leaseend{stroke:var(--ink-muted);stroke-width:1}
.veh-chart .veh-mark-pt{fill:var(--surface);stroke:var(--ink);stroke-width:2}.veh-chart .veh-mark-pt-est{stroke-dasharray:3 2}
.veh-chart .veh-orderby{stroke:var(--veh-s4);stroke-width:1.5;stroke-dasharray:4 3}
.veh-chart .veh-best{stroke:var(--ink);stroke-width:1.5}
.veh-chart .veh-limit{stroke:var(--veh-crit);stroke-width:1}
.veh-chart .veh-dot-today{fill:var(--ink);stroke:var(--surface);stroke-width:2}
.veh-chart .veh-ob{fill:var(--ink)}
.veh-chart .veh-bar{stroke:var(--surface);stroke-width:2}
.veh-s-good{fill:var(--veh-good);background:var(--veh-good)}.veh-s-warn{fill:var(--veh-warn);background:var(--veh-warn)}.veh-s-crit{fill:var(--veh-crit);background:var(--veh-crit)}.veh-s-none{fill:var(--veh-none);background:var(--veh-none)}
.veh-c1{fill:var(--veh-s1);stroke:var(--surface);stroke-width:1.5}.veh-c2{fill:var(--veh-s2);stroke:var(--surface);stroke-width:1.5}.veh-c3{fill:var(--veh-s3);stroke:var(--surface);stroke-width:1.5}.veh-c4{fill:var(--veh-s4);stroke:var(--surface);stroke-width:1.5}.veh-c0{fill:none;stroke:var(--ink-muted);stroke-width:2}
.veh-hit{pointer-events:all}
.veh-chart .veh-eqbar-up{fill:var(--veh-s1)}.veh-chart .veh-eqbar-down{fill:var(--veh-crit)}.veh-chart .veh-assumed{opacity:.55}
.veh-legend{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:12px;color:var(--ink);margin:4px 0}
.veh-key{display:inline-flex;align-items:center;gap:5px}
.veh-swatch{display:inline-block;width:10px;height:10px;border-radius:2px}
.veh-st{display:inline-flex;align-items:center;gap:5px;font-weight:600;color:var(--ink)}
.veh-flag{font-size:11px;color:var(--amber);font-weight:600}
.veh-warnline{color:var(--amber)}
.veh-empty{color:var(--ink-muted);font-style:italic;margin-top:6px}
.veh-life{margin:4px 0 6px 18px;padding:0}
.veh-dealev{margin-top:10px}
.veh-dealtbl input.fsel,.veh-dealtbl select.fsel{min-width:110px}
.veh-dash{color:var(--ink-muted)}
.veh-signals{margin:4px 0 4px 18px}
/* fleetdash (17 Oct 2026): the four spend series reuse the validated hues; a month not synced is a dashed empty slot, never a $0 bar. */
.veh-f1{fill:var(--veh-s1);background:var(--veh-s1)}.veh-f2{fill:var(--veh-s2);background:var(--veh-s2)}.veh-f3{fill:var(--veh-s3);background:var(--veh-s3)}.veh-f4{fill:var(--veh-s4);background:var(--veh-s4)}
.veh-chart .veh-gap{fill:none;stroke:var(--ink-muted);stroke-width:1}.veh-gap-key{border:1px dashed var(--ink-muted);background:transparent}
.veh-chart .veh-ptflag{fill:var(--veh-crit);stroke:var(--surface);stroke-width:2}

/* How the data flows (qbviz, 17 Oct 2026): the pipeline panel at the top of QuickBooks: uploads vs sync */
.qbp{border:1px solid var(--border);border-radius:var(--r);padding:8px 10px;margin:0 0 12px}
.qbp-head{display:flex;flex-wrap:wrap;gap:4px 12px;align-items:center;justify-content:space-between}
.qbp-grid{display:grid;grid-template-columns:minmax(100px,.8fr) repeat(4,minmax(0,1fr));gap:4px;margin-top:8px}
.qbp-colh{font-size:11px;font-weight:700;color:var(--muted);padding:0 2px}
.qbp-rowh{font-weight:700;font-size:12px;padding:5px 2px;overflow-wrap:anywhere}
.qbp-cell{border:1px solid var(--border);border-left-width:4px;border-radius:6px;padding:5px 7px;font-size:12px;line-height:1.4;cursor:pointer;min-width:0;overflow-wrap:anywhere}
.qbp-b-green{border-left-color:var(--status-good-mark)}.qbp-b-amber{border-left-color:var(--status-caution-mark)}.qbp-b-red{border-left-color:var(--status-problem-mark)}.qbp-b-grey{border-left-color:var(--status-unknown-mark)}
.qbp-clab{display:none;font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.qbp-dot{margin-right:6px}
.qbp-tag{margin-top:3px;font-size:11px;color:var(--muted)}
.qbp-why{margin:4px 0 0 14px;padding:0;font-size:11px;color:var(--muted)}
.qbp-line{margin-top:8px;font-size:12px;line-height:1.4}
@media (max-width:720px){.qbp-grid{grid-template-columns:1fr}.qbp-colh{display:none}.qbp-clab{display:block}.qbp-rowh{margin-top:8px;border-top:1px solid var(--border);padding-top:8px}}
.qbp-actw{margin-top:5px}
.qbp-act{max-width:100%;white-space:normal;text-align:left}
.qbp-actwhy{margin-top:2px;font-size:11px;color:var(--muted);line-height:1.35}
.qbp-run{margin-top:8px;padding:8px 10px;border:1px solid var(--border);border-radius:6px;font-size:12px;line-height:1.4}
.qbp-steps{margin:4px 0 6px 18px;padding:0}
@media (max-width:720px){.qbp-act{min-height:40px;width:100%}}
.qbp-all{border:1px solid var(--border);border-radius:var(--r);padding:8px 10px;margin:0 0 8px}
.qbp-when,.qbp-other{line-height:1.45}
.qbp-other{margin-top:8px}
.qbp-src{cursor:help;border-bottom:1px dotted var(--muted)}
.qbp-more{margin-top:4px;font-weight:400}
.qbp-more>summary{cursor:pointer;color:var(--status-info);font-size:12px;width:max-content}
/* Schedule of values setup: four numbered steps, stacked (sovux) */
.sov-step{border:1px solid var(--border);border-radius:var(--r);padding:10px 12px;margin:10px 0;background:var(--surface)}
.sov-step.sov-grey{opacity:.65}
.sov-step-h{display:flex;align-items:baseline;gap:8px;margin-bottom:2px}
.sov-step-n{display:inline-block;min-width:22px;text-align:center;font-weight:700;font-size:12px;border-radius:50%;background:var(--surface-quiet);color:var(--ink-muted);padding:2px 0}
.sov-step-t{font-weight:700;font-size:13px}
.sov-step-i{font-size:12px;color:var(--muted);margin:2px 0 8px}
.sov-how{font-size:11px;color:var(--muted);margin:4px 0 8px}
.sov-how>summary{cursor:pointer}
.sov-choices{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:8px}
.sov-choice{border:1px solid var(--border);border-radius:var(--r);padding:8px}
.sov-choice .btn{width:100%;white-space:normal}
.sov-choice-d{font-size:11px;color:var(--muted);margin-top:4px}
.sov-btns{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:6px}
.sov-note-err{color:var(--red);font-size:11px}
@media (max-width:640px){.sov-step .btn{width:100%}.sov-btns{flex-direction:column;align-items:stretch}}

/* BILLING WORKSHEET type scale and summary (wssum, 20 Oct 2026, Gord on PO# 2676: "make the fonts consistent"). The same scale as the Admin Settings panel: title 14, subhead 13, section / help / table head 11,
   body / table cell / field 12, plus the one big figure (the period total) at 20. Group headers are subhead, column headers table-head, sub-headers section, cells table-cell, numbers right-aligned and tabular. */
#fc-main-content{--ds-fs-title:14px;--ds-fs-sub:13px;--ds-fs-sec:11px;--ds-fs-label:11px;--ds-fs-body:12px;--ds-fs-help:11px;--ds-fs-th:11px;--ds-fs-td:12px;--ds-fs-field:12px;--ds-fs-figure:20px}
#fc-main-content .ds-sub{font-size:var(--ds-fs-sub)}
#fc-main-content .ds-body{font-size:var(--ds-fs-body)}
#fc-main-content .ds-help{font-size:var(--ds-fs-help)}
#fc-main-content .ds-sec{font-family:var(--font-sans);font-size:var(--ds-fs-sec);font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--muted)}
/* The summary is a plain .card (its padding is the card's); its title is the card title size, its sub-headings the section eyebrow. Tokens only (design handoff 1: use the variable, never the hex). */
.ws-sum{font-size:var(--ds-fs-body,12px)}
.ws-sum-title{font-family:var(--font-sans);font-size:var(--ds-fs-title,14px);font-weight:700;color:var(--brn);margin-bottom:var(--space-2)}
.ws-sum-sub{font-family:var(--font-sans);font-size:var(--ds-fs-sec,11px);font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--ink-muted);margin:var(--space-3) 0 var(--space-1);padding-top:var(--space-2);border-top:1px solid var(--rule)}
/* Quiet header for dense in-card tables: no Brunswick band (ui-audit A8) */
.tbl-quiet th{background:transparent;color:var(--ink-muted);font-size:var(--ds-fs-th,11px);font-weight:700;letter-spacing:.4px;text-transform:uppercase;text-align:left;padding:var(--space-1) var(--space-2);border-bottom:1px solid var(--rule)}
.tbl-quiet td{padding:var(--space-1) var(--space-2)}
.ws-sum-tbl{width:100%;border-collapse:collapse;font-size:var(--ds-fs-td,12px)}
.ws-sum-tbl th{font-size:var(--ds-fs-th,11px);font-weight:700;text-transform:uppercase;letter-spacing:.5px;text-align:left;padding:var(--space-1) var(--space-2);background:transparent;color:var(--ink-muted);border-bottom:1px solid var(--rule-strong)}
.ws-sum-tbl th.num{text-align:right}
.ws-sum-tbl td{padding:var(--space-1) var(--space-2);border-bottom:1px solid var(--rule)}
.ws-sum-tbl td.num{text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
.ws-sum-tbl .ws-sum-co td{background:var(--status-info-tint)}   /* Gord, 22 Oct 2026: keep the blue for the change-order row */
.ws-sum-tbl .ws-sum-total td{font-weight:700;border-top:2px solid var(--rule-strong);border-bottom:0}
.ws-sum-unk{color:var(--ink-muted);font-weight:400}
/* A line in the summary. A check is the shared note (.alert with a-s good / a-w caution, above); it only needs block flow and its own spacing. ws-sum-quiet is the neutral tone of that note (no state), ws-sum-plain a help line with no box. */
.ws-sum-line{display:block;margin:var(--space-2) 0 0;font-size:var(--ds-fs-body,12px)}
.ws-sum-line.alert.ws-sum-quiet{background:var(--surface-quiet);border-color:var(--rule);color:var(--ink-muted)}
.ws-sum-plain{color:var(--ink-muted)}
.ws-sum-hrs{display:flex;justify-content:space-between;gap:var(--space-3);padding:var(--space-1) var(--space-2);font-size:var(--ds-fs-body,12px)}
.ws-sum-hrs .num{text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
/* The summary's table scrolls inside its own box (never the page); header cells wrap, numbers stay on one line (wssum2, 20 Oct 2026) */
.ws-sum-scroll{overflow-x:auto;max-width:100%}
.ws-sum-scroll .ws-sum-tbl{min-width:1040px}
.ws-sum-tbl th.num{white-space:normal;vertical-align:bottom;line-height:1.25}
.ws-sum-tbl td.num{white-space:nowrap}
.ws-sum-checks{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-3);margin-top:var(--space-2)}
.ws-sum-checks .ws-sum-line{flex:1 1 320px;margin:0}
.ws-sum-hrs-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:0 var(--space-4)}
/* The billing steps as one slim row (wssum2): number, name and chip on one line, the one-line text in the help size, a small button */
.flow-steps-c{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:4px 10px;font-size:var(--ds-fs-body,12px);margin-top:4px}
.flow-step-c{padding:1px var(--space-2);border-left:3px solid var(--brn)}
.flow-step-ok{border-left-color:var(--status-good-mark)}
.flow-step-warn{border-left-color:var(--status-caution-mark)}
.flow-step-unknown{border-left-color:var(--status-unknown-mark)}
.flow-head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-2);flex-wrap:wrap;margin-top:6px}
.flow-head .ct{margin:0}
.flow-link{background:none;border:0;padding:0;font-size:var(--ds-fs-help,11px);color:var(--ink-muted);text-decoration:underline;cursor:pointer}
.flow-step-head{display:flex;align-items:baseline;flex-wrap:wrap;gap:2px 6px;font-weight:700}
.flow-step-text{font-size:var(--ds-fs-help,11px);color:var(--muted);line-height:1.3}
.flow-step-wait{font-style:italic}
.flow-step-btn{margin-top:3px}
.flow-step-btn .btn{font-size:var(--ds-fs-btn,11px);padding:1px 6px}
.ws-notes{margin:0 0 10px;font-size:var(--ds-fs-body,12px)}
.ws-notes>summary{cursor:pointer;color:var(--ink-muted);font-size:var(--ds-fs-help,11px)}
/* A note above the worksheet is the shared .alert (a-w action, a-d problem); .ws-alert only sets the worksheet's rhythm and stacks its text */
.ws-alert{display:block;margin-bottom:var(--space-3)}
.ws-alert ul{margin:var(--space-1) 0 0;padding-left:var(--space-4)}
.ws-help{font-size:var(--ds-fs-help,11px);color:var(--ink-muted)}
.ws-notes .ws-note{margin-top:6px}

/* The Schedule viewer's colours (schedule viewer step 3, 20 Oct 2026). "Colour by" Trade / Party / Area takes these in order; eight hues that stay apart for a colour-blind viewer
   (blue, orange, bluish green, reddish purple, yellow, dark green, sky blue, purple: the Okabe-Ito set, with the brand's own blue, caution orange and Brunswick where they fit),
   then the same hues paler. Done is the muted grey, never one of these. The legend names every colour, so colour is never the only label. */
:root{
  --sched-c1:var(--blue);  --sched-c2:var(--status-caution-mark);  --sched-c3:#009E73;  --sched-c4:#CC79A7;
  --sched-c5:#E6C700;      --sched-c6:var(--brunswick);            --sched-c7:#56B4E9;  --sched-c8:#6A3D9A;
}
mark.sched-hit{background:#FFE98A;color:inherit;padding:0 1px;border-radius:2px}
input.sched-q{width:150px}
table.sched-list th{color:var(--muted)}
/* invlinks (22 Oct 2026): the compact PDF / Email links beside a supplier invoice or bill row */
.inv-doc-links{display:inline-flex;gap:6px;align-items:baseline;white-space:nowrap}
.inv-doc-link{font-size:10px;font-weight:600;text-decoration:underline;background:none;border:0;padding:0;cursor:pointer;color:var(--status-info,#1d5fa8);font-family:inherit}
.inv-doc-none{color:var(--muted);text-decoration:none;font-weight:400;cursor:default}

/* ONE way to size a tick box, a radio button and a select in a table cell (23 Oct 2026, Gord on Users & Permissions: "the cells don't line up on this page ...
   can we improve the design so we don't have to keep smushing this layout bug?"). The page-wide input rule above (width:100%, padding) is for text fields; it
   stretched every tick box across its cell, and it was patched page by page (#panel-data, #panel-hr, #sched-people-body, #sched-map-body, #sched-school-body,
   #pj-needby, .lc-toggle, and inline style="width:auto" on about twenty boxes in app.js, now redundant). This is the one rule; the scoped rules keep only their margins.
   It sits at the END of the file so it outranks the equal-weight scoped input rules above it (.hr-grid input). A select in a table cell is as wide as its
   options and never wider than the cell; a text field in a cell still fills its column. tools/verify.mjs fails on a new per-page tick-box width patch.
   The table rule itself (one <table> for heading and rows, through dtTable) is in docs/HANDOFF-design-system-2026-09-17.md section 11. */
input[type=checkbox],input[type=radio]{width:auto;flex:none;padding:0;min-height:0}
td select,th select{width:auto;max-width:100%}
.dt-mid td{vertical-align:middle}   /* a table of controls (a select, a tick box, a button in a row): every cell centred on the row */
/* invplain (4 Nov 2026): the Invoices row's one-sentence "Why linked" line, its Details, and the caution lines under it (classes, not inline styles: 200 rows a page). */
.inv-why{font-size:11px;color:var(--muted);margin-top:3px;line-height:1.35}
.inv-why details{margin-top:2px}
.inv-why summary{cursor:pointer;font-size:10px}
.inv-why details div{white-space:pre-line;font-size:10px}
.inv-warn{font-size:11px;margin-top:3px;padding:2px 6px;border-left:3px solid var(--status-caution-mark);background:var(--status-caution-tint)}
