/* =========================================================
   Prusik Health – Design System
   Modern healthcare SaaS aesthetic
   ========================================================= */

/* ---- CSS Custom Properties ---------------------------------
   MOVED to assets/css/tokens.css (loaded before this file).
   All color/spacing/radius/shadow/type values live there. */

/* ---- Reset ------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body { font-family: var(--font-sans); color: var(--text-primary); background: var(--bg-body); line-height: 1.6; -webkit-font-smoothing: antialiased; }
img, svg { display: block; max-width: 100%; }
/* Content links get the brand color; button-styled links (.btn) keep their
   own component text color in both themes (see the dark override below). */
/* Content links only. .btn (button-styled links) and .sidebar-link (nav chrome
   with its own token/brand color) are excluded — otherwise this rule repaints
   them brand blue (invisible on the brand-blue sidebar). The exclusions MUST
   live inside :where() so they contribute ZERO specificity: this keeps the
   rule at the bare `a` level (0,0,1), exactly what every component anchor rule
   (`.data-table a`, `.footer-col a`, `.breadcrumb a`, all 0,1,1) was written
   against. Chained :not():not() (0,2,1) out-specified them all and recolored
   every data-table link brand-blue — caught in review. */
a:where(:not(.btn, .sidebar-link)) { color: var(--primary-text); text-decoration: none; transition: color var(--transition); }
a:where(:not(.btn, .sidebar-link)):hover { color: var(--primary-dark); }
a.btn { text-decoration: none; }
/* Links inside PROSE must not rely on color alone (WCAG 1.4.1 / axe
   link-in-text-block): a colorblind reader can't find them in a paragraph.
   Scoped to running text only — nav, cards, tables, pills and buttons stay
   undecorated. Kept inside :where() so specificity stays 0,0,1 and this cannot
   out-specify the component link rules noted above (`.data-table a` etc.). */
:where(p, li, dd, blockquote, .form-hint, figcaption) > a:where(:not(.btn, .pill, .chip-warning, .link-plain, .link-plain-color, [class*="-card"])) {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
button { font-family: inherit; cursor: pointer; border: none; background: none; }
ul, ol { list-style: none; }
h1,h2,h3,h4,h5,h6 { line-height: 1.25; font-weight: 700; letter-spacing: -0.02em; }

/* ---- Typography -------------------------------------------- */
h1 { font-size: clamp(2rem, 4vw, 3.25rem); }
h2 { font-size: clamp(1.5rem, 3vw, 2.25rem); }
h3 { font-size: 1.25rem; font-weight: 600; }
h4 { font-size: 1rem; font-weight: 600; }
p  { color: var(--gray-600); line-height: 1.75; }

/* ---- Layout ------------------------------------------------ */
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* ---- Buttons ----------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .375rem;
  font-weight: 500;
  font-size: .875rem;
  border-radius: var(--radius-md);
  padding: .5rem 1rem;
  border: 1px solid transparent;
  cursor: pointer;
  transition:
    background var(--transition),
    color var(--transition),
    border-color var(--transition),
    box-shadow var(--transition);
  text-decoration: none;
  white-space: nowrap;
  line-height: 1.25;
}
.btn:active { transform: scale(0.98); }
.btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.btn:disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
.btn-sm  { padding: .3125rem .75rem; font-size: .8125rem; }
.btn-lg  { padding: .75rem 1.5rem; font-size: 1rem; }
.btn-full { width: 100%; }

/* 2026: subtle top-light gradient + brand-tinted ambient glow (see
   docs/style-guide-2026.html §Buttons). Gradient stops derive from --primary
   via color-mix so the treatment is theme-proof. */
.btn-primary {
  background: var(--primary);
  color: var(--color-white);
  border-color: transparent;
  box-shadow: 0 1px 0 rgb(255 255 255 / 0.22) inset, 0 8px 22px -10px var(--primary);
}
.btn-primary:hover {
  background: linear-gradient(180deg, color-mix(in srgb, var(--primary-hover) 82%, var(--color-white)), var(--primary-hover));
  box-shadow: 0 1px 0 rgb(255 255 255 / 0.26) inset, 0 10px 26px -8px var(--primary);
}

.btn-outline {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--border-color);
}
.btn-outline:hover { background: var(--gray-50); border-color: var(--gray-300); }

.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
  border-color: transparent;
}
/* The :where() exclusion keeps an INK utility on hover. This rule is (0,2,0) and
   .text-danger is (0,1,0), so a plain `color: var(--text-primary)` here flipped a
   destructive ghost button to ordinary body text at the exact moment the pointer
   is over it — removing the danger cue during the click, in BOTH themes. Measured
   before the fix: light hover measured 15.13:1 resolving to --text-primary, i.e.
   not red at all. (Colours named by TOKEN, never as a literal — the rule-01
   ratchet scans raw source and counts a hex/rgb in a comment as a real value.) */
.btn-ghost:hover { background: var(--bg-alt); }
.btn-ghost:hover:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .text-muted)) {
  color: var(--text-primary);
}

.btn-danger {
  /* -strong: deep fill so white text stays AA in dark (base dark danger is a
     bright text-red at only 3.71:1 with white). */
  background: var(--color-danger-strong);
  color: white;
  border-color: var(--color-danger-strong);
}
.btn-danger:hover { background: var(--color-danger-strong); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-danger) 20%, transparent); }

/* Soft / tonal button variants — semantic, dark-mode-safe.
   Use when a button should hint at meaning without screaming for attention. */
.btn-primary-soft {
  background: var(--primary-light);
  color: var(--primary-dark);
  border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
}
.btn-primary-soft:hover { background: color-mix(in srgb, var(--primary-light) 80%, var(--primary) 20%); }

/* .btn-{tone}-soft (success/warning/danger) was a second, older spelling of the
   .btn-soft-{tone} family below with different token choices; consolidated onto
   the -bg/-border/-text triad on 2026-09-02. .btn-primary-soft stays: primary is
   the brand accent, not a semantic state, and has no triad sibling. */
/* Outline button tinted danger (text + border) — token-driven so it re-themes
   in dark mode. Used for destructive toggle buttons (deactivate user, etc.). */
.btn-outline.btn-outline-danger { color: var(--color-danger); border-color: var(--color-danger); }

[data-theme="dark"] .btn-primary-soft { color: var(--primary-soft); }
[data-theme="dark"] .btn-success-soft { color: var(--color-success-text); }
/* .btn-warning-soft / .btn-danger-soft dark overrides dropped 2026-07-23:
   their light rules now use --color-warning-text / --color-danger-text, which
   already carry these exact dark values. */

/* .btn-white sits on the fixed deep-ink bands (--bg-inverse: CTA banner,
   footer), which stay dark in BOTH themes — so it stays a literal white fill
   (color token), never a gray-* alias that would flip dark. */
.btn-white {
  background: var(--color-white);
  color: var(--primary-dark);
  font-weight: 700;
  box-shadow: 0 1px 3px rgba(0,0,0,.15);
}
.btn-white:hover { background: color-mix(in srgb, var(--color-white) 92%, var(--primary)); color: var(--primary-dark); box-shadow: 0 4px 12px rgba(0,0,0,.15); }

.btn-ghost-white {
  background: transparent;
  color: rgba(255,255,255,.9);
  border: 1.5px solid rgba(255,255,255,.4);
}
.btn-ghost-white:hover { background: rgba(255,255,255,.12); color: var(--color-white); border-color: rgba(255,255,255,.7); }

.btn-passkey {
  background: var(--gray-50);
  color: var(--gray-800);
  border: 1.5px solid var(--gray-200);
  font-size: .9375rem;
  font-weight: 500;
  justify-content: center;
  padding: .75rem 1.25rem;
}
.btn-passkey:hover { background: var(--gray-100); border-color: var(--gray-300); }

/* ---- Badges ------------------------------------------------ */
/* NOTE: the base `.badge` recipe and the status variants (success/warning/
   danger/info) live in ONE place — the badge block further down (search
   ".badge {" near the status-pill section). A second base block used to sit here
   with a different gap/padding/border-radius; it always lost the cascade to that
   later block, so it was dead code that misrepresented the rendered style.
   Only the variants that exist NOWHERE else are kept here.
   Badge variants are ALIASES of the pill recipe (same status-token triads), so
   dark mode needs no per-variant rules. Prefer `.pill .pill-*` in new code —
   style-guide rule 05 wants a single status family. */
.badge-new      { background: var(--primary-light); color: var(--primary-text); border: 1px solid color-mix(in srgb, var(--primary) 35%, transparent); }
.badge-trust    { background: var(--gray-100); color: var(--gray-600); font-size: .75rem; font-weight: 500; }

/* ---- Navigation -------------------------------------------- */
/* Marketing chrome follows the 2026 guide's instrument language: glass over
   the page ground on a hairline. Token-driven, so no dark override needed. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--bg-body) 82%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border-color);
  transition: box-shadow var(--transition-md);
}
.site-header.scrolled { box-shadow: var(--shadow-md); }

/* Reading-progress bar on the marketing header's bottom edge. The track is a
   thin strip flush with the border; the fill width is set by app.js on scroll. */
.scroll-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;            /* sit on the header's bottom border */
  height: 3px;
  background: transparent;
  overflow: hidden;
  pointer-events: none;
}
.scroll-progress-bar {
  height: 100%;
  width: 0;                /* JS sets this from scroll percentage */
  background: linear-gradient(90deg, var(--primary), var(--primary-text));
  border-radius: 0 2px 2px 0;
  transition: width 80ms linear;
}
[data-theme="dark"] .scroll-progress-bar {
  background: linear-gradient(90deg, var(--primary), var(--primary-soft));
}
@media (prefers-reduced-motion: reduce) {
  .scroll-progress-bar { transition: none; }
}

/* Back-to-top button — fixed bottom-right, revealed by app.js once the user
   scrolls down (.is-visible). Sits above page content but below the header. */
.back-to-top {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--primary) 25%, transparent);
  border-radius: var(--radius-full);
  background: var(--bg-card);
  color: var(--primary-text);
  box-shadow: var(--shadow-lg);
  cursor: pointer;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--transition), transform var(--transition), background var(--transition);
  pointer-events: none;
}
.back-to-top.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.back-to-top:hover { background: var(--primary-light); }
.back-to-top:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .back-to-top { transition: opacity var(--transition); transform: none; }
  .back-to-top.is-visible { transform: none; }
}

.nav-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1.5rem;
  height: 64px;
  display: flex;
  align-items: center;
  gap: 2rem;
}

.nav-logo {
  display: flex;
  align-items: center;
  gap: .625rem;
  text-decoration: none;
  flex-shrink: 0;
  color: var(--primary-dark);   /* the logo mark's lower chevron — see below */
}

/* ---- Logo mark (templates/partials/logo.php) --------------------------------
   The ascent chevron is ONE svg for every theme and ground. Since 2026-07-30 the
   assignment is INVERTED from the original: the LOWER chevron paints with
   `--mark-accent` (the teal/cyan brand accent, which reads as the icon's green)
   and the UPPER paints with `currentColor`. Nothing here sets a colour ON the
   svg, which is the whole trick — the mark INHERITS, so any ancestor owning the
   ground recolors it by normal specificity.

   Why the inherited chevron's colour lives on `.nav-logo` and not on `.oh-mark`:
   a bare `.oh-mark { color }` is (0,1,0) and out-specifies an inherited value,
   so the org-brand sidebar's injected `.app-sidebar .nav-logo { color: … }`
   (head.php, brand-luminance-flipped) LOST the cascade and the mark painted
   brand-blue on the customer's brand colour at 1.02:1 — measured in a browser,
   invisible, the same bug class as the historical 1.04:1 wordmark. Setting it
   on the link keeps inheritance intact and the sidebar keeps working for free.

   `--accent` already flips per theme in tokens.css (deep teal light, bright cyan
   dark), so the lower chevron needs no dark override. The light theme must NOT
   reuse dark's bright cyan — it measures 1.92:1 on white.

   The UPPER chevron is spec'd as brand blue in light and white in dark. Light
   mode therefore has to name the brand colour explicitly: `currentColor` on the
   nav would otherwise inherit `--text-primary` (near-black), which is not the
   spec. Dark mode keeps inheriting `--text-primary`, which IS near-white there.
   (Hex values are spelled out in words in these comments on purpose: the
   style-guide ratchet counts any hex literal in this file, comments included.) */
.oh-mark { flex-shrink: 0; --mark-accent: var(--accent); }
.nav-logo, .legal-logo, .auth-brand-logo { color: var(--primary-text); }
[data-theme="dark"] .nav-logo,
[data-theme="dark"] .legal-logo,
[data-theme="dark"] .auth-brand-logo { color: var(--text-primary); }
/* The footer ground (--bg-inverse) is dark in BOTH themes, so the mark pins the
   light-on-dark pair there instead of inheriting the page theme's — otherwise
   light mode would paint the deep brand blue on near-black at 1.7:1. The teal
   accent holds at 3.96:1 on that ground, over the 3:1 graphics bar. */
.site-footer .footer-brand .nav-logo { color: var(--text-inverse); }
.nav-logo-text {
  font-size: 1.125rem;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: -0.03em;
}
.nav-logo-text strong { font-weight: 800; color: var(--primary-text); }

.nav-menu {
  display: flex;
  align-items: center;
  gap: .25rem;
  flex: 1;
}
.nav-link {
  padding: .5rem .75rem;
  border-radius: var(--radius-sm);
  font-size: .9375rem;
  font-weight: 500;
  color: var(--text-secondary);
  transition: background var(--transition), color var(--transition);
}
.nav-link:hover, .nav-link.active { color: var(--text-primary); background: var(--bg-hover); }

/* CTAs mirrored into the collapsed menu for mobile (see nav.php); the desktop
   bar shows them via .nav-actions instead, so hide these until the mobile block. */
.nav-menu-cta { display: none; }

.nav-actions {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-left: auto;
}

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: .5rem;
  margin-left: auto;
}
.nav-toggle span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--text-secondary);
  border-radius: 2px;
  transition: transform var(--transition-md), opacity var(--transition-md);
}

/* ---- Marketing backdrop + content bands -------------------- */
/* THE RULE, stated once: on a public marketing page the starfield backdrop is
   visible in the OUTSIDE MARGIN for the full height of the page, and the
   reading column is OPAQUE so copy always sits on a solid ground.
 *
 * Before 2026-08-08 the backdrop was `position:absolute` INSIDE the hero, so
 * it stopped at the hero's bottom edge and every section below fell through to
 * whatever `body` was painting. That produced the drift this replaces: 12
 * sections declared no background at all (`.features`, `.faq-section`,
 * `.section-pad`, `.mission-section`, …) while 7 painted an explicit solid
 * (`.product-section`, `.certs-section`, `.roi-band`, …). The homepage was
 * almost entirely the transparent set and /product almost entirely the solid
 * set, so the two pages did not look like the same product.
 *
 * Mechanism, and why it is this and not something simpler:
 *   - `.marketing-backdrop` is `position: fixed`, so ONE element covers the
 *     whole scroll height. An absolute layer would have to be as tall as the
 *     document, which nothing here can know at paint time.
 *   - It is a SIBLING of the content, not an ancestor. An ancestor with a
 *     `filter`/`will-change` establishes a containing block that would break
 *     the sticky header.
 *   - Sections stay `background: transparent` and the opaque ground is painted
 *     by `.marketing-band::before` at `.container` width, so the solid edge
 *     lands exactly on the text edge (same 1200px/1.5rem geometry) with no new
 *     magic number to drift from `.container`.
 * A band that needs its own colour (the deep-ink `.roi-band`/`.cta-banner`)
 * sets `--band-ground`; everything else inherits the surface token. */
.marketing-backdrop {
  position: fixed;
  inset: 0;
  z-index: -2;                 /* backmost: behind .hero-decor, behind content */
  pointer-events: none;
  background: var(--hero-gradient);
}
/* It carries the STARFIELD only. The grid, glows and drifting peaks are hero
   COMPOSITION and live in `.hero-decor` below — between 2026-08-08 and 2026-08-09
   all four sat in this one fixed layer, which ran the GRID the entire document
   height (9,148px on the homepage against a 712px hero). It read as a blueprint
   behind every content section rather than as the hero's own texture, and the peaks
   drifted past the fold with nothing framing them. */

/* HERO-ONLY decoration: grid + glows + drifting peaks (hero-decor.php, included as
 * the first child of `.hero` / `.page-hero`).
 *
 * ABSOLUTE inside the hero, NOT a fixed layer clipped to a height. Real hero heights
 * measured 320px → 712px across pages and viewports (the homepage's two-column hero
 * vs /contact's title band, differing again at 768px), so any single clip height
 * would be wrong on nearly every page — too short stops the grid mid-hero, too tall
 * bleeds it into the first content section. `inset: 0` on the hero's own box is
 * exact everywhere and needs no maintenance.
 *
 * Both hero classes already declare `position: relative; overflow: hidden`, so this
 * needs nothing from them. z-index 0 keeps it under `.hero-layout` / `.container`,
 * which are z-index 1. */
.hero-decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;              /* the peaks drift; keep them inside the hero */
}

/* THE BAND ROSTER. Every marketing content section is listed HERE rather than
   carrying a `.marketing-band` class in markup, and that is deliberate: the
   class would have to be pasted onto ~60 <section> tags across 8 pages, and the
   defect being fixed is precisely that a per-site declaration drifts. One
   selector list is one place to be right, and `MarketingBackgroundTest` parses
   THIS list — so a new section class that is not added here fails the suite
   instead of silently shipping a transparent band.
 *
 * `.hero` and `.page-hero` are deliberately ABSENT: they are the surfaces whose
 * whole job is to show the backdrop through. So is `.social-proof` — it is a
 * thin logo strip immediately under the hero and reads as part of it.
 * `.section-continue` is absent because it is a modifier that always sits on a
 * section already listed here (a band of its own would double-paint the edge).
 *
 * `.roi-band` and `.cta-banner` are also absent, for a DIFFERENT reason worth
 * stating so nobody "completes the set": they are FULL-BLEED deep-ink bands
 * (`--bg-inverse` in both themes) and their own `::before` already carries the
 * audit-grid texture, so they are opaque edge-to-edge and have no pseudo-element
 * left to spend. They satisfy the rule (copy sits on a solid ground) by being
 * solid everywhere rather than by having a band, and they read as intentional
 * punctuation between starfield sections. The test lists them as such. */
.features,
.how-it-works,
.testimonials,
.compare-section,
.integrations-section,
.pricing-preview,
.faq-section,
.section-pad,
.product-section,
.mission-section,
.values-section,
.team-section,
.certs-section,
.security-section,
.marketing-band { position: relative; background: transparent; }

.features > *,
.how-it-works > *,
.testimonials > *,
.compare-section > *,
.integrations-section > *,
.pricing-preview > *,
.faq-section > *,
.section-pad > *,
.product-section > *,
.mission-section > *,
.values-section > *,
.team-section > *,
.certs-section > *,
.security-section > *,
.marketing-band > * { position: relative; z-index: 1; }

/* Opaque reading column. `::before` rather than a background on the section
   itself, because the band must be `.container`-width while the section stays
   full-bleed (its hairline borders and padding are measured edge-to-edge). */
.features::before,
.how-it-works::before,
.testimonials::before,
.compare-section::before,
.integrations-section::before,
.pricing-preview::before,
.faq-section::before,
.section-pad::before,
.product-section::before,
.mission-section::before,
.values-section::before,
.team-section::before,
.certs-section::before,
.security-section::before,
.marketing-band::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  /* Centre a 1200px band regardless of viewport width. `left:50%` +
     translateX(-50%) rather than `inset-inline: max(...)` so the arithmetic is
     one expression and matches `.container`'s `margin: 0 auto` exactly. */
  left: 50%;
  transform: translateX(-50%);
  width: min(100%, 1200px);
  background: var(--band-ground, var(--bg-surface));
  pointer-events: none;
}
/* Below the container width there is no margin left to show stars in, so the
   band is the full width and the backdrop is simply covered. Stated explicitly
   because a reader will otherwise wonder where the mobile starfield went: it is
   visible in the hero and nowhere else, which is correct — a 390px phone has no
   outside margin to spare. */

/* ---- Hero -------------------------------------------------- */
/* 2026 reimagining ("proof, rendered"): the hero pairs the display voice with
   a live-reading compliance instrument panel instead of a fake app screenshot.
   The load is one orchestrated sequence — copy rises in staggered steps, then
   the panel draws its gauge and meters. Everything collapses under
   prefers-reduced-motion. */
/* The hero is deliberately TRANSPARENT: the fixed `.marketing-backdrop` paints
   the gradient and the starfield behind it, so a background here would hide the
   very layer the hero exists to show. `overflow: hidden` is retained — the
   instrument panel bleeds off the right edge by design. */
.hero {
  position: relative;
  padding: 5.5rem 0 4.5rem;
  overflow: hidden;
  background: transparent;
}
/* Two-column layout so the headline column and the decorative screenshot live
   in separate grid tracks and CANNOT overlap, at any width. The screenshot
   column is allowed to bleed off the right edge (negative margin) for the
   intended "peeking in" look, but its LEFT edge is pinned to the track gap, so
   it never crosses into the text. .hero-content keeps the .container centering
   semantics via the wrapper below. */
.hero-layout {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(360px, 33vw, 440px);
  align-items: center;
  gap: 3rem;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1.5rem;
}
/* `.hero-bg` was removed 2026-08-09: it was the single wrapper holding all four
   decorations, and splitting them into `.marketing-backdrop` (page-wide starfield)
   plus `.hero-decor` (hero-only grid/glows/peaks) left it referenced by nothing.
   Deleted rather than left behind — an unused rule is a standing invitation to
   reintroduce the combined layer that caused the full-page grid. */
.hero-grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(var(--hero-grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--hero-grid-line) 1px, transparent 1px);
  background-size: 48px 48px;
}
.hero-glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(80px);
  opacity: var(--hero-glow-opacity);
}
.hero-glow-1 { width: 500px; height: 500px; background: var(--hero-glow-1); top: -100px; left: -100px; }
.hero-glow-2 { width: 400px; height: 400px; background: var(--hero-glow-2); bottom: -100px; right: 0; }

/* Slow-drifting geometric shapes — the HERO-ONLY layer (hero-decor.php partial),
   NOT the page-wide backdrop. The starfield is separate and lives in
   marketing-bg.php; this comment said otherwise until 2026-08-09, and it is exactly
   the comment a reader consults before moving a decoration back into the combined
   layer that caused the full-page grid. Token-driven so both themes stay on-palette.
   CSS-only, compositor-friendly, frozen for reduced motion. */
.hero-shapes { position: absolute; inset: 0; overflow: hidden; }
.hero-shape {
  position: absolute;
  color: var(--hero-shape);
  animation: heroShapeDrift 36s ease-in-out infinite alternate;
  will-change: transform;
}
/* Shapes live on the RIGHT/edge regions, clear of the headline column (text
   occupies the left ~680px on the homepage hero); drift never carries them
   under the title. */
.hero-shape-1 { top: 8%; right: 5%; }
.hero-shape-2 { top: 62%; right: 12%; animation-duration: 44s; animation-delay: -12s; }
.hero-shape-3 { bottom: 6%; right: 32%; animation-duration: 52s; animation-delay: -25s; }
/* Full-width text below 1100px: fade the shapes further so they never compete
   with copy that now spans the whole hero. */
@media (max-width: 1100px) {
  .hero-shapes { opacity: .45; }
}
@keyframes heroShapeDrift {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(3rem, -2rem, 0) rotate(14deg); }
}
.hero-stars,
.hero-stars::after {
  content: "";
  position: absolute;
  inset: -10%;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 28%, var(--hero-star) 50%, transparent 51%),
    radial-gradient(1px 1px at 31% 67%, var(--hero-star) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 48% 18%, var(--hero-star) 50%, transparent 51%),
    radial-gradient(1px 1px at 63% 52%, var(--hero-star) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 78% 31%, var(--hero-star) 50%, transparent 51%),
    radial-gradient(1px 1px at 89% 74%, var(--hero-star) 50%, transparent 51%),
    radial-gradient(1px 1px at 22% 85%, var(--hero-star) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 70% 88%, var(--hero-star) 50%, transparent 51%);
  background-repeat: no-repeat;
  animation: heroStarTwinkle 9s ease-in-out infinite;
}
.hero-stars { animation-delay: 0s; }
.hero-stars::after {
  inset: 0;
  transform: translate3d(4%, 7%, 0) scale(1.15);
  animation-delay: -4.5s;
}
@keyframes heroStarTwinkle {
  0%, 100% { opacity: .25; }
  50%      { opacity: .9; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-shape, .hero-stars, .hero-stars::after { animation: none; }
}

.hero-content {
  position: relative;
  z-index: 1;
  min-width: 0; /* allow the grid track to shrink instead of overflowing */
}

/* Badge chip: the guide's mono "instrument texture" with a live signal dot. */
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  padding: .4rem .95rem;
  background: color-mix(in srgb, var(--bg-surface) 70%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  font-family: var(--font-mono);
  font-size: .78rem;
  letter-spacing: .01em;
  color: var(--text-secondary);
  margin-bottom: 1.9rem;
  backdrop-filter: blur(10px);
}
.hero-badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
  flex: none;
}
.badge-arrow { color: var(--primary-text); margin-left: auto; }

.hero-title {
  font-size: clamp(2.5rem, 5.4vw, 4.25rem);
  font-weight: 800;
  color: var(--text-primary);
  letter-spacing: -0.045em;
  line-height: .98;
  text-wrap: balance;
  margin-bottom: 1.4rem;
}
.gradient-text {
  background: linear-gradient(100deg, var(--primary) 0%, var(--accent) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
.hero-subtitle {
  font-size: 1.125rem;
  color: var(--text-secondary);
  line-height: 1.65;
  margin-bottom: 2rem;
  max-width: 56ch;
}
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .875rem;
  margin-bottom: 1.1rem;
}
.hero-note { font-family: var(--font-mono); font-size: .75rem; letter-spacing: .01em; color: var(--text-muted); }

/* Orchestrated load-in: badge → title → subtitle → actions → note, then the
   instrument panel rises with them and draws its readings. */
@keyframes heroRise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
.hero-content > * { animation: heroRise .65s cubic-bezier(.22,.61,.36,1) both; }
.hero-content > :nth-child(2) { animation-delay: .07s; }
.hero-content > :nth-child(3) { animation-delay: .14s; }
.hero-content > :nth-child(4) { animation-delay: .21s; }
.hero-content > :nth-child(5) { animation-delay: .28s; }
@media (prefers-reduced-motion: reduce) {
  .hero-content > * { animation: none; }
}

/* Compliance instrument panel — the hero's thesis moment. A glass card on a
   hairline showing the product's real semantics: a grade gauge that draws in,
   coverage meters, an open-findings readout, and the mono audit-chain line.
   Decorative (aria-hidden); token-driven end to end, so both themes hold.
   Lives in its own grid column so it can never cross the headline column. */
.hero-panel {
  position: relative;
  background: color-mix(in srgb, var(--bg-surface) 72%, transparent);
  backdrop-filter: blur(16px);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 1.5rem;
  pointer-events: none;
  z-index: 0;
  animation: heroRise .8s .3s cubic-bezier(.22,.61,.36,1) both;
}
.hero-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.35rem;
}
.hero-panel-title {
  font-family: var(--font-mono);
  font-size: .68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .13em;
  color: var(--text-muted);
}
.hero-panel-live {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-family: var(--font-mono);
  font-size: .64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .11em;
  color: var(--color-success-text);
}
.hero-panel-live::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-success);
  box-shadow: 0 0 8px var(--color-success);
  animation: heroLivePulse 2.6s ease-in-out infinite;
}
@keyframes heroLivePulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .35; }
}

.hero-gauge-row { display: flex; align-items: center; gap: 1.5rem; margin-bottom: 1.35rem; }
.hero-gauge { position: relative; width: 116px; height: 116px; flex: none; }
.hero-gauge svg { display: block; transform: rotate(-90deg); }
.hero-gauge-track { fill: none; stroke: var(--bg-alt); stroke-width: 10; }
.hero-gauge-fill {
  fill: none;
  stroke: var(--color-success);
  stroke-width: 10;
  stroke-linecap: round;
  /* r=49 → C≈308; 85/100 ≈ 262 */
  stroke-dasharray: 262 308;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-success) 55%, transparent));
  animation: heroGaugeDraw 1.2s .55s cubic-bezier(.22,.61,.36,1) both;
}
@keyframes heroGaugeDraw { from { stroke-dasharray: 0 308; } }
.hero-gauge-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.hero-gauge-grade { font-size: 1.9rem; font-weight: 800; letter-spacing: -.03em; line-height: 1; color: var(--color-success-text); }
.hero-gauge-sub { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .07em; color: var(--text-muted); margin-top: .3rem; }

.hero-meters { display: flex; flex-direction: column; gap: .8rem; min-width: 0; flex: 1; }
.hero-meter-label {
  display: flex;
  justify-content: space-between;
  gap: .75rem;
  font-size: .78rem;
  color: var(--text-secondary);
  margin-bottom: .3rem;
}
.hero-meter-label .pct { font-family: var(--font-mono); font-size: .7rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.hero-meter {
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--bg-alt);
  overflow: hidden;
}
.hero-meter-fill { height: 100%; border-radius: inherit; animation: heroMeterFill 1s .65s cubic-bezier(.22,.61,.36,1) both; }
.hero-meter-fill-a { width: 96%; background: var(--color-success); }
.hero-meter-fill-b { width: 91%; background: linear-gradient(90deg, var(--primary), var(--accent)); }
.hero-meter-fill-c { width: 82%; background: var(--color-warning); }
@keyframes heroMeterFill { from { width: 0; } }

/* Open-findings readout: state encoded in form (guide §Severity). */
.hero-sev {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.1rem;
  font-size: .78rem;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.hero-sev span { display: inline-flex; align-items: center; gap: .45rem; }
.hero-sev i { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.hero-sev-high i { background: var(--color-orange); }
.hero-sev-med  i { background: var(--color-warning); }
.hero-sev-low  i { background: var(--color-success); }

/* The tamper-evident audit-chain line — the product's signature, in mono. */
.hero-chain {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin-top: 1.2rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--border-color);
  font-family: var(--font-mono);
  font-size: .67rem;
  letter-spacing: .01em;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
}
.hero-chain .ok { color: var(--color-success-text); flex: none; }

@media (prefers-reduced-motion: reduce) {
  .hero-panel { animation: none; }
  .hero-panel-live::before { animation: none; }
  .hero-gauge-fill { animation: none; }
  .hero-meter-fill { animation: none; }
}

/* ---- Social Proof ------------------------------------------ */
.social-proof {
  padding: 2.75rem 0;
  border-bottom: 1px solid var(--border-color);
}
.social-proof-label {
  text-align: center;
  font-family: var(--font-mono);
  font-size: .7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--text-muted);
  margin-bottom: 1.25rem;
}
.logo-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .75rem;
}
/* Framework names read as data, not brands — mono chips on glass hairlines. */
.logo-pill {
  padding: .45rem 1.1rem;
  background: color-mix(in srgb, var(--bg-surface) 60%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  font-family: var(--font-mono);
  font-size: .78rem;
  font-weight: 500;
  letter-spacing: .01em;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* ---- Section Header ---------------------------------------- */
/* Left-aligned with a mono eyebrow whose hairline runs to the section's edge
   (the guide's block-label pattern) — structure over centered symmetry. */
.section-header {
  margin: 0 0 3rem;
}
.section-label {
  display: flex;
  align-items: center;
  gap: .6rem;
  font-family: var(--font-mono);
  font-size: .7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--primary-text);
  margin-bottom: 1rem;
}
.section-label::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--primary);
  flex: none;
}
.section-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border-color);
}
/* Same clamp as the base `h2`. It capped at 2.4rem against the base's 2.25rem — a
   2.4px difference that reads as a mistake rather than a hierarchy, and it put THREE
   h2 sizes on /product and /about (38.4, 36, 32). Two steps is a scale; three that
   close together is drift. Weight and letter-spacing still distinguish a section
   header from an inline h2, which is the distinction that was actually wanted. */
.section-header h2 {
  color: var(--text-primary);
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  font-weight: 800;
  letter-spacing: -.035em;
  line-height: 1.08;
  text-wrap: balance;
  max-width: 26ch;
  margin-bottom: .875rem;
}
.section-header p { font-size: 1.0625rem; color: var(--text-secondary); max-width: 66ch; }

/* ---- Features ---------------------------------------------- */
.features { padding: var(--section-pad) 0; }
.features-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 1rem;
}
/* Glass on hairline; hover answers with the brand hairline + a small lift
   (guide swatch behavior). Tinted icon discs use transparent mixes + the
   *-text tokens, so one rule holds AA in both themes — no dark overrides. */
.feature-card {
  padding: 1.6rem;
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  transition: border-color var(--transition-md), box-shadow var(--transition-md), transform var(--transition-md);
}
.feature-card:hover {
  border-color: color-mix(in srgb, var(--primary) 45%, var(--border-color));
  box-shadow: var(--shadow-sm);
  transform: translateY(-3px);
}
.feature-icon {
  width: 42px; height: 42px;
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1.1rem;
}
.feature-icon-blue   { background: color-mix(in srgb, var(--primary) 12%, transparent); color: var(--primary-text); }
.feature-icon-teal   { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent-text); }
.feature-icon-purple { background: color-mix(in srgb, var(--color-purple) 12%, transparent); color: var(--color-purple); }
.feature-icon-green  { background: color-mix(in srgb, var(--color-success) 12%, transparent); color: var(--color-success-text); }
.feature-icon-orange { background: color-mix(in srgb, var(--color-orange) 12%, transparent); color: var(--color-orange-text); }
.feature-icon-red    { background: color-mix(in srgb, var(--color-danger) 12%, transparent); color: var(--color-danger-text); }
.feature-card h3 { margin-bottom: .5rem; color: var(--text-primary); font-size: 1.05rem; letter-spacing: -.01em; }
.feature-card p { font-size: .92rem; }

/* ---- How It Works ------------------------------------------ */
/* A real sequence, so the numbers stay — as mono indices over per-step
   hairlines, the way the guide numbers its sections. */
.how-it-works { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 2rem; }
.step { padding-top: 1.1rem; border-top: 1px solid var(--border-color); }
.step-number {
  font-family: var(--font-mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .1em;
  color: var(--primary-text);
  line-height: 1;
  margin-bottom: .9rem;
}
.step-content h3 { color: var(--text-primary); font-size: 1.05rem; margin-bottom: .5rem; }
.step-content p { font-size: .92rem; }

/* ---- Testimonials ------------------------------------------ */
.testimonials { padding: var(--section-pad) 0; }
.testimonials-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; }
.testimonial-card {
  padding: 1.6rem;
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
}
.testimonial-card p { font-size: 1rem; color: var(--text-secondary); margin-bottom: 1.25rem; line-height: 1.7; }
.testimonial-card footer { display: flex; align-items: center; gap: .875rem; }
/* Initials on a translucent brand disc (guide avatar recipe). */
.testimonial-avatar {
  width: 40px; height: 40px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary) 35%, transparent);
  color: var(--primary-text);
  display: flex; align-items: center; justify-content: center;
  font-size: .8125rem;
  font-weight: 700;
  flex-shrink: 0;
}
.testimonial-card footer strong { display: block; font-size: .9375rem; color: var(--text-primary); }
.testimonial-card footer span   { font-size: .8125rem; color: var(--text-muted); }

/* ---- Pricing ----------------------------------------------- */
.pricing-preview { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
/* Continuation section: reads as part of the section above (e.g. the
   Professional Services tier continues Pricing). Drops the separating
   hairline — otherwise it collides with the eyebrow's own hairline at the
   section edge — and lets the previous section's bottom padding provide
   the gap, with a small buffer of its own so the label never sits flush. */
.section-continue { border-top: 0; padding-top: .5rem; }
.pricing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1rem;
  align-items: stretch;
}
.pricing-card {
  position: relative;
  padding: 2rem;
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
}
/* The featured tier answers with the brand hairline + a brand-tinted ambient
   glow — no third ring color. */
.pricing-card-featured {
  border-color: color-mix(in srgb, var(--primary) 55%, var(--border-color));
  box-shadow: 0 24px 50px -28px color-mix(in srgb, var(--primary) 45%, transparent);
}
.pricing-badge {
  position: absolute;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--primary);
  color: var(--color-white);
  font-family: var(--font-mono);
  font-size: .66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  padding: .3rem .9rem;
  border-radius: var(--radius-full);
}
.pricing-header { margin-bottom: 1.5rem; }
.pricing-header h3 { font-size: 1.15rem; color: var(--text-primary); letter-spacing: -.01em; margin-bottom: .5rem; }
.pricing-price { margin-bottom: .5rem; }
/* The amount. Scoped to :first-child (was a bare `span`) because the interval
   selector added a second span for the "/mo" suffix, and the bare rule painted
   THAT at 2.4rem/800 too. NOTE: no tabular-nums here. Tabular figures force
   every glyph into one advance width, which is right for a COLUMN of numbers but
   puts a full digit-width gap either side of a comma on a single headline: with
   the annual interval selected, "$2,999" rendered as "$2 , 999". The prices sit
   in separate cards and never align vertically, so there is nothing to gain. */
.pricing-price > span:first-child { font-size: 2.4rem; font-weight: 800; letter-spacing: -.04em; color: var(--text-primary); }
.pricing-price { font-size: .9375rem; color: var(--text-muted); }
/* The "/mo" or "/year" suffix rides at body size next to the amount. */
.pricing-price > span + span { font-size: .9375rem; font-weight: 500; letter-spacing: normal; color: var(--text-muted); }
.pricing-features { flex: 1; margin-bottom: 1.5rem; }
.pricing-features li { padding: .4375rem 0; border-bottom: 1px solid color-mix(in srgb, var(--border-color) 55%, transparent); font-size: .9375rem; color: var(--text-secondary); }
.pricing-features li:last-child { border: none; }
.pricing-note { text-align: center; font-size: .875rem; color: var(--text-muted); margin-top: 1.5rem; }

/* Trust strip — compact reassurance row beside a purchase decision (pricing
   cards). Each item gets a check mark; wraps to a column on small screens. */
.trust-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .5rem 2rem;
  margin: 0 0 2.5rem;
  padding: 0;
  list-style: none;
  font-size: .875rem;
  color: var(--text-secondary);
}
.trust-strip li::before {
  content: "✓";
  margin-right: .5rem;
  font-weight: 700;
  color: var(--color-success-text);
}

/* Pricing page section + FAQ accordion */
.section-pad { padding: var(--section-pad) 0; }
.pricing-grid-4 { max-width: 1200px; margin: 0 auto 2rem; }
/* Single centered pricing card (e.g. the standalone Professional Services callout). */
.pricing-grid-single { max-width: 480px; margin: 0 auto; }
.faq-wrap { max-width: 760px; margin: 4rem 0 0; }
.faq-list { display: flex; flex-direction: column; gap: .75rem; }
.faq-item {
  padding: 1.2rem 1.4rem;
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  transition: border-color var(--transition-md);
}
.faq-item[open], .faq-item:hover { border-color: color-mix(in srgb, var(--primary) 40%, var(--border-color)); }
.faq-q {
  font-weight: 600;
  color: var(--text-primary);
  cursor: pointer;
  font-size: 1rem;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}
.faq-plus { font-family: var(--font-mono); font-size: 1.1rem; color: var(--text-subtle); transition: transform var(--transition-md); }
.faq-item[open] .faq-plus { transform: rotate(45deg); }
.faq-a { margin-top: .875rem; font-size: .9375rem; color: var(--text-secondary); }

/* ---- Homepage comparison + FAQ sections -------------------- */
.compare-section { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
.faq-section { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }

/* ROI / outcomes band — the instrument readout. Deep-ink in BOTH themes
   (--bg-inverse pairs with --text-inverse), with the audit-grid texture and a
   brand glow. On-ink accents mix toward white so small text holds AA on the
   fixed dark ground in either theme. */
.roi-band {
  position: relative;
  overflow: hidden;
  padding: var(--section-pad) 0;
  background: var(--bg-inverse);
}
.roi-band::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(130% 100% at 50% 0, black, transparent 82%);
}
.roi-band::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(640px 320px at 85% -10%, color-mix(in srgb, var(--primary) 28%, transparent), transparent 70%),
    radial-gradient(520px 300px at 8% 110%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 70%);
}
.roi-band .container { position: relative; z-index: 1; }
.roi-band .section-label { color: color-mix(in srgb, var(--accent) 60%, var(--color-white)); }
.roi-band .section-label::before { background: color-mix(in srgb, var(--accent) 60%, var(--color-white)); }
.roi-band .section-label::after { background: rgba(255,255,255,.14); }
.roi-band .section-header h2 { color: var(--text-inverse); }
.roi-band .section-header p { color: color-mix(in srgb, var(--text-inverse) 75%, transparent); }
.roi-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2.5rem;
  max-width: 1080px;
}
.roi-stat { padding-top: 1.1rem; border-top: 1px solid rgba(255,255,255,.14); }
.roi-stat .stat-number { font-size: 3rem; letter-spacing: -.04em; color: var(--text-inverse); font-variant-numeric: tabular-nums; }
.roi-stat .stat-number .text-muted { color: color-mix(in srgb, var(--text-inverse) 60%, transparent); }
.roi-label { font-weight: 600; color: var(--text-inverse); margin: .5rem 0 .375rem; }
.roi-sub { font-size: .9rem; color: color-mix(in srgb, var(--text-inverse) 72%, transparent); line-height: 1.6; margin: 0; }
@media (max-width: 760px) { .roi-grid { grid-template-columns: 1fr; gap: 2.5rem; } }

/* Integrations strip */
.integrations-section { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
.integrations-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  max-width: 1080px;
  margin: 0 auto;
}
.integration-card {
  display: flex;
  flex-direction: column;
  gap: .25rem;
  padding: 1.125rem 1.25rem;
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  transition: border-color var(--transition-md);
}
.integration-card:hover { border-color: color-mix(in srgb, var(--primary) 40%, var(--border-color)); }
.integration-card strong { font-size: .9375rem; color: var(--text-primary); }
.integration-card span { font-size: .8125rem; color: var(--text-muted); line-height: 1.5; }
@media (max-width: 900px) { .integrations-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .integrations-grid { grid-template-columns: 1fr; } }
/* The homepage FAQ provides its own section header, so reset the standalone
   top margin .faq-wrap uses on the pricing page. */
.faq-section .faq-wrap { margin-top: 2rem; }

/* The workhorse-table treatment from the guide: mono uppercase header on the
   alt ground, hairline rows, and a translucent brand tint on the "us" column
   (a transparent mix, so it re-themes without a dark override). */
.compare-table {
  max-width: 900px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
}
/* STRETCH, don't centre. `align-items: center` sized each cell to its own
   content, so the tinted `.compare-us` column and the header bands rendered
   shorter than their row and floated with white strips above and below —
   worst on the two-line "Cross-framework reuse" row (25px short). Cells now
   fill the row and centre their own text instead. */
.compare-row {
  display: grid;
  grid-template-columns: 1fr 9rem 9rem;
  align-items: stretch;
}
.compare-row + .compare-row { border-top: 1px solid color-mix(in srgb, var(--border-color) 65%, transparent); }
.compare-cell { padding: .875rem 1.25rem; font-size: .9375rem; display: flex; align-items: center; }
.compare-feature { color: var(--text-secondary); }
.compare-them, .compare-us { justify-content: center; text-align: center; }
.compare-head .compare-cell {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: .66rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-muted);
  background: var(--bg-alt);
}
/* The top-left header cell is empty — painting it put a grey block over blank
   space. Leave it transparent so the shading reads as column headers. */
.compare-head .compare-feature { background: transparent; }
.compare-us { background: color-mix(in srgb, var(--primary) 7%, transparent); }
.compare-head .compare-us { color: var(--primary-text); background: color-mix(in srgb, var(--primary) 14%, transparent); }
.compare-yes { color: var(--color-success-text); font-weight: 700; }
.compare-no { color: var(--text-subtle); }
@media (max-width: 640px) {
  .compare-row { grid-template-columns: 1fr 4.5rem 4.5rem; }
  .compare-cell { padding: .75rem .75rem; font-size: .875rem; }
}

/* ---- CTA Banner -------------------------------------------- */
/* Deep-ink instrument band (replaces the dated 135° brand gradient): fixed
   --bg-inverse ground in both themes, audit-grid texture, brand + signal
   glows. White CTA reads as the single bright control on the panel. */
.cta-banner {
  position: relative;
  overflow: hidden;
  padding: var(--section-pad) 0;
  background: var(--bg-inverse);
}
.cta-banner::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(130% 120% at 50% 0, black, transparent 85%);
}
.cta-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(560px 300px at 12% -20%, color-mix(in srgb, var(--primary) 30%, transparent), transparent 70%),
    radial-gradient(480px 280px at 92% 120%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 70%);
}
.cta-banner-content {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
}
.cta-banner h2 { color: var(--text-inverse); font-size: clamp(1.5rem, 3vw, 2rem); letter-spacing: -.03em; margin-bottom: .375rem; }
.cta-banner p  { color: color-mix(in srgb, var(--text-inverse) 78%, transparent); font-size: 1.0625rem; }
.cta-banner-actions { display: flex; gap: .875rem; flex-wrap: wrap; }

/* ---- Footer ------------------------------------------------ */
/* Deep-ink band on --bg-inverse (fixed dark in BOTH themes, pairs with
   --text-inverse) — this replaces the gray-900 alias, whose dark-theme flip
   to near-white put white brand text on a light ground (the axe
   footer-brand contrast finding). On-ink text uses white mixes ≥58% so every
   tier clears AA on the fixed ground. Selectors are scoped .site-footer so
   they outrank the global dark-anchor remap regardless of source order. */
.site-footer {
  background: var(--bg-inverse);
  color: color-mix(in srgb, var(--text-inverse) 70%, transparent);
  border-top: 1px solid rgba(255,255,255,.08);
  padding: 4rem 0 0;
}
.site-footer p { color: color-mix(in srgb, var(--text-inverse) 70%, transparent); }
.footer-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1.5rem 3rem;
  display: grid;
  grid-template-columns: 2fr repeat(4, 1fr);
  gap: 3rem;
}
.site-footer .footer-brand .nav-logo-text { color: var(--text-inverse); }
/* The wordmark's brand-blue <strong> mixes toward white on the fixed ink
   ground — light-theme --primary-text alone is only ~3.4:1 there (axe). */
.site-footer .footer-brand .nav-logo-text strong { color: color-mix(in srgb, var(--primary-text) 50%, var(--color-white)); }
.footer-tagline { font-size: .9rem; margin: 1rem 0; line-height: 1.6; }
.footer-badges { display: flex; flex-wrap: wrap; gap: .5rem; }
.site-footer .footer-badges .badge {
  padding: .25rem .625rem;
  border: 1px solid rgba(255,255,255,.18);
  border-radius: var(--radius-full);
  background: transparent;
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .04em;
  color: color-mix(in srgb, var(--text-inverse) 72%, transparent);
}
.footer-links { display: contents; }
.site-footer .footer-col h4 {
  color: color-mix(in srgb, var(--text-inverse) 62%, transparent);
  font-family: var(--font-mono);
  font-size: .68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .13em;
  margin-bottom: 1rem;
}
.footer-col li + li { margin-top: .5rem; }
.site-footer a:not(.btn) { color: color-mix(in srgb, var(--text-inverse) 74%, transparent); font-size: .9rem; transition: color var(--transition); }
.site-footer a:not(.btn):hover { color: var(--text-inverse); }
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,.08);
  padding: 1.5rem;
  text-align: center;
  font-size: .8125rem;
}
.site-footer .footer-disclaimer { margin-top: .5rem; color: color-mix(in srgb, var(--text-inverse) 60%, transparent); font-size: .775rem; max-width: 600px; margin-inline: auto; }

/* ---- Alerts ------------------------------------------------ */
.alert {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  border-radius: var(--radius-md);
  padding: .875rem 1rem;
  font-size: .875rem;
  font-weight: 500;
  border: 1px solid transparent;
  animation: slideDown 200ms ease both;
  margin-bottom: 1.25rem;
}
.alert p { margin: 0; }
.alert-list { margin: 0; padding-left: 1.25rem; list-style: disc; }

/* ---- Legal document pages (privacy / terms / dpa) --------------------------
   Element defaults under .legal-doc do the work, so the documents themselves
   carry no styling at all. Token-driven → dark-mode safe. */
.legal-page { background: var(--gray-50); min-height: 100vh; }
/* ONE reading-column width for every public content page.
 *
 * These nine pages (legal, docs, the HIPAA guide, status, blog) sit behind the same
 * nav and a visitor moves between them in a couple of clicks, so they have to read
 * as one section of the site. They did not: measured, the card was 752px on
 * legal/*, 792px on docs/* and the guide, and 712px on status — three different
 * widths from `.legal-doc` (800), `.maxw-840 .content-pad` (840) and
 * `.legal-doc.maxw-760` (760). Nothing was wrong with any single page, which is why
 * it survived; the defect only exists in the transition between them.
 *
 * 800px is the width the majority already used and sits in the 60-80ch range that
 * long-form prose wants. `--reading-max` so the number lives in ONE place — the
 * previous three were three literals nobody could have diffed. */
.legal-doc,
.content-doc {
  max-width: var(--reading-max, 800px);
  margin: 0 auto;
  padding: 3rem 1.5rem 5rem;
}
.legal-logo {
  font-family: var(--font-sans);
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--primary-text);
  text-decoration: none;
}
.legal-logo svg { vertical-align: -.3em; margin-right: .4em; display: inline-block; }
/* Hairline + shadow, not shadow alone. A white card on the near-white content ground
   separated only by a soft shadow reads as a rendering artefact rather than an edge —
   the card looked unfinished at exactly the point it should feel deliberate. Every
   other card in the system (.feature-card, .value-card, .team-card, .security-item)
   already pairs the two; this was the outlier. */
.legal-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: 2.5rem;
  box-shadow: var(--shadow-sm);
}
/* Headings are INK, not brand blue.
 *
 * Every structural element on these pages used --primary-text: the h1, every h2, and
 * every card title — the same token as the links. (Naming the hex here instead would
 * fail the rule-01 ratchet, which reads raw source.) Brand blue
 * was doing four jobs at once (page title, section title, card title, link), so it
 * signalled none of them, and rank had to be carried by size alone. Ink for headings
 * and blue for things you can click is the whole fix; it costs no layout change. */
.legal-card h1 { font-size: 1.75rem; font-weight: 700; color: var(--text-primary); margin: 0 0 .5rem; }
.legal-meta { color: var(--gray-500); font-size: .875rem; margin: 0 0 2rem; }
.legal-body { font-family: var(--font-sans); font-size: .9375rem; line-height: 1.7; color: var(--text-secondary); }
.legal-body h2 { font-size: 1.125rem; font-weight: 600; color: var(--text-primary); margin: 2rem 0 .75rem; }
.legal-body p { margin: 0 0 1rem; color: inherit; }
.legal-body ul, .legal-body ol { margin: 0 0 1rem; padding-left: 1.5rem; }
.legal-body ul { list-style: disc; }
.legal-body ol { list-style: decimal; }
.legal-body li { margin-bottom: .375rem; }
/* `.legal-accent` was painting HEADINGS in --primary-text, the same brand blue as the
   links around them, so nothing on these pages distinguished a title from something
   clickable. All 30 sites are headings or heading-like (12 h1, 11 h2, 4 div, 2 span,
   1 strong) and now take ink.
 *
   The `a.legal-accent` rule below is a FORWARD GUARD and currently matches nothing —
   verified, zero anchors carry the class. (An earlier version of this comment claimed
   13 mailto uses; that was a misread grep — those lines contain a mailto AND a
   separate heading, and the class is on the heading.) It is kept so that classing a
   link with it later cannot silently paint it as body ink. Prose links on these pages
   get their blue from the base `a:where(:not(.btn, .sidebar-link))` rule and measure
   5.28:1 light / 6.53:1 dark on the card ground, underlined, so colour is not their
   only cue. */
.legal-accent { color: var(--text-primary); }
a.legal-accent { color: var(--primary-text); }
.legal-callout {
  margin: 0 0 1rem;
  padding: 1.25rem 1.5rem;
  background: var(--gray-50);
  border-left: 3px solid var(--primary-dark);
  border-radius: 0 6px 6px 0;
}
.legal-footnote {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--gray-100);
  font-size: .875rem;
  color: var(--gray-500);
}
.legal-foot { text-align: center; margin-top: 1.5rem; font-size: .8125rem; color: var(--text-muted); }

/* ---- Credentials carousel (about page) ------------------------------------
   Token-driven, so the only dark-mode rule needed is the light chip behind the
   PNG logos (they're drawn for light backgrounds). */
/* Same vertical rhythm + divider as its siblings on /about (.values-section,
   .team-section, .security-section all use this exact pair). This rule carried
   only the background, so the section header butted up against the section
   above it with no padding and no divider — visibly inconsistent with every
   other header on the page. */
/* Background intentionally omitted — the band roster paints this section's
   opaque column. A `background` here would be full-bleed and cover the
   starfield gutters the roster exists to preserve. */
.certs-section { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
.team-section .team-avatar { width: 128px; height: 128px; overflow: hidden; }
.team-section .team-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Initials fallback for a team member with no headshot (the photo is optional —
   a new hire may not have one yet). It FILLS the 128px slot so it lines up with
   the photos of their peers; borrowing .testimonial-avatar here rendered a 40px
   chip floating in the corner of a 128px box, because .team-avatar sets a text
   colour but no ground of its own. Fully token-driven, so it re-maps with the
   theme and needs no [data-theme] override; --primary-text is the INK step per
   rule 10 (the --primary fill would be the wrong side of the ink/fill split). */
.team-section .team-avatar-initials {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  /* 10%, not 16%: measured from the PAINTED pixels (not a computed composite),
     a 16% tint put --primary-text at 4.20:1 in light mode — below AA for the
     large text this renders. The lighter ground lifts it clear while dark, whose
     tint darkens rather than lightens, was already comfortable. */
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary) 35%, transparent);
  color: var(--primary-text);
  font-size: 2rem; font-weight: 700;
}
.certs-scroll-wrap { position: relative; max-width: 1000px; margin: 0 auto; }
.certs-scroll {
  display: flex; flex-wrap: nowrap; align-items: center; gap: 1.75rem;
  overflow-x: auto; padding: 1rem .5rem 1.25rem;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; -ms-overflow-style: none; /* hide bar; still scrolls via JS/arrows */
}
.certs-scroll::-webkit-scrollbar { display: none; }
.certs-scroll img, .certs-scroll > span { flex: 0 0 auto; width: 120px; height: 120px; object-fit: contain; }
.certs-scroll-wrap::before, .certs-scroll-wrap::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 48px; pointer-events: none; z-index: 1;
}
.certs-scroll-wrap::before { left: 0;  background: linear-gradient(to right, var(--bg-surface), transparent); }
.certs-scroll-wrap::after  { right: 0; background: linear-gradient(to left,  var(--bg-surface), transparent); }
.certs-scroll-controls { display: flex; justify-content: center; gap: 1rem; margin: .5rem 0 0; }
.certs-arrow {
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--border-color); background: var(--bg-surface);
  color: var(--primary-text); font-size: 1.1rem; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background var(--transition), border-color var(--transition);
}
.certs-arrow:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.certs-arrow:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.certs-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-color); border-radius: 50%; background: var(--bg-alt);
  color: var(--primary-text); font-weight: 700; font-size: .85rem; text-align: center; padding: .5rem;
}
[data-theme="dark"] .certs-scroll img {
  background: var(--color-white); border-radius: 14px; padding: 10px; /* light chip for light-background PNG logos */
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
}

/* ---- Styleguide (/dashboard/_styleguide.php, staff-only) ---------------- */
.sg-swatches { display: flex; flex-wrap: wrap; gap: .5rem; }
.sg-skel-avatar { width: 40px; height: 40px; }
.sg-skel-60 { width: 60%; }
.sg-skel-40 { width: 40%; }
.sg-swatch {
  padding: 1rem .875rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  min-width: 170px;
  font-size: .8125rem;
}
.sg-swatch[data-token="surface-1"]         { background: var(--surface-1); }
.sg-swatch[data-token="surface-2"]         { background: var(--surface-2); }
.sg-swatch[data-token="surface-3"]         { background: var(--surface-3); }
.sg-swatch[data-token="color-success-bg"]  { background: var(--color-success-bg); color: var(--color-success-text); }
.sg-swatch[data-token="color-warning-bg"]  { background: var(--color-warning-bg); color: var(--color-warning-text); }
.sg-swatch[data-token="color-danger-bg"]   { background: var(--color-danger-bg);  color: var(--color-danger-text); }
.sg-swatch[data-token="color-info-bg"]     { background: var(--color-info-bg);    color: var(--color-info-text); }
.sg-hero-demo { border-radius: var(--radius-lg); margin-top: 1.5rem; }
/* 2026: alerts carry a 3px semantic left stripe (docs/style-guide-2026.html
   §Alerts) — the event's state reads at a glance even before the text. */
.alert-error   { background: var(--color-danger-bg);  border-color: var(--color-danger-border);  border-left: 3px solid var(--color-danger);  color: var(--color-danger-text, var(--color-danger)); }
.alert-success { background: var(--color-success-bg); border-color: var(--color-success-border); border-left: 3px solid var(--color-success); color: var(--color-success-text, var(--color-success)); }
.alert-warning { background: var(--color-warning-bg); border-color: var(--color-warning-border); border-left: 3px solid var(--color-warning); color: var(--color-warning-text, var(--color-warning)); }
.alert-info    { background: var(--color-info-bg);    border-color: var(--color-info-border);    border-left: 3px solid var(--color-info);    color: var(--color-info-text, var(--color-info)); }
.alert-list { list-style: disc; padding-left: 1rem; }

/* ---- Forms ------------------------------------------------- */
/* Grouped controls. The wildcard reset zeroes fieldset margin/padding but NOT the
   UA `border: 2px groove`, whose colour the browser derives from color-scheme
   rather than from our tokens — measured live, it lands nowhere near
   var(--border-color) in either theme. That is a theme-deaf paint `make
   theme-parity` structurally CANNOT catch, because there is no declaration to
   scan; it is a UA default. min-inline-size: min-content is the other UA default,
   and it stops a fieldset shrinking inside the flex/grid its siblings live in.
   Both rules remove paint rather than adding it, so dark mode needs no companion.
   <legend class="form-label"> then measures identically to a normal .form-label.
   (No hex literals in this comment: the rule-01 ratchet scans raw source.) */
fieldset { border: 0; min-inline-size: 0; }
legend   { padding: 0; }

.form-group { margin-bottom: 1.25rem; }
.form-label { display: block; font-size: .875rem; font-weight: 500; color: var(--text-secondary); margin-bottom: .375rem; }
.form-label-row { display: flex; justify-content: space-between; align-items: baseline; }
.form-label-link { font-size: .8125rem; color: var(--primary-text); }
.form-input,
.form-select {
  display: block;
  width: 100%;
  padding: .5625rem .75rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  font-size: .9375rem;
  font-family: inherit;
  color: var(--text-primary);
  background: var(--bg-surface);
  line-height: 1.5;
  transition: border-color var(--transition), box-shadow var(--transition);
  appearance: none;
  box-sizing: border-box;
}
/* Compact select for dense toolbars (mirrors .btn-sm sizing). */
.form-select-sm { padding: .3125rem .5rem; font-size: .8125rem; }
.form-input:focus,
.form-select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--focus-ring, 0 0 0 3px color-mix(in srgb, var(--primary) 10%, transparent));
}
.form-input::placeholder { color: var(--text-subtle, var(--text-disabled)); }
/* selects keep a chevron despite appearance:none */
select.form-select, select.form-input {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='M6 9l6 6 6-6' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .75rem center;
  padding-right: 2.25rem;
}
.form-input-code {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  text-align: center;
  letter-spacing: .25em;
  font-weight: 600;
}
.form-hint { font-size: .8125rem; color: var(--text-muted); margin-top: .375rem; display: block; }

.input-wrap { position: relative; }
.input-wrap .form-input { padding-right: 2.75rem; }
.input-toggle-vis {
  position: absolute;
  right: .75rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  padding: .25rem;
  border-radius: 4px;
}
.input-toggle-vis:hover { color: var(--gray-600); }

.form-check { display: flex; align-items: flex-start; gap: .625rem; }
.form-checkbox {
  width: 16px; height: 16px;
  border: 1.5px solid var(--gray-300);
  border-radius: 4px;
  flex-shrink: 0;
  margin-top: 2px;
  appearance: none;
  cursor: pointer;
  position: relative;
  transition: border-color var(--transition), background var(--transition);
}
.form-checkbox:checked { background: var(--primary); border-color: var(--primary); }
.form-checkbox:checked::after {
  content: '';
  position: absolute;
  top: 1px; left: 4px;
  width: 5px; height: 9px;
  border: 2px solid var(--color-white);
  border-top: none;
  border-left: none;
  transform: rotate(45deg);
}
.form-check-label { font-size: .875rem; color: var(--gray-600); line-height: 1.5; }
.form-check-label a { color: var(--primary-text); }

.auth-divider {
  display: flex;
  align-items: center;
  gap: .875rem;
  margin: 1.25rem 0;
  color: var(--text-muted);
  font-size: .8125rem;
}
.auth-divider::before, .auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--gray-200);
}

/* ---- Auth pages -------------------------------------------- */
.auth-body { background: var(--gray-50); min-height: 100vh; }
.auth-layout {
  display: grid;
  grid-template-columns: 480px 1fr;
  min-height: 100vh;
}
.auth-layout-centered { grid-template-columns: 1fr; align-items: center; }
.auth-brand-panel {
  background: linear-gradient(160deg, var(--primary-dark) 0%, var(--primary) 50%, var(--accent) 100%);
  padding: 2.5rem;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  position: sticky;
  top: 0;
  height: 100vh;
}
.auth-brand-logo {
  display: flex;
  align-items: center;
  gap: .625rem;
  text-decoration: none;
  color: var(--color-white);
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.auth-brand-body { flex: 1; display: flex; align-items: center; }
.auth-brand-body blockquote {
  border-left: 3px solid rgba(255,255,255,.3);
  padding-left: 1.25rem;
}
.auth-brand-body blockquote p {
  color: rgba(255,255,255,.9);
  font-size: 1.0625rem;
  font-style: italic;
  line-height: 1.7;
  margin-bottom: .75rem;
}
.auth-brand-body blockquote footer { color: rgba(255,255,255,.6); font-size: .875rem; }
.auth-brand-body h2 { color: var(--color-white); font-size: 1.375rem; margin-bottom: 1rem; }
.auth-brand-list li { color: rgba(255,255,255,.85); padding: .375rem 0; font-size: .9375rem; }
.auth-brand-badges { display: flex; flex-wrap: wrap; gap: .5rem; }
.auth-brand-badges span {
  padding: .3125rem .75rem;
  border: 1px solid rgba(255,255,255,.25);
  border-radius: 100px;
  font-size: .75rem;
  color: rgba(255,255,255,.7);
}
.auth-form-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 2rem;
  background: var(--color-white);
  overflow-y: auto;
}
.auth-panel-solo {
  max-width: 480px;
  width: 100%;
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: 2.5rem;
  margin: 2rem auto;
}
.auth-form-wrap { width: 100%; max-width: 420px; }
.auth-title { font-size: 1.875rem; color: var(--gray-900); margin-bottom: .375rem; }
.auth-subtitle { color: var(--gray-500); font-size: .9375rem; margin-bottom: 1.75rem; }
.auth-switch { text-align: center; font-size: .9rem; color: var(--gray-500); margin-top: 1.25rem; }
.auth-switch a { color: var(--primary-text); font-weight: 500; }

/* "or continue with" divider + OAuth provider buttons (login page) */
.auth-divider { position: relative; margin: 1.25rem 0; text-align: center; }
.auth-divider::before {
  content: "";
  position: absolute;
  inset: 50% 0 auto;
  height: 1px;
  background: var(--border-color);
}
.auth-divider span {
  position: relative;
  background: var(--bg-surface);
  padding: 0 .75rem;
  font-size: .8125rem;
  color: var(--text-muted);
}
.oauth-buttons { display: grid; grid-template-columns: 1fr; gap: .625rem; }
.oauth-buttons-2 { grid-template-columns: 1fr 1fr; }
.btn-oauth { justify-content: center; gap: .5rem; }

/* Code-style input (recovery codes, TOTP) */
.input-code { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: .1em; }
.cursor-pointer { cursor: pointer; }
.my-xl { margin-top: 1.5rem; margin-bottom: 1.5rem; }

/* Key/value detail grid (invite summary, detail blocks) */
.kv-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .5rem 1rem;
  font-size: .9rem;
  margin: 0;
}

/* Context banner — "you are managing org X" style callout (warning surface) */
.context-banner {
  background: var(--color-warning-bg);
  border: 1px solid var(--color-warning-border);
  border-radius: var(--radius);
  padding: .875rem 1.25rem;
  margin-bottom: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .75rem;
}
.context-banner-text {
  display: flex;
  align-items: center;
  gap: .625rem;
  font-size: .875rem;
  font-weight: 500;
  color: var(--color-warning-text);
}
.context-banner-text svg { flex-shrink: 0; }

/* Stacked request/list rows inside a card (border-top separated) */
.request-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
  padding: .625rem 0;
  border-top: 1px solid var(--border-color);
}

/* Highlighted table row (e.g. the org you're currently managing) */
.data-table tr.row-active { background: var(--primary-light); }
.data-table tr.row-danger { background: var(--color-danger-bg); }

/* Card with a warning-colored border (attention sections) */
.card-warning-border { border-color: var(--color-warning-border); }

/* Severity palette — finding severities are semantic, theme-stable colors.
   Backgrounds carry white text; the -text variants are for counts/labels. */
.sev-critical      { background: var(--color-danger); }
.sev-high          { background: var(--color-orange); }
.sev-medium        { background: var(--color-warning); }
.sev-low           { background: var(--color-success); }
.sev-informational { background: var(--gray-500); }
/* Severity-as-text — must use tokens (which have dark remaps) so the color
   stays legible on dark surfaces. */
.sev-text-critical      { color: var(--color-danger); }
.sev-text-high          { color: var(--color-warning-text); }
.sev-text-medium        { color: var(--color-warning-text); }
.sev-text-low           { color: var(--color-success-text); }
.sev-text-informational { color: var(--text-muted); }

/* Meter (thin progress bar) + status fills */
.meter { height: 12px; background: var(--border-color); border-radius: var(--radius-full); overflow: hidden; }
.meter-fill { height: 100%; border-radius: var(--radius-full); }
.fill-success { background: var(--color-success); }
.fill-warning { background: var(--color-warning); }
.fill-danger  { background: var(--color-danger); }

/* Grade palette (certificate legend + grade text) — fixed semantic scale,
   mirrors ComplianceScore::GRADE_THRESHOLDS and the PDF certificate */
.grade-dot-a { background: var(--color-success); }
.grade-dot-b { background: var(--primary); }
.grade-dot-c { background: var(--color-warning); }
.grade-dot-d { background: var(--color-orange); }
.grade-dot-f { background: var(--color-danger); }
/* .grade-text-{a..f} moved to the token-driven block below (~1855, re-themes in
   dark mode + adds -none); these hardcoded-hex duplicates were removed 2026-07-23. */

/* Certificate page extras (below-the-fold controls) */
.cert-extra { width: 100%; max-width: 800px; }
.cert-footnote { max-width: 600px; }
.fs-3xl { font-size: 3rem; }
.upgrade-gate-card { max-width: 480px; margin: 4rem auto; padding: 3rem 1rem; }

/* Compliance badge embed preview */
.badge-preview {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 1rem;
  background: var(--gray-50);
  border-radius: var(--radius);
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.badge-img { border-radius: 6px; box-shadow: var(--shadow-md); }

/* Public status page */
.status-h1 { font-size: 1.5rem; font-weight: 700; color: var(--primary-text); margin: 0 0 1.5rem; }
.status-hero-banner {
  display: flex;
  align-items: center;
  gap: .875rem;
  padding: 1.25rem 1.5rem;
  color: var(--color-white);
  border-radius: 10px;
  font-family: var(--font-sans);
  margin-bottom: 2rem;
}
/* The banner paints WHITE ink, so its ground must be the -strong (white-text)
   step, not the base fill: dark's bright base gave only 2.31:1. */
.status-banner-ok     { background: var(--color-success-strong); }
.status-banner-issues { background: var(--color-warning-strong); }
/* Was opacity:.85, which dimmed the label INTO the saturated banner
   (3.62:1). A solid ink keeps it legible; the banner supplies contrast. */
.status-banner-meta   { color: var(--color-white); }
.status-dot { display: inline-block; width: .5rem; height: .5rem; border-radius: 50%; background: currentColor; }
/* These are TEXT, so they wear the -text inks (rule 10) — the base fills are
   only 4.23-4.4:1 on white. The dot inherits currentColor, so it follows. */
.status-ok          { color: var(--color-success-text); }
.status-degraded    { color: var(--color-warning-text); }
.status-partial     { color: var(--color-orange-text); }
.status-major       { color: var(--color-danger-text); }
.status-maintenance { color: var(--color-purple-text); }

/* Priority inks (remediation template picker). Text, so ink steps — these replaced
   a hardcoded hex map that could not re-theme and failed AA in both modes. */
.prio-ink-critical { color: var(--color-danger-text); }
.prio-ink-high     { color: var(--color-orange-text); }
.prio-ink-medium   { color: var(--color-info-text); }
.prio-ink-low      { color: var(--color-success-text); }
.lh-loose { line-height: 1.7; }

/* Group-2 long-tail components */
.grid-autofill-180 { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: .75rem; }
.grid-autofill-300 { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.25rem; }
.pill-outline { background: var(--bg-surface); color: var(--text-subtle); border-color: var(--border-color); }
.legend-sq { display: inline-block; width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
.fill-neutral { background: var(--text-subtle); }
.opacity-30 { opacity: .3; }
.mb-1px { margin-bottom: 1px; }
.break-word { word-break: break-word; }
.minw-240 { min-width: 240px; }
.maxw-720 { max-width: 720px; }
.maxw-128 { max-width: 128px; }
/* Image fitting for admin media thumbnails: -cover crops a headshot to a square
   without distorting a face, -contain letterboxes a logo/badge so its own
   aspect ratio survives. Both need an explicit width+height on the element. */
.img-cover   { object-fit: cover; }
.img-contain { object-fit: contain; }
.score-box { padding: .6rem .75rem; border: 1px solid var(--border-color); border-radius: 8px; font-size: 1.25rem; font-weight: 700; text-align: center; }
.matrix-grid { display: grid; grid-template-columns: auto repeat(5, 1fr); gap: 2px; font-size: .7rem; }
.matrix-axis { padding: .3rem; font-weight: 600; color: var(--text-muted); }
/* 5x5 risk-matrix tint scale (semantic, theme-stable). Backgrounds are fixed
   light pastels in BOTH themes, so pin a dark text color too — otherwise the
   cell's number inherits the page's light text in dark mode and disappears. */
.matrix-cell-1,
.matrix-cell-2,
.matrix-cell-3,
.matrix-cell-4 { color: var(--gray-800); }
.matrix-cell-1 { background: var(--matrix-1); }
.matrix-cell-2 { background: var(--matrix-2); }
.matrix-cell-3 { background: var(--matrix-3); }
.matrix-cell-4 { background: var(--matrix-4); }
/* Interactive 5×5 matrix cell (risk-register view). Carries the layout the old
   inline style provided; the .matrix-cell-N class supplies the pastel fill +
   pinned dark text (readable in both themes). */
.matrix-grid-cell { padding: .4rem; text-align: center; border-radius: 3px; min-width: 32px; }
.matrix-grid-cell.is-active { outline: 2px solid var(--primary-dark); outline-offset: 1px; font-weight: 700; }
.spark-bars-sm { height: 60px; gap: .5rem; padding-bottom: .375rem; }
.corner-tr { position: absolute; top: .75rem; right: .75rem; }

/* Group-1 long-tail components */
.invite-grid { display: grid; grid-template-columns: 1fr 180px auto; gap: .75rem; align-items: end; }
@media (max-width: 720px) { .invite-grid { grid-template-columns: 1fr; } }
.h-44 { height: 44px; }
.h-auto { height: auto; }
.w-auto { width: auto; }
.check-ring {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: .1rem;
  display: inline-block;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
}
.check-ring-btn { background: none; cursor: pointer; padding: 0; }
.mono-block {
  font-size: .875rem;
  line-height: 1.6;
  white-space: pre-wrap;
  font-family: var(--font-mono);
  background: var(--gray-50);
  border: 1px solid var(--border-color);
  border-radius: 6px;
  padding: .75rem 1rem;
}
.meter-mini { height: 6px; border-radius: 4px; }
.fill-primary { background: var(--primary); }
.strike { text-decoration: line-through; }
/* Question-assignment picker row (settings/users/questions). */
.assignment-label {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  padding: .5rem .625rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background .1s;
}
.assignment-label.is-assigned { background: var(--primary-light); }
/* Active-row / active-card highlight utilities (org switchers, selected rows). */
.row-active { background: var(--primary-light); }
.ring-active { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-light); }
/* Org-switch row (workspace): the static layout, with .row-active toggled on. */
.org-switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .625rem .75rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
}
.logo-preview { max-width: 200px; max-height: 60px; object-fit: contain; }
/* Uploaded org logo. Height-bounded with object-fit so a wide or tall upload
   keeps its aspect ratio and cannot distort a header band — the stored file is
   already downscaled to fit 600x200 (Models\OrgLogo), this is the display cap. */
.org-logo-preview { max-width: 240px; max-height: 64px; object-fit: contain; }
.cert-org-logo    { max-width: 220px; max-height: 56px; object-fit: contain; margin: 0 auto .75rem; display: block; }
.report-org-logo  { max-width: 200px; max-height: 48px; object-fit: contain; margin-bottom: .5rem; }
/* Employer logo on the public policy-acknowledgment page. A staff member opens
   this from an email, outside the app — the mark is what makes it read as coming
   from their employer rather than as a phishing link. */
.ack-org-logo     { max-width: 200px; max-height: 52px; object-fit: contain; margin-bottom: .75rem; display: block; }
.logo-inline { max-height: 28px; max-width: 140px; object-fit: contain; vertical-align: middle; }
.maxw-120 { max-width: 120px; }
.swatch-input { width: 44px; height: 44px; padding: 2px; border: 1px solid var(--border-color); border-radius: 6px; cursor: pointer; flex-shrink: 0; }

/* Compound-batch additions */
.px-xs { padding-left: .375rem; padding-right: .375rem; }
.edge-neutral { border-left: 3px solid var(--text-subtle); }
.col-w-40 { width: 40px; }
.col-w-70 { width: 70px; }
.col-w-130 { width: 130px; }
.maxw-180 { max-width: 180px; }
.minw-90 { min-width: 90px; }
.minw-100 { min-width: 100px; }
.minw-120 { min-width: 120px; }
.flex-3 { flex: 3; }
.d-contents { display: contents; }
.ob-stat-pad { padding: .875rem 1.25rem; }
.grid-main-aside { display: grid; grid-template-columns: 1fr 320px; gap: 1.5rem; align-items: start; }
@media (max-width: 900px) { .grid-main-aside { grid-template-columns: 1fr; } }
.acc-summary {
  cursor: pointer;
  font-size: 1rem;
  font-weight: 600;
  padding: .25rem 0;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.link-card {
  display: block;
  border: 1px solid var(--border-color);
  border-radius: 10px;
  padding: 1.25rem 1.5rem;
  text-decoration: none;
  color: inherit;
  transition: border-color .15s;
}
.link-card:hover { border-color: var(--border-strong); }

/* Missing-class fixes (referenced-but-undefined audit) */
.mt-0 { margin-top: 0; }
.text-inverse { color: var(--text-inverse); }
.row-rule-light { border-bottom: 1px solid var(--gray-100); }
.context-banner-label { font-size: .875rem; font-weight: 500; color: var(--color-warning-text); }
/* Screen-reader-only content (standard clip pattern) — was referenced but
   never defined, so "sr-only" text rendered visibly. */
.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;
}

/* Round-3 utilities */
.w-8 { width: 8px; }   .h-8 { height: 8px; }
.w-10 { width: 10px; } .h-10 { height: 10px; }
.w-12 { width: 12px; } .h-12 { height: 12px; }
.w-14 { width: 14px; } .h-14 { height: 14px; }
.w-16 { width: 16px; } .h-16 { height: 16px; }
.w-18 { width: 18px; } .h-18 { height: 18px; }
.w-20 { width: 20px; } .h-20 { height: 20px; }
.w-24 { width: 24px; } .h-24 { height: 24px; }
.fs-xxl { font-size: 1.5rem; }
.fs-num { font-size: 1.75rem; }
.fs-num-lg { font-size: 2rem; }
.mt-px { margin-top: .125rem; }
.mr-xs { margin-right: .25rem; }
.mr-sm { margin-right: .5rem; }
.opacity-85 { opacity: .85; }
.pl-lg { padding-left: 1.25rem; }
.pad-banner { padding: .75rem 1rem; }
.pad-btn { padding: .625rem 1rem; }
.pad-section { padding: 1.25rem 1.5rem; }
.lh-1 { line-height: 1; }
.lh-tight { line-height: 1.4; }
.lh-normal { line-height: 1.5; }
.text-strong { color: var(--gray-800); }
.bg-danger-soft  { background: var(--color-danger-bg); }
.bg-warning-soft { background: var(--color-warning-bg); }
.bg-success-soft { background: var(--color-success-bg); }
.bg-info-soft    { background: var(--color-info-bg); }
/* Rule 10: a semantic TINT background switches its text utility to that family's
   -text ink. The base tokens (.text-success etc.) are FILL colours — correct on a
   page surface, but 3.9–4.3:1 on their own tint, which fails AA for the small
   badge text these pairings are used for. Scoping it here means markup can keep
   writing `bg-success-soft text-success` and still land on the right ink, instead
   of every call site having to remember. Specificity 0,2,0 beats the 0,1,0
   utilities without !important. */
.bg-success-soft.text-success, .bg-success-soft .text-success { color: var(--color-success-text); }
.bg-warning-soft.text-warning, .bg-warning-soft .text-warning { color: var(--color-warning-text); }
.bg-danger-soft.text-danger,   .bg-danger-soft .text-danger   { color: var(--color-danger-text); }
.bg-info-soft.text-info,       .bg-info-soft .text-info       { color: var(--color-info-text); }
.bg-primary-soft { background: var(--primary-light); }

/* Round-4 debt-paydown utilities (auto-convert-styles.php round 4) */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.grid-kv { grid-template-columns: 140px 1fr; }
.grid-auto-200 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.gap-kv { gap: .5rem .75rem; }
.gap-0 { gap: 0; }
.pad-tag { padding: .2rem .5rem; }
.pad-cell-y { padding: .875rem 1rem; }
.p-card { padding: 1.25rem; }
.p-3xl { padding: 3rem; }
.pt-xl { padding-top: 1.5rem; }
.mt-auto { margin-top: auto; }
.mb-2xs { margin-bottom: .375rem; }
.bt-rule { border-top: 1px solid var(--border-color); }
.border-0 { border: none; }
.bordered-danger  { border: 1px solid var(--color-danger-border); }
.bordered-warning { border: 1px solid var(--color-warning-border); }
.bordered-info    { border: 1px solid var(--color-info-border); }
.bordered-success { border: 1px solid var(--color-success-border); }
.bg-alt { background: var(--bg-alt); }
.bg-border { background: var(--border-color); }
.text-inverse-soft { color: rgba(255, 255, 255, .8); }
.font-sans { font-family: var(--font-sans); }
.align-middle { vertical-align: middle; }
.pos-sticky { position: sticky; }
.scroll-y { overflow-y: auto; }
.opacity-40 { opacity: .4; }
.opacity-50 { opacity: .5; }
.opacity-60 { opacity: .6; }
.opacity-70 { opacity: .7; }
.w-64 { width: 64px; } .h-64 { height: 64px; }
.h-6 { height: 6px; }
.maxw-240 { max-width: 240px; }
.maxw-420 { max-width: 420px; }
.maxw-560 { max-width: 560px; }
.maxw-640 { max-width: 640px; }
.maxw-840 { max-width: 840px; }
.minw-120 { min-width: 120px; }

/* Round-5 debt-paydown utilities */
.py-2xs { padding-top: .25rem; padding-bottom: .25rem; }
.py-xs { padding-top: .375rem; padding-bottom: .375rem; }
.px-sm { padding-left: .5rem; padding-right: .5rem; }
.pl-xl { padding-left: 1.5rem; }
.pt-lg { padding-top: 1rem; }
.pad-kbd { padding: .1rem .4rem; }
.pad-btn-wide { padding: .5rem 1rem; }
.my-lg { margin-top: 1.25rem; margin-bottom: 1.25rem; }
.my-4xl { margin-top: 4rem; margin-bottom: 4rem; }
.ml-md { margin-left: .75rem; }
.ml-lg { margin-left: 1rem; }
.mb-neg-2 { margin-bottom: -2px; }
.list-indent { margin: 0 0 .75rem 1.1rem; }
.content-pad { padding: 3rem 1.5rem 5rem; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.grid-auto-180 { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.grid-side-360 { grid-template-columns: 1fr 360px; }
.gap-rc { gap: .5rem 1rem; }
.align-icon { vertical-align: -2px; }
.pre-line { white-space: pre-line; }
.edge-success { border-left: 3px solid var(--success); }
.bordered-primary { border: 1px solid var(--primary); }
.bg-danger-solid { background: var(--danger); }
.tab-rule-ghost { border-bottom: 2px solid transparent; }
.rounded-0 { border-radius: 0; }
.pos-fixed { position: fixed; }
.pos-abs { position: absolute; }
.inset-0 { inset: 0; }
.top-0 { top: 0; }
.right-0 { right: 0; }
.z-10 { z-index: 10; }
.z-100 { z-index: 100; }
.z-1000 { z-index: 1000; }
.shadow-lg-u { box-shadow: var(--shadow-lg); }
.anim-spin { animation: spin 1s linear infinite; }
.v-label { writing-mode: vertical-rl; transform: rotate(180deg); }
.w-320 { width: 320px; }
.w-40p { width: 40%; }
.h-4 { height: 4px; }
.h-44 { height: 44px; }
.col-w-120 { width: 120px; }
.maxw-120 { max-width: 120px; }
.maxw-140 { max-width: 140px; }
.minw-100 { min-width: 100px; }
.minw-140 { min-width: 140px; }
.minw-160 { min-width: 160px; }
.minw-180 { min-width: 180px; }
.minw-240 { min-width: 240px; }

/* Risk heatmap matrix (risk-register/index.php, risk/heatmap.php) */
.heatmap-axis-y { display: flex; align-items: center; justify-content: center; writing-mode: vertical-rl; transform: rotate(180deg); font-size: .6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); height: 125px; flex-shrink: 0; }
.heatmap-grid { display: grid; grid-template-columns: auto repeat(5, 1fr); gap: 3px; }
.heatmap-row-label { display: flex; align-items: center; justify-content: flex-end; padding-right: .375rem; font-size: .6875rem; color: var(--text-muted); white-space: nowrap; }
.heatmap-cell-sm { width: 25px; height: 25px; border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: .6875rem; font-weight: 400; color: var(--text-muted); }
.heatmap-cell-lg { width: 64px; height: 48px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 1.125rem; font-weight: 700; cursor: default; transition: opacity .15s; }
.heatmap-y-lg { writing-mode: vertical-rl; text-orientation: mixed; transform: rotate(180deg); font-size: .75rem; font-weight: 600; color: var(--text-muted); padding-right: 4px; text-transform: uppercase; letter-spacing: .06em; margin-bottom: 28px; }
.heatmap-row-lg { display: flex; gap: 6px; margin-bottom: 6px; align-items: center; }
.heatmap-row-label-lg { font-size: .6875rem; color: var(--text-muted); width: 68px; text-align: right; padding-right: 6px; }
.heatmap-x-labels { display: flex; gap: 6px; margin-top: 4px; padding-left: 74px; }
.heatmap-x-title { text-align: center; font-size: .75rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .06em; margin-top: 4px; padding-left: 74px; }
.score-pill-solid { display: inline-block; min-width: 2rem; padding: .2rem .5rem; border-radius: 99px; color: var(--color-white); font-weight: 700; font-size: .8125rem; }
.pt-sm { padding-top: .5rem; }
.heatmap-cell-filled { font-weight: 700; color: var(--text-primary); }
.heatmap-axis-x { text-align: center; font-size: .6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin-top: .375rem; padding-left: 28px; }
.legend-title { font-weight: 600; margin-bottom: .125rem; }
.legend-swatch { width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0; }
.legend-swatch-critical { background: color-mix(in srgb, var(--color-danger) 75%, transparent); }
.legend-swatch-high { background: color-mix(in srgb, var(--color-orange) 75%, transparent); }
.legend-swatch-medium { background: color-mix(in srgb, var(--color-warning) 75%, transparent); }
.legend-swatch-low { background: color-mix(in srgb, var(--color-success) 75%, transparent); }
/* .status-pill retired 2026-07-24 — the last consumer (change-log) migrated to
   the canonical .pill family (guide rule 05: one status chip). */
.count-pill-danger { display: inline-block; padding: .15rem .45rem; border-radius: 100px; font-size: .7rem; font-weight: 700; background: var(--color-danger-bg); color: var(--color-danger-text); border: 1px solid var(--color-danger-border); }

/* Stat-pill accent edges (rem-stat-pill rows) */
.edge-neutral { border-left: 3px solid var(--gray-500); }
.edge-primary { border-left: 3px solid var(--primary); }
.edge-orange  { border-left: 3px solid var(--color-orange); }
.text-slate { color: var(--gray-500); }

/* risk-pill color variants (replaces per-instance --pill-bg/--pill-color) */
.risk-pill-neutral { --pill-bg: var(--gray-100); --pill-color: var(--text-secondary); }
.risk-pill-gray    { --pill-bg: var(--gray-100); --pill-color: var(--gray-600); }
.risk-pill-success { --pill-bg: var(--color-success-bg); --pill-color: var(--color-success-text); }
.risk-pill-info    { --pill-bg: var(--color-info-bg); --pill-color: var(--color-info-text); }
.risk-pill-warning { --pill-bg: var(--color-warning-bg); --pill-color: var(--color-warning-text); }
.risk-pill-danger  { --pill-bg: var(--color-danger-bg); --pill-color: var(--color-danger-text); }

/* Brand-preview widget internals (white-on-brand by design) */
.bp-header { padding: .875rem 1rem; border-bottom: 1px solid rgba(255, 255, 255, .15); }
.bp-title { font-weight: 700; font-size: .9375rem; color: var(--color-white); letter-spacing: -.01em; }
.bp-item { padding: .5rem .75rem; border-radius: 6px; background: rgba(255, 255, 255, .15); color: var(--color-white); font-size: .8125rem; font-weight: 500; margin-bottom: .25rem; }
.bp-rule { border-top: 1px solid rgba(255, 255, 255, .15); margin: .5rem 0; }

/* Misc structural one-offs promoted to classes */
.self-center { align-self: center; }
.items-stretch { align-items: stretch; }
.hr-rule { border: none; border-top: 1px solid var(--border-color); margin: 1rem 0; }
.dot-current { background: currentColor; }
.bt-rule-strong { border-top: 2px solid var(--border-color); }
.bc-danger { border-color: var(--color-danger-border); }
.maxw-360 { max-width: 360px; }
.minw-44 { min-width: 44px; }
.grid-auto-260 { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.section-title-rule { padding-bottom: .625rem; border-bottom: 1px solid var(--border-color); }
.overlay-backdrop { display: none; position: fixed; inset: 0; z-index: 1000; background: rgba(0, 0, 0, .45); align-items: center; justify-content: center; }
.overlay-dialog { background: var(--bg-surface); border-radius: var(--radius-lg); padding: 1.75rem; max-width: 420px; width: 90%; box-shadow: var(--shadow-xl); }
.icon-chip { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 8px; background: var(--primary-light); color: var(--primary-text); }
.recommendation-box { background: var(--bg-surface-2, var(--gray-50)); border: 1px solid var(--border-color); border-radius: 6px; padding: .75rem 1rem; margin-bottom: 1rem; }
.danger-strip { display: flex; align-items: center; gap: .5rem; padding: .6rem .9rem; border-radius: 8px; background: var(--color-danger-bg); border: 1px solid var(--color-danger-border); color: var(--color-danger-text); font-size: .875rem; font-weight: 600; }
.event-row { display: flex; align-items: center; gap: 1rem; padding: .75rem 1rem; background: var(--bg-surface); border: 1px solid var(--border-color); border-radius: 8px; }
.event-badge { font-size: .75rem; font-weight: 600; padding: .2rem .5rem; border-radius: 4px; white-space: nowrap; }
.event-badge-default { background: color-mix(in srgb, var(--color-purple) 10%, var(--color-white)); color: var(--color-purple); }
[data-theme="dark"] .event-badge-default { background: color-mix(in srgb, var(--color-purple) 20%, transparent); color: var(--color-purple-text); }

/* Calendar event-type accents — fixed per-type palette. The row gets a colored
   left border; its badge gets a matching translucent tint. Translucent so the
   distinction survives on both light and dark surfaces (a [data-theme] rule can
   still adjust if needed), which the former inline styles could not do. */
.event-edge { border-left: 4px solid var(--border-color); }
/* <summary class="card-header"> (compliance-activity cards): flex display
   removes the native disclosure marker, so draw an explicit chevron. */
details > summary.card-header { list-style: none; }
details > summary.card-header::-webkit-details-marker { display: none; }
details > summary.card-header::after { content: '\25B8'; margin-left: auto; padding-left: .5rem; color: var(--text-muted); flex: none; transition: transform .15s ease; }
details[open] > summary.card-header::after { transform: rotate(90deg); }

.event-type-activity          { border-left-color: var(--color-info); }
/* Rule 10: coloured INK takes -text. The base measured 3.80:1 on this tint,
   below AA — and its DARK sibling was corrected in this same batch, which left
   the light half the odd one out. */
.event-type-activity          .event-badge { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--color-info-text); }
.event-type-assessment        { border-left-color: var(--primary-dark); }
.event-type-assessment        .event-badge { background: color-mix(in srgb, var(--primary) 16%, transparent); color: var(--primary-text); }
.event-type-remediation       { border-left-color: var(--color-danger); }
.event-type-remediation       .event-badge { background: color-mix(in srgb, var(--color-danger) 16%, transparent); color: var(--color-danger); }
.event-type-ba_review,
.event-type-vendor_review     { border-left-color: var(--color-purple); }
.event-type-ba_review         .event-badge,
.event-type-vendor_review     .event-badge { background: color-mix(in srgb, var(--color-purple) 16%, transparent); color: var(--color-purple); }
.event-type-policy_review     { border-left-color: var(--color-success); }
.event-type-policy_review     .event-badge { background: color-mix(in srgb, var(--color-success) 16%, transparent); color: var(--color-success-text); }
.event-type-device_review     { border-left-color: var(--color-info); }
.event-type-device_review     .event-badge { background: color-mix(in srgb, var(--color-info) 16%, transparent); color: var(--color-info); }
.event-type-training_renewal  { border-left-color: var(--color-warning); }
.event-type-training_renewal  .event-badge { background: color-mix(in srgb, var(--color-warning) 18%, transparent); color: var(--color-warning-text); }
[data-theme="dark"] .event-type-training_renewal .event-badge { color: var(--color-warning-text); }
/* Dark parity for the rest of the badge palette: the .16 tints go near-black
   on the dark surface, so the saturated light-mode text colors drop below AA —
   lift each to its 300/400-weight hue (same fix training_renewal had). */
[data-theme="dark"] .event-type-activity        .event-badge { color: var(--accent-text); }
[data-theme="dark"] .event-type-ba_review       .event-badge,
[data-theme="dark"] .event-type-vendor_review   .event-badge { color: var(--color-purple); }
[data-theme="dark"] .event-type-device_review   .event-badge { color: var(--accent-text); }
.event-edge-default { border-left: 4px solid var(--color-purple); }

/* Round-6 utilities — final static-attr paydown */
.top-lg { top: 1rem; }
.pad-empty { padding: 3rem 1.5rem; }
.col-span-all { grid-column: 1 / -1; }
.flex-fixed-140 { flex: 0 0 140px; }
.ml-xl { margin-left: 1.5rem; }
.mt-3xl { margin-top: 3rem; }
.m-md { margin: .75rem; }
.m-lg { margin: 1rem; }
.my-px { margin: .125rem 0; }
.pb-sm { padding-bottom: .5rem; }
.pb-md { padding-bottom: .75rem; }
.pb-lg { padding-bottom: 1rem; }
.pb-xl { padding-bottom: 1.5rem; }
.pl-2xl { padding-left: 2.25rem; }
.grid-kv-160 { grid-template-columns: 160px 1fr; }
.grid-form-qty { grid-template-columns: 1fr 80px 80px; }
.grid-main-aux { grid-template-columns: 1fr auto; }
.grid-auto-240 { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.grid-5 { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 1rem; }

/* Fixed-column grids must collapse on narrow screens (they had no breakpoint,
   so they stayed multi-column and overflowed phones). Mirrors how .form-grid
   collapses at 640px. */
@media (max-width: 900px) {
  .grid-side-360 { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
  .grid-5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .grid-2,
  .grid-3 { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
  .grid-5 { grid-template-columns: 1fr; }
}
/* The 6-column risk heatmap/matrix can't collapse meaningfully, so let it
   scroll horizontally within its card instead of overflowing the page. */
@media (max-width: 640px) {
  .heatmap-grid,
  .matrix-grid { min-width: 420px; }
  .heatmap-wrap,
  .matrix-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}
.kv-list { display: grid; grid-template-columns: max-content 1fr; row-gap: .875rem; column-gap: 1.5rem; }
.vis-hidden { visibility: hidden; }
.h-38 { height: 38px; }
.w-70p { width: 70%; }
.col-w-84 { width: 84px; }
.col-w-104 { width: 104px; }
.col-w-168 { width: 168px; }
.col-w-180 { width: 180px; }
.minw-130 { min-width: 130px; }
.minw-260 { min-width: 260px; }
.maxw-400 { max-width: 400px; }
.maxw-520 { max-width: 520px; }
.maxw-780 { max-width: 780px; }
.maxh-120 { max-height: 120px; }
.maxh-220 { max-height: 220px; }
.maxh-280 { max-height: 280px; }
.maxh-340 { max-height: 340px; }
.maxh-360 { max-height: 360px; }
.edge-purple { border-left: 3px solid var(--color-purple); }
.edge-border { border-left: 3px solid var(--border-color); }
.edge-strong { border-left: 3px solid var(--border-strong, var(--gray-300)); }
.text-purple { color: var(--color-purple); }
.bg-success-solid { background: var(--color-success); }
.bg-warning-solid { background: var(--color-warning); }
.bg-danger-light { background: var(--danger-light); }
.bordered-dashed { border: 1px dashed var(--border-color); }
.table-collapse { border-collapse: collapse; }
.col-tight { width: 1%; white-space: nowrap; }
.select-all { user-select: all; }
.underline { text-decoration: underline; }
.text-inherit { color: inherit; }
.trans-shadow { transition: box-shadow .15s; }
.fs-display { font-size: 1.875rem; }
.callout-brand { background: var(--primary-light); border-left: 4px solid var(--primary); padding: 1rem 1.25rem; border-radius: 6px; }
.num-dot { min-width: 1.75rem; height: 1.75rem; border-radius: 50%; background: var(--primary); color: var(--color-white); display: flex; align-items: center; justify-content: center; font-size: .75rem; font-weight: 700; flex-shrink: 0; margin-top: .1rem; }
.success-orb { width: 64px; height: 64px; border-radius: 50%; background: var(--color-success-bg); display: flex; align-items: center; justify-content: center; }
.swatch-input { width: 44px; height: 44px; padding: 2px; border: 1px solid var(--border-color); border-radius: 6px; cursor: pointer; }
.brand-logo-img { height: 28px; width: auto; max-width: 120px; object-fit: contain; }
.chip-warning { display: inline-flex; align-items: center; gap: .3rem; background: var(--color-warning-bg); color: var(--color-warning-text); border: 1px solid var(--color-warning-border); border-radius: 100px; font-size: .75rem; font-weight: 600; padding: .2rem .6rem; text-decoration: none; }
.pick-card { display: flex; align-items: flex-start; gap: .75rem; padding: .625rem .875rem; border-radius: 8px; border: 1px solid var(--border-color); background: var(--bg-surface); text-align: left; cursor: pointer; transition: background .12s; }
.tool-card { display: flex; flex-direction: column; text-decoration: none; color: inherit; padding: 1.25rem; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-surface); transition: border-color .15s ease, box-shadow .15s ease; }
.link-card { display: block; border: 1px solid var(--border-color); border-radius: 10px; padding: 1.25rem 1.5rem; text-decoration: none; color: inherit; transition: border-color .15s, box-shadow .15s, transform .15s; }

/* Final-sweep structural utilities */
.bordered { border: 1px solid var(--border-color); }
.rounded { border-radius: var(--radius); }
.rounded-sm { border-radius: 4px; }
.rounded-md { border-radius: 6px; }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-full { border-radius: var(--radius-full); }
.uppercase { text-transform: uppercase; }
.tracking { letter-spacing: .05em; }
.overflow-hidden { overflow: hidden; }
.bg-surface { background: var(--bg-surface); }
.shadow-sm-u { box-shadow: var(--shadow-sm); }
.pad-chip { padding: .5rem .75rem; }
.pad-roomy { padding: 1rem 1.25rem; }
.mt-2xs { margin-top: .375rem; }

/* Dashboard home — compliance gauge, dimension meters, score sparkline */
.gauge-wrap { position: relative; width: 110px; height: 110px; flex-shrink: 0; }
.gauge-svg { transform: rotate(-90deg); }
.gauge-arc { transition: stroke-dasharray 800ms cubic-bezier(.4, 0, .2, 1); }
.gauge-center { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0; }
.gauge-grade { font-size: 1.75rem; font-weight: 800; line-height: 1; }
.meter-thin { height: 5px; }
.meter-fill-animated { transition: width 600ms ease; }
.pos-rel { position: relative; }
.minw-280 { min-width: 280px; }
.gap-2xl { gap: 2rem; }
.popover-panel {
  position: absolute;
  left: 0;
  top: 24px;
  z-index: 50;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: 10px;
  padding: 1rem;
  width: 300px;
  box-shadow: var(--shadow-lg);
  font-size: .8125rem;
  color: var(--text-secondary);
}
.popover-list { margin: .25rem 0 .5rem 1.1rem; font-size: .84rem; list-style: disc; }
.info-dot {
  list-style: none;
  cursor: pointer;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--bg-alt);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .6875rem;
  font-weight: 700;
  color: var(--text-muted);
  border: 1px solid var(--border-color);
}
.spark-bars {
  display: flex;
  align-items: flex-end;
  gap: .75rem;
  height: 80px;
  padding-bottom: .5rem;
  border-bottom: 2px solid var(--border-color);
  margin-top: 1rem;
}
.spark-bar { width: 100%; border-radius: 4px 4px 0 0; }
/* A–F grade palette (matches the certificate legend dots) */
/* Rule 10: grade TEXT takes the ink; the BARS keep the fill (a bar is a fill).
   The base tokens measured 4.11-4.49:1 as text on the page ground. */
.grade-text-a { color: var(--color-success-text); }  .grade-bar-a { background: var(--color-success); }
.grade-text-b { color: var(--color-info-text); }     .grade-bar-b { background: var(--primary); }
.grade-text-c { color: var(--color-warning-text); }  .grade-bar-c { background: var(--color-warning); }
.grade-text-d { color: var(--color-warning-text); }  .grade-bar-d { background: var(--color-orange); }
.grade-text-f { color: var(--color-danger-text); }   .grade-bar-f { background: var(--color-danger); }
.grade-text-none { color: var(--text-muted); }   .grade-bar-none { background: var(--gray-500); }
/* Soft grade chips (bg + text) — token-driven so they re-theme in dark mode.
   Replaces the per-page gradeStyle() inline composition. */
.grade-chip { display: inline-block; font-size: .75rem; font-weight: 700; padding: .2rem .5rem; border-radius: 4px; }
.grade-chip-a { background: var(--color-success-bg); color: var(--color-success-text); }
.grade-chip-b { background: var(--color-info-bg);    color: var(--color-info-text); }
.grade-chip-c { background: var(--color-warning-bg); color: var(--color-warning-text); }
.grade-chip-d { background: var(--color-warning-bg); color: var(--color-warning-text); }
.grade-chip-f { background: var(--color-danger-bg);  color: var(--color-danger-text); }
.grade-chip-none { background: var(--bg-alt); color: var(--text-muted); }

/* Tier-5 sweep additions */
/* Underline sub-tabs (e.g. data-flows Assets/Flows). Token-driven; the active
   tab gets .is-active. Replaces a per-page inline-styled tab. */
.subtab {
  padding: .625rem 1.25rem; font-size: .875rem; font-weight: 600; text-decoration: none;
  border-bottom: 2px solid transparent; color: var(--text-muted); margin-bottom: -2px;
}
.subtab:hover { color: var(--text-primary); }
.subtab.is-active { border-bottom-color: var(--primary); color: var(--primary-text); }
.grid-autofit-160 { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1rem; }
.edge-warning { border-left: 3px solid var(--color-warning); }
.edge-danger  { border-left: 3px solid var(--color-danger); }
.col-w-32 { width: 32px; }
.col-w-60 { width: 60px; }
.maxw-220 { max-width: 220px; }
.maxw-760 { max-width: 760px; }
.lvl-stat-sm { padding: .625rem; min-width: 0; }

/* Risk-analysis matrix/table helpers */
.td-cell { padding: .5rem .625rem; }
.align-top { vertical-align: top; }
.capitalize { text-transform: capitalize; }
.maxw-260 { max-width: 260px; }
.cursor-help { cursor: help; }
.fw-800 { font-weight: 800; }
.blank-pad { padding: 2rem 1.5rem; }
.lvl-stat { text-align: center; padding: .75rem 1.25rem; background: var(--gray-50); border-radius: 8px; min-width: 80px; }

/* NIST 800-30 risk levels (RiskAnalysis::levelClass) — semantic, theme-stable */
.risk-lvl-pill { display: inline-block; min-width: 2rem; padding: .15rem .45rem; border-radius: var(--radius-full); color: var(--color-white); font-weight: 700; font-size: .8125rem; text-align: center; }
.risk-lvl-pill.risk-very_high { background: var(--color-danger-strong); }
.risk-lvl-pill.risk-high      { background: var(--color-orange-strong); }
.risk-lvl-pill.risk-moderate  { background: var(--color-warning-strong); }
.risk-lvl-pill.risk-low       { background: var(--primary); }
.risk-lvl-pill.risk-very_low  { background: var(--color-success-strong); }
.risk-lvl-pill.risk-none      { background: var(--gray-500); }
.lvl-text.risk-very_high { color: var(--color-danger); }
.lvl-text.risk-high      { color: var(--color-warning-text); }
.lvl-text.risk-moderate  { color: var(--color-warning-text); }
.lvl-text.risk-low       { color: var(--color-info); }
.lvl-text.risk-very_low  { color: var(--color-success-text); }
.lvl-text.risk-none      { color: var(--text-muted); }

/* Security settings (MFA / recovery codes) */
.input-compact { padding: .375rem .625rem; }
.input-spread { letter-spacing: .2em; }
.maxw-200 { max-width: 200px; }
.maxw-280 { max-width: 280px; }
.maxw-320 { max-width: 320px; }
.self-start { align-self: flex-start; }
.pt-xs { padding-top: .25rem; }
.qr-frame { padding: 8px; }
.recovery-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: .375rem; font-family: var(--font-mono); font-size: .875rem; }
.recovery-grid-boxed { background: var(--color-warning-bg); border: 1px solid var(--color-warning-border); border-radius: 4px; padding: .75rem; margin: 0 0 .75rem; }
.recovery-code-chip { background: var(--bg-alt); padding: .375rem .75rem; border-radius: 4px; }
.callout-warning { background: var(--color-warning-bg); border: 1px solid var(--color-warning-border); border-radius: var(--radius); }
.list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  padding: .625rem .875rem;
}

/* Remediation/task view */
.td-tight { padding: .375rem .5rem; }
.minw-220 { min-width: 220px; }
.maxw-100 { max-width: 100px; }
.btn-bare { background: none; border: none; cursor: pointer; padding: .1rem .3rem; }
.btn-solid-success { background: var(--color-success-strong); color: var(--color-white); border: none; }
.btn-soft-danger  { background: var(--color-danger-bg);  color: var(--color-danger-text);  border: 1px solid var(--color-danger-border); }
.btn-soft-warning { background: var(--color-warning-bg); color: var(--color-warning-text); border: 1px solid var(--color-warning-border); }
.btn-soft-success { background: var(--color-success-bg); color: var(--color-success-text);      border: 1px solid var(--color-success-border); }
.btn-soft-info    { background: var(--color-info-bg);    color: var(--color-info-text);         border: 1px solid var(--color-info-border); }
.btn-soft-danger:hover  { background: color-mix(in srgb, var(--color-danger-bg) 80%, var(--color-danger) 20%); }
.btn-soft-warning:hover { background: color-mix(in srgb, var(--color-warning-bg) 80%, var(--color-warning) 20%); }
.btn-soft-success:hover { background: color-mix(in srgb, var(--color-success-bg) 80%, var(--color-success) 20%); }
.btn-soft-info:hover    { background: color-mix(in srgb, var(--color-info-bg) 80%, var(--color-info) 20%); }
.opacity-65 { opacity: .65; }
.frame-danger-border { border-color: var(--color-danger-border); }
.card-tint-warning { border-color: var(--color-warning-border); background: var(--color-warning-bg); }
.card-tint-success { border-color: var(--color-success-border); background: var(--color-success-bg); }
/* pre-wrap preserves the AUTHOR's line breaks; overflow-wrap stops a long
   unbroken token (a pasted URL, a hash, a 200-char run) from widening the box.
   The two belong together: every consumer of this utility is customer free text,
   where such a token is realistic — measured 2026-08-05, a 220-char run made a
   400px sanctions cell 2252px wide. 13 of 19 consumers lacked a hand-added
   guard, so the fix belongs on the utility rather than at each call site. Same
   idiom the component rules already use (.baa-doc, .comment-body, .md-body). */
.pre-wrap { white-space: pre-wrap; overflow-wrap: anywhere; }
.no-select { user-select: none; }
.q-num { font-size: .75rem; font-weight: 700; color: var(--text-muted); min-width: 20px; padding-top: .1rem; }
.response-box { font-size: .875rem; padding: .5rem .75rem; background: var(--bg-surface); border: 1px solid var(--gray-300); border-radius: 6px; line-height: 1.5; }

/* Onboarding wizard */
.ob-wrap { max-width: 680px; margin: 2.5rem auto; padding: 0 1rem 4rem; }
.ob-topbar { background: var(--bg-surface); border-bottom: 1px solid var(--border-color); padding: .875rem 1.5rem; }
.ob-title { font-size: 1.5rem; font-weight: 700; margin: 0 0 .5rem; }
.ob-stat { padding: .875rem 1.5rem; background: var(--gray-50); border: 1px solid var(--border-color); border-radius: var(--radius); min-width: 140px; }
.ob-stat-value { font-size: 2rem; font-weight: 700; color: var(--primary-text); line-height: 1; }
.ob-row { display: flex; align-items: center; gap: .625rem; padding: .625rem .875rem; background: var(--gray-50); border: 1px solid var(--border-color); border-radius: var(--radius); }

/* S1.4: onboarding launch-step "next steps" (invite team, add BAs) */
.ob-nextsteps { text-align: left; }
.ob-nextstep { display: flex; align-items: center; gap: .75rem; padding: .75rem .875rem; margin-bottom: .5rem; background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-lg); color: inherit; text-decoration: none; transition: border-color var(--transition), background var(--transition); }
.ob-nextstep:hover { border-color: var(--border-strong, var(--text-muted)); background: var(--bg-elevated, var(--bg-alt)); }
.ob-nextstep-icon { flex: none; color: var(--primary-text, var(--primary)); }
.ob-nextstep-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.ob-nextstep-title { font-weight: 600; color: var(--text-primary); font-size: .9375rem; }
.ob-nextstep-sub { font-size: .8125rem; color: var(--text-muted); }
.ob-nextstep-arrow { flex: none; color: var(--text-muted); }
.ob-section { padding: 1.25rem 1.25rem 1rem; }

/* Numbered progress stepper */
.stepper { display: flex; gap: 0; align-items: flex-start; justify-content: center; margin-bottom: 2.5rem; }
.stepper-item { display: flex; flex-direction: column; align-items: center; flex: 1; position: relative; }
.stepper-rule { position: absolute; top: 17px; right: 50%; left: 0; height: 2px; background: var(--border-color); }
.stepper-rule-active { background: var(--primary); }
.stepper-circle {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .875rem;
  font-weight: 700;
  position: relative;
  z-index: 1;
  border: 2px solid transparent;
}
.stepper-circle-active  { background: var(--primary);        color: var(--color-white); border-color: var(--primary); }
.stepper-circle-done    { background: var(--color-success-strong);  color: var(--color-white); border-color: var(--color-success-strong); }
.stepper-circle-pending { background: var(--bg-surface); color: var(--text-subtle); border-color: var(--gray-300); }
.stepper-label { font-size: .75rem; margin-top: .4rem; }
.stepper-label-active  { color: var(--primary-text); font-weight: 600; }
.stepper-label-done    { color: var(--color-success-text); }
.stepper-label-pending { color: var(--text-subtle); }

/* Sweep support */
.page-surface-gray { background: var(--gray-100); min-height: 100vh; }
.icon-disc-lg { width: 72px; height: 72px; }
.icon-disc-success { background: var(--color-success-bg); color: var(--color-success-text); }
.btn-xl-pad { font-size: 1.0625rem; padding: .75rem 2rem; }
.list-plain { list-style: none; padding: 0; margin: 0; }
.maxw-480 { max-width: 480px; }
.mx-sm { margin-left: .5rem; margin-right: .5rem; }

/* Notification step dots (breach workflow checkmarks) */
.step-dot {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border: 2px solid transparent;
}
.step-dot-done   { background: var(--color-success-bg); border-color: var(--color-success); }
.step-dot-warn   { background: var(--color-warning-bg); border-color: var(--color-warning); }
.step-dot-danger { background: var(--color-danger-bg);  border-color: var(--color-danger); }
.frame-danger { border-color: var(--color-danger-border); background: var(--color-danger-bg); }
.icon-disc-danger { background: var(--color-danger-bg); color: var(--color-danger-text); }
.rule-top-md { margin-top: .75rem; padding-top: .75rem; border-top: 1px solid var(--border-color); }
.lh-relaxed { line-height: 1.65; }
.table-mini { font-size: .8125rem; border-collapse: collapse; width: 100%; }
.th-mini { text-align: left; padding: .3rem .5rem; border-bottom: 1px solid var(--border-color); color: var(--text-muted); }
.td-mini { padding: .3rem .5rem; border-bottom: 1px solid var(--gray-100); }
.grid-autofill-200 { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: .4rem .75rem; }

/* Deadline status card (breach HHS deadline etc.) */
.deadline-card-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; margin-bottom: .5rem; }
.deadline-card-date  { font-size: 1.5rem; font-weight: 700; }
.deadline-card-sub   { font-size: .875rem; margin-top: .25rem; }
.deadline-card-success { background: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success-text); }
.deadline-card-warning { background: var(--color-warning-bg); border-color: var(--color-warning-border); color: var(--color-warning-text); }
.deadline-card-danger  { background: var(--color-danger-bg);  border-color: var(--color-danger-border);  color: var(--color-danger-text); }

/* Vertical event timeline (breach lifecycle etc.) */
.timeline { position: relative; padding-left: 1.5rem; }
.timeline-rule { position: absolute; left: .4rem; top: 0; bottom: 0; width: 2px; background: var(--border-color); }
.timeline-item { position: relative; margin-bottom: 1rem; padding-left: .75rem; }
.timeline-dot {
  position: absolute;
  left: -1.1rem;
  top: .1rem;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--bg-surface);
}
.timeline-dot-neutral { background: var(--text-muted);     box-shadow: 0 0 0 1px var(--text-muted); }
.timeline-dot-pending { background: var(--border-strong);  box-shadow: 0 0 0 1px var(--border-strong); }
.timeline-dot-success { background: var(--color-success);  box-shadow: 0 0 0 1px var(--color-success); }
.timeline-dot-warning { background: var(--color-warning);  box-shadow: 0 0 0 1px var(--color-warning); }
.timeline-dot-danger  { background: var(--color-danger);   box-shadow: 0 0 0 1px var(--color-danger); }

/* Status banner — signed/locked/draft strip on shared reports */
.status-banner {
  padding: .75rem 1rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  font-size: .9375rem;
  font-weight: 600;
  margin-bottom: 1.5rem;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.status-banner-success { background: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success-text); }
.status-banner-info    { background: var(--color-info-bg);    border-color: var(--color-info-border);    color: var(--color-info-text); }
.status-banner-neutral { background: var(--gray-100);         border-color: var(--gray-300);             color: var(--text-secondary); }

/* Quoted/aside callout (left-rule) */
.quote-callout {
  margin-top: .5rem;
  padding: .625rem .75rem;
  background: var(--gray-50);
  border-left: 3px solid var(--gray-300);
  border-radius: 0 4px 4px 0;
  font-size: .875rem;
  color: var(--text-secondary);
}

/* Bordered frame for grouped list content */
.frame { border: 1px solid var(--border-color); border-radius: var(--radius); overflow: hidden; }
.frame-row-head { padding: .75rem 1rem; background: var(--gray-50); border-bottom: 1px solid var(--border-color); font-size: .875rem; font-weight: 600; }
.frame-row { padding: .75rem 1rem; background: var(--bg); }
.frame-row + .frame-row { border-top: 1px solid var(--border-color); }
.frame-row-muted { background: var(--gray-50); }
.resize-y { resize: vertical; }

/* Misc text/layout utilities (global sweep) */
.fs-2xs { font-size: .625rem; }
.italic { font-style: italic; }
.break-all { word-break: break-all; }
.grid { display: grid; }
.summary-plain { list-style: none; }
.rule-top { border-top: 1px solid var(--border-color); padding-top: .5rem; margin-top: .25rem; }
.bg-gray-100 { background: var(--gray-100); }
.bg-alt-surface { background: var(--gray-50); }

/* Report page bits */
.mini-th { padding: .5rem; color: var(--text-muted); font-weight: 600; font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; }
.mini-th.ta-left { padding-left: 0; }
.report-footnote { margin-top: 2rem; padding: 1rem; border-top: 1px solid var(--border-color); font-size: .75rem; color: var(--text-muted); line-height: 1.6; }
.row-rule        td, .row-rule        th { border-bottom: 1px solid var(--border-color); }
.row-rule-strong td, .row-rule-strong th { border-bottom: 2px solid var(--border-color); }
.icon-disc { width: 64px; height: 64px; border-radius: 50%; background: var(--primary-light); display: flex; align-items: center; justify-content: center; }
.svg-inline { vertical-align: -.15em; margin-right: .3em; display: inline-block; }
.minw-200 { min-width: 200px; }
.upgrade-gate { max-width: 600px; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 60vh; text-align: center; }
.upgrade-gate-sub { max-width: 380px; margin: 0 0 1.75rem; }
.btn-lg-pad { font-size: 1rem; padding: .625rem 1.75rem; }

/* Vendor questionnaire (public, token-gated form) */
.vq-page { background: var(--gray-50); min-height: 100vh; padding: 2rem 1rem; font-family: var(--font-sans); }
.vq-wrap { max-width: 680px; margin: 0 auto; }
.vq-card {
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-color);
  box-shadow: var(--shadow-lg);
}
.vq-header { background: var(--primary); padding: 1.75rem 2rem; }
.vq-header-title { color: var(--color-white); margin: 0 0 .35rem; }
.vq-header-sub { color: color-mix(in srgb, var(--primary) 24%, var(--color-white)); }
.vq-question { margin-bottom: 1.5rem; padding-bottom: 1.5rem; border-bottom: 1px solid var(--gray-100); }
.vq-question-label { display: block; font-size: .9375rem; font-weight: 600; color: var(--gray-800); margin-bottom: .6rem; line-height: 1.5; }
.vq-option { display: flex; align-items: center; gap: .6rem; font-size: .9375rem; cursor: pointer; font-weight: 400; }
.vq-scale-option { display: flex; flex-direction: column; align-items: center; gap: .3rem; font-size: .8125rem; color: var(--text-muted); cursor: pointer; font-weight: 400; }
.vq-textarea { resize: vertical; }
.vq-emoji { font-size: 2.5rem; }
.vq-success-disc {
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--color-success-bg);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 1.25rem;
}
.p-2xl { padding: 2.5rem; }

/* Auditor portal layout */
.auditor-wrap { max-width: 960px; margin: 2rem auto; padding: 0 1.5rem; }
.auditor-topbar {
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-color);
  padding: .75rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1rem;
  justify-content: space-between;
}

/* Underlined tab strip (auditor scopes etc.) */
.tab-strip {
  display: flex;
  gap: .25rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--border-color);
  overflow-x: auto;
}
.tab-strip-link {
  padding: .625rem 1rem;
  font-size: .875rem;
  font-weight: 400;
  color: var(--text-secondary);
  border-bottom: 2px solid transparent;
  text-decoration: none;
  white-space: nowrap;
  display: inline-block;
  margin-bottom: -1px;
}
.tab-strip-link.active { font-weight: 600; color: var(--primary-text); border-bottom-color: var(--primary); }

/* Inline code chip (tokens, ids) */
.code-chip {
  background: var(--gray-100);
  color: var(--gray-800);
  padding: .15rem .4rem;
  border-radius: 4px;
  font-size: .8125rem;
  font-family: var(--font-mono);
}

/* Plain horizontal scroll (tables inside flush cards) */
.scroll-x { overflow-x: auto; }
.p-xl { padding: 1.5rem; }
.link-plain-color { text-decoration: none; }
/* First/last columns of a table inside a flush card align with the card header */
.card-flush .data-table th:first-child, .card-flush .data-table td:first-child { padding-left: 1.5rem; }
.card-flush .data-table th:last-child,  .card-flush .data-table td:last-child  { padding-right: 1.5rem; }
/* Same alignment problem, same fix, for a LIST inside a flush card. `.card-flush`
   zeroes the card padding and `.list-plain` has none of its own, so `.list-row`
   (which carries its own border + radius) sat 1px from the card edge while the
   sibling `.card-header` text started at 20px — measured, a 19px misalignment.
   Tables were already handled by the two rules above; lists had no equivalent.
   Gutter only: the rows supply their own vertical rhythm via `gap`. */
.card-flush > .list-plain { padding: 0 1.25rem 1.25rem; display: grid; gap: .5rem; }

.w-280 { width: 280px; }

/* Global-sweep additions */
.svg-inline-lg { vertical-align: -.3em; margin-right: .4em; display: inline-block; }
.doc-list { font-size: .875rem; color: var(--text-secondary); margin: 0 0 1.75rem; padding-left: 1.25rem; line-height: 1.8; }
.eyebrow-subtle { font-size: .75rem; color: var(--text-subtle); text-transform: uppercase; letter-spacing: .05em; }
/* Full-bleed opaque page ground. Kept AS-IS for the token-authenticated pages
   (`sign-baa.php`, `policy-acknowledge.php`), which render no starfield and are
   functional rather than marketing — so they must not be re-grounded here.
   The nine PUBLIC CONTENT pages (docs / legal / blog / status / hipaa-guide) add
   `.page-surface-marketing` alongside it, which drops the full-bleed fill so the
   backdrop shows in the gutters while `.legal-card` keeps the reading column
   opaque. Two classes rather than one repurposed class, because the two page
   families genuinely want different things. */
.page-surface-light { background: var(--gray-50); min-height: 100vh; }
.page-surface-marketing { background: transparent; }
.col-w-100 { width: 100px; }
.col-w-160 { width: 160px; }
.maxw-960 { max-width: 960px; }
.maxw-160 { max-width: 160px; }
.section-rule-top { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border-color); }
.mb-0 { margin-bottom: 0; }
.mb-px { margin-bottom: 1px; }
.pb-px { padding-bottom: 1px; }
.maxw-600 { max-width: 600px; }
.maxw-680 { max-width: 680px; }
.maxw-740 { max-width: 740px; }
.maxw-800 { max-width: 800px; }
.maxw-860 { max-width: 860px; }
.maxw-900 { max-width: 900px; }
.eyebrow { font-size: .75rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: .25rem; }
.p-cell { padding: .625rem .75rem; }
.m-opt { margin: .375rem 0 .375rem .25rem; }
.doc-h1 { font-size: 1.375rem; font-weight: 700; color: var(--primary-text); margin: 0 0 .75rem; }
.flex-2  { flex: 2; }
.minw-0  { min-width: 0; }
.my-xs   { margin-top: .25rem; margin-bottom: .25rem; }
.mb-2xs  { margin-bottom: .375rem; }
.list-indent { margin: 0 0 1rem; padding-left: 1.5rem; }
.th-light { text-align: left; padding: .5rem .75rem; color: var(--text-muted); font-weight: 500; }
/* Long-form document heading (docs / guides / blog) */
.doc-h2 { font-size: 1.125rem; font-weight: 600; color: var(--primary-text); margin: 2rem 0 .75rem; }

/* Wave-A additions */
.fs-2xl { font-size: 1.25rem; }
.ml-2xs { margin-left: .375rem; }
.ml-xs  { margin-left: .25rem; }
.ml-sm  { margin-left: .5rem; }
/* Letter-spacing utility for uppercase eyebrow/kicker labels. */
.ls-wide { letter-spacing: .04em; }
.input-mono { font-family: var(--font-mono); }
.link-plain { text-decoration: none; color: inherit; }
.auth-wrap-sm { width: 100%; max-width: 420px; margin: 2rem auto; padding: 0 1rem; }
.auth-center { display: flex; align-items: center; justify-content: center; font-family: var(--font-sans); }
.page-surface { min-height: 100vh; background: var(--bg-alt); }
.blank-state { text-align: center; padding: 4rem 1rem; }
/* Table column width hints */
.col-w-80  { width: 80px; }
.col-w-90  { width: 90px; }
.col-w-110 { width: 110px; }
.col-w-140 { width: 140px; }
/* The padlock inherits from here via currentColor. It used to hardcode the
   the legacy brand blue in three SVG sub-elements, which ignored the token remap
   and measured 2.19:1 in dark — below the 3:1 non-text minimum, on the screen
   every MFA-enrolled user sees at login. The legacy-hex ratchet did not catch it
   because that test scans src/ and templates/ only, so pub/ was a blind spot. */
.auth-mfa-icon { text-align: center; margin-bottom: 1.25rem; color: var(--primary-text); }
.auth-success-state { text-align: center; }
/* The tick inherits via currentColor. Both auth pages using this wrapper
   hardcoded the legacy success green, which ignored the token remap. */
.auth-success-icon { margin: 0 auto 1rem; width: fit-content; color: var(--color-success-text); }
.auth-brand-logo-inline {
  display: flex; align-items: center; gap: .625rem;
  margin-bottom: 2rem;
  text-decoration: none;
}

/* ---- App Layout (Dashboard) -------------------------------- */
.app-body { background: var(--bg-body); }
.app-layout { display: flex; min-height: 100vh; background: var(--bg-body); }
/* overflow-x: clip (not `overflow: hidden`) prevents horizontal overflow from
   wide tables without turning .app-main into a scroll/clip container — the
   latter clips the sticky topbar's absolutely-positioned org/user dropdowns and
   lets page-header content bleed through them. min-width:0 lets the flex child
   shrink so wide content scrolls inside .app-content instead of stretching. */
.app-main { flex: 1; min-width: 0; overflow-x: clip; background: var(--bg-body); }

.app-sidebar {
  width: var(--sidebar-width);
  flex-shrink: 0;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--sidebar-border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}
.sidebar-header {
  padding: 1rem 1rem .75rem;
  border-bottom: 1px solid var(--sidebar-border);
}
.sidebar-header .nav-logo-text { color: var(--text-primary); font-size: 1rem; }
.sidebar-nav { padding: .75rem 0; flex: 1; }
.sidebar-nav ul { padding: 0 .5rem; }
.sidebar-link {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  font-size: .875rem;
  font-weight: 500;
  color: var(--sidebar-link-color);
  transition: background-color var(--transition-fast), color var(--transition-fast);
  text-decoration: none;
  margin: 1px 0;
}
.sidebar-link:hover { background: var(--sidebar-link-hover-bg); color: var(--text-primary); }
.sidebar-link.active { background: var(--sidebar-link-active-bg); color: var(--sidebar-link-active-color); font-weight: 600; }
.sidebar-link-logout { color: var(--color-danger); }
.sidebar-link-logout:hover { background: var(--color-danger-bg); color: var(--color-danger-text); }
.sidebar-divider { height: 1px; background: var(--sidebar-border); margin: .75rem .5rem; }

/* Free-tier freeze banner (requireNotFrozen) — warning family, token-pure. */
.frozen-banner {
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
  border-bottom: 1px solid var(--color-warning-border);
  padding: .5rem 1.25rem;
  font-size: .8125rem;
  font-weight: 500;
}
.frozen-banner a { color: var(--color-warning-text); font-weight: 700; text-decoration: underline; }
/* Free-trial banner. INFO rather than warning: a trial is a normal, healthy
   state, and painting it amber would read as something being wrong. Uses the
   -bg / -text / -border family per Rule 10 (the base fill would be a 2-4:1 ink).
   Flex with a wrap so the CTA never overflows a narrow viewport. */
/* Persistent trial notice above the topbar, on all 159 dashboard pages.
   NAMED DISTINCTLY from the sidebar's `.trial-banner`, which is a different
   control: that one is a WARNING that fires only in the last 7 days, this one is
   a standing statement of what the trial can and cannot do. A first version reused
   `.trial-banner` and the two collided — both are (0,1,0), so the sidebar's
   later-in-file rule won on every shared property and this one rendered as info
   ink on a warning ground with a warning border. Contrast passed AA throughout
   (6.18:1 light / 7.30:1 dark), which is why no ratchet, lint-ui or theme-parity
   saw it: the defect is semantic, not legibility.
   INFO rather than warning on purpose — a trial is a normal, healthy state, and
   painting it amber reads as something being wrong. */
.trial-notice {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .5rem;
  background: var(--color-info-bg);
  color: var(--color-info-text);
  border-bottom: 1px solid var(--color-info-border);
  padding: .5rem 1.25rem;
  font-size: .8125rem;
  font-weight: 500;
}

.app-topbar {
  height: 56px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-color);
  box-shadow: var(--shadow-xs);
  display: flex;
  align-items: center;
  padding: 0 1.5rem;
  gap: 1rem;
  position: sticky;
  top: 0;
  z-index: 50;
}
.sidebar-toggle-btn { color: var(--text-muted); padding: .375rem; border-radius: var(--radius-sm); }
.sidebar-toggle-btn:hover { background: var(--bg-alt); color: var(--text-secondary); }
.topbar-right { display: flex; align-items: center; gap: 1rem; margin-left: auto; }
.topbar-alert {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .25rem .75rem;
  background: var(--color-warning-bg);
  border: 1px solid var(--color-warning-border);
  border-radius: var(--radius-full);
  font-size: .8125rem;
  font-weight: 500;
  color: var(--color-warning-text);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--transition);
}
.topbar-alert:hover { background: var(--color-warning-border); color: var(--color-warning-text); }
.topbar-user { display: flex; align-items: center; gap: .625rem; }
/* .user-avatar removed 2026-07-23 — superseded by the .avatar / .avatar-{size}
   family that oh_avatar() emits; nothing referenced .user-avatar. */
.user-name { font-size: .875rem; font-weight: 500; color: var(--text-secondary); }

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .75rem;
  margin-bottom: 1.5rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--border-color);
}
.page-header h1 { font-size: 1.375rem; font-weight: 700; color: var(--text-primary); margin: 0; letter-spacing: -.02em; }

/* ---- Stat widgets ---- */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  margin-bottom: 1.5rem;
}
@media (max-width: 900px) { .stats-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .stats-grid { grid-template-columns: 1fr; } }
.stat-widget {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: 1.25rem 1.5rem;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--transition-base);
}
.stat-widget:hover { box-shadow: var(--shadow-md); }
.stat-widget-warn  { border-top: 3px solid var(--color-warning); }
.stat-widget-success { border-top: 3px solid var(--color-success); }
.stat-widget-value { font-size: 1.875rem; font-weight: 700; line-height: 1; color: var(--text-primary); }
.stat-widget-value-link { color: inherit; text-decoration: none; }
.stat-widget-value-link:hover { color: inherit; text-decoration: underline; }
.stat-widget-label { font-size: .8125rem; font-weight: 500; color: var(--text-muted); margin-top: .375rem; }
.stat-widget-sub   { font-size: .75rem; color: var(--primary-text); margin-top: .5rem; }
.stat-widget-warn .stat-widget-value    { color: var(--warning); }
.stat-widget-success .stat-widget-value { color: var(--success); }

/* ---- Unified stats-pill row (Findings, Remediation, Change Log, Incidents,
   Breaches, Risk Register, Access Reviews, Vendors all share this look) ---- */
.risk-summary-row,
.remediation-stats-row {
  display: flex;
  flex-wrap: wrap;
  gap: .625rem;
  margin-bottom: 1.25rem;
}
.risk-pill,
.rem-stat-pill {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  min-width: 92px;
  padding: .625rem .875rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius, 8px);
  text-decoration: none;
  transition: box-shadow var(--transition-fast, .12s ease), border-color var(--transition-fast, .12s ease);
}
a.risk-pill:hover,
a.rem-stat-pill:hover { box-shadow: var(--shadow-sm); border-color: var(--border-strong); }
.risk-pill.risk-pill-active { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary) inset; }
/* Active state for a clickable .pill used as a filter toggle. */
.pill.pill-active { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary) inset; }
a.pill:hover { box-shadow: var(--shadow-sm); }
/* Findings pills use --pill-bg/--pill-color custom props for a tinted fill. */
.risk-pill { background: var(--pill-bg, var(--bg-surface)); }
.risk-pill-count,
.rem-stat-count { font-size: 1.375rem; font-weight: 700; line-height: 1; color: var(--pill-color, var(--text-primary)); }
.risk-pill-label,
.rem-stat-label { font-size: .75rem; font-weight: 500; color: var(--text-muted); }

.welcome-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: 2rem;
  display: flex;
  gap: 1.5rem;
  box-shadow: var(--shadow-sm);
}
.welcome-card-icon { flex-shrink: 0; }
.welcome-card h2 { font-size: 1.25rem; color: var(--text-primary); margin-bottom: .5rem; }
.welcome-card p { margin-bottom: 1.5rem; }
.getting-started-steps { display: flex; flex-direction: column; gap: 1rem; }
.gs-step {
  display: flex;
  gap: 1rem;
  padding: 1rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-alt);
}
.gs-step.gs-done { opacity: .6; }
.gs-num {
  width: 28px; height: 28px;
  background: var(--primary);
  color: var(--color-white);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: .8125rem;
  font-weight: 700;
  flex-shrink: 0;
}
.gs-done .gs-num { background: var(--color-success-strong); }
.gs-step strong { display: block; font-size: .9375rem; color: var(--gray-900); margin-bottom: .25rem; }
.gs-step p { font-size: .875rem; margin-bottom: 0; }

/* ---- Page Hero (inner pages) ------------------------------- */
/* Transparent for the same reason as `.hero` — the fixed `.marketing-backdrop`
   owns the gradient and the starfield. */
.page-hero {
  position: relative;
  overflow: hidden;
  padding: 4.5rem 0 4rem;
  background: transparent;
  border-bottom: 1px solid var(--border-color);
  text-align: center;
}
.page-hero > .container { position: relative; z-index: 1; }
.page-hero h1 {
  max-width: 800px;
  margin: .75rem auto 1.25rem;
  color: var(--text-primary);
  font-size: clamp(2.1rem, 4.2vw, 3.2rem);
  font-weight: 800;
  letter-spacing: -.04em;
  line-height: 1.02;
  text-wrap: balance;
}
/* Inner-page heroes keep the mono eyebrow centered (they're title bands, not
   section blocks) — suppress the hairline extension there. */
.page-hero .section-label { justify-content: center; }
.page-hero .section-label::after { display: none; }
.page-hero-sub { font-size: 1.125rem; color: var(--text-secondary); max-width: 680px; margin: 0 auto; }

/* ---- Product page ------------------------------------------ */
/* Backgrounds come from the band roster, NOT from here. This block used to read
   "the star field stays confined to the hero", which was true until 2026-08-08
   and is the invariant that changed: the starfield now runs the page's whole
   height in the outside margin, and a full-bleed `background` on these sections
   would cover it. The two variants still alternate, but they do it by overriding
   `--band-ground` so only the OPAQUE COLUMN changes colour and the gutters keep
   showing the backdrop. */
.product-section { padding: var(--section-pad) 0; }
.product-section-alt { --band-ground: var(--bg-body); }
/* Hero "at a glance" capability cards — link into each product section.
   One row of four on desktop, 2×2 on tablets, stacked on phones. */
.product-glance { max-width: 1060px; grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .product-glance { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .product-glance { grid-template-columns: 1fr; } }
.product-glance-card {
    display: block;
    padding: 1rem 1.25rem;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    background: var(--bg-surface);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s, transform .15s, box-shadow .15s;
}
.product-glance-card:hover {
    border-color: var(--border-strong);
    transform: translateY(-2px);
    box-shadow: var(--shadow-sm, 0 4px 12px rgba(0,0,0,.06));
}
.product-glance-card strong { margin-bottom: .25rem; }
.product-row { display: grid; grid-template-columns: 1fr 1fr; gap: 5rem; align-items: center; }
.product-row-reverse { direction: rtl; }
.product-row-reverse > * { direction: ltr; }
.product-text .section-label { margin-bottom: .75rem; }
.product-text h2 { margin-bottom: 1rem; }
.product-text > p { margin-bottom: 1.5rem; }
.feature-list { display: flex; flex-direction: column; gap: 1rem; }
.feature-list li { display: flex; gap: .875rem; }
.feature-list-icon {
  width: 22px; height: 22px;
  background: color-mix(in srgb, var(--color-success) 14%, transparent);
  color: var(--color-success-text);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: .75rem;
  flex-shrink: 0;
  margin-top: 2px;
}
/* The NEGATIVE variant. The base rule hardcodes the success family, so a list of
   things a feature does NOT do rendered its × glyphs in affirmative green —
   contrast was fine (5.43:1 light / 7.03:1 dark), so this is meaning rather than
   legibility, the same class as the [data-theme="dark"] p bug. Measured against
   the REAL tokens: 6.09:1 light, 6.33:1 dark, both on their own tint, so it
   re-themes with no dark override. Colour is not the only cue either way — the
   glyph already differs (× vs ✓). */
.feature-list-icon-no {
  background: color-mix(in srgb, var(--color-danger) 14%, transparent);
  color: var(--color-danger-text);
}
.feature-list strong { display: block; color: var(--text-primary); margin-bottom: .25rem; }
.feature-list p { font-size: .9rem; margin-bottom: 0; }

.product-card-demo {
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  box-shadow: var(--shadow-sm);
}
.demo-progress-header { display: flex; justify-content: space-between; font-size: .875rem; font-weight: 500; color: var(--text-secondary); margin-bottom: .375rem; }
.demo-pct { font-weight: 700; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.demo-progress-bar-wrap { height: 8px; background: var(--bg-alt); border-radius: 4px; margin-bottom: 1rem; overflow: hidden; }
.demo-progress-fill { height: 100%; border-radius: 4px; transition: width .5s ease; }
/* Static demo widget fills (product page mockup) */
.demo-fill-a { width: 78%; background: var(--primary-dark); }
.demo-fill-b { width: 91%; background: var(--accent); }
.demo-fill-c { width: 64%; background: var(--color-purple); }
.demo-finding { display: flex; align-items: center; gap: .625rem; padding: .625rem; background: var(--bg-alt); border-radius: var(--radius-sm); margin-bottom: .5rem; font-size: .875rem; color: var(--text-secondary); }
.finding-badge { padding: .2rem .5rem; border-radius: 4px; font-size: .75rem; font-weight: 700; }
.finding-high   { background: color-mix(in srgb, var(--color-danger) 12%, transparent); color: var(--color-danger-text); }
.finding-medium { background: color-mix(in srgb, var(--color-warning) 14%, transparent); color: var(--color-warning-text); }
.demo-section-label { font-size: .8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); margin-bottom: 1rem; }
.demo-check-item { padding: .625rem .75rem; border-radius: var(--radius-sm); font-size: .9rem; margin-bottom: .375rem; display: flex; align-items: center; gap: .625rem; }
.demo-check-done::before { content: '✓'; color: var(--success); font-weight: 700; }
.demo-check-warn::before { content: '⚠'; color: var(--warning); }
.demo-check-fail::before { content: '✕'; color: var(--danger); }
.demo-check-done { background: var(--color-success-bg); color: var(--color-success-text); }
.demo-check-warn { background: var(--color-warning-bg); color: var(--color-warning-text); }
.demo-check-fail { background: var(--color-danger-bg); color: var(--color-danger-text); }

.risk-matrix { background: color-mix(in srgb, var(--bg-surface) 78%, transparent); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: 1.5rem; box-shadow: var(--shadow-sm); }
.risk-matrix-label { font-family: var(--font-mono); font-size: .7rem; font-weight: 600; color: var(--text-muted); margin-bottom: 1rem; text-transform: uppercase; letter-spacing: .1em; }
.risk-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: .375rem; }
.risk-cell { padding: .75rem .5rem; border-radius: var(--radius-sm); text-align: center; font-size: .75rem; font-weight: 700; }
.risk-critical { background: color-mix(in srgb, var(--color-danger) 14%, transparent); color: var(--color-danger-text); }
.risk-high     { background: color-mix(in srgb, var(--color-orange) 14%, transparent); color: var(--color-orange-text); }
.risk-medium   { background: color-mix(in srgb, var(--color-warning) 14%, transparent); color: var(--color-warning-text); }
.risk-low      { background: color-mix(in srgb, var(--color-success) 14%, transparent); color: var(--color-success-text); }

.demo-task-item { display: flex; align-items: center; gap: .75rem; padding: .75rem; border-radius: var(--radius-sm); margin-bottom: .5rem; font-size: .9rem; }
.task-status { padding: .2rem .625rem; border-radius: var(--radius-full); font-size: .75rem; font-weight: 600; flex-shrink: 0; }
.demo-task-done     { background: color-mix(in srgb, var(--color-success) 9%, transparent); }
.task-status.done   { background: color-mix(in srgb, var(--color-success) 16%, transparent); color: var(--color-success-text); }
.demo-task-progress { background: color-mix(in srgb, var(--primary) 8%, transparent); }
.task-status.progress { background: color-mix(in srgb, var(--primary) 15%, transparent); color: var(--primary-text); }
.demo-task-open     { background: var(--bg-alt); }
.task-status.open   { background: color-mix(in srgb, var(--border-strong) 45%, transparent); color: var(--text-secondary); }

/* Self-balancing: no orphan card on the last row.
 *
 * `auto-fit` packs as many 220px columns as fit and leaves the remainder stranded —
 * 9 cards rendered [4,4,1] and 5 rendered [4,1], a single card alone on a row reading
 * as a mistake rather than a list. Capping at 4 and letting the count decide keeps
 * every row full: 8 → [4,4], 6 → [3,3], 5 → [3,2], 9 → [3,3,3]. The `:has()` rules
 * only ever REDUCE the column count, so nothing gets wider than the 4-up base and no
 * card falls below the 220px the content needs — with one stated exception: the
 * 7-card rule HOLDS at 4-up rather than reducing, so it keeps `auto-fit` and the
 * 220px floor instead of a hard column count. */
.report-features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
@media (min-width: 900px) {
  /* 5 or 9 cards divide by 3 evenly-ish; 3-up beats a lone widow. */
  .report-features-grid:has(> :nth-child(5):last-child),
  .report-features-grid:has(> :nth-child(9):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  /* 6 cards: 3+3 rather than 4+2. */
  .report-features-grid:has(> :nth-child(6):last-child) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  /* 7 cards: no clean split — 4+3 is the least ragged. This one keeps `auto-fit`
     with the 220px floor rather than a hard `repeat(4, …)`, because it is the only
     rule here that would RAISE the column count: at 900px auto-fit gives 3 columns
     and a hard 4 forced cards to 201px, under the minimum the content needs. With
     the floor restored it is 4-up wherever 4×220 fits and drops to 3 below that. */
  .report-features-grid:has(> :nth-child(7):last-child) {
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  }
}
.report-feature {
  padding: 1.5rem;
  background: color-mix(in srgb, var(--bg-surface) 78%, transparent);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  text-align: center;
}
.report-feature-icon { font-size: 2rem; margin-bottom: .875rem; }
.report-feature h3 { margin-bottom: .5rem; }

/* ---- About page -------------------------------------------- */
.mission-section { padding: var(--section-pad) 0; }
.mission-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4rem; align-items: start; }
.mission-statement h2 { margin-bottom: 1rem; }
.mission-statement p + p { margin-top: 1rem; }
.mission-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
/* Scoped to .mission-stats so the marketing "big number" tile doesn't collide
   with the dashboard mini-stat .stat-card (defined later with a flex/max-width
   layout that was capping these). Surface token instead of var(--color-white) so it adapts
   to dark mode. */
.mission-stats .stat-card {
  padding: 1.5rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-align: center;
}
.stat-number { font-size: 2.25rem; font-weight: 800; color: var(--primary-text); line-height: 1; margin-bottom: .375rem; font-variant-numeric: tabular-nums; letter-spacing: -.03em; }
.stat-label  { font-size: .875rem; color: var(--text-muted); }

.values-section { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
.values-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; }
.value-card { padding: 1.75rem; background: color-mix(in srgb, var(--bg-surface) 78%, transparent); border: 1px solid var(--border-color); border-radius: var(--radius-lg); }
.value-icon { font-size: 2rem; margin-bottom: 1rem; }

.team-section { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
.team-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
.team-card { text-align: center; padding: 1.75rem; background: color-mix(in srgb, var(--bg-surface) 78%, transparent); border: 1px solid var(--border-color); border-radius: var(--radius-lg); }
.team-avatar {
  width: 64px; height: 64px;
  border-radius: 50%;
  color: var(--color-white);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.125rem;
  font-weight: 700;
  margin: 0 auto 1rem;
}
.team-title { font-family: var(--font-mono); font-size: .68rem; color: var(--primary-text); font-weight: 600; text-transform: uppercase; letter-spacing: .1em; margin-bottom: .75rem; }
.team-bio  { font-size: .875rem; color: var(--text-muted); line-height: 1.6; }

.security-section { padding: var(--section-pad) 0; border-top: 1px solid var(--border-color); }
.security-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; }
.security-item { padding: 1.5rem; background: color-mix(in srgb, var(--bg-surface) 78%, transparent); border: 1px solid var(--border-color); border-radius: var(--radius-lg); }
.security-item h4 { margin-bottom: .5rem; color: var(--text-primary); }

/* ---- Responsive -------------------------------------------- */
/* 1100–1280px: tighten the gap so the headline column keeps room; the grid
   tracks already prevent overlap, this just keeps the text comfortable. */
@media (max-width: 1280px) {
  .hero-layout { gap: 1.5rem; }
}

/* Below 1100px the instrument-panel column is dropped entirely and the
   headline goes full-width — a single-column hero. With no panel in the grid,
   there is nothing for the text to overlap. */
@media (max-width: 1100px) {
  .hero-layout { grid-template-columns: 1fr; }
  .hero-panel { display: none; }
  .footer-container { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 900px) {
  .auth-layout { grid-template-columns: 1fr; }
  .auth-brand-panel { display: none; }
  .auth-form-panel { padding: 2rem 1rem; }

  .product-row { grid-template-columns: 1fr; gap: 2.5rem; }
  .product-row-reverse { direction: ltr; }
  .mission-grid { grid-template-columns: 1fr; }
}

@media (max-width: 768px) {
  .nav-menu { display: none; }
  /* The bar's CTA links/buttons overflow the 390px viewport (they push the
     row to ~513px), so drop them here — they're mirrored into the collapsed
     menu as .nav-menu-cta. The theme-toggle (a control, not a CTA) stays. */
  .nav-actions .btn, .nav-actions .nav-link { display: none; }
  .nav-toggle { display: flex; }
  .nav-menu.open {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    position: absolute;
    top: 64px; left: 0; right: 0;
    background: var(--bg-surface);
    padding: 1rem;
    border-bottom: 1px solid var(--border-color);
    box-shadow: var(--shadow-md);
    z-index: 99;
  }
  .nav-menu.open .nav-menu-cta { display: block; }
  /* Visual divider before the mirrored CTAs. */
  .nav-menu.open .nav-menu-cta:first-of-type {
    margin-top: .5rem; padding-top: .75rem;
    border-top: 1px solid var(--border-color);
  }
  .site-header { position: relative; }

  .hero { padding: 3rem 0 2.5rem; }
  .cta-banner-content { flex-direction: column; text-align: center; }
  .features-grid, .testimonials-grid, .pricing-grid { grid-template-columns: 1fr; }

  /* Mobile sidebar behavior lives in the transform-based block further down
     (~line 3019). The old display:none rules here killed its slide transition. */
  .footer-container { grid-template-columns: 1fr; gap: 2rem; }
}

@media (max-width: 480px) {
  .hero-actions { flex-direction: column; }
  .hero-actions .btn { width: 100%; justify-content: center; }
  .steps { grid-template-columns: 1fr; }
}

/* ---- Theme Toggle Button ------------------------------------------- */
.theme-toggle {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius);
  color: var(--text-muted);
  transition: background var(--transition), color var(--transition);
  flex-shrink: 0;
}
.theme-toggle:hover { background: var(--bg-hover); color: var(--text-secondary); }
.theme-icon-moon { display: block; }
.theme-icon-sun  { display: none; }
[data-theme="dark"] .theme-icon-moon { display: none; }
[data-theme="dark"] .theme-icon-sun  { display: block; }

/* ---- Sidebar admin label ------------------------------------------- */
.sidebar-section-label {
  padding: 16px 20px 4px;
  font-size: .6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text-muted);
  margin-top: .25rem;
}

/* ---- Sidebar collapsible groups ------------------------------------ */
.sidebar-group { border: none; }
.sidebar-group summary.sidebar-group-label {
  list-style: none;
  cursor: pointer;
  padding: .3rem .75rem .3rem 1rem;
  font-size: .6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: space-between;
  user-select: none;
  margin-top: .375rem;
}
.sidebar-group summary.sidebar-group-label::-webkit-details-marker { display: none; }
.sidebar-group summary.sidebar-group-label::after {
  content: '';
  display: inline-block;
  width: 10px;
  height: 10px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M9 18l6-6-6-6'/%3E%3C/svg%3E");
  background-size: contain;
  background-repeat: no-repeat;
  transition: transform .2s ease;
  flex-shrink: 0;
}
.sidebar-group[open] summary.sidebar-group-label::after { transform: rotate(90deg); }
.sidebar-group summary.sidebar-group-label:hover { color: var(--text-secondary); }
.sidebar-group > ul { margin: 0; padding: 0; }

/* ---- Admin / Questions table --------------------------------------- */
.admin-toolbar {
  display: flex;
  align-items: center;
  gap: .75rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.admin-toolbar .form-input { max-width: 260px; }
/* ---- Filter row / select ---- */
.filter-row {
  display: flex; flex-wrap: wrap; gap: .625rem; align-items: center;
  margin-bottom: 1.25rem;
}
.filter-select {
  padding: .5rem .75rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  font-size: .875rem;
  font-family: inherit;
  color: var(--text-primary);
  background: var(--bg-surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .625rem center;
  padding-right: 2rem;
  appearance: none;
  cursor: pointer;
  transition: border-color var(--transition), box-shadow var(--transition);
  width: auto; min-width: 140px; max-width: 200px;
}
.filter-select:focus {
  outline: none;
  border-color: var(--primary);
  /* Use the theme-aware focus-ring token (higher contrast, visible in dark mode)
     instead of a hard-coded 0.1-alpha ring that was near-invisible. WCAG 2.4.11. */
  box-shadow: var(--focus-ring, 0 0 0 3px color-mix(in srgb, var(--primary) 40%, transparent));
}

/* ---- Tables ---- */
.data-table-wrap {
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-color);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  overflow-x: auto;
}
/* Horizontal scroll for a wide row (e.g. a multi-tab bar) on narrow screens,
   instead of wrapping to two lines. */
.scroll-x-mobile { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.data-table {
  width: 100%;
  border-collapse: collapse;
}
.data-table thead th {
  background: var(--bg-alt);
  color: var(--text-muted);
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: .625rem 1rem;
  border-bottom: 1px solid var(--border-color);
  white-space: nowrap;
  text-align: left;
}
/* Legacy selector support */
.data-table th {
  padding: .625rem 1rem;
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border-color);
  font-size: .75rem;
  font-weight: 600;
  color: var(--text-muted);
  text-align: left;
  white-space: nowrap;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.data-table tbody tr {
  border-bottom: 1px solid var(--border-color);
  transition: background-color var(--transition-fast);
}
.data-table tbody tr:last-child { border-bottom: none; }
.data-table tbody tr:hover { background: var(--bg-hover); }
.data-table tbody td { padding: .75rem 1rem; font-size: .875rem; color: var(--text-secondary); vertical-align: middle; }
.data-table td {
  padding: .75rem 1rem;
  border-bottom: 1px solid var(--border-color);
  color: var(--text-secondary);
  vertical-align: top;
  font-size: .875rem;
}
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: var(--bg-hover); }
.data-table .q-code { font-family: var(--font-mono); font-size: .8125rem; color: var(--primary-text); font-weight: 600; white-space: nowrap; }
.data-table .q-text { max-width: 380px; }
.data-table .q-text p { font-size: .875rem; margin-bottom: 0; color: var(--gray-700); }
.scope-badge {
  display: inline-block;
  padding: .2rem .625rem;
  border-radius: 100px;
  font-size: .75rem;
  font-weight: 600;
  white-space: nowrap;
}
/* Scope variants resolve from the token triads (dark handled by the remap);
   location_ephi keeps a bespoke orange pair — no orange status token exists. */
.scope-org, .scope-badge-org           { background: var(--primary-light);    color: var(--primary-text); }
.scope-location, .scope-badge-location { background: var(--color-success-bg); color: var(--color-success-text); }
.scope-ephi, .scope-badge-ephi         { background: var(--color-purple-bg);  color: var(--color-purple-text); }
.scope-location_ephi  { background: color-mix(in srgb, var(--color-orange) 8%, var(--color-white)); color: var(--color-orange-text); }
[data-theme="dark"] .scope-location_ephi { background: color-mix(in srgb, var(--color-orange) 15%, transparent); color: color-mix(in srgb, var(--color-orange) 45%, var(--color-white)); }
.status-active   { color: var(--color-success-text); font-weight: 600; }
.status-inactive { color: var(--text-muted); }

.import-zone {
  border: 2px dashed var(--gray-300);
  border-radius: var(--radius-lg);
  padding: 2rem;
  text-align: center;
  background: var(--gray-50);
  margin-bottom: 1rem;
  transition: border-color var(--transition), background var(--transition);
}
.import-zone:hover, .import-zone.dragover {
  border-color: var(--primary-light);
  background: var(--primary-light);
}
.import-zone p { font-size: .9375rem; margin-bottom: .75rem; }
.table-empty {
  text-align: center;
  padding: 3rem;
  color: var(--text-muted);
}

/* ---- Dark Mode ------------------------------------------------------ */
/* Dark-mode TOKEN remap moved to tokens.css — only component-level dark rules
   remain below. New components should not need any: use the tokens. */
/* Base */
[data-theme="dark"] body {
  background: var(--bg-body);
  color: var(--text-primary);
}
/* Same :where() exclusion as the headings below, and for the same reason — this
   rule is (0,1,1) while the ink utilities are (0,1,0), so without it a
   <p class="text-danger"> resolved to --text-muted in dark. Measured 2026-08-04:
   an error, a success and a progress message in one status line all painted the
   IDENTICAL muted grey, so colour — the only thing distinguishing them — carried
   no meaning at all. Contrast was fine throughout, which is why no ratchet saw
   it. <p> was the last bare-element dark override still missing the exclusion
   (.btn-ghost and h1-h6 already had it); 29 <p> sites across 14 files were
   affected. Exclude, never escalate: a .text-danger-qualified override starts a
   specificity arms race the next component rule re-breaks. */
[data-theme="dark"] p:where(:not(.text-danger, .text-success, .text-warning, .text-info, .text-brand, .legal-accent)) { color: var(--text-muted); }
/* :where(:not(.text-*, .legal-accent)) so a heading carrying an INK utility keeps
   it. This rule is (0,1,1) and .legal-accent / .text-danger are (0,1,0), so
   without the exclusion the dark override won: measured, an <h2 class="legal-accent">
   resolved to --primary-text (brand) in light and plain --text-primary in dark,
   losing the accent entirely on 32 heading sites across 20 files. Excluding is
   correct rather than escalating — the utility should win normally, and the
   default stays --text-primary for the ~all headings that carry no ink class. */
[data-theme="dark"] h1:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .legal-accent)),
[data-theme="dark"] h2:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .legal-accent)),
[data-theme="dark"] h3:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .legal-accent)),
[data-theme="dark"] h4:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .legal-accent)),
[data-theme="dark"] h5:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .legal-accent)),
[data-theme="dark"] h6:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .legal-accent)) { color: var(--text-primary); }
/* Content links only — a button-styled link (.btn) keeps its own text color;
   otherwise this recolors .btn-primary anchors (marketing CTAs) blue-on-blue. */
/* :where() for the same zero-specificity reason as the base rule — this stays
   at (0,1,1), the original `[data-theme="dark"] a` level, so component anchor
   rules later in the file (.data-table a, .footer-col a, …) keep winning. */
[data-theme="dark"] a:where(:not(.btn, .sidebar-link)) { color: var(--primary-text); }
[data-theme="dark"] a:where(:not(.btn, .sidebar-link)):hover { color: var(--primary-soft); }

/* Navigation / marketing chrome — dark overrides dropped 2026-07-24: the
   light rules are token-driven (glass mixes over --bg-*, text tokens), and
   the tokens already carry the dark values (guide rule 02). */

/* Buttons */
/* :where() exclusion, zero specificity — the same idiom as .btn-ghost directly
   below, and for the same reason. This rule is (0,2,0) and so is
   .btn-outline.btn-outline-danger, so source order made every destructive
   outline button render BLUE in dark (measured as the dark-theme secondary-text blue on "Deactivate
   user"). Contrast was fine at 6.53:1, which is exactly why no ratchet,
   lint-ui or theme-parity could see it: the defect is meaning, not legibility. */
[data-theme="dark"] .btn-outline:where(:not(.btn-outline-danger)) {
  color: var(--primary-text);
  border-color: var(--primary-text);
}
[data-theme="dark"] .btn-outline:hover { background: color-mix(in srgb, var(--primary) 10%, transparent); color: var(--primary-soft); }
/* :where(:not(.text-*)) so a ghost button carrying an INK utility keeps it.
   This rule is (0,2,0) and .text-danger is (0,1,0), so without the exclusion the
   dark override won and 57 destructive ghost buttons across 39 files lost their
   danger cue — measured, not read: the resting colour resolved to
   --text-secondary blended over the button's translucent ground, while light mode
   correctly showed --color-danger-text at 7.78:1. Same zero-specificity technique,
   and the same reason, as the content-link rule above: exclude rather than
   escalate, so the utility wins normally instead of needing an even more specific
   counter-override. (Colours named by TOKEN here, never as a literal — the rule-01
   ratchet scans raw source and reads a hex/rgb in a comment as a real value.) */
[data-theme="dark"] .btn-ghost {
  background: rgba(255,255,255,.06);
  border-color: rgba(255,255,255,.12);
}
[data-theme="dark"] .btn-ghost:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .text-muted)) {
  color: var(--text-secondary);
}
[data-theme="dark"] .btn-ghost:hover { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.2); }
/* Hover keeps the ink colour too — flipping a destructive button to plain
   --text-primary on hover removes the cue at the exact moment of the click. */
[data-theme="dark"] .btn-ghost:hover:where(:not(.text-danger, .text-success, .text-warning, .text-brand, .text-muted)) {
  color: var(--text-primary);
}
[data-theme="dark"] .btn-passkey {
  background: var(--bg-surface);
  color: var(--text-secondary);
  border-color: rgba(255,255,255,.12);
}
[data-theme="dark"] .btn-passkey:hover { background: var(--border-color); }

/* Hero / cards / section bands / headings — dark overrides dropped
   2026-07-24: the 2026 marketing reskin drives every one of these from
   tokens and transparent color-mixes, which re-theme on their own. The
   .product-section plain/alt alternation is bg-surface/bg-body via tokens
   in BOTH themes. */

/* Forms — base colors handled by variables; keeping specific overrides */
[data-theme="dark"] .form-check-label { color: var(--text-muted); }
[data-theme="dark"] .form-label-link { color: var(--primary-text); }
[data-theme="dark"] .input-toggle-vis { color: var(--text-disabled); }
[data-theme="dark"] .input-toggle-vis:hover { color: var(--text-muted); }
[data-theme="dark"] .form-checkbox { border-color: rgba(255,255,255,.2); background: var(--bg-surface); }
[data-theme="dark"] .auth-divider { color: var(--text-disabled); }
[data-theme="dark"] .auth-divider::before,
[data-theme="dark"] .auth-divider::after { background: var(--border-color); }

/* Auth pages */
[data-theme="dark"] .auth-body { background: var(--bg-body); }
[data-theme="dark"] .auth-form-panel { background: var(--bg-body); }
[data-theme="dark"] .auth-panel-solo { background: var(--bg-surface); border: 1px solid rgba(255,255,255,.08); }
[data-theme="dark"] .auth-title { color: var(--text-primary); }
[data-theme="dark"] .auth-subtitle { color: var(--text-subtle); }
[data-theme="dark"] .auth-switch { color: var(--text-subtle); }
[data-theme="dark"] .auth-switch a { color: var(--primary-text); }

/* Alerts — handled by CSS variables */

/* App layout — dark mode handled by CSS variables above */
[data-theme="dark"] .gs-step strong { color: var(--text-primary); }

/* Footer — dark override dropped 2026-07-24: the footer sits on --bg-inverse,
   the fixed deep-ink chip surface, in BOTH themes. */

/* Admin / questions table — dark mode handled by CSS variables */
[data-theme="dark"] .data-table .q-text p { color: var(--text-muted); }
[data-theme="dark"] .import-zone {
  border-color: rgba(255,255,255,.12);
  background: var(--bg-surface);
}
[data-theme="dark"] .import-zone:hover,
[data-theme="dark"] .import-zone.dragover {
  border-color: var(--primary-text);
  background: color-mix(in srgb, var(--primary) 7%, transparent);
}

/* Pricing / hero panel — dark overrides dropped 2026-07-24 (token-driven
   light rules; the screenshot mockup was replaced by the instrument panel). */

/* ---- Tabs ---------------------------------------------------------- */
.tab-list {
  display: flex;
  gap: .25rem;
  border-bottom: 2px solid var(--border-color);
  padding: 0 0 0 .25rem;
  margin-bottom: 1.5rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.tab-list::-webkit-scrollbar { display: none; }
.tab-btn {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .625rem 1rem;
  font-size: .875rem;
  font-weight: 500;
  color: var(--text-muted);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: color var(--transition-fast), border-color var(--transition-fast);
  white-space: nowrap;
  margin-bottom: -2px;
  cursor: pointer;
}
.tab-btn:hover { color: var(--text-primary); background: var(--bg-hover); }
.tab-btn.active { color: var(--primary-text); border-bottom-color: var(--primary); font-weight: 600; }
.tab-count {
  font-size: .75rem;
  padding: .1rem .4rem;
  border-radius: var(--radius-full);
  background: var(--bg-alt);
  color: var(--text-muted);
  margin-left: .125rem;
}
.tab-btn.active .tab-count { background: var(--primary-light); color: var(--primary-text); }
.tab-panel { display: none; }
.tab-panel.active { display: block; animation: fadeIn 150ms ease; }

/* ---- Settings cards ------------------------------------------------ */
.settings-card,
.card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--transition-base);
}
/* Danger-zone card — destructive sections (account closure, etc.). */
.card-danger { border-color: var(--color-danger-border); }
.settings-card:hover,
.card:hover { box-shadow: var(--shadow); }
@media (max-width: 768px) {
  .settings-card, .card { padding: 1rem; }
}

/* Card variants (component library). `.settings-card` is a legacy alias of
   `.card` — prefer `.card` in new code. */
.card-pad-sm { padding: .875rem 1rem; }
.card-pad-lg { padding: 2rem; }
.card-flush  { padding: 0; }            /* for cards whose content is a table */
.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
  margin: 0 0 1rem;
}
.card-header h2, .card-header h3 { margin: 0; font-size: 1.0625rem; font-weight: 600; }
.card-flush .card-header { padding: 1rem 1.25rem; margin: 0; border-bottom: 1px solid var(--border-color); }
/* Card header/body regions referenced by templates but previously undefined
   (rendered unstyled). .settings-card-header mirrors .card-header's row layout
   and adds a divider; the *-body classes are simple content regions (the
   parent .card/.settings-card supplies padding). */
.settings-card-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap;
  margin: 0 0 1rem; padding-bottom: .75rem;
  border-bottom: 1px solid var(--border-color);
}
.card-body, .settings-card-body, .welcome-card-body { display: block; }

/* ---- Pill family (token-driven status badges) ----------------------------
   One set for every status chip. Colors come ONLY from the status token
   triads, so dark mode needs no extra rules. `.badge-*` / `.scope-badge`
   remain as legacy aliases — prefer `.pill` in new code. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .2em .7em;
  font-size: .75rem;
  font-weight: 600;
  line-height: 1.4;
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  white-space: nowrap;
}
/* 2026: a state dot in currentColor — encodes state in form, not just text
   (docs/style-guide-2026.html §Status pills). Token-driven, so it re-themes. */
.pill::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
/* Text uses the AA-tuned -text tokens (not the base hue): base-on-bg was only
   ~4.0-4.3:1 in light mode for success/info — under AA. -text clears 6.2:1. */
.pill-success { background: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success-text); }
.pill-warning { background: var(--color-warning-bg); border-color: var(--color-warning-border); color: var(--color-warning-text); }
.pill-danger  { background: var(--color-danger-bg);  border-color: var(--color-danger-border);  color: var(--color-danger-text); }
.pill-info    { background: var(--color-info-bg);    border-color: var(--color-info-border);    color: var(--color-info-text); }
.pill-neutral { background: var(--bg-alt);           border-color: var(--border-color);         color: var(--text-muted); }
.pill-primary { background: var(--primary-light);    border-color: transparent;                 color: var(--primary-text); }

/* Change-log status timeline step (change-log/view.php). Was an inline style
   string of light pastel hex built in PHP — theme-deaf, so in dark the ink was
   near-invisible on its own tint. Token-driven now; the pill family's tints. */
.timeline-step {
  width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: .7rem; font-weight: 700;
  border: 2px solid var(--border-color);
  background: var(--bg-alt); color: var(--text-subtle);
}
.timeline-step.is-done        { background: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success-text); }
.timeline-step.is-rolled-back { background: var(--color-warning-bg); border-color: var(--color-warning-border); color: var(--color-warning-text); }

/* Grade-keyed stroke for the dashboard compliance gauge arc. The server used
   to emit GRADE_THRESHOLDS' fixed light hex as the stroke attribute, which
   cannot follow the dark remap; the letter still carries the band, the arc
   is non-text (3:1), so the FILL step is right here. */
.gauge-arc-a    { stroke: var(--color-success); }
.gauge-arc-b    { stroke: var(--primary); }
.gauge-arc-c    { stroke: var(--color-warning); }
.gauge-arc-d    { stroke: var(--color-orange); }
.gauge-arc-f    { stroke: var(--color-danger); }
.gauge-arc-none { stroke: var(--text-subtle); }

/* ---- Dialog (native <dialog> component classes) ---------------------------
   Extracted from the inline-styled modals; pairs with the existing
   data-modal-open / data-modal-close bindings in app.js. */
.dialog {
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: 0;
  max-width: 520px;
  width: 100%;
  box-shadow: var(--shadow-xl);
  background: var(--bg-surface);
  color: var(--text-primary);
}
.dialog::backdrop { background: var(--overlay, color-mix(in srgb, var(--bg-inverse) 50%, transparent)); }
.dialog-body { padding: 1.5rem; }
.dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: 1.25rem;
}
.dialog-header h2 { margin: 0; font-size: 1.125rem; font-weight: 600; }
.dialog-actions {
  display: flex;
  gap: .75rem;
  justify-content: flex-end;
  margin-top: 1.25rem;
}

/* ---- Form layout helpers --------------------------------------------------- */
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
@media (max-width: 640px) {
  .form-grid, .form-grid-3 { grid-template-columns: 1fr; }
}

/* ---- Page header action slot ----------------------------------------------- */
.page-header-actions {
  display: flex;
  gap: .75rem;
  align-items: center;
  flex-wrap: wrap;
}

/* ---- Data table extras -------------------------------------------------------
   `body.density-compact` is toggled by the [data-density-toggle] binding in
   app.js (topbar user menu). (A `.data-table-sticky` sticky-header rule lived
   here too but had no markup consumer — removed 2026-07-23.) */
body.density-compact .data-table th,
body.density-compact .data-table td { padding: .375rem .625rem; }

/* ---- Role checkbox grid -------------------------------------------- */
.role-checkbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: .5rem;
}
.role-checkbox-item {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  padding: .5rem .625rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
  font-size: .875rem;
  color: var(--gray-700);
}
.role-checkbox-item:hover { background: var(--gray-50); border-color: var(--gray-300); }
.role-checkbox-item input[type="checkbox"]:checked ~ span { color: var(--primary-text); font-weight: 500; }
.role-checkbox-item:has(input:checked) { background: var(--primary-light); border-color: color-mix(in srgb, var(--primary) 24%, var(--color-white)); }

/* ---- Modal --------------------------------------------------------- */
/* .modal-overlay / .modal-box removed (2026-06-17, UI review D7): the three
   pages that used them (settings locations / ephi / organization) now use the
   canonical native <dialog class="dialog"> component. */

/* Dark mode: tabs and settings-card handled by CSS variables */

/* Dark mode: role checkbox grid */
[data-theme="dark"] .role-checkbox-item { border-color: rgba(255,255,255,.08); color: var(--text-muted); }
[data-theme="dark"] .role-checkbox-item:hover { background: rgba(255,255,255,.04); border-color: rgba(255,255,255,.15); }
[data-theme="dark"] .role-checkbox-item:has(input:checked) { background: color-mix(in srgb, var(--primary) 10%, transparent); border-color: color-mix(in srgb, var(--primary) 30%, transparent); }

/* ==========================================================================
   Assessment view
   ========================================================================== */

/* Header row */
/* .assessment-view-header layout removed (S3.2): the header now uses the
   canonical .page-header. The content classes below still style its inner parts. */
.assessment-view-meta h1 { font-size: 1.375rem; }
.assessment-view-sub { font-size: .875rem; color: var(--gray-500); margin-top: .25rem; }
.assessment-view-actions { display: flex; gap: .5rem; align-items: center; flex-shrink: 0; }

/* Status badges retired 2026-07-23 — fully merged into the .pill family
   (see .pill / .pill-{success,warning,danger,info,neutral,primary}). */

/* ---- Progress bar ---- */
.assessment-progress {
  display: flex; align-items: center; gap: 1rem;
  margin: 1rem 0 1.5rem;
}
.assessment-progress-bar {
  flex: 1; height: 6px;
  background: var(--border-color);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.assessment-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--primary), var(--primary-text));
  border-radius: var(--radius-full);
  transition: width 600ms cubic-bezier(0.4, 0, 0.2, 1);
}
.assessment-progress-label { font-size: .8125rem; color: var(--text-muted); white-space: nowrap; }
/* Compact inline progress bar (assessment list rows) — width set via data-bar-width */
.mini-progress-fill { height: 4px; background: var(--primary); border-radius: var(--radius-sm); }

/* Part 2 #6: assessment velocity sparkline (answers/day, last 14 days).
   Bar heights are set from data-bar-height in a nonced page script. */
.velocity-sparkline {
  display: flex; align-items: flex-end; gap: 3px;
  height: 48px; padding: .25rem 0;
}
.velocity-bar {
  flex: 1 1 0; min-width: 4px; height: 4%;
  background: var(--primary); border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: height 400ms cubic-bezier(0.4, 0, 0.2, 1);
}
.velocity-bar-empty { background: var(--border-color); }
/* S4.1: near-done completion nudge on the assessment view */
.assessment-nearly-done { margin: -.5rem 0 1.25rem; font-size: .875rem; color: var(--text-secondary); }
.assessment-nearly-done[hidden], .assessment-all-answered[hidden] { display: none; }
/* All-answered cue — success-tinted; appears live when the last question lands. */
.assessment-all-answered {
  margin: -.5rem 0 1.25rem; padding: .5rem .75rem;
  font-size: .875rem; color: var(--color-success-text);
  background: var(--color-success-bg); border: 1px solid var(--color-success-border);
  border-radius: var(--radius-md);
}
/* UX A2: sticky position indicator — stays visible while answering so the user
   keeps a sense of where they are after scrolling/filtering. Sits just below
   the 64px app top bar. */
.assessment-position {
  position: sticky;
  top: 56px; /* flush under the 56px .app-topbar (was 64 — left an 8px gap) */
  z-index: 20;
  display: flex;
  align-items: baseline;
  gap: .5rem;
  padding: .5rem .875rem;
  margin-bottom: .75rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
.assessment-position-name  { font-weight: 600; color: var(--text-primary); }
.assessment-position-count { font-size: .8125rem; color: var(--text-muted); }

/* UX A3: prev/next section pager at the foot of each category panel. */
.section-pager {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-color);
}

/* UX A1: resume-jump hint above the progress bar. */
.assessment-resume-hint {
  display: flex; align-items: center; gap: .5rem;
  margin: 0 0 1rem; padding: .5rem .75rem;
  font-size: .875rem; color: var(--text-secondary);
  background: var(--primary-light); border-radius: var(--radius);
}
.assessment-resume-hint svg { color: var(--primary-text); flex-shrink: 0; }
.assessment-resume-hint a { margin-left: auto; white-space: nowrap; }

/* P3 #13: policy acknowledgments */
.policy-ack { margin-bottom: 1.25rem; }
.policy-ack-me { padding: .625rem .75rem; background: var(--color-warning-bg, var(--bg-alt)); border: 1px solid var(--color-warning-border, var(--border-color)); border-radius: var(--radius-md); }

/* P2 #8: persistent setup hub */
.setup-hub { margin-bottom: 1.25rem; border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: .625rem .875rem; background: var(--bg-alt); }
.setup-hub-summary { display: flex; align-items: center; gap: .5rem; cursor: pointer; font-weight: 600; font-size: .9375rem; color: var(--text-primary); list-style: none; }
.setup-hub-summary::-webkit-details-marker { display: none; }
.setup-hub-count { margin-left: auto; font-size: .8125rem; font-weight: 600; color: var(--text-muted); background: var(--surface-1); border: 1px solid var(--border-color); border-radius: var(--radius-full); padding: .05rem .5rem; }
/* Rule 10: -text for INK. The fill token measured 4.23:1 in light (needs 4.5) —
   a real AA failure that shipped because testTextNeverUsesAFillToken matches only
   var(--primary), while its page-local sibling checks five families. */
.setup-hub-count.is-complete { color: var(--color-success-text); border-color: var(--color-success-border, var(--border-color)); }
.setup-hub-list { list-style: none; margin: .625rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .375rem; }
.setup-hub-item { display: flex; align-items: center; gap: .5rem; font-size: .875rem; }
.setup-hub-check { flex: none; color: var(--text-muted); }
.setup-hub-item.is-done .setup-hub-check { color: var(--color-success); }
.setup-hub-item.is-done .setup-hub-label { color: var(--text-muted); }
.setup-hub-label { color: var(--text-brand, var(--primary)); text-decoration: none; }
.setup-hub-label:hover { text-decoration: underline; }

/* P2 #5: saved views (per-user filter presets) */
.saved-views { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; margin-bottom: 1rem; }
.saved-views-label { font-size: .8125rem; font-weight: 600; color: var(--text-muted); }
.saved-views-chips { display: flex; flex-wrap: wrap; gap: .375rem; }
.saved-view-chip { display: inline-flex; align-items: center; gap: .15rem; border: 1px solid var(--border-color); border-radius: var(--radius-full); padding: .1rem .25rem .1rem .6rem; background: var(--bg-alt); }
.saved-view-chip-link { font-size: .8125rem; color: var(--text-secondary); text-decoration: none; }
.saved-view-chip-link:hover { color: var(--text-primary); }
.saved-view-chip-del { background: transparent; border: 0; color: var(--text-subtle, var(--text-muted)); cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 .25rem; }
.saved-view-chip-del:hover { color: var(--color-danger); }

/* P1 #4: list/board view toggle + remediation kanban */
.view-toggle { display: inline-flex; border: 1px solid var(--border-color); border-radius: var(--radius-full); overflow: hidden; }
.view-toggle-btn { padding: .25rem .75rem; font-size: .8125rem; color: var(--text-secondary); text-decoration: none; }
.view-toggle-btn.is-active { background: var(--primary); color: var(--color-white); }
.rem-board { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .875rem; align-items: start; }
@media (max-width: 900px) { .rem-board { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 540px) { .rem-board { grid-template-columns: 1fr; } }
.rem-board-col { background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: .625rem; }
.rem-board-col-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: .5rem; }
.rem-board-count { font-size: .8125rem; font-weight: 600; color: var(--text-muted); }
.rem-board-drop { display: flex; flex-direction: column; gap: .5rem; min-height: 2.5rem; border-radius: var(--radius-md); transition: background var(--transition); }
.rem-board-drop.is-drop-target { background: var(--primary-light); outline: 2px dashed var(--primary); outline-offset: -2px; }
.rem-board-card { background: var(--surface-1); border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: .625rem .75rem; cursor: grab; }
.rem-board-card[draggable="true"]:active { cursor: grabbing; }
.rem-board-card.is-dragging { opacity: .5; }
.rem-board-card--overdue { border-left: 3px solid var(--color-danger); }
.rem-board-card-title { display: block; font-size: .875rem; font-weight: 600; color: var(--text-primary); text-decoration: none; margin-bottom: .35rem; }
.rem-board-card-title:hover { text-decoration: underline; }
.rem-board-card-meta { display: flex; align-items: center; flex-wrap: wrap; gap: .4rem; font-size: .75rem; color: var(--text-muted); }
.rem-board-card-assignee, .rem-board-card-due { white-space: nowrap; }

/* P1 #3: notification center */
.notif-filter { display: flex; gap: .5rem; margin-bottom: 1rem; }
.notif-filter-chip { background: transparent; border: 1px solid var(--border-color); border-radius: var(--radius-full); padding: .25rem .75rem; font-size: .8125rem; color: var(--text-secondary); cursor: pointer; }
.notif-filter-chip.is-active { background: var(--primary); color: var(--color-white); border-color: var(--primary); }
.notif-filter-n { opacity: .75; margin-left: .15rem; }
.notif-page-item { display: block; color: inherit; text-decoration: none; }
.notif-page-item--unread { background: var(--primary-light); }
.notif-page-item:hover .notif-row { background: var(--bg-alt); }
.notif-row { display: flex; align-items: flex-start; gap: .75rem; padding: .75rem .875rem; border-bottom: 1px solid var(--border-color); }
.notif-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; margin-top: .35rem; background: var(--border-color); }
.notif-page-item--unread .notif-dot { background: var(--primary); }
.notif-main { flex: 1; min-width: 0; }
.notif-title { font-size: .875rem; font-weight: 500; color: var(--text-primary); margin-bottom: .2rem; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.notif-page-item--unread .notif-title { font-weight: 600; }
.notif-tag { flex: none; font-size: .6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted); background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: .05rem .35rem; }

/* P1 #2: pre-flight "ready to complete?" checklist */
.completion-preflight { margin: 0 0 1.25rem; padding: .75rem .875rem; background: var(--color-warning-bg, var(--bg-alt)); border: 1px solid var(--color-warning-border, var(--border-color)); border-radius: var(--radius-lg); }
.completion-preflight-head { display: flex; align-items: center; gap: .5rem; color: var(--color-warning-text, var(--text-primary)); margin-bottom: .375rem; }
.completion-preflight-head strong { color: var(--text-primary); }
.completion-preflight-list { list-style: none; margin: 0 0 .375rem; padding: 0; display: flex; flex-direction: column; gap: .25rem; }
.completion-preflight-item { background: transparent; border: 0; padding: .1rem 0; font-size: .875rem; font-weight: 500; color: var(--text-brand, var(--primary)); cursor: pointer; text-align: left; }
.completion-preflight-item:hover { text-decoration: underline; }

/* P1 #1: team progress (per-person assignment overview) */
/* #2c: quiet grouping label over the collapsible insight panels (velocity +
   team progress) so they read as one cluster, not loose stacked widgets. */
.assessment-insights-label { font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); margin: 0 0 .5rem; }
.team-progress { margin: 0 0 1.25rem; border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: .625rem .875rem; background: var(--bg-alt); }
.team-progress-summary { display: flex; align-items: center; gap: .5rem; cursor: pointer; font-weight: 600; font-size: .9375rem; color: var(--text-primary); list-style: none; }
.team-progress-summary::-webkit-details-marker { display: none; }
.team-progress-count { margin-left: auto; font-size: .8125rem; font-weight: 500; color: var(--text-muted); }

/* Risk-treatment badges (S3.1). Soft token triads, matching .pill/.risk-badge —
   the --color-*-bg/-text tokens carry correct light AND dark values, so there
   are no raw hex and no separate dark-mode rules (the old solid fills used white
   text, which under-contrasted on the lightened dark-mode fills). */
.treatment-badge { display: inline-block; padding: .1rem .5rem; border-radius: var(--radius-sm); font-size: .75rem; font-weight: 600; }
.treatment-badge-mitigate { background: var(--color-info-bg);    color: var(--color-info-text); }
.treatment-badge-accept   { background: var(--color-warning-bg); color: var(--color-warning-text); }
.treatment-badge-transfer { background: var(--color-purple-bg);  color: var(--color-purple-text); }
.treatment-badge-avoid    { background: var(--color-danger-bg);  color: var(--color-danger-text); }

/* Post-completion wayfinding banner (S1.1): what the assessment produced + next steps */
.assessment-complete-banner {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .75rem 1rem; margin: 1rem 0 1.5rem; padding: .875rem 1rem;
  background: var(--color-success-bg); border: 1px solid var(--color-success-border);
  border-radius: var(--radius-lg);
}
.assessment-complete-main { display: flex; align-items: center; gap: .625rem; color: var(--color-success-text); min-width: 0; }
.assessment-complete-main strong { color: var(--text-primary); }
.assessment-complete-main span { color: var(--text-secondary); font-size: .9375rem; }
.assessment-complete-actions { display: flex; flex-wrap: wrap; gap: .5rem; flex: none; }

/* Question subcategory section */
.question-subcategory { margin-bottom: 2rem; }
.question-subcategory-title {
  font-size: .8125rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted);
  padding-bottom: .5rem; border-bottom: 1px solid var(--gray-200);
  margin: 0 0 1rem;
}

/* Question card */
.question-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 1.125rem 1.25rem;
  margin-bottom: .75rem;
}
/* N1-5: brief highlight when a resolved finding deep-links back to its source
   question for re-evaluation. */
.question-card-flash { animation: questionCardFlash 1.6s ease-out 1; }
@media (prefers-reduced-motion: reduce) {
  .question-card-flash { animation: none; }
}
@keyframes questionCardFlash {
  0%, 30% { box-shadow: 0 0 0 2px var(--primary) inset, var(--shadow-md); }
  100%    { box-shadow: none; }
}
.question-header { display: flex; gap: .75rem; align-items: baseline; margin-bottom: .75rem; }
.question-code {
  font-family: var(--font-mono, monospace);
  font-size: .75rem; font-weight: 600;
  color: var(--primary-text); background: var(--primary-light);
  padding: .15rem .45rem; border-radius: 4px;
  white-space: nowrap; flex-shrink: 0;
}
.question-text { margin: 0; font-size: .9375rem; line-height: 1.55; color: var(--text-primary); }

/* Guidance accordion */
.question-guidance {
  font-size: .85rem; color: var(--gray-500);
  margin-bottom: .875rem;
  border: 1px solid var(--border-color); border-radius: var(--radius);
  overflow: hidden;
}
.question-guidance summary {
  cursor: pointer; padding: .4rem .75rem;
  font-size: .8rem; font-weight: 500; color: var(--text-muted);
  list-style: none; user-select: none;
}
.question-guidance summary::-webkit-details-marker { display: none; }
.question-guidance[open] summary { border-bottom: 1px solid var(--border-color); }
.question-guidance p { padding: .625rem .75rem; margin: 0; line-height: 1.6; }

/* Prior-assessment answer snapshot ("what did we answer last time?"). */
.prior-answer {
  font-size: .85rem; margin-bottom: .875rem;
  border: 1px solid var(--border-color); border-radius: var(--radius);
  border-left: 3px solid var(--primary); overflow: hidden;
  background: var(--primary-light);
}
.prior-answer summary {
  cursor: pointer; padding: .4rem .75rem;
  font-size: .8rem; font-weight: 500; color: var(--text-secondary, var(--gray-500));
  list-style: none; user-select: none;
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
}
.prior-answer summary::-webkit-details-marker { display: none; }
.prior-answer-verdict { font-weight: 700; }
.prior-answer-yes     { color: var(--color-success-text); }
.prior-answer-no      { color: var(--color-danger-text); }
.prior-answer-partial { color: var(--color-warning-text); }
.prior-answer-na      { color: var(--text-muted); }
.prior-answer-meta    { color: var(--text-muted); font-weight: 400; margin-left: auto; }
.prior-answer-note    { padding: .5rem .75rem; margin: 0; line-height: 1.55; color: var(--text-secondary, var(--gray-600)); }
.prior-answer [data-prior-copy] { margin: 0 .75rem .6rem; }

/* Scope group (per-location / per-ePHI) */
.scope-group { margin-bottom: .75rem; }
.scope-group-label {
  display: flex; align-items: center; gap: .375rem;
  font-size: .8rem; font-weight: 600; color: var(--gray-500);
  margin-bottom: .4rem;
}
.scope-setup-notice {
  display: flex; align-items: center; gap: .5rem;
  font-size: .85rem; color: var(--gray-500);
  background: var(--gray-50); border: 1px solid var(--border-color);
  border-radius: var(--radius); padding: .6rem .875rem;
}
.scope-setup-notice a { color: var(--primary-text); font-weight: 500; }

/* Answer widget */
.answer-widget { padding-top: .25rem; }
.answer-widget-locked { opacity: .75; pointer-events: none; }

.answer-btns {
  display: flex; flex-wrap: wrap; gap: .375rem;
  margin-bottom: .625rem;
}
.answer-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: .35rem .875rem;
  font-size: .8125rem; font-weight: 500;
  border: 1.5px solid var(--border-color);
  border-radius: var(--radius);
  background: var(--bg-surface); color: var(--gray-500);
  cursor: pointer; transition: all .12s;
}
.answer-btn:hover:not(:disabled) { border-color: var(--text-muted); color: var(--text-primary); }

.answer-btn-yes.active   { background: color-mix(in srgb, var(--color-success) 12%, var(--color-white)); border-color: var(--color-success); color: var(--color-success-text); font-weight: 600; }
.answer-btn-no.active    { background: color-mix(in srgb, var(--color-danger) 12%, var(--color-white)); border-color: var(--color-danger); color: var(--color-danger-text); font-weight: 600; }
.answer-btn-partial.active { background: color-mix(in srgb, var(--color-warning) 10%, var(--color-white)); border-color: var(--color-warning-text); color: var(--color-warning-text); font-weight: 600; }
.answer-btn-na.active    { background: var(--text-primary); border-color: var(--text-muted); color: var(--border-strong); font-weight: 600; }

.answer-btn-clear {
  padding: .35rem .5rem;
  color: var(--text-muted); border-style: dashed;
}
.answer-btn-clear:hover { color: var(--danger); border-color: var(--danger); }

.answer-meta {
  display: flex; flex-wrap: wrap; gap: .5rem;
  align-items: flex-start;
}
.answer-notes {
  flex: 1; min-width: 200px;
  font-size: .8125rem; min-height: 2.5rem; max-height: 8rem;
  resize: vertical; padding: .4rem .6rem;
}
/* N/A chosen but not yet justified — draw the eye to the notes field. */
.answer-na-needs-note .answer-notes {
  border-color: var(--color-warning);
  box-shadow: 0 0 0 2px var(--color-warning-bg);
}
.answer-assign {
  font-size: .8125rem; padding: .375rem .6rem;
  min-width: 160px; max-width: 220px;
}

.answer-status {
  font-size: .75rem; margin-top: .375rem;
  min-height: 1.2em;
}
.answer-status.saving  { color: var(--text-muted); }
.answer-status.saved   { color: var(--color-success-text); }
.answer-status.error   { color: var(--danger); }

/* ---- AI evaluation -------------------------------------------------------- */
.answer-ai-eval {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin-top: .5rem;
}
.ai-eval-btn {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: .75rem; font-weight: 600;
  padding: .25rem .6rem; border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg-alt); color: var(--text-primary);
  cursor: pointer;
}
.ai-eval-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--bg-alt) 70%, var(--primary) 12%); }
.ai-eval-btn:disabled { opacity: .5; cursor: not-allowed; }
.ai-eval-badge {
  display: inline-flex; align-items: center;
  font-size: .72rem; font-weight: 600;
  padding: .2rem .55rem; border-radius: 999px;
}
.ai-eval-badge.ai-eval-success  { background: var(--success-light); color: var(--color-success-text); }
.ai-eval-badge.ai-eval-needsinfo { background: var(--danger-light);  color: var(--color-danger-text); }
.ai-eval-stale { font-size: .72rem; color: var(--text-muted); font-style: italic; }
.ai-eval-msg   { font-size: .72rem; color: var(--text-muted); }
.ai-eval-msg.error { color: var(--danger); }
/* E1: daily-cap message — a budget notice, not an error (neutral, not red). */
.ai-eval-msg-cap { color: var(--text-muted); }
/* E2: one-time inline-AI intro hint. Reuses .alert .alert-info; the dismiss
   button sits at the end without disturbing the icon + copy flex row. */
.ai-intro-hint { align-items: center; }
.ai-intro-hint p { font-weight: 400; }
.ai-intro-dismiss {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: .25rem;
  border: none;
  background: transparent;
  color: inherit;
  opacity: .6;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.ai-intro-dismiss:hover { opacity: 1; }
.ai-intro-dismiss:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; opacity: 1; }
.ai-eval-suggestion {
  flex-basis: 100%;
  font-size: .78rem;
  background: var(--danger-light);
  border: 1px solid color-mix(in srgb, var(--danger) 25%, transparent);
  border-radius: 6px; padding: .4rem .6rem;
}
.ai-eval-suggestion > summary {
  cursor: pointer; font-weight: 600; color: var(--color-danger-text); list-style: revert;
}
.ai-eval-suggestion > p { margin: .4rem 0 0; color: var(--text-primary); }
.ai-eval-all-status { font-size: .78rem; color: var(--text-secondary); margin-left: .25rem; }
/* AI executive summary panel */
.ai-summary-panel {
  margin-top: .75rem; padding: .875rem 1rem;
  background: var(--bg-alt); border: 1px solid var(--border); border-radius: 8px;
}
.ai-summary-head { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .5rem; }
.ai-summary-meta { font-size: .72rem; color: var(--text-muted); }
.ai-summary-body p { margin: 0 0 .5rem; font-size: .875rem; color: var(--text-primary); }
.ai-summary-body h4 { margin: .5rem 0 .25rem; font-size: .8rem; text-transform: uppercase; letter-spacing: .03em; color: var(--text-secondary); }
.ai-summary-body ul { margin: 0 0 .5rem 1.1rem; font-size: .85rem; color: var(--text-primary); }
.ai-summary-posture {
  display: inline-block; font-size: .72rem; font-weight: 600; padding: .15rem .5rem;
  border-radius: 999px; margin-bottom: .5rem;
  background: color-mix(in srgb, var(--primary) 14%, transparent); color: var(--primary-text);
}
.ai-summary-msg { font-size: .72rem; color: var(--text-muted); }
.ai-summary-msg:empty { display: none; }
/* Top-risk → findings deep links in the AI summary. */
.ai-summary-risk-link { text-decoration: none; color: inherit; }
.ai-summary-risk-link:hover strong { text-decoration: underline; }
.ai-summary-risk-sev { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; white-space: nowrap; }
/* AI proposal lists (remediation, risk, crosswalk) */
.ai-proposals { display: flex; flex-direction: column; gap: .5rem; margin-top: .5rem; }
.ai-proposal {
  display: flex; gap: .5rem; align-items: flex-start;
  padding: .5rem .625rem; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-surface); font-size: .85rem; cursor: pointer;
}
.ai-proposal input[type=checkbox] { margin-top: .2rem; }
.ai-proposal-main { display: block; }
.ai-proposal-title { font-weight: 600; color: var(--text-primary); }
.ai-proposal-tag {
  display: inline-block; font-size: .68rem; font-weight: 600; padding: .05rem .4rem; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent-text);
}
.ai-proposal-due { font-size: .7rem; color: var(--text-muted); }
.ai-proposal-steps { margin: .35rem 0 .25rem 1.1rem; font-size: .82rem; color: var(--text-primary); }
.ai-proposal-ev { font-size: .78rem; color: var(--text-secondary); margin-top: .2rem; }
.ai-evidence-summary { background: var(--bg-alt); font-size: .82rem; padding: .625rem .875rem !important; }
.ai-evidence-summary p { margin: .35rem 0; }
.ai-evidence-summary ul { margin: .25rem 0 .25rem 1.1rem; }
.ai-eval-meta  { font-size: .7rem; color: var(--text-muted); }
.ai-eval-disclaimer { font-size: .7rem; color: var(--text-muted); font-style: italic; flex-basis: 100%; }

/* ---- AI #7: self-critique callout (remediation drafter + planner) ---- */
.ai-plan-summary { font-size: .85rem; color: var(--text-secondary); margin: 0 0 .5rem; }
.ai-critique {
  border: 1px solid color-mix(in srgb, var(--text-secondary) 22%, transparent);
  background: var(--bg-alt); border-radius: 8px;
  padding: .5rem .7rem; margin: 0 0 .6rem; font-size: .8rem;
}
.ai-critique.needs-review {
  border-color: color-mix(in srgb, var(--warning) 45%, transparent);
  background: var(--warning-light);
}
.ai-critique-head { display: flex; align-items: center; gap: .4rem; }
.ai-critique-score { font-size: .72rem; color: var(--text-secondary); font-weight: 600; }
.ai-critique-flag {
  font-size: .65rem; font-weight: 600; text-transform: uppercase; letter-spacing: .02em;
  padding: .05rem .4rem; border-radius: 999px;
  background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning);
}
.ai-critique-issues { margin: .35rem 0 0 0; padding-left: 1.1rem; }
.ai-critique-issues li { margin: .15rem 0; color: var(--text-primary); }

/* ---- Workflow-level AI surfaces: kickoff planner, program roadmap, audit readiness ---- */
/* Assessment kickoff planner — ordered section list. */
.ai-plan-list { margin: .25rem 0 .5rem 1.2rem; padding: 0; font-size: .85rem; }
.ai-plan-list li { margin: 0 0 .5rem; color: var(--text-primary); }
.ai-plan-meta { font-size: .72rem; color: var(--text-secondary); font-weight: 400; }
.ai-plan-reason { font-size: .8rem; color: var(--text-secondary); margin-top: .15rem; }

/* Program tools section header on Reports. */
.ai-program-tools { border-left: 3px solid color-mix(in srgb, var(--primary) 40%, transparent); }

/* Selectable generate→apply checklist (audit readiness + roadmap). */
.ai-checklist { display: flex; flex-direction: column; gap: .5rem; margin: .5rem 0; }
.ai-checklist-item {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .55rem .7rem; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-surface); font-size: .85rem; cursor: pointer;
}
.ai-checklist-item:hover { border-color: color-mix(in srgb, var(--primary) 35%, var(--border)); }
.ai-checklist-item input[type=checkbox] { margin-top: .2rem; flex: none; }
.ai-checklist-body { display: block; }
.ai-checklist-body strong { color: var(--text-primary); }
.ai-checklist-bar { margin: .25rem 0 .5rem; }

/* Readiness verdict pill. */
.ai-readiness-verdict { font-size: .85rem; margin: 0 0 .6rem; }
.ai-readiness-verdict strong {
  display: inline-block; padding: .1rem .55rem; border-radius: 999px; font-size: .75rem;
}
.ai-readiness-not_ready strong { background: var(--danger-light); color: var(--color-danger-text); }
.ai-readiness-developing strong { background: var(--warning-light); color: var(--color-warning-text); }
.ai-readiness-audit_ready strong { background: var(--success-light); color: var(--color-success-text); }

/* Findings grouped under a roadmap phase. */
.ai-roadmap-findings { margin: .35rem 0 0 0; padding-left: 1.1rem; font-size: .8rem; }
.ai-roadmap-findings li { margin: .15rem 0; color: var(--text-secondary); }
.ai-critique-sev {
  display: inline-block; font-size: .62rem; font-weight: 700; text-transform: uppercase;
  margin-right: .35rem; color: var(--text-secondary);
}
.ai-critique-issues li.sev-major .ai-critique-sev { color: var(--danger); }

/* ---- AI #4: Posture Coach trend table ---- */
.ai-coach-trend-wrap { overflow-x: auto; margin: 0 0 .75rem; }
.ai-coach-trend { width: 100%; border-collapse: collapse; font-size: .8rem; }
.ai-coach-trend th, .ai-coach-trend td {
  text-align: left; padding: .3rem .5rem; border-bottom: 1px solid var(--border);
}
.ai-coach-trend thead th { color: var(--text-secondary); font-weight: 600; font-size: .72rem; text-transform: uppercase; letter-spacing: .02em; }
.ai-coach-trend tbody th { font-weight: 500; color: var(--text-primary); }
.ai-coach-trend tr.trend-regressed td:nth-child(3) { color: var(--danger); font-weight: 600; }
.ai-coach-trend tr.trend-improved  td:nth-child(3) { color: var(--success); font-weight: 600; }
.ai-coach-empty { font-size: .82rem; color: var(--text-secondary); font-style: italic; }

/* ---- AI #2: evidence-gap one-click attach ---- */
.ai-evgap-attach { margin-left: .5rem; padding: .1rem .5rem; font-size: .72rem; }
.ai-evgap-feedback { list-style: none; margin-top: .4rem; }

/* ---- AI: Next 3 actions card ---- */
.ai-actions-card .ai-summary-head { justify-content: space-between; align-items: center; }
.ai-actions-msg { display: block; font-size: .8rem; color: var(--text-muted, var(--gray-500)); margin: .25rem 0; min-height: 1rem; }
.ai-actions-msg.is-error { color: var(--danger); }
.ai-actions-empty { font-size: .85rem; color: var(--text-muted, var(--gray-500)); margin: .25rem 0 .5rem; }
.ai-actions-list { list-style: none; margin: .25rem 0 .5rem; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.ai-action-item { display: flex; align-items: flex-start; gap: .625rem; padding: .625rem .75rem; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-alt, transparent); }
.ai-action-impact { flex-shrink: 0; font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: .15rem .45rem; border-radius: 5px; margin-top: .1rem; }
.ai-action-impact-high   { background: var(--danger-light);  color: var(--color-danger-text); }
.ai-action-impact-medium { background: var(--primary-light); color: var(--primary-text); }
.ai-action-impact-low    { background: var(--success-light); color: var(--color-success-text); }
.ai-action-body { display: flex; flex-direction: column; gap: .15rem; }
.ai-action-title { font-size: .875rem; font-weight: 600; color: var(--text-primary); }
.ai-action-why { font-size: .8rem; color: var(--text-muted, var(--gray-500)); line-height: 1.45; }
.ai-action-link { align-self: flex-start; margin-top: .15rem; font-size: .8rem; font-weight: 500; color: var(--text-brand, var(--primary)); white-space: nowrap; }
.ai-action-link:hover { text-decoration: underline; }
.ryland-brief-action-link { display: inline-block; margin-top: .15rem; font-size: .8125rem; font-weight: 500; color: var(--text-brand, var(--primary)); }
.ryland-brief-action-link:hover { text-decoration: underline; }
[data-theme="dark"] .ai-action-impact-high   { background: color-mix(in srgb, var(--color-danger) 18%, transparent); }
[data-theme="dark"] .ai-action-impact-medium { background: color-mix(in srgb, var(--primary) 18%, transparent);  color: var(--primary-soft); }
[data-theme="dark"] .ai-action-impact-low    { background: color-mix(in srgb, var(--color-success) 18%, transparent); }

/* ---- AI: Ask about your compliance program ---- */
.ai-assistant-suggestions { display: flex; flex-wrap: wrap; gap: .4rem; margin: .25rem 0 .625rem; }
.ai-ask-chip {
  font-size: .78rem; padding: .3rem .6rem; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-alt, transparent);
  color: var(--text-muted, var(--gray-600)); cursor: pointer; transition: var(--transition-fast, .12s ease);
}
.ai-ask-chip:hover { border-color: var(--primary); color: var(--primary-text); }
.ai-assistant-form { display: flex; gap: .5rem; align-items: center; }
.ai-assistant-form .form-input { flex: 1; }
.ai-assistant-answer { margin: .75rem 0 .5rem; padding: .75rem .875rem; border-radius: 8px; background: var(--bg-alt, var(--gray-50)); border: 1px solid var(--border); font-size: .875rem; line-height: 1.55; color: var(--text-primary); }
.ai-assistant-answer.is-loading { color: var(--text-muted, var(--gray-500)); font-style: italic; }
.ai-assistant-answer.is-error { color: var(--color-danger-text); border-color: var(--danger); background: var(--danger-light); }
.ai-answer-text { margin: 0; }
.ai-answer-flag { margin: .5rem 0 0; font-size: .76rem; color: var(--color-warning-text); }
.ai-answer-cites { margin: .5rem 0 0; font-size: .76rem; color: var(--text-muted, var(--gray-500)); }
.ai-answer-cites span { font-weight: 600; }
.ai-answer-cites ul { margin: .15rem 0 0 1.1rem; padding: 0; }
@media (max-width: 640px) { .ai-assistant-form { flex-direction: column; align-items: stretch; } }

/* Human review (accept / override) */
.ai-eval-review { display: inline-flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
/* "Ryland suggested this — you decide:" — keeps the human-in-the-loop framing
   on the verdict, matching the suggest-only disclaimers on the other AI tools. */
.ai-eval-review-label { font-size: .7rem; color: var(--text-muted); }
.ai-override-btn {
  font-size: .7rem; padding: .15rem .5rem; border-radius: 5px;
  border: 1px solid var(--border);
  background: transparent; color: var(--text-secondary); cursor: pointer;
}
.ai-override-btn:hover { background: var(--bg-alt); }
.ai-override-btn.active {
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  border-color: color-mix(in srgb, var(--primary) 40%, transparent);
  color: var(--primary-text);
  font-weight: 600;
}
.ai-override-state { font-size: .7rem; color: var(--text-muted); }
/* Dark mode: the --success-light/--danger-light backgrounds flip to dark, so the
   light-mode dark-green/dark-red text would be unreadable. Mirror the dark badge palette. */
[data-theme="dark"] .ai-eval-badge.ai-eval-success   { background: color-mix(in srgb, var(--color-success) 15%, transparent); color: var(--color-success-text); }
[data-theme="dark"] .ai-eval-badge.ai-eval-needsinfo { background: color-mix(in srgb, var(--color-danger) 15%, transparent); color: var(--color-danger-text); }
[data-theme="dark"] .ai-eval-suggestion {
  background: color-mix(in srgb, var(--color-danger) 10%, transparent);
  border-color: color-mix(in srgb, var(--color-danger) 25%, transparent);
}

/* ---- Evidence widget (per-answer attachments) -------------------------- */
.answer-evidence {
  margin-top: .625rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
}
.evidence-list {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  flex: 1 1 auto;
  min-width: 0;
}
.evidence-list:empty { display: none; }

.evidence-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  background: var(--bg-alt, var(--gray-50));
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: .25rem .375rem;
  max-width: 240px;
  transition: border-color .15s, background .15s;
}
.evidence-chip:hover { border-color: var(--primary); }
.evidence-chip.is-uploading { opacity: .8; }
/* Upload progress bar — a thin track pinned to the chip's bottom edge, filled
   live from the XHR upload-progress event (#2b). Only shown while uploading. */
.evidence-progress-track {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: var(--border-color);
  border-radius: 0 0 8px 8px;
  overflow: hidden;
}
.evidence-chip:not(.is-uploading) .evidence-progress-track { display: none; }
.evidence-progress-bar {
  display: block;
  height: 100%;
  width: 0;
  background: var(--primary);
  transition: width .2s ease;
}
.evidence-chip-link {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  text-decoration: none;
  color: var(--text-primary);
  min-width: 0;
  flex: 1;
}
.evidence-thumb {
  width: 36px; height: 36px;
  object-fit: cover;
  border-radius: 5px;
  background: var(--gray-100);
  flex-shrink: 0;
  display: block;
}
.evidence-thumb-icon {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--gray-500);
  background: transparent;
}
.evidence-meta {
  display: flex; flex-direction: column;
  min-width: 0; line-height: 1.2;
}
.evidence-name {
  font-size: .8125rem;
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.evidence-size {
  font-size: .6875rem;
  color: var(--text-muted);
  margin-top: .125rem;
}
.evidence-delete {
  background: none; border: 0; cursor: pointer;
  width: 22px; height: 22px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: background .15s, color .15s;
}
.evidence-delete:hover { background: var(--color-danger-strong); color: var(--color-white); }

.evidence-upload-btn {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  font-size: .8125rem;
  font-weight: 500;
  color: var(--primary-text);
  background: transparent;
  border: 1px dashed var(--primary);
  border-radius: 8px;
  padding: .375rem .625rem;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.evidence-upload-btn:hover,
.evidence-upload-btn:focus-within {
  background: var(--primary-light);
  outline: 0;
}
.evidence-upload-btn.is-uploading {
  cursor: wait;
  opacity: .7;
}
.evidence-upload-hint {
  font-size: .6875rem;
  color: var(--text-muted);
}

[data-theme="dark"] .evidence-chip { background: rgba(255,255,255,.04); }
[data-theme="dark"] .evidence-upload-btn:hover,
[data-theme="dark"] .evidence-upload-btn:focus-within { background: color-mix(in srgb, var(--primary) 12%, transparent); }

.answer-widget-locked .evidence-upload-btn,
.answer-widget-locked .evidence-delete { display: none; }

/* Form helper */
.form-row { display: flex; flex-wrap: wrap; gap: .75rem; }
.form-row .form-group { min-width: 120px; }

/* Empty state */
/* .empty-state is declared once, canonically, further down this file (search
   `border: 1px dashed`). Two earlier duplicate declarations lived here and set
   a conflicting padding that the later rule silently overrode. */

/* Alert — duplicate removed; canonical definitions are at the top of this file using CSS variables */

/* ==========================================================================
   Dark mode: assessment view
   ========================================================================== */
/* Status pills need no dark overrides: the .pill-* light rules use the
   --color-*-bg/-text/-border token triads, which carry correct dark values, so
   dark mode is token-derived (consistent with .risk-badge). */

/* assessment progress — handled by CSS variables */

[data-theme="dark"] .question-subcategory-title { color: var(--text-subtle); border-bottom-color: rgba(255,255,255,.08); }
[data-theme="dark"] .question-card { background: var(--bg-surface); border-color: rgba(255,255,255,.08); }
[data-theme="dark"] .question-code { background: color-mix(in srgb, var(--primary) 15%, transparent); color: var(--primary-text); }
[data-theme="dark"] .question-text { color: var(--text-secondary); }

[data-theme="dark"] .question-guidance { border-color: rgba(255,255,255,.08); color: var(--text-muted); }
[data-theme="dark"] .question-guidance[open] summary { border-bottom-color: rgba(255,255,255,.08); }

[data-theme="dark"] .scope-group-label { color: var(--text-subtle); }
[data-theme="dark"] .scope-setup-notice { background: var(--bg-body); border-color: rgba(255,255,255,.08); color: var(--text-subtle); }

[data-theme="dark"] .answer-btn { background: var(--bg-surface); border-color: rgba(255,255,255,.12); color: var(--text-muted); }
[data-theme="dark"] .answer-btn:hover:not(:disabled) { border-color: rgba(255,255,255,.25); color: var(--text-secondary); }
[data-theme="dark"] .answer-btn-yes.active    { background: color-mix(in srgb, var(--color-success) 15%, transparent); border-color: var(--color-success); color: var(--color-success-text); }
[data-theme="dark"] .answer-btn-no.active     { background: color-mix(in srgb, var(--color-danger) 15%, transparent); border-color: var(--color-danger); color: var(--color-danger-text); }
[data-theme="dark"] .answer-btn-partial.active { background: color-mix(in srgb, var(--color-warning) 15%, transparent); border-color: var(--color-warning); color: var(--color-warning-text); }
[data-theme="dark"] .answer-btn-na.active     { background: rgba(255,255,255,.07); border-color: var(--text-muted); color: var(--text-muted); }
[data-theme="dark"] .answer-btn-clear         { color: var(--text-subtle); }
[data-theme="dark"] .answer-btn-clear:hover   { color: var(--color-danger); border-color: var(--color-danger); }

[data-theme="dark"] .alert-error   { background: color-mix(in srgb, var(--color-danger) 15%, transparent); color: var(--color-danger-text); border-color: color-mix(in srgb, var(--color-danger) 30%, transparent); }
[data-theme="dark"] .alert-success { background: color-mix(in srgb, var(--color-success) 12%, transparent); color: var(--color-success-text); border-color: color-mix(in srgb, var(--color-success) 25%, transparent); }
[data-theme="dark"] .alert-warning { background: color-mix(in srgb, var(--color-warning) 10%, transparent); color: var(--color-warning-text); border-color: color-mix(in srgb, var(--color-warning) 30%, transparent); }
[data-theme="dark"] .alert-info    { background: color-mix(in srgb, var(--primary) 12%, transparent); color: var(--primary-text); border-color: color-mix(in srgb, var(--primary) 25%, transparent); }

/* Sidebar group arrow — dark mode uses a lighter stroke color */
[data-theme="dark"] .sidebar-group summary.sidebar-group-label::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M9 18l6-6-6-6'/%3E%3C/svg%3E");
}

/* ==========================================================================
   Risk badges & colors
   ========================================================================== */
/* NOTE: a `.risk-{critical,high,medium,low}` "text colors" block used to live
   here and, being defined after the risk-matrix cells (~2556), silently
   overrode their color — rendering product.php's CRITICAL cell as purple text
   on a red background. It had no standalone consumer, so it was pure harm and
   was removed 2026-07-23. The live definitions are the .risk-cell.risk-* matrix
   rules above; risk *levels* elsewhere use .risk-lvl-* / .sev-* / .risk-badge. */

.risk-badge {
  display: inline-flex; align-items: center;
  padding: .2rem .6rem;
  border-radius: 999px;
  font-size: .75rem; font-weight: 600; letter-spacing: .01em;
  white-space: nowrap;
}
/* Severity triads come straight from the --color-* tokens, which already carry
   correct light AND dark values — so no separate dark-mode block is needed. */
.risk-badge-critical     { background: var(--color-purple-bg);  color: var(--color-purple-text); }
.risk-badge-high         { background: var(--color-danger-bg);  color: var(--color-danger-text); }
.risk-badge-medium       { background: var(--color-warning-bg); color: var(--color-warning-text); }
.risk-badge-low          { background: var(--color-success-bg); color: var(--color-success-text); }
.risk-badge-informational{ background: var(--bg-alt);           color: var(--text-muted); }

/* ==========================================================================
   Answer badges (inline small)
   ========================================================================== */
.answer-badge {
  display: inline-flex; align-items: center;
  padding: .15rem .55rem;
  border-radius: 999px;
  font-size: .75rem; font-weight: 600;
  white-space: nowrap;
}
.answer-badge-yes     { background: color-mix(in srgb, var(--color-success) 12%, var(--color-white)); color: var(--color-success-text); }
.answer-badge-no      { background: color-mix(in srgb, var(--color-danger) 12%, var(--color-white)); color: var(--color-danger-text); }
.answer-badge-partial { background: color-mix(in srgb, var(--color-warning) 10%, var(--color-white)); color: var(--color-warning-text); }
.answer-badge-na      { background: var(--text-primary); color: var(--border-strong); }

[data-theme="dark"] .answer-badge-yes     { background: color-mix(in srgb, var(--color-success) 15%, transparent); color: var(--color-success-text); }
[data-theme="dark"] .answer-badge-no      { background: color-mix(in srgb, var(--color-danger) 15%, transparent); color: var(--color-danger-text); }
[data-theme="dark"] .answer-badge-partial { background: color-mix(in srgb, var(--color-warning) 13%, transparent); color: var(--color-warning-text); }
[data-theme="dark"] .answer-badge-na      { background: rgba(255,255,255,.07); color: var(--text-muted); }

/* Answer dot (used in reports) */
.answer-dot {
  display: inline-block; width: 10px; height: 10px;
  border-radius: 50%; flex-shrink: 0;
}
.answer-dot.answer-yes     { background: var(--color-success); }
.answer-dot.answer-partial { background: var(--color-warning-text); }
.answer-dot.answer-no      { background: var(--color-danger); }
.answer-dot.answer-na      { background: var(--text-muted); }

/* ==========================================================================
   Status pills — the legacy .status-finding-*/.status-rem-* family (raw hex +
   manual dark overrides, with NO rule for the live in_progress/accepted_risk/
   pending_approval states) was retired 2026-07-23: findings + remediation now
   render the canonical `.pill pill-*` via Finding/Remediation::STATUS_PILL.
   ========================================================================== */

/* ==========================================================================
   Stat cards row (findings / remediation dashboards)
   ========================================================================== */
.stats-row {
  display: flex; flex-wrap: wrap; gap: .75rem;
  margin-bottom: 1.5rem;
}
.stat-card {
  flex: 1; min-width: 120px; max-width: 200px;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  padding: .875rem 1rem;
  display: flex; flex-direction: column; gap: .25rem;
}
.stat-card-value {
  font-size: 1.5rem; font-weight: 700; line-height: 1;
  color: var(--text-primary);
}
.stat-card-label {
  font-size: .75rem; color: var(--gray-500); font-weight: 500;
}
.stat-card-danger  .stat-card-value { color: var(--danger); }
.stat-card-warning .stat-card-value { color: var(--warning); }
/* Alias classes used by admin dashboard and physical-safeguards stats */
.stat-value { font-size: 1.5rem; font-weight: 700; color: var(--text-primary); line-height: 1; margin: .125rem 0; }
.stat-sub   { font-size: .75rem; color: var(--text-muted); }

/* ==========================================================================
   Findings list / view
   ========================================================================== */
.finding-detail-grid {
  display: grid;
  grid-template-columns: 1fr 360px;
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 900px) {
  .finding-detail-grid { grid-template-columns: 1fr; }
}

.finding-panel {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 1.25rem 1.5rem;
}
.finding-panel + .finding-panel { margin-top: 1rem; }
.finding-panel-title {
  font-size: .8125rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--gray-500);
  margin-bottom: 1rem;
}

/* ==========================================================================
   Remediation task cards
   ========================================================================== */
.remediation-task-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  padding: .875rem 1rem;
  margin-bottom: .625rem;
}
.remediation-task-card:last-child { margin-bottom: 0; }
.remediation-task-card-header {
  display: flex; align-items: flex-start; gap: .625rem;
  margin-bottom: .375rem;
}
.remediation-task-card-title {
  font-size: .875rem; font-weight: 600; flex: 1;
  color: var(--text-primary);
}
.remediation-task-card-meta {
  font-size: .75rem; color: var(--gray-500);
  display: flex; flex-wrap: wrap; gap: .5rem;
}

/* ==========================================================================
   Evidence file rows
   ========================================================================== */
/* .evidence-file-{row,icon,name,meta} removed 2026-07-23 — orphaned file-list
   design; evidence/index.php uses an id (#evidence-file-name) + utility classes,
   not this component family. */

/* Upload drag-and-drop zone */
.upload-zone {
  border: 2px dashed var(--border-color);
  border-radius: var(--radius-md);
  padding: 2rem 1.5rem;
  text-align: center;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
  color: var(--gray-500);
}
.upload-zone:hover, .upload-zone.drag-over {
  border-color: var(--primary-light);
  background: color-mix(in srgb, var(--primary) 4%, transparent);
}
.upload-zone input[type="file"] { display: none; }
.upload-selected-name {
  font-size: .875rem; font-weight: 500; color: var(--text-primary);
  margin-top: .5rem;
}

/* ==========================================================================
   Report styles
   ========================================================================== */
.report-toolbar {
  position: sticky; top: 0; z-index: 100;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-color);
}

.report-page {
  max-width: 900px; margin: 2rem auto 4rem;
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

.report-cover {
  background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary-dark) 100%);
  color: var(--color-white);
  padding: 3rem 3.5rem;
}
.report-logo {
  display: flex; align-items: center; gap: .75rem;
  margin-bottom: 2.5rem;
  color: var(--color-white);
}
.report-title {
  font-size: clamp(1.5rem, 4vw, 2rem); font-weight: 700;
  margin-bottom: .5rem; letter-spacing: -0.02em;
  color: var(--color-white);
}
.report-subtitle {
  font-size: 1rem; color: rgba(255,255,255,.75);
  margin-bottom: 2rem;
}
.report-meta-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 1rem;
}
.report-meta-item { display: flex; flex-direction: column; gap: .25rem; }
.report-meta-key  { font-size: .75rem; color: rgba(255,255,255,.6); font-weight: 500; text-transform: uppercase; letter-spacing: .05em; }
.report-meta-val  { font-size: .9375rem; font-weight: 600; color: var(--color-white); }

.report-section {
  padding: 2rem 3.5rem;
  border-bottom: 1px solid var(--border-color);
}
.report-section:last-child { border-bottom: none; }
.report-section-title {
  font-size: 1.125rem; font-weight: 700; margin-bottom: 1.25rem;
  color: var(--text-primary);
}

.report-page-break { page-break-before: always; }

/* Score circle */
.report-score-grid {
  display: flex; flex-wrap: wrap; gap: 2rem; align-items: center;
}
.report-score-main { flex-shrink: 0; }
.report-score-circle {
  width: 130px; height: 130px;
  border-radius: 50%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  border: 6px solid currentColor;
}
.score-green  { color: var(--color-success-text); }
.score-yellow { color: var(--color-warning-text); }
.score-red    { color: var(--color-danger); }
/* Nothing applicable was assessed — not a failing grade. A red 0% here would
   read as "you failed" when the truth is "there was nothing in scope to
   score", which is the opposite claim on a report an auditor may read. */
.score-neutral { color: var(--text-muted); }
.score-number { font-size: 2rem; font-weight: 800; line-height: 1; }
.score-label  { font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; margin-top: .2rem; }

.report-answer-breakdown { flex: 1; min-width: 220px; }
.answer-row {
  display: flex; align-items: center; gap: .625rem;
  padding: .375rem 0;
  font-size: .875rem; color: var(--gray-600);
}
.answer-row strong { margin-left: auto; color: var(--text-primary); }

/* Risk summary bar */
.risk-summary-table { display: flex; flex-direction: column; gap: .5rem; }
.risk-summary-row {
  display: flex; align-items: center; gap: .75rem;
}
.risk-summary-bar-wrap {
  flex: 1; height: 8px; background: var(--gray-200);
  border-radius: 999px; overflow: hidden;
}
.risk-summary-bar {
  height: 100%; background: var(--primary-light);
  border-radius: 999px; transition: width .3s ease;
}

/* Detailed findings */
.report-finding {
  background: var(--gray-50);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  margin-bottom: .75rem;
}
.report-finding:last-child { margin-bottom: 0; }
.report-finding-header {
  display: flex; align-items: center; gap: .625rem;
  flex-wrap: wrap;
  margin-bottom: .25rem;
}

/* Q&A table */
.report-qa-table {
  width: 100%; border-collapse: collapse;
  font-size: .875rem; margin-bottom: 1.5rem;
}
.report-qa-table th, .report-qa-table td {
  padding: .5rem .75rem;
  border-bottom: 1px solid var(--border-color);
  text-align: left; vertical-align: top;
}
.report-qa-table th {
  font-size: .75rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
  color: var(--gray-500);
  background: var(--gray-50);
}
.report-footer {
  padding: 1.25rem 3.5rem;
  font-size: .8125rem; color: var(--text-muted);
  text-align: center;
  background: var(--gray-50);
}

/* Report cards (index list) */
.report-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 1.25rem 1.5rem;
  margin-bottom: .75rem;
  display: flex; align-items: center; gap: 1.25rem;
  transition: box-shadow var(--transition), border-color var(--transition);
}
.report-card:hover {
  border-color: var(--primary-light);
  box-shadow: var(--shadow-sm);
}
.report-card-main { flex: 1; min-width: 0; }
.report-card-title {
  font-size: .9375rem; font-weight: 600; color: var(--text-primary);
  margin-bottom: .25rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.report-card-meta { font-size: .8125rem; color: var(--gray-500); display: flex; flex-wrap: wrap; gap: .625rem; }
.report-card-score {
  flex-shrink: 0; width: 52px; height: 52px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: .9375rem; font-weight: 800;
  border: 3px solid currentColor;
}
.report-card-actions { flex-shrink: 0; }
/* Reports-index layout: vertical stack of report cards + each card's header
   row (title block left, action button right). Were referenced but undefined,
   so the cards rendered with no gap and the header didn't lay out. */
.report-list { display: flex; flex-direction: column; gap: 1rem; }
.report-card-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap;
}

/* Per-assessment stat row inside a .report-card (reports index). These were
   referenced by the markup but never defined — the tiles rendered as plain
   stacked spans. Token-driven so they retheme in dark. */
.report-card-stats {
  display: flex; flex-wrap: wrap; gap: 1.5rem;
  margin-top: 1rem; padding-top: 1rem;
  border-top: 1px solid var(--border-color);
}
.report-stat { display: flex; flex-direction: column; gap: .125rem; min-width: 72px; }
.report-stat-value { font-size: 1.25rem; font-weight: 700; line-height: 1.1; color: var(--text-primary); }
.report-stat-label { font-size: .75rem; color: var(--text-muted); font-weight: 500; }

/* ==========================================================================
   Security settings page
   ========================================================================== */
.security-method-row {
  display: flex; align-items: center; gap: 1rem;
  padding: .875rem 1rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  margin-bottom: .5rem;
}
.security-method-row:last-child { margin-bottom: 0; }
/* .security-method-{icon,info,name,detail,actions} removed 2026-07-23 — the
   .security-method-row markup (settings/security.php) styles its contents with
   utility classes, so those child rules were never referenced. The whole
   .session-* / .current-session family (an orphaned session-list design with no
   markup) was removed here too. */

/* Detail list (sidebar metadata) */
/* The BAA document, rendered from the stored snapshot. A <pre> because the
   agreement is stored as plain text with meaningful line breaks and indentation
   (party blocks, signature lines) — reflowing it would misrepresent the document
   the customer is signing. Scrolls in its own box so a long contract cannot make
   the page scroll horizontally. */
.baa-doc {
  max-height: 26rem;
  overflow: auto;
  margin: 0;
  padding: 1rem 1.25rem;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-family: var(--font-mono);
  font-size: .8125rem;
  line-height: 1.7;
  color: var(--text-primary);
  white-space: pre-wrap;   /* wrap long paragraphs, keep intentional breaks */
  overflow-wrap: anywhere; /* never let a long token widen the box */
}
.detail-list { display: flex; flex-direction: column; gap: .625rem; }
.detail-list-row { display: flex; flex-direction: column; gap: .1rem; }
.detail-list-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; color: var(--gray-500); }
.detail-list-value { font-size: .875rem; color: var(--text-primary); }

/* ==========================================================================
   Billing page
   ========================================================================== */
/* Billing-interval selector (three real choices: month to month, an annual term
   paid monthly, an annual term prepaid). This markup shipped with NO styles at
   all, so the three buttons rendered as loose inline buttons rather than one
   control — the segmented rail below is what makes it read as a choice. Kept in
   app.css so the in-app billing page and the public pricing page match. */
.billing-interval-toggle {
  display: inline-flex;
  flex-wrap: wrap;
  gap: .25rem;
  padding: .3rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-alt);
}
.billing-interval-toggle .btn { border-radius: var(--radius-md); white-space: nowrap; }
/* Inactive segments sit ON the rail and must not draw their own border, or the
   control reads as three stacked boxes. The active one is a .btn-primary, which
   already carries white text at AA (--primary is the documented exception to
   rule 10's -strong requirement). */
.billing-interval-toggle .btn-outline { border-color: transparent; background: transparent; }
.billing-interval-toggle .btn-outline:hover { border-color: var(--border); background: var(--bg-surface); }
@media (max-width: 480px) {
  .billing-interval-toggle { width: 100%; flex-direction: column; }
  .billing-interval-toggle .btn { width: 100%; }
}
.billing-plan-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}
.billing-plan-card {
  background: var(--bg-surface);
  border: 2px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 1.5rem;
  display: flex; flex-direction: column; gap: .75rem;
  transition: border-color var(--transition), box-shadow var(--transition);
}
.billing-plan-card.current {
  border-color: var(--primary-light);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 8%, transparent);
}
.billing-plan-name { font-size: 1rem; font-weight: 700; color: var(--text-primary); }
.billing-plan-price { font-size: 1.75rem; font-weight: 800; color: var(--text-primary); line-height: 1; }
/* The AMOUNT and its SUFFIX are two different jobs, so they need two rules.
   `.billing-plan-price span` was unqualified, so it matched BOTH child spans and
   painted the price itself at the suffix's size: measured live, "$999" rendered at
   14.4px/500/grey — pixel-identical to its own "/yr" — while the parent declared
   28px/800 that never reached anything. The headline number on the plan-selection
   screen was therefore not legible AS a price. Both class names are also live JS
   hooks (billing/index.php reads them for the interval toggle), so they must be
   defined here rather than removed. --text-muted rather than the raw --gray-500
   scale value, so the suffix re-themes. */
.billing-plan-amount { font-size: 1.75rem; font-weight: 800; color: var(--text-primary); }
.billing-plan-per    { font-size: .9rem;  font-weight: 500; color: var(--text-muted); }
.billing-plan-features { display: flex; flex-direction: column; gap: .375rem; }
.billing-plan-feature { font-size: .875rem; color: var(--gray-600); display: flex; gap: .5rem; }
.billing-plan-feature::before { content: "✓"; color: var(--success); font-weight: 700; flex-shrink: 0; }

/* ==========================================================================
   Dark mode: new components — most handled by CSS variables now
   ========================================================================== */
[data-theme="dark"] .upload-zone:hover,
[data-theme="dark"] .upload-zone.drag-over { border-color: var(--border-focus); background: color-mix(in srgb, var(--primary) 8%, transparent); }
[data-theme="dark"] .report-card:hover { border-color: var(--border-focus); }
[data-theme="dark"] .billing-plan-card.current { border-color: var(--primary); }
[data-theme="dark"] .risk-summary-bar-wrap { background: var(--border-strong); }
[data-theme="dark"] .answer-row { color: var(--text-muted); }
[data-theme="dark"] .report-answer-breakdown .answer-row strong { color: var(--text-primary); }

/* ==========================================================================
   Settings sub-navigation
   ========================================================================== */
.settings-nav-wrap { margin-bottom: 1.5rem; }
.settings-nav-search {
  display: flex;
  align-items: center;
  gap: .375rem;
  padding: .375rem .625rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  margin-bottom: .75rem;
  max-width: 280px;
  color: var(--text-muted);
}
/* Style-guide rule 07: the inner input clears its own outline (it is borderless
   inside this shell), so the SHELL must carry the focus ring — otherwise
   focusing the settings search left no visible indicator at all. Mirrors the
   .search-inline:focus-within treatment. */
.settings-nav-search:focus-within {
  border-color: var(--primary);
  box-shadow: var(--focus-ring, 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent));
}
.settings-nav-search input {
  flex: 1;
  border: 0;
  outline: 0;
  background: transparent;
  font: inherit;
  color: var(--text-primary);
  font-size: .8125rem;
}
.settings-nav-search input::placeholder { color: var(--text-subtle); }
.settings-subnav {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  border-bottom: 1px solid var(--border-color);
}
.settings-subnav-group {
  display: inline-flex;
  align-items: flex-end;
  gap: .125rem;
  padding-right: .75rem;
  margin-right: .25rem;
  position: relative;
}
.settings-subnav-group:not(:last-child)::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  height: 20px;
  width: 1px;
  background: var(--border-color);
  opacity: .6;
}
.settings-subnav-group-label {
  display: none; /* shown on hover via tooltip / accessible to SR */
  position: absolute;
  bottom: 100%;
  left: 0;
  font-size: .625rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-subtle);
  margin-bottom: .25rem;
}
.settings-subnav-link {
  padding: .5rem .75rem;
  font-size: .8125rem;
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: color .12s, border-color .12s, background .12s;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: .375rem;
}
.settings-subnav-link svg { opacity: .75; flex-shrink: 0; }
.settings-subnav-link:hover { color: var(--text-primary); background: var(--bg-hover); }
.settings-subnav-link:hover svg { opacity: 1; }
.settings-subnav-link.active {
  color: var(--primary-text);
  border-bottom-color: var(--primary);
  font-weight: 600;
}
.settings-subnav-link.active svg { opacity: 1; color: var(--primary-text); }
[data-theme="dark"] .settings-subnav-link:hover { background: rgba(255,255,255,.05); }
[data-theme="dark"] .settings-subnav-group:not(:last-child)::after { background: rgba(255,255,255,.1); }
@media (max-width: 768px) {
  .settings-subnav { overflow-x: auto; flex-wrap: nowrap; padding-bottom: 2px; }
  .settings-subnav-group { padding-right: .5rem; margin-right: .125rem; }
  .settings-subnav-link span { display: none; } /* icon-only on mobile */
  .settings-subnav-link { padding: .5rem .625rem; }
}

/* ---- Notification Bell (styles live in head.php inline <style> to work on every page) --- */

/* ---- Global Search (inline topbar input) ------------------------------- */
.search-wrap-inline { position: relative; }
.search-inline {
  position: relative;
  display: flex;
  align-items: center;
  width: 280px;
  height: 36px;
  background: var(--bg-alt, var(--gray-50));
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 0 .625rem;
  transition: border-color .15s, box-shadow .15s, background .15s;
}
.search-inline:focus-within {
  border-color: var(--primary);
  background: var(--bg-surface);
  box-shadow: var(--focus-ring, 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent));
}
.search-inline-icon {
  flex-shrink: 0;
  color: var(--gray-500);
  margin-right: .5rem;
}
.search-inline-input {
  flex: 1;
  min-width: 0;
  height: 100%;
  background: transparent;
  border: 0;
  outline: 0;
  font: inherit;
  font-size: .875rem;
  color: var(--text-primary);
}
.search-inline-input::placeholder { color: var(--text-muted); }
.search-inline-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
/* P2 #7: ⌘K hint chip inside the search box */
.search-inline-kbd { flex: none; font-size: .6875rem; font-weight: 600; color: var(--text-muted); background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: .05rem .3rem; margin-left: .25rem; }
.search-inline:focus-within .search-inline-kbd { display: none; }
@media (max-width: 768px) { .search-inline-kbd { display: none; } }

.search-inline-results {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 380px;
  max-width: calc(100vw - 2rem);
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: 10px;
  box-shadow: var(--shadow-lg, 0 10px 25px rgba(0,0,0,.12));
  z-index: 250;
}
.search-inline-results[hidden] { display: none; }
.search-inline-empty {
  padding: .875rem 1rem;
  font-size: .875rem;
  color: var(--text-muted);
}

.search-result-item {
  display: flex; align-items: center; gap: .625rem;
  padding: .625rem .875rem; text-decoration: none;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border-color);
  transition: background var(--transition-fast);
}
.search-result-item:last-child { border-bottom: none; }
.search-result-item:hover, .search-result-item.is-active { background: var(--bg-hover); color: var(--text-primary); }
.search-result-type  { font-size: .6875rem; font-weight: 600; text-transform: uppercase; flex-shrink: 0; min-width: 70px; }
.search-result-title { flex: 1; font-size: .875rem; }
.search-result-meta  { font-size: .75rem; color: var(--text-disabled); flex-shrink: 0; }
/* Command-palette rows: a leading icon chip instead of a type badge. */
.search-cmd-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex-shrink: 0;
  border-radius: var(--radius-sm); color: var(--text-muted);
  background: var(--bg-alt);
}
.search-cmd-item:hover .search-cmd-icon,
.search-cmd-item.is-active .search-cmd-icon { color: var(--primary-text); }

/* ---- Comment @mentions + autocomplete ----------------------------------- */
/* (.comment-compose positioning is defined in the comment-thread block below.) */
.comment-mention {
  color: var(--primary-text, var(--primary));
  background: var(--primary-light);
  border-radius: var(--radius-sm);
  padding: 0 .2em; font-weight: 600;
}
.mention-menu {
  position: absolute; left: 0; right: 0; top: calc(100% + 2px); z-index: 20;
  max-height: 220px; overflow-y: auto;
  background: var(--bg-surface); border: 1px solid var(--border-color);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  -webkit-overflow-scrolling: touch; /* momentum scroll on iOS */
}
.mention-menu[hidden] { display: none; }
.mention-option {
  display: flex; align-items: baseline; gap: .5rem; width: 100%;
  padding: .5rem .75rem; text-align: left; background: none; border: 0;
  cursor: pointer; color: var(--text-primary); font: inherit;
  min-height: 2.5rem; /* comfortable touch target */
}
.mention-option:hover, .mention-option.is-active { background: var(--bg-hover); }
/* On short/landscape-phone viewports cap the menu so it can't cover the field. */
@media (max-height: 480px) { .mention-menu { max-height: 40vh; } }
/* Long comment threads scroll within the thread instead of pushing the page. */
.answer-comments-list, .comment-item .comment-body { overflow-wrap: anywhere; }
.mention-option-name { font-weight: 600; font-size: .875rem; }
.mention-option-slug { font-size: .75rem; color: var(--text-muted); }

.srch-type-finding            { color: var(--danger); }
.srch-type-assessment         { color: var(--primary-text); }
.srch-type-remediation        { color: var(--color-success-text); }
.srch-type-business_associate { color: var(--color-purple); }
.srch-type-incident           { color: var(--danger); }
.srch-type-policy             { color: var(--primary-text); }
.srch-type-vendor             { color: var(--color-purple); }
/* Coloured INK takes the -text step (rule 10): the base measured 4.47:1 on the
   results surface in light, below AA, while every sibling in this block already
   used a -text token. Dark's base IS its ink step (9.21:1), so --accent-text
   resolves to the same value there and the override is kept for clarity. */
.srch-type-ephi_system        { color: var(--accent-text); }
[data-theme="dark"] .srch-type-ephi_system { color: var(--accent-text); }
.srch-type-evidence           { color: var(--color-success-text); }
.srch-type-training           { color: var(--color-warning-text); }
[data-theme="dark"] .srch-type-training { color: var(--color-warning-text); }
/* Superadmin-only cross-tenant org result (/api/org-search.php). Coloured TEXT,
   so it takes the -text step per style-guide rule 10 — the base would land
   around 3:1 on the results surface. */
.srch-type-organization       { color: var(--color-purple-text); }
/* Superadmin-only cross-tenant DEAL result, same endpoint. Info rather than purple so
   it reads as distinct from an org row at a glance, and the -text step for the same
   rule-10 reason: coloured INK takes -text, and the base lands near 3:1 here. */
.srch-type-crm_deal           { color: var(--color-info-text); }

/* Narrow viewports: shrink input width so it doesn't crowd the topbar. */
@media (max-width: 720px) {
  .search-inline { width: 200px; }
  .search-inline-results { width: 300px; }
}
@media (max-width: 540px) {
  .search-inline { width: 44px; padding: 0; justify-content: center; }
  .search-inline:focus-within { width: 220px; padding: 0 .625rem; justify-content: flex-start; }
  .search-inline-input { width: 0; padding: 0; }
  .search-inline:focus-within .search-inline-input { width: auto; padding-left: 0; }
  .search-inline-icon { margin-right: 0; }
  .search-inline:focus-within .search-inline-icon { margin-right: .5rem; }
}

/* ==========================================================================
   Animations & Keyframes
   ========================================================================== */
@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes slideDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes slideUp {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.95); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
@keyframes shimmer {
  0%   { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.spinner { animation: spin 600ms linear infinite; }

/* ============================================================
   Marketing motion & wayfinding (driven by initMarketingMotion in app.js)
   ============================================================ */

/* Reveal-on-scroll: hidden until app.js adds .in-view. A [data-reveal-delay]
   step staggers siblings (e.g. cards in a grid). Falls back to fully visible
   under reduced motion or no-JS-observer. */
/* Hidden only when JS is present to un-hide it (.js set by theme.js before
   paint), so no-JS visitors never see blank sections. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 600ms ease, transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--reveal-delay, 0) * 90ms);
  will-change: opacity, transform;
}
.js [data-reveal].in-view { opacity: 1; transform: translateY(0); }

/* Grid/row stagger without per-item markup: cards inside these containers get a
   small incremental delay by position, so a row reveals left-to-right. */
.js .features-grid [data-reveal]:nth-child(3n+2) { transition-delay: 80ms; }
.js .features-grid [data-reveal]:nth-child(3n+3) { transition-delay: 160ms; }
.js .steps [data-reveal]:nth-child(2) { transition-delay: 80ms; }
.js .steps [data-reveal]:nth-child(3) { transition-delay: 160ms; }
.js .steps [data-reveal]:nth-child(4) { transition-delay: 240ms; }
.js .testimonials-grid [data-reveal]:nth-child(2) { transition-delay: 90ms; }
.js .testimonials-grid [data-reveal]:nth-child(3) { transition-delay: 180ms; }
.js .pricing-grid [data-reveal]:nth-child(2) { transition-delay: 90ms; }
.js .pricing-grid [data-reveal]:nth-child(3) { transition-delay: 180ms; }
.js .pricing-grid [data-reveal]:nth-child(4) { transition-delay: 270ms; }
.js .report-features-grid [data-reveal]:nth-child(2) { transition-delay: 80ms; }
.js .report-features-grid [data-reveal]:nth-child(3) { transition-delay: 160ms; }
.js .report-features-grid [data-reveal]:nth-child(4) { transition-delay: 240ms; }

/* Count-up stats keep stable width while the number changes. */
[data-count] { font-variant-numeric: tabular-nums; }

/* Active-section nav link (set by the scroll observer on long pages). */
.nav-link.nav-link-current { color: var(--gray-900); background: var(--gray-100); }
[data-theme="dark"] .nav-link.nav-link-current { color: var(--text-primary); background: rgba(255,255,255,.07); }

/* Feature-card hover: lift (kept) plus a primary border accent and a small
   icon nudge so the grid feels interactive. */
.feature-card { transition: box-shadow var(--transition-md), transform var(--transition-md), border-color var(--transition-md); }
.feature-card:hover { border-color: var(--primary); }
.feature-card .feature-icon { transition: transform var(--transition-md); }
.feature-card:hover .feature-icon { transform: translateY(-2px) scale(1.05); }

/* Logo-strip marquee: a slow, seamless auto-scroll. The track is duplicated in
   markup so the loop has no visible seam; pauses on hover. */
.logo-strip-marquee { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); } /* hex-ok: fixed-art — neutral marquee mask */
.logo-strip-marquee .logo-strip {
  flex-wrap: nowrap;
  width: max-content;
  /* Duration is superadmin-set per strip (Admin → Marketing Content) and arrives
     as --marquee-duration on the element. It is built server-side from a clamped
     INT plus 's' (PlatformSettings::marqueeDuration), so nothing user-typed can
     reach this as anything but digits. 40s is the shipped default and the
     fallback when the property is absent. */
  animation: logoMarquee var(--marquee-duration, 40s) linear infinite;
}
.logo-strip-marquee:hover .logo-strip { animation-play-state: paused; }
@keyframes logoMarquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); } /* one full (duplicated) set */
}

/* Honeypot field for public forms: off-screen (not display:none, which some
   bots skip) and out of the tab order. Real users never see or fill it. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* In-page section nav (product page): sticks just below the 64px site header
   and highlights the section in view (.nav-link-current from the observer). */
/* The sticky section nav on /product and /why-wasatch-assurance.
 *
 * The PAINT is on the inner element, not the sticky wrapper. The wrapper still
 * spans the viewport — it has to, because that is what `position: sticky` pins —
 * but it is transparent, so the white bar is the width of the reading column and
 * the marketing backdrop stays visible in the gutters either side. Painting the
 * wrapper instead drew an edge-to-edge band that cut the page in half and hid the
 * starfield at exactly the scroll position where it is most visible.
 *
 * Consequence to keep: `backdrop-filter` must live on the painted element too, or
 * it blurs the full width behind a bar that only covers the middle. */
.section-nav {
  position: sticky;
  top: 64px;
  z-index: 90;
}
.section-nav-inner {
  display: flex;
  gap: .25rem;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  background: rgba(255,255,255,.92);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--gray-200);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}
.section-nav-inner::-webkit-scrollbar { display: none; }
.section-nav-link {
  flex: 0 0 auto;
  padding: .85rem .9rem;
  font-size: .875rem;
  font-weight: 500;
  color: var(--gray-500);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color var(--transition-base), border-color var(--transition-base);
}
.section-nav-link:hover { color: var(--gray-900); }
.section-nav-link.nav-link-current {
  color: var(--primary-text);
  border-bottom-color: var(--primary);
}
/* Anchor targets clear both the header and the section nav on jump. */
.product-section[id] { scroll-margin-top: 124px; }
/* Must target the same element the light rule paints (.section-nav-inner), or the
 * dark ground goes back on the full-bleed wrapper and the constraint is silently
 * undone in one theme only. */
[data-theme="dark"] .section-nav-inner {
  background: color-mix(in srgb, var(--bg-inverse) 92%, transparent);
  border-bottom-color: rgba(255,255,255,.08);
}
[data-theme="dark"] .section-nav-link { color: var(--text-muted); }
[data-theme="dark"] .section-nav-link:hover { color: var(--text-primary); }
[data-theme="dark"] .section-nav-link.nav-link-current { color: var(--primary-soft); border-bottom-color: var(--primary-soft); }

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
  .feature-card:hover .feature-icon { transform: none; }
  .logo-strip-marquee .logo-strip { animation: none; width: auto; flex-wrap: wrap; }
  .logo-strip-marquee { -webkit-mask-image: none; mask-image: none; }
}

/* Page entrance animations */
.app-content > * {
  animation: fadeInUp 220ms ease both;
}
.app-content > *:nth-child(1) { animation-delay: 0ms; }
.app-content > *:nth-child(2) { animation-delay: 40ms; }
.app-content > *:nth-child(3) { animation-delay: 80ms; }
.app-content > *:nth-child(4) { animation-delay: 120ms; }
.app-content > *:nth-child(5) { animation-delay: 160ms; }
.app-content > *:nth-child(n+6) { animation-delay: 200ms; }
/* Data tables: opacity-only entrance (no translateY). The transform-based
   fadeInUp left a composited ghost of the first row's status badge / Open
   button painting over the header on some browsers. */
.app-content > .data-table-wrap { animation-name: fadeIn; }

/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  .app-content > *, [class*="animate"] {
    animation: none !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   Mobile Responsiveness
   ========================================================================== */
.app-content {
  padding: 1.5rem 2rem;
  max-width: 100%;
}

@media (max-width: 768px) {
  .app-content { padding: 1rem; }

  .app-sidebar {
    position: fixed;
    left: 0; top: 0; bottom: 0;
    z-index: 200;
    transform: translateX(-100%);
    transition: transform var(--transition-md), box-shadow var(--transition-md);
    box-shadow: none;
    width: 240px;
  }
  .app-sidebar.open {
    transform: translateX(0);
    box-shadow: var(--shadow-xl);
  }
  .app-main { margin-left: 0 !important; }
  .sidebar-toggle-btn { display: flex !important; }
  .page-header { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 480px) {
  .page-header .btn { width: 100%; }
  .filter-row { flex-direction: column; }
  .filter-row .filter-select { width: 100%; max-width: 100%; }
  .data-table-wrap { border-radius: var(--radius-md); font-size: .8125rem; }
  .data-table th, .data-table td { padding: .5rem .625rem; }
}

@media (min-width: 769px) {
  .sidebar-toggle-btn { display: none; }
  .app-layout {
    display: grid;
    grid-template-columns: var(--sidebar-width) 1fr;
    min-height: 100vh;
  }
  .app-sidebar {
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
  }
}

/* ==========================================================================
   Sidebar overlay (mobile)
   ========================================================================== */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.4);
  z-index: 199;
  animation: fadeIn 200ms ease;
}
.sidebar-overlay.visible { display: block; }

/* Sidebar nav item aliases (alternate class names for flexibility) */
.sidebar-nav-item {
  display: flex;
  align-items: center;
  gap: .625rem;
  padding: .5rem .75rem;
  border-radius: var(--radius-sm);
  font-size: .875rem;
  font-weight: 500;
  color: var(--sidebar-link-color);
  text-decoration: none;
  transition: background var(--transition), color var(--transition);
  margin: 1px 0;
}
.sidebar-nav-item:hover { background: var(--sidebar-link-hover-bg); color: var(--text-primary); }
.sidebar-nav-item.active { background: var(--sidebar-link-active-bg); color: var(--sidebar-link-active-color); font-weight: 600; }

/* ==========================================================================
   Empty states
   ========================================================================== */
/* .table-empty only — .empty-state's own box (padding, ground, dashed border)
   is declared canonically further down and was overriding the padding here. */
.table-empty {
  text-align: center;
  padding: 3rem 1.5rem;
  color: var(--text-muted);
  font-size: .9375rem;
}

/* ==========================================================================
   Trial banner
   ========================================================================== */
.trial-banner {
  margin: .75rem;
  padding: .875rem;
  background: var(--color-warning-bg);
  border: 1px solid var(--color-warning-border);
  border-radius: var(--radius-md);
  font-size: .8125rem;
  animation: slideDown 300ms ease;
}

/* ==========================================================================
   Pagination
   ========================================================================== */
.pagination {
  display: flex;
  align-items: center;
  gap: .75rem;
  justify-content: center;
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border-color);
  /* Let the whole control wrap to a second line on narrow screens rather than
     letting its items wrap internally — at 390px "Page 2 of 9" otherwise broke
     across three lines and the "Per page" label across two. */
  flex-wrap: wrap;
  row-gap: .5rem;
}
.pagination > * { white-space: nowrap; }

/* ==========================================================================
   Loading bar (injected by JS)
   ========================================================================== */
#loading-bar {
  position: fixed; top: 0; left: 0; height: 2px;
  background: var(--primary); z-index: 9999;
  width: 0; pointer-events: none; opacity: 0;
  transition: width 200ms ease, opacity 300ms ease;
}

/* ==========================================================================
   Assessment DX: filter toolbar, jump-next, lightbox, comments, due date,
   evidence picker, evidence-required badge, activity timeline, bulk N/A
   ========================================================================== */

/* ---- Filter toolbar ----------------------------------------------------- */
.qfilter-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .625rem;
  margin: 1rem 0 .75rem;
  padding: .625rem .75rem;
  background: var(--bg-alt, var(--gray-50));
  border: 1px solid var(--border-color);
  border-radius: 10px;
}
.qfilter-search {
  position: relative;
  flex: 1 1 280px;
  display: flex;
  align-items: center;
  gap: .5rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 0 .625rem;
  height: 34px;
  color: var(--gray-500);
}
.qfilter-search-input {
  flex: 1;
  background: transparent;
  border: 0;
  outline: 0;
  height: 100%;
  font: inherit;
  font-size: .875rem;
  color: var(--text-primary);
}
.qfilter-search:focus-within {
  border-color: var(--primary);
  box-shadow: var(--focus-ring, 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent));
}
/* "My questions" strip + assignment badges (regular-user UX) */
.my-questions-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
  padding: .5rem .875rem;
  margin-bottom: 1rem;
  background: var(--primary-light);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  font-size: .8125rem;
  color: var(--text-secondary);
}
.my-questions-done { color: var(--color-success-text); font-weight: 600; }
.assigned-to-you-badge {
  display: inline-flex;
  align-items: center;
  background: var(--primary-light);
  color: var(--primary-text, var(--primary));
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  font-size: .6875rem;
  font-weight: 600;
  padding: .0625rem .5rem;
  white-space: nowrap;
}
.section-assign { max-width: 220px; font-size: .75rem; }
.answer-slot-flags { display: flex; gap: .375rem; margin-bottom: .375rem; }
.answer-flag-assigned {
  display: inline-flex;
  background: var(--primary-light);
  color: var(--primary-text, var(--primary));
  border-radius: var(--radius-full);
  font-size: .6875rem;
  font-weight: 600;
  padding: .0625rem .5rem;
}
.answer-flag-readonly {
  display: inline-flex;
  background: var(--bg-alt);
  color: var(--text-muted);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  font-size: .6875rem;
  font-weight: 600;
  padding: .0625rem .5rem;
}

.qfilter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
}
.qfilter-chip {
  background: transparent;
  border: 1px solid var(--border-color);
  border-radius: 999px;
  padding: .25rem .625rem;
  font-size: .75rem;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.qfilter-chip:hover { background: var(--bg-hover, var(--gray-100)); }
a.qfilter-chip { text-decoration: none; display: inline-flex; align-items: center; }
.qfilter-chip.is-active {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--color-white);
}
/* Framework filter chips — visually distinct (teal) from the blue state chips. */
.qfilter-fw-chips { align-items: center; margin-top: .375rem; }
.qfilter-fw-label { font-size: .72rem; font-weight: 600; color: var(--text-secondary); margin-right: .125rem; }
/* Rule 10: this paints --color-white, so it takes the -strong step, never the
   base. The base measured 4.47:1 light and 1.92:1 DARK (dark's accent is
   deliberately bright so it reads as text on a dark page), and the "All" chip
   ships is-active in the markup, so the active framework filter on the
   questionnaire was unreadable on page load for every assessment with framework
   links. --accent was the only semantic family with no -strong step. */
.qfilter-fw-chip.is-active,
.qfilter-fw-all.is-active {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--color-white);
}
.qfilter-counts {
  font-size: .75rem;
  color: var(--text-muted);
  margin-left: auto;
}
/* Zero-result filter escape — shown when active filters match no questions. */
.qfilter-empty {
  display: flex; align-items: center; gap: .625rem;
  margin: 1rem 0; padding: 1rem 1.25rem;
  background: var(--bg-alt); border: 1px solid var(--border-color);
  border-radius: var(--radius-md); color: var(--text-secondary); font-size: .875rem;
}
.qfilter-empty[hidden] { display: none; }
.qfilter-empty svg { color: var(--text-muted); flex-shrink: 0; }
.qfilter-empty .btn { margin-left: auto; }

/* ---- Question card filter / highlight states --------------------------- */
.question-card { transition: outline-color .3s, box-shadow .3s; }
.question-card.is-highlighted {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--primary) 8%, transparent);
}

/* ---- Subcategory header (with bulk-NA button) -------------------------- */
.question-subcategory-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.btn-bulk-na {
  background: transparent;
  border: 1px solid var(--border-color);
  border-radius: 6px;
  padding: .25rem .625rem;
  font-size: .75rem;
  color: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s, border-color .15s, color .15s;
}
.btn-bulk-na:hover {
  background: var(--bg-hover, var(--gray-100));
  border-color: var(--primary);
  color: var(--primary-text);
}

/* ---- Evidence-required badge in question header ------------------------ */
.evidence-required-badge {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  font-size: .6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--color-warning-text);
  background: color-mix(in srgb, var(--color-warning) 14%, var(--color-white));
  border-radius: 6px;
  padding: .15rem .4rem;
  margin-left: .5rem;
  white-space: nowrap;
}
[data-theme="dark"] .evidence-required-badge {
  background: color-mix(in srgb, var(--color-warning) 12%, transparent);
  color: var(--color-warning-text);
}

/* ---- Due date input ---------------------------------------------------- */
.answer-due-date {
  font-size: .8125rem;
  padding: .375rem .5rem;
  min-width: 150px;
}

/* ---- Comments section -------------------------------------------------- */
.answer-comments { margin-top: .75rem; }
.answer-comments-details summary {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  font-size: .75rem;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
  list-style: none;
  padding: .25rem 0;
}
.answer-comments-details summary::-webkit-details-marker { display: none; }
.answer-comments-count {
  background: var(--bg-alt, var(--gray-100));
  color: var(--text-muted);
  border-radius: 999px;
  padding: 0 .5rem;
  font-size: .6875rem;
  font-weight: 700;
  min-width: 1.2rem;
  text-align: center;
}
.answer-comments-list {
  margin: .5rem 0;
  display: flex;
  flex-direction: column;
  gap: .5rem;
}
.answer-comments-list:empty { display: none; }
.comment-item {
  background: var(--bg-alt, var(--gray-50));
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: .5rem .625rem;
}
.comment-meta {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  font-size: .75rem;
  color: var(--text-muted);
  margin-bottom: .25rem;
}
.comment-author { font-weight: 600; color: var(--text-primary); }
.comment-time   { color: var(--text-muted); }
.comment-edited { color: var(--text-muted); }
/* Right-aligned action cluster (Edit / delete) shared by all comment surfaces. */
.comment-actions { margin-left: auto; display: inline-flex; align-items: center; gap: .5rem; }
.comment-edit {
  background: transparent; border: 0; cursor: pointer; color: var(--text-muted);
  padding: 0 .25rem; border-radius: 4px;
}
.comment-edit:hover { color: var(--primary-text); background: var(--bg-hover); }
.comment-delete {
  background: transparent; border: 0; cursor: pointer; color: var(--text-muted);
  font-size: 1rem; line-height: 1; padding: 0 .375rem; border-radius: 4px;
}
.comment-delete:hover { background: var(--color-danger-strong); color: var(--color-white); }
.comment-body {
  font-size: .8125rem;
  line-height: 1.5;
  color: var(--text-primary);
  white-space: pre-wrap;
  word-wrap: break-word;
}
/* Compose + inline-edit areas. Relative so the @mention menu anchors to them. */
.comment-compose { position: relative; margin-top: .5rem; }
.comment-edit-area { position: relative; margin-top: .375rem; }
.comment-input { width: 100%; min-height: 2.25rem; font-size: .8125rem; }

/* ---- Activity timeline ------------------------------------------------- */
.answer-activity { margin-top: .375rem; }
.answer-activity-summary {
  font-size: .6875rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-muted);
  cursor: pointer;
  list-style: none;
  padding: .25rem 0;
}
.answer-activity-summary::-webkit-details-marker { display: none; }
.answer-activity-list {
  list-style: none;
  margin: .375rem 0 0;
  padding: 0;
  font-size: .75rem;
  color: var(--text-secondary);
}
.answer-activity-item {
  display: flex;
  gap: .5rem;
  padding: .25rem 0;
  border-top: 1px dashed var(--border-color);
}
.answer-activity-item:first-child { border-top: 0; }
.answer-activity-who { font-weight: 500; color: var(--text-primary); }
.answer-activity-when { margin-left: auto; color: var(--text-muted); }

/* ---- Floating "next unanswered" button --------------------------------- */
.jump-next-btn {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 200;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  background: var(--primary);
  color: var(--color-white);
  border: 0;
  border-radius: 999px;
  padding: .625rem 1.125rem;
  font-size: .875rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 8px 20px color-mix(in srgb, var(--primary) 35%, transparent);
  transition: transform .15s, background .15s;
}
.jump-next-btn:hover { background: var(--primary-hover); transform: translateY(-1px); }
.jump-next-btn.is-done { background: var(--color-success-strong); }
.jump-next-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.18);
  border-radius: 4px;
  padding: 0 .375rem;
  font-size: .75rem;
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-weight: 600;
  min-width: 1.2rem;
}
@media (max-width: 540px) {
  .jump-next-btn { right: 1rem; bottom: 1rem; padding: .5rem .875rem; }
  .jump-next-btn span { display: none; }
}

/* ---- Evidence pick-existing button ------------------------------------- */
.evidence-pick-btn {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  font-size: .8125rem;
  font-weight: 500;
  color: var(--text-secondary);
  background: transparent;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: .375rem .625rem;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.evidence-pick-btn:hover {
  background: var(--bg-hover, var(--gray-100));
  color: var(--primary-text);
  border-color: var(--primary);
}

/* ---- Evidence picker modal -------------------------------------------- */
.evidence-picker-modal {
  position: fixed; inset: 0;
  z-index: 400;
  background: rgba(0,0,0,.55);
  display: flex; align-items: center; justify-content: center;
  padding: 1.5rem;
}
.evidence-picker-modal[hidden] { display: none; }
.evidence-picker-card {
  width: 100%;
  max-width: 540px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0,0,0,.35);
  overflow: hidden;
}
.evidence-picker-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: .875rem 1rem;
  border-bottom: 1px solid var(--border-color);
}
.evidence-picker-close {
  background: transparent;
  border: 0;
  font-size: 1.25rem;
  cursor: pointer;
  color: var(--text-muted);
  padding: 0 .375rem;
  line-height: 1;
}
.evidence-picker-search {
  margin: .75rem 1rem 0;
  padding: .5rem .75rem;
  font-size: .875rem;
}
.evidence-picker-list {
  flex: 1;
  overflow-y: auto;
  padding: .5rem 1rem 1rem;
  display: flex;
  flex-direction: column;
  gap: .25rem;
}
.evidence-picker-item {
  display: flex;
  align-items: center;
  gap: .75rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  padding: .5rem .625rem;
  cursor: pointer;
  text-align: left;
}
.evidence-picker-item:hover {
  background: var(--bg-hover, var(--gray-100));
  border-color: var(--border-color);
}
.evidence-picker-thumb {
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .75rem;
  font-weight: 700;
  color: var(--gray-500);
  background: var(--gray-100);
  border-radius: 5px;
  object-fit: cover;
  flex-shrink: 0;
}
.evidence-picker-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.2;
}
.evidence-picker-name {
  font-size: .875rem; font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.evidence-picker-sub {
  font-size: .75rem;
  color: var(--text-muted);
  margin-top: .125rem;
}
.evidence-picker-empty {
  padding: 2rem 1rem;
  text-align: center;
  font-size: .875rem;
  color: var(--text-muted);
}

/* ---- Evidence lightbox ------------------------------------------------- */
.evidence-lightbox {
  position: fixed; inset: 0;
  z-index: 500;
  background: rgba(0,0,0,.85);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 1.5rem;
}
.evidence-lightbox[hidden] { display: none; }
.evidence-lightbox-body {
  max-width: 92vw;
  max-height: 80vh;
  display: flex; align-items: center; justify-content: center;
}
.evidence-lightbox-body img {
  max-width: 92vw;
  max-height: 80vh;
  border-radius: 8px;
  box-shadow: 0 20px 60px rgba(0,0,0,.5);
  object-fit: contain;
}
.evidence-lightbox-caption {
  margin-top: 1rem;
  color: var(--color-white);
  font-size: .875rem;
  max-width: 92vw;
  text-align: center;
  word-break: break-word;
}
.evidence-lightbox-close {
  position: absolute;
  top: 1rem; right: 1rem;
  background: rgba(255,255,255,.12);
  border: 0;
  border-radius: 50%;
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--color-white);
  cursor: pointer;
}
.evidence-lightbox-close:hover { background: rgba(255,255,255,.22); }
.evidence-thumb[data-lightbox-image] { cursor: zoom-in; }

/* ==========================================================================
   Mobile & tablet polish — touch targets, stacking, viewport-aware widths
   ========================================================================== */
@media (max-width: 768px) {
  /* Tap-target sizing: 36px clears WCAG 2.5.8 AA (24px min) with comfortable
     headroom. Intentionally not 44px (that's AAA) — forcing 44px would crowd
     the dense compliance tables these chips/buttons sit in. */
  .answer-btn,
  .answer-btn-clear,
  .btn-bulk-na,
  .evidence-pick-btn,
  .qfilter-chip,
  .comment-submit,
  .evidence-delete,
  .comment-delete,
  .btn-bare,
  .ai-eval-btn,
  .ai-draft-btn {
    min-height: 36px;
    min-width: 36px;
  }
  /* Icon-only bare buttons need centering once they gain a min box. */
  .btn-bare {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .answer-btn { padding: .5rem .875rem; font-size: .9375rem; }

  /* Answer widget stacks vertically on phones */
  .answer-meta {
    flex-direction: column;
    align-items: stretch;
    gap: .5rem;
  }
  .answer-assign,
  .answer-due-date {
    width: 100%;
    max-width: none;
  }

  /* Filter toolbar collapses to two rows */
  .qfilter-toolbar { flex-direction: column; align-items: stretch; }
  .qfilter-search  { flex: 1 1 auto; height: 38px; }
  .qfilter-chips   { gap: .375rem; }
  .qfilter-chip    { padding: .375rem .75rem; font-size: .8125rem; }
  .qfilter-counts  { margin-left: 0; }

  /* Question card breathing room */
  .question-card { padding: .875rem; }
  .question-header { flex-wrap: wrap; gap: .375rem; }
  .question-code { flex-shrink: 0; }
  .evidence-required-badge { margin-left: 0; }

  /* Subcategory header bulk-NA wraps under the title on phones */
  .question-subcategory-header {
    flex-direction: column;
    align-items: flex-start;
    gap: .375rem;
  }

  /* Evidence chips: don't try to fit two per row at this width */
  .evidence-chip { max-width: 100%; flex: 1 1 100%; }

  /* Lightbox padding */
  .evidence-lightbox { padding: .75rem; }
  .evidence-lightbox-close { top: .5rem; right: .5rem; }

  /* Comments form stacks above button */
  .comment-form { flex-direction: column; align-items: stretch; }
  .comment-submit { align-self: flex-end; }

  /* Reports and settings cards lose horizontal padding to claim more width */
  .settings-card { padding: 1rem; }

  /* Topbar shrinks user name to avatar-only */
  .topbar-user .user-name { display: none; }

  /* Tabs scroll horizontally instead of wrapping awkwardly */
  .tab-list {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    flex-wrap: nowrap;
  }
  .tab-btn { flex-shrink: 0; }

  /* Page header drops the "Back" button below the title */
  .page-header { flex-wrap: wrap; gap: .5rem; }
}

@media (max-width: 480px) {
  /* Smaller phones */
  .question-text { font-size: .9375rem; }
  .answer-btn { font-size: .875rem; padding: .5rem .625rem; }

  /* Evidence chips: full-width single-column list */
  .evidence-list { flex-direction: column; }
  .evidence-chip { max-width: 100%; }
  .evidence-thumb { width: 32px; height: 32px; }

  /* Floating jump-next becomes icon-only */
  .jump-next-btn { padding: .625rem .75rem; }

  /* Smaller modal headers */
  .evidence-picker-card { max-height: 92vh; }
  .evidence-picker-search { margin: .5rem; }
}

/* Touch-device hover state cleanup: many touch devices fire :hover on tap and
   then "stick" — disable hover bg flashes on coarse pointers. */
@media (hover: none) {
  .evidence-chip:hover { border-color: var(--border-color); }
  .qfilter-chip:hover { background: transparent; }
  .btn-bulk-na:hover { background: transparent; color: var(--text-secondary); border-color: var(--border-color); }
  .evidence-pick-btn:hover { background: transparent; color: var(--text-secondary); border-color: var(--border-color); }
}

/* ==========================================================================
   Dark-mode safety net for hardcoded inline backgrounds & text colors.

   The dashboard has many small inline-styled pills (`PHI`, `BAA`, severity
   chips, etc.) that hardcode pastel pairs like var(--color-danger-bg) / var(--color-danger). Those look
   correct in light mode but lose readability or contrast in dark mode.

   Refactoring every one to a class would touch ~50 files. Instead, this
   block uses CSS attribute-substring selectors with !important to override
   just the relevant inline declarations when the page is in dark mode.

   Pattern: keep the SAME accent color so the meaning is preserved, but use a
   subtle translucent background that works on dark surfaces.
   ========================================================================== */
[data-theme="dark"] [style*="background:var(--color-danger-bg)"],
[data-theme="dark"] [style*="background: var(--color-danger-bg)"]   { background: color-mix(in srgb, var(--color-danger) 12%, transparent) !important; }
[data-theme="dark"] [style*="background:#f0fdf4"],
[data-theme="dark"] [style*="background: #f0fdf4"]   { background: color-mix(in srgb, var(--color-success) 10%, transparent) !important; }
[data-theme="dark"] [style*="background:var(--primary-light)"],
[data-theme="dark"] [style*="background: var(--primary-light)"]   { background: color-mix(in srgb, var(--primary) 10%, transparent) !important; }
[data-theme="dark"] [style*="background:var(--color-info-bg)"],
[data-theme="dark"] [style*="background: var(--color-info-bg)"]   { background: color-mix(in srgb, var(--color-info) 12%, transparent) !important; }
[data-theme="dark"] [style*="background:#f1f5f9"],
[data-theme="dark"] [style*="background: #f1f5f9"]   { background: var(--bg-alt) !important; }
[data-theme="dark"] [style*="background:#f8fafc"],
[data-theme="dark"] [style*="background: #f8fafc"]   { background: var(--bg-alt) !important; }
[data-theme="dark"] [style*="background:var(--color-white);"]:not([style*="background:#fff7"]):not([style*="background:#fffb"]):not([style*="background:#fff3"]),
[data-theme="dark"] [style*="background:var(--color-white)"]                                                       { background: var(--bg-surface) !important; }

/* Text color: keep semantic accent visible against dark backgrounds */
[data-theme="dark"] [style*="color:#1046c1"],
[data-theme="dark"] [style*="color: #1046c1"] { color: var(--primary-soft) !important; }
[data-theme="dark"] [style*="color:#1e3a5f"],
[data-theme="dark"] [style*="color: #1e3a5f"] { color: var(--text-secondary) !important; }
[data-theme="dark"] [style*="color:#1e293b"],
[data-theme="dark"] [style*="color: #1e293b"] { color: var(--text-primary) !important; }
[data-theme="dark"] [style*="color:#111827"],
[data-theme="dark"] [style*="color: #111827"] { color: var(--text-primary) !important; }
[data-theme="dark"] [style*="color:#374151"],
[data-theme="dark"] [style*="color: #374151"] { color: var(--text-secondary) !important; }
[data-theme="dark"] [style*="color:#475569"],
[data-theme="dark"] [style*="color: #475569"] { color: var(--text-secondary) !important; }
[data-theme="dark"] [style*="color:#6b7280"],
[data-theme="dark"] [style*="color: #6b7280"] { color: var(--text-muted) !important; }
[data-theme="dark"] [style*="color:var(--color-warning-text)"],
[data-theme="dark"] [style*="color: var(--color-warning-text)"] { color: var(--color-warning-text) !important; }
[data-theme="dark"] [style*="color:var(--color-success-text)"],
[data-theme="dark"] [style*="color: var(--color-success-text)"] { color: var(--color-success-text) !important; }
[data-theme="dark"] [style*="color:var(--color-danger-text)"],
[data-theme="dark"] [style*="color: var(--color-danger-text)"] { color: var(--color-danger-text) !important; }
[data-theme="dark"] [style*="color:#7f1d1d"],
[data-theme="dark"] [style*="color: #7f1d1d"] { color: var(--color-danger-text) !important; }
/* Border colors that lock to light hue */
[data-theme="dark"] [style*="border:1px solid #e5e7eb"],
[data-theme="dark"] [style*="border: 1px solid #e5e7eb"] { border-color: var(--border-color) !important; }

/* ==========================================================================
   Global polish: focus rings, selection, scrollbars, smoothing
   ========================================================================== */

/* Accessible focus ring for every keyboard-reachable control. Browsers'
   default focus rings differ; this is consistent + on-brand. We only paint
   on `:focus-visible` so mouse clicks don't trigger the ring. */
*:focus { outline: none; }
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
details:focus-visible > summary,
[tabindex]:focus-visible,
[role="button"]:focus-visible,
[role="tab"]:focus-visible,
[role="menuitem"]:focus-visible,
[role="link"]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* Existing form inputs use a tinted box-shadow ring — keep theirs. */
input.form-input:focus-visible,
textarea.form-input:focus-visible,
select.filter-select:focus-visible { outline: none; }

/* Text selection — branded but readable */
::selection      { background: color-mix(in srgb, var(--primary) 25%, transparent); color: var(--text-primary); }
::-moz-selection { background: color-mix(in srgb, var(--primary) 25%, transparent); color: var(--text-primary); }

/* Scrollbars — quiet by default, slightly more visible in dark mode */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
*::-webkit-scrollbar              { width: 10px; height: 10px; }
*::-webkit-scrollbar-track        { background: transparent; }
*::-webkit-scrollbar-thumb        {
  background: var(--border-strong);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover  { background: var(--text-muted); background-clip: padding-box; border: 2px solid transparent; }
[data-theme="dark"] *::-webkit-scrollbar-thumb { background: var(--text-disabled); background-clip: padding-box; border: 2px solid transparent; }
[data-theme="dark"] *::-webkit-scrollbar-thumb:hover { background: var(--text-subtle); background-clip: padding-box; border: 2px solid transparent; }

/* Image rendering and font smoothing — keeps thumbnails crisp at retina */
img { image-rendering: -webkit-optimize-contrast; }
body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Disabled state polish — ensure pointer cursor doesn't lie */
button:disabled,
.btn:disabled,
[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: .55;
}

/* `<details>` summary — remove default disclosure triangle on Webkit and
   replace with a subtle chevron so all <details> elements look consistent */
details > summary::-webkit-details-marker { display: none; }
details > summary { list-style: none; }

/* Empty states — uniform muted styling. The ONE .empty-state definition (an
   animation-only duplicate and a separate .empty-state-icon rule were folded in
   here on 2026-09-02; three scattered blocks is how a tweak lands in one and not
   the others). Prefer the empty-state.php partial (.empty-state-card) for new
   pages — see BACKLOG for the hand-rolled sites. */
.empty-state {
  animation: fadeIn 300ms ease;
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--text-muted);
  background: var(--bg-alt);
  border: 1px dashed var(--border-color);
  border-radius: var(--radius-lg);
}
.empty-state svg { opacity: .3; margin-bottom: 1rem; }
.empty-state-icon { font-size: 2.5rem; margin-bottom: .75rem; opacity: .5; }
.empty-state p { font-size: .9375rem; margin: 0 0 1rem; }
.empty-state p:first-child { margin-top: 0; font-size: .9375rem; }
.empty-state p:last-child  { margin-bottom: 0; }
.empty-state .btn { margin-top: 1rem; }

/* Settings card / data card baseline — many pages reinvent this inline */
.settings-card,
.settings-card + .settings-card,

/* Form input/textarea consistency — anchors padding + radius across pages */
.form-input,
.filter-select {
  border-radius: var(--radius-md);
}

/* Tighter button density inside crowded toolbars */
.btn-toolbar { display: inline-flex; gap: .5rem; flex-wrap: wrap; }

/* Smooth color transitions when toggling theme so dark-mode swap doesn't flash */
html, body, .app-body, .app-content,
.app-topbar, .app-sidebar, .settings-card, .question-card,
.btn, .form-input, .filter-select, .badge {
  transition: background-color .25s ease, color .15s ease, border-color .2s ease;
}

/* Suppress those transitions during initial theme load to avoid the FOUC. */

/* Print: respect the user's intent — clean output without dashboard chrome */
@media print {
  .app-sidebar, .app-topbar, .jump-next-btn,
  .qfilter-toolbar, .evidence-upload-btn, .evidence-pick-btn,
  .comment-form, .comment-delete, .evidence-delete {
    display: none !important;
  }
  body, .app-body { background: #fff !important; color: #111 !important; }
  .settings-card, .question-card { box-shadow: none !important; break-inside: avoid; }
}

/* ==========================================================================
   Base refinements: scroll-margin (anchors under sticky topbar), sticky
   table headers, gentler row hovers, page fade-in.
   ========================================================================== */

/* Make #q-... anchor scrolls land just below the 56px sticky topbar */
html { scroll-behavior: smooth; }
.question-card,
[id^="finding-"],
[id^="task-"],
[id^="comment-"],
[id^="ba-"],
[id^="section-"] {
  scroll-margin-top: 72px;
}

/* Table headers: opaque, non-sticky. (A previous position:sticky here did not
   work — the .data-table-wrap uses overflow-x:auto, which silently disables
   vertical sticky and left the first row's status badge / Open button ghosting
   through the header. The lists are short enough not to need a pinned header.) */
.data-table thead th,
.app-content table > thead > tr > th {
  position: static;
  background: var(--bg-alt);
}
[data-theme="dark"] .data-table thead th,
[data-theme="dark"] .app-content table > thead > tr > th { background: var(--bg-surface); }

/* Gentler row hover for data tables */
.data-table tbody tr { transition: background-color .12s; }
.data-table tbody tr:hover { background: var(--bg-hover, var(--bg-alt)); }
.data-table a { color: var(--text-primary); }
.data-table a:hover { color: var(--primary-text); }

/* Page fade-in — pairs with the existing loading bar.
   Suppressed when the user prefers reduced motion. */
@keyframes pageFadeIn {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: translateY(0); }
}
.app-content { animation: pageFadeIn 160ms ease-out both; }
@media (prefers-reduced-motion: reduce) {
  .app-content { animation: none; }
  html { scroll-behavior: auto; }
}

/* ==========================================================================
   Toasts — top-right slide-in for short-lived feedback
   ========================================================================== */
.toast-container {
  position: fixed;
  top: 72px; /* under the 56px topbar + breathing room */
  right: 1rem;
  z-index: 9000;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  pointer-events: none;
  max-width: min(380px, calc(100vw - 2rem));
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: .625rem;
  padding: .75rem .875rem;
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: 0 10px 24px rgba(0,0,0,.12);
  pointer-events: auto;
  font-size: .875rem;
  line-height: 1.4;
  animation: toastSlideIn 160ms ease-out both;
  border-left: 3px solid var(--text-muted);
}
.toast.is-leaving { animation: toastSlideOut 180ms ease-in forwards; }
.toast-icon {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-top: 1px;
}
.toast-body { flex: 1; min-width: 0; word-wrap: break-word; }
.toast-close {
  flex-shrink: 0;
  background: transparent;
  border: 0;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0 .25rem;
  line-height: 1;
  font-size: 1.125rem;
}
.toast-close:hover { color: var(--text-primary); }
.toast.toast-success { border-left-color: var(--color-success); }
.toast.toast-error   { border-left-color: var(--danger); }
.toast.toast-warning { border-left-color: var(--color-warning); }
.toast.toast-info    { border-left-color: var(--primary); }
.toast-success .toast-icon { color: var(--color-success); }
.toast-error   .toast-icon { color: var(--danger); }
.toast-warning .toast-icon { color: var(--color-warning); }
.toast-info    .toast-icon { color: var(--primary-text); }
@keyframes toastSlideIn  { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
@keyframes toastSlideOut { from { opacity: 1; transform: translateX(0);   } to { opacity: 0; transform: translateX(20px); } }

@media (max-width: 480px) {
  .toast-container { left: 1rem; right: 1rem; max-width: none; }
}

/* ==========================================================================
   Tooltips — [data-tip] hover with delay + dark-mode-safe
   ========================================================================== */
[data-tip] { position: relative; }
[data-tip]::before,
[data-tip]::after {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .12s ease, transform .12s ease;
  transition-delay: 0s;
}
[data-tip]::before {
  content: attr(data-tip);
  bottom: calc(100% + 8px);
  left: 50%;
  transform-origin: bottom center;
  transform: translate(-50%, 4px);
  background: var(--text-primary);
  color: var(--bg-surface);
  font-size: .75rem;
  font-weight: 500;
  padding: .375rem .625rem;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  max-width: min(280px, 80vw);
  box-shadow: 0 4px 12px rgba(0,0,0,.18);
  z-index: 50;
}
[data-tip]::after {
  content: "";
  bottom: calc(100% + 2px);
  left: 50%;
  transform: translate(-50%, 4px);
  border: 6px solid transparent;
  border-top-color: var(--text-primary);
  z-index: 50;
}
[data-tip]:hover::before,
[data-tip]:hover::after,
[data-tip]:focus-visible::before,
[data-tip]:focus-visible::after {
  opacity: 1;
  transform: translate(-50%, 0);
  transition-delay: .3s;
}
[data-tip-pos="bottom"]::before { bottom: auto; top: calc(100% + 8px); transform: translate(-50%, -4px); }
[data-tip-pos="bottom"]::after  { bottom: auto; top: calc(100% + 2px); border-top-color: transparent; border-bottom-color: var(--text-primary); transform: translate(-50%, -4px); }
[data-tip-pos="bottom"]:hover::before,
[data-tip-pos="bottom"]:hover::after { transform: translate(-50%, 0); }

/* Inline help marker: a "?" pip next to a label */
.info-tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-left: .25rem;
  font-size: .6875rem;
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--bg-alt);
  border: 1px solid var(--border-color);
  border-radius: 50%;
  cursor: help;
  user-select: none;
  vertical-align: 1px;
}
.info-tip:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.info-tip:hover, .info-tip:focus-visible {
  background: var(--primary-light);
  color: var(--primary-text);
  border-color: color-mix(in srgb, var(--primary) 35%, transparent);
}
[data-theme="dark"] .info-tip:hover, [data-theme="dark"] .info-tip:focus-visible {
  background: color-mix(in srgb, var(--primary) 20%, transparent); color: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 35%, transparent);
}

/* ==========================================================================
   Topbar user-menu dropdown (triggered by clicking the user chip)
   ========================================================================== */
/* ---- Topbar org picker (current-org dropdown; superadmin gets search) ---- */
.topbar-org-menu { position: relative; min-width: 0; }
.topbar-org-trigger {
  background: none;
  border: 0;
  cursor: pointer;
  padding: .375rem .625rem;
  border-radius: var(--radius);
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  color: var(--text-secondary);
  font-size: .875rem;
  font-weight: 500;
  max-width: 100%;
  transition: background .12s ease;
}
.topbar-org-trigger:hover,
.topbar-org-menu.is-open .topbar-org-trigger { background: var(--bg-alt); }
.topbar-org-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 28ch;
  color: var(--text-primary);
}
.topbar-org-role {
  font-size: .6875rem;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--bg-alt);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  padding: .0625rem .5rem;
  white-space: nowrap;
}
.topbar-org-static {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-size: .875rem;
  font-weight: 500;
  color: var(--text-secondary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.topbar-org-dropdown {
  position: absolute;
  top: calc(100% + .5rem);
  left: 0;
  min-width: 300px;
  max-width: calc(100vw - 1rem);
  max-height: min(60vh, 480px);
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px rgba(0,0,0,.18), 0 0 0 1px rgba(0,0,0,.02);
  z-index: 1100;
  animation: userMenuIn 140ms cubic-bezier(.16,.84,.44,1);
}
.topbar-org-dropdown[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .topbar-org-dropdown { animation: none; } }
.topbar-org-search-wrap {
  padding: .625rem;
  border-bottom: 1px solid var(--border-color);
  position: sticky;
  top: 0;
  background: var(--bg-surface);
}
.topbar-org-search { width: 100%; font-size: .875rem; }
.topbar-org-section { padding: .375rem; }
.topbar-org-section + .topbar-org-section { border-top: 1px solid var(--border-color); }
.topbar-org-item { margin: 0; }
.topbar-org-item[hidden] { display: none; }
.topbar-org-item button {
  width: 100%;
  background: none;
  border: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .625rem;
  border-radius: var(--radius);
  font-size: .875rem;
  color: var(--text-primary);
  text-align: left;
}
.topbar-org-item button:hover,
.topbar-org-item button:focus-visible { background: var(--bg-alt); }
.topbar-org-item button[aria-current="true"] { font-weight: 600; }
.topbar-org-item-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.topbar-org-check { color: var(--color-success); font-weight: 700; }
.topbar-org-browse {
  display: block;
  padding: .5rem .625rem;
  border-radius: var(--radius);
  font-size: .8125rem;
  font-weight: 500;
  color: var(--primary-text, var(--primary));
  text-decoration: none;
}
.topbar-org-browse:hover,
.topbar-org-browse:focus-visible { background: var(--bg-alt); }

/* Full-width variant of the context banner, pinned under the topbar */
.context-banner-topbar {
  margin: 0;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  position: sticky;
  top: 56px;
  z-index: 40;
}

.topbar-user-menu { position: relative; }
.topbar-user-menu .topbar-user {
  background: none;
  border: 0;
  cursor: pointer;
  padding: .25rem .5rem .25rem .25rem;
  border-radius: 100px;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  color: var(--text-primary);
  transition: background .12s ease;
}
.topbar-user-menu .topbar-user:hover { background: var(--bg-alt); }
.topbar-user-menu.is-open .topbar-user { background: var(--bg-alt); }
.topbar-user-caret {
  color: var(--text-muted);
  transition: transform .15s ease;
}
.topbar-user-menu.is-open .topbar-user-caret { transform: rotate(180deg); }

.topbar-user-dropdown {
  position: absolute;
  top: calc(100% + .5rem);
  right: 0;
  min-width: 280px;
  max-width: calc(100vw - 1rem);
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px rgba(0,0,0,.18), 0 0 0 1px rgba(0,0,0,.02);
  z-index: 1100;
  overflow: hidden;
  animation: userMenuIn 140ms cubic-bezier(.16,.84,.44,1);
}
.topbar-user-dropdown[hidden] { display: none; }
@keyframes userMenuIn {
  from { opacity: 0; transform: translateY(-4px) scale(.98); transform-origin: top right; }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}
@media (prefers-reduced-motion: reduce) { .topbar-user-dropdown { animation: none; } }

.topbar-user-dropdown-header {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: 1rem;
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border-color);
}
.topbar-user-dropdown-id { min-width: 0; flex: 1; }
.topbar-user-dropdown-name  { font-size: .875rem; font-weight: 600; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar-user-dropdown-email { font-size: .75rem; color: var(--text-muted);  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: .125rem; }
.topbar-user-dropdown-org {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  margin-top: .375rem;
  font-size: .6875rem;
  color: var(--text-muted);
  font-weight: 500;
}
.topbar-user-dropdown-org svg { opacity: .65; }
.topbar-user-dropdown-role {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  color: var(--text-secondary);
  padding: 0 .375rem;
  border-radius: 100px;
  font-size: .625rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-left: .125rem;
}

.topbar-user-dropdown-section {
  padding: .25rem 0;
  border-bottom: 1px solid var(--border-color);
}
.topbar-user-dropdown-section:last-child { border-bottom: 0; }
.topbar-user-dropdown a,
.topbar-user-dropdown button {
  display: flex;
  align-items: center;
  gap: .625rem;
  width: 100%;
  padding: .5rem .875rem;
  background: none;
  border: 0;
  text-align: left;
  font: inherit;
  font-size: .8125rem;
  color: var(--text-secondary);
  cursor: pointer;
  text-decoration: none;
  transition: background .1s ease, color .1s ease;
}
.topbar-user-dropdown a:hover,
.topbar-user-dropdown button:hover,
.topbar-user-dropdown a:focus-visible,
.topbar-user-dropdown button:focus-visible {
  background: var(--bg-alt);
  color: var(--text-primary);
  outline: none;
}
.topbar-user-dropdown svg { opacity: .7; flex-shrink: 0; }
.topbar-user-dropdown a:hover svg,
.topbar-user-dropdown button:hover svg { opacity: 1; }
.topbar-user-dropdown-signout { color: var(--danger) !important; }
.topbar-user-dropdown-signout:hover { background: var(--danger-light) !important; }
[data-theme="dark"] .topbar-user-dropdown-signout:hover { background: color-mix(in srgb, var(--color-danger) 12%, transparent) !important; }
.topbar-user-dropdown-kbd {
  margin-left: auto;
  background: var(--bg-alt);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
  font-size: .6875rem;
  font-family: ui-monospace, SFMono-Regular, monospace;
  color: var(--text-muted);
}
.topbar-user-dropdown-theme-toggle .theme-icon-sun { display: none; }
.topbar-user-dropdown-theme-toggle .theme-icon-moon { display: inline-block; }
[data-theme="dark"] .topbar-user-dropdown-theme-toggle .theme-icon-sun { display: inline-block; }
[data-theme="dark"] .topbar-user-dropdown-theme-toggle .theme-icon-moon { display: none; }

@media (max-width: 480px) {
  .topbar-user-menu .topbar-user .user-name { display: none; }
  .topbar-user-dropdown { right: .5rem; min-width: 240px; }
}

/* ==========================================================================
   Score trend stat strip (dashboard compliance score card)
   ========================================================================== */
.score-stat-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: .75rem;
  padding: .75rem;
  background: var(--bg-alt);
  border-radius: var(--radius-md);
}
.score-stat {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  min-width: 0;
}
.score-stat-label {
  font-size: .6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-muted);
}
.score-stat-value {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.1;
}

/* ==========================================================================
   Dashboard "Needs your attention" cards
   ========================================================================== */
.attention-section { margin: 1rem 0 1.5rem; }
.attention-heading {
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  margin: 0 0 .625rem;
}
.attention-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: .625rem;
}
.attention-card {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .75rem .875rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-left-width: 3px;
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--text-primary);
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
  position: relative;
}
.attention-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0,0,0,.06);
  border-color: var(--primary);
}
.attention-card-icon {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.attention-card-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}
.attention-card-count {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.1;
}
.attention-card-label {
  font-size: .8125rem;
  color: var(--text-secondary);
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
}
.attention-card-arrow {
  color: var(--text-muted);
  opacity: .55;
  transition: transform .12s ease, opacity .12s ease;
}
.attention-card:hover .attention-card-arrow { opacity: 1; transform: translateX(2px); color: var(--primary-text); }

.attention-card-danger {
  border-left-color: var(--danger);
}
.attention-card-danger .attention-card-icon { background: var(--danger-light); color: var(--color-danger-text); }
.attention-card-danger .attention-card-count { color: var(--danger); }

.attention-card-warning {
  border-left-color: var(--color-warning);
}
.attention-card-warning .attention-card-icon { background: var(--color-warning-bg); color: var(--color-warning-text); }
.attention-card-warning .attention-card-count { color: var(--color-warning-text); }

.attention-card-info {
  border-left-color: var(--primary);
}
.attention-card-info .attention-card-icon { background: var(--primary-light); color: var(--primary-text); }
.attention-card-info .attention-card-count { color: var(--primary-text); }

[data-theme="dark"] .attention-card-danger  .attention-card-icon { background: color-mix(in srgb, var(--color-danger) 15%, transparent);  color: var(--color-danger-text); }
[data-theme="dark"] .attention-card-warning .attention-card-icon { background: color-mix(in srgb, var(--color-warning) 15%, transparent); color: var(--color-warning-text); }
[data-theme="dark"] .attention-card-info    .attention-card-icon { background: color-mix(in srgb, var(--primary) 18%, transparent);  color: var(--primary-soft); }
[data-theme="dark"] .attention-card-danger  .attention-card-count { color: var(--color-danger-text); }

[data-theme="dark"] .attention-card-info    .attention-card-count { color: var(--primary-soft); }

.attention-empty {
  display: flex;
  align-items: center;
  gap: .875rem;
  padding: 1rem 1.125rem;
  background: var(--color-success-bg);
  border: 1px solid var(--color-success-border);
  border-radius: var(--radius-md);
  color: var(--color-success-text);
}
.attention-empty p { margin: 0; font-size: .875rem; color: var(--text-secondary); line-height: 1.4; }
[data-theme="dark"] .attention-empty {
  background: color-mix(in srgb, var(--color-success) 8%, transparent);
  border-color: color-mix(in srgb, var(--color-success) 25%, transparent);
  color: var(--color-success-text);
}

/* ==========================================================================
   Compliance deadlines panel — list of upcoming items grouped by category.
   Used by dashboard/index.php after the attention cards.
   ========================================================================== */
.deadlines-card { margin-top: 1rem; padding: 1rem 1.25rem; }
.deadlines-inner {
  border-left: 3px solid var(--color-warning);
  padding-left: .875rem;
}
.deadlines-header {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .875rem;
}
.deadlines-header h3 {
  margin: 0;
  font-size: .9375rem;
  font-weight: 700;
  color: var(--text-primary);
}
.deadlines-header svg { color: var(--color-warning); flex-shrink: 0; }
.deadlines-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}
.deadline-column {
  flex: 1;
  min-width: 220px;
}
.deadline-column-label {
  display: block;
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-muted);
  margin-bottom: .5rem;
}
.deadline-list {
  display: flex;
  flex-direction: column;
  gap: .375rem;
}
.deadline-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .375rem .625rem;
  border-radius: var(--radius-sm);
  background: var(--bg-alt);
  text-decoration: none;
  color: var(--text-primary);
  transition: background .12s, transform .12s;
}
.deadline-row:hover {
  background: var(--bg-hover);
  transform: translateX(2px);
}
.deadline-row-text {
  display: flex;
  align-items: center;
  gap: .375rem;
  min-width: 0;
  flex: 1;
}
.deadline-row-text svg { flex-shrink: 0; }
.deadline-row-text span {
  font-size: .8125rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.deadline-row-badge {
  flex-shrink: 0;
  font-size: .6875rem;
  font-weight: 700;
  color: var(--color-white);
  border-radius: 99px;
  padding: .1rem .45rem;
  white-space: nowrap;
}
.deadline-row-badge-danger  { background: var(--color-danger-strong); }
.deadline-row-badge-warning { background: var(--color-warning-strong); }
.deadline-row-badge-info    { background: var(--color-info-strong); }
@media (max-width: 640px) {
  .deadlines-grid { gap: 1rem; }
  .deadline-column { min-width: 100%; }
}

/* ==========================================================================
   Dark-mode floating-panel shadows — alpha shadows blend into dark bg, so
   bump shadow + add a subtle border-shine for separation.
   ========================================================================== */
[data-theme="dark"] .oh-modal-card,
[data-theme="dark"] .confirm-card,
[data-theme="dark"] .notif-dropdown,
[data-theme="dark"] .topbar-user-dropdown,
[data-theme="dark"] .shortcut-card,
[data-theme="dark"] .evidence-picker-card {
  box-shadow: 0 18px 48px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.04) inset;
}
[data-theme="dark"] .attention-card:hover {
  box-shadow: 0 4px 12px rgba(0,0,0,.4);
}
[data-theme="dark"] .toast {
  box-shadow: 0 10px 24px rgba(0,0,0,.5);
}

/* ==========================================================================
   Attention grid + activity feed — mobile rules
   ========================================================================== */
@media (max-width: 640px) {
  .attention-grid { grid-template-columns: 1fr; gap: .5rem; }
  .attention-card-icon { width: 32px; height: 32px; }
  .activity-item { padding: .5rem 0; font-size: .8125rem; gap: .5rem; }
  .activity-item .avatar-sm { display: none; } /* save space on phone */
}

/* ==========================================================================
   Activity feed — recent events on the dashboard
   ========================================================================== */
.activity-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.activity-item {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .625rem 0;
  border-bottom: 1px solid var(--border-color);
  font-size: .875rem;
}
.activity-item:last-child { border-bottom: 0; }
.activity-item-body { flex: 1; min-width: 0; line-height: 1.4; }
.activity-item-actor { font-weight: 500; color: var(--text-primary); }
.activity-item-event { color: var(--text-secondary); }
.activity-item-event::before { content: " — "; color: var(--text-subtle); }
.activity-item time { font-size: .75rem; color: var(--text-muted); flex-shrink: 0; }

/* CSV import preview summary header */
.import-preview-summary {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  font-size: .875rem;
}

/* ==========================================================================
   Skip-to-main-content link — visible on keyboard focus
   ========================================================================== */
.skip-link {
  position: absolute;
  top: -40px;
  left: 8px;
  z-index: 10000;
  background: var(--primary);
  color: var(--color-white);
  padding: .5rem 1rem;
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-weight: 600;
  font-size: .875rem;
  transition: top .15s ease;
}
.skip-link:focus,
.skip-link:focus-visible {
  top: 8px;
  outline: 2px solid var(--bg-surface);
  outline-offset: 2px;
}

/* ==========================================================================
   Skeleton loaders — shimmer placeholders for AJAX content
   ========================================================================== */
.skeleton {
  display: inline-block;
  background:
    linear-gradient(90deg,
      var(--bg-alt) 0%,
      var(--bg-hover, var(--gray-100)) 50%,
      var(--bg-alt) 100%);
  background-size: 200% 100%;
  animation: skeletonShimmer 1.2s ease-in-out infinite;
  border-radius: var(--radius-sm);
  color: transparent;
  user-select: none;
}
.skeleton-line   { display: block; height: .875em; margin: .375em 0; }
.skeleton-circle { display: inline-block; border-radius: 50%; }
@keyframes skeletonShimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0%   50%; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; background: var(--bg-alt); }
}

/* ==========================================================================
   Shortcut help modal
   ========================================================================== */
.shortcut-modal {
  position: fixed;
  inset: 0;
  z-index: 600;
  background: rgba(0,0,0,.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}
.shortcut-modal[hidden] { display: none; }

/* Subtle always-present affordance so users discover shortcuts exist and can
   see the ones for the current page. Sits out of the way (bottom-left), quiet
   until hover; hidden on touch / narrow screens where shortcuts don't apply. */
.shortcut-hint {
  position: fixed;
  left: .85rem;
  bottom: .85rem;
  z-index: 90;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  color: var(--text-muted);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  opacity: .6;
  transition: opacity var(--transition), color var(--transition), border-color var(--transition);
}
.shortcut-hint:hover,
.shortcut-hint:focus-visible { opacity: 1; color: var(--text-primary); border-color: var(--border-strong, var(--text-muted)); }
.shortcut-hint .shortcut-kbd { border: 0; background: none; box-shadow: none; padding: 0; font-size: .8125rem; }
@media (max-width: 768px), (hover: none) { .shortcut-hint { display: none; } }
@media print { .shortcut-hint { display: none; } }
.shortcut-card {
  width: 100%;
  max-width: 540px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 60px rgba(0,0,0,.35);
  overflow: hidden;
}
.shortcut-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border-color);
}
.shortcut-head h2 { margin: 0; font-size: 1rem; font-weight: 600; }
.shortcut-body {
  flex: 1;
  overflow-y: auto;
  padding: .75rem 1.25rem 1.25rem;
}
.shortcut-section {
  margin-top: 1rem;
}
.shortcut-section:first-child { margin-top: 0; }
.shortcut-section h3 {
  margin: 0 0 .5rem;
  font-size: .6875rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  font-weight: 600;
}
.shortcut-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .375rem 0;
  border-top: 1px dashed var(--border-color);
  font-size: .875rem;
}
.shortcut-row:first-of-type { border-top: 0; }
.shortcut-keys { display: inline-flex; gap: .25rem; }
.shortcut-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-alt);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 0 .375rem;
  min-width: 22px;
  height: 22px;
  font-size: .75rem;
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-weight: 600;
  color: var(--text-secondary);
}

/* ==========================================================================
   Empty-state polish (extends earlier base class with a friendly icon area)
   ========================================================================== */
.empty-state .empty-state-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin: 0 auto 1rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: 50%;
  color: var(--text-muted);
}
.empty-state h3 { margin: 0 0 .375rem; font-size: 1rem; font-weight: 600; color: var(--text-primary); }
.empty-state p  { margin: 0 0 1rem; }

/* ==========================================================================
   Icon normalization — gentle pass that aligns inconsistent SVG strokes
   across pages without touching markup. Targets `<svg fill="none">` icons
   that use `currentColor` strokes (the dominant pattern in this app).
   ========================================================================== */
svg[fill="none"] path[stroke="currentColor"],
svg[fill="none"] circle[stroke="currentColor"],
svg[fill="none"] rect[stroke="currentColor"],
svg[fill="none"] line[stroke="currentColor"],
svg[fill="none"] polyline[stroke="currentColor"],
svg[fill="none"] polygon[stroke="currentColor"] {
  vector-effect: non-scaling-stroke;
}
/* Default icon stroke for the dashboard chrome — only applied when there's
   no inline stroke-width override on the parent element class. */
.btn svg[fill="none"], .sidebar-link svg[fill="none"],
.topbar-right svg[fill="none"], .filter-select + svg[fill="none"] {
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ==========================================================================
   Confirm modal — styled replacement for window.confirm (OHConfirm)
   ========================================================================== */
.confirm-modal {
  position: fixed;
  inset: 0;
  z-index: 9500;
  background: rgba(0,0,0,.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  animation: confirmFadeIn 120ms ease-out;
}
.confirm-modal.is-leaving { animation: confirmFadeOut 160ms ease-in forwards; }
.confirm-card {
  width: 100%;
  max-width: 460px;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 60px rgba(0,0,0,.35);
  padding: 1.25rem 1.25rem 1rem;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: .875rem;
  row-gap: .75rem;
  animation: confirmPopIn 180ms cubic-bezier(.16,.84,.44,1);
}
.confirm-modal.is-leaving .confirm-card { animation: confirmPopOut 140ms ease-in forwards; }
.confirm-icon {
  grid-row: 1;
  grid-column: 1;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.confirm-danger  .confirm-icon { color: var(--color-danger-text);  background: var(--danger-light); }
.confirm-primary .confirm-icon { color: var(--primary-text); background: var(--primary-light); }
[data-theme="dark"] .confirm-danger  .confirm-icon { background: color-mix(in srgb, var(--color-danger) 18%, transparent);  color: var(--color-danger-text); }
[data-theme="dark"] .confirm-primary .confirm-icon { background: color-mix(in srgb, var(--primary) 22%, transparent);  color: var(--primary-soft); }
.confirm-content { grid-row: 1; grid-column: 2; min-width: 0; }
.confirm-title { margin: .125rem 0 .25rem; font-size: 1rem; font-weight: 600; color: var(--text-primary); line-height: 1.3; }
.confirm-body  { margin: 0; font-size: .875rem; color: var(--text-secondary); line-height: 1.5; }
.confirm-typing-label {
  display: block;
  margin: .75rem 0 .25rem;
  font-size: .75rem;
  color: var(--text-muted);
}
.confirm-typing-label strong {
  color: var(--danger);
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-weight: 700;
}
.confirm-typing { width: 100%; font-family: ui-monospace, SFMono-Regular, monospace; }
.confirm-prompt-label {
  display: block;
  margin: .75rem 0 .25rem;
  font-size: .75rem;
  color: var(--text-muted);
}
.confirm-prompt { width: 100%; resize: vertical; }
.confirm-actions {
  grid-row: 2;
  grid-column: 1 / -1;
  display: flex;
  gap: .5rem;
  justify-content: flex-end;
  margin-top: .25rem;
}
@keyframes confirmFadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes confirmFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes confirmPopIn   { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes confirmPopOut  { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(4px) scale(.98); } }
@media (prefers-reduced-motion: reduce) {
  .confirm-modal, .confirm-card { animation: none; }
}

/* ==========================================================================
   Button loading state — toggled by OHBusy.on(btn) / OHBusy.off(btn)
   The original label is preserved; a spinner overlays via ::after.
   ========================================================================== */
.btn[data-busy="1"] {
  position: relative;
  pointer-events: none;
  color: transparent !important;
}
.btn[data-busy="1"] > * { visibility: hidden; }
.btn[data-busy="1"]::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--text-primary);
  animation: btnSpin .6s linear infinite;
}
.btn-primary[data-busy="1"]::after, .btn-primary.is-busy::after,
.btn-danger[data-busy="1"]::after,  .btn-danger.is-busy::after  { color: var(--color-white); }
.btn-outline[data-busy="1"]::after, .btn-outline.is-busy::after,
.btn-ghost[data-busy="1"]::after,   .btn-ghost.is-busy::after   { color: var(--text-primary); }
@keyframes btnSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .btn[data-busy="1"]::after::after { animation: none; }
}

/* ==========================================================================
   Breadcrumbs — rendered by templates/partials/breadcrumb.php
   ========================================================================== */
.breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .125rem;
  margin: 0 0 .75rem;
  padding: 0;
  list-style: none;
  font-size: .8125rem;
  color: var(--text-muted);
}
.breadcrumb li {
  display: inline-flex;
  align-items: center;
  gap: .125rem;
  min-width: 0;
}
.breadcrumb a {
  color: var(--text-muted);
  text-decoration: none;
  border-radius: var(--radius-sm);
  padding: .125rem .375rem;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color .12s ease, background .12s ease;
}
.breadcrumb a:hover { color: var(--text-primary); background: var(--bg-alt); }
[data-theme="dark"] .breadcrumb a:hover { background: rgba(255,255,255,.06); }
.breadcrumb .breadcrumb-sep {
  display: inline-flex;
  color: var(--text-muted);
  opacity: .45;
  padding: 0 .125rem;
}
.breadcrumb [aria-current="page"] {
  color: var(--text-primary);
  font-weight: 500;
  padding: .125rem .375rem;
  max-width: 32ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ==========================================================================
   Sortable data-table headers — enabled by adding `data-sortable` to a
   `.data-table` and `data-sort="key"` to its `<th>` cells.
   ========================================================================== */
.data-table[data-sortable] th[data-sort] {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.data-table[data-sortable] th[data-sort]:hover { color: var(--text-primary); }
.data-table[data-sortable] th[data-sort]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.data-table th .sort-indicator {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-left: .25rem;
  vertical-align: -1px;
  color: var(--text-muted);
  opacity: .35;
  transition: opacity .12s ease, color .12s ease, transform .12s ease;
}
.data-table th[data-sort]:hover .sort-indicator { opacity: .8; }
.data-table th[aria-sort="ascending"]  .sort-indicator,
.data-table th[aria-sort="descending"] .sort-indicator {
  opacity: 1;
  color: var(--primary-text);
}
.data-table th[aria-sort="descending"] .sort-indicator { transform: rotate(180deg); }

/* ==========================================================================
   Utility classes — small, single-purpose; gradually replace inline styles.
   Naming follows: u-{property}-{value}.  Aliases without the `u-` prefix
   exist for the most common cases so markup stays terse.
   ========================================================================== */

/* Text size */
.fs-xs   { font-size: .75rem; }
.fs-sm   { font-size: .8125rem; }
.fs-md   { font-size: .875rem; }
.fs-lg   { font-size: 1rem; }
.fs-xl   { font-size: 1.125rem; }

/* Text weight */
.fw-400  { font-weight: 400; }
.fw-500  { font-weight: 500; }
.fw-600  { font-weight: 600; }
.fw-700  { font-weight: 700; }

/* Text color (semantic — uses tokens, dark-mode safe) */
.text-primary   { color: var(--text-primary); }
.text-secondary { color: var(--text-secondary); }
.text-muted     { color: var(--text-muted); }
.text-subtle    { color: var(--text-subtle); }
/* Rule 10: these are TEXT utilities, so they take the ink. The fills measured
   4.11-4.23:1 on the page ground; the inks clear 6.5:1. */
.text-success   { color: var(--color-success-text); }
.text-warning   { color: var(--color-warning-text); }
/* Was var(--danger) — the raw fill, only 4.32:1 on a raised dark card, which
   failed on real text ("Not set", required-field asterisks). All 295 call sites
   are text, so the carve-out for "icons/accents" did not describe any actual
   usage; .text-danger now takes the ink like its success/warning siblings. */
.text-danger    { color: var(--color-danger-text); }
/* The *-text tokens are the INK steps of each semantic triad — tuned for body copy
   on a surface, where the fill tokens (--danger etc., sized for buttons and
   borders) sit close to the AA line in dark mode. These -ink aliases are kept
   because ~40 call sites name them explicitly; they are now equivalent to the
   plain utilities above. */
.text-danger-ink  { color: var(--color-danger-text); }
.text-success-ink { color: var(--color-success-text); }
.text-warning-ink { color: var(--color-warning-text); }
.text-info        { color: var(--color-info-text); }
.text-orange      { color: var(--color-orange-text); }
.text-brand     { color: var(--primary-text); }
.text-inherit   { color: inherit; }
.muted          { color: var(--text-muted); }

/* Text alignment */
.ta-left   { text-align: left; }
.ta-center { text-align: center; }
.ta-right  { text-align: right; }

/* Truncation */
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nowrap   { white-space: nowrap; }

/* Display */
.d-block        { display: block; }
.d-inline       { display: inline; }
.d-inline-block { display: inline-block; }
.d-none         { display: none; }
.flex           { display: flex; }
.inline-flex    { display: inline-flex; }
.flex-col       { display: flex; flex-direction: column; }
.flex-row       { display: flex; flex-direction: row; }
.flex-wrap      { flex-wrap: wrap; }
.items-center   { align-items: center; }
.items-start    { align-items: flex-start; }
.items-end      { align-items: flex-end; }
.items-baseline { align-items: baseline; }
.justify-start    { justify-content: flex-start; }
.justify-center   { justify-content: center; }
.justify-end      { justify-content: flex-end; }
.justify-between  { justify-content: space-between; }
.flex-1   { flex: 1; }
.flex-grow{ flex-grow: 1; }
.flex-none{ flex: none; }

/* Gap */
.gap-xs { gap: .25rem; }
.gap-sm { gap: .5rem; }
.gap-md { gap: .75rem; }
.gap-lg { gap: 1rem; }
.gap-xl { gap: 1.5rem; }

/* Margin (scale: xs=.25, sm=.5, md=.75, lg=1, xl=1.5, 2xl=2) */
.m-0    { margin: 0; }
.mt-xs  { margin-top: .25rem; }
.mt-sm  { margin-top: .5rem; }
.mt-md  { margin-top: .75rem; }
.mt-lg  { margin-top: 1rem; }
.mt-xl  { margin-top: 1.5rem; }
.mt-2xl { margin-top: 2rem; }
.mb-xs  { margin-bottom: .25rem; }
.mb-sm  { margin-bottom: .5rem; }
.mb-md  { margin-bottom: .75rem; }
.mb-lg  { margin-bottom: 1rem; }
.mb-xl  { margin-bottom: 1.5rem; }
.mb-2xl { margin-bottom: 2rem; }
.ml-auto { margin-left: auto; }
.mr-auto { margin-right: auto; }
.mx-auto { margin-left: auto; margin-right: auto; }

/* Padding (rarely needed inline; provide for one-offs) */
.pt-0  { padding-top: 0; }
.p-0   { padding: 0; }
.p-xs  { padding: .25rem; }
.p-sm  { padding: .5rem; }
.p-md  { padding: .75rem; }
.p-lg  { padding: 1rem; }

/* The "stack" pattern: vertical rhythm via consecutive-child margin */
.stack > * + *      { margin-top: 1rem; }
.stack-sm > * + *   { margin-top: .5rem; }

/* Width helpers */
.w-full      { width: 100%; }

/* Visibility */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* ==========================================================================
   Generic `.badge` — semantic variants. Use this for ad-hoc pills instead of
   handcrafted inline styles. The `.pill`, `.risk-badge`, and `.scope-badge`
   families keep their specific palettes for their dedicated surfaces.
   ========================================================================== */
/* .badge is the NON-status chip (brand / trust / purple / bare). Status chips are
   .pill + variant ONLY — guide rule 05, "no parallel badge family". The six
   .badge-{success,warning,danger,info,muted,gray} status variants were removed
   on 2026-09-02 after 110 sites migrated to .pill; do not reintroduce them. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .15rem .5rem;
  font-size: .75rem;
  font-weight: 600;
  line-height: 1.4;
  border-radius: 100px;
  border: 1px solid transparent;
  background: var(--bg-alt);
  color: var(--text-secondary);
  white-space: nowrap;
}
.badge-brand   { background: var(--primary-light);     color: var(--primary-text);        border-color: color-mix(in srgb, var(--primary) 30%, transparent); }
.badge-purple  { background: var(--color-purple-bg);   color: var(--color-purple-text);   border-color: color-mix(in srgb, var(--color-purple) 30%, transparent); }

/* Dark-mode badge colors come entirely from the token remap (tokens.css) —
   no per-variant overrides needed; badges now match the .pill family exactly. */

/* ==========================================================================
   Filter chips — render active filters as removable chips above a list page.
   Markup:
     <div class="filter-chip-bar" data-filter-chips>
       <span class="filter-chip" data-filter-key="status" data-filter-value="open">
         Status: Open <button type="button" aria-label="Remove filter">×</button>
       </span>
       <button type="button" class="filter-chip-clear">Clear all</button>
     </div>
   The OHFilterChips JS module wires the buttons to remove URL params.
   ========================================================================== */
.filter-chip-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .375rem;
  margin: -.25rem 0 .75rem;
  min-height: 1.75rem;
}
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .25rem .5rem .25rem .625rem;
  font-size: .75rem;
  font-weight: 500;
  background: var(--primary-light);
  color: var(--primary-text);
  border: 1px solid color-mix(in srgb, var(--primary) 25%, transparent);
  border-radius: 100px;
  line-height: 1.3;
}
.filter-chip strong { font-weight: 600; color: var(--text-primary); }
.filter-chip button {
  background: none;
  border: 0;
  padding: 0 .125rem;
  margin-left: .125rem;
  cursor: pointer;
  color: var(--primary-text);
  font-size: 1rem;
  line-height: 1;
  border-radius: 50%;
  opacity: .65;
  transition: opacity .12s ease, background .12s ease;
}
.filter-chip button:hover { opacity: 1; background: color-mix(in srgb, var(--primary) 15%, transparent); }
.filter-chip-clear {
  background: none;
  border: 0;
  font-size: .75rem;
  color: var(--text-muted);
  cursor: pointer;
  padding: .25rem .5rem;
  border-radius: var(--radius-sm);
}
.filter-chip-clear:hover { color: var(--text-primary); background: var(--bg-alt); }
[data-theme="dark"] .filter-chip {
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  color: var(--primary-soft);
  border-color: color-mix(in srgb, var(--primary) 30%, transparent);
}
[data-theme="dark"] .filter-chip strong { color: var(--text-primary); }

/* ==========================================================================
   Bulk action bar — slide-in from bottom when rows are selected.
   Used via OHBulk JS module. Wraps a checkbox table:
     <table data-bulk-select>
       <tbody><tr><td><input type="checkbox" class="bulk-check" value="123"></td></tr></tbody>
     </table>
   ========================================================================== */
.bulk-bar {
  position: fixed;
  bottom: 1.25rem;
  left: 50%;
  transform: translate(-50%, 120%);
  z-index: 8500;
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .625rem .75rem .625rem 1rem;
  background: var(--bg-inverse); /* slate-900 — explicit so it stays dark on both themes */
  color: var(--color-white);
  border-radius: 100px;
  box-shadow: 0 12px 32px rgba(0,0,0,.35), 0 0 0 1px rgba(255,255,255,.08) inset;
  font-size: .8125rem;
  transition: transform .22s cubic-bezier(.16,.84,.44,1), opacity .18s ease;
  opacity: 0;
  pointer-events: none;
  max-width: calc(100vw - 2rem);
}
[data-theme="dark"] .bulk-bar {
  /* background comes from --bg-inverse (raised in dark, so the chip stands out) */
  box-shadow: 0 12px 32px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.06) inset;
}
.bulk-bar.is-visible { transform: translate(-50%, 0); opacity: 1; pointer-events: auto; }
.bulk-bar-count { font-weight: 600; }
.bulk-bar-sep   { width: 1px; height: 16px; background: rgba(255,255,255,.18); }
.bulk-bar .btn {
  background: transparent;
  color: inherit;
  border: 1px solid rgba(255,255,255,.18);
  padding: .25rem .625rem;
  font-size: .75rem;
  font-weight: 500;
}
.bulk-bar .btn:hover { background: rgba(255,255,255,.08); }
.bulk-bar .btn-danger {
  background: var(--color-danger-strong);
  border-color: var(--color-danger-strong);
}
.bulk-bar .btn-danger:hover { background: var(--color-danger-strong); }
.bulk-bar-close {
  background: none;
  border: 0;
  color: rgba(255,255,255,.55);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 .25rem;
}
.bulk-bar-close:hover { color: var(--color-white); }

/* ==========================================================================
   Generic modal primitive (OHModal) — replaces the hand-rolled modals.
   Markup:
     <div class="oh-modal" id="x" hidden>
       <div class="oh-modal-card">
         <header class="oh-modal-head"><h3>Title</h3><button data-oh-modal-close>×</button></header>
         <div class="oh-modal-body">…</div>
         <footer class="oh-modal-foot">…buttons…</footer>
       </div>
     </div>
   ========================================================================== */
.oh-modal {
  position: fixed;
  inset: 0;
  z-index: 8000;
  background: rgba(0,0,0,.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  animation: ohModalFadeIn 140ms ease-out;
}
.oh-modal[hidden] { display: none; }
.oh-modal.is-leaving { animation: ohModalFadeOut 180ms ease-in forwards; }
.oh-modal-card {
  width: 100%;
  max-width: 540px;
  max-height: 85vh;
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 60px rgba(0,0,0,.35);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: ohModalPopIn 200ms cubic-bezier(.16,.84,.44,1);
}
.oh-modal.is-leaving .oh-modal-card { animation: ohModalPopOut 160ms ease-in forwards; }
.oh-modal-card.oh-modal-lg { max-width: 720px; }
.oh-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border-color);
}
.oh-modal-head h3 { margin: 0; font-size: 1rem; font-weight: 600; }
.oh-modal-body { padding: 1.25rem; overflow-y: auto; flex: 1; }
.oh-modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: .5rem;
  padding: .875rem 1.25rem;
  border-top: 1px solid var(--border-color);
  background: var(--bg-alt);
}
.oh-modal-close {
  background: transparent;
  border: 0;
  color: var(--text-muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  padding: .25rem .5rem;
  border-radius: var(--radius-sm);
}
.oh-modal-close:hover { color: var(--text-primary); background: var(--bg-alt); }
@keyframes ohModalFadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes ohModalFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes ohModalPopIn   { from { opacity: 0; transform: translateY(8px) scale(.97); } to { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes ohModalPopOut  { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(4px) scale(.97); } }
@media (prefers-reduced-motion: reduce) {
  .oh-modal, .oh-modal-card { animation: none; }
}

/* ==========================================================================
   Form-field validation — toggled by OHValidate JS module.
   The native :invalid pseudo only kicks in after first interaction (via the
   `is-touched` class we set on blur), so users don't see red errors before
   they've typed anything.
   ========================================================================== */
.form-input.is-touched:invalid,
textarea.form-input.is-touched:invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-danger) 12%, transparent);
}
.form-input.is-touched:valid {
  border-color: color-mix(in srgb, var(--color-success) 60%, var(--border-color));
}
.form-error {
  display: none;
  margin-top: .375rem;
  font-size: .75rem;
  color: var(--danger);
  line-height: 1.4;
}
.form-group.has-error .form-error,
.form-error.is-visible {
  display: block;
}
.form-group.has-error .form-input { border-color: var(--danger); }
/* ==========================================================================
   Avatar — circular initials by default, optionally renders an image.
   Use via the avatar.php partial which picks size + image source.
   ========================================================================== */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bg-alt);
  color: var(--text-secondary);
  font-weight: 600;
  font-size: .75rem;
  letter-spacing: .02em;
  overflow: hidden;
  user-select: none;
  flex-shrink: 0;
  position: relative;
}
.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  position: absolute;
  inset: 0;
}
.avatar-fallback {
  position: absolute;
  inset: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: inherit;
}
.avatar-grav .avatar-fallback { z-index: 1; }
.avatar-grav img { z-index: 2; }
.avatar-sm  { width: 24px; height: 24px; font-size: .625rem; }
.avatar-md  { width: 32px; height: 32px; font-size: .75rem; }
.avatar-lg  { width: 40px; height: 40px; font-size: .875rem; }
.avatar-xl  { width: 56px; height: 56px; font-size: 1.125rem; }
/* Avatar tints — the guide's six-hue set as token mixes (style-guide
   §Steppers & avatars). Fully token-driven: the mixes and the -text tokens
   carry both themes, so no dark overrides are needed. */
/* tint-1 is the DEFAULT avatar tint, so its contrast matters most. In light mode
   --primary-text on the 16% blue tint measured only 4.05:1 — under AA for the
   initials. --primary-dark is the existing darker step and lifts it to 6.04:1.
   Dark mode already passed (6.11:1), so it keeps --primary-text below. */
.avatar-tint-1 { background: color-mix(in srgb, var(--primary) 16%, transparent);       color: var(--primary-dark); }
[data-theme="dark"] .avatar-tint-1 { color: var(--primary-text); }
.avatar-tint-2 { background: color-mix(in srgb, var(--color-purple) 16%, transparent);  color: var(--color-purple); }
.avatar-tint-3 { background: color-mix(in srgb, var(--color-success) 16%, transparent); color: var(--color-success-text); }
.avatar-tint-4 { background: color-mix(in srgb, var(--color-warning) 16%, transparent); color: var(--color-warning-text); }
.avatar-tint-5 { background: color-mix(in srgb, var(--color-danger) 16%, transparent);  color: var(--color-danger-text); }
.avatar-tint-6 { background: color-mix(in srgb, var(--color-info) 16%, transparent);    color: var(--color-info-text); }
.avatar-stack {
  display: inline-flex;
}
.avatar-stack .avatar { box-shadow: 0 0 0 2px var(--bg-surface); }
.avatar-stack .avatar + .avatar { margin-left: -8px; }

/* ==========================================================================
   Empty-state card — used via empty-state.php partial
   ========================================================================== */
.empty-state-card {
  text-align: center;
  padding: 2.5rem 1.5rem;
  background: var(--bg-surface);
  border: 1px dashed var(--border-color);
  border-radius: var(--radius-lg);
}
[data-theme="dark"] .empty-state-card { border-color: rgba(255,255,255,.1); }
.empty-state-card .empty-state-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin: 0 auto .875rem;
  background: var(--bg-alt);
  color: var(--text-muted);
  border-radius: 50%;
}
.empty-state-card h3 { margin: 0 0 .375rem; font-size: 1rem; font-weight: 600; color: var(--text-primary); }
.empty-state-card p  { margin: 0 auto 1rem; max-width: 42ch; color: var(--text-muted); font-size: .875rem; line-height: 1.5; }
.empty-state-card .empty-state-actions { display: inline-flex; gap: .5rem; }

/* ==========================================================================
   Icon-only button polish — subtle ring + press feedback for theme/bell/etc.
   ========================================================================== */
.icon-btn,
.sidebar-toggle-btn,
.theme-toggle,
.notif-bell-btn {
  transition: background .12s ease, transform .08s ease, color .12s ease;
}
.icon-btn:active,
.sidebar-toggle-btn:active,
.theme-toggle:active,
.notif-bell-btn:active {
  transform: scale(.94);
}

/* ==========================================================================
   Notifications drawer — bell button, badge, dropdown panel.
   Mounted into the topbar by head.php JS (looks for `.topbar-right`).
   ========================================================================== */
.notif-bell-wrap { position: relative; display: inline-flex; align-items: center; }
.notif-bell-btn {
  background: none;
  border: 0;
  cursor: pointer;
  padding: .375rem;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.notif-bell-btn:hover { background: var(--bg-alt); color: var(--text-primary); }
.notif-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  /* -strong, not the base: white on dark's bright --danger is 3.71:1. */
  background: var(--color-danger-strong);
  color: var(--color-white);
  border-radius: 50%;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  font-size: .625rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  pointer-events: none;
  box-shadow: 0 0 0 2px var(--bg-surface);
}
.notif-dropdown {
  position: absolute;
  top: calc(100% + .5rem);
  right: 0;
  width: 360px;
  max-width: calc(100vw - 1rem);
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px rgba(0,0,0,.18), 0 0 0 1px rgba(0,0,0,.02);
  z-index: 1050;
  display: none;
  flex-direction: column;
  max-height: 480px;
  overflow: hidden;
  transform-origin: top right;
  animation: notifDropIn 140ms cubic-bezier(.16,.84,.44,1);
}
.notif-dropdown.is-open { display: flex; }
@keyframes notifDropIn {
  from { opacity: 0; transform: translateY(-4px) scale(.98); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .notif-dropdown { animation: none; }
}
.notif-dropdown-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .75rem .875rem;
  border-bottom: 1px solid var(--border-color);
  flex-shrink: 0;
  gap: .5rem;
}
.notif-dropdown-title { font-size: .875rem; font-weight: 600; color: var(--text-primary); flex: 1; }
.notif-mark-all-btn {
  background: none;
  border: 0;
  cursor: pointer;
  font-size: .75rem;
  color: var(--primary-text);
  padding: 0;
  white-space: nowrap;
  font-weight: 500;
}
.notif-mark-all-btn:hover { text-decoration: underline; }
.notif-close-btn {
  background: none;
  border: 0;
  cursor: pointer;
  padding: .25rem;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.notif-close-btn:hover { background: var(--bg-alt); color: var(--text-primary); }
.notif-dropdown-list { overflow-y: auto; flex: 1; }
.notif-item {
  display: block;
  padding: .75rem .875rem;
  border-bottom: 1px solid var(--border-color);
  cursor: pointer;
  transition: background .1s;
  color: var(--text-secondary);
}
.notif-item:hover { background: var(--bg-alt); }
.notif-item.unread {
  background: var(--primary-light);
  border-left: 3px solid var(--primary);
  padding-left: calc(.875rem - 3px);
}
[data-theme="dark"] .notif-item.unread { background: color-mix(in srgb, var(--primary) 12%, transparent); }
.notif-item:last-child { border-bottom: 0; }
.notif-item-title { font-size: .8125rem; font-weight: 500; color: var(--text-primary); margin-bottom: .15rem; line-height: 1.35; }
.notif-item-body  { font-size: .75rem;   color: var(--text-secondary); margin-bottom: .15rem; line-height: 1.4; }
.notif-item-time  { font-size: .6875rem; color: var(--text-muted); }
.notif-empty {
  padding: 2rem 1rem;
  text-align: center;
  font-size: .875rem;
  color: var(--text-subtle);
}
.notif-empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: 0 auto .5rem;
  background: var(--bg-alt);
  color: var(--text-subtle);
  border-radius: 50%;
}
.notif-skeleton {
  padding: .75rem .875rem;
  border-bottom: 1px solid var(--border-color);
}
.notif-skeleton:last-child { border-bottom: 0; }
.notif-dropdown-footer {
  padding: .625rem .875rem;
  border-top: 1px solid var(--border-color);
  text-align: center;
  flex-shrink: 0;
  background: var(--bg-alt);
}
.notif-dropdown-footer a {
  font-size: .8125rem;
  color: var(--primary-text);
  text-decoration: none;
  font-weight: 500;
}
.notif-dropdown-footer a:hover { text-decoration: underline; }

/* ==========================================================================
   Chord nav hint — small badge in the bottom-right corner when `g` is pressed
   ========================================================================== */
.chord-hint {
  position: fixed;
  bottom: 1rem;
  left: 1rem;
  z-index: 9000;
  background: var(--bg-inverse);
  color: var(--color-white);
  padding: .375rem .75rem;
  border-radius: 100px;
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .02em;
  box-shadow: 0 8px 24px rgba(0,0,0,.3);
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition: opacity .12s ease, transform .12s ease;
}
[data-theme="dark"] .chord-hint {
  /* background comes from --bg-inverse (raised in dark) */
  box-shadow: 0 8px 24px rgba(0,0,0,.5);
}
.chord-hint.is-visible { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .chord-hint { transition: none; } }

/* ==========================================================================
   Search dropdown section labels + recent items polish
   ========================================================================== */
.search-section-label {
  display: block;
  padding: .375rem .75rem .25rem;
  font-size: .6875rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  font-weight: 600;
}


/* ==========================================================================
   Page nav transition — fades the main content slightly while a link
   is loading. Pairs with the top loading bar in app.js.
   ========================================================================== */
.is-navigating .app-content {
  opacity: .55;
  pointer-events: none;
  transition: opacity .14s ease;
}
@media (prefers-reduced-motion: reduce) {
  .is-navigating .app-content { opacity: 1; transition: none; }
}

/* ==========================================================================
   Mobile responsive — small-screen rules for the new components.
   ========================================================================== */
@media (max-width: 640px) {
  /* Data tables: horizontal scroll inside the wrap, hint with shadow */
  .data-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    margin-left: -1rem;
    margin-right: -1rem;
    padding: 0 1rem;
  }
  .data-table { font-size: .8125rem; }
  /* Only force the horizontal-scroll min-width when the table is inside a
     scroll wrapper — an unwrapped .data-table must stay within the viewport
     instead of pushing the whole page wide on phones. */
  .data-table-wrap .data-table,
  .scroll-x .data-table { min-width: 600px; }

  /* Filter toolbar: stack vertically, full-width selects */
  .admin-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: .5rem;
  }
  .admin-toolbar .filter-select,
  .admin-toolbar .form-input { width: 100%; max-width: none; }
  .admin-toolbar .btn { width: 100%; }

  /* Modal cards: full-width on small screens */
  .oh-modal { padding: .75rem; align-items: flex-end; }
  .oh-modal-card { max-width: 100%; max-height: 92vh; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
  .oh-modal-head, .oh-modal-body, .oh-modal-foot { padding-left: 1rem; padding-right: 1rem; }
  .confirm-modal { padding: .75rem; align-items: flex-end; }
  .confirm-card { max-width: 100%; }

  /* Bulk bar: full-width, label hidden when actions get tight */
  .bulk-bar {
    left: .5rem;
    right: .5rem;
    transform: translateY(120%);
    max-width: none;
    border-radius: var(--radius-lg);
    flex-wrap: wrap;
  }
  .bulk-bar.is-visible { transform: translateY(0); }

  /* Filter chip bar: tighter gaps */
  .filter-chip-bar { gap: .25rem; }

  /* Chord hint: move up so it's not over thumb area */
  .chord-hint { bottom: 4rem; left: 50%; transform: translate(-50%, 8px); }
  .chord-hint.is-visible { transform: translate(-50%, 0); }

  /* Notification drawer: full-width sheet from the right */
  .notif-dropdown {
    position: fixed;
    top: 56px;
    right: .5rem;
    left: .5rem;
    width: auto;
    max-height: calc(100vh - 72px);
  }

  /* Breadcrumb: hide ellipsis on the leaf to allow wrap */
  .breadcrumb [aria-current="page"] { max-width: none; white-space: normal; }

  /* Stat pills wrap to fill the row on small screens */
  .risk-summary-row .risk-pill,
  .remediation-stats-row .rem-stat-pill { flex: 1 1 calc(50% - .625rem); }

  /* Page header: stack title + actions */
  .page-header { flex-direction: column; align-items: stretch; }
  .page-header > div:last-child { flex-wrap: wrap; }

  /* Form row: stack */
  .form-row { flex-direction: column; gap: 0; }
}
@media (max-width: 480px) {
  .data-table { font-size: .75rem; }
  /* Smaller btn padding when actions are crammed */
  .bulk-bar .btn { padding: .25rem .5rem; font-size: .6875rem; }
}

/* ==========================================================================
   Print — used by reports (board-summary, CAP, certificate, OCR package, etc.)
   Hides app chrome so the content fits A4 / Letter cleanly.
   ========================================================================== */
@media print {
  /* Strip the app shell */
  .app-sidebar,
  .app-topbar,
  .sidebar-toggle-btn,
  .theme-toggle,
  .topbar-right,
  .topbar-user,
  .topbar-alert,
  .notif-bell-wrap,
  .search-wrap,
  .filter-chip-bar,
  .admin-toolbar,
  .bulk-bar,
  .chord-hint,
  .toast-container,
  .breadcrumb,
  .btn,
  .icon-btn,
  .sidebar-overlay,
  #loading-bar,
  .oh-modal,
  .confirm-modal,
  .shortcut-modal,
  .evidence-lightbox,
  .evidence-picker-modal,
  details,
  .skip-link,
  [data-no-print] { display: none !important; }

  /* Reset body / layout for print flow */
  html, body {
    background: #fff !important;
    color: #111 !important;
    font-size: 11pt;
  }
  .app-layout, .app-main {
    display: block !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  .app-content {
    max-width: 100% !important;
    padding: 0 !important;
    margin: 0 !important;
    animation: none !important;
  }
  .page-header { border-bottom: 1px solid #999; padding-bottom: .5rem; margin-bottom: 1rem; }
  .page-header h1 { font-size: 18pt; }
  .settings-card {
    background: #fff !important;
    border: 1px solid #ccc !important;
    box-shadow: none !important;
    page-break-inside: avoid;
  }

  /* Tables: explicit borders, prevent row-splitting */
  .data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 9.5pt;
    page-break-inside: auto;
  }
  .data-table th, .data-table td {
    border: 1px solid #ccc;
    padding: 4pt 6pt;
  }
  .data-table thead { background: #f3f3f3; display: table-header-group; }
  .data-table tbody tr { page-break-inside: avoid; }

  /* Status / risk pills as colorless text */
  .pill, .risk-badge, .badge, .scope-badge {
    background: transparent !important;
    border: 1px solid #999 !important;
    color: #111 !important;
    padding: 1pt 4pt !important;
  }

  /* Anchors: underline + show URL hint for important links */
  a { color: #111; text-decoration: underline; }
  a[href]::after { content: ""; } /* don't pollute with raw URLs by default */
  .data-table a[href]::after { content: ""; }

  /* Avoid breaks inside critical blocks */
  h1, h2, h3, h4 { page-break-after: avoid; }
  table, figure, .empty-state-card { page-break-inside: avoid; }
}


/* ---- Rendered Markdown (blog posts, in-app docs — F22/F23) ---- */
.md-body { color: var(--text-secondary); }
.md-body p { margin: 0 0 1rem; line-height: 1.7; }
.md-body .md-h { color: var(--text-primary); font-weight: 600; margin: 1.5rem 0 .5rem; line-height: 1.3; }
.md-body h2.md-h { font-size: 1.35rem; }
.md-body h3.md-h { font-size: 1.15rem; }
.md-body h4.md-h, .md-body h5.md-h { font-size: 1rem; }
/* Rendered markdown tables. The WRAP scrolls, not the page: a wide table in a
   policy or a blog post must never make the whole document scroll sideways,
   which is the rule the code blocks and diagrams already follow. */
.md-body .md-table-wrap { overflow-x: auto; margin: 0 0 1rem; }
.md-body .md-table { border-collapse: collapse; width: 100%; font-size: .9375rem; }
.md-body .md-table th,
.md-body .md-table td { border: 1px solid var(--border-color); padding: .4rem .6rem; text-align: left; vertical-align: top; }
.md-body .md-table th { background: var(--bg-alt); font-weight: 600; color: var(--text-primary); }
.md-body .md-table th.ta-center, .md-body .md-table td.ta-center { text-align: center; }
.md-body .md-table th.ta-right,  .md-body .md-table td.ta-right  { text-align: right; }
.md-body .md-list { margin: 0 0 1rem 1.25rem; padding: 0; }
.md-body .md-list li { margin: .25rem 0; line-height: 1.6; }
/* Legal outline numbering (1. / 1.1 / 1.1.a) for policy documents.
   Applied at RENDER time from CSS counters, never written into the source, so
   the outline renumbers itself when an item is added, moved or deleted — a
   hand-typed "3." left behind after a deletion is the defect this avoids.
   Opt-in via .md-legal so ordinary markdown lists elsewhere are untouched.
   Verified in dompdf (the PDF export), which supports counters correctly. */
.md-legal ol.md-list { counter-reset: lvl1; list-style: none; padding-left: 0; }
.md-legal ol.md-list > li { counter-increment: lvl1; }
.md-legal ol.md-list > li::before {
    content: counter(lvl1) ". ";
    font-weight: 600;
    color: var(--text-primary);
}
.md-legal ol.md-list ol.md-list { counter-reset: lvl2; }
.md-legal ol.md-list ol.md-list > li { counter-increment: lvl2; }
.md-legal ol.md-list ol.md-list > li::before { content: counter(lvl1) "." counter(lvl2) " "; }
.md-legal ol.md-list ol.md-list ol.md-list { counter-reset: lvl3; }
.md-legal ol.md-list ol.md-list ol.md-list > li { counter-increment: lvl3; }
.md-legal ol.md-list ol.md-list ol.md-list > li::before {
    content: counter(lvl1) "." counter(lvl2) "." counter(lvl3, lower-alpha) " ";
}
.md-body .md-quote { margin: 0 0 1rem; padding: .5rem 1rem; border-left: 3px solid var(--border-strong, var(--border-color)); color: var(--text-muted); background: var(--bg-alt); border-radius: var(--radius-sm); }
.md-body .md-code { margin: 0 0 1rem; padding: .875rem 1rem; background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius); overflow-x: auto; font-size: .8125rem; }
.md-body code { background: var(--bg-alt); padding: .1rem .35rem; border-radius: var(--radius-sm); font-size: .85em; }
.md-body .md-code code { background: none; padding: 0; }
.md-body a { color: var(--primary-text); text-decoration: underline; }
.md-body hr { border: 0; border-top: 1px solid var(--border-color); margin: 1.5rem 0; }
.md-body del { opacity: .7; }
/* A pasted URL or long token must never widen the column and force the whole
   page to scroll sideways on a phone. anywhere (not break-all) so ordinary
   prose still breaks on spaces. min-width:0 lets it shrink inside a flex parent. */
.md-body { overflow-wrap: anywhere; min-width: 0; }
/* Tables and code blocks can't wrap, so they scroll INSIDE their own box. */
.md-body .md-code { max-width: 100%; overflow-x: auto; }
.md-body table { display: block; max-width: 100%; overflow-x: auto; }
/* Author-supplied media in rendered markdown (blog posts). max-width keeps a
   large upload inside the column; height:auto preserves the aspect ratio. */
.md-body .md-img { max-width: 100%; height: auto; border-radius: var(--radius); display: block; }
.md-body .md-figure { margin: 1.5rem 0; }
.md-body .md-caption { margin-top: .5rem; font-size: .8125rem; color: var(--text-muted); text-align: center; }
/* 16:9 responsive embed: aspect-ratio on the wrapper and a fully-stretched
   iframe, so the video scales with the column instead of overflowing on mobile. */
.md-body .md-embed { position: relative; aspect-ratio: 16 / 9; margin: 1.5rem 0; background: var(--bg-alt); border-radius: var(--radius); overflow: hidden; }
.md-body .md-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Blog author byline — photo + name/title. flex-wrap so a long name or title
   drops below the photo on a narrow screen instead of overflowing. */
.blog-byline { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin: 0 0 2rem; }
.blog-byline-photo { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
.blog-byline-text { min-width: 0; }
/* Blog index pager: space-between keeps Older/Newer at the edges with the count
   centred; the empty <span> placeholders hold the ends at page 1 and the last page. */
.blog-pager { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-top: 1rem; }
.bordered-bottom { border-bottom: 1px solid var(--border-color); }

/* Contextual help link in page headers (F23) */
.help-link { display: inline-flex; align-items: center; color: var(--text-muted); margin-left: .4rem; vertical-align: middle; }
.help-link:hover { color: var(--primary-text); }

/* ---- Persistent "Ask Ryland" launcher (topbar button + slide-in panel) ---- */
.topbar-ryland-btn {
    display: inline-flex; align-items: center; gap: .375rem;
    padding: .375rem .625rem; background: transparent;
    border: 1px solid var(--border-color); border-radius: var(--radius-md, 8px);
    color: var(--text-secondary, var(--text-muted)); font-size: .8125rem; font-weight: 500;
    cursor: pointer; transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.topbar-ryland-btn:hover { color: var(--text-primary); border-color: var(--text-muted); }
.topbar-ryland-btn[aria-expanded="true"] { color: var(--primary-text); border-color: var(--primary); }
.topbar-ryland-label { white-space: nowrap; }
@media (max-width: 640px) { .topbar-ryland-label { display: none; } }

.ryland-launcher {
    position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 100vw);
    z-index: 1200; display: flex; flex-direction: column;
    background: var(--bg-surface, var(--bg-body));
    border-left: 1px solid var(--border-color);
    box-shadow: -8px 0 28px rgba(0,0,0,.14);
    transform: translateX(100%); transition: transform .18s ease;
}
.ryland-launcher.is-open { transform: translateX(0); }
@media (prefers-reduced-motion: reduce) { .ryland-launcher { transition: none; } }
.ryland-launcher-head { display: flex; align-items: center; gap: .5rem; padding: .75rem 1rem; border-bottom: 1px solid var(--border-color); }
.ryland-launcher-title { display: inline-flex; align-items: center; gap: .4rem; font-size: .9375rem; }
.ryland-launcher-full { margin-left: auto; font-size: .8125rem; font-weight: 500; color: var(--text-brand, var(--primary)); }
.ryland-launcher-full:hover { text-decoration: underline; }
.ryland-launcher-body { flex: 1 1 auto; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: .75rem; }
.ryland-launcher-empty { display: flex; flex-direction: column; gap: .75rem; }
.ryland-launcher-chips { display: flex; flex-wrap: wrap; gap: .375rem; }
.ryland-launcher-composer { display: flex; gap: .5rem; padding: .75rem 1rem .5rem; border-top: 1px solid var(--border-color); }
.ryland-launcher-composer .form-input { flex: 1 1 auto; }
.ryland-launcher-foot { padding: 0 1rem .75rem; }

/* ---- Ryland (AI chat) ---- */
.ryland-shell { display: flex; flex-direction: column; }
.ryland-transcript {
  display: flex;
  flex-direction: column;
  gap: .75rem;
  min-height: 200px;
  max-height: 52vh;
  overflow-y: auto;
  padding: .25rem;
  margin-bottom: 1rem;
}
.ryland-empty { text-align: center; padding: 2.5rem 1rem; }
.ryland-msg { display: flex; }
.ryland-msg-user { justify-content: flex-end; }
.ryland-msg-ai   { justify-content: flex-start; }
.ryland-bubble {
  max-width: 80%;
  padding: .625rem .875rem;
  border-radius: var(--radius-lg);
  font-size: .9375rem;
  line-height: 1.55;
  white-space: pre-wrap;
}
.ryland-msg-user .ryland-bubble {
  background: var(--primary);
  color: var(--color-white);
  border-bottom-right-radius: var(--radius-sm);
}
.ryland-msg-ai .ryland-bubble {
  background: var(--bg-alt);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-bottom-left-radius: var(--radius-sm);
}
/* A rendered answer is real block markup, so the bubble's pre-wrap (which is
   correct for the user's own message and for plain streaming text) must not
   apply to it — it would double every blank line between blocks. The streaming
   span keeps pre-wrap until the completed answer is rendered. */
.ryland-answer-text.md-body { white-space: normal; display: block; }
.ryland-answer-text.is-streaming { white-space: pre-wrap; }
/* Tighten the shared .md-body rhythm for a chat bubble: article spacing is far
   too airy at this scale, and the first/last block must not add outer padding. */
.ryland-answer-text.md-body > :first-child { margin-top: 0; }
.ryland-answer-text.md-body > :last-child  { margin-bottom: 0; }
.ryland-answer-text.md-body p { margin: 0 0 .5rem; color: inherit; }
.ryland-answer-text.md-body .md-h { margin: .75rem 0 .25rem; font-size: 1rem; color: inherit; }
.ryland-answer-text.md-body .md-list { margin: 0 0 .5rem 1.1rem; }
.ryland-answer-text.md-body .md-list li { margin: .125rem 0; }
.ryland-answer-text.md-body .md-quote { margin: 0 0 .5rem; padding: .25rem .625rem; }
.ryland-answer-text.md-body .md-code { margin: 0 0 .5rem; padding: .5rem .625rem; }
.ryland-answer-text.md-body hr { margin: .75rem 0; }

.ryland-citations {
  margin: .5rem 0 0;
  padding: .5rem 0 0 1rem;
  border-top: 1px solid var(--border-color);
  font-size: .8125rem;
  color: var(--text-muted);
}
.ryland-citations li { margin: .15rem 0; }
.ryland-lowconf { margin: .5rem 0 0; font-size: .8125rem; color: var(--color-warning-text); }
.ryland-suggestions { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1rem; }
.ryland-chip {
  background: transparent;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  padding: .375rem .75rem;
  font-size: .8125rem;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}
.ryland-chip:hover { background: var(--bg-alt); color: var(--text-primary); border-color: var(--border-strong, var(--text-muted)); }
.ryland-composer { display: flex; gap: .5rem; }
.ryland-composer .form-input { flex: 1; }
.ryland-thinking { display: inline-flex; gap: .25rem; align-items: center; }
.ryland-thinking span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-muted);
  animation: sherpaPulse 1.2s infinite ease-in-out;
}
.ryland-thinking span:nth-child(2) { animation-delay: .2s; }
.ryland-thinking span:nth-child(3) { animation-delay: .4s; }
@keyframes sherpaPulse { 0%, 80%, 100% { opacity: .3; } 40% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .ryland-thinking span { animation: none; } }

/* ---- Ryland Daily Brief panel ---- */
.ryland-brief-list { margin: .5rem 0 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: .25rem; }
.ryland-brief-list li { font-size: .9375rem; color: var(--text-secondary); }
.ryland-brief-action {
  display: flex; gap: .625rem; align-items: flex-start;
  margin-top: .875rem; padding-top: .875rem; border-top: 1px solid var(--border-color);
}

/* ---- Ryland Evidence radar panel ---- */
.ryland-radar-subhead {
  margin: .875rem 0 .375rem; font-size: .8125rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted);
}
.ryland-radar-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .375rem; }
.ryland-radar-list li { display: flex; align-items: center; gap: .5rem; font-size: .875rem; }
.ryland-radar-name { color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ryland-radar-due { margin-left: auto; color: var(--text-muted); font-size: .8125rem; flex: none; }

/* ---- Explain this requirement (assessment question) ----
   A collapsible, compact panel: smaller body text than the question itself,
   and a capped scroll height so a long explanation never dominates the page.
   The trigger caret rotates when open (driven by aria-expanded in JS). */
.ai-explain { margin-top: .5rem; }
.ai-explain-caret { transition: transform var(--transition-base); }
[data-ai-explain-btn][aria-expanded="true"] .ai-explain-caret { transform: rotate(180deg); }
.ai-explain-body {
  margin-top: .5rem; padding: .75rem .875rem;
  background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-lg);
  max-height: 22rem; overflow-y: auto;
}
.ai-explain-summary {
  margin: 0 0 .5rem; font-size: .8125rem; line-height: 1.55; color: var(--text-secondary);
  white-space: pre-wrap;
}
/* auto-fit so a single populated section (e.g. only "Good evidence") fills the
   width instead of leaving the second column as dead space; two sections sit
   side-by-side when there's room, then stack under 640px. */
.ai-explain-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; }
@media (max-width: 640px) { .ai-explain-cols { grid-template-columns: 1fr; } }
.ai-explain-subhead {
  margin: 0 0 .25rem; font-size: .6875rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted);
}
.ai-explain-list { margin: 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: .2rem; }
.ai-explain-list li { font-size: .8125rem; color: var(--text-secondary); line-height: 1.5; }

/* ---- Draft answer (suggest-only) ---- */
.ai-draft-btn { /* shares .ai-eval-btn layout */ margin-left: .25rem; }
.ai-draft {
  margin-top: .5rem; padding: .75rem .875rem;
  background: var(--bg-alt); border: 1px dashed var(--border-color); border-radius: var(--radius-lg);
}
.ai-draft-suggested { font-size: .875rem; color: var(--text-secondary); margin-bottom: .375rem; }
.ai-draft-notes { margin: 0 0 .5rem; font-size: .9375rem; line-height: 1.5; color: var(--text-primary); white-space: pre-wrap; }
.ai-draft-caveat { margin: .5rem 0; font-size: .8125rem; color: var(--color-warning-text); }
.ai-draft-suggested-btn { outline: 2px solid var(--primary); outline-offset: 1px; }
.ai-draft-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: .25rem 0 .5rem; }

/* Evidence radar — actionable row deep-links (A4). The row is a flex line:
   badge · name · due (pushed right via its own margin-left:auto) · action. */
.ryland-radar-action { flex: none; margin-left: .5rem; font-size: .8125rem; font-weight: 500; color: var(--text-brand, var(--primary)); white-space: nowrap; }

/* ---- A1 unified question AI panel ---- */
.ai-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: .375rem; }
.ai-toolbar-label { display: inline-flex; align-items: center; gap: .25rem; font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); margin-right: .25rem; }
.ai-toolbar-nudge { background: transparent; border: 0; padding: 0; font-size: .8125rem; font-weight: 500; color: var(--text-brand, var(--primary)); cursor: pointer; }
.ai-toolbar-nudge:hover { text-decoration: underline; }
/* Coverage-unavailable hint: explains the yes/partial + evidence prerequisite
   in the toolbar's place, so the missing "Map to other frameworks" button reads
   as gated, not broken. Muted to sit quietly beside the active tools. */
.ai-cover-locked { display: inline-flex; align-items: center; gap: .25rem; font-size: .75rem; color: var(--text-muted); }
.ai-cover-locked svg { opacity: .6; flex: none; }

/* ---- A2 AI suggestions inbox ---- */
.ai-inbox-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.ai-inbox-item { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem; padding: .75rem .875rem; background: var(--bg-elevated, var(--bg-alt)); border: 1px solid var(--border-color); border-radius: var(--radius-lg); }
.ai-inbox-main { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; min-width: 0; }
.ai-inbox-kind { flex: none; }
.ai-inbox-summary { color: var(--text-primary); font-size: .9375rem; }
.ai-inbox-time { color: var(--text-muted); font-size: .8125rem; }
.ai-inbox-actions { display: flex; gap: .375rem; flex: none; }

/* ---- AI feedback (helpful / not-helpful thumbs on any artifact) ---- */
.ai-feedback { display: inline-flex; align-items: center; gap: .25rem; flex-wrap: wrap; }
.ai-feedback-label { font-size: .8125rem; color: var(--text-muted); margin-right: .125rem; }
.ai-feedback-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.75rem; height: 1.75rem; padding: 0;
    background: transparent; border: 1px solid var(--border-color);
    border-radius: var(--radius-md, 6px); color: var(--text-muted);
    cursor: pointer; transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.ai-feedback-btn:hover { color: var(--text-primary); border-color: var(--text-muted); }
.ai-feedback-up.is-active   { color: var(--color-success-text);      border-color: currentColor; background: var(--color-success-bg); }
.ai-feedback-down.is-active { color: var(--color-danger-text);  border-color: currentColor; background: var(--color-danger-bg); }
.ai-feedback-thanks { font-size: .8125rem; color: var(--text-muted); font-style: italic; }
.ai-feedback-compact .ai-feedback-btn { width: 1.5rem; height: 1.5rem; }
/* Spacing wrapper used where a panel server-renders or JS-injects the widget
   (assessment summary, posture narrative, mock audit, incident triage, evidence
   summary). Empty until populated, so it adds no gap when there's no artifact. */
.ai-summary-feedback:not(:empty) { margin-top: .5rem; }

/* ---- Tier B cohesion ---- */
/* B2: inline "draft remediation" deep-link shown after a verdict when a finding exists */
.ai-eval-remediate { display: inline-block; margin-top: .25rem; font-size: .8125rem; font-weight: 500; color: var(--text-brand, var(--primary)); }
.ai-eval-remediate:hover { text-decoration: underline; }
/* B3: source HIPAA question codes behind a framework's coverage */
.coverage-rollup-sources { color: var(--text-muted); font-size: .8125rem; }

/* ======================================================================== */
/* Tier 2/3 AI expansion features                                           */
/* ======================================================================== */

/* ---- #5 Cross-framework coverage ---- */
.ai-cover { margin-top: .5rem; padding: .75rem .875rem; background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-lg); }
.ai-cover-list { list-style: none; margin: .25rem 0; padding: 0; display: flex; flex-direction: column; gap: .375rem; }
.ai-cover-list li { font-size: .875rem; }
.ai-cover-list label { cursor: pointer; }
.ai-cover-conf { color: var(--text-muted); font-size: .8125rem; margin-left: .25rem; }
.ai-cover-why { display: block; color: var(--text-secondary); font-size: .8125rem; margin-top: .1rem; }
.ai-cover-empty { font-size: .875rem; color: var(--text-muted); margin: .25rem 0; }
.coverage-rollup-list { list-style: none; margin: .25rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .375rem; }
.coverage-rollup-list li { display: flex; align-items: center; gap: .5rem; font-size: .9375rem; }
.coverage-rollup-count { font-weight: 600; color: var(--text-primary); }
.coverage-rollup-meta { color: var(--text-muted); font-size: .8125rem; }

/* Framework coverage (bank reach) — stacked progress: a lighter "assessable"
   track sits behind the solid "answered" fill in the same bar. */
.coverage-bank-row { display: block; }
.coverage-bank-row .assessment-progress-bar { position: relative; height: 8px; }
.coverage-bank-row .assessment-progress-fill { position: absolute; inset: 0 auto 0 0; }
.coverage-mapped-track {
  background: var(--border-strong, var(--border-color)) !important;
  opacity: .55;
}

/* ---- #6 Policy drafting ---- */
.policy-draft-preview { margin-top: 1rem; padding: 1rem; background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-lg); max-height: 50vh; overflow-y: auto; }
.policy-draft-sections .policy-draft-heading { margin: .875rem 0 .25rem; font-size: .9375rem; font-weight: 600; color: var(--text-primary); }
.policy-draft-sections .policy-draft-body { margin: 0 0 .5rem; font-size: .875rem; line-height: 1.55; color: var(--text-secondary); white-space: pre-wrap; }

/* ---- #7 Incident triage ---- */
.triage-deadlines { display: flex; flex-wrap: wrap; gap: .75rem; margin: .5rem 0 .75rem; }
.triage-deadline { flex: 1 1 12rem; padding: .625rem .75rem; background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-md); }
.triage-deadline-label { display: block; font-size: .75rem; text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted); }
.triage-deadline-date { display: block; font-size: 1.0625rem; font-weight: 700; color: var(--text-primary); }
.triage-deadline-cite { display: block; font-size: .75rem; color: var(--text-muted); }
.triage-rec { margin: .5rem 0; }
.triage-factor-list { display: grid; grid-template-columns: max-content 1fr; gap: .25rem .75rem; margin: .5rem 0 0; font-size: .875rem; }
.triage-factor-list dt { font-weight: 600; color: var(--text-muted); }
.triage-factor-list dd { margin: 0; color: var(--text-primary); }
.triage-rationale { margin: .5rem 0 0; font-size: .875rem; color: var(--text-secondary); font-style: italic; }

/* ---- #10 Posture narrative ---- */
.posture-narrative-headline { margin: 0 0 .5rem; font-size: 1rem; font-weight: 600; color: var(--text-primary); }
.posture-narrative-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
@media (max-width: 720px) { .posture-narrative-cols { grid-template-columns: 1fr; } }

/* ---- #11 Mock OCR interview ---- */
.mock-audit-list { display: flex; flex-direction: column; gap: .75rem; margin-top: .75rem; }
.mock-audit-empty { font-size: .9375rem; color: var(--text-muted); margin: .75rem 0; }
.mock-q { padding: .75rem .875rem; background: var(--bg-alt); border: 1px solid var(--border-color); border-radius: var(--radius-lg); }
.mock-q-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .25rem; }
.mock-q-area { font-size: .8125rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
.mock-q-prompt { margin: .25rem 0; font-size: .9375rem; font-weight: 600; color: var(--text-primary); }
.mock-q-why { margin: .15rem 0; font-size: .8125rem; color: var(--text-secondary); }
.mock-q-missing { margin: .35rem 0 0; font-size: .8125rem; color: var(--text-secondary); }
.mock-q-action { display: inline-block; margin-top: .4rem; font-size: .8125rem; font-weight: 500; color: var(--text-brand, var(--primary)); }
.mock-q-action:hover { text-decoration: underline; }

/* ---- shared small affordances ---- */
.ai-link-code { background: transparent; border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: .1rem .4rem; font-size: .8125rem; color: var(--text-brand, var(--primary)); cursor: pointer; }
.ai-link-code:hover { background: var(--bg-alt); }
.ai-link-code.is-linked { color: var(--color-success-text, var(--text-muted)); border-color: var(--color-success-text, var(--border-color)); cursor: default; }
.packet-narrative-opt { font-size: .8125rem; color: var(--text-secondary); display: inline-flex; align-items: center; gap: .25rem; }

/* ---- Data-driven color helpers ----
   Consume the --accent / --fill custom property set by the [data-accent] /
   [data-fill] initializer in app.js, so a genuinely runtime color (risk
   severity, heatmap alpha, user-chosen classification) needs no inline
   style="". Placed last so these utilities win over component color rules
   (e.g. .rem-stat-count) by source order when both apply. */
.accent-text   { color: var(--accent, currentColor); }
.accent-edge-3 { border-left: 3px solid var(--accent, var(--border-color)); }
.fill-bg       { background: var(--fill, transparent); }

/* Data-classification badge tints (data-flows PHI asset table) — fixed enum,
   so a class per classification rather than an inline style. bg ≈ 10% / border
   ≈ 20% of the base color, matching the prior `<color>1a` / `<color>33` form. */
.class-badge-phi            { background: color-mix(in srgb, var(--color-danger) 10%, transparent); color: var(--color-danger); border: 1px solid color-mix(in srgb, var(--color-danger) 20%, transparent); }
.class-badge-de_identified  { background: color-mix(in srgb, var(--color-warning) 10%, transparent); color: var(--color-warning-text); border: 1px solid color-mix(in srgb, var(--color-warning) 20%, transparent); }
.class-badge-administrative { background: color-mix(in srgb, var(--primary) 10%, transparent); color: var(--primary-text); border: 1px solid color-mix(in srgb, var(--primary) 20%, transparent); }
.class-badge-public         { background: color-mix(in srgb, var(--color-success) 10%, transparent); color: var(--color-success-text); border: 1px solid color-mix(in srgb, var(--color-success) 20%, transparent); }

/* Breach-response task cards — left-edge by state, title, per-type badge tint.
   Fixed palettes → classes, not inline styles. */
.breach-task-card { border-left: 4px solid var(--border-color); padding-left: 1.25rem; }
.breach-task-done    { border-left-color: var(--color-success); }
.breach-task-overdue { border-left-color: var(--color-warning); }
.breach-task-open    { border-left-color: var(--primary); }
.breach-task-title { font-size: .9375rem; }
.breach-task-title.is-done { text-decoration: line-through; color: var(--text-subtle); }
.breach-badge { font-size: .75rem; padding: .15rem .5rem; border-radius: 99px; font-weight: 600; }
.breach-badge-classify      { background: color-mix(in srgb, var(--color-purple) 13%, transparent); color: var(--color-purple); border: 1px solid color-mix(in srgb, var(--color-purple) 27%, transparent); }
.breach-badge-assess_phi    { background: color-mix(in srgb, var(--color-warning) 13%, transparent); color: var(--color-warning-text); border: 1px solid color-mix(in srgb, var(--color-warning) 27%, transparent); }
.breach-badge-notify        { background: color-mix(in srgb, var(--color-info) 13%, transparent); color: var(--color-info); border: 1px solid color-mix(in srgb, var(--color-info) 27%, transparent); }
.breach-badge-legal_review  { background: color-mix(in srgb, var(--color-purple) 13%, transparent); color: var(--color-purple); border: 1px solid color-mix(in srgb, var(--color-purple) 27%, transparent); }
.breach-badge-default       { background: color-mix(in srgb, var(--gray-500) 13%, transparent); color: var(--gray-500); border: 1px solid color-mix(in srgb, var(--gray-500) 27%, transparent); }

/* Vendor view — risk dot + score-history mini bars (color via [data-fill]/
   [data-accent], height via [data-bar-height]). */
.risk-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.vendor-score-num { font-size: .6rem; font-weight: 700; }
.vendor-score-bar { width: 100%; border-radius: 3px 3px 0 0; }

/* PHI-asset type/status badges (settings/assets.php) — fixed enums → classes. */
.asset-badge { font-size: .75rem; }
.asset-badge-blue   { background: color-mix(in srgb, var(--primary) 14%, var(--color-white)); color: var(--primary-dark); }
.asset-badge-violet { background: color-mix(in srgb, var(--color-purple) 14%, var(--color-white)); color: var(--color-purple); }
.asset-badge-amber  { background: color-mix(in srgb, var(--color-orange) 25%, var(--color-white)); color: var(--color-warning-text); }
.asset-badge-teal   { background: color-mix(in srgb, var(--accent) 14%, var(--color-white)); color: var(--color-success-text); }
.asset-badge-green  { background: color-mix(in srgb, var(--color-success) 12%, var(--color-white)); color: var(--color-success-text); }
.asset-badge-red    { background: color-mix(in srgb, var(--color-danger) 12%, var(--color-white)); color: var(--color-danger-text); }
.asset-badge-gray   { background: var(--gray-100); color: var(--gray-600); }
[data-theme="dark"] .asset-badge-blue   { background: color-mix(in srgb, var(--primary) 20%, transparent); color: var(--primary-soft); }
[data-theme="dark"] .asset-badge-violet { background: color-mix(in srgb, var(--color-purple) 20%, transparent); color: var(--color-purple-text); }
[data-theme="dark"] .asset-badge-amber  { background: color-mix(in srgb, var(--color-warning) 20%, transparent); }
[data-theme="dark"] .asset-badge-teal   { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent-text); }
[data-theme="dark"] .asset-badge-green  { background: color-mix(in srgb, var(--color-success) 20%, transparent); color: color-mix(in srgb, var(--color-success) 40%, var(--color-white)); }
[data-theme="dark"] .asset-badge-red    { background: color-mix(in srgb, var(--color-danger) 20%, transparent); }

/* ---- Markdown-lite bodies -----------------------------------
   Containers for markdownLite() output (policy statements and
   descriptions). The renderer only ever emits p/br/strong/em/a/
   ul/ol/li. Re-enable list markers (the global reset strips
   them) and match the rhythm of the old nl2br rendering. */
.md-body p { margin: 0 0 .5rem; color: inherit; line-height: 1.6; }
.md-body ul, .md-body ol { margin: 0 0 .5rem; padding-left: 1.25rem; }
.md-body ul { list-style: disc; }
.md-body ol { list-style: decimal; }
.md-body li { margin-bottom: .125rem; }
.md-body a { text-decoration: underline; }
.md-body > :last-child { margin-bottom: 0; }

/* Markdown editor toolbar + server-rendered preview panel */
.md-toolbar { display: flex; align-items: center; gap: .25rem; flex-wrap: wrap; margin-bottom: .375rem; }
/* The toolbar stays reachable while a long document scrolls: sticky just under
   the app topbar (56px, itself sticky at z-index 50), painted on the surface so
   the text does not show through. Below the topbar's z-index so a dropdown from
   the topbar still lays over it. */
/* Scoped to the FULL-FIELD editors. A policy STATEMENT row is a `.frame`, which
   sets `overflow: hidden` for its radius clipping — and an ancestor with
   overflow:hidden becomes the sticky containing block, so sticky there scrolls
   away with the row instead of pinning (measured: top 1860px after a 1400px
   scroll, both themes). It also paints `--bg-alt-surface`, not `--bg-surface`,
   so a toolbar band there read as a lighter stripe. Both defects disappear by
   not making a row-level toolbar sticky at all. Do NOT "fix" this by dropping
   .frame's overflow — that is load-bearing for the border radius. */
.app-body [data-md-editor]:not([data-statement-row]) > .md-toolbar { position: sticky; top: 60px; z-index: 5; background: var(--bg-surface); padding: .25rem 0; }
/* Upload / draft status and the word count sit in the toolbar's live region. */
.md-status, .md-wordcount { white-space: nowrap; }
.md-wordcount { margin-left: auto; }
.md-wordcount + .md-status, .md-charcount + .md-status { margin-left: .5rem; }
/* Drop target for an image file (blog editor only). */
.md-drop-over textarea { outline: 2px dashed var(--primary); outline-offset: 2px; }
/* Draft-recovery bar above the editor. Wraps on narrow viewports so the two
   actions never overflow the field they sit on. */
.md-draft-bar { display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; margin-bottom: .5rem; }
/* Remaining-characters hint, pushed to the end of the toolbar. Empty until the
   author is near the limit, so it reserves no space in the common case. */
.md-charcount { white-space: nowrap; }
/* Editor help line. VISIBLE, not sr-only: the Tab-to-indent behaviour surprises
   sighted authors too, and hiding an instruction from the people who need it
   least does not help the people who need it most. */
.md-editor-help { margin: .25rem 0 0; }
/* Side-by-side editing. The toolbar and the draft bar stay full width above the
   pair, so only the textarea and the preview split. Grid rather than flex so the
   two panes are exactly equal regardless of content height. */
.md-split { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; align-items: start; }
.md-split > .md-toolbar,
.md-split > .md-draft-bar { grid-column: 1 / -1; }
.md-split > .md-preview {
  margin: 0;
  max-height: 60vh;
  overflow-y: auto;
  padding: .625rem .75rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}
/* Below this width two panes are each too narrow to write or read in, so split
   collapses to the stacked layout on its own — the mode button never offers a
   view the viewport cannot actually show. */
@media (max-width: 900px) {
  .md-split { grid-template-columns: 1fr; }
}
.md-toolbar-btn { padding: .125rem .5rem; font-size: .75rem; line-height: 1.4; }
.md-toolbar-preview { margin-left: auto; }
.md-preview { border: 1px dashed var(--border-color); border-radius: var(--radius-md); padding: .75rem; background: var(--bg-surface); }

/* Policy version diff (textDiffHtml output: div.diff-block > span/del/ins) */
.diff-block { white-space: pre-wrap; font-size: .8125rem; line-height: 1.55; border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: .75rem; background: var(--bg-surface); max-height: 480px; overflow-y: auto; }
.diff-block .diff-ctx { display: block; color: var(--text-secondary); }
/* Rule 10: coloured TEXT takes the `-text` step, not the base fill. These lines
   used the base as ink and measured 3.87:1 in light mode (sub-AA) BEFORE the
   word-level marks were added — a pre-existing violation the new highlight made
   visible by making it worse. The tint stays the base, which is the fill half of
   the same rule. */
.diff-block .diff-del { display: block; text-decoration: line-through; color: var(--color-danger-text); background: color-mix(in srgb, var(--danger) 10%, transparent); }
.diff-block .diff-ins { display: block; text-decoration: none; color: var(--color-success-text); background: color-mix(in srgb, var(--success) 10%, transparent); }
/* WORD-level highlight inside a changed line. <mark> carries a bright-yellow UA
   default that ignores both themes and clashes with the red/green line tint, so
   it is overridden rather than inherited. A deeper mix of the SAME hue as the
   surrounding line keeps one visual language: the line says what changed, the
   mark says where. Inherits `color` so the ink stays the line's own token and
   the contrast already established for it still applies. */
.diff-block .diff-del mark,
.diff-block .diff-ins mark { color: inherit; border-radius: 2px; padding: 0 1px; }
.diff-block .diff-del mark { background: color-mix(in srgb, var(--danger) 16%, transparent); }
.diff-block .diff-ins mark { background: color-mix(in srgb, var(--success) 16%, transparent); }

/* ---- Data-visualization primitives ------------------------------------------
   Marks reference the --viz-* tokens so the light/dark swap happens in tokens.css
   and needs no per-component dark rules (style-guide rule 02). Text inside a chart
   wears INK tokens, never the series color — a colored label reads as data-weight
   ink that isn't data. */
.viz-root { width: 100%; }
.viz-svg { display: block; width: 100%; height: auto; overflow: visible; }
/* Axis ticks and direct labels: ink tokens, not series colors. */
.viz-svg .viz-tick  { font-size: 11px; fill: var(--text-muted); font-family: var(--font-sans); }
.viz-svg .viz-label { font-size: 12px; font-weight: 600; fill: var(--text-primary); font-family: var(--font-sans); }
/* A label sitting ON a mark needs a fixed light ink: the mark beneath it is a
   saturated series color in BOTH themes, so a theme-flipping ink would vanish in
   one of them. Both light and dark steps clear 4.5:1 against white text. */
.viz-svg .viz-label-on-mark {
  font-size: 12px; font-weight: 600; fill: var(--color-white);
  font-family: var(--font-sans);
}
.viz-spark { display: block; width: 120px; height: 28px; }

.viz-legend {
  display: flex; flex-wrap: wrap; gap: .375rem 1.25rem;
  list-style: none; margin: .875rem 0 0; padding: 0;
  font-size: .8125rem; color: var(--text-secondary);
}
.viz-legend li { display: inline-flex; align-items: center; gap: .4rem; }
.viz-swatch {
  width: 10px; height: 10px; border-radius: 3px;
  background: var(--swatch, var(--viz-1)); flex: none;
}

/* Two charts side by side on wide screens, stacked on narrow. */
.billing-viz-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
@media (max-width: 1100px) { .billing-viz-grid { grid-template-columns: 1fr; } }

/* Horizontal bars for a single-series ranked list (top discount codes). Built from
   HTML rather than SVG so long labels wrap and the values stay selectable text.
   Width comes from data-bar-width via app.js (CSP-safe, no inline style); the track
   is visible regardless, so a no-JS reader still sees the label + value pair — the
   number is never hidden behind the bar rendering. */
.viz-hbars { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.viz-hbars li {
  display: grid;
  grid-template-columns: minmax(6rem, 12rem) 1fr auto;
  align-items: center;
  gap: .75rem;
}
.viz-hbar-label { font-size: .8125rem; min-width: 0; overflow-wrap: anywhere; }
.viz-hbar-track {
  height: 12px; border-radius: var(--radius-full);
  background: var(--bg-alt); overflow: hidden;
}
.viz-hbar-fill {
  display: block; height: 100%; width: 0;
  border-radius: var(--radius-full);
  background: var(--viz-1);
}
.viz-hbar-value { font-size: .8125rem; font-weight: 600; color: var(--text-primary); font-variant-numeric: tabular-nums; }
/* Legend swatch color by fixed slot. Class-based rather than an inline custom
   property so the page carries no inline style attributes (UI-lint ratchet) — and
   the slot mapping stays in CSS where the palette lives. */
.viz-swatch-1 { background: var(--viz-1); }
.viz-swatch-2 { background: var(--viz-2); }
.viz-swatch-3 { background: var(--viz-3); }
.viz-swatch-4 { background: var(--viz-4); }

/* ===========================================================================
   Superadmin business-intelligence console (/dashboard/admin/bi/)
   ---------------------------------------------------------------------------
   Tokens only — no raw hex, so the light/dark swap happens in tokens.css and
   `make theme-parity` stays clean. The filter bar is a wrapping flex row rather
   than a grid: the dimension count changes with what the facets return, and a
   fixed grid leaves holes when a facet is empty.
   =========================================================================== */
.bi-filter-bar { padding: 1rem; }
.bi-filter-row {
  display: flex; flex-wrap: wrap; gap: .5rem 1rem;
  align-items: flex-end;
}
.bi-filter-field { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bi-filter-label {
  font-size: .75rem; font-weight: 600; letter-spacing: .02em;
  text-transform: uppercase; color: var(--text-muted);
}
.bi-filter-actions { display: flex; gap: .5rem; margin-left: auto; }

/* Active-filter chips. Each is a LINK that removes just that filter, so the
   affordance has to read as removable — hence the trailing ×. */
.bi-active-filters {
  display: flex; flex-wrap: wrap; gap: .25rem .5rem;
  align-items: center; margin-top: 1rem;
  padding-top: 1rem; border-top: 1px solid var(--border-color);
}
.bi-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 10px; border-radius: var(--radius-full);
  background: var(--primary-soft); border: 1px solid var(--primary-light);
  color: var(--primary-text); font-size: .8125rem; text-decoration: none;
}
.bi-chip:hover { background: var(--primary-light); }

.bi-views-bar { padding: 1rem; }
.bi-views-row {
  display: flex; flex-wrap: wrap; gap: .5rem;
  align-items: center;
}
.bi-view-meta { margin-left: auto; }
.bi-inline-form { display: flex; gap: .25rem; flex-wrap: wrap; }
.bi-save-view { position: relative; }
.bi-save-view > summary { cursor: pointer; list-style: none; }
.bi-save-view > summary::-webkit-details-marker { display: none; }
.bi-save-form {
  display: flex; flex-direction: column; gap: .5rem;
  margin-top: .5rem; padding: 1rem;
  background: var(--bg-surface-raised);
  border: 1px solid var(--border-color); border-radius: var(--radius-md);
  min-width: 320px;
}
.bi-save-field { display: flex; flex-direction: column; gap: 2px; }
.bi-save-check {
  display: flex; align-items: flex-start; gap: .5rem;
  font-size: .8125rem; color: var(--text-secondary);
}

/* Panel grid: charts sit two-up on wide screens, stack on narrow. */
.bi-panels { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 1100px) {
  .bi-panels-2up { grid-template-columns: 1fr 1fr; }
}

/* A wide table must scroll inside its own container — the page body must never
   scroll horizontally. */
.bi-scroll { overflow-x: auto; }

/* Answer-distribution mini bar: a 100%-wide part-to-whole strip per row, with a
   2px surface gap between segments (dataviz mark spec). Segment colors are fixed
   per ANSWER VALUE, never by rank, so filtering never repaints the survivors. */
.bi-dist { display: flex; height: 10px; border-radius: var(--radius-full); overflow: hidden; gap: 2px; }
.bi-dist span { display: block; height: 100%; }
.bi-dist-yes     { background: var(--success); }
.bi-dist-partial { background: var(--warning); }
.bi-dist-no      { background: var(--danger); }
.bi-dist-na      { background: var(--text-muted); }
.bi-dist-unanswered { background: var(--bg-alt); }
.bi-dist-legend {
  display: flex; flex-wrap: wrap; gap: .5rem;
  font-size: .75rem; color: var(--text-muted);
}
.bi-dist-legend span { display: inline-flex; align-items: center; gap: 4px; }
.bi-dist-key { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }

/* The question drill-down's regulatory text block. */
.bi-question-text {
  font-size: .9375rem; line-height: 1.6; color: var(--text-primary);
  margin: 0 0 1rem;
}
.bi-meta-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.bi-meta-item { display: flex; flex-direction: column; gap: 2px; }

/* Regulatory citation block (finding detail, remediation templates).
   These strings run to ~450 characters and MUST wrap. As an inline <code> the
   background paints per line box, which rendered a ragged stack of
   mismatched-width bars with padding only at the very start and end of the
   string — the worst-looking element in the app. box-decoration-break makes
   every wrapped line carry the full box; the fallback below keeps it readable
   on engines that ignore it. Not folded into .pad-kbd: its five other callers
   are short single-line badges where the inline box is correct. */
.citation-block {
  display: inline;
  padding: .15rem .4rem;
  border-radius: var(--radius-sm);
  background: var(--gray-100);
  line-height: 1.9;
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
  overflow-wrap: anywhere;
}

/* ---- Product walkthrough ---------------------------------------------------
 * Tokens only in both themes: `--bg-surface`, `--text-primary` and friends are
 * already remapped under [data-theme="dark"], so this needs no dark block of its
 * own and cannot become a theme-deaf paint. The one literal is the backdrop
 * scrim, which matches `.overlay-backdrop` above and is deliberately theme-
 * invariant — a scrim is a dimming layer, not a surface, and it must darken the
 * page identically in both themes.
 * -------------------------------------------------------------------------- */
.oh-tour-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(0, 0, 0, .45);
}
body.oh-tour-open { overflow: hidden; }

.oh-tour-dialog {
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  max-width: 520px;
  width: 100%;
  overflow: hidden;
}
.oh-tour-dialog:focus { outline: none; }

.oh-tour-progress-track { height: 3px; background: var(--border-color); }
.oh-tour-progress { height: 3px; background: var(--primary); transition: width .18s ease; }

.oh-tour-body { padding: 1.5rem 1.75rem .5rem; }
.oh-tour-body h2 { margin: 0 0 .5rem; font-size: 1.125rem; }
.oh-tour-body p { color: var(--text-secondary); line-height: 1.6; }
.oh-tour-count {
  margin: 0 0 .35rem;
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.oh-tour-link { display: inline-block; margin-top: .75rem; font-weight: 500; }

.oh-tour-actions {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: 1rem 1.75rem 1.5rem;
}
.oh-tour-spacer { flex: 1; }

/* Sidebar launcher. Sits below the nav on all 184 dashboard pages, which is why
 * it lives here rather than in a footer — only 15 of those pages have one. */
.sidebar-tour { padding: .5rem .75rem 1rem; border-top: 1px solid var(--sidebar-border); }
.sidebar-tour-btn {
  display: flex;
  align-items: center;
  gap: .75rem;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  font-size: .875rem;
  font-weight: 500;
  color: var(--sidebar-link-color);
  cursor: pointer;
  text-align: left;
}
.sidebar-tour-btn:hover { background: var(--sidebar-link-hover-bg); color: var(--text-primary); }

@media (prefers-reduced-motion: reduce) {
  .oh-tour-progress { transition: none; }
}
