/* ── L3 Clearview — Main Stylesheet ─────────────────────────────────────── */
:root {
  --brand-primary:   #1a4a3a;   /* fixed functional green (buttons, gradient end, score) — NOT per-client */
  --brand-secondary: #2d6b52;
  --brand-accent:    #c8a84b;
  --accent:          #6abf9a;   /* fixed light-green highlight (gradient/score) — NOT per-client */
  --brand-primary-rgb:   26,74,58;
  --brand-secondary-rgb: 45,107,82;
  --banner:          #1B4F8C;   /* per-client: the header banner (L3 blue default) */
  --page-bg:         #1a1a1a;   /* per-client: dashboard page background (area behind the cards) */
  --card-accent:     #6abf9a;   /* per-client: uniform card top-border color (when "apply to all" is on) */
  /* Card theme (per-client) — dark defaults; #cv-app.cv-cards-light overrides them below.
     Card surfaces/text are hardcoded inline for dark, so light mode flips them via the
     card-signature selectors in the cv-cards-light block (no isCard / source change). */
  --card-text:       #ececec;   /* primary text inside cards */
  --card-muted:      #a8a8a8;   /* secondary/dim text inside cards */
  --card-border:     #404040;   /* card side/bottom borders */
  --card-surface:    #2a2a2a;   /* card panel background */
  --card-surface-in: #242424;   /* inner sub-box background */
  --red:   #c9483a;
  --gold:  #8b6914;
  --font:  "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  --font-serif: "DM Serif Display", Georgia, serif;
  --bg:    #202020; --bg2: #282828; --bg3: #303030;
  --panel: #2a2a2a; --border: #404040;
  --text:  #f5f5f5; --text2: #c8c8c8; --text3: #808080;
  --sidebar-w: 220px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; font-family: var(--font); font-size: 15px; }
a { color: inherit; text-decoration: none; }

