/*
Dark "computer application panel" design system -- app-wide theme for CASP's admin/teacher/login
pages (student exam-taking pages, and anything meant to be printed on paper, stay on the light
zorin-theme.css / their own dedicated print CSS; see the per-page notes in each converted view).

Self-hosted only: Inter is vendored as a single variable-weight woff2 (assets/fonts/inter/), the
icon set is vendored SVG strings (assets/js/icons.js), Chart.js is the existing vendored UMD build
(assets/chart/chart.umd.min.js) -- zero CDN dependencies, works fully offline/LAN.

Base/surface colors (2026-08-02 revision, per live user feedback that the original navy-black
read too cold): a GitHub-Dark-inspired palette -- #0D1117/#161B22 -- chosen for being a
widely-used, well-tested-for-comfort dark pair rather than a from-scratch guess.

Contrast re-verified by hand (WCAG relative-luminance formula) after that change -- ratios
against both --bg-base and --surface:
  --text-primary   (#FFFFFF) : 18.9:1 / 17.3:1
  --text-secondary (#C2CCE0) : 11.7:1 / 10.7:1
  --text-muted     (#929EB5) : 7.0:1  / 6.4:1
  --accent-blue     as text  : 8.9:1  / 8.1:1
  --accent-red      as text  : 6.0:1  / 5.5:1
All five clear the 4.5:1 normal-text AA floor on BOTH surfaces now -- the darker pair actually
fixed the one previous shortfall (accent-red on --surface was 4.36:1 under the old, lighter
surface color; it's 5.5:1 now). Still, treat --accent-red as a "large/bold/pill" color by
convention (status pills use solid WHITE bold text on a ~30%-alpha tint of the status color, not
colored text on a faint tint) rather than routinely setting small body text to it directly.
*/

@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('fonts/inter/Inter-Variable-latin.woff2') format('woff2');
}

:root {
    --bg-base: #0D1117;
    --surface: #161B22;
    --panel: rgba(20, 25, 32, 0.92);
    --surface-2: rgba(24, 30, 38, 0.7);
    --border: rgba(255, 255, 255, 0.11);
    --border-strong: rgba(255, 255, 255, 0.22);
    --text-primary: #FFFFFF;
    --text-secondary: #C2CCE0;
    --text-muted: #929EB5;
    --accent-blue: #22C3DB;
    --accent-hover: #4CDCF0;
    --accent-green: #3DDC84;
    --accent-yellow: #FFD60A;
    --accent-orange: #FF9F0A;
    --accent-red: #FF5449;
    /*
    RGB-triplet twins of the 5 colors above, used everywhere a tinted/translucent background is
    needed (rgba(var(--accent-blue-rgb), 0.14) etc.) -- this is what actually makes those tints
    theme-aware. Every theme preset (see theme_palettes.php) sets both the hex AND the matching
    -rgb triplet for each of these; a preset that forgets to update one when changing the other
    will show a mismatched tint, so they're always set as a pair.
    */
    --text-muted-rgb: 146, 158, 181;
    --accent-blue-rgb: 34, 195, 219;
    --accent-green-rgb: 61, 220, 132;
    --accent-yellow-rgb: 255, 214, 10;
    --accent-orange-rgb: 255, 159, 10;
    --accent-red-rgb: 255, 84, 73;
    --shadow-color: 0, 0, 0;
    --sheen-color: 255, 255, 255;
    /* Text color for a SOLID accent-filled surface (the primary button) and for the low-alpha
       danger-tint button -- these can't just be var(--text-primary)/hardcoded, because whether
       dark or light text reads better depends on how bright the theme's own accent-blue/red is,
       which varies by theme (a light theme's accent is deliberately darker/more saturated for
       its own text-on-surface contrast, which flips which button-text color wins). Verified per
       theme in theme_palettes.php; dark default here matches every existing dark theme's bright
       accent. */
    --btn-primary-fg: #06222b;
    --btn-danger-fg: #ffd9d6;
    /* Optional centered radial-gradient wash layered under --bg-base (see body.dapp below) --
       'none' for every theme except the two "overlay" presets (Deep Navy, Azure Sky), which set
       this to a real radial-gradient() value in theme_palettes.php. Kept as a separate layer
       rather than baking the gradient into --bg-base itself so most themes stay a flat, cheap
       solid color. */
    --bg-overlay: none;
    /* 2026-08-03: semantic aliases for the Sidebar/Taskbar/Card/Modal/Workspace consumers below
       -- point at the tokens above by default, so all pre-2026-08-03 themes render identically
       unchanged (theme_palettes.php's css_override() only emits these when a theme explicitly
       sets them, which none of the original 15 do). A theme whose card surface should differ
       from its sidebar/taskbar surface (see theme_palettes.php's 2026-08-03 additions) overrides
       sidebar-bg/card-bg/taskbar-bg independently instead of relying on one shared --panel. */
    --bg-main: var(--bg-base);
    --sidebar-bg: var(--panel);
    --sidebar-text: var(--text-secondary);
    --sidebar-active-bg: rgba(var(--accent-blue-rgb), 0.12);
    --sidebar-active-text: var(--text-primary);
    --card-bg: var(--panel);
    --card-border: var(--border);
    --taskbar-bg: var(--panel);
    --taskbar-text: var(--text-secondary);
    --accent-primary: var(--accent-blue);
    --radius-lg: 12px;
    --radius-md: 10px;
    --radius-sm: 8px;
    --shadow-soft: 0 8px 24px rgba(var(--shadow-color), 0.45);
    --shadow-lift: 0 14px 34px rgba(var(--shadow-color), 0.5);
    --blur: 16px;
    --nav-rail-width: 216px;
    --taskbar-height: 48px;
}

