/* ==========================================================================
   MegaMiner Panel — "Goldlake" liquid-glass theme
   macOS Tahoe / Goldlake inspired. Premium glassmorphism, gold reflective
   accents, layered transparency, high blur, deep hierarchy.
   Pure CSS. No Tailwind, no Bootstrap.  Two themes: Dark Gold / Light Gold.

   Cascade layers (lowest to highest priority):
     tokens     custom properties only (palette, structure, scales)
     base       element defaults, body background, focus visibility
     layout     shell: sidebar/drawer, app bar, content column, nav
     components glass surfaces, cards, badges, buttons, forms, modal, toasts
     utilities  single-purpose helpers (.mono, .text-muted, .u-hide, …)
     responsive every @media block — must outrank components so touch/mobile
                gating keeps beating component hover/layout rules
   Every rule must live in a layer: one unlayered style rule would outrank
   ALL layered rules and silently break the cascade.
   ========================================================================== */

/* ---------- Theme model ------------------------------------------------
   The stored preference (localStorage "mm-theme") maps to markup as:
     "dark"   -> <html data-theme="dark">   forced Dark Gold
     "light"  -> <html data-theme="light">  forced Light Gold
     "system" -> no data-theme attribute    OS decides
   One palette block: every color token is light-dark(light, dark) and
   `color-scheme` picks the side — `light dark` (system, UA resolves) or
   pinned by the forced data-theme rules. System mode works with no JS at
   all; the stored preference is never rewritten to the resolved theme. --- */

@layer tokens, base, layout, components, utilities, responsive;

/* self-hosted Inter variable font (v4.1, SIL OFL 1.1 — LICENSE.txt beside
   the file). @font-face is not a style rule; layers don't apply. Relative
   url is safe: WhiteNoise CompressedStaticFilesStorage does no url rewriting. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/InterVariable.woff2") format("woff2");
}

/* dashboard.js css() reads these two via getComputedStyle().getPropertyValue()
   and feeds them to canvas strokes. Unregistered custom properties hand back
   the raw light-dark() token stream — an invalid canvas color that silently
   strokes black. Registration forces computed-value resolution to a concrete
   color. @property is not a style rule; cascade layers don't apply. */
@property --gold-lt {
  syntax: "<color>";
  inherits: true;
  initial-value: #f4d47a;
}
@property --accent-soft {
  syntax: "<color>";
  inherits: true;
  initial-value: rgba(230, 192, 74, 0.2);
}

/* MPA cross-fade between panel pages (progressive enhancement: browsers
   without view transitions hard-navigate as before; delete these two
   at-rules to remove the effect entirely — nothing else depends on them) */
@view-transition { navigation: auto; }
/* the reduced-motion kill-switch below can't reach view-transition
   pseudo-elements ('*' never matches them), so opt out explicitly */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* ---------- tokens ------------------------------------------------------ */
