/*
 * VITALS stylesheet.
 *
 * Light theme, violet accent, white sidebar with collapsible sections and a user
 * block pinned at the foot. Top-level sections (Operations, Monitoring, Admin)
 * live in the sidebar with their features nested beneath, so everything stays
 * visible as the feature set grows.
 *
 * Dense but airy: the reader is an SRE or FDE examining a cluster they may not
 * know, so scannable data beats decoration.
 *
 * No framework, no build step: one file served from the JAR.
 */

:root {
    /* Surfaces */
    --bg:            #ffffff;
    --bg-page:       #f7f9f9;
    --bg-sidebar:    #fcfdfd;
    --bg-subtle:     #f1f5f4;
    --bg-hover:      #eaf2f1;
    --bg-inset:      #f9fbfb;

    /* Card surface: mostly opaque so it stays crisp under the new layered shadows,
       while still letting a hint of the ambient wash tint through. One token, so
       every card/tile/panel shares the exact same surface. */
    --surface:       color-mix(in srgb, var(--bg) 94%, transparent);

    /* Lines */
    --border:        #e4eaea;
    --border-strong: #d2dada;

    /* Text: warm-neutral ink rather than pure black.
       Secondary tones darkened for more contrast ("brighter"/crisper) — the dim
       and faint greys drove the washed-out look. Revert to #5a6465 / #8a9394 to
       restore the softer original. */
    --text:          #141a1a;
    --text-dim:      #454f50;
    --text-faint:    #6d7778;

    /*
      Ambient wash behind the whole app. Keeps large light surfaces from reading
      as flat paper, without tinting content.
    */
    --wash:
        radial-gradient(1200px 620px at 78% -12%, rgba(62, 156, 147, 0.10) 0%, transparent 62%),
        radial-gradient(880px 520px at 4% 108%, rgba(201, 162, 39, 0.07) 0%, transparent 60%);

    --shadow-color: rgba(20, 32, 32, 0.06);
    --scrim: rgba(255, 255, 255, 0.72);

    /*
      Brand gradient: teal to sage to gold. Emphasis in the wordmark comes from
      weight contrast rather than colour, so the gradient is reserved for the
      mark, the active nav tile, and accent details.
    */
    /* Brand gradient: sunset — coral → orange → gold. Used on the Sign in button, the
       wordmark, badges, the loader and the selected nav icon. The dark theme inherits these,
       so both light and dark share this sunset sweep. */
    --grad-start:    #e05656;
    --grad-mid:      #e8873a;
    --grad-end:      #e0b429;
    --brand-gradient: linear-gradient(120deg,
                        var(--grad-start) 0%,
                        var(--grad-mid) 50%,
                        var(--grad-end) 100%);

    /* Accent: teal carries interactive state on the light theme — borders, links and active
       chips. The logo/wordmark gradient stays the sunset orange (--grad-* below) as the brand
       mark; the interactive accent is a calmer teal that reads better as borders on white. */
    --accent:        #2f8b83;
    --accent-hover:  #257771;
    --accent-soft:   #eaf4f2;
    --accent-line:   #c9e2de;
    --gold:          #C9A227;

    /* Themed table/border tint. On the light theme it is derived from --accent (teal) so the
       table/boxed-cell borders match the interactive accent rather than the orange logo
       gradient. Slate and navy override this below to keep deriving from their own --grad-mid;
       the dark theme overrides it to its teal accent in the [data-theme="dark"] block. */
    --table-accent-border: color-mix(in srgb, var(--accent) 55%, var(--border));

    /* Logo mark gradient stops — the sunset palette, matching --brand-gradient. Light (and
       dark, inherited) share these; other themes override the three below. */
    --logo-a: #e05656;
    --logo-b: #e8873a;
    --logo-c: #e0b429;

    /* Status */
    --ok:            #2f8b6b;
    --ok-soft:       #eaf5f0;
    --warn:          #a2731a;
    --warn-soft:     #fbf4e4;
    --error:         #c4362b;
    --error-soft:    #fbedeb;
    --info:          #2f7392;
    --info-soft:     #eaf2f6;

    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

    --radius:     10px;
    --radius-sm:  7px;
    --sidebar-w:  236px;
    --topbar-h:   52px;

    /*
      Layered, soft shadows. A near-transparent ambient layer plus a tighter
      contact layer reads as depth on paper rather than a hard 1px line — the
      difference between "boxed" enterprise UI and a modern floating surface.
      Every card, table and panel inherits these, so lifting them here lifts the
      whole app at once.
    */
    --shadow-sm: 0 1px 2px rgba(20, 32, 32, 0.04), 0 1px 3px rgba(20, 32, 32, 0.05);
    --shadow:    0 2px 4px rgba(20, 32, 32, 0.05), 0 4px 12px rgba(20, 32, 32, 0.06);
    --shadow-md: 0 4px 10px rgba(20, 32, 32, 0.07), 0 10px 28px rgba(20, 32, 32, 0.08);

    /* 3D lift tokens: a white lit edge and a faint accent-tinted surface gradient. Light
       theme keeps it subtle. Never grey — the tint follows the brand teal. */
    --lift-top:       color-mix(in srgb, #ffffff 85%, transparent);
    --lift-surface-a: color-mix(in srgb, var(--accent) 5%, var(--surface));
    --lift-surface-b: var(--surface);
    --lift-hover-a:   color-mix(in srgb, var(--accent) 9%, var(--surface));
    --lift-hover-b:   color-mix(in srgb, var(--accent) 3%, var(--surface));
}

/*
  Graphite theme — a true medium-grey world, not a tint of the white theme.

  Everything moves into confident cool greys: a graphite page, lighter grey cards that sit
  on it as raised panels, and a slightly darker sidebar. The interactive accent shifts from
  the light theme's teal to a steel-blue, and the brand gradient becomes steel → slate-blue,
  so even the coloured elements say "graphite" rather than borrowing the light palette. Ink
  stays near-black for contrast. This is a distinct fourth world, clearly not Light.
  (data-theme value kept as "slate" so no wiring changes.)
*/
[data-theme="slate"] {
    /* Inverted elevation: the page is the LIGHTEST grey, and cards/menus sit DARKER, so
       surfaces read as recessed panels rather than glaring white ones. */
    --bg:            #d3d8de;
    --bg-page:       #dfe3e8;   /* page — the lightest grey */
    --bg-sidebar:    #cdd3da;
    --bg-subtle:     #c4ccd4;
    --bg-hover:      #b9c2cc;
    --bg-inset:      #c9cfd6;

    --surface:       #c9d0d8;   /* card — a clear step DARKER than the page */

    --border:        #a9b3bf;
    --border-strong: #8c98a7;

    --text:          #1a2230;
    --text-dim:      #46525f;
    --text-faint:    #66717f;

    /* Cool steel wash rather than the teal/gold of light. */
    --wash:
        radial-gradient(1200px 620px at 78% -12%, rgba(79, 109, 143, 0.14) 0%, transparent 62%),
        radial-gradient(880px 520px at 4% 108%, rgba(90, 104, 128, 0.10) 0%, transparent 60%);

    --shadow-color: rgba(40, 52, 70, 0.16);
    --scrim: rgba(207, 212, 218, 0.76);

    /* Grey cards on a graphite page carry a real, defined shadow — this is what gives the
       theme its solid, panelled feel. */
    --shadow-sm: 0 1px 2px rgba(40, 52, 70, 0.12), 0 1px 3px rgba(40, 52, 70, 0.12);
    --shadow:    0 2px 6px rgba(40, 52, 70, 0.15), 0 6px 16px rgba(40, 52, 70, 0.14);
    --shadow-md: 0 6px 16px rgba(40, 52, 70, 0.18), 0 16px 34px rgba(40, 52, 70, 0.20);

    /* Steel-blue accent — distinct from the light theme's teal. */
    --accent:        #4f6d8f;
    --accent-hover:  #3f5c7d;
    --accent-soft:   rgba(79, 109, 143, 0.14);
    --accent-line:   rgba(79, 109, 143, 0.34);
    --gold:          #6a7c93;

    /* Keep this theme's table borders deriving from its own brand mid-tone (the light theme
       now derives --table-accent-border from --accent, so slate/navy restate the grad-mid
       formula to stay unchanged). */
    --table-accent-border: color-mix(in srgb, var(--grad-mid) 42%, var(--border));

    /* Logo mark: teal → deep blue, matching this theme's brand gradient. */
    --logo-a: #2f8b83;
    --logo-b: #2c7188;
    --logo-c: #2a5d8f;

    /* Brand gradient: teal → deep blue (tryout option 2). Used on the Sign in button, the
       wordmark, badges, the loader and the selected nav icon in this grey theme. */
    --grad-start:    #2f8b83;
    --grad-mid:      #2c7188;
    --grad-end:      #2a5d8f;
    --brand-gradient: linear-gradient(120deg,
                        var(--grad-start) 0%,
                        var(--grad-mid) 50%,
                        var(--grad-end) 100%);

    /* Status colours deepened slightly so they hold up on grey rather than looking pastel. */
    --ok:            #2f8b6b;
    --ok-soft:       rgba(47, 139, 107, 0.14);
    --warn:          #9a6f18;
    --warn-soft:     rgba(154, 111, 24, 0.14);
    --error:         #bd382c;
    --error-soft:    rgba(189, 56, 44, 0.14);
    --info:          #3a6e8c;
    --info-soft:     rgba(58, 110, 140, 0.14);

    /* Cards are DARKER than the page, so the surface gradient stays in the darker grey
       (a gentle top-to-bottom deepen) rather than mixing toward white. A faint light top
       edge still catches the "lit rim", and hover deepens slightly for a pressed feel. */
    --lift-top:       color-mix(in srgb, #ffffff 40%, transparent);
    --lift-surface-a: var(--surface);
    --lift-surface-b: color-mix(in srgb, #000000 5%, var(--surface));
    --lift-hover-a:   color-mix(in srgb, #000000 3%, var(--surface));
    --lift-hover-b:   color-mix(in srgb, #000000 8%, var(--surface));
}

/*
  Dark theme.
  Deep blue-black rather than neutral grey, with a teal glow from the top edge.
  Surfaces sit only slightly above the background and rely on hairline borders,
  which keeps dense tables readable without heavy contrast steps.
*/
[data-theme="dark"] {
    --bg:            #101820;
    --bg-page:       #0a1016;
    --bg-sidebar:    #0c131a;
    --bg-subtle:     #16202a;
    --bg-hover:      #182430;
    --bg-inset:      #0e161d;

    /* Slightly lifted above the page on dark, for a subtle elevation cue. */
    --surface:       #131d27;

    --border:        #1e2a35;
    --border-strong: #2b3a47;

    /* Secondary tones lifted for more contrast; revert to #9aa8b0 / #6b7a84. */
    --text:          #f2f6f7;
    --text-dim:      #b3c0c7;
    --text-faint:    #85939c;

    --wash:
        radial-gradient(1300px 680px at 74% -16%, rgba(62, 156, 147, 0.20) 0%, transparent 60%),
        radial-gradient(900px 520px at 2% 106%, rgba(201, 162, 39, 0.08) 0%, transparent 58%);

    --shadow-color: rgba(0, 0, 0, 0.4);
    --scrim: rgba(10, 16, 22, 0.72);

    /* On dark, depth comes from deeper ambient shadow rather than contact lines. */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30), 0 1px 3px rgba(0, 0, 0, 0.22);
    --shadow:    0 2px 6px rgba(0, 0, 0, 0.34), 0 6px 16px rgba(0, 0, 0, 0.30);
    --shadow-md: 0 6px 16px rgba(0, 0, 0, 0.40), 0 14px 34px rgba(0, 0, 0, 0.38);

    /* Teal reads brighter on dark, so interactive accents shift up a step */
    --accent:        #4db6aa;
    --accent-hover:  #63c6ba;
    --accent-soft:   rgba(77, 182, 170, 0.14);
    --accent-line:   rgba(77, 182, 170, 0.34);

    /* Table/border tint on dark follows the teal accent, not the shared sunset --grad-mid
       (which the dark theme inherits from :root for the logo gradient). Overriding the token
       here recolours every table border, boxed-cell chip and themed filter control on dark
       to teal in one place, without touching the dark logo/wordmark gradient. */
    --table-accent-border: color-mix(in srgb, var(--accent) 55%, var(--border));

    --ok:            #4cb98b;
    --ok-soft:       rgba(76, 185, 139, 0.14);
    --warn:          #d3a24a;
    --warn-soft:     rgba(211, 162, 74, 0.14);
    --error:         #e0685c;
    --error-soft:    rgba(224, 104, 92, 0.14);
    --info:          #5aa7c7;
    --info-soft:     rgba(90, 167, 199, 0.14);

    /* 3D lift on dark: a teal-tinted lit edge and a teal lift on the card surface, so cards
       rise as "brighter/tealer", not grey. */
    --lift-top:       color-mix(in srgb, var(--accent) 45%, transparent);
    --lift-surface-a: color-mix(in srgb, var(--accent) 14%, var(--surface));
    --lift-surface-b: var(--surface);
    --lift-hover-a:   color-mix(in srgb, var(--accent) 22%, var(--surface));
    --lift-hover-b:   color-mix(in srgb, var(--accent) 8%, var(--surface));
}

/*
  Navy blue theme — a fresh, self-contained blue world.

  Rich royal-navy surfaces with clear elevation steps between the page and the cards, a
  bright sky-blue accent, and a blue-family brand gradient (indigo → azure → cyan) that
  replaces the teal→gold sweep on buttons and the wordmark. Depth (the 3D look) comes from
  blue-tinted shadows and a blue lift on card surfaces — never grey — via the shared
  --lift-* tokens defined here and consumed by the 3D rules at the foot of this file.
*/
[data-theme="navy"] {
    /* Surfaces: a deliberate ladder from the darkest page up to the lightest raised card,
       all in blue, so elevation reads as "closer/bluer-lit" rather than "greyer". */
    --bg:            #16294d;   /* base card/background blue */
    --bg-page:       #0b1730;   /* darkest — the page behind everything */
    --bg-sidebar:    #0f1f3f;
    --bg-subtle:     #1d3360;
    --bg-hover:      #244079;
    --bg-inset:      #0e1c3a;

    --surface:       #1a2f59;   /* raised card surface, clearly above the page */

    --border:        #2c477e;
    --border-strong: #3d5c9c;

    --text:          #eaf1ff;
    --text-dim:      #b3c4e6;
    --text-faint:    #8298c4;

    /* Cool blue ambient wash. */
    --wash:
        radial-gradient(1300px 680px at 76% -16%, rgba(74, 163, 255, 0.24) 0%, transparent 60%),
        radial-gradient(920px 540px at 0% 108%, rgba(120, 110, 255, 0.14) 0%, transparent 58%);

    --shadow-color: rgba(4, 12, 32, 0.55);
    --scrim: rgba(11, 23, 48, 0.76);

    /* Blue-tinted, not black: shadows carry a navy hue so depth stays in the blue family. */
    --shadow-sm: 0 1px 2px rgba(6, 16, 40, 0.45), 0 1px 3px rgba(6, 16, 40, 0.35);
    --shadow:    0 2px 6px rgba(6, 16, 40, 0.50), 0 8px 20px rgba(8, 20, 52, 0.42);
    --shadow-md: 0 6px 18px rgba(6, 16, 40, 0.55), 0 18px 40px rgba(10, 26, 66, 0.50);

    /* Bright sky-blue accent for interactive state. */
    --accent:        #4aa3ff;
    --accent-hover:  #6cb6ff;
    --accent-soft:   rgba(74, 163, 255, 0.18);
    --accent-line:   rgba(74, 163, 255, 0.40);
    --gold:          #7cc4ff;

    /* Keep this theme's table borders deriving from its own brand mid-tone (the light theme
       now derives --table-accent-border from --accent; slate/navy restate the grad-mid
       formula to stay unchanged). */
    --table-accent-border: color-mix(in srgb, var(--grad-mid) 42%, var(--border));

    /* Logo mark: blue-family sweep (indigo → azure → cyan) to match the navy theme. */
    --logo-a: #5566ff;
    --logo-b: #3f8fff;
    --logo-c: #35d0e0;

    /* Brand gradient: indigo → azure → cyan. A pure blue-family sweep, no gold. Used on the
       Sign in button, the wordmark, the active nav icon, badges and the loader. */
    --grad-start:    #5566ff;
    --grad-mid:      #3f8fff;
    --grad-end:      #35d0e0;
    --brand-gradient: linear-gradient(120deg,
                        var(--grad-start) 0%,
                        var(--grad-mid) 50%,
                        var(--grad-end) 100%);

    /* Status colours stay meaningful, tuned to sit on navy. */
    --ok:            #46c78e;
    --ok-soft:       rgba(70, 199, 142, 0.16);
    --warn:          #f0b24d;
    --warn-soft:     rgba(240, 178, 77, 0.16);
    --error:         #ff7a6b;
    --error-soft:    rgba(255, 122, 107, 0.16);
    --info:          #58b6f0;
    --info-soft:     rgba(88, 182, 240, 0.16);

    /* 3D lift tokens (blue, never grey). The 3D rules at the foot of the file use these so
       every theme can define its own elevation colour without duplicating the rules. */
    --lift-top:      color-mix(in srgb, #6fb4ff 26%, transparent);   /* lit top edge */
    --lift-surface-a: color-mix(in srgb, #4aa3ff 16%, var(--surface));/* card top (bluer) */
    --lift-surface-b: var(--surface);                                 /* card bottom */
    --lift-hover-a:  color-mix(in srgb, #4aa3ff 24%, var(--surface));
    --lift-hover-b:  color-mix(in srgb, #4aa3ff 8%, var(--surface));
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
    margin: 0;
    font-family: var(--sans);
    font-size: 14px;
    line-height: 1.5;
    background: var(--bg-page);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    transition: background-color 0.2s ease, color 0.2s ease;
}

/*
  The ambient wash sits behind everything as a fixed layer, so it does not scroll
  with content and does not repaint on every htmx swap.
*/
body::before {
    content: '';
    position: fixed;
    inset: 0;
    background: var(--wash);
    pointer-events: none;
    /*
      Negative, so the wash sits behind page content without each container needing
      to be lifted above it. An earlier version used z-index:0 here and raised
      individual elements instead, which left anything unraised — and any child using
      a negative z-index — painting underneath the wash.
    */
    z-index: -1;
}

code {
    font-family: var(--mono);
    font-size: 0.86em;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 1px 5px;
    color: var(--text-dim);
}

/* ================= App frame ================= */

.app {
    display: grid;
    grid-template-columns: var(--sidebar-w) 1fr;
    height: 100vh;
    overflow: hidden;
    transition: grid-template-columns 0.16s ease;
}

.app.sidebar-collapsed { grid-template-columns: 0 1fr; }

/* ================= Sidebar ================= */

.sidebar {
    /* Slightly translucent so the ambient wash reads through the chrome */
    background: color-mix(in srgb, var(--bg-sidebar) 88%, transparent);
    backdrop-filter: blur(10px);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/*
  Collapsing narrows the grid column to zero and clips the contents. The sidebar
  must stay a grid item: removing it with display:none shifts main into the
  zero-width first column and blanks the whole page.
*/
.app.sidebar-collapsed .sidebar {
    border-right: none;
    visibility: hidden;
}

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 9px;
    height: var(--topbar-h);
    padding: 0 16px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
    text-decoration: none;
    color: var(--text);
    transition: background 0.12s;
}

.sidebar-brand:hover { background: var(--bg-subtle); }

.brand-logo { width: 24px; height: 24px; flex-shrink: 0; }

/* The mark's gradient follows the theme via --logo-a/b/c. */
.brand-logo .logo-stop-a { stop-color: var(--logo-a); }
.brand-logo .logo-stop-b { stop-color: var(--logo-b); }
.brand-logo .logo-stop-c { stop-color: var(--logo-c); }

/*
  Wordmark: heavy stem, light suffix, single ink colour. The contrast is carried
  by weight rather than colour, which reads as more considered than a two-tone
  treatment and survives being placed on any background.
*/
.brand-name {
    font-weight: 750;
    font-size: 14.5px;
    letter-spacing: 0.2px;
    white-space: nowrap;
    color: var(--text);
}

.brand-name .suffix {
    font-weight: 400;
    letter-spacing: 0.1px;
}

.nav {
    flex: 1;
    overflow-y: auto;
    /* Tight top padding so there is no large gap above the first section. */
    padding: 2px 0 12px;
}

.nav::-webkit-scrollbar { width: 10px; }
.nav::-webkit-scrollbar-track { background: transparent; }
.nav::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: 5px;
    border: 3px solid var(--bg-sidebar);
}
.nav::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }

/* ---- Section: a top-level heading in the sidebar ---- */

.nav-section { margin-bottom: 1px; }

.section-head {
    display: flex;
    align-items: center;
    gap: 7px;
    width: 100%;
    padding: 6px 14px 4px;
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-size: 11px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--text-faint);
    text-align: left;
    border-radius: var(--radius-sm);
    transition: color 0.12s, background 0.12s;
}

.section-head:hover { color: var(--text-dim); background: var(--bg-subtle); }

/* Icon sprite host: present in the DOM but never rendered */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/*
  Icons are single-weight thin strokes in muted grey, so the sidebar reads as a
  calm list. Colour is spent only on the selected item.
*/
.section-icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
    color: var(--text-faint);
    transition: color 0.12s;
}

.section-head:hover .section-icon { color: var(--text-dim); }

.chevron {
    font-size: 8px;
    width: 8px;
    display: inline-block;
    transition: transform 0.16s ease;
    color: var(--text-faint);
    flex-shrink: 0;
}

.nav-section.collapsed .chevron { transform: rotate(-90deg); }

.section-label { flex: 1; }

.section-count {
    font-family: var(--mono);
    font-size: 9.5px;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--text-faint);
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: 9px;
    padding: 0 5px;
    min-width: 17px;
    text-align: center;
}

.section-items {
    list-style: none;
    margin: 0;
    padding: 0 8px 2px;
}

.nav-section.collapsed .section-items,
.nav-section.collapsed .section-empty { display: none; }

/* ---- Feature: nested under its section ---- */

.nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 4px 10px 4px 12px;
    margin-bottom: 1px;
    /* Buttons do not inherit these, so they are set explicitly */
    background: none;
    border: none;
    font-family: inherit;
    text-align: left;
    color: var(--text-dim);
    text-decoration: none;
    font-size: 13.5px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}

.nav-item:focus-visible,
.user-menu-item:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.nav-item:hover { background: var(--bg-subtle); color: var(--text); }

/* Selected: dark ink and weight, matching the wordmark's logic */
.nav-item.selected {
    background: var(--bg-subtle);
    color: var(--text);
    font-weight: 600;
}

/* Item icon: a bordered tile that fills with the brand gradient when selected */
.item-icon {
    width: 26px;
    height: 26px;
    border-radius: 7px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--text-faint);
    transition: background 0.14s, color 0.14s, border-color 0.14s;
}

.item-icon svg {
    width: 14px;
    height: 14px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.nav-item:hover .item-icon { border-color: var(--border-strong); color: var(--text-dim); }

.nav-item.selected .item-icon {
    background: var(--brand-gradient);
    border-color: transparent;
    color: #fff;
}

.nav-item .label { flex: 1; min-width: 0; }

/* Capability-gated: shown so its absence is explained, but inert */
.nav-item.disabled {
    color: var(--text-faint);
    cursor: not-allowed;
    align-items: flex-start;
}

.nav-item.disabled:hover { background: none; }
.nav-item.disabled .item-icon { opacity: 0.55; }

.nav-item .reason {
    display: block;
    font-family: var(--mono);
    font-size: 9.5px;
    font-style: normal;
    color: var(--text-faint);
    margin-top: 1px;
}

.section-empty {
    margin: 0;
    padding: 2px 14px 8px 20px;
    font-size: 12px;
    color: var(--text-faint);
    font-style: italic;
}

/* ---- User block, pinned at the foot ---- */

.sidebar-foot {
    position: relative;
    flex-shrink: 0;
    border-top: 1px solid var(--border);
    padding: 8px 10px;
}

/* The whole block is the menu trigger */
.user-trigger {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 6px 8px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: inherit;
    text-align: left;
    color: var(--text);
    transition: background 0.12s;
}

.user-trigger:hover { background: var(--bg-subtle); }

.trigger-caret {
    color: var(--text-faint);
    flex-shrink: 0;
    transition: transform 0.16s ease;
}

.user-trigger[aria-expanded="true"] .trigger-caret { transform: rotate(180deg); }

.avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--brand-gradient);
    color: #fff;
    font-size: 11.5px;
    font-weight: 650;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    text-transform: uppercase;
}

.avatar-lg { width: 36px; height: 36px; font-size: 13px; }

.user-meta { flex: 1; min-width: 0; }

.user-name {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-role {
    display: block;
    font-size: 10.5px;
    color: var(--text-faint);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- User menu ---- */

.user-menu {
    position: absolute;
    left: 10px;
    right: 10px;
    bottom: calc(100% - 2px);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 8px 28px var(--shadow-color);
    padding: 6px;
    z-index: 40;
}

.user-menu[hidden] { display: none; }

.user-menu-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 10px 12px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 6px;
}

.user-menu-head .user-name { font-size: 13.5px; }
.user-menu-head .user-role { font-size: 11.5px; }

.user-menu-items { display: flex; flex-direction: column; }

.user-menu-form { margin: 0; display: flex; }

.user-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    font-size: 13px;
    font-family: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}

.user-menu-item:hover { background: var(--bg-subtle); color: var(--text); }
.user-menu-item .chrome-icon { color: var(--text-faint); flex-shrink: 0; }
.user-menu-item:hover .chrome-icon { color: var(--accent); }

/* ================= Main ================= */

.main {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-width: 0;
}