* { box-sizing: border-box; }

/* Global fallback for every icons.js-injected SVG (see assets/js/icons.js -- it injects a bare
   <svg> with no width/height attribute of its own, relying entirely on CSS to size it). Several
   specific contexts already size their own icons more precisely (.dbtn svg, .nav-icon svg,
   .status-pill svg, etc., further down this file) -- those win via normal cascade order (same
   specificity, declared later). Anywhere else with no such rule, an unsized inline SVG falls back
   to the browser's intrinsic default (300x150px), which is what "icon renders huge/wrong size"
   bugs were -- this rule is the safety net so that never happens again, anywhere in the app. */
[data-icon] svg { width: 18px; height: 18px; }

html, body {
    height: 100%;
}

body.dapp {
    margin: 0;
    background: var(--bg-overlay) var(--bg-main);
    color: var(--text-primary);
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
    font-size: 16px;
    line-height: 1.45;
    -webkit-font-smoothing: antialiased;
}

/*
Deliberately plain `a`/`a:hover`, not `body.dapp a`/`body.dapp a:hover` -- this stylesheet is only
ever loaded on already-.dapp-scoped pages, so that prefix added no real scoping benefit, just
needless specificity ((0,2,2) instead of (0,1,1)) that beat every component-level `:hover`
override (e.g. .tile:hover, .nav-rail-item:hover both explicitly set text-decoration:none) and
silently produced an underline anywhere a styled component happened to be an <a> element. Kept
deliberately LOW specificity here on purpose so component classes always win without a fight.
*/
a { color: var(--accent-blue); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }
body.dapp small, body.dapp .small { font-size: 14px !important; }

/* ============================= App shell layout ============================= */

.app-body {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
}

.app-body-row {
    display: flex;
    flex: 1;
    min-height: 0;
}

/*
Labeled sidebar (icon + name), matching the width to what it actually needs to show instead of
the reverse. Two real bugs lived here before, both from the same root cause (a rail sized too
narrow for its own content) plus one flexbox gotcha, all fixed now:
  1. "CASP" and every nav label got clipped -- the rail was 76px wide while trying to show text.
  2. The follow-up "fix" went icon-only permanently to dodge that, which then read as the system's
     name/branding having vanished -- not what was wanted; the rail is a real labeled sidebar again.
  3. The icon list could scroll/overflow unpredictably: .nav-rail-items had `overflow-y: auto` but
     no `flex` sizing or `min-height: 0`, so the flexbox default `min-height: auto` refused to let
     it shrink to the space actually available, and the whole column could spill past the rail's
     own box instead of scrolling cleanly inside it. Fixed below with `flex: 1 1 auto; min-height:
     0` -- this is THE standard fix for "a flex child's overflow: auto doesn't work."
*/
.nav-rail {
    width: var(--nav-rail-width);
    flex-shrink: 0;
    background: var(--sidebar-bg);
    backdrop-filter: blur(var(--blur));
    -webkit-backdrop-filter: blur(var(--blur));
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: 16px 0;
}

