/* Employment Equity Tool — Hello Kindred / Kin OS theme.
   Self-contained: no imports, no remote fonts, no CDN. CIH holds its own copy
   of the house tokens rather than importing, because CIH and Command Centre
   are separate applications and neither should be able to restyle the other
   by accident.

   Two rules govern everything below and are worth stating once:

   1. FRESH LIME (#cbff02) IS A FILL, NEVER INK. Lime text on paper is ~1.2:1.
      `--accent` is only ever a background; `--accent-ink` is what you colour
      text with. Anything sitting on lime takes `--on-accent` (#09262d).
   2. THE CHROME IS BASALT IN BOTH THEMES. The rail and topbar do not invert
      with the theme toggle, so they carry their own `--rail-*` token set that
      the dark-mode blocks deliberately leave alone. Only the content column
      flips. */

:root{
  /* Paper ramp. --bg is Paper 2, the page ground. */
  --bg:#eff1f4; --surface:#fff; --surface-2:#f7f8fa; --surface-3:#e5e8ec;

  /* Ink. #9ea8ab is the measured "Basalt 40% subdued" but only clears contrast
     ON DARK, so it lives in --ink-subdued and is used by the rail and topbar.
     On paper the same role is carried by --text-faint at an accessible value:
     #5f6d70 is 5.4:1 on Paper 0 and 4.8:1 on the Paper 2 page ground, where the
     eyebrows actually sit. #9ea8ab there would be 2.3:1 and fail outright. */
  --text:#16202c; --text-muted:#4c5766; --text-faint:#5f6d70;
  --heading-ink:#09262d;
  --ink-subdued:#9ea8ab;
  --border:#e5e8ec; --border-strong:rgba(9,38,45,.18);
  /* The measured Kin OS line (rgba(9,38,45,.18)) is 1.44:1 on white, which is
     fine for a decorative frame and illegal for the edge of a UI control
     (WCAG 1.4.11 wants 3:1). --border-strong keeps the measured value for
     frames; --control-line carries the accessible one and is what every
     input, quiet button, suggestion and state-bearing outline uses.
     .55 over #fff = #78888c = 3.70:1, and 3.26:1 on the Paper 2 page ground. */
  --control-line:rgba(9,38,45,.55);

  /* Always-Basalt chrome. Not overridden by either dark block. */
  --sidebar-bg:#09262d; --topbar-bg:#09262d;
  --rail-ink:rgba(255,255,255,.82); --rail-ink-strong:#fff;
  --rail-ink-muted:#9ea8ab;
  --rail-hover:rgba(255,255,255,.06); --rail-active:rgba(255,255,255,.08);
  --rail-line:rgba(255,255,255,.10);

  /* Accent. Fill vs ink, kept apart on purpose — see rule 1 above. */
  --accent:#cbff02; --accent-hover:#b8e800;
  --accent-ink:#09262d; --on-accent:#09262d;
  --accent-soft:rgba(9,38,45,.06); --accent-ring:rgba(9,38,45,.14);

  /* Focus. Two-tone: a Basalt line does the contrast work, the lime halo does
     the brand. Inside the Basalt chrome the two swap (see .sidebar/.topbar). */
  --focus-ring:#09262d; --focus-halo:rgba(203,255,2,.55);

  /* Status triplets: ink / background / line. */
  --success:#17603c; --success-soft:#e6f4ec; --success-line:#b6ddc7;
  --warning:#6a4a12; --warning-soft:#fbf2e0; --warning-line:#e8d7ad;
  --danger:#8f2d2d;  --danger-soft:#fbecec;  --danger-line:#eecccc;
  --info:#1c4f7c;    --info-soft:#e8f1f9;    --info-line:#b9d4ea;
  --draft:#4c5766;   --draft-soft:#eef1f4;   --draft-line:#d3dae2;

  --shadow-sm:0 1px 2px rgba(24,32,46,.05);
  --shadow:0 1px 2px rgba(24,32,46,.04),0 4px 16px rgba(24,32,46,.06);
  --shadow-lift:0 2px 4px rgba(24,32,46,.05),0 10px 28px rgba(24,32,46,.10);
  --shadow-lg:0 4px 10px rgba(24,32,46,.08),0 24px 60px rgba(24,32,46,.16);

  /* Names kept from the previous theme (~70 usages) and repointed to the Kin OS
     values, rather than renamed to --radius-* for no visual gain. */
  --r-sm:4px; --r:7px; --r-lg:12px; --r-xl:18px; --r-pill:999px;

  --sidebar-w:234px; --topbar-h:54px;
  /* RESERVED. --rail-secondary-width, --text-hero and --ease-doors are carried
     from the Kin OS token set for parity and are deliberately unused here: CIH
     has no secondary rail, no hero page head and no door transition. Nothing
     reads those three — editing one will not change anything on screen.
     --rail-width-collapsed is NOT reserved: the collapsed-rail block reads it
     and it is the sole definition of the collapsed rail's width. */
  --rail-secondary-width:212px;
  --rail-width-collapsed:64px; /* read by the collapsed-rail block */
  --content-max:1440px;

  /* .72rem is the measured Kin OS xs, but it resolves to 11.52px and this token
     carries every chip, eyebrow, field label and table header in the product —
     below the ~12px legibility floor. Raised the half-pixel to .75rem (12px);
     every other value is the measured one. */
  --text-xs:.75rem; --text-sm:.8125rem; --text-base:.9375rem; --text-md:1.0625rem;
  --text-lg:1.35rem; --text-xl:1.8rem;
  --text-hero:clamp(2.1rem,1.3rem + 2.2vw,3.1rem);
  --leading-tight:1.22; --leading-normal:1.55;

  --space-1:.25rem; --space-2:.5rem; --space-3:.75rem; --space-4:1rem;
  --space-5:1.5rem; --space-6:2rem; --space-7:3rem; --space-8:4rem;

  --ease:cubic-bezier(.4,0,.2,1);
  --ease-rail:cubic-bezier(.32,.72,0,1);
  --ease-doors:cubic-bezier(.72,0,.16,1);
  --fast:140ms; --normal:200ms;
  --mono:Consolas,"Cascadia Mono","SF Mono",Menlo,monospace;
}

/* Dark mode. The two blocks below are byte-identical bodies behind different
   wrappers — the media query covers "system dark, no explicit choice", the
   attribute selector covers the toggle. EDIT THEM IN LOCKSTEP.
   Note what is absent: --sidebar-bg, --topbar-bg, --rail-*, --accent and
   --on-accent. The chrome is already Basalt and a lime button already carries
   Basalt ink, so re-inverting them here would undo the design. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]){
    --bg:#04161b; --surface:#0c2b33; --surface-2:#123640; --surface-3:#18414c;
    --text:#e7edee; --text-muted:#9ea8ab; --text-faint:#929ea1;
    --heading-ink:#eef3f4;
    --border:rgba(255,255,255,.10); --border-strong:rgba(255,255,255,.22);
    --control-line:rgba(255,255,255,.42);
    --accent-ink:#dbe5e7;
    --accent-soft:rgba(255,255,255,.07); --accent-ring:rgba(203,255,2,.30);
    --focus-ring:#cbff02; --focus-halo:rgba(9,38,45,.65);
    --success:#7fe0aa; --success-soft:rgba(127,224,170,.14); --success-line:rgba(127,224,170,.34);
    --warning:#f0c86a; --warning-soft:rgba(240,200,106,.14); --warning-line:rgba(240,200,106,.34);
    --danger:#ff9a9a;  --danger-soft:rgba(255,154,154,.14);  --danger-line:rgba(255,154,154,.32);
    --info:#8ec6ee;    --info-soft:rgba(142,198,238,.14);    --info-line:rgba(142,198,238,.32);
    --draft:#a8b2b5;   --draft-soft:rgba(158,168,171,.12);   --draft-line:rgba(158,168,171,.26);
    --shadow-sm:0 1px 2px rgba(0,0,0,.35);
    --shadow:0 1px 2px rgba(0,0,0,.30),0 4px 16px rgba(0,0,0,.40);
    --shadow-lift:0 2px 4px rgba(0,0,0,.30),0 10px 28px rgba(0,0,0,.48);
    --shadow-lg:0 4px 10px rgba(0,0,0,.35),0 24px 60px rgba(0,0,0,.55);
  }
}
[data-theme=dark]{
  --bg:#04161b; --surface:#0c2b33; --surface-2:#123640; --surface-3:#18414c;
  --text:#e7edee; --text-muted:#9ea8ab; --text-faint:#929ea1;
  --heading-ink:#eef3f4;
  --border:rgba(255,255,255,.10); --border-strong:rgba(255,255,255,.22);
  --control-line:rgba(255,255,255,.42);
  --accent-ink:#dbe5e7;
  --accent-soft:rgba(255,255,255,.07); --accent-ring:rgba(203,255,2,.30);
  --focus-ring:#cbff02; --focus-halo:rgba(9,38,45,.65);
  --success:#7fe0aa; --success-soft:rgba(127,224,170,.14); --success-line:rgba(127,224,170,.34);
  --warning:#f0c86a; --warning-soft:rgba(240,200,106,.14); --warning-line:rgba(240,200,106,.34);
  --danger:#ff9a9a;  --danger-soft:rgba(255,154,154,.14);  --danger-line:rgba(255,154,154,.32);
  --info:#8ec6ee;    --info-soft:rgba(142,198,238,.14);    --info-line:rgba(142,198,238,.32);
  --draft:#a8b2b5;   --draft-soft:rgba(158,168,171,.12);   --draft-line:rgba(158,168,171,.26);
  --shadow-sm:0 1px 2px rgba(0,0,0,.35);
  --shadow:0 1px 2px rgba(0,0,0,.30),0 4px 16px rgba(0,0,0,.40);
  --shadow-lift:0 2px 4px rgba(0,0,0,.30),0 10px 28px rgba(0,0,0,.48);
  --shadow-lg:0 4px 10px rgba(0,0,0,.35),0 24px 60px rgba(0,0,0,.55);
}

/* ── base ──────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);
  font-family:Gilroy,Aptos,"Segoe UI",Arial,sans-serif;
  font-size:var(--text-base);line-height:var(--leading-normal);
  -webkit-font-smoothing:antialiased}
h1,h2,h3{margin:0;font-weight:600;line-height:var(--leading-tight);color:var(--heading-ink)}
h1{font-size:var(--text-xl)} h2{font-size:var(--text-md)} h3{font-size:var(--text-base)}
p{margin:0}
a{color:var(--accent-ink);text-decoration:none}
a:hover{text-decoration:underline}
/* Inline content links carry an underline of their own: with lime off the table
   as ink, --accent-ink is the heading ink and 1.04:1 against body copy, so
   colour cannot distinguish a link from the text around it. Every container
   that holds an in-flow link is listed here. Chrome links (.nav-item,
   .ghost-link, .btn, .card-title-link) are deliberately not: they are
   positioned and styled as controls, not as words in a sentence. */