.topbar {
    display: flex;
    align-items: center;
    gap: 14px;
    height: var(--topbar-h);
    padding: 0 20px;
    background: var(--scrim);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.crumb {
    display: flex;
    align-items: baseline;
    gap: 7px;
    font-size: 13px;
    min-width: 0;
    flex: 1;
}

.crumb-section { color: var(--text-faint); }

.crumb-section:not(:empty)::after {
    content: '›';
    margin-left: 7px;
    color: var(--text-faint);
    font-size: 14px;
}

.crumb-item { color: var(--text); font-weight: 600; }

.topbar-right { display: flex; align-items: center; gap: 9px; }

.chip {
    font-size: 10.5px;
    font-weight: 650;
    letter-spacing: 0.3px;
    padding: 3px 9px;
    border-radius: 11px;
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

/* Demo data must never be mistaken for a real cluster reading */
.chip-demo {
    background: var(--warn-soft);
    color: var(--warn);
    border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
}

.chip-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
}

.chip-cluster {
    background: var(--bg-subtle);
    color: var(--text-dim);
    border: 1px solid var(--border);
    font-family: var(--mono);
    font-weight: 500;
}

.btn-icon {
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    cursor: pointer;
    font-size: 14px;
    width: 30px;
    height: 28px;
    line-height: 1;
    padding: 0;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.btn-icon:hover {
    color: var(--accent);
    border-color: var(--accent-line);
    background: var(--accent-soft);
}

.btn-icon.spinning { animation: spin 0.6s linear; }

/* Brief confirmation flash after Copy/Export. */
.btn-icon.flash-ok {
    color: var(--ok);
    border-color: var(--ok);
    background: var(--ok-soft);
}
.btn-icon.flash-error {
    color: var(--error);
    border-color: var(--error);
    background: var(--error-soft);
}

.toggle-glyph { font-size: 13px; line-height: 1; }

.btn-icon { display: inline-flex; align-items: center; justify-content: center; }

.chrome-icon {
    width: 15px;
    height: 15px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Only the glyph for the CURRENT theme shows; default (no attribute) is light. */
.theme-icon-dark,
.theme-icon-navy,
.theme-icon-slate { display: none; }
.theme-icon-light { display: inline; }

[data-theme="slate"] .theme-icon-light { display: none; }
[data-theme="slate"] .theme-icon-slate { display: inline; }

[data-theme="dark"] .theme-icon-light  { display: none; }
[data-theme="dark"] .theme-icon-dark   { display: inline; }

[data-theme="navy"] .theme-icon-light  { display: none; }
[data-theme="navy"] .theme-icon-navy   { display: inline; }

@keyframes spin { to { transform: rotate(360deg); } }

.content {
    flex: 1;
    overflow-y: auto;
    padding: 22px 24px 44px;
    min-width: 0;
    /* Positioning context for the panel loading overlay. */
    position: relative;
}

/* ================= Banners ================= */

.banner {
    padding: 9px 20px;
    font-size: 12.5px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.banner-warn {
    background: var(--warn-soft);
    border-bottom-color: color-mix(in srgb, var(--warn) 30%, transparent);
    color: var(--warn);
}

.banner code { background: color-mix(in srgb, var(--bg) 60%, transparent); border-color: transparent; color: inherit; }

/* ================= Loading ================= */

/*
 * Full-panel loading overlay. Covers the emptied panel while the next view
 * loads, so the previous view is never left visible mid-fetch. vitals.js toggles
 * the [hidden] attribute; the bar animates on its own while shown.
 */
.panel-loader {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 40px;
    background: var(--bg);
    z-index: 5;
}

.panel-loader[hidden] { display: none; }

/*
 * Inline variant, injected into a sub-panel such as the Cluster Insight output.
 * Flows in place rather than covering the whole content area, and needs no
 * positioning context on its parent.
 */
.panel-loader-inline {
    position: static;
    inset: auto;
    min-height: 180px;
    background: transparent;
}

.panel-loader-bar {
    position: relative;
    width: min(280px, 60%);
    height: 4px;
    border-radius: 999px;
    background: var(--bg-subtle);
    overflow: hidden;
}

.panel-loader-fill {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 40%;
    border-radius: 999px;
    background: var(--brand-gradient);
    /* Indeterminate: signals work without implying a known duration. */
    animation: panel-loader-slide 1.1s ease-in-out infinite;
}

@keyframes panel-loader-slide {
    0%   { left: -40%; }
    100% { left: 100%; }
}

.panel-loader-text {
    color: var(--text-faint);
    font-size: 12.5px;
}

/*
 * Respect reduced-motion: replace the sliding bar with a static full bar that
 * gently pulses, so there is still a signal without the horizontal motion.
 */
@media (prefers-reduced-motion: reduce) {
    .panel-loader-fill {
        left: 0;
        width: 100%;
        animation: panel-loader-pulse 1.4s ease-in-out infinite;
    }
    @keyframes panel-loader-pulse {
        0%, 100% { opacity: 0.35; }
        50%      { opacity: 0.8; }
    }
}

.placeholder { color: var(--text-faint); font-size: 13px; }

/* ================= Donuts (Cluster Overview) ================= */

.donut-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 14px;
    margin-bottom: 18px;
}

.donut-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: 14px 16px 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.donut-head {
    align-self: flex-start;
    font-size: 10.5px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-faint);
}

.donut { display: flex; flex-direction: column; align-items: center; gap: 6px; }

.donut-svg { width: 104px; height: 104px; }

.donut-track {
    fill: none;
    stroke: var(--bg-inset);
    stroke-width: 10;
}

.donut-arc {
    fill: none;
    stroke-width: 10;
    stroke-linecap: round;
    transition: stroke-dasharray 0.4s ease;
}
.donut-arc.ok    { stroke: var(--ok); }
.donut-arc.warn  { stroke: var(--warn); }
.donut-arc.error { stroke: var(--error); }
.donut-arc.na    { stroke: var(--border); }

.donut-pct {
    fill: var(--text);
    font-size: 20px;
    font-weight: 650;
    text-anchor: middle;
    dominant-baseline: middle;
    font-family: var(--font);
}

.donut-sub { font-size: 11.5px; color: var(--text-faint); text-align: center; }

/* ================= Overview tile grid ================= */

.tile-grid {
    display: grid;
    /* 10 columns to match the insight strip below, so the top ring tiles and the chips share
       the same column width and align. There are fewer than 10 ring tiles, so this row is not
       full — the tiles simply take the first columns at the same width as the chips. */
    grid-template-columns: repeat(10, 1fr);
    gap: 12px;
}

.tile-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: 9px 12px 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.tile-card:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md, var(--shadow-sm));
    transform: translateY(-2px);
}
.tile-card:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.tile-head {
    align-self: flex-start;
    font-size: 10px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-faint);
}

.tile-ring { display: flex; flex-direction: column; align-items: center; gap: 2px; }
/* Compact ring — still large enough for a long ratio (e.g. 1980/2040) via the
   figure-sm/xs shrink classes — so the tile row stays short and more of the page shows. */
.tile-svg { width: 78px; height: 78px; }
.tile-ring-figure {
    fill: var(--text);
    font-size: 19px;
    font-weight: 650;
    text-anchor: middle;
    dominant-baseline: middle;
    font-family: var(--font);
}

/* Long ring figures (e.g. big ratios like 1980/2040) shrink to stay inside the
   ring, since SVG text does not reflow. Keeps every tile the same size. The viewBox
   is fixed at 100 units, so these sizes are in viewBox units and scale with the ring. */
.tile-ring-figure.figure-sm  { font-size: 15px; }
.tile-ring-figure.figure-xs  { font-size: 12px; }
.tile-ring-figure.figure-xxs { font-size: 9.5px; }

.tile-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 4px 0 2px;
}
.tile-figure {
    font-size: 30px;
    font-weight: 680;
    letter-spacing: -0.5px;
    color: var(--text);
    line-height: 1.05;
}
.tile-figure.ok    { color: var(--ok); }
.tile-figure.warn  { color: var(--warn); }
.tile-figure.error { color: var(--error); }
.tile-figure.na    { color: var(--text-faint); }
.tile-unit {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-dim);
}
.tile-sub {
    font-size: 10.5px;
    color: var(--text-faint);
    text-align: center;
}

/* Count tiles on the Network hub: a large number rather than a health ring,
   since networking objects have no meaningful "healthy percentage". */
.count-grid {
    display: grid;
    /* Fixed-width tracks packed from the left, so a hub with two tiles shows two
       normal-sized cards rather than stretching each to fill half the row. auto-fill
       (not auto-fit) keeps empty tracks from collapsing the sizing. */
    grid-template-columns: repeat(auto-fill, minmax(220px, 240px));
    gap: 14px;
    margin-bottom: 18px;
}
.count-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: 16px 18px 18px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.count-card:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md, var(--shadow-sm));
    transform: translateY(-2px);
}
.count-card:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.count-head {
    font-size: 10.5px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-faint);
}
.count-value {
    font-size: 30px;
    font-weight: 680;
    letter-spacing: -0.5px;
    color: var(--text);
    line-height: 1.1;
}
.count-value.ok   { color: var(--ok); }
.count-value.warn { color: var(--warn); }
.count-value.na   { color: var(--text-faint); }
.count-sub { font-size: 11.5px; color: var(--text-faint); }

/* A non-clickable count tile (e.g. Secrets, which is never read): no pointer, no
   lift, and dimmed so it reads as informational rather than actionable. */
.count-card-static {
    cursor: default;
    opacity: 0.72;
}
.count-card-static:hover {
    border-color: var(--border);
    box-shadow: var(--shadow-sm);
    transform: none;
}

/* Clickable tiles on the Workloads hub: reset the button chrome, then add a
   pointer affordance and a lift on hover so the whole card reads as a link. */
.donut-card-link {
    font: inherit;
    color: inherit;
    text-align: inherit;
    cursor: pointer;
    width: 100%;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.donut-card-link:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md, var(--shadow-sm));
    transform: translateY(-2px);
}
.donut-card-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ================= Module views ================= */

.module-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 18px;
}



/* Title + description share one line: the h2 stays the large label and the description
   sits inline to its right at its own small size, aligned on the baseline. The inner
   wrapper is the unclassed <div> that every module-head holds (h2 + p.module-sub). Wraps
   to a second line only when the viewport is too narrow to hold both. */
.module-head > div {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    min-width: 0;
}

.module-head h2 {
    margin: 0;
    font-size: 20px;
    font-weight: 650;
    letter-spacing: -0.3px;
    flex: 0 0 auto;
}

.module-sub {
    margin: 0;
    font-size: 13px;
    color: var(--text-faint);
    min-width: 0;
}

.module-meta {
    font-size: 11.5px;
    color: var(--text-faint);
    font-family: var(--mono);
    text-align: right;
    white-space: nowrap;
    padding-top: 4px;
}

/* ---- Stat cards ---- */

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
    gap: 12px;
    margin-bottom: 22px;
}

/* Wider tracks for stat cards whose value is a phrase (e.g. "Supported — 321 days
   left") rather than a short figure, so it stays on one line. */
.stat-grid-wide {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

/* The EOL tiles carry the longest phrases in the app ("Past end of life 185 days ago").
   The status value may wrap to a second line, but the tile is wide enough that the
   "Ends <date>" sub-line stays on ONE line. Scoped to the version-eol grid. */
/* Three-line stacked tile: LABEL on line 1, the status phrase on line 2, and the
   "Ends <date>" on line 3 — each a full-width block, left-aligned at the same edge.
   Overrides the shared .pdb-tiles pattern (which renders label/value/sub inline on one
   line); scoped to this grid so other pages are untouched. */
/* Size the tiles to a sensible width and let them sit left, rather than stretching to fill
   the row (auto-fit + 1fr made them full-width). */
#version-eol.stat-grid-wide {
    grid-template-columns: repeat(3, minmax(240px, 300px));
    justify-content: start;
}
/* The tile is a row: icon chip + the text body. The body is a COLUMN so label, status and
   "Ends <date>" stack as three left-aligned lines (overriding the shared pattern that lays
   value + sub inline on one line). */
#version-eol .stat { align-items: center; }
#version-eol .stat-body {
    display: flex; flex-direction: column; align-items: flex-start;
    min-width: 0; white-space: normal; overflow: visible; text-align: left;
}
#version-eol .stat-label { display: block; width: 100%; margin: 0 0 2px; white-space: normal; text-indent: 0; }
#version-eol .stat-value { display: block; width: 100%; margin: 0; padding: 0; text-indent: 0; }
#version-eol .eol-status-value {
    font-size: 14px;
    line-height: 1.35;
    white-space: normal;
    overflow-wrap: anywhere;
}
/* Left-align the "Ends <date>" line to the same edge as the status above it: block, zero
   indent/padding, and collapse the leading whitespace from the template so it does not sit
   a space to the right. */
#version-eol .stat-sub {
    display: block; width: 100%; margin: 3px 0 0; padding: 0;
    white-space: normal; overflow: visible; text-overflow: clip; text-indent: 0; text-align: left;
}
.stat-value.nowrap { white-space: nowrap; }

.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 13px 15px;
    box-shadow: var(--shadow-sm);
}

/* Clickable stat tiles (rendered as <a> or <button>) get the 3D affordance: a resting
   shadow, and a lift + deeper shadow + accent border on hover/focus, so they read as
   controls everywhere the same way the Overview tiles do. Applied to any interactive
   .stat regardless of page, so the look is consistent across the app. */
a.stat, button.stat {
    display: block;
    width: 100%;
    text-align: left;
    color: inherit;
    text-decoration: none;
    font: inherit;
    cursor: pointer;
    transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
a.stat.stat-ic, button.stat.stat-ic { display: flex; }
a.stat:hover, button.stat:hover,
a.stat:focus-visible, button.stat:focus-visible {
    box-shadow: var(--shadow);
    border-color: var(--border-strong);
    transform: translateY(-2px);
    outline: none;
}
a.stat:focus-visible, button.stat:focus-visible {
    border-color: var(--accent);
}
/* A faint corner chevron marks a stat tile as navigable, at rest and stronger on hover. */
a.stat, button.stat { position: relative; }
a.stat::after, button.stat::after {
    content: '›';
    position: absolute;
    top: 8px;
    right: 11px;
    color: var(--accent);
    font-weight: 700;
    opacity: 0.35;
    transition: opacity 0.15s ease;
}
a.stat:hover::after, button.stat:hover::after,
a.stat:focus-visible::after, button.stat:focus-visible::after { opacity: 0.95; }

.stat-label {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--text-faint);
    font-weight: 600;
    margin-bottom: 5px;
}

.stat-value {
    font-size: 22px;
    font-weight: 650;
    font-family: var(--mono);
    line-height: 1.15;
    letter-spacing: -0.5px;
}

.stat-sub { font-size: 11px; color: var(--text-faint); margin-top: 3px; }

.stat-value.ok    { color: var(--ok); }
.stat-value.warn  { color: var(--warn); }
.stat-value.error { color: var(--error); }

/* ---- Stat card with a leading icon chip (modern KPI-card pattern) ----
   A soft-tinted rounded icon square on the left, metric on the right. The chip
   tint follows the metric's status, so an all-clear card reads teal/green and a
   problem card reads amber/red — the colour carries meaning, not decoration. */
.stat-ic {
    display: flex;
    align-items: center;
    gap: 13px;
    transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.stat-ic:hover {
    box-shadow: var(--shadow);
    border-color: var(--border-strong);
    transform: translateY(-1px);
}
.stat-chip {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: 11px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--accent-soft);
    color: var(--accent);
}
.stat-chip svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.stat-chip.ok    { background: var(--ok-soft);    color: var(--ok); }
.stat-chip.warn  { background: var(--warn-soft);  color: var(--warn); }
.stat-chip.error { background: var(--error-soft); color: var(--error); }
.stat-chip.info  { background: var(--info-soft);  color: var(--info); }
.stat-ic .stat-body { min-width: 0; }
.stat-ic .stat-label { margin-bottom: 2px; }
.stat-ic .stat-value { font-size: 21px; }

/* A stat tile that links to a section lower on the page (multi-table pages). Keeps
   the card look, drops the anchor's underline/colour, and lifts slightly on hover so
   it reads as clickable. */
