/* === Brand tokens — Abyssal palette =======================================
   Token names preserved for back-compat (every .vt-* rule still resolves).
   the accent is teal #00BFFF (the old --copper* aliases were removed in TOK-07); --navy* surfaces shift to the
   abyssal three-tier scale (#0A0F24 → #151B33 → #212B45). Headings adopt
   Plus Jakarta Sans (per vornin_brand_system.md); body stays Inter.
   ===========================================================================

   ## !important policy

   This stylesheet overrides MudBlazor 8.x component internals (.mud-*).
   Mud-internal overrides are prefixed with `body ` to raise selector
   specificity to 0,1,X+1, which beats Mud's 0,1,X class-only rules.
   app.css also loads AFTER MudBlazor.min.css (see App.razor head), so
   equal-specificity rules win by source order on top of that.

   ### 2026-07-20 fix — the dead `[data-mud-theme]` prefix

   These overrides were previously prefixed with `[data-mud-theme]` on the
   belief that "Mud writes that attribute on its theme provider." It does
   NOT — MudBlazor 8.15 emits no `data-mud-theme` attribute anywhere (it
   injects palette vars to :root and does not stamp a theme class/attr on
   any ancestor). So 77 override rules matched NOTHING and were silently
   dead: nav active-highlight, table chrome, focus rings, appbar blur,
   dialog chrome, input focus colour, tabs, chips — all absent. Swapped the
   prefix to `body ` (always present, theme-agnostic, correct specificity).
   The remaining `[data-mud-theme="dark"|"light"]` value-variants are also
   dead for the same reason — left in place only where their effect is
   negligible (see inline notes); do NOT add new ones.

   When adding new rules:

   1. Pure .vt-* rules: NEVER use !important.
   2. Mud-internal overrides: prefix with `body ` first. Only fall
      back to !important when Mud writes the property INLINE (style="..."),
      not via its stylesheet.
   3. The 11 surviving !important declarations are documented inline. Each
      fights an inline style or a hardcoded transition that no specificity
      bump can beat:
        - .mud-icon-root font-size overrides (Mud sets icon size inline)
        - prefers-reduced-motion suppression (Mud's transitions are baked)
        - .vt-vulns-grid sticky thead background + backdrop-filter
        - .mud-main-content mobile padding (Mud uses inline padding)
        - .mud-typography.mud-secondary-text colour (Mud ships the class
          !important; retargets ice → muted ink app-wide)
   4. If a rule fights another !important elsewhere in this file, refactor
      the loser instead of adding a third !important — search before adding.

   Last surveyed 2026-07-13. Total !important count is 12. A future Mud
   upgrade should retest each surviving declaration.
   =========================================================================== */
:root {
    /* Surfaces — three tiers from deepest to shallowest (abyssal) */
    --navy: #151B33;            /* mid surface (panels, dialogs)  — was #111827 */
    --navy-dark: #0A0F24;       /* base canvas (hero, page bg)    — was #0b0f1a */
    --navy-deep: #070b1c;       /* deepest (drawer, modals)        — was #070a13 */
    --card: #212B45;            /* lifted card surface (KPI tiles, table-head) */

    /* Accent — teal. The old --copper* aliases held identical values and were removed
       (TOK-07): two names for one colour meant every accent change had to be made twice.
       Aligned with vornin_brand_system.md §1: teal-hover lighter teal #33CCFF,
       teal-dim 7% opacity. ice #4D94FF reserved for gradient endpoints only. */
    --teal: #00BFFF;
    --teal-hover: #33CCFF;
    --teal-dim: rgba(0,191,255,0.07);
    --ice: #4D94FF;

    /* Text + muted scale (matches vornin_brand_system.md §1 ink scale) */
    --off-white: #ffffff;
    --steel: #B0B8C4;                    /* primary muted (mockup --ink-2) */
    --muted: rgba(176,184,196,0.75);     /* tertiary muted (mockup --ink-3) */
    --muted-2: rgba(255,255,255,0.75);

    /* Light-mode slate scale */
    --slate-50: #F4F6F9;
    --slate-100: #E8ECF1;
    --slate-200: #D5DBE5;
    --slate-600: #475569;
    --slate-700: #334155;
    --slate-900: #0F172A;

    /* Borders — hairline teal-tinted on dark */
    --border-dark: rgba(46,58,89,0.70);
    --border-dark-2: rgba(46,58,89,1);
    --border-light: rgba(15,23,42,0.08);

    /* CTA chrome — primary fill is teal per vornin_brand_system.md §3 */
    --btn-bg: #00BFFF;        /* teal — primary fill */
    --btn-hover: #33CCFF;     /* lighter teal — hover */
    --btn-ink: #0A0F24;       /* abyss — primary text */

    --radius: 8px;
    --radius-lg: 12px;
    /* Canonical button geometry — every button shares one radius + two heights
       so variants align in toolbars and dialog footers (see "Canonical button
       system" below). Session 2026-07-24 button-consistency sweep. */
    --btn-radius: 6px;
    --btn-h-sm: 32px;
    --btn-h-md: 38px;
    --btn-h-lg: 44px;
    --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --font-display: 'Plus Jakarta Sans', 'Inter', sans-serif;
    --font-mono: 'JetBrains Mono', 'Fira Code', monospace;

    /* Surface elevation system — three tiers, used by Panel/KpiTile/inset components. */
    --surface-hero: var(--navy-dark);
    --surface-panel: var(--mud-palette-surface);
    --surface-inset: rgba(15,23,42,0.03);

    /* Animation tokens */
    --ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
    --ease-out: cubic-bezier(0.0, 0, 0.2, 1);
    --dur-quick: 120ms;
    --dur-base: 200ms;
    --dur-slow: 320ms;

    /* Shadow scale — black-tinted, three meaningful levels (deeper for v3 dark canvas) */
    --shadow-1: 0 1px 2px rgba(0,0,0,0.20), 0 1px 1px rgba(0,0,0,0.10);
    --shadow-2: 0 4px 16px rgba(0,0,0,0.30), 0 2px 4px rgba(0,0,0,0.15);
    --shadow-3: 0 24px 48px rgba(0,0,0,0.50), 0 8px 16px rgba(0,0,0,0.25);

    /* Severity semantic tokens — canonical (panels, badges, attr-states, tag chips).
       Single source of truth: edit here, .vt-severity / .vt-attr-state / .vt-tag /
       .vt-kpi-value / .vt-vulns-clean all pick up the change.

       Re-hued 2026-07-26 (UI audit, findings COL-01 + A11Y-02). The previous ramp put
       Critical #B85A66 and High #DB7884 at the same hue, separated by lightness only:
       CIE76 deltaE 11.8, where every other adjacent pair measured 41-80. In the dense
       count pill they were indistinguishable, which for a vulnerability-management
       product means the single most important distinction on the screen did not read.

       Now red → orange → amber → green → grey, the ordering a Nessus/Defender/NVD user
       parses pre-attentively. Ink solved to clear 4.5:1 against all three surfaces
       severity text lands on — --navy #151B33, --card #212B45, --navy-dark #0A0F24:

         Critical #F95C66   5.46 / 4.51 / 6.10
         High     #FF8A3D   7.24 / 5.99 / 8.09
         Medium   #E0B341   8.65 / 7.15 / 9.66
         Low      #4CAD7E   6.14 / 5.07 / 6.85   (unchanged, already compliant)
         Info     #94A3B8   6.62 / 5.47 / 7.40   (was rgba(255,255,255,0.55))

       Adjacent separation: Crit→High 39.2, High→Med 34.8, Med→Low 65.0, Low→Info 48.3.
       Verified by audit/playwright/contrast.mjs — run it after touching these.
       Charts read these same values via VtChart.cs; keep the two in step (COL-03). */
    --sev-critical: #F95C66;
    --sev-critical-bg: rgba(249,92,102,0.16);
    --sev-critical-border: rgba(249,92,102,0.38);
    --sev-high: #FF8A3D;
    --sev-high-bg: rgba(255,138,61,0.12);
    --sev-high-border: rgba(255,138,61,0.32);
    --sev-medium: #E0B341;
    --sev-medium-bg: rgba(224,179,65,0.10);
    --sev-medium-border: rgba(224,179,65,0.30);
    --sev-low: #4CAD7E;
    --sev-low-bg: rgba(76,173,126,0.10);
    --sev-low-border: rgba(76,173,126,0.28);
    /* "None/Info" muted text — theme-aware (was fixed white rgba(255,255,255,.55),
       invisible on light-mode panels: priority-reason chips, .vt-vuln.l, unreached
       tag). #B0B8C4 dark / #475569 light. 2026-07-24 light-mode parity. */
    --sev-none: var(--mud-palette-text-secondary);
    --sev-none-bg: rgba(192,200,212,0.06);
    --sev-none-border: rgba(192,200,212,0.18);

    /* Severity SOFT variants — used by .vt-vuln.* row pills inside dense tables.
       Lower alpha keeps pills from competing with row text. Variance is intentional;
       do not unify with canonical tokens above. */
    --sev-critical-bg-soft: rgba(184,90,102,0.07);
    --sev-critical-border-soft: rgba(184,90,102,0.24);
    --sev-high-bg-soft: rgba(219,120,132,0.05);
    --sev-high-border-soft: rgba(219,120,132,0.20);
    --sev-medium-bg-soft: rgba(201,139,63,0.07);
    --sev-medium-border-soft: rgba(201,139,63,0.22);
    --sev-low-bg-soft: rgba(76,173,126,0.06);
    --sev-low-border-soft: rgba(76,173,126,0.22);
    --sev-none-bg-soft: rgba(192,200,212,0.04);
    --sev-none-border-soft: rgba(192,200,212,0.15);

    /* Age-warning amber — 7-30 day vuln age cell on VulnTable. Distinct hue from
       --sev-medium so the "aging, but not breaching" state reads at a glance. */
    --age-warning: #C2A13A;
    --age-warning-bg: rgba(194,161,58,0.10);
    --age-warning-border: rgba(194,161,58,0.30);

    /* .vt-tag.ransom — distinct from canonical critical (lighter bg/border).
       Dedicated tokens preserve the deliberate drift. */
    --tag-ransom-bg: rgba(184,90,102,0.12);
    --tag-ransom-border: rgba(184,90,102,0.28);

    /* Semantic split of the formerly-overloaded rgba(192,200,212,0.06).
       Same value today; decoupled identities so future drift is intentional. */
    /* --hairline was 0.06 = 1.12:1 against --navy, i.e. functionally invisible; a
       1,400-row table read as one undifferentiated block. 0.28 measures 1.96:1 —
       legible without turning the table into a spreadsheet grid. Not a WCAG item
       (decorative dividers are exempt from 1.4.11); this is a density fix.
       --muted-fill keeps the old value: it is a fill, not a boundary.
       2026-07-26 UI audit, finding DENS-02. */
    --hairline: rgba(192,200,212,0.28);
    --muted-fill: rgba(192,200,212,0.06);

    /* Input boundary. WCAG 1.4.11 wants 3:1 for the boundary that identifies a control,
       and MudTheme's old LinesInputs composited to 1.44:1 on --navy — see A11Y-05 in
       VorninTheme.cs, which must stay in step with this value. 4.72:1. */
    --input-border: rgba(138,147,168,0.90);

    /* State tokens, deliberately decoupled from severity (COL-04, audit 2026-07-26).
       Same hues as the severity ramp by design — a warning IS amber — but a separate
       lineage, so retuning "High severity" can no longer restyle every field-validation
       error. Error was previously byte-identical to --sev-high. Mirrored in VorninTheme. */
    --state-error: #F95C66;
    --state-warning: #E0B341;
    --state-success: #4CAD7E;
    --state-info: #4D94FF;

}

/* Dark-mode surface tints — recessed surfaces use a deeper canvas for hierarchy.
   Retargeted 2026-07-26: `.mud-theme-dark` and `[data-mud-theme]` both match ZERO
   nodes (verified live — MudBlazor 8 stamps neither), so this rule was dead in the
   same way the 77 rules described in the file header were. `data-theme` is set on
   <html> by the inline script in App.razor before first paint. */
[data-theme="dark"] {
    --surface-inset: rgba(0,0,0,0.35);
}

/* ===========================================================================
   LIGHT THEME token overrides — 2026-07-26 UI audit, findings A11Y-01 / A11Y-05.

   Until now there was NO light-mode override for any severity or accent token,
   so the dark-theme hexes were reused verbatim as text on white and measured
   1.79–2.99:1 against the 4.5:1 WCAG 2.2 AA floor — 22 violations. The cause was
   structural, not careless: with no theme class in the DOM there was no selector
   to hang a light-mode rule on.

   Values solved to clear 4.5:1 on all three light surfaces (#FFFFFF,
   --slate-50 #F4F6F9, --slate-100 #E8ECF1). Hues stay in the same family as dark
   mode so the product does not change identity between themes — only legibility.

   Verify:  node audit/playwright/contrast.mjs
   =========================================================================== */
[data-theme="light"] {
    /* Accent — #00BFFF is 1.79:1 on --slate-100 and is the colour of every
       section eyebrow in the app. #0A6C93: 5.87 white / 5.42 / 4.95. */
    --teal: #0A6C93;
    --teal-hover: #08607F;
    --teal-dim: rgba(10,108,147,0.07);

    /* Severity ink — 6.95 / 6.11 / 6.37 / 5.38 / 7.58 on white respectively. */
    --sev-critical: #B3121D;
    --sev-critical-bg: rgba(179,18,29,0.10);
    --sev-critical-border: rgba(179,18,29,0.34);
    --sev-high: #A34608;
    --sev-high-bg: rgba(163,70,8,0.09);
    --sev-high-border: rgba(163,70,8,0.30);
    --sev-medium: #7A5A0B;
    --sev-medium-bg: rgba(122,90,11,0.09);
    --sev-medium-border: rgba(122,90,11,0.28);
    --sev-low: #1D794F;
    --sev-low-bg: rgba(29,121,79,0.09);
    --sev-low-border: rgba(29,121,79,0.28);
    --sev-none: #475569;
    --sev-none-bg: rgba(71,85,105,0.07);
    --sev-none-border: rgba(71,85,105,0.22);

    --sev-critical-bg-soft: rgba(179,18,29,0.05);
    --sev-critical-border-soft: rgba(179,18,29,0.22);
    --sev-high-bg-soft: rgba(163,70,8,0.05);
    --sev-high-border-soft: rgba(163,70,8,0.20);
    --sev-medium-bg-soft: rgba(122,90,11,0.05);
    --sev-medium-border-soft: rgba(122,90,11,0.20);
    --sev-low-bg-soft: rgba(29,121,79,0.05);
    --sev-low-border-soft: rgba(29,121,79,0.20);
    --sev-none-bg-soft: rgba(71,85,105,0.04);
    --sev-none-border-soft: rgba(71,85,105,0.16);

    --age-warning: #6E5410;
    --age-warning-bg: rgba(110,84,16,0.09);
    --age-warning-border: rgba(110,84,16,0.28);

    --tag-ransom-bg: rgba(179,18,29,0.08);
    --tag-ransom-border: rgba(179,18,29,0.26);

    /* Light-canvas input boundary — 3.03:1 on white, 3.07:1 on --slate-50 (WCAG 1.4.11). */
    --input-border: rgba(15,23,42,0.47);

    /* State tokens, light canvas (COL-04). Solved for >=4.5:1 on white / slate-50 /
       slate-100, same as the light severity inks. See the :root block for the rationale. */
    --state-error: #B3121D;
    --state-warning: #7A5A0B;
    --state-success: #1D794F;
    --state-info: #1D4ED8;

    /* Ink + boundaries. --steel/--muted are dark-mode greys that wash out on white. */
    --steel: #334155;
    --muted: #475569;
    --muted-2: rgba(15,23,42,0.75);
    --off-white: #0F172A;

    --hairline: rgba(15,23,42,0.24);
    --muted-fill: rgba(15,23,42,0.04);
}

/* .vt-hero keeps its navy canvas in BOTH themes, so the light-mode ink overrides
   above would put #0F172A text on #0A0F24 — the page title effectively disappeared.
   Re-assert the dark-theme ink (and the dark accent) inside the hero only. Scoped
   to the hero subtree so the rest of the light page keeps the legible dark ink.
   2026-07-26 UI audit. */