.prose a,.muted a,.tl-detail a,.kv a,.client-summary a,
.crumbs a,.scope-note a,.data-table td a,.tl-link,
.signal-foot a,.stack-row a{text-decoration:underline;
  text-underline-offset:.15em}
code{font-family:var(--mono);font-size:.88em;background:var(--surface-2);
  padding:1px 5px;border-radius:var(--r-sm)}

/* ── a11y utilities ────────────────────────────────────── */
/* No border-radius here: overriding the element's own radius on focus squares
   off pill buttons mid-interaction. The outline already follows the shape. */
:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px;
  box-shadow:0 0 0 5px var(--focus-halo)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.skip-link{position:absolute;left:-999px;top:8px;z-index:100;background:var(--accent);
  color:var(--on-accent);font-weight:600;padding:10px 16px;border-radius:var(--r-pill)}
.skip-link:focus{left:8px}

/* ── shell: the Basalt rail ────────────────────────────── */
.shell{display:flex;min-height:100vh}
.sidebar{width:var(--sidebar-w);flex:0 0 var(--sidebar-w);background:var(--sidebar-bg);
  color:var(--rail-ink);display:flex;flex-direction:column;
  position:sticky;top:0;height:100vh;overflow-y:auto;
  padding:var(--space-5) var(--space-3);
  /* Inside the Basalt chrome the focus ring inverts: lime line, Basalt halo. */
  --focus-ring:#cbff02; --focus-halo:rgba(9,38,45,.7)}

/* Dismiss surface for the off-canvas rail. Only exists below 900px (see the
   media query): the open rail is 234px of fixed panel at z-index 40 sitting
   directly over #menu-btn, so tapping the toggle again cannot close it.
   Redundant with the Escape key, which is what keyboard users still use. */
.nav-scrim{display:none;position:fixed;inset:0;z-index:35;
  background:rgba(4,22,27,.55);opacity:0;pointer-events:none;
  transition:opacity var(--normal) var(--ease)}
.nav-scrim.open{opacity:1;pointer-events:auto}

.brand{display:flex;gap:var(--space-3);align-items:center;
  padding:0 var(--space-2) var(--space-5);color:var(--rail-ink-strong);
  text-decoration:none;transition:opacity var(--fast) var(--ease)}
.brand:hover,.brand:focus-visible{opacity:.85}
/* Shown only by the collapsed rail. Lime ground, Basalt ink — the same
   icon-tile idiom as .avatar, and legal because lime is the fill and the two
   letters on it are Basalt (13.45:1). 48px of usable width in a collapsed rail
   cannot hold the wordmark, and an unsigned rail is worse than a mark. */
.brand-mark{display:none;width:32px;height:32px;flex:0 0 32px;
  border-radius:var(--r);background:var(--accent);color:var(--on-accent);
  place-items:center;font-size:var(--text-xs);font-weight:700;
  letter-spacing:-.02em}
/* The house wordmark: the real brand vector artwork inlined, sized in em so it tracks
   .wordmark's font-size the same way the old two-span text version did. Single fill,
   inherited -- both halves are the same colour; the weight contrast between hello and
   kindred is baked into the letterform widths in the source file, not applied by CSS. */
.wordmark{display:block;font-size:var(--text-md);line-height:1.1;
  color:currentColor;white-space:nowrap}
.wordmark-svg{display:inline-block;height:.85em;width:auto;vertical-align:middle}
.wm-glyphs{fill:currentColor}
.brand-text span{display:block;font-size:var(--text-xs);letter-spacing:.02em;
  margin-top:2px;color:var(--rail-ink-muted)}

/* The rail is a fixed-height column: brand at the top, status and utilities
   pinned at the bottom, nav taking whatever is left.
   flex:1 (and nothing else) is the BASE, at every width. Below 901px the rail
   is the off-canvas panel and the ONLY navigation there is, so .nav must not
   be an inner scroller: at 375x667 a 240px pinned block below a short nav
   scroller cut Reports and Ask CIH off with no scrollbar and no affordance.
   Below 901px .sidebar's own overflow-y:auto scrolls the panel as one piece,
   which is one natural swipe to everything. */
.nav{display:flex;flex-direction:column;gap:var(--space-1);flex:1}

/* Desktop only: here the footer really is pinned, and with the four utility
   rows the rail is ~815px tall, so on a 720px laptop a rail that scrolled as
   one piece put Sign out and Collapse menu below the fold — the opposite of
   pinned. So on the desktop rail (and only there) the NAV is the scroller.
   min-height:0 is load-bearing: a flex child defaults to min-height:auto and
   refuses to shrink below its content, which hands the overflow straight back
   to .sidebar. .sidebar keeps its own overflow-y:auto as the last resort for
   viewports too short even for brand + status + footer.
   The padding/negative-margin pair is NOT cosmetic. overflow-y:auto forces
   computed overflow-x to auto, which clips ink overflow at the padding box —
   and .nav-item stretches to exactly .nav's box, so the 2px focus outline at
   2px offset plus the 5px halo (max 5px of ink outside the border box) were
   clipped away on all six nav items, leaving two bare horizontal lime lines.
   6px/8px of padding gives the ring room inside the clip box; the matching
   negative margin puts the items back on the same x as the footer rows below
   the hairline. overflow-x:clip does NOT help — it still clips ink overflow.
   The scrollbar is styled for the dark chrome: the OS default is a light
   stripe down the Basalt rail, and 15px of it also shortened every nav row
   against the footer rows. 6px thin, and see the collapsed block for the
   gutter that keeps the 64px icon column centred. */
@media (min-width:900.021px){
  .nav{min-height:0;overflow-y:auto;
    padding:6px var(--space-2);margin:-6px calc(var(--space-2) * -1);
    scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.24) transparent}
  .nav::-webkit-scrollbar{width:6px}
  .nav::-webkit-scrollbar-track{background:transparent}
  .nav::-webkit-scrollbar-thumb{background:rgba(255,255,255,.24);
    border-radius:var(--r-pill)}
  .nav::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.38)}
}
.nav-group{display:flex;flex-direction:column}
.nav-group + .nav-group{margin-top:var(--space-4)}
/* The items are a real list, so a reader can be told "3 items" and skip the
   group. The gap lives here rather than on .nav-group, which now holds the
   label plus the list. */
.nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.nav-group-label{margin:0 0 var(--space-2);padding:0 var(--space-3);
  font-size:var(--text-xs);font-weight:600;letter-spacing:.18em;
  text-transform:uppercase;color:var(--rail-ink-muted)}
/* 44px, not the 38px the rail was drawn at: below 900px this is the off-canvas
   nav and every item is a touch target. The rail has the room. */
.nav-item{display:flex;align-items:center;gap:var(--space-3);
  min-height:44px;padding:var(--space-2) var(--space-3);border-radius:var(--r);
  color:var(--rail-ink);font-size:var(--text-sm);font-weight:500;
  transition:background var(--fast) var(--ease),color var(--fast) var(--ease)}