.nav-rail-logo {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 2px 18px 16px;
    margin: 0 6px 10px;
    border-bottom: 1px solid var(--border);
    font-weight: 700;
    font-size: 16px;
    color: var(--text-primary);
    flex-shrink: 0;
}
.nav-rail-logo svg, .nav-rail-logo img { flex-shrink: 0; width: 24px; height: 24px; color: var(--accent-blue); object-fit: contain; }
.nav-rail-logo .label { white-space: nowrap; }

.nav-rail-items {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 0 10px;
}

.nav-rail-section-title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-muted);
    padding: 14px 10px 4px;
}
.nav-rail-section-title:first-child { padding-top: 4px; }

.nav-rail-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    color: var(--sidebar-text);
    font-size: 14px;
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    border: none;
    background: transparent;
    flex-shrink: 0;
    transition: background .15s ease, color .15s ease, transform .12s ease;
}
.nav-rail-item .label { overflow: hidden; text-overflow: ellipsis; }
.nav-rail-item:hover { background: var(--surface-2); color: var(--text-primary); text-decoration: none; transform: translateX(2px); }
.nav-rail-item:active { transform: scale(0.98); }
.nav-rail-item.active {
    background: var(--sidebar-active-bg);
    color: var(--sidebar-active-text);
}

/*
Colored icon "chip" per nav item -- at-rest color instead of flat gray-until-hover, reusing the
exact same color families the dashboard's own tile icons already use (teal/warning/danger/default
blue) so the two stay visually consistent. Answers "the dashboard/sidebar is too dull, no
animation" together with the hover lift below: a small springy scale+tilt on hover/active gives
the icon itself some life, not just the row background.
*/
.nav-icon {
    width: 28px; height: 28px; flex-shrink: 0;
    border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(var(--accent-blue-rgb), 0.14); color: var(--accent-blue);
    transition: transform .25s cubic-bezier(.34,1.56,.64,1), background .15s ease, color .15s ease;
}
.nav-icon svg { width: 16px; height: 16px; }
.nav-icon.teal { background: rgba(var(--accent-green-rgb), 0.14); color: var(--accent-green); }
.nav-icon.warning { background: rgba(var(--accent-yellow-rgb), 0.14); color: var(--accent-yellow); }
.nav-icon.danger { background: rgba(var(--accent-red-rgb), 0.14); color: var(--accent-red); }

.nav-rail-item:hover .nav-icon { transform: scale(1.14) rotate(-6deg); }
.nav-rail-item:active .nav-icon { transform: scale(0.92); }
/* color: var(--btn-primary-fg), not a hardcoded #06222b -- that hardcoded value was verified
   only against the DARK themes' bright accent-blue and silently carried over to every LIGHT
   theme too, where accent-blue is deliberately darker/more saturated (e.g. Classic Windows'
   navy #000080): near-black text on navy measured 1.03:1, effectively illegible. btn-primary-fg
   is already computed per-theme specifically for text-on-solid-accent-blue (see its definition
   above), fixing this for all 6 light themes at 5.9-16.0:1 with zero change for the 9 dark ones. */
.nav-rail-item.active .nav-icon { background: var(--accent-blue); color: var(--btn-primary-fg); box-shadow: 0 0 0 3px rgba(var(--accent-blue-rgb),0.18); }
/* These 3 status-color variants stay hardcoded (unlike the default blue one above) -- checked
   before assuming btn-primary-fg would fix them too, and it doesn't: it's calibrated per-theme
   against accent-blue specifically, and reusing it against accent-green/yellow/red gives WRONG
   answers for a theme like Warm Sand & Terracotta that overrides btn-primary-fg to dark text for
   its own amber accent (dark-on-status-color drops to ~3:1 there). A correct fix needs its own
   dedicated per-status fg variable, verified per theme -- not done here; every theme still
   inherits DARK_BASE's or LIGHT_STATUS's shared green/yellow/orange/red (only accent-blue is
   themed per-theme for the accent-only presets), so this was already the exact same fixed
   hardcoded value across all 15 pre-2026-08-03 themes, not a new regression from this pass. */