[data-theme="light"] .vt-hero {
    --off-white: #ffffff;
    --steel: #B0B8C4;
    --muted: rgba(176,184,196,0.75);
    --muted-2: rgba(255,255,255,0.75);
    --teal: #00BFFF;
    --teal-hover: #33CCFF;
    --hairline: rgba(192,200,212,0.28);
}

/* === Base === */
html, body {
    font-family: var(--font);
    margin: 0;
    padding: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Paint the correct canvas before the Blazor circuit connects.
   MudThemeProvider only applies its palette once _isDarkMode binds on the
   interactive pass, so a light-mode user saw ~220ms of #0A0F24 on every page
   load. data-theme is stamped on <html> by the inline script in App.razor
   before first paint; these two rules give the browser something to paint
   with. Values mirror VorninTheme's PaletteLight/PaletteDark Background, so
   there is no second repaint when Mud does take over.
   2026-07-26 UI audit, finding PERF-02. */
html[data-theme="dark"], html[data-theme="dark"] body { background-color: #0A0F24; }
html[data-theme="light"], html[data-theme="light"] body { background-color: #F4F6F9; }

code, pre, kbd, samp, .mud-typography-caption code {
    font-family: var(--font-mono);
}

/* === Scrollbar — steel on dark, slate on light === */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}
::-webkit-scrollbar-track {
    background: transparent;
}
::-webkit-scrollbar-thumb {
    background: rgba(176,184,196,0.18);
    border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
    background: rgba(0,191,255,0.45);
}

/* === MudBlazor Overrides ===
   All selectors below prefix with `body ` to gain a 0,1,X+1 specificity
   that beats Mud 8's class-only rules (typically 0,1,X) without !important.
   (Was `[data-mud-theme]` — a dead attribute Mud never emits; see header.) */

/* AppBar — translucent navy with blur to match public site nav */
body .mud-appbar {
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border-dark);
}

/* Nav links — rounded, subtle hover, teal for active */
body .mud-nav-link {
    border-radius: var(--radius);
    margin: 1px 8px;
    transition: background 0.15s ease, color 0.15s ease;
    letter-spacing: -0.005em;
}
body .mud-nav-link.active {
    background: var(--teal-dim);
    color: var(--teal);
}
body .mud-nav-link.active .mud-nav-link-icon {
    color: var(--teal);
}

/* Data grid refinements */
body .mud-table-cell {
    font-size: 0.85rem;
}
body .mud-table-head .mud-table-cell {
    font-weight: 600;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    opacity: 0.7;
}

/* Muted body text — MudText Color="Color.Secondary" emits .mud-secondary-text,
   which resolves to the theme's Secondary (ice #4D94FF). Across the app that
   enum is used for captions/descriptions/hints (muted intent), so ice-colored
   text reads like a non-clickable link and breaks the "ice never signals
   interactivity" rule. Retarget typography-only to the muted ink; icons/chips
   keep .mud-secondary-text via their own classes, so genuine ice accents
   (e.g. the PLATFORM chip) are unaffected. */
.mud-typography.mud-secondary-text {
    /* !important: MudBlazor ships `.mud-secondary-text{color:...!important}`.
       The two-class selector out-specifies Mud's single class, and !important
       matches its priority tier so ours wins. (NOT prefixed with
       body — that attribute is not emitted in this Mud version,
       so such rules are dead; see note below.) Documented exception per the
       policy header — fighting a baked Mud declaration. */
    color: var(--mud-palette-text-secondary) !important;
}

/* Cookie notice (CookieNotice.razor) — was a loud filled-Info blue bar on
   every authed page. Calm it to a subtle, theme-aware surface with a teal
   accent + readable teal link (the old inherit-colored link was low-contrast
   on the saturated blue). 2026-07-24 UI polish. */
body .vt-cookie-notice.mud-alert {
    background: var(--mud-palette-surface);
    border: 1px solid rgba(0,191,255,0.28);
    color: var(--mud-palette-text-primary);
    box-shadow: var(--shadow-1);
}
body .vt-cookie-notice .mud-alert-icon { color: var(--teal); }
body .vt-cookie-notice a,
body .vt-cookie-notice .mud-link { color: var(--teal); text-decoration: underline; }

/* Paper / card hover */
.mud-paper {
    transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
body .mud-paper.mud-paper-outlined:hover {
    border-color: rgba(0,191,255,0.25);
}

/* Chips */
body .mud-chip {
    font-weight: 600;
    letter-spacing: 0.02em;
}

/* Status chips that can carry variable-length scanner progress text
   (ScanList active-scan card + the history grid's Status column). MudBlazor
   chips are white-space:nowrap with no ellipsis, so a long ProgressMessage
   overflowed the running-scan card (clipped by the panel's overflow:hidden,
   which read as broken padding) and set the Status column's min-content width
   to the full string, widening the whole scans table past the viewport.
   Cap the width and truncate; min-width:0 lets the card's flex row shrink the
   chip instead of overflowing. The full text stays available via the title. */
body .mud-chip.vt-chip-truncate {
    max-width: 22rem;
    min-width: 0;
}
body .mud-chip.vt-chip-truncate .mud-chip-content {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* === Canonical button system (2026-07-24 button-consistency sweep) ========
   Every button — regardless of Variant/Color — shares one radius, one type
   ramp and one neutral treatment, so a toolbar or dialog footer reads as a
   single set. Primary-filled keeps its detailed hero block further down
   (higher specificity + later source order both win). Theme-safe: colours
   come from Mud palette vars (--mud-palette-*), which the theme provider
   swaps on light/dark, so no dead [data-mud-theme] prefixes are needed.
   Contrast text on solids handled via MudTheme *ContrastText.
   ========================================================================= */
body .mud-button-root {
    transition: transform 0.15s ease-out, background-color 0.15s ease-out,
                border-color 0.15s ease-out, color 0.15s ease-out, filter 0.15s ease-out;
    border-radius: var(--btn-radius);
    font-family: 'Plus Jakarta Sans', 'Inter', sans-serif;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-transform: none;
}
.mud-button-filled.mud-button-filled-primary:hover {
    transform: translateY(-1px);
}

/* Neutral / secondary — the calm "other" action (dialog Cancel, secondary
   toolbar buttons). Color.Secondary previously rendered as saturated navy
   (light) / ice-blue (dark) — 130 call sites, none of them wanting emphasis.
   Remapped here to one muted treatment that lifts to teal on hover. */
body .mud-button-outlined-secondary,
body .mud-button-text-secondary,
body .mud-button-outlined-tertiary,
body .mud-button-text-tertiary {
    color: var(--mud-palette-text-secondary);
    border-color: var(--mud-palette-lines-default);
}
body .mud-button-outlined-secondary:hover,
body .mud-button-text-secondary:hover,
body .mud-button-outlined-tertiary:hover,
body .mud-button-text-tertiary:hover {
    color: var(--mud-palette-primary);
    border-color: var(--mud-palette-primary);
    background-color: var(--teal-dim);
}

/* Outlined primary — teal-outline "secondary emphasis" (a strong action that
   isn't THE primary). Crisp teal border, teal ink, teal-tint hover. */
body .mud-button-outlined-primary {
    border-color: rgba(0,191,255,0.55);
    color: var(--mud-palette-primary);
}
body .mud-button-outlined-primary:hover {
    border-color: var(--mud-palette-primary);
    background-color: var(--teal-dim);
}

/* Destructive — one tint for outlined + text (Delete, Cancel scan, Erase).
   Solid filled-error is reserved for the final confirm inside a dialog. */
body .mud-button-outlined-error,
body .mud-button-text-error {
    color: var(--mud-palette-error);
    border-color: color-mix(in srgb, var(--mud-palette-error) 45%, transparent);
}
body .mud-button-outlined-error:hover,
body .mud-button-text-error:hover {
    border-color: var(--mud-palette-error);
    background-color: color-mix(in srgb, var(--mud-palette-error) 12%, transparent);
}

/* Hero action buttons — the .vt-hero is ALWAYS dark (both themes), so its
   outlined/text actions must keep light ink + light border. The neutral
   treatment above is theme-aware (dark ink in light mode), which would vanish
   on the dark hero. Higher specificity (.vt-hero-actions) wins over the
   neutral rules. Filled-primary is unaffected (not outlined/text). */
body .vt-hero-actions .mud-button-outlined,
body .vt-hero-actions .mud-button-text {
    color: var(--off-white);
    border-color: rgba(255,255,255,0.28);
}
body .vt-hero-actions .mud-button-outlined:hover,
body .vt-hero-actions .mud-button-text:hover {
    color: var(--off-white);
    border-color: var(--mud-palette-primary);
    background-color: rgba(255,255,255,0.08);
}

/* Inputs — teal focus ring */
body .mud-input.mud-input-underline:after,
body .mud-input-outlined-border {
    transition: border-color 0.15s ease-out;
}
body .mud-input-label.mud-input-label-inputcontrol.mud-focused,
body .mud-input-label-animated.mud-focused {
    color: var(--teal);
}

/* Dialog — lift + edge + ring so it separates from the near-black canvas.
   Root problem: dialog surface #151B33 sat ~2-3% above canvas #0A0F24 and the
   only shadow was black (invisible on dark), so the modal blended into the page.
   Theme-safe: the white wash lifts the DARK surface but is invisible on white
   (light mode); the light-ring separates on dark, the drop shadow on light. */
body .mud-dialog {
    border-radius: var(--radius-lg);
    border: 1px solid rgba(0,191,255,0.22);
    background-image: linear-gradient(0deg, rgba(255,255,255,0.05), rgba(255,255,255,0.05));
    box-shadow: 0 24px 60px rgba(0,0,0,0.60),
                0 0 0 1px rgba(255,255,255,0.06);
}
/* Modal scrim — blur backdrop. Paired with the darker OverlayDark set in
   VorninTheme, this gives the dialog a clear ground. Mud sets no
   backdrop-filter, so source order (app.css after MudBlazor.css) wins. */
.mud-overlay-scrim {
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

/* Tab indicators — teal */
body .mud-tab-slider {
    background: var(--teal);
}
body .mud-tab.mud-tab-active {
    color: var(--teal);
}

/* Vertical settings sub-nav — Settings/Account use Position="Position.Left".
   Left rail of section links + content pane on the right (standard SaaS
   settings pattern) instead of a horizontal tab strip. */
.vt-settings-tabs .mud-tabs-toolbar {
    min-width: 210px;
    border-right: 1px solid var(--border-dark);
    padding-top: 4px;
}
.vt-settings-tabs .mud-tab {
    justify-content: flex-start;
    text-transform: none;
    letter-spacing: -0.005em;
    border-radius: var(--radius);
    margin: 2px 8px;
    min-height: 44px;              /* 44px touch target */
    opacity: 0.85;
}
.vt-settings-tabs .mud-tab:hover { background: rgba(0,191,255,0.06); opacity: 1; }
.vt-settings-tabs .mud-tab.mud-tab-active { background: var(--teal-dim); opacity: 1; }
.vt-settings-tabs .mud-tabs-panels { padding-left: 20px; min-width: 0; }
/* Narrow viewports — tighten the rail so content isn't crushed. */
@media (max-width: 720px) {
    .vt-settings-tabs .mud-tabs-toolbar { min-width: 132px; }
    .vt-settings-tabs .mud-tab { margin: 2px 4px; font-size: 0.82rem; }
    .vt-settings-tabs .mud-tabs-panels { padding-left: 10px; }
}

/* Switches + checkboxes pick up the teal primary automatically via MudTheme */

/* Uppercase nav-brand vibe for AppBar text (opt-in via class) */
.nav-brand-caps {
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 700;
}

/* AppBar brand block — logo + optional mode chip */
.vt-appbar-brand {
    display: flex;
    align-items: center;
    gap: 8px;
}
.vt-appbar-brand img {
    height: 22px;
    width: auto;
    display: block;
}
/* Theme-paired brand mark. One <img> per theme rather than a CSS mask, so a browser
   without mask support still renders a logo instead of an empty box; the hidden one is
   aria-hidden with an empty alt, so a screen reader hears "Vornin" once. */
.vt-appbar-brand .vt-logo-light { display: none; }
html[data-theme="light"] .vt-appbar-brand .vt-logo-dark { display: none; }
html[data-theme="light"] .vt-appbar-brand .vt-logo-light { display: block; }
body .vt-appbar-mode-chip {
    font-size: 0.65rem;
    height: 20px;
}

/* Sidebar footer divider (PlatformNavMenu) */
.vt-nav-divider {
    border-top: 1px solid var(--border-dark);
    margin: 8px 16px;
}

/* ==========================================================================
   Vornin Operations Console — reusable page chrome
   Hero strip, KPI tiles, sector rail, navy panel. Classes all vt-* prefixed.
   Apply anywhere — any .razor page can drop these in.
   ========================================================================== */

.vt-hero {
    position: relative; overflow: hidden;
    padding: 32px 36px;
    background: var(--navy-dark);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-lg);
    margin-bottom: 24px;
}
/* Abyssal: triangle-grid texture masked to top, with a teal radial glow */
.vt-hero::before {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: url('triangle-grid.jpg') center top / cover no-repeat;
    opacity: 0.22;
    -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 20%, black 0%, black 30%, transparent 72%);
            mask-image: radial-gradient(ellipse 80% 100% at 50% 20%, black 0%, black 30%, transparent 72%);
}
.vt-hero::after {
    content: ''; position: absolute; pointer-events: none;
    top: -40px; left: 50%; transform: translateX(-50%);
    width: 600px; height: 260px;
    background: radial-gradient(ellipse, rgba(0,191,255,0.10) 0%, rgba(77,148,255,0.05) 50%, transparent 75%);
    filter: blur(48px);
}
.vt-hero-title-row {
    position: relative; z-index: 1;
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 24px; flex-wrap: wrap;
}
.vt-hero-title-row.has-kpis { margin-bottom: 28px; }
.vt-eyebrow {
    font-family: var(--font-mono);
    font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--teal);
    display: inline-flex; align-items: center; gap: 8px;
    margin-bottom: 12px;
}
/* Abyssal: pulsing teal dot marker */
.vt-eyebrow::before {
    content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--teal);
    animation: vt-eyebrow-pulse 2s infinite;
}
@keyframes vt-eyebrow-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.3; }
}
.vt-hero h1,
.vt-hero .vt-hero-title {
    margin: 0; font-family: var(--font-display);
    font-size: 2rem; font-weight: 700;
    letter-spacing: -0.025em; color: var(--off-white); line-height: 1.08;
}
.vt-hero h1 em,
.vt-hero .vt-hero-title em { font-style: normal; color: var(--teal); }
.vt-hero-sub {
    margin: 8px 0 0; max-width: 580px;
    color: var(--steel); font-size: 0.95rem; line-height: 1.55;
}
.vt-hero-actions { display: flex; gap: 12px; flex-shrink: 0; flex-wrap: wrap; }

/* Mobile hero — drop padding + scale typography */
@media (max-width: 600px) {
    .vt-hero {
        padding: 20px 16px;
        margin-bottom: 16px;
    }
    .vt-hero h1,
    .vt-hero .vt-hero-title { font-size: 1.4rem; }
    .vt-hero-sub { font-size: 0.85rem; }
    .vt-eyebrow {
        font-size: 0.62rem;
        letter-spacing: 0.18em;
        margin-bottom: 8px;
    }
    .vt-eyebrow::before { width: 18px; }
    .vt-hero-title-row { gap: 12px; }
    .vt-hero-title-row.has-kpis { margin-bottom: 16px; }
}

/* Ultra-narrow: tighten eyebrow tracking so it stays legible at 375px */
@media (max-width: 375px) {
    .vt-eyebrow { font-size: 0.58rem; letter-spacing: 0.1em; margin-bottom: 6px; }
}

/* Filter bar — sticky on desktop, static on mobile so it doesn't eat the viewport */
.vt-filter-bar {
    position: sticky;
    top: 64px;
    z-index: 5;
    background: var(--mud-palette-surface);
}
@media (max-width: 768px) {
    .vt-filter-bar { position: static; }
}

/* Preset chip row — wraps on narrow viewports without breaking the search field. */
.vt-preset-chips {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

/* Collapsed filter panel — fixed-width grid so every select lines up
   regardless of label length. Each cell auto-sizes inputs to 100%. */
.vt-filter-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 12px 14px;
    align-items: end;
}
.vt-filter-grid > * {
    width: 100%;
    min-width: 0;
}