.nav-item svg{width:20px;height:20px;flex:0 0 20px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.nav-item:hover{background:var(--rail-hover);color:var(--rail-ink-strong);
  text-decoration:none}
/* The signature: a 3px lime bar hard against the item's own left edge, so it
   follows the item's radius rather than floating at the rail edge. */
.nav-item.active{background:var(--rail-active);color:var(--rail-ink-strong);
  font-weight:600;box-shadow:inset 3px 0 0 var(--accent)}

/* ── rail: status block, then the utility footer ───────── */
/* Two blocks, deliberately. .rail-status is CIH's standing statement about the
   data and the seam it links to; .sidebar-foot is the Kin OS utility set,
   pinned to the bottom under a hairline. The hairline belongs to the
   utilities, so it lives on .sidebar-foot and not between the two. */
.rail-status{display:flex;flex-direction:column;gap:var(--space-2);
  align-items:flex-start;padding:var(--space-4) var(--space-2) var(--space-3)}
.sidebar-foot{display:flex;flex-direction:column;gap:2px;align-items:stretch;
  padding:var(--space-3) 0 0;border-top:1px solid var(--rail-line)}

/* One row shape for every rail utility, link or button, matching .nav-item's
   44px height, radius and hover so the footer reads as the same family as the
   nav above it. 44px and not 40px for exactly the reason .nav-item is 44px:
   below 900px these rows are ordinary links in the off-canvas nav on a phone
   and every one of them is a touch target. It also keeps the vertical rhythm
   unbroken across the hairline. The icon slot is 24px rather than 20px because
   the profile row carries an avatar disc, and a 20px slot would stagger every
   label beside a 24px one. */
.rail-foot-item{display:flex;align-items:center;gap:var(--space-2);width:100%;
  min-height:44px;padding:var(--space-2) var(--space-3);border-radius:var(--r);
  background:none;border:0;cursor:pointer;text-align:left;
  font:inherit;font-size:var(--text-sm);font-weight:500;color:var(--rail-ink);
  transition:background var(--fast) var(--ease),color var(--fast) var(--ease)}
.rail-foot-item:hover{background:var(--rail-hover);color:var(--rail-ink-strong);
  text-decoration:none}
.rail-foot-ico{flex:0 0 24px;display:grid;place-items:center}
.rail-foot-ico svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.rail-foot-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* The collapse control reads a step quieter than its three siblings, as it
   does in Kin OS. "Quieter" is --rail-ink-muted, 6.5:1 on Basalt — emphasis,
   not an accessibility discount — and hover/focus takes it to full strength. */
.rail-foot-quiet{color:var(--rail-ink-muted)}
.rail-foot-quiet:hover,.rail-foot-quiet:focus-visible{color:var(--rail-ink-strong)}

/* "You are here" for the rail-footer pages. /integrations, /profile and
   /suggestions are real pages that are deliberately not NAV items, so nothing
   in the rail marked them and both a reader and a sighted user lost the
   current-page cue that every other page in the product gets. Same treatment
   as .nav-item.active — ground, weight and the 3px lime bar hard against the
   item's own left edge — driven off aria-current so the marker and the
   announcement can never disagree. In the collapsed rail the bar is the only
   thing left marking the page, exactly as it is for the nav above. */
.rail-foot-item[aria-current]{background:var(--rail-active);
  color:var(--rail-ink-strong);font-weight:600;
  box-shadow:inset 3px 0 0 var(--accent)}
/* box-shadow does not merge across rules, so the rule above would otherwise
   cancel the :focus-visible halo. Same re-add as .nav-item.active. */
.rail-foot-item[aria-current]:focus-visible{
  box-shadow:inset 3px 0 0 var(--accent),0 0 0 5px var(--focus-halo)}

/* The amber "note" triplet, written as literals rather than the --warning-*
   tokens on purpose: those tokens invert in dark mode and the rail does not,
   so the badge would change character under a toggle that never touches the
   surface it sits on. Same reason .rail-status .pill-ok carries its own
   values below. #6a4a12 on #fbf2e0 is 7.26:1. */
.badge-note{flex:0 0 auto;font-size:var(--text-xs);font-weight:700;
  letter-spacing:.08em;padding:1px 7px;border-radius:var(--r-pill);
  background:#fbf2e0;color:#6a4a12;border:1px solid #e8d7ad}

/* inline-block + padding-block so a bare inline anchor is a ~32px target
   rather than a 20px one. Nothing shifts: every container it sits in is a
   flex row with align-items:center or flex-start. */
.ghost-link{font-size:var(--text-sm);color:var(--text-muted);font-weight:500;
  display:inline-block;padding-block:6px}
.ghost-link:hover{color:var(--text)}
/* The two `.sidebar .ghost-link` / `.topbar .ghost-link` rules that used to sit
   here dressed the rail's Integration status link and the topbar's Sign out.
   Both are .rail-foot-item rows now and no .ghost-link survives anywhere in
   the chrome, so the rules matched nothing and were removed. */

/* The posture pill sits on Basalt, so it cannot use the paper status grounds. */
.rail-status .pill{border:1px solid transparent}
.rail-status .pill-ok{background:rgba(127,224,170,.16);color:#a8e8c6;
  border-color:rgba(127,224,170,.30)}
.rail-status .pill-warn{background:rgba(240,200,106,.16);color:#f2d98f;
  border-color:rgba(240,200,106,.32)}

/* ── shell: the Basalt topbar ──────────────────────────── */
.main-col{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{height:var(--topbar-h);flex:0 0 var(--topbar-h);display:flex;
  padding:0 var(--space-5);background:var(--topbar-bg);
  color:var(--rail-ink);position:sticky;top:0;z-index:20;
  --focus-ring:#cbff02; --focus-halo:rgba(9,38,45,.7)}
/* The Basalt band stays full-bleed; its contents take the same measure and
   gutters as .content. Without this the trail and the avatar drift away from
   the column they belong to once .main-col grows past --content-max. */
.topbar-inner{display:flex;align-items:center;gap:var(--space-3);min-width:0;
  width:100%;max-width:var(--content-max);margin-inline:auto}

/* Mostly a visual location echo: the authoritative breadcrumb is nav.crumbs
   in the content, the rail carries aria-current, and the H1 names the page.
   The leading "Home" segment is a real link though, so the container itself
   can't be aria-hidden -- only the separators are, individually. */
.topbar-crumbs{display:flex;align-items:center;gap:var(--space-2);
  min-width:0;flex:1;overflow:hidden;font-size:var(--text-sm)}
.topbar-crumb{color:rgba(255,255,255,.62);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
a.topbar-crumb:hover,a.topbar-crumb:focus-visible{color:var(--rail-ink-strong)}
.topbar-crumb.is-leaf{color:var(--rail-ink-strong);font-weight:500}
/* .34 was 3.03:1 on Basalt. aria-hidden takes it out of the reading order,
   not off the screen. */
.topbar-crumb-sep{color:rgba(255,255,255,.50);flex:0 0 auto}

/* Must be allowed to shrink: pinned at max-content this cluster is ~261px and
   pushes the whole document sideways at 320px. The 620px block below sheds the
   two decorative members rather than squashing the real controls. */
/* margin-left:auto, not just the crumbs' flex:1. Above 620px the crumbs push
   this cluster to the right edge; at 620px and below they are display:none and
   the spacer goes with them, so without this the bar collapses to two controls
   huddled in the left corner with the whole width empty beside them. With the
   avatar and Sign out moved to the rail there is less here to hide it. */
.topbar-right{display:flex;align-items:center;gap:var(--space-3);
  flex:0 1 auto;min-width:0;margin-left:auto}
.env-tag{font-size:var(--text-xs);font-weight:700;letter-spacing:.14em;padding:3px 9px;
  border-radius:var(--r-pill);background:rgba(255,255,255,.10);color:var(--rail-ink);
  border:1px solid rgba(255,255,255,.16)}
/* Icon tile: lime ground, Basalt glyph — the house pattern. */
.avatar{width:32px;height:32px;border-radius:50%;display:grid;place-items:center;
  background:var(--accent);color:var(--on-accent);font-size:var(--text-xs);
  font-weight:700;flex:0 0 32px}
/* Two sizes of the same tile: --sm sits in the rail footer's 24px icon slot
   beside the 20px strokes, --lg heads the profile card.
   --sm keeps --text-xs (12px), not the .625rem (10px) it was drawn at: this
   file raises the measured Kin OS xs to 12px precisely because below that is
   under the legibility floor (see the token block), and the initials are the
   only content on the disc. Two uppercase letters at 12px/700 fit 24px once
   the letter-spacing is negative rather than positive. */
.avatar-sm{width:24px;height:24px;flex:0 0 24px;font-size:var(--text-xs);
  letter-spacing:-.02em}
.avatar-lg{width:48px;height:48px;flex:0 0 48px;font-size:var(--text-md)}
/* 44px minimum: #menu-btn is the only control that opens navigation below
   900px, and 20px glyph + 7px padding made it the smallest target on the page.
   place-items:center keeps the glyph optically centred as the box grows. */
.icon-btn{display:grid;place-items:center;background:none;border:0;
  color:var(--rail-ink);cursor:pointer;padding:12px;border-radius:50%;
  min-width:44px;min-height:44px;
  transition:background var(--fast) var(--ease),color var(--fast) var(--ease)}
.icon-btn svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round}
.icon-btn:hover{background:var(--rail-hover);color:var(--rail-ink-strong)}
/* Only the mobile nav toggle is width-conditional. The theme toggle is an
   .icon-btn too and used to inherit display:none, which hid it above 900px. */
.menu-btn{display:none}

/* ── shell: the paper content column ───────────────────── */
.content{width:100%;max-width:var(--content-max);margin-inline:auto;
  padding:var(--space-6) var(--space-5) var(--space-8);
  display:flex;flex-direction:column;gap:var(--space-5);flex:1}
/* :not(:focus-visible), not a bare outline:none -- #main only gets focus via
   the skip link (tabindex=-1), and a bare rule here was beating the global
   :focus-visible ring at (0,2,0), leaving that one jump with no visible cue. */
.content:focus:not(:focus-visible){outline:none}

/* Page head: eyebrow, H1, lede. */
.page-head{display:flex;flex-direction:column;gap:var(--space-2);min-width:0}
.page-head-row{display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-3)}
.eyebrow{margin:0;font-size:var(--text-xs);font-weight:600;letter-spacing:.18em;
  text-transform:uppercase;color:var(--text-faint)}
.page-sub{font-size:var(--text-base);color:var(--text-muted);
  line-height:var(--leading-normal);max-width:68ch;margin:0}

/* ── panels ────────────────────────────────────────────── */
.panel{background:var(--surface);border-radius:var(--r-xl);
  padding:var(--space-5);box-shadow:var(--shadow);color:var(--text)}
.panel-head{display:flex;align-items:flex-start;justify-content:space-between;
  gap:var(--space-3);margin-bottom:var(--space-4)}
/* minmax(0,1fr), not 1fr: a bare 1fr track floors at the item's min-content,
   so one long label pushes the grid wider than its container instead of the
   item wrapping inside it. Same guard as .stat-row and .mini-metrics. */
.grid-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-5)}
.muted{color:var(--text-muted);font-size:var(--text-sm)}
.muted.block{display:block}
.prose{color:var(--text-muted);font-size:var(--text-base);
  line-height:var(--leading-normal);max-width:68ch}
.empty{color:var(--text-faint);font-size:var(--text-sm);font-style:italic;padding:8px 0}

/* ── stats ─────────────────────────────────────────────── */
.stat-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--space-4)}
.stat{background:var(--surface);border-radius:var(--r-xl);
  padding:var(--space-4) var(--space-5);display:flex;flex-direction:column;gap:var(--space-1);
  box-shadow:var(--shadow);
  transition:transform var(--normal) var(--ease),box-shadow var(--normal) var(--ease)}
.stat:hover{transform:translateY(-1px);box-shadow:var(--shadow-lift)}
.stat-label{font-size:var(--text-xs);color:var(--text-faint);text-transform:uppercase;
  letter-spacing:.18em;font-weight:600}
.stat-value{font-size:var(--text-xl);font-weight:600;line-height:var(--leading-tight);
  color:var(--heading-ink)}
.stat-note{font-size:var(--text-sm);color:var(--text-muted)}
.stat-note.tone-warn{color:var(--warning)} .stat-note.tone-risk{color:var(--danger)}

/* ── chips & pills ─────────────────────────────────────── */
/* Every chip is a status triplet: soft ground, status ink, matching line. */
.chip{display:inline-block;font-size:var(--text-xs);font-weight:600;
  padding:2px 9px;border-radius:var(--r-pill);white-space:nowrap;
  border:1px solid transparent}
.chip-strong{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.chip-steady{background:var(--info-soft);color:var(--info);border-color:var(--info-line)}
.chip-watch{background:var(--warning-soft);color:var(--warning);border-color:var(--warning-line)}
.chip-at-risk{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line)}
.chip-sev-high{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line)}
.chip-sev-medium{background:var(--warning-soft);color:var(--warning);border-color:var(--warning-line)}
.chip-sev-low{background:var(--info-soft);color:var(--info);border-color:var(--info-line)}
.chip-stage{background:var(--draft-soft);color:var(--draft);border-color:var(--draft-line)}
/* Was the one lime-ink chip. Lime cannot be ink, and a lime fill in a dense
   pipeline table would shout louder than the row it sits in — so: info. */
.chip-negotiate{background:var(--info-soft);color:var(--info);border-color:var(--info-line)}
.chip-won{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.chip-lost{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line)}
/* The six .chip-stance-* rules that used to sit here matched nothing:
   influence_pill() emits .chip-inf-*, which is defined in the contacts block.
   Removed so that editing the contact-influence colours has an effect. */
.pill{display:inline-block;font-size:var(--text-xs);font-weight:600;
  padding:4px 10px;border-radius:var(--r-pill);border:1px solid transparent}