.nav-rail-item.active .nav-icon.teal { background: var(--accent-green); color: #06281a; box-shadow: 0 0 0 3px rgba(var(--accent-green-rgb),0.18); }
.nav-rail-item.active .nav-icon.warning { background: var(--accent-yellow); color: #2b2200; box-shadow: 0 0 0 3px rgba(var(--accent-yellow-rgb),0.18); }
.nav-rail-item.active .nav-icon.danger { background: var(--accent-red); color: #2b0a08; box-shadow: 0 0 0 3px rgba(var(--accent-red-rgb),0.18); }
@media (prefers-reduced-motion: reduce) {
    .nav-icon, .nav-rail-item { transition: none; }
}

.nav-rail-bottom {
    padding: 10px 10px 0;
    margin: 8px 6px 0;
    border-top: 1px solid var(--border);
    flex-shrink: 0;
}

.app-main {
    flex: 1;
    min-width: 0;
    overflow-y: auto;
    padding: 20px 28px 28px;
}

/* ============================= Taskbar (bottom status bar) ============================= */

.taskbar {
    height: var(--taskbar-height);
    flex-shrink: 0;
    background: var(--taskbar-bg);
    backdrop-filter: blur(var(--blur));
    -webkit-backdrop-filter: blur(var(--blur));
    border-top: 1px solid var(--border);
    display: flex;
    align-items: center;
    padding: 0 14px;
    gap: 14px;
    font-size: 14px;
    color: var(--taskbar-text);
    position: relative;
    z-index: 20;
}

.taskbar-context { display: flex; align-items: center; gap: 8px; min-width: 0; }
.taskbar-spacer { flex: 1; }
.taskbar-tray { display: flex; align-items: center; gap: 6px; }

.taskbar-select {
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text-primary);
    border-radius: var(--radius-sm);
    padding: 6px 10px;
    font-size: 14px;
    font-family: inherit;
}
.taskbar-select:focus { outline: 2px solid var(--accent-blue); outline-offset: 1px; }

.tray-item {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    color: var(--taskbar-text);
    cursor: pointer;
    position: relative;
    border: none;
    background: transparent;
}
.tray-item:hover { background: var(--surface-2); color: var(--text-primary); }
.tray-item svg { width: 18px; height: 18px; }

.tray-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--accent-green);
    flex-shrink: 0;
    box-shadow: 0 0 0 0 rgba(var(--accent-green-rgb), 0.5);
    animation: dot-breathe 2.4s ease-in-out infinite;
}
.tray-dot.offline { background: var(--accent-red); box-shadow: 0 0 0 0 rgba(var(--accent-red-rgb), 0.5); animation: dot-breathe-red 2.4s ease-in-out infinite; }
@keyframes dot-breathe {
    0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-green-rgb), 0.45); }
    50% { box-shadow: 0 0 0 4px rgba(var(--accent-green-rgb), 0); }
}
@keyframes dot-breathe-red {
    0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-red-rgb), 0.45); }
    50% { box-shadow: 0 0 0 4px rgba(var(--accent-red-rgb), 0); }
}
@media (prefers-reduced-motion: reduce) {
    .tray-dot { animation: none; }
}
.tray-dot.unknown { background: var(--text-muted); }

/* color: #fff is intentionally fixed (not per-theme) -- verified against both accent-reds this
   app ships (DARK_BASE's #FF5449 and LIGHT_STATUS's #DC2626, the only two since no theme
   currently overrides accent-red on its own) at 3.2:1/4.8:1; there's no dedicated "solid danger
   fill" fg variable yet (btn-danger-fg is calibrated for a 22%-alpha red TINT, a different
   context -- see .dbtn.danger below), so fixing the dark-mode case properly needs a new
   verified-per-theme variable, not done in this pass. */