.vt-kpis {
    position: relative; z-index: 1;
    display: grid; grid-template-columns: repeat(4, 1fr);
    border-top: 1px solid var(--border-dark);
}
.vt-kpis.cols-3 { grid-template-columns: repeat(3, 1fr); }
.vt-kpis.cols-5 { grid-template-columns: repeat(5, 1fr); }
/* 5-across is too tight on tablets — step down before the shared 720px 2-col rule. */
@media (max-width: 960px) { .vt-kpis.cols-5 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 768px) { .vt-kpis.cols-5 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px) {
    .vt-kpis, .vt-kpis.cols-3, .vt-kpis.cols-5 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
    .vt-kpis, .vt-kpis.cols-3, .vt-kpis.cols-5 { grid-template-columns: 1fr; }
    .vt-kpi {
        border-right: none;
        border-bottom: 1px solid var(--hairline);
        padding: 14px 16px 4px;
    }
    .vt-kpi:last-child { border-bottom: none; }
    .vt-kpi-value { font-size: 1.6rem; }
}
.vt-kpi {
    padding: 20px 24px 6px;
    border-right: 1px solid var(--hairline);
}
.vt-kpi:last-child { border-right: none; }
/* Anchor-shaped KPI (e.g. clickable SLA Breached tile that routes to /vulnerabilities). */
a.vt-kpi.vt-kpi-link {
    display: block;
    color: inherit;
    text-decoration: none;
    transition: background 0.15s ease;
}
a.vt-kpi.vt-kpi-link:hover { background: rgba(0,191,255,0.06); }
a.vt-kpi.vt-kpi-link:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.vt-kpi-label {
    font-family: var(--font-mono);
    font-size: 0.66rem; letter-spacing: 0.16em; text-transform: uppercase;
    /* Bumped 2026-05-02 from rgba(...,0.55) to 0.72 — meets WCAG AA at small sizes on --navy-dark. */
    color: rgba(255,255,255,0.72);
}
.vt-kpi-value {
    margin-top: 8px;
    font-size: 2.1rem; font-weight: 700; line-height: 1.1;
    letter-spacing: -0.03em; color: var(--off-white);
    display: flex; align-items: baseline; gap: 10px;
}
.vt-kpi-value.accent { color: var(--teal); }
.vt-kpi-value.alert  { color: var(--sev-high); }
.vt-kpi-value.success { color: var(--sev-low); }
.vt-kpi-unit {
    font-size: 0.8rem; font-weight: 500; letter-spacing: 0;
    color: rgba(255,255,255,0.55);
}

/* Inline severity bar + breakdown rendered inside a .vt-kpi tile.
   Used on the Vulnerabilities page Open KPI to fold what was previously
   a standalone "Open by severity" Paper into the hero. */
.vt-kpi-sevbar {
    display: flex; gap: 2px;
    margin-top: 10px;
    height: 4px; width: 100%;
    border-radius: 2px; overflow: hidden;
    background: rgba(255,255,255,0.06);
}
.vt-kpi-sevbar-seg {
    height: 100%;
    cursor: pointer;
    transition: opacity 0.15s;
    border: 0;
    padding: 0;
}
.vt-kpi-sevbar-seg:hover { opacity: 0.8; }
.vt-kpi-sevcounts {
    display: flex; flex-wrap: wrap; gap: 10px;
    margin-top: 8px;
    font-family: var(--font-mono);
    font-size: 0.7rem; letter-spacing: 0.04em;
    color: rgba(255,255,255,0.65);
}
.vt-kpi-sevcounts button {
    background: transparent; border: 0; padding: 0;
    cursor: pointer;
    color: inherit; font: inherit; letter-spacing: inherit;
}
.vt-kpi-sevcounts button:hover { color: var(--off-white); }
.vt-kpi-sevcounts strong { color: var(--off-white); font-weight: 600; }
.vt-kpi-sevcount { display: inline-flex; align-items: baseline; gap: 3px; }
.vt-sevcount-letter { font-weight: 700; }

/* KPI tiles double as the Status filter. The click target is an inner button so the
   .vt-kpi wrapper keeps all its existing border/padding/grid/mobile styling untouched. */
.vt-kpi-hit {
    display: block; width: 100%;
    background: transparent; border: 0; padding: 0; margin: 0;
    color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.vt-kpi-hit:hover .vt-kpi-label,
.vt-kpi-hit:focus-visible .vt-kpi-label { color: var(--off-white); }
.vt-kpi.active { background: rgba(0,191,255,0.06); box-shadow: inset 0 -2px 0 rgba(0,191,255,0.85); }
/* Secondary stat under a KPI value (e.g. "N risk accepted" under the Dismissed count). */
.vt-kpi-subnote {
    font-size: 0.68rem;
    letter-spacing: 0.02em;
    color: var(--muted);
    margin-top: 2px;
}

/* Panel with operations-console header (for grids, lists, etc.).
   Edge-lit chrome: cold teal hairline along the top edge + hover lift +
   cold drop-shadow on hover. Brand spec §6 — "cold light emerging from
   darkness." Eyebrow ink-2 (teal reserved for action per §1). */
.vt-panel {
    /* Theme-aware surface (2026-07-24 light-mode parity): dark navy in dark
       mode, white surface in light mode — driven by Mud's --mud-palette-*
       vars (the only theme signal; Mud 8.x stamps no theme class/attr).
       Dark-mode value = #151B33 (was #0A0F24) — a cleaner elevation ramp
       canvas(#0A0F24) < panel(#151B33) < kpi-card(#212B45). */
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 10px; /* MD scale per vornin_brand_system.md §4 */
    overflow: hidden;
    min-width: 0;
    margin-bottom: 16px;
    position: relative;
    transition: border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                transform var(--dur-quick) var(--ease-out);
}
.vt-panel::before {
    content: "";
    position: absolute; left: 0; right: 0; top: 0; height: 1px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(0,191,255,0.55) 22%,
        rgba(0,191,255,0.55) 78%,
        transparent 100%);
    opacity: 0.55;
    pointer-events: none;
    z-index: 1;
}
.vt-panel:hover {
    border-color: rgba(0,191,255,0.40);
    box-shadow: 0 0 16px rgba(0,191,255,0.18),
                0 12px 28px -16px rgba(0,191,255,0.30);
    transform: translateY(-1px);
}
.vt-panel:hover::before { opacity: 0.85; }
.vt-panel-head {
    display: flex; align-items: center; gap: 12px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}
.vt-panel-head-label {
    font-family: var(--font-mono);
    font-size: 0.68rem; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--mud-palette-text-secondary); /* ink-2; teal reserved for action */
    /* Now rendered as h2/h3/h4 rather than a span (2026-07-26 UI audit, A11Y-03).
       Reset the UA heading defaults so the visual result is byte-identical. */
    margin: 0;
    font-weight: 400;
    line-height: inherit;
    display: inline;
}
.vt-panel-head-count {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--mud-palette-text-secondary);
    letter-spacing: 0.04em;
    background: var(--teal-dim);
    border: 1px solid var(--mud-palette-lines-default);
    padding: 2px 10px;
    border-radius: 999px;
}

/* Data grid re-chrome inside .vt-panel — selector is already 0,2,2+ which
   beats Mud's table rules (0,2,1). No !important needed. */
body .vt-panel .mud-table { background: transparent; box-shadow: none; }
body .vt-panel .mud-table-head .mud-table-cell {
    background: transparent;
    color: var(--mud-palette-text-secondary);
    font-family: var(--font-mono);
    font-size: 0.66rem; font-weight: 500;
    letter-spacing: 0.16em; text-transform: uppercase;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    padding: 14px 18px;
}
body .vt-panel .mud-table-body .mud-table-row {
    background: transparent;
    border-left: 2px solid transparent;
    transition: background 0.12s ease, border-left-color 0.12s ease;
}
body .vt-panel .mud-table-body .mud-table-row:hover {
    background: rgba(0,191,255,0.04);
    border-left-color: var(--teal);
}
body .vt-panel .mud-table-body .mud-table-cell {
    background: transparent;
    color: var(--mud-palette-text-primary);
    border-bottom: 1px solid var(--mud-palette-divider);
    padding: 14px 18px;
    font-size: 0.9rem;
}
body .vt-panel .mud-table-body .mud-table-row:last-child .mud-table-cell { border-bottom: none; }
body .vt-panel .mud-table-pagination { background: transparent; border-top: 1px solid var(--hairline); }

/* Mono monospace utility for hosts, counts, timestamps */
.vt-mono {
    font-family: var(--font-mono);
    letter-spacing: -0.005em;
}

/* Modal eyebrow — used on MudDialog TitleContent across the app */
.vt-modal-eyebrow {
    font-family: var(--font-mono);
    font-size: 0.62rem; letter-spacing: 0.22em; text-transform: uppercase;
    color: var(--teal); margin-bottom: 4px;
}
.vt-modal-title { font-size: 1.1rem; font-weight: 600; color: var(--mud-palette-text-primary); letter-spacing: -0.01em; }

/* Empty state */
.vt-empty {
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    text-align: center;
    padding: 72px 24px;
    border: 1px dashed rgba(0,191,255,0.35);
    border-radius: var(--radius-lg);
    background: radial-gradient(ellipse 70% 60% at 50% 20%, rgba(0,191,255,0.06), transparent 70%);
    margin: 24px;
}
.vt-empty-icon {
    width: 64px; height: 64px; border-radius: 50%;
    background: rgba(0,191,255,0.10);
    display: flex; align-items: center; justify-content: center;
    color: var(--teal);
    margin-bottom: 20px;
}
.vt-empty h3 {
    margin: 0 0 8px; color: var(--mud-palette-text-primary);
    font-size: 1.15rem; font-weight: 600; letter-spacing: -0.01em;
}
.vt-empty p {
    margin: 0 0 24px; max-width: 420px;
    color: rgba(255,255,255,0.55); font-size: 0.92rem; line-height: 1.5;
}
.vt-empty-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

/* Severity pills for vuln counts */
.vt-vulns { display: flex; gap: 4px; flex-wrap: wrap; }
.vt-vuln {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px; border-radius: 4px;
    font-family: var(--font-mono);
    font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em;
    border: 1px solid transparent;
}
/* Critical pointed at the *high* tokens and low pointed at *none*, so two of the four
   count pills rendered the wrong severity — Critical and High differed only by
   background alpha (0.12 vs 0.05). 2026-07-26 UI audit, finding COL-02. */
.vt-vuln.c { background: var(--sev-critical-bg);    color: var(--sev-critical); border-color: var(--sev-critical-border); }
.vt-vuln.h { background: var(--sev-high-bg-soft);   color: var(--sev-high);     border-color: var(--sev-high-border-soft); }
.vt-vuln.m { background: var(--sev-medium-bg-soft); color: var(--sev-medium);   border-color: var(--sev-medium-border-soft); }
.vt-vuln.l { background: var(--sev-low-bg-soft);    color: var(--sev-low);      border-color: var(--sev-low-border-soft); }
.vt-vulns-clean {
    font-family: var(--font-mono);
    font-size: 0.72rem; color: var(--sev-low); letter-spacing: 0.1em;
}

/* === Error UI === */
/* Architecture diagram palette (TOK-04). /admin/architecture is a hand-drawn SVG schematic
   that re-declared the whole palette inline: 173 hardcoded hex values across 8 distinct
   colours, one of which (#DB7884) was the PRE-2026-07-26 severity-high and so had already
   drifted out of the system.

   Declared once here, and deliberately theme-INDEPENDENT. The diagram is a dark schematic:
   mapping it onto the normal tokens would have inverted only some of them, because --off-white
   and --steel have light-mode overrides while --card and --navy-dark do not — the result in
   light mode would have been near-black text on still-dark boxes, the same dark-on-dark class
   of bug this audit already hit once. Values are byte-identical to what shipped, so the diagram
   renders exactly as before in both themes; what changes is that there is now one place to
   edit it. Scoped to the page so these names cannot leak into the app's palette.

   The two group colours are decorative section headings, not severity semantics — they must
   NOT be wired to --sev-*, or retuning severity would restyle a diagram legend. */
.vt-arch-diagram {
    --arch-ink: #FFFFFF;
    --arch-ink-muted: #B0B8C4;
    --arch-accent: #00BFFF;
    --arch-surface: #212B45;
    --arch-surface-2: #151B33;
    --arch-canvas: #0A0F24;
    --arch-group-vuln: #DB7884;
    --arch-group-ops: #4CAD7E;
}

/* Unread notification row (TOK-06). Derived from --teal so it tracks the accent in both
   themes; the previous inline style hardcoded Material blue and its own rgba tint. */
.vt-notification-unread {
    background: color-mix(in srgb, var(--teal) 8%, transparent);
    border-left: 3px solid var(--teal);
}

/* Circuit-failure bar (ERR-01). Was the scaffolded `background: lightyellow` styling an
   element that did not exist in any .razor file — so this rule had never once rendered.
   The element now exists in App.razor; these styles put it in the design system.
   Uses --state-error rather than --sev-critical: a dead websocket is an application state,
   not a vulnerability severity (COL-04). */
#blazor-error-ui {
    /* Self-contained, theme-INDEPENDENT palette (fixed 2026-07-26 by the visual pass).
       The first version set `background: var(--navy)` with `color: var(--mud-palette-text-primary)`.
       --navy has no light-mode override but the text token flips to near-black, so in light mode
       this bar rendered dark text on a dark background — the message was there and unreadable.
       Caught only by looking at dashboard/laptop-light.png; no test could see it.
       Same failure class I checked for in TOK-04 and then shipped here, so the same remedy: this
       bar is deliberately dark in both themes, therefore its inks must not follow the theme. */
    --err-canvas: #151B33;
    --err-ink: #F3F6FA;
    --err-ink-muted: #B0B8C4;
    --err-accent: #F95C66;        /* light red — legible ON the dark canvas in both themes */
    --err-accent-ink: #0A0F24;    /* abyss ink on that light red: ~5.5:1 */

    display: none;   /* the framework flips this to block on an unhandled circuit error */
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    z-index: 2000;   /* above MudBlazor's overlays (1300s) so a dialog cannot hide it */
    box-sizing: border-box;
    padding: 0.75rem 3rem 0.75rem 1.25rem;
    background: var(--err-canvas);
    color: var(--err-ink);
    border-top: 2px solid var(--err-accent);
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
    font-size: 0.9rem;
    line-height: 1.45;
    /* NO `display` here. A second `display: flex` used to sit at the end of this rule and, being
       later in the same block, silently overrode the `display: none` above it — so the bar was
       visible on every page for every user, permanently, and shipped that way. The flex layout
       belongs ONLY in the visible state below. (2026-07-26) */
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1rem;
}

/* The framework unhides the bar by setting inline `display: block`, which would otherwise defeat
   the flex layout. This is the only place display becomes flex, so the hidden default cannot be
   overridden by accident again. */
#blazor-error-ui[style*="block"] {
    display: flex !important;
}

#blazor-error-ui .blazor-error-text { flex: 1 1 20rem; min-width: 0; }

#blazor-error-ui .reload {
    color: var(--err-accent-ink);
    background: var(--err-accent);
    padding: 0.35rem 0.85rem;
    border-radius: var(--radius, 8px);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

#blazor-error-ui .reload:hover { filter: brightness(1.08); }

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.6rem;
    color: var(--err-ink-muted);
    font-size: 1rem;
    line-height: 1;
}

#blazor-error-ui .dismiss:hover { color: var(--err-ink); }

/* Keyboard parity: both controls are anchors, and .dismiss has no href, so it would
   otherwise be unreachable and unfocusable. tabindex is set in the markup. */
#blazor-error-ui .reload:focus-visible,
#blazor-error-ui .dismiss:focus-visible {
    /* --err-ink, not --teal: the accent flips to a dark teal in light mode, which would put a
       near-invisible focus ring on this permanently-dark bar. */
    outline: 2px solid var(--err-ink);
    outline-offset: 2px;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,...) no-repeat 1rem/1.8rem;
    padding: 1rem 1rem 1rem 3.7rem;
    color: #b32121;
}

.blazor-error-boundary::after {
    content: "An error has occurred.";
}

/* =========================================================================
   Command Palette (Cmd+K)
   Centered floating panel; dark backdrop; keyboard-driven.
   ========================================================================= */

.cmdk-backdrop {
    position: fixed; inset: 0; z-index: 1500;
    background: rgba(6, 11, 24, 0.55);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    display: flex; align-items: flex-start; justify-content: center;
    padding-top: 14vh;
    animation: cmdk-fade-in 120ms ease-out;
}