.pill-ok{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.pill-warn{background:var(--warning-soft);color:var(--warning);border-color:var(--warning-line)}

/* ── lists ─────────────────────────────────────────────── */
.stack{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.stack-row{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);
  padding:var(--space-3) 0;border-bottom:1px solid var(--border)}
.stack-row:last-child{border-bottom:0}
.stack-row .muted{display:block}
.stack-row .right{display:flex;align-items:center;gap:var(--space-2)}
.num{font-variant-numeric:tabular-nums;font-weight:600;font-size:var(--text-sm)}
.mini-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.mini-list li{display:flex;align-items:center;gap:9px;font-size:var(--text-sm)}
.mini-list li span:nth-child(2){flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.doc-kind{font-size:var(--text-xs);font-weight:700;letter-spacing:.05em;padding:1px 6px;
  border-radius:var(--r-sm);background:var(--draft-soft);color:var(--draft);
  border:1px solid var(--draft-line)}

/* ── signals ───────────────────────────────────────────── */
.signal-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-3)}
.signal-top{display:flex;align-items:center;gap:9px;margin-bottom:5px;flex-wrap:wrap}
.signal-top .src{margin-left:auto;font-family:var(--mono);font-size:var(--text-xs)}
.signal-head{font-weight:600;font-size:var(--text-base);margin-bottom:3px}
.signal-detail{margin:6px 0 0;font-size:var(--text-sm);
  line-height:var(--leading-normal);color:var(--text-muted)}
/* min(280px,100%) is load-bearing: auto-fit reduces the track COUNT, never a
   track below its own floor, so a bare minmax(280px,1fr) overflows any
   container narrower than 280px (the panel is 240px wide at a 320px viewport)
   and scrolls the whole document sideways. */
.signal-grid{display:grid;gap:var(--space-4);
  grid-template-columns:repeat(auto-fit,minmax(min(280px,100%),1fr))}
/* Inline card: lighter than a .panel so nested cards do not read as peers.
   These are always emitted inside a .panel, which is --surface. */
.signal-card{background:var(--surface-2);border:1px solid var(--border);
  border-radius:var(--r-lg);padding:var(--space-4);box-shadow:var(--shadow-sm)}
.signal-card h3{font-size:var(--text-base);line-height:1.4;margin:var(--space-2) 0 0}
.signal-card p{font-size:var(--text-sm);color:var(--text-muted);line-height:var(--leading-normal)}
.signal-foot{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-3);align-items:baseline;
  margin:var(--space-3) 0 0;font-size:var(--text-xs);color:var(--text-faint)}
.signal-foot .src{font-family:var(--mono);font-size:var(--text-xs)}

/* ── charts ────────────────────────────────────────────── */
.chart{margin:0;display:flex;flex-direction:column;gap:9px}
.bar-row{display:grid;grid-template-columns:88px 1fr 56px;align-items:center;gap:11px}
.bar-label{font-size:var(--text-sm);color:var(--text-muted);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* The bar's LENGTH is the datum, and lime on the Paper 3 track is 1.05:1 —
   three empty grey capsules to anyone who cannot resolve a hue-only edge.
   Darkening the track would break the paper ramp, so both edges are drawn
   instead: a Basalt hairline inside the fill (13.4:1 on lime, 12.9:1 on the
   track) and an accessible line around the track's full extent. Dark mode
   already cleared 3:1 and is unharmed. */
.bar-track{height:9px;background:var(--surface-3);border-radius:var(--r-pill);
  overflow:hidden;box-shadow:inset 0 0 0 1px var(--control-line)}
/* Flat lime. A bar carries no text, so lime is a legal fill here; the previous
   two-stop gradient had no meaning once --accent-hover stopped being a hue. */
.bar-fill{display:block;height:100%;border-radius:var(--r-pill);background:var(--accent);
  box-shadow:inset 0 0 0 1px var(--on-accent);
  animation:grow var(--normal) var(--ease) both}
@keyframes grow{from{transform:scaleX(.02);transform-origin:left}to{transform:scaleX(1)}}
.bar-value{font-size:var(--text-sm);font-variant-numeric:tabular-nums;text-align:right;
  color:var(--text-muted)}
.chart-cap{font-size:var(--text-xs);color:var(--text-faint);margin-top:var(--space-1)}

/* ── tables ────────────────────────────────────────────── */
/* The negative margins let a wide table scroll edge-to-edge inside its card,
   so they are tied to .panel's padding. Change one, change both. */
.table-wrap{overflow-x:auto;
  margin:0 calc(var(--space-5) * -1) calc(var(--space-5) * -1);
  padding:0 var(--space-5) var(--space-5)}
.data-table{width:100%;border-collapse:collapse;font-size:var(--text-sm);min-width:660px}
.data-table th{text-align:left;font-size:var(--text-xs);text-transform:uppercase;
  letter-spacing:.18em;color:var(--text-faint);font-weight:600;padding:0 12px 9px 0;
  border-bottom:1px solid var(--border)}
.data-table td{padding:11px 12px 11px 0;border-bottom:1px solid var(--border);
  vertical-align:top}
.data-table tr:last-child td{border-bottom:0}
.data-table tbody tr{transition:background var(--fast) var(--ease)}
.data-table tbody tr:hover{background:var(--surface-2)}
.num-cell{text-align:right;font-variant-numeric:tabular-nums}
/* The per-cell column label used by the ≤620px card layout. Real DOM text, not
   a ::before: generated content is not reliably exposed to assistive tech, and
   below 620px this is the only thing naming a column once thead is
   display:none. Hidden above 620px, where the header row does the job. */
.cell-label{display:none}

/* ── clients ───────────────────────────────────────────── */
.client-list{display:flex;flex-direction:column;gap:var(--space-4)}
.client-head{display:flex;justify-content:space-between;gap:var(--space-4);align-items:flex-start}
.client-head-right{display:flex;align-items:center;gap:var(--space-3);flex-shrink:0}
.client-summary{font-size:var(--text-base);color:var(--text-muted);
  margin-top:var(--space-3);line-height:var(--leading-normal)}
.client-card[hidden]{display:none}

/* ── reports ───────────────────────────────────────────── */
.report-list{display:flex;flex-direction:column;gap:var(--space-4)}
.metric-row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--space-3);
  margin-top:var(--space-4)}
.metric{background:var(--surface-2);border-radius:var(--r-lg);
  padding:var(--space-3) var(--space-4);display:flex;flex-direction:column;gap:2px}
.metric-label{font-size:var(--text-xs);color:var(--text-faint);text-transform:uppercase;
  letter-spacing:.18em;font-weight:600}
.metric-value{font-size:var(--text-lg);font-weight:600;color:var(--heading-ink)}
.metric-delta{font-size:var(--text-xs);color:var(--text-muted);font-variant-numeric:tabular-nums}
.metric-delta.tone-ok{color:var(--success)}
.metric-delta.tone-risk{color:var(--danger)}

/* ── forms: inputs share one radius across the product ─── */
.field{display:flex;flex-direction:column;gap:5px;min-width:0}
.field-grow{flex:1 1 240px}
.field-label{font-size:var(--text-xs);font-weight:600;letter-spacing:.18em;
  text-transform:uppercase;color:var(--text-faint)}
.field input,.ask-form input,.auth-form input,
.toolbar input[type=search],.toolbar select{
  font:inherit;font-size:var(--text-base);color:var(--text);background:var(--surface);
  border:1px solid var(--control-line);border-radius:var(--r);
  padding:9px 13px;
  transition:border-color var(--fast) var(--ease),box-shadow var(--fast) var(--ease)}
.field input{min-width:290px}
.toolbar input[type=search],.toolbar select{padding:9px 10px;min-width:150px}
/* Colour-only cue for pointer focus. The outline is NOT suppressed: this used
   to set outline:none at (0,2,1), which beat the global :focus-visible rule and
   replaced the designed two-tone ring with a 1.3:1 halo on every input and
   select in the product. Declaring border-color alone lets the shared
   indicator through untouched. */
.field input:focus,.ask-form input:focus,.auth-form input:focus,
.toolbar input[type=search]:focus,.toolbar select:focus{border-color:var(--accent-ink)}
.toolbar input[type=search]:hover,.toolbar select:hover{border-color:var(--accent-ink)}
/* Declared, or the UA derives it: Chromium and Firefox use 54% of currentColor,
   which is 3.64:1 on white. opacity:1 is required or Firefox re-applies .54. */
::placeholder{color:var(--text-faint);opacity:1}

/* Filter bar. A real form, so it must lay out as one: fields wrap, the
   count stays on its own line at narrow widths rather than being squeezed.
   The Apply button is never hidden — it is the no-JavaScript fallback. */
.toolbar{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:flex-end;
  background:var(--surface);border-radius:var(--r-xl);
  padding:var(--space-4) var(--space-5);margin-bottom:var(--space-4);
  box-shadow:var(--shadow)}
.toolbar-actions{display:flex;gap:var(--space-2);align-items:center}
.toolbar-count{margin:0;flex-basis:100%;font-size:var(--text-sm);color:var(--text-muted)}

/* ── buttons ───────────────────────────────────────────── */
/* One definition each. The old file declared .btn and .btn-quiet twice and
   left .btn-primary entirely overridden; editing the dead copy looked like an
   edit that did nothing. Primary is the measured Kin OS pill. */
/* The border is Basalt, not lime. Kin OS measures the primary pill as a lime
   fill inside a lime border, but lime on paper is 1.18:1 against #fff and
   1.04:1 against the Paper 2 page ground — in light mode the control has no
   perceivable outline at all and reads as floating Basalt text. --on-accent is
   the ink the fill already carries, so the pill stays brand-correct and gains
   a 15.8:1 edge. Dark mode does not need it (the fill is 12.7:1 on --surface)
   but takes it harmlessly, which keeps one definition instead of two. */
.btn{font:inherit;font-size:var(--text-base);font-weight:600;cursor:pointer;
  padding:10px 15px;border-radius:var(--r-pill);
  border:1px solid var(--on-accent);background:var(--accent);color:var(--on-accent);
  box-shadow:var(--shadow-sm);text-decoration:none;display:inline-block;
  transition:background var(--fast) var(--ease),border-color var(--fast) var(--ease),
    box-shadow var(--fast) var(--ease)}
/* No lime hover hue exists in the system: deepen the lime and lift the shadow,
   never shift toward another colour. */
.btn:hover{background:var(--accent-hover);border-color:var(--on-accent);
  box-shadow:var(--shadow-lift);text-decoration:none}
.btn-primary{background:var(--accent);color:var(--on-accent);border-color:var(--on-accent)}
.btn-primary:hover{background:var(--accent-hover);border-color:var(--on-accent)}
.btn-quiet{background:transparent;color:var(--accent-ink);
  border:1px solid var(--control-line);box-shadow:none}