.tray-badge {
    position: absolute;
    top: 2px; right: 2px;
    min-width: 15px; height: 15px;
    border-radius: 8px;
    background: var(--accent-red);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    padding: 0 3px;
    line-height: 1;
}

.tray-clock { font-variant-numeric: tabular-nums; white-space: nowrap; }

.tray-profile {
    display: flex; align-items: center; gap: 8px;
    padding: 4px 10px 4px 4px;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.tray-profile:hover { background: var(--surface-2); }
.tray-avatar {
    width: 26px; height: 26px; border-radius: 50%;
    background: var(--accent-blue);
    color: var(--btn-primary-fg);
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 13px; flex-shrink: 0;
}

/* Dropdowns anchored to a tray item -- notification list, profile menu. */
.tray-dropdown {
    position: absolute;
    bottom: calc(100% + 10px);
    right: 0;
    min-width: 260px;
    max-height: 360px;
    overflow-y: auto;
    display: none;
    z-index: 40;
}
.tray-dropdown.open { display: block; }
.tray-dropdown-header {
    padding: 10px 14px;
    font-weight: 600;
    font-size: 14px;
    border-bottom: 1px solid var(--border);
}
.tray-dropdown-item {
    padding: 10px 14px;
    font-size: 14px;
    color: var(--text-secondary);
    border-bottom: 1px solid var(--border);
    display: block;
}
.tray-dropdown-item:last-child { border-bottom: none; }
.tray-dropdown-item:hover { background: var(--surface-2); color: var(--text-primary); text-decoration: none; }
.tray-dropdown-empty { padding: 18px 14px; text-align: center; color: var(--text-muted); font-size: 14px; }

/* ============================= Views (hash-routed sections within .app-main) ============================= */

.view { display: none; }
.view.active { display: block; }

.view-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 20px;
}
.view-header h1 { font-size: 22px; font-weight: 700; margin: 0; }
.view-header .view-subtitle { color: var(--text-muted); font-size: 14px; margin-top: 2px; }

/* ============================= Core surface: glass-card ============================= */

.glass-card {
    background: var(--card-bg);
    backdrop-filter: blur(var(--blur));
    -webkit-backdrop-filter: blur(var(--blur));
    border: 1px solid var(--card-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-soft);
    animation: card-in .35s cubic-bezier(.22,1,.36,1) both;
}
.glass-card.solid { background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
.glass-card:hover { box-shadow: var(--shadow-lift); }

/* Entrance animation -- every glass-card fades/slides in on page load instead of just appearing;
   staggered per sibling (up to 10) inside a shared grid/flex parent so a row of cards ripples in
   rather than popping all at once. Answers "no animation at all." */
@keyframes card-in {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}
.tile-grid > *:nth-child(1), .grid > *:nth-child(1) { animation-delay: .00s; }
.tile-grid > *:nth-child(2), .grid > *:nth-child(2) { animation-delay: .04s; }
.tile-grid > *:nth-child(3), .grid > *:nth-child(3) { animation-delay: .08s; }
.tile-grid > *:nth-child(4), .grid > *:nth-child(4) { animation-delay: .12s; }
.tile-grid > *:nth-child(5), .grid > *:nth-child(5) { animation-delay: .16s; }
.tile-grid > *:nth-child(6), .grid > *:nth-child(6) { animation-delay: .20s; }
.tile-grid > *:nth-child(7), .grid > *:nth-child(7) { animation-delay: .24s; }
.tile-grid > *:nth-child(8), .grid > *:nth-child(8) { animation-delay: .28s; }
.tile-grid > *:nth-child(9), .grid > *:nth-child(9) { animation-delay: .32s; }
.tile-grid > *:nth-child(n+10), .grid > *:nth-child(n+10) { animation-delay: .36s; }
@media (prefers-reduced-motion: reduce) {
    .glass-card { animation: none; }
}
.glass-card .card-header {
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: 15px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}
.glass-card .card-body { padding: 18px; }

.stat-card { padding: 18px; display: flex; flex-direction: column; gap: 6px; }
.stat-card .stat-label { font-size: 14px; color: var(--text-muted); display: flex; align-items: center; gap: 6px; }
.stat-card .stat-value { font-size: 30px; font-weight: 700; line-height: 1.1; }
.stat-card .stat-trend { font-size: 14px; color: var(--text-secondary); }

.grid { display: grid; gap: 18px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

/* ============================= Status pill ============================= */
/* --text-primary (bold) on a ~30%-alpha status tint -- white on dark themes, dark on light
   themes, since --text-primary itself flips per theme. See the contrast note at the top of this
   file for why this combination (rather than colored text on a faint tint) is what clears AA. */

.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    font-size: 14px;
    font-weight: 700;
    color: var(--text-primary);
    white-space: nowrap;
}
.status-pill svg { width: 15px; height: 15px; }
.status-pill.optimal, .status-pill.online { background: rgba(var(--accent-green-rgb), 0.32); }
.status-pill.warning { background: rgba(var(--accent-yellow-rgb), 0.34); }
.status-pill.critical { background: rgba(var(--accent-red-rgb), 0.34); }
.status-pill.offline, .status-pill.unknown { background: rgba(var(--text-muted-rgb), 0.30); }

.status-pill.warning svg { animation: pill-bounce 1.6s ease-in-out infinite; }
.status-pill.critical svg { animation: pill-pulse-glow 1.4s ease-in-out infinite; }

@keyframes pill-pulse-glow {
    0%, 100% { filter: drop-shadow(0 0 0 rgba(var(--accent-red-rgb), 0)); transform: scale(1); }
    50% { filter: drop-shadow(0 0 4px rgba(var(--accent-red-rgb), 0.9)); transform: scale(1.12); }
}
@keyframes pill-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-2px); }
}
@media (prefers-reduced-motion: reduce) {
    .status-pill.warning svg, .status-pill.critical svg { animation: none; }
}