@layer tokens {
  :root {
    /* system mode default: UA picks the light-dark() side from the OS */
    color-scheme: light dark;

    /* structural (identical in both themes) */
    --radius:    22px;
    --radius-sm: 14px;
    --blur:      26px;
    --sidebar-w: 268px;

    /* type */
    --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
                 Roboto, Helvetica, Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

    /* scales (defined in Phase 4a; wired into components incrementally) */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --fs-xs: 0.72rem;
    --fs-sm: 0.82rem;
    --fs-md: 0.9rem;
    --fs-lg: 1.05rem;
    --fs-xl: 1.85rem;

    /* motion (defined in Phase 4a; applied in Phase 4c) */
    --ease-out: cubic-bezier(.2, 0, 0, 1);
    --dur-fast: 120ms;
    --dur-med:  200ms;
    --dur-slow: 300ms;

    /* palette: light-dark(Light Gold, Dark Gold) */
    --bg:        light-dark(#f3ecd9, #0b0a07);
    --bg-2:      light-dark(#efe6cd, #12100a);

    --card:      light-dark(rgba(255, 252, 244, 0.62), rgba(38, 32, 18, 0.42));
    --card-2:    light-dark(rgba(184, 134, 11, 0.08),  rgba(212, 175, 55, 0.06));
    --card-hi:   light-dark(rgba(255, 255, 255, 0.7),  rgba(255, 246, 214, 0.10));
    --border:    light-dark(rgba(168, 128, 30, 0.28),  rgba(212, 175, 55, 0.18));
    --border-hi: light-dark(rgba(160, 120, 20, 0.6),   rgba(249, 224, 140, 0.55));

    --gold:      light-dark(#b8860b, #d4af37);
    --gold-lt:   light-dark(#e2b53c, #f4d47a);
    --gold-dp:   light-dark(#8a6410, #a9821f);
    --accent:    light-dark(#a9791a, #e6c04a);
    --accent-soft: light-dark(rgba(169, 121, 26, 0.16), rgba(230, 192, 74, 0.20));

    /* gradients can't be light-dark()'d whole — decomposed into color stops
       (--sheen-2 is the same in both themes) */
    --sheen-1:   light-dark(#f6dd94, #fbe6a8);
    --sheen-2:   #d4af37;
    --sheen-3:   light-dark(#9a7415, #8a6d1f);
    --sheen:     linear-gradient(135deg, var(--sheen-1), var(--sheen-2) 42%, var(--sheen-3));

    /* light sides darkened in Phase 5e: these are used as TEXT on the
       card/badge composites (badges, live badge, pstat, toasts, metric
       ok/bad values) where the old values sat at 2.9–4.1:1 — below AA.
       New values measure ≥4.5:1 on the light card-2 composite; dark
       sides untouched (mandate). */
    --success:   light-dark(#17753f, #4ade80);
    --danger:    light-dark(#b32b30, #f2555a);
    --warning:   light-dark(#8f620e, #f5b13d);

    --text:      light-dark(#3c341d, #ece5d2);
    /* light muted: 4.9:1 on --bg (AA); the old #7c7150 was 4.11:1 */
    --muted:     light-dark(#6f654a, #a99f83);
    --heading:   light-dark(#241d0c, #fdf7e6);

    /* geometry is shared (dark's radii — light-dark() only takes colors);
       light gains +6px/+10px blur vs Phase 3, accepted in the 4a plan */
    --shadow:    0 18px 50px light-dark(rgba(120, 92, 20, 0.20), rgba(0, 0, 0, 0.55)),
                 0 2px 0 light-dark(rgba(255, 255, 255, 0.5), rgba(255, 246, 214, 0.06)) inset;
    --shadow-lg: 0 26px 70px light-dark(rgba(120, 92, 20, 0.26), rgba(0, 0, 0, 0.62));

    --input-bg:  light-dark(rgba(255, 253, 247, 0.7),  rgba(10, 9, 5, 0.55));
    --input-bg-2:light-dark(rgba(255, 255, 255, 0.92), rgba(10, 9, 5, 0.75));
    --glow-1:    light-dark(#e8c65a, #d4af37);
    --glow-2:    light-dark(#d9b24a, #b8860b);
    --glow-3:    light-dark(#f2dc8f, #f4d47a);

    /* status triads (defined in Phase 4a; components wired in Phase 4b —
       dark sides equal today's literals exactly; light sides need an AA
       contrast pass before wiring) */
    --success-border: light-dark(rgba(31, 157, 85, 0.45),  rgba(74, 222, 128, 0.4));
    --danger-border:  light-dark(rgba(180, 40, 45, 0.45),  rgba(242, 85, 90, 0.5));
    --warning-border: light-dark(rgba(154, 100, 16, 0.45), rgba(245, 177, 61, 0.5));
    --success-bg:     light-dark(rgba(31, 157, 85, 0.12),  rgba(74, 222, 128, 0.12));
    --danger-bg:      light-dark(rgba(210, 58, 63, 0.10),  rgba(242, 85, 90, 0.14));
    --warning-bg:     light-dark(rgba(192, 127, 20, 0.10), rgba(245, 177, 61, 0.12));
    --danger-text:    light-dark(#8c1d21, #ffdada);
    --warning-text:   light-dark(#7a5410, #f5b13d);
    --err-text:       light-dark(#a02328, #ffb0b3);
    --danger-ring:    light-dark(rgba(180, 40, 45, 0.18), rgba(242, 85, 90, 0.2));

    /* readable gold for SMALL TEXT in the light theme (Phase 5e): --gold
       (3.0:1) and --gold-lt (1.65:1) fail AA as light-theme text. Two
       tokens because two dark literals must stay byte-identical. */
    --gold-text:    light-dark(#7c5a0d, #d4af37);  /* text that was var(--gold) */
    --gold-lt-text: light-dark(#7c5a0d, #f4d47a);  /* text that was var(--gold-lt) */

    /* glass hierarchy (Phase 4b): L1 nav, L2 card, L3 overlay — blur values
       derive from --blur so the 26/16/10 mobile tiers keep scaling */
    --glass-blur-nav:     var(--blur);
    --glass-blur-card:    calc(var(--blur) * 0.8);
    --glass-blur-overlay: calc(var(--blur) * 1.2);
    --glass-nav:     light-dark(rgba(255, 252, 244, 0.7),  rgba(38, 32, 18, 0.55));
    --glass-overlay: light-dark(rgba(255, 252, 244, 0.82), rgba(38, 32, 18, 0.72));
    /* one dimming veil for drawer scrim + modal backdrop (was two literals) */
    --overlay-scrim: rgba(6, 5, 2, 0.55);
  }

  /* forced themes pin the light-dark() side; theme-boot.js sets the
     attribute pre-paint, app.js keeps it in sync */
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"]  { color-scheme: dark; }
}

/* ---------- base -------------------------------------------------------- */
@layer base {
  * { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; }
  /* in-page anchors (attention links, jump nav) glide instead of teleport;
     initial page-load hash navigation is never smooth-scrolled by browsers.
     The reduced-motion block at the bottom reverts this to auto. */
  html { scroll-behavior: smooth; }

  body {
    margin: 0;
    min-height: 100vh;
    min-height: 100dvh; /* iOS URL-bar-safe; the 100vh line above is the fallback */
    font-family: var(--font-sans);
    color: var(--text);
    background: var(--bg);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
    transition: background var(--dur-slow) var(--ease-out), color var(--dur-slow) var(--ease-out);
  }

  /* fixed-position gradient layer instead of background-attachment: fixed,
     which iOS Safari ignores/repaints jankily. Identical visual result. */
  body::before {
    content: "";
    position: fixed; inset: 0; z-index: -2;
    background:
      radial-gradient(1200px 700px at 80% -10%, rgba(212,175,55,0.10), transparent 60%),
      radial-gradient(900px 600px at -10% 110%, rgba(184,134,11,0.10), transparent 60%),
      linear-gradient(180deg, var(--bg), var(--bg-2));
    transition: background var(--dur-slow) var(--ease-out);
  }

  .sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

  /* focus visibility */
  :focus-visible {
    outline: 2px solid var(--gold-lt);
    outline-offset: 2px;
  }
  /* inputs/selects keep their own focus ring (border + glow in components) */
  input:focus-visible, select:focus-visible { outline: none; }
}

/* ---------- icon system (inline SVG) ----------------------------------- */
@layer components {
  .ico {
    width: 1.15em; height: 1.15em;
    flex: 0 0 auto;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    vertical-align: -0.18em;
  }
  .ico-sm { width: 0.95em; height: 0.95em; }
  .ico-bolt { fill: var(--gold-lt); stroke: var(--gold-dp); stroke-width: 1.2; }
}

/* ---------- first-paint splash ------------------------------------------ */
@layer components {
  .splash {
    position: fixed; inset: 0; z-index: 200;
    display: flex; align-items: center; justify-content: center;
    background:
      radial-gradient(700px 500px at 50% 40%, rgba(212,175,55,0.14), transparent 60%),
      linear-gradient(180deg, var(--bg), var(--bg-2));
    transition: opacity .5s ease, visibility .5s ease;
    /* CSP failsafe: auto-dismiss even if every inline/external script is blocked or fails */
    animation: splashAutoHide .5s ease 4s forwards;
  }
  .splash.hide { opacity: 0; visibility: hidden; pointer-events: none; }
  /* terminal state: fully out of the layout so the loader can never trap the UI */
  .splash.gone { display: none !important; }
  @keyframes splashAutoHide { to { opacity: 0; visibility: hidden; pointer-events: none; } }
  .splash-inner { display: flex; flex-direction: column; align-items: center; gap: 16px; }
  .splash-brand {
    font-weight: 800; font-size: 1.5rem; letter-spacing: 0.4px;
    background: var(--sheen); -webkit-background-clip: text; background-clip: text;
    color: transparent;
  }
  .splash-bar {
    width: 180px; height: 4px; border-radius: 999px;
    background: var(--card-2); overflow: hidden;
  }
  .splash-bar span {
    display: block; height: 100%; width: 40%; border-radius: 999px;
    background: var(--sheen);
    animation: splashSlide 1.1s ease-in-out infinite;
  }
  @keyframes splashSlide {
    0%   { transform: translateX(-120%); }
    100% { transform: translateX(360%); }
  }

  /* 8-bit gif slots (hidden until a real gif is dropped in) */
  .gif-slot { image-rendering: pixelated; display: block; }
  .gif-bitcoin { width: 96px; height: 96px; }
  .gif-fallback { display: none; }
  .gif-fallback.show { display: block; }
  .coin-spin {
    display: inline-block; font-size: 3.4rem; line-height: 1;
    color: var(--gold-lt); text-shadow: 0 0 18px rgba(212,175,55,0.6);
    animation: coinSpin 1.4s linear infinite;
  }
  @keyframes coinSpin {
    0% { transform: rotateY(0); } 100% { transform: rotateY(360deg); }
  }
}

/* ---------- animated background glow ------------------------------------ */
@layer components {
  .bg-glow { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
  .bg-glow span {
    position: absolute; display: block;
    width: 46vw; height: 46vw; border-radius: 50%;
    filter: blur(100px); opacity: 0.4; will-change: transform;
  }
  .bg-glow .g1 { top: -12vw; left: -8vw;
    background: radial-gradient(circle at center, var(--glow-1), transparent 60%);
    animation: drift1 20s ease-in-out infinite alternate; }
  .bg-glow .g2 { bottom: -16vw; right: -10vw;
    background: radial-gradient(circle at center, var(--glow-2), transparent 60%);
    animation: drift2 24s ease-in-out infinite alternate; }
  .bg-glow .g3 { top: 30vh; right: 22vw; width: 30vw; height: 30vw; opacity: 0.22;
    background: radial-gradient(circle at center, var(--glow-3), transparent 65%);
    animation: drift3 28s ease-in-out infinite alternate; }
  @keyframes drift1 { from { transform: translate(0,0) scale(1); }
                      to   { transform: translate(6vw, 5vw) scale(1.15); } }
  @keyframes drift2 { from { transform: translate(0,0) scale(1); }
                      to   { transform: translate(-7vw,-4vw) scale(1.1); } }
  @keyframes drift3 { from { transform: translate(0,0) scale(1); }
                      to   { transform: translate(-4vw, 6vw) scale(1.2); } }
}

/* ---------- layout ------------------------------------------------------ */
@layer layout {
  .layout { display: flex; min-height: 100vh; min-height: 100dvh; }

  .sidebar {
    width: var(--sidebar-w);
    flex-shrink: 0;
    padding: 26px 18px 20px;
    position: sticky; top: 0; align-self: flex-start;
    height: 100vh; height: 100dvh;
    background: var(--glass-nav); /* L1 nav glass */
    backdrop-filter: blur(var(--glass-blur-nav)) saturate(140%);
    -webkit-backdrop-filter: blur(var(--glass-blur-nav)) saturate(140%);
    border-right: 1px solid var(--border);
    box-shadow: 1px 0 0 light-dark(rgba(255, 255, 255, 0.4), rgba(255, 246, 214, 0.04)) inset;
    display: flex; flex-direction: column;
  }

  /* ---------- mobile app bar + drawer scrim ----------------------------
     Hidden on desktop; the <=900px media block turns the sidebar into the
     drawer these control. Open state = .drawer-open class on <html>
     (theme state lives in the data-theme attribute — fully independent). */
  .appbar {
    display: none; /* flex at <=900px */
    position: sticky; top: 0; z-index: 50;
    align-items: center; gap: 8px;
    padding: 8px 12px;
    padding-top: calc(8px + env(safe-area-inset-top));
    padding-left: calc(12px + env(safe-area-inset-left));
    padding-right: calc(12px + env(safe-area-inset-right));
    background: var(--glass-nav); /* L1 nav glass */
    backdrop-filter: blur(var(--glass-blur-nav)) saturate(140%);
    -webkit-backdrop-filter: blur(var(--glass-blur-nav)) saturate(140%);
    border-bottom: 1px solid var(--border);
  }
  .appbar .brand { padding: 0 4px; font-size: 1.1rem; }
  .appbar-menu {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; flex: 0 0 auto;
    border-radius: var(--radius-sm); border: 1px solid transparent;
    background: none; color: var(--heading); cursor: pointer; font: inherit;
    transition: background var(--dur-med) var(--ease-out), border-color var(--dur-med) var(--ease-out);
  }
  .appbar-menu:hover { background: var(--card-2); border-color: var(--border); }
  .appbar-menu .ico { width: 1.35em; height: 1.35em; }
  .appbar-menu .ico-close { display: none; }
  html.drawer-open .appbar-menu .ico-menu { display: none; }
  html.drawer-open .appbar-menu .ico-close { display: inline-block; }

  .scrim {
    position: fixed; inset: 0; z-index: 79; /* just under the drawer (80) */
    background: var(--overlay-scrim);
    backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
    opacity: 0; visibility: hidden; pointer-events: none;
    touch-action: none; /* no touch-drag scroll bleed through the scrim (iOS) */
    transition: opacity var(--dur-slow) var(--ease-out), visibility var(--dur-slow) var(--ease-out);
  }
  html.drawer-open .scrim { opacity: 1; visibility: visible; pointer-events: auto; }

  /* lock page scroll while the drawer is open (drawer itself scrolls) */
  html.drawer-open, html.drawer-open body { overflow: hidden; }

  /* auth footer pinned to the bottom of the sidebar */
  .nav-foot { margin-top: auto; padding-top: 18px; display: grid; gap: 12px; }
  .nav-foot .whoami {
    display: flex; align-items: center; gap: 9px;
    padding: 4px 10px; font-size: 0.85rem; color: var(--muted);
  }
  .nav-foot .who-name { font-weight: 600; color: var(--heading); }
  .nav-foot .who-role {
    margin-left: auto; font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.6px;
    padding: 2px 9px; border-radius: 999px;
    /* AA in light (--gold-lt is 1.65:1 there); dark unchanged */
    border: 1px solid var(--border); color: var(--gold-lt-text);
    background: var(--card-2);
  }
  .nav-foot .logout-form { margin: 0; }
  .btn-block { width: 100%; justify-content: center; }

  /* theme toggle */
  .theme-toggle {
    display: flex; align-items: center; gap: 10px;
    width: 100%; padding: 10px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--card-2);
    color: var(--muted); font: inherit; font-weight: 600; font-size: 0.82rem;
    cursor: pointer;
    transition: border-color var(--dur-med) var(--ease-out), color var(--dur-med) var(--ease-out),
                background var(--dur-med) var(--ease-out), transform var(--dur-fast) var(--ease-out);
  }
  .theme-toggle:hover { color: var(--heading); border-color: var(--border-hi); transform: translateY(-1px); }
  .theme-toggle:active { transform: translateY(0); }
  /* label shows the SELECTED preference, not the resolved appearance; in
     system mode the resolved theme is appended so the user can tell that an
     OS appearance change will re-skin the panel. These media queries are
     label logic, not palette resolution. */
  .theme-toggle .tt-label::after { content: "System"; }
  :root[data-theme="dark"]  .theme-toggle .tt-label::after { content: "Dark Gold"; }
  :root[data-theme="light"] .theme-toggle .tt-label::after { content: "Light Gold"; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme]) .theme-toggle .tt-label::after { content: "System · Dark"; }
  }
  @media (prefers-color-scheme: light) {
    :root:not([data-theme]) .theme-toggle .tt-label::after { content: "System · Light"; }
  }
  .theme-toggle .tt-label { color: inherit; }
  .theme-toggle .tt-label { font-size: 0; }
  .theme-toggle .tt-label::after { font-size: 0.82rem; }
  /* icon mirrors the selected mode: sun = dark, moon = light (pre-existing
     pairing), auto glyph = system */
  .theme-toggle .ico-sun,
  .theme-toggle .ico-moon,
  .theme-toggle .ico-auto { display: none; color: var(--gold-lt); }
  :root:not([data-theme]) .theme-toggle .ico-auto { display: inline-block; }
  :root[data-theme="dark"]  .theme-toggle .ico-sun  { display: inline-block; }
  :root[data-theme="light"] .theme-toggle .ico-moon { display: inline-block; }

  /* brand */
  .brand {
    display: flex; align-items: center; gap: 12px;
    padding: 4px 8px 22px; text-decoration: none;
    font-weight: 800; font-size: 1.28rem; letter-spacing: 0.2px;
    color: var(--heading);
  }
  .brand-logo {
    border-radius: 11px;
    box-shadow: 0 6px 18px rgba(212,175,55,0.35), 0 0 0 1px var(--border);
  }
  .brand-name { line-height: 1; }
  .brand-accent {
    background: var(--sheen); -webkit-background-clip: text; background-clip: text;
    color: transparent;
  }

  .nav { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
  .nav a {
    display: flex; align-items: center; gap: 12px;
    padding: 11px 14px; border-radius: var(--radius-sm);
    color: var(--muted); text-decoration: none; font-weight: 500;
    border: 1px solid transparent;
    transition: background var(--dur-med) var(--ease-out), color var(--dur-med) var(--ease-out),
                border-color var(--dur-med) var(--ease-out), transform var(--dur-fast) var(--ease-out);
  }
  .nav a:hover {
    color: var(--heading); background: var(--card-2);
    border-color: var(--border); transform: translateX(3px);
  }
  .nav a.active {
    color: var(--heading);
    background: linear-gradient(90deg, var(--accent-soft), transparent 85%);
    border-color: var(--border-hi);
    box-shadow: 0 6px 20px rgba(212,175,55,0.14);
  }
  .nav a.active .ico { color: var(--gold-lt); }
  .nav .ico { color: var(--gold); }
  .nav a:hover .ico { color: var(--gold-lt); }

  .content {
    flex: 1; padding: 38px 44px 28px; min-width: 0;
    display: flex; flex-direction: column;
  }
  .content > :last-child { margin-bottom: 0; }

  .page-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 16px; margin-bottom: 28px; flex-wrap: wrap;
  }
  .page-head h1 {
    margin: 0; font-size: 1.85rem; font-weight: 800; color: var(--heading);
    letter-spacing: -0.02em;
  }
  .page-head p { margin: 5px 0 0; color: var(--muted); font-size: 0.92rem; }

  /* footer credit */
  .app-foot {
    margin-top: 34px; padding-top: 18px;
    border-top: 1px solid var(--border);
    color: var(--muted); font-size: 0.82rem; letter-spacing: 0.2px;
    text-align: center;
  }
  .app-foot strong { color: var(--heading); font-weight: 700; }
}

/* ---------- components -------------------------------------------------- */
@layer components {
  /* glass surface */
  .glass {
    position: relative;
    background: var(--card); /* L2 card glass */
    backdrop-filter: blur(var(--glass-blur-card)) saturate(140%);
    -webkit-backdrop-filter: blur(var(--glass-blur-card)) saturate(140%);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
  }
  /* top sheen highlight line */
  .glass::before {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    padding: 1px; pointer-events: none;
    background: linear-gradient(180deg, var(--card-hi), transparent 40%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
    opacity: .8;
  }

  /* stat cards */
  .stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 20px; margin-bottom: 26px;
  }
  .stat {
    padding: 20px 22px; position: relative; overflow: hidden;
    transition: transform var(--dur-med) var(--ease-out), border-color var(--dur-med) var(--ease-out),
                box-shadow var(--dur-med) var(--ease-out);
  }
  /* unified hover lift: cards -2px (buttons -1px, nav keeps translateX) */
  .stat:hover {
    transform: translateY(-2px);
    border-color: var(--border-hi);
    box-shadow: var(--shadow-lg);
  }
  .stat .stat-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
  }
  .stat .stat-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; flex: 0 0 auto; border-radius: 10px;
    color: var(--gold-lt); background: var(--card-2);
    border: 1px solid var(--border);
  }
  .stat .stat-ico .ico { width: 1.05rem; height: 1.05rem; }
  .stat.ok  .stat-ico { color: var(--success); }
  .stat.bad .stat-ico { color: var(--danger); }
  .stat .label {
    font-size: 0.76rem; text-transform: uppercase; letter-spacing: .1em;
    color: var(--muted); font-weight: 600;
  }
  .stat .value {
    font-size: 2.2rem; font-weight: 800; color: var(--heading);
    margin-top: 8px; line-height: 1; letter-spacing: -0.01em;
    font-variant-numeric: tabular-nums; /* live digits don't jitter in width */
  }
  .stat .bar {
    height: 4px; border-radius: 4px; margin-top: 14px;
    background: linear-gradient(90deg, var(--gold), transparent);
  }
  .stat.ok  .value { color: var(--success); }
  .stat.bad .value { color: var(--danger); }
  .stat.ok  .bar { background: linear-gradient(90deg, var(--success), transparent); }
  .stat.bad .bar { background: linear-gradient(90deg, var(--danger), transparent); }

  .stat .value-sub {
    font-size: 1rem; font-weight: 600; color: var(--muted);
    margin-left: 6px; letter-spacing: 0;
  }

  /* sparkline canvas inside a stat card */
  .spark {
    display: block; width: 100%; height: 40px; margin-top: 12px;
    opacity: .95;
  }

  /* KPI-row extras (Phase 5a) */
  .stat .label-note {
    text-transform: none; letter-spacing: 0; font-weight: 500;
    color: var(--muted); opacity: .8;
  }
  .stat .stat-sub {
    margin-top: 6px; font-size: 0.8rem; color: var(--muted);
  }

  /* demoted metrics strip: compact single row, no sparklines */
  .substat-row {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 10px 28px; padding: 12px 20px; margin-bottom: 26px;
  }
  .substat { display: inline-flex; align-items: baseline; gap: 8px; }
  .substat .ss-label {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: .1em;
    color: var(--muted); font-weight: 600;
  }
  .substat .ss-val { font-size: 0.9rem; font-weight: 700; color: var(--heading); }

  /* section titles */
  .section-title {
    display: flex; align-items: center; gap: 12px;
    font-size: 1.05rem; font-weight: 700; color: var(--heading);
    margin: 8px 0 18px;
  }
  .section-title::after { content: ""; flex: 1; height: 1px;
    background: linear-gradient(90deg, var(--border), transparent); }

  /* proxy card grid */
  .card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 22px;
  }
  .tenant-card {
    padding: 22px; display: flex; flex-direction: column; gap: 16px;
    transition: transform var(--dur-med) var(--ease-out), border-color var(--dur-med) var(--ease-out),
                box-shadow var(--dur-med) var(--ease-out);
  }
  .tenant-card:hover {
    transform: translateY(-2px);
    border-color: var(--border-hi);
    box-shadow: var(--shadow-lg);
  }
  /* identity header containment (Phase 5e): tenant names/addresses are
     unbroken tokens ([A-Za-z0-9_-]+, ip:port) — without min-width: 0 the
     flex children refuse to shrink and push the badges out of the card.
     Long names wrap mid-token; badges drop to their own line if needed. */
  .tenant-card .tc-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; flex-wrap: wrap;
  }
  .tenant-card .tc-id { min-width: 0; }
  .tenant-card .tc-name {
    font-size: 1.2rem; font-weight: 800; color: var(--heading);
    overflow-wrap: anywhere;
  }
  .tenant-card .tc-ip {
    font-size: 0.82rem; color: var(--muted);
    min-width: 0; overflow-wrap: anywhere;
  }
  /* address + its copy action share one identity line (Phase 5d) */
  .tc-addr { display: flex; align-items: center; gap: 4px; margin-top: 3px; min-width: 0; }

  .metrics-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
  .metric {
    background: var(--card-2); border: 1px solid var(--border);
    border-radius: var(--radius-sm); padding: 12px 10px; text-align: center;
    min-width: 0; /* grid children otherwise refuse to shrink below content */
  }
  .metric .m-val {
    font-size: 1.3rem; font-weight: 800; color: var(--heading);
    font-variant-numeric: tabular-nums; /* live digits don't jitter in width */
    overflow-wrap: anywhere; /* extreme values wrap instead of escaping */
  }
  .metric .m-lab {
    font-size: 0.64rem; text-transform: uppercase; letter-spacing: .07em;
    color: var(--muted); margin-top: 3px;
  }
  .metric.ok  .m-val { color: var(--success); }
  .metric.bad .m-val { color: var(--danger); }

  /* tenant-card redesign (Phase 5a): spacing over borders — primary
     metrics are plain value/label pairs, not boxed chips */
  .tc-badges { display: flex; gap: 8px; align-items: center; flex: none; }
  .tenant-card .metric {
    background: none; border: none; padding: 0; text-align: left;
  }
  .tenant-card .metric .m-val { font-size: 1.15rem; }
  .tenant-card .metric .m-val.mono { font-size: 0.95rem; line-height: 1.5; }

  /* no_snapshot empty state — data situation only; the insight line owns
     every status/health sentence (Phase 5d: no duplicate prose) */
  .tc-empty { color: var(--muted); font-size: 0.82rem; padding: 2px 0 4px; }
  .tc-empty-title {
    font-size: 0.95rem; font-weight: 600; color: var(--text);
    margin-bottom: 3px;
  }

  /* secondary metrics behind a native disclosure (zero JS) */
  .tc-details summary {
    cursor: pointer; list-style: none; user-select: none;
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 0.78rem; font-weight: 600; letter-spacing: .04em;
    color: var(--muted);
  }
  .tc-details summary::-webkit-details-marker { display: none; }
  .tc-details summary::after {
    content: "▸"; font-size: 0.7rem;
    transition: transform var(--dur-fast) var(--ease-out);
  }
  .tc-details[open] summary::after { transform: rotate(90deg); }
  .tc-details summary:hover { color: var(--heading); }
  .tc-pairs {
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: 12px 14px; margin-top: 12px;
  }
  .tc-pairs .pair { min-width: 0; } /* shrinkable grid children */
  .tc-pairs .p-lab {
    display: block; font-size: 0.62rem; text-transform: uppercase;
    letter-spacing: .07em; color: var(--muted);
  }
  .tc-pairs .p-val {
    font-size: 0.88rem; font-weight: 600; color: var(--text);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere; /* extreme values wrap instead of escaping */
  }

  /* drill-down (Phase 5b commit 3): health reason first, then metrics
     grouped Mining / Pool / Timing. The hs-dot inherits its color from
     the card's hs-* class via the existing descendant rules. */
  .tc-health {
    display: flex; align-items: center; gap: 8px;
    margin-top: 12px; font-size: 0.82rem; color: var(--muted);
  }
  .tc-health .hs-dot { width: 8px; height: 8px; }

  /* card why-line (Phase 5c): the tenant's primary insight, right under
     the identity header — quiet prose, dot color rides the card's hs-*
     class via the existing descendant rules (zero new state mapping) */
  .tc-insight {
    display: flex; align-items: center; gap: 8px;
    margin-top: -6px; /* tuck under the header inside the card's 16px gap */
    font-size: 0.82rem; color: var(--muted);
  }
  .tc-insight .hs-dot { width: 8px; height: 8px; }
  .tc-groups { display: flex; flex-direction: column; gap: 16px; margin-top: 14px; }
  .tc-group .tc-pairs { margin-top: 8px; }
  .tg-title {
    font-size: 0.64rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: .09em; color: var(--gold-text); /* AA in light; dark unchanged */
    border-bottom: 1px solid var(--border); padding-bottom: 5px;
  }

  /* copy connection address — compact icon button in the identity header
     (Phase 5d). The text label is hidden at rest (the aria-label names
     the action) and appears only as inline feedback while dashboard.js
     holds the copied/copy-err class for 1.4s. */
  .tc-copy {
    display: inline-flex; align-items: center; gap: 5px;
    background: none; border: 0; padding: 3px; margin: -3px 0;
    border-radius: var(--radius-sm); cursor: pointer;
    color: var(--muted); font: inherit; font-size: 0.72rem; font-weight: 600;
    transition: color var(--dur-fast) var(--ease-out);
  }
  .tc-copy:hover { color: var(--heading); }
  .tc-copy .ico { width: 14px; height: 14px; }
  .tc-copy .tc-copy-label { display: none; }
  .tc-copy.copied .tc-copy-label,
  .tc-copy.copy-err .tc-copy-label { display: inline; }
  .tc-copy.copied   { color: var(--success); }
  .tc-copy.copy-err { color: var(--danger); }

  .tc-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
  .tc-foot {
    display: flex; gap: 10px; margin-top: auto;
    border-top: 1px solid var(--border); padding-top: 16px;
  }
  .hint { color: var(--muted); font-size: 0.72rem; font-weight: 600; }

  /* badges */
  .badge {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: 0.72rem; font-weight: 600; letter-spacing: .02em;
    padding: 5px 11px; border-radius: 999px;
    border: 1px solid var(--border); color: var(--text); background: var(--card-2);
  }
  .badge .ico { color: var(--gold-lt); }
  .badge::before { content: ""; width: 7px; height: 7px; border-radius: 50%;
                   background: var(--muted); flex: 0 0 auto; }
  /* badges that carry their own icon don't need the leading dot */
  .badge:has(.ico)::before { display: none; }

  /* --gold-lt-text: AA in light (--gold-lt is 1.65:1 there); dark unchanged */
  .badge.mode-devfee { color: var(--gold-lt-text); border-color: var(--border-hi); }
  .badge.mode-devfee::before { background: var(--gold); box-shadow: 0 0 8px var(--gold); }
  .badge.mode-bare { color: var(--text); }

  .badge.pool-dev  { color: var(--warning); border-color: var(--warning-border); }
  .badge.pool-dev::before  { background: var(--warning); box-shadow: 0 0 8px var(--warning); }
  .badge.pool-main { color: var(--success); border-color: var(--success-border); }
  .badge.pool-main::before { background: var(--success); box-shadow: 0 0 8px var(--success); }

  /* worker activity state */
  .badge.state-active  { color: var(--success); border-color: var(--success-border); }
  .badge.state-active::before  { background: var(--success); box-shadow: 0 0 8px var(--success); }
  .badge.state-idle    { color: var(--warning); border-color: var(--warning-border); }
  .badge.state-idle::before    { background: var(--warning); box-shadow: 0 0 8px var(--warning); }
  .badge.state-offline { color: var(--muted); }
  .badge.state-offline::before { background: var(--muted); }

  /* pool reachability (latency badges) */
  .badge.pool-status-up   { color: var(--success); border-color: var(--success-border); }
  .badge.pool-status-up::before   { background: var(--success); box-shadow: 0 0 8px var(--success); }
  .badge.pool-status-down { color: var(--danger); border-color: var(--danger-border); }
  .badge.pool-status-down::before { background: var(--danger); box-shadow: 0 0 8px var(--danger); }

  /* buttons */
  /* .btn's display would silently defeat the hidden attribute (author
     display beats the UA [hidden] rule) — proxy.js relies on hidden to
     swap Start/Restart+Stop, so make it authoritative for buttons */
  .btn[hidden] { display: none !important; }
  .btn {
    --b: var(--border);
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    padding: 10px 17px; border-radius: var(--radius-sm);
    font: inherit; font-weight: 600; font-size: 0.86rem;
    color: var(--text); text-decoration: none; cursor: pointer;
    background: var(--card-2); border: 1px solid var(--b);
    transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-med) var(--ease-out),
                background var(--dur-med) var(--ease-out), border-color var(--dur-med) var(--ease-out),
                color var(--dur-med) var(--ease-out);
  }
  .btn:hover { transform: translateY(-1px); }
  .btn:active { transform: translateY(0) scale(.98); } /* press feedback */
  .btn.block { width: 100%; }
  .btn.sm { padding: 8px 13px; font-size: 0.8rem; }
  .btn .ico { color: currentColor; }

  .btn-accent {
    color: #241d0c; border-color: transparent;
    background: var(--sheen);
    box-shadow: 0 6px 18px rgba(212,175,55,0.28);
  }
  .btn-accent:hover { box-shadow: 0 10px 28px rgba(212,175,55,0.45); }
  .btn-accent .ico { color: #241d0c; }

  .btn-danger {
    color: var(--danger-text); border-color: var(--danger-border);
    background: var(--danger-bg);
  }
  .btn-danger:hover {
    background: color-mix(in srgb, var(--danger) 28%, transparent);
    box-shadow: 0 8px 24px color-mix(in srgb, var(--danger) 30%, transparent);
  }

  .btn-ghost { color: var(--text); }
  .btn-ghost:hover { border-color: var(--border-hi); color: var(--heading); }
  .btn-ghost .ico { color: var(--gold-lt); }

  /* forms */
  .form-card { padding: 30px 32px; max-width: 780px; }
  .form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px 24px; }
  .field { display: flex; flex-direction: column; gap: 8px; }
  .field.full { grid-column: 1 / -1; }
  .field label {
    font-size: 0.78rem; font-weight: 700; color: var(--muted);
    text-transform: uppercase; letter-spacing: .05em;
  }
  .field small { color: var(--muted); font-size: 0.72rem; text-transform: none; letter-spacing: 0; }

  input[type=text], input[type=number], input[type=password], input[type=date], select {
    width: 100%; padding: 12px 14px; color: var(--text);
    /* background-color (not the shorthand) so the select chevron
       background-image below survives */
    background-color: var(--input-bg); border: 1px solid var(--border);
    border-radius: var(--radius-sm); font: inherit; font-size: 0.9rem;
    transition: border-color var(--dur-med) var(--ease-out), box-shadow var(--dur-med) var(--ease-out),
                background var(--dur-med) var(--ease-out);
  }
  input::placeholder { color: var(--muted); opacity: .7; }
  input:focus, select:focus {
    outline: none; border-color: var(--gold-lt);
    box-shadow: 0 0 0 3px var(--accent-soft);
    background-color: var(--input-bg-2);
  }
  select option { background: var(--bg-2); color: var(--text); }
  /* custom chevron: two tiny gradient triangles — no data: URI needed, so
     the arrow color tracks var(--muted) across themes */
  select {
    appearance: none; -webkit-appearance: none;
    padding-right: 38px;
    background-image:
      linear-gradient(45deg, transparent 50%, var(--muted) 50%),
      linear-gradient(135deg, var(--muted) 50%, transparent 50%);
    background-position: calc(100% - 20px) 50%, calc(100% - 14px) 50%;
    background-size: 6px 6px, 6px 6px;
    background-repeat: no-repeat;
  }

  .fieldset {
    grid-column: 1 / -1; border: 1px solid var(--border);
    border-radius: var(--radius); padding: 20px 22px 6px; background: var(--card-2);
  }
  .fieldset > legend {
    padding: 0 10px; font-size: 0.78rem; font-weight: 800;
    /* AA in light (--gold-lt is 1.65:1 there); dark unchanged */
    color: var(--gold-lt-text); text-transform: uppercase; letter-spacing: .07em;
  }

  .switch { display: flex; align-items: center; gap: 12px; text-transform: none; }
  /* checkbox restyled as a pill switch — still a native checkbox underneath
     (keyboard, screen readers, form POST unchanged). 44px wide is already a
     comfortable touch target, so the old (pointer: coarse) upsize is gone. */
  .switch input {
    appearance: none; -webkit-appearance: none;
    width: 44px; height: 24px; flex: 0 0 auto; margin: 0;
    border-radius: 999px; border: 1px solid var(--border);
    background: var(--input-bg);
    position: relative; cursor: pointer;
    transition: background var(--dur-med) var(--ease-out), border-color var(--dur-med) var(--ease-out);
  }
  .switch input::before {
    content: ""; position: absolute; top: 3px; left: 3px;
    width: 16px; height: 16px; border-radius: 50%;
    background: var(--muted);
    transition: transform var(--dur-med) var(--ease-out), background var(--dur-med) var(--ease-out);
  }
  .switch input:checked {
    background: var(--accent-soft); border-color: var(--border-hi);
  }
  .switch input:checked::before {
    transform: translateX(20px); background: var(--gold);
  }
  /* the base layer strips outlines from inputs (they carry their own focus
     ring); the switch has none, so restore focus visibility */
  .switch input:focus-visible {
    outline: 2px solid var(--gold-lt); outline-offset: 2px;
  }

  .form-actions {
    display: flex; gap: 12px; margin-top: 26px;
    border-top: 1px solid var(--border); padding-top: 22px;
  }

  .alert {
    display: flex; align-items: center; gap: 10px;
    padding: 13px 16px; border-radius: var(--radius-sm); margin-bottom: 18px;
    border: 1px solid var(--danger-border); background: var(--danger-bg);
    color: var(--danger-text); font-size: 0.9rem;
  }
  .alert .ico { color: var(--danger); }
  .notice {
    display: flex; align-items: center; gap: 10px;
    padding: 13px 16px; border-radius: var(--radius-sm); margin-bottom: 20px;
    border: 1px solid var(--warning-border); background: var(--warning-bg);
    color: var(--warning-text); font-size: 0.9rem;
  }

  /* table (proxy list) */
  .table-wrap { padding: 10px; overflow-x: auto; margin-bottom: 4px; }
  table.grid { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
  table.grid th, table.grid td {
    text-align: left; padding: 14px 18px; border-bottom: 1px solid var(--border);
    vertical-align: middle;
  }
  table.grid th {
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: .07em;
    color: var(--muted); font-weight: 700;
  }
  table.grid td:last-child, table.grid th:last-child { text-align: right; }
  table.grid tbody tr { transition: background var(--dur-fast) var(--ease-out); }
  table.grid tbody tr:hover { background: var(--card-2); }
  table.grid tbody tr:last-child td { border-bottom: none; }
  .row-actions { display: flex; gap: 8px; justify-content: flex-end; }

  /* audit log viewer (Phase 6d) */
  .audit-filters { padding: 20px 22px; margin-bottom: 18px; }
  .audit-filters .form-grid { grid-template-columns: repeat(3, 1fr); gap: 14px 18px; }
  .audit-filters .form-actions { margin-top: 16px; padding-top: 0; border-top: none; }
  .audit-detail summary {
    cursor: pointer; color: var(--gold-text, var(--gold-lt)); font-size: 0.82rem;
    user-select: none; white-space: nowrap;
  }
  .audit-detail summary:hover { text-decoration: underline; }
  .audit-detail[open] summary { margin-bottom: 8px; }
  .audit-pairs { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px;
                 font-size: 0.82rem; text-align: left; }
  .audit-pairs dt {
    font-size: 0.68rem; text-transform: uppercase; letter-spacing: .07em;
    color: var(--muted); font-weight: 700; align-self: start; padding-top: 2px;
  }
  .audit-pairs dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
  .audit-pre {
    margin: 0; padding: 10px 12px; font-size: 0.78rem; line-height: 1.5;
    background: var(--card-2); border: 1px solid var(--border);
    border-radius: var(--radius-sm); overflow-x: auto; max-width: 560px;
  }
  .pager {
    display: flex; align-items: center; justify-content: center; gap: 14px;
    padding: 16px 0 6px; color: var(--muted); font-size: 0.85rem;
  }

  .empty { text-align: center; color: var(--muted); padding: 44px 20px; }
  /* dashboard empty state spans the whole proxy card grid (was an inline style) */
  .card-grid > .empty { grid-column: 1 / -1; }
  /* the tenant-list empty cell is the row's last td — recenter it */
  table.grid td.empty { text-align: center; }
  .empty-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 46px; height: 46px; border-radius: 14px;
    color: var(--gold-lt); background: var(--card-2);
    border: 1px solid var(--border);
  }
  .empty-ico .ico { width: 1.35em; height: 1.35em; }
  .empty-title { margin-top: 12px; font-weight: 700; color: var(--heading); font-size: 1.02rem; }

  /* snapshot-age readout next to the live badge (1s local ticker) */
  .updated-ago { font-size: 0.75rem; color: var(--muted); white-space: nowrap; }

  /* live status badge */
  .live-badge {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 7px 13px; border-radius: 999px;
    font-size: 0.72rem; font-weight: 700; letter-spacing: .08em;
    border: 1px solid var(--border); background: var(--card-2); color: var(--muted);
  }
  .live-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); }
  .live-ok { color: var(--success); border-color: var(--success-border); }
  .live-ok .live-dot { background: var(--success); box-shadow: 0 0 10px var(--success);
    animation: livePulse 1.6s ease-in-out infinite; }
  /* stale = snapshot frozen (proxy not flushing); dot deliberately does NOT
     pulse — the pulse is the "data is moving" signal */
  .live-stale { color: var(--warning); border-color: var(--warning-border); }
  .live-stale .live-dot { background: var(--warning); box-shadow: 0 0 10px var(--warning); }
  .live-down { color: var(--danger); border-color: var(--danger-border); }
  .live-down .live-dot { background: var(--danger); box-shadow: 0 0 10px var(--danger); }
  @keyframes livePulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

  /* health strip + needs-attention (Phase 5a) ---------------------------
     hs-* classes carry the SEMANTIC state from core/health.py; the token
     mapping lives only here: critical=danger, warning=gold (brand accent),
     no_data=muted, healthy=subtle (dot only, no border accent) */
  .health-strip {
    display: flex; align-items: center; gap: 14px;
    padding: 16px 20px; margin-bottom: 18px;
    /* Phase 5b: the operator's health readout stays visible while the page
       scrolls (the rest of the header scrolls away). z-index below the
       appbar (50) and above the glass cards (which stack via position). */
    position: sticky; top: 12px; z-index: 40;
  }
  .hs-dot {
    width: 12px; height: 12px; border-radius: 50%;
    background: var(--muted); flex: none;
  }
  .hs-critical .hs-dot { background: var(--danger);  box-shadow: 0 0 10px var(--danger); }
  .hs-warning  .hs-dot { background: var(--warning); box-shadow: 0 0 10px var(--warning); }
  .hs-healthy  .hs-dot { background: var(--success); box-shadow: 0 0 10px var(--success); }
  /* no_data / unknown keep the muted default dot */

  .health-strip .hs-label { font-weight: 700; color: var(--heading); }
  /* one line, ellipsized (same rule as .attn-reason): with the control
     cluster on board the strip must stay a single row on desktop — the
     full cause is always readable in the insight bar + attention list */
  .health-strip .hs-main { flex: 1 1 auto; min-width: 0; }
  .health-strip .hs-reason {
    color: var(--muted); font-size: 0.88rem; margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .health-strip .hs-meta {
    margin-left: auto; color: var(--muted);
    font-size: 0.82rem; text-align: right;
  }

  /* proxy control cluster (Phase 5d): the strip is the operator command
     center — status pill + the actions that fix it, one surface. A thin
     divider separates readout from controls on wide layouts. */
  .hs-ctl {
    display: flex; align-items: center; gap: 10px; flex: none;
    border-left: 1px solid var(--border); padding-left: 14px;
  }
  .hs-ctl-actions { display: flex; align-items: center; gap: 8px; }
  .hs-ctl .btn { white-space: nowrap; }

  /* left accent shared by strip, attention rows and tenant cards */
  .hs-critical { border-left: 3px solid var(--danger); }
  .hs-warning  { border-left: 3px solid var(--gold); }
  .hs-no_data  { border-left: 3px solid var(--muted); }

  /* operator insight bar (Phase 5c): one plain-language line explaining
     the strip's state (messages built server-side in core/insights.py).
     Deliberately small and muted — it explains the strip, it never
     competes with it; dot + left accent reuse the hs-* rules above. */
  .insight-bar {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 16px; margin: -8px 0 18px; /* tucks under the strip */
    color: var(--muted); font-size: 0.85rem;
  }
  .insight-bar .hs-dot { width: 8px; height: 8px; }

  .attn-list {
    display: flex; flex-direction: column; gap: 10px; margin-bottom: 22px;
  }
  .attn-row {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 16px; text-decoration: none; color: var(--text);
    transition: border-color var(--dur-med) var(--ease-out);
  }
  .attn-row:hover { border-color: var(--border-hi); }
  /* shrinkable + mid-token wrap: tenant names are unbroken [A-Za-z0-9_-]+
     tokens — flex: none let long names slide out of the row (Phase 5e) */
  .attn-name {
    font-weight: 700; color: var(--heading);
    flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere;
  }
  .attn-reason {
    color: var(--muted); font-size: 0.85rem; flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .attn-go { margin-left: auto; flex: none; }

  /* operator navigation (Phase 5b) --------------------------------------
     section-head puts the jump nav beside the "Proxies" title; the chips
     scroll to the first card in a health state (resolved live in JS) */
  .section-head {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: 8px 16px; margin: 8px 0 18px;
  }
  .section-head .section-title { flex: 1 1 auto; margin: 0; min-width: 160px; }

  .jump-nav, .filter-chips { display: flex; flex-wrap: wrap; gap: 8px; }
  .jump-chip, .filter-chip {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 5px 12px; border-radius: 999px;
    border: 1px solid var(--border); background: var(--card-2);
    color: var(--muted); font: inherit; font-size: 0.74rem; font-weight: 600;
    letter-spacing: .02em; cursor: pointer;
    transition: color var(--dur-med) var(--ease-out),
                border-color var(--dur-med) var(--ease-out),
                background var(--dur-med) var(--ease-out);
  }
  .jump-chip:hover, .filter-chip:hover { color: var(--heading); border-color: var(--border-hi); }
  .jump-chip::before, .filter-chip::before {
    content: ""; width: 7px; height: 7px; border-radius: 50%;
    background: var(--muted); flex: 0 0 auto;
  }
  .jump-chip[data-jump="all"]::before,
  .filter-chip[data-filter="all"]::before { display: none; }
  .jc-healthy::before  { background: var(--success); }
  .jc-warning::before  { background: var(--warning); }
  .jc-critical::before { background: var(--danger); }
  /* jc-no_data keeps the muted default dot */

  /* tenant search + health filtering (Phase 5b commit 2) ----------------
     purely client-side: the query + the active chip hide/show cards the
     server already rendered. The poll keeps updating hidden cards, so a
     card is current the instant a filter change reveals it. Jump chips
     (above) and filter chips share one visual language; only the filter
     chips hold a persistent active state. */
  .filter-bar {
    flex: 1 1 100%; /* second row inside .section-head */
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  }
  .filter-search {
    flex: 0 1 250px; min-width: 170px;
    padding: 6px 14px; border-radius: 999px;
    border: 1px solid var(--border); background: var(--card-2);
    color: var(--text); font: inherit; font-size: 0.8rem;
    transition: border-color var(--dur-med) var(--ease-out),
                box-shadow var(--dur-med) var(--ease-out);
  }
  .filter-search::placeholder { color: var(--muted); opacity: .7; }
  .filter-search:focus {
    outline: none; border-color: var(--gold-lt);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  .filter-chip.is-active {
    color: var(--heading); border-color: var(--gold);
    background: var(--accent-soft);
  }
  .tenant-card.f-hide { display: none; }

  /* anchors land below the sticky strip, not underneath it (applies to
     both native #tenant-... navigation and JS scrollIntoView); the strip
     grew slightly with the control cluster on board */
  .tenant-card { scroll-margin-top: 110px; }

  /* one-shot highlight so the eye finds the card the scroll landed on:
     :target covers anchor navigation, .jump-flash covers the JS jumps */
  @keyframes jumpFlash {
    0% { border-color: var(--border-hi);
         box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow); }
  }
  .tenant-card:target, .tenant-card.jump-flash {
    animation: jumpFlash 1s var(--ease-out);
  }

  /* value-change flash (live updates) */
  @keyframes valFlash {
    0%   { transform: scale(1.16); text-shadow: 0 0 16px var(--gold-lt); }
    100% { transform: scale(1);    text-shadow: none; }
  }
  .val-flash { animation: valFlash 0.55s var(--ease-out); display: inline-block; }

  /* insight change-awareness (Phase 5c): one soft background sweep when an
     insight's message text changes (dashboard.js restarts it); collapsed by
     the global prefers-reduced-motion rule like every other animation */
  @keyframes insightFlash { 0% { background-color: var(--accent-soft); } }
  .insight-flash {
    animation: insightFlash 1.2s var(--ease-out);
    border-radius: var(--radius-sm);
  }

  /* toasts */
  .toast-wrap {
    position: fixed; top: 22px; right: 22px; z-index: 60;
    display: flex; flex-direction: column; gap: 10px;
  }
  .toast {
    min-width: 250px; padding: 14px 17px; border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--glass-overlay); /* L3 overlay glass */
    backdrop-filter: blur(var(--glass-blur-overlay)); -webkit-backdrop-filter: blur(var(--glass-blur-overlay));
    box-shadow: var(--shadow-lg); color: var(--text); font-size: 0.9rem; font-weight: 500;
    display: flex; align-items: center; gap: 10px;
    animation: toastIn var(--dur-slow) var(--ease-out), toastOut .5s var(--ease-out) 4s forwards;
  }
  .toast::before { content: ""; width: 9px; height: 9px; border-radius: 50%; background: var(--muted); }
  .toast.success { border-color: var(--success-border); color: var(--success); }
  .toast.success::before { background: var(--success); box-shadow: 0 0 8px var(--success); }
  .toast.error { border-color: var(--danger-border); color: var(--danger); }
  .toast.error::before { background: var(--danger); box-shadow: 0 0 8px var(--danger); }
  @keyframes toastIn  { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; } }
  @keyframes toastOut { to { opacity: 0; transform: translateY(-8px); visibility: hidden; } }

  /* inline field errors */
  .field-err, .err { color: var(--err-text); font-size: 0.72rem; }
  input.invalid, select.invalid {
    border-color: var(--danger); box-shadow: 0 0 0 3px var(--danger-ring);
  }

  /* proxy process status pill (controls live in the health strip — the
     standalone Proxy Process card is gone since Phase 5d) */
  .pstat {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 7px 14px; border-radius: 999px;
    font-size: 0.75rem; font-weight: 700; letter-spacing: .05em;
    border: 1px solid var(--border); background: var(--card-2); color: var(--muted);
  }
  .pstat-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); }
  .pstat-running { color: var(--success); border-color: var(--success-border); }
  .pstat-running .pstat-dot { background: var(--success); box-shadow: 0 0 10px var(--success);
    animation: livePulse 1.6s ease-in-out infinite; }
  .pstat-stopped, .pstat-failed, .pstat-locked { color: var(--danger); border-color: var(--danger-border); }
  .pstat-stopped .pstat-dot, .pstat-failed .pstat-dot, .pstat-locked .pstat-dot {
    background: var(--danger); box-shadow: 0 0 10px var(--danger); }
  .pstat-restarting { color: var(--warning); border-color: var(--warning-border); }
  .pstat-restarting .pstat-dot { background: var(--warning); box-shadow: 0 0 10px var(--warning);
    animation: livePulse 0.9s ease-in-out infinite; }

  /* modal */
  .modal {
    position: fixed; inset: 0; z-index: 70;
    display: none; align-items: center; justify-content: center;
    background: var(--overlay-scrim);
    backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  }
  .modal.open { display: flex; animation: modalFade var(--dur-med) var(--ease-out); }
  @keyframes modalFade { from { opacity: 0; } }
  /* own entrance (no more borrowed toastIn) + L3 overlay glass */
  .modal.open .modal-card { animation: modalIn var(--dur-med) var(--ease-out); }
  @keyframes modalIn { from { opacity: 0; transform: scale(.97); } }
  .modal-card {
    padding: 28px 30px; max-width: 440px; width: calc(100% - 40px);
    background: var(--glass-overlay);
    backdrop-filter: blur(var(--glass-blur-overlay)) saturate(140%);
    -webkit-backdrop-filter: blur(var(--glass-blur-overlay)) saturate(140%);
    box-shadow: var(--shadow-lg);
  }
  .modal-card h3 { margin: 0 0 10px; color: var(--heading); font-size: 1.18rem; }
  .modal-actions { display: flex; gap: 12px; justify-content: flex-end; margin-top: 24px; }
}

/* ---------- utilities ---------------------------------------------------- */
@layer utilities {
  .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
  .u-hide { display: none; }
  .text-muted { color: var(--muted); }
  /* page-head right-hand cluster (live badge + primary action); wraps so
     ultra-narrow viewports never grow a horizontal scrollbar */
  .head-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
  /* CTA spacing inside empty states */
  .empty-cta { margin-top: 14px; }
  /* auth (login/setup) and confirm (delete) form cards are narrower than
     the default 780px form card */
  .form-card--auth { max-width: 440px; }
  .form-card--confirm { max-width: 520px; }
}

/* ---------- responsive --------------------------------------------------- */
@layer responsive {
  /* touch ergonomics: 44px minimum targets */
  @media (pointer: coarse) {
    .btn, .btn.sm { min-height: 44px; }
    .nav a { min-height: 44px; }
    .jump-chip, .filter-chip { min-height: 44px; }
    .filter-search { min-height: 44px; }
    /* header copy icon-button still needs a full touch target */
    .tc-copy { min-height: 44px; min-width: 44px; justify-content: center; margin: 0; }
    input[type=text], input[type=number], input[type=password], input[type=date], select { min-height: 44px; }
  }

  /* no sticky-hover lifts on touch devices */
  @media (hover: none) {
    .stat:hover, .tenant-card:hover, .btn:hover,
    .nav a:hover, .theme-toggle:hover { transform: none; }
  }

  /* ---- tablet / small (<=900px): app bar + glass drawer ----------------- */
  @media (max-width: 900px) {
    /* lighter glass: cheaper compositing on mobile GPUs, same identity */
    :root { --blur: 16px; }

    .layout { flex-direction: column; }
    .appbar { display: flex; }

    /* audit filters: the 3-col desktop grid outranks the generic .form-grid
       collapse below, so it needs its own tablet/mobile steps */
    .audit-filters .form-grid { grid-template-columns: repeat(2, 1fr); }

    /* the sidebar becomes the drawer: same markup, off-canvas by default */
    .sidebar {
      position: fixed; top: 0; left: 0; bottom: 0;
      width: min(var(--sidebar-w), 84vw);
      height: auto; /* anchored top/bottom */
      z-index: 80;
      transform: translateX(-102%); /* extra 2% hides the box-shadow edge */
      transition: transform var(--dur-slow) var(--ease-out);
      overflow-y: auto;
      overscroll-behavior: contain;
      padding: 18px 16px calc(16px + env(safe-area-inset-bottom));
      padding-top: calc(18px + env(safe-area-inset-top));
    }
    html.drawer-open .sidebar { transform: translateX(0); box-shadow: var(--shadow-lg); }

    /* static glows: keep the depth, drop three infinite blur animations */
    .bg-glow span { animation: none; filter: blur(60px); }
    .bg-glow .g3 { display: none; }

    .content { padding: 24px 20px 22px; }
    .form-grid { grid-template-columns: 1fr; }
    .metrics-row { grid-template-columns: repeat(2, 1fr); }

    /* sticky strip sits below the sticky appbar (~61px + notch inset);
       anchor targets need the extra headroom of appbar + strip */
    .health-strip { top: calc(66px + env(safe-area-inset-top)); flex-wrap: wrap; }
    .tenant-card { scroll-margin-top: calc(200px + env(safe-area-inset-top)); }
    /* control cluster may wrap to its own line on narrow tablets —
       keep it right-aligned rather than full-bleed */
    .hs-ctl { margin-left: auto; }

    /* below 16px iOS Safari zooms the page when an input gets focus */
    input[type=text], input[type=number], input[type=password], input[type=date], select,
    .filter-search {
      font-size: 16px;
    }
  }

  /* ---- phone (<=640px) ------------------------------------------------- */
  @media (max-width: 640px) {
    :root { --blur: 10px; }

    .content {
      padding: 18px 14px 20px;
      padding-left: max(14px, env(safe-area-inset-left));
      padding-right: max(14px, env(safe-area-inset-right));
    }

    .page-head { flex-direction: column; align-items: stretch; }
    .page-head > .btn { justify-content: center; }

    /* compact 2-up stat cards instead of one tall column */
    .stat-grid {
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: 12px; margin-bottom: 20px;
    }
    .stat { padding: 16px; }
    .stat .value { font-size: 1.4rem; }
    .stat .value-sub { font-size: 0.85rem; }
    .spark { height: 32px; margin-top: 10px; }

    .card-grid { gap: 14px; }
    .tenant-card { padding: 18px; }
    .tc-pairs { grid-template-columns: repeat(2, 1fr); }

    /* search takes its own line; chips wrap beneath it */
    .filter-search { flex: 1 1 100%; min-width: 0; }

    .substat-row { gap: 8px 18px; padding: 10px 14px; }

    /* health strip: compact 3-row stack — [dot+label/reason] [meta+status]
       [action buttons]. The control cluster dissolves (display: contents)
       so the status pill can share the meta row; the buttons take one
       full-width row. Meta ellipsizes so ultra-narrow widths can never
       wrap the pill onto a fourth row. */
    .health-strip { flex-wrap: wrap; padding: 12px 16px; gap: 8px 12px; }
    /* basis 0 keeps the nowrap reason's intrinsic width out of the
       line-breaking math (basis auto would wrap main under the dot);
       min-width 70% still forces meta off row 1 so the label never
       gets crushed beside it */
    .health-strip .hs-main { flex: 1 1 0; min-width: 70%; }
    .health-strip .hs-meta {
      margin-left: 0; flex: 1 1 auto; min-width: 0; text-align: left;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .hs-ctl { display: contents; }
    .hs-ctl-actions { flex: 1 1 100%; }
    /* the wrapped strip is taller — anchors need more headroom
       (66px appbar + ~194px strip, measured at 390px) */
    .tenant-card { scroll-margin-top: calc(270px + env(safe-area-inset-top)); }
    .attn-row { flex-wrap: wrap; }
    .attn-reason {
      flex-basis: 100%; order: 4; white-space: normal;
    }

    /* tenant table -> stacked label/value rows (labels via data-label) */
    table.grid thead {
      position: absolute; width: 1px; height: 1px; overflow: hidden;
      clip: rect(0 0 0 0); clip-path: inset(50%);
    }
    table.grid, table.grid tbody, table.grid tr { display: block; }
    table.grid tr { padding: 12px 4px; border-bottom: 1px solid var(--border); }
    table.grid tbody tr:last-child { border-bottom: none; }
    table.grid td {
      display: flex; justify-content: space-between; align-items: center;
      gap: 12px; padding: 6px 8px; border-bottom: none; text-align: left;
      /* long mono hostnames wrap instead of sliding under the card edge */
      overflow-wrap: anywhere; min-width: 0;
    }
    table.grid td::before {
      content: attr(data-label);
      font-size: 0.68rem; text-transform: uppercase; letter-spacing: .07em;
      color: var(--muted); font-weight: 700; flex: 0 0 auto;
    }
    table.grid td:last-child { text-align: left; }
    table.grid td.td-actions { display: block; padding-top: 12px; }
    table.grid td.td-actions::before { display: none; }
    table.grid td.empty { display: block; }
    /* audit rows: the detail disclosure gets its own full-width block */
    table.grid td.td-detail { display: block; }
    table.grid td.td-detail::before { display: none; }
    .audit-filters .form-grid { grid-template-columns: 1fr; }
    .audit-pre { max-width: none; }
    /* 3-button rows (user list) must wrap whole buttons, never break labels */
  .row-actions { flex-wrap: wrap; }
  .row-actions .btn { flex: 1; justify-content: center; min-height: 44px;
                      white-space: nowrap; }

    .form-card { padding: 22px 18px; }
    .form-actions { flex-direction: column; }
    .form-actions .btn { width: 100%; min-height: 46px; }

    .toast-wrap {
      top: calc(64px + env(safe-area-inset-top));
      left: max(14px, env(safe-area-inset-left));
      right: max(14px, env(safe-area-inset-right));
    }
    .toast { min-width: 0; }

    .modal-card { width: calc(100% - 28px); padding: 22px 20px; }
  }

  @media (max-width: 480px) {
    .page-head h1 { font-size: 1.45rem; }
  }

  /* !important wins over every normal declaration regardless of layer */
  @media (prefers-reduced-motion: reduce) {
    * {
      animation-duration: .001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .001ms !important;
    }
    /* smooth in-page scrolling is motion too */
    html { scroll-behavior: auto !important; }
  }
}