.btn-quiet:hover{background:var(--accent-soft);border-color:var(--accent-ink);
  box-shadow:none;text-decoration:none}
/* box-shadow does not merge across rules, so the halo from :focus-visible is
   cancelled by any component that owns a box-shadow of its own. These three
   re-add it, and must stay after the rules they extend. */
.btn:focus-visible{box-shadow:var(--shadow-sm),0 0 0 5px var(--focus-halo)}
.btn-quiet:focus-visible{box-shadow:0 0 0 5px var(--focus-halo)}
.nav-item.active:focus-visible{box-shadow:inset 3px 0 0 var(--accent),
  0 0 0 5px var(--focus-halo)}

/* ── ask ───────────────────────────────────────────────── */
/* min-width:0 on the input, or its intrinsic size (~190px at size=20) keeps the
   flex line from shrinking and pushes the Ask button off the panel at 320px. */
.ask-form{display:flex;flex-wrap:wrap;gap:var(--space-3);
  margin:var(--space-4) 0 var(--space-3)}
.ask-form input{flex:1;min-width:0;padding:11px 15px}
.suggestions{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:center}
/* Secondary button spec: transparent ground, Basalt line, Basalt ink. 40px
   minimum: these are the Ask page's primary affordance and were ~34px. */
.suggestion{background:transparent;border:1px solid var(--control-line);
  color:var(--accent-ink);font:inherit;font-size:var(--text-sm);font-weight:500;
  padding:9px 14px;min-height:40px;border-radius:var(--r-pill);cursor:pointer;
  transition:background var(--fast) var(--ease),border-color var(--fast) var(--ease)}
.suggestion:hover{background:var(--accent-soft);border-color:var(--accent-ink)}
.ask-answer{margin-top:var(--space-4)}
.ask-answer:empty{display:none}
.qa-list{display:flex;flex-direction:column;gap:var(--space-3)}
.qa{background:var(--surface-2);border-radius:var(--r-lg);padding:var(--space-4);
  animation:fade var(--normal) var(--ease) both}
@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.qa-q{font-weight:600;font-size:var(--text-base);margin-bottom:6px}
.qa-a{font-size:var(--text-base);color:var(--text-muted);line-height:var(--leading-normal)}
/* Sources sit under every answer, not behind a disclosure. A citation the
   reader has to go looking for is a citation nobody checks. */
.qa-sources{display:flex;flex-wrap:wrap;gap:6px;align-items:center;
  margin-top:var(--space-3);padding-top:var(--space-3);border-top:1px solid var(--border)}
.qa-sources-label{font-size:var(--text-xs);font-weight:600;letter-spacing:.18em;
  text-transform:uppercase;color:var(--text-faint);margin-right:2px}
.chip-src{background:var(--draft-soft);color:var(--draft);border-color:var(--draft-line);
  font-family:var(--mono);font-size:var(--text-xs)}
/* source_chips() emits an <a> for a citation that resolves and a <span> for one
   that does not. Without a persistent cue the two are identical at rest and the
   difference only appears on hover, which touch and keyboard never see. */
a.chip-src{text-decoration:underline;text-underline-offset:.15em}
a.chip-src:hover{background:var(--surface-3);color:var(--accent-ink);
  border-color:var(--control-line)}
.ask-loading{margin:0;font-size:var(--text-base);color:var(--text-muted)}
.ask-loading::after{content:"";display:inline-block;width:1em;text-align:left;
  animation:ellipsis 1.2s steps(4,end) infinite}
@keyframes ellipsis{0%{content:""}25%{content:"."}50%{content:".."}75%{content:"..."}}

/* ── facts ─────────────────────────────────────────────── */
.fact-grid{display:flex;flex-direction:column;gap:9px;margin-top:var(--space-4)}
.fact{display:flex;gap:var(--space-3);align-items:baseline;flex-wrap:wrap}
.fact-label{font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.18em;
  font-weight:600;color:var(--text-faint);min-width:130px}
.fact-value{font-size:var(--text-sm);word-break:break-all}

/* ── suggestions ───────────────────────────────────────── */
/* Distinct from .suggestion, which is the Ask page's follow-up chip. Class
   matching is exact, so the two never collide — but they are neighbours in a
   grep, and this comment is here so nobody "tidies" one into the other. */
.suggestion-form{display:flex;flex-direction:column;gap:var(--space-3);
  margin-top:var(--space-4)}
/* A textarea is not an <input>, so the shared control rule above does not
   reach it and an unstyled one drops to the UA's monospace default. */
.suggestion-form textarea{font:inherit;font-size:var(--text-base);
  color:var(--text);background:var(--surface);
  border:1px solid var(--control-line);border-radius:var(--r);
  padding:11px 13px;width:100%;min-width:0;resize:vertical;
  line-height:var(--leading-normal);
  transition:border-color var(--fast) var(--ease)}
.suggestion-form textarea:focus{border-color:var(--accent-ink)}
.suggestion-form-foot{display:flex;flex-wrap:wrap;gap:var(--space-3);
  align-items:flex-end;justify-content:space-between}
.suggestion-form-foot select{font:inherit;font-size:var(--text-base);
  color:var(--text);background:var(--surface);
  border:1px solid var(--control-line);border-radius:var(--r);
  padding:9px 10px;min-width:190px;
  transition:border-color var(--fast) var(--ease)}
.suggestion-form-foot select:hover,
.suggestion-form-foot select:focus{border-color:var(--accent-ink)}
.suggestion-list{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:var(--space-3)}
.suggestion-item{background:var(--surface-2);border:1px solid var(--border);
  border-radius:var(--r-lg);padding:var(--space-4)}
.suggestion-top{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-3);
  align-items:baseline;margin-bottom:var(--space-2)}
.suggestion-top time{margin-left:auto;font-variant-numeric:tabular-nums}
.suggestion-body{font-size:var(--text-base);line-height:var(--leading-normal);
  color:var(--text);overflow-wrap:anywhere}
/* Category chips take the same status triplets as every other chip; the
   mapping is arbitrary but stable, and no two adjacent categories share one. */
.chip-cat-product{background:var(--info-soft);color:var(--info);border-color:var(--info-line)}
.chip-cat-data{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.chip-cat-reporting{background:var(--warning-soft);color:var(--warning);border-color:var(--warning-line)}
.chip-cat-integration{background:var(--draft-soft);color:var(--draft);border-color:var(--draft-line)}
.chip-cat-other{background:var(--draft-soft);color:var(--draft);border-color:var(--draft-line)}

/* ── profile ───────────────────────────────────────────── */
.profile-head{display:flex;align-items:center;gap:var(--space-4);
  margin-bottom:var(--space-5);flex-wrap:wrap}
.profile-name{font-size:var(--text-md);font-weight:600;color:var(--heading-ink)}

/* ── auth ──────────────────────────────────────────────── */
/* Full-bleed Basalt field, one white card floating on it. The old radial glow
   was tinted with --accent-soft, which is now Basalt rather than a hue. */
.auth-body{display:grid;place-items:center;min-height:100vh;padding:var(--space-5);
  background:radial-gradient(1100px 600px at 50% -15%,#0f3742,transparent 70%),var(--sidebar-bg)}
.auth-shell{width:100%;max-width:404px;display:flex;flex-direction:column;
  gap:var(--space-5)}
.auth-wordmark{color:var(--rail-ink-strong);text-align:center}
/* .wordmark-svg{height:.85em} tracks .wordmark's own font-size, not the
   ambient one -- .wordmark sets --text-md itself. The rail needs that
   compact scale; a login hero needs the display tier the type scale
   already defines for exactly this. Scoped so the rail is untouched. */
.auth-wordmark .wordmark{font-size:var(--text-hero)}
.auth-card{background:var(--surface);border-radius:var(--r-xl);
  padding:var(--space-6) var(--space-6) var(--space-5);width:100%;max-width:404px;
  box-shadow:var(--shadow-lg);color:var(--text)}
/* The field is pinned to Basalt in both themes but the card follows --surface,
   so in dark it is #0c2b33 floating on #09262d: 1.06:1 at the edge, with a
   black --shadow-lg that is invisible on a dark ground. Light mode is #fff on
   Basalt and needs nothing. Two blocks, same body, edited in lockstep — same
   convention as the token blocks above. */
[data-theme=dark] .auth-card{border:1px solid var(--control-line)}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]) .auth-card{border:1px solid var(--control-line)}
}
.auth-product{margin:var(--space-2) 0 0;font-size:var(--text-xs);font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--ink-subdued)}
.auth-card h1{font-size:var(--text-lg);margin-bottom:6px}
.auth-lead{font-size:var(--text-sm);color:var(--text-muted);
  line-height:var(--leading-normal);margin-bottom:var(--space-5)}
.auth-form{display:flex;flex-direction:column;gap:6px}
.auth-form label{margin-top:var(--space-2)}
.auth-form input{padding:11px 14px}
.auth-form .btn{margin-top:var(--space-5);width:100%}
.auth-hint{font-size:var(--text-sm);color:var(--text-faint);text-align:center;
  margin-top:var(--space-5)}
.form-error{background:var(--danger-soft);color:var(--danger);
  border:1px solid var(--danger-line);font-size:var(--text-sm);
  padding:10px 14px;border-radius:var(--r);margin-bottom:var(--space-4)}
.form-note{background:var(--success-soft);color:var(--success);
  border:1px solid var(--success-line);font-size:var(--text-sm);
  padding:10px 14px;border-radius:var(--r);margin-bottom:var(--space-4)}

/* ── new-client popout (Clients page-head) ────────────── */
/* <details> as its own positioning context: the panel anchors to the
   trigger button, not to the page, so it tracks the button regardless of
   where the page-head row puts it. */
.new-client-pop{position:relative}
.new-client-pop>summary{list-style:none}
.new-client-pop>summary::-webkit-details-marker{display:none}
.new-client-pop>summary::marker{content:""}
.new-client-panel{position:absolute;top:calc(100% + var(--space-2));right:0;
  z-index:20;width:min(560px,calc(100vw - 32px));padding:var(--space-4);
  box-shadow:var(--shadow-lift)}
.new-client-panel form{margin-top:var(--space-4);display:flex;
  flex-direction:column;gap:var(--space-3)}
/* .field-grow's flex-basis:240px means "grow to fill row width" in the
   horizontal toolbar it was written for. This form is a column, where the
   same basis reads as height instead -- 240px of dead space under a
   two-row textarea. Every field already stretches to the column's full
   width by default, so grow has nothing to do here. */
