/* Optimizor Security OptiApp v16 — styled to match the v15 desktop app */

/* ═══ Self-hosted webfonts (16.52.0, skin spec §B) ══════════════════════
   Lato (body) + IBM Plex Sans (display) are the optimizor.ro pair. Both are
   OFL-licensed. The woff2 files were fetched from Google Fonts at BUILD time
   and are served from our own origin: not one Google URL ships, so the app
   makes zero third-party requests at runtime and nothing here depends on
   Google being reachable (tests/test_brand_skin.py is the gate).
   Subsets are latin + latin-ext only. latin-ext is not optional — Romanian's
   ă (U+0103), ș (U+0219) and ț (U+021B) live there (â/î are inside latin's
   U+0000-00FF), so dropping it would silently box-glyph half the RO UI.
   IBM Plex Sans ships as ONE variable file per subset (fvar, wght 100-700),
   which is why 500/600/700 are declared as a weight RANGE rather than three
   static faces — three @font-face rules pointing at one variable file would
   all render at its default instance. */
@font-face {
  font-family: "Lato"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/lato-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Lato"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/lato-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Lato"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("/static/fonts/lato-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Lato"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("/static/fonts/lato-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 100 700; font-display: swap;
  src: url("/static/fonts/ibmplexsans-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 100 700; font-display: swap;
  src: url("/static/fonts/ibmplexsans-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* ── Optimizor brand (16.52.0, skin spec §A) ─────────────────────────
     The palette is optimizor.ro's, introduced as SEMANTIC tokens. The
     legacy names below are ALIASES onto them rather than renames: every
     existing `var(--navy)` / `var(--teal)` in these 2 800 lines keeps
     working and simply picks up the new value, so the reskin is a value
     change, not a find-and-replace across the whole stylesheet. */
  --brand-primary: #001F5B;   /* navy   — chrome, headings, sidebar      */
  --brand-accent:  #009360;   /* green  — actions                        */
  --brand-sky:     #58BFE4;   /* sky    — secondary accent               */

  /* Legacy names (unchanged keys, new values). */
  --navy: var(--brand-primary);
  --teal: var(--brand-accent);          /* legacy name, brand-green value */
  --teal-dark: #00714A;
  --light-blue: var(--brand-sky);
  --green: var(--brand-accent);
  --green-dark: #00714A;
  /* Softened status colours. --amber and --red are used as BACKGROUNDS in
     places (badges, the env banner, meter fills), so they stay mid-tone;
     the label system below carries the tinted bg/ink pairs. */
  --amber: #E0A80F;
  --red: #A8233E;
  --red-dark: #7E1A2C;
  --white: #FFFFFF;
  --light-gray: #F4F6F9;
  --medium-gray: #DDE3EC;
  --dark-gray: #5A6478;
  --off-white: #FAFBFC;

  /* Connection status dots (the mock's quiet pair — a red dot on a list of
     integrations should read as "look here", not as an alarm). */
  --dot-ok:  #7AB031;
  --dot-bad: #C25064;

  /* ── Tinted label pairs (skin spec §E) ───────────────────────────────
     ONE system for every label in the product: a tint of the hue as the
     background, the same hue darkened as the ink. Sentence case, never
     uppercase — a shelf of shouting pills is unreadable at a glance. */
  --tint-crit-bg: #F8E7EC;  --tint-crit-ink: #A8233E;
  --tint-high-bg: #FCEFE0;  --tint-high-ink: #A4540E;
  --tint-med-bg:  #FBF4D2;  --tint-med-ink:  #7D6300;
  --tint-ok-bg:   #E4F2EC;  --tint-ok-ink:   #00714A;
  --tint-navy-bg: #E6EAF3;  --tint-navy-ink: #001F5B;
  --tint-sky-bg:  #E7F4FB;  --tint-sky-ink:  #1F6C94;
  --tint-mute-bg: #EDF0F5;  --tint-mute-ink: #5A6478;

  /* ── Key measures (the gold pair) ────────────────────────────────────
     A SEVENTH pair rather than a reuse, on purpose. Key-ness is not a
     status: a key measure at 4.0 is still key, so the badge may not borrow
     the ok/med/crit hues that mean "how are you doing". It also has to be
     unmistakably not the Important/Essential PROFILE chips on the same row
     — those say which controls apply to you, this says which of them cannot
     be averaged away, and blurring the two vocabularies is the exact
     confusion this whole feature exists to end. Gold, and only here.
     6.1:1 on its own tint — AA-small. */
  --key-bg: #FBF3DC;  --key-ink: #8A6410;  --key-line: #E3D2A0;

  /* ── Type ────────────────────────────────────────────────────────────
     Two families, one job each: Lato reads long, IBM Plex Sans carries the
     structure (headings, buttons, labels, numbers, nav). */
  --font-body: "Lato", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;
  --font-display: "IBM Plex Sans", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;

  --sidebar-bg: var(--navy);
  --sidebar-hover: #072A66;
  --sidebar-selected: #0A3175;
  --sidebar-text: #C6D1E8;
  --sidebar-text-active: var(--white);
  --sidebar-accent: var(--teal);
  --sidebar-muted: #8098C6;  /* 5.36:1 on the navy — AA-small (executor-proposed, applied at deploy) */

  --text-primary: #18181B;
  --text-secondary: #5A6478;
  --text-muted: #9CA3AF;
  --text-on-dark: var(--white);
}

* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  font-family: var(--font-body);
  background: var(--light-gray); color: var(--text-primary);
  height: 100%;
}

/* The display face: shared components + headings, not every one-off. */
h1, h2, h3,
.btn, .pill, .chip, .cal-chip, .cal-fchip, .ev-pill, .rz-tag, .rz-qw,
.rr-pill, .conn-chip, .badge, .env-badge,
.kpi__value, .kpi__label, .prog-score,
.kpi-label, .kpi-hero, .kpi-of, .kpi-chip, .score, .score-card,
.navitem__label, .navgroup__label, .brand-suffix,
.brand-poweredby {
  font-family: var(--font-display);
}
a { color: var(--teal); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── Two-column shell ───────────────────────────────────────────────── */
.shell {
  display: grid;
  grid-template-columns: 250px 1fr;
  min-height: 100vh;
}

/* ── Sidebar ─────────────────────────────────────────────────────────── */
/* 17.50.3: the nav outgrew short viewports - the rail scrolls its own
   overflow instead of hiding the items below the fold. The mobile drawer
   (the max-width:768px block) has always done this. */
.sidebar {
  background: var(--sidebar-bg);
  color: var(--sidebar-text);
  display: flex; flex-direction: column;
  padding: 24px 0 16px;
  position: sticky; top: 0; height: 100vh;
  overflow-y: auto;
  scrollbar-width: thin;
}
/* 16.52.0: the real Optimizor wordmark replaces the mark+text lockup. The
   logo IS the brand line, so the only text left under it is what the product
   is called — sky, tracked out, small enough to read as a suffix rather than
   as a second wordmark. A tenant's own logo (Settings → Branding) renders
   through the same slot with a height cap, because uploads arrive at every
   aspect ratio and a tall one would otherwise push the nav off-screen. */
.sidebar__brand {
  display: block;
  padding: 0 16px 16px;
}
.brand-logo { width: 148px; max-width: 100%; height: auto; display: block; }
.brand-logo--tenant { max-height: 54px; width: auto; max-width: 100%; object-fit: contain; }
/* 16.66.0 (⑧b §2): the no-logo tenant identity. Same disc-plus-name lockup
   the ⑧a My Company header uses, at sidebar scale and on the dark ground: the
   disc keeps the teal fill and white bold initials so one organisation reads
   the same in the header, the sidebar and the usercard. The name wraps to two
   lines at most and then truncates — a 250px rail is not the place to fight
   for a fourth line of a company name. */
.brand-ident { display: flex; align-items: center; gap: 10px; min-width: 0; }
.brand-ident__disc {
  flex: none; display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 9px;
  background: var(--teal); color: var(--white);
  font-family: var(--font-display);
  font-size: 14px; font-weight: 700; line-height: 1; letter-spacing: .01em;
}
.brand-ident__name {
  min-width: 0; color: var(--sidebar-text-active);
  font-family: var(--font-display);
  font-size: 15px; font-weight: 700; line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.brand-suffix {
  display: block; margin-top: 5px; color: var(--brand-sky);
  font-size: 10px; font-weight: 700; letter-spacing: 2.2px;
  text-transform: uppercase;
}
.sidebar__divider {
  height: 1px; background: var(--sidebar-hover); margin: 0 16px;
}
.sidebar__nav { padding: 0 12px; }
.navitem {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; margin: 1px 0; border-radius: 8px;
  color: var(--sidebar-text); font-size: 14px; font-weight: 500;
  transition: background 0.12s ease;
}
.navitem:hover {
  background: var(--sidebar-hover);
  color: var(--sidebar-text-active);
  text-decoration: none;
}
.navitem.is-active {
  background: var(--sidebar-selected);
  color: var(--sidebar-text-active);
  font-weight: 600;
}
.navitem__icon {
  width: 18px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  color: currentColor;
}
.navitem__icon svg { width: 18px; height: 18px; display: block; }
.navitem__pill {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 4px; height: 18px; border-radius: 2px; background: var(--sidebar-accent);
}
.sidebar__spacer { flex: 1; }
.sidebar__footer { padding: 8px 20px 0; }
.brand-version { color: var(--text-muted); font-size: 11px; margin-top: 2px; }
/* 16.66.0 (⑧b §2): shown on every page of a TENANT workspace, whose brand
   slot is the tenant's in all its states now. Outside one our mark is the
   brand and this line would only repeat it. */
.brand-poweredby {
  color: var(--sidebar-muted); font-size: 10px; letter-spacing: .02em;
  margin-bottom: 4px;
}

/* ── Main pane ──────────────────────────────────────────────────────── */
.main { padding: 24px 28px; min-width: 0; }

/* ── Page header (used by every page) ───────────────────────────────── */
.page-header {
  display: flex; justify-content: space-between; align-items: flex-start;
  margin-bottom: 18px; gap: 16px;
}
.page-header h1 {
  margin: 0; font-size: 26px; font-weight: 700; color: var(--navy);
}
.page-header .subtle {
  margin-top: 4px; color: var(--text-secondary); font-size: 13px;
}
/* 16.57.0 (⑦b §4): a page header standing in as a SECTION header - the
   16.53.0 merge left `_import_docs_body` wrapping its stepped-down h2 in
   the page-header it used to be a page for, and paying a page's 18px of
   clearance for it in the middle of Integrations. */
.page-header--sec { margin-bottom: 8px; }
/* Tenant identity line under the dashboard title — must be readable at a
   glance (MSP operators hop between tenant workspaces all day). */
.page-header .dash-tenant {
  margin-top: 6px; color: var(--navy); font-size: 19px; font-weight: 650;
  letter-spacing: 0.01em;
}

/* ── Buttons (match v15 palette) ────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 10px 18px; border-radius: 8px; border: none;
  font-weight: 600; font-size: 14px; cursor: pointer;
  text-decoration: none; transition: background 0.12s ease;
  font-family: inherit;
}
/* 2026-08-12: a disabled button must LOOK disabled. "Attach as evidence"
   is gated while added sections await review, but without this rule it
   rendered exactly like a clickable button and the only explanation was a
   hover tooltip - which read as "the button does nothing". */
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn--primary  { background: var(--teal); color: var(--white); }
.btn--primary:hover  { background: var(--teal-dark); text-decoration: none; }
.btn--navy     { background: var(--navy); color: var(--white); }
.btn--navy:hover     { background: #00164A; text-decoration: none; }
.btn--green    { background: var(--green-dark); color: var(--white); }
.btn--green:hover { background: #005B3B; text-decoration: none; }
.btn--secondary{ background: var(--light-gray); color: var(--text-primary); border: 1px solid var(--medium-gray); }
.btn--secondary:hover { background: var(--medium-gray); text-decoration: none; }
.btn--danger   { background: var(--white); color: var(--red); border: 1px solid var(--red); }
.btn--danger:hover { background: #FDE8E8; text-decoration: none; }
/* 16.54.0 (⑥c §B): the quietest button in the family — the "Scan now" link
   sits beside a page title and must not out-shout it. `is-running` is the
   same control saying the job is already in flight; it stays a link, so the
   colour is the only thing that changes. */
.btn--ghost    { background: transparent; color: var(--navy); border: 1px solid var(--medium-gray); }
.btn--ghost:hover { background: var(--light-gray); text-decoration: none; }
.btn--ghost.is-running { color: var(--tint-med-ink); border-color: var(--tint-med-ink); }
.btn--xs { padding: 4px 10px; font-size: 12px; }

/* ── 16.56.0 (feedback batch ⑦a §a): the ONE help affordance ─────────
   The diagnosis first, because the fix only makes sense next to it: the
   dashboard had no (?) marks AT ALL. Its help was smuggled into <abbr
   title> runs and bare title= attributes on the target bars, so the only
   affordance was a 1px dotted underline beneath 12px grey text and the
   payoff was the browser's native tooltip - roughly a second of stationary
   hover, no pointer feedback, gone again on the smallest mouse move. Worse,
   the one sentence that actually explained the headline score sat inside
   the score-READY branch of card 1, so a tenant under 20% coverage (which
   is every new tenant, and every demo) got no help on that card whatsoever.
   "I hover the (?) and nothing happens" was therefore literally true.
   The replacement is ONE mechanism used by every (?) on the page: a
   focusable `.qhelp` mark carrying its sentence in BOTH `data-help` (the
   tooltip) and `aria-label` (the accessible name), revealed with no delay
   on hover AND on keyboard focus. The visual language is the maturity
   ladder's `data-tip` - the one tooltip on this page that already worked.
   `data-help` is deliberately a plain attribute hook rather than a class:
   any element can explain itself, the mark is just the commonest carrier. */
[data-help] { position: relative; }
[data-help]::after {
  content: attr(data-help);
  position: absolute; left: 50%; bottom: calc(100% + 8px);
  transform: translateX(-50%);
  width: max-content; max-width: 260px; z-index: 60;
  background: var(--navy); color: #fff;
  font-size: 11px; line-height: 1.45; font-weight: 500;
  text-align: left; white-space: normal;
  /* The marks live inside labels that shout - .kpi-label is uppercase with
     tracking - and a pseudo-element inherits all three. A 150-character
     sentence set in capitals is not help. */
  text-transform: none; letter-spacing: 0; font-style: normal;
  padding: 9px 11px; border-radius: 8px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .20);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .1s ease;
}
[data-help]:hover::after,
[data-help]:focus::after,
[data-help]:focus-within::after { opacity: 1; visibility: visible; }
/* A mark sitting at a column's right edge would push its centred tooltip
   into the next column; anchor those to their own right edge instead. */
[data-help].qhelp--end::after { left: auto; right: -4px; transform: none; }
.qhelp {
  display: inline-grid; place-items: center; flex: 0 0 auto;
  width: 15px; height: 15px; border-radius: 50%;
  border: 1px solid var(--medium-gray); background: var(--white);
  color: var(--text-muted);
  font-size: 10px; font-weight: 800; line-height: 1;
  vertical-align: middle; cursor: help; user-select: none;
}
.qhelp:hover, .qhelp:focus { border-color: var(--teal); color: var(--teal); }
.qhelp:focus { outline: 2px solid var(--teal); outline-offset: 1px; }

/* ── Cards / sections ───────────────────────────────────────────────── */
.card {
  background: var(--white); border-radius: 10px;
  border: 1px solid var(--medium-gray);
  padding: 20px;
}
.card h3 { margin: 0 0 10px; color: var(--navy); font-size: 14px; font-weight: 700; }

/* ── Stat cards (Dashboard) ─────────────────────────────────────────── */
.stats {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;
  margin-bottom: 18px;
}
.stat {
  background: var(--white); border-radius: 10px;
  border: 1px solid var(--medium-gray); padding: 16px 18px;
  display: flex; align-items: center; gap: 14px;
}

/* ── Section header ─────────────────────────────────────────────────── */
/* 16.57.0 (⑦b §4): 22px of air above a heading is a page break, not a
   section break. On a merged page like Integrations it was paid three
   times, once per section. */
.section-title {
  font-size: 16px; font-weight: 700; color: var(--navy);
  margin: 14px 0 8px;
}

/* ── Measure grid (Dashboard) ───────────────────────────────────────── */
.measures {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;
}
.measure {
  background: var(--white); border-radius: 10px;
  border: 1px solid var(--medium-gray); padding: 12px;
}

/* ── Forms (Connections, Settings) ──────────────────────────────────── */
.form-row { display: flex; flex-direction: column; gap: 4px; margin-bottom: 14px; }
.form-row > label { font-size: 12px; font-weight: 600; color: var(--text-secondary); }
.form-row input, .form-row textarea, .form-row select {
  padding: 9px 12px; border: 1px solid var(--medium-gray); border-radius: 6px;
  font-family: inherit; font-size: 14px; background: var(--white);
}
.form-row input:focus, .form-row textarea:focus, .form-row select:focus {
  outline: none; border-color: var(--teal);
}
/* 16.65.0 (⑧a §C) — Settings, "Minimum required maturity". A checkbox and its
   label are ONE line: row direction, left aligned, and the box keeps its
   natural size instead of stretching to the .form-row input width. The
   override row underneath starts at the same left edge. Named classes, not
   inline styles, so the next reader can see what they do. */
.set-checkline {
  flex-direction: row; align-items: center; justify-content: flex-start;
  gap: 10px; font-size: 12.5px;
}
.set-checkline > input[type="checkbox"] { flex: none; width: auto; padding: 0; margin: 0; }
.set-overrideline { display: flex; align-items: center; gap: 10px; text-align: left; }
.set-overrideline > input { width: 76px; flex: none; }
.set-overrideline > .btn { margin-left: auto; }

.help { color: var(--text-muted); font-size: 12px; }

/* ── Tables ─────────────────────────────────────────────────────────── */
.table {
  width: 100%; border-collapse: collapse;
  background: var(--white); border-radius: 10px; overflow: hidden;
  border: 1px solid var(--medium-gray);
}
.table th {
  background: var(--navy); color: var(--white);
  text-align: left; padding: 10px 14px; font-size: 12px; font-weight: 600;
}
.table td {
  padding: 10px 14px; font-size: 13px;
  border-top: 1px solid var(--medium-gray);
}
.table tr:hover td { background: var(--off-white); }

/* ── Severity / status pills (16.52.0, skin spec §E) ──────────────────
   THE label component. Solid saturated pills turned every table into a
   traffic light; these are a tint of the hue with the same hue as ink, so a
   column of them stays scannable and the one that matters still stands out.
   Sentence case on purpose — the uppercase was shouting, not signalling. */
.pill {
  display: inline-block; padding: 2.5px 10px; border-radius: 6px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
  background: var(--tint-mute-bg); color: var(--tint-mute-ink);
}
.pill.critical { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.pill.high     { background: var(--tint-high-bg); color: var(--tint-high-ink); }
.pill.medium   { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.pill.low      { background: var(--tint-sky-bg);  color: var(--tint-sky-ink); }
.pill.info     { background: var(--tint-navy-bg); color: var(--tint-navy-ink); }
.pill.compliant      { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.pill.partial        { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.pill.noncompliant   { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.pill.notassessed    { background: var(--tint-mute-bg); color: var(--tint-mute-ink); }

/* ── CyFun controls tree (existing) ─────────────────────────────────── */
.function-tabs { display: flex; gap: 6px; margin-bottom: 14px; flex-wrap: wrap; }
.function-tab {
  padding: 8px 18px; border-radius: 8px; background: var(--white);
  color: var(--text-primary); border: 1px solid var(--medium-gray);
  font-size: 13px; font-weight: 600;
}
.function-tab.is-active {
  background: var(--accent, var(--teal));
  color: var(--white); border-color: var(--accent, var(--teal));
}
.controls__layout { display: grid; grid-template-columns: 1fr 320px; gap: 14px; }
.tree { background: var(--white); border-radius: 10px; padding: 12px; border: 1px solid var(--medium-gray); }
.cat { margin-bottom: 4px; }
.cat > summary {
  cursor: pointer; padding: 8px; display: flex; align-items: center;
  gap: 8px; font-weight: 700; color: var(--navy);
  list-style: none; text-align: left;
}
.cat > summary::-webkit-details-marker { display: none; }
.cat > summary::before { content: "▸"; color: var(--text-secondary); flex: 0 0 auto; }
.cat[open] > summary::before { content: "▾"; }
.sub { margin-left: 16px; padding: 2px 0; }
.sub > summary {
  cursor: pointer; padding: 6px 8px; display: flex; align-items: center;
  gap: 8px; font-size: 13px; font-weight: 600;
  color: var(--text-primary); list-style: none; text-align: left;
}
.sub > summary::-webkit-details-marker { display: none; }
.sub > summary::before { content: "▸"; color: var(--text-muted); font-size: 11px; flex: 0 0 auto; }
.sub[open] > summary::before { content: "▾"; }
.cat__name, .sub__name { flex: 1; min-width: 0; text-align: left; }
.cat > summary .badges, .sub > summary .badges { margin-left: auto; flex: 0 0 auto; }
.reqs { list-style: none; padding: 0; margin: 4px 0 4px 22px; }
.req { margin: 2px 0; }
.req__link {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: 6px; color: var(--text-primary);
  font-size: 12px;
}
.req__link:hover { background: var(--off-white); text-decoration: none; }
.req__id { font-weight: 700; color: var(--navy); min-width: 84px; }
.req__title { flex: 1; }
/* The key-measure badge, one shape for the whole product: the controls tree,
   the requirement header, the findings table, the register. It was a teal
   rectangle that read as a fourth score colour beside the D/I badges; it is
   the gold pill now, pill-shaped and outlined so it cannot be mistaken for
   the square .req__assurance profile chip sitting next to it. */
.req__key, .keybadge {
  display: inline-flex; align-items: center; gap: 3px;
  background: var(--key-bg); color: var(--key-ink);
  border: 1px solid var(--key-line);
  font-size: 10px; font-weight: 700; letter-spacing: 0.03em;
  padding: 1px 7px; border-radius: 999px; white-space: nowrap;
  flex: 0 0 auto;
}
.badges { display: inline-flex; gap: 4px; }
/* 16.52.0: badges follow the same tinted label system as .pill (skin spec
   §E) so the console tables and the CyFun tree read as one product. */
.badge {
  display: inline-block; padding: 2.5px 8px; border-radius: 6px;
  background: var(--tint-mute-bg); color: var(--tint-mute-ink);
  font-size: 11.5px; font-weight: 600; min-width: 36px;
  text-align: center;
}
.badge.green  { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.badge.amber  { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.badge.red    { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.badge.muted  { background: var(--tint-mute-bg); color: var(--tint-mute-ink); }
.badge.dim    { background: var(--off-white); color: var(--text-secondary); border: 1px solid var(--medium-gray); }
/* 16.1.12+: BEM-style badge modifiers used by the SaaS console/portfolio
   tables. Without these the base .badge (white text, no background) rendered
   invisibly — Status / Result columns looked blank. */
.badge--ok      { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.badge--warn    { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.badge--neutral { background: var(--tint-mute-bg); color: var(--tint-mute-ink); }

/* ---------------------------------------------------------------------------
   Mini-RACI control ownership (16.11.0): five fixed roles, each a colour.
   Any element carrying an own-<role> class exposes --role for its chips.
   --------------------------------------------------------------------------- */
.own-management { --role: #4f46e5; }  /* indigo  */
.own-it_ops     { --role: #0284c7; }  /* blue    */
.own-security   { --role: #0d9488; }  /* teal    */
.own-compliance { --role: #b45309; }  /* amber   */
.own-hr         { --role: #15803d; }  /* green   */

.own-chips { display: inline-flex; gap: 4px; align-items: center; }
.own-chip {
  display: inline-flex; align-items: center; white-space: nowrap;
  font-size: 10px; font-weight: 700; line-height: 1.6;
  padding: 0 7px; border-radius: 10px; border: 1px solid transparent;
}
.own-chip--a { background: var(--role, #64748b); color: #fff; }
.own-chip--r {
  background: #f4f6f9;                                   /* fallback */
  background: color-mix(in srgb, var(--role) 12%, #fff);
  color: var(--role, #475569);
  border-color: var(--role, #cbd5e1);                   /* fallback */
  border-color: color-mix(in srgb, var(--role) 45%, #fff);
}

/* Control-list toolbar: owner filter (left) + expand/collapse (right) */
.tree-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 0 0 12px;
}
.tree-expand { display: flex; gap: 6px; align-items: center; }
.control-search {
  font-size: 12px; padding: 4px 10px; border-radius: 14px;
  border: 1px solid var(--medium-gray); background: var(--white);
  color: var(--text-primary); min-width: 180px;
}
.control-search:focus { outline: none; border-color: var(--navy); }
/* While a filter is active, hide the category/sub rollup badges (they
   summarise all controls, not the filtered subset). Leaf badges stay. */
.tree.tree--filtered details > summary .badges { display: none; }
.tree-empty {
  margin: 18px 8px; padding: 16px; text-align: center;
  color: var(--text-secondary); font-size: 13px;
  background: var(--off-white); border-radius: 8px;
}
/* Owner filter bar on the control list */
.owner-filter {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin: 0;
}
.owner-filter__label {
  font-size: 12px; font-weight: 700; color: var(--text-secondary);
  margin-right: 2px;
}
.owner-filter__btn {
  font-size: 11px; font-weight: 700; padding: 3px 11px; border-radius: 14px;
  border: 1px solid var(--medium-gray); background: var(--white);
  color: var(--text-secondary); cursor: pointer; transition: all .12s;
}
.owner-filter__btn[class*="own-"] { color: var(--role); border-color: var(--role); }
.owner-filter__btn.is-active { background: var(--navy); color: #fff; border-color: var(--navy); }
/* The key-measure filter chip wears the badge's gold in every chip idiom the
   product has (.owner-filter__btn on Controls, .rz-fchip on Findings,
   .rr-st on the register) so the chip and the rows it selects are visibly
   the same thing. */
.rz-gopt--key.is-on { background: var(--key-ink); color: #fff; border-color: var(--key-ink); }
.owner-filter__btn--key, .rz-fchip--key, .rr-st--key {
  color: var(--key-ink); border-color: var(--key-line); background: var(--key-bg);
}
.owner-filter__btn--key.is-active, .rz-fchip--key.is-on, .rr-st--key.is-on,
.rr-st--key[aria-pressed="true"] {
  background: var(--key-ink); color: #fff; border-color: var(--key-ink);
}
.owner-filter__btn[class*="own-"].is-active { background: var(--role); color: #fff; border-color: var(--role); }

/* Ownership badge, top-right of the control detail header */
.detail__header { position: relative; }
.ownership-badge {
  position: absolute; top: 16px; right: 18px;
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  justify-content: flex-end; max-width: 55%;
}
.ownership-badge__label {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-secondary); margin-right: 2px;
}
.ownership-badge .own-chip { font-size: 11px; padding: 1px 9px; }
@media (max-width: 640px) {
  .ownership-badge { position: static; max-width: none; justify-content: flex-start; margin-top: 6px; }
}

/* Info callout (16.11.6) */
.callout {
  margin: 10px 0 0; padding: 9px 13px; border-radius: 6px;
  font-size: 13px; line-height: 1.5;
}
.callout--info {
  background: #eef4fb; border: 1px solid #cfe0f2; color: #24435f;
}
.callout--info a { font-weight: 700; }

/* Section divider between merged page sections (16.11.3).
   16.57.0 (⑦b §4): the rule already says "new section"; 50px of combined
   margin was saying it a second time. */
.section-rule {
  border: 0; border-top: 1px solid var(--medium-gray);
  margin: 16px 0 12px;
}

/* ── Document Workspace (16.12.0) ─────────────────────────────── */
.wsx-top{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:0 0 14px;
  padding-bottom:12px;border-bottom:1px solid var(--medium-gray)}
.wsx-back{color:var(--teal-dark);font-weight:700;font-size:12px;text-decoration:none;white-space:nowrap}
.wsx-h1{font-size:16.5px;margin:0}
.wsx-vchip{font-size:10.5px;font-weight:700;color:var(--text-secondary);border:1px solid var(--medium-gray);
  border-radius:4px;padding:1px 7px}
.wsx-vchip.is-gen{color:var(--green);border-color:var(--green)}
.wsx-vchip.is-ver{color:var(--navy);border-color:var(--navy);background:var(--off-white);font-variant-numeric:tabular-nums}
.wsx-chips{display:flex;gap:3px;align-items:center}
.wsx-chips .mchip{display:inline-grid;place-items:center;width:16px;height:16px;border-radius:5px;
  font-size:9.5px;font-weight:800;color:#fff;background:var(--navy);font-family:ui-monospace,Menlo,monospace}
.wsx-chips .fchip{display:inline-grid;place-items:center;height:16px;min-width:22px;padding:0 5px;
  border-radius:5px;font-size:9px;font-weight:800;color:#fff}
.wsx-grow{flex:1}
/* 17.4.4 rider (feedback #6, screenshot-confirmed). The header is a wrapping
   flex row; with a long title it wrapped BETWEEN the action buttons, so
   "Attach as evidence" ended up alone on the bottom LEFT while the rest of
   the cluster sat top right. The actions are now one flex item, so they wrap
   together, and `margin-left:auto` keeps that item on the right edge of
   whichever line it lands on. On a short title .wsx-grow still eats the slack
   and the auto margin is a no-op, so the desktop row is unchanged. */
.wsx-acts{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  justify-content:flex-end;margin-left:auto}

.wsx-grid{display:grid;grid-template-columns:258px minmax(0,1fr) 316px;gap:14px;align-items:start}
.wsx-panel{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px}
.wsx-rail{display:flex;flex-direction:column;gap:12px}
.wsx-panel h3{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--text-secondary);
  margin:0;padding:10px 13px;border-bottom:1px solid var(--medium-gray);display:flex;align-items:center;gap:7px}
.wsx-panel h3 .n{margin-left:auto;font-size:10px;background:var(--light-gray);border-radius:10px;padding:1px 7px;
  color:var(--text-secondary);font-weight:600;letter-spacing:0;text-transform:none}
.wsx-empty{padding:12px 13px;color:var(--text-muted);font-size:11.5px}

.wsx-ctrls{padding:7px 9px;display:flex;flex-direction:column;gap:6px}

/* 16.28.2: mapped-controls rail — grouped human labels, one status pill.
   "Confirm by a person" is open by default, "Verified automatically" folds. */
.wsx-cgroup{border:1px solid var(--light-gray);border-radius:8px;background:var(--off-white)}
.wsx-cgroup>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:7px;
  padding:6px 9px;font-size:11px;font-weight:700;color:var(--navy)}
.wsx-cgroup>summary::-webkit-details-marker{display:none}
.wsx-cgroup>summary::after{content:"›";margin-left:2px;color:var(--text-muted);font-weight:700;
  transition:transform .12s;transform:rotate(90deg)}
.wsx-cgroup:not([open])>summary::after{transform:rotate(0deg)}
.wsx-cgroup>summary:hover{background:var(--light-gray);border-radius:8px}
.wsx-cgroup>summary .gl{flex:1;min-width:0}
.wsx-cgroup>summary .n{font-size:10px;background:var(--white);border:1px solid var(--medium-gray);
  border-radius:10px;padding:0 7px;color:var(--text-secondary);font-weight:700}
.wsx-cgroup .vdot{width:7px;height:7px;border-radius:50%;flex:none}
.wsx-cgroup.is-auto .vdot{background:var(--green)}
.wsx-cgroup.is-manual .vdot{background:var(--amber)}
.wsx-cglist{display:flex;flex-direction:column;gap:2px;padding:2px 5px 6px}
.wsx-cg{display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;padding:5px 6px;
  border-radius:6px;background:var(--white);border:1px solid var(--light-gray)}
.wsx-cg .lbl{flex:1 1 100%;min-width:0;font-size:11.5px;line-height:1.35;color:var(--navy);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.wsx-cg .ids{font-size:9.5px;color:var(--text-muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wsx-cg .ids a{color:var(--text-muted);text-decoration:none}
.wsx-cg .ids a:hover{color:var(--teal-dark);text-decoration:underline}
.wsx-cpill{font-size:9.5px;font-weight:800;border-radius:9px;padding:1px 7px;white-space:nowrap}
.wsx-cpill.st-on-track{background:#E7F6EC;color:#1b7f4b}
.wsx-cpill.st-needs-practice,.wsx-cpill.st-needs-documentation{background:#FDF3D6;color:#8a6d00}
.wsx-cjump{font-size:10px;font-weight:700;color:var(--teal-dark);text-decoration:none;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wsx-cjump:hover{text-decoration:underline}
.ws-verify.is-jumped{box-shadow:0 0 0 3px rgba(255,193,7,.45)}

.wsx-updrop{display:block;margin:10px;border:1.5px dashed var(--medium-gray);border-radius:8px;padding:12px;
  text-align:center;color:var(--text-muted);font-size:11px;cursor:pointer;transition:border-color .12s,background .12s}
.wsx-updrop:hover,.wsx-updrop.is-over{border-color:var(--teal);background:var(--off-white)}
.wsx-updrop b{display:block;color:var(--navy);font-size:12px;margin-bottom:2px}
/* 17.52.1: the drop zone SAYS it is working - upload + analysis take real
   seconds, and a box that swallows a drop without flinching reads as
   "nothing happened" (first field report from Test). */
.wsx-updrop.is-busy{border-color:var(--teal);background:var(--off-white);cursor:progress}
.wsx-updrop.is-busy b::after{content:"";display:inline-block;width:11px;height:11px;
  margin-left:8px;vertical-align:-2px;border:2px solid var(--teal);
  border-top-color:transparent;border-radius:50%;animation:wsxspin .8s linear infinite}
@keyframes wsxspin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion: reduce){.wsx-updrop.is-busy b::after{animation:none}}
.wsx-file{display:flex;gap:9px;align-items:center;padding:8px 13px;border-top:1px solid var(--light-gray);font-size:11.5px}
.wsx-file .ic{flex:none;width:26px;height:30px;border:1px solid var(--medium-gray);border-radius:4px;
  display:grid;place-items:center;font-size:8px;font-weight:800;color:var(--text-secondary);background:var(--off-white)}
/* 16.28.2: min-width:0 all the way down the flex chain, else a long filename
   pushes the tile wider instead of ellipsizing (full name in the title). */
.wsx-file .meta{min-width:0;flex:1}
.wsx-file .name{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;min-width:0}
.wsx-file small{color:var(--text-secondary);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 16.60.0: a source stored before the content channel existed. It still fills
   fields, but it grounds nothing - the hint wraps, it is the actionable bit. */
.wsx-file small.wsx-srcold{white-space:normal;font-style:italic;opacity:.85;margin-top:2px}
/* 17.5.0 - the sweep refreshed this source's text, and says so. */
.wsx-file small.wsx-srcnew{white-space:normal;color:var(--teal);margin-top:2px}
/* 16.70.0 "What we read": the fact sheet under a source. Collapsed by default
   and top-aligned when present, so the DOC tile stays beside the filename
   instead of drifting to the middle of an open sheet. */
.wsx-file.is-sf{align-items:flex-start}
.wsx-sf{margin-top:5px}
.wsx-sf>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:6px;
  font-size:10.5px;font-weight:700;color:var(--teal-dark)}
.wsx-sf>summary::-webkit-details-marker{display:none}
.wsx-sf>summary::after{content:"›";color:var(--text-muted);transition:transform .12s}
.wsx-sf[open]>summary::after{transform:rotate(90deg)}
.wsx-sf>summary .n{font-size:9.5px;background:var(--light-gray);border-radius:9px;padding:0 6px;
  color:var(--text-secondary);font-weight:700}
.wsx-sf__g{margin:6px 0 0}
.wsx-sf__g>b{display:block;font-size:9.5px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--text-muted);margin-bottom:3px}
.wsx-sf__i{display:flex;align-items:flex-start;gap:5px;padding:3px 0;border-top:1px solid var(--light-gray)}
.wsx-sf__i .tx{flex:1;min-width:0;font-size:11px;line-height:1.4;color:var(--navy)}
.wsx-sf__i .tx em{display:block;font-style:italic;color:var(--text-secondary);font-size:10.5px}
.wsx-sf__i .tx em::before{content:"\201C"}.wsx-sf__i .tx em::after{content:"\201D"}
.wsx-sf__x{flex:none;border:0;background:none;cursor:pointer;color:var(--text-muted);
  font-size:11px;line-height:1;padding:2px 3px;border-radius:4px}
.wsx-sf__x:hover{color:var(--red);background:var(--light-gray)}
.wsx-sf__x[disabled]{opacity:.4;cursor:default}
.wsx-fact{display:flex;align-items:flex-start;gap:8px;padding:8px 13px;border-top:1px solid var(--light-gray);font-size:11.5px}
.wsx-fact:first-of-type{border-top:0}
.wsx-fact .dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--teal);margin-top:5px}
.wsx-fact .t b{display:block;color:var(--navy)}.wsx-fact .t small{color:var(--text-secondary)}

.wsx-center{display:flex;flex-direction:column;gap:10px;min-width:0}
/* 16.29.0 guided draft: an inline card above the preview (never a modal), and
   the slim reopen-chip it collapses to once the document has been drafted. */
.wsx-wiz{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px;padding:14px 16px}
.wsx-wiz__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;margin-bottom:9px}
.wsx-wiz__head b{color:var(--navy);font-size:14px}
.wsx-wiz__head span{color:var(--text-secondary);font-size:11.5px}
.wsx-wiz__ramp{display:flex;flex-wrap:wrap;gap:4px 8px;align-items:baseline;font-size:11.5px;
  background:var(--off-white);border-radius:8px;padding:8px 11px;margin-bottom:12px}
.wsx-wiz__ramp b{color:var(--navy)}
.wsx-wiz__ramp a{color:var(--teal-dark);text-decoration:none}
.wsx-wiz__ramp a:hover{text-decoration:underline}
.wsx-wiz__qs{display:flex;flex-direction:column;gap:11px}
.wsx-wq{display:flex;flex-direction:column;gap:5px}
.wsx-wq__p{font-size:12px;font-weight:600;color:var(--navy);line-height:1.35}
.wsx-wq__chips{display:flex;flex-wrap:wrap;gap:5px}
.wsx-wchip{font-size:10.5px;font-weight:700;border-radius:12px;padding:3px 10px;cursor:pointer;
  border:1px solid var(--medium-gray);background:var(--white);color:var(--text-secondary)}
.wsx-wchip:hover{border-color:var(--teal);color:var(--teal-dark)}
.wsx-wchip.is-on{border-color:var(--teal);background:var(--teal);color:var(--white)}
.wsx-wq__in{width:100%;font:inherit;font-size:12px;color:var(--navy);background:var(--white);
  border:1px solid var(--medium-gray);border-radius:7px;padding:6px 9px;resize:vertical}
.wsx-wq__in:focus{outline:none;border-color:var(--teal)}
.wsx-wiz__acts{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:13px}
.wsx-wiz__skip{font-size:11px;border:0;background:none;color:var(--text-muted);cursor:pointer;text-decoration:underline}
.wsx-wiz__skip:hover{color:var(--navy)}
.wsx-wiz__msg{font-size:11px;color:var(--text-secondary)}
.wsx-wizchip{align-self:flex-start;font-size:11px;font-weight:700;text-decoration:none;
  border:1px solid var(--medium-gray);border-radius:12px;padding:4px 12px;
  background:var(--white);color:var(--text-secondary)}
.wsx-wizchip:hover{border-color:var(--teal);color:var(--teal-dark)}
/* 16.31.0 template upgrade: a non-blocking banner above the preview and the
   dry-run panel it opens (the chat's diff cards, display-only). */
.wsx-upg{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;background:#F4F9FC;
  border:1px solid var(--teal);border-left:3px solid var(--teal);border-radius:10px;padding:10px 14px}
.wsx-upg__t{flex:1;min-width:240px;display:flex;flex-direction:column;gap:2px}
.wsx-upg__t b{color:var(--navy);font-size:12.5px}
.wsx-upg__t span{color:var(--text-secondary);font-size:11.5px}
.wsx-upg__later{font-size:11px;border:0;background:none;color:var(--text-muted);cursor:pointer;text-decoration:underline}
.wsx-upg__later:hover{color:var(--navy)}
.wsx-upg-panel{display:flex;flex-direction:column;gap:8px;background:var(--white);
  border:1px solid var(--medium-gray);border-radius:10px;padding:12px 14px;font-size:12px}
.wsx-upgcard{border-color:var(--medium-gray);border-left-color:var(--medium-gray)}
.wsx-upgcard.is-keep_edit{border-color:#8a6d00;border-left-color:#8a6d00}
.wsx-upgcard.is-new{border-color:var(--green);border-left-color:var(--green)}
.wsx-upgcard.is-drop{border-color:var(--red);border-left-color:var(--red)}
.wsx-upg-keep{font-size:11px;color:var(--text-secondary);background:var(--off-white);
  border-radius:8px;padding:7px 11px;line-height:1.45}
.wsx-upg-acts{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:4px}
.wsx-upg-acts .wsx-drift-close{margin-top:0}
.wsx-prevbar{display:flex;align-items:center;gap:10px;background:var(--white);border:1px solid var(--medium-gray);
  border-radius:10px;padding:8px 13px;font-size:11.5px;color:var(--text-secondary)}
.wsx-prevnote{color:var(--text-muted)}
.wsx-prevgrow{flex:1}
.wsx-edited{font-size:10px;font-weight:800;color:#8a6d00;background:#FDF3D6;border-radius:8px;padding:1px 8px}
.wsx-drift{font-size:11px;font-weight:700;border-radius:7px;padding:5px 11px;cursor:pointer;
  border:1px solid var(--medium-gray);background:var(--white);color:var(--navy);white-space:nowrap}
.wsx-drift:hover{border-color:var(--teal);color:var(--teal-dark)}
/* drift-check result panel */
.wsx-drift-panel{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px;padding:12px 14px;
  margin-top:8px;font-size:12px}
.wsx-drift-head{display:flex;align-items:baseline;gap:9px;margin-bottom:8px}
.wsx-drift-badge{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;border-radius:8px;
  padding:2px 9px;white-space:nowrap}
.drift-low .wsx-drift-badge{background:#E7F6EC;color:#1b7f4b}
.drift-medium .wsx-drift-badge{background:#FDF3D6;color:#8a6d00}
.drift-high .wsx-drift-badge{background:#FCE4E4;color:var(--red)}
.wsx-drift-sum{color:var(--navy)}
.wsx-drift-meta{font-size:10.5px;color:var(--text-muted);margin-bottom:6px}
.wsx-drift-issue{display:flex;gap:8px;padding:6px 0;border-top:1px solid var(--light-gray)}
.wsx-drift-issue .k{font-size:9px;font-weight:800;text-transform:uppercase;color:#8a6d00;background:#FDF3D6;
  border-radius:6px;padding:2px 7px;height:fit-content;white-space:nowrap}
.wsx-drift-issue .d{color:var(--text-secondary);line-height:1.45}
.wsx-drift-close{margin-top:10px;font-size:11px;font-weight:700;border:1px solid var(--medium-gray);
  background:var(--white);border-radius:7px;padding:4px 12px;cursor:pointer;color:var(--text-secondary)}
.wsx-diff--body .old,.wsx-diff--body .add{white-space:pre-wrap;display:block;width:100%}
.wsx-edit.is-body{border-color:#8a6d00;border-left-color:#8a6d00}
/* 16.27.0: document decoration — edit highlight (yellow) + verify box (amber) */
.wsx-page .ws-hl{background:#fff7cc;box-shadow:inset 3px 0 0 #e6c200;border-radius:3px;padding:2px 8px;margin:2px -8px}
.wsx-page .ws-verify{border-left:3px solid var(--amber);background:#fff8ec;border-radius:0 6px 6px 0;
  padding:6px 12px;margin:8px 0}
.wsx-page .ws-verify__tag{font-size:10.5px;font-weight:800;color:#8a6d00;text-transform:uppercase;
  letter-spacing:.03em;margin-bottom:4px}
.wsx-page .ws-verify__tag .ids{font-weight:600;color:var(--text-muted);text-transform:none;letter-spacing:0}
.wsx-page .ws-hl .ws-verify{margin:6px 0}

/* 16.42.0: AI-liability notice - persistent, not dismissable (no close). */
.wsx-aidisc{display:flex;align-items:flex-start;gap:8px;margin:0 0 8px;padding:8px 12px;
  border:1px solid #f0d9a0;border-left:3px solid var(--amber);background:#fff8ec;border-radius:0 8px 8px 0;
  font-size:11.5px;line-height:1.5;color:#7a5c00}
.wsx-aidisc svg{flex:none;margin-top:2px;color:#b08400}

/* 17.4.1: the adopted-document note. Green because it is a good state (the
   customer's own words are the document), and it sits directly above the
   AI-liability notice because the two answer the same question in order:
   whose words are these, and who is responsible for them. */
.wsx-adopted{margin:0 0 8px;padding:8px 12px;border:1px solid #C6E9D5;
  border-left:3px solid #1E7A46;background:#F3FAF5;border-radius:0 8px 8px 0;
  font-size:11.5px;line-height:1.5;color:#1E5B38}
.wsx-adopted b{display:block;font-size:12px;color:#1E7A46}
/* 17.4.2: the one thing inside a good state that is not good news. Colour and
   weight only - it lives in the note that is already there, so it adds no
   layout on any screen. */
.wsx-adopted__warn{display:block;margin-top:4px;color:#8A1C1C;font-weight:600}
/* 17.4.5: the severed source. NOT a warning - nothing is wrong with this
   document and nothing about it changed - so it is the note's own colour, one
   shade quieter, and it adds no layout on any screen for the same reason. */
.wsx-adopted__note{display:block;margin-top:4px;opacity:.85}
/* 17.4.4: the replace question and the failure notice, asked in the editor
   when the door was chosen there. Deliberately the evidence library's own
   amber, because it is literally the same question in a second place; it
   wraps for the same reason .slotmap__confirm does. */
.wsx-replace{margin:0 0 8px;padding:8px 10px;border-radius:6px;
  background:#FDF6E3;border:1px solid #F0D98A;
  display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.wsx-replace p{margin:0;font-size:12.5px;color:#6b5400}
.wsx-replace form{margin:0}

/* 17.4.3 review mode. A note like the two above it and in the same column,
   so an adopted document gains a banner and not a layout. Colour only on
   every phone rule this page has. */
.wsx-review{margin:0 0 8px;padding:8px 12px;border:1px solid #C7E5EA;
  border-left:3px solid var(--teal);background:#F2FAFB;border-radius:0 8px 8px 0;
  font-size:11.5px;line-height:1.6;color:#0F5A66}
.wsx-review__head{display:block;font-weight:600}
.wsx-review__src{margin-left:6px;font-weight:700}
.wsx-review__jump{display:block;margin-top:3px}
.wsx-review__to{margin-left:6px;text-decoration:underline}
/* an addition somebody has already read: quiet, struck through, still a link
   (the point of the list is that you can go back to any of them). */
.wsx-review__to.is-done{color:var(--text-muted);text-decoration:line-through}
.wsx-review__gate{display:block;margin-top:4px;color:#8A1C1C;font-weight:600}
/* the inline Accept, on the section itself. Sits in the SAME corner the
   16.42.0 pencil does, one step left of it, and is always visible: a control
   that only appears on hover cannot be a gate. */
.ws-secaccept{position:absolute;top:2px;right:24px;z-index:2;cursor:pointer;
  font-size:10.5px;font-weight:700;line-height:1;padding:5px 9px;border-radius:6px;
  border:1px solid #C6E9D5;background:#E9F7EF;color:#1E7A46}
.ws-secaccept:hover{background:#DBF1E5;border-color:#1E7A46}
.ws-secaccept[disabled]{opacity:.5;cursor:default}

/* 17.4.3 drift. Amber, because the document is fine and the world moved. */
.wsx-srcchg{margin:0 0 8px;padding:8px 12px;border:1px solid #f0d9a0;
  border-left:3px solid var(--amber);background:#fff8ec;border-radius:0 8px 8px 0;
  font-size:11.5px;line-height:1.5;color:#7a5c00}
.wsx-srcchg b{display:block;font-size:12px;color:#8a6d00}
.wsx-srcchg a{margin-left:6px;font-weight:700}

/* 16.42.0: per-section wrapper + hover pencil + inline raw-markdown editor */
.wsx-page .ws-sec{position:relative}
.wsx-page .ws-sec.is-jumped{box-shadow:0 0 0 3px rgba(255,193,7,.45);border-radius:4px}
.ws-secedit{position:absolute;top:2px;right:-6px;z-index:2;opacity:0;pointer-events:none;
  display:grid;place-items:center;width:24px;height:24px;border-radius:6px;cursor:pointer;
  border:1px solid var(--medium-gray);background:var(--white);color:var(--text-secondary);
  transition:opacity .12s,color .12s,border-color .12s}
.wsx-page .ws-sec:hover>.ws-secedit,.ws-secedit:focus-visible{opacity:1;pointer-events:auto}
.ws-secedit:hover{color:var(--teal-dark);border-color:var(--teal)}
.ws-seced{border:1px solid var(--teal);border-radius:8px;padding:10px 12px;margin:8px 0;background:var(--off-white)}
.ws-seced__h{font-size:12.5px;font-weight:700;color:var(--navy);margin-bottom:6px}
.ws-seced__in{width:100%;box-sizing:border-box;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:11.5px;line-height:1.55;color:#33415e;background:var(--white);border:1px solid var(--medium-gray);
  border-radius:6px;padding:8px 10px;resize:vertical}
.ws-seced__hint{font-size:10.5px;color:var(--text-muted);margin-top:5px}
.ws-seced__acts{display:flex;align-items:center;gap:8px;margin-top:8px}
.ws-seced__save,.ws-seced__cancel{font-size:11px;font-weight:700;border-radius:7px;padding:5px 13px;cursor:pointer;
  border:1px solid var(--medium-gray);background:var(--white);color:var(--navy)}
.ws-seced__save{background:var(--teal);border-color:var(--teal);color:#fff}
.ws-seced__save:disabled{opacity:.6;cursor:default}
.ws-seced__cancel{color:var(--text-secondary)}
.ws-seced__msg{font-size:11px;color:var(--red)}
.wsx-edit.is-badcard{color:var(--text-secondary);font-size:11.5px}

.wsx-page{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px;
  padding:30px 40px;box-shadow:0 2px 10px -6px #16233b22;font-size:12.5px;color:#33415e;line-height:1.6}
.wsx-page h1{font-size:19px;color:var(--navy);margin:0 0 12px;border-left:4px solid var(--navy);padding-left:12px}
.wsx-page h2{font-size:14.5px;color:var(--navy);margin:20px 0 6px}
.wsx-page h3{font-size:13px;color:var(--navy);margin:14px 0 5px}
.wsx-page h4{font-size:12px;color:var(--text-secondary);margin:12px 0 4px}
.wsx-page p{margin:0 0 8px}
.wsx-page ul,.wsx-page ol{margin:6px 0 10px 20px;padding:0}
.wsx-page li{margin:2px 0}
.wsx-page hr{border:0;border-top:1px solid var(--medium-gray);margin:16px 0}
.wsx-page .ws-tblwrap{overflow-x:auto;margin:10px 0}
.wsx-page .ws-tbl{border-collapse:collapse;width:100%;font-size:11px}
.wsx-page .ws-tbl th{background:var(--navy);color:#fff;padding:5px 9px;text-align:left;font-size:10px;font-weight:700}
.wsx-page .ws-tbl td{border:1px solid var(--medium-gray);padding:5px 9px;vertical-align:top}
.wsx-page .ws-ai-ph{border:1.5px dashed #b39ddb;background:#f6f2fc;border-radius:8px;padding:10px 12px;margin:10px 0}
.wsx-page .ws-ai-ph__tag{display:inline-block;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;
  color:#6631e0}
.wsx-page .ws-ai-ph__hint{display:block;font-size:11px;color:var(--text-secondary);margin-top:2px}
.wsx-page .ws-ai-inline{background:#f6f2fc;color:#6631e0;font-style:normal;font-size:.9em;font-weight:600;
  border:1px dashed #b39ddb;border-radius:5px;padding:0 5px}

.wsx-chat{display:flex;flex-direction:column;position:sticky;top:14px;max-height:calc(100vh - 118px)}
.wsx-msgs{padding:12px;display:flex;flex-direction:column;gap:9px;overflow-y:auto;flex:1}
.wsx-msg{max-width:93%;border-radius:10px;padding:8px 11px;font-size:11.8px;white-space:pre-wrap;line-height:1.5}
.wsx-msg.user{align-self:flex-end;background:var(--navy);color:#fff;border-bottom-right-radius:3px}
.wsx-msg.ai{align-self:flex-start;background:var(--light-gray);border-bottom-left-radius:3px}
.wsx-msg.pending{color:var(--text-muted);font-style:italic}
.wsx-quick{display:flex;gap:5px;flex-wrap:wrap;padding:0 12px 8px}
.wsx-qchip{font-size:10.5px;font-weight:700;color:var(--teal-dark);border:1px solid var(--teal);
  border-radius:12px;padding:2px 9px;cursor:pointer}
.wsx-qchip:hover{background:var(--teal);color:#fff}
.wsx-qchip--draft{background:var(--teal);color:#fff}
.wsx-qchip--draft:hover{background:var(--teal-dark)}
.wsx-composer{border-top:1px solid var(--medium-gray);padding:10px 12px;display:flex;gap:8px;align-items:center}
.wsx-in{flex:1;border:1px solid var(--medium-gray);border-radius:16px;padding:7px 13px;font-size:11.8px}
.wsx-in:focus{outline:none;border-color:var(--navy)}
.wsx-send{width:30px;height:30px;border:0;border-radius:50%;background:var(--teal);color:#fff;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}
@media(max-width:1100px){.wsx-grid{grid-template-columns:1fr}.wsx-chat{position:static;max-height:none}}

/* ── Company information (16.17.0) ─────────────────────────────── */
/* 16.63.0 (⑦h §B): the identity block. The workspace's own logo beside the
   organization's name, edited where it is read - the input IS the h1, so
   there is no toggle to get stuck in and no JavaScript between a customer
   and their own name. The Save button only earns its space once the field
   is touched, which is what keeps a header from looking like a form. */
.ci-idhead{align-items:flex-start}
.ci-ident{display:flex;align-items:center;gap:14px;min-width:0}
.ci-ident__logo{flex:none;display:grid;place-items:center;width:64px;height:64px;
  background:var(--white);border:1px solid var(--medium-gray);border-radius:12px;padding:8px}
.ci-ident__logo img{max-width:100%;max-height:100%;height:auto;display:block}
/* 16.65.0 (⑧a §A): the no-logo state. Same disc the sidebar usercard draws
   for a person (teal, white, bold, centred initials), scaled to the 64px
   identity slot; the frame's white fill, border and padding go with the
   picture they were framing. */
.ci-ident__logo--initials{background:var(--teal);border-color:var(--teal);color:var(--white);
  padding:0;font-size:24px;font-weight:700;line-height:1;letter-spacing:.01em}
.ci-ident__who{min-width:0}
.ci-ident__name{display:flex;align-items:center;gap:8px;margin:0;flex-wrap:wrap}
.ci-ident__name h1{margin:0;min-width:0}
.ci-ident__input{width:min(46ch,100%);max-width:100%;margin:0;padding:3px 8px;
  font-size:26px;font-weight:700;color:var(--navy);line-height:1.2;
  background:transparent;border:1px solid transparent;border-radius:8px;
  transition:border-color .12s,background .12s}
.ci-ident__input:hover{border-color:var(--medium-gray)}
.ci-ident__input:focus{outline:none;background:var(--white);border-color:var(--teal)}
.ci-ident__name .btn{flex:none}
@media(max-width:1100px){.ci-ident__input{font-size:21px}}
.ci-meter{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px;padding:10px 16px;min-width:230px}
.ci-meter .l{font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--text-secondary)}
.ci-meter .v{font-size:19px;font-weight:800;color:var(--navy)}
.ci-meter .v small{font-size:11px;color:var(--text-muted);font-weight:600}
.ci-meter .bar{height:6px;border-radius:3px;background:var(--medium-gray);margin-top:6px;overflow:hidden}
.ci-meter .bar i{display:block;height:100%;background:linear-gradient(90deg,var(--amber),var(--teal))}
.ci-meter .hint{font-size:10px;color:var(--text-muted);margin-top:5px}
/* 16.55.0 (⑥d §A): same density pass as .detail__grid - these two grids
   already hugged (align-items:start since 16.17.0), so what was left to
   spend was padding and the dropzone, which was a 14px-padded box asking
   for files nobody drops twice. */
.ci-idrow{display:grid;grid-template-columns:repeat(3,1fr);gap:11px;margin-bottom:11px;align-items:start}
.ci-col{display:flex;flex-direction:column;gap:11px;min-width:0}
.ci-col > .card{margin-bottom:0}
.ci-idrow > .card,.ci-col > .card{padding:14px 15px}
/* 16.63.0 (⑦h §B): h2 joins the selector - the Branding card arrived from
   .detail__grid, where its h2 was already stepped down to 13px, and a card
   that changed pages must not change size doing it. */
.ci-idrow > .card h2,.ci-col > .card h2,
.ci-idrow > .card h3,.ci-col > .card h3{font-size:13px;margin:0 0 6px}
/* 16.65.0 (⑧a §F) - Control ownership at ⑦b density. Five roles, five rows:
   name on the left, dropdown on the right, one line each. It was five stacked
   label-over-select pairs (.form-row is a COLUMN flex), which made the
   shortest card on the page the tallest. Scoped to .ci-own so no other
   label/select pair on this page or any other is touched. */
.ci-own .own-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,150px);
  gap:8px;align-items:center;margin:0 0 5px}
.ci-own .own-row__label{font-size:12px;font-weight:600;color:var(--text-secondary);
  min-width:0;overflow:hidden;text-overflow:ellipsis}
.ci-own .own-row select,.ci-own .own-row input{width:100%;margin:0;font-size:12px;padding:4px 6px}
/* The `display:grid` above outranks the UA's [hidden]{display:none}, so the
   MSP-name rows rendered even while their role said In-house - latent since
   ⑧a (one stray field) and five of them once ⑧b gave every role its own.
   Same restoration .ci-editor[hidden] needs, for the same reason. */
.ci-own .own-row[hidden]{display:none}
/* 16.66.0 (⑧b §4): the provider name is a CHILD of the role above it now,
   not a trailing field for the whole card, so it reads as one - indented
   caption, quieter type, and the same two-column grid so its box lines up
   with the five dropdowns rather than beside them. */
.ci-own .own-msp{margin:-2px 0 6px}
.ci-own .own-msp .own-row__label{position:relative;padding-left:14px;
  font-weight:500;color:var(--text-muted)}
.ci-own .own-msp .own-row__label::before{content:"\21B3";position:absolute;
  left:2px;color:var(--medium-gray)}
/* The refused-save state: the field says which field, before the sentence
   under it says why. */
.ci-own .own-msp.is-bad input{border-color:var(--red);background:#fff5f5}
.ci-own .own-msp__err{margin:4px 0 8px;font-size:11.5px;color:var(--red)}
.ci-own form > .btn{margin-top:8px}
.ci-uploads{padding:14px 15px}
.ci-drop{display:block;margin:10px 0 0;border:1.5px dashed var(--medium-gray);border-radius:9px;padding:9px 12px;
  text-align:center;color:var(--text-muted);font-size:11.5px;cursor:pointer;transition:border-color .12s,background .12s}
.ci-drop:hover,.ci-drop.is-over{border-color:var(--teal);background:var(--off-white)}
.ci-drop b{display:block;color:var(--navy);font-size:12.5px;margin-bottom:2px}
.ci-srclist{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.ci-src{display:inline-flex;gap:8px;align-items:center;background:var(--off-white);border:1px solid var(--medium-gray);
  border-radius:9px;padding:6px 10px;font-size:11px;max-width:340px}
.ci-src .ic{flex:none;width:24px;height:28px;border:1px solid var(--medium-gray);border-radius:4px;display:grid;
  place-items:center;font-size:7.5px;font-weight:800;color:var(--text-secondary);background:var(--white)}
.ci-src .name{font-weight:700;color:var(--navy);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:280px}
.ci-src small{color:var(--text-secondary);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:280px}
/* ⑧a §F moved this row above .ci-idrow, so it is the one that now needs the
   gap under it. .ci-idrow keeps its own for the mobile stack. */
.ci-main{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:11px;align-items:start;
  margin-bottom:11px}
.ci-board{padding:18px 22px}
.ci-chat{max-height:calc(100vh - 140px)}
/* 16.66.0 (⑧b §3): the right column holds two cards now - the interview and
   the documents you feed it. The chat gives up the height the uploads card
   needs, because a drop zone parked below a viewport-tall chat is the same
   distance away as the identity row it came from. The h3 step-down comes
   with the card from .ci-col so it does not change size by changing column. */
.ci-side{display:flex;flex-direction:column;gap:11px;min-width:0}
.ci-side > .card{margin-bottom:0}
.ci-side > .card h3{font-size:13px;margin:0 0 6px}
.ci-side .ci-chat{max-height:calc(100vh - 320px)}
@media(max-width:1100px){.ci-main{grid-template-columns:1fr}
  .ci-chat,.ci-side .ci-chat{position:static;max-height:none}}
.ci-grp{border-top:1px solid var(--light-gray);margin-top:10px;padding-top:8px}
.ci-grp:first-of-type{border-top:0;margin-top:6px}
.ci-grp-h{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;color:var(--navy)}
.ci-grp-h .pct{margin-left:auto;font-size:10px;font-weight:800;color:var(--text-muted);font-variant-numeric:tabular-nums}
.ci-facts{display:flex;flex-wrap:wrap;gap:6px;padding:8px 0 4px}
.ci-fact{display:inline-flex;align-items:center;gap:6px;background:var(--off-white);border:1px solid var(--medium-gray);
  border-radius:14px;padding:4px 11px;font-size:11px;color:var(--navy);cursor:pointer;font-family:inherit;text-align:left}
.ci-fact:hover{border-color:var(--teal)}
.ci-fact b{font-weight:700}
.ci-fact .val{max-width:260px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ci-fact .src{font-size:8.5px;font-weight:800;letter-spacing:.03em;text-transform:uppercase;border-radius:7px;padding:0 5px}
.src-manual,.src-chat{background:#E0F2F1;color:#00695C}
.src-doc{background:#E8EAF6;color:#3949AB}
.src-scan{background:#FFF3E0;color:#B26A00}
.ci-fact.is-miss{border-style:dashed;color:var(--text-muted);background:transparent}
.ci-fact.is-miss:hover{color:var(--teal-dark)}
.ci-ground{margin-top:12px;padding-top:10px;border-top:1px solid var(--light-gray);font-size:10.5px;color:var(--text-muted)}
.ci-editor{display:flex;align-items:center;gap:8px;flex-wrap:wrap;background:var(--white);border:1px solid var(--teal);
  border-radius:10px;padding:8px 11px;margin:2px 0;flex-basis:100%}
/* the flex display above outranks the UA [hidden] rule, so restore it — else
   the editor is always shown and Cancel/Save (with no active chip) do nothing */
.ci-editor[hidden]{display:none}
.ci-editor__label{font-size:10.5px;font-weight:700;color:var(--text-secondary);flex-basis:100%}
.ci-editor input{flex:1;min-width:220px;border:1px solid var(--medium-gray);border-radius:7px;padding:6px 10px;font-size:12px}
.ci-editor input:focus{outline:none;border-color:var(--navy)}
.ci-editor__acts{display:flex;gap:6px;align-items:center}
.ci-ed-clear{background:transparent;border:1px solid var(--medium-gray);color:var(--red);border-radius:8px;
  font-size:10.5px;font-weight:700;padding:4px 10px;cursor:pointer}
@media(max-width:1100px){.ci-idrow{grid-template-columns:1fr}}

/* ── create-from-description (16.16.0) ─────────────────────────── */
.dn-descbar{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px;padding:12px 14px;margin-bottom:12px}
.dn-desclabel{display:block;font-size:11px;font-weight:800;letter-spacing:.03em;text-transform:uppercase;color:var(--navy);margin-bottom:6px}
.dn-desc{width:100%;border:1px solid var(--medium-gray);border-radius:8px;padding:9px 11px;font-size:12.5px;
  line-height:1.5;resize:vertical;font-family:inherit}
.dn-desc:focus{outline:none;border-color:var(--navy)}
.dn-descactions{display:flex;align-items:center;gap:10px;margin-top:9px;flex-wrap:wrap}
.dn-hint{font-size:11px;color:var(--text-muted)}
.dn-about{font-size:12px;color:var(--text-secondary);line-height:1.5;padding:2px 0}
.dn-about--muted{color:var(--text-muted);font-size:11px;margin-top:6px;border-top:1px solid var(--light-gray);padding-top:8px}
.dn-blank{padding:34px 18px;text-align:center;color:var(--text-muted);font-size:12.5px}

/* ── inline document fields under the chat (16.21.1) ──────────── */
.wsx-fields{border-top:1px solid var(--medium-gray)}
.wsx-fields summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:7px;
  padding:10px 14px;font-size:12.5px;font-weight:700;color:var(--navy)}
.wsx-fields summary::before{content:"▸";font-size:10px;color:var(--text-muted)}
.wsx-fields[open] summary::before{content:"▾"}
.wsx-fields summary .n{font-size:10px;font-weight:700;color:var(--text-muted)}
.wsx-fields__list{max-height:38vh;overflow-y:auto;padding:2px 14px 12px;display:flex;flex-direction:column;gap:9px}
.wsx-field{display:flex;flex-direction:column;gap:3px}
.wsx-field__l{font-size:10.5px;font-weight:700;color:var(--text-secondary)}
.wsx-field__in{border:1px solid var(--medium-gray);border-radius:7px;padding:6px 9px;font-size:12px;
  font-family:inherit;background:var(--white);transition:border-color .15s,box-shadow .15s}
.wsx-field__in:focus{outline:none;border-color:var(--navy)}
.wsx-field__in.is-saved{border-color:var(--green);box-shadow:0 0 0 2px rgba(76,175,80,.18)}
.wsx-field__in.is-err{border-color:var(--red)}

/* ── document quality panel (16.20.0) ─────────────────────────── */
.wsx-qual h3{display:flex;align-items:center;gap:8px}
.wsx-qbtn{margin-left:auto;font-size:10.5px;font-weight:800;color:#fff;background:var(--teal);border:0;
  border-radius:11px;padding:3px 12px;cursor:pointer}
.wsx-qbtn:disabled{opacity:.6;cursor:default}
.wsx-qbody{padding:11px 14px;display:flex;flex-direction:column;gap:9px}
.wsx-qhint{font-size:11.5px;color:var(--text-muted);line-height:1.45}
.wsx-qscore{display:flex;align-items:baseline;gap:5px}
.wsx-qscore .v{font-size:24px;font-weight:800;color:var(--navy);font-variant-numeric:tabular-nums}
.wsx-qscore .o{font-size:12px;color:var(--text-muted);font-weight:600}
.wsx-qbar{position:relative;height:7px;border-radius:4px;background:var(--medium-gray)}
.wsx-qbar i{display:block;height:100%;border-radius:4px;background:linear-gradient(90deg,var(--amber),var(--green))}
.wsx-qbar .req{position:absolute;top:-3px;width:2px;height:13px;background:var(--navy);transform:translateX(-1px)}
.wsx-qsum{font-size:11.5px;color:var(--text-secondary);line-height:1.5}
.wsx-gap{display:flex;flex-direction:column;gap:2px;border-top:1px solid var(--light-gray);padding-top:8px}
.wsx-gap .area{font-weight:800;color:var(--navy);font-size:11.5px}
.wsx-gap .why{font-size:11px;color:var(--text-secondary);line-height:1.4}
.wsx-gap .sugg{font-size:11px;color:#1b7f4b;line-height:1.4}
.wsx-gap-ask{align-self:flex-start;margin-top:4px;font-size:10px;font-weight:700;color:var(--teal-dark);
  background:transparent;border:1px solid var(--teal);border-radius:10px;padding:2px 9px;cursor:pointer}
.wsx-gap-ask:hover{background:var(--teal);color:#fff}
/* 16.28.1: gap states - Added / Dismiss / Restore + the dismissed drawer */
.wsx-gaps{display:flex;flex-direction:column;gap:9px}
.wsx-gap-acts{display:flex;align-items:center;gap:6px;margin-top:4px}
.wsx-gap-acts .wsx-gap-ask{align-self:auto;margin-top:0}
.wsx-gap-ask.is-added{color:var(--text-muted);border-color:var(--medium-gray);
  background:var(--off-white);cursor:default}
.wsx-gap-ask.is-added:hover{background:var(--off-white);color:var(--text-muted)}
.wsx-gap-dismiss{font-size:10px;font-weight:700;color:var(--text-muted);background:transparent;
  border:1px solid var(--medium-gray);border-radius:10px;padding:2px 9px;cursor:pointer}
.wsx-gap-dismiss:hover{color:var(--navy);border-color:var(--navy)}
.wsx-gap.is-dismissed .area,.wsx-gap.is-dismissed .why,.wsx-gap.is-dismissed .sugg{opacity:.65}
.wsx-qdtoggle{align-self:flex-start;font-size:10.5px;font-weight:700;color:var(--text-muted);
  background:transparent;border:0;padding:2px 0;cursor:pointer;text-decoration:underline}
.wsx-qdtoggle:hover{color:var(--navy)}

/* ── workspace editing (16.13.0): override badge, applied-edits, edit cards ── */
.wsx-obadge{font-size:10.5px;font-weight:700;color:var(--teal-dark);background:var(--off-white);
  border:1px solid var(--teal);border-radius:11px;padding:2px 9px;white-space:nowrap}
.wsx-obadge b{font-variant-numeric:tabular-nums}
.wsx-ovpanel h3{display:flex;align-items:center;gap:6px}
.wsx-ov{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;padding:6px 13px;border-top:1px solid var(--light-gray);font-size:11px}
.wsx-ov .k{flex:0 0 auto;min-width:96px;max-width:40%;font-weight:700;color:var(--navy);font-family:var(--mono,monospace);font-size:10.5px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wsx-ov .v{flex:1;min-width:0;color:var(--text-secondary);word-break:break-word}
.wsx-ov.is-note .k{color:var(--teal-dark)}
/* 17.4.6 — the revert stack on that panel. The row grew a timestamp, an
   affordance and (on one row at a time) a confirmation, so every new part is
   a wrapping flex child with min-width:0: the rail is 350px wide on a phone
   and a section heading is arbitrary text. An undone row is struck through
   and dimmed and it never leaves the list - that IS the audit trail. */
.wsx-ov__when{flex:1 1 100%;min-width:0;color:var(--text-muted);font-size:10px;overflow-wrap:anywhere}
.wsx-ov__act{flex:0 0 auto;min-width:0;margin-left:auto}
.wsx-ov.is-undone .k,.wsx-ov.is-undone .v{text-decoration:line-through;opacity:.62}
.wsx-ov.is-undone .wsx-ov__when{font-style:italic}
.wsx-ovnote{margin:0;padding:6px 13px;font-size:10.5px;color:var(--text-secondary);
  border-top:1px solid var(--light-gray);overflow-wrap:anywhere}
.wsx-ovnote.is-block{color:var(--navy);background:var(--off-white);font-weight:600}
.wsx-ovc{flex:1 1 100%;min-width:0;margin-top:4px;border:1px solid var(--teal);border-radius:9px;
  background:var(--white);padding:8px 10px}
.wsx-ovc__q{margin:0 0 4px;font-size:11px;font-weight:800;color:var(--navy)}
.wsx-ovc__what{margin:0 0 6px;font-size:10.5px;color:var(--text-secondary);overflow-wrap:anywhere}
.wsx-ovc__acts{display:flex;flex-wrap:wrap;gap:6px}

/* proposed-edit card in the chat stream */
.wsx-edit{align-self:stretch;border:1px solid var(--teal);border-left:3px solid var(--teal);border-radius:9px;
  background:var(--white);padding:9px 11px;font-size:11.5px}
.wsx-edit .lbl{display:block;font-weight:800;color:var(--navy);font-size:11px;letter-spacing:.01em}
.wsx-diff{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:5px 0}
.wsx-diff .old{text-decoration:line-through;color:var(--text-muted);background:var(--light-gray);
  border-radius:4px;padding:0 5px}
.wsx-diff .arr{color:var(--text-muted);font-weight:700}
.wsx-diff .add{color:#1b7f4b;background:#e7f6ec;border-radius:4px;padding:0 5px;font-weight:600}
.wsx-why{color:var(--text-secondary);font-size:10.5px;line-height:1.45;margin-bottom:6px}
.wsx-acts{display:flex;gap:7px}
.wsx-acc,.wsx-rej{font-size:10.5px;font-weight:700;border-radius:12px;padding:3px 12px;cursor:pointer;border:1px solid}
.wsx-acc{background:var(--teal);color:#fff;border-color:var(--teal)}
.wsx-acc:disabled{opacity:.6;cursor:default}
.wsx-rej{background:transparent;color:var(--text-secondary);border-color:var(--medium-gray)}
.wsx-rej:hover{border-color:var(--text-secondary)}
.wsx-edit.is-applied{border-color:var(--green);border-left-color:var(--green);opacity:.85}
.wsx-done{font-size:10.5px;font-weight:800;color:var(--green)}

/* ── Documentation Registry (16.12.0) ─────────────────────────── */
.reg-cov{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:0 0 16px}
.reg-cov .cov{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px;padding:12px 14px}
.reg-cov .cov .n{font-size:22px;font-weight:800;font-variant-numeric:tabular-nums}
.reg-cov .cov .n small{font-size:12px;color:var(--text-muted);font-weight:600}
.reg-cov .cov .l{font-size:11px;color:var(--text-secondary);margin-top:2px}
.reg-cov .covbar{height:5px;border-radius:3px;background:var(--medium-gray);margin-top:8px;overflow:hidden}
.reg-cov .covbar i{display:block;height:100%}

.reg-grouper{display:inline-flex;align-items:center;border:1px solid var(--medium-gray);
  border-radius:14px;background:var(--white);overflow:hidden}
.reg-glabel{font-size:10px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  color:var(--text-muted);padding:0 9px 0 12px}
.reg-gopt{font-size:11.5px;font-weight:700;border:0;background:transparent;color:var(--text-secondary);
  padding:5px 13px;cursor:pointer;border-left:1px solid var(--medium-gray)}
.reg-gopt.is-active{background:var(--navy);color:var(--white)}

.reg-grp{margin-bottom:18px}
.reg-grp-h{display:flex;align-items:center;gap:8px;margin:0 0 8px}
.reg-grp-h h3{font-size:13px;font-weight:700;margin:0}
.reg-grp-h .cnt{font-size:11px;color:var(--text-secondary);font-weight:600;background:var(--white);
  border:1px solid var(--medium-gray);border-radius:12px;padding:1px 8px}
.reg-grp-h .dot{width:10px;height:10px;border-radius:50%}
.reg-mhead,.reg-fhead{display:flex;align-items:center;gap:9px;margin:0 0 8px}
.reg-mhead b,.reg-fhead b{font-size:13px}
.reg-fhead{padding-left:10px;border-left:4px solid var(--fh,var(--teal))}
.reg-cov-inline{margin-left:auto;font-size:11px;color:var(--text-secondary)}

.reg-card{background:var(--white);border:1px solid var(--medium-gray);border-radius:10px;overflow:hidden}
/* 17.50.0 (registry alignment, mock docs/mockups/registry_align_mock.html).
   ONE grid, shared by every row shape, and that is the entire fix: a column
   can only start at the same x on every row if every row is measuring
   against the same tracks. The predecessor was a flex row of fixed-width
   members, where a cell's width was its own business and a taller row
   re-centred every short cell in it - which is how the BCDR row came to sit
   visibly out of line with identical rows above and below it. 17.44.0
   patched that with a `reg-row--chips` modifier that switched the affected
   rows to baseline alignment; the badge now lives in the id cell and the
   tracks are shared, so both the modifier and the reason for it are gone.

   The tracks are the mock's: a wide text column paid for by slimmer data
   columns. The one departure is the language cell, which is 88+34+gap in
   the mock and ONE element in the markup (the chip and its toggle share a
   flex parent), so it is one 136px track instead of two.

   `reg-row--nis2` adds a track for the Primary/Supporting cell that only
   that view renders. A modifier rather than an always-present empty cell,
   because that view is a card of its own and every row in it has the cell -
   there is nothing to align it against that does not also have it. */
.reg-row{display:grid;align-items:center;gap:14px;padding:11px 14px;
  grid-template-columns:118px minmax(0,1.35fr) 88px 138px 96px 110px 84px;
  border-bottom:1px solid #eef1f5;transition:background .1s}
.reg-row--nis2{grid-template-columns:118px minmax(0,1.35fr) 76px 88px 138px 96px 110px 84px}
.reg-row:last-child{border-bottom:0}
.reg-row:hover{background:#f7f9fc}
.reg-row.is-proposed{opacity:.8;background:var(--off-white)}
/* The identity cell: the id pill, and beneath it the row's badges, stacked
   and centred inside the narrow column so a two-word badge becomes two short
   lines rather than a wider first track. */
.reg-idcell{display:flex;flex-direction:column;align-items:stretch;gap:5px;min-width:0}
.reg-title{min-width:0;font-weight:600;font-size:12.5px}
/* The description. Two lines, then an ellipsis - and the whole sentence is
   in the cell's `title`, so the clamp hides nothing permanently. NO width
   cap (17.50.1): a 64ch cap left a dead gap between the text and the data
   columns on wide screens - the owner wants the text to fill its track, and
   the two-line clamp already bounds the height. The TITLE above deliberately
   has no truncation of any kind: a long document name wraps. */
.reg-title small{display:block;font-weight:400;color:var(--text-secondary);font-size:11px;
  overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-clamp:2}
.reg-prim{font-size:9px;font-weight:800;letter-spacing:.04em;color:var(--teal-dark);
  border:1px solid var(--teal);border-radius:3px;padding:1px 5px;text-transform:uppercase;
  justify-self:start}
.reg-supp{font-size:9px;font-weight:800;letter-spacing:.04em;color:var(--text-muted);
  border:1px solid var(--medium-gray);border-radius:3px;padding:1px 5px;text-transform:uppercase;
  justify-self:start}
.reg-maps{display:flex;gap:3px;align-items:center;flex-wrap:wrap;min-width:0}
.reg-maps .reg-gap{width:6px}
.reg-cnt{min-width:0}
.reg-cnt .t{font-size:10.5px;color:var(--text-secondary);margin-bottom:3px}
.reg-bar{height:5px;border-radius:3px;background:var(--medium-gray);overflow:hidden}
.reg-bar i{display:block;height:100%}
.reg-status{min-width:0;font-size:11px;font-weight:700}
.reg-status small{display:block;font-weight:400;color:var(--text-muted);font-size:10.5px}
.reg-status.st-generated{color:var(--green)}
.reg-status.st-missing{color:var(--text-secondary)}
.reg-status.st-proposed{color:var(--text-muted)}
/* 17.4.0 client-document chip. Since 17.50.0 it lives in `.reg-idcell`,
   stacked UNDER the doc-id pill, rather than on its own line under the
   subtitle: that line was what made a badged row taller than its siblings
   and pulled its short cells out of column. In the narrow first track it
   wraps to two short centred lines and the row's height is unchanged.
   Quiet by design: it reports what OptiApp knows about the customer's own
   document, never the slot's status. */
.reg-cdoc{font-size:8.5px;font-weight:800;line-height:1.35;
  letter-spacing:.03em;text-transform:uppercase;border-radius:8px;padding:3px 6px;
  text-align:center;color:#10707F;background:#EAF6F8;border:1px solid #C7E5EA}
.reg-cdoc:hover{background:#DCEFF3;text-decoration:none}
.reg-cdoc--have{color:#1E7A46;background:#E9F7EF;border-color:#C6E9D5}
.reg-cdoc--have:hover{background:#DBF1E5}
/* 17.4.1: the slot's client document has been adopted - it IS the document
   now. Filled rather than outlined, which is the one visual step up this
   chip takes across its three states. */
.reg-cdoc--adopted{color:#fff;background:#1E7A46;border-color:#1E7A46}
.reg-cdoc--adopted:hover{background:#186139}
/* 17.4.3: the source this slot was adopted from has moved. It sits beside
   the adopted chip in the same title cell - colour only, no layout, so the
   17.2.4 phone row is untouched by it too. */
.reg-cdoc--srcchg{color:#8a6d00;background:#FFF6E2;border-color:#F0D9A0}
.reg-cdoc--srcchg:hover{background:#FBEBC8}
/* 17.4.1 status states. Adopted is green like Defined because it IS a
   document; below-standard is amber because it is one that does not yet
   meet the bar. Neither borrows .st-generated. */
.reg-status.st-adopted{color:var(--green)}
.reg-status.st-adopted_below{color:#8a6d00}
.reg-optional{display:inline-block;margin-left:7px;font-size:9px;font-weight:800;letter-spacing:.04em;
  text-transform:uppercase;color:#8a6d00;background:#fdf3d6;border:1px solid #f0d98a;border-radius:9px;
  padding:0 6px;vertical-align:middle;cursor:help}

/* doc-id chip: monospace, soft type tint, fixed-width so ids align in a column */
/* 17.50.0: no `min-width` and no `flex:none` any more - the pill fills the
   118px identity track, which is what makes every id start at the same x
   whatever its length. */
.reg-row .did{
  font-family:ui-monospace,"SF Mono","JetBrains Mono",Menlo,Consolas,monospace;
  font-size:11px;font-weight:600;letter-spacing:-.01em;line-height:1;
  padding:4px 9px;border-radius:6px;border:0;min-width:0;
  text-align:left;position:relative;padding-left:16px}
/* little type dot inside the chip */
.reg-row .did::before{content:"";position:absolute;left:7px;top:50%;transform:translateY(-50%);
  width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.85}
.did.t-policy   {color:#3538cd;background:#eef1fe}
.did.t-procedure{color:#0f7267;background:#e5f5f2}
.did.t-plan     {color:#9a5808;background:#fbf1e2}
.did.t-standard {color:#6331e0;background:#f0ecfe}
.did.t-register {color:#1f7a34;background:#e7f5ec}
.did.t-log      {color:#48536a;background:#eef1f5}
.reg-row.is-proposed .did{background:repeating-linear-gradient(-45deg,#f4f6f9,#f4f6f9 5px,#eef1f5 5px,#eef1f5 10px)}
.t-policy-dot{background:#3538cd}.t-procedure-dot{background:#0f7267}.t-plan-dot{background:#9a5808}
.t-standard-dot{background:#6331e0}.t-register-dot{background:#1f7a34}.t-log-dot{background:#48536a}
/* function chips (official CyFun function colours) */
.reg-row .fchip,.reg-mhead .fchip{display:inline-grid;place-items:center;height:16px;min-width:22px;padding:0 5px;
  border-radius:5px;font-size:9px;font-weight:800;color:#fff;letter-spacing:.02em}
.f-GV{background:#5E35B1}.f-ID{background:#1E88E5}.f-PR{background:#43A047}
.f-DE{background:#FB8C00}.f-RS{background:#E53935}.f-RC{background:#00ACC1}
.reg-row .mchip,.reg-mhead .mchip{display:inline-grid;place-items:center;width:16px;height:16px;
  border-radius:5px;font-size:9.5px;font-weight:800;color:#fff;
  background:var(--navy);font-family:ui-monospace,Menlo,monospace}
.reg-mhead .mchip{width:22px;height:22px;font-size:12px;border-radius:6px}
/* 17.50.0: the coverage column is the first to go on a narrow desktop, and
   with a grid its TRACK has to go with it - `display:none` alone would leave
   a 150px hole and slide nothing. */
@media (max-width:820px){
  .reg-cov{grid-template-columns:repeat(2,1fr)}
  .reg-cnt{display:none}
  .reg-row{grid-template-columns:118px minmax(0,1.35fr) 88px 104px 136px 84px}
  .reg-row--nis2{grid-template-columns:118px minmax(0,1.35fr) 76px 88px 104px 136px 84px}
}

/* Grouped sidebar navigation (16.11.2) */
.navgroup { margin: 2px 0; }
.navgroup__head {
  display: flex; align-items: center; justify-content: space-between;
  cursor: pointer; list-style: none; user-select: none;
  padding: 7px 14px 3px; color: var(--sidebar-muted, #8fa1b8);
}
.navgroup__head::-webkit-details-marker { display: none; }
.navgroup__label {
  font-size: 10px; font-weight: 800; text-transform: uppercase;
  letter-spacing: .09em;
}
.navgroup__chev {
  width: 13px; height: 13px; opacity: .7;
  transition: transform .15s ease;
  transform: rotate(-90deg);
}
.navgroup[open] > .navgroup__head .navgroup__chev { transform: rotate(0deg); }
.navgroup__head:hover .navgroup__label,
.navgroup__head:hover .navgroup__chev { color: var(--white); opacity: 1; }
/* 16.65.0 (⑧a §E): the 4px inset belongs to EVERY nav item, not only to the
   ones inside a group. It was written as `.navgroup > .navitem` when groups
   were the only shape in the list; the bare items (Dashboard, Reports) and the
   console links then sat 4px to the LEFT of every grouped item, so the icon
   column ran in two lanes down one sidebar. Same indent, same padding, same
   icon rhythm for all of them now. */
.sidebar__nav .navitem { margin-left: 4px; }

/* Signed-in identity + sign out (sidebar footer, 16.12.0).
   16.54.0 (⑥c §D): collapsed to avatar + name; the email and the sign-out
   live in a popover that opens UPWARD, because the card sits on the floor of
   the sidebar and a downward panel would open off-screen. */
.usercard{margin:0 4px 8px;border-radius:9px;background:var(--sidebar-hover);
  position:relative}
.usercard__id{display:flex;align-items:center;gap:9px;min-width:0;
  padding:8px;cursor:pointer;list-style:none;border-radius:9px}
.usercard__id::-webkit-details-marker{display:none}
.usercard__id:hover{background:var(--sidebar-selected)}
.usercard__avatar{flex:none;width:28px;height:28px;border-radius:50%;background:var(--teal);
  color:#fff;display:grid;place-items:center;font-weight:700;font-size:12px}
.usercard__name{color:#fff;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.usercard__chev{flex:none;width:13px;height:13px;margin-left:auto;color:var(--sidebar-text);
  opacity:.7;transition:transform .12s ease}
.usercard[open] .usercard__chev{transform:rotate(180deg)}
.usercard__pop{position:absolute;bottom:calc(100% + 6px);left:0;right:0;z-index:20;
  display:flex;flex-direction:column;gap:6px;padding:9px 10px;border-radius:9px;
  background:var(--sidebar-selected);border:1px solid rgba(255,255,255,.12);
  box-shadow:0 8px 20px rgba(0,0,0,.35)}
.usercard__email{color:var(--sidebar-text);font-size:10.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 16.52.0 (skin spec §F): sign-out is the quietest control in the sidebar.
   A bordered button competed with the nav for attention on every page; it is
   now icon + muted label, no chrome, and it brightens on hover. The
   accessible name is unchanged (aria-label + title + visible text). */
.usercard__signout{display:flex;align-items:center;gap:6px;justify-content:center;
  padding:3px 6px;border-radius:6px;border:none;background:transparent;
  color:var(--sidebar-text);opacity:.72;
  font-size:11px;font-weight:600;text-decoration:none;transition:opacity .12s ease,color .12s ease}
.usercard__signout svg{width:14px;height:14px}
.usercard__signout:hover{background:transparent;color:#fff;opacity:1;text-decoration:none}

/* ── 17.48.0: the sidebar corner (docs/mockups/footer_corner_mock.html, D) ──
   One row: the identity tile on the left, the two help chips stacked to its
   right. `align-items:stretch` plus `flex:1` on each chip is the whole
   alignment contract - the tile is as tall as the column beside it because
   the column decides the height and the tile is told to fill it, which is the
   same one-growing-region discipline the dashboard rows use. Nothing here
   restyles .helpswitch: the pills are the ones the footer already had, moved.
   The corner is the popover's positioning context (the tile itself goes
   `position:static`), so the account panel spans the full width of the row
   rather than the ~60px of the tile it hangs off. */
.sbcorner{display:flex;align-items:stretch;gap:8px;position:relative;margin:0 4px 8px}
.sbcorner .usercard{position:static;flex:none;margin:0}
.sbcorner .usercard__id{flex-direction:column;justify-content:center;gap:5px;
  padding:8px 13px;height:100%}
.sbcorner .usercard__avatar{width:34px;height:34px;font-size:13px}
.sbcorner .usercard__chev{margin-left:0;width:12px;height:12px}
/* The chip column. `min-width:0` so a long translated label ellipsises inside
   the chip instead of pushing the tile off the sidebar. */
.sbcorner .langswitch{flex:1;min-width:0;flex-direction:column;flex-wrap:nowrap;
  justify-content:center;gap:8px;margin:0}
/* The pair fills the column; the max-height is what keeps a LONE chip a pill
   rather than a 67px slab - /quickstart hides its own chip, and the Manual
   left beside it must still look like the chip it is everywhere else. */
.sbcorner .helpswitch{flex:1;max-height:34px;justify-content:center;min-width:0}
.sbcorner .helpswitch span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Non-production environment badge (OPTI_ENV_LABEL) in the sidebar footer */
.env-badge {
  display: inline-block; margin: 0 auto 8px; padding: 3px 12px;
  border-radius: 4px; font-size: 12px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; text-align: center;
  background: var(--amber); color: #4D2C00;
  border: 1px solid #b45309;
}
/* 17.49.0: the demo band. Top of the main pane on every page of a demo
   workspace, informational and not dismissible. Same amber vocabulary as the
   environment badge above - both say "this is not the ordinary thing" - and
   the same chip idiom for the leading word. */
.demo-band {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 16px; padding: 8px 14px; border-radius: 8px;
  background: var(--tint-med-bg, #FFF6E5); color: var(--tint-med-ink, #4D2C00);
  border: 1px solid #b45309; font-size: 13px; font-weight: 600;
}
.demo-band__tag {
  display: inline-block; padding: 2px 10px; border-radius: 999px;
  background: var(--amber, #F5A623); color: #4D2C00;
  font-size: 11.5px; font-weight: 800; letter-spacing: .08em;
}
.demo-band__cta { margin-left: auto; color: inherit; text-decoration: underline; }

/* 16.1.12+: shared table styling for the SaaS console pages (Manage Tenants /
   Users / Platform, Portfolio). Previously .data-table had no rules at all, so
   columns and action buttons were unaligned. */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th, .data-table td {
  padding: 8px 12px; text-align: left; vertical-align: middle;
  border-bottom: 1px solid var(--medium-gray);
}
.data-table thead th {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-secondary); font-weight: 700;
}
.data-table td { font-size: 14px; }
.data-table tbody tr:hover { background: var(--off-white); }
/* Action cells (buttons) — keep on one line and right-room so they don't wrap. */
.data-table td:last-child, .data-table .actions { white-space: nowrap; }
.data-table .btn--small + .btn--small { margin-left: 4px; }
/* Scan History: the "show older scans" fold (17.40.0).
   This used to be `x-show="showAll || N < 3"` on every row — an Alpine
   expression carrying a comparison and a Jinja-interpolated row index, which
   is precisely the shape the CSP build cannot parse and the shape that kept
   'unsafe-eval' in script-src. The rows are now marked as older by Jinja at
   render time and the fold is one class on the table, so the only thing left
   in the markup is a class binding.
   Collapsed is the CSS DEFAULT and Alpine only ever ADDS the expanded class.
   That ordering is deliberate twice over: the fold is already correct in the
   first painted frame, before Alpine boots (the other way round, every older
   row would flash in and then vanish); and Alpine's class binding only
   removes classes it added itself, so a `scans-table--collapsed` printed into
   the static class attribute by Jinja would be one Alpine could never take
   off again — the fold would open once and then jam. */
.scan-row--older { display: none; }
.scans-table--expanded .scan-row--older { display: table-row; }
.assurance-basic     { background: #E8EAF6; color: #3949AB; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; }
.assurance-important { background: #E3F2FD; color: #1565C0; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; }
.assurance-essential { background: #E0F2F1; color: #00695C; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; }
.hint { background: var(--white); border-radius: 10px; padding: 16px; border: 1px solid var(--medium-gray); }
.hint h2 { margin-top: 0; color: var(--navy); font-size: 16px; }
.hint__key {
  margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--medium-gray);
  font-size: 12.5px; color: var(--text-secondary);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
/* The explainer on a key requirement's header. Gold rule down the left so it
   reads as the badge's own footnote rather than as another callout. */
.req-keynote {
  margin: 10px 0 0; padding: 8px 12px;
  border-left: 3px solid var(--key-ink); background: var(--key-bg);
  border-radius: 0 8px 8px 0;
  font-size: 12.5px; line-height: 1.5; color: var(--text-primary);
}
.req-keynote .keybadge { margin-right: 4px; vertical-align: 1px; }
.kpi { list-style: none; padding: 0; margin: 16px 0 0; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.kpi li { background: var(--off-white); padding: 10px; border-radius: 6px; }
.kpi__label { font-size: 11px; color: var(--text-secondary); text-transform: uppercase; }
.kpi__value { font-size: 20px; font-weight: 700; color: var(--navy); }
/* 16.59.0: the coverage denominator and the scored-only secondary that every
   scoped tile carries, plus the low-coverage tile that spans both columns. */
.kpi__foot { font-size: 11px; line-height: 15px; margin-top: 2px; }
.kpi__wide { grid-column: 1 / -1; }

/* ── Detail page (existing) ─────────────────────────────────────────── */
.back { display: inline-block; margin-bottom: 12px; color: var(--text-secondary); font-size: 13px; }
.detail__header { background: var(--white); padding: 22px; border-radius: 10px; margin-bottom: 14px; border: 1px solid var(--medium-gray); }
.detail__header h1 { margin: 0; color: var(--navy); display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.subcat { margin: 8px 0; font-weight: 600; color: var(--text-primary); }
.req-body { background: var(--off-white); padding: 12px; border-radius: 6px; }
/* 16.55.0 (⑥d §A): `align-items: start` is the whole fix for the voids on
   Settings, Automation and Company information. A CSS grid stretches every
   card to the tallest one in its row, so a two-line "Branding" card
   inherited 150px of nothing from the audit-behaviour form beside it. Cards
   now size to their own content and uneven rows are INTENTIONAL - the page
   is a set of unrelated preferences, not a table. */
.detail__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; align-items: start; }
.detail__grid > .card { padding: 14px 15px; }
.detail__grid > .card h2, .detail__grid > .card h3 {
  font-size: 13px; margin: 0 0 6px; color: var(--navy); font-weight: 700;
}
.detail__grid > .card > p { font-size: 12px; margin: 0 0 8px; line-height: 1.5; }
/* 16.57.0 (⑦b §5): ⑥d named these grids and its selectors DO hit - Settings
   and Automation are both a bare `.detail__grid` of direct-child `.card`s,
   so align-items/padding/heading size all landed. What ⑥d could not reach
   was anything one level deeper, and that is where the rest of the space
   was: `.small` and `.subtle` have no size rule anywhere in this file, so
   every explanatory paragraph inside a form was rendering at the browser
   default 16px, and each of Automation's ~30 of them carried 14px of
   form-row margin under it. Written at `.detail__grid` scope rather than
   behind a new page modifier: the requirement-detail cards use the same
   grid and the same oversized helper text, and ⑥d already restyled that
   surface for exactly this reason. */
.detail__grid .small, .detail__grid .subtle,
.detail__grid .muted.small { font-size: 12px; line-height: 1.5; }
.detail__grid > .card p { margin: 0 0 8px; }
.detail__grid .form-row { margin-bottom: 9px; gap: 3px; }
.detail__grid > .card > ul.small { margin: 0 0 8px; padding-left: 17px; }
.detail__grid > .card > ul.small li { margin-bottom: 4px; }
.detail__grid > .card > form > label { font-size: 12.5px; }
.card--wide { grid-column: 1 / -1; }

/* ── 16.69.0 (⑧d §12): the Settings grid, column-PACKED ─────────────────
   ⑥d's `align-items: start` stopped a card being stretched to its
   neighbour's height, but a two-column GRID still aligns rows: a short card
   beside a tall one leaves the gap under it empty until the next row starts,
   and Settings - seven cards of wildly different heights - is where that
   reads as "the empty space between the tiles looks messy" (PO, screenshot).

   CSS multi-column instead of grid. There are no rows to align, so each
   column is a tight stack and every card sits directly under the one above
   it. `break-inside: avoid` is what keeps a card whole rather than letting a
   form spill across the column break.

   Reading order is COLUMN-MAJOR: the cards keep their document order and
   flow down the left column, then continue down the right. The break point
   is the browser's own height balance, which is the property being bought -
   the packing follows the content instead of a fixed row grid.

   SCOPED to Settings by a modifier. Automation's four cards are equal-height
   by construction and Company information has a custom layout; neither has
   the problem and neither should pay for the fix. */
.detail__grid--packed {
  display: block; columns: 2; column-gap: 11px;
}
.detail__grid--packed > .card {
  break-inside: avoid; -webkit-column-break-inside: avoid;
  margin-bottom: 11px;
}
.upload-form { display: flex; gap: 8px; margin-bottom: 12px; align-items: center; }
.evidence-list { list-style: none; padding: 0; }
.evidence {
  display: grid; grid-template-columns: 1fr auto auto; gap: 8px; align-items: center;
  background: var(--off-white); padding: 8px 12px; border-radius: 6px; margin-bottom: 4px;
}
.evidence__name { font-weight: 600; font-size: 13px; }
.evidence__meta { font-size: 11px; color: var(--text-secondary); }
.score-row { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
.score-card { padding: 12px 20px; border-radius: 8px; color: var(--white); min-width: 120px; }
.score-card.good { background: var(--green); }
.score-card.bad  { background: var(--red); }
.score-card .big { font-size: 22px; font-weight: 700; }
.confidence { font-size: 12px; color: var(--text-secondary); }
.muted { color: var(--text-muted); }

/* ── Dashboard panels (CyFun) ────────────────────────────────────────── */
.score { padding: 4px 12px; border-radius: 4px; font-weight: 700; font-size: 13px; }
.score.good  { background: var(--green); color: var(--white); }
.score.bad   { background: var(--red); color: var(--white); }
.score.muted { background: var(--medium-gray); color: var(--text-secondary); }

/* Wide-screen tweak */
@media (max-width: 1100px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  .measures { grid-template-columns: repeat(3, 1fr); }
  .detail__grid { grid-template-columns: 1fr; }
  /* 16.69.0 (⑧d §12): one column is one stack either way. */
  .detail__grid--packed { columns: 1; }
  .controls__layout { grid-template-columns: 1fr; }
}

/* ── Evidence Library page ──────────────────────────────────────────── */
.evlib .card { padding: 20px 22px; }
.evlib__upload { margin-bottom: 18px; }
.evlib__upload-form {
  display: grid; grid-template-columns: 1fr auto; gap: 14px; align-items: center;
}
.evlib__dropzone {
  display: block;
  border: 2px dashed var(--medium-gray);
  background: var(--off-white);
  border-radius: 10px;
  padding: 18px 20px;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.evlib__dropzone:hover { border-color: var(--teal); background: #f7fbfa; }
/* 2026-08-12: the zone answers back. Picked files turn the dashes solid
   green and the title lists the names; a drag hover lights the target; the
   submit button spins while the classify step runs. */
.evlib__dropzone--picked { border-style: solid; border-color: var(--green); background: #f4faf7; }
.evlib__dropzone.is-over { border-color: var(--green); background: #eef8f3; }
.btn.is-busy { opacity: .75; cursor: progress; }
.btn.is-busy::after {
  content: ""; display: inline-block; vertical-align: -2px; margin-left: 8px;
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .45); border-top-color: #fff;
  animation: evspin 700ms linear infinite;
}
@keyframes evspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn.is-busy::after { animation: none; } }
.evlib__dropzone input[type="file"] {
  position: absolute; opacity: 0; pointer-events: none;
  width: 1px; height: 1px;
}
.evlib__dropzone-inner { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; align-items: center; }
.evlib__dropzone-icon { font-size: 28px; grid-row: span 2; }
.evlib__dropzone-title { font-weight: 600; color: var(--navy); font-size: 14px; }
.evlib__dropzone-sub { color: var(--text-muted); font-size: 12px; }

/* 17.5.0 — the "Connected sources" strip, between the upload zone and the
   list. One row per configured connector; at 375px the row's four parts
   stack and nothing scrolls sideways. */
.evsrc {
  background: var(--white); border: 1px solid var(--light-gray);
  border-radius: 10px; padding: 16px 18px; margin: 0 0 18px;
}
.evsrc__head { margin: 0 0 10px; }
.evsrc__head h2 { margin: 0; color: var(--navy); font-size: 15px; font-weight: 700; }
.evsrc__head .subtle { margin: 2px 0 0; font-size: 12px; }
.evsrc__note { margin: 0 0 12px; }
.evsrc__rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.evsrc__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  gap: 6px 12px; align-items: center;
  border: 1px solid var(--light-gray); border-radius: 8px;
  padding: 10px 12px; background: var(--off-white);
}
.evsrc__icon { color: var(--teal); display: flex; align-items: center; }
.evsrc__who { min-width: 0; }
.evsrc__name { font-weight: 600; color: var(--navy); font-size: 14px; }
.evsrc__locator {
  font-size: 12px; color: var(--text-secondary);
  overflow-wrap: anywhere; word-break: break-word;
}
.evsrc__meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
.evsrc__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.evsrc__chip {
  display: inline-block; font-size: 11px; font-weight: 600;
  padding: 2px 8px; border-radius: 999px;
  background: var(--light-gray); color: var(--text-secondary);
}
.evsrc__chip--ok { background: #e8f5f1; color: #0d6b57; }
.evsrc__chip--warn { background: #fdf0e3; color: #94540d; }
.evsrc__acts { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.evsrc__acts form { margin: 0; }
.evsrc__why {
  grid-column: 1 / -1; margin: 4px 0 0;
  font-size: 12px; color: var(--text-secondary);
}

/* ── 17.6.0 — the Implementation proof ledger ──────────────────────────
   docs/evidence_proof_spec.md; the mock's states, in the app's tokens.
   The four status chips are the whole visual language of this table, so
   they are named for what they MEAN (current / due / overdue / automated)
   and reused by the summary line, the history and the removal confirm. */
.chip--ok   { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.chip--due  { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.chip--over { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.chip--auto { background: var(--tint-navy-bg); color: var(--tint-navy-ink); }
.chip--ctl  { background: var(--tint-navy-bg); color: var(--tint-navy-ink); font-weight: 700; }

.evlib__views { margin: 6px 0 10px; }

/* 17.6.1 — the segmented stat strip (evidence_header_mock.html). Grey at
   rest; a cell only takes color when its number means something. */
.evproof__sum { display: flex; flex-wrap: wrap; width: max-content; max-width: 100%;
  margin: 14px 0; border: 1px solid var(--light-gray); border-radius: 10px; overflow: hidden; }
.evproof__sum .s { display: flex; align-items: baseline; gap: 7px; padding: 10px 18px;
  border-right: 1px solid var(--light-gray); font-size: 11.5px; color: var(--text-muted); }
.evproof__sum .s:last-child { border-right: 0; }
.evproof__sum b { font-size: 17px; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.evproof__sum .s.is-bad b { color: #b91c1c; }
.evproof__sum .s.is-warn b { color: #b45309; }
.evproof__sum .s.is-ok b { color: var(--teal-dark); }
.evproof__sum .s--auto { background: var(--off-white); }
.evproof__sum .s--auto span { max-width: 20em; }
/* 17.44.0 (feedback batch ⑥): the key-measure lens above the ledger. One
   row, one chip, and it borrows the findings toolbar's own .rz-fchip family
   rather than growing a second chip look for the same control - the only
   thing this rule adds is the row's spacing and the fact that an <a> has to
   be told it is not a link. */
.evproof__filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
.evproof__filters .rz-fchip { text-decoration: none; }
/* The header contract: title left, toggle right, ONE subtitle line under.
   17.6.2 root cause of the "sloppy" render: .page-header is itself a flex
   ROW with space-between, so the title row shrank to content width on the
   left and the subtitle became the second flex item pushed to the far
   right. The header goes column here and the row spans full width - which
   is also what lets its own space-between put the pills on the right. */
.page-header.evhead { flex-direction: column; align-items: stretch; gap: 0; }
.evhead__row { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; width: 100%; }
.evhead__row h1 { margin: 0; }
.evhead__sub { max-width: 72ch; margin-top: 6px; }
.evhead__sub a { font-weight: 600; }
/* The pills, bulletproofed against the global link states that made the
   active pill render underlined: scoped, higher specificity, every state. */
.evlib__views { margin: 0; }
.evlib__views .rz-gopt,
.evlib__views .rz-gopt:hover,
.evlib__views .rz-gopt:focus { white-space: nowrap; text-decoration: none; }
.evlib__views .rz-gopt.is-on,
.evlib__views .rz-gopt.is-on:hover,
.evlib__views .rz-gopt.is-on:focus {
  background: var(--teal); border-color: var(--teal); color: var(--white); }

.evproof__t { width: 100%; border-collapse: collapse; font-size: 13px; }
.evproof__t th {
  text-align: left; font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-muted); padding: 8px 10px; border-bottom: 1px solid var(--medium-gray);
}
.evproof__t td { padding: 10px; border-bottom: 1px solid var(--light-gray); vertical-align: top; }
.evproof__name { font-weight: 700; color: var(--navy); }
.evproof__what { color: var(--text-secondary); font-size: 11.5px; margin-top: 2px; max-width: 46ch; }
.evproof__why { color: var(--text-secondary); font-size: 11.5px; margin-top: 4px; font-style: italic; }
.evproof__row--auto, .evproof__row.is-off { background: var(--off-white); }
.evproof__row--auto .evproof__name { color: var(--text-secondary); }
.evproof__acts { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }
.evproof__secthead td {
  font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-muted); padding: 16px 10px 6px;
}

/* The server-rendered panels: attach, settings, history, the confirm. */
.evproof__panelrow td { background: var(--off-white); padding: 0; }
.evproof__panel {
  display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: flex-end;
  padding: 14px 12px; margin: 0;
}
.evproof__panel--slim { padding-top: 0; align-items: center; }
.evproof__panel--warn { border-left: 3px solid var(--tint-crit-ink); display: block; }
.evproof__panelh { flex: 1 0 100%; margin: 0; font-weight: 700; color: var(--navy); }
.evproof__panel > p { flex: 1 0 100%; margin: 0; font-size: 12.5px; }
.evproof__f { display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
.evproof__f > span { color: var(--text-secondary); font-weight: 600; }
.evproof__f--wide { flex: 1 1 260px; }
.evproof__f--wide input { width: 100%; }
.evproof__f--check { flex-direction: row; align-items: center; gap: 7px; }
.evproof__hint { flex: 1 0 100%; margin: 0; font-size: 11.5px; color: var(--text-secondary); }
.evproof__panelacts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.evproof__panelacts form { margin: 0; }

.evproof__hist { list-style: none; margin: 0; padding: 0; flex: 1 0 100%; }
.evproof__histrow {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 7px 0; border-bottom: 1px solid var(--light-gray); font-size: 12.5px;
}
.evproof__histrow.is-gone { opacity: .65; text-decoration: line-through; }
.evproof__histrow.is-gone .chip, .evproof__histrow.is-gone .btn { text-decoration: none; }

/* The three-way upload question. Three equal doors, and the pre-selected
   one is marked - never pre-submitted. */
.evproof__ask { padding: 14px 16px; margin-bottom: 14px; }
.evproof__askq { font-size: 14px; color: var(--navy); margin: 0 0 10px; }
.evproof__doors { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.evproof__door {
  border: 1.5px solid var(--medium-gray); border-radius: 9px; padding: 10px 12px;
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
}
.evproof__door--sel { border-color: var(--teal); background: var(--off-white); }
.evproof__door b { font-size: 12.5px; color: var(--navy); }
.evproof__door span { font-size: 11.5px; color: var(--text-secondary); }
.evproof__askform { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.evproof__askform select, .evproof__askform input { width: 100%; }
.evproof__add { margin-top: 14px; }
.evproof__add > summary { cursor: pointer; font-weight: 650; color: var(--navy); font-size: 13px; }

.evlib__list-head { display: flex; align-items: baseline; justify-content: space-between; margin: 6px 2px 12px; }
.evlib__list-head h2 { margin: 0; color: var(--navy); font-size: 15px; font-weight: 700; }

.evlib__empty {
  text-align: center; padding: 36px 20px;
  color: var(--text-secondary);
}
.evlib__empty-icon { font-size: 32px; opacity: 0.6; }
.evlib__empty-title { font-weight: 600; margin-top: 8px; color: var(--navy); }
.evlib__empty-sub { font-size: 13px; margin-top: 4px; }

/* ── Document card ──────────────────────────────────────────────────── */
/* 16.57.0 (⑦b §1): collapsed by default. The card is now a summary row —
   name, validity pill, mapping count — over a body that only exists while
   the disclosure is open, so the padding lives on the two halves rather
   than on the article (a collapsed row is a row, not a box with 20px of
   air under one line of text). */
/* `.evlib .card` (the upload panel's own padding) outranks a bare
   `.doc-card`, so the reset has to be written at the same weight. */
.evlib .doc-card { margin-bottom: 8px; padding: 0; }
.doc-card__summary {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px 10px 12px;
  cursor: pointer; list-style: none;
  border-radius: 10px;
}
.doc-card__summary::-webkit-details-marker { display: none; }
.doc-card__summary:hover { background: var(--off-white); }
.doc-card__summary::after {
  content: "▾"; margin-left: auto; flex: 0 0 auto;
  color: var(--text-muted); font-size: 12px;
}
.doc-card__disclose[open] > .doc-card__summary::after { content: "▴"; }
.doc-card__disclose[open] > .doc-card__summary {
  border-bottom: 1px solid var(--medium-gray);
  border-radius: 10px 10px 0 0;
}
.doc-card__body { padding: 12px 14px 14px; }
.doc-card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  padding-bottom: 10px; margin-bottom: 12px;
  border-bottom: 1px solid var(--medium-gray);
}
.doc-card__icon { line-height: 0; flex: 0 0 auto; color: var(--text-secondary); }
/* 17.6.3 — the card language (mock: doccard frames): two-line identity on
   the left (bold navy name over one muted meta line), the state chips
   clustered on the right, in front of the caret. Same facts, arranged. */
.doc-card__id { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.doc-card__name {
  font-weight: 700; color: var(--navy); font-size: 13.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; max-width: 540px;
}
.doc-card__sub { font-size: 11.5px; color: var(--text-muted); }
.doc-card__state { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  justify-content: flex-end; flex: 0 1 auto; margin-left: auto; }
.doc-card__meta { font-size: 11.5px; color: var(--text-muted); }
.doc-card__count { font-size: 12px; color: var(--text-secondary); white-space: nowrap; }

.doc-card__mappings {
  list-style: none; padding: 0; margin: 0 0 12px;
  display: grid; gap: 10px;
}
.mapping {
  background: var(--off-white);
  border: 1px solid var(--medium-gray);
  border-radius: 8px;
  padding: 10px 12px;
}
.mapping__head { display: flex; align-items: center; gap: 10px; }
.mapping__pill {
  display: inline-block;
  background: var(--navy); color: var(--white);
  font-weight: 700; font-size: 12px;
  padding: 3px 9px; border-radius: 999px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  white-space: nowrap;
}
.mapping__pill:hover { background: var(--teal); text-decoration: none; }
.mapping__sub {
  font-size: 13px; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  flex: 1; min-width: 0;
}
.mapping__remove { margin-left: auto; }
.iconbtn {
  background: transparent; border: 0; padding: 4px 8px;
  color: var(--text-muted); cursor: pointer; font-size: 14px;
  border-radius: 6px; line-height: 1;
}
.iconbtn:hover { background: var(--medium-gray); color: var(--red); }
.mapping__why {
  margin-top: 6px; margin-left: 0;
  font-size: 12.5px; color: var(--text-secondary); font-style: italic;
  border-left: 3px solid var(--teal); padding-left: 10px;
}

.doc-card__foot { padding-top: 6px; }
.add-mapping {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center;
}
.add-mapping__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.add-mapping__select {
  padding: 6px 10px; border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white); font-size: 13px; min-width: 0;
}
.btn--sm { padding: 6px 14px; font-size: 13px; }

.chip { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 11.5px; font-weight: 600; }
.chip--warn { background: #FFF4E5; color: #B7791F; border: 1px solid #F6E2B8; }
/* 17.4.0 — client-document mapping. A suggestion is a QUESTION, so it is
   the quiet teal outline rather than anything that reads as a result; a
   confirmed one is green because a person said yes to it. Neither is a
   status pill: adoption is 17.4.1 and nothing here claims it. */
/* …and they never wrap: a pill with a line break in it renders as a blob,
   which is exactly what "2 sugestii pentru registru" did at 375px. */
.chip--slot { background: #EAF6F8; color: #10707F; border: 1px solid #C7E5EA; white-space: nowrap; }
.chip--slot-ok { background: #E9F7EF; color: #1E7A46; border: 1px solid #C6E9D5; white-space: nowrap; }

/* ── 17.4.0 — registry-slot suggestions in the evidence library ──────
   The same card-in-a-list shape as .mapping above it, so the two lists in
   one disclosure read as two answers to the same question ("what does
   this document cover?") rather than as two different widgets. */
.doc-card__slots { list-style: none; padding: 0; margin: 0 0 12px; display: grid; gap: 10px; }
.slotmap {
  background: var(--off-white);
  border: 1px solid var(--medium-gray);
  border-left: 3px solid var(--teal);
  border-radius: 8px;
  padding: 10px 12px;
}
.slotmap--confirmed { border-left-color: var(--green); }
.slotmap__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.slotmap__label { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.slotmap__slot { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.slotmap__acts { display: flex; gap: 8px; margin-left: auto; }
.slotmap__done { margin-left: auto; font-size: 11.5px; font-weight: 700; color: var(--green); }
.slotmap__why {
  margin-top: 6px; font-size: 12.5px; color: var(--text-secondary);
  font-style: italic;
}
.slotmap__note { margin: 0 0 12px; font-size: 11.5px; color: var(--text-muted); }
/* 17.4.1 adoption. The adopted row is the settled one, so it is the only
   solid-left card in the list; the replace confirmation is the loud one,
   because replacing a document a slot already has is the single
   irreversible-feeling action on this page. Colour and spacing only - the
   375px row rules above are untouched, and .slotmap__confirm wraps for the
   same reason .slotmap__head does. */
.slotmap--adopted { border-left-color: #1E7A46; background: #F3FAF5; }
.slotmap__why--bad { color: var(--red); font-style: normal; }
.slotmap__confirm {
  margin-top: 8px; padding: 8px 10px; border-radius: 6px;
  background: #FDF6E3; border: 1px solid #F0D98A;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.slotmap__confirm p { margin: 0; font-size: 12.5px; color: #6b5400; }

/* ── 17.4.5 — remove, and same-name versions ─────────────────────────
   Two pieces of new markup on this page, both written to the 375px rules
   the 17.2.2 audit set: no fixed width anywhere, every row a wrapping
   flex, and the one string a customer cannot shorten (a filename, a slot
   title) allowed to break rather than to inflate the viewport.

   The remove confirmation is the LOUD one - it is the only irreversible
   action in the product - so it borrows the replace question's shape and
   turns it red, and its two paragraphs are the ones that name what the
   removal reaches. */
.doc-rm__confirm {
  margin: 0 0 12px; padding: 10px 12px; border-radius: 8px;
  background: #FDF1F1; border: 1px solid #F3C9C9;
}
.doc-rm__q { margin: 0; font-size: 13px; font-weight: 700; color: #8A2B2B; }
.doc-rm__what, .doc-rm__sever {
  margin: 6px 0 0; font-size: 12.5px; color: var(--text-secondary);
  overflow-wrap: anywhere;
}
.doc-rm__sever strong { color: var(--text-primary); }
.doc-rm__acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* The version disclosure. A `details` inside the card's own `details`, the
   same idiom as .ev-validity-edit beside it, so no new interaction has to
   be learned and the browser keeps the open/closed state per element. */
.doc-vers { margin: 0 0 12px; }
.doc-vers__toggle {
  cursor: pointer; font-size: 12px; font-weight: 600; color: var(--teal);
  list-style: none; padding: 4px 0;
}
.doc-vers__toggle::-webkit-details-marker { display: none; }
.doc-vers__toggle::before { content: "▾ "; }
.doc-vers[open] > .doc-vers__toggle::before { content: "▴ "; }
.doc-vers__list {
  list-style: none; padding: 0; margin: 4px 0 0;
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px;
}
.doc-ver {
  min-width: 0; padding: 8px 10px; border-radius: 8px;
  background: var(--off-white); border: 1px solid var(--medium-gray);
}
.doc-ver__head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12px; color: var(--text-muted);
}
.doc-ver__by { overflow-wrap: anywhere; }
.doc-ver__sha {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; color: var(--text-secondary);
}
.doc-ver__acts { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }
.doc-ver__chips {
  display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px;
}
/* A slot title is arbitrary text, so the chip that carries one wraps here
   even though .chip--slot is nowrap on the row it was designed for. */
.doc-ver__chips .chip { white-space: normal; overflow-wrap: anywhere; }
.doc-card__vers { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; }
/* The hint (spec: a hint, never an action) - quiet on the summary row and
   spelled out in the body, where there is room to name the documents. */
.chip--hint { background: #FBF3E0; color: #7A5B10; border: 1px solid #EBDBB0; }
.doc-vers__inuse {
  margin: 0 0 10px; font-size: 12.5px; color: var(--text-secondary);
  overflow-wrap: anywhere;
}

/* ── 17.4.4 — the three-door decision overlay ────────────────────────
   docs/mockups/confirm_flow_mock.html, in this stylesheet's own variables.
   One modal, two mounts (evidence library, document editor), so all of it
   lives on .dov* and neither surface owns a rule.

   The 375px promise is built in rather than bolted on below: every measure
   here is a max-width or a percentage, the option card is a 2-column grid
   whose text column is minmax(0, 1fr), and the footer is a wrapping flex
   row. The @media block at the foot of this file only has to stack the
   option's icon and drop the dialog's outer margin. */
.dov-wrap { position: fixed; inset: 0; z-index: 80; display: grid;
  place-items: start center; overflow-y: auto; padding: 24px 12px; }
/* The workspace ships this dialog in the page HIDDEN (entry B opens it on a
   drop). An author display rule beats the UA's [hidden] { display:none }, so
   without this line the "hidden" overlay is visible on every editor load -
   with no file behind it, so any door 404s (found live, 2026-08-12). */
.dov-wrap[hidden] { display: none; }
/* 2026-08-12: a chosen door says what it is doing. The clicked card keeps
   its color and gains a spinner; the other doors and Cancel go quiet. */
.dov__opt.is-busy { border-color: var(--green); cursor: progress; }
.dov__opt.is-busy .dov__go { visibility: hidden; }
.dov__opt.is-busy .dov__t::after {
  content: ""; display: inline-block; vertical-align: -2px; margin-left: 8px;
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid rgba(0, 147, 96, .3); border-top-color: var(--green);
  animation: evspin 700ms linear infinite;
}
@media (prefers-reduced-motion: reduce) { .dov__opt.is-busy .dov__t::after { animation: none; } }
.dov__opt[disabled] { opacity: .5; cursor: default; }
.dov--busy .dov__cancel { pointer-events: none; opacity: .5; }
.dov-dim { position: fixed; inset: 0; background: rgba(10, 20, 45, .45); }
.dov {
  position: relative; width: 100%; max-width: 660px;
  background: var(--white); border-radius: 14px;
  box-shadow: 0 18px 60px rgba(0, 31, 91, .35);
  padding: 20px 22px 18px;
}
.dov__eyebrow { font-size: 10.5px; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--green); }
.dov__title { font-size: 17px; font-weight: 800; color: var(--navy);
  margin: 3px 0 0; }
.dov__doc {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 12px 0 4px; background: var(--off-white);
  border: 1px solid var(--medium-gray); border-radius: 9px;
  padding: 8px 11px; font-size: 12.5px;
}
.dov__file { font-weight: 700; color: var(--navy); word-break: break-word; }
.dov__arrow { color: var(--text-muted); }
.dov__slot { font-weight: 700; color: var(--green); }
.dov__sub { color: var(--text-muted); font-size: 12px; margin: 8px 0 14px; }
.dov__opts { display: grid; gap: 10px; }
.dov__form { margin: 0; }
.dov__opt {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; gap: 12px;
  align-items: center; text-align: left; width: 100%;
  border: 1.5px solid var(--medium-gray); border-radius: 11px;
  padding: 12px 14px; background: var(--white); cursor: pointer;
  font: inherit; color: inherit;
}
.dov__opt:hover, .dov__opt:focus-visible { border-color: var(--green); background: #F7FCFA; }
.dov__opt--rec { border-color: var(--green); }
.dov__ic { display: grid; place-items: center; width: 40px; height: 40px;
  border-radius: 9px; background: var(--light-gray); font-size: 17px;
  color: var(--navy); }
.dov__opt--rec .dov__ic { background: #E7F5EE; color: #1E7A46; }
.dov__body { display: block; min-width: 0; }
.dov__t { display: block; font-weight: 700; color: var(--navy); font-size: 13.5px; }
.dov__rec { font-size: 10px; font-weight: 800; color: #1E7A46;
  border: 1px solid #BFE6D3; border-radius: 999px; padding: 1px 8px;
  margin-left: 6px; white-space: nowrap; }
.dov__d { display: block; color: var(--text-secondary); font-size: 12px; margin-top: 2px; }
.dov__go { color: var(--text-muted); font-size: 16px; }
.dov__foot {
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px; flex-wrap: wrap; margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--medium-gray);
}
.dov__hint { color: var(--text-muted); font-size: 11.5px; max-width: 30em; }
.dov__cancel { background: none; border: 0; padding: 0; color: var(--text-muted);
  font: inherit; font-size: 12.5px; cursor: pointer; text-decoration: underline; }

/* ── Evidence validity (16.49.0) ─────────────────────────────────────
   Four states, outline pills so a shelf of them stays readable: ok is
   quiet green, expired is the only loud one, aging is the amber nudge,
   unset is deliberately neutral (no date is not a finding). */
/* 16.52.0: folded into the one tinted label system (skin spec §E). The
   four states keep their meaning; only the outline-pill styling went. */
.ev-pill {
  display: inline-block; padding: 2.5px 10px; border-radius: 6px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
.ev-pill--ok { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.ev-pill--expired { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.ev-pill--aging { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.ev-pill--unset { background: var(--tint-mute-bg); color: var(--tint-mute-ink); }

.evlib__validity { display: grid; gap: 4px; align-content: start; font-size: 12px; }
.evlib__validity-label { font-weight: 600; color: var(--navy); }
.evlib__validity-hint { color: var(--text-muted); }
.evlib__validity input[type="date"] {
  padding: 6px 10px; border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white); font-size: 13px;
}

.ev-validity-edit { font-size: 12px; }
.ev-validity-edit__toggle {
  cursor: pointer; color: var(--text-muted); list-style: none;
  border-bottom: 1px dotted var(--medium-gray);
}
.ev-validity-edit__toggle:hover { color: var(--teal); }
.ev-validity-edit__form {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px;
}
.ev-validity-edit__label { color: var(--text-muted); }
.ev-validity-edit__hint { flex-basis: 100%; color: var(--text-muted); }
.ev-validity-edit input[type="date"] {
  padding: 5px 8px; border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white); font-size: 12.5px;
}

.upload-form__validity { display: grid; gap: 4px; font-size: 12px; margin: 8px 0; }
.upload-form__validity input[type="date"] {
  padding: 6px 10px; border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white); font-size: 13px; justify-self: start;
}

@media (max-width: 900px) {
  .evlib__upload-form { grid-template-columns: 1fr; }
  .add-mapping { grid-template-columns: 1fr; gap: 6px; }
  .doc-card__name { max-width: 100%; }
}

/* ── Templates gallery + form ───────────────────────────────────────── */
.tplgallery .page-header { margin-bottom: 18px; }


.tplgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}
.tplcard {
  background: var(--white);
  border: 1px solid var(--medium-gray);
  border-radius: 10px;
  padding: 18px 20px;
  display: flex; flex-direction: column;
  transition: border-color 120ms ease, transform 120ms ease;
}
.tplcard:hover { border-color: var(--teal); }
.tplcard__title { margin: 0 0 6px; font-size: 14px; font-weight: 700; color: var(--navy); }
.tplcard__desc { margin: 0 0 12px; font-size: 13px; color: var(--text-secondary); line-height: 1.4; flex: 1; }
.tplcard__targets {
  display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px;
}
.tplchip {
  display: inline-block; padding: 3px 9px; border-radius: 999px;
  background: var(--light-gray); color: var(--navy);
  font-size: 11px; font-weight: 600;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  border: 1px solid var(--medium-gray);
}
.tplcard__foot {
  display: flex; align-items: center; justify-content: space-between;
  border-top: 1px solid var(--medium-gray); padding-top: 12px;
}
.tplcard__meta { color: var(--text-muted); font-size: 12px; }

.formfield { display: grid; gap: 4px; }
/* Checkbox fields: box inline-LEFT of the label, hint indented beneath it
   (these labels wrap the checkbox + a label span + an optional hint span,
   so each is pinned to an explicit cell — a bare 2-col grid mis-packs the
   wide hint into column 1). Covers ai_personalize + narrative grounding. */
.formfield__label { font-size: 13px; font-weight: 600; color: var(--navy); }
.formfield input[type="text"],
.formfield textarea {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white);
  font-size: 14px; font-family: inherit;
}
.formfield input:focus, .formfield textarea:focus {
  outline: none; border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(42, 157, 143, 0.15);
}
.formfield .required { color: var(--red); }

/* ── CyFun controls — defensive text-align ─────────────────────────── */
.controls__layout, .tree, .cat__name, .sub__name, .req__title,
.hint, .hint h2, .hint p,
.requirement-body { text-align: left; }
/* When a category/sub title is long, let it fill its space then ellipsis */
.cat__name, .sub__name { flex: 1; min-width: 0; }
.cat > summary, .sub > summary { text-align: left; }

/* ── Evidence library — doc-card actions block ─────────────────────── */
.doc-card__actions {
  display: flex; align-items: center; gap: 10px;
  white-space: nowrap;
}
.doc-card__count { font-size: 12px; color: var(--text-secondary); }

/* ── Progress page ────────────────────────────────────────────────── */
.progress-page .page-header { margin-bottom: 18px; }
/* 17.44.0 (feedback batch ④): five cards, not four - the key-measure floor
   is the first of DNSC's two conformity conditions, so the page that answers
   "where do we stand" prints it beside the four it already did. The card
   itself is a .prog-summary__card like its siblings; only the track count
   changes here, and the ≤1000px pair below is unaffected. */
.prog-summary {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px;
  margin-bottom: 18px;
}
.prog-summary__card { padding: 14px 16px; }
.prog-summary__label {
  font-size: 11px; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.prog-summary__value {
  font-size: 26px; font-weight: 700; color: var(--navy);
  margin-top: 4px; line-height: 1.1;
}
.prog-summary__sub {
  font-size: 13px; color: var(--text-muted); margin-left: 4px; font-weight: 600;
}
/* 17.44.0 (④): the key card's label carries the branded key mark - the same
   `icon('key')` stroke SVG the rest of the product uses, never the 🗝 the
   mock drew (tests/test_no_emoji_icons.py, 16.4.4). Gold, like every other
   key affordance, and aligned with the uppercase label beside it. */
.prog-summary__label--key { display: flex; align-items: center; gap: 5px; }
.prog-summary__label--key .ic { color: var(--key-ink); flex: 0 0 auto; }
/* 17.44.0 (④): the key card's number goes red while a key measure is below
   target - the one accent the card takes beyond the 🗝 in its label. Scoped
   to this card rather than wiring up the inert `tl-bad` its four siblings
   carry: that would have reddened three numbers this batch was not asked to
   touch. Same ink as every other failing number on the page. */
.prog-summary__value--keybad { color: var(--red); }

.prog-bar {
  margin-top: 8px;
  background: var(--medium-gray); border-radius: 999px;
  height: 6px; overflow: hidden;
}
.prog-bar__fill {
  background: var(--teal); height: 100%; border-radius: 999px;
  transition: width 200ms ease;
}

.prog-functions__head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 12px;
}
.prog-functions__head h2 {
  margin: 0; font-size: 14px; font-weight: 700; color: var(--navy);
}

.prog-fn-table {
  width: 100%; border-collapse: collapse; font-size: 13px;
}
.prog-fn-table th, .prog-fn-table td {
  text-align: left; padding: 10px 12px;
  border-bottom: 1px solid var(--medium-gray);
}
.prog-fn-table th {
  color: var(--text-secondary); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em; font-size: 11px;
}
.prog-fn-table tbody tr:last-child td { border-bottom: 0; }

.prog-fn-pill {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  background: var(--accent, var(--navy)); color: var(--white);
  font-weight: 700; font-size: 11px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  margin-right: 8px;
}
.prog-score {
  display: inline-block; min-width: 36px;
  padding: 3px 10px; border-radius: 4px;
  font-weight: 700; font-size: 12.5px; text-align: center;
}
.prog-score--good  { background: var(--green); color: var(--white); }
.prog-score--mid   { background: var(--amber); color: #4D2C00; }
.prog-score--bad   { background: var(--red); color: var(--white); }
.prog-score--muted { background: var(--medium-gray); color: var(--text-secondary); }

.prog-nextsteps { padding: 18px 22px; }
.prog-gaps { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.prog-gap {
  display: grid; grid-template-columns: auto 1fr auto;
  gap: 12px; align-items: center;
  padding: 10px 12px;
  background: var(--off-white); border: 1px solid var(--medium-gray);
  border-radius: 8px;
}
.prog-gap__pill {
  display: inline-block; padding: 3px 9px; border-radius: 999px;
  background: var(--navy); color: var(--white);
  font-weight: 700; font-size: 12px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.prog-gap__pill:hover { background: var(--teal); text-decoration: none; }
.prog-gap__title { color: var(--text-primary); font-size: 13.5px; }
.prog-gap__metric { font-size: 12.5px; color: var(--text-secondary); white-space: nowrap; }
.prog-gap__delta { color: var(--red); font-weight: 700; }

@media (max-width: 1000px) {
  .prog-summary { grid-template-columns: repeat(2, 1fr); }
}

/* ── Template form: select + hint ──────────────────────────────────── */
.formfield select {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white);
  font-size: 14px; font-family: inherit;
  color: var(--text-primary);
}
.formfield select:focus {
  outline: none; border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(42, 157, 143, 0.15);
}
.formfield__hint {
  font-size: 12px; color: var(--text-muted); margin-top: 4px;
  font-style: italic;
}

/* ── Progress charts ───────────────────────────────────────────────── */
/* ── Progress redesign (16.20.3): general radar + doc/impl drill-down ── */
.prog-general { padding: 18px 22px; }
.prog-genrow { display: grid; grid-template-columns: 300px 1fr; gap: 18px; align-items: start; }
.prog-radarwrap { display: flex; flex-direction: column; align-items: center; }
.prog-leg { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; margin-top: 12px; font-size: 11px; color: var(--text-secondary); }
.prog-leg .sw { display: inline-block; width: 11px; height: 11px; border-radius: 3px; margin-right: 5px; vertical-align: -1px; }
.prog-leg .sw-avg { background: var(--teal); } .prog-leg .sw-tgt { background: #9BA8C8; }
.prog-leg--wide { justify-content: flex-start; margin: -4px 0 16px; }
.sw.tl-good, .rdot.tl-good { background: var(--green); fill: var(--green); }
.sw.tl-mid, .rdot.tl-mid { background: var(--amber); fill: var(--amber); }
.sw.tl-bad, .rdot.tl-bad { background: var(--red); fill: var(--red); }
.sw.tl-na, .rdot.tl-na { background: var(--medium-gray); fill: var(--medium-gray); }
.rdot { stroke: #fff; stroke-width: 1.3; }
/* maturity rows (function / category / control) */
.mrow { display: grid; grid-template-columns: minmax(150px, 200px) 1fr 50px; align-items: center; gap: 10px; padding: 6px 4px; border-radius: 7px; }
.mfn, .mcat { cursor: pointer; }
.mfn:hover, .mcat:hover { background: var(--light-gray); }
.mlabel { font-size: 13px; font-weight: 600; color: var(--navy); display: flex; align-items: center; min-width: 0; }
.mlabel.mcat-l { font-size: 12px; font-weight: 500; color: var(--text-secondary); }
.mlabel > .prog-fn-pill { margin-right: 8px; }
.mchev { display: inline-block; width: 14px; flex: none; color: var(--text-muted); font-size: 10px; }
.mtrack { position: relative; height: 9px; border-radius: 5px; background: var(--medium-gray); }
.mfill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 5px; }
.mfill.tl-good { background: var(--green); } .mfill.tl-mid { background: var(--amber); }
.mfill.tl-bad { background: var(--red); } .mfill.tl-na { background: var(--medium-gray); }
.mtmark { position: absolute; top: -3px; width: 2px; height: 15px; background: var(--navy); border-radius: 1px; }
.mval { font-size: 12px; font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }
.mval.tl-good { color: #1b7f4b; } .mval.tl-mid { color: #8a6d00; } .mval.tl-bad { color: var(--red); } .mval.tl-na { color: var(--text-muted); }
.mkids { display: none; padding: 0 0 4px 22px; }
.mkids.show { display: block; }
.mkids .mkids { padding-left: 16px; }
/* 16.59.0: "n/m scored" pushed to the right edge of the label column, so the
   scoped bar next to it is never read as a measured value on its own. */
.mcov { margin-left: auto; padding-left: 8px; font-size: 10.5px; font-weight: 600; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.mctl { grid-template-columns: minmax(120px, 170px) 1fr 46px; }
.mctl-id { font-family: var(--mono, monospace); font-size: 10.5px; font-weight: 700; color: var(--navy); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mctl-id:hover { color: var(--teal-dark); }
/* 17.44.0 (feedback batch ④): the control row's id cell holds the id AND,
   when the framework designates it, the KEY badge. A flex wrapper inside the
   FIRST grid column - exactly what .mlabel already is one row up - so the
   three .mrow columns that align the whole page are untouched, and the id
   keeps its own ellipsis. */
.mctl-l { display: flex; align-items: center; gap: 5px; min-width: 0; }
.mctl-l .keybadge { font-size: 8.5px; padding: 0 5px; }
/* The per-function key column on the maturity bars: the product's gold key
   pill (.keybadge, which this borrows whole) with the pass/fail ink of the
   traffic lights it sits beside. Red until every key measure in the function
   is at target, green at y/y - a floor, so there is no amber. */
.prog-fnbars .mrow--key { grid-template-columns: minmax(150px, 200px) 1fr 50px 62px; }
.mkey { font-variant-numeric: tabular-nums; text-align: center; justify-content: center; }
a.mkey:hover { text-decoration: none; filter: brightness(.97); }
.mkey--bad { color: var(--red); }
.mkey--ok { color: #1b7f4b; }
.mkey--none { font-size: 10.5px; color: var(--text-muted); text-align: center; }
.prog-leg .sw-key { background: var(--key-bg); border: 1px solid var(--key-line); }
/* 16.64.0 (⑦j): the grouping toggle for the two trees. Right-aligned above
   them so it reads as a control OVER the pair rather than a header of the
   left-hand card; the switch itself is ⑦c's .rz-gsel, unchanged. A measure
   pill has no framework colour to inherit (functions do), so it takes the
   navy the .prog-fn-pill default already falls back to. */
.prog-grouprow { display: flex; justify-content: flex-end; margin: 0 0 8px; }
.prog-fn-pill--ms { letter-spacing: .02em; }
.prog-two { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; margin-bottom: 14px; }
.prog-two > .card { margin-bottom: 0; padding: 16px 18px; }
@media (max-width: 1050px) { .prog-genrow { grid-template-columns: 1fr; } .prog-two { grid-template-columns: 1fr; } }

.radar-svg { display: block; width: 100%; height: auto; max-width: 280px; margin: 0 auto; }


.hbars { list-style: none; padding: 0; margin: 10px 0 0; display: grid; gap: 8px; }
.hbar {
  display: grid; grid-template-columns: 200px 1fr 36px;
  gap: 12px; align-items: center;
}
.hbar__label {
  font-size: 13px; color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hbar__track {
  position: relative;
  height: 14px; background: var(--medium-gray); border-radius: 999px;
  overflow: hidden;
}
.hbar__fill {
  display: block; height: 100%;
  border-radius: 999px; transition: width 200ms ease;
}
.hbar__value {
  font-size: 13px; font-weight: 700; color: var(--navy);
  text-align: right;
}

@media (max-width: 1100px) {
  .hbar { grid-template-columns: 140px 1fr 36px; }
}

/* 16.65.0 (⑧a §D): the .tl__* family is gone. ⑦b kept the EVENT row when
   it removed the Progress timeline because index.html still rendered it
   for the dashboard's "Recent activity" card; that card is now removed
   too, and this was its last caller. Nothing else in the app draws a
   timeline row. */

/* ── Cumulative progress chart ─────────────────────────────────────── */
.prog-cumulative { padding: 18px 22px; margin-bottom: 14px; }
.prog-cumulative__meta {
  display: flex; gap: 32px; margin: 10px 0 14px;
}
.prog-cumulative__big {
  font-size: 22px; font-weight: 700; color: var(--navy);
  line-height: 1.1; margin-top: 2px;
}
.cumulative-svg {
  display: block; width: 100%; height: auto; max-height: 220px;
  background: var(--off-white); border-radius: 8px;
  border: 1px solid var(--medium-gray);
}

/* ── Connections page ─────────────────────────────────────────────── */
/* 16.57.0 (⑦b §4): the ⑥d treatment, applied to the three merged
   Integrations sections. `align-items: start` was already here (16.53.0),
   so the panels never stretched — what was actually being spent was
   PADDING: eight cards at 20/22 with a 16px title and a 14px form gap,
   under section headings and rules that cost another 50px apiece. The
   card titles drop to the ⑥d card-heading size, which also restores the
   hierarchy they had lost against the 16px .section-title above them. */
.conn-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 11px;
  margin-bottom: 11px;
  align-items: start;
}
/* Vendor groups (17.1.0; columns 17.1.1; accordion 17.1.3 mock variant A) —
   each group is a <details> grid item of .conn-grid: chevron + vendor mark +
   name + a language-free ok/total chip in the summary, stacked cards in the
   body. Open by default; collapse state kept per vendor in localStorage. */
details.conn-vendor {
  border: 1px solid var(--medium-gray); border-radius: 10px;
  background: #fbfcfe; align-self: start;
}
details.conn-vendor > summary {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 13px; cursor: pointer; list-style: none; user-select: none;
}
details.conn-vendor > summary::-webkit-details-marker { display: none; }
.conn-chev {
  width: 15px; height: 15px; flex: none;
  color: var(--muted, #6b7280); transition: transform .15s;
}
details.conn-vendor[open] > summary .conn-chev { transform: rotate(90deg); }
.conn-vlogo {
  width: 24px; height: 24px; border-radius: 6px; flex: none;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 800; color: #fff;
}
.conn-vendor__count { margin-left: auto; }
.conn-vendor__body { display: grid; gap: 11px; padding: 2px 12px 12px; }
.conn-group {
  font-size: 12px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted, #6b7280);
  margin: 0;
}
.conn-card { padding: 14px 15px; margin-bottom: 0; }
.conn-card__head {
  display: flex; flex-direction: column; gap: 3px;
  padding-bottom: 8px; margin-bottom: 10px;
  border-bottom: 1px solid var(--medium-gray);
}
.conn-card__title-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.conn-card__title { margin: 0; font-size: 13px; font-weight: 700; color: var(--navy); }
/* `.small` and `.subtle` carry no size of their own anywhere in this
   stylesheet, so every explanatory line on this page was rendering at the
   browser's default 16px — bigger than the card headings it sat under. */
.connections-page .small, .connections-page .subtle,
.connections-page .muted.small { font-size: 12px; line-height: 1.5; }
.connections-page p { margin: 0 0 8px; }

.conn-form { display: grid; gap: 10px; }
.conn-form__actions {
  display: flex; align-items: center; gap: 10px; margin-top: 4px;
  flex-wrap: wrap;
}

@media (max-width: 1100px) {
  .conn-grid { grid-template-columns: 1fr; }
}

.conn-chip {
  display: inline-flex; align-items: center;
  padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 700;
  white-space: nowrap;
}
/* 16.52.0: the scan-history + connector status badges use the one tinted
   label system (skin spec §E). The outline went with it - a tint carries
   the state on its own, and the border was the only thing making a row of
   "Interrupted" chips look like a row of errors. */
.conn-chip--ok    { background: var(--tint-ok-bg);   color: var(--tint-ok-ink);   border: none; }
.conn-chip--saved { background: var(--tint-med-bg);  color: var(--tint-med-ink);  border: none; }
.conn-chip--err   { background: var(--tint-crit-bg); color: var(--tint-crit-ink); border: none; }
.conn-chip--idle  { background: var(--tint-mute-bg); color: var(--tint-mute-ink); border: none; }

.conn-test-result { transition: color 200ms ease; }
.conn-test-result--ok  { color: var(--green-dark); }
.conn-test-result--err { color: var(--red); }

/* 16.65.0 (⑧a §B): the Graph permission manifest, collapsed inside the M365
   card. Same quiet dashed-box idiom as .conn-more below - it is the same kind
   of thing, a list you open when you need it - at card-interior scale. */
.conn-perms {
  margin-top: 11px; padding: 8px 11px;
  background: var(--off-white);
  border: 1px dashed var(--medium-gray); border-radius: 9px;
}
.conn-perms > summary {
  cursor: pointer; font-weight: 600; color: var(--navy); font-size: 12.5px;
}
.conn-perms .perm-list { margin: 8px 0 6px; padding-left: 18px; }
.conn-perms .perm-list li { margin-bottom: 3px; }

.conn-more {
  margin-top: 11px;
  background: var(--off-white);
  border: 1px dashed var(--medium-gray);
  border-radius: 10px;
  padding: 9px 14px;
}
.conn-more > summary {
  cursor: pointer; font-weight: 600; color: var(--navy);
  font-size: 13px;
}
.conn-more__grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 9px; margin-top: 10px;
}
/* 16.57.0 (⑦b §4): SCOPED. The dashboard's connector strip claimed the same
   `.conn-mini` name lower down the file (16.52.0) and, being later, won -
   the Phase-2 cards here were inheriting `padding: 0` and `display: flex`
   from a rule written for a pill list. Scoping to the grid these actually
   live in settles it without renaming a class two pages read. */
.conn-more__grid .conn-mini {
  background: var(--white); border: 1px solid var(--medium-gray);
  border-radius: 8px; padding: 8px 11px;
  display: grid; gap: 3px; margin: 0; list-style: none;
}

/* ── Dashboard ─────────────────────────────────────────────────────── */
/* 16.56.0 (⑦a §e): `align-items: start`. A grid stretches its items by
   default, so the maturity ladder was as tall as whatever the left column
   happened to contain - five rows of content in a card sized by the
   function bars plus the machinery panel, with the difference showing as
   dead white space under "Your score". Nothing inside the ladder was ever
   constrained (no fixed height, no inner scroll); the constraint was the
   ROW. Same one-line fix, and the same reasoning, as the ⑥d card grids. */
/* 17.44.0 (feedback batch ③): `minmax(0, 1fr)`, not `1fr`. The file already
   learned this once, at the ≤640px collapse below: `1fr` is
   `minmax(auto, 1fr)`, so a track is never allowed narrower than its
   content's MIN-content - and the key-measure tile's failing list is a
   nowrap-with-ellipsis line whose min-content contribution is the whole
   requirement title. Measured at 1440px with `1fr 1fr`: the tile claimed
   770px and squeezed the maturity ladder beside it to 350. The ellipsis was
   already there to make this text shrink; `minmax(0, …)` is what lets it. */
/* 17.47.0 (row-alignment feedback): `stretch`, not `start`. ⑥d's `start` was
   the right answer to a DIFFERENT problem — a grid stretching a short card to
   the height of a tall neighbour and leaving the difference as void under its
   last line. The fix for that is not to let the cards end wherever their
   content ends (which is what `start` does, and what made row 2 read as two
   cards of unrelated heights and row 3 as three); it is to stretch them and
   give each one a region that is ALLOWED to carry the extra height. Both
   halves ship together: `stretch` here, the flex columns below. */
.dashboard-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 14px;
  align-items: stretch;
  margin-bottom: 14px;
}
/* 17.44.0 (feedback batch ③): the collage's two rows. Row 2 is the plain
   1fr 1fr above (maturity ladder · key measures); row 3 is half / quarter /
   quarter. Declared HERE, before the ≤1100px collapse below, so the stack
   is inherited from .dashboard-row rather than restated — the modifier owns
   one property and nothing else. */
.dashboard-row--thirds {
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
}
/* 17.47.0 (row-alignment feedback): row 3 opens on ONE heading and ONE top
   rhythm. "By function" wore .prog-functions__head h2 (14px sentence-case
   navy) and the two quarter cards wore .kpi-label (11.5px uppercase muted
   with tracking), so three cards side by side started their content on three
   different lines and read as three unrelated widgets. The shared style is
   By function's, because it is the one a whole page already uses.
   Scoped to the row on purpose: the key-measures tile in row 2 keeps
   .kpi-label, the KPI idiom it was drawn in.
   The three numbers that put the first content row on one line: the same
   card padding, an explicit heading line-height (the qhelp mark inside a
   .kpi-label is 15px and would otherwise size that line box differently from
   an h2's), and 10px of air below the heading - .kpi-card's own column gap,
   which .prog-functions__head's margin is matched to here. */
.dashboard-row--thirds > .card { padding: 18px 22px; }
.dashboard-row--thirds .prog-functions__head { margin-bottom: 10px; }
.dashboard-row--thirds .prog-functions__head h2,
.dashboard-row--thirds .kpi-label {
  font-size: 14px; font-weight: 700; line-height: 1.4; color: var(--navy);
  text-transform: none; letter-spacing: normal;
  /* TWO LINES, always. "Documentation vs implementation" does not fit on one
     line of a quarter-width card and "Where do we stand" does, so without a
     reservation the third card's content started 19px below the second's -
     the mismatch this section exists to remove. It is a line count, not a
     pixel height: it follows the font-size and line-height above, and it
     reserves rhythm rather than fixing the CARD, which is still exactly as
     tall as the row's tallest content. */
  min-height: 2.8em;
}
/* ⑧a §D dropped .dashboard-gaps and .dashboard-recent from this list with
   the two cards that wore them; 17.44.0 dropped .dashboard-conn2 with the
   connections panel, and .dashboard-col with the column that stacked the
   panel under the function bars. */
/* 17.47.0 (row-alignment feedback) — `display: flex` here is the other half
   of `stretch` above. A stretched card has to decide WHERE its extra height
   goes, or it goes under the last line as a void, which is the whole
   complaint. Each card is a flex column and its LIST is the one region
   allowed to grow: the ladder in the maturity card, the function bars in
   "By function", the failing rows in "Key measures". Not one height,
   min-height or max-height is declared — the row is still exactly as tall as
   its tallest card's content wants. */
.dashboard-fn, .dashboard-mat {
  padding: 18px 22px;
  display: flex; flex-direction: column;
}
.dashboard-mat .matladder, .dashboard-fn .hbars { flex: 1 1 auto; }
/* Flex items do NOT collapse margins. The heading's margin-bottom and each
   list's own margin-top used to overlap in a block card; the moment the card
   became a column they started adding up, which pushed the first content row
   of every one of these cards down by its own margin. The heading owns the
   rhythm, so the list drops its top margin - and the "no data yet" paragraph
   drops both, because a UA margin is nobody's rhythm. */
.dashboard-mat > .matladder, .dashboard-fn > .hbars { margin-top: 0; }
.dashboard-fn > p { margin: 0; }
/* .hbars is a grid, whose auto rows share the slack on their own; the ladder
   is a flex column, whose rows have to be told to. This is what "the ladder
   carries the height" means in practice, and it is why the score line below
   it now sits on the card's bottom rhythm instead of floating above a gap. */
.dashboard-mat .matrow { flex: 1 1 auto; }
/* The other contributor to that gap: a bare `margin-top` left the paragraph's
   UA bottom margin (~1em) under the last line of the card. */
.dashboard-mat__foot { margin: 12px 0 0; }
/* Two `.kpi-foot`s in one flex column (the "All N below target" link and the
   "Learn more" link) each carry `margin-top: auto`, and flexbox splits the
   free space BETWEEN them - which floated the first one mid-card the moment
   the tile started stretching. Give the list the slack and there is none left
   to split. */
.kpi-card--key .kpi-keylist { flex: 1 1 auto; }
.conn-mini { list-style: none; padding: 0; margin: 11px 0 0; display: flex; flex-wrap: wrap; gap: 7px; }

/* 17.44.0 (feedback batch ③): the "Connections & automation" tile's whole
   family (.mach*, and the .conn-dot* traffic light it was the only caller
   of) went with the tile — the dashboard no longer carries a connector
   panel, and the Integrations page has always had its own. The two
   ≤640px blocks that tuned .mach for a phone went with it. */
/* The dashboard's one header action (16.54.0 §B). */
.page-header__act { display: flex; align-items: center; gap: 8px; flex: none; }

/* ── Start here: guided plan (16.21.0) ────────────────────────── */
.sh-card{padding:18px 22px;margin-bottom:14px}

/* 16.66.0 (⑧b §5) — the slim band. The journey stepper, the programme bar
   and the Start-here header were three stacked blocks and ~330px of the
   dashboard between the page title and the maturity strip; they are one
   ~90px band now. Two rows: the title line (with the (?) that absorbed the
   descriptive sentence, the programme bar and the two put-it-away controls)
   and the step dots. The four fat action rows below them are gone entirely -
   the "What to do next" KPI card is the only queue. */
.shb{padding:11px 16px 12px;margin-bottom:12px}
.shb__head{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.shb__head h2{margin:0;font-size:15px;line-height:1.2}
/* The bar rides the title line and takes the slack, which is what lets the
   band be two rows instead of three. It collapses out of the way first when
   the controls and the title need the width. */
.shb__prog{display:flex;align-items:center;gap:8px;flex:1 1 200px;min-width:120px;
  font-size:11.5px;color:var(--text-secondary);white-space:nowrap}
.shb__prog b{color:var(--navy);font-weight:800}
.shb__steps{display:flex;align-items:center;gap:6px;margin-top:9px}
.shb__step{flex:1;display:flex;align-items:center;gap:7px;min-width:0;position:relative}
/* The connector runs BETWEEN dots, behind them, the way the tall stepper's
   did - it is what makes five chips read as one path. */
.shb__step::before{content:"";position:absolute;left:0;right:0;top:50%;height:2px;
  background:var(--medium-gray);z-index:0}
.shb__step.done::before,.shb__step.current::before{background:var(--teal)}
.shb__dot{position:relative;z-index:1;flex:none;width:21px;height:21px;border-radius:50%;
  display:grid;place-items:center;background:var(--white);
  border:2px solid var(--medium-gray);font-size:10px;font-weight:800;color:var(--text-muted)}
.shb__step.done .shb__dot{background:var(--teal);border-color:var(--teal);color:#fff}
.shb__step.current .shb__dot{border-color:var(--teal);color:var(--teal-dark);
  box-shadow:0 0 0 3px rgba(42,157,143,.15)}
.shb__name{position:relative;z-index:1;background:var(--white);padding-right:6px;
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:11.5px;font-weight:700;color:var(--text-muted)}
.shb__step.done .shb__name{color:var(--navy)}
.shb__step.current .shb__name{color:var(--teal-dark)}
.shb .sh-here{position:relative;z-index:1;margin:0;flex:none}
.shb .sh-bar{min-width:60px}
@media(max-width:1100px){.shb__steps{flex-wrap:wrap;gap:6px 12px}
  .shb__step{flex:0 0 auto}.shb__step::before{display:none}}

/* ── 16.69.0 (⑧d §11): the band's expanded view ─────────────────────────
   The numbered action rows ⑧b's compression removed, back on demand. A
   native <details>, collapsed by default, so the slim band costs exactly
   one extra 24px control until somebody asks for the detail - which is the
   trade ⑧b made and this keeps. Everything below is on the current tokens:
   the tag is `.pill` (skin spec §E's one label system) rather than a
   resurrected .sh-tag, and the row inherits the list-row type scale. */
.shb__more{margin-top:6px}
.shb__ex{list-style:none;cursor:pointer;display:flex;align-items:center;gap:6px;
  padding:3px 0 1px;font-size:11.5px;font-weight:700;color:var(--text-muted)}
.shb__ex::-webkit-details-marker{display:none}
.shb__ex:hover{color:var(--navy)}
.shb__ex:focus-visible{outline:2px solid var(--teal);outline-offset:2px;border-radius:4px}
.shb__ex-chev{font-size:10px;transition:transform .15s}
.shb__more[open] .shb__ex-chev{transform:rotate(180deg)}
/* One control, two labels: the closed state offers, the open state undoes. */
.shb__more[open] .shb__ex-more{display:none}
.shb__ex-less{display:none}
.shb__more[open] .shb__ex-less{display:inline}

.shb__acts{list-style:none;margin:8px 0 2px;padding:0;display:grid;gap:8px}
.shb__act{display:grid;grid-template-columns:28px minmax(0,1fr) auto;gap:12px;
  align-items:center;border:1px solid var(--medium-gray);border-radius:10px;
  padding:10px 13px;background:var(--off-white)}
.shb__act:hover{border-color:var(--teal)}
.shb__act-n{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;
  font-weight:800;font-size:13px;background:var(--white);
  border:2px solid var(--navy);color:var(--navy)}
.shb__act-body{min-width:0;display:block}
.shb__act-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.shb__act-t{font-size:13.5px;font-weight:650;color:var(--navy)}
.shb__act-eff{font-size:10.5px;color:var(--text-muted);font-weight:600}
.shb__act-why{display:block;font-size:12px;color:var(--text-secondary);
  margin-top:3px;line-height:1.45;max-width:78ch}
.shb__act-imp{display:block;margin-top:5px;font-size:11px;font-weight:700;
  color:var(--teal-dark)}
@media(max-width:1100px){.shb__act{grid-template-columns:28px minmax(0,1fr);row-gap:8px}
  .shb__act > .btn{grid-column:2}}

.sh-here{margin-top:5px;font-size:8.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  background:var(--teal);color:#fff;border-radius:8px;padding:1px 8px}
.sh-bar{flex:1;height:6px;border-radius:3px;background:var(--medium-gray);overflow:hidden}
.sh-bar i{display:block;height:100%;background:linear-gradient(90deg,var(--amber),var(--teal))}
/* first-run checklist */
.sh-chk{display:flex;flex-direction:column;gap:8px;margin-top:10px}
.sh-chki{display:flex;align-items:center;gap:12px;border:1px solid var(--medium-gray);border-radius:10px;padding:10px 14px}
.sh-chki.done{opacity:.62;background:var(--off-white)}
.sh-chkc{width:24px;height:24px;border-radius:50%;display:grid;place-items:center;flex:none;
  border:2px solid var(--medium-gray);color:transparent;font-size:12px;font-weight:800}
.sh-chki.done .sh-chkc{background:var(--green);border-color:var(--green);color:#fff}
.sh-chkt{font-size:13px;font-weight:600;color:var(--navy)}
.sh-chkt small{display:block;font-size:11px;font-weight:400;color:var(--text-muted)}
.sh-chki .btn{margin-left:auto}
.sh-hint{font-size:11px;color:var(--text-muted);margin-top:10px}
/* 16.56.0 (⑦a §c): Dismiss / Hide on the guide's header row. Quiet by
   design - they are an escape hatch on a card whose whole job is to be the
   first thing a new customer reads, so they must never compete with it.
   The two are genuinely different promises and the wording carries that:
   Dismiss is "not now" (sessionStorage, back next visit), Hide is "not
   ever" (a stored flag, undone from Settings). */
.sh-ctl{display:flex;align-items:center;gap:6px;margin-left:auto}
.sh-ctl form{margin:0}
.sh-x{
  background:none;border:1px solid transparent;border-radius:6px;
  padding:2px 8px;font:inherit;font-size:11.5px;font-weight:600;
  color:var(--text-muted);cursor:pointer;font-family:inherit;
}
.sh-x:hover{border-color:var(--medium-gray);color:var(--navy);background:var(--off-white)}
.sh-x:focus{outline:2px solid var(--teal);outline-offset:1px}

/* ── maturity-levels ladder (16.14.0) ─────────────────────────── */
.matladder { list-style: none; padding: 0; margin: 12px 0 0; display: flex; flex-direction: column; gap: 7px; }
.matrow {
  position: relative;
  display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 11px; align-items: center;
  padding: 8px 10px; border: 1px solid var(--medium-gray); border-radius: 9px;
  background: var(--off-white); opacity: .62; transition: opacity .12s, box-shadow .12s;
}
.matrow.is-reached { opacity: 1; }
.matrow[data-tip] { cursor: help; }
/* richer hover explanation, to the left of the ladder (into the page) */
.matrow[data-tip]::after {
  content: attr(data-tip); position: absolute; right: calc(100% + 12px); top: 50%;
  transform: translateY(-50%); width: 250px; z-index: 40;
  background: var(--navy); color: #fff; font-size: 11px; line-height: 1.45; font-weight: 500;
  padding: 9px 11px; border-radius: 8px; box-shadow: 0 6px 22px rgba(0,0,0,.20);
  opacity: 0; pointer-events: none; transition: opacity .12s;
}
.matrow[data-tip]:hover { opacity: 1; z-index: 41; }
.matrow[data-tip]:hover::after { opacity: 1; }
@media (max-width: 1100px) {
  /* single-column dashboard: drop the tooltip above the row, full width */
  .matrow[data-tip]::after {
    right: auto; left: 0; top: auto; bottom: calc(100% + 8px);
    transform: none; width: auto; max-width: 100%;
  }
}
.matrow__lv {
  width: 34px; height: 34px; border-radius: 8px; display: grid; place-items: center;
  font-weight: 800; font-size: 15px; color: var(--medium-gray);
  background: var(--white); border: 2px solid var(--medium-gray); font-variant-numeric: tabular-nums;
}
.matrow.is-reached .matrow__lv { color: #fff; background: var(--lvc); border-color: var(--lvc); }
.matrow__name { font-weight: 700; color: var(--navy); font-size: 13px; display: flex; align-items: center; gap: 7px; }
.matrow__desc { display: block; font-size: 11px; color: var(--text-secondary); line-height: 1.35; margin: 1px 0 5px; }
.matrow__bar { display: block; height: 5px; border-radius: 3px; background: var(--medium-gray); overflow: hidden; }
.matrow__bar i { display: block; height: 100%; background: var(--lvc); border-radius: 3px; }
.matrow.is-current {
  opacity: 1; border-color: var(--lvc); box-shadow: 0 0 0 2px color-mix(in srgb, var(--lvc) 28%, transparent);
  background: var(--white);
}
.matrow__here {
  font-style: normal; font-size: 9px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: #fff; background: var(--lvc); border-radius: 9px; padding: 1px 7px;
}
.dashboard-mat__foot { margin-top: 12px; }

@media (max-width: 1100px) {
  .dashboard-row { grid-template-columns: 1fr; }
}

/* ── 16.2.0 scoring truth: provenance chips + scan headline tile ───── */
/* Source chips: ⚙ scan-derived / ✎ assessed / ◉ manual override. Sit next
   to implementation cells (dashboard function rows, scan tile label). */
.score-chip {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 6px; border-radius: 999px;
  font-size: 10px; font-weight: 700; line-height: 1.5;
  white-space: nowrap; vertical-align: middle; cursor: default;
}
.score-chip--scan     { background: #E8F1FD; color: #1B5CB8;            border: 1px solid #C7DCF7; }
.score-chip--assessed { background: #E0F5E9; color: var(--green-dark);  border: 1px solid #BDE5C9; }
.score-chip--override { background: #FFF5E0; color: #A35A00;            border: 1px solid #F6E2B8; }

/* Scan maturity tile — 5th headline card, rendered only when a scan exists */
@media (min-width: 1001px) {
  /* matches the .prog-summary breakpoint at max-width:1000px above */
}

/* Dashboard function rows gain D/I + chips columns (progress page keeps
   the shared 3-column .hbar layout untouched). 16.5.0: FIXED column
   widths — an auto column let each row's D/I+chips width squeeze the
   track differently, so bars/values/chips never lined up. */
/* 16.56.0 (⑦a §d): a SIXTH fixed column for the row's help mark. Same
   16.5.0 lesson as the D/I and chip columns - an inline mark inside
   .hbar__label would be eaten by that cell's ellipsis, and an auto column
   would let each row's mark sit at a different x. */
.dashboard-fn .hbar {
  grid-template-columns: 132px minmax(0, 1fr) 42px 84px 86px 16px;
  gap: 9px;
}
/* The whole label is the drill-down link into the Progress page's
   expandable detail; it must not read as a coloured link inside a row that
   is already carrying a coloured pill and a coloured bar. */
.hbar__link { color: inherit; display: inline; }
.hbar__link:hover { color: var(--teal); text-decoration: underline; }
.hbar__di {
  font-size: 11.5px; color: var(--text-secondary); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.hbar__chips {
  display: flex; gap: 4px; align-items: center; white-space: nowrap;
}
@media (max-width: 1250px) {
  .dashboard-fn .hbar { grid-template-columns: 120px minmax(0, 1fr) 46px 92px 96px 16px; }
}

/* Assurance profiles — out-of-profile rows stay visible but muted */
.req--oop { opacity: 0.45; }
.req--oop .req__link { pointer-events: auto; }   /* still clickable → detail explains */
.req__oop-chip {
  font-size: 0.68rem; padding: 1px 7px; border-radius: 9px;
  background: #6b72801a; color: #6b7280; border: 1px solid #6b728055;
  text-transform: uppercase; letter-spacing: .03em;
}

/* docgen-v3 — template-class badges + gallery sections (Auto vs Describe) */
.tplgallery h2 { margin: 30px 0 6px; }
.tplgallery h2 + .subtle { margin: 0 0 14px; }
.tplbadge {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; vertical-align: middle; margin-left: 6px;
}
.tplbadge--derived {
  background: var(--light-gray); color: var(--navy);
  border: 1px solid var(--medium-gray);
}
.tplbadge--narrative {
  background: #2a9d8f1f; color: var(--teal-dark);
  border: 1px solid #2a9d8f66;
}

/* ── 16.2.3: templates search slicer + findings measure pill ─────────── */
.tplsearch {
  display: flex; align-items: center; gap: 12px; margin: 0 0 16px;
}
.tplsearch input[type="search"] {
  flex: 1; max-width: 480px; padding: 9px 12px;
  border: 1px solid var(--medium-gray); border-radius: 8px;
  font-family: inherit; font-size: 14px;
}
.tplsearch input[type="search"]:focus {
  outline: none; border-color: var(--teal);
}
.measure-pill {
  display: inline-block; padding: 1px 7px; border-radius: 10px;
  background: #E0F5F2; color: var(--teal-dark);
  font-size: 11px; font-weight: 700;
}

/* ── 16.3.0: shared background-job progress card (jobs.js) ───────────── */
.job-card {
  background: var(--white); border: 1px solid var(--medium-gray);
  border-radius: 10px; padding: 12px 14px; margin: 10px 0;
}
.job-card__msg { font-size: 13px; font-weight: 600; color: var(--navy); }
.job-card__bar {
  margin-top: 8px; background: var(--medium-gray);
  border-radius: 999px; height: 6px; overflow: hidden;
}
.job-card__fill {
  background: var(--teal); height: 100%; border-radius: 999px;
  transition: width 200ms ease;
}
.job-card__note { margin-top: 6px; font-size: 12px; color: var(--text-muted); }

/* ── 16.4.0: Import Docs — collapsible source cards + future mock-ups ── */
.conn-disclose > summary {
  list-style: none; cursor: pointer; position: relative;
  padding-right: 84px;   /* room for the Configure/Close affordance */
}
.conn-disclose > summary::-webkit-details-marker { display: none; }
.conn-summary__cta {
  position: absolute; top: 0; right: 0;
  font-size: 12px; font-weight: 600; color: var(--teal);
}
.conn-summary__cta::before { content: "Configure ▾"; }
.conn-disclose[open] > summary .conn-summary__cta::before { content: "Close ▴"; }
.conn-disclose[open] > summary { margin-bottom: 12px; }
.conn-disclose > summary:hover .conn-summary__cta { text-decoration: underline; }

.conn-card--soon { opacity: 0.62; }
.conn-card--soon .conn-card__title { color: var(--text-secondary); }
.conn-chip--soon {
  background: #EEF1F6; color: var(--text-muted);
  font-weight: 600; letter-spacing: 0.02em;
}

/* ── 16.4.1: control→template shortcut (requirement detail, Evidence card) ── */
.gen-evidence {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--medium-gray);
}
.gen-evidence__head {
  font-size: 13px; font-weight: 650; color: var(--navy); margin-bottom: 8px;
}
.gen-evidence__row { display: flex; flex-wrap: wrap; gap: 6px; }
.gen-evidence__row .btn { text-align: left; }
.gen-evidence__mode { font-size: 11px; color: var(--text-muted); font-weight: 500; }

/* ── 16.4.1: gallery cards — uniform height, chips collapse past 4 ── */
.tplcard__extra { display: contents; }
.tplcard__targets.is-collapsed .tplcard__extra { display: none; }
.tplchip--more {
  cursor: pointer; background: var(--white); color: var(--teal);
  border: 1px dashed var(--teal); font-family: inherit;
}
.tplchip--more:hover { background: var(--light-gray); }

/* ── 16.4.4 (G): branded stroke icons replace emoji ──────────────────── */
.ic { vertical-align: -2px; }
.btn .ic { margin-right: 2px; }
.score-chip .ic { vertical-align: -1px; }
.dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  vertical-align: 0;
}
.dot--red   { background: #dc2626; }
.dot--amber { background: #d97706; }
.dot--green { background: #16a34a; }
/* 16.4.4: Overall-maturity card key/value rows (replaces wrapping prose) */

/* ── 16.32.0: dashboard KPI strip (one card = one question) ──────────
   Replaces the four .prog-summary tiles on index.html ONLY; the progress
   page keeps .prog-summary untouched. The three data colours below come
   from the approved mock and were validated for colour-vision deficiency
   against the card background - do not substitute brand greens/ambers. */
:root {
  --kpi-good: #1D7A3E;      /* assessed / on-target fill */
  --kpi-warn: #CE7A1E;      /* in progress */
  --kpi-neutral: #D7DBE4;   /* not started */
  --kpi-track: #EDF0F5;     /* empty bar track */
}
/* 17.44.0 (feedback batch ③): .kpi-strip is gone. The five-across strip was
   the layout the collage replaced — its three survivors are cards in the
   two .dashboard-row grids now, and .kpi-card below still dresses all three
   exactly as it did inside the strip (it never depended on the strip as an
   ancestor). Its three breakpoints went with it; .dashboard-row's own
   ≤1100px collapse is what stacks the cards on a phone. */

/* ── Key measures tile ──────────────────────────────────────────────
   Two boxes, one verdict, the failing list. The gold that marks a key
   measure everywhere else in the product is the card's accent; the boxes
   themselves stay on the product's ok/crit tint pairs, because they report
   a PASS or a FAIL and gold would be a third status colour. */
.kpi-card--key { border-top: 2px solid var(--key-ink); }
.kpi-two { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.kpi-two__box { border-radius: 8px; padding: 8px 10px; }
.kpi-two__box--ok  { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.kpi-two__box--bad { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.kpi-two__h { font-size: 10.5px; font-weight: 700; text-transform: uppercase;
              letter-spacing: 0.05em; line-height: 1.25; }
.kpi-two__v { font-size: 20px; font-weight: 700; line-height: 1.2;
              font-variant-numeric: tabular-nums; }
.kpi-verdict { font-size: 12.5px; line-height: 1.45; border-radius: 8px;
               padding: 8px 10px; }
.kpi-verdict--red   { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.kpi-verdict--amber { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.kpi-verdict--green { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.kpi-keylist { list-style: none; margin: 0; padding: 0; }
.kpi-keylist li { border-top: 1px solid var(--light-gray); }
.kpi-keylist a {
  display: flex; align-items: center; gap: 8px; padding: 5px 0;
  font-size: 12px; color: var(--text-primary);
}
.kpi-keylist a:hover { text-decoration: none; color: var(--navy); }
.kpi-keylist__id { font-weight: 700; color: var(--navy); flex: 0 0 auto; }
.kpi-keylist__t {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--text-secondary);
}

.kpi-card {
  padding: 16px 18px 15px;
  display: flex; flex-direction: column; gap: 10px;
  min-height: 168px;
}
.kpi-label {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); font-weight: 700;
}
.kpi-heroline { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.kpi-hero {
  font-size: 30px; font-weight: 700; line-height: 1; color: var(--navy);
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
.kpi-of { font-size: 15px; font-weight: 600; color: var(--text-muted); }
.kpi-chip {
  font-size: 11.5px; font-weight: 700; padding: 2px 9px; border-radius: 20px;
  white-space: nowrap;
}
/* 16.52.0: onto the tinted label pairs (skin spec §E) - the maturity ladder
   runs crimson → yellow → sky → green → green, same hues as everywhere else. */
.kpi-chip--l1 { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.kpi-chip--l2 { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.kpi-chip--l3 { background: var(--tint-sky-bg);  color: var(--tint-sky-ink); }
.kpi-chip--l4 { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.kpi-chip--l5 { background: var(--tint-ok-bg);   color: var(--kpi-good); }
.kpi-ctx { font-size: 12.5px; color: var(--text-secondary); line-height: 1.45; }
/* 16.36.0 trend line: a direction, not a verdict - muted by default, and
   coloured only when the posture actually moved. */
.kpi-delta {
  font-size: 12px; color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.kpi-delta--up   { color: var(--kpi-good); }
.kpi-delta--down { color: var(--kpi-warn); }
.kpi-scope { font-size: 12px; color: var(--text-muted); }
.kpi-foot {
  font-size: 12px; color: var(--text-muted); margin-top: auto;
  border-top: 1px dashed var(--medium-gray); padding-top: 8px;
}
.kpi-foot abbr {
  cursor: help; text-decoration: underline dotted; color: inherit;
}

/* Target bar: fill = value/5, tick = the required maturity. A distance,
   not a verdict - red is reserved for genuinely critical states. */
.kpi-tbar {
  position: relative; height: 8px; border-radius: 4px;
  background: var(--kpi-track); margin-top: 10px;
}
.kpi-tbar__fill {
  position: absolute; inset: 0 auto 0 0; border-radius: 4px;
  background: var(--teal);
}
.kpi-tbar__tick {
  position: absolute; top: -3px; bottom: -3px; width: 2px; border-radius: 1px;
  background: var(--text-secondary);
}
/* Only a tick that names itself prints a label (card 3's rows do not - two
   labels there would land on the row's own name/value line). */
.kpi-tbar__tick[data-l]::after {
  content: attr(data-l); position: absolute; top: -15px; left: 50%;
  transform: translateX(-50%);
  font-size: 10.5px; color: var(--text-muted); white-space: nowrap;
}

/* 17.44.0 (feedback batch ③): the stacked coverage bar (.kpi-seg*) and its
   legend (.kpi-legend, .kpi-dot*) went with the "Assessment coverage" tile.
   The --kpi-good / --kpi-warn / --kpi-neutral tokens they read stay: the
   bars and chips on this page and on Progress are the same three states. */

/* Documentation vs implementation rows. */
.kpi-dim {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  align-items: center; font-size: 12.5px;
}
.kpi-dim__name { color: var(--text-secondary); }
.kpi-dim__val {
  font-weight: 700; text-align: right; color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.kpi-dim .kpi-tbar { grid-column: 1 / -1; margin-top: 4px; margin-bottom: 10px; }

/* 17.44.0 (feedback batch ③): "What to do next" left the dashboard, and its
   whole family (.kpi-card--act, .kpi-act*, .kpi-acts*) with it. The queue it
   rendered is unchanged and still on the page — the Start-here band's
   numbered rows, which wear .shb__act* and always did. */

/* ── 16.7.0 (E4): floating in-app assistant widget ───────────────────── */
/* app.css declares no other z-index (sticky sidebar sits at auto), so
   1000 puts the widget above every page surface. */
#opti-chat { position: fixed; right: 20px; bottom: 20px; z-index: 1000; }
#opti-chat-launcher {
  width: 56px; height: 56px; border-radius: 50%;
  border: none; cursor: pointer;
  background: var(--navy); color: var(--white);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 18px rgba(27, 42, 74, 0.35);
}
#opti-chat-launcher:hover { background: var(--sidebar-selected); }
#opti-chat-launcher .ic { vertical-align: 0; }
#opti-chat-panel {
  position: absolute; right: 0; bottom: 68px;
  /* 16.7.1: 380px felt cramped (user feedback) - modest bump, still
     mobile-safe via the viewport max-width. */
  width: 460px; max-width: calc(100vw - 24px);
  max-height: 600px;
  display: flex; flex-direction: column;
  background: var(--white);
  border: 1px solid var(--medium-gray); border-radius: 12px;
  box-shadow: 0 12px 32px rgba(27, 42, 74, 0.22);
  overflow: hidden;
}
#opti-chat-panel[hidden] { display: none; }
.opti-chat-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 10px 12px;
  background: var(--navy); color: var(--white);
}
#opti-chat-title {
  font-size: 13px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.opti-chat-head__actions {
  display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
}
.opti-chat-headbtn {
  border: 1px solid var(--sidebar-selected); border-radius: 6px;
  background: transparent; color: var(--sidebar-text);
  font: inherit; font-size: 11px; font-weight: 600;
  padding: 3px 8px; cursor: pointer;
}
.opti-chat-headbtn:hover { background: var(--sidebar-hover); color: var(--white); }
.opti-chat-headbtn--icon { display: inline-flex; align-items: center; padding: 4px; }
/* 17.16.0: the conversation picker. Drops between the head and the message
   pane; the pane keeps its own scroll, so the list is capped rather than
   allowed to push the composer off the panel. */
#opti-chat-history {
  max-height: 220px; overflow-y: auto; flex-shrink: 0;
  border-bottom: 1px solid var(--medium-gray); background: var(--white);
}
#opti-chat-history[hidden] { display: none; }
.opti-chat-histempty {
  margin: 0; padding: 14px 12px; font-size: 12px; color: var(--text-secondary);
}
.opti-chat-histrow {
  display: flex; align-items: stretch; gap: 6px;
  padding: 4px 8px 4px 4px;
  border-bottom: 1px solid var(--light-gray);
}
.opti-chat-histrow:last-child { border-bottom: none; }
.opti-chat-histopen {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px;
  border: none; background: transparent; cursor: pointer;
  font: inherit; text-align: left; padding: 6px 8px; border-radius: 6px;
}
.opti-chat-histopen:hover { background: var(--off-white); }
.opti-chat-histtitle {
  font-size: 12.5px; font-weight: 600; color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.opti-chat-histmeta { font-size: 11px; color: var(--text-secondary); }
.opti-chat-histdel {
  flex-shrink: 0; align-self: center;
  border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white); color: var(--text-secondary);
  font: inherit; font-size: 11px; font-weight: 600;
  padding: 4px 8px; cursor: pointer;
}
.opti-chat-histdel:hover { border-color: var(--red); color: var(--red); }
#opti-chat-messages {
  /* min-height overrides the flex auto minimum so the pane scrolls inside
     the panel's max-height instead of pushing the form off the bottom. */
  flex: 1; min-height: 140px; overflow-y: auto;
  padding: 12px; display: flex; flex-direction: column; gap: 8px;
  background: var(--off-white);
}
.opti-chat-msg {
  max-width: 85%; padding: 8px 11px; border-radius: 10px;
  font-size: 13px; line-height: 1.45; overflow-wrap: break-word;
}
.opti-chat-msg p { margin: 0 0 6px; }
.opti-chat-msg p:last-child { margin-bottom: 0; }
.opti-chat-msg ul { margin: 0 0 6px; padding-left: 18px; }
.opti-chat-msg ul:last-child { margin-bottom: 0; }
/* 16.8.1: linkified tenant paths - clean labels, brand accent, no GUID. */
.opti-chat-link {
  color: var(--navy); font-weight: 600; text-decoration: underline;
  text-underline-offset: 2px; word-break: break-word;
}
.opti-chat-link:hover { color: var(--sidebar-selected); }
.opti-chat-msg--user {
  align-self: flex-end; background: var(--navy); color: var(--white);
  border-bottom-right-radius: 3px;
}
.opti-chat-msg--assistant {
  align-self: flex-start; background: var(--white); color: var(--text-primary);
  border: 1px solid var(--medium-gray);
  border-bottom-left-radius: 3px;
}
/* Answer export: a quiet control under a FINISHED assistant answer. The
   assistant offers nothing and produces nothing - this is the widget's own
   affordance, so it reads as chrome rather than as part of the answer. */
.opti-chat-msgtools { margin-top: 6px; }
.opti-chat-dl {
  border: 1px solid var(--medium-gray); border-radius: 999px;
  background: var(--white); color: var(--text-secondary);
  font: inherit; font-size: 11px; font-weight: 600;
  padding: 2px 9px; cursor: pointer;
}
.opti-chat-dl:hover { border-color: var(--teal); color: var(--teal); }
.opti-chat-dl:disabled { opacity: 0.55; cursor: default; }
.opti-chat-dots { display: inline-flex; gap: 4px; }
.opti-chat-dots i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--dark-gray);
  animation: opti-chat-pulse 1.1s infinite ease-in-out;
}
.opti-chat-dots i:nth-child(2) { animation-delay: 0.18s; }
.opti-chat-dots i:nth-child(3) { animation-delay: 0.36s; }
@keyframes opti-chat-pulse {
  0%, 100% { opacity: 0.25; transform: translateY(0); }
  40%      { opacity: 1;    transform: translateY(-2px); }
}
#opti-chat-chips {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 0 12px 10px; background: var(--off-white);
}
#opti-chat-chips[hidden] { display: none; }
.opti-chat-chip {
  border: 1px dashed var(--teal); border-radius: 999px;
  background: var(--white); color: var(--teal);
  font: inherit; font-size: 12px; font-weight: 600;
  padding: 4px 10px; cursor: pointer; text-align: left;
}
.opti-chat-chip:hover { background: var(--light-gray); }
#opti-chat-form {
  display: flex; gap: 8px; padding: 10px 12px;
  border-top: 1px solid var(--medium-gray); background: var(--white);
}
#opti-chat-input {
  flex: 1; min-width: 0;
  border: 1px solid var(--medium-gray); border-radius: 8px;
  padding: 8px 10px; font: inherit; font-size: 13px;
  color: var(--text-primary);
}
#opti-chat-input:focus {
  outline: none; border-color: var(--teal);
  box-shadow: 0 0 0 2px rgba(42, 157, 143, 0.25);
}
#opti-chat-input:disabled, #opti-chat-send:disabled { opacity: 0.55; cursor: default; }
#opti-chat-send {
  border: none; border-radius: 8px; cursor: pointer;
  background: var(--teal); color: var(--white);
  font: inherit; font-size: 13px; font-weight: 700;
  padding: 8px 14px;
}
#opti-chat-send:hover:enabled { background: var(--teal-dark); }

/* 16.10.0: sidebar language switch (EN / RO) */
.langswitch { display: flex; gap: 6px; margin-bottom: 8px; }
.langswitch__opt {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  padding: 4px 10px; border-radius: 7px; text-decoration: none;
  color: var(--sidebar-text); background: transparent;
  border: 1px solid rgba(255,255,255,.18);
}
.langswitch__opt:hover { background: var(--sidebar-hover); color: #fff; }
.langswitch__opt.is-on { background: var(--sidebar-selected); color: #fff; border-color: transparent; }
.langswitch .flagsvg {
  width: 20px; height: 14px; border-radius: 2px; display: block; flex: 0 0 auto;
  box-shadow: 0 0 0 1px rgba(255,255,255,.25);
}
.langswitch .flagsvg--txt {
  width: auto; height: auto; font-size: 10px; font-weight: 800;
  box-shadow: none; opacity: .8;
}
.langswitch__opt.is-on .flagsvg { box-shadow: 0 0 0 1px rgba(255,255,255,.55); }
.langswitch__code { line-height: 1; }

/* ── 16.33.0: Results page — triage / remediation plan / all findings ──
   Namespaced `rz-` so nothing here can leak into the other tables. The
   severity colours are the CVD-validated set introduced with the 16.32.0
   KPI strip; the `ok` slug is the synthetic "healthy" bucket (status
   Compliant), which the severity chips never filter. */
:root {
  /* 16.52.0: retuned onto the brand's tinted label pairs (skin spec §E).
     These variables are the single source for severity across Results, the
     Remediation register AND the calendar's overdue text, so changing them
     here is what moves all three onto the softened palette at once. */
  --rz-crit: var(--tint-crit-ink);  --rz-crit-soft: var(--tint-crit-bg);
  --rz-high: var(--tint-high-ink);  --rz-high-soft: var(--tint-high-bg);
  --rz-med:  var(--tint-med-ink);   --rz-med-soft:  var(--tint-med-bg);
  --rz-low:  var(--tint-sky-ink);   --rz-low-soft:  var(--tint-sky-bg);
  --rz-info: var(--tint-mute-ink);  --rz-info-soft: var(--tint-mute-bg);
  --rz-ok:   var(--tint-ok-ink);    --rz-ok-soft:   var(--tint-ok-bg);
  --rz-line: #E5E8EF;  --rz-track:     #EDF0F5;
}
.is-hidden { display: none !important; }

.rz-meta { color: var(--text-secondary); font-size: 13px; margin-top: 3px; }
.rz-meta b { color: var(--navy); font-weight: 650; }

/* 16.55.0 (⑥d §D): ONE toolbar row. Severity chips + search + the source
   dropdown + the matrix toggle + the view switch, in that reading order.
   It wraps rather than scrolls, so a narrow window loses nothing. */
.rz-toolbar { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
/* 17.2.3: the severity chips gained a wrapper so the phone tier can keep
   them together as one scrolling row (docs/mobile_polish_spec.md §2). At
   this width the wrapper must not exist: `display: contents` generates no
   box at all, so the five chips stay direct flex children of the toolbar
   and every pixel of the desktop row is the pixel it was. The media block
   at the foot of this file is where it becomes a real element.
   Not `.rz-sevrow`: that was one of the three rows ⑥d collapsed into this
   toolbar (16.55.0), and the name is pinned as retired. */
.rz-sevs { display: contents; }
.rz-sev {
  border: 1px solid var(--medium-gray); background: var(--white);
  border-radius: 20px; padding: 4px 11px; font: inherit; font-size: 12px;
  font-weight: 650; cursor: pointer; color: var(--text-secondary);
  display: inline-flex; gap: 6px; align-items: center;
  font-variant-numeric: tabular-nums;
}
.rz-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.rz-dot--crit { background: var(--rz-crit); } .rz-dot--high { background: var(--rz-high); }
.rz-dot--med  { background: var(--rz-med); }  .rz-dot--low  { background: var(--rz-low); }
.rz-dot--info { background: var(--rz-info); }
.rz-sev.is-on { border-color: currentColor; }
.rz-sev--crit.is-on { color: var(--rz-crit); background: var(--rz-crit-soft); }
.rz-sev--high.is-on { color: var(--rz-high); background: var(--rz-high-soft); }
.rz-sev--med.is-on  { color: var(--rz-med);  background: var(--rz-med-soft); }
.rz-sev--low.is-on  { color: var(--rz-low);  background: var(--rz-low-soft); }
.rz-sev--info.is-on { color: var(--rz-info); background: var(--rz-info-soft); }
.rz-sumline { margin-left: auto; font-size: 13px; color: var(--text-secondary); }
.rz-sumline b { color: var(--navy); }

.rz-vtoggle { display: inline-flex; border: 1px solid var(--medium-gray); border-radius: 8px; overflow: hidden; }
.rz-vtoggle button {
  padding: 5px 11px; font: inherit; font-size: 12px; font-weight: 650;
  cursor: pointer; color: var(--text-secondary); background: var(--white); border: none;
}
.rz-vtoggle button.is-on { background: var(--teal); color: var(--white); }

.rz-search {
  flex: 1 1 200px; border: 1px solid var(--medium-gray); border-radius: 8px;
  background: var(--white); color: var(--text-primary); padding: 6px 11px;
  font: inherit; font-size: 13px;
}
.rz-search:focus { outline: none; border-color: var(--teal); }
/* The source filter: one <select> where seven chips used to sit. */
.rz-fsel {
  border: 1px solid var(--medium-gray); border-radius: 8px; background: var(--white);
  color: var(--text-secondary); padding: 6px 10px; font: inherit; font-size: 12.5px;
  max-width: 190px;
}
.rz-fsel:focus { outline: none; border-color: var(--teal); }
.rz-fchip {
  border: 1px solid var(--medium-gray); background: var(--white);
  color: var(--text-secondary); border-radius: 8px; padding: 6px 11px;
  font: inherit; font-size: 12.5px; cursor: pointer;
}
.rz-fchip.is-on { border-color: var(--teal); color: var(--teal-dark); background: #E0F5F2; }
.rz-fchip--clear.is-on, .rz-fchip--clear { border-color: var(--rz-low); color: var(--navy); background: var(--rz-low-soft); }

/* Measures x severity matrix — every row is a filter.
   16.55.0 (⑥d §D): compact, and TINTED ONLY WHERE THERE IS SOMETHING. The
   full-column wash repeated what the column header already said while
   giving every zero the same visual weight as a real count; the tint now
   rides a chip around the number itself, and a zero is a faint dot. */
.rz-mxwrap { margin-bottom: 14px; }
.rz-matrix {
  width: 100%; border-collapse: collapse; background: var(--white);
  border: 1px solid var(--rz-line); border-radius: 10px; overflow: hidden;
  font-size: 12px; font-variant-numeric: tabular-nums;
}
.rz-matrix th, .rz-matrix td { padding: 4px 8px; text-align: center; border-top: 1px solid var(--rz-line); }
.rz-matrix thead th {
  border-top: none; color: var(--text-muted); font-size: 10.5px;
  text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700;
}
.rz-matrix td:first-child, .rz-matrix th:first-child { text-align: left; }
.rz-matrix tbody tr { cursor: pointer; }
.rz-matrix tbody tr:hover td, .rz-matrix tbody tr.is-on td { background: var(--off-white); }
.rz-matrix tbody tr.is-on td:first-child { box-shadow: inset 3px 0 0 var(--teal); }
.rz-mname { font-weight: 600; color: var(--text-primary); }
.rz-z { color: var(--text-muted); }
.rz-n {
  display: inline-block; min-width: 20px; padding: 1px 5px; border-radius: 4px;
  font-weight: 700;
}
.rz-n--crit { background: var(--rz-crit-soft); color: var(--rz-crit); }
.rz-n--high { background: var(--rz-high-soft); color: var(--rz-high); }
.rz-n--med  { background: var(--rz-med-soft);  color: var(--rz-med); }
.rz-n--low  { background: var(--rz-low-soft);  color: var(--rz-low); }
.rz-n--info { background: var(--rz-info-soft); color: var(--rz-info); }
.rz-n--ok   { background: transparent; color: var(--text-secondary); font-weight: 600; }
.rz-matrix tfoot td { font-weight: 700; background: var(--rz-track); cursor: pointer; }

/* Measure group / quiet group (native <details>, 16.28.2 idiom). */
.rz-grp {
  background: var(--white); border: 1px solid var(--rz-line); border-radius: 10px;
  margin-bottom: 14px; overflow: hidden;
}
.rz-grp > summary {
  list-style: none; cursor: pointer; padding: 12px 16px;
  display: flex; gap: 12px; align-items: baseline;
}
.rz-grp > summary::-webkit-details-marker { display: none; }
.rz-gname { font-weight: 650; font-size: 14.5px; color: var(--navy); }
.rz-gcounts { font-size: 12.5px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.rz-garrow { margin-left: auto; color: var(--text-muted); font-size: 12px; transition: transform .15s; }
.rz-grp[open] > summary .rz-garrow { transform: rotate(180deg); }
.rz-quiet { background: transparent; border-style: dashed; }
.rz-quiet > summary .rz-gname { color: var(--text-secondary); font-weight: 600; font-size: 13.5px; }
.rz-quiet .rz-frow > summary { padding-top: 8px; padding-bottom: 8px; }

/* ── 16.58.0 (⑦c §1): grouping ────────────────────────────────────────
   The toggle is a LINK pair (the grouping is a server-side render), so it
   borrows the .rz-vtoggle look without borrowing its markup. */
.rz-gsel { display: inline-flex; align-items: center; gap: 6px; }
.rz-glabel { font-size: 11.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; }
.rz-gopt {
  border: 1px solid var(--medium-gray); border-radius: 8px; padding: 5px 10px;
  font-size: 12px; font-weight: 650; color: var(--text-secondary);
  background: var(--white); text-decoration: none;
}
.rz-gopt:hover { border-color: var(--teal); color: var(--teal-dark); }
.rz-gopt.is-on { background: var(--teal); border-color: var(--teal); color: var(--white); }

/* A CyFun function's categories, INSIDE its (collapsed-by-default) group.
   A quiet band, not a second disclosure: two clicks to reach a finding is
   a filing cabinet, not a triage queue. */
.rz-sub + .rz-sub { border-top: 1px solid var(--rz-line); }
.rz-subhead {
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  padding: 8px 16px; background: var(--off-white);
}
.rz-subhead b { font-size: 12.5px; font-weight: 650; color: var(--text-secondary); }
.rz-subnote { padding: 8px 16px; margin: 0; font-size: 12px; color: var(--text-secondary); }

/* ── 16.58.0 (⑦c §3): the embedded AI analysis ───────────────────────── */
.rz-aiquiet {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  margin: 18px 0 4px; padding: 10px 14px; border: 1px dashed var(--rz-line);
  border-radius: 10px; font-size: 12.5px; color: var(--text-secondary);
}
.rz-aihead { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
.rz-aihead .section-title { margin-bottom: 0; }
.rz-aiact { margin-left: auto; font-size: 12.5px; color: var(--text-muted); }
.rz-stale {
  margin: 8px 0 12px; padding: 8px 12px; font-size: 12.5px;
  border-left: 3px solid var(--rz-med); background: var(--rz-med-soft);
  color: var(--text-secondary); border-radius: 0 8px 8px 0;
}
/* 16.67.0 (⑧c §3): the embedded "the last run failed" line. Deliberately
   .rz-stale's shape in the CRITICAL tint rather than a card: this sits under
   a findings list, and a red card there would read as a finding. Inside the
   dashed .rz-aiquiet box it takes the whole row so the retry button beside
   it keeps its own line. */
.rz-aifail {
  flex: 1 1 100%;
  margin: 0 0 8px; padding: 8px 12px; font-size: 12.5px;
  border-left: 3px solid var(--rz-crit); background: var(--rz-crit-soft);
  color: var(--text-secondary); border-radius: 0 8px 8px 0;
}
.rz-aifail .ic { vertical-align: -2px; color: var(--rz-crit); }
.rz-planlink { margin: 12px 0; font-size: 13px; color: var(--text-secondary); }
.rz-grp--ai > summary { padding: 10px 16px; }
.ai-md--sect > summary { padding: 4px 0; }

/* ── 16.69.0 (⑧d §4): AI-rendered markdown ──────────────────────────────
   These rules were an inline <style> block at the foot of
   _ai_analysis_body.html. That partial was included by the standalone /ai
   page as well as by the embedded block on /results (16.71.0 retired the
   former), so the block was injected into Results as a second stylesheet
   on every render - and, being written
   before the reskin, it was painting the AI's own prose in the 16.52.0
   palette while the page around it had moved on. One copy, in the one
   stylesheet, on the tokens: a heading here is the same navy as a heading
   anywhere else, by construction rather than by luck. */
.ai-md__body h1, .ai-md__body h2, .ai-md__body h3 {
  margin: 14px 0 8px; color: var(--navy); font-weight: 700;
}
.ai-md__body h1 { font-size: 1.15em; }
.ai-md__body h2 { font-size: 1.05em; }
.ai-md__body h3 { font-size: 0.98em; }
.ai-md__body p { margin: 6px 0; line-height: 1.5; }
.ai-md__body ul, .ai-md__body ol { margin: 6px 0 6px 22px; }
.ai-md__body li { margin: 3px 0; line-height: 1.45; }
.ai-md__body table, .ai-md__body .md-table {
  border-collapse: collapse; margin: 10px 0; width: 100%;
  font-size: 0.92em; background: var(--white);
}
.ai-md__body th {
  background: var(--light-gray); border: 1px solid var(--medium-gray);
  padding: 8px 10px; text-align: left; font-weight: 600; color: var(--navy);
}
.ai-md__body td {
  border: 1px solid var(--medium-gray); padding: 7px 10px; vertical-align: top;
}
.ai-md__body tr:nth-child(even) td { background: var(--off-white); }
.ai-md__body code {
  background: var(--light-gray); padding: 1px 6px; border-radius: 4px;
  font-size: 0.9em; color: var(--navy);
}
.ai-md__body pre {
  background: var(--light-gray); padding: 10px; border-radius: 6px;
  overflow-x: auto; font-size: 0.88em;
}
.ai-md__body blockquote {
  border-left: 3px solid var(--teal); padding: 4px 12px; margin: 8px 0;
  color: var(--text-secondary); background: var(--tint-ok-bg);
}
.ai-md__body strong { color: var(--navy); }

.ai-md--measure summary { padding: 4px 0; }

/* ── 16.55.0 (⑥d §B): the shared list-row type system ──────────────────
   One rule for every list row on the product: the title is near-black
   13.5px/600 clamped to two lines, the meta line is 11.5px muted, and the
   left status bar is 3px. BOLD NAVY IS RESERVED FOR PAGE AND PANEL
   HEADINGS from here on - when 44 rows shouted in navy, the headings that
   organise them stopped being findable, which is the whole complaint.
   The clamp lives on an inner span so it cannot swallow the meta line. */
.rz-t, .rr-t {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Finding rows. */
.rz-frow { border-top: 1px solid var(--rz-line); }
.rz-frow > summary {
  list-style: none; cursor: pointer; display: grid;
  grid-template-columns: 3px minmax(0, 1fr) auto; gap: 10px; align-items: baseline;
  padding: 9px 16px 9px 12px;
}
.rz-frow > summary::-webkit-details-marker { display: none; }
.rz-stripe { width: 3px; height: 26px; border-radius: 2px; background: var(--rz-info); }
.rz-stripe--crit { background: var(--rz-crit); } .rz-stripe--high { background: var(--rz-high); }
.rz-stripe--med { background: var(--rz-med); }   .rz-stripe--low  { background: var(--rz-low); }
.rz-stripe--ok  { background: var(--rz-ok); }
.rz-ft { font-size: 13.5px; font-weight: 600; color: var(--text-primary); min-width: 0; line-height: 1.35; }
.rz-ft small { display: block; font-weight: 400; color: var(--text-muted); font-size: 11.5px; margin-top: 1px; }
.rz-fright {
  display: inline-flex; gap: 8px; align-items: baseline; white-space: nowrap;
}
.rz-tag {
  font-size: 11.5px; font-weight: 600; padding: 2.5px 10px; border-radius: 6px;
  white-space: nowrap;
}
/* 16.55.0: .rz-tag--src and .rz-tag--effort are gone with the boxes they
   styled - source and effort are plain text in the meta line now. */
.rz-tag--crit { background: var(--rz-crit-soft); color: var(--rz-crit); }
.rz-tag--high { background: var(--rz-high-soft); color: var(--rz-high); }
.rz-tag--med  { background: var(--rz-med-soft);  color: var(--rz-med); }
.rz-tag--low  { background: var(--rz-low-soft);  color: var(--rz-low); }
.rz-tag--info { background: var(--rz-info-soft); color: var(--rz-info); }
.rz-tag--ok   { background: var(--rz-ok-soft);   color: var(--rz-ok); }
.rz-qw {
  font-size: 11px; font-weight: 800; padding: 1px 8px; border-radius: 20px;
  background: var(--rz-ok-soft); color: var(--rz-ok); white-space: nowrap;
}

/* The gap readout is on the COLLAPSED row: triage without a single click.
   It is a grid item of the summary, spanning from the title column to the
   end so it sits under the title rather than beside the tags. */
.rz-gapline {
  grid-column: 2 / -1; font-size: 12px; color: var(--text-secondary);
  margin-top: -2px; line-height: 1.4;
}
.rz-now { color: var(--rz-crit); font-weight: 650; }
.rz-want { color: var(--rz-ok); font-weight: 650; }
.rz-arr { color: var(--text-muted); padding: 0 4px; }

.rz-fbody { padding: 4px 16px 16px 28px; display: grid; gap: 12px; font-size: 13.5px; }
.rz-fbody p { margin: 0; color: var(--text-secondary); max-width: 72ch; }
.rz-compare { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; max-width: 560px; }
.rz-cell { border: 1px solid var(--rz-line); border-radius: 8px; padding: 8px 12px; }
.rz-k {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted); font-weight: 700; margin-bottom: 2px;
}
.rz-cell--now .rz-v { color: var(--rz-crit); font-weight: 650; }
.rz-cell--want .rz-v { color: var(--rz-ok); font-weight: 650; }
.rz-fix { background: #E0F5F2; border-radius: 8px; padding: 10px 14px; max-width: 640px; color: var(--navy); }
.rz-fix .rz-k { color: var(--teal-dark); }
.rz-flinks { display: flex; gap: 16px; flex-wrap: wrap; font-size: 13px; }
.rz-flinks a { color: var(--teal-dark); font-weight: 650; }
.rz-art { color: var(--text-muted); }

/* 16.71.0 (⑦k §1): the LLM's per-finding detail, on the expanded plan row.
   Deliberately quieter than .rz-fix beside it: the recommendation is the
   scanner's own finding, this is generated advice about it. Same box idiom,
   neutral fill, one navy edge - not a second accent colour. */
.rz-aistep {
  background: var(--light-gray); border-left: 3px solid var(--navy);
  border-radius: 8px; padding: 10px 14px; max-width: 640px;
  display: grid; gap: 8px;
}
.rz-aistep .rz-k { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; color: var(--navy); }
.rz-aistale { text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--text-muted); }
.rz-aistale code { font-size: 11px; }
.rz-aistep ol { margin: 0; padding-left: 20px; color: var(--text-secondary); display: grid; gap: 4px; }
.rz-aimeta { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12.5px; color: var(--text-secondary); }
.rz-aimeta b { color: var(--navy); font-weight: 650; }

/* Remediation phases — severity-tinted bands. */
.rz-phase {
  border-radius: 10px; border: 1px solid var(--rz-line); background: var(--white);
  margin-bottom: 14px; overflow: hidden;
}
.rz-ph { display: flex; gap: 12px; align-items: baseline; padding: 11px 16px; flex-wrap: wrap; }
.rz-ph-empty { padding: 10px 16px; font-size: 12.5px; color: var(--text-secondary); }
.rz-ph b { font-size: 13.5px; color: var(--navy); }
.rz-phase--crit .rz-ph { background: var(--rz-crit-soft); } .rz-phase--crit .rz-ph b { color: var(--rz-crit); }
.rz-phase--high .rz-ph { background: var(--rz-high-soft); } .rz-phase--high .rz-ph b { color: var(--rz-high); }
.rz-phase--med  .rz-ph { background: var(--rz-med-soft); }  .rz-phase--med  .rz-ph b { color: var(--rz-med); }
.rz-phase--low  .rz-ph { background: var(--rz-low-soft); }  .rz-phase--low  .rz-ph b { color: var(--rz-low); }
.rz-win {
  font-size: 12px; color: var(--text-secondary); margin-left: auto;
  font-variant-numeric: tabular-nums;
}

/* All-findings table — sortable, same filters. */
.rz-alltab {
  width: 100%; border-collapse: collapse; background: var(--white);
  border: 1px solid var(--rz-line); font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.rz-alltab th, .rz-alltab td { padding: 7px 10px; border-top: 1px solid var(--rz-line); text-align: left; }
.rz-alltab thead th {
  border-top: none; color: var(--text-muted); font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.05em; cursor: pointer;
  white-space: nowrap; user-select: none;
}
.rz-alltab thead th[data-sort-dir="asc"]::after  { content: " ▴"; }
.rz-alltab thead th[data-sort-dir="desc"]::after { content: " ▾"; }
.rz-alltab tbody tr:hover td { background: var(--off-white); }
.rz-cx { color: var(--text-secondary); }

.rz-empty { color: var(--text-secondary); padding: 18px 2px; font-size: 13.5px; }

/* ── 16.37.0: Remediation register ─────────────────────────────────────
   Namespaced `rr-` and built on the SAME severity variables the Results
   redesign defines above (--rz-crit …): one gap has one colour wherever it
   is rendered. The register adds two ideas Results has no use for — an
   accepted-risk note (violet, the only non-severity accent on the page) and
   a provenance timeline whose dots say who acted: grey a human, teal a
   scan, red a regression. */
:root {
  --rr-acc: #5B4FB3;  --rr-acc-soft: #ECEAF7;
}

.rr-meta { color: var(--text-secondary); font-size: 13px; margin-top: 3px; }
.rr-meta b { color: var(--navy); font-weight: 650; }

/* Status chips = the summary AND the filter. */
.rr-strow { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.rr-st {
  border: 1px solid var(--medium-gray); background: var(--white);
  border-radius: 20px; padding: 5px 13px; font-size: 12.5px; font-weight: 600;
  cursor: pointer; color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.rr-st.is-on { border-color: var(--teal); color: var(--teal-dark); background: #E0F5F2; }
.rr-st--over { color: var(--rz-crit); }
.rr-st--over.is-on {
  border-color: var(--rz-crit); background: var(--rz-crit-soft); color: var(--rz-crit);
}
.rr-sumline { margin-left: auto; font-size: 13px; color: var(--text-secondary); }
.rr-sumline b { color: var(--navy); }

.rr-filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.rr-search {
  flex: 1 1 220px; border: 1px solid var(--medium-gray); border-radius: 8px;
  background: var(--white); color: var(--navy); padding: 7px 12px; font-size: 13.5px;
}
.rr-search:focus { outline: none; border-color: var(--teal); }
.rr-fsel {
  border: 1px solid var(--medium-gray); border-radius: 8px; background: var(--white);
  color: var(--text-secondary); padding: 7px 12px; font-size: 13px;
}

/* Item rows — native <details>, same idiom as the Results finding rows. */
.rr-item {
  background: var(--white); border: 1px solid var(--rz-line); border-radius: 10px;
  margin-bottom: 10px; overflow: hidden;
}
/* 16.55.0 (⑥d §B+§C): three columns, not six. The right-hand cells are
   CONDITIONAL now - no owner, no due chip layout to reserve - so they live
   in one flex group that simply renders what is true. */
.rr-item > summary {
  list-style: none; cursor: pointer; display: grid;
  grid-template-columns: 3px minmax(0, 1fr) auto;
  gap: 10px; align-items: baseline; padding: 9px 16px 9px 12px;
}
.rr-item > summary::-webkit-details-marker { display: none; }
.rr-stripe { width: 3px; height: 30px; border-radius: 2px; background: var(--rz-info); }
.rr-stripe--crit { background: var(--rz-crit); } .rr-stripe--high { background: var(--rz-high); }
.rr-stripe--med { background: var(--rz-med); }   .rr-stripe--low  { background: var(--rz-low); }
.rr-stripe--info { background: var(--rz-info); } .rr-stripe--ok   { background: var(--rz-ok); }
.rr-it { font-size: 13.5px; font-weight: 600; color: var(--text-primary); min-width: 0; line-height: 1.35; }
.rr-it small { display: block; font-weight: 400; color: var(--text-muted); font-size: 11.5px; margin-top: 1px; }
.rr-right { display: inline-flex; gap: 10px; align-items: baseline; white-space: nowrap; }
/* The enumeration the row title dropped: still evidence, one click away. */
.rr-more { color: var(--teal-dark); font-weight: 650; font-size: 12.5px; white-space: nowrap; }
.rr-enum { font-size: 12px; color: var(--text-secondary); line-height: 1.55; max-width: 92ch; }

.rr-pill {
  font-size: 11.5px; font-weight: 600; padding: 2.5px 10px; border-radius: 6px;
  white-space: nowrap;
}
/* No .rr-pill--open: "Open gap" is the left bar plus the header count, never
   a per-row chip (16.55.0, ⑥d §C). The three that remain are all states
   somebody CHOSE. */
.rr-pill--prog { background: var(--tint-sky-bg); color: var(--tint-sky-ink); }
.rr-pill--acc  { background: var(--rr-acc-soft); color: var(--rr-acc); }
.rr-pill--res  { background: var(--rz-ok-soft); color: var(--rz-ok); }

/* An owner renders ONLY when there is one (16.55.0): the italic
   "unassigned" was the same six characters on 44 rows and told nobody
   anything the empty space does not. */
.rr-owner {
  font-size: 12px; color: var(--text-secondary); display: inline-flex;
  gap: 6px; align-items: center; white-space: nowrap;
}
.rr-owner i {
  width: 18px; height: 18px; border-radius: 50%; background: #E0F5F2;
  color: var(--teal-dark); font-style: normal; font-size: 9.5px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.rr-due {
  font-size: 12px; color: var(--text-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Overdue is the one date that earns colour: someone promised and missed. */
.rr-due--over { color: var(--rz-crit); font-weight: 700; }
.rr-reg {
  font-size: 11px; font-weight: 700; color: var(--rz-high);
  background: var(--rz-high-soft); border-radius: 20px; padding: 1px 8px;
  white-space: nowrap;
}

.rr-body { padding: 4px 16px 16px 28px; display: grid; gap: 12px; font-size: 13.5px; }
.rr-gapline { font-size: 12.5px; color: var(--text-secondary); }
.rr-now { color: var(--rz-crit); font-weight: 650; }
.rr-want { color: var(--rz-ok); font-weight: 650; }
.rr-arr { color: var(--text-muted); padding: 0 4px; }

/* Provenance timeline — the item's whole life, dot-coded by who acted. */
.rr-tl {
  display: flex; flex-direction: column; gap: 6px;
  border-left: 2px solid var(--rz-line); padding-left: 14px; margin-left: 4px;
}
.rr-tl > div { font-size: 12.5px; color: var(--text-secondary); position: relative; }
.rr-tl > div::before {
  content: ""; position: absolute; left: -19px; top: 6px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--text-muted);
}
.rr-tl > div.ev--auto::before { background: var(--teal); }
.rr-tl > div.ev--bad::before { background: var(--rz-crit); }
.rr-tl b { color: var(--navy); }

.rr-links { display: flex; gap: 16px; flex-wrap: wrap; font-size: 13px; }
.rr-links a { color: var(--teal-dark); font-weight: 650; }

.rr-acts { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.rr-form { display: inline-flex; gap: 6px; align-items: center; margin: 0; }
.rr-form--wide { flex: 0 1 320px; }
.rr-btn {
  border: 1px solid var(--medium-gray); background: var(--white);
  color: var(--text-secondary); border-radius: 8px; padding: 6px 13px;
  font-size: 12.5px; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.rr-btn--p { background: var(--teal); border-color: var(--teal); color: var(--white); }
.rr-in {
  border: 1px solid var(--medium-gray); border-radius: 8px; background: var(--white);
  color: var(--navy); padding: 5px 10px; font-size: 12.5px; min-width: 0;
}
.rr-form--wide .rr-in { flex: 1 1 auto; }

.rr-accnote {
  background: var(--rr-acc-soft); border-radius: 8px; padding: 9px 13px;
  font-size: 12.5px; color: var(--text-secondary);
}
.rr-accnote b { color: var(--rr-acc); }
.rr-accept {
  background: var(--off-white); border: 1px solid var(--rz-line);
  border-radius: 8px; padding: 12px 14px; max-width: 560px;
}
.rr-accept-form { display: grid; gap: 10px; margin: 0; }
.rr-lbl {
  display: grid; gap: 4px; font-size: 12px; font-weight: 650; color: var(--navy);
}
.rr-lbl textarea, .rr-lbl input {
  border: 1px solid var(--medium-gray); border-radius: 8px; background: var(--white);
  color: var(--navy); padding: 7px 10px; font-size: 13px; font-weight: 400;
  font-family: inherit; width: 100%;
}
.rr-hint { margin: 0; font-size: 12px; color: var(--text-muted); }

/* ── 16.69.0 (⑧d §2): the group family is .rz-grp, once ─────────────────
   The register carried a complete second copy of the Results disclosure:
   .rr-grp (dashed + transparent) for the resolved archive, .rr-grp--q
   (solid + white + a loud name) for the queue, and .rr-sub/.rr-subhead for
   the category band inside a group - which is the point at which "the same
   shape as the Results sub-band" stopped being a comment and became a
   convergence override maintained by hand.
   All four are the rz- family's existing states: .rz-grp is the solid one,
   .rz-grp + .rz-quiet is the dashed one, .rz-sub/.rz-subhead is the band.
   What survives here is the two rules that are genuinely the register's,
   because they are about the register's OWN children (.rr-item, .rr-stripe)
   and can therefore never reach a Results group. */
.rz-grp .rr-item { border-radius: 8px; margin: 8px 12px; }
/* The worst-severity stripe on a group header: taller than a text baseline,
   so it centres itself rather than dragging the summary's alignment (and
   with it every Results group) off baseline. */
.rz-grp > summary .rr-stripe { height: 20px; flex: 0 0 auto; align-self: center; }

.rr-empty { color: var(--text-secondary); padding: 18px 2px; font-size: 13.5px; }

/* The Results plan-view "Tracked" chip: muted on purpose — it is provenance,
   not a call to action, and the row's own severity must stay the loudest
   thing on the line. */
.rz-trk {
  font-size: 11.5px; font-weight: 650; padding: 2px 9px; border-radius: 20px;
  background: var(--rz-track); color: var(--text-secondary); white-space: nowrap;
  text-decoration: none;
}
.rz-trk:hover, .rz-trk:focus-visible { color: var(--teal-dark); }
.rz-trk--over { background: var(--rz-crit-soft); color: var(--rz-crit); }

/* ── Audit binder (16.39.0) ─────────────────────────────────────────────
   The contents list is a promise: every line is a folder the zip really
   carries (the counts come straight from the binder manifest), so it reads
   as a checklist rather than as marketing. */
.binder-contents {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 4px 18px; font-size: 13px; color: var(--text-secondary);
}
.binder-contents li { position: relative; padding-left: 16px; }
.binder-contents li.is-missing { color: var(--text-secondary); }
.binder-contents li.is-missing::before { content: "○"; color: var(--text-secondary); }
.binder-contents li.is-missing a { font-weight: 600; }
.binder-contents li::before {
  content: "\2713"; position: absolute; left: 0;
  color: var(--teal-dark); font-weight: 700;
}
.binder-actions {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  margin-top: 14px;
}
.binder-baseline {
  display: inline-flex; gap: 7px; align-items: center;
  font-size: 13px; color: var(--text-secondary);
}
.binder-baseline select {
  font-size: 13px; padding: 5px 8px; border-radius: 8px;
  border: 1px solid var(--medium-gray); background: var(--white);
  color: var(--navy);
}

/* ── Audit: "From the auditor" — the questionnaire fill (16.40.0) ────────
   The colour grammar carries the non-negotiable: an answered slot is green,
   a slot still waiting on a human is neutral, and a slot the platform CANNOT
   answer is amber with an empty box. Amber is louder than neutral on
   purpose — those are the ones a reviewer must not skim past. */
.af-msg p { margin: 0; font-size: 13.5px; color: var(--text-secondary); }
.af-msg--bad  { border-left: 3px solid var(--red); }
.af-msg--warn { border-left: 3px solid var(--amber); }

.af-drop-form { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.af-drop {
  flex: 1 1 320px; display: block; cursor: pointer; text-align: center;
  border: 2px dashed var(--medium-gray); border-radius: 10px; padding: 20px;
  color: var(--text-secondary); font-size: 13.5px;
}
.af-drop:hover, .af-drop.is-over {
  border-color: var(--teal); background: var(--off-white);
}
.af-drop b { display: block; font-size: 14.5px; color: var(--text-primary); margin-bottom: 3px; }
.af-drop span { display: block; max-width: 62ch; margin: 0 auto; }
.af-drop small { display: block; margin-top: 6px; color: var(--text-muted); }
/* The whole card is the picker: the bare input would be a second, uglier one. */
.af-drop input[type="file"] { position: absolute; width: 1px; height: 1px;
  opacity: 0; overflow: hidden; }

.af-head { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
.af-name { font-weight: 650; font-size: 14.5px; }
/* 16.41.0 — the Word half says beta on the card and again above the download
   button. A format that can surprise a tenant must not do it quietly. */
.af-beta {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 999px;
  color: var(--amber); border: 1px solid var(--amber); background: #FFF8E7;
}
.af-note {
  margin: 8px 0 0; font-size: 12.5px; color: var(--text-secondary);
  max-width: 78ch;
}
.af-note--beta { border-left: 3px solid var(--amber); padding-left: 10px; }
.af-meta { font-size: 12.5px; color: var(--text-muted); }
.af-right { margin-left: auto; }
.af-bar {
  height: 8px; background: var(--medium-gray); border-radius: 4px;
  margin: 10px 0 4px; overflow: hidden;
}
.af-bar span { display: block; height: 100%; background: var(--teal); border-radius: 4px; }
.af-stats { font-size: 12.5px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.af-stats b { color: var(--text-primary); }
.af-nodata { color: var(--amber); font-weight: 600; }

.af-slot {
  border: 1px solid var(--medium-gray); border-radius: 10px;
  margin-top: 12px; overflow: hidden;
}
.af-slot--accepted, .af-slot--edited, .af-slot--user_written {
  border-color: var(--green);
}
.af-slot--no_data { border-color: var(--amber); }
.af-where {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted); font-weight: 700; padding: 8px 14px 0;
}
.af-q { font-size: 13.5px; font-weight: 600; padding: 2px 14px 8px; }
.af-a {
  display: block; width: calc(100% - 28px); margin: 0 14px 10px;
  padding: 10px 13px; border-radius: 8px; font-size: 13.5px;
  font-family: inherit; line-height: 1.45; color: var(--text-primary);
  border: 1px solid transparent; background: #E8F4F2; resize: vertical;
}
.af-a:focus { outline: none; border-color: var(--teal); background: var(--white); }
.af-slot--accepted .af-a, .af-slot--edited .af-a,
.af-slot--user_written .af-a { background: #E8F5E9; }
.af-slot--skipped .af-a { background: var(--light-gray); color: var(--text-secondary); }
.af-a--none {
  background: #FDF3E7; color: var(--text-secondary); font-style: italic;
  border: 0; padding: 10px 13px;
}
.af-a--write { background: var(--white); border-color: var(--amber); }
.af-slot.is-dirty .af-a { background: var(--white); border-color: var(--teal); }

.af-src { display: flex; gap: 6px; flex-wrap: wrap; padding: 0 14px 10px; }
.af-chip {
  font-size: 11px; font-weight: 600; padding: 2px 9px; border-radius: 20px;
  background: var(--medium-gray); color: var(--text-secondary);
}
.af-chip--document { background: #E8F4F2; color: var(--teal-dark); }
.af-chip--scan     { background: #E8F5E9; color: var(--green-dark); }
.af-chip--evidence { background: #EAF3FB; color: #2C6CA8; }
.af-chip--none     { background: #FDF3E7; color: #B3641A; }

.af-acts { display: flex; gap: 8px; padding: 0 14px 12px; align-items: center;
  flex-wrap: wrap; }
.af-acts .btn { padding: 5px 12px; font-size: 12.5px; }
.af-state { margin-left: auto; font-size: 11.5px; font-weight: 700; }
.af-state--ok    { color: var(--green-dark); }
.af-state--pend  { color: var(--text-muted); }
.af-state--skip  { color: var(--text-secondary); font-weight: 600; }
.af-state--needs { color: #B3641A; }

.af-apply {
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--medium-gray);
}
.af-check {
  font-size: 13px; color: var(--text-secondary);
  display: inline-flex; gap: 7px; align-items: center;
}
.af-delete { margin-top: 10px; }
.af-delete .btn { font-size: 12px; padding: 4px 10px; color: var(--text-muted); }

/* ── 16.50.0: Compliance calendar ──────────────────────────────────────
   Four card bands, one row per dated obligation, per the approved Arc 4
   mock. The row is chip · title with its inline source · right-aligned
   when. Bands are never hidden by the filter chips — only their rows are,
   and the header count follows (calendar.js). */
.cal-meta { color: var(--text-secondary); font-size: 13px; margin-top: 3px; }

.cal-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 16px; }
.cal-fchip {
  border: 1px solid var(--medium-gray); background: #fff; cursor: pointer;
  border-radius: 14px; padding: 4px 12px; font-size: 12px; font-weight: 600;
  color: var(--text-secondary); font-family: inherit;
}
.cal-fchip.is-on { background: var(--navy); color: #fff; border-color: var(--navy); }

.cal-band {
  background: #fff; border: 1px solid var(--medium-gray); border-radius: 10px;
  margin-bottom: 14px; overflow: hidden;
}
.cal-bhead {
  display: flex; align-items: center; gap: 8px; padding: 10px 16px;
  border-bottom: 1px solid var(--medium-gray);
}
.cal-bhead h2 { margin: 0; font-size: 13px; font-weight: 700; color: var(--navy); }
.cal-bcount {
  background: var(--light-gray); border-radius: 10px; padding: 1px 8px;
  font-size: 11px; font-weight: 600; color: var(--text-secondary);
}
/* An empty Overdue band is the good news, so the heading is loud either way. */
.cal-band--overdue .cal-bhead h2 { color: var(--rz-crit); }

.cal-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px; border-bottom: 1px solid var(--light-gray);
}
.cal-row:last-child { border-bottom: none; }
.cal-title { font-size: 13.5px; min-width: 0; }
.cal-title a { color: var(--navy); border-bottom: 1px dotted var(--medium-gray); }
.cal-title a:hover { color: var(--teal-dark); border-bottom-color: var(--teal); }
.cal-src { font-size: 11px; color: var(--text-muted); }
.cal-when {
  margin-left: auto; font-size: 12px; color: var(--text-secondary);
  white-space: nowrap;
}
.cal-when--over { color: var(--rz-crit); font-weight: 700; }

/* 16.52.0: the kind chips joined the one label system (skin spec §E) -
   tinted, sentence case, same metrics as .pill. A calendar is read as a
   list, and six solid colours down the left edge read as six alarms. */
.cal-chip {
  flex: 0 0 auto; border-radius: 6px; padding: 2.5px 10px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
/* 17.41.0: an unfiled DNSC filing. Crit, and it is the only DERIVED kind
   that gets the loud end of the palette: every other row on this page is a
   date the organisation set itself, and this one is a date the law set. */
.cal-chip--inc { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.cal-chip--doc { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
/* 17.4.3: "source changed" is a document row that is NOT a cadence falling
   due, so it reads warmer than a review and calmer than remediation. */
.cal-chip--srcchg { background: var(--tint-med-bg); color: var(--tint-med-ink); }
/* 17.6.0: the proof ledger's due dates. Sky, so a month with both reads as
   two colours rather than two shades of amber. */
.cal-chip--proof { background: var(--tint-sky-bg); color: var(--tint-sky-ink); }
.cal-chip--rr  { background: var(--tint-high-bg); color: var(--tint-high-ink); }
.cal-chip--ev  { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
/* 16.51.0: the three custom kinds. Deliberately the calmest colours on the
   page - a date somebody typed is not an alarm, and the derived rows have to
   keep the loud end of the palette. */
.cal-chip--audit    { background: var(--tint-navy-bg); color: var(--tint-navy-ink); }
.cal-chip--training { background: var(--tint-sky-bg);  color: var(--tint-sky-ink); }
.cal-chip--other    { background: var(--tint-mute-bg); color: var(--tint-mute-ink); }

.cal-none {
  margin: 0; padding: 12px 16px; font-size: 12.5px; font-style: italic;
  color: var(--text-muted);
}

/* ── 16.51.0: custom events + the ICS subscription box ──────────────────
   Both are secondary to the four bands: the add form is folded away behind
   a summary, and the subscribe card sits after everything, because the
   common visit to this page is a read. */
.cal-add { margin-bottom: 14px; }
.cal-add-toggle {
  display: inline-block; cursor: pointer; font-size: 12.5px; font-weight: 600;
  color: var(--teal-dark); list-style: none; padding: 2px 0;
}
.cal-add-toggle::-webkit-details-marker { display: none; }
.cal-add-toggle:hover { text-decoration: underline; }
.cal-add-form {
  display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: flex-end;
  background: #fff; border: 1px solid var(--medium-gray); border-radius: 10px;
  padding: 14px 16px; margin-top: 8px;
}
.cal-fld { display: flex; flex-direction: column; gap: 4px; }
.cal-fld > span { font-size: 11px; font-weight: 600; color: var(--text-secondary); }
.cal-fld input, .cal-fld select {
  border: 1px solid var(--medium-gray); border-radius: 6px; padding: 5px 8px;
  font-size: 13px; font-family: inherit; color: var(--text-primary);
}
.cal-fld--wide { flex: 1 1 260px; }
.cal-fld--wide input { width: 100%; }
.cal-check {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px;
  color: var(--text-secondary); padding-bottom: 5px;
}

/* The delete affordance exists only on rows this page owns, so it stays
   quiet until the row is hovered - a permanent × on every custom row would
   read as the point of the row. */
.cal-del { margin: 0; line-height: 0; }
.cal-del-btn {
  border: none; background: none; cursor: pointer; font-size: 16px;
  line-height: 1; padding: 0 2px; color: var(--medium-gray);
  font-family: inherit;
}
.cal-row:hover .cal-del-btn { color: var(--text-muted); }
.cal-del-btn:hover { color: var(--rz-crit); }

/* 16.57.0 (⑦b §2): the box moved from under the four bands to the top of
   the page and became a disclosure - closed it is one quiet line, so
   promoting it costs the reader nothing. `margin-top` went with the move. */
.cal-sub {
  background: #fff; border: 1px solid var(--medium-gray); border-radius: 10px;
  padding: 10px 14px; margin-bottom: 12px;
}
.cal-sub-toggle { cursor: pointer; list-style: none; }
.cal-sub-toggle::-webkit-details-marker { display: none; }
.cal-sub-toggle::after { content: "▾"; margin-left: 6px; color: var(--text-muted); font-size: 11px; }
.cal-sub[open] > .cal-sub-toggle::after { content: "▴"; }
.cal-sub[open] > .cal-sub-toggle { margin-bottom: 8px; }
.cal-sub h2 { display: inline; margin: 0; font-size: 13px; font-weight: 700; color: var(--navy); }
.cal-sub-url {
  margin: 0 0 6px; padding: 8px 10px; background: var(--light-gray);
  border-radius: 6px; overflow-wrap: anywhere;
}
.cal-sub-url code { font-size: 12px; color: var(--navy); }
.cal-sub-once { margin: 0 0 8px; font-size: 12px; font-weight: 600; color: var(--teal-dark); }
.cal-sub-warn { margin: 0 0 10px; font-size: 12px; color: var(--text-secondary); }

/* ── 16.62.0 (⑦f): the timeline view ───────────────────────────────────
   Everything here is positioned from a percentage the SERVER computed
   (services/calendar_timeline.py) and printed into a style="left:…%".
   There is no chart library and nothing measures the DOM, so the whole
   drawing is: absolute children of a relative track. */

/* 16.69.0 (⑧d §2): the view switch WEARS .rz-gsel/.rz-gopt now instead of
   carrying a cal- copy of them. The copy said in its own comment that it
   "borrows the .rz-gopt look" - which is exactly the state two families are
   in the moment before they drift apart. */

.cal-tl {
  /* The label gutter, shared by the axis, every row and the gridline layer:
     one number, so the lines cannot drift off the dates they mark. */
  --tl-gutter: 290px;
  background: #fff; border: 1px solid var(--medium-gray); border-radius: 10px;
  overflow: hidden;
}
.cal-tl-head {
  display: flex; align-items: center; gap: 8px; padding: 10px 16px;
  border-bottom: 1px solid var(--medium-gray);
}
.cal-tl-head h2 { margin: 0; font-size: 13px; font-weight: 700; color: var(--navy); }
.cal-tl-range { font-size: 11.5px; color: var(--text-muted); }
.cal-tl-head .cal-bcount { margin-left: auto; }

/* The PANEL scrolls sideways on a narrow screen, never the page. */
.cal-tl-scroll { overflow-x: auto; }
.cal-tl-inner { min-width: 780px; padding: 0 16px 14px; }

.cal-tl-axis, .cal-tl-row {
  display: grid; grid-template-columns: var(--tl-gutter) 1fr; align-items: center;
}
/* Two lines, not one: the today marker and a month tick can be four days
   apart, and on one line they overlap into an unreadable smear whenever the
   week happens to straddle a month boundary. */
.cal-tl-axis {
  height: 34px; border-bottom: 1px solid var(--medium-gray); background: #fff;
}
.cal-tl-track { position: relative; height: 100%; }
.cal-tl-tick, .cal-tl-now {
  position: absolute; font-size: 10.5px; white-space: nowrap;
  transform: translateX(-50%);
}
.cal-tl-tick { top: 3px; color: var(--text-muted); }
.cal-tl-now { top: 18px; color: var(--tint-crit-ink); font-weight: 700; }

.cal-tl-body { position: relative; padding-top: 4px; }
.cal-tl-lines {
  position: absolute; left: var(--tl-gutter); right: 0; top: 0; bottom: 0;
  pointer-events: none;
}
.cal-tl-line, .cal-tl-nowline {
  position: absolute; top: 0; bottom: 0; width: 1px;
}
.cal-tl-line { background: var(--light-gray); }
/* Today is the one line that is not a gridline, so it is the one line that
   is coloured - the softened alert red the rest of the product uses. */
.cal-tl-nowline { background: var(--tint-crit-ink); opacity: .55; width: 2px; }

.cal-tl-lane { position: relative; z-index: 1; padding: 6px 0 2px; }
.cal-tl-lhead { display: flex; align-items: center; gap: 6px; padding: 4px 0 2px; }

.cal-tl-row { min-height: 22px; }
/* Title first and elastic, the date pinned after it: a long document name
   must lose its tail rather than push the date it is due out of the row. */
.cal-tl-lbl {
  display: flex; align-items: baseline; gap: 8px; min-width: 0;
  padding-right: 14px; font-size: 12.5px;
}
.cal-tl-lbl a {
  color: var(--navy); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal-tl-lbl a:hover { color: var(--teal-dark); }
.cal-tl-when {
  flex: 0 0 auto; margin-left: auto; font-size: 11px; color: var(--text-muted);
  white-space: nowrap;
}
.cal-tl-row .cal-tl-track { height: 22px; }

/* The mark: a short bar, not a duration - a calendar entry is a DAY, and a
   bar with a width would be inventing a range nobody recorded. */
.cal-tl-bar {
  position: absolute; top: 7px; left: 0; width: 9px; height: 9px;
  border-radius: 3px; transform: translateX(-50%);
  background: var(--tint-mute-ink); display: block;
}
.cal-tl-bar:hover { outline: 2px solid var(--teal); outline-offset: 1px; }
.cal-tl-bar--incident   { background: var(--tint-crit-ink); }
.cal-tl-bar--doc_review { background: var(--tint-ok-ink); }
.cal-tl-bar--source_changed { background: var(--tint-med-ink); }
.cal-tl-bar--proof      { background: var(--tint-sky-ink); }
.cal-tl-bar--register   { background: var(--tint-high-ink); }
.cal-tl-bar--evidence   { background: var(--tint-med-ink); }
.cal-tl-bar--audit      { background: var(--tint-navy-ink); }
.cal-tl-bar--training   { background: var(--tint-sky-ink); }
.cal-tl-bar--other      { background: var(--tint-mute-ink); }
/* Overdue outranks the kind colour: it is the one state on this page that
   means somebody already missed a date. Softened, per the skin spec. */
.cal-tl-bar.is-over { background: var(--tint-crit-ink); }
/* Pinned to an edge because its date is outside the window - drawn as a
   chevron so it never reads as a date that happens to fall exactly there. */
.cal-tl-bar.is-clamped { border-radius: 2px; width: 6px; opacity: .8; }

/* ── 16.52.0: Branding card (Settings) ─────────────────────────────────
   The preview sits on navy because that is the only background the logo
   will ever be seen against - a white-on-white preview would let an admin
   upload an invisible logo and only find out from the sidebar. */
.brandcard-preview {
  background: var(--brand-primary); border-radius: 8px;
  padding: 14px 16px; margin: 10px 0; display: flex; align-items: center;
}
.brandcard-preview img { max-width: 180px; max-height: 54px; height: auto; display: block; }
.brandcard-meta { font-size: 11.5px; color: var(--text-muted); margin: 0 0 10px; }
.brandcard-err {
  background: var(--tint-crit-bg); color: var(--tint-crit-ink);
  border-radius: 6px; padding: 8px 12px; font-size: 12.5px; font-weight: 600;
  margin: 0 0 10px;
}
.brandcard-forms { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.brandcard-forms form { margin: 0; }
/* 16.56.0 (⑦a §c): one extra line inside an existing card, divided from what
   is above it rather than boxed off from it — a setting, not a section. */
.set-line {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-top: 14px; padding-top: 12px;
  border-top: 1px dashed var(--medium-gray);
}
.set-line form { margin: 0; }
.set-line > .muted { flex: 1 1 200px; }

/* ═══════════════════════════════════════════════════════════════════════
   16.68.0 (⑦i) — the shared audit report
   ═══════════════════════════════════════════════════════════════════════
   Two audiences, one stylesheet. `.sh-*` is the Reports-hub card an admin
   uses (inside the shell, beside the binder and the fills); `.sr-*` is the
   two OUTSIDER pages — the mailbox gate and the frozen viewer — which have
   no shell around them at all and so have to bring their own page frame.
   They share this file rather than inlining their own because an auditor
   looking at a tenant's posture should be looking at the tenant's product,
   with the same type, the same pills and the same tints as everything else. */

/* ── The hub card ───────────────────────────────────────────────────── */
.sh-form { margin-top: 4px; }
.sh-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }
.sh-field { flex: 1 1 260px; display: block; }
.sh-field--narrow { flex: 0 1 150px; }
.sh-label {
  display: block; font-family: var(--font-display);
  font-size: 11.5px; font-weight: 600; color: var(--navy); margin-bottom: 4px;
}
.sh-field input, .sh-field select {
  width: 100%; padding: 7px 9px; font: inherit; font-size: 13px;
  border: 1px solid var(--medium-gray); border-radius: 6px;
  background: var(--white); color: var(--text-primary);
}
.sh-field input:disabled, .sh-field select:disabled { background: var(--light-gray); }
.sh-go { flex: 0 0 auto; }
.sh-check { display: flex; gap: 8px; align-items: flex-start; margin-top: 12px; font-size: 12.5px; }
.sh-sub { display: block; color: var(--text-secondary); font-size: 11.5px; margin-top: 2px; }
.sh-note {
  margin: 12px 0 0; padding: 8px 10px; font-size: 11.5px;
  color: var(--text-secondary); background: var(--light-gray);
  border: 1px dashed var(--medium-gray); border-radius: 6px;
}
.sh-list { list-style: none; margin: 6px 0 0; padding: 0; }
.sh-item {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  padding: 10px 0; border-top: 1px solid var(--light-gray); font-size: 13px;
}
.sh-item__who { flex: 1 1 240px; min-width: 0; }
.sh-item__sub, .sh-item__exp { display: block; font-size: 11.5px; color: var(--text-secondary); }
.sh-item__exp { flex: 0 0 auto; }
.sh-item form { margin: 0; }
.sh-revoke { color: var(--red); border: 1px solid var(--tint-crit-bg); background: var(--white); }
.sh-revoke:hover { background: var(--tint-crit-bg); }

/* ── The outsider pages ─────────────────────────────────────────────── */
.sr-body { background: var(--light-gray); padding: 0 0 48px; }
.sr-page { max-width: 1080px; margin: 26px auto; padding: 0 14px; }

/* The gate (mock S2): one narrow card, centred, and nothing else on the
   page — every pixel of chrome would be a pixel of information about a
   tenant, handed to whoever typed an address in. */
.sr-gate {
  max-width: 460px; margin: 12vh auto; background: var(--white);
  border-radius: 12px; padding: 28px; text-align: center;
  box-shadow: 0 8px 30px rgba(0, 31, 91, .14);
}
.sr-gate__brand {
  font-family: var(--font-display); font-weight: 700;
  color: var(--navy); font-size: 14px; margin-bottom: 4px;
}
.sr-gate__sub, .sr-gate__sent { color: var(--text-secondary); font-size: 12.5px; margin: 0 0 16px; }
.sr-gate__form { margin: 18px 0 0; text-align: left; }
.sr-gate__label {
  display: block; font-family: var(--font-display); font-size: 11.5px;
  font-weight: 600; color: var(--navy); margin: 10px 0 4px;
}
.sr-gate__input {
  width: 100%; padding: 8px 10px; font: inherit; font-size: 13px;
  border: 1px solid var(--medium-gray); border-radius: 6px; background: var(--white);
}
.sr-gate__input--code {
  font-family: var(--font-display); font-size: 22px; font-weight: 700;
  letter-spacing: 8px; text-align: center; color: var(--navy);
}
.sr-gate__btn { width: 100%; margin-top: 14px; justify-content: center; }
.sr-gate__again { margin: 10px 0 0; }
.sr-gate__link {
  background: none; border: none; padding: 0; font: inherit; font-size: 12px;
  color: var(--navy); text-decoration: underline; cursor: pointer;
}
.sr-note {
  margin: 16px 0 0; padding: 8px 10px; text-align: left; font-size: 11.5px;
  color: var(--text-secondary); background: var(--light-gray);
  border: 1px dashed var(--medium-gray); border-radius: 6px;
}
.sr-note--warn {
  background: var(--tint-med-bg); color: var(--tint-med-ink);
  border: 1px solid var(--tint-med-bg); font-weight: 600; text-align: left;
}

/* The viewer (mock S3): navy header carrying the TENANT's identity, our
   credit opposite it, and the frozen-snapshot banner directly under both —
   the reader should not be able to scroll past what it is they are reading. */
.sr-head {
  background: var(--navy); color: var(--white); border-radius: 10px 10px 0 0;
  padding: 16px 20px; display: flex; align-items: center; gap: 14px;
}
.sr-head__logo { max-height: 40px; max-width: 160px; height: auto; display: block; }
.sr-head__disc {
  width: 40px; height: 40px; border-radius: 9px; background: var(--green);
  display: grid; place-items: center; font-family: var(--font-display);
  font-weight: 700; flex: 0 0 auto;
}
.sr-head__who { min-width: 0; }
.sr-head__name { font-family: var(--font-display); font-weight: 700; font-size: 15px; }
.sr-head__meta { color: var(--sidebar-text); font-size: 12px; margin-top: 2px; }
.sr-head__credit {
  margin-left: auto; text-align: right; font-size: 11px;
  color: var(--sidebar-text); font-family: var(--font-display);
}
.sr-head__credit b { display: block; color: var(--white); }
.sr-banner {
  background: var(--tint-navy-bg); color: var(--tint-navy-ink);
  font-family: var(--font-display); font-size: 12px; padding: 7px 20px;
  display: flex; flex-wrap: wrap; gap: 14px;
}
.sr-banner__end { margin-left: auto; }
.sr-body-panel {
  background: var(--white); border: 1px solid var(--medium-gray);
  border-top: none; border-radius: 0 0 10px 10px; padding: 20px;
}
.sr-kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px; margin-bottom: 18px;
}
.sr-kpi { border: 1px solid var(--light-gray); border-radius: 8px; padding: 12px 14px; }
.sr-kpi__n {
  font-family: var(--font-display); font-size: 21px; font-weight: 700; color: var(--navy);
}
.sr-kpi__l { font-family: var(--font-display); font-size: 11px; color: var(--text-secondary); margin-top: 2px; }
.sr-sec { margin-top: 22px; }
.sr-sec__head { display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; }
.sr-sec__head h2 { margin: 0; font-size: 15px; color: var(--navy); }
.sr-sec__note { color: var(--text-secondary); font-size: 12px; }
.sr-toggle { margin-left: auto; display: inline-flex; border: 1px solid var(--medium-gray); border-radius: 6px; overflow: hidden; }
.sr-toggle__opt {
  padding: 4px 11px; font-family: var(--font-display); font-size: 11.5px;
  font-weight: 700; color: var(--navy); text-decoration: none; background: var(--white);
}
.sr-toggle__opt.is-on { background: var(--navy); color: var(--white); }
.sr-group { border: 1px solid var(--light-gray); border-radius: 8px; margin-top: 10px; }
.sr-group > summary {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  padding: 10px 14px; cursor: pointer; font-size: 13px;
}
.sr-group > summary b { font-family: var(--font-display); color: var(--navy); }
.sr-group__counts { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.sr-group__n { margin-left: auto; color: var(--text-secondary); font-size: 12px; }
.sr-group--quiet > summary b { color: var(--text-secondary); }
.sr-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.sr-table th, .sr-table td {
  text-align: left; padding: 7px 14px; border-top: 1px solid var(--light-gray);
  vertical-align: top;
}
.sr-table th { font-family: var(--font-display); font-size: 11px; color: var(--text-secondary); }
.sr-table__sev { width: 108px; }
.sr-table__meta { width: 210px; color: var(--text-secondary); }
.sr-table__d { color: var(--text-secondary); margin-top: 2px; }
.sr-table--matrix { border: 1px solid var(--light-gray); border-radius: 8px; margin-top: 10px; }
.sr-table--matrix td:not(:first-child), .sr-table--matrix th:not(:first-child) { text-align: right; width: 74px; }
.sr-phases { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-top: 10px; }
.sr-phase { border: 1px solid var(--light-gray); border-radius: 8px; padding: 12px 14px; }
.sr-phase__t { font-family: var(--font-display); font-size: 11.5px; font-weight: 700; color: var(--navy); }
.sr-phase__n { font-family: var(--font-display); font-size: 21px; font-weight: 700; color: var(--navy); }
.sr-phase__e { font-size: 11.5px; color: var(--text-secondary); }
.sr-empty, .sr-foot { color: var(--text-secondary); font-size: 12px; margin: 12px 0 0; }
.sr-foot { border-top: 1px solid var(--light-gray); padding-top: 12px; margin-top: 22px; }

/* ── 16.73.0: /manual ──────────────────────────────────────────────────────
   The product manual: the sidebar-footer entry button (.helpswitch, the same
   visual idiom as .langswitch__opt beside it) and the TOC-rail + article
   layout (man-*). Everything the page needs is namespaced man- because the
   chapter BODIES are authored HTML in app/manual_content.py: a body that
   reached for a generic class (.callout, .steps) would be styled by whatever
   the rest of the app happens to mean by that name today. */

/* Entry point — sits beside the language flags in the sidebar footer, inside
   the same .langswitch row. Three chips do not fit across a 250px sidebar, so
   the row has to wrap: the Manual chip drops onto its own line under EN/RO,
   which is exactly how the approved mockup rendered. This is the one rule in
   the block that touches a class outside the man-/helpswitch namespace, and it
   lives here rather than up at .langswitch so the whole /manual change stays
   findable in one place. */
.langswitch { flex-wrap: wrap; }

.helpswitch {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  padding: 4px 10px; border-radius: 7px; text-decoration: none;
  color: var(--sidebar-text); background: transparent;
  border: 1px solid rgba(255, 255, 255, .18);
}
.helpswitch:hover { background: var(--sidebar-hover); color: var(--white); text-decoration: none; }
.helpswitch svg { width: 14px; height: 14px; }
.helpswitch .helpswitch__ext { width: 10px; height: 10px; opacity: .6; }

/* Search box in the page header. */
.man-search { position: relative; }
.man-search input {
  width: 320px; max-width: 100%; padding: 10px 12px 10px 36px;
  border-radius: 8px; border: 1px solid var(--medium-gray);
  background: var(--white); font: inherit;
}
.man-search input:focus {
  outline: none; border-color: var(--brand-sky);
  box-shadow: 0 0 0 3px rgba(88, 191, 228, .25);
}
.man-search__icon {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-muted);
}
.man-search kbd {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-family: ui-monospace, Menlo, monospace; font-size: 10px;
  color: var(--text-muted); border: 1px solid var(--medium-gray);
  border-radius: 4px; padding: 1px 5px; background: var(--off-white);
}

/* Layout: sticky TOC rail + article column. */
.man { display: grid; grid-template-columns: 248px 1fr; gap: 18px; align-items: start; }
@media (max-width: 900px) { .man { grid-template-columns: 1fr; } }

.man-toc {
  background: var(--white); border: 1px solid var(--medium-gray);
  border-radius: 10px; padding: 10px 8px;
  position: sticky; top: 24px; max-height: calc(100vh - 48px); overflow-y: auto;
}
.man-toc__group {
  font-family: var(--font-display); font-size: 10.5px; font-weight: 700;
  letter-spacing: 1.4px; text-transform: uppercase; color: var(--text-muted);
  padding: 12px 12px 4px;
}
.man-toc__group:first-child { padding-top: 4px; }
.man-toc a {
  display: block; padding: 7px 12px; border-radius: 7px;
  color: var(--text-primary); font-size: 13.5px;
}
.man-toc a:hover { background: var(--light-gray); text-decoration: none; }
.man-toc a.is-on { background: var(--tint-navy-bg); color: var(--tint-navy-ink); font-weight: 700; }
.man-toc__empty { padding: 10px 12px; font-size: 12.5px; color: var(--text-muted); }
/* Set by manual.js on filtered-out entries; also what hides the chapters the
   reader is not on. With JS off nothing carries it, so everything renders. */
.man-hidden { display: none; }

/* min-width:0 or a long unbroken string in a chapter blows the grid column
   out past the viewport instead of wrapping inside it. */
.man-arts { min-width: 0; }
.man-art {
  background: var(--white); border: 1px solid var(--medium-gray);
  border-radius: 10px; padding: 28px 32px 22px; max-width: 860px;
}
/* JS off: every chapter renders in reading order, so they need separating. */
.man-art + .man-art { margin-top: 18px; }

.man-crumbs { font-size: 12px; color: var(--text-muted); margin-bottom: 10px; }
.man-crumbs b { color: var(--text-secondary); font-weight: 600; }
.man-art h2 { margin: 0 0 4px; font-size: 22px; color: var(--navy); font-weight: 700; }
.man-meta { font-size: 12px; color: var(--text-muted); margin-bottom: 18px; }
.man-art p { line-height: 1.65; color: var(--text-primary); margin: 0 0 14px; }
.man-art h3 { font-size: 15px; color: var(--navy); margin: 22px 0 8px; }
.man-art ul { line-height: 1.65; margin: 0 0 14px; padding-left: 20px; }
.man-art ul li { margin-bottom: 6px; }

/* Numbered step list. */
.man-steps { counter-reset: manstep; list-style: none; padding: 0; margin: 0 0 14px; }
.man-steps li {
  counter-increment: manstep; position: relative;
  padding: 0 0 14px 40px; line-height: 1.6;
}
.man-steps li::before {
  content: counter(manstep); position: absolute; left: 0; top: 0;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--tint-navy-bg); color: var(--tint-navy-ink);
  font-weight: 700; font-size: 13px; display: grid; place-items: center;
  font-family: var(--font-display);
}
.man-steps li + li::after {
  content: ""; position: absolute; left: 12.5px; top: -12px;
  width: 1px; height: 10px; background: var(--medium-gray);
}

/* Callouts: tip / note / warning. */
.man-callout {
  display: flex; gap: 10px; border-radius: 8px; padding: 12px 14px;
  margin: 0 0 14px; font-size: 13px; line-height: 1.55;
}
.man-callout svg { width: 16px; height: 16px; flex: none; margin-top: 1px; }
.man-callout--tip  { background: var(--tint-ok-bg);  color: var(--tint-ok-ink); }
.man-callout--note { background: var(--tint-sky-bg); color: var(--tint-sky-ink); }
.man-callout--warn { background: var(--tint-med-bg); color: var(--tint-med-ink); }
.man-callout a { color: inherit; text-decoration: underline; }

/* Landing tiles (the Welcome chapter). */
.man-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 6px 0 16px; }
@media (max-width: 900px) { .man-tiles { grid-template-columns: 1fr; } }
.man-tile {
  border: 1px solid var(--medium-gray); border-radius: 10px; padding: 14px;
  background: var(--white); color: var(--text-primary); display: block;
}
.man-tile:hover { border-color: var(--brand-sky); box-shadow: 0 1px 6px rgba(0, 31, 91, .08); text-decoration: none; }
.man-tile b {
  display: flex; align-items: center; gap: 8px; color: var(--navy);
  font-size: 13.5px; font-family: var(--font-display);
}
.man-tile b svg { width: 16px; height: 16px; color: var(--teal); flex: none; }
.man-tile span { display: block; margin: 6px 0 0; font-size: 12.5px; color: var(--text-secondary); line-height: 1.5; }

/* Related-in-group links + prev/next pager. */
.man-rel { border-top: 1px solid var(--medium-gray); margin-top: 22px; padding-top: 14px; }
.man-rel h4 {
  margin: 0 0 8px; font-size: 11px; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--text-muted);
  font-family: var(--font-display);
}
.man-rel a { display: inline-flex; align-items: center; gap: 6px; margin: 0 14px 6px 0; font-size: 13px; }
.man-rel a svg { width: 14px; height: 14px; flex: none; }

.man-pager { display: flex; justify-content: space-between; gap: 12px; margin-top: 18px; }
.man-pager a {
  flex: 1; max-width: 46%; border: 1px solid var(--medium-gray);
  border-radius: 8px; padding: 10px 14px; color: var(--text-primary);
}
.man-pager a:hover { border-color: var(--brand-sky); text-decoration: none; }
.man-pager a span { display: block; font-size: 11px; color: var(--text-muted); }
.man-pager a b { color: var(--navy); font-size: 13.5px; }
.man-pager a.man-pager--next { text-align: right; margin-left: auto; }

/* ═══════════════════════════════════════════════════════════════════════
   Increment 4 (docs/portfolio_invite_licensing_spec.md) — the portfolio
   surface, drawn to docs/mockups/portfolio_mgmt_mock.html.

   Deliberately small. The mock's frame IS the product's .card, its table IS
   .data-table, and its status pills ARE the .chip--ok/--due/--over the
   licence cell has used since 17.10.0 — so everything below is the handful
   of shapes the mock adds and nothing else: the navy HOME badge, the neutral
   identity/role pill, the tinted sub-row, the action cluster and the
   collapsed legacy block. Re-declaring a palette here would have given the
   portfolio its own colours, which is the one thing a management screen must
   not have.
   ═══════════════════════════════════════════════════════════════════════ */
.pf-who { display: flex; gap: 12px; align-items: center; }

.pf-filters { display: flex; gap: 8px; margin-bottom: 12px; align-items: center; flex-wrap: wrap; }
.pf-filters__q { flex: 1 1 240px; max-width: 340px; }
.pf-filters input, .pf-filters select {
  font: inherit; font-size: 12.5px; padding: 7px 10px;
  border: 1px solid var(--medium-gray); border-radius: 8px; background: #fff;
}
.pf-count { margin: 0 0 8px; }

.pf__t td { vertical-align: middle; }
.pf__name { font-weight: 700; color: var(--brand-primary, #001F5B); }
.pf__sub { margin-top: 1px; }
/* The GUID's new home. A closed <details> is one line of chrome and no
   JavaScript; the title attribute on the name covers the hover case. Kept
   deliberately quiet - it is a lookup an admin performs once a month, not a
   column, and the row's job is the tenant's NAME. */
.pf__id { margin-top: 2px; }
.pf__id > summary { cursor: pointer; font-size: 10px; list-style: none;
                    letter-spacing: .04em; text-transform: uppercase; opacity: .65; }
.pf__id > summary:hover { opacity: 1; }
.pf__id > summary::-webkit-details-marker { display: none; }
.pf__id code { word-break: break-all; font-size: 10.5px; }

/* HOME. The one place the portfolio spends a solid brand colour, because it
   marks the one row that behaves differently: no licence, no extend, no
   suspend. */
.pf-chip--home {
  background: var(--brand-primary, #001F5B); color: #fff;
  letter-spacing: .04em; margin-left: 4px;
}
/* Identity and role pills: NEUTRAL on purpose. Neither answers a question
   about health, so neither may borrow the ok/due/over tints that do. */
.pf-chip--id { background: #eef2f9; color: var(--brand-primary, #001F5B); }

.pf-actrow { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; align-items: center; }
.pf-actrow > form { display: inline; margin: 0; }
.pf-danger { color: var(--color-danger, #c0392b); }

.pf__subrow > td { background: #fbfcfe; }
.pf-sub { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.pf-sub > form { display: inline; margin: 0; }
.pf-sub select { font: inherit; font-size: 11.5px; padding: 3px 7px; border: 1px solid var(--medium-gray); border-radius: 7px; background: #fff; }

.pf-legacy { margin-top: 16px; }
.pf-legacy > summary { cursor: pointer; display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.pf-legacy__body { margin-top: 14px; }


/* ═══════════════════════════════════════════════════════════════════════
   17.2.2 — the mobile unlock (docs/mobile_unlock_spec.md)
   ═══════════════════════════════════════════════════════════════════════
   The PO decision this whole section is written under: a responsive pass,
   not a second site, and the desktop stays PIXEL-IDENTICAL. That is why
   everything below except the two `display: none` declarations that hide
   the new chrome sits inside a `max-width` media block, and why no rule
   here re-opens .shell / .sidebar / .main at the top level. The two
   exceptions are the new elements themselves - a top bar and a backdrop
   that did not exist before this increment and render nothing above the
   breakpoint, so hiding them cannot move a desktop pixel.
   tests/test_mobile_unlock.py is the gate on all of that.

   The audit this answers measured, at a 375px device: 739px of virtual
   viewport on the dashboard, 682 on /results, 633 on /remediation, 576 on
   /calendar and 488 on /integrations - a page that is wider than the phone
   is a page every reader scrolls sideways to finish. */

/* Mobile-only chrome. Absent from the desktop layout entirely. */
.mtop { display: none; }
.drawer-backdrop { display: none; }

/* ── ≤768px: the sidebar becomes a drawer ──────────────────────────────
   The 250px rail is the single largest viewport forcer in the product: at
   375px it spends two thirds of the screen on navigation and leaves the
   content column too narrow to set a sentence in. Off-canvas by TRANSFORM
   rather than `display: none`, so the nav stays in the accessibility tree
   and the open/close is a state a screen reader can follow (the button's
   aria-expanded, the drawer's aria-hidden - both wired in base.html). */
@media (max-width: 768px) {
  /* Two columns become one document flow: the drawer is fixed and out of
     flow, so nothing is left holding a 250px track open. */
  .shell { display: block; }

  .mtop {
    display: flex; align-items: center; gap: 12px;
    position: sticky; top: 0; z-index: 45;
    min-height: 52px; padding: 8px 12px;
    background: var(--sidebar-bg); color: var(--sidebar-text-active);
  }
  .mtop__burger {
    flex: none; display: grid; place-items: center;
    width: 40px; height: 40px; padding: 0;
    border: 0; border-radius: 9px; cursor: pointer;
    background: transparent; color: var(--sidebar-text-active);
  }
  .mtop__burger:hover { background: var(--sidebar-hover); }
  .mtop__burger svg { width: 22px; height: 22px; display: block; }
  .mtop__logo { height: 24px; width: auto; max-width: 60vw; display: block; }
  .mtop__ident {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-family: var(--font-display); font-size: 15px; font-weight: 700;
    color: var(--sidebar-text-active);
  }

  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0;
    width: 268px; max-width: 84vw; height: 100%;
    z-index: 60; overflow-y: auto;
    transform: translateX(-100%);
    transition: transform .18s ease;
  }
  body.drawer-open .sidebar { transform: translateX(0); }
  /* The page behind an open drawer must not scroll under the reader's
     thumb. Released the moment the drawer closes; nothing persists. */
  body.drawer-open { overflow: hidden; }

  body.drawer-open .drawer-backdrop {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: rgba(0, 12, 40, .45);
  }

  /* 28px of side padding on a 375px screen is a tenth of the reader's
     screen spent on nothing. */
  .main { padding: 16px 14px; }
}

/* Motion is a nicety here, and a drawer that slides is the one place in
   this stylesheet where that matters enough to say so. */
@media (max-width: 768px) and (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
}

/* ── ≤640px: the collapse tier ──────────────────────────────────────────
   The drawer alone does not make the pages fit: the grids, filter rows and
   KPI strips underneath were laid out for the width the sidebar used to
   leave, and several of them carry FIXED tracks whose min-content keeps
   inflating the viewport even after the rail is gone. Each rule below was
   found with a computed-style sweep of the five audit pages, not guessed;
   the selector comment says which page produced it. */
@media (max-width: 640px) {
  /* 17.6.3: the card summary's state cluster stacks under the name */
  .doc-card__summary { flex-wrap: wrap; }
  .doc-card__state { justify-content: flex-start; margin-left: 0; flex-basis: 100%; }

  /* Every page. The header is a nowrap flex row of an <h1> and an action
     cluster; on /results that cluster alone is 275px, so the row was
     pushing the page 48px past the device on its own. */
  .page-header { flex-direction: column; align-items: stretch; gap: 10px; }
  .page-header__act { flex-wrap: wrap; }

  /* 17.5.0 sources strip. Four columns holding an icon, a site URL, chips
     and three buttons is 480px of guaranteed width; at 375px the row stacks
     and the icon column goes, which is the only part that carries nothing. */
  .evsrc__row { grid-template-columns: minmax(0, 1fr); }
  .evsrc__icon { display: none; }
  .evsrc__acts .btn { flex: 1 1 auto; }

  /* 17.6.0 proof ledger. Seven columns of a compliance table cannot be
     made narrow, so at 375px the table stops being a table: each row is a
     card and every cell prints its own header from data-l. The three-door
     question stacks for the same reason - three 1fr doors at 375px are
     three unreadable columns. */
  .evproof__t, .evproof__t tbody, .evproof__t tr, .evproof__t td { display: block; width: 100%; }
  .evproof__t thead { display: none; }
  .evproof__row { border: 1px solid var(--light-gray); border-radius: 10px; margin-bottom: 10px; }
  .evproof__t td { border-bottom: 0; padding: 6px 12px; }
  .evproof__t td[data-l]::before {
    content: attr(data-l) " "; display: block;
    font-size: 10px; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-muted); font-weight: 700;
  }
  .evproof__t td:empty { display: none; }
  .evproof__acts { justify-content: flex-start; }
  .evproof__acts .btn { flex: 1 1 auto; text-align: center; }
  .evproof__what { max-width: none; }
  .evproof__doors { grid-template-columns: minmax(0, 1fr); }

  /* Dashboard. `1fr` is `minmax(auto, 1fr)`, so the 1100px collapse to one
     column did NOT stop the column's min-content from sizing the track -
     the row was 461px wide inside a 319px content column. minmax(0, 1fr)
     is the whole fix; the .hbar rule below is what makes the 0 survivable. */
  .dashboard-row { grid-template-columns: minmax(0, 1fr); }

  /* Dashboard + Progress. Six FIXED columns (132/42/84/86/16 plus gaps =
     415px of guaranteed width) were the row's real forcer. Alignment
     across rows was the point of those columns (16.5.0); at this width
     there is no column to align because the row wraps, so the grid becomes
     a wrapping flex line: label on its own line, bar and numbers under it. */
  .hbar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 5px 8px;
  }
  .hbar__label { flex: 1 1 100%; white-space: normal; }
  .hbar__track { flex: 1 1 110px; }
  .hbar__value, .hbar__di, .hbar__chips { flex: 0 0 auto; }

  /* Every page, and the subtlest forcer the sweep turned up: the ⑧c help
     bubbles. `[data-help]::after` is a 260px absolutely-positioned box
     anchored to a 15px mark, and an out-of-flow box still contributes to
     its ancestors' scrollable overflow even while it is invisible - so
     every (?) near the right of a column was quietly widening the page by
     up to 64px, hover or no hover. Fixed positioning takes it out of the
     document's overflow entirely, and a full-width bar pinned to the foot
     of the screen is the readable shape for a 150-character sentence on a
     phone anyway: a 260px bubble tethered to a mark has nowhere to go at
     this width without hanging off one edge or the other. */
  [data-help]::after,
  [data-help].qhelp--end::after {
    position: fixed; left: 12px; right: 12px; bottom: 12px; top: auto;
    transform: none; width: auto; max-width: none;
  }

  /* Results. The NIS2/CyFun matrix is a table of counts: collapsing it
     would destroy the comparison it exists to make, so the WRAPPER
     scrolls and the page does not (the .cal-tl-scroll precedent). */
  .rz-mxwrap { overflow-x: auto; }

  /* Integrations. Same minmax(0, 1fr) story as the dashboard row: the
     vendor accordions' min-content was sizing a 780px track inside a
     319px column. */
  .conn-grid { grid-template-columns: minmax(0, 1fr); }
  .conn-more__grid { grid-template-columns: minmax(0, 1fr); }
  /* …and once more one level down, for the same reason. The Ansible card
     carries a <pre> of a cron line: the block already scrolls on its own
     (`overflow-x: auto`), but an auto grid track is sized by min-content
     regardless, so the accordion body was 754px wide around a panel that
     was perfectly happy to be 345. */
  .conn-vendor__body, .conn-form { grid-template-columns: minmax(0, 1fr); }
  /* A <select> is as wide as its widest option unless it is told not to
     be, and "App Service Managed Identity (recommended)" is 326px of it. */
  .conn-form input, .conn-form select, .conn-form textarea { max-width: 100%; }

  /* Increment 4's portfolio rules sit HERE - inside 17.2.2's fit tier and
     ABOVE the 17.2.3 read slice - rather than at the end of the block. The
     read slice promises a phone reader is never shown less than a desktop
     one (tests/test_mobile_polish pins it as a grep for display:none), and
     the stacked table legitimately drops the table HEAD, whose labels move
     onto the cells themselves. It is a FIT rule, so it lives with the fit
     rules. */
  /* Increment 4 (portfolio spec) - the portfolio table. Six columns of
     tenant, identity, status, licence, users and three buttons cannot be
     made narrow, so at 375px the table stops being a table: each row is a
     card and every cell prints its own header from data-l. Exactly the
     .evproof__t idiom two blocks up, and for exactly its reason - nothing is
     hidden, a phone reader gets the same six answers about the same tenant.
     The filter bar stacks so its two selects are full-width targets rather
     than 90px slivers. */
  .pf-filters { flex-direction: column; align-items: stretch; }
  /* The flex BASIS has to go with the direction. `flex: 1 1 240px` sizes the
     main axis, and once the bar is a column that axis is the HEIGHT - the
     search box grew into a 240px-tall well. */
  .pf-filters__q { flex: 0 0 auto; }
  .pf-filters__q, .pf-filters select { max-width: none; width: 100%; }
  .pf-filters .btn { align-self: flex-start; }
  .pf__t, .pf__t tbody, .pf__t tr, .pf__t td { display: block; width: 100%; }
  .pf__t thead { display: none; }
  .pf__row { border: 1px solid var(--medium-gray); border-radius: 10px; margin-bottom: 10px; }
  .pf__t td { border-bottom: 0; padding: 6px 12px; }
  .pf__t td[data-l]::before {
    content: attr(data-l) " "; display: block;
    font-size: 10px; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-muted); font-weight: 700;
  }
  .pf__t td:empty { display: none; }
  .pf-actrow { justify-content: flex-start; }
  .pf-actrow .btn, .pf-actrow > form { flex: 1 1 auto; }
  .pf-actrow .btn { width: 100%; text-align: center; }
  /* The sub-rows are their own cards, tucked under the row they belong to:
     a colspan means nothing once the table is a stack. */
  .pf__subrow { border: 1px solid var(--medium-gray); border-radius: 10px; margin: -6px 0 10px; }
  .pf-sub { flex-direction: column; align-items: stretch; }
  .pf-sub .btn, .pf-sub > form { width: 100%; text-align: center; }
  .pf-sub select { width: 100%; }
  .pf-who { flex-wrap: wrap; }

  /* ═════════════════════════════════════════════════════════════════════
     17.2.3 — the read slice (docs/mobile_polish_spec.md)
     ═════════════════════════════════════════════════════════════════════
     17.2.2 made every page FIT a phone. Everything below makes the five
     read-mostly surfaces good on one: dashboard, results, the remediation
     register, the calendar and the two pages an outsider reads.

     Written INSIDE 17.2.2's collapse tier rather than in a second block of
     its own, and that is a decision: 640px is the product's phone boundary,
     and a stylesheet with two identical breakpoints is two places to
     remember. Same iron rule as 17.2.2 - nothing here escapes the media
     query, so the desktop cannot move.

     The rule this whole section is written under is "stack, never hide".
     Every fix below takes a row that was laid out side by side at 1280px
     and puts its parts on separate lines; not one of them removes anything
     from the page, because a phone reader is reading the SAME report. */

  /* ── Touch targets ────────────────────────────────────────────────────
     40px is the floor, reached with padding rather than markup. Listed by
     name rather than applied to `a, button` wholesale: a link inside a
     sentence is not a target, and giving every inline anchor a 40px box
     would re-flow the prose this increment is trying to make readable.
     The (?) mark is the one deliberate omission - it is 15px because it
     sits INSIDE labels, and growing it would push apart the very words it
     annotates; it is also the one control whose payload is a bar across
     the whole foot of the screen, so a near miss costs a second tap. */
  .btn, .rz-sev, .rz-fchip, .rz-gopt, .rr-st, .cal-fchip, .rz-vtoggle button,
  .sr-toggle__opt {
    min-height: 40px;
  }
  .rz-search, .rz-fsel, .rr-search, .rr-fsel, .rr-in, .rr-btn,
  .cal-fld input, .cal-fld select, .sr-gate__input {
    min-height: 40px;
  }
  /* Links that are the row's whole job. inline-flex so the padding is a
     hit area rather than a gap in a paragraph. */
  .hbar__link, .prog-functions__head > a, .cal-add-toggle,
  .sr-gate__link, .cal-del-btn, .rz-flinks a, .rr-links a {
    display: inline-flex; align-items: center; min-height: 40px;
  }

  /* ── A table that cannot collapse scrolls in its own wrapper ──────────
     17.2.2 shipped this for the Results matrix; three more tables need it,
     and one of them (the shared report's statement of coverage) was
     forcing a 466px viewport on a page an auditor opens from their mail.

     The fade is Lea Verou's scroll-shadow: two cover gradients painted in
     the container's own background scroll WITH the content (`local`) and
     two shadows painted over the edges do not (`scroll`), so each hint
     appears only while there is something in that direction. A fade that
     is always on says "there is more" even at the end of the table, which
     is the one moment it is wrong. */
  .rz-mxwrap, .rz-tabwrap, .sr-mxwrap, .cal-tl-scroll {
    overflow-x: auto;
    background:
      linear-gradient(to right, var(--white), rgba(255, 255, 255, 0))
        left center / 24px 100% no-repeat local,
      linear-gradient(to left, var(--white), rgba(255, 255, 255, 0))
        right center / 24px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%, rgba(0, 31, 91, .17), rgba(0, 31, 91, 0))
        left center / 11px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%, rgba(0, 31, 91, .17), rgba(0, 31, 91, 0))
        right center / 11px 100% no-repeat scroll,
      var(--white);
  }
  /* …and the two tables that paint their OWN white hand it back, because a
     background belongs to the deepest box that draws one: a child's paint
     covers its parent's, so a table with `background: var(--white)` hides
     the hint its wrapper is drawing. The white above is the same white. */
  .rz-matrix, .rz-alltab { background: transparent; }

  /* ── 1. Dashboard ────────────────────────────────────────────────────
     The KPI strip is already one column here (the 560px rule) and 17.2.2
     unpinned the measures grid; what is left is the two rows that still
     read as desktop rows. */

  /* A card header is an <h2> and a link with `justify-content: space-
     between` and no wrap, so "Maturity levels" and "CyberFundamentals 1-5
     scale" were two columns of two words each. */
  .prog-functions__head { flex-wrap: wrap; gap: 2px 10px; }

  /* The function bars' label line is a link (17.2.2 gave it the whole
     first line); the tick and the chips that follow it wrap under. */
  .hbar__link { gap: 7px; }

  /* 17.44.0 (feedback batch ④): the key column, on a phone. Its 62px track
     plus the label's 150px minimum plus the score's 50px left the maturity
     BAR exactly 0px wide at 375px - measured, and the bar is the row. So the
     chip drops to a line of its own, right-aligned under the row it belongs
     to, and the three columns that were there before go back to what they
     were. Desktop is untouched. */
  .prog-fnbars .mrow--key {
    grid-template-columns: minmax(110px, 1fr) minmax(0, 1fr) 46px;
    row-gap: 3px;
  }
  .prog-fnbars .mkey { grid-column: 1 / -1; justify-self: end; }

  /* 17.44.0 (feedback batch ③): the connections panel's two-line phone
     treatment, and the note about the "what to do next" queue needing no
     rule at all, went with their tiles. Both live on other pages now and
     are covered by those pages' own rules. */

  /* ── 2. Results (triage) ─────────────────────────────────────────────
     One toolbar row of nine controls becomes a column of full-width ones,
     with the severity chips - which are the page's summary as much as its
     filter - kept together as a single row that scrolls. */
  /* Two things travel with the direction change, and both are the same
     mistake if they are left behind. `flex-wrap: wrap` on a COLUMN wraps
     into a second column - a nine-control toolbar laid out as a grid
     nobody asked for. And a `flex` shorthand written for a row (the search
     field's `1 1 200px`) is a MAIN-AXIS basis: rotate the axis and the
     search box becomes 200px TALL and then grows. Every child is sized by
     its content down this axis. */
  .rz-toolbar, .rr-filters {
    flex-direction: column; flex-wrap: nowrap; align-items: stretch;
  }
  .rz-toolbar > *, .rr-filters > * { flex: 0 0 auto; }
  .rz-sevs {
    display: flex; gap: 6px; flex-wrap: nowrap;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    padding-bottom: 2px;
  }
  .rz-sevs .rz-sev { flex: 0 0 auto; }
  /* …and the source select's desktop cap comes off with them: 190px was
     there to stop one <option> from owning half a toolbar ROW. */
  .rz-search, .rz-fsel { width: 100%; max-width: none; }
  /* `margin-left: auto` is how a flex ROW pushes a summary to its far end;
     in a column it indents the whole line instead. */
  .rz-sumline, .rr-sumline { margin-left: 0; }
  .rz-vtoggle { align-self: stretch; }
  .rz-vtoggle button { flex: 1 1 0; justify-content: center; }

  /* A finding row: the title takes the full width and the status pill
     wraps under it, instead of a 108px pill squeezing the sentence into a
     three-word column. The gap readout already spans 2 / -1, so it keeps
     its place under both. */
  .rz-frow > summary, .rr-item > summary { grid-template-columns: 3px minmax(0, 1fr); }
  .rz-fright, .rr-right {
    grid-column: 2 / -1; flex-wrap: wrap; white-space: normal;
    align-items: center; row-gap: 4px;
  }
  /* …and the two-line clamp comes off with it. At this width the clamp
     was eating the second half of most titles, and a hidden half-sentence
     is the one thing this increment does not do. */
  .rz-t, .rr-t { display: block; overflow: visible; }
  /* Current / Expected side by side is two 150px columns of wrapped
     fragments; one under the other is two readable lines. */
  .rz-compare { grid-template-columns: minmax(0, 1fr); }

  /* ── 3. Remediation register ─────────────────────────────────────────
     The row fix above is shared with Results (same three-column summary,
     same failure). What is left is the filters and the group headers.

     The register's rows carry an owner's e-mail AND a due date in one
     nowrap group, which is ~270px of a 375px screen: the title column was
     measuring ZERO pixels wide.
     (.rr-filters stacks with .rz-toolbar above - same shape, same fix.) */
  .rr-search, .rr-fsel { width: 100%; }
  .rr-form--wide { flex: 1 1 100%; }
  .rr-form { flex-wrap: wrap; }
  .rr-in { flex: 1 1 100%; }
  .rr-lbl input, .rr-lbl textarea { width: 100%; }

  /* Group headers stay put while their queue scrolls under them, so a
     reader thirty rows into Protect can still see which group they are
     in. 56px is the mobile top bar 17.2.2 added, which is itself sticky.
     `overflow: hidden` on the group would make the summary's scrollport
     the group box rather than the page, which is a sticky that never
     sticks - the radius it was clipping is on the summary now instead. */
  .rz-grp { overflow: visible; }
  .rz-grp > summary {
    position: sticky; top: 56px; z-index: 3;
    background: var(--white); border-radius: 10px 10px 0 0;
  }
  .rz-quiet > summary { background: var(--light-gray); }

  /* ── 4. Calendar ─────────────────────────────────────────────────────
     The agenda IS the month list, and it is already the default render -
     nothing here changes which view the server chooses. The timeline keeps
     its own scroller (it now shares the fade above); the agenda rows stop
     reserving a right-hand column for the date. */
  .cal-row { flex-wrap: wrap; align-items: flex-start; row-gap: 4px; }
  .cal-title { flex: 1 1 170px; }
  .cal-src { display: block; }
  .cal-when { margin-left: 0; }
  /* The row's one link, and the row is the only thing on the line - so it
     gets the padding. inline-block rather than inline-flex because the
     title still has to wrap INSIDE the anchor, and the dotted rule moves
     from a border (which the padding would push 11px below the words) to a
     text decoration, which stays where the reader expects it. */
  .cal-title a {
    display: inline-block; max-width: 100%; padding: 11px 0;
    border-bottom: none;
    text-decoration: underline dotted var(--medium-gray);
    text-underline-offset: 3px;
  }
  .cal-title a:hover { text-decoration-color: var(--teal); }
  /* A 13px checkbox is not a target on any phone. */
  .cal-check { min-height: 40px; }
  .cal-check input[type="checkbox"] { width: 22px; height: 22px; }
  /* The add-event form: four fields on one line is four unusable fields. */
  .cal-add-form { flex-direction: column; align-items: stretch; }
  .cal-fld, .cal-fld--wide { flex: 1 1 auto; }
  .cal-fld input, .cal-fld select { width: 100%; }

  /* ── 5. Shared report + gate — the pages an outsider reads ───────────
     Never audited at 375px before this increment, and the worst offender
     in the product: the statement-of-coverage table's seven columns forced
     the whole page to 466px, so an auditor opening the link on a phone
     scrolled sideways through somebody else's compliance report. The
     wrapper above fixes the width; everything here is the rest of it. */
  .sr-page { margin: 12px auto; padding: 0 10px; }
  .sr-head { flex-wrap: wrap; padding: 14px 16px; gap: 10px 12px; }
  .sr-head__credit { margin-left: 0; text-align: left; flex: 1 1 100%; }
  .sr-banner { padding: 8px 16px; gap: 2px 12px; }
  .sr-banner__end { margin-left: 0; }
  .sr-body-panel { padding: 16px 14px; }
  .sr-sec__head { align-items: flex-start; }
  .sr-toggle, .sr-group__n { margin-left: 0; }
  .sr-group > summary { min-height: 40px; }
  /* 108px + 210px of fixed columns out of 347px left 29px for the finding
     title, which rendered one word per line. The severity pill and the
     status line size themselves at this width. */
  .sr-table__sev, .sr-table__meta { width: auto; }
  .sr-table th, .sr-table td { padding: 8px 10px; }
  .sr-table--matrix { margin-top: 0; }
  .sr-mxwrap { margin-top: 10px; }

  /* The gate. One card on an empty screen, so the only thing between it
     and the phone's edge is its own margin. */
  .sr-gate { margin: 6vh 10px; padding: 24px 18px; }
  .sr-gate__input { padding: 10px 12px; }
  /* letter-spacing puts a trailing gap after the sixth digit, which reads
     as an off-centre field on a box this narrow. */
  .sr-gate__input--code { letter-spacing: 6px; padding-left: 18px; }
  .sr-gate__link { padding: 0 6px; }

  /* ── The help bar, finished (17.2.2 carry-over) ──────────────────────
     17.2.2 turned `[data-help]::after` into a bar across the foot of the
     screen because a 260px absolutely-positioned bubble was widening every
     page it appeared on. That was a fix, not a design: the bar arrived with
     a tethered bubble's padding, no way to tell it apart from the page
     under it, and nothing saying how to make it go away.

     ::before is the backdrop AND the label: one box, dimming the page and
     placing a small-caps caption in the strip BELOW the bar (both are
     anchored to the bottom of the screen, so the caption lands under a bar
     of any height). It sits under the bar in the stack, which is what lets
     the same box dim the page without dimming the bar.

     The caption reads from `data-help-label`, which _help.html renders
     through the catalogue - CSS `content` cannot reach the translations,
     so a word written here would be the one thing on a Romanian phone that
     speaks English. A carrier without the attribute simply gets a
     backdrop and no caption. */
  [data-help]::before {
    content: attr(data-help-label);
    position: fixed; inset: 0; z-index: 58;
    display: flex; align-items: flex-end;
    padding: 0 0 calc(11px + env(safe-area-inset-bottom, 0px)) 26px;
    background: rgba(0, 12, 40, .42);
    font-size: 10px; font-weight: 700; letter-spacing: .14em;
    font-variant-caps: all-small-caps; text-transform: lowercase;
    text-align: left; color: rgba(255, 255, 255, .72);
    pointer-events: none; opacity: 0; visibility: hidden;
    transition: opacity .1s ease;
  }
  [data-help]:hover::before,
  [data-help]:focus::before,
  [data-help]:focus-within::before { opacity: 1; visibility: visible; }

  [data-help]::after,
  [data-help].qhelp--end::after {
    left: 10px; right: 10px; top: auto;
    /* Above the caption strip, and clear of the home indicator under it. */
    bottom: calc(32px + env(safe-area-inset-bottom, 0px));
    z-index: 59;
    padding: 13px 44px 13px 16px;
    font-size: 13px; line-height: 1.5; border-radius: 12px;
    box-shadow: 0 -8px 34px rgba(0, 12, 40, .34);
    /* The close affordance. A glyph rather than a word (nothing in CSS can
       be translated) and decoration rather than a control: the bar closes
       on the next tap anywhere, because it is open only while its mark has
       hover or focus. The 44px of right padding above is its room. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-opacity='.62' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6L6 18'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 15px top 14px;
    background-size: 15px 15px;
  }

  /* ── 17.2.4 — document registry rows (Alex's phone screenshot) ───────
     At phone width the title column shrank toward zero and its wrapped
     words painted under the chips. Same recipe as the register rows: a
     stacked card - identity + chips first, then the title, then state +
     action on one line, button pushed right. The code pill is too wide to
     share a line with the title.

     17.50.0: the desktop row became a shared GRID, so this tier collapses
     the tracks rather than re-wrapping a flex line. `order` still does the
     stacking (grid honours it), and the spanning members are what force a
     new row - which is why the four numbers below moved by one each: the
     Primary/Supporting cell and the language cell are placed here too,
     where the flex version simply let them fall where they landed. */
  .reg-row, .reg-row--nis2 {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: 7px; column-gap: 10px;
  }
  .reg-maps { order: 1; justify-self: end; }
  .reg-prim, .reg-supp { order: 2; grid-column: 1 / -1; }
  .reg-title { order: 3; grid-column: 1 / -1; }
  /* Three lines rather than two: the column is the full width of a phone
     here, so the same sentence needs fewer characters cut, not more. */
  .reg-title small { max-width: none; -webkit-line-clamp: 3; line-clamp: 3; }
  .reg-status { order: 4; }
  .reg-row > .btn { order: 5; justify-self: end; min-height: 40px;
                    display: inline-flex; align-items: center;
                    justify-content: center; }
  .reg-lang { order: 6; grid-column: 1 / -1; }
  .reg-status small { display: inline; margin-left: 4px; }

  /* ── 17.2.5 — AI-analysis markdown tables (Alex's phone screenshots) ──
     The report body's tables are born from markdown at render time, so
     no template wrapper exists to scroll them. Under the tier the TABLE
     is its own scroll container: columns keep their natural widths and
     the finger scrolls the table, never the page. Desktop unaffected. */
  .ai-md__body table, .ai-md__body .md-table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* ── 17.4.0 — the evidence library's collapsed row ───────────────────
     The summary carries the filename, the validity pill, the control
     count and now the client-document chips. At 375px that is more than
     one line, and the first thing a nowrap flex row gives up is the
     FILENAME - the one fact the collapsed row exists to show (Alex's
     phone: a row with no name and a chip rendered as a circle). Same
     recipe as the 17.2.3 register row: the name keeps line one, the
     chips take line two. Desktop is untouched - the row fits there. */
  .doc-card__summary { flex-wrap: wrap; row-gap: 6px; }
  .doc-card__name { flex: 1 1 auto; }

  /* ── 17.4.0 rider — the EXPANDED evidence card (chip task, latent since
     16.57.0): .doc-card__mappings is a grid with no explicit column, so
     its auto track grows to the max-content width of the longest
     unwrapped mapping text (676-776px measured) - the same
     minmax(0,1fr) disease 17.2.2 cured in five other grids. The 17.2.2/3
     audits measured collapsed views only, which is how it hid. */
  .doc-card__mappings { grid-template-columns: minmax(0, 1fr); }
  .mapping { min-width: 0; }
  .mapping__head { flex-wrap: wrap; row-gap: 4px; }
  .mapping__sub {
    white-space: normal; overflow: visible; text-overflow: clip;
    flex: 1 1 100%;
  }
  .mapping__why { overflow-wrap: anywhere; }
  .add-mapping { grid-template-columns: minmax(0, 1fr); }
  .add-mapping__label { white-space: normal; }

  /* 17.4.4. The decision overlay is a MODAL, so it is the one thing on a
     375px screen that cannot be scrolled sideways out of trouble: the option
     card stacks to a single column, its icon shrinks and its chevron moves
     to the end of the stack. Nothing is hidden - 17.2.3's ground rule holds
     here too, and a phone reader gets the same three doors with the same
     words on each of them. */
  .dov-wrap { padding: 12px 8px; }
  .dov { padding: 16px 14px 14px; }
  .dov__opt { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .dov__ic { width: 28px; height: 28px; font-size: 14px; border-radius: 7px; }
  .dov__go { text-align: right; }
  .dov__foot { flex-direction: column; align-items: flex-start; }

  /* 17.4.5. Two new blocks of markup on the library, measured at 375px with
     the version groups both open and closed. The version row is the risky
     one: it carries a date, an uploader name, a sha and two buttons on one
     line, and its chips carry SLOT TITLES - arbitrary text that .chip--slot
     refuses to wrap on the desktop row it was drawn for. Under the tier the
     action cluster keeps its own line rather than pushing the row wide, and
     the confirmation's buttons stack full width so the destructive one is
     never a 30px sliver against the screen edge. */
  .doc-ver__acts { margin-left: 0; flex: 1 1 100%; }
  .doc-ver__acts .btn { flex: 1 1 auto; text-align: center; }
  .doc-ver__head { row-gap: 6px; }
  .doc-rm__acts > form, .doc-rm__acts > .btn { flex: 1 1 100%; }
  .doc-rm__acts .btn { width: 100%; text-align: center; }
  .doc-card__vers { flex: 0 0 auto; }

  /* 17.4.6. The applied-edits rows carry an affordance now, inside a rail
     that is the full 375px on a phone. The key column stops reserving 96px
     it cannot spare, the action takes its own line rather than squeezing the
     edit's text into a column of single words, and the revert confirmation
     stacks full width like every other destructive answer in the product.
     Nothing is hidden: the same rows, the same question, the same words. */
  .wsx-ov .k { flex: 1 1 100%; min-width: 0; max-width: 100%; }
  .wsx-ov__act { flex: 1 1 100%; margin-left: 0; }
  .wsx-ov__act .btn { width: 100%; text-align: center; }
  .wsx-ov__act > form { width: 100%; }
  .wsx-ovc__acts > form, .wsx-ovc__acts > .btn { flex: 1 1 100%; }
  .wsx-ovc__acts .btn { width: 100%; text-align: center; }

  /* 17.48.0 /quickstart. In THIS block rather than a phone tier of its own:
     640px is the product's one phone boundary (17.2.3's rule), and
     tests/support/css reads the LAST media block with that prelude, so a new
     one at the foot of the file would quietly become "the collapse tier" for
     every mobile assertion in the suite.
     The guide's action column stops being a column; the numbered mark keeps
     its 34px and the text indents past it. */
  .qs-head, .qs-step, .qs-outro { padding-left: 16px; padding-right: 16px; }
  .qs-step { flex-wrap: wrap; }
  .qs-act { align-items: flex-start; flex-direction: row; flex-wrap: wrap;
            width: 100%; padding-left: 50px; }
  .qs-manual { text-align: left; }

  /* 17.50.0 - the tenant-reset danger zone (docs/tenant_reset_spec.md).
     INSIDE this tier, per the warning above: a second block with this same
     prelude at the foot of the file would become "the collapse tier" for
     every mobile assertion in the suite, and two tests count the preludes to
     make sure nobody adds one. The card's body is a row on desktop; here the
     button goes under the paragraph and the two-column ledger stacks. */
  .danger__body { flex-direction: column; }
  .danger__body p { max-width: none; }
  /* The CARD's button only. `.btn-danger` alone would also stretch the
     modal's arm button, and its row is `justify-content: flex-end` - the
     Cancel beside it went off the left edge of the dialog. */
  .danger__body .btn-danger { margin-left: 0; width: 100%; }
  .reset-cols { flex-direction: column; }
  .reset-overlay { padding: 18px 12px; }
  .reset-modal { padding: 18px 16px; }
  .reset-modal__row { flex-direction: column-reverse; }
  .reset-modal__row .btn-ghost,
  .reset-modal__row .btn-danger { width: 100%; margin-left: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   17.2.6 — desktop sidebar collapse (Alex: "an arrow to hide the menu
   for more screen estate"). Two pieces of chrome that exist ONLY at
   desktop widths: an arrow in the sidebar's top-right corner that hides
   the rail, and a fixed tab at the left edge that brings it back. The
   preference persists per browser. Every layout rule sits inside a
   min-width block, so the 17.2.2 top-level freeze and the whole mobile
   tier never see any of it; the two top-level lines below only remove
   chrome mobile must never show (the .mtop precedent).
   ═══════════════════════════════════════════════════════════════════════ */
.sb-collapse { display: none; }
.sb-expand { display: none; }

@media (min-width: 769px) {
  .sb-collapse {
    display: grid; place-items: center;
    position: absolute; top: 12px; right: 8px;
    width: 26px; height: 26px; border-radius: 6px;
    background: transparent; border: 0; cursor: pointer;
    color: rgba(255, 255, 255, .5);
  }
  .sb-collapse:hover, .sb-collapse:focus-visible {
    color: #fff; background: rgba(255, 255, 255, .14);
  }
  .sb-collapse svg { width: 15px; height: 15px; }

  body.sidebar-hidden .sidebar { display: none; }
  body.sidebar-hidden .shell { display: block; }

  body.sidebar-hidden .sb-expand {
    display: grid; place-items: center;
    position: fixed; left: 0; top: 14px; z-index: 60;
    width: 24px; height: 42px;
    border: 0; border-radius: 0 9px 9px 0; cursor: pointer;
    background: var(--navy, #001F5B); color: #fff;
    box-shadow: 0 2px 8px rgba(0, 31, 91, .28);
  }
  .sb-expand svg { width: 14px; height: 14px; }
}

/* ── 17.12.0 — the sign-in door's look (mock: signin_door_mock.html).
   A branded navy ground with two quiet radial glows, one floating card.
   Own .door-* family: the sr-gate look stays untouched for the shared
   report gate and the invite page. Single-theme by design - the door
   commits to the brand ground. */
.door-body { margin: 0; min-height: 100vh; font-family: inherit;
  background:
    radial-gradient(120% 90% at 85% -10%, rgba(88, 191, 228, .18), transparent 55%),
    radial-gradient(100% 80% at -10% 110%, rgba(0, 147, 96, .14), transparent 50%),
    linear-gradient(160deg, #001F5B 0%, #0A2E73 55%, #123a85 100%); }
.door { min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; padding: 44px 16px 28px;
  position: relative; }
.door__back { position: absolute; top: 18px; left: 20px; color: #9db2dd;
  font-size: 13px; text-decoration: none; }
.door__back:hover { color: #fff; text-decoration: none; }
.door__brand { text-align: center; margin-bottom: 26px; }
.door__mark { width: 46px; height: 46px; margin: 0 auto 12px; border-radius: 12px;
  background: linear-gradient(135deg, var(--green), #00b273);
  display: grid; place-items: center; box-shadow: 0 8px 24px rgba(0, 147, 96, .35); }
.door__word { color: #fff; font-weight: 800; font-size: 20px; letter-spacing: -.01em; }
.door__word span { color: #58BFE4; }
.door__tag { color: #9db2dd; font-size: 11px; margin-top: 3px; letter-spacing: .06em; }
.door__card { width: 100%; max-width: 360px; background: #fff; border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 15, 45, .35); padding: 26px 24px 22px; }
.door__h { font-size: 17px; color: var(--navy); margin: 0 0 2px; }
.door__sub { color: var(--text-muted); font-size: 12.5px; margin: 0 0 16px; }
.door__sub b { color: var(--text-primary); }
.door__email { font-size: 13px; color: var(--text-primary); background: var(--off-white);
  border-radius: 9px; padding: 9px 12px; margin: 0 0 14px; overflow-wrap: anywhere; }
.door__label { display: block; font-size: 11px; font-weight: 700; color: var(--navy);
  margin-bottom: 5px; }
.door__input { width: 100%; box-sizing: border-box; font: inherit; font-size: 14.5px;
  padding: 11px 12px; border: 1.5px solid var(--light-gray); border-radius: 9px; }
.door__input:focus { outline: none; border-color: var(--green);
  box-shadow: 0 0 0 3px rgba(0, 147, 96, .12); }
.door__input--code { text-align: center; font-size: 24px; letter-spacing: .55em;
  padding-left: calc(12px + .55em); font-variant-numeric: tabular-nums; }
.door__go { display: block; width: 100%; box-sizing: border-box; margin-top: 12px;
  font: inherit; font-size: 14px; font-weight: 700; text-align: center;
  padding: 11px 0; border: 0; border-radius: 9px; background: var(--green);
  color: #fff; cursor: pointer; text-decoration: none; }
.door__go:hover { background: var(--teal-dark); color: #fff; text-decoration: none; }
.door__hint { color: var(--text-muted); font-size: 11.5px; margin: 12px 0 0;
  line-height: 1.5; }
.door__again { margin: 12px 0 0; text-align: center; }
.door__link { border: 0; background: none; font: inherit; font-size: 12.5px;
  font-weight: 600; color: var(--green); cursor: pointer; text-decoration: none; }
.door__link:hover { text-decoration: underline; }
.door__foot { margin-top: 26px; color: #8ca3d8; font-size: 11px; text-align: center; }
.door__foot b { color: #b9c9ef; font-weight: 600; }
/* 17.12.1 — the code screen's address line, and the request-access exit */
.door__for { margin: 14px 0 0; text-align: center; font-size: 12px;
  color: var(--text-muted); overflow-wrap: anywhere; }
.door__request { margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--light-gray); }
.door__request .door__hint { margin: 0 0 8px; text-align: center; }
.door__go--ghost { background: #fff; color: var(--navy);
  border: 1.5px solid var(--light-gray); }
.door__go--ghost:hover { background: var(--off-white); color: var(--navy);
  border-color: var(--navy); }

/* ── Document-language arc, increment 1 — the language surfaces ──────────
   docs/mockups/docs_language_mock.html surfaces 1, 2 and 6, translated out
   of the mock's throwaway CSS into this stylesheet's own variables and the
   .dov* overlay idiom 17.4.4 established. Two dialogs share one block
   because they ask the same question in two moments: .doclang-modal is the
   save warning on the Settings tile, .langpop is the first-login popup on
   the dashboard, and a rule that drifted between them would be the product
   describing one choice two ways. */
.doclang-modal, .langpop {
  position: fixed; inset: 0; z-index: 80; display: grid;
  place-items: start center; overflow-y: auto; padding: 24px 12px;
  background: rgba(10, 20, 45, .45);
}
/* The same author-display trap .dov-wrap documents: the display rule above
   would beat the UA's [hidden] { display: none } and both dialogs would be
   open on every page load. */
.doclang-modal[hidden], .langpop[hidden] { display: none; }
.doclang-modal__box, .langpop__box {
  position: relative; width: 100%; max-width: 620px; margin-top: 6vh;
  background: var(--white); border-radius: 14px;
  box-shadow: 0 18px 60px rgba(0, 31, 91, .35);
  padding: 20px 22px 18px;
}
.doclang-modal__box h3, .langpop__box h3 {
  margin: 0 0 10px; font-size: 17px; color: var(--navy);
}
.doclang-modal__list {
  border: 1px solid var(--light-gray); border-radius: 10px;
  margin: 12px 0; max-height: 220px; overflow-y: auto;
}
.doclang-modal__list label {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 9px 12px; border-top: 1px solid var(--light-gray);
  font-size: 13px; cursor: pointer;
}
.doclang-modal__list label:first-child {
  border-top: 0; background: var(--off-white);
}
.doclang-modal__list input[disabled] + span { color: var(--text-muted); }
.doclang-modal__actions, .langpop__actions {
  display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px;
  flex-wrap: wrap;
}
/* The popup's read-only line for an invited member: the workspace's document
   language is a fact they are told, not a control they are offered. */
.langpop__told {
  background: var(--off-white); border: 1px solid var(--light-gray);
  border-radius: 10px; padding: 10px 13px; font-size: 12.5px;
  color: var(--text-muted); margin: 12px 0;
}
@media (max-width: 560px) {
  .doclang-modal__box, .langpop__box { margin-top: 0; padding: 16px; }
}

/* ── Document-language arc, increment 2 — the override and the read-only
   secondary. docs/mockups/docs_language_mock.html surfaces 3 and 4, in this
   stylesheet's own variables. Two surfaces, one vocabulary: a CHIP always
   says which language a document is controlled in and whether that is the
   workspace default or this document's own override, and every read-only
   translation is drawn on a tinted ground so that "you cannot edit this" is
   visible before anything is clicked. */
.reg-lang { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.reg-lang .lchip { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 1px;
  font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 999px;
  background: var(--off-white); color: var(--navy); white-space: nowrap; }
.reg-lang .lchip.is-ov { background: #e7f6fc; color: #1179a3; }
.reg-lang .lchip i { font-style: normal; font-weight: 500; opacity: .7;
  font-size: 10.5px; }
.reg-langf { display: inline-flex; margin: 0; }
/* The toggle is a real submit button with no label: the state is in the chip
   beside it, and a second copy of the state in the control would be a second
   thing to keep true. */
.lswitch { position: relative; width: 36px; height: 20px; border: 0; padding: 0;
  border-radius: 999px; background: var(--light-gray); cursor: pointer;
  flex: none; transition: background .15s; }
.lswitch::after { content: ""; position: absolute; top: 2.5px; left: 3px;
  width: 15px; height: 15px; border-radius: 50%; background: #fff;
  transition: left .15s; }
.lswitch.is-on { background: var(--green); }
.lswitch.is-on::after { left: 18px; }
.lswitch:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }

/* The editor's switch (mock surface 4). Segmented, because the two sides are
   two views of ONE document and not two documents. */
.wsx-langsw { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 14px 0 10px; }
.wsx-langsw__b { display: inline-flex; align-items: baseline; gap: 6px;
  padding: 7px 14px; border: 1px solid var(--light-gray); border-radius: 9px;
  font-size: 13px; font-weight: 600; color: var(--medium-gray);
  background: #fff; text-decoration: none; }
.wsx-langsw__b + .wsx-langsw__b { margin-left: -9px;
  border-top-left-radius: 0; border-bottom-left-radius: 0; }
.wsx-langsw__b:first-child { border-top-right-radius: 0;
  border-bottom-right-radius: 0; }
.wsx-langsw__b.is-active { background: var(--navy); border-color: var(--navy);
  color: #fff; }
.wsx-langsw__b i { font-style: normal; font-weight: 500; opacity: .75;
  font-size: 11px; }
.wsx-langf { display: inline-flex; margin: 0; }

/* The staleness banner + the read-only ground. */
.wsx-tbanner { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--off-white); border: 1px dashed var(--light-gray);
  border-radius: 10px; padding: 9px 14px; font-size: 12.5px;
  color: var(--medium-gray); margin-bottom: 10px; }
.wsx-tempty { font-size: 12.5px; color: var(--medium-gray); margin: 8px 2px; }
.wsx-page--ro { background: var(--off-white); }
.wsx-tnote { font-size: 11.5px; color: var(--medium-gray); margin: 8px 2px; }
.wsx-tgate { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; background: #fff7e6; border: 1px solid #e6b74a;
  border-radius: 10px; padding: 10px 14px; font-size: 12.5px; color: #7a5a10;
  margin: 10px 0; }

/* ── Document-language arc, increment 3 — the binder's language rule and the
   fillings' info line. docs/mockups/docs_language_mock.html surfaces 4 and 5.
   The chip vocabulary is increment 2's, extended by exactly two states: a
   green one for "this reached the binder" and an amber one for "this did
   not, and here is why". Amber rather than red on purpose — an excluded
   document is a task, not a fault. */
.doclang-note { font-size: 12.5px; color: var(--text-secondary);
  margin: 10px 0 0; }
.doclang-note a { font-weight: 600; }
.binder-lang { margin-top: 14px; overflow-x: auto; }
.binder-langtab td { font-size: 13px; vertical-align: middle; }
.binder-langtab td:last-child { white-space: normal; }
.binder-langtab tr.is-excluded td:first-child { color: var(--medium-gray); }
.binder-langtab .lchip { display: inline-flex; align-items: baseline; gap: 5px;
  font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 999px;
  background: var(--off-white); color: var(--navy); white-space: nowrap; }
.binder-langtab .lchip.is-ov { background: #e7f6fc; color: #1179a3; }
.binder-langtab .lchip.is-ok { background: #e8f6ec; color: #1c6b34; }
.binder-langtab .lchip.is-warn { background: #fff7e6; color: #7a5a10; }
.binder-langtab .lchip i { font-style: normal; font-weight: 500; opacity: .7;
  font-size: 10.5px; }

/* ── 17.41.0: the DNSC incident register (docs/dnsc_incident_spec.md §1c) ─
   Three surfaces, one small family. The register list borrows the calendar's
   band/row metrics deliberately - an incident deadline and a policy review
   are both "a thing owed by a date", and two pages that answer that question
   in two different shapes are two pages nobody learns.

   The one thing this family adds is the CLOCK CHIP: three of them per row,
   in the order the law puts them, coloured by how much time is left. Green
   is not in the palette here on purpose - a filing that has not been made is
   never reassuring, it is merely not yet late. */
.inc-meta { font-size: 12.5px; color: var(--text-secondary); margin-top: 4px; }

.inc-strow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 14px; }
.inc-st { border-radius: 14px; padding: 4px 12px; font-size: 12px;
  font-weight: 600; background: var(--light-gray);
  color: var(--text-secondary); }
.inc-st--live { background: var(--tint-navy-bg); color: var(--tint-navy-ink); }
.inc-st--over { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.inc-sumline { margin-left: auto; font-size: 12px; color: var(--text-secondary); }

.inc-band { background: #fff; border: 1px solid var(--medium-gray);
  border-radius: 10px; margin-bottom: 14px; overflow: hidden; }
.inc-bhead { display: flex; align-items: center; gap: 8px; padding: 10px 16px;
  border-bottom: 1px solid var(--medium-gray); }
.inc-band--closed > .inc-bhead { cursor: pointer; list-style: none; }
.inc-bhead h2 { margin: 0; font-size: 13px; font-weight: 700; color: var(--navy); }

.inc-row { display: flex; align-items: center; gap: 10px; padding: 10px 16px;
  border-bottom: 1px solid var(--light-gray); flex-wrap: wrap; }
.inc-row:last-child { border-bottom: none; }
/* The left stripe is the row's loudest clock, so a list of ten incidents can
   be read for "which of these is late" without reading a single word. */
.inc-row--overdue { box-shadow: inset 3px 0 0 var(--tint-crit-ink); }
.inc-row--soon    { box-shadow: inset 3px 0 0 var(--tint-high-ink); }
.inc-title { font-size: 13.5px; min-width: 0; display: flex;
  flex-direction: column; gap: 2px; }
.inc-title a { color: var(--navy); border-bottom: 1px dotted var(--medium-gray); }
.inc-title a:hover { color: var(--teal-dark); border-bottom-color: var(--teal); }
.inc-src { font-size: 11px; color: var(--text-muted); }
.inc-clocks { margin-left: auto; display: flex; gap: 6px; flex-wrap: wrap; }
.inc-noclock { font-size: 11.5px; color: var(--text-muted); font-style: italic; }

.inc-clock { border-radius: 6px; padding: 2.5px 9px; font-size: 11.5px;
  white-space: nowrap; background: var(--tint-mute-bg);
  color: var(--tint-mute-ink); }
.inc-clock b { font-weight: 700; }
.inc-clock--run   { background: var(--tint-navy-bg); color: var(--tint-navy-ink); }
.inc-clock--soon  { background: var(--tint-high-bg); color: var(--tint-high-ink); }
.inc-clock--over  { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.inc-clock--filed { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }

.inc-pill--open   { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }
.inc-pill--cont   { background: var(--tint-high-bg); color: var(--tint-high-ink); }
.inc-pill--res    { background: var(--tint-sky-bg);  color: var(--tint-sky-ink); }
.inc-pill--closed { background: var(--tint-mute-bg); color: var(--tint-mute-ink); }

/* -- the create form + the detail cards -------------------------------- */
.inc-card { margin-bottom: 14px; }
.inc-chead { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.inc-chead h2 { margin: 0; font-size: 13px; font-weight: 700; color: var(--navy); }

.inc-form { display: flex; flex-direction: column; gap: 14px; }
.inc-times { display: flex; gap: 14px; flex-wrap: wrap; }
.inc-fld { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; }
.inc-fld--wide { width: 100%; }
.inc-fld > span { font-weight: 600; color: var(--navy); }
.inc-fld > small { color: var(--text-muted); font-size: 11px; }
.inc-fld input, .inc-fld textarea, .inc-fld select {
  font-family: inherit; font-size: 13px; padding: 6px 9px;
  border: 1px solid var(--medium-gray); border-radius: 6px; background: #fff;
}
.inc-fld textarea { resize: vertical; }

.inc-crit { border: 1px solid var(--medium-gray); border-radius: 8px;
  padding: 12px 14px; }
.inc-crit legend { font-size: 12.5px; font-weight: 700; color: var(--navy);
  padding: 0 6px; }
.inc-crithint { margin: 0 0 8px; font-size: 11.5px; color: var(--text-muted); }
.inc-check { display: flex; align-items: flex-start; gap: 8px;
  font-size: 13px; padding: 3px 0; }
.inc-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.inc-filings { width: 100%; border-collapse: collapse; }
.inc-filings th, .inc-filings td { text-align: left; font-size: 13px;
  padding: 8px 10px; border-bottom: 1px solid var(--light-gray);
  vertical-align: top; }
.inc-filings tr:last-child th, .inc-filings tr:last-child td { border-bottom: none; }
.inc-filings th { font-weight: 600; color: var(--navy); }
.inc-filings th small { display: block; font-weight: 400; font-size: 11px;
  color: var(--text-muted); }
.inc-due { white-space: nowrap; color: var(--text-secondary); }
.inc-filing--over .inc-state { color: var(--tint-crit-ink); }
.inc-filing--soon .inc-state { color: var(--tint-high-ink); }
.inc-filing--filed .inc-state { color: var(--tint-ok-ink); }
.inc-portal { margin: 10px 0 0; }

.inc-summary { font-size: 13.5px; white-space: pre-wrap; margin: 0 0 10px; }
.inc-svcs, .inc-crits { margin-top: 10px; }
.inc-lbl { display: block; font-size: 11.5px; font-weight: 700;
  color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em;
  margin-bottom: 5px; }
.inc-crits ul { margin: 0; padding-left: 18px; font-size: 13px; }

.inc-statusform, .inc-entryform { display: flex; align-items: flex-end;
  gap: 10px; flex-wrap: wrap; }
.inc-entryform { align-items: center; margin-bottom: 12px; }
.inc-entryform input[type="text"] { flex: 1 1 320px; font-family: inherit;
  font-size: 13px; padding: 6px 9px; border: 1px solid var(--medium-gray);
  border-radius: 6px; }

.inc-tl { list-style: none; margin: 0; padding: 0; }
.inc-tl li { display: flex; gap: 10px; font-size: 13px; padding: 6px 0;
  border-bottom: 1px solid var(--light-gray); flex-wrap: wrap; }
.inc-tl li:last-child { border-bottom: none; }
.inc-tl-when { color: var(--text-muted); font-size: 11.5px; white-space: nowrap;
  min-width: 108px; }
.inc-tl-entry { flex: 1 1 240px; }
.inc-tl-who { color: var(--text-muted); font-size: 11.5px; }

/* ── 17.46.0 (docs/tos_acceptance_spec.md) — the Terms surfaces ───────────
   Three pages, one block: the acceptance interstitial, the hold screen and
   the terms page share a body of legal prose and must not describe it three
   ways. The hold screen reuses .sr-gate unchanged (it is the same shape as
   the licence notice - a short sentence and one way out); only the two
   surfaces that carry the TEXT need width, and that is what .tos-page is. */
.tos-page {
  max-width: 780px; margin: 6vh auto 48px; padding: 28px 30px 24px;
  background: var(--white); border-radius: 14px;
  box-shadow: 0 18px 60px rgba(0, 31, 91, .16);
}
.tos-page h1 { font-size: 21px; color: var(--navy); margin: 6px 0 6px; }
.tos-page__meta { font-size: 12.5px; color: var(--text-muted); margin: 0 0 14px; }
.tos-page__meta a { color: var(--text-secondary); }

/* The interstitial's reading box. A fixed viewport with its own scrollbar,
   not the page's: the checkbox and the button have to stay visible while the
   terms are being read, or "I have read" is a claim the layout worked
   against. Focusable (tabindex on the element) so a keyboard reader can
   scroll it without a mouse. */
.tos-scroll {
  max-height: 46vh; overflow-y: auto; padding: 16px 18px; margin: 0 0 16px;
  border: 1px solid var(--light-gray); border-radius: 10px;
  background: var(--off-white);
}
.tos-scroll:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }

.tos-body { font-size: 13.5px; line-height: 1.62; color: var(--text-secondary); }
.tos-body h2 { font-size: 14px; color: var(--navy); margin: 18px 0 6px; }
.tos-body h2:first-of-type { margin-top: 4px; }
.tos-body p { margin: 0 0 9px; }
.tos-body ul { margin: 0 0 9px; padding-left: 20px; }
/* The placeholder marker. Deliberately loud: while the lawyer-final text is
   outstanding, every reader of this page must be able to see at a glance
   which paragraphs are structure and which are agreed wording. */
.tos-body .tos-placeholder {
  color: var(--tint-high-ink); background: var(--tint-high-bg);
  border-radius: 6px; padding: 6px 9px; font-size: 12.5px;
}
.tos-body .tos-meta { color: var(--text-muted); font-size: 12px; }

.tos-page__form { margin: 0; }
.tos-page__check {
  display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px;
  color: var(--navy); cursor: pointer;
}
.tos-page__check input { margin-top: 3px; }
.tos-page__foot {
  display: flex; gap: 4px; flex-wrap: wrap; justify-content: center;
  margin: 16px 0 0;
}

@media (max-width: 560px) {
  .tos-page { margin: 0; border-radius: 0; padding: 20px 16px; }
  .tos-scroll { max-height: 40vh; padding: 12px 13px; }
}

/* ── 17.48.0: /quickstart — the "Primii pasi" guide ────────────────────────
   Contract: docs/quickstart_manual_spec.md; approved shape:
   docs/mockups/quickstart_mock.html. Namespaced qs- for the reason the
   manual's block gives for man-: this page is one authored artefact and a
   generic class name here would be restyled by whatever the rest of the app
   decides .step or .mark means next release.

   The mock's own tokens map onto the product's: --navy, --teal/--teal-dark
   (the .btn--green pair), --brand-sky for the progress fill, and the ok tint
   pair for a completed step's mark. Nothing new is introduced. */
.qs-page { max-width: 920px; }
.qs-wrap { padding: 0; overflow: hidden; }

.qs-head { background: var(--navy); color: var(--white); padding: 26px 30px 22px; }
.qs-head h1 {
  margin: 0 0 6px; font-size: 22px; font-weight: 700; color: var(--white);
  font-family: var(--font-display);
}
.qs-head p { margin: 0; font-size: 14px; color: #C9D4EA; max-width: 640px; }
.qs-progress { display: flex; align-items: center; gap: 12px; margin-top: 16px; flex-wrap: wrap; }
.qs-bar {
  flex: 0 0 260px; max-width: 100%; height: 8px; border-radius: 99px;
  background: rgba(255, 255, 255, .18); overflow: hidden;
}
.qs-bar i { display: block; height: 100%; background: var(--brand-sky); border-radius: 99px; }
.qs-progress span { font-size: 13px; color: #C9D4EA; font-weight: 600; }

.qs-list { padding: 8px 0; }
.qs-step {
  display: flex; gap: 16px; padding: 18px 30px; align-items: flex-start;
  border-top: 1px solid var(--light-gray);
}
.qs-step:first-child { border-top: 0; }
.qs-mark {
  flex: 0 0 34px; height: 34px; border-radius: 50%; margin-top: 2px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 15px; font-variant-numeric: tabular-nums;
}
.qs-step.done .qs-mark { background: var(--tint-ok-bg); color: var(--tint-ok-ink); }
.qs-step.todo .qs-mark {
  background: var(--light-gray); color: var(--navy);
  border: 1.5px solid var(--medium-gray);
}
/* The tick is icon('check') - stroke geometry, never a glyph or an emoji. */
.qs-step.done .qs-mark .ic { width: 17px; height: 17px; stroke-width: 2.4; }

.qs-body { flex: 1; min-width: 0; }
.qs-body h3 { margin: 0 0 3px; font-size: 15.5px; color: var(--navy); font-weight: 700; }
/* A finished step steps back rather than striking itself out: it is still
   the thing to re-read when somebody asks what it was for. */
.qs-step.done .qs-body h3 { color: var(--text-secondary); }
.qs-body p { margin: 0; font-size: 13.5px; color: var(--text-secondary); max-width: 60ch; line-height: 1.5; }

/* The non-M365 branch link. A form, because it WRITES a preference - and a
   button that has to read like the quiet link the mock drew. */
.qs-branch { margin: 7px 0 0; }
.qs-branch__btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 600;
  color: var(--teal); text-decoration: none;
}
.qs-branch__btn:hover { color: var(--teal-dark); text-decoration: underline; }
.qs-branch__btn:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; border-radius: 4px; }

.qs-act {
  flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end;
  gap: 6px; margin-top: 2px;
}
.qs-manual { font-size: 12px; color: var(--text-muted); text-align: right; }
.qs-manual:hover { color: var(--navy); }
.qs-done-label { font-size: 12px; color: var(--green-dark); font-weight: 700; }

.qs-outro {
  padding: 18px 30px 22px; border-top: 1px solid var(--light-gray);
  background: var(--off-white); font-size: 13.5px; color: var(--text-secondary);
  line-height: 1.55;
}

/* ═══ 17.50.0 — the tenant reset danger zone ═════════════════════════════
   docs/tenant_reset_spec.md, mock docs/mockups/tenant_reset_mock.html.
   A palette of its OWN rather than the product's --red: the mock's crit
   family is warmer and darker than the score-band red, and the score bands
   are read as data while this is read as a warning. Four tokens, scoped to
   the block, so nothing here can bleed into a chart.
   Present on Dev1 only - the card is not rendered without
   OPTI_TENANT_RESET_ENABLED, and neither is the route it posts to. */
.danger {
  --crit: #B3261E; --crit-ink: #8C1D16; --crit-bg: #FCEEED; --crit-line: #F0C9C6;
  margin-top: 22px; background: var(--white);
  border: 1px solid var(--crit-line); border-radius: 12px; overflow: hidden;
}
.danger__head {
  display: flex; align-items: center; gap: 10px; padding: 14px 20px;
  border-bottom: 1px solid var(--crit-line); background: var(--crit-bg);
}
/* Stroke SVG, like every other icon in the product. No emoji. */
.danger__head svg {
  width: 17px; height: 17px; flex: none; fill: none; stroke: var(--crit);
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.danger__head h3 { margin: 0; font-size: 14.5px; color: var(--crit-ink); }
.danger__devbadge {
  margin-left: auto; font-size: 11px; font-weight: 700; letter-spacing: .05em;
  color: #8a6d00; background: #F8E9B8; border-radius: 99px; padding: 3px 10px;
}
.danger__body {
  padding: 16px 20px; display: flex; gap: 18px; align-items: flex-start;
}
.danger__body p {
  margin: 0; font-size: 13.5px; color: var(--crit-ink); max-width: 470px;
}
.btn-danger {
  margin-left: auto; flex: none; background: var(--crit); color: var(--white);
  border: 0; border-radius: 7px; padding: 9px 16px; font-size: 13.5px;
  font-weight: 700; cursor: pointer;
}
.btn-danger:hover { background: var(--crit-ink); }
.btn-danger[disabled] { opacity: .45; cursor: not-allowed; }
.btn-danger:focus-visible { outline: 2px solid var(--crit-ink); outline-offset: 2px; }

/* The confirmation dialog. `position: fixed` rather than the mock's inline
   overlay - the mock draws it open beside the card to show both at once; in
   the product it is a modal over the page. */
.reset-overlay {
  position: fixed; inset: 0; z-index: 60; background: rgba(11, 18, 35, .55);
  display: flex; justify-content: center; align-items: flex-start;
  padding: 44px 16px; overflow-y: auto;
}
.reset-overlay[hidden] { display: none; }
.reset-modal {
  background: var(--white); border-radius: 14px; max-width: 480px; width: 100%;
  border-top: 4px solid var(--crit); box-shadow: 0 18px 50px rgba(0, 0, 0, .35);
  padding: 22px 24px;
}
.reset-modal h4 { margin: 0 0 4px; font-size: 16.5px; color: var(--crit-ink); }
.reset-modal__sub { font-size: 13px; color: var(--text-secondary); margin: 0 0 14px; }
/* The ledger. Two columns that say the same thing twice from opposite
   sides, because "what survives" is the half an administrator is actually
   uncertain about. */
.reset-cols { display: flex; gap: 12px; margin-bottom: 14px; }
.reset-col { flex: 1; border-radius: 9px; padding: 10px 12px; font-size: 12.5px; }
.reset-col b { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; }
.reset-col ul { margin: 6px 0 0; padding-left: 16px; }
.reset-col li { margin: 2px 0; }
.reset-col--del { background: var(--crit-bg); border: 1px solid var(--crit-line); color: var(--crit-ink); }
.reset-col--keep { background: #EDF7F2; border: 1px solid #CBE7DA; color: #0B5B3E; }
.reset-warnline {
  font-size: 12.5px; color: var(--crit-ink); background: var(--crit-bg);
  border-radius: 7px; padding: 8px 10px; margin: 0 0 14px;
}
.reset-confirm label { display: block; font-size: 12.5px; color: var(--text-primary); margin-bottom: 6px; }
.reset-confirm code { background: var(--light-gray); padding: 1px 6px; border-radius: 4px; font-size: 12px; }
.reset-confirm input {
  width: 100%; border: 1.5px solid var(--medium-gray); border-radius: 7px;
  padding: 9px 11px; font-size: 14px; margin-bottom: 14px;
}
.reset-confirm input:focus { outline: none; border-color: var(--crit); }
.reset-modal__row { display: flex; gap: 10px; justify-content: flex-end; }
.btn-ghost {
  background: var(--light-gray); color: var(--text-primary);
  border: 1px solid var(--medium-gray); border-radius: 7px; padding: 9px 16px;
  font-size: 13.5px; font-weight: 600; cursor: pointer;
}
.btn-ghost:hover { background: var(--medium-gray); }

/* ── console redesign (17.51.0) - begin ──────────────────────────────────
   Management-console visual system (spec 2026-08-18). Every selector in
   this block starts from a console-scoped CLASS - zero bare element
   selectors - so the frozen desktop-layout rules stay untouched by
   construction, not by discipline. Pills reuse the tint token pairs; the
   danger button already exists in the btn family above. */

/* Table rhythm: one pair of vars carries the cell padding everywhere. */
.console-table {
  --ct-pad-y: 7px;
  --ct-pad-x: 12px;
  width: 100%; border-collapse: collapse;
}
.console-table th,
.console-table td { padding: var(--ct-pad-y) var(--ct-pad-x); }
.console-table thead th {
  position: sticky; top: 0; z-index: 5;
  background: var(--white);
  box-shadow: 0 1px 0 var(--medium-gray);
}

/* Action column: a TWO-column grid of small buttons (17.51.1 - the
   five-deep stack made every row five buttons tall). The primary action
   spans the full width on its own line; the licence levers pair up
   below it. The container owns the width so rows line up. */
.console-table .action-cell { text-align: right; vertical-align: top; width: 1%; }
.action-stack {
  display: inline-grid; grid-template-columns: repeat(2, minmax(94px, 1fr));
  gap: 4px; min-width: 196px; text-align: center;
}
.action-stack .btn { width: 100%; padding-top: 4px; padding-bottom: 4px; }
/* Two-phase POSTs sit in the stack as forms; contents lets their buttons
   join the grid directly so every control shares the cell rhythm. */
.action-stack form { display: contents; }
/* The row's first control - Open, or Resend on an invited row - reads as
   THE action and takes the full line; keyed on position, not on colour,
   because an expired row demotes Open to secondary. */
.action-stack > *:first-child,
.action-stack > form:first-child > .btn { grid-column: 1 / -1; }

/* Slim separate column for destructive actions (btn--danger lives here). */
.console-table .cell-danger {
  text-align: right; vertical-align: top;
  width: 1%; white-space: nowrap;
}

/* Semantic pill modifiers (compose with .pill): licence/state at a glance. */
.pill--ok    { background: var(--tint-ok-bg);   color: var(--tint-ok-ink); }
.pill--muted { background: var(--tint-mute-bg); color: var(--tint-mute-ink); }
.pill--warn  { background: var(--tint-med-bg);  color: var(--tint-med-ink); }
.pill--bad   { background: var(--tint-crit-bg); color: var(--tint-crit-ink); }

/* Org ids: first8 in monospace, full GUID on the clipboard via .id-copy. */
.org-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px; color: var(--dark-gray); letter-spacing: .2px;
}
.id-copy {
  border: none; background: transparent; cursor: pointer;
  padding: 0 4px; font-size: 12px; color: var(--navy);
  border-radius: 4px; vertical-align: baseline;
}
.id-copy:hover { background: var(--light-gray); }
.id-copy:focus-visible { outline: 2px solid var(--navy); outline-offset: 1px; }
.id-copy.is-copied { color: var(--tint-ok-ink); }

/* Page header pattern: title + row-count chip + the page's primary CTA. */
.page-head {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 16px; flex-wrap: wrap;
}
.page-head h1 { margin: 0; }
.head-count {
  display: inline-block; padding: 2px 10px; border-radius: 999px;
  background: var(--tint-navy-bg); color: var(--tint-navy-ink);
  font-size: 12px; font-weight: 600;
}
.page-head .head-cta { margin-left: auto; }

/* Usage bars for the limits sub-row (plain CSS, no JS). */
.usage-bar {
  height: 6px; max-width: 180px; border-radius: 3px;
  background: var(--medium-gray); overflow: hidden;
}
.usage-bar .usage-bar__fill {
  display: block; height: 100%; border-radius: 3px;
  background: var(--teal-dark);
}
.usage-bar .usage-bar__fill.is-warn { background: var(--amber); }
.usage-bar .usage-bar__fill.is-bad  { background: var(--red); }

/* Licence-health filter chips (spec §C): plain links, active = filled. */
.health-chips { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 14px; }
.health-chip {
  padding: 4px 12px; border-radius: 999px;
  font-size: 12.5px; font-weight: 600;
  background: var(--light-gray); color: var(--text-primary);
  border: 1px solid var(--medium-gray);
}
.health-chip:hover { background: var(--medium-gray); text-decoration: none; }
.health-chip.is-active { background: var(--navy); color: var(--white); border-color: var(--navy); }

/* Form utilities: the classes that replace the console's inline styles. */
.console-section { margin-bottom: 16px; }
.console-title { margin-top: 0; font-size: 1rem; }
.console-title--gap { margin-top: 18px; }
.console-form { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-end; }
.console-form .field label { display: block; margin-bottom: 2px; }
.console-form input, .console-form select { padding: 6px 10px; font-size: 0.875rem; }
.console-form .grow { flex: 1; min-width: 200px; }
.console-input--id { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.875rem; width: 260px; max-width: 100%; }
.console-input--email { width: 220px; }
.console-input--days { width: 4.5em; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
.form-grid label { display: block; margin-bottom: 2px; }
.form-grid input, .form-grid select { width: 100%; padding: 6px 10px; }
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.fieldset-plain { margin: 18px 0 0; border: 0; padding: 0; }
.fieldset-plain legend { padding: 0; margin-bottom: 6px; }
.radio-row { display: block; margin-bottom: 8px; }
.card--error { border-left: 3px solid var(--red); margin-bottom: 16px; }
.table-filter { margin-bottom: 10px; padding: 6px 10px; width: 260px; max-width: 100%;
                border: 1px solid var(--medium-gray); border-radius: 6px; font-size: 14px; }
/* ── console redesign (17.51.0) - end ──────────────────────────────── */

/* ── surfaces round 1 (17.54.0) - begin ──────────────────────────────────
   The console visual system applied to the tenant daily drivers (spec
   2026-08-20 v3). Same contract as the console block above: every
   selector class-scoped, zero bare element selectors, no new colours. */

/* Scan history (chunk 1): the toolbar that replaced the file's inline
   flex, the checkbox column width, and the filter's escape hatch over
   the Alpine older-rows fold. */
.sc-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.sc-toolbar .table-filter { margin-bottom: 0; flex: 1; max-width: 320px; }
.sc-checkcol { width: 40px; }
.sc-more { margin-top: 8px; }
.sc-note { margin-top: 8px; }
.is-filtering .scan-row--older { display: table-row; }

/* Dashboard (chunk 2): the header's "View full progress" link kept its
   underline; only the inline style moved here. */
.head-link { text-decoration: underline; }

/* Auditor binder (chunk 4): the language-status markers joined the pill
   vocabulary; these two rules carry over the anatomy the lchips drew -
   the baseline row and the small de-italicised sub-label. */
.binder-langtab .pill { display: inline-flex; align-items: baseline; gap: 5px; }
.binder-langtab .pill i { font-style: normal; font-weight: 500; opacity: .7; font-size: 10.5px; }

/* Workspace seed-localization wait card (17.54.2): shown INSTEAD of the
   English seed while the document is rewritten into the workspace's
   language; the spinner reuses the wsxspin keyframes the upload drop
   already ships (with its reduced-motion guard mirrored). */
.wsx-seedwait { padding: 48px 16px; text-align: center; }
.wsx-seedwait__spin::after { content: ""; display: inline-block; width: 22px; height: 22px;
  border: 3px solid var(--teal); border-top-color: transparent; border-radius: 50%;
  animation: wsxspin .8s linear infinite; }
@media (prefers-reduced-motion: reduce) { .wsx-seedwait__spin::after { animation: none; } }

/* CyFun tree (17.54.4, OptiTester run #6): a requirement row carries id +
   KEY + title + assurance + owner chips + D/I badges - ~490px of nowrap
   flex children on a 283px track at 375px viewport, so the tail spilled
   108px past the screen. Measured live, not guessed: the owner chips
   (190px) are the widest child. Under 480px the row wraps (chips and
   badges drop to a second line); min-width:0 lets the title shrink
   instead of hoarding its longest word. Desktop rows never wrap - the
   column is wider than the children's sum. */
.req__link .req__title { min-width: 0; }
@media (max-width: 480px) {
  .req__link { flex-wrap: wrap; }
  /* The toolbar's search-and-buttons cluster is its own nowrap flex
     row (469px) - the second layer the measurement exposed once the
     rows stopped being the widest thing. */
  .tree-expand { flex-wrap: wrap; }
}
/* ── surfaces round 1 (17.54.0) - end ─────────────────────────────────── */
