/* =============================================================================
   layout.css — CINTI OS Admin Layout
   Top bar, dock navigation, desktop area, avatar panel, taskbar,
   page containers, panel spacing, dashboard grid
   ============================================================================= */

  /* === TOP BAR === */
  .topbar {
    position: fixed; top: 0; left: 0; right: 0; height: var(--topbar-h); z-index: 200;
    background: rgba(8, 12, 26, 0.92); backdrop-filter: blur(24px);
    border-bottom: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.1);
    display: flex; align-items: center; justify-content: space-between;
    padding: 0 16px; font-size: 12px;
    box-shadow: 0 1px 12px rgba(0,0,0,0.3);
  }
  .topbar-left { display: flex; align-items: center; gap: 12px; }
  .topbar-logo { font-size: 14px; font-weight: 700; color: var(--accent); letter-spacing: 0.5px; }
  .topbar-logo span { color: var(--bright); }
  .topbar-search {
    background: rgba(14,22,42,0.6); border: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.1);
    border-radius: 8px; padding: 4px 12px; color: var(--text); font-family: inherit;
    font-size: 11px; width: 180px; outline: none; transition: border-color 0.2s;
  }
  .topbar-search:focus { border-color: var(--accent); }
  .topbar-search::placeholder { color: var(--dim); }
  /* Emmanuel 2026-05-13: theme/help gap was 8px (inside the slot) while
     help→bell + bell→clock were 16px (parent gap). All buttons in the
     topbar now sit at the same 8px gap; the clock gets a small left
     padding so it visually separates from the bell without a different gap. */
  .topbar-right { display: flex; align-items: center; gap: 8px; }
  .topbar-right .topbar-clock { margin-left: 6px; }
  .topbar-conn { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--dim); }
  .topbar-conn .dot { width: 6px; height: 6px; border-radius: 50%; }
  /* Emmanuel 2026-05-13: bell-icon container now matches the framed
     square shape of the help-feedback button + theme toggle in the
     same topbar slot. Same 32x32 with border-radius:8px + cyan-tinted
     border, transparent fill, subtle hover wash. */
  .topbar-bell {
    cursor: pointer;
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 8px;
    border: 1px solid var(--cinti-border, rgba(var(--accent-rgb, 38, 189, 214), 0.18));
    background: transparent;
    color: var(--cinti-text-secondary, #cfd6e2);
    opacity: 1;
    transition: background 0.18s, border-color 0.18s, opacity 0.2s;
    position: relative;
  }
  .topbar-bell:hover {
    background: var(--cinti-bg-hover, rgba(var(--accent-rgb, 38, 189, 214), 0.08));
    border-color: var(--accent);
  }
  .topbar-clock { font-size: 11px; color: var(--dim); font-weight: 500; font-variant-numeric: tabular-nums; }

  /* === LEFT DOCK === */
  /* Emmanuel 2026-05-12 — 26 dock icons + chevron + separators overflowed
     the fixed height on shorter viewports, cutting the bottom sign-out
     button in half. Added overflow-y: auto so the dock scrolls when it
     can't fit. Thin transparent scrollbar so the chrome doesn't intrude.
     padding-bottom bumped slightly so the last icon never sits right
     against the edge. */
  .dock-left {
    position: fixed; top: var(--topbar-h); left: 0; bottom: var(--taskbar-h);
    width: var(--dock-w); z-index: 100;
    background: var(--bg-dock);
    border-right: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.08);
    display: flex; flex-direction: column; align-items: center;
    padding: 12px 0 16px; gap: 4px;
    box-shadow: 2px 0 20px rgba(0,0,0,0.3);
    overflow-y: auto; overflow-x: hidden;
    scrollbar-width: thin; scrollbar-color: rgba(var(--accent-rgb, 38, 189, 214),0.3) transparent;
  }
  .dock-left::-webkit-scrollbar { width: 4px; }
  .dock-left::-webkit-scrollbar-track { background: transparent; }
  .dock-left::-webkit-scrollbar-thumb { background: rgba(var(--accent-rgb, 38, 189, 214),0.3); border-radius: 2px; }
  .dock-left::-webkit-scrollbar-thumb:hover { background: rgba(var(--accent-rgb, 38, 189, 214),0.5); }
  .dock-icon {
    width: 40px; height: 40px; border-radius: 12px;
    display: flex; align-items: center; justify-content: center;
    font-size: 18px; cursor: pointer; position: relative;
    transition: all 0.2s ease; color: var(--dim);
  }
  .dock-icon:hover {
    background: rgba(var(--accent-rgb, 38, 189, 214),0.1); color: var(--text);
    transform: scale(1.1);
  }
  .dock-icon.active {
    background: rgba(var(--accent-rgb, 38, 189, 214),0.15); color: var(--bright);
  }
  .dock-icon.active::after {
    content: ''; position: absolute; left: -8px; top: 50%; transform: translateY(-50%);
    width: 3px; height: 20px; border-radius: 0 3px 3px 0;
    background: var(--accent);
  }
  .dock-icon .tooltip {
    position: absolute; left: calc(100% + 10px); top: 50%; transform: translateY(-50%);
    background: rgba(8,12,26,0.95); border: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.2);
    border-radius: 6px; padding: 4px 10px; font-size: 11px; font-weight: 500;
    color: var(--text); white-space: nowrap; pointer-events: none;
    opacity: 0; transition: opacity 0.15s;
    box-shadow: 0 4px 12px rgba(0,0,0,0.4);
  }
  .dock-icon:hover .tooltip { opacity: 1; }
  /* CW #1921 — divider was so faint (0.1 opacity) it read as a missing
     icon. 3× opacity + a touch more vertical breathing room so it's
     clearly an intentional group break. */
  .dock-separator { width: 28px; height: 1px; background: rgba(var(--accent-rgb, 38, 189, 214),0.3); margin: 10px 0; }

  /* === EXPANDED DOCK MODE === (2026-05-11) */
  /* Operator toggles via the chevron button at the top of the dock.    */
  /* Persisted in localStorage so the choice survives page reloads.     */
  /* CW #2024 — dock width sized to fit the longest label ("Marketplace
     Submissions" measures 145.5px at 12px Inter). Math: 8 + 10 + 18 + 12 +
     145.5 + 10 + 8 = 211.5px. Round up to 224px for kerning headroom + any
     future label up to "_______________________" (~25 chars). If a longer
     label lands later, bump this AND the layout.css cache pin together. */
  body.dock-expanded { --dock-w: 224px; }
  body.dock-expanded .dock-left {
    align-items: stretch;
    padding: 12px 8px;
  }
  /* Top-level dock icons become full-width rows with icon + label */
  body.dock-expanded .dock-icon {
    width: auto; height: 36px; border-radius: 8px;
    justify-content: flex-start; gap: 12px; padding: 0 10px;
  }
  /* Long labels (e.g. "Marketplace Submissions") were shrinking the
     sibling SVG to 0 width via default flex-shrink:1. Pin the SVG so
     it can never give up its width; the dock-w above is sized so the
     full label fits inline. */
  body.dock-expanded .dock-icon svg { flex-shrink: 0; min-width: 18px; }
  body.dock-expanded .dock-icon .tooltip { white-space: nowrap; }
  body.dock-expanded .dock-icon:hover {
    transform: none;
  }
  /* Tooltip becomes the inline label */
  body.dock-expanded .dock-icon .tooltip {
    position: static; transform: none; opacity: 1; pointer-events: auto;
    background: transparent; border: none; padding: 0;
    box-shadow: none; font-size: 12px; color: inherit;
  }
  body.dock-expanded .dock-icon.active::after { left: -8px; }
  body.dock-expanded .dock-separator { width: calc(100% - 16px); margin: 6px 8px; }
  /* The toggle button at the top of the dock */
  .dock-toggle {
    width: 36px; height: 36px; border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    color: var(--dim); cursor: pointer; margin-bottom: 4px;
    transition: all 0.15s ease; gap: 12px;
  }
  .dock-toggle:hover { background: rgba(var(--accent-rgb, 38, 189, 214),0.1); color: var(--text); }
  body.dock-expanded .dock-toggle {
    width: auto; justify-content: flex-start; padding: 0 10px;
    font-size: 11px; height: 36px;
  }
  .dock-toggle .label { display: none; font-size: 11px; }
  body.dock-expanded .dock-toggle .label { display: inline; }

  /* === DOCK SUBMENU === */
  .dock-has-submenu { position: relative; }
  /* Dock submenu — hidden by default, flies out right on parent hover */
  .dock-has-submenu { position: relative; }
  .dock-submenu {
    position: absolute; left: 100%; top: 50%; transform: translateX(4px) translateY(-50%);
    background: rgba(8,12,26,0.96); border: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.18);
    border-radius: 10px; padding: 4px; margin-left: 6px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.5); backdrop-filter: blur(12px);
    opacity: 0; pointer-events: none;
    transition: opacity 0.15s ease, transform 0.15s ease; z-index: 300;
    display: flex; flex-direction: column; gap: 2px;
  }
  .dock-has-submenu:hover .dock-submenu { opacity: 1; pointer-events: auto; transform: translateX(0) translateY(-50%); }
  .dock-submenu-item {
    width: 36px; height: 36px; border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    color: var(--dim); cursor: pointer; transition: all 0.15s;
  }
  .dock-submenu-item:hover { background: rgba(var(--accent-rgb, 38, 189, 214),0.1); color: var(--text); }
  .dock-submenu-item.active { color: var(--accent); background: rgba(var(--accent-rgb, 38, 189, 214),0.08); }

  /* === DESKTOP AREA === */
  .desktop-wrapper {
    position: fixed;
    top: var(--topbar-h);
    left: var(--dock-w);
    right: 0;
    bottom: var(--taskbar-h);
    display: flex;
  }
  .desktop {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
    min-width: 0;
  }
  /* === AVATAR PANEL (right side) === */
  .avatar-panel {
    width: 440px;
    min-width: 280px;
    border-left: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.1);
    background: rgba(10, 14, 28, 0.6);
    backdrop-filter: blur(12px);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
  }
  .avatar-panel.expanded {
    width: 70%;
  }
  .avatar-panel.fullscreen {
    position: fixed;
    top: var(--topbar-h);
    left: var(--dock-w);
    right: 0;
    bottom: var(--taskbar-h);
    width: auto;
    min-width: 0;
    z-index: 50;
    border-left: none;
    background: rgba(10, 14, 28, 0.98);
  }
  .avatar-resize-handle {
    position: absolute;
    left: -3px;
    top: 0;
    bottom: 0;
    width: 6px;
    cursor: col-resize;
    z-index: 10;
    background: transparent;
    transition: background 0.15s;
  }
  .avatar-resize-handle:hover,
  .avatar-resize-handle.dragging {
    background: rgba(var(--accent-rgb, 38, 189, 214), 0.3);
  }
  /* Emmanuel 2026-05-12 — third attempt, definitive. Previous tries
     mixed parent flex gap + per-button auto/negative margins which
     produced visually irregular spacing whenever buttons had different
     intrinsic widths (the speed button's "1x" text vs the icon
     buttons' 16×16 SVGs). New layout: explicit cluster wrapper with
     ONE gap value, ALL buttons fixed at 28×24 (no width minimums, no
     per-button padding), content centered inside the box. Result:
     gaps between buttons are mathematically uniform. */
  .avatar-hdr-cluster {
    display: flex; align-items: center; gap: 4px;
    margin-left: auto;
  }
  .avatar-hdr-btn {
    background: none; border: none; color: var(--dim);
    cursor: pointer; padding: 0; transition: color 0.2s;
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 24px; box-sizing: border-box;
  }
  .avatar-hdr-btn:hover { color: var(--accent); }
  .avatar-mute-btn.muted { color: var(--red); }
  .avatar-mute-btn.muted:hover { color: var(--bad-text,#ff7878); }
  .avatar-cam-btn.active { color: var(--accent); }
  .avatar-cam-btn.preview { color: var(--green); }
  .avatar-settings-btn.active { color: var(--accent); }
  .avatar-v2-btn.active { color: var(--green); }
  /* Speed button is text-labeled (1x/1.25x/1.5x/2x). Font sized to
     fit "1.25x" inside the same 28×24 box as the icon buttons — no
     min-width override, content stays centered. Mono+bold+tight
     tracking keeps it crisp at this size. Emmanuel 2026-05-12 — bumped
     from 11 → 13px to visually match the 16px SVG icons next to it. */
  .avatar-speed-btn {
    font-family: ui-monospace, 'SF Mono', 'JetBrains Mono', monospace;
    font-size: 13px; font-weight: 700; line-height: 1;
    letter-spacing: -0.04em;
  }
  /* Mute icon's SVG geometry only fills ~70% of its 24×24 viewBox
     (speaker trapezoid + sound waves leave room on the right), so at
     16px it visually reads smaller than camera/settings. Bump to 18px
     to match perceived weight. Emmanuel 2026-05-12. */
  .avatar-mute-btn svg { width: 18px; height: 18px; }
  .avatar-speed-btn:not(.baseline) { color: var(--accent); }
  /* CW #1931 (Emmanuel 2026-05-12) — tightened from 52px → ~38px tall
     (27% shorter) so the chat panel gives more vertical room to the
     conversation. Orb pulse + name + status all preserved, just trimmed. */
  .avatar-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    border-bottom: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.1);
    background: rgba(8, 12, 24, 0.5);
  }
  .avatar-header .avatar-orb {
    width: 22px; height: 22px; border-radius: 50%;
    background: radial-gradient(circle at 40% 40%, rgba(var(--accent-rgb, 38, 189, 214),0.6), rgba(var(--accent-rgb, 38, 189, 214),0.15) 55%, transparent 70%);
    box-shadow: 0 0 10px rgba(var(--accent-rgb, 38, 189, 214),0.3);
    animation: orbPulse 3s ease-in-out infinite;
  }
  @keyframes orbPulse {
    0%, 100% { box-shadow: 0 0 10px rgba(var(--accent-rgb, 38, 189, 214),0.3); }
    50% { box-shadow: 0 0 16px rgba(var(--accent-rgb, 38, 189, 214),0.5), 0 0 32px rgba(var(--accent-rgb, 38, 189, 214),0.15); }
  }
  .avatar-header .avatar-name { font-size: 13px; font-weight: 600; color: var(--accent); }
  .avatar-header .avatar-status { font-size: 9px; color: var(--green); }
  .avatar-iframe {
    flex: 1;
    border: none;
    background: #0a0d1a;
  }

  /* === PAGES === */
  .page { display: none; }
  .page.active { display: block; animation: fadeInPanel 0.35s ease; }

  /* === CANONICAL STAT-CARD PATTERN ===
     Use this for ANY "big number + label + optional subline" card in
     CINTI admin (Dashboard quick-stats, AI Spend strip, Diagnostic
     headline, future KPI rows, etc). Emmanuel 2026-05-12 made this the
     project-wide rule — don't invent parallel layouts.

     Markup:
       <div class="panel dash-stat">
         <div class="dash-stat-val [is-good|is-warn|is-bad]">123</div>
         <div class="dash-stat-label">Active Sessions</div>
         <div class="dash-stat-sub">across all platforms</div>
       </div>

     Why grid + fixed row heights instead of flex-centering:
       Flex-centering shifts the value/label up or down when one card's
       subline wraps to 2 lines and another doesn't. Grid with fixed row
       heights (38 / 18 / 30) locks every value, label, and subline to
       the same Y across every card in the row — no drift.

     For semantic color on the value, toggle .is-good / .is-warn /
     .is-bad via classList in JS. Never set style.color inline (it beats
     the class).
  */
  .dash-stat {
    margin: 0; padding: 18px 12px;
    display: grid;
    grid-template-rows: 38px 18px 30px;
    align-items: center; justify-items: center;
    gap: 4px; text-align: center;
  }
  .dash-stat-val {
    font-size: 32px; font-weight: 700;
    color: var(--text); line-height: 1;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .dash-stat-val.is-good { color: var(--green); }
  .dash-stat-val.is-warn { color: var(--gold); }
  .dash-stat-val.is-bad  { color: var(--red); }
  /* Labels are single-line concepts ("Knowledge Base", "Active Sessions").
     nowrap so they never split across two lines and break the row-3
     baseline alignment. Emmanuel 2026-05-12. */
  .dash-stat-label {
    font-size: 12px; font-weight: 600;
    color: var(--text); line-height: 1;
    letter-spacing: 0.02em;
    white-space: nowrap;
  }
  .dash-stat-sub {
    font-size: 10.5px; font-weight: 400;
    color: var(--dim); line-height: 1.3;
    /* Emmanuel 2026-05-13: keep the sub on a single line so longer
       summaries like "0 skill · 0 LLM · top X" don't wrap mid-card.
       Truncate with ellipsis when it really doesn't fit. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }
  /* Click-affordance for System Health quick-stats (Emmanuel 2026-05-13 —
     every drilldown until there is no drilldown left). Override the button
     user-agent defaults but let .panel + .dash-stat do the actual styling.
     Visually identical until hover, then a subtle cyan-edge nudge. */
  button.dash-stat-click {
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: transform 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
  }
  button.dash-stat-click:hover {
    border-color: var(--accent);
    transform: translateY(-1px);
    box-shadow: 0 4px 16px rgba(var(--accent-rgb, 38, 189, 214), 0.15);
  }
  button.dash-stat-click:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* === DASHBOARD HERO ROW — v3 (Emmanuel 2026-05-13 #2) ===
     Look IDENTICAL to the System Health .dash-stat cards down below.
     No differential top border, no separate styling. The only delta is
     a subtle hover lift (because they're clickable) and a cursor.
     State color sits on the value text via .is-good/.is-warn/.is-bad,
     same as System Health. Click opens a drill-down modal — see
     .dash-modal block below. */
  .dash-hero-stat {
    text-decoration: none;
    cursor: pointer;
    transition: transform 0.12s ease, box-shadow 0.2s, border-color 0.2s;
  }
  .dash-hero-stat:hover {
    transform: translateY(-1px);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.30);
    border-color: rgba(var(--accent-rgb, 38, 189, 214), 0.22);
  }

  /* === DRILL-DOWN MODAL (Emmanuel 2026-05-13) ===
     Click any hero tile → this opens with the rich detail for that
     signal (donut for priority breakdown, gauge for MFA, traffic-light
     for adapters, sparkline for response times, etc). Closes on
     backdrop click / Escape / ✕. */
  .dash-modal-backdrop {
    position: fixed; inset: 0; z-index: 9999;
    background: rgba(8, 14, 26, 0.78);
    backdrop-filter: blur(6px);
    display: none; align-items: center; justify-content: center;
    padding: 4vh 4vw;
  }
  .dash-modal-backdrop.open { display: flex; }
  .dash-modal {
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 16px;
    max-width: 780px; width: 100%;
    max-height: 86vh; overflow: hidden;
    display: flex; flex-direction: column;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  }
  .dash-modal-head {
    display: flex; align-items: center; gap: 12px;
    padding: 18px 22px; border-bottom: 1px solid var(--panel-border);
  }
  .dash-modal-title { flex: 1; font-size: 16px; font-weight: 600; color: var(--text); }
  .dash-modal-state {
    font-size: 10px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.6px;
    padding: 4px 10px; border-radius: 12px;
    background: rgba(100, 116, 139, 0.15); color: var(--dim);
  }
  .dash-modal-state.is-good { background: rgba(0, 230, 118, 0.12); color: var(--green); }
  .dash-modal-state.is-warn { background: rgba(255, 183, 77, 0.14); color: var(--gold); }
  .dash-modal-state.is-bad  { background: rgba(255, 64, 96, 0.14); color: var(--red); }
  .dash-modal-close {
    background: transparent; border: 1px solid var(--panel-border);
    color: var(--dim); border-radius: 6px;
    padding: 6px 10px; font-size: 13px; cursor: pointer;
  }
  .dash-modal-close:hover { background: rgba(76, 197, 210, 0.08); color: var(--text); }
  /* Back button — appears only when there's something to go back to.
     Backspace also triggers it. */
  .dash-modal-back {
    background: transparent; border: 1px solid var(--panel-border);
    color: var(--accent); border-radius: 6px;
    padding: 6px 10px; font-size: 14px; line-height: 1; cursor: pointer;
    margin-right: 4px;
  }
  .dash-modal-back:hover { background: rgba(76, 197, 210, 0.10); }
  /* Breadcrumb trail — shows the navigation path back to the tile root.
     Each step is clickable; clicking jumps directly to that level. */
  .dash-modal-breadcrumbs {
    padding: 8px 22px; border-bottom: 1px solid var(--panel-border);
    font-size: 11px; color: var(--dim);
    display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  }
  .dash-modal-breadcrumbs .crumb {
    background: transparent; border: 0;
    color: var(--accent); cursor: pointer; padding: 2px 4px;
    font: inherit;
  }
  .dash-modal-breadcrumbs .crumb:hover { text-decoration: underline; }
  .dash-modal-breadcrumbs .sep { color: var(--dim); opacity: 0.5; }
  .dash-modal-breadcrumbs .here { color: var(--text); font-weight: 600; }
  /* In-modal clickable info-links — wraps a technical term + opens a
     nested info modal explaining it. Style as a soft chip so users
     visually know it's clickable. */
  .info-link {
    background: rgba(76, 197, 210, 0.08);
    border: 1px dashed rgba(76, 197, 210, 0.30);
    color: var(--accent);
    border-radius: 4px;
    padding: 1px 6px; cursor: pointer;
    font: inherit; font-weight: 600;
    transition: background 0.15s, border-color 0.15s;
  }
  .info-link:hover {
    background: rgba(76, 197, 210, 0.16);
    border-color: var(--accent);
    border-style: solid;
  }
  .info-link::after {
    content: ' ›';
    color: var(--dim);
    font-weight: normal;
  }
  .dash-modal-body {
    padding: 22px; overflow-y: auto;
    font-size: 13px; color: var(--text);
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 22px;
  }
  .dash-modal-body .full { grid-column: 1 / -1; }
  .dash-modal-section {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--panel-border);
    border-radius: 10px;
    padding: 14px 16px;
    min-width: 0;
  }
  .dash-modal-section h4 {
    margin: 0 0 10px; font-size: 11px;
    color: var(--dim); text-transform: uppercase;
    letter-spacing: 0.6px; font-weight: 600;
  }
  .dash-modal-section .row {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: 8px; padding: 4px 0; font-size: 12.5px;
  }
  .dash-modal-section .row .k { color: var(--dim); }
  .dash-modal-section .row .v {
    color: var(--text); font-weight: 600;
    font-variant-numeric: tabular-nums; text-align: right;
    word-break: break-word; max-width: 65%;
  }
  .dash-modal-foot {
    padding: 14px 22px; border-top: 1px solid var(--panel-border);
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
  }
  .dash-modal-link {
    display: inline-flex; align-items: center; gap: 6px;
    background: var(--accent); color: #0a0e1a !important;
    border: 0; border-radius: 6px;
    padding: 8px 14px; font-size: 13px; font-weight: 600;
    text-decoration: none;
  }
  .dash-modal-link:hover { filter: brightness(1.1); }
  /* Inline viz primitives — used by the drill-down modal */
  .viz-donut svg { display: block; margin: 0 auto; }
  .viz-bars { display: grid; grid-template-columns: 50px 1fr 30px; gap: 6px 10px; align-items: center; font-size: 11.5px; }
  .viz-bars .bar-track {
    background: rgba(255, 255, 255, 0.05);
    border-radius: 4px; height: 10px;
    position: relative; overflow: hidden;
  }
  .viz-bars .bar-fill {
    position: absolute; top: 0; bottom: 0; left: 0;
    background: var(--accent); border-radius: 4px;
    transition: width 0.3s ease;
  }
  .viz-gauge {
    --gauge-val: 0;  /* 0..100 */
    --gauge-color: var(--accent);
    width: 140px; height: 140px; margin: 0 auto;
    position: relative;
  }
  .viz-gauge .center {
    position: absolute; inset: 0;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    font-size: 22px; font-weight: 700; color: var(--text);
    font-variant-numeric: tabular-nums;
  }
  .viz-gauge .center .lbl { font-size: 10px; color: var(--dim); font-weight: 500; margin-top: 2px; }
  .viz-spark svg { display: block; width: 100%; height: 60px; }
  .viz-pills { display: flex; flex-wrap: wrap; gap: 5px; }
  .viz-pill {
    font-size: 10.5px; font-weight: 600;
    padding: 3px 8px; border-radius: 6px;
    background: rgba(100, 116, 139, 0.15); color: var(--dim);
  }
  .viz-pill.good { background: rgba(0, 230, 118, 0.12); color: var(--green); }
  .viz-pill.warn { background: rgba(255, 183, 77, 0.14); color: var(--gold); }
  .viz-pill.bad  { background: rgba(255, 64, 96, 0.14); color: var(--red); }

  .dash-hero-grid {
    /* Emmanuel 2026-05-13: keep all visible cards on ONE row, never
       wrap. Tile #5 is role-dependent — Fleet on master, Activity on
       tenant — only one renders at a time, so the live grid is always
       5-col. Hidden cards are removed from layout flow (`display:none`
       on `[hidden]`) so they don't reserve a column. */
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 12px; margin-bottom: 16px;
  }
  .dash-hero-grid > [hidden] { display: none !important; }
  .dash-hero {
    display: grid;
    grid-template-rows: 24px 38px 18px 1fr;
    gap: 6px;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-left: 3px solid var(--accent);
    border-radius: 12px;
    padding: 14px 16px;
    text-decoration: none;
    transition: border-color 0.18s, transform 0.12s ease, box-shadow 0.2s;
    cursor: pointer;
    min-width: 0;
  }
  .dash-hero:hover {
    border-color: rgba(var(--accent-rgb, 38, 189, 214), 0.32);
    transform: translateY(-1px);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
  }
  .dash-hero.state-good   { border-left-color: var(--green); }
  .dash-hero.state-warn   { border-left-color: var(--gold); }
  .dash-hero.state-bad    { border-left-color: var(--red); }
  .dash-hero-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px;
  }
  .dash-hero-label {
    font-size: 10.5px; font-weight: 700;
    color: var(--text); text-transform: uppercase;
    letter-spacing: 0.8px; line-height: 1;
  }
  .dash-hero-state {
    font-size: 9px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.5px;
    padding: 3px 8px; border-radius: 10px;
    background: rgba(100, 116, 139, 0.12);
    color: var(--dim);
    line-height: 1;
    white-space: nowrap;
  }
  .dash-hero-state.good { background: rgba(0, 230, 118, 0.10); color: var(--green); }
  .dash-hero-state.warn { background: rgba(255, 183, 77, 0.12); color: var(--gold); }
  .dash-hero-state.bad  { background: rgba(255, 64, 96, 0.12); color: var(--red); }
  .dash-hero-val {
    font-size: 30px; font-weight: 700; color: var(--text);
    font-variant-numeric: tabular-nums; line-height: 1;
    letter-spacing: -0.02em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .dash-hero-val.good { color: var(--green); }
  .dash-hero-val.warn { color: var(--gold); }
  .dash-hero-val.bad  { color: var(--red); }
  .dash-hero-sub {
    font-size: 11px; font-weight: 500; color: var(--dim);
    line-height: 1.2;
  }
  .dash-hero-mini {
    display: flex; gap: 4px; flex-wrap: wrap; margin-top: auto;
    padding-top: 4px;
  }
  .prio-pill {
    font-size: 9.5px; font-weight: 700; padding: 2px 6px;
    border-radius: 6px; line-height: 1.2;
    font-variant-numeric: tabular-nums; letter-spacing: 0.2px;
  }
  .prio-pill.p0 { background: rgba(255, 64, 96, 0.14); color: var(--red); }
  .prio-pill.p1 { background: rgba(255, 183, 77, 0.14); color: var(--gold); }
  .prio-pill.p2 { background: rgba(var(--accent-rgb, 38, 189, 214), 0.12); color: var(--accent); }
  .prio-pill.p3 { background: rgba(100, 116, 139, 0.18); color: var(--dim); }
  .dash-hero-meta {
    display: flex; flex-direction: column; gap: 3px;
    font-size: 10.5px; margin-top: auto;
    padding-top: 4px;
  }
  .dash-hero-meta .meta-row {
    display: flex; justify-content: space-between; gap: 8px;
    align-items: baseline;
    min-width: 0;
  }
  .dash-hero-meta .k { color: var(--dim); font-weight: 500; }
  .dash-hero-meta .v {
    color: var(--text); font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    max-width: 60%;
  }
  .dash-hero-foot {
    font-size: 10px; color: var(--dim);
    font-style: italic; line-height: 1.2;
    margin-top: auto;
  }

  /* === PANELS (windows) === */
  .panel {
    background: var(--panel-bg);
    backdrop-filter: blur(20px);
    border: 1px solid var(--panel-border);
    border-radius: 16px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.4), 0 0 1px rgba(var(--accent-rgb, 38, 189, 214),0.2);
    overflow: hidden;
    transition: border-color 0.3s, box-shadow 0.3s;
  }
  .panel:hover {
    border-color: rgba(var(--accent-rgb, 38, 189, 214),0.22);
    box-shadow: 0 8px 40px rgba(0,0,0,0.5), 0 0 2px rgba(var(--accent-rgb, 38, 189, 214),0.3);
  }
  .panel-header {
    display: flex; align-items: center; gap: 8px;
    padding: 12px 16px;
    background: var(--panel-header);
    border-bottom: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.08);
    font-size: 13px; font-weight: 500;
  }
  .panel-header-icon { font-size: 14px; }
  .panel-header-title { flex: 1; }
  .panel-minimize {
    width: 20px; height: 20px; border-radius: 6px;
    background: rgba(var(--accent-rgb, 38, 189, 214),0.1); border: none; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    font-size: 10px; color: var(--dim); transition: all 0.2s;
  }
  .panel-minimize:hover { background: rgba(var(--accent-rgb, 38, 189, 214),0.2); color: var(--text); }
  .panel-body { padding: 16px; }

  /* === DASHBOARD GRID === */
  .dash-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }
  .dash-grid .panel { animation: fadeInPanel 0.4s ease backwards; }
  .dash-grid .panel:nth-child(1) { animation-delay: 0s; }
  .dash-grid .panel:nth-child(2) { animation-delay: 0.08s; }
  .dash-grid .panel:nth-child(3) { animation-delay: 0.16s; }
  .dash-grid .panel:nth-child(4) { animation-delay: 0.24s; }

  /* === BOTTOM TASKBAR === */
  .taskbar {
    position: fixed; bottom: 0; left: 0; right: 0; height: var(--taskbar-h); z-index: 200;
    background: rgba(8, 12, 26, 0.97); backdrop-filter: blur(24px);
    border-top: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.1);
    display: flex; align-items: center;
    padding: 0 10px;
    box-shadow: 0 -2px 20px rgba(0,0,0,0.3);
  }
  .taskbar::before {
    content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
    background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb, 38, 189, 214),0.25), transparent);
  }
  /* Left: CINTI button + search */
  .taskbar-left { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
  .taskbar-start {
    width: 38px; height: 38px; border-radius: 10px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    background: rgba(var(--accent-rgb, 38, 189, 214),0.08); border: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.15);
    color: var(--accent); transition: all 0.15s; flex-shrink: 0;
  }
  .taskbar-start:hover { background: rgba(var(--accent-rgb, 38, 189, 214),0.2); border-color: rgba(var(--accent-rgb, 38, 189, 214),0.35); transform: translateY(-1px); }
  .tb-search {
    width: 200px; height: 32px; border-radius: 8px;
    background: rgba(255,255,255,0.04); border: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.08);
    color: var(--text); font-family: inherit; font-size: 12px;
    padding: 0 10px 0 30px; outline: none; transition: all 0.2s;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 24 24' fill='none' stroke='%236b7d9a' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: 9px center;
  }
  .tb-search:focus { border-color: rgba(var(--accent-rgb, 38, 189, 214),0.35); background-color: rgba(255,255,255,0.06); width: 260px; }
  .tb-search::placeholder { color: var(--dim); font-size: 11px; }
  /* Center: pinned app launchers */
  .taskbar-center { display: flex; align-items: center; gap: 2px; margin: 0 auto; }
  .taskbar-tools { display: flex; align-items: center; gap: 2px; }
  .taskbar-tool {
    width: 38px; height: 38px; border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; color: var(--dim); transition: all 0.15s; position: relative;
    background: transparent; border: none; font-family: inherit;
  }
  .taskbar-tool:hover { background: rgba(var(--accent-rgb, 38, 189, 214),0.12); color: var(--text); transform: translateY(-1px); }
  .taskbar-tool.active { background: rgba(var(--accent-rgb, 38, 189, 214),0.18); color: var(--accent); }
  .taskbar-tool.active::after {
    content: ''; position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
    width: 16px; height: 3px; border-radius: 2px; background: var(--accent);
  }
  .taskbar-tool .tool-tooltip {
    position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
    background: rgba(8,12,26,0.95); border: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.2);
    border-radius: 6px; padding: 5px 12px; font-size: 11px; font-weight: 500;
    color: var(--text); white-space: nowrap; pointer-events: none;
    opacity: 0; transition: opacity 0.15s;
    box-shadow: 0 4px 12px rgba(0,0,0,0.4);
  }
  .taskbar-tool:hover .tool-tooltip { opacity: 1; }
  .taskbar-divider { width: 1px; height: 28px; background: rgba(var(--accent-rgb, 38, 189, 214),0.1); margin: 0 8px; }
  /* Right: system tray */
  .taskbar-right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
  .taskbar-services { display: flex; align-items: center; gap: 10px; }
  .taskbar-svc {
    display: flex; align-items: center; gap: 5px; font-size: 13px;
    position: relative; cursor: default;
  }
  .taskbar-svc .svc-dot {
    width: 7px; height: 7px; border-radius: 50%; position: absolute;
    bottom: -2px; right: -4px;
  }
  .taskbar-svc .svc-tooltip {
    position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
    background: rgba(8,12,26,0.95); border: 1px solid rgba(var(--accent-rgb, 38, 189, 214),0.2);
    border-radius: 6px; padding: 5px 12px; font-size: 11px; font-weight: 500;
    color: var(--text); white-space: nowrap; pointer-events: none;
    opacity: 0; transition: opacity 0.15s;
    box-shadow: 0 4px 12px rgba(0,0,0,0.4);
  }
  .taskbar-svc:hover .svc-tooltip { opacity: 1; }
  .taskbar-sep { width: 1px; height: 24px; background: rgba(var(--accent-rgb, 38, 189, 214),0.15); margin: 0 4px; }
  .tb-clock { font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums; padding: 4px 8px; border-radius: 6px; cursor: default; text-align: right; line-height: 1.3; }
  .tb-clock:hover { background: rgba(255,255,255,0.04); }

  /* === PORTAL MODE — user-facing dashboards === */
  body.portal-mode .topbar-search { display: none; }
  body.portal-mode .tile-body { font-size: 15px; line-height: 1.7; }
  body.portal-mode .tile-body .stat-val { font-size: 22px; }
  body.portal-mode .tile-body .stat-label { font-size: 13px; }
  body.portal-mode .tile-body strong { font-size: inherit; }
  body.portal-mode .tile-title { font-size: 14px; }
  body.portal-mode .health-row { font-size: 14px; }
  body.portal-mode .taskbar { font-size: 13px; }
  /* 2026-05-14 (Joe + Gene reported): /api/health returns a stripped
     payload to non-admin viewers, so the brain/discord/teams/avatar/tunnel
     dots in the right-side system tray have no data to bind against and
     render permanently red on portal hosts. These dots are admin-internal
     diagnostics — they don't belong in a portal user's chrome anyway.
     Backend fix shipped alongside (core/routes/health-api.js: authed-non-
     admin sessions now receive the connections block too), but we also
     hide the admin-internal surfaces in portal-mode as defense in depth. */
  body.portal-mode #taskbar-services { display: none; }
  body.portal-mode .taskbar-sep { display: none; }
  /* Brain Operations Center is a brain-telemetry tile (Token Optimizer +
     Service Status). Portal users do not need brain-internal diagnostics. */
  body.portal-mode .brain-ops-center,
  body.portal-mode .tile[data-tile-id="brain_viz"] { display: none; }

  /* Backup & Restore: moved to partner.cinti.com 2026-05-22 (dock icon + page
     mount removed here), then RE-ADDED to this SPA as a standard tenant
     feature — admin/index.html ships the data-page="backup-restore" dock icon
     + #page-backup-restore mount again, and admin/js/backup-restore.js loads
     on every page view. The role gates above are therefore live, not the
     no-op the pre-2026-07-24 comment claimed. It is ADMIN-ONLY: portal users
     are covered by the portal-mode rules below, not by those gates.
     (Comment corrected 2026-07-24 — the stale "removed / selectors match
     nothing" text had it backwards and read as a security guarantee.) */

  /* ===== Portal mode: NO admin navigation, ever (2026-07-24) =============
     A portal user was seeing the full admin dock — Users, Partners,
     Customers, Backup & Restore, OnePass, Settings, Fleet, Releases,
     Patches, Deployment (29 icons). The ONLY thing removing them was the
     JS dock rebuild in loadPortalConfig(), which (a) lives inside
     `if (_portalConfig)` so it never ran when GET /api/portals 401'd for a
     non-admin, and (b) runs in a setTimeout, leaving a visible race even on
     the happy path. Admin DATA was never exposed — those endpoints return
     401, or 404 via assertSystemHost, for a non-admin (verified) — but
     admin NAVIGATION must not render either.
     Every STATIC admin dock icon carries data-page="…"; the JS-rebuilt
     portal icons deliberately do NOT (they use _portalDockAction onclick),
     so this attribute selector removes exactly the admin set and leaves the
     portal's own icons untouched. CSS applies at first paint — no API call,
     no JS, no race. Fail closed. */
  body.portal-mode .dock-left .dock-icon[data-page] { display: none !important; }
  /* Stale-hash defence: block the highest-sensitivity admin page mounts from
     rendering in portal mode even if someone lands on #backup-restore etc. */
  body.portal-mode #page-backup-restore,
  body.portal-mode #page-users,
  body.portal-mode #page-onepass,
  body.portal-mode #page-partners,
  body.portal-mode #page-customers,
  body.portal-mode #page-settings,
  body.portal-mode #page-releases,
  body.portal-mode #page-patches,
  body.portal-mode #page-deployment,
  body.portal-mode #page-fleet-activity,
  body.portal-mode #page-fleet-deploys { display: none !important; }

  /* =========================================================
     CW Project #63 follow-up — Light theme overrides
     The hardcoded rgba(8,12,26)/(10,14,28)/(14,22,42) literals
     above don't theme — in light mode they stay dark and any
     icon/text inside (which DOES theme to dark) becomes invisible.
     Override every dark-rgba surface with a matching light value.
     Gene 2026-05-08.
     ========================================================= */
  [data-theme="light"] .topbar {
    background: rgba(255, 255, 255, 0.92);
    border-bottom-color: rgba(26, 154, 181, 0.18);
    box-shadow: 0 1px 12px rgba(20, 30, 50, 0.06);
  }
  [data-theme="light"] .topbar-search {
    background: rgba(238, 242, 247, 0.85);
    border-color: rgba(13, 80, 100, 0.4);
  }
  [data-theme="light"] .dock-left {
    border-right-color: rgba(13, 80, 100, 0.22);
    box-shadow: 2px 0 20px rgba(20, 30, 50, 0.06);
  }
  /* Emmanuel 2026-05-13 — dock-icon contrast pass. Was #5c6a82 (light
     grey) on a light bg = nearly invisible. Now uses --text deep slate
     with a darker hover/active that reads against #dce3ec dock bg. */
  [data-theme="light"] .dock-icon { color: var(--text); }
  [data-theme="light"] .dock-icon:hover { background: rgba(13, 80, 100, 0.14); color: var(--accent); }
  [data-theme="light"] .dock-icon.active {
    background: rgba(13, 80, 100, 0.20);
    color: var(--accent);
    box-shadow: inset 3px 0 0 var(--accent);
  }
  [data-theme="light"] .dock-icon .tooltip {
    background: rgba(255, 255, 255, 0.98);
    border-color: rgba(26, 154, 181, 0.22);
    color: #1a2332;
    box-shadow: 0 4px 12px rgba(20, 30, 50, 0.12);
  }
  [data-theme="light"] .dock-separator { background: rgba(26, 154, 181, 0.3); }
  [data-theme="light"] .dock-submenu {
    background: rgba(255, 255, 255, 0.98);
    border-color: rgba(26, 154, 181, 0.22);
    box-shadow: 0 8px 24px rgba(20, 30, 50, 0.16);
  }

  [data-theme="light"] .avatar-panel {
    background: rgba(255, 255, 255, 0.85);
    border-left-color: rgba(26, 154, 181, 0.18);
  }
  [data-theme="light"] .avatar-panel.fullscreen {
    background: rgba(255, 255, 255, 0.98);
  }
  [data-theme="light"] .avatar-resize-handle:hover,
  [data-theme="light"] .avatar-resize-handle.dragging {
    background: rgba(26, 154, 181, 0.25);
  }
  [data-theme="light"] .avatar-header {
    border-bottom-color: rgba(26, 154, 181, 0.14);
    background: rgba(244, 247, 251, 0.6);
  }
  [data-theme="light"] .avatar-iframe { background: #f4f7fb; }

  [data-theme="light"] .panel {
    box-shadow: 0 8px 32px rgba(20, 30, 50, 0.06), 0 0 1px rgba(26, 154, 181, 0.18);
  }
  [data-theme="light"] .panel:hover {
    border-color: rgba(26, 154, 181, 0.32);
    box-shadow: 0 8px 40px rgba(20, 30, 50, 0.08), 0 0 2px rgba(26, 154, 181, 0.4);
  }
  [data-theme="light"] .panel-header {
    border-bottom-color: rgba(26, 154, 181, 0.14);
  }
  [data-theme="light"] .panel-minimize { background: rgba(26, 154, 181, 0.12); color: #5c6a82; }
  [data-theme="light"] .panel-minimize:hover { background: rgba(26, 154, 181, 0.22); color: #1a2332; }

  [data-theme="light"] .taskbar {
    background: rgba(255, 255, 255, 0.97);
    border-top-color: rgba(26, 154, 181, 0.18);
    box-shadow: 0 -2px 20px rgba(20, 30, 50, 0.06);
  }
  [data-theme="light"] .taskbar-start { color: var(--accent-text,#0d8aa0); }
  [data-theme="light"] .taskbar-start:hover { background: rgba(26, 154, 181, 0.22); border-color: rgba(26, 154, 181, 0.45); }
  [data-theme="light"] .tb-search {
    background: rgba(238, 242, 247, 0.6);
    border-color: rgba(26, 154, 181, 0.18);
    color: #1a2332;
  }
  [data-theme="light"] .tb-search:focus {
    background-color: rgba(238, 242, 247, 0.9);
    border-color: rgba(26, 154, 181, 0.45);
  }
  [data-theme="light"] .taskbar-tool { color: #5c6a82; }
  [data-theme="light"] .taskbar-tool:hover { background: rgba(26, 154, 181, 0.14); color: #1a2332; }
  [data-theme="light"] .taskbar-tool.active { background: rgba(26, 154, 181, 0.22); color: var(--accent-text,#0d8aa0); }
  [data-theme="light"] .taskbar-tool .tool-tooltip {
    background: rgba(255, 255, 255, 0.98);
    border-color: rgba(26, 154, 181, 0.22);
    color: #1a2332;
    box-shadow: 0 4px 12px rgba(20, 30, 50, 0.12);
  }
  [data-theme="light"] .taskbar-divider { background: rgba(26, 154, 181, 0.18); }

  /* === RESPONSIVE: give the content column its space back =================
     Gene 2026-09-03: the accounting dashboard "is getting half cut ... give it
     more space".

     The agent panel was a flat 440px at every viewport, and this file had no
     media queries at all. Since .desktop-wrapper is a flex row of
     [.desktop][.avatar-panel], every pixel the panel holds is taken from the
     content — and the left dock expanding to 224px takes its share from the
     content too, never from the panel. Measured on the accounting portal with
     the dock expanded: 736px of content at 1440px wide, 396px at 1100px. At
     396px a five-across KPI strip gets ~66px per card, which is the clipping.

     So the panel now narrows with the viewport. Floor is the existing
     min-width: 280px, which still fits the chat. Deliberately excludes the
     .expanded and .fullscreen states — those are explicit user choices and
     must keep their own widths. */
  @media (max-width: 1500px) {
    .avatar-panel:not(.expanded):not(.fullscreen) { width: 380px; }
  }
  @media (max-width: 1280px) {
    .avatar-panel:not(.expanded):not(.fullscreen) { width: 320px; }
  }
  @media (max-width: 1120px) {
    .avatar-panel:not(.expanded):not(.fullscreen) { width: 300px; }
  }