.new-client-panel .field-grow{flex:none}
.form-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--space-3)}
/* .field input's 290px floor (written for the flex-wrap toolbar, where a
   wide track just wraps) doesn't fit a fixed-width 3-column grid: three
   168px tracks in the 560px panel can't shrink a 290px-floored input, so it
   overran the panel. Grid tracks don't wrap, so the floor has to go. */
.form-grid .field input{min-width:0;width:100%}
@media (max-width:720px){.form-grid{grid-template-columns:1fr}}

/* ── breadcrumb (in the content, on paper) ─────────────── */
.crumbs{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:center;
  font-size:var(--text-sm);color:var(--text-muted);margin-bottom:var(--space-3)}
.crumbs a{color:var(--text-muted);display:inline-block;padding-block:6px}
.crumbs a:hover{color:var(--text)}
.crumbs [aria-current=page]{color:var(--text);font-weight:500}
.crumb-sep{color:var(--text-faint)}

/* ── detail heads ──────────────────────────────────────── */
/* The record name is the shell H1 now, so this row carries the meta line and
   the status chips rather than repeating the title underneath it. */
.detail-head{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:flex-start;
  justify-content:space-between;margin-bottom:var(--space-2)}
.detail-summary{margin-bottom:var(--space-5);max-width:68ch}
.detail-head-right{display:flex;gap:var(--space-2);align-items:center;flex-wrap:wrap}

.stat-row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:var(--space-3);margin-bottom:var(--space-5)}

.mini-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:var(--space-3);margin:var(--space-3) 0 0;padding:var(--space-3) 0 0;
  border-top:1px solid var(--border)}
.mini-metrics dt{font-size:var(--text-xs);color:var(--text-faint);
  text-transform:uppercase;letter-spacing:.18em;font-weight:600}
.mini-metrics dd{margin:2px 0 0;font-size:var(--text-base);font-weight:600}

.kv{display:grid;gap:var(--space-3);margin:0}
.kv div{display:flex;justify-content:space-between;gap:var(--space-3);align-items:baseline;
  padding-bottom:var(--space-2);border-bottom:1px solid var(--border)}
.kv div:last-child{border-bottom:0;padding-bottom:0}
.kv dt{font-size:var(--text-sm);color:var(--text-muted)}
.kv dd{margin:0;font-size:var(--text-sm);font-weight:600;text-align:right}

.card-title-link{color:inherit}
.card-title-link:hover{color:inherit;text-decoration:underline}
.card-actions{display:flex;gap:var(--space-3);align-items:center;margin-top:var(--space-4);
  flex-wrap:wrap}
.row-link{font-weight:600}
.scope-note{font-size:var(--text-base);color:var(--text-muted);margin:0 0 var(--space-3)}

/* Verdict line. Carries the judgement the relationship panel exists to make,
   so it is typographically louder than the list beneath it. Same 3px-bar
   idiom as the active rail item, in the status line colour. */
.verdict{font-size:var(--text-base);font-weight:500;line-height:1.45;
  margin:0 0 var(--space-4);padding:var(--space-3);border-radius:var(--r-sm);
  border-left:3px solid var(--draft-line);background:var(--draft-soft);color:var(--text)}
.verdict.tone-ok{border-left-color:var(--success-line);background:var(--success-soft);
  color:var(--text)}
.verdict.tone-warn{border-left-color:var(--warning-line);background:var(--warning-soft)}
.verdict.tone-risk{border-left-color:var(--danger-line);background:var(--danger-soft)}

/* ── contacts ──────────────────────────────────────────── */
.contact-list{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-3)}
.contact{padding:var(--space-3);border:1px solid var(--border);border-radius:var(--r-lg);
  background:var(--surface-2)}
/* No opacity. Group opacity composites the whole subtree toward the panel
   ground, and it took the note, the role and the "Lapsed" chip — the three
   things a reader opens a lapsed contact for — from 6.9:1 to 3.5:1. The state
   is already carried twice, by the dashed frame and by the chip that says
   "Lapsed"; the frame just needs to be visible enough to count as a cue. */
.contact.is-lapsed{border-style:dashed;border-color:var(--control-line)}
.contact-main{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline}
.contact-name{font-weight:600;font-size:var(--text-base)}
.contact-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.contact-note{margin:var(--space-2) 0 0;font-size:var(--text-sm);
  line-height:var(--leading-normal);color:var(--text-muted)}
.small{font-size:var(--text-xs)}

.chip-inf-champion{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.chip-inf-economic{background:var(--info-soft);color:var(--info);border-color:var(--info-line)}
.chip-inf-technical{background:var(--draft-soft);color:var(--draft);border-color:var(--draft-line)}
.chip-inf-blocker{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line)}
.chip-inf-neutral{background:var(--draft-soft);color:var(--draft);border-color:var(--draft-line)}
.chip-eng-strong{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.chip-eng-occasional{background:var(--warning-soft);color:var(--warning);border-color:var(--warning-line)}
.chip-eng-lapsed{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line)}

/* ── timeline ──────────────────────────────────────────── */
/* The rail is a border on the list, not a pseudo-element per item, so it stays
   continuous when an entry wraps. padding-left and .tl-marker's left offset
   are a matched pair — move one and the dots drift off the rail. */
.timeline{list-style:none;margin:0;padding:0 0 0 22px;
  border-left:2px solid var(--border);display:grid;gap:var(--space-5)}
.tl-item{position:relative}
.tl-marker{position:absolute;left:-29px;top:4px;width:12px;height:12px;
  border-radius:50%;background:var(--surface);border:2px solid var(--border-strong)}
.tl-risk .tl-marker{border-color:var(--danger);background:var(--danger-soft)}
/* A marker carries no text, so this is the one place lime can mark the brand
   event outright. The chip beside it stays on the info triplet. */
.tl-stage_change .tl-marker{border-color:var(--accent-ink);background:var(--accent)}
.tl-meeting .tl-marker{border-color:var(--success);background:var(--success-soft)}
.tl-head{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:baseline;margin:0}
.tl-title{font-weight:600;font-size:var(--text-base)}
.tl-detail{margin:5px 0 0;font-size:var(--text-sm);line-height:var(--leading-normal);
  color:var(--text-muted);max-width:68ch}
.tl-meta{display:flex;flex-wrap:wrap;gap:var(--space-3);margin:7px 0 0;
  font-size:var(--text-xs);color:var(--text-faint)}
.tl-actor::before{content:"·";margin-right:10px}
.tl-link::before{content:"·";margin-right:10px;color:var(--text-faint)}
.chip-act-risk{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line)}
.chip-act-stage_change{background:var(--info-soft);color:var(--info);border-color:var(--info-line)}
.chip-act-meeting{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.chip-act-note,.chip-act-email,.chip-act-document{background:var(--draft-soft);
  color:var(--draft);border-color:var(--draft-line)}

.chip-kind-risk{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line)}
.chip-kind-expansion{background:var(--success-soft);color:var(--success);border-color:var(--success-line)}
.chip-kind-relationship{background:var(--info-soft);color:var(--info);border-color:var(--info-line)}
.chip-kind-market{background:var(--draft-soft);color:var(--draft);border-color:var(--draft-line)}

/* ── empty states, notices, loading ────────────────────── */
/* Two distinct concepts, deliberately not merged: .empty is the inline italic
   line inside a list, .empty-state is the designed panel. */
.empty-state{text-align:center;padding:var(--space-6) var(--space-5);
  border:1px dashed var(--border-strong);border-radius:var(--r-xl);
  background:var(--surface-2)}
.empty-title{margin:0 0 4px;font-weight:600;font-size:var(--text-base)}
.empty-state .muted{max-width:52ch;margin:0 auto}
.empty-state .btn-quiet{margin-top:var(--space-4)}

/* Lime bar on a notice: legal, because the bar carries no text. Inset rather
   than a real border so it follows the card's 18px radius. The bar is the only
   thing marking this panel as a caveat and lime on #f7f8fa is 1.11:1, so a 1px
   Basalt keyline (14.9:1) is painted under it and shows at x 3-4. */
.notice{background:var(--surface-2);
  box-shadow:inset 3px 0 0 var(--accent),inset 4px 0 0 var(--on-accent),var(--shadow)}
.notice .prose{margin:0}

.head-actions{display:flex;gap:var(--space-3);align-items:center;flex-wrap:wrap}
.inline-form{display:inline}

/* The export preview is machine output; monospace and a scroll container,
   never wrapped, because wrapping a fixed-width report is a lie about what
   the file would contain. */
/* --text-sm, not --text-xs: the export text is content, not a label. */
.preview{font-family:var(--mono);font-size:var(--text-sm);line-height:var(--leading-normal);
  background:var(--surface-2);border:1px solid var(--border);
  border-radius:var(--r-lg);padding:var(--space-4);margin:var(--space-3) 0 0;
  overflow-x:auto;white-space:pre;color:var(--text)}

/* Applied by app.js while a filter submit is in flight; without JS the
   browser's own navigation indicator does this job. */
/* .85, not .55: this dims the whole content column, and at .55 --text-muted
   fell to 2.6:1 for the duration of the request. */
.is-loading{opacity:.85;pointer-events:none;transition:opacity var(--fast) var(--ease)}
/* Same idea one control down: app.js disables the suggestion form's submit
   button once the POST is away, so a second click cannot file the same
   suggestion twice. Kept legible rather than greyed out — the button still
   says what it did, it just cannot be pressed again. Without JavaScript the
   button is never disabled and the form works exactly as before. */
.btn:disabled{opacity:.85;cursor:default;box-shadow:none}
.btn:disabled:hover{background:var(--accent);box-shadow:none}

/* Theme toggle: show the palette you would switch *to*, not the one you are
   in — a sun on a light page invites nobody to press it. */
#theme-btn .ico-sun{display:none}
#theme-btn .ico-moon{display:block}
[data-theme=dark] #theme-btn .ico-moon{display:none}
[data-theme=dark] #theme-btn .ico-sun{display:block}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]) #theme-btn .ico-moon{display:none}
  :root:not([data-theme=light]) #theme-btn .ico-sun{display:block}
}
#theme-btn svg{width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}

/* ── layout-correctness guards (not styling) ───────────── */
/* Grid and flex children default to min-width:auto, so a child whose
   min-content width exceeds the track pushes the whole page sideways instead
   of wrapping. Fixed at the container level rather than per-component,
   because the next dense row would reintroduce it. Do not remove. */
.content > *,
.grid-2 > *,
.stack-row,
.stack-row > *,
.tl-item,
/* .kv rows were missing from this list, and the profile page's Environment
   row put a white-space:nowrap .chip in the dd: 73px label + 12px gap + 189px
   chip inside a 240px track hung the chip 10px outside the white card at
   320px. The narrow-width stack that finishes the fix is in the 420px block. */
.kv div,
.kv div > *,
.contact{min-width:0}

