/* Fortinet GSI Showcase Portal
   System font stack (no external fonts; strict CSP).

   UI pass (2026-09-25): one design system instead of per-feature values.
   Everything below the report section still predates it and uses the same
   tokens where it was cheap to move them. Rules of thumb the pass settled on:
     * RED is the brand and the page's ONE main action -- not filters, not
       "Manage ...", not every card's Open. A screen with five red buttons has
       no main action.
     * Type comes from the scale below; a new size needs a reason.
     * Text links are blue; red text is reserved for danger and the brand eyebrow.
     * Tables are 13px and never inherit the 14px body size, so a free-text cell
       (a note) can no longer render larger than the cells around it. */

:root {
  /* brand + status */
  --fg-red: #da291c;
  --fg-red-dark: #b11e14;
  --blue: #175cd3;
  --teal: #0a7d76;
  --amber: #b7791f;
  --ok-fg: #067647;  --ok-bg: #ecfdf3;  --ok-line: #abefc6;
  --warn-fg: #93370d; --warn-bg: #fffaeb; --warn-line: #fedf89;
  --bad-fg: #b42318; --bad-bg: #fef3f2; --bad-line: #f5c2bd;
  /* neutrals */
  --ink: #16233d;
  --muted: #5b6b85;
  --subtle: #8793a8;
  --line: #e4e8ef;
  --line-strong: #cdd4e0;
  --bg: #f5f7fa;
  --card: #ffffff;
  --card-2: #f8f9fb;
  --head: #101b33;
  --link: #175cd3;

  /* type scale -- 11 / 12 / 13 / 14 / 16 / 20 / 26 */
  --fs-2xs: 11px;
  --fs-xs: 12px;
  --fs-sm: 13px;
  --fs-md: 14px;
  --fs-lg: 16px;
  --fs-xl: 20px;
  --fs-2xl: 26px;

  --radius: 10px;
  --radius-sm: 7px;
  --shadow: 0 1px 2px rgba(16, 27, 51, .05), 0 1px 3px rgba(16, 27, 51, .04);
  --focus: 0 0 0 3px rgba(23, 92, 211, .22);
  --page-max: 1400px;
  --gutter: 32px;
}