a.stat-link {
    display: block;
    text-decoration: none;
    color: inherit;
    cursor: pointer;
    transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
a.stat-link.stat-ic { display: flex; }
a.stat-link:hover {
    box-shadow: var(--shadow);
    border-color: var(--border-strong);
    transform: translateY(-1px);
}
a.stat-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* The shared overview fragment builds each tile from spans (so a tile can be an <a>
   without invalid block-in-inline nesting). Force the body to stack and the parts to
   read as blocks, matching the hand-written .stat tiles that use <div>s. */
.stat > .stat-body,
.stat-link > .stat-body { display: block; }
.stat-body > .stat-label,
.stat-body > .stat-value,
.stat-body > .stat-sub { display: block; }

/* When a section is jumped to from an overview tile, leave room above it so its title
   is not hidden under the sticky topbar and reads clearly as the scroll target. */
[data-overview-target] { scroll-margin-top: 76px; }
[data-overview-target].overview-flash { animation: overview-flash 1.2s ease; }
@keyframes overview-flash {
    0%   { box-shadow: 0 0 0 2px var(--accent); }
    100% { box-shadow: var(--shadow-sm); }
}

/* ---- Tables ---- */

.table-wrap {
    background: var(--bg);
    /* A stronger edge + a resting shadow so the table reads as a raised card against the
       page, which the hairline border alone did not — the white surface and the faint-grey
       page were too close to tell apart. */
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    overflow: hidden;
    margin-bottom: 18px;
    box-shadow: var(--shadow);
    transition: box-shadow 0.16s ease;
}
.table-wrap:hover { box-shadow: var(--shadow-md); }

.table-title {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 15px;
    font-size: 12px;
    font-weight: 650;
    color: var(--text);
    background: var(--bg-inset);
    border-bottom: 1px solid var(--border);
}

/*
  A plain single-table page repeats its own name in the table title ("Pods" → "Pods, worst
  first"), which the page heading already says — a wasted line. Hide a table title that is
  just text (no child pills, notes or legends). Titles that carry a badge/count/legend, or
  that distinguish one section from another on a multi-table page, add the `keep` class to
  stay visible. This is a density win applied app-wide without editing every template.
*/
.table-title:not(.keep):not(:has(*)) { display: none; }

/*
  Scroll in both directions and cap the height. Columns already scrolled
  horizontally; capping the height means a long table scrolls vertically inside
  its own box instead of pushing the header off the top of the page. A short
  table never reaches the cap, so it shows no vertical scrollbar — the behaviour
  only kicks in for the large tables that need it, with no per-table markup.

  The cap is viewport-relative so it adapts to screen size, less the room the
  page chrome and surrounding cards take.
*/
.table-scroll {
    overflow: auto;
    /*
      Cap the visible height to exactly the sticky header + ten data rows, so a longer table
      scrolls vertically inside this box rather than pushing the page. The precise height is
      set per-table by wireTableRowCap() in vitals.js (measuring the real header + 10 rows,
      since usage-bar rows are taller than plain ones). This is the no-JS fallback: a sane
      cap so the page still does not run away if the script has not run yet.
    */
    max-height: 70vh;
    /*
      Reserve the scrollbar gutter at all times. Without this, a table tall enough
      to scroll gains a vertical scrollbar that narrows the body's content box but
      not the sticky header row (which is painted across the full width), so the
      header cells drift a few pixels right of the columns beneath them. A stable
      gutter gives header and body the same available width, keeping them aligned
      whether or not the scrollbar is present.
    */
    scrollbar-gutter: stable;
}

table.data {
    width: 100%;
    /*
      border-collapse: separate, not collapse. A collapsed table computes column
      widths for sticky <th> independently of the body cells, so the pinned header
      row drifts out of line with the columns beneath it. With separate borders the
      header and body share one column layout and stay aligned. Spacing is zeroed so
      the visual is unchanged; row dividers are drawn per-cell below.
    */
    border-collapse: separate;
    border-spacing: 0;
    font-size: 14px;
}

/*
  Sticky header row: stays pinned to the top of .table-scroll while the body
  scrolls under it. A header cell inherits the alignment of its column via the
  align-* helpers below, so a right-aligned numeric column gets a right-aligned
  header and the two line up.
*/
table.data th {
    text-align: left;
    padding: 8px 16px;
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    /* Darker than the old faint grey so the header reads as its own band above row one,
       not as another dim row. */
    color: var(--text-dim);
    /* A subtle translucent header that stays legible when it sticks over content. */
    background: color-mix(in srgb, var(--bg-inset) 88%, var(--bg));
    backdrop-filter: saturate(1.1) blur(2px);
    /* A crisper 2px accent-tinted rule under the header separates it clearly from the body. */
    border-bottom: 2px solid color-mix(in srgb, var(--accent) 22%, var(--border-strong));
    white-space: nowrap;
    position: sticky;
    top: 0;
    z-index: 3;
}

/* Sortable headers (wired by vitals.js wireTableSort). The caret is a pseudo-element so
   it never shifts the header text or fights the sticky/right-align rules. Neutral by
   default, accent-coloured and pointed once a column is the active sort key. */
table.data th.th-sort { cursor: pointer; user-select: none; }
table.data th.th-sort:hover { color: var(--text); background: color-mix(in srgb, var(--accent) 14%, var(--bg-inset)); }
table.data th.th-sort:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
/* A clear, boxed sort glyph so the control reads as an obvious button, not a faint hint.
   Bigger (13px), higher-contrast, and carried in a small chip after the label. */
table.data th.th-sort::after {
    content: '\21C5';   /* up-down paired arrows: sortable but unsorted */
    margin-left: 6px;
    /* Match the header label's own size and sit on the same baseline so the glyph rides
       the NAMESPACE text line rather than dropping below it. */
    font-size: 1em;
    line-height: inherit;
    color: var(--text-dim);
    opacity: 0.85;
    vertical-align: baseline;
}
table.data th.th-sort:hover::after { color: var(--accent); opacity: 1; }
table.data th.th-sort.sort-asc::after,
table.data th.th-sort.sort-desc::after {
    opacity: 1;
    color: #fff;
    background: var(--accent);
    border-radius: 4px;
    padding: 0 3px;
    font-size: 1em;
}
table.data th.th-sort.sort-asc::after  { content: '\2191'; }  /* up arrow */
table.data th.th-sort.sort-desc::after { content: '\2193'; }  /* down arrow */
/* Right-aligned numeric headers keep the glyph snug to the label on the right edge. */
table.data th.num.th-sort::after { margin-left: 5px; }

table.data td {
    padding: 5px 16px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}

/* Zebra striping: a very faint tint on alternate rows so the eye tracks across a wide,
   dense row (Pod → Phase → Restarts → Containers) without losing the line. Kept subtle so
   it reads as texture, not colour. The tint sits under the hover so hover still wins. */
table.data tbody tr:nth-child(even) td {
    background: color-mix(in srgb, var(--text-faint) 5%, var(--bg));
}

/* A soft accent-tinted hover reads more alive than a flat grey; the transition
   keeps it from flickering as the pointer sweeps a dense table. */
table.data tbody tr {
    transition: background 0.12s ease;
}
table.data tbody tr:hover td {
    background: color-mix(in srgb, var(--accent) 6%, var(--bg));
}

/* Hover accent bar: a thin accent edge on the row's first cell that appears on hover, a
   small tactile cue that the row is a unit. Drawn with an inset shadow so it adds no width
   and never shifts the layout. */
table.data tbody tr td:first-child {
    box-shadow: inset 3px 0 0 transparent;
    transition: box-shadow 0.12s ease;
}
table.data tbody tr:hover td:first-child {
    box-shadow: inset 3px 0 0 var(--accent);
}

/* Round the body's outer corners so it sits flush inside the rounded card, and drop the
   last row's divider so it meets the card edge cleanly. */
table.data tbody tr:last-child td { border-bottom: none; }
table.data tbody tr:last-child td:first-child { border-bottom-left-radius: var(--radius); }
table.data tbody tr:last-child td:last-child  { border-bottom-right-radius: var(--radius); }

td.num, th.num { text-align: right; font-family: var(--mono); }

/* Left-aligned numeric column: mono figures that read better sitting under their
   header on a wide table (small integer counts, not magnitudes to compare). */
td.num-left, th.num-left { text-align: left; font-family: var(--mono); }
table.data th.num-left { text-align: left; }
/* Header cells for numeric columns must right-align to line up with their
   right-aligned values. Scoped with table.data to outrank `table.data th`
   (which sets left) regardless of source order. */
table.data th.num { text-align: right; }
/* Centre-aligned column: header and cell content centred. Uses table.data-scoped selectors
   so it outranks table.data th.num / th.num-left (which set right/left) regardless of source
   order. In a boxed table the chip's inner value is centred too. */
table.data td.col-center,
table.data th.col-center { text-align: center; }
/* The boxed-cell centring (justify-content on the inner chip) is defined AFTER the
   td.num / td.num-left chip-alignment rules below, so it wins for a num.col-center cell —
   see the "col-center overrides" block further down. */

td.mono   { font-family: var(--mono); font-size: 13px; }
td.dim    { color: var(--text-faint); }
td.nowrap { white-space: nowrap; }

/*
  Keep data-table rows on a single line by default. With many columns the browser used to
  squeeze and wrap short cells (a hyphenated namespace, a phase pill, the created age) across
  two or three lines, which read as clutter. Now every cell stays on one line and the whole
  table scrolls horizontally inside its .table-scroll wrapper when it genuinely cannot fit —
  the standard dense-table behaviour. Genuinely long free text (an event message, a pending
  reason, a dependents list) opts back into wrapping with the `wrap` class so it does not
  force the table absurdly wide.
*/
table.data th, table.data td { white-space: nowrap; }
table.data th.wrap, table.data td.wrap { white-space: normal; }

/* A cell that stays on one line and shows an ellipsis when it overflows; the full text
   is available on hover via the element's title attribute. Used for long, low-priority
   columns (e.g. ConfigMap key names) so they do not force a row to wrap. */
td.cell-truncate {
    max-width: 380px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Give identifier columns enough room to stay on one line rather than wrapping a
   hyphenated namespace like "aws-privateca-issuer" across three lines. */
table.data th.col-ns,   table.data td.col-ns   { min-width: 150px; }
table.data th.col-name, table.data td.col-name { min-width: 190px; }

/* Name/identifier cell that shows in full but is capped to ~40 characters wide by default,
   with an ellipsis when it overflows. The complete value is in the cell's title (hover) and
   its data-copy (export), and the column can be dragged wider via the header grip. Replaces
   the old server-side 40-char abbreviation: nothing is trimmed from the value now, only the
   rendered width. */
table.data td.col-cap {
    max-width: 40ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* A link/button inside a capped cell must itself clip, or it overflows the cell. */
table.data td.col-cap .xlink,
table.data td.col-cap > span,
table.data td.col-cap > button {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* Last column exemption: the final column (e.g. Image on the Workload pages) must never
   be width-capped. It has no neighbour to the right to bleed into, and capping it forces
   the user to drag the column just to read the full value — which is awkward at the
   table's right edge. So the last cell shows its content in full and lets the table's own
   horizontal scroll handle any overflow, rather than clipping with an ellipsis. This
   overrides both the base col-cap ceiling and the post-resize clipping for the last
   column only; every other column keeps its cap and drag-to-resize behaviour. Scoped to
   the plain data table (:not(.cell-boxed)); boxed tables size via their own .cell-val
   and are left untouched. */
table.data:not(.cell-boxed) td:last-child.col-cap,
table.data:not(.cell-boxed).cols-resized td:last-child {
    max-width: none;
    overflow: visible;
    text-overflow: clip;
    white-space: nowrap;
}
table.data:not(.cell-boxed) td:last-child.col-cap > span,
table.data:not(.cell-boxed) td:last-child.col-cap > button,
table.data:not(.cell-boxed) td:last-child.col-cap .xlink,
table.data:not(.cell-boxed).cols-resized td:last-child > * {
    max-width: none;
    overflow: visible;
    text-overflow: clip;
}

/* The last column is never capped on ANY data table, boxed ones included. The
   earlier rule above only reached plain (:not(.cell-boxed)) tables, but nearly every
   module list is a cell-boxed table, so their final column was still clipped at 40ch.
   The final column has no neighbour to bleed into, and clipping it forces a drag just to
   read the value at the table's right edge. So uncap the last cell everywhere and let the
   table's own horizontal scroll carry any overflow. Boxed tables render their content in a
   .cell-val chip, so that inner element must be uncapped too. Every other column keeps its
   cap and drag-to-resize behaviour. */
table.data td:last-child.col-cap,
table.data.cols-resized td:last-child {
    max-width: none;
    overflow: visible;
    text-overflow: clip;
    white-space: nowrap;
}
table.data td:last-child.col-cap > span,
table.data td:last-child.col-cap > button,
table.data td:last-child.col-cap .xlink,
table.data td:last-child.col-cap .cell-val,
table.data.cols-resized td:last-child > *,
table.data.cols-resized td:last-child .cell-val {
    max-width: none;
    overflow: visible;
    text-overflow: clip;
}

/* Capped-width cell that WRAPS rather than ellipsing. Same 40ch ceiling as col-cap, but
   the content flows onto multiple lines instead of being clipped. For the few columns
   whose full text is the most critical data on the page and must stay readable — e.g. the
   Message column on Events and Problems — so the column never grows wider than 40ch yet
   nothing is hidden. */
table.data td.col-cap-wrap,
table.data th.col-cap-wrap {
    max-width: 40ch;
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* ---- Draggable column resize ---- */

/* The absolutely-placed grip anchors to its header cell. The header keeps
   position: sticky (from the base `table.data th` rule) so it stays pinned when the
   body scrolls — a sticky element is itself a positioning context, so the grip still
   anchors correctly. Do NOT set position: relative here: that would override sticky and
   the header would scroll away under the body (only the non-resizable # column stayed
   pinned otherwise). */

/* A thin grab strip on the right edge of a resizable header. Invisible until hovered so it
   does not clutter the header, but always grabbable. Sits above the sort click target and
   swallows its own events in JS, so dragging never sorts. */
table.data th.th-resizable .col-resize-grip {
    position: absolute;
    top: 0;
    right: 0;
    width: 7px;
    height: 100%;
    cursor: col-resize;
    user-select: none;
    z-index: 2;
}
table.data th.th-resizable .col-resize-grip::after {
    content: "";
    position: absolute;
    top: 25%;
    right: 3px;
    width: 2px;
    height: 50%;
    background: var(--border);
    /* Always visible, so the column boundaries read as an affordance the user can grab —
       not a hidden control that only appears on a pixel-perfect edge hover. Brightens to
       the accent when the header is hovered or the grip itself is grabbed. */
    opacity: 0.7;
    transition: opacity 0.12s ease, background 0.12s ease, width 0.12s ease;
}
table.data th.th-resizable:hover .col-resize-grip::after { opacity: 1; }
table.data th.th-resizable .col-resize-grip:hover::after {
    opacity: 1;
    background: var(--accent);
    /* A touch wider on direct hover so the grabbable strip is obvious. */
    width: 3px;
}
/* While a drag is in progress, show the resize cursor everywhere and suppress text
   selection so the drag reads cleanly. */
body.col-resizing { cursor: col-resize !important; user-select: none !important; }

/* Once any column has been resized, switch the table to fixed layout so the explicit
   <col> pixel widths are honoured exactly rather than redistributed by auto layout. The
   table keeps width:100%, so untouched columns still share the remaining space. */
table.data.cols-resized { table-layout: fixed; }

/* Under fixed layout a nowrap cell overflows its box when a column is dragged narrow,
   bleeding text into the next column. Clip every body/header cell to its column width
   with an ellipsis so narrowing hides the overflow instead of overwriting neighbours;
   the full value stays available via the cell's title/data-copy. Cells that opt into
   wrapping (.wrap) keep wrapping and just clip vertically as needed. */
table.data.cols-resized th,
table.data.cols-resized td {
    overflow: hidden;
    text-overflow: ellipsis;
}
table.data.cols-resized td > * {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: bottom;
}
/* Keep the header label clipping, but never clip the resize grip: it is absolutely
   positioned at the column's right edge (right:0, inside the box), so it survives the
   header's overflow:hidden and stays grabbable at any column width. */
table.data.cols-resized th.th-resizable .col-resize-grip { overflow: visible; }

/* ---- Boxed cell values (the launchpad "chip in every cell" look) ----
   Opt-in per table via `table.data.cell-boxed`. Any value wrapped in a `.cell-val`
   span inside that table renders as a bordered, rounded, subtly-tinted chip, so each
   data point reads as its own token rather than loose text — the distinctive launchpad
   table look, rebuilt with vitals' own tokens (no inline styles, CSP-safe). State tints
   reuse the pill palette: .cell-val-ok / -warn / -error / -info for green/amber/red/blue.
   Left as a modifier so it can be rolled out table by table. */
/* The chip fills its cell so every box in a column is the same width regardless of the
   value's length (the "uniform column" look). Border follows the active theme via
   --table-accent-border. */
table.data.cell-boxed .cell-val {
    display: flex;
    align-items: center;
    gap: 5px;
    width: 100%;
    box-sizing: border-box;
    padding: 1px 8px;
    border: 1px solid var(--table-accent-border);
    border-radius: var(--radius-sm);
    background: var(--bg-inset);
    color: var(--text-dim);
    font-family: var(--mono);
    font-size: 11.5px;
    line-height: 1.7;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Numeric columns (#, Pods, CPU/Mem used) right-align their value inside the box. */
table.data.cell-boxed td.num .cell-val { justify-content: flex-end; }
/* num-left columns (Right-Sizing metrics, Updated/desired) keep their value left-aligned. */
table.data.cell-boxed td.num-left .cell-val { justify-content: flex-start; }

/* col-center overrides — !important so a centred column's chip content centres no matter
   what other .cell-val alignment rule (num right-align, meter left-align) or cache-stale
   ordering is in play. Also centre the chip's own text as a belt-and-suspenders. */
table.data.cell-boxed td.col-center .cell-val {
    justify-content: center !important;
    text-align: center;
}
table.data.cell-boxed td.col-center > .dim:only-child { justify-content: center !important; }
table.data.cell-boxed td.col-center .cell-meter { justify-content: center !important; }

/* Meter variant: the value text sits ON TOP of a proportional background fill inside a
   single chip (no separate bar row below), so the committed CPU/Memory reads at a glance
   without widening the column — the launchpad "text merged with progress" treatment. The
   fill is an absolutely-positioned layer sized to the percent; the text is above it. */
/* The meter must establish its OWN positioning context so the absolutely-positioned fill
   stays inside the cell. This is NOT scoped to .cell-boxed: the CPU/Memory drill-down modal
   renders a plain `table.data` (no .cell-boxed), and without position:relative here the fill
   anchored to the nearest positioned ancestor instead and painted a full-size green band
   across the modal/page (the regression). isolation keeps the z-index:-1 fill from slipping
   behind the panel. */
.cell-meter.cell-val,
.cell-val.cell-meter {
    position: relative;
    isolation: isolate;
    /* Must be a sized flex box even outside .cell-boxed (the CPU/Memory modal table), so the
       percentage fill has a width to size against and a full-height cell to fill. Without this
       the meter collapsed to a zero-box inline span in the modal and the absolute fill escaped. */
    display: flex;
    align-items: center;
    width: 100%;
    box-sizing: border-box;
    padding: 1px 8px;
}
table.data.cell-boxed .cell-val.cell-meter {
    justify-content: flex-start;
}
.cell-meter .cell-meter-fill {
    position: absolute;
    inset: 0 auto 0 0;   /* left-anchored, full height, width set inline per row */
    z-index: -1;
    border-radius: inherit;
    background: color-mix(in srgb, var(--ok) 22%, transparent);
}
.cell-meter .cell-meter-fill.warn  { background: color-mix(in srgb, var(--warn) 26%, transparent); }
.cell-meter .cell-meter-fill.error { background: color-mix(in srgb, var(--error) 30%, transparent); }
.cell-meter .cell-meter-text { position: relative; z-index: 1; }

/* Non-mono variant for plain text values (zone, instance type) that should not be mono. */
table.data.cell-boxed .cell-val.sans { font-family: var(--sans); }

/* Wrap variant: for long free-text columns (event message, selectors) that should wrap
   inside the box rather than truncate to one line. */
table.data.cell-boxed td.col-cap-wrap .cell-val,
table.data.cell-boxed .cell-val.wrap {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* When a chip holds a value plus a trailing marker pill (System "this instance", RBAC
   capability tags), the value text takes the room it needs and truncates only if the cell is
   genuinely too narrow, while the pill keeps its size. The text is given a sensible minimum
   so it can never collapse to nothing behind the pill (the earlier bug). */
table.data.cell-boxed .cell-val > .cell-val-text {
    flex: 1 1 auto;
    min-width: 3ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: none;
}

/* A small secondary line under a cell's primary value — e.g. the operator workload (Kind/name)
   a pending or crashing pod belongs to, shown beneath the pod name. Forced to its own full-width
   line so it sits BELOW the value even inside a flex .cell-val, and dimmed/smaller so it reads as
   context, not a second value. */
.cell-sub {
    flex-basis: 100%;
    display: block;
    width: 100%;
    font-size: 10.5px;
    line-height: 1.3;
    color: var(--text-faint);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A trailing marker pill that sits AFTER a .cell-val-text inside a chip (System "this
   instance", RBAC capability tags) is a flat coloured token: it keeps its size and drops its
   OWN border so it does not read as a box-inside-a-box (the RBAC double-border). Scoped with
   the sibling combinator so it does NOT touch the CronJob run-marker pills (which are a group
   of standalone chips and keep their coloured borders). */
table.data.cell-boxed .cell-val > .cell-val-text ~ .pill,
table.data.cell-boxed .cell-val > .cell-val-text ~ .pill-sm {
    flex: 0 0 auto;
    width: auto;
    max-width: none;
    overflow: visible;
    border-color: transparent;
    justify-content: center;
}

/* A cross-link button (.xlink / .link-btn) sitting inside a chip stays a borderless link —
   it must not clip its own text away or add a nested box; the chip owns the border and the
   ellipsis. Truncation happens on the .cell-val, not the link. */
table.data.cell-boxed .cell-val > .xlink,
table.data.cell-boxed .cell-val > .link-btn,
table.data.cell-boxed .cell-val > span:not(.cell-val-text):not(.pill):not(.pill-sm):not(.sev-dot) {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 0;
}

/* Tighter horizontal cell padding so the themed boxes sit close together and the gap
   between columns is small — the boxes themselves now carry the visual separation. */
table.data.cell-boxed td,
table.data.cell-boxed th { padding-left: 5px; padding-right: 5px; padding-top: 3px; padding-bottom: 3px; }
table.data.cell-boxed td:first-child,
table.data.cell-boxed th:first-child { padding-left: 12px; }
table.data.cell-boxed td:last-child,
table.data.cell-boxed th:last-child { padding-right: 12px; }

/* Themed outer border on the boxed table's wrapper and its filter controls, so the whole
   surface picks up the theme colour, not just the cells. */
.table-wrap.cell-boxed-wrap { border: 1px solid var(--table-accent-border); }
.table-wrap.cell-boxed-wrap .filter-bar .input {
    border-color: var(--table-accent-border);
}

/* Mesh pages: a status-tile grid that lives INSIDE a boxed card (directly under the card's
   title) is a free-floating block, unlike the full-bleed table/title — so without an inset it
   runs flush to the card's top/left/right border. Match the title's 15px horizontal inset and
   lift it off the title's bottom border. Applies to every such embedded grid (Istio #sec-mtls,
   Linkerd #sec-servers, Consul #sec-intentions, Cilium #sec-cilium) via the structural
   `.cell-boxed-wrap > .stat-grid.pdb-tiles` selector; page-level tile grids elsewhere sit on
   the page (not inside a card), so they are not direct children of .cell-boxed-wrap and are
   untouched. */
.cell-boxed-wrap > .stat-grid.pdb-tiles { margin: 12px 15px 16px; }

/* On this page the "mesh-wide STRICT" healthy banner uses .notice-ok (green). The user
   wants it to read as part of the active theme rather than a fixed green, so retint just
   this card's banner to the accent. Scoped to #sec-mtls so OK banners on other pages keep
   their semantic green (an SRE relies on that colour elsewhere). */
#sec-mtls .notice-ok {
    background: var(--accent-soft);
    border: 1px solid var(--table-accent-border);
}
#sec-mtls .notice-ok .notice-title { color: var(--accent); }

/* A pill/meter often sits inside a trigger button (info-trigger opens a modal; cell-btn
   opens a drill-down). For the box to fill the whole column — so every row's box is the
   same width regardless of label length — the button must be full-width too, not shrink to
   its text. Without this the pill filled only the button and widths varied per row. */
table.data.cell-boxed td > .info-trigger,
table.data.cell-boxed td > .cell-btn,
table.data.cell-boxed td > button.info-trigger,
table.data.cell-boxed td .info-trigger.usage-cell {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: inherit;
    padding: 0;
    background: none;
    border: 0;
}

/* The Status pill adopts the same rectangular box shape AND the same height as the other
   cell chips inside a boxed table, so the whole row lines up as one consistent token style.
   Padding/line-height/font-size are matched to .cell-val so the box height is identical.
   width:100% + the full-width button rule above make every pill fill its column uniformly. */
table.data.cell-boxed td .pill {
    border-radius: var(--radius-sm);
    display: flex;
    width: 100%;
    box-sizing: border-box;
    justify-content: center;
    padding: 1px 8px;
    font-size: 11.5px;
    line-height: 1.7;
}
/* A pill whose state has no coloured border (e.g. an event "Normal" with a transparent
   border) still shows a box in a boxed table, so no status cell reads as borderless. */
table.data.cell-boxed td > .pill[class*="pill-"] { border-color: var(--table-accent-border); }
table.data.cell-boxed td > .pill.pill-ok      { border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
table.data.cell-boxed td > .pill.pill-warn    { border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
table.data.cell-boxed td > .pill.pill-error   { border-color: color-mix(in srgb, var(--error) 30%, transparent); }
table.data.cell-boxed td > .pill.pill-info    { border-color: color-mix(in srgb, var(--info) 30%, transparent); }

/* Small run/outcome pills that appear as a GROUP in one cell (CronJob recent runs) are a
   set of chips, not one status: keep them inline, auto-width, on a single clipped line
   inside the chip (the cell opens a modal for the full list). */
table.data.cell-boxed td .cron-runs {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    width: 100%;
    overflow: hidden;
}
table.data.cell-boxed td .cron-runs .pill { display: inline-flex; width: auto; flex-shrink: 0; }
/* Compact single-letter run marker (S/F/R) when a CronJob has many runs — a tight square
   so the strip reads like a run history without wide outcome words. */
table.data.cell-boxed td .cron-runs .cron-run-letter {
    min-width: 16px;
    padding: 1px 0;
    justify-content: center;
    font-weight: 700;
}

/* A small leading severity dot inside a chip (Blast Radius namespace), coloured by state.
   Sits within the .cell-val so the dot + value read as one boxed token. */
.cell-val .sev-dot {
    flex-shrink: 0;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--text-faint);
}
.cell-val .sev-dot.sev-error   { background: var(--error); }
.cell-val .sev-dot.sev-warn    { background: var(--warn); }
.cell-val .sev-dot.sev-ok      { background: var(--ok); }
.cell-val .sev-dot.sev-neutral { background: var(--text-faint); }

/* A small amber dot before an image marks an image-hygiene risk (mutable tag / pull Always)
   without a wide badge — the full detail is in the Status modal. */
.cell-val .img-risk-dot {
    flex-shrink: 0;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--warn);
}

/* Blank / dash placeholders get a box too, so an empty value reads as an empty token
   rather than a gap — "a box everywhere" even when the cell has no value. Covers both an
   explicit .cell-dash chip and a bare .dim em-dash placeholder that is the sole direct
   child of a boxed cell (the common `<span class="dim">—</span>` pattern). */
table.data.cell-boxed .cell-dash,
table.data.cell-boxed td > .dim:only-child {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    box-sizing: border-box;
    padding: 1px 8px;
    border: 1px solid var(--table-accent-border);
    border-radius: var(--radius-sm);
    background: var(--bg-inset);
    color: var(--text-faint);
    font-family: var(--sans);
    font-size: 11.5px;
    line-height: 1.7;
}
table.data.cell-boxed td.num > .dim:only-child { justify-content: flex-end; }

/* State tints, matching the status-pill palette so colour meaning is consistent app-wide. */
table.data.cell-boxed .cell-val-ok {
    background: var(--ok-soft);    color: var(--ok);
    border-color: color-mix(in srgb, var(--ok) 30%, transparent);
}
table.data.cell-boxed .cell-val-warn {
    background: var(--warn-soft);  color: var(--warn);
    border-color: color-mix(in srgb, var(--warn) 30%, transparent);
}
table.data.cell-boxed .cell-val-error {
    background: var(--error-soft); color: var(--error);
    border-color: color-mix(in srgb, var(--error) 30%, transparent);
}
table.data.cell-boxed .cell-val-info {
    background: var(--info-soft);  color: var(--info);
    border-color: color-mix(in srgb, var(--info) 30%, transparent);
}

/* ---- Status pills ---- */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 650;
    line-height: 1.5;
    white-space: nowrap;
    border: 1px solid transparent;
}
/* A pill that is a clickable link to the area's detail page. Keeps the pill look but
   signals interactivity on hover. */
.pill-btn {
    cursor: pointer;
    font-family: inherit;
    transition: filter .12s, box-shadow .12s;
}
.pill-btn:hover {
    filter: brightness(0.94);
    box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 25%, transparent);
}
/* A tiny leading dot gives each status pill a modern "signal" glyph without an icon.
   Excludes .pill-sm, which is used as a bare colour swatch (no text) in some views. */
.pill:not(.pill-sm).pill-ok::before,
.pill:not(.pill-sm).pill-warn::before,
.pill:not(.pill-sm).pill-error::before,
.pill:not(.pill-sm).pill-info::before,
.pill:not(.pill-sm).pill-neutral::before {
    content: '';
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}

/* A pill that opens a detail modal (e.g. the Nodes Status column) leads with a crisp info
   icon instead of the signal dot, so the column reads as clickable at a glance rather than
   looking like a plain status badge. The .info-pill class suppresses the default ::before dot
   (each state listed to match that rule's 3-class specificity and win by source order); the
   actual mark is an inline SVG (.pill-info-icon) using the same #icon-info the Help button
   uses, so it renders smoothly and consistently rather than as a font-dependent glyph. */
.pill.info-pill.pill-ok::before,
.pill.info-pill.pill-warn::before,
.pill.info-pill.pill-error::before,
.pill.info-pill.pill-info::before,
.pill.info-pill.pill-neutral::before {
    content: none;
}
.pill-info-icon {
    width: 12px;
    height: 12px;
    flex: 0 0 auto;
    stroke: currentColor;       /* inherit the pill's state colour */
    fill: none;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* An expandable parent row's status pill (e.g. a Right-Sizing namespace row that expands to
   its workloads) leads with a down chevron instead of the signal dot, so it reads as "expands"
   rather than as a clickable modal (those use the info icon). Same dot-suppression approach as
   .info-pill: list each state so this matches the dot rule's specificity and wins by order. */
.pill.expand-pill.pill-ok::before,
.pill.expand-pill.pill-warn::before,
.pill.expand-pill.pill-error::before,
.pill.expand-pill.pill-info::before,
.pill.expand-pill.pill-neutral::before {
    content: none;
}
.pill-expand-icon {
    width: 12px;
    height: 12px;
    flex: 0 0 auto;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.pill-ok      { background: var(--ok-soft);    color: var(--ok);        border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.pill-warn    { background: var(--warn-soft);  color: var(--warn);      border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.pill-error   { background: var(--error-soft); color: var(--error);     border-color: color-mix(in srgb, var(--error) 30%, transparent); }
.pill-neutral { background: var(--bg-subtle);  color: var(--text-dim);  border-color: var(--border); }
.pill-info    { background: var(--info-soft);  color: var(--info);      border-color: color-mix(in srgb, var(--info) 30%, transparent); }

/* ---- States ---- */

.state-empty, .state-error {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 26px;
    text-align: left;
    box-shadow: var(--shadow-sm);
}

.state-error { border-color: color-mix(in srgb, var(--error) 30%, transparent); }

.state-icon {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    color: var(--text-faint);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 19px;
    margin: 0 0 12px;
}

.state-title  { font-weight: 650; margin: 0 0 5px; font-size: 14.5px; }
.state-reason { color: var(--text-dim); margin: 0 0 6px; font-size: 13px; }

/*
 * Inline "all clear" panel, shown in place of a table when a diagnostic section has
 * nothing to report. Keeps the section visible and self-documenting rather than hiding it,
 * so a reader can tell the check ran and passed.
 */
.allclear .allclear-body {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 14px 16px;
    font-size: 13px;
    color: var(--text-dim);
}
.allclear-mark { font-size: 15px; font-weight: 700; }
.allclear-text { color: var(--text-dim); }

/*
 * Readiness-by-area breakdown: one horizontal bar per check category, showing where the
 * score is being lost. Bars share the same 0–100 scale as the headline score.
 */
/*
 * Readiness page header: verdict + score on the left, the per-area breakdown on the right,
 * side by side. The left column sizes to its content (the verdict banner narrows to the
 * width of the score/severity tile row beneath it), and the chart takes the rest of the row.
 */
.readiness-top {
    display: grid;
    grid-template-columns: minmax(420px, auto) minmax(0, 1fr);
    gap: 18px;
    align-items: start;
    margin-bottom: 22px;
}
.readiness-top-left {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
}
/* Inside the left column the verdict and score-row lose their own bottom margins;
   the grid gap spaces them. */
.readiness-top-left .verdict,
.readiness-top-left .score-row { margin-bottom: 0; }
.readiness-top-right { margin-bottom: 0; align-self: stretch; }

@media (max-width: 1100px) {
    .readiness-top { grid-template-columns: 1fr; }
}

.readiness-bars {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px 16px;
}
.readiness-bar-row {
    display: grid;
    grid-template-columns: minmax(120px, 170px) minmax(60px, 1fr) 56px auto;
    align-items: center;
    gap: 12px;
}
/* The whole row is a link to the dimension's detail page. */
.readiness-bar-btn {
    width: 100%;
    background: transparent;
    border: 0;
    border-radius: 6px;
    padding: 4px 6px;
    cursor: pointer;
    font-family: inherit;
    text-align: left;
    transition: background .12s;
}
.readiness-bar-btn:hover { background: var(--bg-hover); }
.readiness-bar-label {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}
.readiness-bar-label > span:first-child { font-weight: 600; font-size: 13px; }
.readiness-bar-note {
    color: var(--text-faint);
    font-size: 11px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 200px;
}
.readiness-bar-track {
    height: 12px;
    border-radius: 6px;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    overflow: hidden;
}
.readiness-bar-fill {
    height: 100%;
    border-radius: 6px 0 0 6px;
    transition: width .3s ease;
}
.readiness-bar-fill.bar-ok    { background: var(--ok); }
.readiness-bar-fill.bar-warn  { background: var(--warn); }
.readiness-bar-fill.bar-error { background: var(--error); }
.readiness-bar-figure {
    font-weight: 700;
    font-size: 14px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.readiness-bar-findings {
    display: flex;
    gap: 5px;
    flex-wrap: wrap;
    justify-content: flex-end;
}

@media (max-width: 720px) {
    .readiness-bar-row { grid-template-columns: 1fr 40px; grid-auto-flow: row; }
    .readiness-bar-track { grid-column: 1 / -1; }
    .readiness-bar-findings { grid-column: 1 / -1; justify-content: flex-start; }
}
.state-help   {
    color: var(--text-faint);
    margin: 0;
    font-size: 12.5px;
    text-align: left;
}

/*
 * Explanatory note at the foot of a view.
 *
 * Runs the full width of the page, left-aligned like .state-help — no max-width and no
 * auto margin, so prose is not artificially wrapped into extra lines.
 */
.page-note {
    color: var(--text-faint);
    font-size: 12.5px;
    line-height: 1.55;
    text-align: left;
    margin: 18px 0 0;
    max-width: none;
}

/* Same note placed inside a card, where the card supplies no padding of its own. */
.page-note.inset {
    margin: 0;
    padding: 12px 14px;
}

/* Partial failures shown alongside successful data (R16.1) */
.error-list {
    background: var(--error-soft);
    border: 1px solid color-mix(in srgb, var(--error) 30%, transparent);
    border-radius: var(--radius);
    padding: 11px 14px;
    margin-bottom: 16px;
    font-size: 12.5px;
}

.error-list-title { font-weight: 650; color: var(--error); margin-bottom: 3px; }
.error-list ul { margin: 0; padding-left: 18px; color: var(--text-dim); }

.notice {
    border-radius: var(--radius);
    padding: 11px 14px;
    margin-bottom: 16px;
    font-size: 12.5px;
}

/* A notice placed inside a table card (e.g. the "Finding what is calling it" callout under
   the deprecated-APIs table) should inset from the card edges rather than running flush to
   its border. Page-level notices, outside a table-wrap, are unaffected and stay full-width. */
.table-wrap .notice { margin: 0 14px 14px; }

/* Warn notices follow the ACTIVE THEME's accent (sunset on light/dark, steel-blue on
   slate, azure on navy) rather than a fixed amber, so these page banners read as part of
   the current theme. Error (red) and OK (green) notices keep their status colour, since
   recolouring those would strip a meaning an SRE relies on. */
.notice-warn { background: var(--accent-soft); border: 1px solid var(--table-accent-border); }

/* A healthy, reassuring notice (e.g. mesh-wide STRICT mTLS is on). */
.notice-ok { background: var(--ok-soft); border: 1px solid color-mix(in srgb, var(--ok) 30%, transparent); }
.notice-ok .notice-title { color: var(--ok); }

/* Informational and plain (neutral) notices follow the ACTIVE THEME's accent rather than a
   fixed blue, so the banner reads as part of the current theme — sunset on light/dark, teal
   on graphite, azure on navy. Semantic notices (warn/ok/error) deliberately keep their
   status colour, because recolouring a warning would strip its meaning. The accent tokens
   (--accent, --accent-soft, --table-accent-border) are overridden per theme, so this needs
   no per-theme rule. A plain <div class="notice"> with no modifier gets the same treatment. */
.notice-info,
.notice:not(.notice-warn):not(.notice-ok):not(.notice-error):not(.notice-info) {
    background: var(--accent-soft);
    border: 1px solid var(--table-accent-border);
    color: var(--text-dim);
    line-height: 1.55;
}
.notice-info .notice-title,
.notice:not(.notice-warn):not(.notice-ok):not(.notice-error) .notice-title {
    color: var(--accent);
}

/* Default notice title colour follows the theme accent (warn notices are now accent-tinted).
   Semantic variants set their own below. */
.notice-title { font-weight: 650; color: var(--accent); margin-bottom: 3px; }
.notice-warn .notice-title  { color: var(--accent); }
.notice-error .notice-title { color: var(--error); }
.notice p { margin: 0; color: var(--text-dim); }

.truncated-note { font-size: 12px; color: var(--warn); margin-bottom: 10px; }

/* ================= Login ================= */

.login-body {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 32px 24px;
}

/* Two columns: brand statement on the left, form on the right */
.login-split {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: 60px;
    width: 100%;
    max-width: 980px;
}

/* ---- Left column ---- */

.login-pitch { padding-left: 6px; }

/* Gradient pill, the loudest use of the brand colours */
.badge-pill {
    display: inline-block;
    background: var(--brand-gradient);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.4px;
    padding: 5px 15px;
    border-radius: 999px;
    margin-bottom: 18px;
}

/*
  Large, light-weight wordmark with the gradient carried by the letters. Falls
  back to the teal end where background-clip:text is unsupported.
*/
/*
  Gradient-filled text. The transparent fill is applied only inside @supports, so a
  browser that cannot clip a background to text keeps solid teal letters rather than
  rendering them invisible.
*/
.pitch-word {
    margin: 0;
    font-size: 62px;
    font-weight: 300;
    letter-spacing: -1.6px;
    line-height: 1.05;
    color: var(--grad-start);
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
    .pitch-word {
        background: var(--brand-gradient);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
    }
}

.pitch-sub {
    margin: 10px 0 0;
    font-size: 16.5px;
    color: var(--text-dim);
}

.pitch-points {
    margin: 26px 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.pitch-points li {
    position: relative;
    padding-left: 20px;
    font-size: 13.5px;
    color: var(--text-faint);
}

.pitch-points li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 7px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--brand-gradient);
}

/* ---- Right column ---- */

.login-panel { display: flex; flex-direction: column; align-items: center; }

/*
  The glow is a sibling behind the card rather than a negative-z-index child: a
  negative child escapes the card's stacking context and can paint over unrelated
  content. isolation:isolate keeps this contained.
*/
.login-card {
    position: relative;
    isolation: isolate;
    width: 100%;
    max-width: 372px;
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    backdrop-filter: blur(12px);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 32px 30px 26px;
    box-shadow: 0 10px 34px var(--shadow-color);
}

/* Gradient glow bleeding out from behind the card */
.login-card::before {
    content: '';
    position: absolute;
    inset: -20px;
    border-radius: 26px;
    background: var(--brand-gradient);
    opacity: 0.15;
    filter: blur(26px);
    z-index: -1;
    pointer-events: none;
}

.card-title {
    margin: 0 0 4px;
    font-size: 20px;
    font-weight: 650;
    letter-spacing: -0.2px;
}

.card-sub { margin: 0 0 20px; font-size: 13px; color: var(--text-faint); }

/* Full-width gradient button, matching the reference */
.btn-gradient {
    margin-top: 22px;
    padding: 11px;
    background: var(--brand-gradient);
    border: none;
    border-radius: var(--radius-sm);
    color: #fff;
    font-size: 14px;
    font-weight: 650;
    font-family: inherit;
    cursor: pointer;
    transition: filter 0.14s, transform 0.06s;
}

.btn-gradient:hover  { filter: brightness(1.07) saturate(1.05); }
.btn-gradient:active { transform: translateY(1px); }

/* ---- SSO, not yet wired ---- */

.sso-block { margin-top: 22px; }

.divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
    color: var(--text-faint);
    font-size: 11.5px;
}

.divider::before, .divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
}

.btn-outline {
    width: 100%;
    padding: 10px;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    font-size: 13.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}

.btn-outline:disabled { opacity: 0.5; cursor: not-allowed; }

.sso-note {
    margin: 8px 0 0;
    font-size: 11.5px;
    color: var(--text-faint);
    text-align: center;
}

.login-form { display: flex; flex-direction: column; }

.login-form label {
    font-size: 11.5px;
    font-weight: 650;
    color: var(--text-dim);
    margin-top: 12px;
    margin-bottom: 5px;
}

.login-form input {
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 9px 11px;
    color: var(--text);
    font-size: 14px;
    font-family: inherit;
}

.login-form input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Terms acceptance row: a checkbox and a short line with the modal link. */
.login-terms {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 16px;
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.45;
}
.login-terms input[type="checkbox"] {
    margin: 1px 0 0;
    width: 15px;
    height: 15px;
    accent-color: var(--accent);
    flex-shrink: 0;
    cursor: pointer;
}

/* An inline text button that reads as a link — used for the Terms link inside a sentence. */
.link-inline {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--accent);
    font-weight: 600;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.link-inline:hover { color: var(--accent-hover); }

/* Disabled Sign in button (until terms are accepted). */
.btn-gradient:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    filter: none;
}
.btn-gradient:disabled:hover { filter: none; }

/* Terms modal body: readable prose, roomier than a data table. */
.terms-body {
    font-size: 13px;
    color: var(--text-dim);
    line-height: 1.6;
    max-height: 60vh;
}
.terms-body p { margin: 0 0 10px; }
.terms-body ol { margin: 0 0 10px; padding-left: 20px; }
.terms-body li { margin: 0 0 8px; }
.terms-body strong { color: var(--text); }

.btn-primary {
    margin-top: 20px;
    padding: 10px;
    background: var(--accent);
    border: none;
    border-radius: var(--radius-sm);
    color: #fff;
    font-size: 14px;
    font-weight: 650;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.12s;
}
.btn-primary:hover { background: var(--accent-hover); }

.alert {
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    margin-bottom: 12px;
}

/* Borders derived from the status colour so both themes work from one rule */
.alert-error {
    background: var(--error-soft);
    border: 1px solid color-mix(in srgb, var(--error) 32%, transparent);
    color: var(--error);
}
.alert-info {
    background: var(--info-soft);
    border: 1px solid color-mix(in srgb, var(--info) 32%, transparent);
    color: var(--info);
}

.login-hint {
    margin-top: 18px;
    padding: 9px 11px;
    background: var(--warn-soft);
    border: 1px solid color-mix(in srgb, var(--warn) 32%, transparent);
    border-radius: var(--radius-sm);
    font-size: 12px;
    color: var(--warn);
    text-align: center;
}

.login-hint code { background: color-mix(in srgb, var(--bg) 60%, transparent); border-color: transparent; color: inherit; }

.login-footer {
    margin: 20px 0 0;
    text-align: center;
    font-size: 11.5px;
    color: var(--text-faint);
    letter-spacing: 0.4px;
    font-weight: 700;
}

.login-footer span:last-child {
    font-family: var(--mono);
    font-weight: 400;
    margin-left: 6px;
    letter-spacing: 0;
}

/* Single column below the split's comfortable width */
@media (max-width: 860px) {
    .login-split { grid-template-columns: 1fr; gap: 34px; max-width: 400px; }
    .login-pitch { text-align: center; padding-left: 0; }
    .pitch-word  { font-size: 46px; }
    .pitch-points { display: none; }
}

/* ================= Utility ================= */

.btn-sm {
    padding: 4px 10px;
    font-size: 12px;
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    cursor: pointer;
    font-family: inherit;
}
.btn-sm:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }

.bar {
    height: 6px;
    background: color-mix(in srgb, var(--text-faint) 14%, transparent);
    border: none;
    border-radius: 999px;
    overflow: hidden;
    min-width: 54px;
    display: inline-block;
    vertical-align: middle;
}

/* Rounded, softly shaded fill — a subtle top-light gradient reads as a modern meter
   rather than a flat block. Colour still comes from the threshold class. */
.bar-fill {
    height: 100%;
    display: block;
    border-radius: 999px;
    background: var(--accent);
    background-image: linear-gradient(180deg, rgba(255,255,255,0.25), rgba(255,255,255,0));
    transition: width 0.3s ease;
}
.bar-fill.ok    { background-color: var(--ok); }
.bar-fill.warn  { background-color: var(--warn); }
.bar-fill.error { background-color: var(--error); }

/* ================= Toolbar and inputs ================= */

.toolbar {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

/*
  Reusable per-table filter bar (fragments/filters.html). It sits inside the
  padding-less .table-wrap, between the title and the table, so it needs its own
  horizontal padding to line up with the title rather than hugging the border.
*/
.table-wrap > .filter-bar {
    padding: 12px 15px 4px;
    margin-bottom: 0;
}
.filter-bar .input { min-width: 200px; }
.filter-bar select.input { min-width: 150px; }
/* Filter controls (Namespace, Category, Status, etc.) take the themed accent border on every
   page, so they are consistent with the accent-bordered cards/tables rather than fading into
   a plain grey outline. Previously only filter bars inside a boxed table got this. */
.filter-bar .input,
.filter-bar .combo-control,
.filter-bar select.input { border-color: var(--table-accent-border); }

/* Pod Insights uses a .toolbar (not a .filter-bar) for its cascading Namespace / Pod /
   Container pickers, so those selects previously kept the plain grey --border-strong while
   every filter-bar control carried the themed accent border. Give the toolbar's pickers the
   same accent border so the console controls match the rest of the app. The token is
   theme-derived, so this follows all four themes automatically. */
.toolbar .input,
.toolbar select.input,
.target-fields .input,
.target-fields select.input { border-color: var(--table-accent-border); }

/* Live row-count badge. In a filter bar it sits to the far right of the controls;
   in a table title (a table with no filter controls) it also floats right. vitals.js
   keeps its text in step with the serial-number column as filters change. */
.table-count {
    font-size: 12px;
    color: var(--text-faint);
    font-family: var(--mono);
    white-space: nowrap;
}
.filter-bar .table-count {
    margin-left: auto;
    align-self: flex-end;
    padding-bottom: 6px;
}
/* When placed in a table title, push it to the right edge of the title row. */
.table-title { display: flex; align-items: center; gap: 8px; }
.table-title > .table-count { margin-left: auto; }

/* Serial-number column: a narrow, right-aligned, faint index down the left edge. */
.data th.sl-col, .data td.sl-col { width: 1%; white-space: nowrap; }
.data td.sl-col { color: var(--text-faint); font-family: var(--mono); text-align: right; }

/* ---- Right-Sizing tree (namespace row expands to per-workload child rows) ---- */
/* The whole namespace row is the toggle: clicking anywhere on it expands the workloads.
   Show a pointer and a hover tint so the affordance is obvious. */
.rs-tree tr.rs-row-toggle { cursor: pointer; }
.rs-tree tr.rs-row-toggle:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.rs-tree tr.rs-row-toggle:hover .rs-caret { color: var(--accent); }
.rs-tree tr.rs-row-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* A larger, easier-to-hit caret. Sized up from 12px and given a fixed-width gutter so the
   namespace name still lines up whether or not a row has children. */
.rs-caret {
    display: inline-block;
    width: 18px;
    margin-right: 4px;
    color: var(--text-dim);
    font-size: 17px;
    line-height: 1;
    text-align: center;
    transition: color 0.12s ease, transform 0.12s ease;
}
/* Reserve the same left gutter on rows with no children, so names line up with parents. */
.rs-toggle-spacer { display: inline-block; width: 18px; margin-right: 4px; }
.rs-child-count { font-size: 11px; margin-left: 6px; }

/* Child workload rows: a faint tinted band, mono name. The child name left-aligns with the
   PARENT namespace name (both start after the same 22px caret gutter) rather than being
   indented under the caret — a subtle guide line keeps the tree readable without pushing the
   child text further right than its parent. */
.rs-tree tr.rs-child { background: color-mix(in srgb, var(--text-faint) 6%, transparent); }
.rs-tree tr.rs-child:hover { background: color-mix(in srgb, var(--text-faint) 10%, transparent); }
.rs-child-name { padding-left: 22px; position: relative; }
.rs-child-name::before {
    content: '';
    position: absolute;
    left: 8px;
    top: 0;
    bottom: 0;
    border-left: 2px solid var(--border);
}
.rs-child .rs-kind { font-size: 10.5px; margin-left: 6px; }

/* The client-side filter hides non-matching items with the hidden attribute. A card/list
   item styled with its own `display` would otherwise ignore it, so enforce hiding for any
   filterable item. Table rows do not need this; card/list views (data-filter-item) do. */
[data-filter-item][hidden] { display: none !important; }

/* Replace the native <select> arrow with the same chevron the combobox uses, so the
   two controls match. Light theme stroke is --text-dim (#454f50); the dark theme
   overrides the image below with its lighter stroke. */
select.input {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 30px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%23454f50' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    background-size: 12px 12px;
}
[data-theme="dark"] select.input {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%23b3c0c7' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.field { display: flex; flex-direction: column; gap: 5px; }

.field-label {
    font-size: 10.5px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-faint);
}

/* Filter controls are self-explanatory via placeholder / "All …" option, so the
   label row above them is redundant. Hide it for any field that holds a filter
   input (text/status/multi-select), app-wide, and for the Namespace Details picker,
   whose placeholder already names it — so its control matches the standard filter bar
   on every other page. Other functional pickers (diagnostics pod/container/lines)
   stay labelled. */
.field:has(> [data-filter-name]) > .field-label,
.field:has(> [data-filter-status]) > .field-label,
.field:has(> #ns-detail-select) > .field-label,
.combo[data-filter-select] > .field-label { display: none; }

.input {
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 7px 10px;
    color: var(--text);
    font-size: 13px;
    font-family: inherit;
    min-width: 180px;
}

.input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ================= Searchable multi-select combobox ================= */

.combo { position: relative; }

.combo-control { position: relative; display: flex; align-items: center; }

.combo-input {
    min-width: 220px;
    width: 100%;
    padding-right: 58px;   /* room for the summary + caret */
}

/* When options are selected the count sits over the input's right side. */
.combo-summary {
    position: absolute;
    right: 26px;
    font-size: 11.5px;
    color: var(--text-dim);
    pointer-events: none;
}

/* Uses the exact same SVG chevron as the native <select>, so the two controls
   match pixel-for-pixel. The text glyph from the markup is hidden; the arrow is
   drawn as a 12px background image, with a dark-theme colour override below. */
.combo-caret {
    position: absolute;
    right: 9px;
    width: 12px;
    height: 12px;
    font-size: 0;                 /* hide the ▾ text glyph */
    color: transparent;
    pointer-events: none;
    transition: transform 0.12s ease;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%23454f50' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 12px 12px;
}
[data-theme="dark"] .combo-caret {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%23b3c0c7' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.combo.combo-open .combo-caret { transform: rotate(180deg); }

/*
  Fixed positioning so the menu escapes the .table-wrap's overflow:hidden clip
  (the filter bar sits inside that clipped box). vitals.js sets top/left/width to
  align it under the control when opening, and keeps it in sync on scroll/resize.
*/
.combo-menu {
    display: none;
    position: fixed;
    z-index: 200;
    min-width: 240px;
    max-height: 320px;
    overflow-y: auto;
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md, 0 6px 20px rgba(0,0,0,0.18));
    padding: 4px;
}
.combo.combo-open .combo-menu { display: block; }

.combo-option {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 9px;
    font-size: 13px;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.combo-option:hover { background: var(--bg-subtle); }
.combo-option input { flex-shrink: 0; }
.combo-option[hidden] { display: none; }

/* "Clear selection" action at the top of the namespace picker menu. */
.combo-clear {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--border);
    padding: 7px 9px;
    margin-bottom: 4px;
    font: inherit;
    font-size: 12px;
    color: var(--accent);
    cursor: pointer;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.combo-clear:hover { background: var(--accent-soft); }

/* Namespace Details navigator (single-select): its options are <button>s, not checkbox
   rows, so give them a plain full-width text-row look and mark the current selection with
   the accent. Reuses the shared .combo* menu/positioning; only the option row differs. */
.combo[data-ns-navigator] .combo-option,
.combo[data-console-ns-navigator] .combo-option {
    width: 100%;
    background: none;
    border: 0;
    font: inherit;
    font-size: 13px;
    color: var(--text);
    text-align: left;
}
.combo[data-ns-navigator] .combo-option.is-selected,
.combo[data-console-ns-navigator] .combo-option.is-selected {
    color: var(--accent);
    font-weight: 700;
    background: var(--accent-soft);
}

/* ================= Usage cells ================= */

/*
  Numbers on top, proportion bar underneath. Both matter: the numbers for capacity
  planning, the bar for spotting a workload near its limit at a glance.
*/
.usage-cell {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 150px;
}

.usage-text { font-size: 12px; white-space: nowrap; }

.usage-cell .bar { width: 100%; min-width: 0; }

.filter-empty {
    margin: 0;
    padding: 16px;
    text-align: center;
    color: var(--text-faint);
    font-size: 13px;
}

/* ================= Drill-down modal ================= */

.modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 32px 20px;
    background: color-mix(in srgb, var(--text) 34%, transparent);
    backdrop-filter: blur(3px);
}

.modal-overlay[hidden] { display: none; }

/* Prevents the page scrolling behind an open modal */
body.modal-open { overflow: hidden; }

.modal {
    width: 100%;
    /* Wide enough that a table of full pod names plus several columns (Restarts, CPU, Memory,
       Node, Zone) reads on one line without a horizontal scrollbar in most cases. Widened ~20%
       (1040 → 1248px) after the enriched drill-down tables gained columns. Still width:100% so it
       shrinks on narrower viewports, and the min() keeps a gutter on small screens — where a
       genuinely wide table scrolls horizontally inside its wrapper instead. */
    max-width: min(1248px, 94vw);
    max-height: 86vh;
    display: flex;
    flex-direction: column;
    /* The modal floats over the blurred full-page overlay, so its panel MUST be fully opaque —
       otherwise the page bleeds through. --surface is opaque on dark/slate/navy, so it is the
       right raised-surface colour there; the light theme deliberately makes --surface ~6%
       translucent (a subtle ambient wash for cards sitting ON the page), which is wrong for a
       floating modal — it let the page's colours (e.g. a red On-Fire area) show through and
       tint the table rows. The light-theme override below restores an opaque panel. */
    background: var(--surface, var(--bg));
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: 0 24px 64px color-mix(in srgb, var(--text) 24%, transparent),
                0 2px 8px color-mix(in srgb, var(--text) 12%, transparent);
    overflow: hidden;
    /* A quick, calm entrance so the modal reads as a distinct surface popping forward. */
    animation: modal-pop 0.16s cubic-bezier(0.2, 0.8, 0.3, 1);
}

/* The Troubleshoot modal is wider than the shared default — its Events/Describe tables and log
   lines are long, so ~20% more width (1248 → 1498px) keeps full pod names, event messages and
   describe output on one line far more often. The .ts-modal marker is on the fragment's own
   shell (which is display:contents), so :has lifts the width onto the .modal container. The
   94vw gutter still caps it on narrow viewports. */
.modal:has(.ts-modal) {
    max-width: min(1498px, 94vw);
}

/* Light theme: --surface is ~6% translucent for the ambient card wash, so pin the floating
   modal to an opaque white panel. Scoped to the light theme only (the html element with no
   data-theme, or an explicit "light"); dark/slate/navy keep their already-opaque --surface. */
html:not([data-theme]) .modal,
html[data-theme="light"] .modal {
    background: #ffffff;
}

@keyframes modal-pop {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}

/* Header bar sits on top: a titled band with a gentle accent wash and a clear divider,
   so every modal reads as a proper window. */
.modal-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--accent) 8%, var(--bg-inset)),
        var(--bg-inset));
    flex-shrink: 0;
}

/* App logo on every modal header: the VITALS brand mark (theme brand gradient + white
   pulse), so a modal reads as part of the application. Injected here so all modals — the
   server-loaded fragments and the client-built info modals, which all share .modal-head —
   get it without editing each one. */
.modal-head::before {
    content: "";
    flex: 0 0 auto;
    align-self: flex-start;
    width: 30px;
    height: 30px;
    border-radius: 9px;
    background-image:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none' stroke='%23FFFFFF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.6 16.6h3.1l2.1-5.4 2.9 10.2 2.3-6.4 1.5 1.6h3'/%3E%3C/svg%3E"),
        var(--brand-gradient);
    background-repeat: no-repeat;
    background-position: center, center;
    background-size: 21px 21px, cover;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.25);
    margin-top: 1px;
}
/* The close button sits at the far right; the title block takes the middle. */
.modal-head .modal-close { margin-left: auto; }

.modal-title {
    margin: 0;
    font-size: 15.5px;
    font-weight: 650;
    letter-spacing: -0.2px;
    color: var(--text);
}

.modal-sub { margin: 4px 0 0; font-size: 11.5px; color: var(--text-faint); }

.modal-close {
    background: none;
    border: 1px solid transparent;
    color: var(--text-faint);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    width: 30px; height: 30px;
    display: grid; place-items: center;
    border-radius: 8px;
    flex-shrink: 0;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.modal-close:hover {
    color: var(--text);
    background: var(--bg-subtle);
    border-color: var(--border);
}
.modal-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* .modal is the flex column with the 86vh cap and overflow:hidden. Between it and the fragment's
   .modal-head/.modal-body there are up to two unstyled wrappers: #modal-content (the swap target)
   and the server fragment's own root (.modal-shell). Both must be display:contents so they drop
   out of the box tree and .modal-head/.modal-body become the direct flex children of .modal —
   the header then stays fixed and the body scrolls. Without this a wrapper grows to fit its
   content and a tall modal is clipped by .modal's overflow, showing no scrollbar. The client-built
   info modals inject .modal-head/.modal-body straight into #modal-content, so contents also works
   there (one wrapper instead of two). */
#modal-content,
.modal-shell { display: contents; }

/* flex:1 + min-height:0 lets the body take the remaining height under the fixed header
   and scroll internally; without min-height:0 a flex child refuses to shrink below its
   content, so a tall modal overflowed instead of scrolling. */
.modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 18px; }

.modal-body .table-scroll {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    /*
      Inside a modal the BODY is the single vertical scroller — so this inner wrapper must not
      add a second vertical scrollbar (the double-scrollbar bug). It keeps only HORIZONTAL
      overflow for a wide table (e.g. the Zone column), grows to the table's full height, and
      drops the page-table 70vh cap. The sticky header then pins against the modal body, which
      is the correct nearest scroll ancestor.
    */
    overflow-x: auto;
    overflow-y: visible;
    max-height: none;
}

/*
  Keep modal table cells on one line so identifier-style values (pod names like
  embedding-service-7d9f8c-x2kfp) are not broken mid-token. The wider modal fits the
  common tables outright; anything genuinely too wide scrolls horizontally inside the
  .table-scroll above rather than wrapping. The STATE column is allowed to wrap since it
  carries a longer phrase and reads fine on two lines.
*/
.modal-body table.data th,
.modal-body table.data td {
    white-space: nowrap;
}
.modal-body table.data td.wrap,
.modal-body table.data th.wrap {
    white-space: normal;
}

/*
  The insight line, which is the reason the modal exists: the table is evidence, this
  is the reading of it.
*/
.insight {
    padding: 11px 13px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    line-height: 1.5;
    margin-bottom: 14px;
    border: 1px solid transparent;
}

.insight-ok    { background: var(--ok-soft);    color: var(--ok);
                 border-color: color-mix(in srgb, var(--ok) 26%, transparent); }
.insight-warn  { background: var(--warn-soft);  color: var(--warn);
                 border-color: color-mix(in srgb, var(--warn) 26%, transparent); }
.insight-error { background: var(--error-soft); color: var(--error);
                 border-color: color-mix(in srgb, var(--error) 26%, transparent); }
/* Neutral/informational lead (e.g. a Normal cluster event): a calm, uncoloured box so the
   message reads as context rather than a verdict. */
.insight-neutral,
.insight-info  { background: var(--bg-inset);   color: var(--text-dim);
                 border-color: var(--border); }

/* ---- Drillable cells ---- */

/*
  Cells are buttons but must not look like a wall of buttons: the affordance is a
  subtle hover, and the pill inside carries the meaning.
*/
.cell-btn {
    display: block;
    width: 100%;
    padding: 3px 5px;
    margin: -3px -5px;
    background: none;
    border: none;
    border-radius: 5px;
    font-family: inherit;
    font-size: inherit;
    color: inherit;
    text-align: inherit;
    cursor: pointer;
    transition: background 0.1s;
}

.cell-btn:hover { background: var(--bg-subtle); }

.cell-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
}

.cell-btn.dim { color: var(--text-faint); }

table.drillable td { padding-top: 6px; padding-bottom: 6px; }

/* ================= Readiness score ================= */

/*
  The go/no-go verdict. Sized to be the first thing read, because "should we ship" is the
  question being asked and a number does not answer it.
*/
.verdict {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 16px 18px;
    border-radius: var(--radius);
    border: 1px solid transparent;
    margin-bottom: 18px;
}

.verdict-ok    { background: var(--ok-soft);
                 border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.verdict-warn  { background: var(--warn-soft);
                 border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.verdict-error { background: var(--error-soft);
                 border-color: color-mix(in srgb, var(--error) 34%, transparent); }

.verdict-mark {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 17px;
    font-weight: 700;
    flex-shrink: 0;
    background: color-mix(in srgb, var(--bg) 60%, transparent);
}

.verdict-ok .verdict-mark    { color: var(--ok); }
.verdict-warn .verdict-mark  { color: var(--warn); }
.verdict-error .verdict-mark { color: var(--error); }

.verdict-body { min-width: 0; }

.verdict-label {
    font-size: 16px;
    font-weight: 700;
    letter-spacing: -0.2px;
    margin-bottom: 3px;
}

.verdict-ok .verdict-label    { color: var(--ok); }
.verdict-warn .verdict-label  { color: var(--warn); }
.verdict-error .verdict-label { color: var(--error); }

.verdict-summary {
    margin: 0;
    font-size: 13px;
    color: var(--text-dim);
    line-height: 1.55;
    max-width: 90ch;
}

.score-row {
    display: grid;
    grid-template-columns: 190px 1fr;
    gap: 12px;
    margin-bottom: 22px;
    align-items: stretch;
}

.score-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.score-caption {
    font-size: 10px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-faint);
    margin-bottom: 4px;
}

.score-value {
    font-size: 46px;
    font-weight: 700;
    font-family: var(--mono);
    line-height: 1;
    letter-spacing: -2px;
}

/* The "/100" reads as a unit, not part of the figure, so keep it small and dim. */
.score-max {
    font-size: 18px;
    font-weight: 500;
    color: var(--text-faint);
    letter-spacing: 0;
    margin-left: 2px;
}

.score-ok    { color: var(--ok); }
.score-info  { color: var(--info); }
.score-warn  { color: var(--warn); }
.score-error { color: var(--error); }

.score-label {
    font-size: 13px;
    font-weight: 650;
    margin-top: 6px;
}

.score-sub { font-size: 11px; color: var(--text-faint); margin-top: 2px; }

.score-stats { margin-bottom: 0; }

/* ---- Findings ---- */

.finding-list { padding: 4px; }

.finding {
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
}

.finding:last-child { border-bottom: none; }

/* Readiness "Fix first": the same theme-accent card treatment as the On Fire rows — each
   parameter is its own bordered card with a full theme-accent border and an always-on
   accent left rail, and its count chip takes the theme accent (no per-severity red/amber).
   Scoped to .rs-fix-first so the plain .finding list used elsewhere is untouched. */
.rs-fix-first { display: flex; flex-direction: column; gap: 6px; padding: 0; }
.rs-fix-first .finding {
    position: relative;
    padding: 10px 12px 10px 38px;
    border: 1px solid var(--accent);
    border-left: 3px solid var(--accent);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}
.rs-fix-first .finding:last-child { border-bottom: 1px solid var(--accent); }
.rs-fix-first .finding:hover { box-shadow: var(--shadow); }

/* Leading marker dot, mirroring the Change Timeline marker (an 11px circle with a soft ring).
   The dot reads in the same colour as the count pill beside it, keyed off the finding's
   severity class (finding-error / -warn / -ok / -info from barClass()), so the circle matches
   the label — the same principle as the timeline. Sits in the left gutter reserved by the
   padding above, aligned to the first row. Scoped to .rs-fix-first so the plain .finding list
   used elsewhere is untouched. The base rule is a neutral fallback for an unclassed finding. */
.rs-fix-first .finding::before {
    content: "";
    position: absolute;
    left: 11px;
    top: 15px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--text-dim);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-dim) 22%, transparent);
}
.rs-fix-first .finding-error::before {
    background: var(--error);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 22%, transparent);
}
.rs-fix-first .finding-warn::before {
    background: var(--warn);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent);
}
.rs-fix-first .finding-ok::before {
    background: var(--ok);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
}
.rs-fix-first .finding-info::before {
    background: var(--info);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--info) 22%, transparent);
}