@keyframes cmdk-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.cmdk-panel {
    width: 100%; max-width: 640px;
    margin: 0 16px;
    background: #0F172A;
    border: 1px solid rgba(192, 200, 212, 0.12);
    border-radius: 12px;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6),
                0 0 0 1px rgba(0, 191, 255,0.06);
    overflow: hidden;
    animation: cmdk-slide-in 140ms ease-out;
}

@keyframes cmdk-slide-in {
    from { opacity: 0; transform: translateY(-8px); }
    to   { opacity: 1; transform: translateY(0); }
}

.cmdk-input-row {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 14px;
    border-bottom: 1px solid rgba(192, 200, 212, 0.08);
}
.cmdk-input-icon { color: var(--teal, #00BFFF); flex-shrink: 0; }
.cmdk-input {
    flex: 1; min-width: 0;
    background: transparent; border: none; outline: none;
    color: #FFFFFF;
    font: 500 0.98rem 'Inter', sans-serif;
    letter-spacing: -0.005em;
}
.cmdk-input::placeholder { color: rgba(192, 200, 212, 0.35); }
.cmdk-kbd, .cmdk-footer kbd {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.66rem;
    color: rgba(192, 200, 212, 0.55);
    background: rgba(192, 200, 212, 0.06);
    border: 1px solid rgba(192, 200, 212, 0.12);
    border-radius: 4px;
    padding: 2px 6px;
    letter-spacing: 0.04em;
}

.cmdk-results {
    max-height: 56vh;
    overflow-y: auto;
    padding: 6px;
}
.cmdk-results::-webkit-scrollbar { width: 6px; }
.cmdk-results::-webkit-scrollbar-thumb { background: rgba(192, 200, 212, 0.18); border-radius: 3px; }

.cmdk-empty {
    padding: 32px 14px;
    text-align: center;
    color: rgba(192, 200, 212, 0.45);
    font-size: 0.88rem;
}

.cmdk-group-label {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.62rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: rgba(192, 200, 212, 0.45);
    padding: 12px 12px 4px;
}
.cmdk-group-label:first-child { padding-top: 6px; }

.cmdk-item {
    display: flex; align-items: center; gap: 10px;
    width: 100%;
    padding: 9px 12px;
    background: transparent; border: none;
    border-radius: 6px;
    color: #B0B8C4;
    font: 500 0.92rem 'Inter', sans-serif;
    letter-spacing: -0.005em;
    cursor: pointer;
    text-align: left;
    transition: background 80ms ease;
}
.cmdk-item-active,
.cmdk-item:hover {
    background: rgba(0, 191, 255,0.10);
    color: #FFFFFF;
}
.cmdk-item-icon { color: rgba(192, 200, 212, 0.55); flex-shrink: 0; font-size: 1.05rem !important; }
.cmdk-item-active .cmdk-item-icon { color: var(--teal, #00BFFF); }
.cmdk-item-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdk-item-sub {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.72rem;
    color: rgba(192, 200, 212, 0.45);
    letter-spacing: 0.005em;
    flex-shrink: 0;
    max-width: 260px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.cmdk-footer {
    display: flex; gap: 14px;
    padding: 8px 14px;
    border-top: 1px solid rgba(192, 200, 212, 0.08);
    font-size: 0.72rem;
    color: rgba(192, 200, 212, 0.45);
}
.cmdk-footer kbd { margin-right: 4px; }

/* =========================================================================
   Micro-interactions — focus ring, row-hover, button-press, first-paint stagger.
   These run app-wide; opt-out via .no-anim on a parent if needed.
   ========================================================================= */

/* Focus ring: 2px teal outline + 4px halo. Works across inputs, buttons, table rows.
   body prefix raises specificity above Mud's own focus styles. */
body .mud-input.mud-focused .mud-input-outlined-border {
    border-color: var(--teal);
    border-width: 2px;
    box-shadow: 0 0 0 4px var(--teal-dim);
}
body .mud-button-root:focus-visible,
body .mud-icon-button:focus-visible,
body .mud-nav-link:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--teal), 0 0 0 6px var(--teal-dim);
}

/* Button press — momentary 1px scale-down, accent-aware. */
body .mud-button-root:active:not(:disabled) {
    transform: translateY(0) scale(0.985);
    transition-duration: 50ms;
}

/* Generic table row hover — mirrors the .vt-panel pattern outside of vt panels. */
.mud-table-body .mud-table-row {
    border-left: 2px solid transparent;
    transition: background var(--dur-quick) var(--ease-out),
                border-left-color var(--dur-quick) var(--ease-out);
}
.mud-table-body .mud-table-row:hover {
    border-left-color: var(--teal);
}

/* Entry stagger — KPI tiles, table rows, cards fade-up on first paint.
   Apply .vt-stagger on the parent; children animate with 30ms cumulative delay. */
@keyframes vt-fade-up {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}
.vt-stagger > * {
    animation: vt-fade-up var(--dur-base) var(--ease-emphasized) both;
}
.vt-stagger > *:nth-child(1) { animation-delay: 0ms; }
.vt-stagger > *:nth-child(2) { animation-delay: 30ms; }
.vt-stagger > *:nth-child(3) { animation-delay: 60ms; }
.vt-stagger > *:nth-child(4) { animation-delay: 90ms; }
.vt-stagger > *:nth-child(5) { animation-delay: 120ms; }
.vt-stagger > *:nth-child(6) { animation-delay: 150ms; }
.vt-stagger > *:nth-child(7) { animation-delay: 180ms; }
.vt-stagger > *:nth-child(8) { animation-delay: 210ms; }
.vt-stagger > *:nth-child(9) { animation-delay: 240ms; }
.vt-stagger > *:nth-child(10) { animation-delay: 270ms; }
.vt-stagger > *:nth-child(n+11) { animation-delay: 300ms; }

@media (prefers-reduced-motion: reduce) {
    .vt-stagger > *,
    .mud-button-root:active:not(:disabled),
    .mud-table-body .mud-table-row {
        animation: none !important;
        transition: none !important;
        transform: none !important;
    }
}

/* =========================================================================
   <Panel> component — used as a Razor wrapper around .vt-panel chrome.
   Adds slot-based header (label + count + actions) and an optional non-navy
   variant for light-mode pages.
   ========================================================================= */

.vt-panel-head-actions {
    margin-left: auto;
    display: flex; align-items: center; gap: 8px;
}
.vt-panel-head-count + .vt-panel-head-actions { margin-left: 12px; }
.vt-panel-body { padding: 0; }
.vt-panel-body.padded { padding: 16px 20px; }

/* Panel surface variant — adapts to light/dark via Mud surface tokens. */
.vt-panel.surface {
    background: var(--surface-panel);
    border-color: var(--mud-palette-lines-default);
}
.vt-panel.surface .vt-panel-head {
    border-bottom-color: var(--mud-palette-lines-default);
}
.vt-panel.surface .vt-panel-head-label { color: var(--teal); }

/* =========================================================================
   Density tokens — replace inline styles for opacity/letter-spacing/SLA colors.
   ========================================================================= */

.vt-meta-label {
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    opacity: 0.6;
    text-transform: uppercase;
}
.vt-meta-value { font-weight: 600; }
.vt-meta-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 1rem 1.5rem;
}
.vt-muted { opacity: 0.6; }
.vt-muted-strong { opacity: 0.75; }
/* Was opacity: 0.5, which composited text-primary down to 3.41:1 on the light-mode
   panel — below AA for the empty-state and zero-count text it carries. A semantic
   colour holds in both themes (7.0:1 light / 6.5:1 dark). Body prefix beats
   .mud-typography without !important. */
body .vt-muted-soft { color: var(--mud-palette-text-secondary); }
.vt-muted-faint { opacity: 0.4; }
/* Monospace section label inside MudMenu popovers (GROUP BY / DENSITY etc.) */
.vt-menu-section-label {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.16em;
    opacity: 0.55;
}
/* Suppression-rule preview panel (vuln list dialog + detail page) */
body .vt-suppression-preview {
    background: var(--sev-none-bg);
    border: 1px solid var(--sev-none-border);
}
/* Left accent edge for status-tinted cards (compliance panels etc.).
   Palette-var suffixes so the classes track the active Mud theme. */
body .vt-accent-left-success { border-left: 3px solid var(--mud-palette-success); }
body .vt-accent-left-error   { border-left: 3px solid var(--mud-palette-error); }
body .vt-accent-left-warning { border-left: 3px solid var(--mud-palette-warning); }
body .vt-accent-left-info    { border-left: 3px solid var(--mud-palette-info); }
body .vt-accent-left-primary { border-left: 3px solid var(--mud-palette-primary); }
body .vt-accent-left-default { border-left: 3px solid var(--mud-palette-lines-default); }
.vt-sla-breached { color: var(--state-error); font-weight: 700; }
.vt-sla-warning { color: var(--state-warning); font-weight: 600; }
.vt-sla-ok { color: var(--vt-success, #4CAD7E); font-weight: 600; }
.vt-mono-block {
    background: var(--mud-palette-background);
    font-family: 'Consolas', 'Courier New', monospace;
    font-size: 0.85rem;
    white-space: pre-wrap;
    line-height: 1.6;
    border-radius: 4px;
}
.vt-help-icon {
    font-size: 0.95rem;
    opacity: 0.55;
    cursor: help;
    vertical-align: middle;
    margin-left: 6px;
}
.vt-vornin-insights {
    background: var(--mud-palette-background);
    border-left: 3px solid var(--mud-palette-secondary);
}
.vt-vornin-insights-pending {
    background: var(--mud-palette-background);
    border-left: 3px solid var(--mud-palette-lines-default);
}
.vt-prose {
    white-space: pre-wrap;
    line-height: 1.7;
    opacity: 0.8;
}
.vt-section-title { font-weight: 700; }
.vt-tab-bordered { border-top: 1px solid var(--mud-palette-lines-default); }
.vt-evidence-card {
    background: var(--mud-palette-background);
    border-radius: 4px;
}
.vt-evidence-pre {
    font-family: 'Consolas', 'Courier New', monospace;
    font-size: 0.82rem;
    white-space: pre-wrap;
    line-height: 1.5;
    margin: 0;
}
.vt-meta-footer { opacity: 0.5; }
.vt-mono { font-family: monospace; }
/* Certificate detail drawer title — long subjects (wildcards, SANs) wrap instead of clipping the close button. */
.vt-cert-detail-title { word-break: break-word; min-width: 0; }
.vt-changelog-content { white-space: pre-line; }
.vt-setup-complete-copy { max-width: 480px; }
.vt-wish-vote-col { min-width: 60px; }
.vt-wish-vote-count { line-height: 1; }
.vt-wish-description { white-space: pre-wrap; }
.vt-error-cell { max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vt-chart-donut-wrap { min-height: 300px; }
.vt-num-cell { text-align: right; }
.vt-cell-error { color: var(--mud-palette-error); }
.vt-meta-secondary { opacity: 0.6; font-size: 0.85em; }
.vt-as-host { font-size: 13px; }
.vt-as-target { font-size: 11px; opacity: 0.6; margin-top: 2px; }
.vt-risk-score { font-weight: 600; font-size: 13px; }
.vt-risk-score.critical { color: var(--sev-critical); }
.vt-risk-score.high { color: var(--sev-high); }
.vt-risk-score.medium { color: var(--sev-medium); }
.vt-risk-score.low { color: var(--sev-low); }
.vt-risk-score-empty { opacity: 0.4; font-size: 12px; }
.vt-report-iframe { width: 100%; height: 80vh; border: none; }
.vt-scan-running-card { cursor: pointer; }
.vt-agent-dot-inline { color: var(--steel); }
.vt-agent-dot-inline.online { color: var(--sev-low); }
.vt-agent-dot-inline.busy { color: var(--sev-medium); }
.vt-repo-branch-select { max-width: 200px; }
.vt-repo-branch-select-narrow { max-width: 180px; }
.vt-repo-branch-chip { cursor: pointer; }
.vt-repo-branch-spinner { width: 14px; height: 14px; margin-left: 4px; }
.vt-repo-import-row { margin-bottom: 8px; }
.vt-scan-name-edit { max-width: 400px; }
.vt-prewrap { white-space: pre-wrap; }
.vt-break-all { word-break: break-all; }
.vt-row-clickable { cursor: pointer; }
.vt-cvss-score-num { font-weight: 700; }
.vt-tooltip-content { max-width: 280px; }
.vt-tooltip-list { padding-left: 18px; margin: 6px 0 0; }
.vt-evidence-pre { font-family: var(--font-mono); font-size: 0.8rem; white-space: pre-wrap; margin: 0; }
.vt-evidence-pre-body { max-height: 400px; overflow-y: auto; }
.vt-evidence-pre-json { overflow-x: auto; }
.vt-badge-preview { padding: 16px; border-radius: var(--radius); }
.vt-badge-preview-light { background: #FFFFFF; border: 1px solid var(--slate-100); }
.vt-badge-preview-dark { background: #1A1A2E; border: 1px solid rgba(255,255,255,0.10); }
.vt-badge-code { padding: 12px; background: var(--muted-fill); border-radius: var(--radius); overflow-x: auto; }
.vt-badge-code-text { font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; }
.vt-scanner-card { border: 1px solid var(--mud-palette-lines-default); }
.vt-review-summary { border: 1px solid var(--hairline); border-radius: var(--radius); overflow: hidden; }
.vt-review-summary td { padding: 8px 14px; border-bottom: 1px solid var(--hairline); }
.vt-review-summary tr:last-child td { border-bottom: none; }
.vt-review-summary td:first-child { width: 40%; opacity: 0.75; }
.vt-tabs-hairline .mud-tabs-toolbar { border-bottom: 1px solid var(--hairline); }
.vt-bulk-bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px; margin-bottom: 8px; background: var(--teal-dim); border: 1px solid rgba(0,191,255,0.25); border-radius: var(--radius); flex-wrap: wrap; }
/* Keyboard skip-to-content link — hidden until focused. */
.vt-skip-link { position: absolute; top: -48px; left: 8px; z-index: 10000; background: var(--teal); color: var(--navy-dark); padding: 8px 16px; border-radius: var(--radius); text-decoration: none; font-weight: 600; transition: top 0.15s ease; }
.vt-skip-link:focus { top: 8px; }
.vt-compliance-card { height: 100%; box-shadow: var(--shadow-2); }
.vt-qr-frame { padding: 12px; background: #FFFFFF; border-radius: var(--radius); max-width: 220px; }
.vt-totp-field { max-width: 200px; }
.vt-attr-id { font-size: 0.85rem; }
.vt-status-cell { width: 6px; height: 22px; margin-right: 1px; border-radius: 1px; }
.vt-incident-update { border-left: 2px solid var(--mud-palette-lines-default); padding-left: 8px; }
.vt-panel-accent-warning { border-left: 4px solid var(--mud-palette-warning); }
.vt-panel-accent-info { border-left: 4px solid var(--mud-palette-info); }
.vt-compliance-card-head {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
}
.vt-compliance-card-title {
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
}
.vt-compliance-card-title .mud-typography-h6 {
    line-height: 1.25;
    word-break: break-word;
}
.vt-compliance-card-score {
    flex-shrink: 0;
    white-space: nowrap;
    line-height: 1;
}

/* Guided first-run / gap checklist — numbered steps used on the Compliance empty state. */
.vt-checklist {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}
.vt-checklist-step {
    display: flex;
    align-items: flex-start;
    gap: 0.9rem;
}
.vt-checklist-num {
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.9rem;
    color: var(--teal);
    background: var(--muted-fill);
    border: 1px solid var(--mud-palette-lines-default);
    line-height: 1;
}

/* =========================================================================
   <KpiTile> component — standalone card variant of .vt-kpi (works outside .vt-hero).
   ========================================================================= */

.vt-kpi-tile {
    padding: 18px 22px;
    background: var(--surface-panel);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--radius-lg);
    transition: border-color var(--dur-quick) var(--ease-out),
                box-shadow var(--dur-quick) var(--ease-out);
    display: flex; flex-direction: column; gap: 8px;
    min-width: 0;
}
.vt-kpi-tile:hover {
    border-color: rgba(0,191,255,0.35);
    box-shadow: var(--shadow-2);
}
/* Clickable KpiTile wrapper — lets a whole tile act as a link without altering the card. */
.vt-kpi-tile-anchor {
    display: block;
    height: 100%;
    text-decoration: none;
    color: inherit;
}
.vt-kpi-tile-anchor:hover .vt-kpi-tile {
    border-color: rgba(0,191,255,0.35);
    box-shadow: var(--shadow-2);
}
.vt-kpi-tile-anchor:focus-visible { outline: none; }
.vt-kpi-tile-anchor:focus-visible .vt-kpi-tile {
    outline: 2px solid var(--teal);
    outline-offset: 2px;
    border-color: rgba(0,191,255,0.35);
}
.vt-kpi-tile-head {
    display: flex; align-items: center; gap: 10px;
}
.vt-kpi-tile-icon {
    color: var(--teal);
    font-size: 1rem !important;
    width: 18px; height: 18px;
}
.vt-kpi-tile-label {
    font-family: var(--font-mono);
    font-size: 0.66rem; letter-spacing: 0.16em; text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}
.vt-kpi-tile-value {
    font-size: 2rem; font-weight: 700; line-height: 1.1;
    letter-spacing: -0.03em;
    color: var(--mud-palette-text-primary);
    display: flex; align-items: baseline; gap: 8px;
}
.vt-kpi-tile-unit {
    font-size: 0.78rem; font-weight: 500; letter-spacing: 0;
    color: var(--mud-palette-text-secondary);
}
.vt-kpi-tile-delta {
    font-family: var(--font-mono);
    font-size: 0.74rem; letter-spacing: 0.04em;
    display: inline-flex; align-items: center; gap: 4px;
}
.vt-kpi-tile-delta.up    { color: var(--state-success); }
.vt-kpi-tile-delta.down  { color: var(--state-error); }
.vt-kpi-tile-delta.flat  { color: var(--mud-palette-text-secondary); }
/* Placeholder rendered when ReserveTrailSlots=true and Delta is absent. Reserves
   the same vertical footprint as a real delta line so KPI tiles stay uniform. */
.vt-kpi-tile-delta-empty { visibility: hidden; }
.vt-kpi-tile-trend { margin-top: 2px; opacity: 0.75; }

/* Accent variants for the value (mirrors .vt-kpi-value classes inside hero) */
/* State lineage, not the severity ramp: a KPI band is a state. The literals here
   were the pre-2026-07-26 ramp and were dark-mode only. */
.vt-kpi-tile-value.accent  { color: var(--teal); }
.vt-kpi-tile-value.alert   { color: var(--state-error); }
.vt-kpi-tile-value.success { color: var(--state-success); }
/* Middle band. Was Accent.Copper -> .accent (teal), which broke the rule that teal
   marks action: a mid-range posture score is not a thing you can click. */
.vt-kpi-tile-value.warn    { color: var(--state-warning); }

/* =========================================================================
   Severity bar list — replaces dense table on dashboard "Open by Severity"
   panel. Each row is a flex row: SeverityBadge + bar track + count, all
   wrapped in an anchor that filters /vulnerabilities. Bars use the brand
   semantic palette (red / orange / green) — teal stays action-only.
   ========================================================================= */
.vt-sev-list {
    display: flex; flex-direction: column;
    gap: 0;
}
/* GRID, not flex. Under flex the <SeverityBadge> sized to its own label, and the
   labels differ by a factor of two: "CRITICAL" plus its alert glyph measures ~100px
   against "LOW" at ~55px, so the four bar tracks each began at a different x and the
   panel read as misaligned. A fixed first column parks every track on one edge; the
   pill keeps its natural width via justify-self, so it is the COLUMN that is uniform,
   not the badge (stretching the pill would make "LOW" look padded out).
   108px is the measured width of the widest badge (Critical, mono 0.72rem +
   0.06em tracking + icon + padding + border) with a few px of headroom. */
.vt-sev-row {
    display: grid;
    grid-template-columns: 108px 1fr auto;
    align-items: center; gap: 14px;
    padding: 12px 18px;
    text-decoration: none;
    color: inherit;
    border-bottom: 1px solid rgba(46,58,89,0.30);
    transition: background var(--dur-quick) var(--ease-out);
    cursor: pointer;
}
.vt-sev-row:last-child { border-bottom: none; }
.vt-sev-row:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
/* Zero rows are shown on purpose (absence and zero are different statements), but
   they are not work — drop the count to secondary ink so the eye skips them. */
.vt-sev-row.is-zero .vt-sev-count { color: var(--mud-palette-text-secondary); font-weight: 400; }
.vt-sev-row:hover { background: var(--teal-dim); }
/* The badge sits at the start of its fixed column rather than filling it. */
.vt-sev-row > .vt-severity { justify-self: start; }
.vt-sev-track {
    /* No `flex: 1` — the row is a grid and its middle `1fr` column does the stretching. */
    height: 6px;
    background: rgba(46,58,89,0.40);
    border-radius: 3px;
    overflow: hidden;
    min-width: 0;
}
.vt-sev-fill {
    height: 100%;
    transition: width var(--dur-base) var(--ease-out);
}
/* Canonical severity tokens — these bars sit in the same row as a <SeverityBadge>,
   which already reads --sev-*. They were left on the pre-2026-07-26 ramp, so one
   severity rendered in two colours a few pixels apart, and Critical/High were the
   same hue separated only by opacity (the exact deltaE 11.8 defect COL-01 fixed
   everywhere else). Theme-aware now too — the old literals were dark-mode only. */
.vt-sev-fill.critical { background: var(--sev-critical); }
.vt-sev-fill.high     { background: var(--sev-high); }
.vt-sev-fill.medium   { background: var(--sev-medium); }
.vt-sev-fill.low      { background: var(--sev-low); }
.vt-sev-fill.info     { background: var(--sev-none); }
.vt-sev-count {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--off-white);
    min-width: 36px;
    text-align: right;
    flex-shrink: 0;
}

/* Sparkline — cold teal stroke + faint area fill. Renders 30-day trend
   inline below the KPI value. SVG viewBox is 100x24, scaled non-uniformly
   to fill the row width. */
.vt-kpi-tile-spark {
    margin-top: 4px;
    display: flex; align-items: center; gap: 8px;
}
.vt-kpi-tile-spark svg {
    flex: 1;
    height: 24px;
    width: 100%;
    display: block;
}
.vt-kpi-tile-spark-line {
    fill: none;
    stroke: var(--teal);
    stroke-width: 1.4;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}
.vt-kpi-tile-spark-fill {
    fill: rgba(0,191,255,0.12);
    stroke: none;
}
.vt-kpi-tile-spark-label {
    font-family: var(--font-mono);
    font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--muted);
    flex-shrink: 0;
}
/* Placeholder rendered when ReserveSparkSlot=true and Spark data is absent.
   Reserves the same vertical footprint as the real spark so KPI tiles stay
   uniform height across a row. */
.vt-kpi-tile-spark-empty {
    height: 24px;
}

/* =========================================================================
   <SectionHeader> — secondary header (eyebrow + title + actions) used inside
   pages between hero and content.
   ========================================================================= */

.vt-section-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
    margin: 24px 0 12px;
}
.vt-section-head-title {
    margin: 4px 0 0; font-size: 1.15rem; font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--mud-palette-text-primary);
}
.vt-section-head-sub {
    margin: 4px 0 0; font-size: 0.85rem; color: var(--mud-palette-text-secondary);
}
.vt-section-head-actions { display: flex; gap: 8px; }

/* =========================================================================
   <TagBadge> — compact icon-only tag for KEV / EXPLOIT / RANSOM / REACHED markers.
   Tooltip carries the label; the badge itself is just iconography.
   ========================================================================= */

.vt-tag {
    display: inline-flex; align-items: center; justify-content: center; gap: 4px;
    width: 22px; height: 22px;
    border-radius: 4px;
    font-family: var(--font-mono);
    font-size: 0.66rem; font-weight: 700; letter-spacing: 0.04em;
    border: 1px solid transparent;
    cursor: default;
    text-transform: uppercase;
}
.vt-tag .mud-icon-root { font-size: 0.95rem !important; }
/* Text variant — shows the label inline (e.g. "KEV", "REACHED"). */
.vt-tag.with-text {
    width: auto;
    height: 20px;
    padding: 0 8px;
    letter-spacing: 0.06em;
}
.vt-tag.kev       { background: var(--sev-high-bg);   color: var(--sev-high);     border-color: var(--sev-high-border); }
.vt-tag.exploit   { background: var(--sev-medium-bg); color: var(--sev-medium);   border-color: var(--sev-medium-border); }
.vt-tag.ransom    { background: var(--tag-ransom-bg); color: var(--sev-critical); border-color: var(--tag-ransom-border); }
.vt-tag.reached   { background: rgba(0,191,255,0.10); color: var(--teal);       border-color: rgba(0,191,255,0.30); }
.vt-tag.unreached { background: var(--sev-none-bg);   color: var(--sev-none);     border-color: var(--sev-none-border); }

.vt-tags { display: inline-flex; gap: 4px; flex-wrap: nowrap; }

/* =========================================================================
   Vulnerability priority chip (table column + mobile card + drawer header).
   Inline `--prio-color` is set from C# so the same markup can adopt any of
   the four severity-token colors without a class explosion.
   ========================================================================= */
.vt-priority {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 6px;
    border: 1px solid color-mix(in srgb, var(--prio-color, var(--sev-none)) 36%, transparent);
    background: color-mix(in srgb, var(--prio-color, var(--sev-none)) 14%, transparent);
    color: var(--prio-color, var(--sev-none));
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    white-space: nowrap;
}
.vt-priority-label { font-weight: 700; }
.vt-priority-score { opacity: 0.75; font-size: 0.66rem; }
.vt-priority-chip {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 6px;
    border: 1px solid color-mix(in srgb, var(--prio-color, var(--sev-none)) 36%, transparent);
    background: color-mix(in srgb, var(--prio-color, var(--sev-none)) 14%, transparent);
    color: var(--prio-color, var(--sev-none));
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
}

/* Secondary context row inside the Vulnerability cell — scanner · CVE · CVSS · EPSS · evidence. */
.vt-vuln-cell { display: flex; flex-direction: column; gap: 2px; }
/* Vuln metadata sub-line (scanner·CVE·CVSS) — real content, not decoration.
   Was opacity 0.65 which dropped below 4.5:1 on the lifted --card surface. */
.vt-vuln-sub { font-size: 0.7rem; opacity: 0.82; display: flex; flex-wrap: wrap; gap: 4px; }
.vt-vuln-sub-item { white-space: nowrap; }
.vt-vuln-sub-sep { opacity: 0.5; }

/* Severity dot inside the priority chip — raw severity at a glance now that the
   standalone Severity column is gone. */
.vt-priority-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    flex: 0 0 auto;
    align-self: center;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.25);
}
/* Scanner label + glyph at the head of the row sub-line. Short label restores the
   finding category; icon is a quiet visual anchor. */