/* ============================= Gauges ============================= */

.gauge-wrap { position: relative; display: inline-flex; align-items: center; justify-content: center; }
.gauge-wrap svg circle.gauge-track { stroke: var(--surface-2); }
.gauge-wrap svg circle.gauge-value { stroke: var(--accent-blue); stroke-linecap: round; transition: stroke-dashoffset .4s ease; }
.gauge-wrap.optimal svg circle.gauge-value { stroke: var(--accent-green); }
.gauge-wrap.warning svg circle.gauge-value { stroke: var(--accent-yellow); }
.gauge-wrap.critical svg circle.gauge-value { stroke: var(--accent-red); }
.gauge-label {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
}
.gauge-label .gauge-number { font-size: 20px; font-weight: 700; }
.gauge-label .gauge-unit { font-size: 14px; color: var(--text-muted); }

/* ============================= Buttons ============================= */

/* Pill-shaped, soft-glossy (macOS/Zorin style): a faint top-highlight gradient for a sheen,
   a gentle lift on hover, and a satisfying inward press on click rather than a flat rectangle. */
.dbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 20px;
    border-radius: 999px;
    font-size: 16px;
    font-weight: 600;
    font-family: inherit;
    border: 1px solid var(--border-strong);
    background: linear-gradient(180deg, rgba(var(--sheen-color),0.07), rgba(var(--sheen-color),0) 55%), var(--surface-2);
    color: var(--text-primary);
    cursor: pointer;
    white-space: nowrap;
    box-shadow: 0 1px 2px rgba(var(--shadow-color),0.35);
    transition: transform .16s cubic-bezier(.34,1.56,.64,1), filter .12s ease, box-shadow .16s ease;
}
.dbtn svg { width: 18px; height: 18px; }
.dbtn:hover { filter: brightness(1.15); transform: translateY(-1px); box-shadow: var(--shadow-soft); text-decoration: none; color: var(--text-primary); }
.dbtn:active { transform: scale(0.96); box-shadow: inset 0 1px 3px rgba(var(--shadow-color),0.35); }
.dbtn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; filter: none; box-shadow: none; }
.dbtn.primary { background: linear-gradient(180deg, rgba(var(--sheen-color),0.22), rgba(var(--sheen-color),0) 55%), var(--accent-blue); border-color: var(--accent-blue); color: var(--btn-primary-fg); }
.dbtn.primary:hover { background: linear-gradient(180deg, rgba(var(--sheen-color),0.22), rgba(var(--sheen-color),0) 55%), var(--accent-hover); border-color: var(--accent-hover); }
.dbtn.danger { background: linear-gradient(180deg, rgba(var(--sheen-color),0.1), rgba(var(--sheen-color),0) 55%), rgba(var(--accent-red-rgb), 0.22); border-color: var(--accent-red); color: var(--btn-danger-fg); }
.dbtn.danger:hover { background: linear-gradient(180deg, rgba(var(--sheen-color),0.1), rgba(var(--sheen-color),0) 55%), rgba(var(--accent-red-rgb), 0.34); }
.dbtn.sm { padding: 7px 16px; font-size: 14px; }