/* A connecting thread down the marker column, so the "fix first" rows read as one list
   like the Change Timeline. A 1px line drawn from just below the dot down through the inter-
   card gap to the next row's dot. Drawn only when another finding follows (:has), so the last
   row has no dangling line. Centered under the 11px dot at left: 11px (centre ≈ 16px). */
.rs-fix-first .finding:has(~ .finding)::after {
    content: "";
    position: absolute;
    left: 16px;
    top: 29px;
    width: 1px;
    height: calc(100% - 29px + 6px);
    background: var(--border);
}

/* The "FIX" chip (.remedy-label) tracks the finding's severity, the same way the count chip
   does, so a critical row's Fix reads red and a warn row's reads amber — matching how the
   action label is coloured on the other pages, rather than the fixed accent. The finding's
   severity class comes from barClass() (ok / info / warn / error) set in the template; borders
   use a colour-mix of the status colour since there is no per-status --*-line token. */
.rs-fix-first .finding-error .remedy-label {
    color: var(--error);
    background: var(--error-soft);
    border-color: color-mix(in srgb, var(--error) 32%, transparent);
}
.rs-fix-first .finding-warn .remedy-label {
    color: var(--warn);
    background: var(--warn-soft);
    border-color: color-mix(in srgb, var(--warn) 32%, transparent);
}
.rs-fix-first .finding-ok .remedy-label {
    color: var(--ok);
    background: var(--ok-soft);
    border-color: color-mix(in srgb, var(--ok) 32%, transparent);
}
.rs-fix-first .finding-info .remedy-label {
    color: var(--info);
    background: var(--info-soft);
    border-color: color-mix(in srgb, var(--info) 32%, transparent);
}

/* The count chip (e.g. "23 of 29") keeps its original severity colour (red/amber) — the
   border is accent, but the label stays meaningful. */

.finding-head {
    display: flex;
    align-items: baseline;
    gap: 9px;
    flex-wrap: wrap;
    margin-bottom: 6px;
}

.finding-subject { font-size: 12.5px; font-weight: 600; }

.finding-detail { font-size: 13px; }

/* Which rule produced this, pushed right so it does not compete with the finding */
.finding-check {
    margin-left: auto;
    font-size: 10.5px;
    color: var(--text-faint);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.finding-impact {
    margin: 0 0 6px;
    font-size: 12.5px;
    color: var(--text-dim);
    line-height: 1.55;
}

.finding-remedy {
    margin: 0;
    font-size: 12.5px;
    color: var(--text);
    line-height: 1.55;
}

.remedy-label {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--accent);
    background: var(--accent-soft);
    border: 1px solid var(--accent-line);
    border-radius: 4px;
    padding: 1px 6px;
    margin-right: 7px;
    vertical-align: 1px;
}

/* Workload Doctor: the "View crash log" button, pushed to the right of the head. */
.doctor-log-btn { margin-left: 8px; }

/* Readiness findings: the "Inspect" deep-link sits at the right of the finding head,
   just after the check label, so every finding is one click from the page that
   proves it and lets the reader act. */
.finding-inspect {
    margin-left: 12px;
    font-size: 11.5px;
    font-weight: 600;
    flex-shrink: 0;
}

/* Restart Leaderboard: inline severity legend beside a table title, and the dot
   separating a row's two actions. */
.legend-inline {
    margin-left: 12px;
    font-size: 11px;
    font-weight: 400;
    color: var(--text-dim);
    text-transform: none;
    letter-spacing: 0;
}

.legend-inline .pill { margin: 0 3px 0 9px; }
.legend-inline .pill:first-child { margin-left: 0; }

.row-action-sep { color: var(--text-faint); margin: 0 5px; }

/* Row-level severity tint is intentionally disabled app-wide: tables no longer paint a
   coloured background on the highest-severity row. Severity still reads from the status
   pill in each row (e.g. a red "Insufficient memory" / "Failed" chip), so the signal is
   preserved without tinting the whole row. The .row-error class is still emitted by the
   templates (and used by JS/filters via data-status) — only its background is cleared, so
   the hook remains if a tint is ever wanted again. */
table.data tbody tr.row-error td { background: transparent; }

/* ---- Help ---- */

.help-section { margin: 0 0 22px; }
.help-section h3 { margin: 0 0 8px; font-size: 14px; }
/* Help definition tables carry prose in the detail column; let it wrap and keep the table
   within the modal width so there is no horizontal scrollbar. The term column stays tight. */
.help-section table.data { width: 100%; table-layout: fixed; }
.help-section table.data td,
.help-section table.data th { white-space: normal; overflow-wrap: anywhere; vertical-align: top; }
.help-section table.data td:first-child { width: 170px; }

.help-text {
    margin: 0 0 8px;
    font-size: 13px;
    color: var(--text-dim);
    line-height: 1.6;
}

.help-list {
    margin: 0;
    padding-left: 18px;
    font-size: 13px;
    color: var(--text-dim);
    line-height: 1.6;
}

.help-list li { margin-bottom: 6px; }
.help-list strong { color: var(--text); font-weight: 600; }

/* ---- Help page, modern "docs home" layout ----
   A visual/layout refresh toward a cleaner, world-class look that matches the sibling
   About page: one consistent card system (soft shadow + accent hover lift), accent-chip
   icons, generous whitespace, and a calm QA definition list instead of competing boxes.

   SCOPING (modal safety): every selector below is page-exclusive — none is referenced by
   fragments/page-help.html, which owns .help-section*, .help-lead, and .help-text. The one
   formerly-shared class (.help-text, used by the nav-guide intro) was de-shared on the page
   in favour of the new page-scoped .help-lead-text, so restyling here cannot touch a modal.
   The modal-facing .help-text / .help-lead / .help-section rules are left untouched. */

.help-heading {
    margin: 30px 0 14px;
    font-size: 12px;
    font-weight: 650;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
}

/* The navigation-guide intro paragraph. This REPLACES the page's single former
   <p class="help-text"> — .help-text is shared with the per-page help modals, so the page
   uses this page-scoped class instead and the modal rule stays untouched. Kept visually
   identical to the old look so nothing shifts. */