.vt-vuln-sub-scanner {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-weight: 500;
    white-space: nowrap;
}
.vt-vuln-sub-scanner-icon {
    font-size: 0.9rem !important;
    width: 0.9rem; height: 0.9rem;
    opacity: 0.7;
    flex: 0 0 auto;
}
/* De-emphasised per-row affordances — present but receding until the row is
   hovered/focused, so the grid reads as data, not a wall of buttons. */
.vt-owner-add,
.vt-row-actions-btn { opacity: 0.5; transition: opacity 0.12s ease; }
tr:hover .vt-owner-add,
tr:hover .vt-row-actions-btn,
.vt-owner-add:hover, .vt-owner-add:focus-visible,
.vt-row-actions-btn:hover, .vt-row-actions-btn:focus-visible,
.vt-row-actions-btn[aria-expanded="true"] { opacity: 1; }

/* Quick-view "peek" compact facts strip (replaces the old 6-cell metadata grid). */
.vt-peek-facts {
    display: flex; flex-wrap: wrap;
    gap: 6px 18px;
    font-size: 0.85rem;
    align-items: baseline;
}
.vt-peek-fact { white-space: nowrap; }
/* Muted em-dash for SLA on closed (terminal) findings — the live clock no longer applies. */
.vt-sla-na { opacity: 0.6; }  /* was 0.4 — SLA "N/A" is content, keep it legible */

/* "Vornin AI" attribution tag on AI-written Description/Solution panels. */
.vt-ai-tag {
    display: inline-flex; align-items: center; gap: 4px;
    font-family: var(--font-mono);
    font-size: 0.6rem; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--mud-palette-secondary, var(--teal));
    white-space: nowrap;
}
.vt-ai-tag .mud-icon-root { font-size: 0.85rem !important; }
.vt-peek-fact-k {
    font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.12em;
    text-transform: uppercase; opacity: 0.55; margin-right: 6px;
}

/* Reasons list inside the Recommended Action panel (detail page + quick-view). */
.vt-prio-reasons {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.vt-prio-reasons li {
    list-style: none;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--sev-none-bg);
    color: var(--sev-none);
    border: 1px solid var(--sev-none-border);
    font-size: 0.72rem;
}

/* =========================================================================
   Gauge ring grid — 4-up at md+, 2-up below 600px. Pairs with <GaugeRing>.
   ========================================================================= */
.vt-gauge-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
}
@media (max-width: 599px) {
    .vt-gauge-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* =========================================================================
   Equal-height paired-row utility — MudGrid items default to align-items:
   stretch but Panel (.vt-panel) lacks height:100%, so the shorter panel
   falls short of its sibling. Apply .vt-equal-row to MudGrid wrappers to
   stretch siblings + flex their panels and bodies to equal height.
   ========================================================================= */
.vt-equal-row { align-items: stretch; }
.vt-equal-row > .mud-grid-item { display: flex; }
.vt-equal-row > .mud-grid-item > .vt-panel {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}
.vt-equal-row > .mud-grid-item > .vt-panel > .vt-panel-body { flex: 1 1 auto; }

.vt-panel-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}

/* Vornin Insights — markdown remediation block. Lists default to 40px
   padding-inline-start which pushes content past the surrounding text;
   re-align so list bullets sit just inside the panel's pa-4 inset. */
.vt-vornin-insights-md { padding-left: 1.25rem; }
.vt-vornin-insights-md > :first-child { margin-top: 0; }
.vt-vornin-insights-md > :last-child  { margin-bottom: 0; }
.vt-vornin-insights-md ul,
.vt-vornin-insights-md ol {
    padding-inline-start: 1.25rem;
    margin: 0 0 0.75rem 0;
}
.vt-vornin-insights-md li { margin-bottom: 0.25rem; }
.vt-vornin-insights-md p  { margin: 0 0 0.5rem 0; }
.vt-vornin-insights-md code {
    font-family: var(--font-mono);
    font-size: 0.85em;
    padding: 1px 4px;
    border-radius: 3px;
    background: rgba(192,200,212,0.10);
}

/* Rendered SCANNER advisory markdown (AdvisoryText.razor). Distinct from the AI
   insights block above because the shapes are different: a GitHub/OSV advisory brings
   ATX headings, block quotes, thematic breaks, tables and fenced proof-of-concept code,
   none of which the insights styling covers.

   Two rules here are load-bearing rather than decorative. `white-space: normal` undoes
   the `vt-prewrap` / `vt-prose` class the call site still passes for its plain-text
   sibling — left at pre-wrap, every rendered block gains the source's own newlines on
   top of its margin and the panel reads double-spaced. And `pre { overflow-x: auto }`
   keeps a long PoC line inside the panel: an advisory fence is arbitrary third-party
   code with no wrap points, and without this it stretches the two-column grid and
   scrolls the whole page sideways. 2026-08-26. */