* { 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: var(--fs-md);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Text links. Buttons, chips and nav links are <a>s too; their class rules
   outrank this element rule, so they keep their own colour. */
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible, button:focus-visible, summary:focus-visible, .field-input:focus-visible {
  outline: none; box-shadow: var(--focus); border-radius: var(--radius-sm);
}

h1, h2, h3 { letter-spacing: -.01em; }
p { margin: 0 0 10px; }

.wrap { max-width: var(--page-max); margin: 0 auto; padding: 24px var(--gutter) 8px; }

/* ------------------------------------------------------------- admin top bar
   Replaced the full-width gradient masthead (2026-09-25): it spent ~125px of
   every admin screen repeating the page title in white, above a second bar that
   repeated "ADMIN" in a red tag. The title now lives in the page (.page-head). */
.topbar { background: var(--head); color: #fff; border-bottom: 2px solid var(--fg-red); }
.topbar-inner {
  max-width: var(--page-max); margin: 0 auto; padding: 0 var(--gutter);
  height: 54px; display: flex; align-items: center; gap: 14px;
}
.topbar-brand { display: flex; align-items: center; gap: 12px; color: #fff; text-decoration: none; }
.topbar-brand:hover { text-decoration: none; }
.topbar-logo { height: 20px; width: auto; display: block; }
.topbar-product { font-size: var(--fs-md); font-weight: 700; padding-left: 12px;
  border-left: 1px solid rgba(255, 255, 255, .22); }
.topbar-area { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: #ff8a80; }
.topbar-spacer { flex: 1; }
.topbar-user { color: #b9c1d4; font-size: var(--fs-sm); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.inline-form { display: inline; margin: 0; }

/* ------------------------------------------------------------- admin nav
   One row that SCROLLS sideways when it runs out of room, rather than wrapping.
   The old rule wrapped (`flex-wrap` was load-bearing: an unwrappable row once
   pushed every admin page 12px wider than a 768 viewport); scrolling solves that
   the same way -- overflow stays inside the nav -- without stacking four rows of
   links above the content on a phone. */
.adminnav-bar { background: var(--card); border-bottom: 1px solid var(--line); }
.adminnav {
  max-width: var(--page-max); margin: 0 auto; padding: 0 var(--gutter);
  display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none;
}
.adminnav::-webkit-scrollbar { display: none; }
.adminnav-link {
  flex: none; padding: 13px 11px 11px; font-size: var(--fs-sm); font-weight: 600;
  color: var(--muted); text-decoration: none; border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.adminnav-link:hover { color: var(--ink); text-decoration: none; }
.adminnav-link.active { color: var(--ink); border-bottom-color: var(--fg-red); }

/* ------------------------------------------------------------- page header */
.page-head { margin: 4px 0 18px; }
.page-title { margin: 0; font-size: var(--fs-xl); font-weight: 800; line-height: 1.25; }

/* Top-of-page action row (a page's main button, sometimes a note beside it). */
.page-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px;
  margin: 0 0 14px; }

/* ------------------------------------------------------------- partner masthead
   Partner-facing pages keep a branded header -- it is the one place the portal
   introduces itself to an external reader -- at about two-thirds of its old
   height. */
.masthead { background: linear-gradient(118deg, #0a0e1f 0%, #191233 55%, #331636 100%);
  color: #fff; border-bottom: 2px solid var(--fg-red); }
.masthead-inner {
  max-width: var(--page-max); margin: 0 auto; padding: 20px var(--gutter);
  display: flex; justify-content: space-between; align-items: center; gap: 24px;
}
.brand { display: flex; align-items: center; gap: 18px; min-width: 0; }
.brand-logo { height: 24px; width: auto; flex: none; }
.eyebrow {
  margin: 0 0 2px; font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: #ff8a80;
}
.brand-text { min-width: 0; padding-left: 18px; border-left: 1px solid rgba(255, 255, 255, .18); }
.brand-text h1 { margin: 0; font-size: 22px; font-weight: 800; line-height: 1.2; }
.brand-sub { margin: 4px 0 0; max-width: 620px; color: #b9c1d4; font-size: var(--fs-sm); }
.viewer-id { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.viewer-email { color: #b9c1d4; font-size: var(--fs-sm); }

/* ------------------------------------------------------------- buttons
   Four kinds: default (neutral outline), primary (red, one per screen), danger
   (red outline, destructive), and on-dark (for the dark bars). `.btn-ghost`
   predates the pass and is the default kind by another name; it stays so that
   every existing template reads the same. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 34px; padding: 0 14px; border-radius: var(--radius-sm);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; line-height: 1.2;
  border: 1px solid var(--line-strong); background: var(--card); color: var(--ink);
  text-decoration: none; cursor: pointer; white-space: nowrap;
}
.btn:not([disabled]):hover { background: var(--card-2); border-color: #b8c1d1; text-decoration: none; }
/* 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. Hover rules are scoped
   `:not([disabled])` so no variant needs its own disabled override. */
.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: var(--card); }
.btn-danger { background: var(--card); color: var(--bad-fg); border-color: var(--bad-line); }
.btn-danger:not([disabled]):hover { background: var(--bad-bg); border-color: #e79b93; }
.btn-on-dark { background: transparent; color: #fff; border-color: rgba(255, 255, 255, .3); }
.btn-on-dark:not([disabled]):hover { background: rgba(255, 255, 255, .1); border-color: rgba(255, 255, 255, .5); }
.btn-sm { min-height: 28px; padding: 0 10px; font-size: var(--fs-xs); border-radius: 6px; }
.btn-block { width: 100%; min-height: 40px; }

/* ------------------------------------------------------------- auth pages */
.auth-wrap { min-height: calc(100vh - 120px); display: flex; align-items: center;
  justify-content: center; padding: 32px 16px; }
.auth-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow);
  padding: 32px; width: 100%; max-width: 400px;
}
.auth-card-center { text-align: center; }
.auth-logo { height: 24px; width: auto; margin-bottom: 20px; }
.auth-title { margin: 0 0 2px; font-size: var(--fs-xl); font-weight: 800; }
.auth-sub { margin: 0 0 22px; color: var(--muted); font-size: var(--fs-md); }
.auth-error {
  background: var(--bad-bg); border: 1px solid var(--bad-line); color: var(--bad-fg);
  border-radius: var(--radius-sm); padding: 10px 14px; font-size: var(--fs-sm); margin-bottom: 16px;
}
.auth-error ul { margin: 0; padding-left: 18px; }
.auth-form { display: flex; flex-direction: column; gap: 14px; }
.auth-card > .btn-block + .auth-note,
.auth-card > .btn-block + .auth-form { margin-top: 14px; }
.auth-note { margin: 16px 0 0; font-size: var(--fs-xs); color: var(--muted); text-align: center; }
.error-code { font-size: 48px; font-weight: 800; margin: 4px 0; color: var(--fg-red); line-height: 1.1; }

/* ------------------------------------------------------------- form fields */
.field { display: flex; flex-direction: column; gap: 6px; }
.field-label { font-size: var(--fs-sm); font-weight: 600; color: var(--ink); }
.field-input {
  min-height: 36px; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: 7px 11px; font: inherit; font-size: var(--fs-md); color: var(--ink); background: var(--card);
}
select.field-input { padding-right: 28px; }
.field-input:focus { outline: none; border-color: var(--blue); box-shadow: var(--focus); }
.field-help { margin: 2px 0 4px; font-size: var(--fs-xs); color: var(--muted); max-width: 78ch; }
.opt, .field-opt { color: var(--muted); font-weight: 400; font-size: var(--fs-xs); }

.hidden { display: none !important; }

/* --- progressive enhancement ------------------------------------------------
   Every admin action here is a plain form POST, so nothing breaks when admin.js
   is blocked. `.no-js-only` is DEFAULT-VISIBLE: admin.js adds `js-on` to <body>,
   which hides it, so if the stylesheet AND the script both fail the user still
   gets a working, guarded control. */
body.js-on .no-js-only { display: none !important; }

/* --- confirmation page (2026-08-07) -----------------------------------------
   Stage 2 of every destructive action. NOTHING here may be `.no-js-only`: this
   page IS the guard, so it renders identically in every script state. */
.confirm-strip {
  background: #fff7f6; 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: var(--fs-lg); color: var(--fg-red-dark); }
.confirm-strip-body { margin: 0 0 12px; font-size: var(--fs-md); max-width: 72ch; }
.confirm-strip-form { display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
  margin-top: 14px; }
/* Full-size, not muted: each of these agrees to lose something unrecoverable. */
.confirm-strip .confirm-box { display: flex; align-items: flex-start; gap: 8px;
  font-size: var(--fs-md); color: var(--ink); max-width: 72ch; }
.confirm-strip .confirm-box input { margin: 4px 0 0; flex: 0 0 auto; }
.confirm-strip-actions { display: flex; align-items: center; gap: 10px; margin-top: 4px; }

/* ------------------------------------------------------------- bulk edit
   A toolbar card, like every other form surface, instead of controls floating on
   the page background (the allowlist's did; the documents page's was a card). */
.bulkbar {
  display: flex; align-items: flex-end; flex-wrap: wrap; gap: 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 16px; margin: 0 0 12px;
}
.bulkbar-label { font-weight: 700; color: var(--muted); text-transform: uppercase;
  letter-spacing: .06em; font-size: var(--fs-2xs); padding-bottom: 9px; }
.bulkbar-field { display: flex; flex-direction: column; gap: 4px; font-size: var(--fs-sm); font-weight: 600; }
/* Compound selector: a bare rule would fight `.field-input`'s own width at equal
   specificity depending on source order. */
.bulkbar .field-input { width: auto; min-width: 160px; }
/* Inside a card (the documents page) the bar is a row of that card, not a second
   card nested in the first. */
.formcard > .bulkbar { background: none; border: 0; padding: 0; }
.bulkpick { display: inline-flex; align-items: center; }
.bulkpick input { margin: 0; }
.col-pick { width: 1%; }
/* Live "N selected" count beside Preview (admin.js), on the button's baseline. */
.bulkbar-count { color: var(--muted); font-size: var(--fs-sm); padding-bottom: 8px;
  font-variant-numeric: tabular-nums; }
/* The bulk form holds only hidden fields; its controls live in the bar and its
   tick-boxes in the table, wired by the HTML5 `form` attribute. */
.wl-bulkform { display: none; }
.wl-note { display: block; font-size: var(--fs-xs); color: var(--muted); }
.nojs-note {
  background: var(--warn-bg); border: 1px solid var(--warn-line); color: var(--warn-fg);
  border-radius: var(--radius-sm); padding: 10px 14px; margin: 0 0 16px;
  font-size: var(--fs-sm); line-height: 1.5;
}

/* ------------------------------------------------------------- flash banners */
.flash {
  border-radius: var(--radius-sm); padding: 10px 14px; font-size: var(--fs-md); margin-bottom: 16px;
  border: 1px solid var(--line); background: var(--card);
}
.flash-ok { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok-fg); }
.flash-error { background: var(--bad-bg); border-color: var(--bad-line); color: var(--bad-fg); }

/* Admin "View as group" banner. Deliberately LOUD -- the value of the preview is
   knowing you are looking at somebody else's catalog. Amber: a heads-up, not an
   error. */
.preview-banner {
  margin: 0 0 18px; padding: 12px 16px; border-radius: var(--radius-sm); font-size: var(--fs-md);
  background: var(--warn-bg); border: 1px solid var(--warn-line); border-left: 4px solid var(--amber);
  color: #7a4d05;
}
.preview-banner strong { color: #62330a; }
.preview-banner a { color: #7a4d05; font-weight: 700; text-decoration: underline; }

/* Read-only summary of an allowlist row's current group scope. */
.scope-current { margin: 6px 0 0; font-size: var(--fs-xs); color: var(--muted); }

/* ------------------------------------------------------------- cards / panels
   `.panel` is the dashboard card. The report defines its own, smaller panel
   INSIDE `.panel-row` (further down) -- it used to redefine `.panel` globally,
   which quietly shrank the dashboard cards' padding and radius too. */
.panel-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px;
}
.panel {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px 22px; min-width: 0;
}
.panel-title { margin: 0 0 8px; font-size: var(--fs-lg); font-weight: 700; }
.panel-body { margin: 0 0 14px; color: var(--muted); font-size: var(--fs-md); }
.panel-soon {
  display: inline-block; background: #eef1f4; color: #6a7688; border-radius: 999px;
  padding: 3px 12px; font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase;
}
.panel-wide { margin-top: 16px; }
/* Right-aligned "see more" link inside a panel heading. */
.panel-title-link { float: right; font-size: var(--fs-sm); font-weight: 500; }

.stat-line { margin: 0 0 14px; color: var(--muted); font-size: var(--fs-md); }
.stat-big { font-size: var(--fs-2xl); font-weight: 800; color: var(--ink); letter-spacing: -.02em; }

/* ------------------------------------------------------------- data tables */
.toolbar { display: flex; align-items: center; gap: 12px 16px; margin-bottom: 16px; flex-wrap: wrap; }
.admin-hint { color: var(--muted); font-size: var(--fs-sm); max-width: 90ch; }
/* One-line "there is more to the right" hint above a horizontally-scrolling
   region. Default-visible and REMOVED by admin.js when the region does not
   actually overflow -- the same default-visible discipline as `.no-js-only`. */
.scroll-hint {
  margin: 0 0 6px; font-size: var(--fs-xs); color: var(--muted);
  font-weight: 600; letter-spacing: .02em;
}
.table-scroll { overflow-x: auto; margin-bottom: 14px; }
.dtable {
  width: 100%; border-collapse: separate; border-spacing: 0; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  font-size: var(--fs-sm);
}
.dtable th, .dtable td { padding: 10px 12px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--line); }
.dtable th { background: var(--card-2); font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.dtable tbody tr:hover > td { background: #fbfcfe; }
.dtable tr:last-child td { border-bottom: none; }
.dtable td.num, .dtable th.num { text-align: right; white-space: nowrap;
  font-variant-numeric: tabular-nums; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--fs-xs); }
.nowrap { white-space: nowrap; }
.muted-sm { color: var(--muted); font-size: var(--fs-xs); }

/* Pager -- shared by the view log + audit browser (50 rows/page). */
.pager { display: flex; align-items: center; gap: 12px; margin: 14px 0 4px; }
.pager-pos { color: var(--muted); font-size: var(--fs-sm); }
/* Row actions: two per line. The column is `width: 1%` so it hugs its buttons,
   which with a WRAPPING flex row put every button on its own line -- four
   stacked buttons made each document row ~110px tall. */
.col-actions { width: 1%; white-space: nowrap; }
/* A wide table's title column otherwise shrinks to its longest word and wraps a
   description to five lines. In auto layout a cell width is a floor, not a cap. */
.col-title { min-width: 230px; }
.action-row { display: grid; grid-template-columns: repeat(2, max-content); gap: 6px; }
.action-row > .inline-form { display: contents; }

/* ------------------------------------------------------------- badges */
.badge {
  display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: var(--fs-2xs);
  font-weight: 700; letter-spacing: .02em; background: #eef1f5; color: #4a5670; white-space: nowrap;
  line-height: 1.5;
}
.badge-pub { background: var(--ok-bg); color: var(--ok-fg); }
.badge-draft { background: #fff5e6; color: #a15c07; }

/* ------------------------------------------------------------- admin forms */
.formcard {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px 24px; margin-bottom: 20px; max-width: 760px;
}
.formcard-title { margin: 0 0 14px; font-size: var(--fs-lg); font-weight: 700; }
.stack-form { display: flex; flex-direction: column; gap: 16px; }
.stack-form textarea.field-input { resize: vertical; font: inherit; }
.form-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.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; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .9em;
  background: #f0f2f6; padding: 1px 5px; border-radius: 4px; }

/* ------------------------------------------------------------- viewer catalog */
.doc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
.doc-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;
}
.doc-title { margin: 0; font-size: var(--fs-lg); font-weight: 700; line-height: 1.35; }
.doc-desc { margin: 0; color: var(--muted); font-size: var(--fs-sm); }
/* Pinned to the card's foot so a row of cards lines its buttons up. */
.doc-open { margin-top: auto; align-self: flex-start; }
.doc-card .doc-desc + .doc-open, .doc-card .doc-title + .doc-open { margin-top: auto; }

/* ------------------------------------------------------------- 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: var(--radius); padding: 10px; line-height: 0;
}
.enroll-qr img { display: block; width: 200px; height: 200px; }
.enroll-key { flex: 1; min-width: 240px; }
.keybox {
  background: #f0f2f6; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 10px 12px;
  font-size: 15px; letter-spacing: .08em; word-break: break-all; margin: 4px 0 10px;
}
/* The share link is a readonly INPUT so it can be copied with no script. Only the
   font FAMILY is inherited: the `font` shorthand would also reset `.keybox`'s
   size at this higher specificity. */
input.keybox { width: 100%; font-family: inherit; color: inherit; }

/* ------------------------------------------------------------- responsive */
@media (max-width: 900px) {
  :root { --gutter: 16px; }
  .masthead-inner { flex-direction: column; align-items: flex-start; gap: 14px; padding: 16px var(--gutter); }
  .brand-text h1 { font-size: 19px; }
  .topbar-product { display: none; }
  .page-title { font-size: 18px; }
  .wrap { padding-top: 18px; }
  /* The nav scrolls sideways here; fade its right edge so it visibly continues,
     with end padding so the last link can scroll clear of the fade. */
  .adminnav { padding-right: 32px;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
}

/* Best-knowledge disclaimer footer (see ../CLAUDE.md). Aligned with the page
   content (same max width and gutter as .wrap) -- it used to sit outside that
   container, flush against the window's left edge. */
.disclaimer {
  max-width: var(--page-max);
  margin: 32px auto 0;
  padding: 14px var(--gutter) 28px;
  position: relative;
  color: #6b778c;
  font-size: var(--fs-2xs);
  line-height: 1.55;
}
/* The rule is inset to the content edges, not the padding box's. */
.disclaimer::before { content: ""; position: absolute; top: 0; left: var(--gutter);
  right: var(--gutter); border-top: 1px solid var(--line); }

/* =====================================================================
   PORTED 2026-07-29 from ../gsi-nse-enablement-portal/app/static/css/portal.css
   (the reference implementation). This portal had no report layer at all, so
   the whole reporting section AND the print stylesheet come across together --
   they are one unit: the charts are SVG precisely because the print block is
   what this report exists for, and splitting them is how the A4-landscape /
   overflow-visible / disclaimer-survives-print rules get lost.
   ===================================================================== */
/* ======================================================================
   Admin reporting tool (/admin/report)
   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);
}

/* ------------------------------------------------------------------- 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)); }
/* Scoped to the report (2026-09-25): a bare `.panel` here re-styled the dashboard cards. */
.panel-row .panel { box-shadow: none; border-radius: var(--radius); padding: 15px 16px; }
.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); }

/* Status ramp -- one hue family per meaning, used by stack, donut and legend. */
.seg-certified { fill: #067647; stroke: #067647; }
.seg-progress  { fill: var(--blue); stroke: var(--blue); }
.seg-expired   { fill: var(--fg-red); stroke: var(--fg-red); }
.seg-none      { fill: #d7dde8; stroke: #d7dde8; }

.chart-legend { list-style: none; display: flex; flex-wrap: wrap; gap: 6px 20px;
  margin: 10px 0 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-certified { background: #067647; }
.swatch.seg-progress  { background: var(--blue); }
.swatch.seg-expired   { background: var(--fg-red); }
.swatch.seg-none      { background: #d7dde8; }

/* --------------------------------------------------------------- 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 th.w-mix { width: 190px; }
.dtable th.w-bar { width: 146px; }
.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; }
/* Amber, between .badge (neutral) and .badge-bad (already broken): something
   still working that is about to stop. Same --amber the medium severity tags
   and .kpi-warn use, so "act soon" reads consistently across the console. */
.badge-warn { background: #fff5e6; color: var(--amber); }
/* Slate, deliberately NOT .badge-bad. A Secured document is in no catalog and is
   still serveable by share link -- a configuration an admin picks, not a broken
   one -- and it is the DEFAULT for every upload, so badging it red would paint
   the whole list as failing. Background and colour are set TOGETHER: a partial
   override inherits .badge's grey text, which is how .badge-warn spent weeks at
   1.23:1 in the sibling portal. Measured 8.1:1. */
.badge-secure { background: #eaf0f6; color: #33506e; }
.badge-cert { display: inline-block; padding: 1px 6px; margin-right: 3px; border-radius: 4px;
  background: #eef2f9; color: var(--ink); font-size: 10.5px; font-weight: 700;
  white-space: nowrap; }
.badge-expert { background: #efe9f6; color: #56348f; }
.badge-stage { background: #e1f3f4; color: #0a6e69; }
.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 credential column absorb all
   the slack and squeezes the person 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-certified { background: #e7f6ee; color: #067647; }
.mx-in_progress { background: #eaf1fd; color: var(--blue); }
.mx-expired { background: #fdecea; color: var(--fg-red); }
.mx-none { color: #b9c1d0; }
.mx-target { outline: 2px solid var(--teal); outline-offset: -2px; }

/* ------------------------------------------- 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; }

/* ====================================================================== print
   The report is landscape by default: the widest tables here carry 13-15
   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 {
  /* Filter chrome is not content -- keep it off a printed catalog. */
  .filterbar { display: none !important; }
  @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, .badge-cert, .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; margin-top: 18px; }
  /* Tighter gap cards in print: the ranked list is the densest thing a reader
     wants on paper, and the screen padding wastes ~2 entries per page. */
  .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; }
  .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; }
  /* The event list is deliberately single-column (see the rule above) -- do not
     reintroduce CSS columns here: a short list reads as a run-on row. */
  .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; }
}

/* ---- catalog filter bar (2026-07-30) -----------------------------------
   Server-side GET controls; same vocabulary as the sibling portals' boards. */
.filterbar { margin: 0 0 18px; display: flex; flex-direction: column; gap: 8px; }
.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);
}
.chip {
  padding: 6px 13px; border-radius: 999px; border: 1px solid var(--line);
  background: #fff; color: var(--ink); font-weight: 600; font-size: 12.5px;
  text-decoration: none; display: inline-block; transition: border-color .12s;
}
.chip:hover { border-color: #c3ccdb; text-decoration: none; }
/* Ink, not red (2026-09-25): a selected filter is state, not the page's main action. */
.chip-active { background: var(--ink); color: #fff; border-color: var(--ink); }
.chip-active:hover { border-color: var(--ink); text-decoration: none; }
.searchform { display: flex; align-items: center; gap: 10px 14px; flex-wrap: wrap; margin: 0 0 16px; }
/* The Apply button is a small button in markup; in this row it matches the inputs. */
.searchform .btn { min-height: 36px; font-size: var(--fs-sm); padding: 0 14px; }
.field-inline {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--muted);
}
.search-input { width: 240px; max-width: 100%; }
.filter-summary { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }

/* Clamp the Links table's "Sent to / note" cell: an email
   address has no break opportunities, so one long recipient would balloon the
   column and push the widest table in the app wider still (measured: the region
   went 924px -> 1134px before this rule). `overflow-wrap: anywhere` lets the
   address break instead of setting the column width. */
.dtable .share-who { display: block; max-width: 170px; overflow-wrap: anywhere; }

/* ---------------------------------------- allowlist: scope picker + row editors
   (2026-08-02). The scope control used to be `<select multiple size="3">`: at
   1366 it rendered a 76px box holding 200px of options, so an admin saw 3 of 10
   collections, auto-scrolled to a different arbitrary window on every row, with
   the 4th option sliced in half and a second pick needing Ctrl/Cmd-click. It is
   the single highest-stakes field on the page -- ticking nothing grants the whole
   catalog -- so it is now the one you can read without interacting with it. */

/* Visually hidden, still announced. `position: absolute` inside a `.table-scroll`
   is the documented trap here (a visually-hidden checkbox once stretched the NSE
   board's page by 419px by resolving against the initial containing block), so
   the two fieldsets below are `position: relative` to contain it, AND this is
   clipped to 1px so it cannot contribute width even if that ever regresses. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.scopepick { position: relative; }
.scopepick {
  border: 1px solid var(--line); border-radius: 6px; padding: 8px 10px; margin: 0;
  background: #fff;
}
/* auto-fill, so 10 collections lay out in columns and 30 still fit without a
   scrollbar that hides the rest. No fixed `size` means nothing can clip. */
.scopepick-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 2px 14px;
}
.scopepick-item {
  display: flex; align-items: center; gap: 6px; font-size: 12.5px;
  color: var(--ink); cursor: pointer; padding: 1px 0;
}
.scopepick-note { margin: 6px 0 0; font-size: 11.5px; color: var(--muted); }
.scopepick-note strong { color: #a01e15; }
/* Watermark picker (_watermark_pick.html). Deliberately NOT .scopepick-grid:
   that lays out in auto-fill columns for a long list of short collection names,
   and these are three options each carrying a line of explanation. Stacked, so
   every option reads as a sentence and nothing depends on the column width. */
.wmpick { display: grid; gap: 7px; }
.wmpick-item {
  display: flex; align-items: flex-start; gap: 8px; cursor: pointer; padding: 1px 0;
}
/* flex: none so the control keeps its size when the help text wraps; the small
   top offset lines the radio up with the label's cap height rather than the
   centre of a two-line block. */
.wmpick-item input { flex: none; margin-top: 3px; }
.wmpick-body { display: flex; flex-direction: column; gap: 1px; }
.wmpick-label { font-size: 12.5px; font-weight: 600; color: var(--ink); }
.wmpick-help { font-size: 11.5px; color: var(--muted); }
/* Inside a row editor the column is narrow, so drop to one column. Deliberately
   NO max-height: item heights vary (a long collection name wraps to two lines --
   measured 56px against 24px for a short one), so any fixed cap slices whichever
   item straddles it. That is the exact defect being fixed, reintroduced one level
   down. The editor opens one row at a time on purpose, so it can afford to show
   the whole list; `min-width` keeps most names on a single line. */
.dtable .scopepick-grid { grid-template-columns: 1fr; }
.dtable .scopepick { padding: 6px 8px; min-width: 186px; }

/* Collapsed row editors. Native <details> -- no script, which matters because
   this deployment can lose its scripts with nothing in the app's logs. Every row
   used to render a live 10-option select inline, so one entry stood ~280px tall
   and seven of them ran to 2000px. Current state stays visible above the
   disclosure; only the controls fold away. */
/* The expiry cell used to borrow `.share-who`, which is the Links table's
   recipient-email rule: `max-width: 170px` + `overflow-wrap: anywhere`. That is
   right for an address with no break opportunities and wrong here -- once the
   table stopped overflowing and the columns tightened, it broke "expired" into
   "expir/ed" and "2026-08-04" across two lines. A status word and an ISO date are
   atomic; they get nowrap and the column sizes to them. */
/* "Added by" is an ADMIN EMAIL, which has no break opportunities, so one entry
   added by a real account (rather than a short CLI actor like "cli") set the
   column width for the whole table and pushed the last column out of the region
   -- measured: 0 -> 159px of overflow the moment a real admin added an entry.
   Same rule `.share-who` uses on the Links table, and for the same reason. */
.addedby-cell {
  display: block; max-width: 98px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
.expiry-cell { white-space: nowrap; }
.expiry-cell .rowedit-summary { white-space: normal; }

/* Group names DO wrap (some run to "Accenture Federal Services"), but on word
   boundaries only, and the column gets enough width that they land on two lines
   rather than three. */
.dtable .scope-current { min-width: 118px; }
.dtable .scope-current .badge { overflow-wrap: normal; }

/* An unadorned list inside a row editor -- the GSI member roster. No bullets and
   no left padding, because it sits in a table cell where the marker column would
   push the text out of line with the state badge above it. */
.plainlist { list-style: none; margin: 4px 0 0; padding: 0; font-size: 12px; }
.plainlist li { padding: 2px 0; }

.rowedit { margin: 6px 0 0; }
.rowedit-summary {
  display: inline-block; cursor: pointer; font-size: var(--fs-xs); color: var(--link); font-weight: 600;
  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; }

/* ------------------------------------------------- allowlist: numbered add form
   Three steps instead of one flat flex row, because the old layout wrapped an
   unlabelled date input and an unlabelled scope box onto a second line where
   neither said what it was. */
.formstep { display: flex; gap: 10px; padding: 10px 0; border-top: 1px solid var(--line); }
.formstep:first-of-type { border-top: 0; padding-top: 0; }
.formstep-num {
  flex: 0 0 20px; height: 20px; border-radius: 50%; background: var(--ink);
  color: #fff; font-size: 11px; font-weight: 700; line-height: 20px; text-align: center;
}
.formstep-body { flex: 1; min-width: 0; }
.formstep-body .field-label { display: block; margin-bottom: 6px; }
.formstep-split { display: flex; gap: 22px; flex-wrap: wrap; }
.formstep-split > div { min-width: 240px; }
.field-optional {
  font-size: 11px; font-weight: 500; color: var(--muted); text-transform: none;
  letter-spacing: 0;
}