.help-lead-text {
    margin: 0 0 12px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-dim);
    max-width: 90ch;
}

/* Hero band — the shared visual anchor with the About page's hero. */
.help-hero {
    display: flex;
    gap: 16px;
    align-items: flex-start;
    padding: 20px 22px;
    margin: 2px 0 10px;
    border-radius: var(--radius);
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--accent) 12%, transparent),
        color-mix(in srgb, var(--accent) 4%, transparent));
    border: 1px solid var(--accent-line, color-mix(in srgb, var(--accent) 24%, transparent));
}
.help-hero-mark {
    flex: 0 0 auto;
    width: 42px; height: 42px;
    display: grid; place-items: center;
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    color: var(--accent);
}
.help-hero-mark .icon { width: 22px; height: 22px; }
/* The text column (the non-mark flex child) must flex to fill the band, or it shrinks to its
   content width and the hero looks capped at roughly half-width with empty space on the right.
   min-width:0 lets it shrink below content size at narrow widths so it stays responsive.
   Shared with the About hero (same markup), so both fill edge-to-edge; the mark stays left. */
.help-hero > :not(.help-hero-mark) { flex: 1 1 auto; min-width: 0; }
/* Balance the title across lines so it reads as one or two even lines rather than a
   ragged mid-phrase break at normal panel widths. text-wrap:balance is progressively
   enhanced; the pretty fallback (and plain wrapping where neither is supported) keeps
   it from breaking awkwardly. A max-width stops it running the full panel width. */
.help-hero-title {
    margin: 3px 0 7px;
    font-size: 15px;
    font-weight: 650;
    color: var(--text);
    letter-spacing: -0.01em;
    text-wrap: pretty;
    text-wrap: balance;
}
.help-hero-text { margin: 0; font-size: 13px; line-height: 1.65; color: var(--text-dim); }

/* Card grid for the safety promises, sharing/availability, and the demo note.
   One card language: soft surface, hairline border, consistent radius + shadow, and a
   quiet accent hover lift so the cards feel contemporary rather than heavy boxes. */
.help-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
    gap: 14px;
    margin: 0 0 10px;
}
.help-card {
    padding: 16px 18px;
    border-radius: var(--radius);
    background: var(--surface, var(--bg-subtle));
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(20, 32, 32, 0.04));
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.help-card:hover {
    border-color: var(--accent-line, var(--table-accent-border));
    box-shadow: var(--shadow, 0 2px 10px rgba(20, 32, 32, 0.07));
    transform: translateY(-1px);
}
.help-card-icon {
    width: 32px; height: 32px;
    display: grid; place-items: center;
    border-radius: var(--radius-sm);
    margin-bottom: 11px;
    background: var(--accent-soft);
    color: var(--accent);
    border: 1px solid var(--accent-line, var(--table-accent-border));
}
.help-card-icon .icon { width: 17px; height: 17px; }
.help-card-icon.ok   { background: var(--ok-soft);   color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.help-card-icon.warn { background: var(--warn-soft); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.help-card-title { font-size: 13.5px; font-weight: 650; color: var(--text); margin-bottom: 6px; }
.help-card-text  { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--text-dim); }
.help-card-text strong { color: var(--text); font-weight: 600; }

/* Demo note — follows the ACTIVE THEME accent like the themed notices elsewhere, rather
   than a fixed amber/cream box. All colours come from theme tokens, so it recolours
   correctly across light (teal), dark, slate and navy. Extra padding and comfortable
   line-height / max-width keep the copy breathing instead of cramping against the edge. */
.help-card-demo {
    background: var(--accent-soft);
    border: 1px solid var(--table-accent-border);
    box-shadow: none;
    padding: 16px 18px;
}
.help-card-demo:hover {
    border-color: var(--accent-line, var(--table-accent-border));
    transform: none;
    box-shadow: none;
}
/* The demo note's icon chip and title take the accent so the block reads as one themed
   unit; this overrides the generic .help-card-icon.warn tint for this card only. */
.help-card-demo .help-card-icon,
.help-card-demo .help-card-icon.warn {
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    color: var(--accent);
    border-color: var(--table-accent-border);
}
.help-card-demo .help-card-title { color: var(--accent); }
.help-card-demo .help-card-text { line-height: 1.65; }

/* Navigation section grid — lighter, denser tiles so nine items scan as a calm grid
   rather than nine heavy boxes. Same card language, tightened. */
.help-nav-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 12px;
    margin: 4px 0 10px;
}
.help-nav-item {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 13px 15px;
    border-radius: var(--radius-sm);
    background: var(--surface, var(--bg-subtle));
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(20, 32, 32, 0.04));
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.help-nav-item:hover {
    border-color: var(--accent-line, var(--table-accent-border));
    box-shadow: var(--shadow, 0 2px 10px rgba(20, 32, 32, 0.07));
    transform: translateY(-1px);
}
.help-nav-item .icon {
    flex: 0 0 auto;
    width: 18px; height: 18px;
    margin-top: 1px;
    color: var(--accent);
}
.help-nav-name { font-size: 13px; font-weight: 650; color: var(--text); margin-bottom: 3px; }
.help-nav-desc { font-size: 12px; line-height: 1.55; color: var(--text-dim); }

/* Questions VITALS answers: a clean two-column definition list with a single hairline
   between rows (no per-row boxes). A subtle accent tick on the page label ties it to the
   theme accent. The last row's rule is removed so the block closes cleanly. */
.help-qa-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
    gap: 2px 24px;
    margin: 4px 0 16px;
    padding: 4px 16px;
    border-radius: var(--radius);
    background: var(--surface, var(--bg-subtle));
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(20, 32, 32, 0.04));
}
.help-qa {
    display: grid;
    grid-template-columns: 160px 1fr;
    gap: 14px;
    padding: 11px 0;
    border-bottom: 1px solid var(--border);
    align-items: baseline;
}
/* Last row in each column track closes without a trailing hairline. In a multi-column
   auto-fit grid, the final one or two items are the visual last rows. */
.help-qa:last-child,
.help-qa:nth-last-child(2) { border-bottom: none; }
.help-qa-page {
    position: relative;
    padding-left: 11px;
    font-size: 12.5px;
    font-weight: 650;
    color: var(--text);
}
.help-qa-page::before {
    content: "";
    position: absolute;
    left: 0; top: 2px; bottom: 2px;
    width: 2px;
    border-radius: 2px;
    background: var(--table-accent-border);
}
.help-qa-ans { font-size: 12.5px; line-height: 1.55; color: var(--text-dim); }

@media (max-width: 640px) {
    .help-qa { grid-template-columns: 1fr; gap: 2px; }
    .help-qa-page { padding-left: 9px; }
    .help-hero { flex-direction: column; }
    .help-card-grid,
    .help-nav-grid { grid-template-columns: 1fr; }
}

/* ---- About page (scoped; reuses the Help card system for everything else) ----
   The hero, headings, mission/safety cards and footer all reuse Help classes; About
   only needs balanced heading wrapping and its own calm, theme-accent demo note. Every
   colour comes from a theme token (--accent-soft/--table-accent-border/--text/--text-dim),
   which each theme overrides, so light/dark/slate/navy are covered with no per-theme rule.
   All selectors are about-prefixed, so no other page is affected. */

/* Balanced wrapping on the About headings so a title breaks into even lines rather than a
   ragged mid-phrase break. No max-width cap: the user wants the hero to run edge-to-edge at
   the full content width, so balancing only tidies the break points, it does not narrow it.
   text-wrap:balance is progressive — browsers without it simply wrap normally. */
.about-title,
.about-hero-title { text-wrap: balance; }

/* Demo note: About gets its own calm, theme-accent framing instead of Help's amber warn box
   the user found cramped. Every colour is a theme token (--accent-soft fill,
   --table-accent-border edge, --accent icon, --text/--text-dim copy), so it reads correctly on
   light teal, dark, slate and navy with no per-theme rule and no hardcoded colour. Roomy
   padding + comfortable line-height. No max-width cap: it spans the full content width,
   matching the hero and the card grid rather than sitting half-width with empty space. */
.about-demo {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    background: var(--accent-soft);
    border: 1px solid var(--table-accent-border);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin: 6px 0 14px;
}
.about-demo-icon {
    flex: 0 0 auto;
    width: 26px; height: 26px;
    display: grid; place-items: center;
    border-radius: var(--radius-sm);
    color: var(--accent);
    margin-top: 1px;
}
.about-demo-icon .icon { width: 16px; height: 16px; }
.about-demo-body { min-width: 0; }
.about-demo-title { font-size: 13px; font-weight: 650; color: var(--text); margin-bottom: 3px; }
.about-demo-text { margin: 0; font-size: 13px; line-height: 1.6; color: var(--text-dim); }

/* Parent-company card: the same surface/border language as .help-card, but allowed to run
   as a single full-width statement block. Colours are theme tokens only, so it reads
   correctly on light/dark/slate/navy with no per-theme rule. */
.about-company {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin: 2px 0 14px;
    max-width: 760px;
}
.about-company p { margin: 0; color: var(--text-dim); font-size: 13px; line-height: 1.6; }
.about-company strong { color: var(--text); }

/* Contact rows: an icon chip beside a column that stacks an uppercase micro-label over the
   link. The outer row keeps align-items:center so the chip is centred against the two
   stacked lines; .about-contact-text is the column that does the stacking. Links are styled
   ONLY here (not via .xlink, which carries table-cell clip/max-width behaviour) so they pick
   up var(--accent) and theme correctly. */
.about-contact { display: flex; flex-direction: column; gap: 10px; max-width: 520px; margin: 2px 0 14px; }
.about-contact-row {
    display: flex; align-items: center; gap: 12px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
}
.about-contact-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.about-contact-icon {
    flex: 0 0 auto; width: 30px; height: 30px;
    display: grid; place-items: center;
    border-radius: var(--radius-sm);
    background: var(--accent-soft);
    color: var(--accent);
    border: 1px solid var(--accent-line, var(--table-accent-border));
}
.about-contact-icon .icon { width: 16px; height: 16px; }
.about-contact-label { font-size: 11px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: .03em; }
.about-contact-row a { color: var(--accent); text-decoration: none; font-size: 13.5px; }
.about-contact-row a:hover { color: var(--accent-hover); text-decoration: underline; }

/* ---- Cluster Snapshot ---- */

/* Actions sit at the top-right of the module head, beside the title. */
.module-actions {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-shrink: 0;
}

.snapshot-wrap {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: auto;
    max-height: 64vh;
}

.snapshot-md {
    margin: 0;
    padding: 16px 18px;
    font-family: var(--mono);
    font-size: 12px;
    line-height: 1.6;
    color: var(--text);
    white-space: pre-wrap;
    word-break: break-word;
    background: var(--bg-inset);
}

/* ClusterRole grant builder: the action buttons sit at the trailing edge of the section title,
   and the checkbox column centres its box. */
.grant-actions {
    margin-left: auto;
    display: inline-flex;
    gap: 6px;
    flex-wrap: wrap;
}
#perm-table td.col-center .perm-check { cursor: pointer; }

/* ---- Change Timeline ---- */

.timeline {
    margin-top: 6px;
    position: relative;
    padding-left: 6px;
}

/* The filter bar sits above the thread, not on it: cancel the left padding and give
   it breathing room so it does not align to the marker column. */
.timeline > .filter-bar {
    margin: 0 0 12px -6px;
}

/* The client-side filter hides non-matching entries with the hidden attribute. A class
   that sets `display` would otherwise override the attribute's default display:none, so
   filtered-out timeline entries must be explicitly hidden. */
.tl-entry[hidden] { display: none; }

.tl-entry {
    display: grid;
    grid-template-columns: 16px 1fr;
    gap: 12px;
    /* Theme-accent card treatment, matching On Fire / Readiness: a full accent border and an
       always-on accent left rail. The leading marker circle and its thread are kept. */
    padding: 10px 12px 10px 10px;
    margin-bottom: 6px;
    border: 1px solid var(--accent);
    border-left: 3px solid var(--accent);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    transition: background 0.12s ease, box-shadow 0.12s ease, transform 0.08s ease;
}

.tl-entry:last-child { margin-bottom: 0; }

/* A quiet hover lift so each entry reads as its own card in the thread. */
.tl-entry:hover {
    box-shadow: var(--shadow);
    transform: translateY(-1px);
}

/* The marker dot and the thread it sits on. A soft ring in the category colour makes the
   node read as a real timeline stop rather than a flat dot. */
.tl-marker {
    position: relative;
    width: 11px;
    height: 11px;
    margin-top: 4px;
    border-radius: 50%;
    /* Default to the neutral pill colour (--text-dim), so the dot matches the category pill
       beside it. Each variant below overrides to its category colour. */
    background: var(--text-dim);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-dim) 22%, transparent);
    flex-shrink: 0;
}

/* Each marker uses the SAME colour as its category pill (pill-warn / pill-info / pill-neutral),
   so the circle matches the label: Problem = warn amber, Rollout = info blue, Scaling/Node =
   neutral grey. Previously the info dot used --accent and the neutral dot used --text-faint,
   which drifted from their pills (especially once --accent became teal). */
.tl-marker.tl-warn {
    background: var(--warn);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent);
}
.tl-marker.tl-info {
    background: var(--info);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--info) 22%, transparent);
}
.tl-marker.tl-neutral {
    background: var(--text-dim);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-dim) 20%, transparent);
}

/* A connecting line down the column, so the entries read as one thread. Drawn only
   when another entry follows (:has), so the filter bar, the empty-state message, or a
   hidden trailing entry never leaves a dangling connector below the last one. */
.tl-entry:has(~ .tl-entry) .tl-marker::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 14px;
    transform: translateX(-50%);
    width: 1px;
    height: calc(100% + 18px);
    background: var(--border);
}

.tl-head {
    display: flex;
    align-items: baseline;
    gap: 9px;
    flex-wrap: wrap;
}

.tl-reason { font-size: 12.5px; font-weight: 600; }
.tl-object { font-size: 12px; color: var(--text-dim); }
.tl-count  { font-size: 10px; }
.tl-age    { margin-left: auto; font-size: 11.5px; }

.tl-message {
    margin: 3px 0 0;
    font-size: 12.5px;
    color: var(--text-dim);
    line-height: 1.5;
}

/* Explanatory line above the log tail in the Doctor modal. */
.log-note {
    margin: 0 0 10px;
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.5;
}

/* Constrain the log tail so a long trace scrolls inside the modal, not the page. */
.modal-body .log-body { max-height: 52vh; overflow: auto; }

/* ---- Troubleshoot modal (reusable Events / Logs / Describe for a workload) ---- */
.ts-controls {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.ts-field { display: flex; flex-direction: column; gap: 4px; }
.ts-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-dim);
}
.ts-select {
    min-width: 260px;
    max-width: 440px;
    padding: 6px 8px;
    font-size: 12.5px;
    font-family: var(--mono, monospace);
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.ts-pod-phase { padding-bottom: 6px; }

/* Tab strip: segmented button group so Events / Logs / Describe clearly read as clickable
   buttons (bordered, filled chips) rather than plain text. The active tab is filled with the
   accent; the others are outlined and lift on hover. */
.ts-tabs {
    display: flex;
    gap: 6px;
    margin-bottom: 14px;
}
.ts-tab {
    padding: 6px 16px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    cursor: pointer;
    user-select: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.ts-tab:hover {
    background: var(--bg-subtle);
    border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
}
.ts-tab.is-active {
    color: #fff;
    background: var(--accent, var(--info));
    border-color: var(--accent, var(--info));
}
.ts-tab.is-active:hover { background: var(--accent, var(--info)); }

/* The per-tab action row (copy + download), right-aligned above each panel's content. */
.ts-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 6px;
    margin-bottom: 8px;
}
.ts-icon-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 9px;
    font-size: 11.5px;
    font-weight: 550;
    color: var(--text-dim);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    white-space: nowrap;
}
.ts-icon-btn:hover { color: var(--text); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.ts-icon-btn svg { width: 13px; height: 13px; }

/* Clickable pod rows in the Namespace Health drill-down modal — each row opens the Troubleshoot
   modal for that pod. A pointer cursor, hover tint and a trailing chevron make the row read as
   actionable. */
.modal-body table.data tr.row-click { cursor: pointer; }
.modal-body table.data tr.row-click:hover td {
    background: color-mix(in srgb, var(--accent) 9%, transparent);
}
.row-click-caret { font-size: 15px; font-weight: 700; color: var(--text-dim); }
.modal-body table.data tr.row-click:hover .row-click-caret { color: var(--accent, var(--info)); }

/* A small risk flag pill (OOMKilled / no limit) beside the resource meter in the CPU/Memory
   drill-down. Compact so it annotates the row without competing with the meter. */
.ts-flag {
    margin-left: 8px;
    font-size: 10px;
    padding: 1px 7px;
    vertical-align: middle;
}
/* A fixed panel height so switching Events / Logs / Describe does not resize the modal — the
   tallest content (Describe) sets the size, and every tab scrolls inside this same box. The
   panel owns the scroll; its children fill it rather than imposing their own differing caps. */
.ts-panel {
    height: 52vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
/* The scrollable content of each tab fills the fixed panel and scrolls internally. */
.ts-panel .table-scroll,
.ts-panel .log-body {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;      /* override the modal-body log-body 52vh cap; the panel bounds it now */
    overflow: auto;
    margin: 0;
}
/* Notes/controls above the scroll area keep their natural height; only the body scrolls. */
.ts-panel > .ts-log-controls,
.ts-panel > .state-help,
.ts-panel > .log-note { flex: 0 0 auto; }
.ts-log-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}
.ts-check { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text); cursor: pointer; }
.modal-sub-note { color: var(--text-dim); }

/* The per-row Troubleshoot action: a quiet secondary link beside the workload/pod name, so it
   is discoverable without competing with the primary name. Appears on the row like the other
   xlinks; a leading wrench glyph marks it as the diagnostics entry point. */
.ts-trigger {
    /* Pushed to the trailing edge of its flex cell so the Troubleshoot action is consistently
       right-aligned in the Workload/Pod column across every page — the per-kind workload pages
       already got this via the growing .cell-val-text; margin-left:auto makes Pending Pods and
       Workload Sync (where the name is a link, not a text span) line up the same way. */
    margin-left: auto;
    padding-left: 8px;
    font-size: 11px;
    font-weight: 550;
    color: var(--text-dim);
    opacity: 0.85;
    white-space: nowrap;
    flex: 0 0 auto;
}
.ts-trigger::before { content: '\1F527'; margin-right: 3px; font-size: 10px; }
.ts-trigger:hover { color: var(--accent, var(--info)); opacity: 1; }

/* The CPU/Memory drill-down modal's Details column: an optional risk flag pill (OOMKilled /
   no limit) followed by the Troubleshoot link, laid out inline on one line so neither wraps
   under the meter (the old inline flag wrapped and the bare ">" row-caret read as noise). The
   flag keeps its size; the Troubleshoot link sits right after it rather than being pushed to
   the far edge, so the two read as one "details" group. */
/* The CPU/Memory & pod drill-down modal's Details cell: a boxed chip (.cell-val, so it gets
   the same border/background as every other cell in the cell-boxed table) holding an optional
   risk-flag pill and the Troubleshoot link, laid out inline on one line. */
.cell-val.ts-details {
    display: flex;
    align-items: center;
    gap: 8px;
}
.cell-val.ts-details .ts-trigger { margin-left: 0; padding-left: 0; }
.cell-val.ts-details .pill,
.cell-val.ts-details .pill-sm { display: inline-flex; width: auto; flex: 0 0 auto; }

.modal-actions {
    margin-top: 12px;
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

/*
  Hanging indent for any remedy line that carries a label (Fix / Next).
  Without this, a wrapped remedy sentence returns to the far-left margin under the
  label pill rather than under the sentence — so a fragment like "the node." reads
  as a stray line. Making the paragraph a flex row puts the label in its own column
  and lets the text wrap as a block beside it. Applies wherever the pattern is used
  (Readiness findings, Workload Doctor next steps); the label-less .finding-remedy on
  the Overview card is untouched because it has no .remedy-label child.
*/
.finding-remedy:has(> .remedy-label) {
    display: flex;
    align-items: baseline;
    gap: 7px;
}

/* The flex gap handles spacing now, so the label's block-layout margin is dropped. */
.finding-remedy:has(> .remedy-label) .remedy-label { margin-right: 0; }

/* Everything after the label shares one wrapping column, so wraps hang correctly. */
.finding-remedy:has(> .remedy-label) > :not(.remedy-label) { flex: 1; }

.next-step { margin-bottom: 4px; }

/* Workload Doctor: the correlated evidence behind a verdict, one fact per row. */
.signal-list {
    margin: 2px 0 10px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.signal-row {
    display: flex;
    align-items: baseline;
    gap: 9px;
}

.signal-detail {
    font-size: 12.5px;
    color: var(--text-dim);
    line-height: 1.5;
    max-width: 92ch;
}

/* A compact pill for the signal "kind" label, narrower than a table pill. */
.pill-sm {
    font-size: 9.5px;
    padding: 0 6px;
    min-width: 58px;
    text-align: center;
    flex-shrink: 0;
}

/* In the grouped tables the impact sits under the detail rather than beside it */
.finding-impact-inline {
    display: block;
    font-size: 11.5px;
    color: var(--text-faint);
    margin-top: 3px;
    line-height: 1.5;
}

.category-note {
    margin-left: auto;
    font-size: 11px;
    font-weight: 400;
    color: var(--text-faint);
    text-transform: none;
    letter-spacing: 0;
}

@media (max-width: 720px) {
    .score-row { grid-template-columns: 1fr; }
}

/* ================= Connectivity matrix ================= */

/*
  Sized to content rather than stretched to the container: with `width:100%` the row-header
  column absorbed all the slack and pushed the cells into a cramped strip on the right.
*/
table.matrix {
    border-collapse: separate;
    border-spacing: 0;
    width: auto;
    min-width: 0;
}

.matrix-corner {
    font-size: 9.5px !important;
    color: var(--text-faint);
    white-space: nowrap;
    position: sticky;
    /* Pinned on both axes: the corner must stay put whether the matrix scrolls
       sideways (many columns) or down (many rows), so it sits above every other
       sticky element. */
    left: 0;
    top: 0;
    background: var(--bg-inset);
    z-index: 5;
    /* Bounded, so the header cannot claim the space the cells need */
    width: 1%;
}

/*
  Column headers are rotated fully vertical (reading bottom-to-top). A 45/60-degree
  slant drifted each label away from its column, so a header no longer sat above the
  cells it names. At 90 degrees the label is centred on its own 34px column and lines
  up with the marks beneath it.
*/
.matrix-head {
    height: 120px;
    padding: 0 !important;
    vertical-align: bottom;
    white-space: nowrap;
    width: 34px;
    max-width: 34px;
    /* Pin the rotated column headers to the top so they stay visible when a tall
       matrix scrolls down, matching the sticky behaviour of data tables. */
    position: sticky;
    top: 0;
    background: var(--bg-inset);
    z-index: 3;
}

.matrix-head span {
    /*
      Vertical label using writing-mode, which lays the text out bottom-to-top
      within the cell reliably — unlike a rotated flex box, which rendered outside
      the 34px cell and appeared blank. mixed orientation keeps the glyphs upright
      but stacked; the 180deg rotate makes it read bottom-to-top so it sits under
      the column rather than over it.
    */
    display: inline-block;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    text-orientation: mixed;
    white-space: nowrap;
    max-height: 112px;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 10.5px;
    font-family: var(--mono);
    text-transform: none;
    letter-spacing: 0;
}

/* The row header stays visible while the matrix scrolls sideways */
.matrix-row-head {
    position: sticky;
    left: 0;
    background: var(--bg);
    font-size: 12px;
    white-space: nowrap;
    z-index: 1;
    border-right: 1px solid var(--border);
    /* Shrink to the longest namespace name; the cells take the rest */
    width: 1%;
    padding-right: 18px !important;
}

table.matrix tbody tr:hover .matrix-row-head { background: var(--bg-inset); }

.matrix-cell {
    text-align: center;
    padding: 4px !important;
    width: 34px;
}

.matrix-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    cursor: default;
    border: 1px solid transparent;
}

.mark-allowed    { background: var(--ok-soft);    color: var(--ok);
                   border-color: color-mix(in srgb, var(--ok) 26%, transparent); }
.mark-open       { background: var(--bg-subtle);  color: var(--text-faint);
                   border-color: var(--border); }
.mark-denied     { background: var(--bg-subtle);  color: var(--text-faint); }
.mark-referenced { background: var(--accent-soft); color: var(--accent);
                   border-color: var(--accent-line); }
/* A configured link that policy blocks: the one cell that demands attention */
.mark-conflict   { background: var(--error-soft); color: var(--error);
                   border-color: color-mix(in srgb, var(--error) 40%, transparent); }
.mark-self       { background: none; color: var(--border-strong); }
.mark-unknown    { background: var(--bg-subtle);  color: var(--text-faint); }

.matrix-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    padding: 12px 14px;
    border-top: 1px solid var(--border);
    font-size: 11.5px;
    color: var(--text-dim);
}

.matrix-legend span { display: inline-flex; align-items: center; gap: 6px; }

.matrix-legend .matrix-mark { width: 18px; height: 18px; font-size: 10.5px; }

/* ================= Diagnostic console ================= */

.target-fields { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; }

.console-layout {
    display: grid;
    grid-template-columns: 290px 1fr;
    gap: 16px;
    align-items: start;
}

.console-library {
    background: var(--surface);
    /* Themed accent border so the inspection library matches the Pod Insights pickers and the
       accent-bordered surfaces elsewhere, rather than fading into a faint grey outline. */
    border: 1px solid var(--table-accent-border);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
}

.library-scroll { max-height: 62vh; overflow-y: auto; padding: 6px; }

.command-group { margin-bottom: 8px; }

.command-group-label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.7px;
    color: var(--text-faint);
    padding: 7px 9px 4px;
}

.command-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 6px 9px;
    margin-bottom: 1px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    font-family: inherit;
    font-size: 12.5px;
    text-align: left;
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}

.command-item:hover { background: var(--bg-subtle); color: var(--text); }

.command-item.active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

.command-item:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.command-label { flex: 1; min-width: 0; }

/* Scope badge — a small square, one letter, colour-keyed to the inspection's scope so the
   grouped library reads at a glance (echoes the launchpad command library rhythm, in
   vitals' own tokens). Every inspection is a read-only typed API call; the colour marks
   what target it needs, not a command class. */