/* Long unbroken values (ids, URLs, machine strings) must wrap rather than
   set the width of their column. */
.tl-detail,.contact-note,.client-summary,.prose,.detail-summary{overflow-wrap:anywhere}

/* A pill button beside a heading must not wrap its own label. .panel-head is
   a flex row with justify-content:space-between, so inside a narrow track
   (a .grid-2 column at ~470px) the button is the item that gives -- and a
   two-word label breaking across lines inside a border-radius:999px pill
   renders as a blob rather than a control. The heading is the element with
   room to wrap, so the button keeps its line and the h2 takes the pressure.
   flex-shrink:0 alone is not enough: the label itself has to be told not to
   break. */
.panel-head > .btn,
.panel-head > .head-actions > .btn{flex:0 0 auto;white-space:nowrap}

/* Anchored cards (home deep-links to /clients#<id>) must clear the sticky
   topbar rather than landing underneath it. */
.client-card{scroll-margin-top:calc(var(--topbar-h) + var(--space-4))}

/* ── collapsed rail (the desktop side of the breakpoint) ─ */
/* The mirror image of .menu-btn's scoping. #menu-btn is display:none on the
   desktop side; #rail-toggle is display:none on the narrow side, so the two
   navigation controls are never both on screen and the collapse state can
   never be the reason someone cannot reach the nav on a phone.
   Everything that narrows the rail lives inside the query for the same reason:
   on the narrow side the rail is position:fixed and translated off-canvas, and
   a 64px width there would fight the off-canvas rules rather than help them.
   The default — expanded — is the CSS default, so with JavaScript off the rail
   is always full width and always navigable.

   THE BOUNDARY IS 900.02 / 900.021, NOT 900 / 901. Written as a pair of
   adjacent fractional widths because min-width:901px and max-width:900px are
   NOT complements: they leave the whole open interval (900, 901) uncovered,
   and fractional CSS viewport widths in that band are ordinary rather than
   exotic — a 1126px window at 125% zoom is 900.8 CSS px. In that band neither
   block matched, so the rail fell back to the base 234px sticky column while
   <html> still said data-rail="collapsed", with BOTH #rail-toggle and
   #menu-btn hidden: no navigation-width control on the page at all. The
   0.001px that still separates the two is finer than any device pixel ratio
   quantises to. Keep the two numbers in step — see the narrow block below. */