.vt-advisory-md { white-space: normal; }
.vt-advisory-md > :first-child { margin-top: 0; }
.vt-advisory-md > :last-child  { margin-bottom: 0; }
.vt-advisory-md p { margin: 0 0 0.6rem 0; }
.vt-advisory-md h1,
.vt-advisory-md h2,
.vt-advisory-md h3,
.vt-advisory-md h4,
.vt-advisory-md h5,
.vt-advisory-md h6 {
    /* Advisory headings are section labels inside a panel, not page headings — one
       weight, one size, so "## Summary" cannot out-shout the panel's own title. */
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1.35;
    margin: 1rem 0 0.35rem 0;
    text-transform: none;
}
.vt-advisory-md ul,
.vt-advisory-md ol {
    padding-inline-start: 1.25rem;
    margin: 0 0 0.6rem 0;
}
.vt-advisory-md li { margin-bottom: 0.2rem; }
.vt-advisory-md code {
    font-family: var(--font-mono);
    font-size: 0.85em;
    padding: 1px 4px;
    border-radius: 3px;
    background: rgba(192,200,212,0.10);
    overflow-wrap: anywhere;
}
.vt-advisory-md pre {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    line-height: 1.45;
    margin: 0 0 0.6rem 0;
    padding: 0.6rem 0.75rem;
    border-radius: 4px;
    border: 1px solid var(--mud-palette-lines-default);
    background: rgba(192,200,212,0.07);
    max-height: 22rem;
    overflow: auto;
}
.vt-advisory-md pre code {
    padding: 0;
    background: none;
    overflow-wrap: normal;
}
.vt-advisory-md blockquote {
    margin: 0 0 0.6rem 0;
    padding: 0.1rem 0 0.1rem 0.75rem;
    border-inline-start: 2px solid var(--mud-palette-secondary);
    opacity: 0.9;
}
.vt-advisory-md hr {
    border: 0;
    border-top: 1px solid var(--mud-palette-lines-default);
    margin: 0.9rem 0;
}
.vt-advisory-md table {
    border-collapse: collapse;
    display: block;
    width: max-content;
    max-width: 100%;
    overflow-x: auto;
    margin: 0 0 0.6rem 0;
}
.vt-advisory-md th,
.vt-advisory-md td {
    border: 1px solid var(--mud-palette-lines-default);
    padding: 3px 8px;
    text-align: start;
}
.vt-advisory-md img { max-width: 100%; }

/* Truncate text to N lines with an ellipsis. Width is constrained by the
   parent — pair with `max-width: 0` on the host cell when used in tables. */
.vt-clamp-2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
    overflow: hidden;
    text-overflow: ellipsis;
    /* break at word boundaries first (scan names wrap at " - ", not mid-token
       like "VULNW EB.CO" on narrow/mobile columns); only split a token when it
       genuinely can't fit. 2026-07-24. */
    word-break: normal;
    overflow-wrap: anywhere;
    line-height: 1.35;
}

/* =========================================================================
   <MudDataGrid> overrides for the vulnerabilities list.
   Stronger zebra striping + clickable-row hover affordance so the slim list
   reads cleanly even at high density.
   ========================================================================= */

.vt-vulns-grid .mud-table-row { cursor: pointer; }

/* Alternating row tint — bumped to be visible against the navy panel.
   The accent at 0.045 vanished on dark mode, so we layer a subtle white wash
   on top so the band is readable in both palettes. */
.vt-vulns-grid tbody .mud-table-row:nth-child(even) > td {
    background: rgba(192, 200, 212, 0.055);
}
body .vt-vulns-grid tbody .mud-table-row:hover > td {
    background: rgba(0, 191, 255,0.14);
}

/* Sticky header on the vulnerabilities grid. Default rendering shows the
   underlying rows through the head as you scroll — force a solid surface so
   column titles stay readable. Match the page surface so the header blends
   with the panel chrome rather than fighting it.
   KEEP !important — Mud sets backdrop-filter inline on sticky table headers. */
.vt-vulns-grid .mud-table-head .mud-table-cell,
.vt-vulns-grid thead th {
    background: var(--mud-palette-surface) !important;
    backdrop-filter: none !important;
}

/* =========================================================================
   Vulnerabilities page search field — clamp to 24px to match Size.Small
   chips. Selectors use plain .vt-vuln-search (no body prefix)
   because data-mud-theme may not be set at the time these rules cascade,
   which caused the entire selector to not match and left the field at its
   natural content height (~240px).
   ========================================================================= */
.vt-vuln-search,
.vt-vuln-search.mud-input-control-margin-dense {
    min-height: 0 !important;
    height: 24px !important;
    max-height: 24px !important;
    margin-top: 0 !important;
    margin-bottom: 0 !important;
    align-self: center !important;
    overflow: hidden !important;
}
.vt-vuln-search .mud-input-control-input-container,
.vt-vuln-search .mud-input-control,
.vt-vuln-search .mud-input,
.vt-vuln-search .mud-input-outlined,
.vt-vuln-search .mud-input-slot {
    min-height: 0 !important;
    height: 24px !important;
    max-height: 24px !important;
    margin-top: 0 !important;
    margin-bottom: 0 !important;
}
.vt-vuln-search div.mud-input.mud-input-text {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
}
.vt-vuln-search .mud-input-outlined-border,
.vt-vuln-search fieldset {
    top: 0 !important;
    height: 24px !important;
    min-height: 24px !important;
    max-height: 24px !important;
}
.vt-vuln-search input {
    height: 22px !important;
    min-height: 22px !important;
    line-height: 22px !important;
    padding: 0 8px !important;
    font-size: 0.78rem !important;
}
.vt-vuln-search .mud-input-adornment {
    height: 22px !important;
    margin-top: 0 !important;
}
.vt-vuln-search .mud-input-adornment .mud-icon-root {
    font-size: 1rem !important;
}

/* =========================================================================
   <Skeleton> — line / card / table-row placeholders.
   Replaces generic spinners during data loads. CSS-only shimmer.
   ========================================================================= */

@keyframes vt-shimmer {
    0%   { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}
.vt-skeleton {
    display: block;
    background: linear-gradient(
        90deg,
        var(--muted-fill) 0%,
        rgba(192,200,212,0.14) 50%,
        var(--muted-fill) 100%
    );
    background-size: 200% 100%;
    animation: vt-shimmer 1.4s linear infinite;
    border-radius: 4px;
}
.vt-skeleton-line { height: 0.85rem; width: 100%; }
.vt-skeleton-line.short { width: 40%; }
.vt-skeleton-line.medium { width: 65%; }
.vt-skeleton-line.long { width: 90%; }
.vt-skeleton-card {
    height: 96px;
    border-radius: var(--radius-lg);
}
.vt-skeleton-row {
    height: 48px;
    border-radius: 4px;
    margin-bottom: 6px;
}
.vt-skeleton-stack { display: flex; flex-direction: column; gap: 8px; }

@media (prefers-reduced-motion: reduce) {
    .vt-skeleton { animation: none; }
}

/* =========================================================================
   <ActionMenu> — primary button + overflow dots, replaces stacked button clusters.
   Visual: primary action sits flush with a divider, then the overflow trigger.
   ========================================================================= */

.vt-action-menu {
    display: inline-flex; align-items: stretch;
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow-1);
}
body .vt-action-menu .mud-button-root.vt-action-primary {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}
body .vt-action-menu .vt-action-overflow {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    border-left: 1px solid rgba(13,31,60,0.18);
}

/* =========================================================================
   Attributes grid — labeled key/value pairs for detail-page metadata.
   Used inside <Panel Label="ATTRIBUTES"> on VulnerabilityDetail.
   ========================================================================= */

.vt-attrs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px 24px;
}
.vt-attr-label {
    font-family: var(--font-mono);
    font-size: 0.62rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 4px;
}
.vt-attr-value {
    font-size: 0.92rem;
    color: var(--mud-palette-text-primary);
    display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
}
.vt-attr-sub {
    font-size: 0.78rem;
    color: var(--mud-palette-text-secondary);
    font-weight: 400;
}
.vt-attr-state {
    font-family: var(--font-mono);
    font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em;
    padding: 2px 8px;
    border-radius: 4px;
    border: 1px solid transparent;
    text-transform: uppercase;
}
.vt-attr-state.alert   { background: var(--sev-high-bg);   color: var(--sev-high);   border-color: var(--sev-high-border); }
.vt-attr-state.warn    { background: var(--sev-medium-bg); color: var(--sev-medium); border-color: var(--sev-medium-border); }
.vt-attr-state.success { background: var(--sev-low-bg);    color: var(--sev-low);    border-color: var(--sev-low-border); }

/* =========================================================================
   <SeverityBadge> size variants — sm uses tighter padding, md is default.
   ========================================================================= */

.vt-severity {
    display: inline-flex; align-items: center; gap: 4px;
    border-radius: 4px;
    font-family: var(--font-mono);
    font-weight: 700; letter-spacing: 0.06em;
    border: 1px solid transparent;
    text-transform: uppercase;
}
.vt-severity.sm { padding: 1px 6px; font-size: 0.62rem; }
.vt-severity.md { padding: 3px 10px; font-size: 0.72rem; }
.vt-severity .mud-icon-root { font-size: 0.85rem !important; }
.vt-severity.critical { background: var(--sev-critical-bg); color: var(--sev-critical); border-color: var(--sev-critical-border); }
.vt-severity.high     { background: var(--sev-high-bg);     color: var(--sev-high);     border-color: var(--sev-high-border); }
.vt-severity.medium   { background: var(--sev-medium-bg);   color: var(--sev-medium);   border-color: var(--sev-medium-border); }
.vt-severity.low      { background: var(--sev-low-bg);      color: var(--sev-low);      border-color: var(--sev-low-border); }
.vt-severity.info,
.vt-severity.none     { background: var(--sev-none-bg);     color: var(--sev-none);     border-color: var(--sev-none-border); }

/* ==========================================================================
   Targets page — sector rail, health rings, agent dots, target/group pills,
   action buttons. Hoisted from Targets.razor's inline <style> block and
   rebased onto v3 tokens (--teal / --steel / --muted / --border-dark).
   ========================================================================== */

.vt-shell {
    display: grid; grid-template-columns: 256px minmax(0, 1fr);
    gap: 24px; align-items: start;
}
@media (max-width: 980px) {
    .vt-shell { grid-template-columns: 1fr; gap: 16px; }
    .vt-rail { position: static; top: auto; }
}

.vt-rail {
    position: sticky; top: 24px;
    background: var(--navy);
    border: 1px solid var(--border-dark);
    border-radius: var(--radius-lg);
    padding: 8px;
}
.vt-rail-header {
    padding: 14px 14px 14px;
    border-bottom: 1px solid var(--border-dark);
    margin-bottom: 8px;
}
.vt-rail-header-label {
    font-family: var(--font-mono);
    font-size: 0.64rem; letter-spacing: 0.22em; text-transform: uppercase;
    color: var(--muted);
}
.vt-rail-header-title {
    margin-top: 4px;
    font-size: 0.95rem; font-weight: 600; color: var(--off-white);
}
.vt-rail-item {
    position: relative;
    display: flex; align-items: center; gap: 10px;
    width: 100%; padding: 10px 12px 10px 14px;
    background: transparent;
    border: none; border-left: 2px solid transparent;
    border-radius: 0 6px 6px 0;
    color: var(--steel); font: inherit; font-size: 0.9rem;
    cursor: pointer; text-align: left;
    transition: background var(--dur-quick) var(--ease-out),
                color var(--dur-quick) var(--ease-out),
                border-color var(--dur-quick) var(--ease-out);
}
.vt-rail-item:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.vt-rail-item:hover { background: rgba(255,255,255,0.04); color: var(--off-white); }
.vt-rail-item.active {
    background: var(--teal-dim);
    border-left-color: var(--teal);
    color: var(--off-white);
}
.vt-rail-swatch { width: 8px; height: 8px; border-radius: 2px; background: var(--muted); flex-shrink: 0; }
.vt-rail-all .vt-rail-swatch { background: transparent; border: 1px solid var(--steel); }
.vt-rail-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vt-rail-count {
    font-family: var(--font-mono);
    font-size: 0.75rem; color: var(--muted); letter-spacing: 0.02em;
}
.vt-rail-item.active .vt-rail-count { color: var(--teal); }
.vt-rail-delete {
    opacity: 0; padding: 2px; margin-left: 2px;
    background: transparent; border: none; color: var(--muted);
    cursor: pointer; display: flex; border-radius: 4px;
    transition: opacity var(--dur-quick) var(--ease-out),
                color var(--dur-quick) var(--ease-out),
                background var(--dur-quick) var(--ease-out);
}
.vt-rail-item:hover .vt-rail-delete { opacity: 0.7; }
.vt-rail-delete:hover {
    opacity: 1;
    color: var(--state-error);
    background: color-mix(in srgb, var(--state-error) 10%, transparent);
}
.vt-rail-add {
    display: flex; align-items: center; gap: 8px;
    width: 100%; padding: 10px 14px;
    background: transparent;
    border: 1px dashed var(--border-dark-2);
    border-radius: 6px; margin-top: 10px;
    color: var(--muted); font: inherit; font-size: 0.85rem;
    cursor: pointer;
    transition: color var(--dur-quick) var(--ease-out),
                border-color var(--dur-quick) var(--ease-out);
}
.vt-rail-add:hover { color: var(--teal); border-color: rgba(0,191,255,0.45); }

.vt-target-name { font-weight: 600; color: var(--mud-palette-text-primary); margin-bottom: 2px; letter-spacing: -0.005em; }
.vt-target-host {
    font-family: var(--font-mono);
    font-size: 0.76rem; color: var(--muted); letter-spacing: -0.005em;
}

.vt-group-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 10px 3px 8px; border-radius: 999px;
    font-size: 0.72rem; font-weight: 600; letter-spacing: 0.015em;
}
.vt-group-swatch { width: 6px; height: 6px; border-radius: 50%; }