.command-badge {
    flex-shrink: 0;
    width: 15px;
    height: 15px;
    border-radius: 3px;
    display: inline-grid;
    place-items: center;
    font-family: var(--mono);
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
    color: #fff;
    background: var(--text-faint);
}
.command-badge-cluster   { background: var(--accent); }
.command-badge-namespace { background: var(--info, var(--accent)); }
.command-badge-pod       { background: var(--ok, #2e7d32); }
.command-badge-container { background: var(--warn, #b26a00); }

/* Marks inspections that need a pod or container selected first */
.command-scope {
    font-size: 9.5px;
    font-family: var(--mono);
    color: var(--text-faint);
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0 5px;
    flex-shrink: 0;
}

/* The result panel is a defined, bordered surface with a header, so the area reads as a
   result pane even before an inspection is picked — never a blank void. */
.console-output {
    min-width: 0;
    /* Themed accent border, matching the inspection library and the page's pickers. */
    border: 1px solid var(--table-accent-border);
    border-radius: var(--radius);
    background: var(--surface);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.console-output-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 9px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-subtle);
}
.console-output-title { font-size: 12.5px; font-weight: 650; color: var(--text); }
.console-output-hint  { font-size: 11px; color: var(--text-faint); }

.console-output-body { min-height: 320px; }

.console-result {
    border: none;
    border-radius: 0;
    overflow: hidden;
    background: var(--surface);
}

.console-result .log-body { border: none; border-radius: 0; max-height: 56vh; }

.console-result .table-scroll { max-height: 56vh; overflow: auto; }

/* Dark terminal surface for text inspection output (describe, logs, manifests). Fixed dark
   palette in both themes so it always reads as a console, not a document — the same intent
   as the launchpad Cluster Insight output pane, expressed with vitals' spacing/mono token. */
.console-term {
    background: #1e1e1e;
    color: #e6e6e6;
    padding: 12px 14px;
    margin: 0;
    font-family: var(--mono);
    font-size: 12px;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    overflow: auto;
    max-height: 56vh;
}

/* Empty state centred inside the bordered result body. */
.console-empty {
    min-height: 320px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 30px 22px;
    color: var(--text-faint);
    font-size: 13px;
}
.console-empty-icon {
    width: 44px; height: 44px;
    display: grid; place-items: center;
    border-radius: 12px;
    margin-bottom: 12px;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    color: var(--text-faint);
}
.console-empty-icon .icon { width: 22px; height: 22px; }

@media (max-width: 1100px) {
    .console-layout { grid-template-columns: 1fr; }
    .library-scroll { max-height: 34vh; }
}

/* ================= Log browser ================= */

.log-layout {
    display: grid;
    grid-template-columns: 320px 1fr;
    gap: 16px;
    align-items: start;
}

.log-pods {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
}

.pod-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 62vh;
    overflow-y: auto;
}

.pod-entry {
    padding: 10px 13px;
    border-bottom: 1px solid var(--border);
}

.pod-entry:last-child { border-bottom: none; }

.pod-head {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-bottom: 7px;
    flex-wrap: wrap;
}

.pod-name {
    font-size: 12px;
    word-break: break-all;
    flex: 1;
    min-width: 0;
}

.pod-restarts {
    font-size: 10.5px;
    color: var(--warn);
    font-weight: 600;
}

.pod-containers { display: flex; gap: 5px; flex-wrap: wrap; }

.container-chip {
    padding: 3px 9px;
    font-size: 11.5px;
    font-family: var(--mono);
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: 11px;
    color: var(--text-dim);
    cursor: pointer;
}

.container-chip:hover {
    background: var(--accent-soft);
    border-color: var(--accent-line);
    color: var(--accent);
}

.log-output { min-width: 0; }

.log-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 13px;
    background: var(--bg-inset);
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: var(--radius) var(--radius) 0 0;
    font-size: 12.5px;
    flex-wrap: wrap;
}

.log-target { font-weight: 600; }

.log-actions { display: flex; align-items: center; gap: 10px; font-size: 11.5px; }

/* Inline line-count selector in the log header */
.log-lines {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.log-lines .field-label { margin: 0; }

.input-sm {
    padding: 3px 8px;
    font-size: 11.5px;
    height: auto;
    width: auto;
    min-width: 64px;
}

/*
  Log output is a dark surface in both themes: log text is monospaced, dense and
  read for long stretches, and light-on-dark is easier for that than the reverse.
*/
.log-body {
    margin: 0;
    padding: 13px;
    background: #0d1117;
    color: #d5dde5;
    border: 1px solid var(--border);
    border-radius: 0 0 var(--radius) var(--radius);
    font-family: var(--mono);
    font-size: 12px;
    line-height: 1.55;
    max-height: 62vh;
    overflow: auto;
    white-space: pre;
    tab-size: 4;
}

@media (max-width: 1100px) {
    .log-layout { grid-template-columns: 1fr; }
    .pod-list { max-height: 30vh; }
}

@media (max-width: 900px) {
    :root { --sidebar-w: 208px; }
    .content { padding: 16px 14px 30px; }
}

/* ============================================================
   Home page
   ============================================================ */

/*
 * Screen-reader-only text.
 *
 * Deliberately not the classic `position: absolute` recipe. Absolute positioning resolves
 * against the nearest positioned ancestor, and the shell has none between here and the
 * document, so such an element is measured against the document rather than the scrolling
 * panel. Placed far down a long view it extends the document height and raises a second,
 * browser-level scrollbar beside the panel's own.
 *
 * Clipping to a zero-size box keeps the text in the accessibility tree while occupying no
 * space and, being still in flow, it cannot escape its container.
 */
.sr-only {
    display: inline-block;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Overview sits above the sections and has no collapsible heading of its own */
.nav-section-flat { margin-bottom: 4px; }
.nav-item-home .label { font-weight: 600; }

/* ---- Verdict banner ---- */

.verdict {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    gap: 18px;
    align-items: center;
    background: var(--bg);
    border: 1px solid var(--border);
    border-left-width: 3px;
    border-radius: var(--radius);
    padding: 18px 20px;
    margin-bottom: 22px;
    box-shadow: var(--shadow-sm);
}

.verdict-ok    { border-left-color: var(--ok);    background: var(--ok-soft); }
.verdict-warn  { border-left-color: var(--warn);  background: var(--warn-soft); }
.verdict-error { border-left-color: var(--error); background: var(--error-soft); }

.verdict-mark {
    width: 40px; height: 40px;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 20px; font-weight: 600;
    background: var(--bg);
    border: 1px solid var(--border);
}

.verdict-ok    .verdict-mark { color: var(--ok); }
.verdict-warn  .verdict-mark { color: var(--warn); }
.verdict-error .verdict-mark { color: var(--error); }

.verdict-body h3 {
    margin: 0 0 4px;
    font-size: 15.5px;
    font-weight: 650;
}

.verdict-body p {
    margin: 0;
    font-size: 13px;
    color: var(--text-dim);
    line-height: 1.5;
    max-width: 92ch;
}

.verdict-counts {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
}

.count-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 4px 11px;
    font-size: 12px;
    color: var(--text-dim);
    cursor: pointer;
    font-family: inherit;
    transition: border-color .12s, background .12s;
}

.count-chip:hover { border-color: var(--accent); background: var(--bg-hover); }
.count-chip strong { font-size: 13.5px; font-weight: 650; color: var(--text); }
.count-error strong { color: var(--error); }
.count-warn  strong { color: var(--warn); }

/*
 * The shared readiness summary: two tiles side by side — the verdict, and "what the score is
 * measuring" with the score figure. Used identically on the Overview and the Production
 * Readiness page.
 */
.readiness-summary {
    /* Flexbox wrap rather than grid auto-fit: grid auto-fit will keep two columns and shrink
       both below their comfortable width whenever 2×min just barely fits, which is what let
       the two tiles stay cramped side by side on a 13–14" laptop. Flex wrap is decisive —
       each tile declares the width it needs, and the verdict tile drops to its own full-width
       line the instant both cannot get that width. Measured against the real content area
       (not the viewport), so it accounts for the sidebar. */
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-bottom: 22px;
    align-items: stretch;
    /* Fill the full content width, edge to edge, matching the tile grid below. The score
       block and verdict banner share the row proportionally (see their flex weights). */
}
/* Each tile has a real min-width (so flex-wrap drops the verdict to its own line the moment
   both cannot fit without cramming) and proportional grow weights that restore the original
   ~1.15 : 1 split when they share a row. They grow to fill the row as the original grid did,
   keeping the score block the wider of the two. */
.readiness-summary-score {
    flex: 1.15 1 520px;
    min-width: 520px;
}
.readiness-summary-verdict {
    flex: 1 1 400px;
    min-width: 400px;
    margin-bottom: 0;
}
/* Overview: the verdict tile is omitted, so the score tile takes the whole row rather than
   sitting at ~60% with an empty gap beside it. */
.readiness-summary-score-only .readiness-summary-score {
    flex-basis: 100%;
}
.readiness-summary-score {
    display: grid;
    /* The score ring sizes to content; the measuring grid takes the rest. flex-wrap-like
       behaviour comes from the measure grid's own auto-fit below, so nothing overflows. */
    grid-template-columns: auto minmax(0, 1fr);
    gap: 18px;
    align-items: center;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
    box-shadow: var(--shadow-sm);
}
.rs-measure { min-width: 0; }
.rs-measure-title {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-faint);
    margin-bottom: 8px;
}
.rs-measure-grid {
    display: grid;
    /* Three rows, edge to edge: the column count is ceil(count / 3), supplied per-render as
       --rs-cols by the template, and every column is an equal 1fr so the chips fill the full
       width whatever the dimension count. Falls back to 4 columns if the variable is absent. */
    grid-template-columns: repeat(var(--rs-cols, 4), minmax(0, 1fr));
    gap: 4px 8px;
    width: 100%;
}
/* Step the column count down on narrower content so chips never squeeze below a readable
   width; this adds rows rather than shrinking cells. The full six-across layout holds on a
   normal desktop; below that it halves to three, then two, then one. The score tile is
   full-width on the Overview, so these track the real content area. */
@media (max-width: 1300px) {
    .rs-measure-grid { grid-template-columns: repeat(min(var(--rs-cols, 6), 3), minmax(0, 1fr)); }
}
@media (max-width: 720px) {
    .rs-measure-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 460px) {
    .rs-measure-grid { grid-template-columns: 1fr; }
}
.rs-dim {
    display: grid;
    /* Label on the left, figure+bar on the right. The figure now carries "problems/total ·
       pct%", so it gets a touch more of the chip than the label; both tracks are zero-min so
       neither overruns. A long label truncates with an ellipsis and shows in full on hover. */
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    align-items: center;
    gap: 8px;
    /* Resting state carries the accent tint by default — the chips read as a lit, cohesive
       strip rather than plain outlines that only light up on hover. Hover then adds a small
       lift and a stronger border for feedback, without a jarring colour change. */
    background: var(--accent-soft);
    border: 1px solid var(--accent-line, color-mix(in srgb, var(--accent) 35%, transparent));
    border-radius: 6px;
    box-shadow: var(--shadow-sm);
    /* Vertical padding kept tiny; horizontal padding is absorbed by the reduced grid gap
       above, so rows read as tappable chips without making the strip taller or wider. */
    padding: 2px 6px;
    cursor: pointer;
    font-family: inherit;
    text-align: left;
    transition: background 0.12s ease, border-color 0.12s ease,
                box-shadow 0.12s ease, transform 0.12s ease;
}
.rs-dim:hover,
.rs-dim:focus-visible {
    /* Sharp accent border on hover, matching the Overview tile cards: a full-accent edge
       plus a deeper shadow and a small lift, so the chip pops the same way the tiles do. */
    border-color: var(--accent);
    box-shadow: var(--shadow-md), 0 0 0 1px var(--accent);
    transform: translateY(-1px);
    outline: none;
}
/* Parameters with active Critical/High problems are what pull the score down, so give
   them a faint tinted background and a stronger name, drawing the eye to the cells worth
   acting on. Clean parameters stay quiet. */
.rs-dim-problem {
    background: color-mix(in srgb, var(--error) 8%, transparent);
    border-radius: 7px;
}
.rs-dim-problem .rs-dim-name { color: var(--text); font-weight: 600; }

.rs-dim-name {
    font-size: 11.5px;
    color: var(--text-dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rs-dim:hover .rs-dim-name { text-decoration: underline; text-underline-offset: 2px; }

/* The figure box: the coloured percentage bar is its background, the percentage text reads
   on top. Merging the two lets the label take the freed width. The track base tint sits
   under the fill so a low percentage still shows the box outline. */
.rs-dim-figure {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    height: 16px;
    padding: 0 5px;
    border-radius: 4px;
    overflow: hidden;
    white-space: nowrap;
    background: color-mix(in srgb, var(--text-faint) 18%, transparent);
}
.rs-dim-fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    border-radius: 4px 0 0 4px;
    /* Softer than the old solid bar, so the percentage text stays legible on top. */
    opacity: 0.45;
}
.rs-dim-fill.bar-ok    { background: var(--ok); }
.rs-dim-fill.bar-warn  { background: var(--warn); }
.rs-dim-fill.bar-error { background: var(--error); }
/* Unassessed (N/A): no fill, a neutral flat box. */
.rs-dim-figure-na { background: color-mix(in srgb, var(--text-faint) 12%, transparent); }
.rs-dim-figure-text {
    position: relative;   /* above the fill */
    z-index: 1;
    font-size: 10px;
    font-weight: 650;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.rs-dim-figure-na .rs-dim-figure-text { color: var(--text-faint); }
.rs-dim:hover .rs-dim-name { color: var(--text); }
.rs-score-figure { align-self: center; }

/* ---- Readiness page: per-parameter "how the score is calculated" cards ----
   One card per parameter (the 18 that feed the score), three across, each showing its
   problem/total, a health bar, and the plain-English detail — so the page explains exactly
   why the score is what it is and which parameter to open to lift it. */
.section-intro {
    margin: 2px 0 12px;
    font-size: 12.5px;
    color: var(--text-faint);
    line-height: 1.5;
}
.rs-param-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin-bottom: 22px;
}
@media (max-width: 900px)  { .rs-param-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .rs-param-grid { grid-template-columns: 1fr; } }
.rs-param {
    display: block;
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md, 10px);
    padding: 12px 14px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.14);
    transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}
.rs-param:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0,0,0,0.18);
    border-color: var(--accent);
}
.rs-param:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A problem parameter gets a red left edge; a clean one green; unassessed stays neutral. */
.rs-param-problem { border-left: 3px solid var(--error); }
.rs-param-clean   { border-left: 3px solid var(--ok); }
.rs-param-na      { border-left: 3px solid var(--border-strong); opacity: 0.75; }
.rs-param-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}
.rs-param-name { font-size: 13px; font-weight: 650; color: var(--text); }
.rs-param-figure {
    font-size: 15px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
/* The figure is now the healthy count (good / total · pct%), tinted by health tier. */
.rs-param-figure.bar-ok      { color: var(--ok); }
.rs-param-figure.bar-warn    { color: var(--warn); }
.rs-param-figure.bar-error   { color: var(--error); }
.rs-param-figure.bar-neutral { color: var(--text-faint); }
.rs-param-figure.na          { color: var(--text-faint); font-weight: 600; }
.rs-param-bar {
    display: block;
    height: 6px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--text-faint) 16%, transparent);
    overflow: hidden;
    margin-bottom: 9px;
}
.rs-param-fill { display: block; height: 100%; border-radius: 999px; }
.rs-param-fill.bar-ok      { background: var(--ok); }
.rs-param-fill.bar-warn    { background: var(--warn); }
.rs-param-fill.bar-error   { background: var(--error); }
.rs-param-fill.bar-neutral { background: var(--text-faint); }
.rs-param-detail {
    margin: 0 0 6px;
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.4;
}
.rs-param-note { margin: 0; font-size: 11px; color: var(--text-faint); }
.rs-param-health.health-ok    { color: var(--ok); }
.rs-param-health.health-warn  { color: var(--warn); }
.rs-param-health.health-error { color: var(--error); }

/* The readiness summary and its measure grid now reflow via auto-fit against their own
   container width (see .readiness-summary / .rs-measure-grid above), so they adapt to the
   real content area — including the space the sidebar takes — without viewport media
   queries. Only the very narrow case stacks the score ring above the dimensions. */
@media (max-width: 600px) {
    .readiness-summary-score { grid-template-columns: 1fr; }
}

/*
 * (Legacy) "Where the score is going" panel — kept for any view still referencing it.
 */
.verdict-areas {
    align-self: stretch;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 16px;
    border-left: 1px solid var(--border);
    border-right: 1px solid var(--border);
}
.verdict-areas-title {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-faint);
}
.verdict-areas-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(140px, 1fr));
    gap: 6px 16px;
}
.verdict-area-row {
    display: grid;
    grid-template-columns: 84px 1fr 24px;
    align-items: center;
    gap: 8px;
    background: transparent;
    border: 0;
    padding: 2px 0;
    cursor: pointer;
    font-family: inherit;
    text-align: left;
}
.verdict-area-name {
    font-size: 11.5px;
    color: var(--text-dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.verdict-area-track {
    height: 7px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--text-faint) 22%, transparent);
    overflow: hidden;
}
.verdict-area-fill { display: block; height: 100%; border-radius: 4px; }
.verdict-area-fill.bar-ok    { background: var(--ok); }
.verdict-area-fill.bar-warn  { background: var(--warn); }
.verdict-area-fill.bar-error { background: var(--error); }
.verdict-area-figure {
    font-size: 11.5px;
    font-weight: 650;
    color: var(--text-dim);
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.verdict-area-row:hover .verdict-area-name,
.verdict-area-row:hover .verdict-area-figure { color: var(--text); }

/* Below three columns' worth of width, drop to two then one, and unstack the verdict grid. */
@media (max-width: 1200px) {
    .verdict-areas-grid { grid-template-columns: repeat(2, minmax(140px, 1fr)); }
}
@media (max-width: 980px) {
    .verdict { grid-template-columns: auto 1fr auto; }
    .verdict-areas {
        grid-column: 1 / -1;
        border-left: 0; border-right: 0;
        border-top: 1px solid var(--border);
        padding: 12px 0 0;
    }
    .verdict-areas-grid { grid-template-columns: repeat(3, minmax(120px, 1fr)); }
}

/* Score is secondary to the verdict, so it is quieter and set to one side */
.verdict-score {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 18px;
    cursor: pointer;
    font-family: inherit;
    transition: border-color .12s;
}

.verdict-score:hover { border-color: var(--accent); }

/*
 * The readiness-summary score tile gets a soft teal wash from the app's accent, so the
 * headline figure reads as the hero of the summary rather than a plain white box. The tint
 * is gentle enough that the coloured figure (ok/warn/error) still stands out on top.
 */
.rs-score-figure {
    background: linear-gradient(150deg,
                var(--accent-soft) 0%,
                color-mix(in srgb, var(--accent-soft) 55%, var(--bg)) 60%,
                var(--bg) 100%);
    border-color: var(--accent-line);
    box-shadow: var(--shadow-sm);
    padding: 14px 20px;
}
.rs-score-figure .score-caption { color: var(--accent); }
.rs-score-figure:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md, var(--shadow-sm));
}

.score-figure {
    font-size: 30px;
    font-weight: 300;
    line-height: 1.05;
    letter-spacing: -0.5px;
}

.score-figure.ok    { color: var(--ok); }
.score-figure.info  { color: var(--info); }
.score-figure.warn  { color: var(--warn); }
.score-figure.error { color: var(--error); }

.score-of    { font-size: 11px; color: var(--text-faint); }
.score-grade { font-size: 11.5px; color: var(--text-dim); font-weight: 600; }

/* ---- Section blocks ---- */

.home-block { margin-bottom: 26px; }
/* A section whose heading was removed for density; tighter top margin so it sits close
   to the block above without the heading's visual anchor. */
.home-block-tight { margin-bottom: 18px; }

.home-block-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}

.home-block-head h3 {
    margin: 0;
    font-size: 13px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-dim);
}

.home-block-meta { font-size: 12.5px; color: var(--text-faint); }
.ok-text    { color: var(--ok);    font-weight: 650; }
.error-text { color: var(--error); font-weight: 650; }

.link-btn {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: 12.5px;
    color: var(--accent);
    cursor: pointer;
    white-space: nowrap;
}

.link-btn:hover { color: var(--accent-hover); text-decoration: underline; }

/* ---- Finding cards ---- */

.finding-cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(390px, 1fr));
    gap: 12px;
}

.finding-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 13px 15px;
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.finding-card-top {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.finding-subject {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text);
}

.finding-area {
    font-size: 11px;
    color: var(--text-faint);
    margin-left: auto;
    text-transform: uppercase;
    letter-spacing: .04em;
}

.finding-detail {
    margin: 0;
    font-size: 13px;
    color: var(--text);
    line-height: 1.45;
}

/* The consequence, set apart so it is not skimmed past as more description */
.finding-impact {
    margin: 0;
    font-size: 12.5px;
    color: var(--text-dim);
    line-height: 1.5;
    border-left: 2px solid var(--border-strong);
    padding-left: 10px;
}

.finding-card-foot {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
    margin-top: auto;
    padding-top: 4px;
}

.finding-remedy {
    font-size: 12px;
    color: var(--accent);
    line-height: 1.45;
}

/* ---- Ring charts ---- */

.chart-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: 14px;
}

.chart-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 15px 17px;
    box-shadow: var(--shadow-sm);
}

.chart-card-head h4 {
    margin: 0 0 2px;
    font-size: 13.5px;
    font-weight: 650;
}

.chart-card-head p {
    margin: 0 0 14px;
    font-size: 11.5px;
    color: var(--text-faint);
}

.chart-body {
    display: flex;
    align-items: center;
    gap: 20px;
    flex-wrap: wrap;
}

.donut-wrap {
    position: relative;
    width: 132px;
    height: 132px;
    flex: 0 0 auto;
}

.donut { width: 100%; height: 100%; }

.donut-track {
    fill: none;
    stroke: var(--bg-subtle);
    stroke-width: 13;
}

.donut-slice {
    fill: none;
    stroke-width: 13;
    stroke-linecap: butt;
    transition: stroke-width .12s;
}

.donut-slice:hover { stroke-width: 15.5; }

/* Centre figure is the number a reader takes away, so it sits inside the hole */
.donut-centre {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.donut-figure {
    font-size: 21px;
    font-weight: 300;
    letter-spacing: -0.4px;
    line-height: 1.1;
}

.donut-unit { font-size: 10.5px; color: var(--text-faint); }

.chart-legend {
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1 1 170px;
    min-width: 0;
    font-size: 12px;
}

.chart-legend li {
    display: grid;
    grid-template-columns: 9px 1fr auto auto;
    gap: 8px;
    align-items: center;
    padding: 2.5px 0;
}

.legend-swatch {
    width: 9px; height: 9px;
    border-radius: 2px;
    display: inline-block;
}

.legend-label {
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.legend-value   { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.legend-percent {
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
    min-width: 32px;
    text-align: right;
}

/* ---- Workload block grid ---- */

.grid-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin-bottom: 12px;
    font-size: 11.5px;
    color: var(--text-faint);
}

.grid-legend span { display: inline-flex; align-items: center; gap: 5px; }

.swatch {
    width: 10px; height: 10px;
    border-radius: 2px;
    display: inline-block;
    border: 1px solid transparent;
}

.swatch-ok      { background: var(--ok); }
.swatch-warn    { background: var(--warn); }
.swatch-error   { background: var(--error); }
.swatch-neutral { background: var(--border-strong); }
.swatch-restart {
    background: var(--ok);
    border-color: var(--warn);
    box-shadow: inset 0 0 0 1.5px var(--warn);
}

.ns-grid {
    display: grid;
    grid-template-columns: 190px 1fr;
    gap: 12px;
    align-items: start;
    padding: 7px 0;
    border-bottom: 1px solid var(--border);
}

.ns-grid:last-of-type { border-bottom: none; }

.ns-grid-label {
    font-size: 12.5px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    padding-top: 2px;
}

.ns-grid-label em {
    display: block;
    font-style: normal;
    font-size: 11px;
    color: var(--error);
    margin-top: 1px;
}

.ns-grid-blocks {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.wl-block {
    width: 17px;
    height: 17px;
    border-radius: 3px;
    border: 1px solid transparent;
    padding: 0;
    cursor: pointer;
    transition: transform .1s, outline-color .1s;
    outline: 2px solid transparent;
    outline-offset: 1px;
}

.wl-block:hover {
    transform: scale(1.22);
    outline-color: var(--accent);
}

.wl-ok      { background: var(--ok); }
.wl-warn    { background: var(--warn); }
.wl-error   { background: var(--error); }
.wl-neutral { background: var(--border-strong); }

/*
 * Restarting is orthogonal to status: a workload can be fully ready and still be
 * crash-looping. Shown as an inset ring so it overlays any base colour.
 */
.wl-restart { box-shadow: inset 0 0 0 2px var(--warn); }

@media (max-width: 820px) {
    .verdict { grid-template-columns: auto 1fr; }
    .verdict-score { grid-column: 2; justify-self: start; }
    .ns-grid { grid-template-columns: 1fr; }
}

/* ================= Dependency graph (Blast Radius) ================= */

.graph-wrap {
    padding: 10px 14px 4px;
    display: flex;
    justify-content: center;
}

/* Fill the full page width so the oval is large and readable; the wide viewBox keeps a
   landscape aspect. No max-width cap — the diagram is meant to dominate this page. */
.dep-graph {
    width: 100%;
    height: auto;
}
/* The graph card spans the panel edge to edge rather than centring a narrow drawing. */
.graph-wrap { justify-content: stretch; padding: 8px 10px; }

.dep-edge {
    stroke: var(--text-faint);
    stroke-width: 2.4;
    opacity: 0.85;
}

/* Arrowhead: solid and in the text-dim ink so direction reads clearly against the wash. */
.dep-arrowhead {
    fill: var(--text-dim);
    opacity: 1;
}

/* Node fill is set inline per namespace (its own colour); health is shown by the ring. */
.dep-node {
    stroke: var(--bg);
    stroke-width: 3;
}
/* Health ring: a coloured halo around the namespace-coloured dot. */
.dep-node.ring-ok      { stroke: var(--ok);    stroke-width: 3; }
.dep-node.ring-warn    { stroke: var(--warn);  stroke-width: 4; }
.dep-node.ring-error   { stroke: var(--error); stroke-width: 5; }
.dep-node.ring-neutral { stroke: var(--bg);    stroke-width: 3; }

.dep-label {
    font-size: 17px;
    fill: var(--text-dim);
    font-family: var(--mono, monospace);
    paint-order: stroke;
    stroke: var(--bg);
    stroke-width: 3.5px;
    stroke-linejoin: round;
}

/* ================= Page help panel (modal) ================= */

.help-lead {
    font-size: 14.5px;
    font-weight: 600;
    line-height: 1.5;
    color: var(--text);
    margin: 0 0 12px;
}

.help-text {
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--text-dim);
    margin: 0 0 12px;
}
.help-text:last-child { margin-bottom: 0; }

/* Inline risk badge next to an image tag (e.g. :latest). Compact so it sits on the row. */
.tag-risk {
    margin-left: 6px;
    font-size: 10px;
    vertical-align: middle;
}

/* Extra separation before the probe-risk table so it doesn't crowd the note above it. */
.probe-risk-table { margin-top: 26px; }

/* Sub-section heading within a merged page (e.g. Right-sizing inside Waste Sweep). */
.section-heading {
    margin: 30px 0 12px;
    font-size: 15px;
    font-weight: 650;
    color: var(--text);
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
}

/* ===== On Fire Now (incident triage) ===================================== */

/* Situation card: the severity donut + legend on the left, scope verdict on the right.
   Reuses .chart-card so it matches the Overview's resource-distribution panels exactly. */
.fire-situation {
    display: grid;
    grid-template-columns: minmax(260px, 340px) 1fr;
    gap: 24px;
    align-items: center;
}
.fire-situation-chart {
    display: flex;
    align-items: center;
    gap: 22px;
}
.fire-legend { align-self: center; }
.fire-legend li { grid-template-columns: 9px 1fr auto; }

/* Scope verdict: a tinted panel that reads as the headline takeaway. */
.fire-situation-scope {
    border-left: 3px solid var(--border-strong);
    padding: 4px 0 4px 16px;
}
.fire-situation-scope.scope-error { border-left-color: var(--error); }
.fire-situation-scope.scope-warn  { border-left-color: var(--warn); }
.fire-situation-scope.scope-ok    { border-left-color: var(--ok); }
.fire-scope-text {
    margin: 2px 0 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--text);
    line-height: 1.45;
}

@media (max-width: 760px) {
    .fire-situation { grid-template-columns: 1fr; gap: 16px; }
    .fire-situation-scope { border-left: none; border-top: 3px solid var(--border-strong); padding: 12px 0 0; }
}

/* Incident cards, stacked worst-first — matched to the Overview finding cards. */
.incident-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 18px;
}
.incident {
    position: relative;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: 14px 16px 14px 18px;
    background: var(--bg);
    display: flex;
    flex-direction: column;
    gap: 9px;
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
/* A slim severity rail on the left edge, drawn with a border so it hugs the radius. */
.incident-error { border-left: 3px solid var(--error); }
.incident-warn  { border-left: 3px solid var(--warn); }
.incident:hover { box-shadow: var(--shadow); border-color: var(--border-strong); }

.incident-head {
    display: flex;
    align-items: center;
    gap: 9px;
}
.incident-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: 0 0 auto;
}
.incident-dot.dot-error { background: var(--error); }
.incident-dot.dot-warn  { background: var(--warn); }
.incident-title {
    font-weight: 650;
    font-size: 14px;
    margin: 0;
    flex: 1 1 auto;
    color: var(--text);
}
/* A quiet, tinted badge rather than a loud pill. */
.incident-badge {
    font-size: 10.5px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 2px 8px;
    border-radius: 999px;
}
.incident-badge.badge-error { color: var(--error); background: var(--error-soft); }
.incident-badge.badge-warn  { color: var(--warn);  background: var(--warn-soft); }

.incident-cause {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-dim);
}

/* Small uppercase eyebrow label above the evidence and action blocks. */
.incident-eyebrow {
    display: block;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 650;
    color: var(--text-faint);
    margin-bottom: 5px;
}

/* Evidence: a light left-rule, not a heavy filled slab. */
.incident-evidence {
    border-left: 2px solid var(--border-strong);
    padding-left: 11px;
}
.incident-evidence ul {
    margin: 0;
    padding-left: 15px;
}
.incident-evidence li {
    font-family: var(--mono, monospace);
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.65;
}
.incident-foot {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    margin-top: 1px;
    padding-top: 11px;
    border-top: 1px solid var(--border);
}
.incident-action {
    flex: 1 1 auto;
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
}
.incident-action > span:last-child { color: var(--text); }