#rail-toggle{display:none}
@media (min-width:900.021px){
  #rail-toggle{display:flex}
  .sidebar{transition:width var(--normal) var(--ease-rail),
    flex-basis var(--normal) var(--ease-rail)}
  [data-rail=collapsed] .sidebar{width:var(--rail-width-collapsed);
    flex:0 0 var(--rail-width-collapsed);padding-inline:var(--space-2)}

  /* Every label collapses to .sr-only GEOMETRY, never display:none. The text
     stays in the accessibility tree, so not one row in the collapsed rail is
     an unnamed icon; the title attributes in the markup cover the pointer. */
  [data-rail=collapsed] .nav-label,
  [data-rail=collapsed] .nav-group-label,
  [data-rail=collapsed] .brand-text,
  [data-rail=collapsed] .rail-foot-label,
  [data-rail=collapsed] .badge-note,
  [data-rail=collapsed] .pill-text{
    position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
    clip:rect(0 0 0 0);white-space:nowrap;border:0}

  /* Centre what is left. The active item keeps its 3px lime bar — with the
     labels gone it is the only thing marking the current page. */
  [data-rail=collapsed] .nav-item,
  [data-rail=collapsed] .rail-foot-item{justify-content:center;gap:0;
    padding-inline:0}
  [data-rail=collapsed] .brand{justify-content:center;padding-inline:0}
  [data-rail=collapsed] .brand-mark{display:grid}
  [data-rail=collapsed] .rail-status{align-items:center;padding-inline:0}

  /* The posture pill has nothing to say without its text, so it becomes the
     dot it always was underneath. Colour ALONE is not allowed to carry it:
     the pill's translucent grounds composite to 1.47:1 (ok) and 1.44:1 (warn)
     against Basalt and are 1.02:1 apart from EACH OTHER, so the two postures
     were pixel-indistinguishable and both were far under the 3:1 floor for a
     graphic that conveys information (WCAG 1.4.11), and colour was the only
     carrier (1.4.1). So: repaint with the pill's own ink, which clears 3:1 on
     Basalt (#a8e8c6 is 7.72:1, #f2d98f higher), AND give the two states
     different SHAPES — demo is a solid disc, live connectors is a ring. The
     row also carries a title in the markup, so the collapsed dot has a
     pointer name like every other row. .pill-text is still announced. */
  [data-rail=collapsed] .rail-status .pill{width:12px;height:12px;padding:0;
    border-radius:50%;box-sizing:border-box}
  [data-rail=collapsed] .rail-status .pill-ok{background:#a8e8c6;
    border:1px solid #a8e8c6}
  [data-rail=collapsed] .rail-status .pill-warn{background:transparent;
    border:3px solid #f2d98f}

  /* Reserve the scrollbar on BOTH edges of the nav scroller. At 64px the rail
     has 48px of content and a scrollbar taken off one side only re-centred the
     20px glyphs 7px left of the brand mark, the posture dot and the four
     footer icons — a visible break in the icon column, and it appeared and
     disappeared with the viewport height. both-edges keeps the content box
     symmetric, so the icons sit on the rail's centre line whether the nav
     scrolls or not. */
  [data-rail=collapsed] .nav{scrollbar-gutter:stable both-edges}

  /* The group labels no longer take space, so the gap that separated the
     groups has to come from the margin alone. */
  [data-rail=collapsed] .nav-group + .nav-group{margin-top:var(--space-3)}
}

/* ── responsive ────────────────────────────────────────── */
@media (max-width:1080px){
  /* All four KPI grids step down together. .stat-row and .mini-metrics used to
     stay 4-up until 900px, so the detail pages were at their most crowded in
     the band where the visually identical home cards had already relaxed. */
  .stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .metric-row{grid-template-columns:repeat(2,minmax(0,1fr))}
  .stat-row{grid-template-columns:repeat(2,minmax(0,1fr))}
  .mini-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}
}
/* The narrow side of the rail breakpoint. 900.02px, not 900px: this and the
   collapsed-rail block's min-width:900.021px are deliberately adjacent
   fractional widths so no viewport can fall between them. Keep them in step. */
@media (max-width:900.02px){
  .grid-2{grid-template-columns:1fr}
  /* visibility, not transform alone. A transformed element is still rendered,
     still in the accessibility tree and still in the tab order, so the closed
     rail parked 7 focusable links at x=-234px on every narrow page while
     #menu-btn advertised aria-expanded="false". The delayed visibility keeps
     the slide: hidden only once the transform has finished. */
  .sidebar{position:fixed;z-index:40;transform:translateX(-100%);visibility:hidden;
    transition:transform var(--normal) var(--ease-rail),
      visibility 0s linear var(--normal)}
  /* The shadow moves here: with no x-offset, half of --shadow-lg's 60px blur
     spilled across x=0 onto the page while the rail was shut. */
  .sidebar.open{transform:none;visibility:visible;transition-delay:0s;
    box-shadow:var(--shadow-lg)}
  /* The open rail paints over its own toggle, so without this there is no way
     to close it on touch — Escape is the only other dismissal and a phone has
     no Escape key. */
  .nav-scrim{display:block}
  .menu-btn{display:grid}
  .content{padding:var(--space-5) var(--space-4) var(--space-7)}
  .topbar{padding:0 var(--space-4)}
}
@media (max-width:620px){
  .stat-grid,.metric-row,.stat-row,.mini-metrics{grid-template-columns:1fr}
  h1{font-size:var(--text-lg)}
  .stat-value{font-size:var(--text-lg)}
  .client-head{flex-direction:column;gap:var(--space-3)}
  /* Both are decorative: .env-tag repeats what the rail status block says and
     .topbar-crumbs is aria-hidden and already an ellipsis at this width.
     Sign out used to be exempt because the topbar was the only place it
     existed; it lives in the rail footer now, so the bar sheds down to the
     nav toggle and the theme toggle — two real controls, one at each end. */
  .env-tag{display:none}
  .topbar-crumbs{display:none}
  .table-wrap{margin:0;padding:0}
  .data-table{min-width:0}
  .data-table thead{display:none}
  .data-table tr{display:block;border-bottom:1px solid var(--border);padding:11px 0}
  /* flex-wrap, because a cell can hold more than one child: the Opportunity
     cell carries a title link plus the next step, and blockification made them
     siblings on one right-aligned line. */
  .data-table td{display:flex;flex-wrap:wrap;justify-content:space-between;
    gap:var(--space-3);border:0;padding:4px 0;text-align:right}
  /* Capped rather than flex-shrink:0 — an unshrinkable 111px label ate 46% of
     a 240px row at 320px. */
  .cell-label{display:block;font-size:var(--text-xs);
    text-transform:uppercase;letter-spacing:.18em;color:var(--text-faint);
    font-weight:600;text-align:left;max-width:45%}
  .data-table td > *:not(.cell-label){min-width:0}
  .bar-row{grid-template-columns:74px 1fr 48px}
  .toolbar{padding:var(--space-3)}
  .toolbar input[type=search],.toolbar select{min-width:0;width:100%}
  .field input{min-width:0;width:100%}
  .field,.field-grow{flex:1 1 100%}
  /* The category select's 190px floor plus the Add button will not sit on one
     line inside a 240px panel, so the row stacks rather than overflowing. */
  .suggestion-form-foot{flex-direction:column;align-items:stretch}
  .suggestion-form-foot select{min-width:0;width:100%}
  .suggestion-top time{margin-left:0}
  .timeline{padding-left:18px}
  .tl-marker{left:-25px}
}
/* Below ~340px the value-plus-stage cluster on a stack row cannot sit beside
   the title without pushing the page sideways. Let it drop to its own line
   rather than clipping it or shrinking the chip text. */
@media (max-width:420px){
  .stack-row{flex-wrap:wrap}
  .stack-row .right{width:100%;justify-content:flex-start;margin-top:6px}
  /* Same reason, one row further in: a .kv label and a nowrap chip cannot
     share a line inside a 240px panel. Below ~354px the profile page's
     Environment chip hung outside the card's rounded corner, so the pair
     stacks — label, then value, both left-aligned. */
  .kv div{flex-wrap:wrap}
  .kv dd{width:100%;text-align:left}
}

/* One reduced-motion block, not two. */
@media (prefers-reduced-motion:reduce){
  /* transition-delay too, or the off-canvas rail's deferred visibility keeps
     it in the tab order for 200ms after a close that had no animation. */
  *,*::before,*::after{animation-duration:.001ms!important;
    transition-duration:.001ms!important;transition-delay:0s!important}
  .ask-loading::after{animation:none;content:"…"}
}

/* ── Demographic grids (EEA2 / EEA4 / Workforce Profile) ───────
   Ten near-identical numeric columns is the hardest thing in this application
   to read. Four changes, all presentation -- no figure moves:

   1. A rule after WM and after WF, so the male / female / foreign-national
      blocks read as blocks instead of as one undifferentiated run.
   2. The level column sticks while the grid scrolls sideways, so a figure
      three columns in still has a row label attached to it.
   3. Tabular figures, right-aligned, so digits line up in a column and the
      eye can compare down it.
   4. The header row sticks to the top of a long grid.
*/
.grid-table{min-width:820px;}
.grid-table .col-num{text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;}
.grid-table thead th.col-num{text-align:right;}

/* The two group rules. Set on both the header cell and the body cell so the
   line is continuous down the whole table rather than stopping at the head. */
.grid-table [data-group-end="1"]{
  border-right:2px solid var(--border-strong);
  padding-right:14px;
}
.grid-table [data-group-end="1"] + *{padding-left:14px;}

/* The abbreviated codes. A dotted underline is the long-standing convention
   for "there is an expansion available"; without it the tooltip is invisible
   to anyone who does not think to hover. */
.grid-table thead abbr{
  text-decoration:underline dotted; text-decoration-thickness:1px;
  text-underline-offset:3px; cursor:help;
}

/* Sticky row label. Needs its own background or the scrolling columns show
   through it. */
.grid-table .col-level{
  position:sticky; left:0; z-index:2;
  background:var(--surface); text-align:left; white-space:nowrap;
  font-weight:500;
}
.grid-table tbody tr:hover .col-level{background:var(--surface-2);}
.grid-table thead .col-level{z-index:3;}

.grid-table thead th{
  position:sticky; top:0; z-index:1; background:var(--surface);
}
.grid-table .col-total{font-weight:600;}
.grid-table .row-total th,.grid-table .row-total td{
  border-top:2px solid var(--border-strong); font-weight:600;
}

/* ── Print and export ─────────────────────────────────────────
   There was no print block at all, which is how a ten-column statutory grid
   ends up clipped at the right margin on paper: the screen layout relies on
   .table-wrap scrolling horizontally, and paper cannot scroll.

   Landscape, chrome removed, sticky positioning dropped (it computes to a
   fixed offset in paged media and pushes cells off the sheet), and the header
   row repeated on every page so the second page of a long grid is still
   readable. */
@media print{
  @page{size:A4 landscape; margin:12mm;}

  .sidebar,.topbar,.rail,.toolbar,.btn,.skip,nav,
  .panel-head .btn,.crumbs{display:none !important;}

  body{background:#fff; color:#000; font-size:10pt;}
  .panel{
    border:1px solid #999; box-shadow:none; break-inside:avoid;
    margin:0 0 8mm; padding:6mm;
  }

  /* The clipping fix: no scroll container, no minimum width, table shrinks
     to the sheet. */
  .table-wrap{overflow:visible !important; max-width:none;}
  .data-table,.grid-table{min-width:0 !important; width:100%; font-size:9pt;}
  .grid-table .col-level,.grid-table thead th{position:static !important;}

  /* Repeat the header on each page, and never split a row across a break. */
  .data-table thead{display:table-header-group;}
  .data-table tr{break-inside:avoid;}

  .grid-table [data-group-end="1"]{border-right:1.5pt solid #000;}
  .grid-table .row-total th,.grid-table .row-total td{border-top:1.5pt solid #000;}

  /* Keep the provenance visible on paper -- a printed statutory table with no
     source line is the version that ends up in a folder unattributed. */
  .muted{color:#333 !important;}
  abbr{text-decoration:none;}
}

/* ── Navigation overflow affordance ───────────────────────────
   Debs did not realise the sidebar scrolled, and she was right not to: with
   fifteen destinations the rail overflows on a 720px laptop, and the only
   signal was a 6px dark scrollbar on a Basalt rail -- which on any machine
   with overlay scrollbars is invisible until you are already scrolling. The
   destinations were reachable and undiscoverable, which is the same thing as
   missing.

   The fix is a soft fade at whichever edge has more content behind it, set by
   JS on the scroller (see the "nav overflow" feature in app.js). Three
   reasons it is a mask rather than an overlaid gradient element:

   - A mask applies to the element's own box, so the fade stays pinned to the
     visual edge instead of scrolling away with the content. A pseudo-element
     on the scroller would scroll with it.
   - No extra DOM, so the carefully-tuned flex geometry of the desktop rail is
     untouched.
   - Nothing to intercept a click, which an overlaid element would need
     pointer-events:none to avoid.

   The fade only appears in the direction there is actually something to see,
   so a rail that fits shows no decoration at all. */
.nav[data-overflow-bottom="1"]{
  -webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 30px),transparent);
  mask-image:linear-gradient(to bottom,#000 calc(100% - 30px),transparent);
}
.nav[data-overflow-top="1"]{
  -webkit-mask-image:linear-gradient(to bottom,transparent,#000 30px);
  mask-image:linear-gradient(to bottom,transparent,#000 30px);
}
.nav[data-overflow-top="1"][data-overflow-bottom="1"]{
  -webkit-mask-image:linear-gradient(to bottom,transparent,#000 30px,
    #000 calc(100% - 30px),transparent);
  mask-image:linear-gradient(to bottom,transparent,#000 30px,
    #000 calc(100% - 30px),transparent);
}

/* Load-bearing for keyboard users, not cosmetic. Tabbing to the last nav item
   scrolls it just into view -- which under the mask is the faded zone, so the
   focused row and its lime ring would both be half-transparent. scroll-padding
   makes the browser scroll 34px further, clearing the fade, so focus always
   lands somewhere fully opaque. */
.nav{scroll-padding-block:34px;}

/* The scrollbar is the second, independent signal, and it was too quiet to be
   one: 6px at .24 alpha on Basalt. 8px at .34 is still unobtrusive on a dark
   rail but survives a glance. The collapsed-rail gutter maths below depends on
   this width, so the two moved together. */
@media (min-width:900.021px){
  .nav{scrollbar-color:rgba(255,255,255,.34) transparent}
  .nav::-webkit-scrollbar{width:8px}
  .nav::-webkit-scrollbar-thumb{background:rgba(255,255,255,.34)}
  .nav::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.52)}
}

/* Below 901px the whole sidebar scrolls as one piece, so the affordance
   belongs there instead. Same attributes, same JS. */
@media (max-width:900.02px){
  .sidebar[data-overflow-bottom="1"]{
    -webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 30px),transparent);
    mask-image:linear-gradient(to bottom,#000 calc(100% - 30px),transparent);
  }
  .sidebar[data-overflow-top="1"]{
    -webkit-mask-image:linear-gradient(to bottom,transparent,#000 30px);
    mask-image:linear-gradient(to bottom,transparent,#000 30px);
  }
  .sidebar[data-overflow-top="1"][data-overflow-bottom="1"]{
    -webkit-mask-image:linear-gradient(to bottom,transparent,#000 30px,
      #000 calc(100% - 30px),transparent);
    mask-image:linear-gradient(to bottom,transparent,#000 30px,
      #000 calc(100% - 30px),transparent);
  }
  .sidebar{scroll-padding-block:34px;}
}

/* A mask is decoration. On paper there is no scrolling to hint at, and a
   faded edge would print as a genuinely washed-out row. */
@media print{
  .nav,.sidebar{-webkit-mask-image:none !important;mask-image:none !important}
}

/* The end-of-list case, which the focus nudge alone cannot fix.
   The last row has nothing below it, so there is no scroll distance left to
   move it out of the bottom fade -- the scroller is already at maximum. Give
   the scrolled content a fade's worth of empty space at each end and the
   first and last rows can both reach fully-opaque territory. Only applied
   while actually overflowing, so a rail that fits gains no dead space.
   Cheaper and far more reliable than depending on how a browser chooses to
   scroll a focused element into view. */
.nav[data-overflow-bottom="1"],
.nav[data-overflow-top="1"]{padding-bottom:36px}
@media (max-width:900.02px){
  .sidebar[data-overflow-bottom="1"],
  .sidebar[data-overflow-top="1"]{padding-bottom:36px}
}


/* -- Data-entry forms ------------------------------------------------------
   The capture screens. Everything here exists because a validation message
   the typist does not notice is the same as no validation at all: the error
   sits under its own field, in the danger colour, and the field it belongs to
   is outlined so the eye lands on it without reading. */
.field-grid{display:grid;gap:var(--space-4);
  grid-template-columns:repeat(auto-fit,minmax(290px,1fr))}
/* .field sets min-width:290px on inputs; inside the grid the cell governs, or
   a two-column layout overflows its container on a narrow window. */
/* :not([type=checkbox]) matters. Without it the checkbox was stretched to
   the full width of its grid cell and the label wrapped underneath it,
   which read as a heading over an empty box rather than as a tickbox with
   a name. */
.field-grid .field input:not([type=checkbox]),
.field-grid .field select{min-width:0;width:100%}
.field-grid .field-check input[type=checkbox]{width:16px;height:16px;
  min-width:16px;flex:0 0 auto;margin:0;accent-color:var(--accent-ink)}
.field-grid .field select{font:inherit;font-size:var(--text-base);color:var(--text);
  background:var(--surface);border:1px solid var(--control-line);
  border-radius:var(--r);padding:9px 13px}
.field-grid .field select:focus{border-color:var(--accent-ink)}
.field-hint{font-size:var(--text-xs);color:var(--text-faint);line-height:1.45}
.field-error{font-size:var(--text-xs);font-weight:600;color:var(--danger)}
.field-bad input,.field-bad select{border-color:var(--danger)}
.field-check{flex-direction:row;align-items:center;gap:9px;flex-wrap:wrap}
.field-check .field-label{text-transform:none;letter-spacing:normal;
  font-size:var(--text-base);font-weight:500;color:var(--text)}
.field-check .field-hint,.field-check .field-error{flex-basis:100%}
.req{color:var(--danger);font-weight:700}
.form-actions{display:flex;gap:var(--space-2);align-items:center;
  flex-wrap:wrap;margin-top:var(--space-4)}
/* Archiving takes a person out of every statutory count. It should not look
   like Save. */
.btn-danger{background:var(--danger-soft);color:var(--danger);
  border-color:var(--danger-line);box-shadow:none}
.btn-danger:hover{background:var(--danger);color:var(--surface);
  border-color:var(--danger);box-shadow:none;text-decoration:none}