/* ============================= Form controls ============================= */

.dfield label { display: block; font-size: 14px; color: var(--text-secondary); margin-bottom: 6px; font-weight: 500; }
.dinput, .dselect, .dtextarea {
    width: 100%;
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text-primary);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-size: 16px;
    font-family: inherit;
}
.dinput:focus, .dselect:focus, .dtextarea:focus { outline: 2px solid var(--accent-blue); outline-offset: 1px; border-color: var(--accent-blue); }
.dselect option { background: var(--surface); color: var(--text-primary); }

/* Native title tooltips look broken on a dark custom UI -- use data-tooltip + ::after instead. */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    background: #05070c;
    color: var(--text-primary);
    font-size: 14px;
    font-weight: 500;
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-strong);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
    z-index: 50;
}
[data-tooltip]:hover::after { opacity: 1; }

/* ============================= Modals ============================= */

.modal-overlay {
    position: fixed;
    inset: 0;
    /* rgba(var(--shadow-color), 0.6): a dimming scrim deliberately stays dark regardless of
       theme (same convention as light-mode OS/app modal scrims generally) -- was a literal
       rgba(4,6,10,0.6) before; --shadow-color is 0,0,0 for every theme today (dark AND light
       alike), so this is a zero-visual-change swap to the semantically-correct token, not a
       behavior change, and it stays theme-independent unless a future theme deliberately sets
       --shadow-color to something else. */
    background: rgba(var(--shadow-color), 0.6);
    backdrop-filter: blur(var(--blur));
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 100;
    padding: 20px;
}
.modal-overlay.open { display: flex; }
.modal-sheet {
    width: 100%;
    max-width: 560px;
    max-height: 86vh;
    overflow-y: auto;
    background: var(--card-bg);
}
/* Size modifiers for content-heavy modals (data tables, folder browsers, embedded iframes) --
   the base 560px is a comfortable default for simple forms only. */
.modal-sheet.lg { max-width: 760px; }
.modal-sheet.xl { max-width: 1140px; }
.modal-sheet.fullscreen { max-width: 95vw; width: 95vw; height: 90vh; max-height: 90vh; display: flex; flex-direction: column; }
.modal-sheet.fullscreen .modal-body { flex: 1 1 auto; overflow: hidden; }
.modal-sheet-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--card-border);
}
.modal-sheet-header h2 { font-size: 17px; font-weight: 700; margin: 0; }
.modal-close-btn {
    width: 32px; height: 32px;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    background: transparent; border: none; cursor: pointer;
}
.modal-close-btn:hover { background: var(--surface-2); color: var(--text-primary); }
.modal-body { padding: 20px; }
.modal-footer { padding: 14px 20px; border-top: 1px solid var(--card-border); display: flex; justify-content: flex-end; gap: 10px; }

.modal-tabs { display: flex; gap: 4px; padding: 0 20px; border-bottom: 1px solid var(--border); }
.modal-tab {
    padding: 10px 14px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-muted);
    border: none;
    background: transparent;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    margin-bottom: -1px;
}
.modal-tab:hover { color: var(--text-primary); }
.modal-tab.active { color: var(--accent-blue); border-bottom-color: var(--accent-blue); }
.modal-pane { display: none; }
.modal-pane.active { display: block; }

/* ============================= Floating persistent alert badge ============================= */