/* The call-to-action: subtle outline button, filling with accent on hover. */
.incident-cta {
    flex: 0 0 auto;
    background: var(--accent-soft);
    color: var(--accent);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 7px 13px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.14s ease, color 0.14s ease;
}
.incident-cta:hover { background: var(--accent); color: #fff; }

@media (max-width: 720px) {
    .incident-foot { flex-direction: column; align-items: stretch; }
}

/* ===== Insights at-a-glance strip (Overview) ============================= */

.insight-strip {
    display: grid;
    /* Exactly 10 chips per row, matching the pre-workload-tile layout. A fixed 10-column
       grid is used instead of auto-fill/minmax because auto-fill picks the column count from
       the min width and would fit an 11th narrow chip on a wide panel; pinning to 10 keeps
       the row count stable and the tiles at their original width. Narrows to fewer columns
       on smaller screens via the media queries below. */
    grid-template-columns: repeat(10, 1fr);
    gap: 12px;
}

/* Both the top ring-tile row and the chip strip step down together on smaller screens, so a
   fixed 10-wide grid never squeezes tiles unreadably narrow on a laptop or tablet. */
@media (max-width: 1500px) {
    .tile-grid, .insight-strip { grid-template-columns: repeat(8, 1fr); }
}
@media (max-width: 1200px) {
    .tile-grid, .insight-strip { grid-template-columns: repeat(6, 1fr); }
}
@media (max-width: 900px) {
    .tile-grid, .insight-strip { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 600px) {
    .tile-grid, .insight-strip { grid-template-columns: repeat(2, 1fr); }
}

/* Each chip: a big count, a label, and a short note; a coloured top rule encodes
   severity so a healthy cluster reads as a calm row and problems light up. */
.insight-chip {
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--border);
    border-top: 3px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: 12px 14px;
    font: inherit;
    color: inherit;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 2px;
    transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.insight-chip:hover {
    box-shadow: var(--shadow);
    transform: translateY(-2px);
}
.insight-chip:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.insight-count {
    font-size: 26px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
}
.insight-label {
    font-size: 12px;
    font-weight: 650;
    color: var(--text);
    margin-top: 5px;
}
.insight-note { font-size: 11px; color: var(--text-faint); }

/* Severity: the top rule and the count colour. ok stays neutral-green and quiet. */
.insight-chip.chip-ok    { border-top-color: color-mix(in srgb, var(--ok) 55%, var(--border)); }
.insight-chip.chip-ok    .insight-count { color: var(--ok); }
.insight-chip.chip-warn  { border-top-color: var(--warn); }
.insight-chip.chip-warn  .insight-count { color: var(--warn); }
.insight-chip.chip-error { border-top-color: var(--error); }
.insight-chip.chip-error .insight-count { color: var(--error); }
.insight-chip.chip-na    .insight-count { color: var(--text-faint); }


/* ============================================================================
 * 3D depth
 *
 * A raised, tactile look for the primary surfaces — nav items, Overview tiles and
 * chips, and the readiness summary. Depth comes from each theme's own --lift-*
 * tokens (a coloured top highlight and a tinted surface gradient) plus the layered
 * shadow tokens, so the effect is always in the theme's colour family and never a
 * flat grey. Hover lifts; active presses in.
 * ==========================================================================*/

/* ---- Left navigation items ---- */
.nav-item {
    border: 1px solid transparent;
    transition: background .12s, color .12s, box-shadow .12s, transform .08s, border-color .12s;
}
.nav-item:hover {
    background: linear-gradient(180deg, var(--lift-hover-a) 0%, var(--lift-hover-b) 100%);
    border-color: var(--accent-line);
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
    transform: translateY(-1px);
}
.nav-item.selected {
    background: linear-gradient(180deg, var(--lift-surface-a) 0%, var(--lift-surface-b) 100%);
    border-color: var(--accent-line);
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
    color: var(--text);
    font-weight: 600;
}
.nav-item:active { transform: translateY(0); }

.item-icon { box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top); }
.nav-item.selected .item-icon { box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top); }

/* ---- Overview tiles ---- */
.tile-card,
.count-card {
    background: linear-gradient(180deg, var(--lift-surface-a) 0%, var(--lift-surface-b) 100%);
    border-color: var(--border);
    box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top);
}
.tile-card:hover,
.count-card:hover {
    background: linear-gradient(180deg, var(--lift-hover-a) 0%, var(--lift-hover-b) 100%);
    border-color: var(--accent);
    box-shadow: var(--shadow-md), inset 0 1px 0 var(--lift-top);
    transform: translateY(-3px);
}
.tile-card:active,
.count-card:active { transform: translateY(-1px); box-shadow: var(--shadow-sm); }

/* ---- Overview insight chips ---- */
.insight-chip {
    background: linear-gradient(180deg, var(--lift-surface-a) 0%, var(--lift-surface-b) 100%);
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
    transition: background .12s, border-color .12s, box-shadow .12s, transform .08s;
}
.insight-chip:hover {
    background: linear-gradient(180deg, var(--lift-hover-a) 0%, var(--lift-hover-b) 100%);
    border-color: var(--accent);
    box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top);
    transform: translateY(-2px);
}
.insight-chip:active { transform: translateY(0); box-shadow: var(--shadow-sm); }

/* ---- Readiness summary tiles ---- */
.readiness-summary-score {
    background: linear-gradient(180deg, var(--lift-surface-a) 0%, var(--lift-surface-b) 100%);
    box-shadow: var(--shadow-md), inset 0 1px 0 var(--lift-top);
}
/* The verdict tile keeps its status tint (error/warn/ok soft), so only lift its
   shadow and border, not its background. */
.readiness-summary-verdict {
    box-shadow: var(--shadow-md), inset 0 1px 0 var(--lift-top);
}

/* ===== Latency page ===================================================== */

/* The p99 figure, tinted by how slow it is, so the eye lands on the tail. */
.lat-p99 {
    font-variant-numeric: tabular-nums;
    font-weight: 650;
    padding: 1px 6px;
    border-radius: 4px;
}
.lat-p99.lat-ok    { color: var(--ok); }
.lat-p99.lat-warn  { color: var(--warn); background: color-mix(in srgb, var(--warn) 12%, transparent); }
.lat-p99.lat-error { color: var(--error); background: color-mix(in srgb, var(--error) 14%, transparent); }

/* The inline diagnosis under a slow service: root cause, evidence and specific fixes. */
.lat-diag-row td { background: color-mix(in srgb, var(--text-faint) 5%, transparent); }
.lat-diag { padding: 4px 2px 8px; }
.lat-diag-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.lat-conf { font-size: 11px; color: var(--text-faint); }
.lat-diag-summary { margin: 2px 0 8px; color: var(--text-dim); font-size: 12.5px; }
.lat-diag-cols {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) minmax(260px, 1.4fr);
    gap: 18px;
}
.lat-diag-label {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-faint);
    margin-bottom: 4px;
}
.lat-diag-evidence ul,
.lat-diag-fixes ul { margin: 0; padding: 0; list-style: none; }
.lat-diag-evidence li {
    font-size: 12px;
    color: var(--text-dim);
    padding: 2px 0 2px 14px;
    position: relative;
}
.lat-diag-evidence li::before {
    content: '·';
    position: absolute;
    left: 4px;
    color: var(--text-faint);
}
.lat-diag-fixes li {
    font-size: 12px;
    padding: 4px 0;
    border-top: 1px solid var(--border);
}
.lat-diag-fixes li:first-child { border-top: 0; }
.fix-kind {
    display: inline-block;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--accent);
    background: var(--accent-soft);
    border-radius: 3px;
    padding: 1px 5px;
    margin-right: 6px;
    vertical-align: middle;
}
.fix-action { font-weight: 600; color: var(--text); }
.fix-detail { display: block; color: var(--text-dim); font-size: 11.5px; margin: 2px 0 0 0; }

/* ===== Shared diagnosis card (Workload Doctor, On Fire, Latency) ========= */

.diag-card {
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    padding: 12px 14px;
    margin-bottom: 10px;
}
.diag-card.diag-error   { border-left-color: var(--error); }
.diag-card.diag-warn    { border-left-color: var(--warn); }
.diag-card.diag-info    { border-left-color: var(--info, var(--accent)); }
.diag-card.diag-neutral { border-left-color: var(--border-strong); }

.diag-card-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}
.diag-subject { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.diag-subject-kind {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-faint);
}
.diag-subject-name {
    font-weight: 650;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Subject copy icon on a diagnosis card: a quiet, inline affordance that brightens on hover.
   self-align center keeps the icon vertically centred against the baseline-aligned name. */
.diag-copy {
    display: inline-flex;
    align-items: center;
    align-self: center;
    padding: 2px;
    border: 0;
    background: transparent;
    color: var(--text-faint);
    cursor: pointer;
    border-radius: 4px;
    line-height: 0;
    flex: 0 0 auto;
}
.diag-copy:hover { color: var(--accent); background: var(--accent-soft); }
.diag-copy.flash-ok { color: var(--ok); }
.diag-copy.flash-error { color: var(--error); }

.diag-head { display: flex; align-items: center; gap: 10px; margin-bottom: 3px; }
.diag-title { font-weight: 650; color: var(--text); font-size: 13px; }
.diag-conf { font-size: 11px; color: var(--text-faint); }
.diag-summary { margin: 2px 0 8px; color: var(--text-dim); font-size: 12.5px; }

/* ===== On Fire: compact single-column incident list =====
   On Fire renders the shared diagnosis card inside .fire-compact. Each card collapses to one
   scannable row — the title line (title + confidence) plus the subject/pill/Hide line — and
   the heavy detail (summary, evidence, fixes, open-link) is hidden until the row is expanded
   by its chevron. The shared card is untouched elsewhere (Workload Doctor, Latency). */
.fire-compact { padding: 0; display: flex; flex-direction: column; gap: 6px; }
.fire-compact .diag-card { margin-bottom: 0; padding: 9px 12px; cursor: pointer; }
.fire-compact .diag-card:hover { box-shadow: var(--shadow); }

/* Theme-colour treatment (On Fire): the incident rows use the single theme accent for the
   full border AND the always-on left rail, regardless of severity — "critical" is already
   the label, so a second colour is not needed. Overrides the per-severity border/rail. */
.fire-compact .diag-card,
.fire-compact .diag-card.diag-error,
.fire-compact .diag-card.diag-warn,
.fire-compact .diag-card.diag-info,
.fire-compact .diag-card.diag-neutral {
    /* Reserve room on the left for the leading marker dot (see the ::before rule below),
       so the dot sits in its own gutter with clear breathing room before the pill chip,
       matching the Change Timeline's 12px gap after its marker column. */
    position: relative;
    padding-left: 38px;
    border: 1px solid var(--accent);
    border-left: 3px solid var(--accent);
}
.fire-compact .diag-card:hover { border-color: var(--accent); }

/* Leading marker dot, mirroring the Change Timeline marker (an 11px circle with a soft ring
   in the entry's severity colour). It sits in the left gutter reserved by the padding above,
   aligned to the first row. Keyed to the card's severity modifier so the dot reads in exactly
   the same colour as the severity pill beside it (error red, warn amber, info the info blue,
   neutral the dim grey) — the same "circle matches the label" principle as the timeline.
   Scoped to .fire-compact so the shared card elsewhere is untouched. */
.fire-compact .diag-card::before {
    content: "";
    position: absolute;
    left: 11px;
    top: 15px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--text-dim);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-dim) 22%, transparent);
}
.fire-compact .diag-card.diag-error::before {
    background: var(--error);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 22%, transparent);
}
.fire-compact .diag-card.diag-warn::before {
    background: var(--warn);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent);
}
.fire-compact .diag-card.diag-info::before {
    background: var(--info);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--info) 22%, transparent);
}

/* A connecting thread down the marker column, so the incident rows read as one list like the
   Change Timeline. A 1px line from just below the dot through the inter-card gap to the next
   row's dot, drawn only when another card follows (:has) so the last row has no dangling line.
   100% tracks each card's own height, so it still reaches the next dot when a row is expanded. */
.fire-compact .diag-card:has(~ .diag-card)::after {
    content: "";
    position: absolute;
    left: 16px;
    top: 29px;
    width: 1px;
    height: calc(100% - 29px + 6px);
    background: var(--border);
}

/* The severity label chip keeps its original colour (red = critical, amber = high) — the
   border and left rail are theme-accent, but the label stays a meaningful severity signal. */

/* The collapsed header: subject row first (pill chip-first + subject + Hide + chevron),
   then the title line directly under it — both compact, no extra margins. */
.fire-compact .diag-card-top { margin-bottom: 4px; gap: 8px; }
.fire-compact .diag-card-top .pill { order: -1; flex: 0 0 auto; }   /* chip leads, like the reference */
.fire-compact .diag-head { margin-bottom: 0; gap: 8px; }
.fire-compact .diag-title { font-size: 13.5px; }
.fire-compact .diag-subject { font-size: 11.5px; }

/* The expand chevron, injected by vitals.js, sits at the far right after Hide. */
.fire-compact .diag-chevron {
    flex: 0 0 auto; margin-left: 2px; width: 22px; height: 22px;
    display: inline-grid; place-items: center;
    border: 0; background: none; cursor: pointer;
    color: var(--text-faint); font-size: 15px; line-height: 1;
    transition: transform 0.15s ease, color 0.12s ease;
}
.fire-compact .diag-card:hover .diag-chevron { color: var(--text); }
.fire-compact .diag-card.is-expanded .diag-chevron { transform: rotate(180deg); }

/* Detail hidden when collapsed; revealed (with a hairline divider) when expanded. */
.fire-compact .diag-summary,
.fire-compact .diag-cols,
.fire-compact .diag-inspect { display: none; }
.fire-compact .diag-card.is-expanded .diag-summary { display: block; }
.fire-compact .diag-card.is-expanded .diag-cols { display: grid; }
.fire-compact .diag-card.is-expanded .diag-inspect { display: block; }
.fire-compact .diag-card.is-expanded .diag-body {
    margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border);
}

.diag-cols {
    display: grid;
    grid-template-columns: minmax(200px, 1fr) minmax(260px, 1.4fr);
    gap: 18px;
}
@media (max-width: 720px) {
    .diag-cols { grid-template-columns: 1fr; gap: 10px; }
}
.diag-label {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-faint);
    margin-bottom: 4px;
}
.diag-evidence ul, .diag-fixes ul { margin: 0; padding: 0; list-style: none; }
.diag-evidence li {
    font-size: 12px;
    color: var(--text-dim);
    padding: 2px 0 2px 14px;
    position: relative;
}
.diag-evidence li::before {
    content: '·';
    position: absolute;
    left: 4px;
    color: var(--text-faint);
}
.diag-fixes li {
    font-size: 12px;
    padding: 4px 0;
    border-top: 1px solid var(--border);
}
.diag-fixes li:first-child { border-top: 0; }
.diag-inspect { margin-top: 8px; }

/* ===== On Fire "Situations by cause" multi-column grid ================== */

.situation-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 6px 10px;
    padding: 4px 2px;
}
.situation-cell {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    cursor: pointer;
    font: inherit;
    text-align: left;
    transition: border-color 0.12s ease, background 0.12s ease;
}
.situation-cell:hover { border-color: var(--accent); background: var(--accent-soft); }
.situation-dot {
    width: 8px; height: 8px; border-radius: 50%;
    flex: 0 0 auto;
    background: var(--border-strong);
}
.situation-dot.dot-error { background: var(--error); }
.situation-dot.dot-warn  { background: var(--warn); }
.situation-dot.dot-info  { background: var(--info, var(--accent)); }
.situation-dot.dot-neutral { background: var(--text-faint); }
.situation-name {
    flex: 1 1 auto;
    font-size: 12px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.situation-count {
    flex: 0 0 auto;
    font-size: 12px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-dim);
    min-width: 18px;
    text-align: right;
}

/* ===== On Fire header band (donut · 2×2 counts · scope) ================= */

/* The consolidated header tile: scope strip on top, then the donut + 2×2 + situations row. */
.fire-header {
    margin-bottom: 16px;
    padding: 14px 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}

/* Scope: a thin, tinted one-line strip across the top of the tile. */
.fire-scope-strip {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 6px 10px;
    margin-bottom: 14px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--text-faint) 5%, transparent);
}
.fire-scope-strip.scope-error { border-left-color: var(--error); }
.fire-scope-strip.scope-warn  { border-left-color: var(--warn); }
.fire-scope-strip.scope-ok    { border-left-color: var(--ok); }
.fire-scope-strip .fire-scope-text {
    font-size: 13px;
    color: var(--text-dim);
    line-height: 1.4;
}

/* The header row: donut on the left, then the counts+scope group filling the rest. Centered
   vertically so the group sits mid-height in the tile; the scope still matches the count-tile
   height because .fire-header-main stretches its own children (not the donut). */
.fire-header-row {
    display: flex;
    align-items: center;
    gap: 18px;
}
/* Counts + scope, side by side, stretched to each other's height. */
.fire-header-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: stretch;
    gap: 8px;
}
/* Scope takes the remaining width beside the counts, and stretches to the counts' height. */
.fire-scope-inline {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--text-faint) 5%, transparent);
}
.fire-scope-inline.scope-error { border-left-color: var(--error); }
.fire-scope-inline.scope-warn  { border-left-color: var(--warn); }
.fire-scope-inline.scope-ok    { border-left-color: var(--ok); }

/* Header-right group on the On Fire page: the "Unhide all (N)" button sits at the far right
   of the "On Fire Now" title line, before the critical/high meta. */
.fire-head-right { display: flex; align-items: center; gap: 12px; }
.fire-head-right .fire-scope-actions { margin-left: 0; }

/* Standalone scope banner above the severity/cause tiles — a tinted one-line verdict with
   the unhide-button slot pushed to the right. */
.fire-scope {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--text-faint) 5%, transparent);
}
.fire-scope.scope-error { border-left-color: var(--error); background: color-mix(in srgb, var(--error) 7%, transparent); }
.fire-scope.scope-warn  { border-left-color: var(--warn);  background: color-mix(in srgb, var(--warn) 8%, transparent); }
.fire-scope.scope-ok    { border-left-color: var(--ok);    background: color-mix(in srgb, var(--ok) 8%, transparent); }
.fire-scope .fire-scope-text { font-size: 13px; color: var(--text); font-weight: 600; }
.fire-scope .incident-eyebrow { flex: 0 0 auto; }
/* The cause-chip grid sits right under the severity tiles with a touch less top gap. */
.fire-cause-tiles { margin-top: -4px; }
.fire-scope-inline .fire-scope-text {
    font-size: 13px;
    color: var(--text-dim);
    line-height: 1.4;
}

/* Compact donut for the header. */
.donut-wrap.donut-sm { width: 84px; height: 84px; }
.donut-wrap.donut-sm .donut-figure { font-size: 20px; }
.donut-wrap.donut-sm .donut-unit { font-size: 9.5px; }

/* The four counts in a single 1×4 row, each a bordered tile. */
.fire-header-stats {
    display: grid;
    grid-template-columns: repeat(4, minmax(84px, auto));
    gap: 8px;
}
.fire-stat {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg);
}
.fire-stat-value {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.02em;
}
.fire-stat-value.ok    { color: var(--ok); }
.fire-stat-value.error { color: var(--error); }
.fire-stat-value.warn  { color: var(--warn); }
.fire-stat-label {
    font-size: 10.5px;
    color: var(--text-faint);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Situations by cause: a separate full-width tile below the header band. An even grid that
   fills the width — ~6 across on a wide screen (12 causes → 2 rows), fewer as it narrows,
   wrapping cleanly. Cells share the row evenly (1fr) so they align, and the label ellipsises
   rather than pushing the count off. */
.fire-situations-tile {
    margin-bottom: 16px;
    padding: 10px 12px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
.fire-situations-tile .situation-grid {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    padding: 0;
}

/* Let the one-line header wrap gracefully when the content area is too narrow for donut +
   1×4 + scope: the scope drops below, then the counts wrap to 2×2 on very narrow widths. */
@media (max-width: 900px) {
    .fire-header-row { flex-wrap: wrap; }
    .fire-scope-inline { flex-basis: 100%; }
}
@media (max-width: 560px) {
    .fire-header-stats { grid-template-columns: repeat(2, minmax(84px, 1fr)); }
}

/* ================= Drain Simulation ================= */
/*
  Each node is its own card so the per-node assessments read as separate blocks rather
  than one undifferentiated list. A severity-tinted left edge lets the eye find the risky
  nodes at a glance, and the at-risk workloads sit in a responsive grid so they use the
  full width instead of stacking in a narrow column with dead space to the right.
*/
/* ============================================================================
 * Drain Simulation — master–detail triage console
 *
 * A verdict banner, then a two-column console: a ranked node QUEUE on the left (one row
 * per node, severity rail + identity + count-chips) and a DETAIL panel on the right that
 * shows the selected node only (headroom gauge + grouped reasons). Overview → select →
 * detail, so the page scales from 3 nodes to 300 and triage happens in the list.
 * ==========================================================================*/

.drain-dot { color: var(--text-faint); }

/* ---- Verdict banner ---- */
.drain-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 20px;
    padding: 14px 16px;
    margin-bottom: 14px;
    border: 1px solid var(--border);
    border-left: 4px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}
.drain-banner-risk { border-left-color: var(--error); }
.drain-banner-ok   { border-left-color: var(--ok, var(--accent)); }

.drain-banner-head { display: block; font-size: 15px; font-weight: 700; color: var(--text); }
.drain-banner-sub  { display: block; margin-top: 2px; font-size: 12.5px; color: var(--text-dim); }

.drain-banner-figs { display: flex; flex-wrap: wrap; gap: 16px; }
.drain-fig { font-size: 12px; color: var(--text-dim); white-space: nowrap; }
.drain-fig b { font-size: 17px; font-weight: 700; color: var(--text); margin-right: 4px; }
.drain-fig b.error { color: var(--error); }
.drain-fig b.ok    { color: var(--ok, var(--accent)); }

/* ---- Two-column console ---- */
.drain-console {
    display: grid;
    grid-template-columns: minmax(260px, 340px) 1fr;
    gap: 14px;
    margin-bottom: 18px;
    align-items: start;
}

/* ---- Left: triage queue ---- */
.drain-queue {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 70vh;
    overflow-y: auto;
    padding-right: 2px;
}

.drain-q-row {
    display: flex;
    gap: 0;
    width: 100%;
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0;
    cursor: pointer;
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    transition: border-color .12s, box-shadow .12s, transform .08s;
}
.drain-q-row:hover { transform: translateY(-1px); box-shadow: var(--shadow); }
.drain-q-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The severity rail: a solid colour bar down the left of each row, so the queue reads as a
   heatmap of risk at a glance. */
.drain-q-rail { flex: 0 0 5px; background: var(--border-strong); }
.drain-q-error .drain-q-rail { background: var(--error); }
.drain-q-warn  .drain-q-rail { background: var(--warn); }
.drain-q-ok    .drain-q-rail { background: var(--ok, var(--accent)); }

/* Selected row: lifted, accented, so it clearly pairs with the detail panel. */
.drain-q-row.is-active {
    border-color: var(--accent);
    box-shadow: var(--shadow), 0 0 0 1px var(--accent) inset;
}

.drain-q-body { flex: 1 1 auto; min-width: 0; padding: 9px 11px; }

.drain-q-top { display: flex; align-items: center; gap: 6px; min-width: 0; }
.drain-q-node {
    font-size: 12.5px; font-weight: 600; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.drain-q-meta {
    display: flex; gap: 6px; margin-top: 2px;
    font-size: 11.5px; color: var(--text-dim); white-space: nowrap;
}
.drain-q-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }

/* Compact count-chips so triage happens in the queue before you click. */
.drain-chip {
    font-size: 10.5px; font-weight: 600; line-height: 1;
    padding: 3px 6px; border-radius: 999px;
    border: 1px solid var(--border);
    color: var(--text-dim); background: var(--bg);
    white-space: nowrap;
}
.drain-chip-risk { color: var(--error); border-color: color-mix(in srgb, var(--error) 36%, var(--border)); background: color-mix(in srgb, var(--error) 8%, var(--bg)); }
.drain-chip-warn { color: var(--warn);  border-color: color-mix(in srgb, var(--warn) 36%, var(--border));  background: color-mix(in srgb, var(--warn) 8%, var(--bg)); }
.drain-chip-ok   { color: var(--ok, var(--accent)); border-color: color-mix(in srgb, var(--ok, var(--accent)) 36%, var(--border)); background: color-mix(in srgb, var(--ok, var(--accent)) 8%, var(--bg)); }

/* ---- Right: detail panel ---- */
.drain-detail { min-width: 0; }
.drain-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: 16px 18px 18px;
}
.drain-panel.drain-error { border-left-color: var(--error); }
.drain-panel.drain-warn  { border-left-color: var(--warn); }
.drain-panel.drain-ok    { border-left-color: var(--ok, var(--accent)); }
.drain-panel.is-hidden { display: none; }

.drain-panel-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 12px; }
.drain-panel-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.drain-panel-node { font-size: 14px; font-weight: 700; color: var(--text); }
.drain-panel-meta { font-size: 12px; color: var(--text-dim); display: flex; gap: 7px; white-space: nowrap; }

/* ---- Headroom gauge ---- */
.drain-gauge { margin-top: 14px; }
.drain-gauge-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 5px; }
.drain-gauge-label { font-size: 12.5px; font-weight: 600; }
.drain-gauge.is-ok  .drain-gauge-label { color: var(--ok, var(--accent)); }
.drain-gauge.is-bad .drain-gauge-label { color: var(--error); }
.drain-gauge-note { font-size: 11.5px; color: var(--text-dim); text-align: right; }
.drain-gauge-track {
    height: 8px; border-radius: 999px; overflow: hidden;
    background: color-mix(in srgb, var(--text-faint) 30%, var(--bg));
}
.drain-gauge-fill { display: block; height: 100%; border-radius: 999px; }
.drain-gauge.is-ok  .drain-gauge-fill { width: 62%;  background: var(--ok, var(--accent)); }
.drain-gauge.is-bad .drain-gauge-fill { width: 100%; background: var(--error); }

.drain-panel-foot { margin-top: 16px; }

/* On narrow content areas stack the console: queue above, detail below. */
@media (max-width: 860px) {
    .drain-console { grid-template-columns: 1fr; }
    .drain-queue { max-height: none; flex-direction: row; flex-wrap: wrap; }
    .drain-q-row { flex: 1 1 220px; }
}

/* ---- Affected workloads, grouped by reason ----
   Each group states its reason once (a short chip + the shared sentence) and lists the
   affected workloads as compact chips beneath — instead of repeating the same sentence on
   every workload, which was the old eyesore. Groups stack vertically so each reads as a
   distinct block; the subject chips wrap in a tidy row. */
.drain-reasons {
    margin-top: 10px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.drain-reason {
    padding: 9px 11px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--radius-sm);
    min-width: 0;
}

/* A hairline severity edge per group, kept subtle so the stack stays calm. */
.drain-reason.drain-reason-error { border-left-color: var(--error); }
.drain-reason.drain-reason-warn  { border-left-color: var(--warn); }

/* The reason heading: the short chip and the one-line explanation, stated once. */
.drain-reason-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    min-width: 0;
}
.drain-reason-head .pill { flex-shrink: 0; }

.drain-reason-detail {
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.45;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* The affected workloads as a wrapped row of neutral chips under the reason. */
.drain-reason-subjects {
    margin-top: 7px;
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}
.drain-subject {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.drain-clear {
    margin: 10px 0 0;
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 12.5px;
    color: var(--text-dim);
    line-height: 1.5;
}
.drain-clear .pill { flex-shrink: 0; }

/* ============================================================================
 * 3D depth — extended to every button and tile
 *
 * Continues the raised, tactile treatment from the section above onto the rest of
 * the interactive surfaces: general buttons, the stat tiles used across module pages,
 * the On Fire severity chips, and the Drain Simulation cards. Everything consumes the
 * same theme-aware --lift-* tokens (lit top edge + tinted surface gradient) and the
 * layered shadow tokens, so the depth is always in the theme's colour family, never a
 * flat grey. Hover lifts; active presses in. Solid/gradient-filled buttons (primary,
 * gradient) keep their fill and gain depth from shadow + a lit inset edge only.
 * ==========================================================================*/

/* ---- Icon buttons (topbar: refresh, copy, export, theme) ---- */
.btn-icon {
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
    transition: color .12s, background .12s, border-color .12s, box-shadow .12s, transform .08s;
}
.btn-icon:hover {
    box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top);
    transform: translateY(-1px);
}
.btn-icon:active { transform: translateY(0); box-shadow: var(--shadow-sm); }

/* ---- Small utility buttons ---- */
.btn-sm {
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
    transition: color .12s, background .12s, border-color .12s, box-shadow .12s, transform .08s;
}
.btn-sm:hover  { box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top); transform: translateY(-1px); }
.btn-sm:active { transform: translateY(0); box-shadow: var(--shadow-sm); }