.vt-agent { display: flex; align-items: center; gap: 8px; font-size: 0.88rem; color: var(--steel); }
.vt-agent-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); flex-shrink: 0; }
.vt-agent-dot.online { background: #4CAD7E; box-shadow: 0 0 0 2px rgba(76,173,126,0.18); }
.vt-agent-dot.busy   { background: var(--teal-hover); box-shadow: 0 0 0 2px rgba(0,191,255,0.18); }
.vt-agent.direct { color: var(--muted); font-style: italic; }

/* Health ring — circumference = 2πr ≈ 100 when r=15.915, so dasharray uses score directly */
.vt-health { display: flex; align-items: center; gap: 12px; }
.vt-health-ring { position: relative; width: 40px; height: 40px; flex-shrink: 0; }
.vt-health-ring svg { transform: rotate(-90deg); }
.vt-health-ring-track { stroke: rgba(255,255,255,0.10); }
.vt-health-ring-letter {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 0.9rem;
}
.vt-health-score {
    font-family: var(--font-mono);
    font-size: 0.78rem; color: var(--muted);
}
.vt-health-na {
    font-family: var(--font-mono);
    font-size: 0.76rem; color: var(--muted); letter-spacing: 0.06em;
}

.vt-actions { display: flex; gap: 2px; justify-content: flex-end; }
body .vt-actions .mud-icon-button { color: var(--muted); }
body .vt-actions .mud-icon-button:hover { background: rgba(255,255,255,0.06); color: var(--off-white); }
body .vt-actions .mud-icon-button.vt-action-scan { color: var(--teal); }
body .vt-actions .mud-icon-button.vt-action-scan:hover { background: rgba(0,191,255,0.12); color: var(--teal-hover); }
body .vt-actions .mud-icon-button.vt-action-delete:hover {
    background: color-mix(in srgb, var(--state-error) 8%, transparent);
    color: var(--state-error);
}

.vt-csv-hint {
    font-family: var(--font-mono);
    font-size: 0.78rem; color: var(--muted); background: var(--navy-deep);
    padding: 10px 12px; border: 1px solid var(--border-dark);
    border-radius: 6px; margin-bottom: 12px;
}

/* =========================================================================
   Abyssal CTA glow — animated conic-gradient border on primary buttons.
   Applied globally to MudButton[Color=Primary, Variant=Filled] and to .btn-cta.
   Browsers without @property fall back gracefully (no animation, just border).
   ========================================================================= */
@property --vt-glow-angle {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
}
@keyframes vt-glow-spin {
    to { --vt-glow-angle: 360deg; }
}

/* App Primary Button — solid teal fill + white conic glow ring at reduced peak.
   Per vornin_brand_system.md §3 "App Primary Button":
   - color: #0A0F24, background: #00BFFF
   - ring peak white at 0.65 opacity (vs 0.95 on the website primary)
   - 4s spin (vs 3s website) for in-app calm
   - hover lifts +1px and lightens to #33CCFF
   body prefix raises specificity to 0,3,2 — beats Mud's
   .mud-button-filled.mud-button-filled-primary (0,2,0) without !important. */
body .mud-button-filled.mud-button-filled-primary,
body .btn-cta,
body .vt-btn-primary {
    font-family: 'Plus Jakarta Sans', sans-serif;
    font-size: 13px;
    font-weight: 600;
    color: var(--btn-ink);
    background: var(--btn-bg);
    padding: 0 20px;
    min-height: var(--btn-h-md);
    border-radius: var(--btn-radius);
    border: none;
    cursor: pointer;
    letter-spacing: 0.01em;
    position: relative;
    transition: transform 0.15s ease, background-color 0.15s ease, filter 0.15s ease;
    overflow: visible;
    text-transform: none;
    box-shadow: none;
}
body .mud-button-filled.mud-button-filled-primary:hover,
body .btn-cta:hover,
body .vt-btn-primary:hover {
    transform: translateY(-1px);
    background: var(--btn-hover);
    color: var(--btn-ink);
    filter: drop-shadow(0 0 8px rgba(0,191,255,0.35));
}

/* ::before — white conic glow ring (4s, reduced peak per app spec).
   Mud doesn't use ::before on filled buttons, so no specificity fight. */
body .mud-button-filled.mud-button-filled-primary::before,
body .btn-cta::before,
body .vt-btn-primary::before {
    content: "";
    position: absolute;
    inset: -1.5px;
    border-radius: calc(var(--btn-radius) + 1px);
    z-index: -1;
    padding: 1.5px;
    background: conic-gradient(from var(--vt-glow-angle),
        transparent 70%,
        rgba(255,255,255,0.65) 85%,
        rgba(255,255,255,0.25) 92%,
        transparent 96%);
    -webkit-mask: linear-gradient(#000 0 0) content-box,
                  linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box,
                  linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    animation: vt-glow-spin 4s linear infinite;
    pointer-events: none;
}
body .mud-button-filled.mud-button-filled-primary::after,
body .btn-cta::after,
body .vt-btn-primary::after {
    content: none;
    display: none;
}

/* Keep label above ring */
body .mud-button-filled.mud-button-filled-primary .mud-button-label,
body .mud-button-filled.mud-button-filled-primary > *,
body .btn-cta > *,
body .vt-btn-primary > * { position: relative; z-index: 1; }

/* Light mode — flat solid teal, no orbit */
/* Retargeted 2026-07-26: both prior selectors matched 0 nodes. See :root note. */
[data-theme="light"] .mud-button-filled.mud-button-filled-primary::before {
    display: none;
}
[data-theme="light"] .mud-button-filled.mud-button-filled-primary {
    background: var(--teal);
}

/* Brand wordmark — "VORNIN." with teal period (per vornin_brand_system.md §8) */
.vt-brand {
    font-family: var(--font-display);
    font-weight: 700;
    letter-spacing: -0.01em;
    text-transform: uppercase;
    color: var(--off-white);
    user-select: none;
}
.vt-brand .vt-brand-dot { color: var(--teal); }

/* Logo silhouette — invert black artwork onto dark canvas */
.vt-logo-invert { filter: invert(1); }

@media (prefers-reduced-motion: reduce) {
    .mud-button-filled.mud-button-filled-primary::before,
    .btn-cta::before {
        animation: none;
        background: var(--teal);
        opacity: 0.5;
    }
}

/* Coverage-by-category heatmap — used on /admin/compliance/{slug}.
   Each row: category label, stacked pass/fail bar, raw counts. */
.vt-coverage-row {
    display: grid;
    grid-template-columns: 200px 1fr 80px;
    gap: 12px;
    align-items: center;
}
.vt-coverage-label {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.85;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.vt-coverage-bar {
    display: flex;
    height: 14px;
    border-radius: 7px;
    overflow: hidden;
    background: rgba(255,255,255,0.04);
    border: 1px solid var(--border-dark);
}
.vt-coverage-seg { height: 100%; transition: width var(--dur-base) var(--ease-out); }
.vt-coverage-seg.pass { background: #4CAD7E; }
.vt-coverage-seg.fail { background: var(--state-error); }
.vt-coverage-count {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
}
@media (max-width: 640px) {
    .vt-coverage-row { grid-template-columns: 100px 1fr 60px; gap: 8px; }
    .vt-coverage-label { font-size: 0.7rem; }
}

/* Sticky bulk-action toolbar on the Vulnerabilities list — keeps the action
   menu in view as the user scrolls a long results table. */
.vt-bulk-actions {
    position: sticky;
    /* Sits BELOW the sticky filter bar (which pins at 64px). Was also 64px, so
       when scrolled with rows selected this opaque bar pinned on top of and hid
       the filter bar. 120px = appbar (64) + collapsed filter row (~56). */
    top: 120px;
    z-index: 10;
    backdrop-filter: blur(8px);
    background: rgba(33, 43, 69, 0.95); /* --card with high alpha; covers list rows behind */
    border: 1px solid var(--border-dark);
}
/* Filter bar goes static on mobile (see .vt-filter-bar) — match it so the bulk
   bar doesn't pin to a phantom offset on phones. */
@media (max-width: 768px) {
    .vt-bulk-actions { position: static; }
}

/* Live indicator — pulsing teal dot used near scan-progress headers. */
.vt-live-dot {
    display: inline-block; width: 8px; height: 8px;
    border-radius: 50%; background: var(--teal);
    box-shadow: 0 0 0 0 rgba(0,191,255,0.55);
    animation: vt-live-pulse 1.6s ease-out infinite;
}
@keyframes vt-live-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(0,191,255,0.55); }
    70%  { box-shadow: 0 0 0 8px rgba(0,191,255,0); }
    100% { box-shadow: 0 0 0 0 rgba(0,191,255,0); }
}
@media (prefers-reduced-motion: reduce) {
    .vt-live-dot { animation: none; }
}

/* ==========================================================================
   PlanGate — uniform "feature locked" overlay for plan-tier gated UI.
   Used by <PlanGate Feature="..."> when the tenant lacks the feature.
   ========================================================================== */
.vt-plan-gate {
    display: flex; gap: 18px; align-items: flex-start;
    padding: 20px 22px;
    border: 1px dashed rgba(0,191,255,0.35);
    border-radius: var(--radius-lg);
    background: linear-gradient(180deg, rgba(0,191,255,0.04) 0%, rgba(0,191,255,0.01) 100%);
    margin: 8px 0;
}
.vt-plan-gate-icon {
    flex: 0 0 48px; width: 48px; height: 48px;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0,191,255,0.10);
    color: var(--teal);
}
.vt-plan-gate-body { flex: 1; min-width: 0; }
.vt-plan-gate-title {
    margin: 4px 0 6px;
    font-family: var(--font-display);
    font-size: 1.05rem; font-weight: 600; letter-spacing: -0.01em;
    color: var(--mud-palette-text-primary);
}
.vt-plan-gate-sub {
    margin: 0;
    color: rgba(255,255,255,0.72);
    font-size: 0.92rem; line-height: 1.5;
}

/* ==========================================================================
   Severity utility text classes — single source of truth for severity colors
   in inline contexts (KPIs, table cells, banners). Replaces hard-coded
   `style="color: #..."` patterns scattered across razor files.
   ========================================================================== */
.vt-severity-text-critical { color: var(--sev-critical); }
.vt-severity-text-high     { color: var(--sev-high); }
.vt-severity-text-medium   { color: var(--sev-medium); }
.vt-severity-text-low      { color: var(--sev-low); }
.vt-severity-text-info     { color: var(--sev-none); }
.vt-severity-text-success  { color: var(--state-success); }

/* Severity-bar segment (used by Vulnerabilities filter chips). Replaces
   inline-styled <button> elements that bypassed the global focus ring. */
.vt-severity-segment {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 12px;
    border-radius: 999px;
    border: 1px solid transparent;
    background: rgba(255,255,255,0.04);
    color: rgba(255,255,255,0.72);
    font-family: var(--font-mono);
    font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase;
    cursor: pointer;
    transition: background var(--dur-quick) var(--ease-out), border-color var(--dur-quick) var(--ease-out);
}
.vt-severity-segment:hover { background: rgba(255,255,255,0.08); }
.vt-severity-segment.active { border-color: rgba(0,191,255,0.45); background: rgba(0,191,255,0.10); }
.vt-severity-segment:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* ==========================================================================
   Mobile breakpoints — adaptive padding so narrow viewports don't feel cramped.
   Tier 5.4 of the 2026-05-02 UX audit.
   ========================================================================== */
@media (max-width: 640px) {
    /* .mud-main-content: !important required because Mud sets padding inline via Class. */
    .mud-main-content { padding-left: 12px !important; padding-right: 12px !important; }
    /* Pure .vt-* rules below — no !important needed. */
    .vt-hero { padding: 16px; }
    .vt-hero-title { font-size: 1.6rem; }
    .vt-kpis { grid-template-columns: 1fr 1fr; gap: 8px; }
    .vt-panel-body.padded { padding: 14px; }
}

/* ==========================================================================
   Mobile UX hardening (2026-05-10) — touch / hover / scrollable-tabs sweep.
   - Touch-action: manipulation removes the 300ms tap-delay on iOS Safari and
     prevents double-tap-to-zoom from swallowing primary taps.
   - hover: none reveals hover-only affordances (e.g. .vt-rail-delete) on
     touch devices that never fire :hover.
   - MudTabs' default toolbar can clip when many tabs render side-by-side; the
     overflow-x rule lets users scroll the tab strip horizontally on narrow
     viewports without breaking MudBlazor's prev/next button layout.
   ========================================================================== */
button, a, [role="button"],
body .mud-button-root,
body .mud-icon-button-root,
body .mud-nav-link,
.vt-rail-item, .vt-severity-segment {
    touch-action: manipulation;
}

@media (hover: none) {
    /* Hover-only affordances must be reachable on touch. */
    .vt-rail-delete { opacity: 0.7; }
    .vt-rail-item:hover .vt-rail-delete { opacity: 0.7; }
}

@media (max-width: 768px) {
    /* MudTabs — let the toolbar scroll horizontally on narrow widths so tabs
       with many panels (ScanDetail per-scanner, Settings sections, Account)
       don't wrap into multiple rows or get clipped. */
    body .mud-tabs-toolbar-content {
        overflow-x: auto;
        scrollbar-width: thin;
    }
    body .mud-tabs-toolbar-content::-webkit-scrollbar {
        height: 4px;
    }
}

/* Hide brand wordmark + username text + ExpandMore caret on Xs (≤599px) so the
   AppBar fits hamburger + logo + tenant switcher + bell + theme + user-menu on
   a 375px viewport. The user-menu activator collapses to a 36px account icon;
   the StartIcon (AccountCircle) remains so the click target is recognizable. */
@media (max-width: 599.95px) {
    .vt-appbar-brand-text { display: none; }
    .vt-appbar-username { display: none; }
    .vt-appbar-user .mud-button-icon-end { display: none; }
    .vt-appbar-user { min-width: 0; padding-left: 8px; padding-right: 8px; }
}

/* Mobile dialogs go (effectively) full-screen — MaxWidth.Medium / Large
   dialogs (compliance attestation drilldown, vulnerability quick-view, scan
   wizard) waste 32px+ of side margin on a 375px viewport. Push them to edge
   on Xs so the inner content has room to breathe. */
@media (max-width: 599.95px) {
    body .mud-dialog-container .mud-dialog {
        max-width: 100%;
        max-height: 100%;
        width: 100%;
        height: 100%;
        margin: 0;
        border-radius: 0;
    }
    body .mud-dialog-container .mud-dialog .mud-dialog-content {
        max-height: calc(100dvh - 132px);
    }
}

/* ==========================================================================
   Mobile review fixes (2026-05-10) — second pass after live phone testing.
   ========================================================================== */

/* (#1) Attack Surface — host column was overflowing because the cell stamps
   the discovered host on line 1 + the root target name on line 2. On Xs the
   target line eats the only viewport space we have; truncate the host with
   ellipsis and hide the target line entirely. */
@media (max-width: 599.95px) {
    .vt-as-host { max-width: 60vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .vt-as-target { display: none; }
}

/* (#2) ScanNow — the "1. Select Targets / 2. Choose Scanners / 3. Review &
   Launch" chip strip is wider than 375px and clutters the header. Hide on Xs
   (the in-step "Back / Next" buttons make navigation obvious anyway).
   `!important` is required because MudStack stamps display:flex inline on the
   root <div>; class-level rules can't beat inline declarations otherwise. */
@media (max-width: 599.95px) {
    .vt-scan-step-indicator { display: none !important; }
}

/* (#3 / R2) ScanNow Step 2 — let the "Select scan types" header row stack
   vertically so the title takes its own line and the Recommended/Comprehensive
   buttons drop below it full-width. Same `!important` rationale as (#2). */
@media (max-width: 599.95px) {
    .vt-scan-step-header { flex-direction: column !important; align-items: stretch !important; gap: 8px !important; }
    .vt-scan-step-header > .mud-button-root,
    .vt-scan-step-header .mud-tooltip-root { width: 100%; }
    .vt-scan-step-header .mud-tooltip-root .mud-button-root { width: 100%; }
    /* Category card row also stacks: name+desc on top, count + Advanced below. */
    .vt-scan-cat-row { flex-direction: column !important; align-items: stretch !important; gap: 8px !important; }
    /* Inner scanner option card flips to column too. */
    .vt-scanner-card .mud-stack-row { flex-direction: column; align-items: flex-start; gap: 4px; }
}

/* (#6) Targets — sector rail is hidden on Xs entirely; the user can manage
   groups on tablet+. Targets-list still shows the group pill per row. */
@media (max-width: 599.95px) {
    .vt-rail { display: none; }
    .vt-shell { gap: 0; }
}

/* (#9 / R2) VulnerabilityDetail header — on Xs the entire MudStack splits
   into a vertical column: title row on top, action row below. We use
   !important to beat MudStack's inline flex-direction:row, and force each
   inner stack to take a full row of its own. The view-mode toggle (Formatted/
   Table/JSON) still hides on Xs since "Formatted" is the only useful default
   on a phone. */
@media (max-width: 599.95px) {
    .vt-vulndetail-header { flex-direction: column !important; align-items: stretch !important; gap: 12px !important; }
    .vt-vulndetail-header > .mud-stack-row { width: 100%; }
    .vt-vulndetail-title { font-size: 1.25rem !important; line-height: 1.25 !important; word-break: break-word; }
    .vt-vulndetail-toggle { display: none; }
}

/* (#10) Compliance settings — Mud popover sometimes renders behind the
   dialog/panel because its z-index defaults to 1300. Bump select popovers
   above the rest of the surface chrome. */
body .mud-popover.mud-popover-open {
    z-index: 1500;
}

/* (#11) Compliance dashboard — `<Actions>` slot has 3 buttons that can blow
   past 375px even with flex-wrap. On Xs stack them full-width. */
@media (max-width: 599.95px) {
    .vt-hero-actions { width: 100%; }
    .vt-hero-actions > * { flex: 1 1 auto; }
    .vt-hero-actions .mud-button-root { width: 100%; }
}

/* (#12 / R4) Compliance control drilldown — earlier rounds tried to morph
   MudTable cells into cards via display:block + nth-of-type pseudo-labels;
   that approach was unreliable across MudBlazor's table renderer (cells
   stayed in their column box on some renders, content kept overflowing
   horizontally). Round 4 ships an explicit parallel card list in markup
   (.vt-drilldown-mobile in the dialog razor) and toggles the table vs the
   cards via display visibility per breakpoint. Tap a card → opens that
   vulnerability detail in a new tab. */
.vt-drilldown-mobile { display: none; }
@media (max-width: 599.95px) {
    .vt-drilldown-table-wrap { display: none; }
    .vt-drilldown-mobile { display: flex; flex-direction: column; gap: 10px; }
}
.vt-drilldown-mobile-card {
    display: block;
    text-decoration: none;
    color: inherit;
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    background: var(--mud-palette-surface);
    padding: 10px 12px;
    transition: border-color var(--dur-quick) var(--ease-out), background var(--dur-quick) var(--ease-out);
}
.vt-drilldown-mobile-card:hover,
.vt-drilldown-mobile-card:active {
    border-color: rgba(0, 191, 255, 0.45);
    background: rgba(0, 191, 255, 0.04);
}
.vt-drilldown-mobile-card-row {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    padding: 4px 0;
    text-align: left;
}
.vt-drilldown-mobile-card-row + .vt-drilldown-mobile-card-row {
    border-top: 1px solid rgba(192, 200, 212, 0.06);
}
.vt-drilldown-mobile-card-label {
    flex: 0 0 90px;
    font-family: var(--font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55);
    padding-top: 3px;
}
.vt-drilldown-mobile-card-value {
    flex: 1;
    min-width: 0;
    word-break: break-word;
    overflow-wrap: anywhere;
    text-align: left;
}

/* (#R2 #8 / Compliance framework page) Per-control card row stacks on Xs so
   the failing-count action button drops below the long control title. */
@media (max-width: 599.95px) {
    .vt-control-card .mud-stack-row { flex-direction: column !important; align-items: flex-start !important; gap: 6px !important; }
    .vt-control-card .mud-stack-row .mud-button-root,
    .vt-control-card .mud-stack-row .mud-chip { width: auto; }
}

/* (#R2 #4 / Scan compare) Two scan dropdowns + Compare button stack
   vertically on Xs; each control takes its own line, full-width. */
@media (max-width: 599.95px) {
    .vt-compare-controls { flex-direction: column !important; align-items: stretch !important; gap: 12px !important; }
    .vt-compare-controls > * { width: 100%; }
}

/* (#R2 #5 / Vuln mobile actions) Inline action row at the bottom of every
   mobile vuln card — same actions the desktop overflow menu offers. We
   render them as text-only icon buttons so multiple fit on one row at
   375px. The cluster lives inside the card, click events stop propagating
   so tapping an action doesn't trigger the parent card's navigation. */
.vt-vulns-mobile-card-actions {
    display: flex;
    gap: 4px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid rgba(192, 200, 212, 0.06);
    flex-wrap: wrap;
}
.vt-vulns-mobile-card-actions .mud-icon-button-root { width: 36px; height: 36px; }

/* (R3 #1 / Attack Surface toolbar) Filter chip row + bulk-action buttons
   stack vertically on Xs. The filters (chip cluster) get their own line
   that wraps; the action buttons (Add as Target / Ignore) drop below into
   a stretched row so each tap target stays ≥44px tall. */
.vt-as-toolbar-filters { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.vt-as-toolbar-actions { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; }
@media (max-width: 599.95px) {
    .vt-as-toolbar { flex-direction: column !important; align-items: stretch !important; gap: 8px !important; }
    .vt-as-toolbar > * { width: 100%; }
    .vt-as-toolbar-actions { flex-direction: column; }
    .vt-as-toolbar-actions > .mud-button-root { width: 100%; }
}

/* (#13) Schedule rows — bump alternate-row contrast so paused/active scans
   and report schedules are visually distinct. Default Mud striping is too
   subtle on dark surfaces. */
body .vt-schedule-grid .mud-table-body .mud-table-row:nth-child(even) > td {
    background: rgba(255, 255, 255, 0.04);
}
[data-theme="light"] .vt-schedule-grid .mud-table-body .mud-table-row:nth-child(even) > td {
    background: rgba(15, 23, 42, 0.04);
}

/* (#14 / R2) Reports — the post-generate PDF iframe doesn't render usefully
   on Chrome mobile (the embedded PDF viewer either fails or shows a download
   shim). Hide it entirely on tablets/phones (≤768px) and rely on the
   Download PDF / Other Formats buttons. The pre-generate cover-preview
   placeholder is also hidden on Xs.
   Buttons stack via the wrapper .vt-report-actions; we use !important to
   beat MudStack's inline flex-direction:row, and also stack the inner
   .vt-report-actions-row when there are 2+ actions so they get a 50/50
   split below the title. */
@media (max-width: 768px) {
    .vt-report-iframe { display: none !important; }
}
@media (max-width: 599.95px) {
    .vt-report-preview-wrap { display: none; }
    .vt-report-actions { flex-direction: column !important; align-items: stretch !important; gap: 8px !important; }
    .vt-report-actions > .mud-button-root,
    .vt-report-actions > .mud-menu { width: 100%; }
    .vt-report-actions-row { display: flex; gap: 8px; width: 100%; }
    .vt-report-actions-row > * { flex: 1 1 50%; min-width: 0; }
    .vt-report-actions-row .mud-button-root,
    .vt-report-actions-row .mud-menu { width: 100%; }
}

/* (#15) ApiKeys — the "Usage" curl example block was centred because the
   parent <Panel> applies text-align:center on the heading row. Force the
   inner <pre> to left-align. */
.vt-mono-block, .mud-mono-block {
    text-align: left;
}

/* (#7) Vulnerabilities — on Xs we render a parallel card list (.vt-vulns-mobile)
   instead of trying to stack MudDataGrid cells via CSS. The data-grid gets
   display:none on Xs, the mobile list gets display:block. Each card stamps
   each property as a label/value pair stacked vertically. */
.vt-vulns-mobile { display: none; }
@media (max-width: 599.95px) {
    .vt-vulns-grid-wrap { display: none; }
    .vt-vulns-mobile { display: flex; flex-direction: column; gap: 12px; }
}
.vt-vulns-mobile-card {
    border: 1px solid var(--border-dark);
    border-radius: var(--radius);
    background: var(--mud-palette-surface);
    padding: 12px 14px;
    cursor: pointer;
    transition: border-color var(--dur-quick) var(--ease-out), background var(--dur-quick) var(--ease-out);
}
.vt-vulns-mobile-card:hover,
.vt-vulns-mobile-card:active {
    border-color: rgba(0, 191, 255, 0.45);
    background: rgba(0, 191, 255, 0.04);
}
.vt-vulns-mobile-card-row { display: flex; gap: 8px; align-items: flex-start; padding: 4px 0; }
.vt-vulns-mobile-card-row + .vt-vulns-mobile-card-row { border-top: 1px solid rgba(192, 200, 212, 0.06); }
.vt-vulns-mobile-card-label {
    flex: 0 0 90px;
    font-family: var(--font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55);
    padding-top: 3px;
}
.vt-vulns-mobile-card-value { flex: 1; min-width: 0; word-break: break-word; }
.vt-vulns-mobile-card-title { font-weight: 600; margin-bottom: 4px; word-break: break-word; }

/* ==========================================================================
   Report cover preview — rendered on /reports before the user generates.
   Embeds the actual PDF cover HTML inside an iframe srcdoc, scaled via a
   container query so what the user sees on screen matches the first page
   of the PDF 1:1. Watermark + disclaimer pill are overlaid in the parent
   DOM (above the iframe) so they don't end up in the rendered cover.
   ========================================================================== */
.vt-report-preview-wrap {
    display: flex;
    justify-content: center;
    padding: 8px 0 24px;
}
.vt-preview-stage {
    container-type: inline-size;
    width: 100%;
    max-width: 720px;
    aspect-ratio: 210 / 297; /* A4 portrait */
    position: relative;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 18px 48px rgba(0,0,0,0.55), 0 2px 6px rgba(0,0,0,0.35);
    user-select: none;
    background: #0A0F24;
}
.vt-preview-frame {
    position: absolute;
    top: 0;
    left: 0;
    width: 794px;   /* A4 width @ 96dpi (210mm) */
    height: 1123px; /* A4 height @ 96dpi (297mm) */
    border: 0;
    background: transparent;
    transform-origin: top left;
    transform: scale(calc(100cqw / 794));
    pointer-events: none;
}
.vt-cover-watermark {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    z-index: 2;
}
.vt-cover-watermark span {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: clamp(3rem, 9vw, 5.5rem);
    letter-spacing: 0.35em;
    color: rgba(255,255,255,0.10);
    transform: rotate(-22deg);
    text-transform: uppercase;
    white-space: nowrap;
    border: 4px solid rgba(255,255,255,0.10);
    padding: 0.35em 0.7em;
    border-radius: 12px;
}
.vt-cover-disclaimer {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    z-index: 3;
    background: linear-gradient(0deg, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0) 100%);
    padding: 3rem 1.5rem 1.5rem;
    text-align: center;
    pointer-events: none;
}
.vt-cover-disclaimer-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: rgba(0,191,255,0.18);
    border: 1px solid rgba(0,191,255,0.45);
    color: #fff;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 0.55rem 1rem;
    border-radius: 999px;
    backdrop-filter: blur(4px);
}

/* Attack Surface Overview grid */
.vt-as-overview-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
.vt-empty-line { padding: 12px 0; opacity: 0.6; }

/* Cloudflare Turnstile widget is fixed 300x65; scale down when auth card
   narrower than that on mobile so it stops overflowing past the email field.
   Card chrome on /auth/login + /auth/register: ExtraSmall MudContainer (24px)
   + MudPaper pa-10 (80px) = 104px viewport chrome. */
#login-turnstile, #register-turnstile {
    transform-origin: left top;
}
@media (max-width: 420px) {
    #login-turnstile, #register-turnstile {
        transform: scale(calc((100vw - 104px) / 300));
        height: calc(65px * ((100vw - 104px) / 300));
    }
}