/* ── Flash messages ─────────────────────────────────────────────────────── */
.flash-container { position: fixed; top: 16px; right: 16px; z-index: 9999; display: flex; flex-direction: column; gap: 8px; max-width: 360px; }
.flash { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-radius: 8px; font-size: 13px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); gap: 10px; }
.flash-success { background: #e6f4ec; color: #1a5c38; border: 1px solid #b8dfca; }
.flash-danger  { background: #fdecea; color: #7a1c1c; border: 1px solid #f0b4b4; }
.flash-warning { background: #fff8e6; color: #7a5500; border: 1px solid #f0d888; }
.flash-info    { background: #e6f0fc; color: #1a4a80; border: 1px solid #b4ccf0; }
.flash-close   { background: none; border: none; cursor: pointer; font-size: 14px; opacity: 0.6; padding: 0 2px; }
.flash-close:hover { opacity: 1; }

/* ── LOGIN PAGE ─────────────────────────────────────────────────────────── */
.login-page { background: var(--bg); }
.login-shell { display: flex; height: 100vh; }
.login-brand {
  width: 45%; background: var(--brand-primary);
  display: flex; align-items: center; justify-content: center;
  padding: 40px; flex-shrink: 0;
}
.login-brand-inner { max-width: 380px; }
.login-logo { display: flex; align-items: center; gap: 14px; margin-bottom: 40px; }
.login-logo-mark { width: 52px; height: 52px; border-radius: 12px; background: rgba(255,255,255,0.15); display: flex; align-items: center; justify-content: center; font-family: var(--font-serif); font-size: 22px; font-weight: 700; color: #fff; border: 1px solid rgba(255,255,255,0.2); }
.login-logo-name { font-family: var(--font-serif); font-size: 24px; font-weight: 700; color: #fff; }
.login-logo-by { font-size:12px; color: rgba(255,255,255,0.5); margin-top: 2px; }
.login-tagline h1 { font-family: var(--font-serif); font-size: 32px; font-weight: 400; color: #fff; line-height: 1.3; margin-bottom: 14px; }
.login-tagline p { font-size: 14px; color: rgba(255,255,255,0.65); line-height: 1.7; margin-bottom: 32px; }
.login-features { display: flex; flex-direction: column; gap: 10px; }
.login-feature { display: flex; align-items: center; gap: 10px; font-size: 13px; color: rgba(255,255,255,0.75); }
.lf-icon { font-size: 16px; }
.login-form-panel { flex: 1; background: #f8f7f5; display: flex; align-items: center; justify-content: center; padding: 40px; }
.login-form-inner { width: 100%; max-width: 380px; }
.login-form-header { margin-bottom: 28px; }
.login-form-header h2 { font-family: var(--font-serif); font-size: 26px; font-weight: 400; color: #1a1a1a; margin-bottom: 6px; }
.login-form-header p { font-size: 13px; color: #666; }
.btn-microsoft { display: flex; align-items: center; justify-content: center; gap: 12px; width: 100%; padding: 13px 20px; background: #fff; border: 1px solid #d0d0d0; border-radius: 8px; font-size: 14px; font-weight: 500; color: #1a1a1a; cursor: pointer; transition: all .15s; font-family: var(--font); }
.btn-microsoft:hover { background: #f5f5f5; border-color: #aaa; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
.login-divider { display: flex; align-items: center; gap: 12px; margin: 20px 0; color: #aaa; font-size:12px; }
.login-divider::before, .login-divider::after { content: ''; flex: 1; height: 1px; background: #e0e0e0; }
.login-info { font-size: 12px; color: #888; line-height: 1.6; background: #f0f0ee; border-radius: 6px; padding: 12px; }
.login-footer { margin-top: 32px; font-size:12px; color: #bbb; display: flex; gap: 8px; align-items: center; }

/* ── ADMIN SHELL ─────────────────────────────────────────────────────────── */
.admin-layout { background: #f5f4f2; }
.admin-shell { display: flex; height: 100vh; overflow: hidden; }
.admin-sidebar { width: var(--sidebar-w); background: #1e1e2e; flex-shrink: 0; display: flex; flex-direction: column; border-right: 1px solid #2e2e40; }
.sidebar-brand { padding: 18px 16px; border-bottom: 1px solid #2e2e40; display: flex; align-items: center; gap: 10px; }
.sb-logo-mark { width: 34px; height: 34px; border-radius: 8px; background: var(--brand-primary); display: flex; align-items: center; justify-content: center; font-family: var(--font-serif); font-size: 14px; font-weight: 700; color: #fff; flex-shrink: 0; }
.sb-logo-name { font-family: var(--font-serif); font-size: 16px; font-weight: 700; color: #fff; line-height: 1; }
.sb-logo-sub { font-size:11px; color: rgba(255,255,255,0.4); margin-top: 2px; }
.sidebar-nav { flex: 1; padding: 12px 8px; overflow-y: auto; }
.sb-section-label { font-size:11px; font-weight: 600; text-transform: uppercase; letter-spacing: .1em; color: rgba(255,255,255,0.3); padding: 6px 8px 3px; }
.sb-link { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 6px; font-size: 13px; color: rgba(255,255,255,0.6); transition: all .12s; border-left: 2px solid transparent; }
.sb-link:hover { background: rgba(255,255,255,0.07); color: rgba(255,255,255,0.9); }
.sb-link.active { background: var(--brand-primary); color: #fff; border-left-color: var(--brand-accent); font-weight: 500; }
.sb-icon { font-size: 14px; }
.sidebar-user { padding: 12px 14px; border-top: 1px solid #2e2e40; display: flex; align-items: center; gap: 9px; }
.su-avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--brand-primary); display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; color: #fff; flex-shrink: 0; text-transform: uppercase; }
.su-info { flex: 1; min-width: 0; }
.su-name { font-size: 12px; font-weight: 500; color: rgba(255,255,255,0.8); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.su-role { font-size:11.5px; color: rgba(255,255,255,0.4); text-transform: capitalize; }
.su-logout { color: rgba(255,255,255,0.4); font-size: 16px; cursor: pointer; padding: 3px; transition: color .12s; }
.su-logout:hover { color: var(--brand-accent); }
.admin-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.admin-topbar { background: #fff; border-bottom: 1px solid #e5e5e5; padding: 10px 20px; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; }
.admin-content { flex: 1; overflow-y: auto; padding: 20px; }
.breadcrumb { display: flex; align-items: center; gap: 6px; font-size: 13px; color: #888; }
.breadcrumb a { color: #888; } .breadcrumb a:hover { color: #333; }
.breadcrumb .bc-sep { color: #ccc; } .breadcrumb .bc-current { color: #333; font-weight: 500; }

/* The engagement crumb, when the client has more than one cycle, is a switcher. It has to read as
   the breadcrumb it replaces rather than as a control bolted beside it — same size, weight and
   colour as .bc-current, with only the caret announcing that it opens. */
.breadcrumb .bc-cycle { position: relative; display: inline-flex; }
.breadcrumb .bc-cycle-btn {
  font: inherit; color: #333; font-weight: 500; background: none; border: 0;
  padding: 1px 4px; margin: -1px -4px; border-radius: 4px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
.breadcrumb .bc-cycle-btn:hover { background: #eef1f5; }
.breadcrumb .bc-cycle-btn:focus-visible { outline: 2px solid #1C5A9E; outline-offset: 1px; }
.breadcrumb .bc-cycle-caret { font-size: 10px; color: #8a8a8a; }
.breadcrumb .bc-cycle-menu {
  display: none; position: absolute; top: 100%; left: -4px; margin-top: 5px; z-index: 60;
  min-width: 190px; max-height: 60vh; overflow-y: auto;
  background: #fff; border: 1px solid #dde2e8; border-radius: 6px;
  box-shadow: 0 8px 22px rgba(0,0,0,.13); padding: 4px;
}
.breadcrumb .bc-cycle-menu.is-open { display: block; }
.breadcrumb .bc-cycle-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 7px 10px; border-radius: 4px; font-size: 13px; color: #333 !important;
  text-decoration: none; white-space: nowrap;
}
.breadcrumb .bc-cycle-item:hover { background: #f2f5f9; }
.breadcrumb .bc-cycle-item.is-current { font-weight: 600; color: #1C5A9E !important; }
/* Amber, matching the "needs attention" register elsewhere — a draft is not an error, it is a
   cycle the client cannot see yet. */
.breadcrumb .bc-cycle-draft {
  font-size: 11px; color: #8b6914; border: 1px solid #d8c68a; background: #fdf7e3;
  border-radius: 8px; padding: 0 6px; font-weight: 600;
}
.topbar-actions { display: flex; gap: 8px; }

/* ── BUTTONS ─────────────────────────────────────────────────────────────── */
.btn { display: inline-flex; align-items: center; gap: 6px; padding: 7px 14px; border-radius: 6px; font-size: 13px; font-weight: 500; cursor: pointer; border: 1px solid; transition: all .15s; font-family: var(--font); }
.btn-primary { background: var(--brand-primary); color: #fff; border-color: var(--brand-primary); }
.btn-primary:hover { background: var(--brand-secondary); border-color: var(--brand-secondary); }
.btn-secondary { background: #fff; color: #333; border-color: #d0d0d0; }
.btn-secondary:hover { background: #f5f5f5; }
.btn-danger { background: var(--red); color: #fff; border-color: var(--red); }
.btn-sm { padding: 4px 10px; font-size: 12px; }

/* ── PANELS ─────────────────────────────────────────────────────────────── */
.panel { background: #fff; border: 1px solid #e5e5e5; border-radius: 10px; padding: 16px; }
.panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid #f0f0f0; }
.panel-header span { font-size: 13px; font-weight: 600; color: #1a1a1a; }
.panel-link { font-size: 12px; color: var(--brand-secondary); }
.panel-link:hover { color: var(--brand-primary); }

/* ── STATS ─────────────────────────────────────────────────────────────── */
.stats-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.stat-card { background: #fff; border: 1px solid #e5e5e5; border-radius: 10px; padding: 14px 16px; display: flex; align-items: center; gap: 12px; }
.stat-icon { width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; }
.stat-val { font-family: var(--font-serif); font-size: 26px; font-weight: 400; color: #1a1a1a; line-height: 1; }
.stat-lbl { font-size:12px; color: #888; margin-top: 2px; }

/* ── LISTS ─────────────────────────────────────────────────────────────── */
.two-col-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.client-list, .engagement-list { display: flex; flex-direction: column; }
.client-row, .engagement-row { display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 6px; transition: background .12s; }
.client-row:hover, .engagement-row:hover { background: #f5f5f5; }
.cr-avatar { width: 34px; height: 34px; border-radius: 8px; background: var(--brand-primary); display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 600; color: #fff; flex-shrink: 0; text-transform: uppercase; }
.cr-info, .er-info { flex: 1; min-width: 0; }
.cr-name, .er-name { font-size: 13px; font-weight: 500; color: #1a1a1a; }
.cr-meta, .er-year { font-size:12px; color: #888; margin-top: 1px; }
.er-right { display: flex; gap: 5px; align-items: center; }

/* ── BADGES ─────────────────────────────────────────────────────────────── */
.badge { display: inline-block; font-size:11.5px; padding: 2px 8px; border-radius: 10px; font-weight: 600; text-transform: capitalize; }
.badge-planning   { background: #e8f2ee; color: #1a5c38; }
.badge-fieldwork  { background: #e8f0fc; color: #1a4a80; }
.badge-reporting  { background: #fff3e0; color: #7a5500; }
.badge-complete   { background: #e8f2ee; color: #1a5c38; }
.badge-archived   { background: #f0f0f0; color: #666; }
.badge-inactive   { background: #f0f0f0; color: #999; }
.badge-published  { background: #fff3d4; color: #7a5500; }

/* ── FORMS ──────────────────────────────────────────────────────────────── */
.form-group { margin-bottom: 16px; }
.form-label { display: block; font-size: 12px; font-weight: 600; color: #444; margin-bottom: 5px; text-transform: uppercase; letter-spacing: .04em; }
.form-control { display: block; width: 100%; padding: 8px 11px; border: 1px solid #d0d0d0; border-radius: 6px; font-size: 13px; font-family: var(--font); color: #1a1a1a; background: #fff; outline: none; transition: border-color .15s; }
.form-control:focus { border-color: var(--brand-secondary); box-shadow: 0 0 0 3px rgba(45,107,82,0.1); }
.form-control-sm { padding: 5px 9px; font-size: 12px; }
select.form-control { cursor: pointer; }
textarea.form-control { resize: vertical; min-height: 100px; }
.form-hint { font-size:12px; color: #888; margin-top: 4px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }

/* Premium dark input used across the dashboard's manual-entry boxes (remediation, parts, notes).
   Softer surface + subtle border, rounded, with a calm focus ring that adapts to the client brand. */
.cv-field {
  background: #23272f;
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 8px;
  color: #eef1f5;
  font-size: 13px;
  font-family: var(--font);
  line-height: 1.5;
  padding: 9px 11px;
  outline: none;
  box-sizing: border-box;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.18);
  transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
.cv-field::placeholder { color: #828a95; }
.cv-field:hover { border-color: rgba(255,255,255,0.20); }
.cv-field:focus {
  border-color: var(--brand-secondary);
  background: #262b34;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.18), 0 0 0 3px color-mix(in srgb, var(--brand-secondary) 26%, transparent);
}
textarea.cv-field { resize: vertical; min-height: 40px; }
select.cv-field, input[type=date].cv-field { color-scheme: dark; cursor: pointer; }
.form-actions { display: flex; gap: 8px; margin-top: 20px; padding-top: 16px; border-top: 1px solid #eee; }

/* ── TABLES ─────────────────────────────────────────────────────────────── */
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table th { text-align: left; padding: 8px 10px; font-size:12px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: #888; background: #f8f8f8; border-bottom: 1px solid #eee; }
.data-table td { padding: 10px 10px; border-bottom: 1px solid #f0f0f0; color: #1a1a1a; vertical-align: middle; }
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: #f8f8f8; }

/* ── EMPTY STATE ─────────────────────────────────────────────────────────── */
.empty-state { text-align: center; padding: 28px 20px; color: #888; font-size: 13px; }
.empty-state p { margin-bottom: 12px; }

/* ── DASHBOARD HEADER BUTTONS ────────────────────────────────────────────── */
.cv-hdr-btn { padding: 5px 12px; border-radius: 4px; font-size:12px; font-weight: 500; cursor: pointer; border: 1px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.8); transition: all .15s; font-family: var(--font); white-space: nowrap; }
.cv-hdr-btn:hover { background: rgba(255,255,255,0.15); color: #fff; }
.cv-hdr-btn-green { background: var(--brand-secondary); border-color: var(--brand-secondary); color: #fff; }
.cv-hdr-btn-green:hover { background: var(--brand-primary); }

/* ── DASHBOARD NAV ───────────────────────────────────────────────────────── */
.cv-nav { padding: 4px 11px; border-radius: 3px; font-size:12px; font-weight: 500; cursor: pointer; border: 1px solid #363636; background: #2a2a2a; color: #a0a0a0; transition: all .12s; font-family: var(--font); }
.cv-nav:hover { color: #fff; border-color: #555; }
.cv-nav.active { background: var(--brand-secondary); color: #fff; border-color: var(--brand-secondary); }
.cv-filter { padding: 3px 10px; border-radius: 3px; font-size:11.5px; font-weight: 500; cursor: pointer; border: 1px solid #363636; background: #1e1e1e; color: #a8a8a8; transition: all .12s; font-family: var(--font); }
.cv-filter:hover { border-color: #555; color: #ccc; }
/* superseded by the one pill system further down: selection is a border highlight, never a fill */

/* ── SCROLLBAR ───────────────────────────────────────────────────────────── */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #363636; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--brand-secondary); }

/* ── SCORE INFO TOOLTIP ──────────────────────────────────────────────────── */
.cv-info { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 17px; height: 17px; margin-left: 7px; border: 1px solid #707070; border-radius: 50%; color: #a8a8a8; font: italic 600 11px/1 var(--font); text-transform: none; letter-spacing: 0; cursor: help; vertical-align: middle; }
.cv-info:hover { border-color: var(--accent); color: var(--accent); }
.cv-info .cv-info-box { display: none; position: absolute; top: 22px; left: -8px; z-index: 80; width: 340px; padding: 13px 15px; background: #1d1d1d; border: 1px solid #4a4a4a; border-radius: 7px; font: 400 12px/1.6 var(--font); text-transform: none; letter-spacing: normal; color: #c8c8c8; box-shadow: 0 8px 26px rgba(0,0,0,.55); white-space: normal; }
/* The box is a DARK popover in both themes, so its ink must be near-white in both. It was not:
   ".cv-info .cv-info-box" is (0,2,0) and "#cv-body div, #cv-body span, ..." { color: inherit }
   is (1,0,1), so inside the body the box ignored its own colour and inherited .cv-info:hover's
   accent. In the light theme that put the brand blue on #1d1d1d -- about 2:1, unreadable -- while
   the <b> runs stayed white, because <b> is not in that inherit list. Hence the reported mix of
   green and white. #cv-body-prefixed to outrank the inherit, one ink for body and bold alike. */
#cv-body .cv-info .cv-info-box, .cv-info .cv-info-box { color: #ececec; }
#cv-body .cv-info .cv-info-box b, .cv-info .cv-info-box b { color: #ececec; }
.cv-info:hover .cv-info-box, .cv-info:focus .cv-info-box { display: block; }
/* Definition boxes were 250px with every line butted against the next (2026-09-14, the user's
   note). 340px with room to breathe everywhere; the posture score's box is the longest and takes
   the label/value layout below, which the request list and the admin pages already use. */
.cv-info .cv-info-box.wide { width: 370px; }
.cv-info-box .cv-info-lead { margin-bottom: 9px; }
.cv-info-box .cv-info-rows { display: grid; grid-template-columns: 78px 1fr; gap: 6px 12px; }
.cv-info-box .cv-info-k { color: #a8a8a8; font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; padding-top: 2px; }
.cv-info-box .cv-info-fine { margin-top: 10px; padding-top: 8px; border-top: 1px solid #383838; color: #a8a8a8; font-size: 11.5px; line-height: 1.5; }
/* Stacked rows: a label OVER its paragraph, for definitions whose parts are paragraphs rather
   than short values (the two-column cv-info-rows put two paragraphs side by side). */
.cv-info-box .cv-info-stack { display: grid; grid-template-columns: 1fr; gap: 9px; }
.cv-info-box .cv-info-stack .cv-info-k { display: block; margin-bottom: 2px; padding-top: 0; }
#cv-body .cv-info .cv-info-box .cv-info-k, #cv-body .cv-info .cv-info-box .cv-info-fine { color: #a8a8a8; }

/* ── RICH TEXT CONTENT (drawer) ──────────────────────────────────────────── */
.rich-content p { margin-bottom: 8px; }
.rich-content p:last-child { margin-bottom: 0; }
/* List treatment lives with .rich-content, not on the drawer's ids — the finding narrative and
   the recommendations render in the expandable rows too, and prose that formats one way in the
   drawer and another in the panel is the same content telling two stories. */
.rich-content ul { list-style: disc; padding-left: 22px; margin: 8px 0; }
.rich-content ol { list-style: decimal; padding-left: 22px; margin: 8px 0; }
.rich-content li { margin: 6px 0; }
.rich-content ul ul, .rich-content ul ol, .rich-content ol ul { list-style: circle; margin: 5px 0; }
.rich-content strong, .rich-content b { color: #f5f5f5; font-weight: 600; }
.rich-content u { color: #a8d5c2; }

/* Drawer detail/recommendation render report HTML (incl. bulleted/numbered lists).
   Typography mirrors the source reports: Verdana, ~1.5 line height, and ~6pt of air
   before/after paragraphs and bullets so it reads like the deliverable, not a cramped blob.
   #dr-detail and #dr-rec are reused by both the finding drawer and the housekeeping drawer. */
#dr-detail, #dr-rec { font-family: Verdana, Geneva, Tahoma, sans-serif; font-size: 12.5px; line-height: 1.5; color: #c8c8c8; }
/* (list rules moved to .rich-content above — #dr-detail and #dr-rec both carry that class, so
   they keep exactly the treatment they had, and the panels now share it) */
#dr-detail p, #dr-rec p { margin: 8px 0; }
#dr-detail > p:first-child, #dr-rec > p:first-child { margin-top: 0; }
#dr-detail > p:last-child, #dr-rec > p:last-child { margin-bottom: 0; }
/* A descriptive sentence tucked under a category bullet (Word: paragraph inside the list item). */
#dr-detail li > p, #dr-rec li > p { margin: 4px 0 2px; }

/* ── GRIDSTACK OVERRIDES ─────────────────────────────────────────────────── */
.grid-stack-item-content { overflow: hidden; }
.gs-edit .grid-stack-item { cursor: move; }
.gs-edit .grid-stack-item > .grid-stack-item-content { border: 1px dashed rgba(200,168,75,0.4) !important; }

/* ── FINDINGS PAGE ────────────────────────────────────────────────────────── */
.cv-ft { width: 100%; border-collapse: collapse; font-size: 14px; }
.cv-ft th {
  text-align: left; padding: 10px 12px;
  font-size:12px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: #a8a8a8; background: #222;
  border-bottom: 1px solid #404040; cursor: pointer; white-space: nowrap;
}
.cv-ft th:hover { color: #2d6b52; }
.cv-ft th.sorted { color: #2d6b52; }
.cv-ft td {
  /* No rule between rows. A line under the column headings separates labels from values and
     earns its place; a line between every finding says only "these are different rows", which
     the rows already say. Padding and the hover highlight carry it. */
  padding: 12px 12px;
  vertical-align: middle; font-size: 14px; color: #f5f5f5; line-height: 1.4;
}
.cv-ft tr:last-child td { border-bottom: none; }
.cv-ft tr:hover td { background: rgba(45,107,82,.08); cursor: pointer; }
.cv-ft tr:hover td:first-child { color: #6abf9a; }

/* ── KPI CARD DARK THEME ──────────────────────────────────────────────────── */
.cv-kpi-card {
  background: #2a2a2a; border: 1px solid #404040;
  border-radius: 7px; padding: 13px 14px;
  position: relative; overflow: hidden;
}
.cv-kpi-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
}
.cv-kpi-card.cg::before { background: #2d6b52; }
.cv-kpi-card.ct::before { background: #2d9b8a; }
.cv-kpi-card.cr::before { background: #c9483a; }
.cv-kpi-card.ca::before { background: #c8a84b; }
.cv-kpi-lbl {
  font-size:11.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .08em; color: #a8a8a8; margin-bottom: 6px;
}
.cv-kpi-val {
  font-family: var(--font); font-size: 30px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #f5f5f5; line-height: 1; margin-bottom: 4px;
}
.cv-kpi-sub { font-size:12px; color: #c8c8c8; }
.cv-kpi-badge {
  display: inline-block; font-size:11.5px; padding: 2px 8px;
  border-radius: 10px; font-weight: 500; margin-top: 5px;
}
.b-g { background: rgba(45,107,82,.25); color: #6abf9a; }
.b-r { background: rgba(192,57,43,.25); color: #ef8b80; }
.b-a { background: rgba(139,105,20,.25); color: #cdae57; }
.b-b { background: rgba(45,100,180,.2); color: #7ab0e8; }

/* ── PRIMARY NAV TABS ────────────────────────────────────────────────────── */
.cv-tab {
  padding: 0 20px;
  height: 48px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  border: none;
  background: transparent;
  color: rgba(255,255,255,0.5);
  border-bottom: 3px solid transparent;
  transition: all .15s;
  font-family: var(--font);
  white-space: nowrap;
  flex-shrink: 0;
}
.cv-tab:hover { color: rgba(255,255,255,0.85); background: rgba(255,255,255,0.06); }
.cv-tab.active { color: #fff; border-bottom-color: #cdae57; font-weight: 600; }

/* ── AUDIT TYPE FILTER BUTTONS ───────────────────────────────────────────── */
.cv-audit-filter {
  padding: 4px 12px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid #404040;
  background: #1e1e1e;
  color: #a8a8a8;
  transition: all .12s;
  font-family: var(--font);
}
.cv-audit-filter:hover { border-color: #606060; color: #d0d0d0; }

/* ── FILTER BUTTONS ──────────────────────────────────────────────────────── */
.cv-filter {
  padding: 4px 12px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid #404040;
  background: transparent;
  color: #a8a8a8;
  transition: all .12s;
  font-family: var(--font);
}
.cv-filter:hover { border-color: #606060; color: #d0d0d0; }

/* ── HEADER BUTTONS ──────────────────────────────────────────────────────── */
.cv-hdr-btn {
  padding: 6px 14px;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid rgba(255,255,255,0.25);
  background: rgba(255,255,255,0.1);
  color: rgba(255,255,255,0.85);
  transition: all .15s;
  font-family: var(--font);
  white-space: nowrap;
  text-decoration: none;
  display: inline-block;
  text-align: center;
}
.cv-hdr-btn:hover { background: rgba(255,255,255,0.18); color: #fff; }
.cv-hdr-btn-green { background: #2d6b52; border-color: #2d6b52; color: #fff; }
.cv-hdr-btn-green:hover { background: #1a4a3a; }

/* ── DASHBOARD OVERRIDES — matched to the design demo ─────────────────────────
   Canonical dashboard rules lifted from dashboard_demo_final_4_17.html so the
   live dashboard renders identically. Appended last so they win over any older
   copies above. (The demo banner/watermark rules are intentionally omitted.) */
.cv-tab{padding:0 20px;height:48px;display:inline-flex;align-items:center;gap:7px;font-size:14px;font-weight:500;cursor:pointer;border:none;background:transparent;color:rgba(255,255,255,.5);border-bottom:3px solid transparent;transition:all .15s;font-family:var(--font);white-space:nowrap;flex-shrink:0;}
.cv-tab:hover{color:rgba(255,255,255,.85);background:rgba(255,255,255,.06);}
.cv-tab.active{color:#fff;border-bottom-color:#cdae57;font-weight:600;}
.cv-audit-filter{padding:4px 12px;border-radius:4px;font-size:12px;font-weight:500;cursor:pointer;border:1px solid #404040;background:#1e1e1e;color:#a8a8a8;transition:all .12s;font-family:var(--font);}
.cv-audit-filter:hover{border-color:#606060;color:#d0d0d0;}
.cv-filter{padding:4px 12px;border-radius:4px;font-size:12px;font-weight:500;cursor:pointer;border:1px solid #404040;background:transparent;color:#a8a8a8;transition:all .12s;font-family:var(--font);}
.cv-filter:hover{border-color:#606060;color:#d0d0d0;}
.cv-hdr-btn{padding:6px 14px;border-radius:5px;font-size:12px;font-weight:500;cursor:pointer;border:1px solid rgba(255,255,255,.25);background:rgba(255,255,255,.1);color:rgba(255,255,255,.85);transition:all .15s;font-family:var(--font);white-space:nowrap;text-decoration:none;display:inline-block;text-align:center;}
.cv-hdr-btn:hover{background:rgba(255,255,255,.18);color:#fff;}
.cv-ft{width:100%;border-collapse:collapse;font-size:14px;}
.cv-ft th{text-align:left;padding:10px 12px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#a8a8a8;background:#222;border-bottom:1px solid #404040;cursor:pointer;white-space:nowrap;}
.cv-ft th:hover,.cv-ft th.sorted{color:#2d6b52;}
/* Reserve a fixed-width slot for the housekeeping sort arrow so a column gaining or losing its
   arrow never nudges the label — matching the findings register's inline-styled si- spans. */
.cv-ft th span[id^="hk-si-"]{display:inline-block;width:9px;margin-left:4px;font-size:8px;line-height:1;vertical-align:middle;color:#a8a8a8;}
/* The "click to expand" hint on the finding-detail disclosure disappears once it is open. */
details[open] > summary .rem-fd-hint{display:none;}
/* ── Theme-aware text on translucent tints ─────────────────────────────
   Cells drawn on a translucent fill sit on a surface that is dark in the default theme and light
   in the card-light one, so a fixed inline hex can only be legible in one of the two — which is
   how the retired trends heatmap once ended up at 1.6:1 in light mode. Colour comes from these
   classes instead, and the light theme overrides them. Now carried by the trends matrix and
   delta views, which draw the same way. */
/* #cv-body-prefixed for the same reason .cv-attr is, and it took a <span> to expose it:
   "#cv-body div, #cv-body span, #cv-body td, #cv-body th { color: inherit }" sits at (1,0,1) and
   outranks a bare class, so these three had been silently inert in the DARK theme everywhere
   inside the body — inheriting whatever their parent set. It went unnoticed because the inherited
   ink happened to sit near cv-t-strong's, and because the LIGHT overrides carry an extra class
   (1,2,0) and so were winning all along. The id lifts these to (1,1,0), under the light rules and
   over the inherit. */
#cv-body .cv-t-strong{color:#f2f4f5;}
#cv-body .cv-t-label{color:#c6c8cb;}
#cv-body .cv-t-muted{color:#949aa1;}
.cv-t-strong{color:#f2f4f5;}
.cv-t-label{color:#c6c8cb;}
.cv-t-muted{color:#949aa1;}
#cv-app.cv-cards-light .cv-t-strong,
#cv-app.cv-page-light .cv-t-strong{color:#14202d;}
#cv-app.cv-cards-light .cv-t-label,
#cv-app.cv-page-light .cv-t-label{color:#1f262e;}
#cv-app.cv-cards-light .cv-t-muted,
#cv-app.cv-page-light .cv-t-muted{color:#4a535d;}
/* Year column on the findings register — always shown (the finding's cycle is worth seeing in
   every view, not only under All). ft-show-year is retained but no longer gates it. */
.cv-ft .ft-year-col{display:table-cell;}
/* Hideable columns on the findings register and the remediation queue. The table names what it
   hides (data-hide="dept cycle"), every header and cell carries its column (cv-c-dept), and this
   one rule hides the pair — no cell is dropped, so the sort indexes, the sort keys and the panel
   colspans all stay positional. One selector per column key; a key with no selector here would
   silently never hide, so scripts/test_hideable_columns.py pins this list against cvColDefs.
   After the ft-year-col rule above on purpose: same specificity class, later wins. */
.cv-ft[data-hide~="num"] .cv-c-num,
.cv-ft[data-hide~="sev"] .cv-c-sev,
.cv-ft[data-hide~="severity"] .cv-c-severity,
.cv-ft[data-hide~="dept"] .cv-c-dept,
.cv-ft[data-hide~="rc"] .cv-c-rc,
.cv-ft[data-hide~="type"] .cv-c-type,
.cv-ft[data-hide~="audit"] .cv-c-audit,
.cv-ft[data-hide~="status"] .cv-c-status,
.cv-ft[data-hide~="cycle"] .cv-c-cycle,
.cv-ft[data-hide~="priority"] .cv-c-priority,
.cv-ft[data-hide~="date"] .cv-c-date{display:none;}
.cv-ft td{padding:12px;vertical-align:middle;font-size:14px;color:#f5f5f5;}
.cv-ft tr:last-child td{border-bottom:none;}
/* The register's style at card density. The overview's Audit Findings card IS .cv-ft -- same
   header band, same badges, same hover, same deliberate absence of a rule between rows -- but it
   renders in roughly 550px against the register's 1250, so the register's 12px/14px metrics make
   nine of eleven titles wrap and push the card past 750px tall. Only the density changes here;
   every visual decision is inherited, so the two cannot drift apart. */
.cv-ft-tight td{padding:8px 10px;font-size:13px;}
.cv-ft-tight th{padding:6px 10px;}

/* ── THE OVERVIEW'S TWO SUMMARY CARDS (2026-09-23) ────────────────────────────
   Audit Findings and Housekeeping are the register's table rendered in about a third of the
   register's width. They had been given the register's columns as well, so a title that reads on
   one line there wrapped to three here and the columns beside it did the squeezing. They now name
   the item and (for a finding) how bad it is; everything that qualifies it -- department, type,
   root cause, status, target -- is a pill in the row the item opens. */

/* The title is clamped by its CELL. table-layout is fixed on both cards, so the ellipsis lands at
   whatever width the column actually gets rather than at a character count guessed for one screen.
   The full title is on the cell's title attribute and is the heading of the open row, so nothing
   the clamp trims is out of reach. min-width:0 on BOTH the flex line and the text: a flex item's
   default min-width is auto, which is the text's own width, and without this the cell simply grows
   and the ellipsis never appears. */
.cv-ovt { font-weight: 500; }
.cv-ovt-w { display: flex; align-items: center; gap: 5px; min-width: 0; }
.cv-ovt-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.cv-ovt-w > span:not(.cv-ovt-t) { flex: 0 0 auto; }

/* The open row's heading: the whole title, unclamped. */
.cv-ovft { font-size: 13.5px; font-weight: 600; color: #f5f5f5; line-height: 1.4; margin-bottom: 8px; }

/* The pills. Every value IS a badge now and brings its own fill, edge and ink (2026-09-23), so the
   wrapper draws nothing at all -- it exists to carry the field name as a hover title and to be the
   flex item. A bordered chip around a bordered chip reads as a rendering fault. */
.cv-ovps { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.cv-ovp { display: inline-flex; align-items: center; }

/* The chips for the three fields that had no badge of their own: department, root cause and the
   target date. ftStatusTag's geometry, because that is the pill they sit beside.
   #cv-body-prefixed, and NOT optional: "#cv-body div, #cv-body span, #cv-body td, #cv-body th
   { color: inherit }" is (1,0,1) and outranks a bare class, so an unprefixed .cv-ovc-dept would
   set a background and silently lose the ink. Each tone's light value is written directly beside
   its dark one -- the app's older badges are inline-styled and depend on the light theme having
   been told about each hex, which is how a colour it does not recognise stays dark-theme ink on a
   white card. */
.cv-ovc { font-size: 12px; padding: 3px 9px; border-radius: 10px; font-weight: 600;
          border: 1px solid; white-space: nowrap; }
/* ONE TONE PER FIELD. Type and Status keep their own value colours because their values ARE states
   -- Remediated is not merely a different kind of status, it is the good one. These three are not
   like that, and a stable hue per field is what makes the row scannable: "IT" is always teal.
   Rose and teal are the two hues nothing else in the open row is using; the status pill alone
   already spans grey, green, gold, orange, blue and slate. */
#cv-body .cv-ovc-rc   { background: rgba(158,66,116,.26);  border-color: rgba(230,168,200,.42); color: #e6a8c8; }
#cv-body .cv-ovc-dept { background: rgba(38,122,116,.26);  border-color: rgba(106,197,190,.45); color: #7fcdc7; }
#cv-body .cv-ovc-date { background: rgba(120,132,156,.22); border-color: rgba(174,182,198,.4);  color: #bcc2ce; }
/* Light: the same hue at a fraction of the alpha, with the ink darkened to carry the contrast
   rather than the fill. */
#cv-app.cv-cards-light #cv-body .cv-ovc-rc,
#cv-app.cv-page-light  #cv-body .cv-ovc-rc   { background: rgba(158,66,116,.11);  border-color: rgba(158,66,116,.38); color: #8e2f5e; }
#cv-app.cv-cards-light #cv-body .cv-ovc-dept,
#cv-app.cv-page-light  #cv-body .cv-ovc-dept { background: rgba(20,110,104,.12);  border-color: rgba(20,110,104,.4);  color: #0d5f5a; }
#cv-app.cv-cards-light #cv-body .cv-ovc-date,
#cv-app.cv-page-light  #cv-body .cv-ovc-date { background: rgba(70,80,96,.09);    border-color: rgba(70,80,96,.32);   color: #414a59; }

/* The report's own words, three lines at a time. The dashboard cannot reword a finding -- what it
   shows has to be what the report says -- so what it does instead is show less of it at once.
   line-clamp needs the -webkit- box; the off state has to put display back to block or the
   unclamped text keeps the box's line-breaking. */
.cv-ovclamp { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.cv-ovclamp.cv-ovclamp-off { display: block; -webkit-line-clamp: none; overflow: visible; }
.cv-ovclamp p { margin: 0 0 6px; }
.cv-ovclamp p:last-child { margin-bottom: 0; }
.cv-ovmore { background: none; border: 0; padding: 0; margin-top: 4px; font-family: var(--font);
             font-size: 12px; font-weight: 600; color: #8fbce8; cursor: pointer; }
.cv-ovmore:hover { text-decoration: underline; }

/* Light. #cv-body-prefixed where the colour is concerned, because
   "#cv-body div, #cv-body span, #cv-body td, #cv-body th { color: inherit }" sits at (1,0,1) and
   outranks a bare class -- the trap that has caught this stylesheet before. */
#cv-app.cv-cards-light #cv-body .cv-ovft,
#cv-app.cv-page-light #cv-body .cv-ovft { color: var(--card-text); }
#cv-app.cv-cards-light .cv-ovmore,
#cv-app.cv-page-light .cv-ovmore { color: #1c5a9e; }

/* The Open status pill is a white wash at 10%, which on a light card is white on white: no fill,
   no edge, one bare word where every other status is a pill. The light theme already repaints its
   INK (the color:#c4c4c4 family below) and never touched the face. ftStatusTag and cvHkBadge are
   now the same style, so they share the fix too -- which is the point of making them match. */
#cv-app.cv-cards-light .cv-card [style*="background:rgba(255,255,255,.10)"],
#cv-app.cv-page-light .cv-page [style*="background:rgba(255,255,255,.10)"] {
  background: rgba(0,0,0,.05) !important; border-color: rgba(0,0,0,.18) !important;
}
/* The light theme flips a table header by matching its INLINE background -- see the
   [style*="background:#1e1e1e"] families below. ftHead and rqHead build their headers inline, so
   the register, the housekeeping table and the remediation queue are all caught. .cv-ft th sets
   its background in the STYLESHEET, so a header without an inline one (ftHeadPlain, on the
   overview's Audit Findings card) was never matched: the ink flipped to black and the face stayed
   #222, i.e. black on near-black. Same two tokens the attribute rules resolve to, so it lands on
   exactly the surface its neighbours do. */
#cv-app.cv-cards-light .cv-ft thead tr,
#cv-app.cv-page-light .cv-ft thead tr,
#cv-app.cv-cards-light .cv-ft th,
#cv-app.cv-page-light .cv-ft th {
  background: var(--card-surface-in);
  border-bottom-color: var(--card-border);
  /* The ink too, and it has to be set HERE. The light theme flips a header's ink by matching
     its inline color: string, the same way it flips the face -- so it reaches ftHead's headers
     and not ftHeadPlain's. Without this the dark rule below (1,1,1) simply carried into light
     mode and left a grey label on a near-white face at 1.6:1. This selector is (1,2,1), so it
     wins, and --card-text is exactly what the attribute rules resolve to. */
  color: var(--card-text);
}
/* And the dark-mode ink, #cv-body-prefixed for the usual reason: "#cv-body td, #cv-body th
   { color: inherit }" is (1,0,1) and outranks .cv-ft th at (0,1,1), so a header with no inline
   colour inherited its card's #f5f5f5 instead of the muted label grey its neighbours use. */
#cv-body .cv-ft th { color: #c8c8c8; background: #1e1e1e; }
/* The header ROW takes the same colour as its cells. They used to disagree on the findings
   register alone -- an inline #222 on the <tr> under #1e1e1e cells -- which is covered at 100%
   zoom and leaves a hairline of the wrong shade at fractional zoom or on a HiDPI display. One
   colour for the whole band means no seam can appear, on any table, at any zoom. */
#cv-body .cv-ft thead tr { background: #1e1e1e; }
/* The face too, for the same reason: .cv-ft th's own #222 is a shade off the #1e1e1e that
   ftHead and rqHead set inline, so the card's header sat a step lighter than the register's
   in dark mode. Inline styles outrank this, so the tables that set their own are untouched. */
.cv-ft tr:hover td{background:rgba(45,107,82,.08);cursor:pointer;}
.cv-year-btn{padding:3px 12px;border-radius:4px;font-size:12px;font-weight:600;cursor:pointer;border:1px solid #404040;background:transparent;color:#a8a8a8;transition:all .12s;font-family:var(--font);}
.cv-year-btn:hover{border-color:#606060;color:#d0d0d0;}
/* Interim (quarter) periods in the switcher: dashed border to distinguish from annual years. */
.cv-interim-btn{border-style:dashed;}
.cv-interim-btn.active-filter{border-style:solid;}

/* ── Card classes (tagged at render time by cvTagCards in dashboard.js) ────────
   .cv-card = outer card panel · .cv-card-inner = nested box/track. Theming keys off
   these classes, so it is independent of the per-page card background shade. */

/* Uniform card top-border accent — when on, every OUTER card gets --card-accent,
   overriding its own default accent. */
#cv-app.cv-accent-all .cv-card {
  border-top: 3px solid var(--card-accent) !important;
}
/* #68 — no accent at all. The stripe is repainted to the card's own border color rather than
   removed, so the card keeps its geometry (no 2px jump as you toggle it) and it stays correct in
   both themes. Light gets its own value because --card-border is the light token. */
#cv-app.cv-accent-off .cv-card { border-top-color: #404040 !important; }
#cv-app.cv-accent-off.cv-cards-light .cv-card,
#cv-app.cv-accent-off.cv-page-light .cv-card { border-top-color: var(--card-border) !important; }

/* ── Light card theme ─────────────────────────────────────────────────────────
   When #cv-app has .cv-cards-light, card surfaces flip to a light background, neutral
   text inside cards flips dark, dark inner tracks flip light-gray, and low-contrast
   semantic greens/blues/golds are darkened for readability on white. Semantic reds,
   dark greens/golds and all chart fills are left untouched. Chrome (banner/nav/filters)
   carries no .cv-card, so it stays dark. Per-card top/left colored accents are kept
   (only the right/bottom frame is lightened). */
/* Light on light, with BORDERS doing the separating rather than a gray canvas and a drop shadow.
   The earlier version set the page to #eef1f5 and floated white cards on it with a shadow — which
   is the dark theme's logic (cards lifted off a darker field) translated literally, and translated
   literally it reads as heavy. On a light surface the card edge is the thing that defines a card,
   so the canvas goes near-white and the border earns its keep. */
/* The sandbox palette, adopted wholesale: a cool blue-tinted neutral rather than white-on-white.
   The earlier version put a white card on a near-white page and asked the border to carry the
   whole separation — which is why it kept needing a darker, then thicker, then darker border.
   Giving the card its own tinted surface does that work instead, so the border can go back to a
   hairline. The tint is biased toward the brand blue, so the neutral reads as chosen rather than
   defaulted. Primary text stays true black; muted steps down to a slate that still clears AA. */
#cv-app.cv-cards-light {
  --card-text:   #000000;
  --card-muted:  #4d5966;      /* a real hierarchy step below the primary, not washed out.
                                  Darker than it looks it needs to be on a white card: the
                                  dim labels also sit on the tinted inner strips, where the
                                  earlier #5b6874 measured 4.37:1. */
  --card-border: #e3e9f0;      /* a hairline: the surface difference does the separating */
  --card-surface:    #ffffff;
  --card-surface-in: #f7f9fb;  /* inner boxes sit a shade DOWN from the card they are inside */
}
/* The page is the tinted ground and the cards are white on top of it — the sandbox's
   relationship, and the first version of this had it backwards: it took the sandbox's colors
   but made the CARD the tinted one, which is why it never read like the sandbox despite using
   the same hexes. A card is a raised panel, so it is the lighter of the two. */
#cv-app.cv-page-light { --page-bg: #eef2f7; }
/* The brand VARIABLES, which the [style*="color:#hex"] rules below can never reach.
   They are the reason the light theme read as broken rather than merely imperfect: the rules
   only match literal hex in a style attribute, and the most prominent text in the app is written
   as color:var(--brand-accent) (card titles, ~60 uses) or color:var(--accent) (score figures and
   status). Those kept their DARK-theme values on a white card — #A9C9E8 titles at 1.7:1 and
   #6abf9a figures at 2.2:1, where AA wants 4.5.

   Accent maps to the brand's own deep tone rather than to a neutral, so a client's dashboard still
   looks like theirs in light mode. The palettes are built deep-primary / light-accent, so this is
   the same brand read the other way up.

   Scoped to .cv-card: the banner and nav are deliberately still dark, and they need the light
   accent to stay legible against it. */
#cv-app.cv-cards-light .cv-card {
  --brand-accent:    var(--brand-primary);
  --brand-secondary: #2563a8;
  --accent:          #1a7a50;   /* #1f8a5c left a "Low" pill at 2.99 on its own tint */
}
/* The same vocabulary for page-level content — the Trends tagline and anything else that sits
   directly on the page rather than inside a card. Custom properties only: the card's surface rule
   is NOT copied here, because a page is not a card and forcing card text/background onto it
   repaints elements that are legitimately dark. */
#cv-app.cv-page-light .cv-page {
  --brand-accent:    var(--brand-primary);
  --brand-secondary: #2563a8;
  --accent:          #1a7a50;
}
/* …except ON the brand color itself. An element with a brand-primary background writes its label
   as var(--brand-accent), so remapping accent to primary painted those labels their own background
   — the active chart-type toggle came out at 1.0:1, invisible. Inside such an element the accent
   goes back to being light, which is what it is for. */
#cv-app.cv-cards-light .cv-card [style*="background:var(--brand-primary)"],
#cv-app.cv-cards-light .cv-card [style*="background:var(--banner)"],
#cv-app.cv-cards-light .cv-card [style*="background: var(--brand-primary)"],
#cv-app.cv-cards-light .cv-card [style*="background: var(--banner)"] {
  --brand-accent: #eaf2fb;
  --accent:       #d8f0e4;   /* active filter buttons label with --accent, not --brand-accent */
}
/* Warm semantics. The old note said reds were "left untouched" deliberately; measured on white
   they run 2.0–2.6:1 where AA wants 4.5, so the severity of a finding was the least readable text
   on the page. These are the print report's palette — the light surface this app already had, with
   colors already chosen for legibility on paper. */
#cv-app.cv-cards-light .cv-card [style*="color:#e87a6e"],
#cv-app.cv-cards-light .cv-card [style*="color:#ef8b80"],
#cv-app.cv-page-light .cv-page [style*="color:#e87a6e"],
#cv-app.cv-page-light .cv-page [style*="color:#ef8b80"],
#cv-app.cv-cards-light .cv-card [style*="color:#f09090"],
#cv-app.cv-page-light .cv-page [style*="color:#f09090"],
#cv-app.cv-cards-light .cv-card [style*="color:#ff8675"],
#cv-app.cv-page-light .cv-page [style*="color:#ff8675"],
#cv-app.cv-page-light .cv-page [style*="color:#e05c5c"],
#cv-app.cv-cards-light .cv-card [style*="color:#e05c5c"] {
  color: #a4322f !important;
}
#cv-app.cv-cards-light .cv-card [style*="color:#e8a060"],
#cv-app.cv-page-light .cv-page [style*="color:#e8a060"],
#cv-app.cv-page-light .cv-page [style*="color:#e0b060"],
#cv-app.cv-cards-light .cv-card [style*="color:#e0b060"] {
  color: #9a5510 !important;
}
#cv-app.cv-cards-light .cv-card [style*="color:#a3c563"],
#cv-app.cv-page-light .cv-page [style*="color:#a3c563"],
#cv-app.cv-page-light .cv-page [style*="color:#b5d17a"],
#cv-app.cv-cards-light .cv-card [style*="color:#b5d17a"] {
  color: #4f6b1a !important;
}
/* Inner PANELS that stayed dark. These were the worst of it away from Overview — a remediation row
   on #262626 kept its dark surface while the text above it had already flipped dark, so the row
   read at 1.03:1: black on near-black. Nested panels do not match the .cv-card rule, only the
   enumerated list, and these values were missing from it. */
#cv-app.cv-cards-light .cv-card [style*="background:#262626"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(38, 38, 38)"],
#cv-app.cv-cards-light .cv-card [style*="background:#2a2a2a"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(42, 42, 42)"],
#cv-app.cv-cards-light .cv-card [style*="background:#2f2f2f"],
#cv-app.cv-cards-light .cv-card [style*="background:#303030"],
#cv-app.cv-cards-light .cv-card [style*="background:#313131"],
#cv-app.cv-cards-light .cv-card [style*="background:#333"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(51, 51, 51)"],
#cv-app.cv-cards-light .cv-card [style*="background:#353535"] {
  background: var(--card-surface-in) !important;
  /* The three sides, never the shorthand. `border-color` sets border-top-color too, and with
     !important it beat even the inline style a card declares its accent in -- so any hand-built
     card caught by one of these background selectors lost its top stripe on the way into light
     mode and came back a neutral hairline. That is what stripped the Help cards, and the report
     cards lost theirs to the same shorthand a few rules down. These selectors are really aimed at
     inner PANELS, which have no top accent to keep, so naming three sides costs them nothing. */
  border-right-color: var(--card-border) !important;
  border-bottom-color: var(--card-border) !important;
  border-left-color: var(--card-border) !important;
}
/* Stragglers the earlier lists missed, found by sweeping every page rather than Overview alone:
   two "+ Add" buttons, the "Remediation underway" badge, a secondary-button gray, and the pale
   gray the Trends figures inherit. */
#cv-app.cv-cards-light .cv-card [style*="color:#d8d8d8"] { color: var(--card-text) !important; }
#cv-app.cv-cards-light .cv-card [style*="color:#c3d0e2"],
#cv-app.cv-page-light .cv-page [style*="color:#c3d0e2"],
#cv-app.cv-cards-light .cv-card [style*="color:#9aa4b2"],
#cv-app.cv-cards-light .cv-card [style*="color:#a6afbb"] { color: #000000 !important; }
#cv-app.cv-cards-light .cv-card [style*="color:#9fe3c0"] { color: #1f6b4a !important; }
#cv-app.cv-cards-light .cv-card [style*="color:#e0c072"] { color: #7a5c10 !important; }
/* Card chrome that never flipped: the ⋮ menu kept a dark button on a white card (1.07:1), and the
   drag grip is class-styled, so no inline-hex rule could ever have reached it. */
#cv-app.cv-cards-light .cv-card .cv-cc-h {
  background: var(--card-surface-in) !important;
  border-color: var(--card-border) !important;
  color: #000000 !important;
}
/* ── Page-level chrome: the same treatment cards get, for content that belongs to no card ─────
   Found by sweeping whole pages rather than .cv-card descendants. Every earlier pass selected
   cards — the same set the CSS covered — so it could only ever confirm itself. These are the
   elements that sit directly on the page: filter buttons, the Trends tagline, framework labels. */

/* Dark inline backgrounds outside a card — e.g. the Overview "Set as client default" button, which
   kept a #232323 face and, once page text went dark, showed dark on dark at 1.07:1. */
#cv-app.cv-page-light .cv-page [style*="background:#232323"],
#cv-app.cv-page-light .cv-page [style*="background:#242424"],
#cv-app.cv-page-light .cv-page [style*="background:#2a2a2a"],
#cv-app.cv-page-light .cv-page [style*="background:#1e1e1e"],
#cv-app.cv-page-light .cv-page [style*="background:#1a1a1a"] {
  background: var(--card-surface-in) !important;
  /* The three sides, never the shorthand. `border-color` sets border-top-color too, and with
     !important it beat even the inline style a card declares its accent in -- so any hand-built
     card caught by one of these background selectors lost its top stripe on the way into light
     mode and came back a neutral hairline. That is what stripped the Help cards, and the report
     cards lost theirs to the same shorthand a few rules down. These selectors are really aimed at
     inner PANELS, which have no top accent to keep, so naming three sides costs them nothing. */
  border-right-color: var(--card-border) !important;
  border-bottom-color: var(--card-border) !important;
  border-left-color: var(--card-border) !important;
}
/* ON the brand color, the accent goes back to being light — the card rule's page-level twin.
   Without it an ACTIVE filter button (brand-primary face, accent label) paints its label its own
   background: the selected year on Remediation came out at 1.0:1, and Trends' selected audit
   type at 1.3:1. */
#cv-app.cv-page-light .cv-page [style*="background:var(--brand-primary)"],
#cv-app.cv-page-light .cv-page [style*="background:var(--banner)"],
#cv-app.cv-page-light .cv-page [style*="background:var(--brand-secondary)"],
#cv-app.cv-page-light .cv-page [style*="background: var(--brand-primary)"],
#cv-app.cv-page-light .cv-page [style*="background: var(--banner)"],
#cv-app.cv-page-light .cv-page [style*="background: var(--brand-secondary)"] {
  --brand-accent: #eaf2fb;
  --accent:       #d8f0e4;
  /* The literal `color:` half of this exception lives at the END of the light block — see the
     note there. Setting it here loses to the neutral-text mappings that come after it. */
}
/* Framework page labels, tuned for a dark panel. */
#cv-app.cv-page-light .cv-page [style*="color:#9fc4e8"],
#cv-app.cv-cards-light .cv-card [style*="color:#9fc4e8"] { color: #1c5a9e !important; }
#cv-app.cv-page-light .cv-page [style*="color:#9fb0c4"],
#cv-app.cv-cards-light .cv-card [style*="color:#9fb0c4"] { color: #000000 !important; }
#cv-app.cv-page-light .cv-page [style*="color:#2f9e73"],
#cv-app.cv-page-light .cv-page [style*="color:#5cb894"],
#cv-app.cv-cards-light .cv-card [style*="color:#2f9e73"],
#cv-app.cv-cards-light .cv-card [style*="color:#5cb894"] { color: #1a7a50 !important; }



/* Faint on purpose, but WCAG 1.4.11 still wants 3:1 for a UI affordance — #b6bcc4 was 1.9. */
#cv-app.cv-cards-light .cv-card .cv-card-grip {
  color: #8b929b !important;
}
#cv-app.cv-cards-light .cv-card {
  /* Anything that states no color of its own INHERITS #e0e0e0 from #cv-body, which no
     [style*="color:…"] rule can reach because there is no style attribute to match. On a white
     card that is near-invisible — the Trends figures were 1.3:1. Setting it on the card fixes
     every such element at once; descendants carrying their own inline color are unaffected. */
  color: var(--card-text) !important;
  background: var(--card-surface) !important;
  /* Left as well as right and bottom, so the frame closes. Half a frame only reads as a card when
     a shadow is finishing the job, and the shadow is gone. border-TOP is deliberately untouched:
     it carries the card's colored accent, which is how card types are told apart, and a shorthand
     here would flatten all four sides including that one. */
  border-left-color:   var(--card-border) !important;
  border-right-color:  var(--card-border) !important;
  border-bottom-color: var(--card-border) !important;
  /* 2px, not 1px. With no shadow and near-identical surfaces the border is the only thing
     defining a card, and past ~2.1:1 more darkness starts reading as a frame rather than an
     edge — weight gets presence without that. border-TOP keeps its colored accent width. */
  border-left-width:   2px !important;
  border-right-width:  2px !important;
  border-bottom-width: 2px !important;
  /* No shadow. Lifting a white card off a white page casts a smudge, not a highlight. */
  box-shadow: none !important;
}
#cv-app.cv-cards-light .cv-card-inner {
  background: var(--card-surface-in) !important;
  border-right-color:  var(--card-border) !important;
  border-bottom-color: var(--card-border) !important;
}
/* dark inner backgrounds (bar tracks, sub-boxes) that aren't tagged → light-gray */
#cv-app.cv-cards-light .cv-card [style*="background:#242424"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(36, 36, 36)"],
#cv-app.cv-cards-light .cv-card [style*="background:#1e1e1e"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(30, 30, 30)"],
#cv-app.cv-cards-light .cv-card [style*="background:#1a1a1a"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(26, 26, 26)"],
#cv-app.cv-cards-light .cv-card [style*="background:#222"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(34, 34, 34)"],
#cv-app.cv-cards-light .cv-card [style*="background:#383838"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(56, 56, 56)"],
#cv-app.cv-cards-light .cv-card [style*="background:#363636"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(54, 54, 54)"],
#cv-app.cv-cards-light .cv-card [style*="background:#1d1d1d"],
#cv-app.cv-cards-light .cv-card [style*="background:rgb(29, 29, 29)"] {
  background: var(--card-surface-in) !important;
}
/* neutral primary text → dark */
#cv-app.cv-cards-light .cv-card [style*="color:#ffffff"],
#cv-app.cv-page-light .cv-page [style*="color:#ffffff"],
#cv-app.cv-cards-light .cv-card [style*="color:#fff"],
#cv-app.cv-page-light .cv-page [style*="color:#fff"],
#cv-app.cv-cards-light .cv-card [style*="color:#f5f5f5"],
#cv-app.cv-page-light .cv-page [style*="color:#f5f5f5"],
#cv-app.cv-cards-light .cv-card [style*="color:#f2f2f2"],
#cv-app.cv-page-light .cv-page [style*="color:#f2f2f2"],
#cv-app.cv-cards-light .cv-card [style*="color:#f0f0f0"],
#cv-app.cv-page-light .cv-page [style*="color:#f0f0f0"],
#cv-app.cv-cards-light .cv-card [style*="color:#ededed"],
#cv-app.cv-page-light .cv-page [style*="color:#ededed"],
#cv-app.cv-cards-light .cv-card [style*="color:#e8e8e8"],
#cv-app.cv-page-light .cv-page [style*="color:#e8e8e8"],
#cv-app.cv-cards-light .cv-card [style*="color:#e0e0e0"],
#cv-app.cv-page-light .cv-page [style*="color:#e0e0e0"],
/* Panel body text: the finding narrative, the framework leaf descriptions and the remediation
   notes. They only render with a row open, which is why they sat below AA on the light card long
   after the rest of the theme was clean. Nothing special about them -- they belong in this run and
   were missing from it. */
#cv-app.cv-cards-light .cv-card [style*="color:#d8d8d8"],
#cv-app.cv-page-light .cv-page [style*="color:#d8d8d8"],
#cv-app.cv-cards-light .cv-card [style*="color:#c4c4c4"],
#cv-app.cv-page-light .cv-page [style*="color:#c4c4c4"],
#cv-app.cv-cards-light .cv-card [style*="color:#b9b9b9"],
#cv-app.cv-page-light .cv-page [style*="color:#b9b9b9"],
#cv-app.cv-cards-light .cv-card [style*="color:#dcdcdc"],
#cv-app.cv-page-light .cv-page [style*="color:#dcdcdc"],
#cv-app.cv-cards-light .cv-card [style*="color:#d0d0d0"],
#cv-app.cv-page-light .cv-page [style*="color:#d0d0d0"],
#cv-app.cv-cards-light .cv-card [style*="color:#cfcfcf"],
#cv-app.cv-page-light .cv-page [style*="color:#cfcfcf"],
#cv-app.cv-cards-light .cv-card [style*="color:#c8c8c8"],
#cv-app.cv-page-light .cv-page [style*="color:#c8c8c8"],
#cv-app.cv-cards-light .cv-card [style*="color:#c0c0c0"],
#cv-app.cv-page-light .cv-page [style*="color:#c0c0c0"],
#cv-app.cv-page-light .cv-page [style*="color:#b8b8b8"],
#cv-app.cv-cards-light .cv-card [style*="color:#b8b8b8"] {
  color: var(--card-text) !important;
}
/* neutral secondary text → muted gray */
#cv-app.cv-cards-light .cv-card [style*="color:#b0b0b0"],
#cv-app.cv-page-light .cv-page [style*="color:#b0b0b0"],
#cv-app.cv-cards-light .cv-card [style*="color:#a0a0a0"],
#cv-app.cv-page-light .cv-page [style*="color:#a0a0a0"],
#cv-app.cv-cards-light .cv-card [style*="color:#9a9a9a"],
#cv-app.cv-page-light .cv-page [style*="color:#9a9a9a"],
#cv-app.cv-cards-light .cv-card [style*="color:#6a6a6a"],
#cv-app.cv-cards-light .cv-card [style*="color:#949494"],
#cv-app.cv-page-light .cv-page [style*="color:#6a6a6a"],
#cv-app.cv-page-light .cv-page [style*="color:#949494"],
#cv-app.cv-cards-light .cv-card [style*="color:#909090"],
#cv-app.cv-cards-light .cv-card [style*="color:#a3a3a3"],
#cv-app.cv-page-light .cv-page [style*="color:#909090"],
#cv-app.cv-page-light .cv-page [style*="color:#a3a3a3"],
#cv-app.cv-cards-light .cv-card [style*="color:#8c8c8c"],
#cv-app.cv-page-light .cv-page [style*="color:#8c8c8c"],
#cv-app.cv-cards-light .cv-card [style*="color:#8897a6"],
#cv-app.cv-page-light .cv-page [style*="color:#8897a6"],
#cv-app.cv-cards-light .cv-card [style*="color:#7a8a9a"],
#cv-app.cv-page-light .cv-page [style*="color:#7a8a9a"],
#cv-app.cv-cards-light .cv-card [style*="color:#8a8a8a"],
/* The findings rank cell. It left var(--brand-secondary) to survive the open
   row's own blue tint in dark mode, and a bare literal has no light mapping
   until it is given one. */
#cv-app.cv-cards-light .cv-card [style*="color:#a8a8a8"],
#cv-app.cv-page-light .cv-page [style*="color:#8a8a8a"],
#cv-app.cv-page-light .cv-page [style*="color:#a8a8a8"],
#cv-app.cv-cards-light .cv-card [style*="color:#888"],
#cv-app.cv-page-light .cv-page [style*="color:#888"],
#cv-app.cv-cards-light .cv-card [style*="color:#808080"],
#cv-app.cv-cards-light .cv-card [style*="color:#8f8f8f"],
#cv-app.cv-page-light .cv-page [style*="color:#808080"],
#cv-app.cv-page-light .cv-page [style*="color:#8f8f8f"],
#cv-app.cv-cards-light .cv-card [style*="color:#777"],
#cv-app.cv-page-light .cv-page [style*="color:#777"],
#cv-app.cv-cards-light .cv-card [style*="color:#666"],
#cv-app.cv-page-light .cv-page [style*="color:#666"],
#cv-app.cv-cards-light .cv-card [style*="color:#606060"],
#cv-app.cv-page-light .cv-page [style*="color:#606060"],
#cv-app.cv-cards-light .cv-card [style*="color:#aeb6c6"],
#cv-app.cv-cards-light .cv-card [style*="color:#bcc2ce"],
#cv-app.cv-page-light .cv-page [style*="color:#aeb6c6"],
#cv-app.cv-page-light .cv-page [style*="color:#bcc2ce"],
#cv-app.cv-cards-light .cv-card [style*="color:#aab4c2"],
#cv-app.cv-page-light .cv-page [style*="color:#aab4c2"],
#cv-app.cv-cards-light .cv-card [style*="color:#c2ccd8"],
#cv-app.cv-page-light .cv-page [style*="color:#c2ccd8"],
#cv-app.cv-page-light .cv-page [style*="color:#7f93a8"],
#cv-app.cv-cards-light .cv-card [style*="color:#7f93a8"] {
  color: var(--card-muted) !important;
}
/* low-contrast semantic accents darkened for white */
#cv-app.cv-cards-light .cv-card [style*="color:#6abf9a"],
#cv-app.cv-page-light .cv-page [style*="color:#6abf9a"],
#cv-app.cv-page-light .cv-page [style*="color:#7ad0aa"],
#cv-app.cv-cards-light .cv-card [style*="color:#7ad0aa"] {
  color: #1f8a5c !important;
}
/* #8fbce8 belongs here, not with the muted grays: it is the rank cell and the Details button,
   and de-emphasised gray would make an action read as disabled. */
#cv-app.cv-cards-light .cv-card [style*="color:#8fbce8"],
#cv-app.cv-page-light .cv-page [style*="color:#8fbce8"],
#cv-app.cv-cards-light .cv-card [style*="color:#7ab0e8"],
#cv-app.cv-page-light .cv-page [style*="color:#7ab0e8"],
#cv-app.cv-cards-light .cv-card [style*="color:#90c0f0"],
#cv-app.cv-page-light .cv-page [style*="color:#90c0f0"],
#cv-app.cv-page-light .cv-page [style*="color:#5b9bd5"],
#cv-app.cv-cards-light .cv-card [style*="color:#5b9bd5"] {
  color: #2563a8 !important;
}
#cv-app.cv-cards-light .cv-card [style*="color:#c8a84b"],
#cv-app.cv-cards-light .cv-card [style*="color:#cdae57"],
#cv-app.cv-page-light .cv-page [style*="color:#c8a84b"],
#cv-app.cv-page-light .cv-page [style*="color:#cdae57"],
#cv-app.cv-cards-light .cv-card [style*="color:#c8b070"],
#cv-app.cv-page-light .cv-page [style*="color:#c8b070"],
#cv-app.cv-cards-light .cv-card [style*="color:#d4b060"],
#cv-app.cv-page-light .cv-page [style*="color:#d4b060"],
#cv-app.cv-page-light .cv-page [style*="color:#c8a050"],
#cv-app.cv-cards-light .cv-card [style*="color:#c8a050"] {
  color: #8a6a16 !important;
}



/* Global card text boost: +2pt and brighter colors */
#cv-body { font-size: 13px; color: #f5f5f5; }
#cv-body div, #cv-body span, #cv-body td, #cv-body th { color: inherit; }

/* ── MOBILE / RESPONSIVE (core pass) ──────────────────────────────────────────
   Desktop-first dashboard uses inline grid layouts; these stack the Overview
   grids and reshape the 3-section header on small screens. Inline
   grid-template-columns (and applyPeriodView's inline set) need !important. */
@media (max-width: 768px) {
  #cv-header { flex-wrap: wrap; min-height: 0; }
  #cv-header > div { min-width: 0 !important; border-left: none !important; border-right: none !important; }
  #cv-header > div:nth-child(2) { flex: 1 1 100% !important; padding-top: 2px !important; }
  #cv-row1, #cv-row3 { grid-template-columns: 1fr !important; }
  #kpi-row > div { grid-template-columns: 1fr !important; }
  .rc-2col { grid-template-columns: 1fr !important; }
  /* The layout editor relocates managed cards into #cv-grid-* rows whose inline
     grid-template-columns (repeat(N,…)) bypasses the #kpi-row stack rule above.
     Stack those rows too so narrow cards can't shrink until their toolbars overflow
     onto the neighbor (which steals the neighbor's clicks). */
  #cv-grid-overview .cv-grid-row,
  #cv-grid-trends .cv-grid-row,
  #cv-grid-insights .cv-grid-row { grid-template-columns: 1fr !important; }
}
/* Mid-width (e.g. tablet, or desktop with the sidebar expanded): 2-up instead of the
   editor's 4-up so the breakdown cards keep their chart toolbars inside the card. */
@media (min-width: 769px) and (max-width: 1024px) {
  #cv-grid-overview .cv-grid-row,
  #cv-grid-trends .cv-grid-row,
  #cv-grid-insights .cv-grid-row { grid-template-columns: 1fr 1fr !important; }
}
@media (max-width: 480px) {
  #cv-header > div { padding: 9px 14px !important; }
  #cv-body { padding-left: 10px !important; padding-right: 10px !important; }
}

/* Scroll-hide (legacy, header only): collapse the banner on scroll-down. The nav moved to a
   left sidebar, so #cv-navbar is intentionally excluded — it must not get a clipping max-height. */
#cv-header {
  transition: max-height .25s ease, min-height .25s ease, opacity .2s ease,
              padding .25s ease, border-width .25s ease;
  max-height: 200px;
}
#cv-app.cv-chrome-hidden #cv-header {
  max-height: 0 !important;
  min-height: 0 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  border-bottom-width: 0 !important;
  opacity: 0;
  overflow: hidden;
}

/* ── LEFT SIDEBAR NAV (collapsible) ───────────────────────────────────────────
   The primary nav moved from a top bar to a left sidebar. #cv-shell is a flex row
   [sidebar | content]; #cv-content is a flex column [filters | edit-banner | body]. */
#cv-shell { flex: 1; display: flex; overflow: hidden; min-height: 0; }
#cv-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
#cv-sidebar {
  width: 214px; flex-shrink: 0; background: #1e1e2e; border-right: 1px solid #2e2e40;
  display: flex; flex-direction: column; overflow-y: auto; overflow-x: hidden;
  transition: width .18s ease;
}
#cv-app.cv-side-collapsed #cv-sidebar { width: 58px; }
#cv-navbar { display: flex; flex-direction: column; padding: 4px 0; max-height: none; }
.cv-side-toggle {
  background: transparent; border: none; color: rgba(255,255,255,.55); cursor: pointer;
  font-size: 17px; padding: 13px 19px; text-align: left; line-height: 1; flex-shrink: 0;
}
.cv-side-toggle:hover { color: #fff; }
/* Vertical nav tabs — override the horizontal .cv-tab geometry */
#cv-sidebar .cv-tab {
  height: auto; width: 100%; padding: 10px 19px; justify-content: flex-start;
  border-bottom: none; border-left: 3px solid transparent; font-size: 13px; gap: 11px;
}
#cv-sidebar .cv-tab:hover { background: rgba(255,255,255,.06); color: rgba(255,255,255,.9); }
#cv-sidebar .cv-tab.active {
  border-bottom: none; border-left-color: #cdae57; background: rgba(255,255,255,.07); color: #fff;
}
.cv-nav-ico { width: 20px; text-align: center; flex-shrink: 0; font-size: 15px; }
.cv-nav-lbl { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-side-fill { flex: 1; min-height: 8px; }
#cv-side-actions { padding: 6px 0 8px; border-top: 1px solid #2e2e40; display: flex; flex-direction: column; gap: 1px; }
.cv-side-btn {
  display: flex; align-items: center; gap: 11px; width: 100%; padding: 9px 19px;
  background: transparent; border: none; border-left: 3px solid transparent; color: rgba(255,255,255,.72);
  font-size: 13px; font-weight: 500; cursor: pointer; font-family: var(--font); text-decoration: none; text-align: left;
}
.cv-side-btn:hover { background: rgba(255,255,255,.06); color: #fff; }
.cv-side-btn-green { color: #7ad0aa; }
/* Collapsed = icon rail: hide labels, center the icons */
#cv-app.cv-side-collapsed .cv-nav-lbl { display: none; }

/* ── Cycle switcher, at the top of the menu ────────────────────────────────────────────────
   Above Overview, because selecting a cycle LOADS that cycle's engagement -- it is navigation.
   Three previous homes: the filter sub-bar (hidden on Requests, Documents, Resources, Findings
   and several other pages, so most of the dashboard could not change cycle at all); the sidebar
   as a stacked list of every cycle, which fixed the reach and cost a permanent block of menu
   height; and the header, one line, correct but quiet. This is the third form in the second's
   place -- a picker, one line, where the reader is already looking for how to move around.
   Bordered rather than flat: it is the only control in this rail that opens something, and it
   has to read as a control rather than as another tab. */
/* Themed through variables rather than a light-mode override of each property. The rail is dark
   in one theme and near-white in the other, so every colour here has two values; declaring each
   one ONCE and swapping what it resolves to is both shorter and immune to the specificity
   arm-wrestling that a second full rule invites. */
#cv-cycle-nav {
  position: relative; flex-shrink: 0; padding: 4px 10px 9px;
  --cyc-bg: rgba(255,255,255,.06);
  --cyc-bg-hi: rgba(255,255,255,.12);
  --cyc-bd: rgba(255,255,255,.16);
  --cyc-bd-hi: rgba(255,255,255,.3);
  --cyc-ink: #fff;
  --cyc-cap: rgba(255,255,255,.55);
}
#cv-app.cv-page-light #cv-cycle-nav {
  --cyc-bg: rgba(0,0,0,.045);
  --cyc-bg-hi: rgba(0,0,0,.08);
  --cyc-bd: rgba(0,0,0,.16);
  --cyc-bd-hi: rgba(0,0,0,.32);
  --cyc-ink: #14202d;
  --cyc-cap: #5c6674;
}
#cv-cycle-btn {
  display: flex; flex-direction: column; gap: 2px; width: 100%;
  padding: 7px 11px; border-radius: 6px;
  border: 1px solid var(--cyc-bd); background: var(--cyc-bg);
  color: var(--cyc-ink); font-family: inherit; cursor: pointer;
  text-align: left; line-height: 1.1; transition: background .12s, border-color .12s;
}
#cv-cycle-btn:hover, #cv-cycle-btn[aria-expanded="true"] {
  background: var(--cyc-bg-hi); border-color: var(--cyc-bd-hi);
}
.cv-cycle-cap {
  font-size: 8.5px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--cyc-cap);
}
.cv-cycle-now {
  display: flex; align-items: baseline; justify-content: space-between; gap: 6px;
  font-family: var(--font); font-variant-numeric: tabular-nums; font-size: 15px; font-weight: 600; color: var(--cyc-ink);
}
#cv-cycle-now-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-cycle-caret { font-size: 9px; color: var(--cyc-cap); flex-shrink: 0; }
/* position:fixed, placed by cvCycleMenu — the sidebar is overflow-y:auto / overflow-x:hidden, so
   an absolutely-positioned menu is clipped by its own container. Same reason as the client
   switcher and the Manage menu. */
#cv-cycle-menu {
  display: none; position: fixed; z-index: 4000;
  min-width: 194px; max-height: 60vh; overflow-y: auto;
  background: #1e1e2e; border: 1px solid #2e2e40;
  border-radius: 6px; box-shadow: 0 8px 24px rgba(0,0,0,.5); padding: 4px;
}
#cv-cycle-menu.cv-open { display: block; }
#cv-cycle-menu .cv-year-btn {
  display: block; width: 100%; text-align: left; padding: 7px 12px; border-radius: 4px;
  background: transparent; border: 1px solid transparent; color: #d0d0d0;
  font-size: 13px; font-family: inherit; cursor: pointer; white-space: nowrap;
}
#cv-cycle-menu .cv-year-btn:hover { background: #2a2a3e; color: #fff; }
#cv-cycle-menu .cv-year-btn.active-filter {
  background: #2a2a3e; color: #c8a84b; font-weight: 700; border-color: transparent;
  box-shadow: none;
}
#cv-cycle-menu .cv-interim-btn { font-style: italic; }
.cv-cycle-draft {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .07em;
  color: #d9b45a; margin-left: 3px;
}
/* Collapsed to the icon rail: the caption goes and the year stands alone, centred, still the full
   width of the rail -- changing cycle never needs the menu expanded first. */
/* IN THE HEADER (2026-09-16). The picker sits beside the client's name, on the brand band, so
   it keeps the white-on-dark variables in BOTH themes -- the band is dark in both. Horizontal
   and compact; the menu still drops from wherever the button is. */
#cv-header #cv-cycle-nav {
  display: flex; align-items: center; padding: 0 16px; border-right: 1px solid rgba(255,255,255,0.12);
  --cyc-bg: rgba(255,255,255,.08); --cyc-bg-hi: rgba(255,255,255,.16);
  --cyc-bd: rgba(255,255,255,.22); --cyc-bd-hi: rgba(255,255,255,.4);
  --cyc-ink: #fff; --cyc-cap: rgba(255,255,255,.62);
}
#cv-app.cv-page-light #cv-header #cv-cycle-nav {
  --cyc-bg: rgba(255,255,255,.08); --cyc-bg-hi: rgba(255,255,255,.16);
  --cyc-bd: rgba(255,255,255,.22); --cyc-bd-hi: rgba(255,255,255,.4);
  --cyc-ink: #fff; --cyc-cap: rgba(255,255,255,.62);
}
#cv-header #cv-cycle-btn { width: auto; min-width: 156px; padding: 5px 12px; }
#cv-header .cv-cycle-now { font-size: 14px; }
@media (max-width: 720px) { #cv-header #cv-cycle-nav { padding: 0 8px; } #cv-header #cv-cycle-btn { min-width: 0; } }
/* (The collapsed-rail rules for the picker went with its move to the header: a collapsed sidebar
   no longer holds it, and their higher specificity would have overridden the header padding.) */
/* Light mode is handled by the --cyc-* variables at the top of this block, not by a second copy
   of every rule. The menu itself keeps its dark surface in both themes, as the client switcher
   and the Manage menu do. */

/* ── Scope headings in the menu ────────────────────────────────────────────────────────────
   The menu is split in two: THIS AUDIT, which the cycle picker governs, and ONGOING, which it
   does not. Changing cycle used to change four of nine items and leave five untouched while the
   picker sat above all of them looking like it governed everything; the heading says where its
   authority ends, so nobody has to discover it by clicking.
   Quiet by design -- a rule with a word on it, not a title. It is orientation, not content, and
   it must not compete with the tabs underneath it. */
.cv-navhead {
  display: flex; align-items: center; gap: 8px;
  padding: 13px 12px 5px;
  font: 700 9.5px/1 var(--font);
  letter-spacing: .15em; text-transform: uppercase;
  color: rgba(255,255,255,.38);
  user-select: none;
}
.cv-navhead::after {
  content: ""; flex: 1; height: 1px; background: currentColor; opacity: .3;
}
#cv-app.cv-page-light .cv-navhead { color: rgba(0,0,0,.42); }
/* Collapsed to the icon rail there is no room for the word, and a heading with no text is just a
   divider -- which is still the useful half, so keep the rule and drop the label. */
#cv-app.cv-side-collapsed .cv-navhead { padding: 10px 10px 6px; }
#cv-app.cv-side-collapsed .cv-navhead::after { opacity: .22; }

/* ── Merged nav tabs ───────────────────────────────────────────────────────────────────────
   Requests + Documents are one "Documents" tab; Controls + Framework Alignment are one
   "Coverage" tab. The four originals stay in the DOM -- they hold each cycle's gate, they are
   what showPage lights, and the tour, the Help guide and several in-page links reach for them by
   id -- but they are never shown. !important because cvApplyCycle writes an inline display onto
   them as it re-gates each cycle; that inline value is the RECORD, not an instruction to paint. */
#cv-navbar .cv-tab.cv-merged { display: none !important; }
/* ── The dashboard switcher and the split (2026-09-14) ────────────────────────────────
   body[data-dash] names the dashboard on screen; every sidebar item with a data-dash that does not
   list it is hidden. Items with no data-dash (Team Access, Help, Manage) belong to all three. The
   inline display:none the template writes for gated tabs still wins -- these rules only ever HIDE. */
#cv-dash-switch { display: flex; flex-direction: column; gap: 2px; padding: 6px 8px 4px; }
#cv-dash-switch button { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; padding: 7px 10px; border-radius: 6px;
  border: 1px solid transparent; background: transparent; color: rgba(255,255,255,.62); font: 600 12.5px/1.2 var(--font); cursor: pointer; letter-spacing: .01em; }
#cv-dash-switch button:hover { background: rgba(255,255,255,.06); color: #fff; }
#cv-dash-switch button.active { background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.14); color: #fff; }
#cv-dash-switch button .cv-nav-ico { width: 16px; text-align: center; font-size: 12px; opacity: .85; }
#cv-dash-switch button[hidden] { display: none; }
#cv-sidebar.collapsed #cv-dash-switch button { justify-content: center; padding: 7px 0; }
#cv-sidebar.collapsed #cv-dash-switch .cv-nav-lbl { display: none; }
body[data-dash="current"]     #cv-sidebar [data-dash]:not([data-dash~="current"])     { display: none !important; }
body[data-dash="past"]        #cv-sidebar [data-dash]:not([data-dash~="past"])        { display: none !important; }
body[data-dash="overview"]    #cv-sidebar [data-dash]:not([data-dash~="overview"])    { display: none !important; }
body[data-dash="remediation"] #cv-sidebar [data-dash]:not([data-dash~="remediation"]) { display: none !important; }
/* Remediation leads its own dashboard; the DOM keeps Findings first for the Overview's sake. */
body[data-dash="remediation"] #nav-remediation { order: -1; }
/* Status leads Current Audit whatever a saved navOrder says: it is that dashboard's front page,
   and a layout saved before the page existed cannot have placed it. */
body[data-dash="current"] #nav-status { order: -1; }
body[data-dash="current"] #cv-dash-head { display: none !important; }
/* Past Audits names the audit in its own banner, so the sidebar heading would say it twice. */
body[data-dash="past"] #cv-dash-head { display: none !important; }
/* Past Audits keeps the SAME sidebar order as Current Audit (2026-09-16, the user: "the sub nav
   menu should be ordered the same across both menus"). Exit Meeting used to jump to the top here;
   it still LANDS first on a finished audit (cvDashDefaultPage), but the list reads the same. */
body[data-dash="past"] #nav-status { order: -1; }

/* ── The Past Audits banner ─────────────────────────────── */
/* A THIN strip, not a banner (2026-09-15, the user: it took up too much space). It still has to
   say which audit is on screen -- a read-only page that looks live is worse than a lost line --
   so the height comes off the padding and the wording, not the fact. */
#cv-past-bar {
  display: none; align-items: center; gap: 10px; flex-wrap: wrap; flex-shrink: 0;
  padding: 3px 22px; background: rgba(200,168,75,.10); border-bottom: 1px solid rgba(200,168,75,.30);
  font-size: 11.5px; line-height: 1.6; color: #e0cf9a;
}
body[data-dash="past"] #cv-past-bar { display: flex; }
#cv-past-bar b { color: #f5e7bd; font-weight: 700; }
#cv-past-bar button {
  margin-left: auto; font: 600 11.5px/1.5 var(--font); cursor: pointer;
  padding: 1px 9px; border-radius: 4px; border: 1px solid rgba(200,168,75,.45);
  background: transparent; color: #e0cf9a;
}
#cv-past-bar button:hover { border-color: #c8a84b; color: #f5e7bd; }
#cv-app.cv-page-light #cv-past-bar { background: rgba(154,101,22,.10); border-bottom-color: rgba(154,101,22,.32); color: #6d4a08; }
#cv-app.cv-page-light #cv-past-bar b { color: #4a3205; }
#cv-app.cv-page-light #cv-past-bar button { border-color: rgba(154,101,22,.45); color: #6d4a08; }

/* ── "Opened from … · Back" (2026-09-16) ──────────────────── */
/* The past bar's twin in the accent blue: one thin line under the banner that says which
   dashboard a cross-over came from and takes the reader back to it. Hidden until a cross-over. */
#cv-came-from {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex-shrink: 0;
  padding: 3px 22px; background: rgba(122,176,232,.10); border-bottom: 1px solid rgba(122,176,232,.30);
  font-size: 11.5px; line-height: 1.6; color: #b9d3ee;
}
#cv-came-from[hidden] { display: none; }
#cv-came-from b { color: #dbe9f8; font-weight: 700; }
#cv-came-from button {
  margin-left: auto; font: 600 11.5px/1.5 var(--font); cursor: pointer;
  padding: 1px 9px; border-radius: 4px; border: 1px solid rgba(122,176,232,.45);
  background: transparent; color: #b9d3ee;
}
#cv-came-from button:hover { border-color: #7ab0e8; color: #dbe9f8; }
#cv-app.cv-page-light #cv-came-from { background: rgba(28,90,158,.08); border-bottom-color: rgba(28,90,158,.30); color: #1c4a80; }
#cv-app.cv-page-light #cv-came-from b { color: #12345e; }
#cv-app.cv-page-light #cv-came-from button { border-color: rgba(28,90,158,.45); color: #1c4a80; }

/* The sub-view switcher that stands in for the two tabs a merged one replaced. A strip above the
   page, in the same segmented-pill idiom as every other toggle in the app. */
#cv-viewswitch {
  display: none; gap: 4px; align-items: center; flex-shrink: 0;
  padding: 9px 22px; background: #242424; border-bottom: 1px solid #404040;
}
.cv-viewswitch-btn {
  padding: 5px 15px; border-radius: 5px; font-size: 12.5px; font-weight: 600;
  font-family: var(--font); cursor: pointer;
  border: 1px solid #404040; background: transparent; color: #a8a8a8;
  transition: border-color .12s, color .12s, box-shadow .12s;
}
.cv-viewswitch-btn:hover { border-color: #606060; color: #d0d0d0; }
.cv-viewswitch-btn.cv-on {
  color: #f5f5f5; border-color: var(--brand-secondary);
  box-shadow: inset 0 0 0 1px var(--brand-secondary);
}
#cv-app.cv-page-light #cv-viewswitch {
  background: var(--page-bg); border-bottom-color: #d9dee6;
}
#cv-app.cv-page-light .cv-viewswitch-btn { color: #4a535d; border-color: #c8cfd9; }
#cv-app.cv-page-light .cv-viewswitch-btn:hover { color: #1f262e; border-color: #a6afbb; }
#cv-app.cv-page-light .cv-viewswitch-btn.cv-on { color: #14202d; border-color: var(--brand-secondary); }

/* ── The Manage menu ───────────────────────────────────────────────────────────────────────
   Seven auditor-only tools -- arrange, publish, preview, provision -- behind one sidebar item.
   Same dark popup surface as the client switcher, which sits two items above it. */
.cv-manage-item {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 7px 12px; border-radius: 4px; border: none; background: transparent;
  color: #d0d0d0; font-size: 13px; font-family: var(--font); text-align: left;
  text-decoration: none; white-space: nowrap; cursor: pointer;
}
.cv-manage-item:hover { background: #2a2a3e; color: #fff; }
.cv-manage-sep { height: 1px; background: #2e2e40; margin: 4px 6px; }
#cv-app.cv-side-collapsed #cv-sidebar .cv-tab,
#cv-app.cv-side-collapsed .cv-side-btn { padding-left: 0; padding-right: 0; justify-content: center; gap: 0; }
#cv-app.cv-side-collapsed .cv-side-toggle { text-align: center; padding-left: 0; padding-right: 0; }
@media (max-width: 768px) {
  #cv-sidebar { width: 58px; }
  #cv-app:not(.cv-side-collapsed) #cv-sidebar { width: 200px; }
}

/* ── PAGE THEME: LIGHT (per-client · model.theme.pageScheme='light') ───────────
   Light mode for the page canvas + left nav + sub-nav (filters bar). The BANNER
   is intentionally left on its client color (--banner) and is not themed here.
   Cards flip via the cv-cards-light block (applyTheme adds it whenever the page
   is light), so a light page has coherently light cards + dark text. */
/* Left sidebar / nav */
/* The nav and the canvas are ONE surface. They were three grays — sidebar #f4f6f9, sub-nav
   #e7ebf1, page #fbfcfd — which banded the window into strips that meant nothing: the shading did
   not mark a boundary anyone cares about, it just made the chrome look bolted on. They now all
   take the page color and are separated by a rule, the same way the cards are. */
#cv-app.cv-page-light #cv-sidebar { background: var(--page-bg); border-right-color: #dde2e9; }
/* .5 measured 3.89:1 on the light sidebar. Alpha is the mechanism here, so the fix is more
   of it rather than a different color. */
#cv-app.cv-page-light .cv-side-toggle { color: rgba(0,0,0,.62); }
#cv-app.cv-page-light .cv-side-toggle:hover { color: #111; }
#cv-app.cv-page-light #cv-sidebar .cv-tab { color: #000000; }
#cv-app.cv-page-light #cv-sidebar .cv-tab:hover { background: rgba(0,0,0,.05); color: #1b2029; }
#cv-app.cv-page-light #cv-sidebar .cv-tab.active { background: rgba(0,0,0,.06); color: #1b2029; border-left-color: #b5892b; }
/* The active tab's LABEL has to be targeted directly. .cv-tab.active carries color:#fff from the
   base (horizontal, dark-banner) nav, and that white survives even an !important on the tab here —
   so the label inherited it and the current page's name came out white on near-white, at 1.03:1.
   The label is the text anyone actually reads, so setting it there is both sufficient and honest. */
#cv-app.cv-page-light #cv-sidebar .cv-tab .cv-nav-lbl { color: #000000 !important; }
#cv-app.cv-page-light #cv-sidebar .cv-tab.active .cv-nav-lbl { color: #1b2029 !important; }
/* Sub-nav labels ("Period:", "Audit:") carry an inline #b0b0b0 tuned for a dark bar — 2.1:1 here. */
#cv-app.cv-page-light #cv-subnav [style*="color:#b0b0b0"],
#cv-app.cv-page-light #cv-subnav [style*="color:#8a8a8a"],
#cv-app.cv-page-light #cv-subnav [style*="color:#a8a8a8"],
#cv-app.cv-page-light #cv-subnav [style*="color:#909090"],
#cv-app.cv-page-light #cv-subnav [style*="color:#a3a3a3"] { color: #000000 !important; }
#cv-app.cv-page-light #cv-side-actions { border-top-color: #d9dee6; }
#cv-app.cv-page-light .cv-side-btn { color: #4a5462; }
#cv-app.cv-page-light .cv-side-btn:hover { background: rgba(0,0,0,.05); color: #111; }
#cv-app.cv-page-light .cv-side-btn-green { color: #1f7a52; }
/* Sub-nav (year switcher + audit/type filters) — inline #242424, so override hard */
#cv-app.cv-page-light #cv-subnav { background: var(--page-bg) !important; border-bottom-color: #d9dee6 !important; color: #000000; }
#cv-app.cv-page-light #cv-subnav .cv-year-btn,
#cv-app.cv-page-light #cv-subnav .cv-audit-filter,
#cv-app.cv-page-light #cv-subnav .cv-filter { color: #4a535d; border-color: #c8cfd9; background: transparent; }
#cv-app.cv-page-light #cv-subnav .cv-year-btn:hover,
#cv-app.cv-page-light #cv-subnav .cv-audit-filter:hover,
#cv-app.cv-page-light #cv-subnav .cv-filter:hover { color: #1f262e; border-color: #a6afbb; }
/* Selected = the same border highlight the rest of the app uses, in the same colour. The green
   fill this replaced was the only place a chosen filter changed its own ink. */
#cv-app.cv-page-light #cv-subnav .cv-year-btn.active-filter,
#cv-app.cv-page-light #cv-subnav .cv-audit-filter.active-filter,
#cv-app.cv-page-light #cv-subnav .cv-filter.active-filter { background: transparent; color: #14202d; border-color: var(--brand-secondary); box-shadow: inset 0 0 0 1px var(--brand-secondary); }


/* ── One pill system ───────────────────────────────────────────────────
   Every toggle in the app - filter pills, chart-option glyphs, segmented view switches - renders
   through .cv-pill / .cv-gpill / .cv-seg. Selection is a BORDER highlight, never a fill: a fill
   forces the ink to change with the ground, which is what left some pills reading dark-on-colour
   and others light-on-transparent, some blue and some green. Hover is now the same everywhere.
   --cv-pa carries an option's own hue (severity, type) for the selected border; without it the
   brand secondary is used, so one pill can never be a different colour from its neighbour by
   accident. Ink is the same three tiers the rest of the dashboard uses, in both themes. */
.cv-pill,.cv-gpill{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;border:1px solid #404040;background:transparent;color:#a8a8a8;border-radius:4px;font-family:var(--font);font-size:12px;font-weight:600;line-height:1.5;cursor:pointer;transition:border-color .12s,color .12s,box-shadow .12s;}
.cv-pill{padding:4px 10px;}
.cv-gpill{padding:1px 7px;}
.cv-pill:hover,.cv-gpill:hover{border-color:#606060;color:#d0d0d0;}
.cv-pill:focus-visible,.cv-gpill:focus-visible{outline:2px solid var(--brand-secondary);outline-offset:1px;}
.cv-pill.cv-on,.cv-gpill.cv-on{background:transparent;color:#f5f5f5;border-color:var(--cv-pa,var(--brand-secondary));box-shadow:inset 0 0 0 1px var(--cv-pa,var(--brand-secondary));}
.cv-pill.cv-on:hover,.cv-gpill.cv-on:hover{color:#ffffff;border-color:var(--cv-pa,var(--brand-secondary));}
.cv-pill-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;flex-shrink:0;}
/* Segmented switch - one shell, hairline dividers, the selected tab underlined rather than filled. */
.cv-seg{display:inline-flex;border:1px solid #404040;border-radius:6px;overflow:hidden;}
.cv-seg .cv-pill{border:0;border-radius:0;box-shadow:none;padding:5px 12px;}
.cv-seg .cv-pill+.cv-pill{border-left:1px solid #404040;}
.cv-seg .cv-pill:hover{color:#d0d0d0;}
.cv-seg .cv-pill.cv-on{box-shadow:inset 0 -2px 0 var(--brand-secondary);color:#f5f5f5;}
#cv-app.cv-cards-light .cv-pill,#cv-app.cv-page-light .cv-pill,
#cv-app.cv-cards-light .cv-gpill,#cv-app.cv-page-light .cv-gpill{border-color:#c8cfd9;color:#4a535d;}
#cv-app.cv-cards-light .cv-pill:hover,#cv-app.cv-page-light .cv-pill:hover,
#cv-app.cv-cards-light .cv-gpill:hover,#cv-app.cv-page-light .cv-gpill:hover{border-color:#a6afbb;color:#1f262e;}
/* The selected border has to be restated here: the light rest-state rule above carries an id and
   would otherwise repaint a chosen pill's accent border back to the neutral light hairline. */
#cv-app.cv-cards-light .cv-pill.cv-on,#cv-app.cv-page-light .cv-pill.cv-on,
#cv-app.cv-cards-light .cv-gpill.cv-on,#cv-app.cv-page-light .cv-gpill.cv-on{color:#14202d;border-color:var(--cv-pa,var(--brand-secondary));box-shadow:inset 0 0 0 1px var(--cv-pa,var(--brand-secondary));}
#cv-app.cv-cards-light .cv-seg .cv-pill.cv-on,#cv-app.cv-page-light .cv-seg .cv-pill.cv-on{border-color:transparent;box-shadow:inset 0 -2px 0 var(--brand-secondary);}
#cv-app.cv-cards-light .cv-seg,#cv-app.cv-page-light .cv-seg{border-color:#c8cfd9;}
#cv-app.cv-cards-light .cv-seg .cv-pill+.cv-pill,#cv-app.cv-page-light .cv-seg .cv-pill+.cv-pill{border-left-color:#c8cfd9;}
/* The sub-nav's own filter pills answer to the same system - they used to fill green (.cv-filter)
   and blue (.cv-year-btn) when selected, which is where "some blue, some green" came from. */
.cv-filter,.cv-audit-filter,.cv-year-btn{padding:4px 12px;border-radius:4px;font-size:12px;font-weight:600;cursor:pointer;border:1px solid #404040;background:transparent;color:#a8a8a8;transition:border-color .12s,color .12s,box-shadow .12s;font-family:var(--font);}
.cv-filter:hover,.cv-audit-filter:hover,.cv-year-btn:hover{border-color:#606060;color:#d0d0d0;}
.cv-filter.active-filter,.cv-audit-filter.active-filter,.cv-year-btn.active-filter{background:transparent;color:#f5f5f5;border-color:var(--brand-secondary);box-shadow:inset 0 0 0 1px var(--brand-secondary);}

/* Chart-options corner control: hide while arranging the layout (its spot collides with the editor remove button). */
#cv-body.gs-edit .cv-cc { display: none; }

/* ── Text ON a brand-colored face — LAST in the file, deliberately ──────────────────────────
   A button with a brand background writes color:#fff inline, and the neutral-text mapping turns
   any inline white dark. Both rules match with identical specificity, so the one later in the
   file wins — and the mappings are spread through the light block, so "later" only reliably means
   the end. Placed anywhere earlier this lost, and Overview's Executive/Detailed toggle showed
   dark text on its own blue pill at 2.4:1. */
/* Text ON a brand-colored face, placed LAST on purpose.
   A button with a brand background writes color:#fff inline, and the neutral-text mapping above
   turns any inline #fff dark — both rules match with identical specificity, so whichever comes
   later wins. Earlier in the block this lost, and the Overview Executive/Detailed toggle showed
   dark text on its own blue pill at 2.4:1. The variables are set with the background rule above;
   only the literal color has to be here. */
#cv-app.cv-page-light .cv-page [style*="background:var(--brand-primary)"],
#cv-app.cv-page-light .cv-page [style*="background: var(--brand-primary)"],
#cv-app.cv-page-light .cv-page [style*="background:var(--banner)"],
#cv-app.cv-page-light .cv-page [style*="background: var(--banner)"],
#cv-app.cv-page-light .cv-page [style*="background:var(--brand-secondary)"],
#cv-app.cv-page-light .cv-page [style*="background: var(--brand-secondary)"],
#cv-app.cv-cards-light .cv-card [style*="background:var(--brand-secondary)"],
#cv-app.cv-cards-light .cv-card [style*="background: var(--brand-secondary)"] {
  color: #eaf2fb !important;
}

/* ── SVG chart text ──────────────────────────────────────────────────────────────────────────
   The donut's center labels are <text fill="#f5f5f5">, and fill is a PRESENTATION ATTRIBUTE, not
   a style declaration — so no [style*="color:…"] rule could ever reach them, and SVG text ignores
   `color` anyway. In light mode the total and its TOTAL caption were near-white on a white card:
   invisible, exactly as reported.

   Mapped by fill value rather than blanket `svg text`, because one series label is drawn in its
   own data color (fill="' + c + '") and must keep it. */
#cv-app.cv-cards-light .cv-card svg text[fill="#f5f5f5"],
#cv-app.cv-page-light  .cv-page svg text[fill="#f5f5f5"] { fill: var(--card-text) !important; }
#cv-app.cv-cards-light .cv-card svg text[fill="#a3a3a3"],
#cv-app.cv-page-light  .cv-page svg text[fill="#a3a3a3"],
#cv-app.cv-cards-light .cv-card svg text[fill="#808080"],
#cv-app.cv-page-light  .cv-page svg text[fill="#808080"] { fill: var(--card-muted) !important; }
#cv-app.cv-cards-light .cv-card svg text[fill="#6abf9a"],
#cv-app.cv-page-light  .cv-page svg text[fill="#6abf9a"] { fill: #1a7a50 !important; }

/* ── Form controls: one border, everywhere ───────────────────────────────────────────────────
   Two idioms had grown up side by side — .cv-field (8px radius, a translucent white border) and
   search boxes styled inline (5px radius, #363636). In dark they merely differed; on a light
   surface a translucent-white border disappears entirely, so the search boxes looked like a
   different control from the fields beside them.
   One weight, one color, one radius — matched to the card border so a control reads as part of
   the same drawing. Card frames are 2px because they enclose; controls stay 1px. */
#cv-app.cv-page-light input:not([type=checkbox]):not([type=radio]),
#cv-app.cv-page-light select,
#cv-app.cv-page-light textarea,
#cv-app.cv-cards-light .cv-card input:not([type=checkbox]):not([type=radio]),
#cv-app.cv-cards-light .cv-card select,
#cv-app.cv-cards-light .cv-card textarea {
  border: 1px solid var(--card-border) !important;
  border-radius: 6px !important;
  background: #ffffff !important;
  color: #000000 !important;
  box-shadow: none !important;
}
#cv-app.cv-page-light input::placeholder,
#cv-app.cv-page-light textarea::placeholder { color: #5c6670 !important; }
#cv-app.cv-page-light input:focus,
#cv-app.cv-page-light select:focus,
#cv-app.cv-page-light textarea:focus {
  border-color: var(--brand-primary) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-primary) 18%, transparent) !important;
}

/* ── Remediation queue: rows that expand in place ───────────────────────────
   Structure and motion only. Every color in these rows is written inline by
   dashboard.js, which is what the light-theme mappings above key off — putting
   colors here would bypass them and strand the queue in dark. The one color
   that does appear is a neutral translucent gray, which reads correctly on
   either ground. */
/* The queue rows are <tr> now, so these hang off the row itself rather than a child. The old
   rules were written as `> .cv-rem-row`; left as they were they would match nothing at all and
   the queue would silently lose its hover, cursor and open-row highlight. Row separation moved
   to the cells' own border-bottom, since a <tr> border does not paint reliably and the panel row
   sits between every pair anyway. */
.cv-rem-item { cursor: pointer; transition: background .13s cubic-bezier(.4,0,.2,1); }
.cv-rem-item:hover { background: rgba(127,127,127,.13); }
.cv-rem-item:focus-visible { outline: 2px solid var(--brand-secondary); outline-offset: -2px; }
.cv-rem-item.cv-cursor { background: rgba(127,127,127,.10); }
/* The open row is marked by a STRUCTURAL rail, not only a wash. The translucent blue reads on a
   dark ground but all but vanishes over a light card, so the open row lost its marker exactly
   where the rows are palest. The rail is drawn with an inset shadow on the first cell — a <tr>
   does not paint a border reliably — so it costs no layout width and shows on either ground. */
.cv-rem-item.cv-open { background: rgba(91,155,213,.14); }
.cv-rem-item.cv-open > td:first-child { box-shadow: inset 3px 0 0 #5b9bd5; }
#cv-app.cv-cards-light .cv-rem-item.cv-open,
#cv-app.cv-page-light .cv-rem-item.cv-open { background: rgba(28,90,158,.10); }
.cv-rem-panel { height: 0; overflow: hidden; transition: height .24s cubic-bezier(.4,0,.2,1); }
@media (prefers-reduced-motion: reduce) {
  .cv-rem-item, .cv-rem-panel, .cv-fnd-item, .cv-hk-item { transition: none; }
}

/* ── Findings register: table rows that expand in place ─────────────────────
   Same motion as the remediation queue (.cv-rem-panel above), applied to a row
   that spans the table. Structure only — colors stay inline so the light-theme
   mappings still reach them. */
.cv-fnd-item, .cv-hk-item { transition: background .13s cubic-bezier(.4,0,.2,1); }
.cv-fnd-item:hover, .cv-hk-item:hover { background: rgba(127,127,127,.10); }
.cv-fnd-item:focus-visible,
.cv-hk-item:focus-visible { outline: 2px solid var(--brand-secondary); outline-offset: -2px; }
.cv-fnd-item.cv-open, .cv-hk-item.cv-open { background: rgba(91,155,213,.13); }
/* The panel row must not inherit the table's cell padding or borders, or a collapsed
   panel still occupies a visible strip between rows. */
.cv-ft tr.cv-fnd-prow > td,
.cv-ft tr.cv-hk-prow > td,
/* The queue tables are .cv-rq, deliberately not .cv-ft: that class carries a hard-coded #222
   header background and a green row hover from the old palette, neither of which the light theme
   remaps for a table that does not override them inline. */
.cv-rq tr.cv-rem-prow > td { padding: 0 !important; border: 0 !important; }

/* Sortable remediation headers carry no colour change — matching the findings register, whose
   sorted/hover green is overridden by its own inline colour, so its headers stay light. The sort
   is shown by the arrow alone. Cursor is set inline. */

/* ── Headings take a serif ──────────────────────────────────────────────────
   The sandbox pairs a serif for headings against the sans body, and that contrast is most of
   why it reads as considered rather than uniform. Card and section titles are written inline by
   dashboard.js — there is no class to hang this on — so they are matched by attribute, which is
   the same mechanism the light-theme color mappings above already rely on. The pairing is
   deliberately narrow: titles only, so the data stays in the sans face where digits align and
   dense rows stay legible. */
/* Pinned to the card-title signature (14px + 700 + brand accent). Matching on weight+color
   alone also caught the score figures — "90", "/100", "156" — which are data, not headings, and
   belong in the sans face where digits align. */
#cv-app [style*="font-size:14px"][style*="font-weight:700"][style*="color:var(--brand-accent)"],
#cv-app [style*="font-size:14px"][style*="font-weight:700"][style*="color: var(--brand-accent)"] {
  font-family: var(--font); font-variant-numeric: tabular-nums; font-weight: 600;
  letter-spacing: .005em;
}
/* The banner's client name is the largest heading on the page and should lead the pairing. */
#cv-app .cv-banner-title, #cv-app #header-client-name
  { font-family: var(--font); font-variant-numeric: tabular-nums; font-weight: 600; }

/* ── Light mode: pills, badges and tinted panels ────────────────────────────
   These carry a translucent fill and a LIGHT text color, which is correct over a dark card and
   wrong over a white one: the fill composites to a pale tint and the light text vanishes into it.
   Measured with alpha compositing (the un-composited rgba lies about this), it was the single
   biggest source of sub-AA text in the theme.

   Keyed on the HUE TRIPLE rather than the exact rgba, so one rule covers every alpha the app
   uses for that family — the source has ~40 distinct rgba values across ~12 hues, and matching
   them individually would break the first time an alpha changed.

   Each family becomes: pale fill, dark ink of the same hue, mid-tone border. */
#cv-app.cv-cards-light [style*="rgba(45,100,180,"],
#cv-app.cv-cards-light [style*="rgba(var(--brand-secondary-rgb),"] {
  background: rgba(37,99,168,.11) !important; color: #1c4a80 !important; border-color: rgba(37,99,168,.38) !important;
}
#cv-app.cv-cards-light [style*="rgba(45,107,82,"],
#cv-app.cv-cards-light [style*="rgba(106,191,154,"] {
  background: rgba(26,122,80,.12) !important; color: #14603d !important; border-color: rgba(26,122,80,.38) !important;
}
#cv-app.cv-cards-light [style*="rgba(139,105,20,"],
#cv-app.cv-cards-light [style*="rgba(200,168,75,"],
/* 150,115,20 is the MEDIUM severity badge, which moved onto amber when the severity ramp was
   converged — Medium used to be rgba(192,57,43,…), a red, and was picked up by the red rule below.
   Without this line it keeps its dark-card tint on a light card and the pale ink goes with it. */
#cv-app.cv-cards-light [style*="rgba(150,115,20,"] {
  background: rgba(139,105,20,.14) !important; color: #6b4d12 !important; border-color: rgba(139,105,20,.42) !important;
}
/* The Recurring/Ongoing type pill, moved off amber to violet so it stops reading as a severity now
   that Medium owns amber. New hue, so it needs its own light-card rule or it inherits nothing. */
#cv-app.cv-cards-light [style*="rgba(92,82,170,"] {
  background: rgba(92,82,170,.13) !important; color: #443b96 !important; border-color: rgba(92,82,170,.4) !important;
}
#cv-app.cv-cards-light [style*="rgba(192,57,43,"] {
  background: rgba(164,50,47,.12) !important; color: #8c2028 !important; border-color: rgba(164,50,47,.38) !important;
}
#cv-app.cv-cards-light [style*="rgba(150,28,28,"] {
  background: rgba(140,32,40,.13) !important; color: #7d1b22 !important; border-color: rgba(140,32,40,.42) !important;
}
#cv-app.cv-cards-light [style*="rgba(200,100,30,"],
#cv-app.cv-cards-light [style*="rgba(200,120,40,"],
/* 232,160,96 arrived as the trends heatmap's "High" band and outlived it. This is an attribute
   selector, so it catches everything carrying that rgba string — High-severity badges, the retest
   tag, the 'b-h' chart fill — all of which would keep a dark-theme tint on a light card without
   it. The companion rule for the heatmap's "Moderate" band (150,190,120) went with the heatmap;
   nothing else ever used that hue. */
#cv-app.cv-cards-light [style*="rgba(232,160,96,"] {
  background: rgba(154,101,22,.14) !important; color: #8a4b0a !important; border-color: rgba(154,101,22,.4) !important;
}
#cv-app.cv-cards-light [style*="rgba(150,160,175,"],
#cv-app.cv-cards-light [style*="rgba(120,132,156,"] {
  background: rgba(91,104,116,.13) !important; color: #3f4c58 !important; border-color: rgba(91,104,116,.38) !important;
}
/* White overlays are how the dark theme lifts a surface. On white they lift nothing, so they
   invert to a faint dark wash — and the text on them goes to the card ink rather than staying a
   pale gray chosen to sit on #2a2a2a. */
#cv-app.cv-cards-light [style*="rgba(255,255,255,.0"],
#cv-app.cv-cards-light [style*="rgba(255,255,255,0.0"],
#cv-app.cv-cards-light [style*="rgba(255,255,255,.1"] {
  background: rgba(20,32,45,.05) !important; color: var(--card-text) !important; border-color: rgba(20,32,45,.14) !important;
}
/* Inline dark surfaces the token mappings never reach — panels written straight into the markup
   as #2a2a2a / #262626 / #242424 rather than through --card-surface. */
#cv-app.cv-cards-light [style*="background:#2a2a2a"],
#cv-app.cv-cards-light [style*="background:#262626"],
#cv-app.cv-cards-light [style*="background:#242424"],
#cv-app.cv-cards-light [style*="background:#1e1e1e"],
#cv-app.cv-cards-light [style*="background:#1f1f1f"] {
  background: var(--card-surface-in) !important; color: var(--card-text) !important;
}

/* The layout editor reads and writes inline styles through el.style, and the browser normalizes
   hex to rgb() on the way back out — so a card it has touched carries `background: rgb(42, 42, 42)`
   and the hex selectors above sail straight past it. Same surfaces, second spelling. */
#cv-app.cv-cards-light [style*="background: rgb(42, 42, 42)"],
#cv-app.cv-cards-light [style*="background: rgb(38, 38, 38)"],
#cv-app.cv-cards-light [style*="background: rgb(36, 36, 36)"],
#cv-app.cv-cards-light [style*="background: rgb(30, 30, 30)"],
#cv-app.cv-cards-light [style*="background: rgb(31, 31, 31)"] {
  background: var(--card-surface) !important; color: var(--card-text) !important;
}
/* Near-misses: these cleared 3:1 but not the 4.5:1 that small text needs, so the ink goes a
   step darker rather than the fill going paler — a paler fill would stop reading as a pill. */
#cv-app.cv-cards-light [style*="rgba(139,105,20,"],
#cv-app.cv-cards-light [style*="rgba(200,168,75,"] { color: #574009 !important; }
#cv-app.cv-cards-light [style*="rgba(45,107,82,"],
#cv-app.cv-cards-light [style*="rgba(106,191,154,"] { color: #0f5334 !important; }
/* The drag grip measured 3.14:1 on white — it is a character, not an icon, so it takes the
   small-text bar like everything else. */
#cv-app.cv-cards-light .cv-card-grip { color: #4a5663 !important; }

/* The three families above kept their old ink because the existing per-color mappings are
   written as `#cv-app.cv-cards-light .cv-card [style*="color:#hex"]` — one class more specific
   than the family rules, so they won at the cascade regardless of order. Matched depth here. */
#cv-app.cv-cards-light .cv-card [style*="rgba(139,105,20,"],
#cv-app.cv-cards-light .cv-card [style*="rgba(200,168,75,"],
#cv-app.cv-cards-light .cv-card [style*="color:#c8a84b"],
#cv-app.cv-cards-light .cv-card [style*="color:#cdae57"],
#cv-app.cv-cards-light .cv-card [style*="color:#c8b070"] { color: #574009 !important; }
#cv-app.cv-cards-light .cv-card [style*="rgba(45,107,82,"],
#cv-app.cv-cards-light .cv-card [style*="rgba(106,191,154,"],
#cv-app.cv-cards-light .cv-card [style*="color:#6abf9a"],
#cv-app.cv-cards-light .cv-card [style*="color:#7fd0ab"] { color: #0f5334 !important; }
#cv-app.cv-cards-light .cv-card .cv-card-grip,
#cv-app.cv-cards-light [data-wid] > .cv-card-grip { color: #4a5663 !important; }

/* The last six sit outside .cv-card — page-level chrome rather than card content — so they need
   the same green at page depth. 4.33:1 is a near-miss, not a wrong color, but the bar is 4.5. */
#cv-app.cv-page-light .cv-page [style*="color:#6abf9a"],
#cv-app.cv-page-light .cv-page [style*="color:#7fd0ab"],
#cv-app.cv-page-light .cv-page [style*="rgba(45,107,82,"],
#cv-app.cv-page-light .cv-page [style*="rgba(106,191,154,"] { color: #0f5334 !important; }

/* Rich text in light mode. .rich-content sets strong/b to #f5f5f5 and u to #a8d5c2 for the dark
   drawer; on a white card those are invisible and near-invisible respectively. The emphasis has
   to be carried by the card's own ink instead — bold is emphasis, so it goes DARKER than the
   surrounding prose rather than lighter. */
#cv-app.cv-cards-light .rich-content strong,
#cv-app.cv-cards-light .rich-content b,
#cv-app.cv-page-light .rich-content strong,
#cv-app.cv-page-light .rich-content b { color: var(--card-text) !important; font-weight: 700; }
#cv-app.cv-cards-light .rich-content u,
#cv-app.cv-page-light .rich-content u { color: #0f5334 !important; }

/* Settled rows open onto their closure record. */
.cv-closed-row { transition: background .13s cubic-bezier(.4,0,.2,1); }
.cv-closed-row:hover { background: rgba(127,127,127,.10); }
.cv-closed-row:focus-visible { outline: 2px solid var(--brand-secondary); outline-offset: -2px; }
.cv-closed-row.cv-open { background: rgba(91,155,213,.13); }
@media (prefers-reduced-motion: reduce) { .cv-closed-row { transition: none; } }

/* ── Attribute chips ──────────────────────────────────────────────────────────
   Department, finding type, audit type and root cause. Deliberately NOT filled: a filled
   chip is what a reader takes for a status, and these sit inches from the severity and
   status pills, which own that language. The color lives in a hairline border and the ink,
   so the family reads as quieter no matter how many values a client has.

   The slot is assigned by hashing the VALUE, because departments are per-client data — there
   is no fixed list to map. Same value, same color, everywhere it appears.

   Color is carried by CLASS rather than inline style, unlike the older pills. The inline
   rgba-triple selectors those need exist because dashboard.js writes their color inline;
   nothing forces that here, and a class means the light theme is one override instead of a
   dozen attribute matches. Worst measured contrast across every surface these land on:
   5.78:1 dark, and better in light.

   The #cv-body prefix is NOT decoration. "#cv-body div, #cv-body span, ... { color: inherit }"
   sits above this at (1,0,1), which outranks a bare class — the reason every other colored
   thing in the app resorts to an inline style. The id lifts these to (1,1,0) so they win.
   ────────────────────────────────────────────────────────────────────────── */
#cv-body .cv-attr {
  display: inline-block;
  font-size: 12px;
  line-height: 1.45;
  padding: 2px 8px;
  border-radius: 10px;
  border: 1px solid;
  background: none;
  font-weight: 600;
  white-space: nowrap;
}
/* The key ("Department:") is scaffolding, not the value — it steps back so the value reads
   first. It stays the chip ink rather than a gray, because a gray here would be a third
   color inside a chip whose whole point is to be quiet. */
#cv-body .cv-attr > .cv-attr-k { font-weight: 500; opacity: .72; }
#cv-body .cv-attr-0 { border-color: rgba(91,127,166,0.55); color: #a5b9ce; }   /* slate · 6.0:1 */
#cv-body .cv-attr-1 { border-color: rgba(63,140,132,0.55); color: #95c0bb; }   /* teal · 6.1:1 */
#cv-body .cv-attr-2 { border-color: rgba(138,106,158,0.55); color: #bfadca; }   /* plum · 5.8:1 */
#cv-body .cv-attr-3 { border-color: rgba(160,112,90,0.55); color: #cbb0a4; }   /* clay · 5.9:1 */
#cv-body .cv-attr-4 { border-color: rgba(111,133,66,0.55); color: #b0bc97; }   /* olive · 6.0:1 */
#cv-body .cv-attr-5 { border-color: rgba(107,122,140,0.55); color: #aeb6c0; }   /* steel · 5.9:1 */
/* Not a department: Type, Audit, Root cause, Kind. Closed lists of two or three values,
   so color per value would say nothing — and neutral here is what makes it impossible
   for two chips on one row to share a color. */
#cv-body .cv-attr-n { border-color: rgba(150,160,172,0.45); color: #c3c3c3; }   /* 6.9:1 */

#cv-app.cv-cards-light #cv-body .cv-attr-n,
#cv-app.cv-page-light #cv-body .cv-attr-n { border-color: rgba(90,102,116,0.4); color: #4a5560; }   /* 6.8:1 */
#cv-app.cv-cards-light #cv-body .cv-attr,
#cv-app.cv-page-light #cv-body .cv-attr { background: none; }
#cv-app.cv-cards-light #cv-body .cv-attr-0,
#cv-app.cv-page-light #cv-body .cv-attr-0 { border-color: rgba(91,127,166,0.5); color: #3b536c; }   /* 7.1:1 */
#cv-app.cv-cards-light #cv-body .cv-attr-1,
#cv-app.cv-page-light #cv-body .cv-attr-1 { border-color: rgba(63,140,132,0.5); color: #295b56; }   /* 6.8:1 */
#cv-app.cv-cards-light #cv-body .cv-attr-2,
#cv-app.cv-page-light #cv-body .cv-attr-2 { border-color: rgba(138,106,158,0.5); color: #5a4567; }   /* 7.5:1 */
#cv-app.cv-cards-light #cv-body .cv-attr-3,
#cv-app.cv-page-light #cv-body .cv-attr-3 { border-color: rgba(160,112,90,0.5); color: #68493a; }   /* 7.2:1 */
#cv-app.cv-cards-light #cv-body .cv-attr-4,
#cv-app.cv-page-light #cv-body .cv-attr-4 { border-color: rgba(111,133,66,0.5); color: #48562b; }   /* 7.1:1 */
#cv-app.cv-cards-light #cv-body .cv-attr-5,
#cv-app.cv-page-light #cv-body .cv-attr-5 { border-color: rgba(107,122,140,0.5); color: #464f5b; }   /* 7.4:1 */

/* ── Table cards on the remediation page ──────────────────────────
   LAST IN THE FILE ON PURPOSE. These tie with the theme remaps above on specificity -- both are
   one id plus three classes, counting an attribute selector as a class -- so source order is the
   only thing that decides, and sitting earlier meant losing. */
/* ...and the CARD under it must not resolve to that same colour, or the band disappears into it.
   The remediation table cards carry an inline background:#2a2a2a, and the light themes remap that
   family to --card-surface-in -- which is exactly what a .cv-ft header resolves to. Card and
   header both landed on #f7f9fb and the header stopped reading as a header. The findings register
   never hit this because its card has no inline background: .cv-card paints it --card-surface
   (#ffffff), so its #f7f9fb header stands off it. Same surface for these, so the card-to-header
   contrast is identical on both pages.
   The two light themes need different answers, and the findings card is the reference for both.
   Under page-light the cards stay DARK -- the findings card measures #2a2a2a there -- while the
   header goes to #242424; the attribute rule was dragging these cards to #242424 as well.
   Under cards-light the findings card is #ffffff against a #f7f9fb header. page-light is listed
   first so that with BOTH classes on, cards-light wins, which is what the header already does. */
#cv-app.cv-page-light .cv-page [data-rqsec] { background: #2a2a2a !important; }
#cv-app.cv-cards-light .cv-page [data-rqsec] { background: var(--card-surface) !important; }

/* ── The Reports page chooser ────────────────────────────────────────────────
   These cards were styled INLINE from JS, and both faults that produced came from that.

   The light themes repaint by matching inline style strings, so an unselected card carrying
   background:#2a2a2a was remapped to white correctly, while a SELECTED one — which JS set to
   #323232, a value no remap rule has heard of — kept its dark fill and took black text. Black on
   #323232 is the "blacked out" selection. Selection is a class now, and the colour lives here,
   where both themes can answer for it.

   The second fault was height: six descriptive cards came to 647px against a 360px body, so the
   report opened entirely below the fold. .compact collapses them to a single row of pills once a
   report is showing — descriptive while you are choosing, out of the way while you are reading. */
.cv-rpt-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; }
.cv-rpt-card {
  text-align: left; font-family: inherit; cursor: pointer;
  background: #2a2a2a; border: 1px solid #404040; border-top: 3px solid var(--acc, #6abf9a);
  border-radius: 7px; padding: 13px 15px;
}
.cv-rpt-card:hover { border-color: var(--brand-secondary); }
.cv-rpt-card.on { background: #323232; border-color: var(--brand-secondary); }
/* A report the auditor has hidden from this client: still on the auditor's chooser, dimmed and
   labelled, so it can be switched back on from the same place. Clients never receive the card. */
.cv-rpt-card.cv-rpt-hidden { opacity: .5; border-style: dashed; }
.cv-rpt-hidtag { font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: #c8a84b; margin-left: 6px; vertical-align: middle; }
/* #cv-body prefixed, and NOT optional. `#cv-body div, #cv-body span { color: inherit }` is (1,0,1)
   and outranks a bare class at (0,1,0), so `.cv-rpt-title { color: … }` loses and the div inherits
   from the <button>, whose default ink is black — black on #2a2a2a. The light rules below already
   carry #cv-app and outrank it, which is why only DARK broke. Same rule that once turned the
   definition popover brand-blue; see the light-theme notes above. */
#cv-body .cv-rpt-title { font-size: 14px; font-weight: 700; color: #f5f5f5; margin-bottom: 4px; }
#cv-body .cv-rpt-desc { font-size: 12.5px; color: #a8a8a8; line-height: 1.55; }

/* Compact: the chooser becomes a row of pills, and the report gets the page. */
/* One row, always. Wrapping put three rows of pills back above the report at narrow widths,
   which is the problem this mode exists to solve. Overflowing sideways keeps the height
   fixed whatever the width, and the strip scrolls in itself rather than pushing the page. */
.cv-rpt-cards.compact { display: flex; flex-wrap: nowrap; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
.cv-rpt-cards.compact .cv-rpt-card { flex: 0 0 auto; white-space: nowrap; }
.cv-rpt-cards.compact .cv-rpt-card { padding: 6px 12px; border-top-width: 1px; border-radius: 15px; }
.cv-rpt-cards.compact .cv-rpt-desc { display: none; }
#cv-body .cv-rpt-cards.compact .cv-rpt-title { font-size: 12.5px; font-weight: 600; margin-bottom: 0; }
#cv-body .cv-rpt-intro { font-size: 12.5px; color: #a8a8a8; margin-bottom: 12px; }
.cv-rpt-cards.compact ~ .cv-rpt-intro, .cv-rpt-panel.compact .cv-rpt-intro { display: none; }

/* Both light themes. Written here rather than left to the inline-string remaps, because a
   selection state that only exists in JS is exactly what those remaps cannot see. */
/* border-color is a SHORTHAND -- it sets all four sides, border-top-color included -- so this
   line was quietly repainting the 3px accent stripe that identifies each report with the neutral
   hairline used for the other three sides. The stripe simply vanished on the way into light mode.
   Restated explicitly after it, because the shorthand is still wanted for the other three. */
#cv-app.cv-cards-light .cv-rpt-card {
  background: var(--card-surface); border-color: var(--card-border);
  border-top-color: var(--acc, #6abf9a);
}
#cv-app.cv-cards-light .cv-rpt-card.on { background: var(--card-surface-in); border-color: var(--brand-secondary); }
#cv-app.cv-cards-light #cv-body .cv-rpt-title { color: var(--card-text); }
#cv-app.cv-cards-light #cv-body .cv-rpt-desc,
#cv-app.cv-cards-light #cv-body .cv-rpt-intro { color: var(--card-muted); }
#cv-app.cv-page-light .cv-rpt-card { background: #2a2a2a; }
#cv-app.cv-page-light .cv-rpt-card.on { background: #323232; }
#cv-app.cv-page-light.cv-cards-light .cv-rpt-card { background: var(--card-surface); }
#cv-app.cv-page-light.cv-cards-light .cv-rpt-card.on { background: var(--card-surface-in); }

/* The reports scope control. Its own row under the chooser, and it survives the compact collapse —
   changing scope is exactly the thing you want to do WHILE reading a report, so hiding it with the
   descriptions would be the wrong economy. */
.cv-rpt-scopebar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.cv-rpt-scope {
  font: inherit; font-size: 12px; font-weight: 600; padding: 4px 11px; border-radius: 13px;
  border: 1px solid #404040; background: #242424; color: #b0b0b0; cursor: pointer;
}
.cv-rpt-scope.on { border-color: var(--brand-secondary); background: rgba(45,100,180,.18); color: #e8e8e8; }
#cv-body .cv-rpt-scopenote { font-size: 11.5px; color: #8a949c; margin-left: 4px; }
#cv-app.cv-cards-light .cv-rpt-scope { background: var(--card-surface-in); border-color: var(--card-border); color: var(--card-muted); }
#cv-app.cv-cards-light .cv-rpt-scope.on { background: rgba(28,90,158,.10); border-color: var(--brand-secondary); color: var(--card-text); }
#cv-app.cv-cards-light #cv-body .cv-rpt-scopenote { color: var(--card-muted); }

/* Audit Report section toggles. Multi-select, so they are square-ended rather than pills — a pill
   row reads as "pick one", which is what the report cards above them are. */
.cv-rpt-secbar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 10px;
                 padding-top: 10px; border-top: 1px solid #363636; }
#cv-body .cv-rpt-seclbl { font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em;
                          color: #8a949c; font-weight: 700; margin-right: 4px; }
.cv-rpt-sec { font: inherit; font-size: 12px; font-weight: 600; padding: 4px 11px; border-radius: 5px;
              border: 1px solid #404040; background: #242424; color: #9a9a9a; cursor: pointer; }
.cv-rpt-sec.on { border-color: var(--brand-secondary); background: rgba(45,100,180,.18); color: #e8e8e8; }
#cv-app.cv-cards-light .cv-rpt-secbar { border-top-color: var(--card-border); }
#cv-app.cv-cards-light .cv-rpt-sec { background: var(--card-surface-in); border-color: var(--card-border); color: var(--card-muted); }
#cv-app.cv-cards-light .cv-rpt-sec.on { background: rgba(28,90,158,.10); border-color: var(--brand-secondary); color: var(--card-text); }
#cv-app.cv-cards-light #cv-body .cv-rpt-seclbl { color: var(--card-muted); }

/* ── Team panel, embedded in the dashboard ──────────────────────────────────────────────────
   The panel was written for its own page: a light document with dark ink. Injected into the
   dashboard it kept the light CARDS and inherited the dashboard's LIGHT text, so every name sat
   white-on-white — present, correct, unreadable. The same failure the report cards had.

   Scoped `#cv-body #page-team` (2,0,0), NOT `#page-team` (1,0,0): `#cv-body div, #cv-body span
   { color: inherit }` is (1,0,1) and beats the plain id, so the bare selector lost and the page
   went on inheriting the dark theme's ink. The h1, not being a div or span, was the one element
   that DID take the colour — which made it look like a half-applied stylesheet rather than a
   specificity fight.

   Set on the container rather than per element, because that same inherit rule already routes
   every div and span through whatever this resolves to. */
#cv-body #page-team {
  color: #232c35;
  /* A light SURFACE, not just light ink. The panel's cards are white and its headings are dark,
     but the heading and intro sit outside those cards — directly on the dashboard's dark ground,
     where dark ink is invisible. Rather than re-inking the panel for a dark background (and
     maintaining two colourways of one form), give it the light ground it was drawn for. It reads
     as a document inside the dashboard, which is what it is: an admin form, not a data card. */
  background: #f4f7fa;
  border-radius: 10px;
}
#cv-body #page-team h1 { color: #1b2530; }
#cv-body #page-team .btn-primary { color: #fff; }


/* Placed at the END of the file on purpose. These selectors tie on specificity with the
   `[style*="color:#8fbce8"]` light-theme mapping above -- (1 id, 3) against (1 id, 3) -- so
   source order is the whole of the cascade here. Declared earlier, the mapping won and the
   open button rendered #2563a8 ink on its own #2563a8 fill: contrast 1.00, a button with an
   invisible label. Nothing after this point may reintroduce a rule for these two classes.
   ------------------------------------------------------------------------------------ */
/* ── Remediation action buttons: which one is OPEN ──────────────────────
   These are classes, and that is the fix rather than the tidying. remSyncFormButtons marks the
   open button, and it used to do that by writing el.style.boxShadow. Writing ANY property through
   el.style makes the browser re-serialise the whole attribute on the way out -- `color:#8fbce8`
   comes back as `color: rgb(143, 188, 232)` -- so every light-theme rule below, which matches on
   the literal hex in the style attribute, silently stopped matching that button. The ink reverted
   to its dark-theme light blue on a white card the instant the button was pressed. It is the same
   normalisation the layout editor already forced a second spelling of `background: rgb(42,42,42)`
   for, arriving by a different route.

   The solid fill TRAVELS to whichever action is open. The fill previously meant only "recommended
   next action" and stayed put, so opening a different action left the emphasis pointing at the one
   you had not chosen. One fill, on the thing you are actually doing; the recommended action falls
   back to the outline every other action wears (cv-act-muted) while something else is open, and
   takes the fill back when it closes.

   !important throughout because the buttons carry their base colours inline, and inline beats a
   class. The light values are not invented: #2563a8 and #eaf2fb are what the attribute rules below
   already resolve these same two treatments to, measured off the rendered page. */
.rem-btn.cv-act-open {
  /* Same treatment as remPrimaryCss: a deepened brand fill with light ink, so the OPEN button and
     the RECOMMENDED button are visibly the same kind of thing. They swap roles as forms open and
     close, and two different fills for one treatment made that swap look like a bug. */
  background: color-mix(in srgb, var(--brand-secondary) 65%, #0b1420) !important;
  color: #f5f9ff !important;   /* not #fff — the light theme rewrites that to black; see remPrimaryCss */
  border: 1px solid transparent !important;
}
.rem-btn.cv-act-muted {
  background: none !important; color: #8fbce8 !important; border: 1px solid #4a5663 !important;
}
#cv-app.cv-cards-light .rem-btn.cv-act-muted,
#cv-app.cv-page-light .rem-btn.cv-act-muted { color: #2563a8 !important; }

/* ── Help page: the same card the rest of the app uses ──────────────────
   Help was the only page whose cards carried their accent on the LEFT while every other card in
   the app carries a 3px stripe on top, so it read as a quotation rather than as one of the set.
   And both themes are declared here rather than left to the inline-hex remaps: the old left
   border was written with the raw #5B9BD5 fallback, which the [style*="color:#hex"] rules cannot
   reach because it is a BORDER, so it kept its dark-theme value on a white card and washed out.
   That is the same class of bug as the report card's stripe above, arriving from the other side.
   Resolved by declaring the border INLINE on the card instead, the way every other card does --
   the layout editor writes border-top-color inline to pin each card's accent, and inline always
   beats a class, so a stylesheet accent here could never survive the editor touching the page. */
/* The controls on that page. Both were outlined in var(--card-border), which is deliberately a
   HAIRLINE -- #e3e9f0 on white -- because its job is separating a card from the page behind it.
   On the edge of something you are meant to PRESS it reads as no outline at all, which is what
   was reported. A button border has to be visible against the surface it sits on. */
.cv-help-btn {
  background: none; border: 1px solid #566370; color: #c6c8cb; font-family: inherit;
}
.cv-help-btn:hover { border-color: var(--brand-secondary, #5B9BD5); }
#cv-app.cv-cards-light .cv-help-btn,
#cv-app.cv-page-light .cv-help-btn { border-color: #9fb3c8; color: #1f262e; }
#cv-app.cv-cards-light .cv-help-btn:hover,
#cv-app.cv-page-light .cv-help-btn:hover { border-color: #2563a8; }

/* ── Subcard LEFT accents, in light mode ────────────────────────────────
   This app puts a card's accent on TOP and an inner panel's accent on the LEFT, and the light
   theme was erasing the second. The neutralising rules further up exist to repaint inner panels
   that stayed dark, and they set border-left-color along with right and bottom -- correct for a
   plain panel whose four sides match, and wrong for the twenty-odd panels whose left edge is the
   only colour on them. Nine distinct accents were coming out as the #e3e9f0 hairline, and several
   more as rgba(20,32,45,.14), which is a hairline with extra steps.

   These restate the left edge afterwards, per accent, using the light value this file ALREADY maps
   the same hex to as a text colour -- so a stripe and the words beside it agree instead of being
   two opinions about one accent. Same inline-hex technique the rest of the light theme uses, and
   it must stay AFTER the neutralising rules: equal specificity, so source order decides.

   The doubled theme class is deliberate: the neutralising rules carry an extra .cv-card or
   .cv-page and sit at (1,3,0), so a plain (1,2,0) restore lost outright and only appeared to work
   on the few subcards outside both. Repeating the class matches their weight and source order does
   the rest.

   A colour added to a subcard's left border in future needs a line here. There is no way around
   that short of moving every accent to a class, which is a bigger change than this file wants. */
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #6abf9a"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #6abf9a"] { border-left-color: #1f8a5c !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #5cb894"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #5cb894"] { border-left-color: #1f8a5c !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #5b9bd5"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #5b9bd5"] { border-left-color: #2563a8 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #7ab0e8"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #7ab0e8"] { border-left-color: #2563a8 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #c8a84b"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #c8a84b"] { border-left-color: #574009 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #d4b455"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #d4b455"] { border-left-color: #574009 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #e8a060"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #e8a060"] { border-left-color: #9a5510 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #a3c563"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #a3c563"] { border-left-color: #4f6b1a !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #c9483a"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #c9483a"] { border-left-color: #a4322f !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #ef8b80"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #ef8b80"] { border-left-color: #a4322f !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #8b6914"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #8b6914"] { border-left-color: #574009 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #8a6a12"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #8a6a12"] { border-left-color: #574009 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #2d6b52"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #2d6b52"] { border-left-color: #1f8a5c !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid #5b6470"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid #5b6470"] { border-left-color: #4a5663 !important; }

/* Two subcards write their accent as a var() rather than a hex, so the literal-hex selectors above
   cannot see them. Inside a .cv-card or .cv-page the var is already remapped for light mode and
   they come out right; these cover the ones that sit outside both, where the var still resolves to
   its dark-theme value and the neutralising rule then flattens it. */
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid var(--brand-secondary)"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid var(--brand-secondary)"] { border-left-color: #2563a8 !important; }
#cv-app.cv-cards-light.cv-cards-light [style*="border-left:3px solid var(--accent)"],
#cv-app.cv-page-light.cv-page-light [style*="border-left:3px solid var(--accent)"] { border-left-color: #1a7a50 !important; }

/* The SPA's scroller must not re-anchor across a page swap. showPage hides every .cv-page and
   shows one, which collapses and restores the content height in a single pass — and the browser
   helpfully puts the previous scroll offset back afterwards, so navigating from half way down
   Findings opened Trends half way down too, often past everything it had. The reset in showPage
   was running and being undone a frame later; this is why it now sticks. */
#cv-body { overflow-anchor: none; }

/* ── Framework refs link to their section on Resources ──────────────────────────────
   A citation ("FFIEC IS.II.C") only helps a reader who already knows what it says, and the full
   text lives on another page. These are buttons, not anchors: the destination is a page swap in
   the SPA, and an <a href> that does not navigate is worse for a screen reader than a button that
   does what it says.

   Inheriting colour is deliberate. The ref already carries its own ink from the surrounding span
   (green where mapped, in a drawer, on a card), and giving links a fixed colour here would fight
   whichever context they appear in — and, in the light themes, would have to be re-mapped for each
   one. Underline carries the affordance instead. */
.cv-fwlink {
  background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit;
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: rgba(122, 176, 232, .55); text-decoration-thickness: 1px;
}
.cv-fwlink:hover, .cv-fwlink:focus-visible { text-decoration-color: currentColor; }
.cv-fwlink:focus-visible { outline: 2px solid var(--brand-secondary); outline-offset: 2px; border-radius: 2px; }

/* The landing flash. An outline rather than a background: these nodes sit at three different
   depths in the tree, each with its own surface, and a background would read as a different colour
   at each one. */
[data-fwnode].cv-fwnode-hit {
  outline: 2px solid var(--brand-secondary);
  outline-offset: 3px;
  border-radius: 4px;
  transition: outline-color .4s ease;
}

/* ── Document request list ──────────────────────────────────────────────────────────
   The outstanding count is the number a client actually reads, so it carries colour. Defined for
   both themes rather than inline: the light theme repaints by matching inline style strings, so an
   inline amber is invisible to it and lands at 1.73:1 on white. */
.cv-req-open { color: #d9be6a; }
.cv-req-done { color: #8fd3ae; }
#cv-app.cv-cards-light .cv-req-open, #cv-app.cv-page-light .cv-req-open { color: #7a5500; }
#cv-app.cv-cards-light .cv-req-done, #cv-app.cv-page-light .cv-req-done { color: #1a5c38; }
/* A rejected request, explained where the client will act on it. Amber rather than red: being
   asked for something else is a normal step in an audit, not a failure. Both themes, because an
   inline colour here would be invisible to the light one. */
.cv-req-sentback { background: rgba(200,168,75,.12); border: 1px solid rgba(200,168,75,.35); color: #d9be6a; }
#cv-app.cv-cards-light .cv-req-sentback, #cv-app.cv-page-light .cv-req-sentback {
  background: #fdf7e3; border-color: #d8c68a; color: #7a5500;
}
/* Status cards on the request list. Each one is the filter for its own status: the number you
   want to act on is the thing you click, so the summary and the control are the same object
   rather than a row of counts above a row of buttons saying the same words. */
.cv-req-card {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start;
  background: #242424; border: 1px solid #363636; border-radius: 6px;
  padding: 8px 11px; cursor: pointer; font-family: inherit; text-align: left;
  transition: border-color .12s ease, background .12s ease;
}
.cv-req-card:hover { background: #2a2a2a; border-color: #454545; }
.cv-req-card:focus-visible { outline: 2px solid var(--brand-secondary); outline-offset: 1px; }
.cv-req-card-n { font-size: 19px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.cv-req-card-l { font-size: 11px; color: #a8a8a8; letter-spacing: .02em; }
#cv-app.cv-cards-light .cv-req-card, #cv-app.cv-page-light .cv-req-card {
  background: #fff; border-color: #dde2e9;
}
#cv-app.cv-cards-light .cv-req-card:hover, #cv-app.cv-page-light .cv-req-card:hover {
  background: #f4f6f9; border-color: #c3ccd8;
}
#cv-app.cv-cards-light .cv-req-card-l, #cv-app.cv-page-light .cv-req-card-l { color: #5a6472; }

/* The client fills these in, so they have to be legible in both themes. .cv-field is defined
   dark-only, and a dark input on a white card is the alpha-composited-contrast trap again.
   Scoped to the request list rather than fixing .cv-field globally: the remediation workbench
   uses the same class and changing its appearance is not part of this. */
#cv-app.cv-cards-light #req-list .cv-field,
#cv-app.cv-page-light #req-list .cv-field {
  background: #fff; border-color: #cfd6e0; color: #23272f;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);
}
#cv-app.cv-cards-light #req-list .cv-field::placeholder,
#cv-app.cv-page-light #req-list .cv-field::placeholder { color: #8a929e; }
#cv-app.cv-cards-light #req-list select.cv-field,
#cv-app.cv-page-light #req-list select.cv-field { color-scheme: light; }

/* ── WAITING ON YOU ────────────────────────────────────────────────────────────────────────
   Responsible and Status are required on a request, and Department is a gap worth closing; all
   three say "not yet" in the same voice. A CLASS, not an inline border-color, because the light
   theme repaints every select above with `border: 1px solid var(--card-border) !important` — an
   !important that beats an inline style. Written inline, the amber was perfectly visible in the
   dark theme and flattened to ordinary grey in the light one, so half the users saw no signal at
   all. Specificity here (2 ids) outranks that blanket (1 id) with !important on both.
   .cv-need = required and empty; .cv-gap = missing but not blocking.

   BLUE, NOT RED (2026-09-22, the user: "i don't really care for the color scheme on the
   responsible personnel, department drop down lists"). Nothing has gone wrong on a request nobody
   has picked up yet — it is simply waiting on someone. In alarm red, a freshly issued list of 150
   rows opened as a wall of errors. This is the product's own sky blue, fixed rather than
   var(--brand-secondary), because that one is the CLIENT's colour and would make the signal mean
   something different at every client (and green, or red, at some of them). */
#cv-app #req-list select.cv-need,
#cv-app #req-list input.cv-need { border-color: #5b9bd5 !important; color: #8fbce8 !important; }
/* A gap, not a requirement: the same blue, carried more lightly. */
#cv-app #req-list select.cv-gap,
#cv-app #req-list input.cv-gap { border-color: rgba(91,155,213,.55) !important; color: #9fc0dd !important; }
/* On white the dark theme's pale blues vanish; these are the same two signals restated. */
#cv-app.cv-page-light #req-list select.cv-need,
#cv-app.cv-cards-light #req-list select.cv-need { color: #1c5a9e !important; }
#cv-app.cv-page-light #req-list select.cv-gap,
#cv-app.cv-cards-light #req-list select.cv-gap { color: #3d6e9c !important; }
/* The same signal as WORDS, for readers who may not fill the gap in themselves ("Not assigned",
   "Not set") and for the task list, which has no picker at all. Prefixed with #cv-app / #cv-body
   because `#cv-body div, span, td, th { color: inherit }` outranks any bare class. */
#cv-app .cv-waiting,
#cv-body .cv-waiting { color: #8fbce8; }
#cv-app.cv-page-light .cv-waiting,
#cv-app.cv-cards-light .cv-waiting { color: #1c5a9e; }

/* ── KEY INSIGHTS TILES ─────────────────────────────────────────────────────────────────────
   2026-09-22: Key Insights absorbs the Security Posture Score card (option A of the mockups) and
   its rows become tiles — the shape the user picked out of Since Last Cycle. CLASSES, not inline
   colours: the light theme repaints and an inline colour is invisible on white (trap #33). */
/* FLEX, NOT GRID (2026-09-23, the user: "it would be nice if they could fill most of the empty
   space dynamically"). A four-column grid leaves a hole whenever a row's spans do not add up to
   four -- put the two-column posture gauge beside one ordinary tile and a quarter of that row is
   empty. Flex wraps into the same rows, but the tiles in a SHORT row share the slack out between
   them instead: flex-basis keeps the column widths when a row is full, and flex-grow in proportion
   to the span widens them when it is not, so a wide tile takes a wide share of the leftover.
   Items in a flex line stretch to the tallest by default, so rows still line up as they did. */
/* ROWS (2026-09-24, the user: "i want the free 2d placement like the cards"). The grid is a COLUMN
   of rows now, and each row is the flex line the whole grid used to be. What changed is that the
   break between rows is arranged rather than wherever the widths happened to run out -- so a row
   can be left three wide on purpose, and a fourth tile can be dropped into it.

   Tiles still stretch to fill their row (the user's call), which is what already happened when a
   row ran short and is why no gaps ever appear. */
#cv-app .cv-ki-tiles { display: flex; flex-direction: column; gap: 9px; }
#cv-app .cv-ki-row { display: flex; flex-wrap: wrap; gap: 9px; align-items: stretch; }
/* Where a tile goes to start a row of its own. Hidden at rest -- a dashed band under the tiles
   when nobody is dragging is furniture for a gesture nobody is making -- and revealed for the
   duration of a drag by .cv-ki-dragging on the card. */
#cv-app .cv-ki-newrow {
  display: none; padding: 9px; border: 1px dashed #4a5563; border-radius: 6px;
  font-size: 11.5px; color: #8f99a4; text-align: center; user-select: none;
}
#cv-app .cv-ki-dragging .cv-ki-newrow { display: block; }
#cv-app .cv-ki-dragging .cv-ki-newrow.cv-ki-over,
#cv-app .cv-ki-newrow.cv-ki-over { border-color: #5b9bd5; color: #b8d3ec; background: rgba(91,155,213,.10); }
/* A row being aimed at, when the pointer is in its own space rather than on one of its tiles --
   the gesture that appends to that row. */
#cv-app .cv-ki-row.cv-ki-rowover { box-shadow: inset 0 0 0 1px #5b9bd5; border-radius: 7px; }
#cv-app.cv-cards-light #cv-body .cv-ki-newrow,
#cv-app.cv-page-light  #cv-body .cv-ki-newrow { border-color: #c3ccd6; color: #5f6c7b; }

/* A COLUMN, so the content can use the height the row gives it (2026-09-23, the user: "can you
   look at the pb dashboard to see what I mean about dead space on some of the tiles?").
   Filling the row sideways was only half of it. Tiles in a flex line stretch to the tallest, and
   on Peoples Bank the posture gauge -- arc, score and the peer block -- stands 221px while the
   three number tiles beside it need about 110. They were pinned to the top of their boxes with
   ninety to a hundred and eleven pixels of nothing underneath. Measured, not guessed: Peer
   Alignment 111 unused, Findings 104, Control Pass Rate 91. */
#cv-app .cv-ki-tile {
  background: #242424; border: 1px solid #3a3a3a; border-radius: 6px; padding: 9px 11px;
  min-width: 0; position: relative;
  display: flex; flex-direction: column;
  /* Each tile is its own query container (2026-09-24). The figure below is set at 54px, which is
     right in a 290px tile on a full-width card and far too wide in the ~138px a tile gets when the
     card sits in a half-width slot. The tile cannot ask how wide the CARD is -- spans mean four
     tiles in one container are four different widths -- so it asks how wide IT is. */
  container-type: inline-size;
}
#cv-app.cv-cards-light .cv-ki-tile,
#cv-app.cv-page-light .cv-ki-tile { background: #f6f8fa; border-color: #e2e8ef; }
/* 9.5 -> 11px, and up from #9a9a9a (2026-09-24, the user: "make the tile titles a little more
   noticeable - larger font and brighten the color"). At 9.5 and a mid grey the label was quieter
   than the context line under the figure, which made the tile hard to scan: you found the number
   first and then had to hunt for what it was of. #c3cbd6 measures about 9:1 on the tile ground --
   clearly legible, still well under the figure it introduces. The light value darkens rather than
   brightens, for the same reason: contrast against its own ground is what "noticeable" means. */
/* ── THE SAMPLED DATES ON A REQUEST (2026-09-24) ──────────────────────────────
   Set apart from the ask rather than run into it: the ask is prose the auditor wrote, and these
   are the specific days the client has to go and pull. Grouped by month, one row each, with the
   month held in a fixed column so the day lists line up down the block and can be counted. */
#cv-app .cv-req-sample {
  margin-top: 7px; padding: 8px 10px; border-radius: 6px;
  background: rgba(255,255,255,.04); border: 1px solid #3a3a3a;
}
#cv-app .cv-req-sample-h {
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: #c3cbd6; margin-bottom: 5px;
}
#cv-app .cv-req-sample-r {
  display: flex; gap: 9px; font-size: 12.5px; line-height: 1.7; color: #c8c8c8;
}
#cv-app .cv-req-sample-m {
  flex: 0 0 62px; color: #98a1aa; font-weight: 600; font-variant-numeric: tabular-nums;
}
/* The substitution rule. Separated by a rule rather than just spacing: it is an instruction about
   the list above it, not another line of the list. */
#cv-app .cv-req-sample-n {
  margin-top: 7px; padding-top: 6px; border-top: 1px solid #3a3a3a;
  font-size: 11.5px; line-height: 1.6; color: #98a1aa;
}
#cv-app.cv-cards-light #cv-body .cv-req-sample-n {
  border-top-color: #e2e8ef; color: #5f6c7b;
}
#cv-app.cv-page-light #cv-body .cv-req-sample-n {
  border-top-color: #e2e8ef; color: #5f6c7b;
}
#cv-app.cv-cards-light #cv-body .cv-req-sample,
#cv-app.cv-page-light  #cv-body .cv-req-sample { background: rgba(0,0,0,.03); border-color: #e2e8ef; }
#cv-app.cv-cards-light #cv-body .cv-req-sample-h,
#cv-app.cv-page-light  #cv-body .cv-req-sample-h { color: #3f4a57; }
#cv-app.cv-cards-light #cv-body .cv-req-sample-r,
#cv-app.cv-page-light  #cv-body .cv-req-sample-r { color: #3d4553; }
#cv-app.cv-cards-light #cv-body .cv-req-sample-m,
#cv-app.cv-page-light  #cv-body .cv-req-sample-m { color: #5f6c7b; }
#cv-app .cv-ki-k {
  font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: #c3cbd6; display: flex; align-items: center; gap: 4px; line-height: 1.35;
}
#cv-app.cv-cards-light .cv-ki-k,
#cv-app.cv-page-light .cv-ki-k { color: #3f4a57; }
/* 25 -> 30 -> 54px (2026-09-23 "increase the number font size a few points, maybe that will help
   fill out the tiles", then 2026-09-24 against four mocked-up densities: "d with 13px bars").
   It is the figure the tile exists to state, and the tile is top-justified again below, so the
   figure is what fills the box rather than whitespace pushed around it. */
/* 54 -> 46px, and every step under it, down 15% (2026-09-25, the user: "reduce the large font
   size on the tiles by 15%% or so"). 15%% is not arbitrary and is close to the most the existing
   checks allow: test_key_insights_density pins the figure at four times the label above it, and
   46/11 clears 44 by two pixels. Going further means arguing with that ratio, which is the thing
   keeping the figure a headline rather than just large text. */
/* DM SANS 600, TABULAR (2026-09-24, the user: "i don't like how the numbers don't align, e.g.,
   95.2", then "use dm sans 600 tabular display for the tiles").
   Georgia sets OLD-STYLE figures -- numerals drawn to sit inside running prose, where 3 4 5 7 9
   hang below the baseline and 6 8 rise above it. Correct in a paragraph, wrong in the one figure
   the tile exists to state: 95.2 read as three glyphs at three different heights. DM Sans sets
   lining figures, so every digit tops out level, and it is already the face of the label above and
   the context line below. 600 is the heaviest weight the page loads.
   tabular-nums is the SECOND half of the fix and a different problem: it gives every digit one
   width, so a figure does not shift sideways when the cycle changes it. The bar values and the
   matrix cells already ask for it. */
#cv-app .cv-ki-v {
  font-family: var(--font); font-size: 46px; font-weight: 600; line-height: 1.05;
  font-variant-numeric: tabular-nums;
  margin-top: 3px; color: #f0f0f0;
}
/* Most values are three or four glyphs. A few are WORDS -- Leading Root Cause says "Lack of
   Controls" on a bad day, Framework Alignment says "Aligned" -- and a word set at 54px runs
   straight out of the tile. cvKiVSize() measures the value's plain text and picks a step. */
#cv-app .cv-ki-v.cv-ki-vm { font-size: 32px; }
#cv-app .cv-ki-v.cv-ki-vs { font-size: 22px; }
/* ...and the same climb-down by tile width, for a card in a half- or third-width slot. */
@container (max-width: 210px) {
  #cv-app .cv-ki-v { font-size: 32px; }
  #cv-app .cv-ki-v.cv-ki-vm { font-size: 26px; }
  #cv-app .cv-ki-v.cv-ki-vs { font-size: 19px; }
}
@container (max-width: 155px) {
  #cv-app .cv-ki-v { font-size: 24px; }
  #cv-app .cv-ki-v.cv-ki-vm { font-size: 20px; }
  #cv-app .cv-ki-v.cv-ki-vs { font-size: 15px; }
}
#cv-app.cv-cards-light .cv-ki-v,
#cv-app.cv-page-light .cv-ki-v { color: #1d2733; }
#cv-app .cv-ki-v .u { font-size: .44em; color: #98a1aa; font-weight: 400; }
/* Up from 11.5 / 11 (2026-09-23, the user: "increase the font size under the numbers a few
   pts"). The delta and the context line were set small enough to read as a footnote to the
   number; they carry the movement and the detail, which is most of what the tile says. */
#cv-app .cv-ki-d { font-size: 15px; font-weight: 600; margin-top: 2px;
                   font-variant-numeric: tabular-nums; }
#cv-app .cv-ki-c { font-size: 12.5px; color: #98a1aa; margin-top: 3px; line-height: 1.45; }
#cv-app.cv-cards-light .cv-ki-c,
#cv-app.cv-page-light .cv-ki-c { color: #5f6c7b; }
/* The three readings a delta can carry. Semantic, not the brand accent. */
/* An auditor drags a tile BY ITS GRIP to rearrange the card; the order publishes to the client.
   The grip exists because most tiles also open a page (2026-09-23): with the whole tile draggable,
   a drag that ended a few pixels from where it began came back as a click and took the auditor off
   the Overview. So the tile keeps the pointer cursor it earns by being clickable, and only the
   grip says "grab".

   Always visible rather than hover-only -- an auditor cannot look for a handle they have never
   seen -- but at a third opacity until the tile is hovered, so eleven tiles do not become eleven
   pieces of furniture. It sits in the corner the label does not reach. */
/* The DOTS are 8px wide; the TARGET is the padding around them. Set as a box rather than left to
   the glyphs, because two ellipses at 11px measured 7px across -- a handle nobody can hit is not a
   handle, and this one has to be grabbed accurately enough not to be mistaken for the tile. */
#cv-app .cv-ki-grip {
  position: absolute; top: 1px; right: 2px; z-index: 1;
  display: inline-block; box-sizing: content-box; width: 8px; text-align: center;
  font-size: 11px; line-height: 13px; letter-spacing: 1px; color: #9a9a9a; opacity: .35;
  padding: 4px 6px; cursor: grab; user-select: none; transition: opacity .12s;
}
#cv-app .cv-ki-tile:hover .cv-ki-grip { opacity: .85; }
#cv-app .cv-ki-grip:active { cursor: grabbing; opacity: 1; }
#cv-app.cv-cards-light .cv-ki-grip,
#cv-app.cv-page-light .cv-ki-grip { color: #5f6c7b; }
/* The label is a flex line that would otherwise run under the grip. */
#cv-app .cv-ki-drag .cv-ki-k { padding-right: 22px; }
@media (prefers-reduced-motion: reduce) { #cv-app .cv-ki-grip { transition: none; } }
/* The drop indicator marks the EDGE the tile will land against, not just "this one" (2026-09-24).
   A single highlight cannot say whether the tile goes in front of or after the one under the
   pointer, and that is the whole difference between a move and what used to look like a swap. */
#cv-app .cv-ki-tile.cv-ki-over   { border-color: #5b9bd5; box-shadow: inset  3px 0 0 #5b9bd5; }
#cv-app .cv-ki-tile.cv-ki-over-a { border-color: #5b9bd5; box-shadow: inset -3px 0 0 #5b9bd5; }
#cv-app .cv-ki-up { color: #6abf9a; }
#cv-app .cv-ki-dn { color: #ef8b80; }
#cv-app .cv-ki-fl { color: #a8a8a8; }
#cv-app.cv-cards-light .cv-ki-up, #cv-app.cv-page-light .cv-ki-up { color: #1f5a3c; }
#cv-app.cv-cards-light .cv-ki-dn, #cv-app.cv-page-light .cv-ki-dn { color: #a4322a; }
#cv-app.cv-cards-light .cv-ki-fl, #cv-app.cv-page-light .cv-ki-fl { color: #5f6c7b; }
/* The peer figures under a posture display (2026-09-23). One block, used by the Key Insights
   header and by the gauge tile when the header is off, so the comparison reads the same either
   way. The sentence inside is cvPeerLineHtml, which the standalone Security Posture Score card
   has always used -- this is the same wording in another place, not another wording. */
#cv-app .cv-peerfig {
  margin: 10px 0 2px; padding: 9px 11px; border-radius: 6px;
  background: rgba(255,255,255,.04); border: 1px solid #3a3a3a;
}
#cv-body .cv-peerfig-k {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em;
  color: #c8c8c8; margin-bottom: 4px;
}
#cv-app.cv-cards-light .cv-peerfig,
#cv-app.cv-page-light .cv-peerfig { background: rgba(0,0,0,.035); border-color: var(--card-border); }
#cv-app.cv-cards-light #cv-body .cv-peerfig-k,
#cv-app.cv-page-light  #cv-body .cv-peerfig-k { color: #4a5462; }

/* ── THE POSTURE ARC (2026-09-23) ─────────────────────────────────────────────
   The gauge as a tile. Its parts are CLASSED rather than given inline SVG attributes, because the
   light theme flips a colour by matching an inline `color:` and that never reaches an SVG stroke
   or fill: the needle shipped for ten minutes as #ffffff, which is 1.03:1 on a light card, i.e.
   not there at all. Each part's light value is written directly beside its dark one. The band
   arcs keep their inline colours on purpose — they are the same four band colours in both themes,
   and they carry no text. */
/* The posture bar's band boundaries (2026-09-24). The bar itself is the same four colours in
   either theme -- a linear-gradient is not one of the inline signatures the light rules repaint --
   so one value serves both. White at .55 reads on all four bands: the lightest of them, #c8a84b,
   still takes it as a clear lighter rule. A CLASS and not an inline rgba(), which is the lesson
   the arc needle cost: an inline colour the theme does not know is an invisible one the day the
   ground underneath it changes. */
#cv-body .cv-gauge-tick {
  position: absolute; top: -3px; width: 2px; height: 16px; border-radius: 1px;
  background: rgba(255,255,255,.62); pointer-events: none;
}
/* The tick is mostly read by its OVERHANG -- the 3px standing proud of the bar top and bottom --
   because over the gradient itself nothing reaches 2.5:1 against the band it crosses. The overhang
   sits on the CARD, which does flip, so this is the one part that needs a light value. At 1px and
   .55 white it measured 1.7:1 on the bar and vanished entirely on a light card: invisible ticks on
   a bar whose whole purpose was to show where the bands divide. */
#cv-app.cv-cards-light #cv-body .cv-gauge-tick,
#cv-app.cv-page-light  #cv-body .cv-gauge-tick { background: rgba(25,34,45,.55); }
/* THIS CLIENT'S OWN SCORE ON THE POSTURE BAR (2026-09-24). Geometry here so the peer mark below
   can be held to exactly half of it, and colour here because inline it could not be themed: as
   `border-top:10px solid #fff` written into the markup, the mark measured 1.00:1 on a light card
   and was drawn entirely by its drop-shadow. */
#cv-body .cv-gauge-score {
  position: absolute; top: -14px; transform: translateX(-50%);
  width: 0; height: 0; pointer-events: none;
  border-left: 6px solid transparent; border-right: 6px solid transparent;
  border-top: 10px solid #ffffff;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.5));
  transition: left .8s cubic-bezier(.4,0,.2,1);
}
#cv-app.cv-cards-light #cv-body .cv-gauge-score,
#cv-app.cv-page-light  #cv-body .cv-gauge-score {
  border-top-color: #1d2733; filter: drop-shadow(0 1px 2px rgba(0,0,0,.22));
}
/* THE PEER MEDIAN ON THE POSTURE BAR (2026-09-24). The score mark is a 12x10 triangle; this is
   the same triangle at half of each dimension, 6x5, so the two read as one family and which is
   the subject is never in question. Derived, not coincidental: if the score triangle is ever
   resized, verify_peer_alignment.py fails until this follows it.

   Flat, with no drop-shadow -- the score mark has one, and that is part of what separates them.
   And a LIGHT VALUE, which the ring it replaces never had: #c3cbd6 on a #f6f8fa card measures
   1.35:1, so on a light card the peer mark has been invisible since the day it shipped. */
#cv-body .cv-gauge-peer {
  position: absolute; top: -9px; transform: translateX(-50%);
  width: 0; height: 0; pointer-events: auto;
  border-left: 3px solid transparent; border-right: 3px solid transparent;
  border-top: 5px solid #c3cbd6;
}
#cv-body .cv-gauge-peer-label {
  position: absolute; top: -22px; transform: translateX(-50%);
  font-size: 10.5px; font-weight: 600; line-height: 1; color: #c3cbd6; white-space: nowrap;
}
#cv-app.cv-cards-light #cv-body .cv-gauge-peer,
#cv-app.cv-page-light  #cv-body .cv-gauge-peer { border-top-color: #5d6875; }
#cv-app.cv-cards-light #cv-body .cv-gauge-peer-label,
#cv-app.cv-page-light  #cv-body .cv-gauge-peer-label { color: #5d6875; }
#cv-body .cv-arc-needle { stroke: #ffffff; }
#cv-body .cv-arc-hub    { fill: #ffffff; }
#cv-body .cv-arc-peer   { stroke: #dbe1ea; }
#cv-app.cv-cards-light #cv-body .cv-arc-needle,
#cv-app.cv-page-light  #cv-body .cv-arc-needle { stroke: #1d2733; }
#cv-app.cv-cards-light #cv-body .cv-arc-hub,
#cv-app.cv-page-light  #cv-body .cv-arc-hub    { fill: #1d2733; }
#cv-app.cv-cards-light #cv-body .cv-arc-peer,
#cv-app.cv-page-light  #cv-body .cv-arc-peer   { stroke: #2d3643; }

/* ── TILE WIDTH, 1-4 COLUMNS (2026-09-23) ─────────────────────────────────────
   Per tile, set by the auditor in the gear panel and published with the rest of the curation.
   Spans are relative to the CARD's own four-column grid, so a tile keeps its share whether the
   card sits full width or in a half-width slot. */
/* WIDTHS ARE PROPORTIONS OF THEIR ROW (2026-09-24), not fixed quarters of the card.
   With explicit rows, a basis of 25% means a fifth tile dropped into a row wraps onto a second
   line -- a visual row break that is not in the data, which is exactly what rows were meant to
   stop. Basis 0 with grow = span divides whatever the row holds: four 1-wide tiles take a quarter
   each, and a 2-wide beside two 1-wides takes half. That is what "stretch to fill the row" means
   once the auditor, rather than the wrap point, decides where a row ends. */
#cv-app .cv-ki-row { flex-wrap: nowrap; }
#cv-app .cv-ki-tile { flex: 1 1 0; min-width: 0; }
#cv-app .cv-ki-tile.cv-ki-w2 { flex-grow: 2; }
#cv-app .cv-ki-tile.cv-ki-w3 { flex-grow: 3; }
#cv-app .cv-ki-tile.cv-ki-w4 { flex-grow: 4; }
/* ── THE TILE CHARTS (2026-09-23) ─────────────────────────────────────────────
   The user: "fill in the tiles a little more. there's a lot of dead space in several tiles" and
   "add some color. it's very drab." A tile with a series draws it here instead of writing the same
   distribution out as grey text. The FILLS come from the series -- the app's own severity ramp,
   type and root-cause colours -- so the colour means the same thing it means in the table below;
   only the furniture is themed here. */
/* TOP-JUSTIFIED, and the dead space taken out by making the content bigger rather than by moving
   it around (2026-09-24, the user: "i prefer it top justified. we just need to make the font
   bigger for the main data number... make the bar graphs thicker before adding space between").
   The previous pass centred everything and spread the bar rows to fill the height the posture
   gauge sets; that filled the box without making anything easier to read. A fixed 9px between rows
   now, with the bars themselves at 13px, so three rows read as a chart instead of as three
   hairlines drifting apart. */
#cv-app .cv-ki-viz { margin-top: 7px; display: flex; flex-direction: column; gap: 9px; }
#cv-app .cv-ki-viz.cv-ki-ring { flex-direction: row; align-items: center; gap: 13px; }
/* ONE SET OF COLUMNS FOR THE WHOLE CHART (2026-09-24, the user: "i also want bar graphs to start
   at the longest word so that they're all uniform in width"). Each row was its own flex line, so
   the label sized itself and the trough began wherever that label happened to end: "Risk accepted"
   pushed its bar in by forty pixels that "Closed" did not, and three bars of three different
   lengths cannot be compared by length, which is the one thing a bar chart is for.

   The rows become `display: contents` and the CHART is the grid, so all three cells line up in
   one track each. The label track is `auto` -- as wide as the longest word, and no wider -- and it
   can still give way, because the trough track holds a 64px floor. A label too long for what is
   left ellipsises as it always did. */
#cv-app .cv-ki-viz.cv-ki-bars {
  display: grid; grid-template-columns: minmax(0, auto) minmax(64px, 1fr) auto;
  align-items: center; column-gap: 7px; row-gap: 9px;
}
#cv-app .cv-ki-viz.cv-ki-bars .cv-ki-brow { display: contents; }
/* Spans the lot: it is a footnote to the rows, not a fourth column. */
#cv-app .cv-ki-viz.cv-ki-bars .cv-ki-bmore { grid-column: 1 / -1; }
/* The 42% cap was a share of the ROW. In the grid the track already bounds it, and a percentage
   resolved against the track it is helping to size is not a cap at all. */
#cv-app .cv-ki-viz.cv-ki-bars .cv-ki-bl { max-width: none; }
/* ── THE MATRIX (2026-09-24) ──────────────────────────────────────────────────
   Category down the side, one column per cycle, movement on the right. Same grid-with-
   display:contents idiom as the bars above, so the columns line up down the whole tile however
   long the category names are. --mxn is the number of cycles, set on the element: a tile cannot
   know at author time whether a client has two cycles on record or six. */
#cv-app .cv-ki-viz.cv-ki-mx {
  display: grid; align-items: center;
  grid-template-columns: minmax(0, auto) repeat(var(--mxn, 3), minmax(26px, 1fr)) minmax(0, auto);
  column-gap: 9px; row-gap: 7px; font-size: 13px;
}
#cv-app .cv-ki-viz.cv-ki-mx .cv-ki-mxr { display: contents; }
#cv-app .cv-ki-viz.cv-ki-mx .cv-ki-bl { max-width: none; color: #b6bec8; }
#cv-app .cv-ki-mxv { text-align: right; color: #98a1aa; font-variant-numeric: tabular-nums; }
/* The NEWEST cycle is the one being briefed, so it is the one set in the reading colour; the
   others are there to be compared against, not read one by one. */
#cv-app .cv-ki-mxn { color: #e8e8e8; font-weight: 700; }
#cv-app .cv-ki-mxd { text-align: right; font-size: 11.5px; font-weight: 600; white-space: nowrap; }
/* The header row carries the years. Smaller and quieter than the numbers under it: it is a key. */
#cv-app .cv-ki-viz.cv-ki-mx .cv-ki-mxh > span {
  font-size: 10.5px; font-weight: 600; letter-spacing: .04em; color: #8f99a4;
  text-align: right; padding-bottom: 1px;
}
#cv-app .cv-ki-mxc {
  display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 6px;
  flex: 0 0 auto; vertical-align: baseline;
}
#cv-app.cv-cards-light #cv-body .cv-ki-mxv,
#cv-app.cv-page-light  #cv-body .cv-ki-mxv { color: #5a6270; }
#cv-app.cv-cards-light #cv-body .cv-ki-mxn,
#cv-app.cv-page-light  #cv-body .cv-ki-mxn { color: #1d2733; }
#cv-app.cv-cards-light #cv-body .cv-ki-viz.cv-ki-mx .cv-ki-mxh > span,
#cv-app.cv-page-light  #cv-body .cv-ki-viz.cv-ki-mx .cv-ki-mxh > span { color: #5f6c7b; }

/* ── THIS CYCLE / ACROSS CYCLES ───────────────────────────────────────────────
   A two-state switch on the card header. Quiet at rest -- it sits beside the card's own heading
   and must not compete with it -- and the selected side carries the brand accent so which set is
   showing is readable without reading the words. */
#cv-app .cv-ki-modes {
  display: inline-flex; flex: 0 0 auto; border: 1px solid #3f3f3f; border-radius: 6px;
  overflow: hidden; background: #1e1e1e;
}
#cv-app .cv-ki-modes button {
  padding: 3px 10px; font-family: var(--font); font-size: 11.5px; font-weight: 600;
  letter-spacing: .02em; border: 0; background: transparent; color: #98a1aa; cursor: pointer;
  white-space: nowrap; transition: background .12s, color .12s;
}
#cv-app .cv-ki-modes button:hover { background: rgba(255,255,255,.07); color: #e0e0e0; }
#cv-app .cv-ki-modes button.on { background: rgba(91,155,213,.18); color: #b8d3ec; }
#cv-app.cv-cards-light .cv-ki-modes,
#cv-app.cv-page-light  .cv-ki-modes { background: #eef1f5; border-color: #d3dae2; }
#cv-app.cv-cards-light #cv-body .cv-ki-modes button,
#cv-app.cv-page-light  #cv-body .cv-ki-modes button { color: #5f6c7b; }
#cv-app.cv-cards-light #cv-body .cv-ki-modes button.on,
#cv-app.cv-page-light  #cv-body .cv-ki-modes button.on { background: #d7e6f5; color: #1c5a9e; }
#cv-app .cv-ki-brow { display: flex; align-items: center; gap: 7px; font-size: 13px; line-height: 1.5; }
#cv-app .cv-ki-bl { flex: 0 0 auto; max-width: 42%; overflow: hidden; text-overflow: ellipsis;
                    white-space: nowrap; color: #b6bec8; }
#cv-app .cv-ki-bt { flex: 1 1 auto; min-width: 0; height: 13px; border-radius: 7px;
                    background: rgba(255,255,255,.07); overflow: hidden; }
#cv-app .cv-ki-bt > i { display: block; height: 100%; border-radius: 7px; }
#cv-app .cv-ki-bv { flex: 0 0 auto; font-weight: 700; color: #e8e8e8; font-variant-numeric: tabular-nums; }
#cv-app .cv-ki-bmore { font-size: 12px; color: #8f99a4; margin-top: 2px; }
/* One strip: the whole cycle at a glance, each band its own width. */
#cv-app .cv-ki-strip { display: flex; height: 16px; border-radius: 8px; overflow: hidden;
                       background: rgba(255,255,255,.07); }
#cv-app .cv-ki-strip > i { display: block; height: 100%; }
#cv-app .cv-ki-leg { display: flex; flex-wrap: wrap; gap: 3px 11px; margin-top: 0;
                     font-size: 13px; color: #b6bec8; }
#cv-app .cv-ki-leg > span { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
#cv-app .cv-ki-leg > span > i { width: 8px; height: 8px; border-radius: 2px; flex: 0 0 auto; }
#cv-app .cv-ki-ring svg { flex: 0 0 auto; }
#cv-app .cv-ki-leg-col { flex-direction: column; gap: 3px; margin-top: 0; }
#cv-app.cv-cards-light #cv-body .cv-ki-bl,
#cv-app.cv-page-light  #cv-body .cv-ki-bl,
#cv-app.cv-cards-light #cv-body .cv-ki-leg,
#cv-app.cv-page-light  #cv-body .cv-ki-leg { color: #4a5462; }
#cv-app.cv-cards-light #cv-body .cv-ki-bv,
#cv-app.cv-page-light  #cv-body .cv-ki-bv { color: #1d2733; }
#cv-app.cv-cards-light #cv-body .cv-ki-bmore,
#cv-app.cv-page-light  #cv-body .cv-ki-bmore { color: #5a6270; }
#cv-app.cv-cards-light .cv-ki-bt,
#cv-app.cv-page-light .cv-ki-bt,
#cv-app.cv-cards-light .cv-ki-strip,
#cv-app.cv-page-light .cv-ki-strip { background: rgba(0,0,0,.07); }

/* The shape picker, on the tile. Hidden until the tile is hovered -- four buttons on every tile at
   rest would be more furniture than data -- and on its own ground, because on hover it sits over
   the label it would otherwise have to leave room for. */
#cv-app .cv-ki-shapes {
  position: absolute; top: 1px; right: 24px; z-index: 2; display: inline-flex; gap: 1px;
  padding: 2px; border-radius: 6px; background: #1e1e1e; border: 1px solid #3f3f3f;
  opacity: 0; pointer-events: none; transition: opacity .12s;
}
#cv-app .cv-ki-tile:hover .cv-ki-shapes,
#cv-app .cv-ki-shapes:focus-within { opacity: 1; pointer-events: auto; }
#cv-app .cv-ki-shapes button {
  width: 20px; height: 18px; padding: 0; font-family: var(--font); font-size: 11px; line-height: 1;
  border: 0; background: transparent; color: #98a1aa; border-radius: 4px; cursor: pointer;
}
#cv-app .cv-ki-shapes button:hover { background: rgba(255,255,255,.09); color: #e0e0e0; }
#cv-app .cv-ki-shapes button.on { background: rgba(91,155,213,.26); color: #cfe3f6; }
#cv-app.cv-cards-light .cv-ki-shapes,
#cv-app.cv-page-light .cv-ki-shapes { background: #ffffff; border-color: #d3dae2; box-shadow: 0 2px 6px rgba(0,0,0,.12); }
#cv-app.cv-cards-light #cv-body .cv-ki-shapes button,
#cv-app.cv-page-light  #cv-body .cv-ki-shapes button { color: #5a6270; }
#cv-app.cv-cards-light #cv-body .cv-ki-shapes button.on,
#cv-app.cv-page-light  #cv-body .cv-ki-shapes button.on { background: rgba(28,90,158,.13); color: #1c5a9e; }
@media (prefers-reduced-motion: reduce) { #cv-app .cv-ki-shapes { transition: none; } }

/* The panel's own way out. It sits in the header rather than the foot so it is visible without
   scrolling a panel that is fourteen rows long. */
#cv-app .cv-ki-done {
  font-family: var(--font); font-size: 11px; font-weight: 600; letter-spacing: .02em;
  padding: 3px 11px; border-radius: 5px; cursor: pointer;
  border: 1px solid #5b9bd5; background: rgba(91,155,213,.18); color: #cfe3f6;
}
#cv-app .cv-ki-done:hover { background: rgba(91,155,213,.3); }
#cv-app.cv-cards-light #cv-body .cv-ki-done,
#cv-app.cv-page-light  #cv-body .cv-ki-done { border-color: #1c5a9e; background: rgba(28,90,158,.1); color: #1c5a9e; }

/* The 1-4 selector in the picker. */
#cv-app .cv-ki-wsel { display: inline-flex; gap: 2px; flex: 0 0 auto; }
#cv-app .cv-ki-wsel button {
  width: 21px; height: 20px; padding: 0; font-family: var(--font); font-size: 11px; font-weight: 600;
  border: 1px solid #3f3f3f; background: transparent; color: #a8a8a8; border-radius: 4px;
  cursor: pointer; line-height: 1;
}
#cv-app .cv-ki-wsel button:hover { border-color: #5b9bd5; color: #d0d0d0; }
#cv-app .cv-ki-wsel button.on { background: rgba(91,155,213,.22); border-color: #5b9bd5; color: #cfe3f6; }
#cv-app.cv-cards-light #cv-body .cv-ki-wsel button,
#cv-app.cv-page-light  #cv-body .cv-ki-wsel button { border-color: #ccd4de; color: #5a6270; }
#cv-app.cv-cards-light #cv-body .cv-ki-wsel button.on,
#cv-app.cv-page-light  #cv-body .cv-ki-wsel button.on { background: rgba(28,90,158,.12); border-color: #1c5a9e; color: #1c5a9e; }
/* A span wider than the grid overflows the card, so each breakpoint clamps what it cannot fit.
   Written as explicit caps rather than min() so it holds in any browser that renders the rest. */
/* Two columns, then one. A span wider than the row still cannot overflow: its basis is capped at
   the full width, and flex-grow only ever divides what is left over. */
@media (max-width: 1100px) {
  /* Narrow: an arranged row stops being readable however its width is divided, so rows wrap --
     the same degradation the card had before rows existed. */
  #cv-app .cv-ki-row { flex-wrap: wrap; }
  #cv-app .cv-ki-tile { flex-basis: calc(50% - 4.5px); }
  #cv-app .cv-ki-tile.cv-ki-w2,
  #cv-app .cv-ki-tile.cv-ki-w3,
  #cv-app .cv-ki-tile.cv-ki-w4 { flex-basis: 100%; }
}
@media (max-width: 560px)  {
  #cv-app .cv-ki-row { flex-wrap: wrap; }
  #cv-app .cv-ki-tile,
  #cv-app .cv-ki-tile.cv-ki-w2,
  #cv-app .cv-ki-tile.cv-ki-w3,
  #cv-app .cv-ki-tile.cv-ki-w4 { flex-basis: 100%; }
}

/* ── THE OPENED ROW ─────────────────────────────────────────────────────────────────────────
   2026-09-22, the user picked this from four drawings: the request across the top, then Response
   and Files each inside their own edge. Before it, everything floated in one block and the answer
   bled into the file list. Panel labels are the NOUN — "Response", never "Your answer". */
#cv-app .cv-req-ask {
  padding-bottom: 12px; margin-bottom: 13px;
  border-bottom: 1px solid rgba(128,128,128,.25);
}
#cv-app.cv-cards-light .cv-req-ask,
#cv-app.cv-page-light .cv-req-ask { border-bottom-color: #dbe2ea; }
#cv-app .cv-req-panel {
  border: 1px solid rgba(128,128,128,.28); border-radius: 6px; padding: 11px 13px; min-width: 0;
}
#cv-app.cv-cards-light .cv-req-panel,
#cv-app.cv-page-light .cv-req-panel { border-color: #dbe2ea; }
#cv-app .cv-req-plbl {
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: #9a9a9a; margin-bottom: 9px;
}
#cv-app.cv-cards-light .cv-req-plbl,
#cv-app.cv-page-light .cv-req-plbl { color: #5f6c7b; }
/* The control a request answers, as a tag rather than another line of grey prose.
   NOT .cv-req-chip: that name is already the upload tray's chip (green in the light theme), and
   reusing it inherited its shape and lost the colour to the later rule at equal specificity. */
#cv-app .cv-req-ctl {
  display: inline-block; font-size: 10.5px; padding: 1px 8px; border-radius: 9px;
  background: rgba(45,100,180,.18); color: #8fbce8;
}
#cv-app.cv-cards-light .cv-req-ctl,
#cv-app.cv-page-light .cv-req-ctl { background: #e8f0fa; color: #1c5a9e; }

/* ── THE FILES ON A REQUEST, AS ROWS ────────────────────────────────────────────────────────
   2026-09-22, the user: "i want the format to be rows with file name, date uploaded, by whom."
   text-align is set explicitly: this table sits in the opened row's right-hand rail, which is
   right-aligned, and a column of right-aligned filenames of different lengths is the ragged edge
   the rail was meant to fix. The table fills the rail's width, so it is flush right either way. */
#cv-app .cv-reqf-tbl { width: 100%; border-collapse: collapse; font-size: 11.5px; text-align: left; }
#cv-app .cv-reqf-tbl th { font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  font-weight: 700; color: #9a9a9a; padding: 0 7px 3px 0; white-space: nowrap; }
#cv-app .cv-reqf-tbl td { padding: 4px 7px 4px 0; vertical-align: top; color: #d8d8d8;
  border-top: 1px solid rgba(128,128,128,.22); }
#cv-app .cv-reqf-tbl th:last-child,
#cv-app .cv-reqf-tbl td:last-child { padding-right: 0; }
#cv-app .cv-reqf-tbl input[type="checkbox"] { margin: 2px 0 0; vertical-align: top; }
#cv-app.cv-cards-light .cv-reqf-tbl th,
#cv-app.cv-page-light .cv-reqf-tbl th { color: #5f6c7b; }
#cv-app.cv-cards-light .cv-reqf-tbl td,
#cv-app.cv-page-light .cv-reqf-tbl td { color: #33404d; border-top-color: #dde3ea; }

/* ── Visibility pass, 2026-09-14 (measured, both themes) ─────────────────────────────────────
   Every rule below is prefixed with #cv-body or #cv-app because `#cv-body div/span/td/th
   { color: inherit }` outranks any bare class -- which is how .cv-req-card-l declared #a8a8a8
   and rendered as the button's default BLACK on a dark card. */

/* The dashboard switcher had no light-theme rules at all: white text on a light sidebar. */
#cv-app.cv-page-light #cv-dash-switch button { color: #3d4652; }
#cv-app.cv-page-light #cv-dash-switch button:hover { background: rgba(0,0,0,.05); color: #111; }
#cv-app.cv-page-light #cv-dash-switch button.active { background: rgba(0,0,0,.07); border-color: rgba(0,0,0,.12); color: #14202d; }
/* Section heads read at 3.5:1 dark / 3.0:1 light. */
.cv-navhead { color: rgba(255,255,255,.58); }
#cv-app.cv-page-light .cv-navhead { color: rgba(0,0,0,.62); }

/* Request summary cards: the button's own ink, and the label's, in both themes. */
#cv-body .cv-req-card { color: #f5f5f5; }
#cv-body .cv-req-card-l { color: #a8a8a8; }
#cv-app.cv-cards-light #cv-body .cv-req-card, #cv-app.cv-page-light #cv-body .cv-req-card { color: #14202d; }
#cv-app.cv-cards-light #cv-body .cv-req-card-l, #cv-app.cv-page-light #cv-body .cv-req-card-l { color: #5a6472; }

/* The Status page's stage pipeline. The lit pill is the client's brand mixed toward the card
   ground, so white ink clears on any hue; done pills tint, upcoming ones recede. */
#cv-body .cv-stage { display: inline-block; font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: 12px;
  background: #1e1e1e; color: #a8a8a8; border: 1px solid #404040; }
#cv-body .cv-stage.done { background: rgba(91,155,213,.16); color: #d6e2ef; border-color: rgba(91,155,213,.35); }
#cv-body .cv-stage.on { background: color-mix(in srgb, var(--brand-secondary) 38%, #1e1e1e); color: #fff; border-color: var(--brand-secondary); }
#cv-body .cv-stage-sep { color: #707070; font-size: 12px; }
#cv-app.cv-cards-light #cv-body .cv-stage { background: #f1f4f8; color: #5a6472; border-color: #d5dbe3; }
#cv-app.cv-cards-light #cv-body .cv-stage.done { background: rgba(28,90,158,.10); color: #1c3f66; border-color: rgba(28,90,158,.30); }
#cv-app.cv-cards-light #cv-body .cv-stage.on { background: color-mix(in srgb, var(--brand-secondary) 22%, #ffffff); color: #14202d; border-color: var(--brand-secondary); }
#cv-app.cv-cards-light #cv-body .cv-stage-sep { color: #9aa3ad; }

/* Status inks the light theme never repainted: the dark-card greens, reds and ambers used for
   deltas, severity words and notes read at ~2:1 on white. Same idiom as the rgba() remaps above:
   match the inline signature, repaint the ink. Pinned to `color:` so a border of the same hue is
   left alone. */
#cv-app.cv-cards-light [style*="color:#7fc79c"], #cv-app.cv-page-light [style*="color:#7fc79c"],
#cv-app.cv-cards-light [style*="color:#6abf9a"], #cv-app.cv-page-light [style*="color:#6abf9a"],
#cv-app.cv-cards-light [style*="color:#7ccaa8"], #cv-app.cv-page-light [style*="color:#7ccaa8"],
#cv-app.cv-cards-light [style*="color:#7fd0ab"], #cv-app.cv-page-light [style*="color:#7fd0ab"] { color: #1b6e4a !important; }
#cv-app.cv-cards-light [style*="color:#ef8b80"], #cv-app.cv-page-light [style*="color:#ef8b80"],
#cv-app.cv-cards-light [style*="color:#ef6a5a"], #cv-app.cv-page-light [style*="color:#ef6a5a"],
#cv-app.cv-cards-light [style*="color:#ff9d8f"], #cv-app.cv-page-light [style*="color:#ff9d8f"] { color: #a4322f !important; }
#cv-app.cv-cards-light [style*="color:#e8a060"], #cv-app.cv-page-light [style*="color:#e8a060"],
#cv-app.cv-cards-light [style*="color:#f7c390"], #cv-app.cv-page-light [style*="color:#f7c390"] { color: #8a4b0a !important; }
#cv-app.cv-cards-light [style*="color:#d4b455"], #cv-app.cv-page-light [style*="color:#d4b455"],
#cv-app.cv-cards-light [style*="color:#c8a84b"], #cv-app.cv-page-light [style*="color:#c8a84b"],
#cv-app.cv-cards-light [style*="color:#d9b45a"], #cv-app.cv-page-light [style*="color:#d9b45a"],
#cv-app.cv-cards-light [style*="color:#d9be6a"], #cv-app.cv-page-light [style*="color:#d9be6a"],
#cv-app.cv-cards-light [style*="color:#e8c86a"], #cv-app.cv-page-light [style*="color:#e8c86a"] { color: #7d6112 !important; }
#cv-app.cv-cards-light [style*="color:#8fbde8"], #cv-app.cv-page-light [style*="color:#8fbde8"],
#cv-app.cv-cards-light [style*="color:#7ab0e8"], #cv-app.cv-page-light [style*="color:#7ab0e8"],
#cv-app.cv-cards-light [style*="color:#92c1ee"], #cv-app.cv-page-light [style*="color:#92c1ee"] { color: #1c5a9e !important; }
#cv-app.cv-cards-light [style*="color:#98a1aa"], #cv-app.cv-page-light [style*="color:#98a1aa"],
#cv-app.cv-cards-light [style*="color:#8f8f8f"], #cv-app.cv-page-light [style*="color:#8f8f8f"],
#cv-app.cv-cards-light [style*="color:#aab4c2"], #cv-app.cv-page-light [style*="color:#aab4c2"] { color: #5f6c7b !important; }

/* AUDIT IN PROGRESS (2026-09-18): the Results dashboard's note for a cycle whose report is not
   final. Sits with #cv-came-from, above the page body; the scores it explains are withheld in JS. */
#cv-inprogress { margin: 0 22px 12px; padding: 9px 14px; border-radius: 6px; font-size: 13px; line-height: 1.5;
  background: rgba(200, 168, 75, .14); border: 1px solid rgba(200, 168, 75, .55); color: #f0e6c8; }
#cv-inprogress b { color: #cdae57; }
#cv-app.cv-cards-light #cv-inprogress { background: #fbf4dd; border-color: #d9c27a; color: #5a4a12; }
#cv-app.cv-cards-light #cv-inprogress b { color: #8b6914; }

/* THE REQUEST LIST AS A WORKLIST (2026-09-18): one table, folded categories, a tray for dropped
   files, a bulk bar for ticked rows, a drop target over the page. Dark first; the light theme
   block below it. Text colours here are classes on purpose: the light theme cannot see inline hex. */
.cv-req-tb { font: inherit; font-size: 12px; padding: 5px 10px; border-radius: 5px; cursor: pointer; text-decoration: none;
  border: 1px solid #454545; background: #2a2a2a; color: #c8c8c8; white-space: nowrap; }
.cv-req-tb:hover { border-color: #5a5a5a; color: #e8e8e8; }
.cv-req-tb.on { border-color: var(--brand-secondary); color: var(--accent); box-shadow: inset 0 0 0 1px var(--brand-secondary); }
.cv-req-tbl td { padding: 6px 10px; vertical-align: middle; }
.cv-req-grp td { background: #1e1e1e; border-top: 1px solid #363636; border-bottom: 1px solid #363636; font-size: 12.5px; color: #c8c8c8; }
.cv-req-caret { display: inline-block; width: 12px; color: #8f8f8f; font-size: 11px; }
.cv-req-title { cursor: pointer; }
.cv-req-title:hover { color: #dbe9f8; }
.cv-req-det td { background: #202020; border-bottom: 1px solid #363636; }
.cv-req-droppable.over td { background: rgba(122,176,232,.14); box-shadow: inset 0 0 0 1px #7ab0e8; }
.cv-req-bulkbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; padding: 8px 10px; border-radius: 6px;
  background: rgba(122,176,232,.10); border: 1px solid rgba(122,176,232,.35); font-size: 12.5px; color: #dbe9f8; }
.cv-req-bulkgrp { display: inline-flex; align-items: center; gap: 4px; }
.cv-req-tray { margin-top: 10px; padding: 10px 12px; border-radius: 6px; background: rgba(217,190,106,.08); border: 1px solid rgba(217,190,106,.45); color: #e8e8e8; }
.cv-req-trayrow { display: grid; grid-template-columns: minmax(160px, .8fr) minmax(0, 2fr) auto; gap: 8px 14px; align-items: center; padding: 6px 0; border-top: 1px solid rgba(255,255,255,.07); }
.cv-req-trayrow.err { background: rgba(224,128,128,.08); }
.cv-req-trayfile { font-size: 12.5px; min-width: 0; }
.cv-req-trayfile b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-req-trayfile span { color: #8f8f8f; font-size: 11px; }
.cv-req-traypick { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; min-width: 0; }
.cv-req-trayst { font-size: 12px; white-space: nowrap; }
.cv-req-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: 600; padding: 2px 6px 2px 8px; border-radius: 9px;
  background: rgba(45,107,82,.28); color: #8fd3ae; max-width: 320px; }
.cv-req-chip button { font: inherit; border: 0; background: none; color: inherit; cursor: pointer; padding: 0 2px; opacity: .8; }
.cv-req-sugg { font: inherit; font-size: 11.5px; padding: 2px 8px; border-radius: 9px; cursor: pointer; border: 1px dashed #6a7a8a; background: transparent; color: #c8c8c8; max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-req-sugg:hover { border-style: solid; color: #dbe9f8; }
.cv-req-flash { margin: 0 0 10px; padding: 8px 12px; border-radius: 6px; font-size: 13px; background: rgba(45,107,82,.22); border: 1px solid rgba(143,211,174,.5); color: #8fd3ae; }
#req-dropzone { position: fixed; inset: 0; z-index: 9000; display: none; align-items: center; justify-content: center; pointer-events: none;
  background: rgba(10,20,30,.55); border: 4px dashed #7ab0e8; box-sizing: border-box; }
#req-dropzone > div { pointer-events: none; text-align: center; color: #fff; background: rgba(20,30,40,.92); padding: 22px 30px; border-radius: 10px; font-size: 16px; }
#req-dropzone > div b { display: block; font-size: 19px; margin-bottom: 6px; }
#req-dropzone > div span { font-size: 13px; color: #c8d8e8; }
#cv-app.cv-cards-light .cv-req-tb { border-color: #c9d2dc; background: #fff; color: #33404d; }
#cv-app.cv-cards-light .cv-req-tb:hover { color: #1c2a38; border-color: #a9b6c4; }
#cv-app.cv-cards-light .cv-req-grp td { background: #eef2f6; border-color: #dbe2ea; color: #33404d; }
#cv-app.cv-cards-light .cv-req-caret { color: #6b7885; }
#cv-app.cv-cards-light .cv-req-title:hover { color: #1c5a9e; }
#cv-app.cv-cards-light .cv-req-det td { background: #f6f8fa; border-color: #dbe2ea; }
#cv-app.cv-cards-light .cv-req-bulkbar { background: #eaf2fb; border-color: #b9d3ee; color: #1c3a5e; }
#cv-app.cv-cards-light .cv-req-tray { background: #fbf6e6; border-color: #e2cf8a; color: #2a2a2a; }
#cv-app.cv-cards-light .cv-req-trayrow { border-color: rgba(0,0,0,.07); }
#cv-app.cv-cards-light .cv-req-trayfile span { color: #6b7885; }
#cv-app.cv-cards-light .cv-req-chip { background: #dff1e7; color: #1f5a3c; }
#cv-app.cv-cards-light .cv-req-sugg { border-color: #8a9aab; color: #33404d; }
#cv-app.cv-cards-light .cv-req-flash { background: #e3f4ea; border-color: #9ed3b6; color: #1f5a3c; }
@media (max-width: 760px) {
  .cv-req-trayrow { grid-template-columns: 1fr; gap: 6px; }
  .cv-req-trayst { white-space: normal; }
}
/* HOW IT WORKS on the Requests page (2026-09-18): open until dismissed once; ? brings it back. */
.cv-req-help { margin-top: 10px; padding: 10px 14px 6px; border-radius: 6px; background: rgba(122,176,232,.08); border: 1px solid rgba(122,176,232,.3); color: #d0d8e0; font-size: 12.5px; line-height: 1.55; }
.cv-req-helpcols { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 4px 28px; margin-top: 4px; align-items: start; }
.cv-req-helph { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #a8a8a8; margin: 6px 0 2px; }
.cv-req-help ol, .cv-req-help ul { margin: 0 0 6px; padding-left: 20px; }
.cv-req-help li { margin: 2px 0; }
.cv-req-help b { color: #e8eef4; }
#cv-app.cv-cards-light .cv-req-help { background: #eef4fb; border-color: #c3d6ec; color: #33404d; }
#cv-app.cv-cards-light .cv-req-help b { color: #1c2a38; }
#cv-app.cv-cards-light .cv-req-helph { color: #6b7885; }
.cv-req-help.collapsed { padding: 7px 12px; cursor: pointer; font-size: 12.5px; }
.cv-req-help.collapsed:hover { border-color: rgba(122,176,232,.6); }
.cv-req-help.collapsed:focus-visible { outline: 2px solid var(--brand-secondary); outline-offset: 1px; }
.cv-req-helphead { cursor: pointer; user-select: none; }
.cv-req-helphead:focus-visible { outline: 2px solid var(--brand-secondary); outline-offset: 1px; border-radius: 4px; }
.cv-req-helptbl { border-collapse: collapse; width: 100%; font-size: 12.5px; margin: 0 0 6px; }
.cv-req-helptbl th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: #a8a8a8; padding: 4px 8px 4px 0; border-bottom: 1px solid rgba(255,255,255,.12); }
.cv-req-helptbl td { padding: 2px 8px 3px 0; vertical-align: top; border-bottom: 1px solid rgba(255,255,255,.07); line-height: 1.55; }
.cv-req-helptbl tr:first-child td { padding-top: 0; }
.cv-req-helptbl td:first-child { width: 42%; font-weight: 600; color: #e8eef4; }
.cv-req-helptbl kbd { font: inherit; font-size: 11.5px; padding: 0 5px; border: 1px solid #6a7a8a; border-radius: 4px; }
#cv-app.cv-cards-light .cv-req-helptbl th { color: #6b7885; border-color: #d5dde6; }
#cv-app.cv-cards-light .cv-req-helptbl td { border-color: #e6ebf0; }
#cv-app.cv-cards-light .cv-req-helptbl td:first-child { color: #1c2a38; }
#cv-app.cv-cards-light .cv-req-helptbl kbd { border-color: #a9b6c4; }


/* ── TERMS AND PRIVACY (2026-09-24) ───────────────────────────────────────────
   Read by a client's IT administrator from Microsoft's consent screen, before they have an
   account and often before they have decided to trust us. So: a reading measure rather than the
   app's dense working layout, and light regardless of the dashboard theme, because a legal
   document that arrives in dark mode reads as a web app rather than as a document. */
.cv-legal {
  max-width: 44rem; margin: 0 auto; padding: 56px 24px 72px;
  font-family: var(--font); color: #24303d; background: #ffffff;
}
body:has(.cv-legal) { background: #ffffff; }
.cv-legal-head { border-bottom: 1px solid #e2e8ef; padding-bottom: 20px; margin-bottom: 30px; }
.cv-legal-brand {
  font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: #1c5a9e; margin-bottom: 12px;
}
.cv-legal h1 { font-family: var(--font-serif); font-size: 34px; margin: 0; color: #172430; }
.cv-legal-date { font-size: 13px; color: #6b7684; margin: 8px 0 0; }
.cv-legal-body { font-size: 15.5px; line-height: 1.72; }
.cv-legal-body h2 {
  font-size: 15px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: #1c5a9e; margin: 34px 0 10px;
}
.cv-legal-body p { margin: 0 0 14px; }
.cv-legal-body ul { margin: 0 0 16px; padding-left: 22px; }
.cv-legal-body li { margin-bottom: 7px; }
.cv-legal-body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13.5px;
  background: #f1f5f9; padding: 1px 5px; border-radius: 3px;
}
.cv-legal-body a, .cv-legal-foot a { color: #1c5a9e; }
.cv-legal-lead { font-size: 17px; line-height: 1.65; color: #3b4652; margin-bottom: 26px; }
/* The bracketed drafting notes. Visibly unfinished ON PURPOSE -- a placeholder that looks like
   prose is one that ships as prose. */
.cv-legal-note {
  margin-top: 34px; padding: 12px 14px; border-left: 3px solid #c8a84b;
  background: #fdf8ec; font-size: 14px; color: #6a5a2e;
}
.cv-legal-foot {
  margin-top: 48px; padding-top: 18px; border-top: 1px solid #e2e8ef;
  font-size: 13px; color: #6b7684;
}
.cv-legal-foot p { margin: 0 0 5px; }
@media (max-width: 600px) { .cv-legal { padding: 36px 18px 56px; } .cv-legal h1 { font-size: 27px; } }