/* ---- Filled action buttons: depth from shadow + a lit top edge, fill unchanged ---- */
.btn-primary,
.btn-gradient {
    box-shadow: var(--shadow), inset 0 1px 0 color-mix(in srgb, #ffffff 30%, transparent);
}
.btn-primary:hover,
.btn-gradient:hover {
    box-shadow: var(--shadow-md), inset 0 1px 0 color-mix(in srgb, #ffffff 34%, transparent);
    transform: translateY(-1px);
}
.btn-primary:active,
.btn-gradient:active { transform: translateY(1px); box-shadow: var(--shadow-sm); }

/* ---- Outline button (SSO) ---- */
.btn-outline {
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
    transition: color .12s, background .12s, border-color .12s, box-shadow .12s, transform .08s;
}
.btn-outline:hover:not(:disabled) { box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top); transform: translateY(-1px); }
.btn-outline:active:not(:disabled) { transform: translateY(0); box-shadow: var(--shadow-sm); }

/* ---- Stat tiles across every module page (rendered as <a>/<button>) ---- */
/* These already carry a resting shadow; give them the full lift gradient + lit edge so
   they match the Overview count cards everywhere, not just on the landing page. */
a.stat, button.stat {
    background: linear-gradient(180deg, var(--lift-surface-a) 0%, var(--lift-surface-b) 100%);
    box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top);
}
a.stat:hover, button.stat:hover,
a.stat:focus-visible, button.stat:focus-visible {
    background: linear-gradient(180deg, var(--lift-hover-a) 0%, var(--lift-hover-b) 100%);
    box-shadow: var(--shadow-md), inset 0 1px 0 var(--lift-top);
    transform: translateY(-3px);
}
a.stat:active, button.stat:active { transform: translateY(-1px); box-shadow: var(--shadow-sm); }

/* ---- Drillable table cells ---- */
.cell-btn:hover { box-shadow: inset 0 1px 0 var(--lift-top); }

/* ---- On Fire: situation chips and count tiles ---- */
.situation-cell {
    background: linear-gradient(180deg, var(--lift-surface-a) 0%, var(--lift-surface-b) 100%);
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
    transition: border-color .12s ease, background .12s ease, box-shadow .12s ease, transform .08s ease;
}
.situation-cell:hover {
    background: linear-gradient(180deg, var(--lift-hover-a) 0%, var(--lift-hover-b) 100%);
    box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top);
    transform: translateY(-2px);
}
.situation-cell:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
/* Selected cause: the chip stays lit while its cards are the only ones shown, so it is
   obvious which filter is active and how to clear it (click it again). */
.situation-cell.is-active {
    border-color: var(--accent);
    background: var(--accent-soft);
    box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top);
}
.situation-cell.is-active .situation-name { color: var(--text); font-weight: 600; }

.fire-stat {
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--lift-top);
}

/* ---- Drain Simulation cards ---- */
.drain-card {
    box-shadow: var(--shadow), inset 0 1px 0 var(--lift-top);
}

/* ===== PDB status tiles (clickable quick filters) ===================== */
/* The tiles reuse .stat for the card look; these make them read as buttons and mark the
   active filter. Clicking a tile sets the table's status filter (see wirePdbTiles). */
.pdb-tile {
    cursor: pointer;
    font-family: inherit;
    text-align: left;
    border: 1px solid var(--border);
}
.pdb-tile:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md), 0 0 0 1px var(--accent);
    transform: translateY(-2px);
}
.pdb-tile.is-active {
    border-color: var(--accent);
    background: var(--accent-soft);
    box-shadow: var(--shadow), 0 0 0 1px var(--accent);
}

/*
  Compact status-filter tiles. These sit above every table and were consuming a full card's
  worth of height; halved here so they read as a slim filter strip, not a KPI band. The tile
  drops to a single dense line: label + big value inline, with the descriptive sub-line
  hidden (its text lives in the tooltip/context). Scoped to .pdb-tiles so the Overview
  landing KPI cards are untouched.
*/
.stat-grid.pdb-tiles { gap: 10px; margin-bottom: 16px; }
/* Page tiles: a compact, modern KPI card with a leading icon chip.
   Layout is a horizontal row — a soft-tinted rounded icon square on the left (injected via
   ::before so no per-page markup is needed), and a tight text block on the right whose
   value and sub-line share ONE line. This trims roughly 30% off the stacked version's
   height while restoring the icon that made the old tiles read as polished KPI cards. */
.pdb-tiles .stat {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 8px 13px;
}
/* Leading brand chip — the VITALS mark: the theme's brand gradient square with the white
   pulse glyph on top, so every page tile carries the application logo. The gradient follows
   the active theme via --brand-gradient (sunset on light/dark, teal→blue on graphite,
   indigo→cyan on navy), matching the sidebar logo. Injected via ::before so no per-page
   markup is needed. The white pulse is a data-URI background layered above the gradient. */
.pdb-tiles .stat::before {
    content: "";
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    /* The FULL VITALS mark, matching the sidebar brand: the white pulse trace over a faint
       (22%) Kubernetes helm-wheel watermark, on the theme brand gradient. Both the wheel and
       the pulse live in one SVG data-URI layered above the gradient, so every page tile
       carries the same logo the sidebar shows. */
    background-image:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none'%3E%3Cg stroke='%23FFFFFF' stroke-opacity='0.22' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M16 4.3 25.1 8.7 27.4 18.6 21 26.5 11 26.5 4.6 18.6 6.9 8.7Z' stroke-width='1.4'/%3E%3Ccircle cx='16' cy='16' r='4.1' stroke-width='1.4'/%3E%3Cpath d='M16 11.9V6.6M19.9 14.7 24.2 11.6M18.7 20 21.9 24.4M13.3 20 10.1 24.4M12.1 14.7 7.8 11.6' stroke-width='1.1'/%3E%3C/g%3E%3Cpath d='M7.6 16.6h3.1l2.1-5.4 2.9 10.2 2.3-6.4 1.5 1.6h3' fill='none' stroke='%23FFFFFF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"),
        var(--brand-gradient);
    background-repeat: no-repeat;
    background-position: center, center;
    background-size: 26px 26px, cover;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.25);
    align-self: center;
}
/* Block flow (not flex) so the merged value + sub sit together on one inline line under
   the label. The label is a block on its own line; value and sub are inline and share the
   second line, value bold and sub dimmed right after it. */
.pdb-tiles .stat .stat-body {
    display: block;
    min-width: 0;
}
.pdb-tiles .stat .stat-label { display: block; margin-bottom: 2px; font-size: 9.5px; letter-spacing: 0.5px; }
.pdb-tiles .stat .stat-value {
    display: inline;
    font-size: 18px;
    line-height: 1.1;
    vertical-align: baseline;
}
.pdb-tiles .stat .stat-sub {
    display: inline;
    font-size: 10.5px;
    margin: 0 0 0 6px;
    vertical-align: baseline;
}
/* Keep the merged value + sub on a single line so a long sub can never wrap and grow the
   tile taller than its neighbours. The value stays put; the sub takes the remaining width
   and ellipsizes if it would overflow (the full text remains in the tile's title/tooltip
   and on the page). This guarantees a uniform tile height across every page. */
.pdb-tiles .stat .stat-body { white-space: nowrap; overflow: hidden; }
.pdb-tiles .stat .stat-label { overflow: hidden; text-overflow: ellipsis; }
.pdb-tiles .stat .stat-sub { overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
/* The navigable chevron sits in the tile's top-right, a touch smaller than the KPI cards. */
.pdb-tiles a.stat::after, .pdb-tiles button.stat::after { top: 6px; right: 9px; font-size: 12px; }

/* ---- 3D / elevated tile look (all page-level tiles) ----
   Give tiles a raised card feel: a gradient surface, a lit top edge, a soft drop shadow
   and a crisper border, so they read as physical chips above the page rather than flat
   panels. Applies to every .stat tile (filter tiles, info tiles, scroll tiles). */
.stat-grid .stat {
    /* The former hover look is now the resting default: accent border and the stronger
       elevation, so tiles read as raised, active chips without needing a mouseover. */
    border: 1px solid var(--accent);
    border-radius: var(--radius-md, 10px);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--surface) 92%, #fff) 0%,
        var(--surface) 100%);
    box-shadow: 0 2px 4px rgba(0,0,0,0.22),
                0 8px 18px rgba(0,0,0,0.20),
                inset 0 1px 0 color-mix(in srgb, #fff 65%, transparent);
    transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}
/* Hover keeps only a small lift and a touch more shadow, so there is still feedback on
   interactive tiles now that the accent look is the resting state. */
.stat-grid a.stat:hover, .stat-grid button.stat:hover {
    transform: translateY(-2px);
    box-shadow: 0 3px 6px rgba(0,0,0,0.26),
                0 12px 24px rgba(0,0,0,0.24),
                inset 0 1px 0 color-mix(in srgb, #fff 70%, transparent);
}

/* ===== Zone list cells ================================================= */
/*
  Availability-zone lists like "us-west-2a, us-west-2b, us-west-2c" contain spaces, commas
  and hyphens, all of which are break opportunities — so on a narrow screen the browser
  fragments each zone vertically ("us- / west- / 2a,"), which is unreadable. Keep the whole
  list on one line and let the table's own horizontal scroll absorb the width, matching how
  the other identifier columns behave. Used wherever a zone list is shown (Namespaces,
  Version & Upgrade).
*/
.zone-list {
    white-space: nowrap;
}

/* ---- Client-side info modal (status/phase/reason explainers) ---- */
/* Sits inside .modal-body, so it inherits the modal chrome; these style the structured body. */
.info-modal { display: flex; flex-direction: column; gap: 2px; }
.info-modal-title { margin: 0 0 10px; font-size: 15px; font-weight: 650; color: var(--text); }
/* The first line reads as the lead statement, a touch stronger. */
.info-modal-text { margin: 0 0 10px; font-size: 13px; line-height: 1.6; color: var(--text-dim); }
.info-modal-text:first-child { color: var(--text); font-weight: 550; }
.info-modal-text:last-child { margin-bottom: 0; }
/* A "•" line becomes a bordered, indented point — used for suggested fixes and detail lists. */
.info-modal-point {
    margin: 0 0 6px;
    padding: 8px 12px;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-dim);
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius-sm);
}
.info-modal-point:last-child { margin-bottom: 0; }

/*
 * A cell whose pill/text opens an info modal. Rendered as a bare button so it is keyboard
 * reachable, but styled to look like the surrounding cell content — a subtle dotted underline
 * on hover signals it is interactive without shouting.
 */
.info-trigger {
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    text-align: left;
}
.info-trigger:hover { text-decoration: underline dotted; text-underline-offset: 3px; }
.info-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* A clickable link cell (namespace/workload/pod cross-links). Reuses link-btn where present;
 * this is the lighter inline variant for table cells. */
.xlink {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: var(--accent);
    cursor: pointer;
}
.xlink:hover { text-decoration: underline; }
.xlink:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* CronJobs: recent Job runs shown as a wrapping row of small outcome pills. */
.cron-runs { display: inline-flex; flex-wrap: wrap; gap: 4px; max-width: 260px; }

/* ─── Export format menu (Markdown / CSV) ─────────────────────────────────────
   The Export topbar button opens this small menu to choose a format. The menu is
   appended to <body> and positioned with position:fixed in JS, so it is never clipped
   by an ancestor's overflow and always sits above the panel content. */
.export-menu {
    position: fixed;
    z-index: 3000;
    min-width: 168px;
    padding: 5px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md, 0 10px 28px rgba(0, 0, 0, 0.22));
}
.export-menu[hidden] { display: none; }

.export-menu-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 8px 10px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
/* Hover/focus uses VITALS' own accent-highlight pattern (soft accent tint + accent text),
   matching the sidebar and other menus, so it reads as VITALS — not a foreign colour. */
.export-menu-item:hover,
.export-menu-item:focus-visible {
    background: var(--accent-soft);
    color: var(--accent);
    outline: none;
}
.export-menu-item:hover .export-ext,
.export-menu-item:focus-visible .export-ext { color: var(--accent); }

.export-fmt { font-weight: 600; }
.export-ext { font-family: var(--mono, monospace); font-size: 11px; color: var(--text-faint); }

/* ===== On Fire: per-browser Hide / Unhide-all triage ===================== */

/* A user-hidden diagnosis card is removed from view entirely (localStorage-backed,
   this browser only, until "Unhide all"). A dedicated class rather than the [hidden]
   attribute, so it never collides with the cause-filter which toggles [hidden]. */
.diag-card.is-user-hidden { display: none; }

/* With a Hide button added, the top row holds three items (subject, severity pill, Hide).
   Push the subject to take the free space so the pill and Hide button stay grouped at the
   right edge, instead of space-between spreading all three apart. */
.diag-card-top .diag-subject { margin-right: auto; }

/* The per-card Hide button sits at the far right of the card's top row, grouped with
   the severity pill. Quiet by default so it never competes with the diagnosis content;
   it firms up on hover/focus. Injected only on the On Fire page by vitals.js. */
.diag-card-top .diag-hide {
    flex: 0 0 auto;
    margin-left: 4px;
    padding: 3px 9px;
    font: inherit;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-faint);
    background: none;
    border: 1px solid var(--border);
    border-radius: 999px;
    cursor: pointer;
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.diag-card-top .diag-hide:hover,
.diag-card-top .diag-hide:focus-visible {
    color: var(--text);
    border-color: var(--border-strong);
    background: var(--bg-subtle);
    outline: none;
}

/* On-Fire "Unhide all (N)" button, injected into the scope tile's right-hand actions slot
   only while something is hidden. The scope text keeps its natural width; the actions slot
   is pushed to the far right of the scope tile (margin-left:auto) so the button sits at the
   tile's trailing edge, using the empty space the scope line always had. Vertically centred
   against the baseline-aligned scope text. The slot reserves no space when empty, so
   showing/hiding the button never shifts anything. */
.fire-scope-actions {
    margin-left: auto;
    align-self: center;
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}
.fire-scope-actions .btn-unhide {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: 3px 11px;
    font: inherit;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--accent);
    background: var(--accent-soft);
    border: 1px solid var(--accent-line, var(--border-strong));
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
.fire-scope-actions .btn-unhide:hover,
.fire-scope-actions .btn-unhide:focus-visible {
    color: #ffffff;
    background: var(--accent);
    outline: none;
}

/* ===== Diagnostic modal (reusable "understand and fix this" detail view) ==========
   One card per checked item, worst-first. Each card carries a status pill, what the check
   is, the evidence found on this workload, why it matters and how to fix it. The .diag-*
   classes are generic so detail modals on other pages can reuse this exact shape. */

.diag-intro {
    margin: 12px 0 14px;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--text-faint);
}

.diag-list { display: flex; flex-direction: column; gap: 10px; }

.diag-card {
    border: 1px solid var(--border);
    border-left: 3px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    padding: 12px 14px;
}
/* The left rule encodes severity, so the eye finds the failures first. */
.diag-card.diag-fail        { border-left-color: var(--error); }
.diag-card.diag-unspecified { border-left-color: var(--warn); }
.diag-card.diag-ok          { border-left-color: color-mix(in srgb, var(--ok) 55%, var(--border)); }
.diag-card.diag-na          { border-left-color: var(--border); }

.diag-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 6px;
}
.diag-card-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.diag-name { font-size: 13.5px; font-weight: 650; color: var(--text); }
.diag-profile {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-faint);
    padding: 1px 6px;
    border: 1px solid var(--border);
    border-radius: 999px;
}

.diag-what { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--text-dim); }

.diag-detail { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }

/* Each labelled row: a fixed-width label, then the body — a compact definition list. */
.diag-row { display: grid; grid-template-columns: 116px 1fr; gap: 10px; align-items: start; }
.diag-row-label {
    font-size: 11px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-faint);
    padding-top: 1px;
}
.diag-row-body { font-size: 12.5px; line-height: 1.55; color: var(--text-dim); }
.diag-row-body.mono { color: var(--text); }

/* The YAML fix: a fenced, copyable-looking block. */
.diag-fix {
    margin: 2px 0 0;
    padding: 10px 12px;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow-x: auto;
}
.diag-fix code {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 12px;
    line-height: 1.5;
    color: var(--text);
    white-space: pre;
}

.diag-ok-note { margin: 8px 0 0; font-size: 12px; color: var(--text-faint); }

@media (max-width: 640px) {
    .diag-row { grid-template-columns: 1fr; gap: 2px; }
}

/* Diagnostic modal identity block: a prominent row of chips naming the exact resource the
   modal describes, so it is never ambiguous which object is in question. Sits at the very top
   of the modal body, above the lead summary. Each field is its own chip — a small uppercase
   label with the value beside it — laid out in a wrapping row rather than stacked lines. */
.diag-identity {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}
/* The Troubleshoot jump sits at the trailing edge of the identity band and is styled as another
   identity chip (it carries .diag-id-field), so it reads as part of that band rather than a
   separate button — just interactive. Reset the button chrome and add a hover affordance. */
.diag-troubleshoot {
    margin-left: auto;
    cursor: pointer;
    font: inherit;
    white-space: nowrap;
    transition: background 0.12s ease, border-color 0.12s ease;
}
.diag-troubleshoot:hover {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    border-color: color-mix(in srgb, var(--accent) 50%, transparent);
}
.diag-troubleshoot .diag-id-value { font-size: 12px; }
.diag-id-field {
    display: inline-flex;
    align-items: baseline;
    gap: 7px;
    padding: 6px 12px;
    /* A light wash of the theme accent so the identity band reads as branded and cohesive,
       rather than a flat grey row. color-mix keeps it subtle and correct in both themes. */
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
    border-radius: 999px;
    min-width: 0;
}
.diag-id-label {
    font-size: 10px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: color-mix(in srgb, var(--accent) 75%, var(--text-faint));
    flex: 0 0 auto;
}
.diag-id-value {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}


/* ============================================================================
 * Drain Simulation — in-panel Kubernetes Observability workspace
 * The application navigation remains untouched. This node rail and detail view live only
 * inside the Drain Simulation content panel.
 * ========================================================================== */
/* Alias the native VITALS palette so this page uses the app's accent/semantic colours,
   not a bespoke blue — keeping it visually part of VITALS. */
.drain-observability {
    --dobs-blue: var(--accent);
    --dobs-blue-soft: color-mix(in srgb, var(--accent) 12%, var(--bg));
    --dobs-risk: var(--error);
    --dobs-risk-soft: color-mix(in srgb, var(--error) 10%, var(--bg));
    --dobs-warn: var(--warn);
    --dobs-warn-soft: color-mix(in srgb, var(--warn) 12%, var(--bg));
    --dobs-ok: var(--ok, var(--accent));
    --dobs-ok-soft: color-mix(in srgb, var(--ok, var(--accent)) 12%, var(--bg));
}
/* Header row: title + subtitle on the left, the four headline metrics on the right. */
.drain-observability .stat-grid { margin-bottom: 14px; }
.dobs-workspace { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 14px; align-items: stretch; margin-top: 4px; }
.dobs-node-rail, .dobs-detail { min-width: 0; }
.dobs-node-rail { padding: 10px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm); }
.dobs-rail-head { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; color: var(--text); font-size: 13px; }
.dobs-rail-head > span { color: var(--text-dim); }
.dobs-status-filter { margin-left: auto; border: 1px solid var(--border); border-radius: 5px; background: var(--bg); color: var(--text-dim); font-size: 11px; padding: 4px 6px; }
.dobs-node-list { display: flex; flex-direction: column; gap: 3px; margin-top: 10px; max-height: 600px; overflow-y: auto; }
.dobs-node-row { display: flex; align-items: flex-start; width: 100%; gap: 8px; padding: 9px 7px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--text); text-align: left; cursor: pointer; }
.dobs-node-row:hover { background: var(--bg); border-color: var(--border); }
.dobs-node-row.is-active { background: var(--dobs-blue-soft); border-color: color-mix(in srgb, var(--dobs-blue) 55%, var(--border)); box-shadow: inset 3px 0 0 var(--dobs-blue); }
.dobs-node-row:focus-visible { outline: 2px solid var(--dobs-blue); outline-offset: 1px; }
.dobs-node-dot { flex: 0 0 auto; width: 7px; height: 7px; margin-top: 5px; border-radius: 50%; background: var(--text-faint); }
.dobs-error .dobs-node-dot { background: var(--dobs-risk); }.dobs-warn .dobs-node-dot { background: var(--dobs-warn); }.dobs-ok .dobs-node-dot { background: var(--dobs-ok); }
.dobs-node-copy { min-width: 0; flex: 1; }.dobs-node-copy strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 600; }.dobs-node-copy small { display: block; margin-top: 3px; color: var(--text-faint); font-size: 11px; }
.dobs-chevron { align-self: center; color: var(--text-faint); font-size: 18px; line-height: 1; }

.dobs-selected { min-height: 640px; padding: 14px 16px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm); }.dobs-selected.is-hidden { display: none; }
.dobs-node-row.is-filtered { display: none; }
.dobs-selected-head { margin-bottom: 2px; }
.dobs-node-title { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }.dobs-node-title h3 { margin: 0; font-size: 15px; }
/* Zone · pods · schedulable sits inline, right after the hostname. */
.dobs-node-meta { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 7px; color: var(--text-faint); font-size: 12px; }

/* The per-node signal tiles reuse the shared .stat/.pdb-tile pattern; space them below the
   node header. The shared pattern clips tile text to one line for uniform height on other
   pages, but these tiles carry longer labels/subs (e.g. "Zone us-west-2a" / "single-zone
   outage"), so allow their text to wrap rather than be cut off — scoped to this page only. */
/* Wider tracks so the longest sub-line ("CPU 7400m / mem 12207Mi") stays on one line
   instead of wrapping; tiles size to content and drop to fewer-per-row only when the panel
   is genuinely narrow. Label/value/sub each sit on their own line, no clipping. */
.dobs-node-tiles { margin: 12px 0 4px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.dobs-node-tiles .stat { align-items: flex-start; }
/* Label, value and sub each occupy their own full-width line and start at the same left
   edge. The shared .pdb-tiles pattern renders value+sub inline (which left a leading space
   on the sub-line); force block flow with no indent so all three lines are left-aligned. */
.dobs-node-tiles .stat .stat-body { white-space: normal; overflow: visible; text-align: left; }
.dobs-node-tiles .stat .stat-label { display: block; width: 100%; margin: 0; overflow: visible; text-overflow: clip; white-space: normal; text-indent: 0; }
.dobs-node-tiles .stat .stat-value { display: block; width: 100%; margin: 0; text-indent: 0; }
.dobs-node-tiles .stat .stat-sub { display: block; width: 100%; margin: 0; overflow: visible; text-overflow: clip; white-space: nowrap; max-width: 100%; text-indent: 0; }

.dobs-workloads { margin-top: 10px; padding: 13px; border: 1px solid var(--border); border-radius: var(--radius-sm); }.dobs-workloads h4 { margin: 0; color: var(--text); font-size: 13px; }.dobs-section-head { display: flex; justify-content: space-between; align-items: baseline; }.dobs-section-head .xlink { font-size: 12px; }.dobs-workload-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; margin-top: 10px; }.dobs-workload { display: flex; align-items: flex-start; gap: 7px; min-width: 0; padding: 9px; border: 1px solid var(--accent); border-radius: 6px; background: var(--surface); overflow: hidden; }.dobs-workload-icon { flex: 0 0 auto; color: var(--accent); font-size: 13px; }
/* The text column must be allowed to shrink below its content width, or the name overflows
   the cell and overlaps the neighbour on narrow screens. min-width:0 lets ellipsis work. */
.dobs-workload > div { min-width: 0; flex: 1 1 auto; overflow: hidden; }
.dobs-workload b, .dobs-workload small { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.dobs-workload b { color: var(--text); font-size: 12px; }.dobs-workload small { margin-top: 2px; color: var(--text-faint); font-size: 11px; }
@media (max-width: 1280px) { .dobs-workload-grid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
@media (max-width: 1050px) { .dobs-workspace { grid-template-columns: 210px minmax(0,1fr); }.dobs-workload-grid { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 760px) { .dobs-workspace { grid-template-columns: 1fr; }.dobs-node-list { max-height: 280px; }.dobs-selected-head { align-items: flex-start; flex-direction: column; }.dobs-workload-grid { grid-template-columns: 1fr; } }

/* ============================================================================
   Registry + Images columns (shared across all workload list pages)
   ----------------------------------------------------------------------------
   A pod runs one or more containers, each with its own image. The Registry
   column shows the shared host once (or "mixed"); the Images column shows one
   coloured box per container image on a single row that ellipses on overflow,
   with the full references in the cell tooltip. The box colour is deterministic
   per repo:tag (img-c0..img-c7), so the same image is always the same tint.
   ============================================================================ */

/* The Images cell's own .cell-val must NOT draw a box — it only holds the row of
   per-image boxes, so neutralise the boxed-table chip border/background here to
   avoid a box-inside-a-box. The individual .img-box tokens carry the colour. */
table.data.cell-boxed td.img-cell > .cell-val {
    border: none;
    background: none;
    padding: 0;
    overflow: visible;
}

/* One horizontal, nowrap row of image boxes that ellipses when the cell is too
   narrow; the full set of references is always available via the cell title. */
.img-box-row {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 5px;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
}

/* One image reference, boxed. repo:tag is shown; the registry lives in its own
   column, and the box tooltip carries the full registry/repo:tag. The box itself
   never shrinks its text to nothing — it clips with the row's overflow instead. */
.img-box {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex: 0 1 auto;
    min-width: 0;
    max-width: 100%;
    box-sizing: border-box;
    padding: 1px 7px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-family: var(--mono);
    font-size: 11px;
    line-height: 1.7;
    white-space: nowrap;
}

.img-box .img-box-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* Hygiene dot on a risky image box (:latest / untagged) — the detail is in the
   box tooltip and the Status modal. Amber regardless of the box's own tint. */
.img-box .img-dot {
    flex: 0 0 auto;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--warn);
}

/* A risky box gets an amber ring on top of its palette tint so it reads as
   flagged without losing its deterministic colour identity. */
.img-box.img-risky {
    border-color: color-mix(in srgb, var(--warn) 55%, transparent);
}

/* "mixed" registry reads as a soft italic hint rather than a literal host. */
.img-cell .dim-italic,
td .dim-italic { font-style: italic; opacity: 0.85; }

/* ----------------------------------------------------------------------------
   The 8-slot calm tint palette. Each tint is a theme-aware mix of a hue anchor
   into the inset surface, so the boxes stay quiet on every theme (they tint the
   surface rather than shout a saturated colour). Text stays the dim table ink
   with a touch of the hue for legibility. Border is a slightly stronger mix.
   The hue order is spread around the wheel so adjacent slots are easy to tell
   apart: teal, violet, amber, rose, blue, green, orange, indigo.
   ---------------------------------------------------------------------------- */
.img-box.img-c0 { background: color-mix(in srgb, #2bb3a3 20%, var(--bg-inset)); border-color: color-mix(in srgb, #2bb3a3 42%, transparent); color: var(--text-dim); }
.img-box.img-c1 { background: color-mix(in srgb, #8b6cef 20%, var(--bg-inset)); border-color: color-mix(in srgb, #8b6cef 42%, transparent); color: var(--text-dim); }
.img-box.img-c2 { background: color-mix(in srgb, #d9a21b 22%, var(--bg-inset)); border-color: color-mix(in srgb, #d9a21b 44%, transparent); color: var(--text-dim); }
.img-box.img-c3 { background: color-mix(in srgb, #e0699a 20%, var(--bg-inset)); border-color: color-mix(in srgb, #e0699a 42%, transparent); color: var(--text-dim); }
.img-box.img-c4 { background: color-mix(in srgb, #3f8fe0 20%, var(--bg-inset)); border-color: color-mix(in srgb, #3f8fe0 42%, transparent); color: var(--text-dim); }
.img-box.img-c5 { background: color-mix(in srgb, #4cb98b 20%, var(--bg-inset)); border-color: color-mix(in srgb, #4cb98b 42%, transparent); color: var(--text-dim); }
.img-box.img-c6 { background: color-mix(in srgb, #e08244 20%, var(--bg-inset)); border-color: color-mix(in srgb, #e08244 42%, transparent); color: var(--text-dim); }
.img-box.img-c7 { background: color-mix(in srgb, #6a74d6 20%, var(--bg-inset)); border-color: color-mix(in srgb, #6a74d6 42%, transparent); color: var(--text-dim); }

/* The Pods "Containers" modal table: one row per container (app + init) with its image box.
   Kept compact and left-aligned; the image box carries its own palette tint. */
.pod-container-table { width: 100%; }
.pod-container-table th,
.pod-container-table td { padding: 7px 10px; vertical-align: middle; }
.pod-container-table td .pill { display: inline-flex; width: auto; }
.pod-container-table td .img-box { max-width: 100%; }