/* ─────────────────────────────────────────────────────────────────────
   AuthShell — canonical chrome for /auth/* pages under EmptyLayout.
   Login / Register / TotpVerify / TotpSetup / AcceptUpdatedTerms all
   route through <AuthShell> for shared logo size, padding, border,
   and typography.
   ───────────────────────────────────────────────────────────────────── */
.vt-auth-shell {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
    box-sizing: border-box;
}

.vt-auth-card {
    width: 100%;
    max-width: 440px;
    padding: 2.5rem;
    border-radius: 14px;
    background: var(--card);
    border: 1px solid rgba(148, 163, 184, 0.1);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset, 0 24px 48px -24px rgba(0, 0, 0, 0.5);
}

.vt-auth-shell.wide .vt-auth-card {
    max-width: 640px;
}

.vt-auth-logo {
    display: block;
    margin-bottom: 1.75rem;
    text-decoration: none;
    line-height: 0;
}

.vt-auth-logo img {
    height: 36px;
    width: auto;
    display: block;
}

.vt-auth-eyebrow {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 0.7rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--teal);
    margin-bottom: 0.5rem;
}

.vt-auth-title {
    font-family: var(--font-display, 'Plus Jakarta Sans', sans-serif);
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    margin: 0 0 0.4rem 0;
    color: var(--text);
}

.vt-auth-subtitle {
    margin: 0 0 1.5rem 0;
    color: var(--mud-palette-text-secondary, rgba(255, 255, 255, 0.6));
    font-size: 0.92rem;
    line-height: 1.5;
}

.vt-auth-footer {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid rgba(148, 163, 184, 0.1);
    text-align: center;
    font-size: 0.85rem;
    color: var(--mud-palette-text-secondary, rgba(255, 255, 255, 0.6));
}

@media (max-width: 480px) {
    .vt-auth-shell { padding: 1rem 0.75rem; }
    .vt-auth-card { padding: 1.75rem 1.25rem; }
    .vt-auth-logo img { height: 30px; }
    .vt-auth-title { font-size: 1.3rem; }
}

/* Social-login row — moved out of Login.razor inline <style> block. */
.social-login-stack {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-bottom: 1rem;
}
.social-login-btn { justify-content: flex-start; }
.social-icon {
    display: inline-flex;
    align-items: center;
    margin-right: 0.75rem;
}
.social-login-divider {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1rem 0;
    color: var(--mud-palette-text-secondary);
}
.social-login-divider::before,
.social-login-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--mud-palette-lines-default);
}

/* TOTP QR halo — moved out of TotpSetup.razor inline <style> block,
   plus a soft halo so the required white background reads as an
   intentional inset rather than a forgotten dark-mode case. */
.vt-totp-qr {
    display: flex;
    justify-content: center;
    margin-bottom: 1rem;
}
.vt-totp-qr-box {
    width: 220px;
    height: 220px;
    background: #fff;
    padding: 8px;
    border-radius: 8px;
    box-sizing: border-box;
    box-shadow: 0 0 0 8px rgba(255, 255, 255, 0.06);
}
.vt-totp-qr-box svg {
    width: 100%;
    height: 100%;
    display: block;
}

/* ─────────────────────────────────────────────────────────────────────
   EPSS chip — replaces MudBlazor Color.Error/Warning/Default enum.
   Buckets map to severity tokens for visual continuity with the rest
   of the row chrome.
   ───────────────────────────────────────────────────────────────────── */
.vt-epss-chip {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.4;
    border: 1px solid;
}
.vt-epss-chip--high     { background: var(--sev-high-bg);     color: var(--sev-high);     border-color: var(--sev-high-border); }
.vt-epss-chip--medium   { background: var(--sev-medium-bg);   color: var(--sev-medium);   border-color: var(--sev-medium-border); }
.vt-epss-chip--low      { background: var(--sev-none-bg);     color: var(--sev-none);     border-color: var(--sev-none-border); }

/* VulnTable age cell — replaces inline `style="color: ...; font-weight: 600/400"`
   on VulnTable.razor age column. */
.vt-vuln-age          { font-weight: 400; }
.vt-vuln-age--warning { color: var(--age-warning); font-weight: 400; }
.vt-vuln-age--aged    { color: var(--sev-medium);  font-weight: 600; }
.vt-vuln-age--stale   { color: var(--sev-high);    font-weight: 600; }

/* ScanDetail summary panel — repo/commit/duration meta, per-scanner coverage
   chips, top-affected files. */
.vt-scan-summary-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    align-items: center;
}
.vt-summary-item {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.85rem;
    color: var(--mud-palette-text-secondary);
}
.vt-scanners-run {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.25rem;
}
.vt-scanner-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    border: 1px solid var(--mud-palette-lines-default);
    font-size: 0.8rem;
}
.vt-scanner-chip .vt-scanner-count {
    font-weight: 600;
    font-family: var(--font-mono);
}
.vt-scanner-chip.clean { border-color: var(--sev-low-border); }
.vt-scanner-chip.clean .vt-scanner-count { color: var(--sev-low); }
.vt-scanner-chip.warn  { border-color: var(--sev-medium-border); }
.vt-scanner-chip.warn  .vt-scanner-count { color: var(--sev-medium); }
.vt-top-files {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    margin-top: 0.75rem;
    font-size: 0.8rem;
}
.vt-top-files-label { color: var(--mud-palette-text-secondary); }
.vt-top-file { word-break: break-all; }

/* ScanDetail code-scan grouped findings — file/CWE groups with inline snippet. */
.vt-code-group-key { word-break: break-all; font-weight: 600; }
.vt-code-group-meta {
    font-size: 0.8rem;
    color: var(--mud-palette-text-secondary);
}
.vt-code-finding {
    padding: 0.6rem 0;
    border-top: 1px solid var(--mud-palette-lines-default);
}
.vt-code-finding:first-child { border-top: none; }
.vt-code-finding-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}
.vt-code-finding-title { font-weight: 500; }
.vt-code-line { color: var(--mud-palette-text-secondary); }
.vt-code-finding-rule,
.vt-code-finding-pkg {
    font-size: 0.78rem;
    color: var(--mud-palette-text-secondary);
    margin-top: 0.2rem;
    word-break: break-all;
}
.vt-code-snippet {
    margin: 0.4rem 0 0;
    padding: 0.6rem 0.75rem;
    background: var(--mud-palette-background-grey);
    border-radius: 4px;
    font-size: 0.78rem;
    line-height: 1.45;
    white-space: pre;
    overflow-x: auto;
}
.vt-code-finding-desc { margin-top: 0.4rem; font-size: 0.85rem; }
.vt-code-finding-sol  { margin-top: 0.4rem; font-size: 0.85rem; }

/* CodeBlock — IDE-style formatter for finding snippets. Dark code surface in both
   themes so scanner snippets read like a source editor rather than plain text. */
.vt-codeblock {
    margin: 0.4rem 0 0;
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 8px;
    overflow: hidden;
    background: #0d1117;
    font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
    font-size: 0.78rem;
    line-height: 1.5;
}
.vt-codeblock-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.4rem 0.75rem;
    background: rgba(255, 255, 255, 0.04);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 0.72rem;
}
.vt-codeblock-file {
    color: #9aa4b2;
    word-break: break-all;
    min-width: 0;
}
.vt-codeblock-lang {
    color: var(--teal, #00BFFF);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
    flex: none;
}
.vt-codeblock-body {
    display: flex;
    overflow-x: auto;
}
.vt-codeblock-gutter {
    margin: 0;
    padding: 0.6rem 0.6rem 0.6rem 0.75rem;
    text-align: right;
    color: rgba(255, 255, 255, 0.28);
    user-select: none;
    white-space: pre;
    flex: none;
    border-right: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(0, 0, 0, 0.18);
    font: inherit;
}
.vt-codeblock-code {
    margin: 0;
    padding: 0.6rem 0.9rem;
    white-space: pre;
    color: #e6edf3;
    flex: 1 0 auto;
    font: inherit;
}
.vt-codeblock-code code {
    font: inherit;
    background: none;
    padding: 0;
    color: inherit;
}