.floating-alert-badge {
    position: fixed;
    bottom: calc(var(--taskbar-height) + 18px);
    right: 22px;
    z-index: 90;
    display: none;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    border-radius: var(--radius-lg);
    /* Fixed, theme-independent red (not var(--accent-red)) -- this badge is meant to read as
       urgent regardless of the ambient palette, and several themes' accent-red is bright enough
       that white text on it only reaches ~3.2:1 (found during the 2026-08-02 multi-theme
       contrast pass -- verified #B91C1C gives white text 6.47:1 instead). */
    background: rgba(185, 28, 28, 0.94);
    color: #fff;
    font-weight: 700;
    font-size: 15px;
    box-shadow: var(--shadow-lift);
    cursor: pointer;
    border: 1px solid rgba(var(--sheen-color), 0.3);
}
.floating-alert-badge.visible { display: flex; }
.floating-alert-badge svg { width: 20px; height: 20px; animation: pill-pulse-glow 1.2s ease-in-out infinite; }

/* ============================= Table (dark) ============================= */

.dtable { width: 100%; border-collapse: collapse; font-size: 15px; }
.dtable th {
    text-align: left;
    font-size: 14px;
    color: var(--text-muted);
    font-weight: 600;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-strong);
    white-space: nowrap;
}
.dtable td { padding: 12px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.dtable tbody tr:hover { background: var(--surface-2); }

/* ============================= Login / pre-auth full-viewport states ============================= */

.auth-screen, .app-shell {
    position: fixed;
    inset: 0;
    display: none;
}
.auth-screen.visible, .app-shell.visible { display: flex; }

.auth-screen {
    align-items: center;
    justify-content: center;
    background:
        radial-gradient(circle at 20% 15%, rgba(var(--accent-blue-rgb), 0.10), transparent 40%),
        radial-gradient(circle at 85% 85%, rgba(var(--accent-green-rgb), 0.08), transparent 45%),
        var(--bg-base);
    padding: 20px;
}
.auth-card { width: 100%; max-width: 400px; padding: 34px 32px; }
.auth-card .auth-logo { display: flex; align-items: center; justify-content: center; margin-bottom: 10px; }
.auth-card .auth-logo svg, .auth-card .auth-logo img { width: 84px; height: 84px; color: var(--accent-blue); object-fit: contain; }
.auth-card .auth-copyright { text-align: center; font-size: 11px; line-height: 1.5; color: var(--text-muted); margin: 0 0 22px; }
.auth-card h1 { font-size: 20px; text-align: center; margin: 0 0 22px; font-weight: 700; }
.auth-card .dfield { margin-bottom: 16px; }
.auth-card .dbtn { width: 100%; }
.auth-links { text-align: center; margin-top: 16px; font-size: 14px; color: var(--text-muted); }

/* ============================= Utility ============================= */

.text-muted-d { color: var(--text-muted); }
.text-secondary-d { color: var(--text-secondary); }
.text-critical-d { color: var(--accent-red); font-weight: 700; } /* bold-only use, see contrast note */
.d-flex { display: flex; }
.gap-2 { gap: 8px; }
.mb-2 { margin-bottom: 8px; }
.mb-3 { margin-bottom: 14px; }
.text-center { text-align: center; }
.flex-wrap { flex-wrap: wrap; }
.align-items-center { align-items: center; }
.justify-content-between { justify-content: space-between; }
.justify-content-center { justify-content: center; }
.d-none { display: none; }
.d-inline { display: inline; }

/* ============================= Print override ============================= */
/* Translucent dark glass is unreadable/wasteful on paper -- force plain black-on-white and hide
   every piece of persistent app chrome when printing any page that uses this theme. Pages meant
   to BE printed (reports/print.php, print_questionnaire) have their own dedicated print CSS and
   never load this stylesheet at all. */

@media print {
    .nav-rail, .taskbar, .modal-overlay, .floating-alert-badge, .tray-dropdown, .no-print { display: none !important; }
    body.dapp, .app-main { background: #fff !important; color: #000 !important; }
    .glass-card {
        background: #fff !important;
        color: #000 !important;
        border: 1px solid #999 !important;
        box-shadow: none !important;
        backdrop-filter: none !important;
    }
    .status-pill { background: #eee !important; color: #000 !important; }
}
