/* Fortinet Practice - GSI Portfolio-Alignment Portal
   System font stack (no external fonts; strict CSP). */

:root {
  --fg-red: #da291c;
  --fg-red-dark: #b11e14;
  --blue: #175cd3;
  --teal: #0a7d76;
  --amber: #b7791f;
  --ink: #16233d;
  --muted: #5b6b85;
  --line: #e5e9f0;
  --bg: #f4f6f9;
  --card: #ffffff;
  --head: #101b33;

  --radius: 12px;
  --shadow: 0 1px 2px rgba(16, 27, 51, .06), 0 1px 3px rgba(16, 27, 51, .05);
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
    Arial, "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  background: var(--bg);
  color: var(--ink);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: 1440px; margin: 0 auto; padding: 24px 32px; }

/* ------------------------------------------------------------- masthead */
.masthead {
  background: linear-gradient(118deg, #0a0e1f 0%, #191233 55%, #331636 100%);
  color: #fff;
  border-bottom: 3px solid var(--fg-red);
}
.masthead-inner {
  max-width: 1440px; margin: 0 auto; padding: 30px 32px;
  display: flex; justify-content: space-between; align-items: flex-start; gap: 32px;
}
.brand { display: flex; align-items: flex-start; gap: 18px; }
.brand-logo { height: 34px; width: auto; margin-top: 4px; }
.eyebrow {
  margin: 0 0 2px; font-size: 12px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-red);
}
.brand-text h1 { margin: 0; font-size: 30px; font-weight: 800; letter-spacing: -.01em; }
.brand-sub { margin: 8px 0 0; max-width: 620px; color: #b9c1d4; font-size: 14px; }

.stats { display: flex; flex-wrap: wrap; gap: 40px; flex-shrink: 0; }
.stat { display: flex; flex-direction: column; align-items: flex-start; }
.stat-num { font-size: 36px; font-weight: 800; line-height: 1; }
.stat-label {
  margin-top: 6px; font-size: 11px; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: #9aa7bd;
}

/* -------------------------------------------------- board toggle (technical/sales) */
.board-toggle-bar { background: var(--card); border-bottom: 1px solid var(--line); }
.board-toggle {
  max-width: 1440px; margin: 0 auto; padding: 12px 32px;
  display: flex; align-items: center;
}
.board-tab {
  padding: 8px 20px; border: 1px solid var(--line); background: #fff; color: var(--ink);
  font-weight: 700; font-size: 13px; letter-spacing: .03em; text-decoration: none;
  transition: background .12s, color .12s, border-color .12s;
}
.board-tab + .board-tab { margin-left: -1px; }
.board-tab:first-child { border-radius: 999px 0 0 999px; }
.board-tab:last-child { border-radius: 0 999px 999px 0; }
.board-tab:hover { border-color: #c3ccdb; z-index: 1; }
.board-tab.active { background: var(--fg-red); color: #fff; border-color: var(--fg-red); z-index: 2; }

/* ------------------------------------------------------------- controls */
.controls {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 16px 20px; margin-bottom: 22px;
}
.control-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.control-row + .control-row { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 12px; }
.control-label {
  flex: 0 0 auto; min-width: 110px; font-size: 12px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.searchform { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.search-input { width: 220px; max-width: 100%; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  padding: 8px 16px; border-radius: 999px; border: 1px solid var(--line);
  background: #fff; color: var(--ink); font-weight: 600; font-size: 13px; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.chip:hover { border-color: #c3ccdb; }
/* Chips are links now (server-side ?section= filter), so undo the anchor look. */
a.chip { text-decoration: none; display: inline-block; }
.chip-active { background: var(--fg-red); color: #fff; border-color: var(--fg-red); }
.chip-active:hover { border-color: var(--fg-red); }

/* --- progressive enhancement ------------------------------------------------
   Everything on the admin pages works as a plain form. admin.js / deals.js add
   `js-on` to <body> the moment they execute; anything marked .no-js-only is for
   the path where they never arrived (blocked CSP, unreadable static file,
   rewritten content-type) and is hidden once they have. Default-visible on
   purpose: if the stylesheet AND the script both fail, the user still gets the
   working controls rather than a page of invisible buttons. */
body.js-on .no-js-only { display: none !important; }
.nojs-note {
  background: #fffaeb; border: 1px solid #fedf89; color: #93370d;
  border-radius: var(--radius); padding: 10px 14px; margin: 0 0 16px;
  font-size: 13px; line-height: 1.5;
}
/* Per-row / per-GSI name save. The bulk section save carries enum cells ONLY --
   free text goes through these so a value like "FortiWeb (MCP)" is transmitted
   only when deliberately saved, not on every bulk submit (a FortiWeb syntax
   signature reads IDENT(ARGS) as a SQL function call and blocks the POST). */
.rowname-form { display: none; }
.namesave {
  background: transparent; border: 1px solid var(--line); border-radius: 6px;
  color: var(--muted); cursor: pointer; font-size: 11px; font-weight: 700;
  padding: 2px 7px; margin-left: 6px; white-space: nowrap;
}
.namesave:hover { border-color: var(--fg-red); color: var(--fg-red); }
.section-save, .deal-save { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.addgsi-form { display: inline-flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
/* Same cascade trap as .path-add-name above -- keep the compound selector. */
.addgsi-form .addgsi-name { width: 220px; flex: 0 0 220px; min-width: 0; border: 1px solid var(--line) !important; }
/* <details> replaces the old JS class toggle for the roster panel. */
.roster-summary {
  cursor: pointer; font-weight: 700; font-size: 13px; color: var(--ink);
  list-style: none; padding: 2px 0;
}
.roster-summary::-webkit-details-marker { display: none; }
.roster-summary::before { content: "\25B8  "; color: var(--muted); }
details[open] > .roster-summary::before { content: "\25BE  "; }

.legend { display: flex; flex-wrap: wrap; gap: 18px; }
.legend-item { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--muted); }
.legend-swatch { min-width: 26px; }

/* ------------------------------------------------------- alignment cells */
.cell {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 30px; padding: 0 7px; border-radius: 8px;
  font-weight: 800; font-size: 12px; box-sizing: border-box;
}
/* not engaged / not adopted: faint dash */
.cell-none { background: transparent; color: #b7c0cf; border: 2px solid transparent; }
/* journey FOCUS ramp (brand-red intensity: emerging -> established -> strategic) */
.cell-emerging    { background: #fdeaea; color: #b11e14; border: 2px solid #f6cfcb; }
.cell-established { background: #f0897f; color: #fff;     border: 2px solid #ea6d61; }
.cell-strategic   { background: var(--fg-red); color: #fff; border: 2px solid var(--fg-red-dark); }
/* product STANDARDIZATION ramp (green intensity: evaluating -> standardized -> preferred) */
.cell-evaluating   { background: #e6f4ee; color: #1c6b48; border: 2px solid #c3e6d4; }
.cell-standardized { background: #7cc9a4; color: #0f5233; border: 2px solid #57b98a; }
.cell-preferred    { background: #2d8f64; color: #fff;    border: 2px solid #24734f; }
/* deal-evidence markers on a cell */
.cell-win, .cell-deal { font-size: 10px; margin-left: 3px; line-height: 1; }
.cell-win { color: #f0b429; }
.cell-deal { color: currentColor; opacity: .75; }
.legend-ev { gap: 6px; }
.legend-ev .cell-win { color: var(--amber); }
.legend-ev .cell-deal { color: var(--muted); }
.cell-evi { margin-left: 5px; font-size: 11px; font-weight: 700; color: var(--muted); white-space: nowrap; }

/* ------------------------------------------------------------- tracks */
.track-section { margin-bottom: 26px; }
.track-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.track-badge {
  background: var(--jcolor, var(--blue)); color: #fff; padding: 4px 12px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
}
.track-count { color: var(--muted); font-size: 13px; }

/* ------------------------------------------- My Portfolio (Supervisor tier) */
.myportfolio-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.myportfolio-title { margin: 0; }
.myportfolio-legend { margin-bottom: 18px; }
.myportfolio-section { margin-bottom: 26px; }
.myportfolio-section-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.myportfolio-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.myportfolio-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 14px 16px;
}
.myportfolio-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.myportfolio-desc { font-size: 13px; color: var(--muted); margin: 4px 0; }
.myportfolio-status { font-size: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.myportfolio-empty {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 24px; text-align: center; color: var(--muted);
}

/* row label cell (journey / product name + tagline) */
.cert-name { font-weight: 700; color: var(--ink); vertical-align: middle; }
.cert-sub { display: block; margin-top: 3px; font-size: 12px; font-weight: 400; color: var(--muted); max-width: 360px; white-space: normal; }
.cert-sub-input { display: block; margin-top: 4px; font-size: 12px; color: var(--muted); width: auto; min-width: 220px; white-space: normal; }
/* the row-label column holds a wrapping tagline, so let it wrap (overrides the global nowrap) */
.matrix .col-cert { white-space: normal; }
.matrix .col-cert .cert-name { white-space: nowrap; }

/* One-line "there is more to the right" hint above a horizontally-scrolling
   region. Default-visible and REMOVED by portal.js when the region does not
   actually overflow -- the same default-visible discipline as `.no-js-only`,
   and for the same reason: with scripts blocked the wheel->horizontal helper is
   gone too, so that is precisely when a reader needs telling. */
.scroll-hint {
  margin: 0 0 6px; font-size: 12px; color: var(--muted);
  font-weight: 600; letter-spacing: .02em;
}

.table-scroll {
  overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); box-shadow: var(--shadow);
}
table.matrix { width: 100%; border-collapse: collapse; font-size: 14px; }
table.matrix thead th {
  background: var(--head); color: #fff; text-align: left; padding: 13px 16px;
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  white-space: nowrap; position: sticky; top: 0;
}
table.matrix th.col-cell, table.matrix td.col-cell { text-align: center; }
table.matrix tbody td { padding: 12px 16px; border-top: 1px solid var(--line); vertical-align: middle; }
table.matrix tbody tr:hover { background: #fafbfd; }
.col-cert { white-space: nowrap; }
.col-desc { color: #56678a; max-width: 380px; }
.col-notes { color: #24425f; max-width: 240px; font-size: 13px; }

.pagefoot { margin-top: 8px; padding: 18px 0; text-align: center; }
.foot-link, .save-status.err { color: var(--muted); }
.foot-link { font-size: 13px; text-decoration: none; }
.foot-link:hover { color: var(--fg-red); text-decoration: underline; }

/* ------------------------------------------------------------- admin bar */
.adminbar { background: #fff; border-bottom: 1px solid var(--line); }
.adminbar-inner { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; padding-top: 12px; padding-bottom: 12px; }
.adminbar-tag {
  background: var(--fg-red); color: #fff; padding: 3px 10px; border-radius: 6px;
  font-size: 11px; font-weight: 800; letter-spacing: .06em;
}
.adminbar-user { color: var(--muted); font-size: 13px; }
.adminbar-spacer { flex: 1; }
.save-status { font-size: 13px; font-weight: 600; min-width: 60px; }
.save-status.ok { color: #067647; }
.save-status.err { color: var(--fg-red); }
.inline-form { display: inline; margin: 0; }

.admin-actions { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; flex-wrap: wrap; }
.admin-hint { color: var(--muted); font-size: 13px; }
.scope-form { margin-bottom: 18px; }

/* roster panel (manage people across all paths) */
.roster-panel { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 16px 20px; margin-bottom: 22px; }
.roster-title { margin: 0 0 4px; font-size: 15px; }
.roster-hint { margin: 0 0 12px; font-size: 12.5px; color: var(--muted); }
.roster-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.roster-item { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--line); border-radius: 999px; padding: 5px 6px 5px 14px; background: #fbfcfe; }
.roster-name { font-weight: 700; font-size: 13px; }
.roster-paths { font-size: 12px; color: var(--muted); }
/* Delete-GSI controls in the roster panel. The pill is inline-flex, so let a
   long row wrap rather than stretching the panel and pushing the board wider. */
.roster-item { flex-wrap: wrap; }
.confirm-box { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); }
.confirm-box input { margin: 0; }

/* --- confirmation page (2026-08-07) -----------------------------------------
   Stage 2 of every destructive action, replacing a dialog plus a checkbox that
   was hidden under body.js-on -- see app/confirm.py for the two ways that left a
   button silently doing nothing. NOTHING here may be `.no-js-only`: this page IS
   the guard, so it must render identically in every script state. */
.confirm-strip {
  background: #fff5f4; border: 1px solid #f3c3bd; border-left: 4px solid var(--fg-red);
  border-radius: var(--radius); padding: 16px 20px; margin-bottom: 22px;
}
.confirm-strip-title { margin: 0 0 6px; font-size: 16px; color: var(--fg-red-dark); }
.confirm-strip-body { margin: 0 0 12px; font-size: 13.5px; max-width: 70ch; }
.confirm-strip-form { display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
  margin-top: 14px; }
/* Compound selector, and full-size rather than the muted 12px above: each of
   these agrees to lose something specific and unrecoverable, so it is body text,
   not a hint. */
.confirm-strip .confirm-box { display: flex; align-items: flex-start; gap: 7px;
  font-size: 13.5px; color: var(--ink); max-width: 70ch; }
.confirm-strip .confirm-box input { margin: 3px 0 0; flex: 0 0 auto; }
.confirm-strip-actions { display: flex; align-items: center; gap: 10px; margin-top: 4px; }

/* ------------------------------------------------------------- bulk edit
   The per-section bar plus the two tick-boxes that feed it. Both boxes belong to
   `#bulkf-<section>` via the HTML5 `form` attribute, not to the section form. */
.bulkbar {
  display: flex; align-items: flex-end; flex-wrap: wrap; gap: 12px;
  border: 1px solid var(--line); border-radius: 8px; background: #f8fafc;
  padding: 10px 12px; margin-bottom: 10px;
}
.bulkbar-label { font-weight: 700; color: var(--muted); text-transform: uppercase;
  letter-spacing: .04em; font-size: 11px; padding-bottom: 8px; }
.bulkbar-field { display: flex; flex-direction: column; gap: 3px; }
/* Compound selector: `.field-input { width: 100% }` sits elsewhere in this file
   and a bare rule would fight it at equal specificity. */
.bulkbar .field-input { width: auto; min-width: 150px; }
/* Inline with the name it selects, and NOT position:absolute -- an absolutely
   positioned descendant escapes `.table-scroll` clipping unless the cell is
   positioned, which is the page-overflow bug the sibling hit on 2026-07-27. */
.bulkpick { display: inline-flex; align-items: center; margin-right: 6px; }
.bulkpick input { margin: 0; }

/* add-to-path toolbar (per track section) */
.path-add { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.path-add-label { font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; font-size: 11px; }
.path-add-or { color: var(--muted); font-size: 12px; }
.path-add-select { border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; font: inherit; font-size: 13px; background: #fff; }
/* Compound selector on purpose: `.cell-input { width: 100% }` is declared LATER
   in this file, so a bare `.path-add-name` loses the cascade at equal
   specificity and the input stretches, pushing "+ Add" onto its own row. */
.path-add .path-add-name { width: auto; flex: 0 1 220px; min-width: 180px; border: 1px solid var(--line) !important; }
.btn-sm { padding: 5px 11px; font-size: 12px; }

/* empty-path note (public board) */
.empty-path { color: var(--muted); font-style: italic; }

/* buttons */
.btn {
  padding: 8px 15px; border-radius: 8px; font-weight: 600; font-size: 13px; cursor: pointer;
  border: 1px solid var(--line); background: #fff; color: var(--ink); text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.btn:not([disabled]):hover { border-color: #c3ccdb; }
/* Operator tier (2026-08-18): a control the signed-in admin may not use renders
   `disabled` with a title= explanation rather than vanishing, so the row does not
   look broken next to a full admin's view. Without this rule it was pixel-identical
   to a live button -- .btn sets `cursor: pointer` unconditionally -- so the only
   signal was a tooltip nobody would hover for, and clicking did nothing visible.
   The hover rules are scoped `:not([disabled])` rather than overridden afterwards,
   because restoring each variant's own background/border on hover would mean three
   more rules that have to stay in step with the variants they mirror. */
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn-primary { background: var(--fg-red); color: #fff; border-color: var(--fg-red); }
.btn-primary:not([disabled]):hover { background: var(--fg-red-dark); border-color: var(--fg-red-dark); }
.btn-ghost { background: #fff; }
.btn-block { width: 100%; padding: 11px; }

/* ------------------------------------------------------------- admin edit */
.matrix-admin td { padding: 8px 12px; }
.cell-input {
  width: 100%; min-width: 90px; border: 1px solid transparent; background: transparent;
  padding: 6px 8px; border-radius: 6px; font: inherit; color: inherit;
}
.cell-input:hover { border-color: var(--line); background: #fff; }
.cell-input:focus { border-color: var(--fg-red); outline: none; background: #fff; }
.cert-name-input { font-weight: 700; color: var(--ink); min-width: 220px; display: inline-block; width: auto; vertical-align: middle; }
.col-desc .cell-input { color: #56678a; }
.col-notes .cell-input { color: #24425f; font-size: 13px; }
.add-cert-level { width: 58px; min-width: 58px; display: inline-block; margin-right: 6px; text-align: center; }
.add-cert-name { width: auto; min-width: 200px; display: inline-block; }

.hdr-edit { display: flex; align-items: center; gap: 4px; }
.hdr-input {
  width: 88px; background: rgba(255, 255, 255, .08); border: 1px solid rgba(255, 255, 255, .22);
  color: #fff; border-radius: 6px; padding: 4px 6px; font-size: 12px; font-weight: 700;
}
.hdr-input:focus { background: #fff; color: var(--ink); outline: none; border-color: var(--fg-red); }
.col-del { background: transparent; border: none; color: rgba(255, 255, 255, .6); cursor: pointer; font-size: 17px; line-height: 1; padding: 0 2px; }
.col-del:hover { color: #fff; }

.col-actions { width: 34px; text-align: center; }
.row-del { background: transparent; border: none; color: #c0392b; cursor: pointer; font-size: 18px; line-height: 1; opacity: .55; }
.row-del:hover { opacity: 1; }

/* attainment cell editor: status select + target toggle + target-by date */
.att-cell { display: inline-flex; align-items: center; gap: 4px; }
.att-status {
  -webkit-appearance: none; appearance: none; border: 2px solid transparent; border-radius: 8px;
  padding: 4px 8px; font-weight: 700; font-size: 12px; cursor: pointer;
  min-width: 104px; text-align: center; text-align-last: center;
}
.att-status:focus { outline: 2px solid var(--ink); outline-offset: 1px; }
.att-none { background: #eef1f4; color: #6a7688; }
.att-emerging    { background: #fdeaea; color: #b11e14; }
.att-established { background: #f0897f; color: #fff; }
.att-strategic   { background: var(--fg-red); color: #fff; }
.att-evaluating   { background: #e6f4ee; color: #1c6b48; }
.att-standardized { background: #7cc9a4; color: #0f5233; }
.att-preferred    { background: #2d8f64; color: #fff; }
.att-target {
  background: transparent; border: none; cursor: pointer; font-size: 16px; line-height: 1;
  color: #c3ccdb; padding: 0 2px;
}
.att-target:hover { color: var(--fg-red); }
.att-cell.is-target .att-target { color: var(--fg-red); }
.att-date { display: none; width: 130px; border: 1px solid var(--line); border-radius: 6px; padding: 3px 5px; font: inherit; font-size: 12px; }
.att-cell.is-target .att-date { display: inline-block; }

.add-row td { background: #fbfcfe; }
.add-row .cell-input:hover { border-color: var(--line); }
.row-add { background: transparent; border: none; color: #067647; cursor: pointer; font-size: 20px; line-height: 1; font-weight: 700; }
.row-add:hover { color: #04593a; }

/* ------------------------------------------------------------- auth pages */
.auth-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.auth-card {
  background: #fff; border: 1px solid var(--line); border-radius: 16px; box-shadow: var(--shadow);
  padding: 34px 32px; width: 100%; max-width: 400px;
}
.auth-card-center { text-align: center; }
.auth-logo { height: 30px; width: auto; margin-bottom: 18px; }
.auth-title { margin: 0 0 4px; font-size: 21px; font-weight: 800; }
.auth-sub { margin: 0 0 20px; color: var(--muted); font-size: 14px; }
.auth-error {
  background: #fef3f2; border: 1px solid #fdd; color: #b42318; border-radius: 8px;
  padding: 10px 14px; font-size: 13px; margin-bottom: 16px;
}
.auth-error ul { margin: 0; padding-left: 18px; }
.auth-form { display: flex; flex-direction: column; gap: 14px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field-label { font-size: 13px; font-weight: 600; color: var(--ink); }
.field-input {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; font: inherit;
  color: var(--ink); background: #fff;
}
.field-input:focus { outline: none; border-color: var(--fg-red); box-shadow: 0 0 0 3px rgba(218, 41, 28, .12); }
.field-help { margin: -4px 0 4px; font-size: 12px; color: var(--muted); }
.auth-note { margin: 16px 0 0; font-size: 12px; color: var(--muted); text-align: center; }
.error-code { font-size: 56px; font-weight: 800; margin: 6px 0; color: var(--fg-red); }

.hidden { display: none !important; }

/* ------------------------------------------------------------- viewer identity (gated board) */
.viewer-id { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.viewer-email { color: #b9c1d4; font-size: 13px; }

/* ------------------------------------------------------------- admin nav (whitelist/viewers) */
.adminnav { display: flex; gap: 4px; }
.adminnav-link {
  padding: 6px 12px; border-radius: 8px; font-size: 13px; font-weight: 600;
  color: var(--muted); text-decoration: none;
}
.adminnav-link:hover { background: #f1f3f7; color: var(--ink); }
.adminnav-link.active { background: var(--fg-red); color: #fff; }

/* ------------------------------------------------------------- flash banners */
.flash {
  border-radius: 8px; padding: 10px 14px; font-size: 13.5px; margin-bottom: 16px;
  border: 1px solid var(--line);
}
.flash-ok { background: #ecfdf3; border-color: #abefc6; color: #067647; }
.flash-error { background: #fef3f2; border-color: #fdd; color: #b42318; }

/* ------------------------------------------------------------- allowlist / viewers data tables */
.dtable-scroll { overflow-x: auto; }
.dtable {
  width: 100%; border-collapse: collapse; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
.dtable th, .dtable td { padding: 10px 14px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); }
.dtable th { background: #f7f8fb; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.dtable tr:last-child td { border-bottom: none; }
.dtable td.col-actions, .dtable th.col-actions { width: 1%; white-space: nowrap; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; }
.nowrap { white-space: nowrap; }

/* The allowlist bulk tick column (2026-08-12). `.bulkbar*` and `.bulkpick`
   already exist for the board's bulk; only the narrow column is new. */
.dtable td.col-pick, .dtable th.col-pick { width: 1%; white-space: nowrap; }

/* Pager for the audit-log browser (2026-08-12). New vocabulary in this repo --
   the sibling showcase portal styles the same two class names, so keep them
   spelled identically if either is ever ported again. */
.pager { display: flex; gap: 12px; align-items: center; justify-content: center;
         margin: 14px 0 0; flex-wrap: wrap; }
.pager-pos { font-size: 12px; color: var(--muted); }

.badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 11px;
  font-weight: 700; letter-spacing: .03em; background: #eef1f4; color: #5a6378;
}
.badge-pub { background: #ecfdf3; color: #067647; }
.badge-draft { background: #fff5e6; color: #b7791f; }

.formcard {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 22px 24px; margin-bottom: 22px; max-width: 720px;
}
.inline-add { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.inline-add .grow { flex: 1; min-width: 220px; }
.field-input.compact { width: auto; }
.form-actions { display: flex; gap: 10px; }
.btn-danger { background: #fff; color: #b42318; border-color: #f3c0bb; }
.btn-danger:not([disabled]):hover { background: #fef3f2; border-color: #e79b93; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; background: #f1f3f7; padding: 1px 5px; border-radius: 4px; }

/* ------------------------------------------------------------- TOTP enrollment */
.enroll-card { max-width: 640px; }
.enroll-grid { display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap; margin: 18px 0; }
.enroll-qr {
  background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: 10px; line-height: 0;
}
.enroll-qr img { display: block; width: 200px; height: 200px; }
.enroll-key { flex: 1; min-width: 240px; }
.keybox {
  background: #f1f3f7; border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px;
  font-size: 15px; letter-spacing: .08em; word-break: break-all; margin: 4px 0 10px;
}

/* ------------------------------------------------------------- deals + wins */
.masthead-slim .masthead-inner { padding-top: 22px; padding-bottom: 22px; }
.masthead-slim .brand-text h1 { font-size: 24px; }

.warn-panel { background: #fff8ec; border: 1px solid #f3d9a4; border-radius: var(--radius); padding: 14px 18px; margin-bottom: 22px; }
.warn-title { margin: 0 0 8px; font-size: 14px; color: #a35b00; }
.warn-list { margin: 0; padding-left: 18px; font-size: 13px; color: #6b5220; }
.warn-list li { margin: 3px 0; }

.formcard-title { margin: 0 0 14px; font-size: 15px; }
.deal-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.deal-form .field-input { min-width: 150px; }
.deal-form .grow { flex: 1; min-width: 220px; }

.deal-stage { margin-bottom: 22px; }
.stage-badge { background: var(--muted); }
.stage-identified  { background: #8a94a8; }
.stage-qualified   { background: var(--blue); }
.stage-proposal    { background: var(--teal); }
.stage-negotiation { background: var(--amber); }
.stage-closedwon   { background: #2d8f64; }
.stage-closedlost  { background: #9aa3b2; }

.deal-empty { color: var(--muted); font-style: italic; font-size: 13px; margin: 4px 0 0; }
.deal-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 14px 16px; margin-bottom: 12px; }
.deal-top { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.deal-id { display: flex; flex-direction: column; min-width: 200px; flex: 1; }
.deal-account { font-weight: 800; color: var(--ink); }
.deal-alias { font-size: 12px; color: var(--muted); }
.deal-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.deal-tag { font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: 999px; letter-spacing: .03em; }
.deal-partner { background: #eef2fb; color: var(--blue); }
.deal-journey { background: #f2eef8; color: #6b46b0; }
.deal-stage-sel { min-width: 128px; }
.deal-body { margin-top: 12px; display: flex; flex-direction: column; gap: 10px; }
.deal-field { display: flex; flex-direction: column; gap: 3px; }
.deal-field-label { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.deal-field .cell-input { border: 1px solid var(--line); background: #fff; }
/* Long-form deal description. A textarea, so it needs its own sizing -- the
   shared .cell-input rules assume a one-line input. */
.deal-field-wide { grid-column: 1 / -1; }
.deal-desc {
  width: 100%; min-height: 96px; resize: vertical; line-height: 1.5;
  font: inherit; font-size: 13.5px; padding: 8px 10px;
  border: 1px solid var(--line) !important; border-radius: 8px; background: #fff;
}
.deal-desc:focus { border-color: var(--fg-red) !important; outline: none; }
.deal-field-hint { font-weight: 400; color: var(--muted); text-transform: none; letter-spacing: 0; }

.prodchk-row { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 4px; }
.prodchk { font-size: 13px; display: inline-flex; align-items: center; gap: 5px; color: var(--ink); }
.refchk { font-size: 13px; display: inline-flex; align-items: center; gap: 6px; color: #067647; font-weight: 600; }
.deal-closed { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; font-size: 13px; color: var(--muted); }
.deal-closed-on { font-weight: 600; }

/* `.win-filter` (the old JS toggle row) is gone -- the gallery uses the shared
   `.filterbar`/`.chiprow` server-side controls now. */
.win-gallery + .filterbar, .filterbar + .win-gallery { margin-top: 4px; }
.win-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 16px; }
.win-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 18px 20px; display: flex; flex-direction: column; gap: 8px; }
.win-internal { border-left: 3px solid var(--amber); }
.win-card-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 2px; }
.win-badge { font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: 999px; background: #eef2fb; color: var(--blue); letter-spacing: .03em; }
.win-badge-journey { background: #f2eef8; color: #6b46b0; }
.win-title { margin: 4px 0 0; font-size: 18px; font-weight: 800; }
.win-account { margin: 0; font-size: 12px; color: var(--muted); }
.win-usecase, .win-value, .win-outcome { margin: 4px 0 0; font-size: 13.5px; }
.win-value { color: #43506a; }
.win-products { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0; }
.win-prod { font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 6px; background: #fdeaea; color: var(--fg-red-dark); }
.win-repl { margin-top: 6px; }
.win-repl-body { margin: 4px 0 0; font-family: inherit; font-size: 12.5px; color: #43506a; white-space: pre-wrap; background: #f7f8fb; border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; }
.win-actions { margin-top: 8px; }

/* ------------------------------------------------------------- win-card print sheet */
.print-toolbar { display: flex; gap: 10px; justify-content: flex-end; max-width: 800px; margin: 18px auto 0; padding: 0 20px; }
.card-page { max-width: 800px; margin: 18px auto; padding: 0 20px; }
.card-sheet { background: #fff; border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow); padding: 40px 44px; }
.card-sheet-head { display: flex; justify-content: space-between; align-items: center; gap: 16px; border-bottom: 2px solid var(--fg-red); padding-bottom: 14px; margin-bottom: 14px; }
.card-logo { height: 28px; }
.card-sheet-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.card-eyebrow { margin: 0; font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-red); }
.card-title { margin: 4px 0 6px; font-size: 27px; font-weight: 800; }
.card-usecase { margin: 0 0 8px; font-size: 15px; color: #43506a; }
.card-block { margin-top: 16px; }
.card-h2 { margin: 0 0 6px; font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.card-block p { margin: 0; font-size: 14px; }
.card-foot { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--line); font-size: 12px; color: var(--muted); }

/* ------------------------------------------------- deal tracker (2026-07-29)
   Compound selectors throughout, never source order: `.cell-input { width:100% }`
   silently won the cascade over an equal-specificity earlier rule on 2026-07-27
   and stretched inputs full width. Anything here that must not be 100% wide says
   so on a compound selector. */
.deal-stats { gap: 28px; margin: 0 0 18px; }
.deal-stats .stat { min-width: 150px; }
.deal-stats .stat-note { margin-top: 4px; font-size: 12px; color: var(--muted); max-width: 220px; }
.deal-stats .stat-warn .stat-num { color: var(--fg-red); }

.filterbar { margin: 0 0 18px; display: flex; flex-direction: column; gap: 8px; }
.filterbar .searchform { gap: 10px 14px; align-items: flex-end; }
.filterbar .search-input { width: 260px; }
.filter-summary { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }
/* Why the card matched when the matching text is on the deal's own page. */
.deal-matchnote {
  margin: 6px 0 0; padding: 5px 10px; border-radius: 8px;
  background: #eef2fb; color: #2b4a7d; font-size: 12px;
}
.chiprow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.chiprow-label {
  font-size: 11px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.chiprow .chip { padding: 6px 13px; font-size: 12.5px; }
.chip-n {
  display: inline-block; margin-left: 5px; padding: 0 6px; border-radius: 999px;
  background: rgba(0, 0, 0, .08); font-size: 11px; font-weight: 700;
}
.chip-active .chip-n { background: rgba(255, 255, 255, .28); }

/* An at-risk deal is marked by a border AND a spelled-out reason line -- colour
   is never the only channel, so the flag survives greyscale and colour-blindness
   (the same rule the report's charts follow). */
.deal-card.deal-risk { border-left: 4px solid var(--fg-red); }
.deal-riskline {
  margin: 8px 0 0; padding: 6px 10px; border-radius: 8px;
  background: #fdeaea; color: #8c1912; font-size: 12.5px;
}
.deal-meta { font-size: 12px; color: var(--muted); display: flex; flex-wrap: wrap; gap: 6px; }
/* The index row: the daily hygiene fields side by side, wrapping on narrow
   viewports rather than overflowing (there is no horizontal scroll region on
   this page, so nothing may be pushed out of reach). */
.deal-body-slim {
  flex-direction: row; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px;
}
.deal-body-slim .deal-field { min-width: 150px; }
.deal-body-slim .deal-field-grow { flex: 1 1 320px; }
.deal-save .deal-meta { margin-right: auto; }
.deal-size { background: #eef7f2; color: #1f7a52; }
.deal-owner { background: #f5f2e8; color: #8a6d1f; }
a.deal-account { text-decoration: none; }
a.deal-account:hover { color: var(--fg-red); text-decoration: underline; }
.deal-save { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 4px; }
.deal-del { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.field-inline {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--muted);
}
.field-inline .field-input { width: auto; }
.adminbar-scope { display: inline-flex; gap: 5px; flex-wrap: wrap; }

/* Append-only activity + stage history.
   `.formcard` caps at 720px, which is right for a short add-form but leaves the
   detail page's editor in a narrow column with the rest of a 1366px viewport
   empty. Overridden on a COMPOUND selector rather than by declaring a later
   `.formcard` rule -- source-order overrides at equal specificity are what
   silently resized the board inputs on 2026-07-27. */
.deal-detail .formcard { max-width: 1080px; }
.deal-detail .deal-body {
  flex-direction: row; flex-wrap: wrap; gap: 12px 16px; align-items: flex-end;
}
.deal-detail .deal-field { flex: 1 1 240px; }
.deal-detail .deal-field-wide { flex: 1 1 100%; }
.deal-detail .deal-save { width: 100%; }
.deal-detail .formcard + .formcard { margin-top: 18px; }
.note-form { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.note-form .cell-input { width: 100%; min-height: 68px; resize: vertical; font: inherit; font-size: 13.5px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; background: #fff; }
.timeline { list-style: none; margin: 12px 0 0; padding: 0; }
/* Row layout, not a column: a stage transition is one short sentence
   ("Identified -> Qualified"), and stacking its parts put the arrow and the new
   stage on separate lines, which read as two events instead of one. */
.timeline-item {
  padding: 7px 0 7px 12px; border-left: 2px solid var(--line);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
}
.timeline-item + .timeline-item { margin-top: 2px; }
.timeline-when { font-size: 11.5px; color: var(--muted); flex: 0 0 auto; }
.timeline-who { font-size: 11.5px; color: var(--muted); font-weight: 600; flex: 0 0 auto; }
/* A note body keeps its author's line breaks and takes its own row. */
.timeline-body { margin: 0; font-size: 13.5px; white-space: pre-wrap; flex: 1 1 100%; }
/* A stage transition is one short sentence and stays on the timestamp's row --
   deliberately WITHOUT pre-wrap, so the template's own indentation is collapsed. */
.timeline-stage { font-size: 13.5px; flex: 1 1 auto; }

/* The allowlist row's current scope, restated below the multi-select: a
   multiple-select shows only as many rows as it is tall, so on an entry with
   several GSIs the selection is not fully visible in the control itself. */
.scope-current { margin: 4px 0 0; display: flex; gap: 4px; flex-wrap: wrap; }
/* The dark fill needs a light foreground: `.badge` sets `color: #5a6378`, and
   grey-on-olive measures 1.23:1 -- effectively unreadable. White is 4.90:1 (AA).
   PRE-EXISTING and found by screenshotting the expiry port on 2026-08-03: this
   class was already live on "sees account names" and "all GSIs", both of which
   have been illegible since they shipped. A background-only override inherits
   whatever the base class sets, so change the pair together. */
.badge-warn { background: #8a6d1f; color: #fff; }
.myportfolio-links { display: flex; gap: 8px; flex-wrap: wrap; }
.myportfolio-gsi { font-weight: 700; margin-right: 6px; }

/* ------------------------------------------------------------ viewer expiry
   Ported with `allowlist.expires_at` (2026-08-03). These class names arrived
   with the template from ../gsi-showcase-portal and were styled ONLY there --
   the ported markup would otherwise have rendered an unstyled date pile with no
   error anywhere, which is the ported-CSS-class-drift trap this workspace has
   hit before. Check every class against the TARGET stylesheet when porting. */
.expiry-cell { white-space: nowrap; }
.expiry-cell .rowedit-summary { white-space: normal; }

/* A `<details>` row editor: collapsed by default so the column stays narrow on
   an already-wide table, and it works with no scripting at all -- `<details>` is
   native, which is the whole reason it is used here rather than a JS toggle. */
.rowedit { margin: 6px 0 0; }
.rowedit-summary {
  display: inline-block; cursor: pointer; font-size: 11.5px; color: var(--fg-red);
  padding: 1px 0; list-style-position: inside;
}
.rowedit-summary:hover { text-decoration: underline; }
.rowedit[open] > .rowedit-summary { margin-bottom: 6px; }
.rowedit .inline-form { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }

/* The add form's date field carries its own label -- an unlabelled date input in
   a flat row of text boxes says nothing about what it bounds. */
.compact-label { display: inline-flex; flex-direction: column; gap: 3px;
  font-size: 11px; color: var(--muted); }

/* A one-line standing notice above a table (counts that are true of the whole
   set, not of the filtered view). Deliberately not `.flash` -- it is not the
   result of an action and must not be dismissed or read as one. */
.notice-line { margin: 10px 0; font-size: 12.5px; color: var(--ink); }
.notice-line .badge { margin-right: 4px; }

@media print {
  .print-toolbar { display: none; }
  body { background: #fff; }
  .card-page { margin: 0; padding: 0; max-width: none; }
  .card-sheet { border: none; box-shadow: none; border-radius: 0; padding: 0; }
  @page { margin: 1.6cm; }
}

/* ------------------------------------------------------------- responsive */
@media (max-width: 900px) {
  .masthead-inner { flex-direction: column; gap: 20px; padding: 24px 18px; }
  .stats { gap: 18px 24px; }
  .board-toggle { padding: 10px 18px; }
  .wrap { padding: 20px 18px; }
  .control-label { min-width: 0; flex-basis: 100%; }
}
@media (max-width: 460px) {
  .stats { gap: 14px 20px; }
  .stat-num { font-size: 30px; }
}

/* Best-knowledge disclaimer footer (see ../CLAUDE.md). */
.disclaimer {
  margin-top: 2rem;
  padding-top: 0.75rem;
  border-top: 1px solid #dfe2e8;
  color: #5a6378;
  font-size: 0.6875rem;
  line-height: 1.5;
  max-width: 70ch;
}
/* base.html puts the footer directly in <body>, which has no padding: inset it so it never
   touches the edge. Same rule as disclaimer.html_style(); :where() keeps it at zero specificity. */
:where(body > .disclaimer) {
  margin-left: 2rem;
  margin-right: 2rem;
  margin-bottom: 1.5rem;
}

/* ======================================================================
   GTM heatmap (2026-07-27)
   Posture is a CATEGORY, not an intensity, so these are five distinct hues
   rather than one ramp. Every cell also carries its text label -- colour is
   never the only channel, so the grid survives greyscale printing and
   colour-vision deficiency.
   ====================================================================== */
.gtm-intro { color: var(--muted); font-size: 0.875rem; max-width: 78ch; margin: 18px 0 14px; }
.gtm-intro strong { color: var(--ink); }

.posture-legend { list-style: none; display: flex; flex-wrap: wrap; gap: 10px 22px;
  padding: 12px 14px; margin: 0 0 18px; background: #fff; border: 1px solid var(--line);
  border-radius: 10px; }
.posture-legend-item { display: flex; align-items: center; gap: 8px; font-size: 0.75rem; }
.posture-meaning { color: var(--muted); }

.posture { display: inline-block; min-width: 74px; text-align: center; padding: 4px 9px;
  border-radius: 999px; font-size: 0.6875rem; font-weight: 700; letter-spacing: .02em;
  white-space: nowrap; }
.posture-champion { background: #2d8f64; color: #fff; }
.posture-build    { background: #c08a00; color: #fff; }
.posture-harvest  { background: #2563b0; color: #fff; }
.posture-watch    { background: #5a6378; color: #fff; }
.posture-absent   { background: #f4f5f7; color: #97a1b5; border: 1px solid var(--line); }

.gtm-matrix td.col-cell { text-align: center; }

/* --- simplified alignment panel (hand-set Yes/No) --------------------
   Deliberately NOT the posture palette: the two grids answer different
   questions and must not be mistaken for each other at a glance. Yes/No also
   carry a glyph, so the panel stays readable in greyscale and when printed --
   colour is never the only channel (same rule as the report's charts). */
.align { display: inline-block; min-width: 52px; text-align: center; padding: 4px 9px;
  border-radius: 6px; font-size: 0.6875rem; font-weight: 700; letter-spacing: .02em;
  white-space: nowrap; }
.align-yes   { background: #14532d; color: #fff; }
.align-no    { background: #fdeaea; color: #a01e15; border: 1px solid #f3c9c6; }
.align-unset { background: #f4f5f7; color: #97a1b5; border: 1px solid var(--line); }
.align-yes::before { content: "\2713\00a0"; }
.align-no::before  { content: "\00d7\00a0"; }

.align-matrix td.col-cell, .align-matrix th.col-cell { text-align: center; }
.align-sel { min-width: 68px; }
/* The editor's selects echo the panel's colours so a saved grid reads the same
   way as the page it feeds -- muted, since a form full of solid blocks is
   harder to scan than the read-only panel. */
select.align-yes   { background: #e6f4ee; color: #14532d; }
select.align-no    { background: #fdeaea; color: #a01e15; }
select.align-unset { background: #fff; color: var(--muted); }
.gsi-link { color: inherit; text-decoration: none; border-bottom: 1px dotted currentColor; }
.gsi-link:hover { color: var(--fg-red); }

/* --- per-GSI radar ------------------------------------------------- */
.gtm-partner-name { margin: 6px 0 16px; font-size: 1.375rem; color: var(--ink); }
.crumbs { margin-top: 16px; }
.gtm-partner-grid { display: grid; grid-template-columns: minmax(320px, 460px) 1fr;
  gap: 28px; align-items: start; }
.gtm-chart { background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: 10px; }
.radar-figure { margin: 0; }
.radar { width: 100%; height: auto; display: block; }
.radar-ring  { fill: none; stroke: #e5e9f0; stroke-width: 1; }
.radar-spoke { stroke: #e5e9f0; stroke-width: 1; }
.radar-expertise { fill: rgba(37, 99, 176, .28); stroke: #2563b0; stroke-width: 2; }
.radar-appetite  { fill: none; stroke: #da291c; stroke-width: 2.5; stroke-dasharray: 6 4; }
.radar-label { font-size: 10px; fill: #5b6b85; font-family: inherit; }
.radar-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; justify-content: center;
  padding: 6px 0 4px; font-size: 0.6875rem; color: var(--muted); }
.radar-key::before { content: ""; display: inline-block; width: 16px; height: 0;
  border-top-width: 3px; margin-right: 6px; vertical-align: middle; }
.radar-key-appetite::before  { border-top-style: dashed; border-top-color: #da291c; }
.radar-key-expertise::before { border-top-style: solid;  border-top-color: #2563b0; }

.gtm-h3 { font-size: 0.9375rem; color: var(--ink); margin: 0 0 4px; }
.gtm-note { color: var(--muted); font-size: 0.75rem; margin: 0 0 10px; max-width: 60ch; }
.gap-list { list-style: none; padding: 0; margin: 0 0 22px; }
.gap-item { display: flex; justify-content: space-between; align-items: center;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; margin-bottom: 6px;
  background: #fff; font-size: 0.8125rem; }
.gap-journey { color: var(--ink); }
.gap-size { font-weight: 700; color: #c08a00; }
.gtm-axes td, .gtm-axes th { font-size: 0.75rem; }

/* Journey accent colours as CLASSES, not a style= attribute.
   The strict CSP (style-src 'self') blocks inline style attributes, so the
   previous `style="--jcolor: ..."` on .track-section never applied and every
   journey badge fell back to the default blue. Keyed on journey_key, which is
   a closed set of nine (app/seed.py JOURNEYS, itself a gated copy of the practice
   brief -- tools/recopy_journeys.py rewrites this block too). ADD A CLASS HERE IN THE SAME
   COMMIT AS ANY NEW JOURNEY -- a missing one does not error, it renders the
   default colour and looks fine. */
.j-zta    { --jcolor: #c9221a; }
.j-ai     { --jcolor: #7a50b0; }
.j-dp     { --jcolor: #2563b0; }
.j-ot     { --jcolor: #2d8f64; }
.j-sase   { --jcolor: #1a9ba0; }
.j-cloud  { --jcolor: #c08a00; }
.j-secops { --jcolor: #b0436a; }
.j-lan    { --jcolor: #5b6f8a; }
.j-dc     { --jcolor: #6b7a2f; }

@media (max-width: 860px) {
  .gtm-partner-grid { grid-template-columns: 1fr; }
}

/* Landscape leave-behind: the radar page prints as it renders (no JS chart to
   re-flow), matching the browser-print win-card approach. */
@media print {
  .board-toggle-bar, .pagefoot, .crumbs { display: none; }
  .gtm-partner-grid { grid-template-columns: minmax(300px, 420px) 1fr; }
  .posture { border: 1px solid #999; }
}

/* ======================================================================
   Admin reporting tool (/admin/report)
   Ported from ../gsi-nse-enablement-portal/app/static/css/portal.css -- the
   layout is identical; only the status ramp differs (this portal has two
   4-rung ladders plus the five GTM postures, so the ramp is generic
   seg-l0..seg-l3 and the LABEL carries the meaning).

   Built to be PRINTED: the export path is the browser's own Print -> Save as
   PDF, so every visual here has to survive that. Two consequences drive the
   rules below.
     1. Charts are SVG <rect>/<circle>, never a coloured <div>. A CSS
        background is dropped by default print settings; a real element is not.
     2. Anything the reader needs is also stated in text -- colour is a second
        channel, never the only one -- so a greyscale print stays complete.
   ====================================================================== */

/* `.noprint` is a marker class only -- the @media print block at the bottom of
   this file is what acts on it. Nothing to declare for screen. */

.report-builder {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px 22px; margin-bottom: 26px;
}
.builder-grid {
  display: grid; gap: 20px 26px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.builder-group { border: none; margin: 0; padding: 0; min-width: 0; }
.builder-group legend {
  padding: 0 0 8px; font-size: 11px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.radio-row {
  display: flex; gap: 9px; align-items: flex-start; padding: 5px 0;
  font-size: 13.5px; cursor: pointer;
}
.radio-row input { margin-top: 3px; flex-shrink: 0; }
.radio-row strong { font-weight: 600; display: block; }
.radio-row em { display: block; font-style: normal; font-size: 12px; color: var(--muted); }
.builder-actions {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line);
}
.builder-note { font-size: 12px; color: var(--muted); flex: 1; min-width: 240px; }

/* ------------------------------------------------------------ report sheet */
.report {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 30px 32px 24px;
}
.report-head { display: flex; gap: 20px; align-items: flex-start; padding-bottom: 18px;
  border-bottom: 3px solid var(--fg-red); }
.report-logo { height: 30px; width: auto; flex-shrink: 0; }
.report-eyebrow { margin: 0 0 3px; font-size: 11px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-red); }
.report-title { margin: 0; font-size: 25px; font-weight: 800; letter-spacing: -.01em; }
.report-meta { margin: 7px 0 0; font-size: 12.5px; color: var(--muted); }
.report-classification {
  margin: 16px 0 0; padding: 9px 13px; border-left: 3px solid var(--fg-red);
  background: #fef3f2; color: #912018; font-size: 12.5px; font-weight: 600;
}

.report-section { margin-top: 34px; }
.report-h {
  margin: 0 0 6px; padding-bottom: 7px; border-bottom: 1px solid var(--line);
  font-size: 17px; font-weight: 800; letter-spacing: -.01em;
}
.report-h4 { margin: 22px 0 8px; font-size: 13.5px; font-weight: 700; }
.section-intro { margin: 8px 0 14px; font-size: 13px; color: var(--muted); max-width: 92ch; }
.report-foot {
  margin-top: 34px; padding-top: 12px; border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--muted);
}
.ladder-line { margin: 6px 0 0; font-size: 11.5px; display: flex; gap: 12px;
  align-items: center; flex-wrap: wrap; }
.posture-meanings { list-style: none; margin: 12px 0 0; padding: 0; font-size: 11.5px;
  color: var(--muted); }
.posture-meanings li { padding: 2px 0; }
.posture-meanings strong { color: var(--ink); }

/* ------------------------------------------------------------------- KPIs */
.kpi-grid {
  display: grid; gap: 12px; margin: 16px 0 6px;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
}
.kpi-grid-sm { grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); }
.kpi {
  border: 1px solid var(--line); border-radius: 10px; padding: 12px 13px;
  border-top: 3px solid #c3ccdb; background: #fcfdfe;
}
.kpi-good { border-top-color: #067647; }
.kpi-bad { border-top-color: var(--fg-red); }
.kpi-warn { border-top-color: var(--amber); }
.kpi-num { margin: 0; font-size: 26px; font-weight: 800; line-height: 1.05; }
.kpi-label { margin: 5px 0 0; font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); }

/* ----------------------------------------------------------------- panels */
.panel-row { display: grid; gap: 18px; margin-top: 18px;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); }
.panel { border: 1px solid var(--line); border-radius: 10px; padding: 15px 16px; min-width: 0; }
.panel-h { margin: 0 0 12px; font-size: 13px; font-weight: 700; }
.panel-note { margin: 10px 0 0; font-size: 11.5px; color: var(--muted); }
.donut-row { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }
.donut-key, .mini-stats { list-style: none; margin: 0; padding: 0; font-size: 12.5px; }
.donut-key li { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.mini-stats { display: flex; flex-wrap: wrap; gap: 6px 20px; margin-top: 12px; color: var(--muted); }
.mini-stats strong { color: var(--ink); font-size: 15px; }

/* ----------------------------------------------------------------- charts */
.chart-bar, .chart-stack { display: block; }
.chart-cols { display: block; margin: 4px 0 2px; }
.bar-track { fill: #eef1f6; }
.bar-fill { fill: var(--blue); }
.bar-fill-warn { fill: var(--amber); }
.bar-fill-bad { fill: var(--fg-red); }
.col-fill { fill: var(--blue); }
.col-fill-target { fill: var(--teal); }
.col-fill-audit { fill: #7a50b0; }
.donut-track { stroke: #eef1f6; }
.donut-centre { font-size: 17px; font-weight: 800; fill: var(--ink); }

/* One generic 4-rung ramp serves BOTH ladders (journey focus and product
   standardization), because they are deliberately parallel -- the legend under
   each table names the rungs, so the shared ramp never has to mean two things
   at once on the same chart. */
.seg-l3 { fill: #067647; stroke: #067647; }
.seg-l2 { fill: #2e9e6b; stroke: #2e9e6b; }
.seg-l1 { fill: var(--blue); stroke: var(--blue); }
.seg-l0 { fill: #d7dde8; stroke: #d7dde8; }

/* The five GTM postures are CATEGORIES, not an intensity -- five distinct hues,
   matching the /gtm grid's existing `.posture-*` colours. */
.seg-champion { fill: #2d8f64; stroke: #2d8f64; }
.seg-build    { fill: #c08a00; stroke: #c08a00; }
.seg-harvest  { fill: #2563b0; stroke: #2563b0; }
.seg-watch    { fill: #5a6378; stroke: #5a6378; }
.seg-absent   { fill: #e2e6ee; stroke: #e2e6ee; }

.chart-legend { list-style: none; display: flex; flex-wrap: wrap; gap: 6px 20px;
  margin: 0; padding: 0; font-size: 11.5px; color: var(--muted); }
.chart-legend li { display: flex; align-items: center; gap: 7px; }
.swatch { width: 11px; height: 11px; border-radius: 3px; flex-shrink: 0;
  background: #d7dde8; display: inline-block; }
.swatch.seg-l3 { background: #067647; }
.swatch.seg-l2 { background: #2e9e6b; }
.swatch.seg-l1 { background: var(--blue); }
.swatch.seg-l0 { background: #d7dde8; }
.swatch.seg-champion { background: #2d8f64; }
.swatch.seg-build    { background: #c08a00; }
.swatch.seg-harvest  { background: #2563b0; }
.swatch.seg-watch    { background: #5a6378; }
.swatch.seg-absent   { background: #e2e6ee; }

/* --------------------------------------------------------------- findings */
.finding-list { list-style: none; margin: 14px 0 0; padding: 0; }
.finding { border: 1px solid var(--line); border-left: 4px solid #c3ccdb;
  border-radius: 8px; padding: 12px 15px; margin-bottom: 10px; }
.finding.sev-high { border-left-color: var(--fg-red); background: #fffbfa; }
.finding.sev-medium { border-left-color: var(--amber); background: #fffcf5; }
.finding.sev-low { border-left-color: var(--blue); background: #fbfcff; }
.finding-head { margin: 0; font-size: 14px; font-weight: 700; display: flex;
  align-items: center; gap: 9px; flex-wrap: wrap; }
.finding-detail { margin: 5px 0 0; font-size: 12.5px; color: var(--muted); }
.finding-items { margin: 8px 0 0; padding-left: 18px; font-size: 12px; color: var(--ink); }
.finding-items li { padding: 1px 0; }
.sev-tag { font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 4px; color: #fff; }
.sev-tag-high { background: var(--fg-red); }
.sev-tag-medium { background: var(--amber); }
.sev-tag-low { background: var(--blue); }

/* ------------------------------------------------------------------- gaps */
.gap-grid { display: grid; gap: 14px; margin-top: 14px;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); }
.gap-card { border: 1px solid var(--line); border-radius: 10px; padding: 13px 15px; }
.gap-num { margin: 0; font-size: 24px; font-weight: 800; line-height: 1; }
.gap-label { margin: 4px 0 8px; font-size: 11px; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted); }
.gap-list { margin: 0; padding-left: 16px; font-size: 12px; }
.gap-list li { padding: 1px 0; }

/* ------------------------------------------------------- table extensions */
.dtable-compact th, .dtable-compact td { padding: 6px 9px; font-size: 12.5px;
  vertical-align: middle; }
.dtable-compact th { font-size: 10px; }
.dtable td.num, .dtable th.num { text-align: right; }
.dtable th.w-mix { width: 190px; }
.dtable th.w-bar { width: 146px; }
.muted-sm { color: var(--muted); font-size: 11.5px; }
.strong-good { color: #067647; font-weight: 700; }
.strong-bad { color: var(--fg-red); font-weight: 700; }
.row-muted td { background: #fafbfd; }
.badge-bad { background: #fef3f2; color: #b42318; }
.flag { display: inline-block; padding: 1px 6px; margin-right: 3px; border-radius: 4px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  white-space: nowrap; }
.flag-bad { background: #fef3f2; color: #b42318; }
.flag-warn { background: #fff5e6; color: #92400e; }
.flag-info { background: #eef2f9; color: #344767; }
.pill { display: inline-block; padding: 1px 7px; margin: 1px 3px 1px 0; border-radius: 999px;
  background: #f1f4f9; font-size: 11px; }

/* ---------------------------------------------------------- board appendix */
/* `.dtable` is width:100%, which makes the nowrap row column absorb all the
   slack and squeezes the GSI columns into a sliver at the right edge. The
   matrix wants to be exactly as wide as its content instead. */
.matrix-table { width: auto; min-width: auto; }
.matrix-table th.matrix-col { padding: 4px 2px; text-align: center; vertical-align: bottom; }
.matrix-colname { writing-mode: vertical-rl; transform: rotate(180deg);
  white-space: nowrap; font-size: 10px; letter-spacing: normal; text-transform: none;
  color: var(--ink); font-weight: 600; }
.matrix-table td.matrix-rowhead, .matrix-table th.matrix-rowhead {
  text-align: left; white-space: nowrap; font-size: 11.5px; }
.matrix-cell { text-align: center; font-family: ui-monospace, SFMono-Regular, Menlo,
  Consolas, monospace; font-weight: 700; font-size: 12px; width: 22px; padding: 4px 0; }
.mx-l3 { background: #d7f0e2; color: #05502f; }
.mx-l2 { background: #e7f6ee; color: #067647; }
.mx-l1 { background: #eaf1fd; color: var(--blue); }
.mx-l0 { color: #b9c1d0; }

/* ------------------------------------------- report rewrite (2026-07-29)
   The three-panel report: KPI trend lines, the ranked gap list, and the
   movement event list. Colour for a trend comes from a SENTIMENT class the
   payload chooses, never from the sign of the number -- "+4 expired" and
   "+4 certified" are the same arithmetic and opposite news. */
.kpi-detail { margin: 6px 0 0; font-size: 11.5px; color: var(--muted); }
.kpi-trend { margin: 5px 0 0; font-size: 11.5px; font-weight: 700; }
.trend-good { color: #067647; }
.trend-bad { color: var(--fg-red); }
.trend-neutral { color: var(--muted); font-weight: 600; }
.kpi-note { margin: 7px 0 0; font-size: 11px; line-height: 1.4; color: var(--muted); }

.report-lede { margin: 0 0 4px; font-size: 12.5px; color: var(--muted); }
.report-note { margin: 12px 0 0; font-size: 11.5px; color: var(--muted); }
.report-empty { margin: 12px 0 0; font-size: 13px; color: var(--muted); }
.report-flag { color: var(--fg-red); font-weight: 600; }

/* Ranked gaps. A numbered <ol> with the marker suppressed: the rank is rendered
   as its own element so it can sit inline with the title and the severity tag. */
.gap-rank { list-style: none; margin: 14px 0 0; padding: 0; counter-reset: none; }
.gap-row { border: 1px solid var(--line); border-left: 4px solid #c3ccdb;
  border-radius: 8px; padding: 11px 14px; margin-bottom: 9px; }
.gap-row.sev-high { border-left-color: var(--fg-red); background: #fffbfa; }
.gap-row.sev-medium { border-left-color: var(--amber); background: #fffcf5; }
.gap-row.sev-low { border-left-color: var(--blue); background: #fbfcff; }
.gap-row-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.gap-rank-num { flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%;
  background: var(--ink); color: #fff; font-size: 11.5px; font-weight: 800;
  display: inline-flex; align-items: center; justify-content: center; }
.gap-row-title { margin: 0; font-size: 14px; font-weight: 700; flex: 1; min-width: 0; }
.gap-row-why { margin: 6px 0 0; font-size: 12.5px; color: var(--ink); }
.gap-row-ev { margin: 4px 0 0; font-size: 11.5px; color: var(--muted); }
.gap-ev-label { font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  font-size: 10px; margin-right: 5px; color: var(--ink); }

.event-list { list-style: none; margin: 8px 0 0; padding: 0; font-size: 12.5px; }
.event-list li { display: flex; gap: 14px; padding: 2px 0; break-inside: avoid;
  border-bottom: 1px solid var(--line); }
.event-list li:last-child { border-bottom: none; }
.event-when { flex: 0 0 84px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* --------------------------------------- admin "View as GSI" preview banner
   Deliberately loud. The whole value of previewing a partner's view is knowing
   that is what you are looking at; a subtle marker defeats the feature. Only
   rendered when views_public.index() resolved an admin `?as=`, so a real partner
   viewer can never see it. `.noprint` because a preview is not a deliverable. */
.preview-banner { margin: 0 0 16px; padding: 10px 14px; border-radius: 8px;
  border: 1px solid var(--amber); background: #fff5e6; color: #7a4a00;
  font-size: 13px; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.preview-banner a { color: #7a4a00; font-weight: 700; }

/* ====================================================================== print
   The report is landscape by default: the GSI scorecard alone carries 21
   columns, and portrait clips the rightmost ones off the PDF entirely (the
   workspace print corollary -- see ../CLAUDE.md). Scroll regions must go
   `overflow: visible` for the same reason: an overflow container prints only
   its visible slice, silently dropping the columns scrolled out of view.
   ====================================================================== */
@media print {
  @page { size: A4 landscape; margin: 11mm; }

  .noprint, .masthead, .adminbar, .report-builder, .scroll-hint { display: none !important; }

  /* The best-knowledge disclaimer is NOT hidden here on purpose. The PDF is a
     deliverable that leaves the workspace, so it carries the notice like every
     other one (workspace guardrail). It sits after the report footer, small and
     muted -- a footer, never a banner. */
  .disclaimer { margin-top: 10px; padding-top: 6px; font-size: 8px; max-width: none;
    break-inside: avoid; }

  body { background: #fff; font-size: 11px; }
  .wrap { max-width: none; padding: 0; }
  .report { border: none; box-shadow: none; border-radius: 0; padding: 0; }

  /* An overflow container prints its visible slice only. */
  .dtable-scroll, .table-scroll { overflow: visible !important; }

  /* Backgrounds carry meaning on badges, matrix cells and severity strips. */
  .badge, .flag, .sev-tag, .pill, .swatch, .kpi, .finding,
  .matrix-cell, .report-classification, .dtable th, .row-muted td {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }

  .report-section { break-before: auto; break-inside: auto; }
  .report-h { break-after: avoid; }
  .report-h4, .panel-h { break-after: avoid; }
  .kpi, .gap-card, .panel, .finding, .gap-row { break-inside: avoid; }
  /* The appendix is a separate document section -- start it on a fresh page so
     the three panels read as a self-contained report when both are printed. */
  .appendix-start { break-before: page; }
  .report-section { margin-top: 18px; }
  .gap-row { padding: 8px 11px; margin-bottom: 6px; }
  .gap-row-why { margin-top: 3px; }
  .gap-row-ev { margin-top: 2px; }
  .kpi-note { margin-top: 5px; }
  .dtable tr { break-inside: avoid; }
  .dtable thead { display: table-header-group; }   /* repeat headers per page */
  .dtable-compact th, .dtable-compact td { padding: 3px 5px; font-size: 9px; }
  .dtable-compact th { font-size: 8px; }
  .kpi-num { font-size: 19px; }
  .report-title { font-size: 20px; }
  .report-h { font-size: 14px; }
  .matrix-colname { font-size: 8px; }
  .dtable th.w-mix { width: 130px; }
  .dtable th.w-bar { width: 100px; }
  .panel-row { break-inside: auto; }
}

/* Viewer FLAGS -- a read-only summary of the row's assigned labels, sitting
   above its multi-select editor. The select is `size="3"` on a vocabulary that
   can run longer, so a selected option can be scrolled out of view; this line is
   what makes "what is this viewer flagged as" answerable at a glance.
   Deliberately NOT styled like `.chip` (the flag FILTER above the table): those
   are clickable links that change the view, these are inert labels, and giving
   them the same affordance would invite a click that does nothing. */
.flagchips { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }
.flagchip {
  display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px;
  font-weight: 600; background: #eef1f4; color: #5a6378; white-space: nowrap;
}
.flagchips-none { font-size: 11px; color: var(--muted); font-style: italic; }
/* A DIMENSION value (persona) reads differently from an additive label: it is the
   answer to a single-answer question, and there can only ever be one of them on a
   row. Without this the Flags column is a wall of identical grey pills and the one
   chip that classifies the person is the hardest to find. Colour is not the only
   channel -- the picker labels the dimension in words, and the chip is bolder and
   outlined, so this survives a greyscale print and a colour-blind reader. */
.flagchip.flagchip-dim {
  background: #fdeaea; color: #a01e15; box-shadow: inset 0 0 0 1px #f3c9c6;
}

/* The flag PICKER. Checkboxes rather than a `<select multiple>`: the vocabulary
   is capped at a handful so they fit, they need no Ctrl/Cmd-click, and they
   cannot clip an option mid-scroll the way a sized select does. Compound
   selectors throughout -- `.cell-input { width: 100% }` losing the cascade to an
   equal-specificity rule declared later is a bug this stylesheet has already
   shipped once. */
.flagpick {
  display: flex; flex-direction: column; gap: 2px;
  border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px; margin: 0;
  background: #fff;
}
/* Clamp the chip summary so a viewer carrying every flag cannot balloon the
   column to the width of the whole run. The Flags column is what pushes this
   table toward horizontal scrolling; that is handled the way every wide table
   here is (`.dtable-scroll` + the "Scroll sideways" hint + the wheel->horizontal
   helper), and the PAGE itself must never overflow at any width. */
.dtable .flagchips { max-width: 124px; }
.dtable .flagpick { padding: 5px 6px; }
.flagpick .flagpick-legend {
  padding: 0 4px; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
.flagpick .flagpick-item {
  display: flex; align-items: center; gap: 6px; font-size: 12px;
  color: var(--ink); white-space: nowrap; cursor: pointer;
}
/* The add form lays its picker out horizontally -- it sits in a flex row that
   already wraps, and a vertical stack there would push the row's height past
   everything beside it. */
.inline-add .flagpick { flex-direction: row; flex-wrap: wrap; gap: 4px 12px; align-items: center; }
.inline-add .flagpick .flagpick-legend { width: 100%; }

/* An exclusive dimension inside the picker. Boxed and captioned so the radio set
   reads as one question with several answers, rather than as checkboxes that
   happen to behave oddly -- the radios alone communicate "pick one" only after
   you have already tried to tick two. */
.flagpick .flagpick-group {
  display: flex; flex-direction: column; gap: 2px;
  border-left: 2px solid #f3c9c6; padding: 2px 0 4px 6px; margin-bottom: 4px;
}
.flagpick .flagpick-grouplabel {
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: #a01e15;
}
/* Match the horizontal add-form layout: the group stays a unit, but wraps inline
   with the loose checkboxes instead of forcing its own full-width row. */
.inline-add .flagpick .flagpick-group { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 4px 10px; }

/* GPP_DEBUG only: the exception chain on the 500 page. */
.error-detail { text-align: left; white-space: pre-wrap; font-size: 11.5px; max-height: 50vh;
  overflow: auto; background: #f1f3f7; padding: 10px; border-radius: 6px; }
