/* ===== ZFS NAS Portal — Global Design System ===== */

/* Tron Legacy / Matrix Reload theme fonts: loaded by each page's <head> with
   media="print" + onload, never @import here, which would hold up the whole
   stylesheet (and the first paint) until Google answers, on a VPN with no
   Internet route until it times out. */

/* Fixed page zoom on touch devices: pan gestures only. The viewport meta's
   maximum-scale/user-scalable lock is ignored by newer iOS Safari for
   accessibility, but touch-action IS honoured — together they pin the page
   zoom like the pre-v6.7.0 mobile builds. Elements with their own gesture
   handling (topology map, terminal, resize handles) override this locally
   with touch-action:none. */
html, body { touch-action: pan-x pan-y; }

:root {
  --bg:          #0d0d0f;
  --surface:     #1c1c1e;
  --surface2:    #2c2c2e;
  --border:      rgba(255, 255, 255, 0.08);
  --border-focus: rgba(10, 132, 255, 0.6);

  --accent-1:    #bf5af2;
  --accent-2:    #0a84ff;
  --accent-3:    #32d74b;
  --accent-4:    #30d158;
  --accent-warn: #ffd60a;
  --accent-danger:#ff453a;
  --zvol-color:  #64d2ff;

  --gradient:    linear-gradient(135deg, var(--accent-1), var(--accent-2));
  --gradient-full: linear-gradient(135deg, #bf5af2, #0a84ff, #32d74b);

  --text:        #f5f5f7;
  --text-2:      #c7c7cc;
  --text-3:      #8e8e93;

  --radius:      12px;
  --radius-sm:   8px;
  --radius-lg:   18px;

  --shadow:      0 4px 24px rgba(0, 0, 0, 0.4);
  --shadow-lg:   0 8px 40px rgba(0, 0, 0, 0.6);
}

/* ===== Light theme overrides ===== */
body.light {
  --bg:          #f2f2f7;
  --surface:     #ffffff;
  --surface2:    #e5e5ea;
  --border:      rgba(0, 0, 0, 0.10);
  --border-focus: rgba(10, 132, 255, 0.6);
  --text:        #1c1c1e;
  --text-2:      #3a3a3c;
  --text-3:      #6c6c70;
  --shadow:      0 4px 24px rgba(0, 0, 0, 0.10);
  --shadow-lg:   0 8px 40px rgba(0, 0, 0, 0.18);
  --accent-warn: #d4a017;
  --zvol-color:  #0071a8;
}
body.light .badge-smbonly { background: rgba(212,160,23,0.15); color: #d4a017; }

/* ===== Tron theme ===== */
/* ===== Tron theme (classic — normal fonts) ===== */
body.tron {
  --bg:          #000d1a;
  --surface:     #001428;
  --surface2:    #002040;
  --border:      rgba(0, 210, 255, 0.18);
  --border-focus: rgba(0, 210, 255, 0.7);
  --accent-1:    #00d2ff;
  --accent-2:    #00b4d8;
  --accent-3:    #00f0ff;
  --accent-4:    #00e0ff;
  --accent-warn: #ffe600;
  --accent-danger:#ff2d55;
  --zvol-color:  #00d2ff;
  --gradient:    linear-gradient(135deg, #005f80, #00d2ff);
  --gradient-full: linear-gradient(135deg, #005f80, #00d2ff, #00f0ff);
  --text:        #c8f0ff;
  --text-2:      #7ecfed;
  --text-3:      #4a9ab5;
  --shadow:      0 4px 24px rgba(0, 210, 255, 0.15);
  --shadow-lg:   0 8px 40px rgba(0, 210, 255, 0.25);
}
body.tron a { color: #00d2ff; }
body.tron .logo {
  background: linear-gradient(135deg, #00d2ff, #00f0ff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 12px rgba(0, 210, 255, 0.6));
}
body.tron .card, body.tron .card-sm { box-shadow: 0 0 0 1px rgba(0,210,255,0.15), inset 0 0 30px rgba(0,210,255,0.03); }
body.tron .btn { box-shadow: 0 0 8px rgba(0,210,255,0.2); }
body.tron .badge-smbonly { background: rgba(0,210,255,0.15); color: #00d2ff; }

/* ===== Tron Legacy theme (Orbitron fonts + bright neon) ===== */
body.tron-legacy {
  --bg:          #000d1a;
  --surface:     #001428;
  --surface2:    #002040;
  --border:      rgba(0, 210, 255, 0.22);
  --border-focus: rgba(0, 210, 255, 0.8);
  --accent-1:    #00d2ff;
  --accent-2:    #00b4d8;
  --accent-3:    #00f0ff;
  --accent-4:    #00e0ff;
  --accent-warn: #ffe600;
  --accent-danger:#ff2d55;
  --zvol-color:  #00d2ff;
  --gradient:    linear-gradient(135deg, #005f80, #00d2ff);
  --gradient-full: linear-gradient(135deg, #005f80, #00d2ff, #00f0ff);
  --text:        #d8f6ff;
  --text-2:      #00d2ff;
  --text-3:      #00b0d8;
  --shadow:      0 4px 24px rgba(0, 210, 255, 0.18);
  --shadow-lg:   0 8px 40px rgba(0, 210, 255, 0.30);
}
/* Tron Legacy — futuristic typography */
body.tron-legacy,
body.tron-legacy input,
body.tron-legacy select,
body.tron-legacy textarea,
body.tron-legacy button {
  font-family: 'Orbitron', 'Segoe UI', sans-serif;
  letter-spacing: 0.03em;
}
body.tron-legacy code,
body.tron-legacy pre,
body.tron-legacy .font-mono,
body.tron-legacy [style*="monospace"] {
  font-family: 'Share Tech Mono', 'Courier New', monospace !important;
  letter-spacing: 0.04em;
  color: #00f0ff;
  text-shadow: 0 0 6px rgba(0, 240, 255, 0.5);
}
body.tron-legacy .nav-item,
body.tron-legacy .nav-label,
body.tron-legacy h1, body.tron-legacy h2, body.tron-legacy h3, body.tron-legacy h4 {
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-shadow: 0 0 8px rgba(0, 210, 255, 0.55), 0 0 20px rgba(0, 210, 255, 0.2);
}
/* Orbitron is ~15% wider than the base UI font and these labels are uppercased,
   which wrapped 2-word nav items onto a second line. Trim the size + tracking
   so even the longest labels (CAPACITY TREND, ABOUT / SUPPORT) stay single-line
   and keep the same row height as other themes. */
body.tron-legacy .nav-item,
body.tron-legacy .nav-label {
  font-size: 11px;
  letter-spacing: 0.01em;
}
body.tron-legacy .sidebar-section {
  color: #00d2ff;
  text-shadow: 0 0 10px rgba(0, 210, 255, 0.65), 0 0 24px rgba(0, 210, 255, 0.25);
}
body.tron-legacy .nav-item.active {
  color: #00f0ff;
  text-shadow: 0 0 10px rgba(0, 240, 255, 0.7), 0 0 24px rgba(0, 240, 255, 0.3);
}
body.tron-legacy .stat-label,
body.tron-legacy .text-xs {
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.08em;
  color: #00d2ff;
  text-shadow: 0 0 6px rgba(0, 210, 255, 0.45);
}
body.tron-legacy a { color: #00d2ff; text-shadow: 0 0 6px rgba(0, 210, 255, 0.4); }
body.tron-legacy .logo {
  background: linear-gradient(135deg, #00d2ff, #00f0ff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 14px rgba(0, 210, 255, 0.75));
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.1em;
}
body.tron-legacy .card, body.tron-legacy .card-sm { box-shadow: 0 0 0 1px rgba(0,210,255,0.18), inset 0 0 30px rgba(0,210,255,0.04); }
body.tron-legacy .btn { box-shadow: 0 0 10px rgba(0,210,255,0.25); font-family: 'Orbitron', sans-serif; letter-spacing: 0.05em; }
body.tron-legacy .badge-smbonly { background: rgba(0,210,255,0.15); color: #00d2ff; }

/* Tron Legacy — light-cycle sidebar divider animation
 *
 * Gradient is 3× the element height with hard color stops:
 *   0–33%  = neon blue
 *   33–66% = red
 *   66–100%= neon blue
 *
 * background-position animates 100%→0% (REVERSE) so the visible
 * window travels UP through the gradient — this puts the color front
 * at the TOP of the line first, sweeping downward:
 *
 *   0–15%:   hold all-blue              (pos 100%)
 *   15–48%:  red front sweeps top→bot   (pos 100%→50%)
 *   48–62%:  hold all-red               (pos 50%)
 *   62–95%:  blue front sweeps top→bot  (pos 50%→0%)
 *   95–100%: hold all-blue              (pos 0%)
 *   loop → jump pos 0%→100%: no visual cut (both are all-blue)
 */
@keyframes tron-legacy-divider {
  0%   {
    background-position: 0 100%;
    box-shadow: 0 0 4px 0 #00f0ff, 0 0 14px 3px rgba(0, 240, 255, 0.55), 0 0 32px 6px rgba(0, 240, 255, 0.2);
  }
  15%  {
    background-position: 0 100%;
    box-shadow: 0 0 4px 0 #00f0ff, 0 0 14px 3px rgba(0, 240, 255, 0.55), 0 0 32px 6px rgba(0, 240, 255, 0.2);
  }
  48%  {
    background-position: 0 50%;
    box-shadow: 0 0 4px 0 #ff3030, 0 0 14px 3px rgba(255, 48,  48, 0.6),  0 0 32px 6px rgba(255, 48, 48,  0.25);
  }
  62%  {
    background-position: 0 50%;
    box-shadow: 0 0 4px 0 #ff3030, 0 0 14px 3px rgba(255, 48,  48, 0.6),  0 0 32px 6px rgba(255, 48, 48,  0.25);
  }
  95%  {
    background-position: 0 0%;
    box-shadow: 0 0 4px 0 #00f0ff, 0 0 14px 3px rgba(0, 240, 255, 0.55), 0 0 32px 6px rgba(0, 240, 255, 0.2);
  }
  100% {
    background-position: 0 0%;
    box-shadow: 0 0 4px 0 #00f0ff, 0 0 14px 3px rgba(0, 240, 255, 0.55), 0 0 32px 6px rgba(0, 240, 255, 0.2);
  }
}
/*
 * The "orb" — a point-light that travels with each color front.
 * Timing mirrors ::before exactly:
 *   0–15%:   invisible (blue hold)
 *   15–48%:  sweep top→bottom with the red front
 *   48–62%:  invisible (red hold)
 *   62–95%:  sweep top→bottom with the blue restoring front
 *   95–100%: invisible (blue hold before loop)
 */
@keyframes tron-legacy-orb {
  /* blue hold — invisible */
  0%    { top: 0%;               opacity: 0; }
  14.9% { top: 0%;               opacity: 0; }
  /* phase 1: orb leads red front downward — red glow throughout */
  15%   { top: 0%;               opacity: 1;
          box-shadow: 0 0 6px 4px #ffffff,
                      0 0 16px 8px rgba(255, 80,  80, 0.95),
                      0 0 32px 14px rgba(255, 48,  48, 0.55),
                      0 0 56px 22px rgba(255, 40,  40, 0.2); }
  48%   { top: 100%;             opacity: 1;
          box-shadow: 0 0 6px 4px #ffffff,
                      0 0 16px 8px rgba(255, 80,  80, 0.95),
                      0 0 32px 14px rgba(255, 48,  48, 0.55),
                      0 0 56px 22px rgba(255, 40,  40, 0.2); }
  /* snap back — invisible during red hold */
  48.1% { top: 0%;               opacity: 0; }
  61.9% { top: 0%;               opacity: 0; }
  /* phase 2: orb leads blue restoring front downward */
  62%   { top: 0%;               opacity: 1;
          box-shadow: 0 0 6px 4px #ffffff,
                      0 0 16px 8px rgba(0, 240, 255, 0.95),
                      0 0 32px 14px rgba(0, 240, 255, 0.55),
                      0 0 56px 22px rgba(0, 240, 255, 0.2); }
  95%   { top: 100%;             opacity: 1;
          box-shadow: 0 0 6px 4px #ffffff,
                      0 0 16px 8px rgba(0, 240, 255, 0.95),
                      0 0 32px 14px rgba(0, 240, 255, 0.55),
                      0 0 56px 22px rgba(0, 240, 255, 0.2); }
  /* invisible during blue hold before loop */
  95.1% { top: 0%;               opacity: 0; }
  100%  { top: 0%;               opacity: 0; }
}
/* Remove the static sidebar border — the animated pseudo-element replaces it */
body.tron-legacy .sidebar { border-right: none; }
/* The animated line lives on main-content::before (left edge, always in-bounds) */
body.tron-legacy .main-content {
  position: relative;
}
body.tron-legacy .main-content::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 2px;
  height: 100%;
  background: linear-gradient(
    to bottom,
    #00f0ff 0%,     #00f0ff 33.33%,
    #ff3030 33.33%, #ff3030 66.66%,
    #00f0ff 66.66%, #00f0ff 100%
  );
  background-size: 100% 300%;
  background-position: 0 100%;
  box-shadow: 0 0 4px 0 #00f0ff, 0 0 14px 3px rgba(0, 240, 255, 0.55), 0 0 32px 6px rgba(0, 240, 255, 0.2);
  animation: tron-legacy-divider 108s linear infinite;
  pointer-events: none;
  z-index: 10;
}
/* The orb — bright point-light riding the leading edge of each color front */
body.tron-legacy .main-content::after {
  content: '';
  position: absolute;
  left: 1px;          /* center of the 2px line */
  top: 0;
  width: 0;
  height: 0;
  border-radius: 50%;
  opacity: 0;
  animation: tron-legacy-orb 108s linear infinite;
  pointer-events: none;
  z-index: 11;
}

/* ===== Matrix theme ===== */
body.matrix {
  --bg:          #000300;
  --surface:     #001200;
  --surface2:    #001e00;
  --border:      rgba(0, 200, 50, 0.18);
  --border-focus: rgba(0, 255, 65, 0.7);
  --accent-1:    #00ff41;
  --accent-2:    #00c832;
  --accent-3:    #39ff14;
  --accent-4:    #00e620;
  --accent-warn: #ffe600;
  --accent-danger:#ff2d55;
  --zvol-color:  #00ff41;
  --gradient:    linear-gradient(135deg, #004d00, #00ff41);
  --gradient-full: linear-gradient(135deg, #004d00, #00c832, #39ff14);
  --text:        #b8ffcc;
  --text-2:      #6adc7a;
  --text-3:      #3a8f4a;
  --shadow:      0 4px 24px rgba(0, 255, 65, 0.12);
  --shadow-lg:   0 8px 40px rgba(0, 255, 65, 0.22);
}
body.matrix a { color: #00ff41; }
body.matrix .logo {
  background: linear-gradient(135deg, #00c832, #39ff14);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 12px rgba(0,255,65,0.6));
}
body.matrix .card, body.matrix .card-sm { box-shadow: 0 0 0 1px rgba(0,255,65,0.12), inset 0 0 30px rgba(0,255,65,0.02); }
body.matrix .btn { box-shadow: 0 0 8px rgba(0,255,65,0.2); }
body.matrix .badge-smbonly { background: rgba(0,255,65,0.15); color: #00ff41; }

/* ===== Matrix Reload theme (Matrix colors + Tron Legacy fonts) ===== */
body.matrix-reload {
  --bg:          #000300;
  --surface:     #001200;
  --surface2:    #001e00;
  --border:      rgba(0, 200, 50, 0.18);
  --border-focus: rgba(0, 255, 65, 0.7);
  --accent-1:    #00ff41;
  --accent-2:    #00c832;
  --accent-3:    #39ff14;
  --accent-4:    #00e620;
  --accent-warn: #ffe600;
  --accent-danger:#ff2d55;
  --zvol-color:  #00ff41;
  --gradient:    linear-gradient(135deg, #004d00, #00ff41);
  --gradient-full: linear-gradient(135deg, #004d00, #00c832, #39ff14);
  --text:        #b8ffcc;
  --text-2:      #6adc7a;
  --text-3:      #3a8f4a;
  --shadow:      0 4px 24px rgba(0, 255, 65, 0.12);
  --shadow-lg:   0 8px 40px rgba(0, 255, 65, 0.22);
}
/* Matrix Reload — Orbitron + Share Tech Mono, green glow */
body.matrix-reload,
body.matrix-reload input,
body.matrix-reload select,
body.matrix-reload textarea,
body.matrix-reload button {
  font-family: 'Orbitron', 'Segoe UI', sans-serif;
  letter-spacing: 0.03em;
}
body.matrix-reload code,
body.matrix-reload pre,
body.matrix-reload .font-mono,
body.matrix-reload [style*="monospace"] {
  font-family: 'Share Tech Mono', 'Courier New', monospace !important;
  letter-spacing: 0.04em;
  color: #39ff14;
  text-shadow: 0 0 6px rgba(57, 255, 20, 0.5);
}
body.matrix-reload .nav-item,
body.matrix-reload .nav-label,
body.matrix-reload h1, body.matrix-reload h2, body.matrix-reload h3, body.matrix-reload h4 {
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-shadow: 0 0 8px rgba(0, 255, 65, 0.55), 0 0 20px rgba(0, 255, 65, 0.2);
}
/* Keep Orbitron nav labels single-line (see tron-legacy note above). */
body.matrix-reload .nav-item,
body.matrix-reload .nav-label {
  font-size: 11px;
  letter-spacing: 0.01em;
}
body.matrix-reload .sidebar-section {
  color: #00c832;
  text-shadow: 0 0 10px rgba(0, 200, 50, 0.65), 0 0 24px rgba(0, 200, 50, 0.25);
}
body.matrix-reload .nav-item.active {
  color: #39ff14;
  text-shadow: 0 0 10px rgba(57, 255, 20, 0.7), 0 0 24px rgba(57, 255, 20, 0.3);
}
body.matrix-reload .stat-label,
body.matrix-reload .text-xs {
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.08em;
  color: #00c832;
  text-shadow: 0 0 6px rgba(0, 200, 50, 0.45);
}
body.matrix-reload a { color: #00ff41; text-shadow: 0 0 6px rgba(0, 255, 65, 0.4); }
body.matrix-reload .logo {
  background: linear-gradient(135deg, #00c832, #39ff14);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 14px rgba(0, 255, 65, 0.75));
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.1em;
}
body.matrix-reload .card, body.matrix-reload .card-sm { box-shadow: 0 0 0 1px rgba(0,255,65,0.18), inset 0 0 30px rgba(0,255,65,0.04); }
body.matrix-reload .btn { box-shadow: 0 0 10px rgba(0,255,65,0.25); font-family: 'Orbitron', sans-serif; letter-spacing: 0.05em; }
body.matrix-reload .badge-smbonly { background: rgba(0,255,65,0.15); color: #00ff41; }

/* ===== Purple Neon theme ===== */
body.purple {
  --bg:          #0a0010;
  --surface:     #130020;
  --surface2:    #1e0035;
  --border:      rgba(180, 0, 255, 0.18);
  --border-focus: rgba(200, 0, 255, 0.7);
  --accent-1:    #d400ff;
  --accent-2:    #b000e0;
  --accent-3:    #e060ff;
  --accent-4:    #cc00ff;
  --accent-warn: #ffe600;
  --accent-danger:#ff2d55;
  --zvol-color:  #d400ff;
  --gradient:    linear-gradient(135deg, #5500aa, #d400ff);
  --gradient-full: linear-gradient(135deg, #5500aa, #b000e0, #e060ff);
  --text:        #c8f0ff;
  --text-2:      #7ecfed;
  --text-3:      #4a9ab5;
  --shadow:      0 4px 24px rgba(180, 0, 255, 0.15);
  --shadow-lg:   0 8px 40px rgba(180, 0, 255, 0.25);
}
body.purple a { color: #d400ff; }
body.purple .logo {
  background: linear-gradient(135deg, #b000e0, #e060ff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 12px rgba(180,0,255,0.6));
}
body.purple .card, body.purple .card-sm { box-shadow: 0 0 0 1px rgba(180,0,255,0.15), inset 0 0 30px rgba(180,0,255,0.03); }
body.purple .btn { box-shadow: 0 0 8px rgba(180,0,255,0.2); }
body.purple .badge-smbonly { background: rgba(180,0,255,0.15); color: #d400ff; }

/* ===== Ghost in the Shell theme (Purple Neon colors + Orbitron/Share Tech Mono fonts) ===== */
body.ghost-shell {
  --bg:          #0a0010;
  --surface:     #130020;
  --surface2:    #1e0035;
  --border:      rgba(180, 0, 255, 0.18);
  --border-focus: rgba(200, 0, 255, 0.7);
  --accent-1:    #d400ff;
  --accent-2:    #b000e0;
  --accent-3:    #e060ff;
  --accent-4:    #cc00ff;
  --accent-warn: #ffe600;
  --accent-danger:#ff2d55;
  --zvol-color:  #d400ff;
  --gradient:    linear-gradient(135deg, #5500aa, #d400ff);
  --gradient-full: linear-gradient(135deg, #5500aa, #b000e0, #e060ff);
  --text:        #c8f0ff;
  --text-2:      #7ecfed;
  --text-3:      #4a9ab5;
  --shadow:      0 4px 24px rgba(180, 0, 255, 0.15);
  --shadow-lg:   0 8px 40px rgba(180, 0, 255, 0.25);
}
/* Ghost in the Shell — Orbitron + Share Tech Mono, purple glow */
body.ghost-shell,
body.ghost-shell input,
body.ghost-shell select,
body.ghost-shell textarea,
body.ghost-shell button {
  font-family: 'Orbitron', 'Segoe UI', sans-serif;
  letter-spacing: 0.03em;
}
body.ghost-shell code,
body.ghost-shell pre,
body.ghost-shell .font-mono,
body.ghost-shell [style*="font-family:monospace"],
body.ghost-shell [style*="font-family: monospace"] {
  font-family: 'Share Tech Mono', 'Courier New', monospace !important;
  letter-spacing: 0.04em;
  color: #e060ff;
  text-shadow: 0 0 6px rgba(224, 96, 255, 0.5);
}
body.ghost-shell .nav-item,
body.ghost-shell .nav-label,
body.ghost-shell h1, body.ghost-shell h2, body.ghost-shell h3, body.ghost-shell h4 {
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-shadow: 0 0 8px rgba(212, 0, 255, 0.55), 0 0 20px rgba(212, 0, 255, 0.2);
}
body.ghost-shell .sidebar-section {
  color: #b000e0;
  text-shadow: 0 0 10px rgba(176, 0, 224, 0.65), 0 0 24px rgba(176, 0, 224, 0.25);
}
body.ghost-shell .nav-item.active {
  color: #e060ff;
  text-shadow: 0 0 10px rgba(224, 96, 255, 0.7), 0 0 24px rgba(224, 96, 255, 0.3);
}
body.ghost-shell .stat-label,
body.ghost-shell .text-xs {
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.08em;
  color: #b000e0;
  text-shadow: 0 0 6px rgba(176, 0, 224, 0.45);
}
body.ghost-shell a { color: #d400ff; text-shadow: 0 0 6px rgba(212, 0, 255, 0.4); }
body.ghost-shell .logo {
  background: linear-gradient(135deg, #b000e0, #e060ff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 14px rgba(180, 0, 255, 0.75));
  font-family: 'Orbitron', sans-serif;
  letter-spacing: 0.1em;
}
body.ghost-shell .card, body.ghost-shell .card-sm { box-shadow: 0 0 0 1px rgba(180,0,255,0.18), inset 0 0 30px rgba(180,0,255,0.04); }
body.ghost-shell .btn { box-shadow: 0 0 10px rgba(180,0,255,0.25); font-family: 'Orbitron', sans-serif; letter-spacing: 0.05em; }
body.ghost-shell .badge-smbonly { background: rgba(180,0,255,0.15); color: #d400ff; }

/* ===== Dusk theme (midtone — between dark and light) ===== */
body.dusk {
  --bg:          #2a2a38;
  --surface:     #34344a;
  --surface2:    #3e3e56;
  --border:      rgba(190, 190, 220, 0.14);
  --border-focus: rgba(100, 160, 255, 0.6);
  --accent-1:    #c06ef0;
  --accent-2:    #5b9cf6;
  --accent-3:    #3ec96b;
  --accent-4:    #36d45a;
  --accent-warn: #e8a020;
  --accent-danger:#e84040;
  --zvol-color:  #70c8f8;
  --gradient:    linear-gradient(135deg, #c06ef0, #5b9cf6);
  --gradient-full: linear-gradient(135deg, #c06ef0, #5b9cf6, #3ec96b);
  --text:        #eaeaf4;
  --text-2:      #a8a8c8;
  --text-3:      #6868a0;
  --shadow:      0 4px 24px rgba(0, 0, 0, 0.28);
  --shadow-lg:   0 8px 40px rgba(0, 0, 0, 0.40);
}
body.dusk .badge-smbonly { background: rgba(232,160,32,0.15); color: #e8a020; }

/* ===== Dawn theme (warm light brown — soft morning feel) ===== */
body.dawn {
  --bg:          #eee8dc;
  --surface:     #f5f0e8;
  --surface2:    #e4ddd0;
  --border:      rgba(90, 68, 38, 0.13);
  --border-focus: rgba(72, 120, 200, 0.55);
  --accent-1:    #9060c0;
  --accent-2:    #4878c8;
  --accent-3:    #3a9850;
  --accent-4:    #38a855;
  --accent-warn: #c87800;
  --accent-danger:#cc2828;
  --zvol-color:  #4878c8;
  --gradient:    linear-gradient(135deg, #9060c0, #4878c8);
  --gradient-full: linear-gradient(135deg, #9060c0, #4878c8, #3a9850);
  --text:        #2c2010;
  --text-2:      #5a4832;
  --text-3:      #8a7258;
  --shadow:      0 4px 24px rgba(80, 58, 20, 0.12);
  --shadow-lg:   0 8px 40px rgba(80, 58, 20, 0.20);
}
body.dawn a { color: #4878c8; }
body.dawn .logo {
  background: linear-gradient(135deg, #9060c0, #4878c8);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: none;
}
body.dawn .badge-smbonly { background: rgba(200,120,0,0.12); color: #c87800; }
body.dawn .iscsi-icon { filter: none; }

/* ===== Theme picker dropdown ===== */
.theme-picker-wrap {
  position: relative;
  display: flex;
  align-items: center;
}
.theme-picker-btn {
  padding: 6px 8px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-2);
  display: flex;
  align-items: center;
  gap: 4px;
  opacity: 0.7;
  transition: opacity .15s;
  border-radius: var(--radius-sm);
}
.theme-picker-btn:hover { opacity: 1; }
.theme-picker-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  min-width: 170px;
  z-index: 9999;
  overflow: hidden;
}
.theme-picker-dropdown.open { display: block; }
.theme-picker-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text);
  transition: background .12s;
  white-space: nowrap;
}
.theme-picker-item:hover { background: var(--surface2); }
.theme-picker-item.active { color: var(--accent-2); font-weight: 600; }
.theme-picker-item .theme-swatch {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex-shrink: 0;
  border: 1px solid rgba(255,255,255,0.15);
}
.theme-picker-item .theme-check {
  margin-left: auto;
  opacity: 0;
  font-size: 14px;
}
.theme-picker-item.active .theme-check { opacity: 1; }

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI",
               system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-2); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ===== Typography ===== */
.logo {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.5px;
  background: var(--gradient-full);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 12px rgba(191, 90, 242, 0.35));
}

h1 { font-size: 28px; font-weight: 700; letter-spacing: -0.5px; }
h2 { font-size: 20px; font-weight: 600; }
h3 { font-size: 16px; font-weight: 600; }

.text-2 { color: var(--text-2); }
.text-3 { color: var(--text-3); }
.text-sm { font-size: 13px; }
.text-xs { font-size: 11px; }

/* ===== Cards / Panels ===== */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
  break-inside: avoid;
  margin-bottom: 20px;
}

.card-sm {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 16px;
}

/* ===== Buttons ===== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 20px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: opacity 0.15s, transform 0.1s;
  white-space: nowrap;
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

.btn-primary {
  background: var(--gradient);
  color: #fff;
  box-shadow: 0 2px 12px rgba(10, 132, 255, 0.3);
}
.btn-primary:hover:not(:disabled) { opacity: 0.88; }

.btn-ghost {
  background: transparent;
  color: var(--text-2);
  border: 1px solid var(--border);
}
.btn-ghost:hover:not(:disabled) { background: var(--surface2); color: var(--text); }

.btn-danger {
  background: var(--accent-danger);
  color: #fff;
}
.btn-danger:hover:not(:disabled) { opacity: 0.85; }

.btn-sm { padding: 6px 14px; font-size: 13px; }
.btn-xs { padding: 3px 9px; font-size: 11px; border-radius: 5px; }

/* Icon-only toggle (e.g. Monitor pause/resume) — themed, never the UA white bg. */
.ci-iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 26px; padding: 0; border-radius: 7px; cursor: pointer;
  background: var(--surface2); border: 1px solid var(--border);
  color: var(--accent-2); font-size: 13px; line-height: 1;
  transition: background .15s, color .15s, border-color .15s, transform .1s;
}
.ci-iconbtn:hover { background: var(--surface); border-color: var(--accent-2); }
.ci-iconbtn:active { transform: scale(.94); }
.ci-iconbtn.paused { color: #3fb950; }              /* resume (play) shown in green */
.ci-iconbtn.paused:hover { border-color: #3fb950; }
.btn-lg { padding: 14px 28px; font-size: 16px; width: 100%; justify-content: center; }

/* ===== Forms ===== */
.form-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 18px;
}

label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  letter-spacing: 0.02em;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input:not([type]),
input.input,
select,
textarea {
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 16px; /* 16px minimum prevents iOS auto-zoom on focus */
  padding: 11px 14px;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
  width: 100%;
  font-family: inherit;
  -webkit-appearance: none;
  appearance: none;
}

input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="reset"]):focus,
select:focus, textarea:focus {
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.15);
}

input::placeholder { color: var(--text-3); }

/* ===== Alerts ===== */
.alert {
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  margin-bottom: 16px;
}
.alert-error {
  background: rgba(255, 69, 58, 0.12);
  border: 1px solid rgba(255, 69, 58, 0.3);
  color: #ff6b61;
}
.alert-success {
  background: rgba(50, 215, 75, 0.12);
  border: 1px solid rgba(50, 215, 75, 0.3);
  color: var(--accent-3);
}
.alert-info {
  background: rgba(10, 132, 255, 0.12);
  border: 1px solid rgba(10, 132, 255, 0.3);
  color: #5aafff;
}

/* ===== Badge / Pill ===== */
.badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
}
.badge-admin   { background: rgba(191,90,242,0.18); color: #d47fff; }
.badge-readonly { background: rgba(10,132,255,0.15); color: #5aafff; }
.badge-smbonly { background: rgba(255,214,10,0.15); color: #ffd60a; }
.badge-ok      { background: rgba(50,215,75,0.15); color: var(--accent-3); }
.badge-error   { background: rgba(255,69,58,0.15); color: var(--accent-danger); }

/* ===== Spinner ===== */
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
  width: 18px; height: 18px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-2);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  display: inline-block;
}

/* ===== Compose deploy-target chooser (v6.6.22) ===== */
.compose-target-card {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px; border: 1.5px solid var(--border);
  border-radius: var(--radius-sm); cursor: pointer;
  background: var(--bg-2, rgba(255,255,255,0.03));
  transition: border-color .12s, background .12s;
}
.compose-target-card:hover { border-color: var(--accent-2); }
.compose-target-card.selected { border-color: var(--accent-2); background: color-mix(in srgb, var(--accent-2) 12%, transparent); }
.compose-target-card .ctc-icon { font-size: 26px; line-height: 1; width: 32px; text-align: center; }
.compose-target-card .ctc-title { font-weight: 600; font-size: 14px; color: var(--text); }
.compose-target-card .ctc-desc { font-size: 12px; color: var(--text-3); margin-top: 2px; }
.compose-rt-badge {
  display: inline-block; font-size: 10px; font-weight: 700; padding: 1px 7px;
  border-radius: 10px; text-transform: uppercase; letter-spacing: .03em;
}
.compose-rt-badge.docker { background: rgba(62,99,221,0.18); color: #5b7cff; }
.compose-rt-badge.podman { background: rgba(142,78,198,0.18); color: #b78be0; }

/* ===== Divider ===== */
.divider {
  height: 1px;
  background: var(--border);
  margin: 20px 0;
}

/* ===== Auth layout (login / setup pages) ===== */
.auth-page {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background:
    radial-gradient(ellipse 60% 40% at 20% 20%, rgba(191,90,242,0.08) 0%, transparent 70%),
    radial-gradient(ellipse 50% 50% at 80% 80%, rgba(10,132,255,0.07) 0%, transparent 70%),
    var(--bg);
}

.auth-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 40px;
  width: 100%;
  max-width: 420px;
  box-shadow: var(--shadow-lg);
}

.auth-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 32px;
}

.auth-logo-icon {
  width: 40px; height: 40px;
  border-radius: 10px;
  background: var(--gradient);
  display: flex; align-items: center; justify-content: center;
  font-size: 20px;
  box-shadow: 0 4px 16px rgba(191,90,242,0.3);
}

.auth-card h1 {
  font-size: 24px;
  margin-bottom: 6px;
}

.auth-subtitle {
  color: var(--text-2);
  font-size: 14px;
  margin-bottom: 28px;
}

/* ===== Main app layout ===== */
.app-layout {
  display: flex;
  height: 100vh;
  height: 100dvh; /* iOS Safari: dynamic viewport excludes browser chrome */
  overflow: hidden;
}
/* iPad / iOS home-screen ("standalone") web app: the status bar is translucent
   and viewport-fit=cover lets the page draw under it, so the iPad top bar covers
   the sidebar/top bar. Inset the desktop+tablet layout by the safe area so nothing
   hides beneath the status bar or home indicator. These insets are 0 in a normal
   desktop browser, so this only affects the installed iOS web app. The phone
   layout has its own inset rule (body.is-phone .app-layout). */
body:not(.is-phone) .app-layout {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

/* Sidebar */
.sidebar {
  width: 190px;
  min-width: 190px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 20px 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.sidebar-header {
  padding: 0 20px 12px;
}

.sidebar-header .logo {
  font-size: 18px;
}

.sidebar-mode-selector {
  display: flex;
  justify-content: center;
  gap: 4px;
  padding: 6px 16px 2px;
  margin-bottom: 0;
}

.sidebar-mode-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--text-3);
  cursor: pointer;
  transition: background .15s, color .15s;
}

.sidebar-mode-btn:hover {
  background: var(--surface2);
  color: var(--text);
}

.sidebar-mode-btn.active {
  background: var(--surface2);
  color: var(--accent);
}

.lxd-tbl-icon {
  cursor: pointer;
  padding: 4px 6px;
  color: var(--text-2);
  display: inline-flex;
  align-items: center;
  border-radius: 4px;
  transition: color .15s;
}
.lxd-tbl-icon:hover { color: var(--text); }
.lxd-tbl-icon-busy { opacity: 0.35; cursor: default; pointer-events: none; }
/* Tighten the per-row terminal + burger pair: shared cell, white-space:nowrap
   keeps them on one line so the row never grows vertically when columns get
   crowded, and the horizontal padding shrink matches the lost gap from the
   merged TDs. */
.lxd-row-actions {
  white-space: nowrap;
  text-align: right;
  padding-left: 4px !important;
  padding-right: 8px !important;
}
.lxd-row-actions .lxd-tbl-icon { padding: 2px 4px; margin-right: 2px; }
.lxd-power-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  border: 1px solid transparent;
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: opacity .15s;
}
.lxd-power-badge.lxd-tbl-icon-busy { opacity: 0.45; cursor: default; pointer-events: none; }

/* v6.6.16 — metric columns (VCPU/RAM/Filesystem) "%used + bar". */
.lxd-metric-wrap { display: inline-flex; align-items: center; gap: 6px; width: 100%; }
.lxd-metric-pct { min-width: 30px; text-align: right; color: var(--text); font-variant-numeric: tabular-nums; }
.lxd-bar {
  flex: 1 1 auto;
  height: 7px;
  min-width: 28px;
  background: var(--surface2, rgba(255,255,255,0.08));
  border-radius: 4px;
  overflow: hidden;
}
.lxd-bar > i { display: block; height: 100%; border-radius: 4px; transition: width .3s ease; }
.lxd-metric-cell { vertical-align: middle; }

/* Per-cell hover popup (filesystems / NICs / disks breakdown). */
#lxd-cell-popup {
  position: fixed;
  z-index: 5000;
  display: none;
  max-width: min(92vw, 420px);
  background: var(--surface, #1c2128);
  border: 1px solid var(--border, #30363d);
  border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0,0,0,0.45);
  padding: 10px 12px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text);
  pointer-events: auto;
}
/* VMs & Containers list — make the table area independently scrollable.
   Pages-container handles outer scroll for tall pages, but the LXD table
   has 10 columns and many rows; on short or narrow viewports the action
   column gets clipped. Constraining the card with internal scroll keeps
   every column reachable, with a sticky header so you can see what
   column is what while scrolling. */
.lxd-table-card {
  overflow: auto;
  max-height: calc(100vh - 200px);
  min-height: 180px;
}
.lxd-table-card thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 2;
}

th.lxd-sortable { cursor: pointer; user-select: none; }
th.lxd-sortable:hover { color: var(--text); }
th.lxd-sortable .sort-arrow { font-size: 10px; margin-left: 3px; }
th.lxd-sortable.sort-asc .sort-arrow,
th.lxd-sortable.sort-desc .sort-arrow { opacity: 1; }

/* VMs & Containers instance tables (.lxd-inst-table — the main VMs & Containers
   page plus the per-bridge and per-datastore "VMs & Containers" tables) — fixed
   layout so spare horizontal space is given to the graph-bar columns (CPU / RAM /
   Filesystem) instead of padding the text columns. The text + control columns get
   fixed widths; cpu/ram/fs are the only `width:auto` columns, so under
   table-layout:fixed they split the leftover width — on a wide screen their bars
   get wider, and on a narrower window they shrink so the whole table reflows to
   fit the available space (no horizontal scrollbar until the window is narrower
   than the fixed columns themselves). No min-width, so width:100% always tracks
   the current container size.
   (!important overrides the per-th inline widths that were tuned for auto layout.)
   Skipped on phone, where the table renders as cards. */
/* Responsive Stack-Containers tables (.stk-resp-table): the JS fitter
   (_ciFitStackTables) toggles these classes to drop columns from the right
   when the card is too narrow — ports first, then image, then service. The
   selector hits the <col>, <th> and <td> for that column (all carry data-col),
   so the column collapses cleanly under table-layout:fixed and the freed width
   flows to the auto-width Name column. */
.stk-resp-table.stk-hide-ports   [data-col="ports"],
.stk-resp-table.stk-hide-image   [data-col="image"],
.stk-resp-table.stk-hide-service [data-col="service"] { display: none; }

/* User column chooser (the gear "Columns" button on each Stack Containers card).
   Persisted per-browser via cookie. Mirrors the responsive hide rules above so
   a deselected column collapses its <col>/<th>/<td> cleanly under
   table-layout:fixed. Container/State/actions are always kept. */
.stk-resp-table.stk-uhide-service [data-col="service"],
.stk-resp-table.stk-uhide-image   [data-col="image"],
.stk-resp-table.stk-uhide-state   [data-col="state"],
.stk-resp-table.stk-uhide-ports   [data-col="ports"] { display: none; }

body:not(.is-phone) .lxd-inst-table { width: 100%; table-layout: fixed; }
/* When the window is narrower than the fixed columns, scroll horizontally rather
   than clip. The main page table has its own .lxd-table-card scroller; the bridge
   and datastore member tables get this wrapper. */
body:not(.is-phone) .lxd-inst-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
body:not(.is-phone) .lxd-inst-table th[data-col="type"]      { width: 40px  !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="name"]      { width: 150px !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="ipv4"]      { width: 128px !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="image"]     { width: 124px !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="datastore"] { width: 102px !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="net"]       { width: 80px  !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="diskio"]    { width: 86px  !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="status"]    { width: 102px !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="autostart"] { width: 90px  !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="actions"]   { width: 62px  !important; }
body:not(.is-phone) .lxd-inst-table th[data-col="cpu"],
body:not(.is-phone) .lxd-inst-table th[data-col="ram"],
body:not(.is-phone) .lxd-inst-table th[data-col="fs"] { width: auto !important; }
/* Keep cramped metric cells from spilling into neighbours on narrow screens, and
   trim their side padding so more of the column width goes to the bar itself. */
body:not(.is-phone) .lxd-inst-table td.lxd-metric-cell { overflow: hidden; }
body:not(.is-phone) .lxd-inst-table td.lxd-metric-cell,
body:not(.is-phone) .lxd-inst-table th[data-col="cpu"],
body:not(.is-phone) .lxd-inst-table th[data-col="ram"],
body:not(.is-phone) .lxd-inst-table th[data-col="fs"],
body:not(.is-phone) .lxd-inst-table th[data-col="net"],
body:not(.is-phone) .lxd-inst-table th[data-col="diskio"],
body:not(.is-phone) .lxd-inst-table th[data-col="autostart"],
body:not(.is-phone) .lxd-inst-table td[data-col="autostart"] { padding-left: 6px; padding-right: 6px; }

/* The Name cell stacks the display name over the monospace id (the wrapper keeps
   them as one unit so they stack on phone cards instead of sitting side by side).
   Each line truncates with an ellipsis within the fixed column width. */
.lxd-inst-table td[data-col="name"] .lxd-name-cell { display: block; min-width: 0; }
.lxd-inst-table td[data-col="name"] .lxd-name-1,
.lxd-inst-table td[data-col="name"] .lxd-name-2 { display: block; }
body:not(.is-phone) .lxd-inst-table td[data-col="datastore"],
body:not(.is-phone) .lxd-inst-table td[data-col="name"] .lxd-name-1,
body:not(.is-phone) .lxd-inst-table td[data-col="name"] .lxd-name-2 {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Image cell: wrap to at most 2 lines within the cell, then ellipsis. */
.lxd-inst-table td[data-col="image"] .lxd-img {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; white-space: normal; overflow-wrap: anywhere; line-height: 1.3;
}

/* The global `th` is centred; left-align the headers whose cell content is
   left-aligned so each title lines up with its column. */
.lxd-inst-table th[data-col="name"],
.lxd-inst-table th[data-col="ipv4"],
.lxd-inst-table th[data-col="image"],
.lxd-inst-table th[data-col="datastore"],
.lxd-inst-table th[data-col="cpu"],
.lxd-inst-table th[data-col="ram"],
.lxd-inst-table th[data-col="fs"] { text-align: left; }

.nav-compute-instance {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 20px 7px 24px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
  border-radius: 0;
  transition: background .12s;
  overflow: hidden;
}

.nav-compute-instance:hover {
  background: var(--surface2);
  color: var(--text);
}

.nav-compute-instance.active {
  background: var(--surface2);
  color: var(--accent);
}

.sidebar-section {
  padding: 6px 12px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--text-3);
  text-transform: uppercase;
  margin-top: 12px;
}

/* A clickable section header (Compute / Services / Networking) that IS the page
   you are on. Same highlight language as .nav-item.active below, so "where am I"
   reads identically whether you picked a page or a section. `body ` prefix so it
   outranks the per-theme .sidebar-section colour overrides. */
body .sidebar-section.nav-root-active {
  background: rgba(10, 132, 255, 0.12);
  color: var(--accent-2);
}

.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 20px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  border-radius: 0;
  transition: background 0.1s, color 0.1s;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
}
.nav-item:hover { background: var(--surface2); color: var(--text); }
.nav-item.active {
  background: rgba(10, 132, 255, 0.12);
  color: var(--accent-2);
  font-weight: 600;
}

/* Menu entries always stay on one line — the bold active state must not
   push a long label ("Groups & countries", "Intrusion detection") to a
   second row: a little of the right padding goes to the text, the active
   label is tightened by the width bold adds, and ellipsis is the last resort. */
.nav-item { padding-right: 10px; }
.nav-label { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-item.active .nav-label { letter-spacing: -0.012em; }

.nav-icon { width: 18px; text-align: center; font-size: 16px; opacity: 0.8; }

/* Self-hosted SVG icons (see static/icons.js). Inherit text color; align to
   the baseline of adjacent text so they sit correctly in nav items, buttons,
   table cells, and headings. */
.znas-icon { display: inline-block; vertical-align: middle; flex-shrink: 0; }
.nav-icon .znas-icon { vertical-align: middle; }

.nav-dot {
  position: absolute !important;
  right: 5px !important;
  top: 5% !important;
  height: 90% !important;
  width: 3px !important;
  border-radius: 2px !important;
  margin: 0 !important;
  flex-shrink: 0 !important;
}

/* VM/Container tags (v6.6.19) — color bullets in the compute tree */
.nav-tag-dots {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-right: 9px; /* clear the absolute status bar at right:5px */
  flex-shrink: 0;
}
.nav-tag-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}
.nav-tag-more {
  font-size: 9px;
  color: var(--text-3);
  line-height: 1;
}

/* Collapsible group headers in the compute tree */
.nav-group-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 20px 6px 16px;
  cursor: pointer;
  user-select: none;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
}
.nav-group-header:hover { color: var(--text-2); }
.nav-group-caret {
  font-size: 9px;
  width: 9px;
  flex-shrink: 0;
  transition: transform .12s;
}
/* By-Tag rows: a single tag-colored triangle replacing the caret + dot.
   Points right when collapsed, rotates to point down when open. */
.nav-group-tri {
  width: 0;
  height: 0;
  flex-shrink: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 8px solid currentColor; /* color set inline per tag */
  transition: transform .12s;
  transform-origin: 3px center;
}
.nav-group-tri.open { transform: rotate(90deg); }
.nav-group-swatch {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.nav-group-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.nav-group-count {
  font-size: 10px;
  font-weight: 600;
  color: var(--text-3);
  background: var(--surface2);
  border-radius: 8px;
  padding: 0 6px;
  flex-shrink: 0;
}
.nav-group-body.collapsed { display: none; }

/* Tag pills (instance detail header + table cells) */
.tag-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  padding: 3px 7px;
  border-radius: 10px;
  color: #fff;
  white-space: nowrap;
}
.tag-pill .tag-pill-x {
  cursor: pointer;
  opacity: 0.75;
  font-size: 12px;
  margin-left: 1px;
}
.tag-pill .tag-pill-x:hover { opacity: 1; }
.tag-pill.tag-pill-clickable { cursor: pointer; }
#ci-tags {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.tag-add-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 10px;
  border: 1px dashed var(--border);
  color: var(--text-3);
  background: none;
  cursor: pointer;
}
.tag-add-chip:hover { color: var(--text); border-color: var(--text-3); }
.tag-cell-pills { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; }

/* Tag color-swatch popover */
.tag-swatch-pop {
  position: absolute;
  z-index: 4000;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0,0,0,.4);
  padding: 10px;
  display: grid;
  grid-template-columns: repeat(6, 20px);
  gap: 6px;
}
.tag-swatch-pop .sw {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid transparent;
}
.tag-swatch-pop .sw:hover { border-color: var(--text); }

/* Main content */
.main-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Wrapper that puts the CPU gauge panel to the left of topbar+io-bar */
#topbar-io-wrapper {
  display: flex;
  flex-direction: row;
  flex-shrink: 0;
}

/* CPU process gauge panel */
#cpu-gauge-panel {
  width: 36px;
  flex-shrink: 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 6px 4px 8px;
  gap: 4px;
  cursor: default;
  user-select: none;
}

/* MEM process gauge panel — same layout as CPU */
#mem-gauge-panel {
  width: 36px;
  flex-shrink: 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 6px 4px 8px;
  gap: 4px;
  cursor: default;
  user-select: none;
}

/* SWAP process gauge panel (v6.5.3+) — same chrome as CPU/MEM. Sits to the
   right of MEM. Hidden via inline style when no swap device is configured. */
#swap-gauge-panel {
  width: 36px;
  flex-shrink: 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 6px 4px 8px;
  gap: 4px;
  cursor: default;
  user-select: none;
}

/* SWAP gauge tooltip — same look as #mem-gauge-tooltip. */
#swap-gauge-tooltip {
  position: fixed;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  min-width: 420px;
  z-index: 10000;
  display: none;
  box-shadow: var(--shadow-lg);
  font-size: 12px;
  pointer-events: none;
}

#mem-gauge-tooltip {
  position: fixed;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  min-width: 420px;
  z-index: 10000;
  display: none;
  box-shadow: var(--shadow-lg);
  font-size: 12px;
  pointer-events: none;
}
.cpu-gauge-title {
  font-size: 9px;
  font-weight: 700;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  writing-mode: vertical-lr;
  transform: rotate(180deg);
  flex-shrink: 0;
}
.cpu-gauge-bar {
  flex: 1;
  width: 16px;
  background: var(--surface2);
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  flex-direction: column-reverse;
  min-height: 0;
}
.cpu-gauge-seg {
  width: 100%;
  flex-shrink: 0;
  transition: height 0.5s ease;
}
/* v6.5.3+: traffic-light percentage readout under each vertical bar.
   Two-digit number (max 4 chars including the % sign), monospaced for
   stable width as the value ticks. JS toggles --pct-color via a class:
   .pct-ok (green) / .pct-warn (yellow) / .pct-crit (red). */
.bar-pct-readout {
  font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-align: center;
  width: 100%;
  margin-top: 2px;
  flex-shrink: 0;
  color: var(--text-3);
  transition: color 0.3s ease;
}
.bar-pct-readout.pct-ok   { color: #3fb950; }
.bar-pct-readout.pct-warn { color: #f0b429; }

/* Web Login Timeout dropdowns only have 2-3 options each — the global
   .cs-list { max-height: 260px; overflow-y: auto } would otherwise paint
   a scrollbar even though the content is well short of the cap. Force
   the list to size purely to its options. !important defeats both the
   inline <style> rule that loads later AND any iOS Safari quirk that
   re-derives an overflow gutter on touch-scrollable elements. */
.cs-panel[data-cs-id="ws-mode"]      .cs-list,
.cs-panel[data-cs-id="ws-idle-unit"] .cs-list {
  max-height: none !important;
  overflow: visible !important;
  -webkit-overflow-scrolling: auto !important;
}

/* Compact-Header view (per-browser, persisted in localStorage). Hides
   the multi-row top capacity bar + the three live IO charts and reduces
   the CPU/MEM/SWAP gauge panels to just their percentage readout, so
   the entire header collapses to a single row containing the pool
   selector, pool summary line, and the small CPU/MEM/SWAP numbers. */
body.compact-header #pool-bar,
body.compact-header #pool-legend,
body.compact-header #pool-tip,
body.compact-header #io-bar,
body.compact-header #io-chart-tooltip {
  display: none !important;
}
body.compact-header .cpu-gauge-bar,
body.compact-header .cpu-gauge-title {
  display: none !important;
}
/* Strip all the breathing room intended for the multi-row layout — bottom
   margin on the inner row, the topbar's vertical padding (8px top+bottom),
   and the gauge-panel vertical padding (6+8px). The single visible row is
   then bounded purely by its content height (~22 px text + 6 px padding). */
body.compact-header .topbar {
  padding-top: 3px;
  padding-bottom: 3px;
}
body.compact-header .topbar-inner {
  /* Drop the multi-row grid (1fr auto 1fr) — it squeezes the centre cell
     and forces the long pool-summary text to wrap onto 2-3 lines. Flex
     instead, with the summary growing to fill leftover space and ellipsing
     if the viewport is narrower than its full content.
     NOTE: do NOT set overflow:hidden here — that clips the absolutely-
     positioned theme-picker and user-menu dropdowns (the panel sits below
     the topbar boundary), which on iPad / small touch screens makes both
     menus impossible to open. The summary has its own overflow:hidden
     for ellipsis, so this container can stay overflow-visible. */
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 0;
  flex-wrap: nowrap;
}
body.compact-header .topbar-title {
  flex-shrink: 0;
}
body.compact-header #pool-summary {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
}
/* Anchor the UPS / theme / activity / user-menu cluster to the right edge
   even after the title and summary are hidden by the narrow-viewport rule
   below. In the original grid the cell used `justify-self:end`; in flex
   that's a no-op, so we use `margin-left:auto` to push the cluster right
   regardless of which siblings are visible. */
body.compact-header .topbar-inner > div:last-child {
  margin-left: auto;
}
/* Below ~1100 px viewport, the gauges + pool-title + long summary + theme/
   activity/user buttons no longer fit on one line. Hide the title and the
   pool-summary entirely so the remaining items stay on a single row instead
   of wrapping into a second line. The pool selector dropdown lives in the
   sidebar (under the hostname label), so functionality isn't lost. */
@media (max-width: 1100px) {
  body.compact-header .topbar-title,
  body.compact-header #pool-summary { display: none !important; }
}
body.compact-header #cpu-gauge-panel,
body.compact-header #mem-gauge-panel,
body.compact-header #swap-gauge-panel {
  padding: 2px 4px;
  justify-content: center;
}
body.compact-header .bar-pct-readout {
  margin-top: 0;
  font-size: 11px;
  line-height: 1.1;
}
.bar-pct-readout.pct-crit { color: #ff453a; }

/* ===== SysInfo modal — colourful hardware overview ===========================
   The modal renders a stylised motherboard with each component (CPU, DIMMs,
   chipset, BIOS chip, storage controllers + their disks) as a discrete
   "chip" card glowing in its own accent colour. Tron theme overrides the
   palette to neon cyan/magenta for the in-house look. */

.sysinfo-modal { background: var(--surface); }

/* Container: motherboard PCB tint, subtle grid lines for tech-blueprint feel. */
.sysinfo-board {
  position: relative;
  border-radius: 14px;
  padding: 22px;
  background:
    radial-gradient(1200px 600px at 30% 20%, rgba(0,180,216,0.06), transparent 60%),
    radial-gradient(800px 500px at 80% 80%, rgba(48,209,88,0.06), transparent 60%),
    linear-gradient(180deg, rgba(0,0,0,0.18), rgba(0,0,0,0.02));
  border: 1px solid var(--border);
  box-shadow: inset 0 0 60px rgba(0,0,0,0.25);
}
.sysinfo-board::before {
  content: "";
  position: absolute; inset: 12px;
  background-image:
    linear-gradient(rgba(255,255,255,0.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.025) 1px, transparent 1px);
  background-size: 24px 24px;
  border-radius: 10px;
  pointer-events: none;
  opacity: 0.5;
}

/* Section grid: top row split into CPU + RAM, then chassis identity row, then
   storage controllers list. Auto-fits when the modal is narrow. */
.sysinfo-grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 16px;
  position: relative;
  z-index: 1;
}
.sysinfo-grid-row { grid-column: 1 / -1; }
@media (max-width: 760px) {
  .sysinfo-grid { grid-template-columns: 1fr; }
}

/* A "chip" is one component card. Each gets its own accent via --chip-accent. */
.sysinfo-chip {
  --chip-accent: var(--accent);
  position: relative;
  background: linear-gradient(180deg, rgba(255,255,255,0.04), rgba(0,0,0,0.10));
  border: 1px solid color-mix(in srgb, var(--chip-accent) 38%, transparent);
  border-radius: 12px;
  padding: 14px 16px 14px;
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.02) inset,
    0 0 26px color-mix(in srgb, var(--chip-accent) 18%, transparent);
}
.sysinfo-chip-label {
  position: absolute;
  top: -10px; left: 14px;
  background: var(--surface);
  padding: 2px 9px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--chip-accent);
  border: 1px solid color-mix(in srgb, var(--chip-accent) 50%, transparent);
  border-radius: 6px;
  display: flex; align-items: center; gap: 6px;
}
.sysinfo-chip-label .sysinfo-chip-icon { font-size: 12px; }

/* Per-component accents */
.sysinfo-chip.is-cpu       { --chip-accent: #ef4444; }
.sysinfo-chip.is-ram       { --chip-accent: #06b6d4; }
.sysinfo-chip.is-mobo      { --chip-accent: #22c55e; }
.sysinfo-chip.is-bios      { --chip-accent: #a855f7; }
.sysinfo-chip.is-system    { --chip-accent: #f59e0b; }
.sysinfo-chip.is-ctrl      { --chip-accent: #f97316; }

/* Headline value (model name / total RAM) */
.sysinfo-headline {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  margin-top: 6px;
  line-height: 1.35;
}
.sysinfo-subline {
  font-size: 12px;
  color: var(--text-2);
  margin-top: 4px;
  line-height: 1.5;
}

/* Specs grid: small label/value pairs under the headline. */
.sysinfo-specs {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 14px;
  row-gap: 4px;
  margin-top: 10px;
  font-size: 12px;
}
.sysinfo-specs dt {
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 10px;
  align-self: center;
}
.sysinfo-specs dd {
  margin: 0;
  color: var(--text-2);
  font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
  font-weight: 500;
}

/* DIMM strip: little upright sticks of memory drawn with CSS. */
.sysinfo-dimm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 10px;
  margin-top: 12px;
}
.sysinfo-dimm {
  position: relative;
  height: 86px;
  border-radius: 4px;
  border: 1px solid color-mix(in srgb, var(--chip-accent) 55%, transparent);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--chip-accent) 32%, transparent) 0 6px,
    color-mix(in srgb, var(--chip-accent) 14%, transparent) 6px 100%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 6px 8px 8px;
  box-shadow: 0 0 14px color-mix(in srgb, var(--chip-accent) 22%, transparent);
}
.sysinfo-dimm.is-empty {
  border-style: dashed;
  background: transparent;
  opacity: 0.45;
}
.sysinfo-dimm-locator {
  font-size: 9px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  position: absolute; top: 4px; left: 8px;
}
.sysinfo-dimm-size {
  font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
  font-weight: 700;
  font-size: 14px;
  color: var(--text);
}
.sysinfo-dimm-speed {
  font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
  font-size: 11px;
  color: var(--text-2);
  margin-top: 1px;
}
.sysinfo-dimm-vendor {
  font-size: 10px;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Controller card: header with model + lscpi class, list of attached disks. */
.sysinfo-ctrl {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.sysinfo-ctrl-card {
  border: 1px solid color-mix(in srgb, var(--chip-accent) 35%, transparent);
  border-radius: 10px;
  padding: 12px 14px;
  background: rgba(0,0,0,0.18);
}
.sysinfo-ctrl-head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px;
  margin-bottom: 8px;
}
.sysinfo-ctrl-model {
  font-weight: 700;
  font-size: 13px;
  color: var(--text);
}
.sysinfo-ctrl-class {
  font-size: 11px;
  color: var(--chip-accent);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
}
.sysinfo-ctrl-slot {
  font-family: 'SF Mono','Menlo','Consolas',monospace;
  font-size: 11px;
  color: var(--text-3);
}
.sysinfo-ctrl-disks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 8px;
}
.sysinfo-disk {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px;
  background: rgba(255,255,255,0.025);
  border-radius: 8px;
  border: 1px solid var(--border);
  font-size: 12px;
}
.sysinfo-disk-icon { font-size: 16px; line-height: 1; }
.sysinfo-disk-info { display:flex; flex-direction:column; min-width:0; flex:1; }
.sysinfo-disk-dev {
  font-family: 'SF Mono','Menlo','Consolas',monospace;
  font-weight: 700;
  color: var(--text);
}
.sysinfo-disk-meta {
  font-size: 11px;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Tron neon override: brighter accents + outer glow. */
body.tron .sysinfo-chip,
body.tron-legacy .sysinfo-chip {
  background: linear-gradient(180deg, rgba(0,210,255,0.04), rgba(0,0,0,0.18));
  box-shadow:
    0 0 0 1px rgba(0,210,255,0.08) inset,
    0 0 32px color-mix(in srgb, var(--chip-accent) 32%, transparent);
}
body.tron .sysinfo-chip.is-cpu,
body.tron-legacy .sysinfo-chip.is-cpu       { --chip-accent: #ff2d55; }
body.tron .sysinfo-chip.is-ram,
body.tron-legacy .sysinfo-chip.is-ram       { --chip-accent: #00f0ff; }
body.tron .sysinfo-chip.is-mobo,
body.tron-legacy .sysinfo-chip.is-mobo      { --chip-accent: #00ff95; }
body.tron .sysinfo-chip.is-bios,
body.tron-legacy .sysinfo-chip.is-bios      { --chip-accent: #c084fc; }
body.tron .sysinfo-chip.is-system,
body.tron-legacy .sysinfo-chip.is-system    { --chip-accent: #ffe600; }
body.tron .sysinfo-chip.is-ctrl,
body.tron-legacy .sysinfo-chip.is-ctrl      { --chip-accent: #ff9d00; }

/* SMBIOS type1 sub-section inside the Firmware row of VM Create / Edit.
   Renders as a tighter sibling of the existing collapsibles — matches the
   visual weight of `vm-hw-row` headers without re-implementing the click
   handler (uses native <details>/<summary>). */
.vm-smbios-details summary {
  cursor: pointer;
  user-select: none;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  padding: 6px 0;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  border-top: 1px dashed var(--border);
  margin-top: 6px;
}
.vm-smbios-details summary::-webkit-details-marker { display: none; }
/* Chevron drawn from the ZNAS icon set as a mask so it takes currentColor
   and stays consistent with znasIcon('chevron-right') used elsewhere. */
.vm-smbios-details summary::before {
  content: "";
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  background-color: var(--text-3);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E") center / 10px 10px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E") center / 10px 10px no-repeat;
  transition: transform 0.15s;
}
.vm-smbios-details[open] summary::before { transform: rotate(90deg); }
.vm-smbios-details summary:hover { color: var(--text); }

/* VM Edit modal — compact disks table. The default 13px/16px padding plus
   regular 14px input height pushes the disks list past 1300px wide, which
   overflows the modal on common 1366×768 / split-screen layouts. The
   tighter rules here cut the row height roughly in half and clip ZFS-path
   cells to a fixed width with ellipsis; the wrapper falls back to a
   horizontal scrollbar when even that isn't enough so every column stays
   reachable. */
.vm-edit-disks-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.vm-edit-disks-table {
  border-collapse: collapse;
  font-size: 11px;
}
.vm-edit-disks-table th,
.vm-edit-disks-table td {
  padding: 4px 6px;
  vertical-align: middle;
  border-top: 1px solid var(--border);
}
.vm-edit-disks-table thead th {
  padding: 4px 6px;
  font-size: 9px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-top: none;
  white-space: nowrap;
  color: var(--text-3);
  font-weight: 600;
  text-align: left;
}
.vm-edit-disks-table .input {
  padding: 2px 4px;
  height: 22px;
  font-size: 11px;
  line-height: 1.2;
}
.vm-edit-disks-table input[type="checkbox"] {
  margin: 0;
}

/* SysInfo CPU chip — P/E core breakdown highlights. Colours chosen so they
   read clearly on both light and dark surfaces; Tron picks up its neon palette. */
.sysinfo-pcore { color: #d97706; font-weight: 700; }
.sysinfo-ecore { color: #0891b2; font-weight: 700; }
body.tron .sysinfo-pcore, body.tron-legacy .sysinfo-pcore { color: #ffe600; text-shadow: 0 0 5px rgba(255,230,0,0.6); }
body.tron .sysinfo-ecore, body.tron-legacy .sysinfo-ecore { color: #00f0ff; text-shadow: 0 0 5px rgba(0,240,255,0.6); }

/* "Hybrid" badge in the CPU subline — pill background gives it AA contrast
   on both light surfaces (#fff/#f2f2f7) and dark ones (#001428–#1c1c1e). */
.sysinfo-hybrid-badge {
  display: inline-block;
  padding: 1px 8px;
  margin-left: 2px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(217,119,6,0.16);
  color: #b45309;
  border: 1px solid rgba(217,119,6,0.45);
}
body.light .sysinfo-hybrid-badge {
  background: rgba(180,83,9,0.12);
  color: #92400e;
  border-color: rgba(180,83,9,0.55);
}
body.tron .sysinfo-hybrid-badge,
body.tron-legacy .sysinfo-hybrid-badge {
  background: rgba(255,230,0,0.12);
  color: #ffe600;
  border-color: rgba(255,230,0,0.55);
  text-shadow: 0 0 6px rgba(255,230,0,0.55);
}

/* zram "compressed" segment of the MEM tooltip's first row. Default: bold,
   bright green so it pops against the regular tooltip text. Tron: glowing
   cyan to match the rest of the theme's neon palette. */
.mem-tooltip-compressed {
  color: #22c55e;
  font-weight: 800;
  text-shadow: 0 0 6px rgba(34,197,94,0.55);
}
body.tron .mem-tooltip-compressed,
body.tron-legacy .mem-tooltip-compressed {
  color: #00f0ff;
  font-weight: 800;
  text-shadow: 0 0 6px rgba(0,240,255,0.85), 0 0 12px rgba(0,210,255,0.55);
}

/* v6.5.3+: footer row inside each Virtualization-tab card holding the Apply
   button and a status message. Right-aligns the button, lets the message
   take the remaining width. Replaces the panel-wide Save / Reload footer. */
.virt-card-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.virt-card-msg {
  flex: 1;
  font-size: 12px;
  color: var(--text-3);
  min-height: 16px;
}
.virt-card-msg.ok    { color: #3fb950; }
.virt-card-msg.error { color: #ff453a; }
#cpu-gauge-tooltip {
  position: fixed;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  min-width: 420px;
  z-index: 10000;
  display: none;
  box-shadow: var(--shadow-lg);
  font-size: 12px;
  pointer-events: none;
}

#io-chart-tooltip {
  position: fixed;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  min-width: 200px;
  z-index: 10000;
  display: none;
  box-shadow: var(--shadow-lg);
  font-size: 12px;
  pointer-events: none;
  white-space: nowrap;
}

/* Top bar with pool chart — border-bottom only when io-bar is hidden */
.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 8px 16px;
  flex-shrink: 0;
}
/* When io-bar is visible it follows immediately and provides its own border-bottom,
   so suppress the topbar separator to keep capacity bar + IO graphs visually merged. */
.topbar:has(+ #io-bar.io-bar-visible) {
  border-bottom: none;
}

.topbar-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  margin-bottom: 10px;
}

.topbar-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  justify-self: start;
}

/* Pool bar chart — liquid cylinder */
.pool-bar-container {
  height: 44px;
  background: var(--surface2);
  border-radius: 22px;
  overflow: hidden;
  position: relative;
  display: flex;
  box-shadow:
    inset 0 2px 6px rgba(0,0,0,0.55),
    inset 0 -1px 3px rgba(255,255,255,0.04),
    0 1px 0 rgba(255,255,255,0.04);
}
/* Glass highlight overlay */
.pool-bar-container::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 22px;
  background: linear-gradient(
    180deg,
    rgba(255,255,255,0.16) 0%,
    rgba(255,255,255,0.04) 40%,
    transparent 60%
  );
  pointer-events: none;
  z-index: 5;
}

.pool-bar-segment {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  color: rgba(255,255,255,0.92);
  cursor: pointer;
  transition: filter 0.15s;
  white-space: nowrap;
  overflow: hidden;
  min-width: 0;
  position: relative;
  text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}
.pool-bar-segment:hover { filter: brightness(1.2); z-index: 4; }

.pool-bar-legend {
  display: flex;
  flex-wrap: nowrap;
  gap: 12px;
  margin-top: 8px;
  overflow: hidden;
}

.pool-bar-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-2);
  cursor: pointer;
  flex-shrink: 0;
}

.pool-bar-legend-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* Pages container — wraps all page divs, handles scrolling */
.pages-container {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
}

/* Individual page */
.page {
  padding: 28px 32px;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
}

/* ===== Tables ===== */
.table-wrap {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

table {
  width: 100%;
  border-collapse: collapse;
}

thead tr {
  background: var(--surface2);
}

th {
  padding: 11px 16px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-3);
  text-align: center;
  vertical-align: middle;
}

td {
  padding: 13px 16px;
  font-size: 14px;
  border-top: 1px solid var(--border);
}

tr:hover td { background: rgba(255,255,255,0.02); }

/* Physical Disks tables — many narrow columns (Type/Speed/Wearout/Temp/
   Power/SMART), so the default 16 px side padding wastes horizontal space.
   Tighten side padding and let the table size to content so columns sit
   next to each other instead of being stretched to fill the wrapper. */
.disks-table { width: auto; min-width: 100%; }
.disks-table th,
.disks-table td { padding-left: 8px; padding-right: 8px; }
.disks-table th:first-child,
.disks-table td:first-child { padding-left: 14px; }
.disks-table th:last-child,
.disks-table td:last-child  { padding-right: 14px; }

/* ===== User avatar ===== */
.avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--gradient);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  flex-shrink: 0;
}

/* ===== Modal ===== */
.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 24px;
}

.modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 480px;
  box-shadow: var(--shadow-lg);
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 28px 32px 20px;
  flex-shrink: 0;
}

.modal-close {
  background: none;
  border: none;
  color: var(--text-3);
  font-size: 20px;
  cursor: pointer;
  padding: 4px;
  line-height: 1;
  flex-shrink: 0;
}
.modal-close:hover { color: var(--text); }

/* ── Share availability toggle ─────────────────────────────────────────────── */
.share-toggle-track {
  display: inline-flex;
  align-items: center;
  width: 36px;
  height: 20px;
  border-radius: 10px;
  background: var(--surface2);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: background .2s, border-color .2s;
  flex-shrink: 0;
  position: relative;
}
.share-toggle-track.on {
  background: var(--accent-4);
  border-color: var(--accent-4);
}
.share-toggle-thumb {
  position: absolute;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--text-3);
  transition: left .2s, background .2s;
}
.share-toggle-track.on .share-toggle-thumb {
  left: 18px;
  background: #fff;
}

.modal-body {
  padding: 4px 32px 16px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
  scrollbar-gutter: stable;
}

/* Simple modals: <form> or direct <div> children after the header */
.modal > form,
.modal > div:not(.modal-header):not(.modal-body):not(.modal-footer) {
  flex: 1;
  min-height: 0;
  padding: 4px 32px;
}
.modal > form:last-child,
.modal > div:not(.modal-header):not(.modal-body):not(.modal-footer):last-child {
  padding-bottom: 28px;
}
/* Forms AND plain content divs may need to scroll on small screens so the
   action buttons at the bottom stay reachable. Without overflow-y on the
   plain-div case, a tall modal whose content lives in a bare <div> (not a
   <form> or .modal-body) clipped under .modal's overflow:hidden and the
   bottom buttons became unreachable in the phone sheet layout. */
.modal > form,
.modal > div:not(.modal-header):not(.modal-body):not(.modal-footer) {
  overflow-y: auto;
  scrollbar-gutter: stable;
  -webkit-overflow-scrolling: touch;
}

.modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 16px 32px 24px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

/* ===== Terminal Panel ===== */
/* Resize handle inside a term-panel gets the header surface color, not the terminal black */
.term-panel > .panel-resize-handle {
  background: var(--surface);
}

/* ===== Vertical resize handle (activity bar + terminal) ===== */
.panel-resize-handle {
  height: 6px;
  cursor: ns-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  user-select: none;
  position: relative;
  z-index: 1;
}
.panel-resize-handle::before {
  content: '';
  width: 36px;
  height: 3px;
  border-radius: 2px;
  background: var(--border);
  transition: background .15s, width .15s;
}
.panel-resize-handle:hover::before,
.panel-resize-handle.dragging::before {
  background: var(--text-3);
  width: 52px;
}

.term-panel {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  height: 600px;
  background: #0d0d0f;
  border-top: 2px solid var(--accent-2);
}
.term-panel.hidden { display: none; }
.term-panel-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
  height: 36px;
  flex-shrink: 0;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

/* ===== Activity Bar (bottom of main content) ===== */
.activity-bar {
  flex-shrink: 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
  transition: height 0.2s cubic-bezier(.4,0,.2,1);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.activity-bar.collapsed { height: 38px; }
.activity-bar.expanded  { height: 220px; }

.activity-bar-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 20px;
  height: 38px;
  cursor: pointer;
  flex-shrink: 0;
  user-select: none;
}
.activity-bar-header:hover { background: rgba(255,255,255,0.03); }
.activity-bar-title { font-size: 12px; font-weight: 600; color: var(--text-2); }
.activity-bar-body {
  flex: 1;
  overflow-y: auto;
  padding: 0 20px 12px;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.ab-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  font-size: 12px;
  line-height: 1.4;
}
.ab-row:last-child { border-bottom: none; }
.ab-time { color: var(--text-3); white-space: nowrap; min-width: 110px; }
.ab-icon { flex-shrink: 0; }
.ab-user { font-weight: 600; color: var(--text); }
.ab-action { color: var(--text-2); }
.ab-target { color: var(--text-3); }
.ab-job-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  font-size: 12px;
  color: var(--accent-2);
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.ab-tab-strip {
  display: flex;
  align-items: center;
  gap: 2px;
}
.ab-tab {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 11px;
  font-weight: 500;
  color: var(--text-3);
  padding: 3px 9px;
  border-radius: 4px;
  transition: color .15s, background .15s;
  line-height: 1.5;
  white-space: nowrap;
}
.ab-tab:hover { color: var(--text-2); background: rgba(255,255,255,0.05); }
.ab-tab.ab-tab-active { color: var(--text); background: rgba(255,255,255,0.09); font-weight: 600; }

/* ===== Prereqs Modal ===== */
#prereq-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  column-gap: 32px;
}
.prereq-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.prereq-name { font-weight: 600; }
.prereq-desc { font-size: 12px; color: var(--text-2); margin-top: 2px; }

/* Stream output box (install / update output) */
.stream-box {
  background: #0a0a0c;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px;
  font-family: "SF Mono", "Fira Code", monospace;
  font-size: 12px;
  color: #b4f0a0;
  height: 240px;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-all;
  margin-top: 12px;
}

/* ===== Misc utils ===== */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: 8px; }
.gap-3 { gap: 12px; }
.gap-4 { gap: 16px; }
.mt-2 { margin-top: 8px; }
.mt-4 { margin-top: 16px; }
.mb-4 { margin-bottom: 16px; }
.w-full { width: 100%; }
.hidden { display: none !important; }

/* ===== Responsive: slim icon-only sidebar on small screens ===== */
/* Legacy narrow-desktop rail: a 52px icon-only sidebar. Scoped to
   body:not(.is-phone) so a real phone (handled by the Mobile Shell block at the
   end of this file) gets the off-canvas drawer instead of the cramped rail. */
@media (max-width: 600px) {
  body:not(.is-phone) .sidebar {
    width: 52px;
    min-width: 52px;
    padding: 8px 0;
  }
  body:not(.is-phone) .sidebar-header {
    display: none;
  }
  body:not(.is-phone) .sidebar-section {
    display: none;
  }
  body:not(.is-phone) .nav-label {
    display: none;
  }
  body:not(.is-phone) .nav-item {
    justify-content: center;
    padding: 11px 0;
    gap: 0;
  }
  body:not(.is-phone) .nav-icon {
    width: auto;
    font-size: 20px;
    opacity: 0.85;
  }
  body:not(.is-phone) .nav-item.active .nav-icon {
    opacity: 1;
  }
  body:not(.is-phone) .sidebar-footer {
    display: none;
  }
  body:not(.is-phone) .nav-dot {
    display: none !important;
  }
  body:not(.is-phone) .sidebar-collapse-row {
    display: none;
  }
}

/* Collapse button is now merged into the Overview section header */
.sidebar-collapse-row {
  border-top: 1px solid var(--border);
  margin-top: 4px;
}

/* ===== Programmatic compact sidebar (mirrors the responsive media query above) ===== */
.sidebar.sidebar-compact {
  width: 52px;
  min-width: 52px;
  padding: 8px 0;
}
.sidebar.sidebar-compact .sidebar-header { display: none; }
.sidebar.sidebar-compact .sidebar-section { display: none; }
.sidebar.sidebar-compact .nav-label { display: none; }
.sidebar.sidebar-compact .nav-item { justify-content: center; padding: 11px 0; gap: 0; }
.sidebar.sidebar-compact .nav-icon { width: auto; font-size: 20px; opacity: 0.85; }
.sidebar.sidebar-compact .nav-item.active .nav-icon { opacity: 1; }
.sidebar.sidebar-compact .sidebar-footer { display: none; }
.sidebar.sidebar-compact .nav-dot { display: none !important; }
.sidebar.sidebar-compact .sidebar-collapse-row { display: none !important; } /* !important: the row carries an inline display:flex */
.sidebar.sidebar-compact #sidebar-expand-btn { display: flex !important; }

/* Compact sidebar — Compute/Networking collapse to single icons.
   The dynamic per-VM / per-bridge / per-datastore lists and their section
   headers are hidden; in their place a single icon (.compact-only) stands in.
   Clicking it expands the full menu so the user can pick a precise entry.
   Scoped to body:not(.is-phone): the phone drawer always shows the full tree,
   never the compact rail. */
.sidebar .compact-only { display: none; }
body:not(.is-phone) .sidebar.sidebar-compact .compact-only { display: flex; justify-content: center; padding: 11px 0; gap: 0; }
body:not(.is-phone) .sidebar.sidebar-compact .compact-only .nav-icon { width: auto; font-size: 20px; opacity: 0.85; }
body:not(.is-phone) .sidebar.sidebar-compact .nav-compute-instance,
body:not(.is-phone) .sidebar.sidebar-compact #nav-compute-instances,
body:not(.is-phone) .sidebar.sidebar-compact #nav-networking-bridges,
body:not(.is-phone) .sidebar.sidebar-compact #nav-datastores-list { display: none !important; }
body:not(.is-phone) .sidebar.sidebar-compact #nav-compute-root,
body:not(.is-phone) .sidebar.sidebar-compact #nav-networking-root,
body:not(.is-phone) .sidebar.sidebar-compact #nav-datastores-root { display: none !important; }
/* Terminal job-count badge is noise in the narrow rail. */
body:not(.is-phone) .sidebar.sidebar-compact #nav-term-badge { display: none !important; }
/* The storage/compute/networking switcher stacks vertically to fit the 52px rail. */
body:not(.is-phone) .sidebar.sidebar-compact .sidebar-mode-selector { flex-direction: column; align-items: center; gap: 6px; padding: 6px 0 2px; }

/* Temporary expand = OVERLAY: the full menu floats over the page content (it does
   not push it). The 52px rail's footprint is preserved so nothing shifts, and a
   backdrop dims the rest (click it to collapse). */
#sidebar-overlay-backdrop { display: none; }
body.sidebar-overlay #sidebar-overlay-backdrop {
  display: block;
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 1100;
}
body.sidebar-overlay .app-layout { padding-left: 52px; }   /* keep the rail's space */
body.sidebar-overlay #sidebar {
  position: fixed; top: 0; left: 0;
  height: 100dvh;
  width: 190px; min-width: 190px;
  z-index: 1200;
  box-shadow: 6px 0 28px rgba(0,0,0,0.45);
  overflow-y: auto;
}

/* ===== Capacity Trend page ===== */
.cap-range-btn {
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  transition: background .15s, color .15s;
}
.cap-range-btn:hover {
  background: var(--surface-2);
  color: var(--text);
}
.cap-range-btn.active {
  background: rgba(10,132,255,0.15);
  border-color: rgba(10,132,255,0.4);
  color: var(--accent-2);
}
.cap-selector-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px;
  min-width: 340px;
  box-shadow: var(--shadow);
}

/* Capacity Trend — inner tab bar */
.cap-tab {
  padding: 8px 18px;
  font-size: 13px;
  font-weight: 600;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-2);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  border-radius: 0;
  transition: color .15s, border-color .15s;
}
.cap-tab:hover { color: var(--text); }
.cap-tab.active {
  color: var(--accent-2);
  border-bottom-color: var(--accent-2);
}

/* Responsive scan-time label in Dataset Tree Usage tab */
.cap-scan-short { display: none; }
@media (max-width: 680px) {
  .cap-scan-long  { display: none; }
  .cap-scan-short { display: inline; }
}

/* ===== Row burger menu ===== */
.row-menu-btn {
  background: none; border: 1px solid transparent; border-radius: var(--radius-sm);
  color: var(--text-2); cursor: pointer; font-size: 18px; line-height: 1;
  padding: 2px 8px; transition: background .15s, border-color .15s;
}
.row-menu-btn:hover { background: var(--surface2); border-color: var(--border); color: var(--text-1); }
.row-menu-dropdown {
  /* appended to <body> with position:fixed set inline — escapes table overflow clipping */
  z-index: 9999;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); min-width: 170px; overflow: hidden;
}
.row-menu-dropdown button {
  display: block; width: 100%; text-align: left; background: none; border: none;
  color: var(--text); cursor: pointer; font-size: 13px; padding: 9px 14px;
  transition: background .12s;
}
.row-menu-dropdown button:hover:not(:disabled) { background: var(--surface2); }
/* A menu action that currently does nothing (e.g. "Apply now" with no pending
   network change) stays visible but clearly inert. */
.row-menu-dropdown button:disabled { opacity: 0.4; cursor: not-allowed; }
.row-menu-dropdown button.danger { color: var(--accent-danger); }
.row-menu-dropdown .menu-sep {
  border: none; border-top: 1px solid var(--border); margin: 3px 0;
}

/* ===== ARC Level 1 Modal ===== */
.arc-stat-card {
  background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px 14px; display: flex; flex-direction: column; gap: 4px;
}
.arc-stat-card .arc-card-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-3);
  display: flex; align-items: center; gap: 4px;
}
.arc-stat-card .arc-card-value {
  font-size: 18px; font-weight: 700; color: var(--text-1);
}
.arc-bar-row { margin-bottom: 8px; }
.arc-bar-row .arc-bar-label {
  display: flex; justify-content: space-between; font-size: 12px; color: var(--text-2);
  margin-bottom: 4px;
}
.arc-bar-track {
  height: 7px; background: var(--surface2); border-radius: 4px; overflow: hidden;
}
.arc-bar-fill {
  height: 100%; border-radius: 4px; transition: width .4s ease;
}
.arc-max-opt {
  padding: 10px 14px; cursor: pointer; border-bottom: 1px solid var(--border);
  transition: background .12s;
}
.arc-max-opt:last-child { border-bottom: none; }
.arc-max-opt:hover { background: var(--surface2); }
.arc-max-opt-active { background: rgba(10,132,255,0.10); }
.arc-max-opt-active:hover { background: rgba(10,132,255,0.15); }

/* iSCSI SVG icon — invert for dark theme (SVG has dark strokes) */
.iscsi-icon { width: 16px; height: 16px; display: inline-block; vertical-align: middle; filter: invert(1) brightness(1.2); }
body.light .iscsi-icon { filter: none; }

/* UPS top-bar widget */
.ups-topbar-widget { transition: background .15s; border-radius: var(--radius-sm); }
.ups-topbar-widget:hover { background: var(--surface2); }
.ups-state-badge {
  font-size: 10px; font-weight: 700; padding: 1px 4px;
  border-radius: 3px; white-space: nowrap;
}
.ups-badge-online  { background: rgba(48,209,88,0.18);  color: #30d158; }
.ups-badge-batt    { background: rgba(255,159,10,0.18);  color: #ff9f0a; }
.ups-badge-low     { background: rgba(255,69,58,0.18);   color: #ff453a; }
@keyframes upsBlink { 0%,100% { opacity:1; } 50% { opacity:0; } }

/* ===== InterLink ===== */
#hostname-dropdown-wrapper { position: relative; display: block; }


.hdd-current {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px; background: var(--surface2);
  font-weight: 600; font-size: 0.875rem;
}
.hdd-badge {
  margin-left: auto; font-size: 0.7rem; font-weight: 400;
  color: var(--text-3); background: var(--border);
  border-radius: 4px; padding: 1px 6px;
}

.hdd-server {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 14px; cursor: pointer; font-size: 1rem;
}
.hdd-server:hover { background: var(--surface2); }

.hdd-hostname { flex: 1; }
.hdd-url { font-size: 0.8rem; color: var(--text-3); }

.hdd-divider { border-top: 1px solid var(--border); margin: 4px 0; }

.hdd-manage {
  padding: 9px 14px; cursor: pointer;
  font-size: 0.9rem; color: var(--accent-2);
  font-weight: 500;
}
.hdd-manage:hover { background: var(--surface2); }

.status-dot {
  width: 8px; height: 8px; border-radius: 50%;
  flex-shrink: 0; display: inline-block;
}
.status-dot.online   { background: #22c55e; }
.status-dot.offline  { background: #ff453a; }
.status-dot.pending  { background: var(--border); }

.hdd-server.offline {
  cursor: not-allowed;
  pointer-events: none;
}
.hdd-server.offline .hdd-hostname,
.hdd-server.offline .hdd-url {
  opacity: 0.45;
}

.interlink-table { width: 100%; border-collapse: collapse; margin-top: 4px; }
.interlink-table th {
  text-align: left; padding: 8px 10px; font-size: 0.8rem;
  color: var(--text-3); border-bottom: 1px solid var(--border);
}
.interlink-table td {
  padding: 10px 10px; font-size: 0.875rem;
  border-bottom: 1px solid var(--border); vertical-align: middle;
}
.interlink-table tr:last-child td { border-bottom: none; }
.interlink-table .col-status { width: 32px; }
.interlink-table .col-actions { width: 80px; text-align: right; }

/* Sudoers diff rows */
.sudoers-diff-row           { display:flex; gap:12px; padding:10px 0; border-bottom:1px solid var(--border); align-items:flex-start; }
.sudoers-diff-row:last-child { border-bottom:none; }
.sudoers-row-prefix         { font-size:1.1rem; font-weight:700; width:18px; flex-shrink:0; line-height:1.4; }
.sudoers-tr                 { border-bottom:1px solid var(--border); transition:background .15s; }
.sudoers-tr:last-child      { border-bottom:none; }
.sudoers-radio              { background:none; border:none; cursor:pointer; font-size:18px; line-height:1;
                               padding:2px 8px; transition:color .1s; }
.sudoers-radio:hover        { opacity:.8; }
.sudoers-section-label      { font-size:.72rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
                               color:var(--text-3); margin:14px 0 6px; }

/* ── File Browser ─────────────────────────────────────────────────────────── */
.fb-breadcrumb         { display:flex; align-items:center; gap:6px; padding:10px 16px;
                         border-bottom:1px solid var(--border); flex-wrap:wrap;
                         font-size:.85rem; }
.fb-breadcrumb a       { color:var(--accent-2); text-decoration:none; cursor:pointer; }
.fb-breadcrumb a:hover { text-decoration:underline; }
.fb-breadcrumb .sep    { color:var(--text-3); }
.fb-table-wrap         { overflow-y:auto; max-height:calc(72vh - 130px); }
.fb-name-link          { cursor:pointer; color:var(--accent-2); text-decoration:none; }
.fb-name-link:hover    { text-decoration:underline; }
.fb-target-path        { font-size:.8rem; color:var(--text-3); word-break:break-all;
                         margin:0 0 14px; }
.fb-chmod-table        { width:100%; margin-bottom:4px; }
.fb-chmod-table td,
.fb-chmod-table th     { text-align:center; padding:4px 8px; }
.fb-chmod-table td:first-child { text-align:left; font-weight:500; width:70px; }

.ci-related-row:hover  { background: var(--surface2); border-radius: 4px; }

/* ── Settings Tabs ─────────────────────────────────────────────────────────── */
.settings-tab-bar      { display:flex; gap:4px; margin-bottom:20px;
                         border-bottom:2px solid var(--border); padding-bottom:0; }
.settings-tab          { background:none; border:none; border-bottom:2px solid transparent;
                         margin-bottom:-2px; padding:8px 20px; font-size:14px; font-weight:500;
                         color:var(--text-2); cursor:pointer; border-radius:4px 4px 0 0;
                         transition:color .15s, border-color .15s; }
.settings-tab:hover    { color:var(--text-1); background:var(--surface2); }
.settings-tab.active   { color:var(--accent-2); border-bottom-color:var(--accent-2);
                         background:none; }

/* ════════════════════════ Live Storage Map (v6.6.3) ════════════════════════ */
/* Theme-aware palette. Dark themes keep the bright neon HUD set; the light theme
   gets darker, higher-contrast hues. Box fills, text and rails follow the active
   theme's own variables so the map stays readable on every theme. */
:root{
  --map-fill:#0a1018;               /* node box fill — near-black HUD on dark themes */
  --map-disk:#22d3ee; --map-pool:#a855f7; --map-dataset:#2dd4bf; --map-mergerfs:#84cc16;
  --map-consumer:#f59e0b; --map-vm:#ec4899; --map-remote:#38bdf8; --map-docker:#4aa3df;
}
body.light, body.dawn{       /* every light-background theme uses the dark-hue set */
  --map-fill:var(--surface);        /* light surface so dark text reads on it */
  --map-disk:#0e7490; --map-pool:#7c3aed; --map-dataset:#0f766e; --map-mergerfs:#4d7c0f;
  --map-consumer:#b45309; --map-vm:#be185d; --map-remote:#0369a1; --map-docker:#1d6fa5;
}
.map-toolbar{display:flex;align-items:center;justify-content:space-between;gap:16px;
  margin-bottom:14px;flex-wrap:wrap;}
.map-legend{display:flex;gap:14px;flex-wrap:wrap;font-size:12px;color:var(--text-3);}
.map-legend-item{display:inline-flex;align-items:center;gap:6px;}
.map-legend-item i{width:10px;height:10px;border-radius:3px;display:inline-block;
  box-shadow:0 0 6px currentColor;}
.map-legend-item[data-layer="remote"]   i{background:var(--map-remote);color:var(--map-remote);}
.map-legend-item[data-layer="consumer"] i{background:var(--map-consumer);color:var(--map-consumer);}
.map-legend-item[data-layer="vm"]        i{background:var(--map-vm);color:var(--map-vm);}
.map-legend-item[data-layer="dataset"]   i{background:var(--map-dataset);color:var(--map-dataset);}
.map-legend-item[data-layer="mergerfs"]  i{background:var(--map-mergerfs);color:var(--map-mergerfs);}
.map-legend-item[data-layer="pool"]      i{background:var(--map-pool);color:var(--map-pool);}
.map-legend-item[data-layer="disk"]      i{background:var(--map-disk);color:var(--map-disk);}
.map-toolbar-right{display:flex;align-items:center;gap:12px;}
.map-status{font-size:12px;color:var(--text-3);font-variant-numeric:tabular-nums;}
/* zoom controls (− / level / + / fullscreen) */
.map-zoom{display:inline-flex;align-items:stretch;
  border:1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  border-radius:8px;overflow:hidden;}
.map-zoom-btn{background:color-mix(in srgb, var(--accent-2) 12%, transparent);
  border:none;color:var(--accent-2);font-size:13px;font-weight:600;line-height:1;
  padding:5px 9px;cursor:pointer;}
.map-zoom-btn+.map-zoom-btn{border-left:1px solid color-mix(in srgb, var(--accent-2) 25%, transparent);}
.map-zoom-btn:hover{background:color-mix(in srgb, var(--accent-2) 26%, transparent);}
.map-zoom-lvl{min-width:46px;font-variant-numeric:tabular-nums;
  font-size:11px;letter-spacing:.02em;}

/* Height is set precisely in JS (_mapSizeViewport) from the element's real top
   so the map fits the visible area exactly; this calc is only a pre-JS fallback. */
.map-viewport{position:relative;overflow:auto;height:calc(100vh - 300px);min-height:120px;
  background:transparent;border:none;
  /* JS owns pan + pinch-zoom on touch, so suppress the browser's own gestures
     (page pinch-zoom / two-finger scroll). Wheel + scrollbars still work. */
  touch-action:none;-webkit-user-select:none;user-select:none;}
/* Fullscreen (the button in the zoom group): paint the theme background instead of the
   browser's default black fullscreen backdrop. */
.map-viewport:fullscreen{background:var(--bg);}
.map-viewport:-webkit-full-screen{background:var(--bg);} /* prefixed Safari (separate rule: an unknown pseudo would void a shared one) */
/* Pseudo-fullscreen for platforms without an element-fullscreen API (iOS
   Safari): fill the browser window instead. Below the bg menu (z 99999). */
.map-viewport.map-vp-fullwindow{position:fixed;inset:0;z-index:9000;background:var(--bg);}
/* Floating exit button — the toolbar is not visible while the map is full
   screen, and phones have no Esc key. Hidden otherwise. */
.map-fs-exit{display:none;position:fixed;top:12px;right:12px;z-index:9500;
  width:36px;height:36px;align-items:center;justify-content:center;
  border-radius:10px;cursor:pointer;font-size:15px;line-height:1;
  background:color-mix(in srgb, var(--accent-2) 14%, var(--surface));
  border:1px solid color-mix(in srgb, var(--accent-2) 40%, transparent);
  color:var(--accent-2);}
.map-fs-exit:hover{background:color-mix(in srgb, var(--accent-2) 28%, var(--surface));}
.map-viewport:fullscreen .map-fs-exit{display:flex;}
.map-viewport:-webkit-full-screen .map-fs-exit{display:flex;}
.map-viewport.map-vp-fullwindow .map-fs-exit{display:flex;}
/* In-fullscreen zoom control: the toolbar (with the −/%/+ buttons) is a sibling
   of the viewport, so it's not visible once the viewport itself goes
   fullscreen. This clone lives INSIDE the viewport and shows only in fullscreen,
   pinned top-right beside the exit button. Reuses .map-zoom pill styling. */
.map-fs-zoom{display:none;position:fixed;top:12px;right:56px;z-index:9500;}
.map-viewport:fullscreen .map-fs-zoom{display:inline-flex;}
.map-viewport:-webkit-full-screen .map-fs-zoom{display:inline-flex;}
.map-viewport.map-vp-fullwindow .map-fs-zoom{display:inline-flex;}
#map-svg{display:block;background:transparent;}
/* Graph-paper background (v6.7.4): minor 16px squares, stronger line every
   5th. Colours derive from the theme's muted text colour so the grid stays
   subtle on light, dark and exotic themes; dimmed further under the HUD glow. */
.map-grid{pointer-events:none;}
.map-grid-minor{stroke:color-mix(in srgb, var(--text-3) 14%, transparent);stroke-width:1;}
.map-grid-major{stroke:color-mix(in srgb, var(--text-3) 26%, transparent);stroke-width:1;}
.map-hud .map-grid{opacity:.35;}
/* below 50% zoom the minor squares collapse into noise — majors only */
.map-zoom-tiny .map-grid-minor{display:none;}
/* Star placement (d3-force): nodes are draggable */
.map-force .map-node, .map-force .net-node{cursor:grab;}
.map-force .map-node:active, .map-force .net-node:active{cursor:grabbing;}
.map-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:var(--text-3);font-size:14px;}

/* header */
.map-header .map-host{fill:var(--text);font-size:15px;font-weight:700;}
.map-header .map-host-stats{fill:var(--text-3);font-size:11px;font-variant-numeric:tabular-nums;}
.map-header .map-peers{fill:var(--map-remote);font-size:11px;}

/* links — orthogonal energy conduits */
.map-link-rail{fill:none;stroke:var(--border);stroke-width:6;
  stroke-linecap:round;stroke-linejoin:round;}
.map-link{fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:5 7;animation:map-flow-up 1.4s linear infinite;}
.map-link.down{animation-name:map-flow-down;}
.map-link.idle{animation:none;opacity:.3;}
.map-link.l5{stroke:var(--map-disk);}
.map-link.l4{stroke:var(--map-pool);}
.map-link.l3{stroke:var(--map-dataset);}
.map-link.l2{stroke:var(--map-mergerfs);}
.map-link.l1{stroke:var(--map-consumer);}
/* VM self-mount conduit (share ↔ guest that mounts it) */
.map-peerlink{stroke:var(--map-vm);stroke-dasharray:4 5;opacity:.85;}
.map-hud .map-peerlink{filter:drop-shadow(0 0 4px var(--map-vm));}
/* label sits over the SVG background → halo it with the page bg so it reads */
.map-link-label{fill:var(--text);font-size:10px;font-variant-numeric:tabular-nums;
  paint-order:stroke;stroke:var(--bg);stroke-width:3px;}
@keyframes map-flow-up  {to{stroke-dashoffset:-24;}}
@keyframes map-flow-down{to{stroke-dashoffset: 24;}}

/* nodes — glass + neon */
/* NOTE: never put a CSS `transform` on .map-node — it overrides the SVG
   transform="translate(x,y)" attribute, snapping the node to the origin and
   causing a hover shake loop. Hover feedback is glow-only. */
.map-node{--hue:var(--map-remote);cursor:default;}
/* Box widths come from canvas measureText (_tw), which cannot see CSS
   letter-spacing — the fancy themes' body-level tracking (e.g. 0.03em) would
   inherit into SVG text and make glyphs wider than measured, so long names
   spill past the box edge. Dense map text never wants tracking anyway. */
.map-node text,.net-node text{letter-spacing:normal;}
/* Actionable nodes (open detail / edit / instance view) show a pointer. */
.map-node--dataset,.map-node--mergerfs,.map-node--consumer,.map-node--vm{cursor:pointer;}
/* Opaque base occludes any link drawn behind the box, so conduits read as
   running *between* boxes, never through them. The tint + border sit on top. */
.map-node .map-node-base{fill:var(--map-fill);}
.map-node .map-node-bg{fill:color-mix(in srgb, var(--hue) 12%, var(--map-fill));
  stroke:var(--hue);stroke-width:1.2;}
.map-node .map-node-title{fill:var(--text);font-size:11px;font-weight:600;}
.map-node .map-node-sub {fill:var(--text-2);font-size:9.5px;}
.map-node .map-node-sub2{fill:var(--text-3);font-size:9.5px;font-variant-numeric:tabular-nums;}
.map-node--disk   {--hue:var(--map-disk);}
.map-node--pool   {--hue:var(--map-pool);}
.map-node--dataset{--hue:var(--map-dataset);}
.map-node--mergerfs{--hue:var(--map-mergerfs);}
.map-node--consumer{--hue:var(--map-consumer);}
.map-node--vm     {--hue:var(--map-vm);}
.map-node--remote {--hue:var(--map-remote);}
.map-node--docker {--hue:var(--map-docker);}
/* docker health dot (top-right of the box): green healthy, amber transient,
   red stopped. Colour comes from the dock-<state> class on the node group. */
.map-dock-dot{fill:var(--text-3);stroke:var(--map-fill);stroke-width:1;}
.map-node--docker.dock-up   .map-dock-dot{fill:#3fb950;}
.map-node--docker.dock-warn .map-dock-dot{fill:#d29922;}
.map-node--docker.dock-down .map-dock-dot{fill:#f85149;}
.map-hud .map-node--docker.dock-up   .map-dock-dot{filter:drop-shadow(0 0 4px #3fb950);}
.map-hud .map-node--docker.dock-warn .map-dock-dot{filter:drop-shadow(0 0 4px #d29922);}
.map-hud .map-node--docker.dock-down .map-dock-dot{filter:drop-shadow(0 0 4px #f85149);}

/* HUD glow */
.map-hud .map-node .map-node-bg{filter:drop-shadow(0 0 7px var(--hue));}
.map-hud .map-node:hover .map-node-bg{filter:drop-shadow(0 0 16px var(--hue));}
.map-hud .map-link{filter:url(#mapGlow);}
.map-hud .map-node--pool .map-node-bg{animation:map-breathe 3s ease-in-out infinite;}
.map-hud .map-node--disk .map-node-bg{
  filter:drop-shadow(0 0 calc(3px + var(--busy,0) * 0.12px) var(--hue));}

/* health states */
.map-node.st-degraded{--hue:#fbbf24;}
.map-node.st-faulted,.map-node.st-missing{--hue:#ef4444;}
body.light .map-node.st-degraded,
body.dawn  .map-node.st-degraded{--hue:#b45309;}            /* darker amber on a light bg */
body.light .map-node.st-faulted, body.light .map-node.st-missing,
body.dawn  .map-node.st-faulted, body.dawn  .map-node.st-missing{--hue:#dc2626;}
.map-node.st-missing .map-node-bg{stroke-dasharray:4 4;}
.map-hud .map-node.st-faulted .map-node-bg,
.map-hud .map-node.st-missing .map-node-bg{animation:map-alarm 1.1s ease-in-out infinite;}

/* hover focus — dim everything except the hovered node, its links and the
   neighbour nodes at the other end of those links (.map-hl is set by JS). */
.map-focusing .map-node:not(.map-hl){opacity:.22;transition:opacity .12s ease;}
.map-focusing .map-linkg:not(.map-hl){opacity:.12;transition:opacity .12s ease;}
.map-focusing .map-node.map-hl,
.map-focusing .map-linkg.map-hl{opacity:1;}
.map-focusing .map-linkg.map-hl .map-link{stroke-width:3;}
.map-focusing .map-linkg.map-hl .map-link.idle{opacity:.7;}
.map-hud .map-focusing .map-linkg.map-hl .map-link{filter:drop-shadow(0 0 5px currentColor);}
.map-hud .map-focusing .map-node.map-hl .map-node-bg{filter:drop-shadow(0 0 16px var(--hue));}

@keyframes map-breathe{0%,100%{filter:drop-shadow(0 0 6px var(--hue));}
  50%{filter:drop-shadow(0 0 16px var(--hue));}}
@keyframes map-alarm{0%,100%{filter:drop-shadow(0 0 4px var(--hue));}
  50%{filter:drop-shadow(0 0 18px var(--hue));}}

/* flat mode — same colours, no glow/animation */
.map-flat .map-node-bg{filter:none !important;animation:none !important;}
.map-flat .map-link{filter:none !important;}

@media (prefers-reduced-motion: reduce){
  .map-link{animation:none !important;}
  .map-node-bg{animation:none !important;}
}

/* ════════════════════════════════════════════════════════════════════════
   Networking Layer (radial topology) — v6.6.4
   Bridges are glowing hubs; instances orbit on animated spokes; docker
   containers ring each instance. Each bridge cluster gets its own hue (set
   inline as --h, an HSL hue angle), so the layer reads as a multi-colour map.
   ════════════════════════════════════════════════════════════════════════ */
/* layer switch (top-left of every map toolbar) */
.map-layer-switch{display:inline-flex;gap:2px;
  background:color-mix(in srgb, var(--accent-2) 8%, transparent);
  border:1px solid color-mix(in srgb, var(--accent-2) 30%, transparent);
  border-radius:9px;padding:2px;}
.map-layer-btn{background:none;border:none;color:var(--text-3);border-radius:7px;
  padding:5px 12px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap;}
.map-layer-btn:hover{color:var(--text);}
.map-layer-btn.active{background:var(--gradient);color:#fff;}
/* networking legend swatches */
.map-legend-item[data-layer="bridge"] i{background:var(--accent-2);color:var(--accent-2);}
.map-legend-item[data-layer="netvm"]  i{background:var(--map-vm);color:var(--map-vm);}
.map-legend-item[data-layer="docker"] i{background:var(--map-docker);color:var(--map-docker);}

.net-title{fill:var(--text-2);font-size:13px;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;opacity:.75;}
/* hub pulse rings — opacity/glow only (no transform: would re-origin the circle) */
.net-hub-ring{fill:none;stroke:hsl(var(--h) 90% 60%);stroke-width:1.4;opacity:.4;}
.map-hud .net-hub-ring{animation:net-pulse 2.6s ease-in-out infinite;
  filter:drop-shadow(0 0 6px hsl(var(--h) 90% 60%));}
@keyframes net-pulse{0%,100%{opacity:.16;}50%{opacity:.55;}}
/* spokes — straight neon conduits, dash speed scales with bandwidth (--dur) */
.net-spoke-rail{stroke:var(--border);stroke-width:4;stroke-linecap:round;opacity:.45;}
.net-spoke{stroke:hsl(var(--h) 90% 62%);stroke-width:2;stroke-linecap:round;
  stroke-dasharray:5 7;animation:net-flow linear infinite;animation-duration:var(--dur,1.6s);}
.net-spoke.idle{animation:none;opacity:.26;stroke-dasharray:2 6;}
.net-spoke-dock .net-spoke{stroke-width:1.4;stroke-dasharray:3 5;opacity:.7;}
.net-spoke-dock .net-spoke-rail{stroke-width:2.5;}
.map-hud .net-spoke{filter:url(#netGlow);}
.net-spoke-label{fill:var(--text);font-size:9.5px;font-variant-numeric:tabular-nums;
  paint-order:stroke;stroke:var(--bg);stroke-width:3px;}
@keyframes net-flow{to{stroke-dashoffset:-24;}}
/* nodes */
.net-node{--h:200;cursor:default;}
.net-node .net-node-bg{fill:hsl(var(--h) 55% 15% / .93);stroke:hsl(var(--h) 90% 60%);stroke-width:1.2;}
body.light .net-node .net-node-bg,
body.dawn  .net-node .net-node-bg{fill:hsl(var(--h) 65% 95% / .96);}
.net-node .net-node-title{fill:var(--text);font-size:11px;font-weight:600;}
.net-node .net-node-sub{fill:var(--text-2);font-size:9.5px;font-variant-numeric:tabular-nums;}
.net-node--bridge .net-node-bg{stroke-width:2;}
.net-node--bridge .net-node-title{font-weight:700;font-size:12px;}
.net-node--docker .net-node-sub{fill:hsl(var(--h) 80% 70%);}
body.light .net-node--docker .net-node-sub,
body.dawn  .net-node--docker .net-node-sub{fill:hsl(var(--h) 70% 35%);}
.map-hud .net-node .net-node-bg{filter:drop-shadow(0 0 7px hsl(var(--h) 90% 60%));}
/* v6.6.11 — colour VM/CT and Docker nodes BY TYPE so they match the Networking
   legend (a VM and a container on the same bridge were previously identical,
   both tinted by the bridge hue). Bridges keep their distinct per-bridge hue,
   and the spokes stay bridge-hued so you can still trace what's on which bridge.
   color-mix keeps the dark translucent fill of the bridge-hued nodes. */
.net-node--vm .net-node-bg,
.net-node--ct .net-node-bg{fill:color-mix(in srgb, var(--map-vm) 16%, #0d0d0f);stroke:var(--map-vm);}
.net-node--docker .net-node-bg{fill:color-mix(in srgb, var(--map-docker) 16%, #0d0d0f);stroke:var(--map-docker);}
.net-node--docker .net-node-sub{fill:color-mix(in srgb, var(--map-docker) 80%, #fff);}
body.light .net-node--vm .net-node-bg, body.dawn .net-node--vm .net-node-bg,
body.light .net-node--ct .net-node-bg, body.dawn .net-node--ct .net-node-bg{fill:color-mix(in srgb, var(--map-vm) 12%, #fafafa);}
body.light .net-node--docker .net-node-bg, body.dawn .net-node--docker .net-node-bg{fill:color-mix(in srgb, var(--map-docker) 12%, #fafafa);}
body.light .net-node--docker .net-node-sub, body.dawn .net-node--docker .net-node-sub{fill:color-mix(in srgb, var(--map-docker) 70%, #000);}
.map-hud .net-node--vm .net-node-bg,
.map-hud .net-node--ct .net-node-bg{filter:drop-shadow(0 0 7px var(--map-vm));}
.map-hud .net-node--docker .net-node-bg{filter:drop-shadow(0 0 7px var(--map-docker));}
.map-hud .net-node--bridge .net-node-bg{animation:net-breathe 3.2s ease-in-out infinite;}
@keyframes net-breathe{0%,100%{filter:drop-shadow(0 0 10px hsl(var(--h) 90% 60%));}
  50%{filter:drop-shadow(0 0 20px hsl(var(--h) 90% 60%));}}
/* docker health dot */
.net-dock-dot{fill:var(--text-3);stroke:var(--bg);stroke-width:1;}
.net-node.dock-up   .net-dock-dot{fill:#3fb950;}
.net-node.dock-warn .net-dock-dot{fill:#d29922;}
.net-node.dock-down .net-dock-dot{fill:#f85149;}
.map-hud .net-node.dock-up   .net-dock-dot{filter:drop-shadow(0 0 4px #3fb950);}
.map-hud .net-node.dock-down .net-dock-dot{filter:drop-shadow(0 0 4px #f85149);}
.net-node.dock-down{opacity:.6;}
/* flat mode + reduced motion */
.map-flat .net-spoke{animation:none !important;}
.map-flat .net-node-bg{filter:none !important;animation:none !important;}
.map-flat .net-hub-ring{animation:none !important;}
@media (prefers-reduced-motion: reduce){
  .net-spoke{animation:none !important;}
  .net-hub-ring{animation:none !important;}
  .net-node-bg{animation:none !important;}
}

/* ── Map hovercard (hightech info + live metrics) ── */
.map-hovercard{position:fixed;z-index:5000;pointer-events:auto;max-width:300px;min-width:210px;
  opacity:1;transition:opacity .12s ease;}
.map-hovercard.hidden{opacity:0;visibility:hidden;}
.mhc{--hue:var(--map-remote);position:relative;border-radius:12px;padding:11px 13px 12px;overflow:hidden;
  background:linear-gradient(160deg,color-mix(in srgb,var(--surface) 95%,transparent),color-mix(in srgb,var(--bg) 96%,transparent));
  border:1px solid color-mix(in srgb, var(--hue) 70%, transparent);
  box-shadow:0 0 0 1px var(--border), var(--shadow-lg), 0 0 22px color-mix(in srgb, var(--hue) 35%, transparent);
  backdrop-filter:blur(9px);color:var(--text-2);}
.mhc-scan{position:absolute;left:0;top:0;height:2px;width:100%;
  background:linear-gradient(90deg,transparent,var(--hue),transparent);opacity:.8;
  animation:mhc-scan 2.4s linear infinite;}
@keyframes mhc-scan{0%{transform:translateX(-100%);}100%{transform:translateX(100%);}}
.mhc-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;}
.mhc-ic{font-size:16px;filter:drop-shadow(0 0 5px var(--hue));}
.mhc-ti{font-weight:700;font-size:13px;color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.mhc-tp{font-size:9px;text-transform:uppercase;letter-spacing:.08em;color:var(--hue);
  border:1px solid color-mix(in srgb,var(--hue) 50%,transparent);border-radius:5px;padding:2px 6px;flex-shrink:0;}
.mhc-info{display:flex;flex-direction:column;gap:3px;margin-bottom:8px;}
.mhc-row{display:flex;justify-content:space-between;gap:12px;font-size:11px;}
.mhc-row>span{color:var(--text-3);}
.mhc-row>b{color:var(--text);font-weight:600;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:170px;}
.mhc-metrics{display:flex;flex-wrap:wrap;gap:6px;padding-top:8px;border-top:1px solid var(--border);}
.mhc-metric{flex:1 1 calc(50% - 6px);background:color-mix(in srgb,var(--text) 5%,transparent);border-radius:7px;padding:5px 8px;}
.mhc-metric>span{display:block;font-size:9px;color:var(--text-3);text-transform:uppercase;letter-spacing:.05em;}
.mhc-metric>b{font-size:13px;color:var(--hue);font-variant-numeric:tabular-nums;
  text-shadow:0 0 8px color-mix(in srgb,var(--hue) 45%,transparent);}
.mhc-bar{flex:1 1 100%;margin-top:2px;}
.mhc-bar-top{display:flex;justify-content:space-between;font-size:9.5px;color:var(--text-3);margin-bottom:3px;}
.mhc-track{height:6px;border-radius:4px;background:color-mix(in srgb,var(--text) 12%,transparent);overflow:hidden;}
.mhc-fill{height:100%;border-radius:4px;background:linear-gradient(90deg,var(--hue),color-mix(in srgb,var(--hue) 50%,#fff));
  box-shadow:0 0 8px var(--hue);transition:width .4s ease;}
.mhc-chips{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px;padding-top:8px;border-top:1px solid var(--border);}
.mhc-load{font-size:10.5px;color:var(--text-3);font-style:italic;padding:4px 0 2px;}
/* live zpool iostat charts */
.mhc-io{margin-top:8px;padding-top:8px;border-top:1px solid var(--border);}
.mhc-io-h{font-size:9px;text-transform:uppercase;letter-spacing:.06em;color:var(--text-3);margin-bottom:5px;}
.mhc-io-c{margin-bottom:7px;}
.mhc-io-t{font-size:9.5px;color:var(--text-3);margin-bottom:2px;}
.mhc-io-cv{display:block;width:100%;height:44px;border-radius:5px;
  background:color-mix(in srgb,var(--text) 6%,transparent);}
.mhc-io-leg{display:flex;gap:10px;justify-content:flex-end;font-size:10px;
  font-variant-numeric:tabular-nums;margin-top:2px;}
.mhc-chips>span{font-size:10px;padding:2px 7px;border-radius:5px;color:var(--text-2);
  background:color-mix(in srgb,var(--hue) 14%,transparent);border:1px solid color-mix(in srgb,var(--hue) 35%,transparent);}
@media (prefers-reduced-motion: reduce){ .mhc-scan{animation:none;} .mhc-fill{transition:none;} }

/* ============================================================================
   MOBILE SHELL (v6.7.0) — phone-only chrome.
   Everything here is gated behind `body.is-phone`, a class toggled by JS from
   matchMedia('(max-width:760px)') + (pointer:coarse). Desktop never matches, so
   none of these rules affect the desktop layout.

   Shell pieces:
     • #m-topbar   — slim top app bar (hamburger · page title · action slot)
     • .sidebar    — repurposed as an off-canvas drawer (slides in from the left)
     • #m-backdrop — dim layer behind the open drawer
     • #m-tabbar   — bottom tab bar (Perf · Storage · Sharing · VMs · More)
     • .activity-bar — demoted to a collapsed bottom strip above the tab bar
   iOS safe-area insets (notch / home indicator) are honoured throughout.
   ============================================================================ */

:root {
  --m-topbar-h: 52px;   /* app-bar height (excl. safe-area) */
  --m-tabbar-h: 56px;   /* bottom tab-bar height (excl. safe-area) */
}

/* Mobile chrome is hidden by default; only a phone reveals it. */
#m-topbar, #m-tabbar, #m-backdrop { display: none; }
.m-only { display: none; }                 /* drawer footer etc. — phone-only bits */

body.is-phone #m-topbar,
body.is-phone #m-tabbar { display: flex; }
body.is-phone .m-only   { display: block; }

/* The desktop topbar/IO wrapper (CPU+MEM gauges, theme picker, account menu,
   live IO sparklines) is replaced by the mobile app bar on phone. */
body.is-phone #topbar-io-wrapper { display: none !important; }

/* --- Layout: make room for the fixed top bar + bottom tab bar --------------- */
body.is-phone .app-layout {
  padding-top:    calc(var(--m-topbar-h) + env(safe-area-inset-top));
  padding-bottom: calc(var(--m-tabbar-h) + env(safe-area-inset-bottom));
}
/* main-content fills the remaining space; its inner page area scrolls. */
body.is-phone .main-content { width: 100%; min-width: 0; }

/* --- Top app bar ------------------------------------------------------------ */
.m-topbar {
  position: fixed; top: 0; left: 0; right: 0;
  height: calc(var(--m-topbar-h) + env(safe-area-inset-top));
  padding-top: env(safe-area-inset-top);
  z-index: 1400;
  align-items: center;
  display: grid;
  grid-template-columns: 52px 1fr 52px;   /* hamburger · title · action slot */
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 8px rgba(0,0,0,0.18);
}
.m-topbar .m-iconbtn {
  width: 52px; height: var(--m-topbar-h);
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--text); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.m-topbar .m-iconbtn:active { background: rgba(255,255,255,0.06); }
.m-title {
  text-align: center;
  font-size: 17px; font-weight: 700; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.m-topbar-right { display: flex; align-items: center; justify-content: flex-end; }

/* --- Off-canvas drawer (the .sidebar, on phone) ----------------------------- */
body.is-phone .sidebar {
  position: fixed; top: 0; left: 0;
  height: 100dvh;
  width: 84vw; max-width: 320px;
  min-width: 0;
  padding: calc(env(safe-area-inset-top) + 14px) 0 calc(env(safe-area-inset-bottom) + 14px);
  z-index: 1600;
  transform: translateX(-100%);
  transition: transform .26s cubic-bezier(.4,0,.2,1);
  box-shadow: 6px 0 28px rgba(0,0,0,0.45);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
body.is-phone.drawer-open .sidebar { transform: translateX(0); }

/* Drawer always shows full labels/sections — neutralise any compact/rail state
   that a desktop preference (sidebar-compact) might have left behind. */
body.is-phone .sidebar.sidebar-compact { width: 84vw; max-width: 320px; padding-left: 0; padding-right: 0; }
body.is-phone .sidebar .nav-label,
body.is-phone .sidebar .sidebar-header,
body.is-phone .sidebar .sidebar-section,
body.is-phone .sidebar .nav-dot { display: ''; }
body.is-phone .sidebar .nav-label { display: inline; }
body.is-phone .sidebar .sidebar-header { display: block; }
body.is-phone .sidebar .nav-item { justify-content: flex-start; gap: 10px; padding: 12px 18px; }
body.is-phone .sidebar #sidebar-expand-btn,
body.is-phone .sidebar .sidebar-collapse-row { display: none !important; }
/* Bigger touch targets for nav rows in the drawer. */
body.is-phone .sidebar .nav-icon { font-size: 18px; opacity: 0.95; }

/* Backdrop behind the open drawer. */
.m-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 1500;
  opacity: 0; pointer-events: none;
  transition: opacity .26s ease;
}
body.is-phone.drawer-open #m-backdrop { display: block; opacity: 1; pointer-events: auto; }

/* Drawer footer (account / theme / logout). */
.m-drawer-foot {
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--border);
}
.m-drawer-user {
  padding: 4px 18px 8px;
  font-size: 12px; color: var(--text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* --- Bottom tab bar --------------------------------------------------------- */
.m-tabbar {
  position: fixed; left: 0; right: 0; bottom: 0;
  height: calc(var(--m-tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  z-index: 1400;
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -1px 10px rgba(0,0,0,0.22);
}
.m-tab {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  background: none; border: none; cursor: pointer;
  color: var(--text-3); font-size: 10px; font-weight: 600;
  -webkit-tap-highlight-color: transparent;
  padding: 6px 0 4px;
}
.m-tab .m-tab-ic { font-size: 20px; line-height: 1; }
.m-tab .m-tab-lbl { letter-spacing: .01em; }
.m-tab.active { color: var(--accent-2); }
.m-tab:active { background: rgba(255,255,255,0.05); }

/* --- Activity bar → collapsed bottom strip above the tab bar ---------------- */
body.is-phone .activity-bar.collapsed { height: 40px !important; }
/* Expanded height is the --ab-ph-h CSS var (default 55dvh), updated by the
   phone touch-resize (_abPhoneResize) — a var avoids fighting the desktop
   inline-px path while the !important still beats any stale desktop px. */
body.is-phone .activity-bar.expanded  { height: var(--ab-ph-h, 55dvh) !important; }
body.is-phone .activity-bar-header { padding: 0 14px; gap: 8px; }
body.is-phone .activity-bar-body { padding: 0 14px 12px; }
/* The drag handle is hidden while COLLAPSED (the strip is a tap-to-expand
   target); once OPEN it becomes a comfy touch grip to resize the sheet. */
body.is-phone .activity-bar.collapsed #activity-resize-handle { display: none; }
body.is-phone .activity-bar.expanded  #activity-resize-handle {
  height: 20px; touch-action: none; cursor: row-resize;
}
body.is-phone .activity-bar.expanded #activity-resize-handle::before { width: 52px; height: 5px; }
/* CRITICAL: the in-header tab strip ("Activity & Events" / "Live Alerts") +
   "All Servers" button do event.stopPropagation(), and on a narrow phone they
   cover the whole header — so a tap to expand landed on a tab button and never
   reached the header's toggleActivityBar(). Hide them while COLLAPSED so the
   entire 40px strip is one big tap-to-expand target; they reappear once open
   (where you tap the title/chevron to collapse). */
body.is-phone .activity-bar.collapsed .ab-tab-strip,
body.is-phone .activity-bar.collapsed #ab-all-servers-btn { display: none !important; }
/* Bigger, obviously-tappable chevron + comfortable header height on phone. */
body.is-phone .activity-bar-header { height: 40px; }
body.is-phone #ab-chevron { font-size: 14px; padding: 6px; margin-left: 4px; }

/* Generic phone niceties */
body.is-phone { -webkit-text-size-adjust: 100%; }

/* ============================================================================
   MOBILE PHASE 2 — cross-cutting transforms (phone only).
   Engines that fix whole categories of screens at once:
     • table → card list   (.m-card-table, applied by _mCardifyTables in JS)
     • modal → bottom sheet (all .modal at once)
     • inline grids → single column inside modals
     • tab strips / chip rows → horizontally scrollable
   ============================================================================ */

/* --- Table → card list ------------------------------------------------------
   _mCardifyTables() tags wide data tables with .m-card-table and stamps every
   <td> with data-label="<column header>". Here each row becomes a card and each
   cell a label:value line. Cells spanning columns (empty-state rows) get
   .m-cell-full and render as a centred full-width line. */
body.is-phone table.m-card-table { display: block; width: 100%; border: none; }
body.is-phone table.m-card-table thead { display: none; }
body.is-phone table.m-card-table tbody { display: block; }
body.is-phone table.m-card-table tbody tr {
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: 10px;
  overflow: hidden;
}
body.is-phone table.m-card-table tbody tr:hover td { background: none; }
body.is-phone table.m-card-table td {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  border: none; border-top: 1px solid rgba(255,255,255,0.05);
  padding: 9px 14px; font-size: 14px; text-align: right;
  min-height: 0;
}
body.is-phone table.m-card-table tr td:first-child { border-top: none; }
body.is-phone table.m-card-table td::before {
  content: attr(data-label);
  flex: 0 0 38%;
  text-align: left;
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.is-phone table.m-card-table td.m-cell-full { display: block; text-align: center; color: var(--text-3); }
body.is-phone table.m-card-table td.m-cell-full::before { display: none; }
/* Action / icon cells (blank column header) — no empty label gutter. */
body.is-phone table.m-card-table td:not([data-label]):not(.m-cell-full) { justify-content: flex-end; }
body.is-phone table.m-card-table td:not([data-label]):not(.m-cell-full)::before { display: none; }
/* Avoid the doubled border when a cardified table sits in a .table-wrap. */
body.is-phone .table-wrap:has(table.m-card-table) { border: none; overflow: visible; border-radius: 0; }
/* Rows whose action cell holds an absolutely-positioned dropdown (the Stack
   Containers table's terminal + burger menus) must NOT clip it — the
   default cardified-row overflow:hidden hid the menu the moment it opened, so
   the buttons looked dead on a phone. Let those rows (and their action cell)
   overflow so the menu is visible and tappable. */
body.is-phone table.m-card-table tbody tr:has(.row-menu-dropdown),
body.is-phone table.m-card-table td:has(.row-menu-dropdown) { overflow: visible; }
/* Fallback: tables we didn't cardify still scroll sideways rather than clipping. */
body.is-phone .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --- Modal → bottom sheet ---------------------------------------------------
   One treatment covers every .modal: dock to the bottom, full width, sticky
   header (with a grab handle) + footer, scrollable body, safe-area aware. */
/* The bottom tab bar (#m-tabbar, z-index:1400) otherwise paints OVER an open
   modal — hiding the sheet's sticky footer/action buttons at the very bottom.
   Lift the backdrop above it so the sheet covers the tab bar and its footer is
   fully visible. (Toasts at z-index 99999 still sit above modals.) */
body.is-phone .modal-backdrop { padding: 0; align-items: flex-end; z-index: 1500; }
body.is-phone .modal {
  max-width: none; width: 100%;
  /* !important defeats per-modal inline `max-height:NNvh` — on phones `vh`
     includes the URL-bar area, so e.g. 96vh pushes the sticky footer below the
     visible viewport. `dvh` tracks the actually-visible height. */
  max-height: 92dvh !important;
  border-radius: 18px 18px 0 0;
  border-left: none; border-right: none; border-bottom: none;
  animation: m-sheet-up .26s cubic-bezier(.4,0,.2,1);
}
/* Bare action buttons that sit as direct children of .modal (a few modals put a
   single "Got it"/close button after the scroll region instead of in a
   .modal-footer) must never be squeezed out by the flex:1 scroll area. */
body.is-phone .modal > button { flex-shrink: 0; }

/* Compose YAML / .env editors on phone: AUTO height, so the modal sheet body
   is the single scroller. A fixed-height CodeMirror inside the sheet is a
   nested-scroll trap on iOS — the inner scroller maxes out while its lower
   half sits under the keyboard / below the sheet, so the end of the document
   could never be reached. (JS side: _mountCMEditor sets viewportMargin
   Infinity on phones so CodeMirror actually renders every line.) */
body.is-phone .znas-cm-yaml.CodeMirror,
body.is-phone .znas-cm-env.CodeMirror,
body.is-phone .znas-cm-docker-yaml.CodeMirror,
body.is-phone .znas-cm-docker-env.CodeMirror {
  height: auto !important;
  /* the editors are flex children of the sheet's column — without this they
     get SQUEEZED to fit instead of letting the sheet body scroll */
  flex-shrink: 0;
}

/* ===== File browser — phone layout =========================================
   The desktop file browser is a resizable two-pane window (260px tree + a
   multi-column listing) that overflowed a phone screen badly. On phone it
   becomes a full-screen page: no tree, full-width touch listing trimmed to
   icon · name · size, a horizontally-scrollable touch toolbar, and a wrapping
   nav row with a full-width search. Placed AFTER the `.modal` phone rules so
   these (equal-specificity) overrides win the cascade. */
body.is-phone .fbnew-modal {
  width: 100% !important; height: 100dvh !important;
  min-width: 0 !important; min-height: 0 !important;
  max-width: none !important; max-height: 100dvh !important;
  border: none !important; border-radius: 0 !important; resize: none !important;
}
/* Full-screen ⇒ the header sits under the notch and the screen's rounded
   corners. Pad it down past the top safe-area (the header surface bleeds up
   behind the notch) and in from the left/right insets. `!important` is required
   to beat the `.fbnew-modal > div { padding:0 !important }` reset. */
body.is-phone .fbnew-modal-header {
  padding: calc(12px + env(safe-area-inset-top)) calc(14px + env(safe-area-inset-right))
           12px calc(14px + env(safe-area-inset-left)) !important;
  gap: 10px;
}
body.is-phone .fbnew-modal-header h3 { font-size: 15px; }
body.is-phone #fbnew-maximize-btn { display: none; }            /* no resize on phone */
body.is-phone .fbnew-iconbtn { font-size: 20px; padding: 6px 8px; }

/* Toolbar → one horizontally-scrollable row of touch pills (snappy, no scrollbar). */
body.is-phone .fbnew-toolbar {
  flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
  gap: 8px; scrollbar-width: none;
  /* !important beats the `.fbnew-modal > div{padding:0 !important}` reset; pad in
     from the side safe-area so the first/last pill clears rounded corners. */
  padding: 9px calc(12px + env(safe-area-inset-right)) 9px calc(12px + env(safe-area-inset-left)) !important;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 22px), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 22px), transparent);
}
body.is-phone .fbnew-toolbar::-webkit-scrollbar { display: none; }
body.is-phone .fbnew-toolbar .fbnew-spacer { display: none; }
body.is-phone .fbnew-btn {
  flex-shrink: 0; padding: 9px 14px; font-size: 13px; border-radius: 9px; min-height: 38px;
}

/* Nav row → back · refresh · breadcrumb on one line; search wraps to full width. */
body.is-phone .fbnew-navrow {
  flex-wrap: wrap; gap: 7px;
  padding: 9px calc(12px + env(safe-area-inset-right)) 9px calc(12px + env(safe-area-inset-left)) !important;
}
body.is-phone #fbnew-forward { display: none; }                 /* rarely used on phone */
body.is-phone .fbnew-navbtn { min-width: 40px; min-height: 36px; font-size: 17px; border-radius: 9px; }
body.is-phone .fbnew-crumb { flex: 1 1 auto; height: 36px; font-size: 13px; }
body.is-phone input.fbnew-search {
  order: 9; flex: 1 1 100%; max-width: none; min-width: 0; height: 38px; font-size: 16px;
}

/* Body → drop the tree, listing fills the screen. */
body.is-phone .fbnew-modal > .fbnew-body { display: block !important; }
body.is-phone .fbnew-tree { display: none; }
body.is-phone .fbnew-listing { width: 100%; height: 100%; }
/* Keep the last rows clear of the home indicator. */
body.is-phone .fbnew-listing[data-view="list"] { padding-bottom: env(safe-area-inset-bottom); }
body.is-phone .fbnew-listing:not([data-view="list"]) { padding-bottom: calc(10px + env(safe-area-inset-bottom)); }

/* List view → touch rows, trimmed to checkbox · name · size. */
body.is-phone .fb-list-head,
body.is-phone .fb-list-row {
  grid-template-columns: 34px 1fr auto; gap: 10px; padding: 12px 14px; font-size: 14px;
}
body.is-phone .fb-list-head > :nth-child(n+4),
body.is-phone .fb-list-row  > :nth-child(n+4) { display: none; }
body.is-phone .fb-list-row .fb-name { font-size: 15px; }
body.is-phone .fb-list-row .fb-name .fb-icon { font-size: 22px; width: 28px; }
body.is-phone .fb-list-row .fb-mono { font-size: 12.5px; }
body.is-phone .fb-list-head input[type="checkbox"],
body.is-phone .fb-list-row input[type="checkbox"] { width: 21px; height: 21px; }

/* Tile / icon views → phone-sized columns. */
body.is-phone .fbnew-listing[data-view="tiles"]  { grid-template-columns: 1fr; }
body.is-phone .fbnew-listing[data-view="small"]  { grid-template-columns: repeat(auto-fill, minmax(82px, 1fr)); }
body.is-phone .fbnew-listing[data-view="medium"] { grid-template-columns: repeat(auto-fill, minmax(108px, 1fr)); }
body.is-phone .fbnew-listing[data-view="large"]  { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

/* New-folder inline form → full-width input. */
body.is-phone #fbnew-mkdir-form { flex-wrap: wrap; }
body.is-phone #fbnew-mkdir-name { flex: 1 1 100%; font-size: 16px; }
@keyframes m-sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
body.is-phone .modal-header {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface);
  padding: 22px 18px 12px;
}
/* Grab handle */
body.is-phone .modal-header::before {
  content: ""; position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
  width: 38px; height: 4px; border-radius: 2px; background: var(--border);
}
body.is-phone .modal-body { padding: 4px 18px 16px; }
body.is-phone .modal > form,
body.is-phone .modal > div:not(.modal-header):not(.modal-body):not(.modal-footer) { padding: 4px 18px; }
body.is-phone .modal > form:last-child,
body.is-phone .modal > div:not(.modal-header):not(.modal-body):not(.modal-footer):last-child {
  padding-bottom: calc(18px + env(safe-area-inset-bottom));
}
body.is-phone .modal-footer {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--surface);
  padding: 14px 18px calc(16px + env(safe-area-inset-bottom));
}
/* Wrap-friendly footer buttons (some modals pack 3+ actions). */
body.is-phone .modal-footer { flex-wrap: wrap; }

/* --- Inline multi-column grids → single column inside modals/forms ---------- */
body.is-phone .modal [style*="grid-template-columns"] { grid-template-columns: 1fr !important; }

/* --- Tab strips / chip rows → horizontally scrollable ----------------------
   :has() targets the immediate container of a tab/chip group without needing a
   class hook, so this covers Settings, Prereqs, Capacity/Perf ranges, dashboard
   tabs and any future tab row. */
body.is-phone *:has(> .settings-tab),
body.is-phone *:has(> .cap-tab),
body.is-phone *:has(> .cap-range-btn) {
  display: flex !important;
  flex-wrap: nowrap !important;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
body.is-phone *:has(> .settings-tab)::-webkit-scrollbar,
body.is-phone *:has(> .cap-tab)::-webkit-scrollbar,
body.is-phone *:has(> .cap-range-btn)::-webkit-scrollbar { display: none; }
body.is-phone .settings-tab,
body.is-phone .cap-tab,
body.is-phone .cap-range-btn { flex: 0 0 auto; white-space: nowrap; }

/* Page headers (h2 + action buttons) ran their buttons off the right edge on
   phone; let the header and its button group wrap instead of overflowing. */
body.is-phone .page-header { flex-wrap: wrap; gap: 10px; align-items: flex-start; }
body.is-phone .page-header > div { flex-wrap: wrap; }

/* ============================================================================
   MOBILE PHASE 3 — per-screen / page-body polish (phone only).
   ============================================================================ */

/* Card grids use `repeat(auto-fit, minmax(<min>, 1fr))`. A min track wider than
   the ~358px usable width forces horizontal overflow, so collapse the large ones
   to a single column. Small-tile grids (minmax ≤ 340px) already fit and are left
   alone so they keep their intended multi-column layout. */
body.is-phone [style*="minmax(340px"],
body.is-phone [style*="minmax(360px"],
body.is-phone [style*="minmax(380px"],
body.is-phone [style*="minmax(460px"],
body.is-phone [style*="minmax(480px"],
body.is-phone [style*="minmax(525px"] { grid-template-columns: minmax(0, 1fr) !important; }
/* minmax(0,1fr) — not plain 1fr — so the single column can shrink to the
   container width. A bare 1fr track has an implicit min of auto (min-content),
   which lets a card grow past the container (≈8px on a slim phone), giving an
   uneven right gutter vs the symmetric full-width cards. */
/* The VM/Container detail view lays its cards out in a class-based grid
   (.ci-cards-grid, minmax(360px,1fr)) — the inline-style override above doesn't
   catch it, so on a narrow phone the 360px min track overflows the screen and
   the cards look oversized. Collapse to a single full-width column. */
body.is-phone .ci-cards-grid { grid-template-columns: minmax(0, 1fr) !important; }

/* Monitor tab realtime row: 4 cards forced into one row (flex-wrap:nowrap) are
   too slim on a phone. Wrap them into a 2×2 grid (two rows of two); on a very
   narrow phone they drop to one-per-row (four rows). */
body.is-phone .ci-mon-rt-row { flex-wrap: wrap !important; }
body.is-phone .ci-mon-rt-row .ci-mon-rt-card {
  flex: 1 1 calc(50% - 6px) !important;
  min-width: calc(50% - 6px);
}
@media (max-width: 380px) {
  body.is-phone .ci-mon-rt-row .ci-mon-rt-card {
    flex-basis: 100% !important;
    min-width: 100%;
  }
}

/* Capacity Trend "Select pools & datasets" panel. On desktop it's an absolute
   340px-wide dropdown anchored to its button; on a phone that overflows the
   viewport and renders under the phone app bar / bottom tabs (higher z-index),
   so the checkboxes are unreachable. Turn it into a full-width fixed overlay
   above the chrome, with its own scroll, so it's usable on small screens. */
body.is-phone .cap-selector-panel {
  position: fixed;
  top: 64px;
  left: 8px;
  right: 8px;
  min-width: 0;
  width: auto;
  max-height: calc(100vh - 150px);
  overflow-y: auto;
  z-index: 1600;
}
/* Let the whole panel scroll instead of the inner tree (overrides the inline
   max-height:320px on #cap-tree) so the Apply button is always reachable. */
body.is-phone .cap-selector-panel #cap-tree { max-height: none !important; }

/* Custom-styled selects (.cs-wrap/.cs-trigger, built by initAllCustomSelects)
   carry an inline min/explicit width copied from the native control; clamp them
   so they never push past the screen edge. */
body.is-phone .cs-wrap,
body.is-phone .cs-trigger { max-width: 100%; }

/* Final safety net: never let a page scroll sideways. Inner scroll regions
   (tables, tab strips, code blocks) keep their own overflow-x, so this only traps
   stray content that would otherwise drag the whole page. */
body.is-phone .page { overflow-x: hidden; }

/* VMs & Containers: the table card has its own vertical scroll region
   (overflow:auto + max-height) which, with the cardified rows, produces a nested
   scrollbar — the page scrolls a little, then the cards scroll separately. On
   phone let the card grow with its content so the whole view is a single scroll. */
body.is-phone .lxd-table-card { overflow: visible; max-height: none; min-height: 0; }
body.is-phone .lxd-table-card thead th { position: static; }


/* ── Services category + pages (v6.8.1) ───────────────────────────────────── */

/* Styled hover label for the sidebar mode icons. Replaces the native title=
   tooltip, which is slow to appear and unstyleable. */
.sidebar-mode-btn { position: relative; }
.sidebar-mode-btn::after {
  content: attr(data-label);
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 3px 8px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s;
  z-index: 60;
  box-shadow: 0 3px 10px rgba(0,0,0,.28);
}
.sidebar-mode-btn:hover::after { opacity: 1; }
/* the last icon has the longest label: open it leftward so it stays in the sidebar */
#mode-btn-firewall::after { left: auto; right: -16px; transform: none; }

/* Service rows in the left nav — mirrors .nav-compute-instance. */
.nav-service-row { display: flex; align-items: center; gap: 7px; width: 100%; }
.nav-service-row .svc-ico {
  width: 15px; height: 15px; flex-shrink: 0; border-radius: 3px; object-fit: contain;
}
.nav-service-row.svc-offline { opacity: .5; }

/* Services table icon cell. */
.svc-tbl-icon {
  width: 22px; height: 22px; border-radius: 4px; object-fit: contain;
  vertical-align: -6px; margin-right: 8px;
}

/* Embedded app panel. */
.svc-embed-wrap { position: relative; width: 100%; height: calc(100vh - 150px); min-height: 420px; }
.svc-embed-wrap > iframe { width: 100%; height: 100%; border: none; border-radius: 8px; background: #fff; }
.svc-embed-overlay {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px; text-align: center;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 30px;
}

/* ── Services icon view (v6.8.1) — home-screen style grid ─────────────────── */
.svc-icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 22px 14px;
  padding: 10px 4px 30px;
}
.svc-tile {
  /* iPadOS shows its own callout ("Save Image", text selection) on long-press,
     which pre-empts our context menu. Suppress it here and drive the menu from
     an explicit long-press handler instead. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: 12px;
  text-align: center;
  transition: transform .12s ease, background .12s;
}
.svc-tile:hover { background: var(--surface2); transform: translateY(-2px); }
.svc-tile:active { transform: scale(.96); }
.svc-tile-art {
  position: relative;
  width: 64px; height: 64px;
  border-radius: 15px;
  background: var(--surface2);
  border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  box-shadow: 0 3px 10px rgba(0,0,0,.22);
}
.svc-tile-art img.svc-tile-img {
  width: 46px; height: 46px; object-fit: contain;
  -webkit-touch-callout: none; -webkit-user-select: none; user-select: none;
  pointer-events: none; /* long-press must target the tile, never the image */
}
.svc-tile-art svg { opacity: .8; }
.svc-tile-label {
  font-size: 12px; font-weight: 600; color: var(--text-1);
  max-width: 104px; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.svc-tile-sub { font-size: 10.5px; color: var(--text-3); margin-top: -4px; }
/* Status pip on the tile corner. */
.svc-tile-dot {
  position: absolute; right: -3px; top: -3px;
  width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--surface);
}
/* A service whose container is not running: fully desaturated, and its click
   opens the context menu instead of the app — hence the menu cursor and no
   hover lift, so it never reads as "openable". */
.svc-tile.svc-dim .svc-tile-art { box-shadow: none; background: var(--surface); }
/* Scoped to the artwork, not the .svc-tile-art box: opacity/filter apply to the
   whole subtree and a child cannot opt out, which would fade the status pip
   nested inside it — the one thing that must stay legible. */
.svc-tile.svc-dim .svc-tile-art img.svc-tile-img,
.svc-tile.svc-dim .svc-tile-art svg { opacity: .34; filter: grayscale(1); }
.svc-tile.svc-dim .svc-tile-label { color: var(--text-3); font-weight: 500; }
.svc-tile.svc-dim .svc-tile-sub { opacity: .65; }
.svc-tile.svc-dim { cursor: context-menu; }
.svc-tile.svc-dim:hover { transform: none; }
.svc-tile.svc-dim:active { transform: scale(.98); }
/* A hidden service, shown only while "Show hidden" is on. */
.svc-tile.svc-is-hidden .svc-tile-art { outline: 1px dashed var(--text-3); opacity: .55; }

/* View-settings dropdown on the Services page. */
.svc-viewmenu {
  position: absolute; z-index: 9999; min-width: 208px;
  background: var(--surface2); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px; box-shadow: 0 10px 30px rgba(0,0,0,.36);
}
.svc-viewmenu-item {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: 6px; cursor: pointer;
  font-size: 13px; color: var(--text-1);
}
.svc-viewmenu-item:hover { background: var(--surface); }
.svc-viewmenu-sep { height: 1px; background: var(--border); margin: 5px 3px; }
.svc-viewmenu-head {
  padding: 6px 10px 3px; font-size: 10px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--text-3); font-weight: 700;
}


/* Long-press menu also drives the left-nav service rows on touch devices. */
.nav-compute-instance { -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; }

/* v6.8.28 appliance: OS packages are managed by reflashing the USB image */
body.appliance .osupdate-ui { display: none !important; }
body.appliance .uninstall-ui { display: none !important; }
/* Elements that only make sense while running from the USB appliance image
   (e.g. the SSH-unlock card — off-appliance SSH is managed normally). */
body:not(.appliance) .appliance-only { display: none !important; }
body.appliance .appliance-hide { display: none !important; }
/* Optional Features on the appliance: features ship in the image, so rows show
   a read-only "Enabled" checkbox; install/enable buttons, status text and
   menus go — except UPS's menu (Re-Detect UPS, widget visibility) and S3's
   while it is not configured (Hide / Show the S3 Buckets menu item). */
body.appliance #ptab-panel-optfeat [id^="prereq-"][id$="-status"],
body.appliance #ptab-panel-optfeat [id^="prereq-"][id$="-btn"] { display: none !important; }
body.appliance #ptab-panel-optfeat [id^="prereq-"][id$="-menu-wrap"]:not(#prereq-ups-menu-wrap):not(.appl-menu) { visibility: hidden !important; pointer-events: none; }
.optfeat-appl-check { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-2); white-space: nowrap; cursor: default; }
.optfeat-appl-check input { accent-color: var(--accent-ok, #30d158); margin: 0; }
/* Optional Features: the ⋮ menu gets its own fixed column at the far right of
   every row, present (just invisible) when a row has no menu, so statuses,
   buttons and checkboxes line up whether or not a menu is showing. */
#ptab-panel-optfeat [id^="prereq-"][id$="-menu-wrap"],
#ptab-panel-optfeat .optfeat-menu-slot { flex: 0 0 28px; width: 28px; box-sizing: border-box; text-align: center; padding: 2px 0 !important; }
#ptab-panel-optfeat [id^="prereq-"][id$="-menu-wrap"].hidden { display: inline-block !important; visibility: hidden; pointer-events: none; }
/* Column order in each row: name · action button · status · ⋮ menu. The status
   has a fixed width so "Installed" / "Not installed" line up as a column. */
#ptab-panel-optfeat [id^="prereq-"][id$="-btn"] { order: 1; }
#ptab-panel-optfeat [id^="prereq-"][id$="-status"],
#ptab-panel-optfeat .optfeat-appl-check { order: 2; flex: 0 0 104px; }
#ptab-panel-optfeat [id^="prereq-"][id$="-menu-wrap"],
#ptab-panel-optfeat .optfeat-menu-slot { order: 3; }

/* USB appliance upgrade bar: moving stripes on a section whose progress
   cannot be measured (write phase without a byte count). */
@keyframes applSegStripes { from { background-position: 0 0; } to { background-position: 28px 0; } }
.appl-seg-indet {
  background-image: repeating-linear-gradient(-45deg, rgba(255,255,255,.35) 0 7px, transparent 7px 14px) !important;
  background-size: 28px 100% !important;
  animation: applSegStripes .8s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .appl-seg-indet { animation: none; } }

/* ── v6.10.4: in-app Guide ─────────────────────────────────────────────────── */
.guide-modal { max-width: 1120px; width: 96vw; height: min(86vh, 860px); display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.guide-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 18px 24px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
.guide-brand { display: flex; align-items: center; gap: 12px; }
.guide-brand-ic { width: 40px; height: 40px; border-radius: 11px; display: inline-flex; align-items: center; justify-content: center; color: #fff; background: linear-gradient(135deg, #bf5af2, #0a84ff); box-shadow: 0 4px 14px rgba(10,132,255,.28); flex-shrink: 0; }
.guide-sub { font-size: 12.5px; color: var(--text-3); margin-top: 2px; }
.guide-body { display: flex; flex: 1; min-height: 0; }
.guide-nav { width: 272px; flex-shrink: 0; border-right: 1px solid var(--border); padding: 12px 10px; overflow-y: auto; background: var(--surface2); }
.guide-topic { display: flex; gap: 11px; align-items: flex-start; width: 100%; text-align: left; padding: 10px 11px; margin-bottom: 3px; border: 1px solid transparent; border-radius: 10px; background: none; color: var(--text-1); cursor: pointer; transition: background .12s, border-color .12s; }
.guide-topic:hover { background: var(--surface); }
.guide-topic.active { background: var(--surface); border-color: var(--border); box-shadow: 0 1px 4px rgba(0,0,0,.08); }
.guide-topic-ic { width: 30px; height: 30px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: #fff; }
.guide-topic-t { font-size: 13.5px; font-weight: 600; line-height: 1.25; }
.guide-topic-d { font-size: 11.5px; color: var(--text-3); line-height: 1.35; margin-top: 2px; }
.guide-topic-n { font-size: 10.5px; font-weight: 700; color: var(--text-3); letter-spacing: .04em; }
.guide-content { flex: 1; overflow-y: auto; padding: 22px 30px 28px; outline: none; }
.guide-content h2 { font-size: 22px; margin: 0 0 6px; }
.guide-content h4 { font-size: 14px; margin: 22px 0 8px; }
.guide-lead { font-size: 14px; color: var(--text-2); line-height: 1.6; margin: 0 0 16px; max-width: 760px; }
.guide-content p, .guide-content li { font-size: 13.5px; color: var(--text-2); line-height: 1.6; }
.guide-content ul { padding-left: 20px; margin: 6px 0 10px; }
.guide-content strong { color: var(--text-1); }
.guide-shot { display: block; max-width: 100%; max-height: 260px; width: auto; margin: 10px 0 6px; border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 6px 22px rgba(0,0,0,.12); background: #fff; }
.guide-cap { font-size: 11.5px; color: var(--text-3); margin-bottom: 8px; }
.guide-ideas { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 10px; margin: 14px 0 6px; }
.guide-idea { border: 1px solid var(--border); border-radius: 10px; padding: 11px 13px; background: var(--surface2); }
.guide-idea b { display: block; font-size: 13px; color: var(--text-1); margin-bottom: 3px; }
.guide-idea span { font-size: 12.5px; color: var(--text-2); line-height: 1.5; }
.guide-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 4px; }
.guide-tip { display: flex; gap: 10px; align-items: flex-start; margin-top: 16px; padding: 11px 14px; border-radius: 10px; background: rgba(10,132,255,.08); border: 1px solid rgba(10,132,255,.25); font-size: 13px; color: var(--text-2); line-height: 1.55; }
.guide-tip svg { color: var(--accent-2); flex-shrink: 0; margin-top: 2px; }
/* Quick Start checklist */
.guide-progress { display: flex; align-items: center; gap: 12px; margin: 4px 0 18px; }
.guide-progress-bar { flex: 1; max-width: 360px; height: 8px; border-radius: 4px; background: var(--surface2); overflow: hidden; }
.guide-progress-bar > div { height: 100%; border-radius: 4px; background: linear-gradient(90deg, #bf5af2, #0a84ff); transition: width .5s; }
.guide-progress-t { font-size: 12.5px; font-weight: 600; color: var(--text-2); }
.guide-step { display: grid; grid-template-columns: 34px 1fr; gap: 14px; padding: 16px 0; border-top: 1px solid var(--border); }
.guide-step:first-of-type { border-top: none; }
.guide-step-n { width: 34px; height: 34px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: 14px; color: var(--text-2); background: var(--surface2); border: 1px solid var(--border); }
.guide-step.done .guide-step-n { background: #30d158; border-color: #30d158; color: #fff; }
.guide-step-h { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.guide-step-t { font-size: 15px; font-weight: 700; color: var(--text-1); }
.guide-badge { font-size: 10.5px; font-weight: 700; letter-spacing: .03em; padding: 2px 8px; border-radius: 20px; }
.guide-badge.ok { color: #1f9d48; background: rgba(48,209,88,.14); }
.guide-badge.todo { color: #c77c00; background: rgba(245,166,35,.15); }
.guide-badge.opt { color: var(--text-3); background: var(--surface2); border: 1px solid var(--border); }
.guide-step-s { font-size: 12.5px; color: var(--text-3); margin-top: 3px; }
.guide-step p { margin: 6px 0 0; }
.guide-done-banner { display: flex; gap: 12px; align-items: center; padding: 14px 16px; border-radius: 12px; margin-bottom: 14px; background: linear-gradient(135deg, rgba(48,209,88,.12), rgba(10,132,255,.10)); border: 1px solid rgba(48,209,88,.35); font-size: 13.5px; color: var(--text-2); }
.guide-done-banner svg { color: #30d158; flex-shrink: 0; }
.guide-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 24px; border-top: 1px solid var(--border); flex-shrink: 0; background: var(--surface); }
.guide-hide { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-2); cursor: pointer; user-select: none; }
.guide-hide input { margin: 0; width: auto; }
.guide-pager { display: flex; align-items: center; gap: 8px; }
.guide-pos { font-size: 12px; color: var(--text-3); margin-right: 4px; }
@media (max-width: 760px) {
  .guide-modal { width: 100vw; max-width: 100vw; height: 100dvh; border-radius: 0; }
  .guide-head { padding: 12px 14px; }
  .guide-sub { display: none; }
  .guide-body { flex-direction: column; }
  .guide-nav { width: auto; display: flex; gap: 6px; overflow-x: auto; overflow-y: hidden; border-right: none; border-bottom: 1px solid var(--border); padding: 8px; }
  .guide-topic { width: auto; flex-shrink: 0; margin: 0; padding: 7px 10px; align-items: center; }
  .guide-topic-d, .guide-topic-n { display: none; }
  .guide-topic-ic { width: 24px; height: 24px; }
  .guide-content { padding: 16px; }
  .guide-foot { padding: 10px 14px; flex-wrap: wrap; }
  .guide-pos { display: none; }
}
/* The generic `.modal > div:not(.modal-header)…` rule gives every direct child
   flex:1 and its own padding, which split the Guide into three equal thirds.
   Same fix as the file browser (.fbnew-modal): take sizing back. */
.guide-modal > .guide-head { flex: 0 0 auto !important; padding: 18px 24px 14px !important; min-height: 0 !important; }
.guide-modal > .guide-body { flex: 1 1 auto !important; padding: 0 !important; min-height: 0 !important; }
.guide-modal > .guide-foot { flex: 0 0 auto !important; padding: 12px 24px !important; min-height: 0 !important; }
@media (max-width: 760px) {
  .guide-modal > .guide-head { padding: 12px 14px !important; }
  .guide-modal > .guide-foot { padding: 10px 14px !important; }
}
body.is-phone .guide-modal > .guide-head { padding: 12px 14px !important; }
body.is-phone .guide-modal > .guide-body { padding: 0 !important; }
body.is-phone .guide-modal > .guide-foot { padding: 10px 14px !important; }

/* ===== Top header modes (user menu → Top header; static/net-header.js) ===== */
#user-menu-dropdown .um-section { padding: 8px 14px 4px; font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-3); border-top: 1px solid var(--border); }
#user-menu-dropdown .um-hdr { width: 100%; text-align: left; padding: 7px 14px; background: none; border: none; color: var(--text);
  font-size: 13px; cursor: pointer; display: flex; align-items: center; gap: 8px; white-space: nowrap; }
#user-menu-dropdown .um-hdr:hover { background: var(--surface3, rgba(255,255,255,0.07)); }
#user-menu-dropdown .um-hdr .hdr-check { visibility: hidden; flex: none; color: var(--accent-2, #0a84ff); }
#user-menu-dropdown .um-hdr.on .hdr-check { visibility: visible; }
#user-menu-dropdown .um-hdr.on { font-weight: 600; }

#net-title, #net-summary, #net-eq, #net-bar { display: none; }
/* Network focus: the gateway replaces the pool capacity bar and disk I/O */
body.net-header #topbar-pool-title,
body.net-header #pool-summary,
body.net-header #pool-bar,
body.net-header #pool-legend,
body.net-header #pool-tip,
body.net-header #io-bar,
body.net-header #io-chart-tooltip { display: none !important; }
body.net-header #net-title { display: inline; }
body.net-header:not(.compact-header) #net-summary { display: inline; text-align: center; }
body.net-header:not(.compact-header) .topbar-inner { margin-bottom: 0; }
body.net-header:not(.compact-header) #net-bar { display: flex; }
body.net-header.compact-header #net-eq { display: flex; }

#net-bar { flex-shrink: 0; background: var(--surface); border-bottom: 1px solid var(--border); padding: 6px 16px 6px;
  gap: 14px; align-items: stretch; }
.net-chart-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; position: relative; }
.net-chart-wrap canvas { display: block; width: 100% !important; height: 110px !important; }
.net-key { float: right; display: inline-flex; align-items: center; gap: 5px; text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--text-2); }
#vn-chart { cursor: crosshair; }
/* the stack's legend: busiest lines over its 20 minutes, avg / max */
.vn-top { flex: none; width: 300px; display: flex; flex-direction: column; min-height: 0; overflow: hidden; font-size: 11px;
  font-variant-numeric: tabular-nums; border-left: 1px solid var(--border); padding-left: 12px; }
.vn-top .vt-r { display: grid; grid-template-columns: 9px minmax(0,1fr) 92px 92px; gap: 6px; align-items: center; height: 15px; line-height: 15px; }
.vn-top .vt-h { color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .02em; margin-bottom: 2px; flex: none; }
.vn-top .vt-h > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vn-top .vt-r { flex: none; }
.vn-top .vt-sw { width: 9px; height: 9px; border-radius: 2px; }
.vn-top .vt-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.vn-top .vt-v { text-align: right; white-space: nowrap; color: var(--text-2); }
.vn-top .vt-v b { font-weight: 600; }
.vn-top .vt-ul b { color: #bf5af2; }
.vn-top .vt-dl b { color: #64d2ff; }
.vn-top .vt-z { color: var(--text-3); opacity: .6; }
@media (max-width: 1100px) { .vn-top { display: none !important; } }
.net-chart-wrap.vn-on .io-chart-label { display: none; }
.net-chart-wrap.vn-on #vn-chart { flex: 1 1 0; height: 0 !important; min-height: 110px; } /* fills the row; never sized by its own pixels */
.vn-tip { position: fixed; z-index: 3500; pointer-events: none; min-width: 270px; max-width: calc(100vw - 16px);
  background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 14px 40px rgba(0,0,0,.38); font-size: 12px; overflow: hidden; }
.vn-tip .vn-h { padding: 8px 12px; background: var(--surface2); border-bottom: 1px solid var(--border); }
.vn-tip .vn-t { color: var(--text-3); font-size: 11px; }
.vn-tip .vn-tot { display: flex; gap: 14px; margin-top: 3px; font-variant-numeric: tabular-nums; }
.vn-tip .vn-tot b { font-size: 13px; }
.vn-tip .vn-rows { padding: 4px 0; }
.vn-tip .vn-r { display: grid; grid-template-columns: 10px minmax(0,1fr) auto auto; gap: 4px 10px; align-items: center; padding: 3px 12px; font-variant-numeric: tabular-nums; }
.vn-tip .vn-r.vn-other { border-top: 1px solid var(--border); margin-top: 3px; padding-top: 6px; color: var(--text-2); }
.vn-tip .vn-sw { width: 10px; height: 10px; border-radius: 3px; }
.vn-tip .vn-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vn-tip .vn-dl { color: #64d2ff; text-align: right; }
.vn-tip .vn-ul { color: #bf5af2; text-align: right; }
.vn-tip .vn-z { color: var(--text-3); opacity: .6; }
.net-sw { width: 8px; height: 8px; border-radius: 2px; display: inline-block; margin-left: 6px; }
.net-empty { position: absolute; inset: 18px 0 0; display: none; align-items: center; justify-content: center; font-size: 12px; color: var(--text-3); }
.net-gauges { display: flex; gap: 4px; flex: none; position: relative; }
/* Gateway latency, in the empty space at the top between the two dials */
.net-rtt { position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 1; padding: 1px 5px; border: 0; border-radius: 6px;
  background: none; color: var(--text-2); font: 600 10.5px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; cursor: pointer;
  font-variant-numeric: tabular-nums; }
.net-rtt:hover, .net-rtt:focus-visible { background: var(--surface2); color: var(--text); outline: none; }
.net-rtt-s { opacity: .45; margin: 0 1px; }
.net-rtt-u { color: var(--text-3); font-weight: 500; margin-left: 1px; }
.net-rtt-bad { color: #ff453a; }
.vn-tip.net-rtt-pop { pointer-events: auto; width: min(400px, calc(100vw - 16px)); }
.net-rtt-pop .vn-tot { display: flex; gap: 14px; }
.net-rtt-pop .vn-tot span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.net-rtt-body { padding: 10px 12px; font-size: 12px; color: var(--text-2); }
.net-rtt-body p { margin: 8px 0 0; line-height: 1.45; }
.net-rtt-svg { width: 100%; height: auto; display: block; }
.net-rtt-line { fill: none; stroke: #64d2ff; stroke-width: 1.5; stroke-linejoin: round; }
.net-rtt-lost { stroke: #ff453a; stroke-width: 1; opacity: .55; }
.net-rtt-grid { stroke: var(--border); stroke-width: 1; }
.net-rtt-ax { fill: var(--text-3); font-size: 9px; font-variant-numeric: tabular-nums; }
.net-rtt-empty { padding: 26px 0; text-align: center; color: var(--text-3); }
.net-gauge { width: 132px; display: flex; flex-direction: column; align-items: center; }
.net-gauge svg { display: block; overflow: visible; }
.ng-track { fill: none; stroke: rgba(128,128,128,.22); stroke-width: 10; stroke-linecap: round; }
.ng-val { fill: none; stroke-width: 10; stroke-linecap: round; }
.ng-tick { stroke: var(--text-3); stroke-width: 1; opacity: .7; }
.ng-tl { fill: var(--text-3); font-size: 7.5px; text-anchor: middle; font-variant-numeric: tabular-nums; }
.ng-needle { stroke-width: 2.5; stroke-linecap: round; }
.ng-peak { stroke: #ff453a; stroke-width: 3; stroke-linecap: round; }
.ng-v { fill: var(--text); font-size: 18px; font-weight: 700; text-anchor: middle; font-variant-numeric: tabular-nums; }
.ng-u { fill: var(--text-3); font-size: 9px; font-weight: 500; }
.ng-n { font-size: 8.5px; font-weight: 700; letter-spacing: .08em; }
.ng-max { margin-top: -4px; font-size: 10.5px; color: var(--text-3); display: flex; align-items: center; gap: 5px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ng-mk { width: 10px; height: 3px; border-radius: 2px; background: #ff453a; display: inline-block; }

/* Network focus, compact: two equalizer lines in the one-row header */
#net-eq { flex: 1; min-width: 0; flex-direction: column; gap: 2px; justify-content: center; }
.net-eq-row { display: flex; align-items: center; gap: 6px; }
.net-eq-l { font-size: 9px; font-weight: 700; color: var(--text-3); width: 14px; letter-spacing: .04em; }
.net-eq-bars { display: flex; gap: 2px; }
.net-eq-bars i { width: 9px; height: 5px; border-radius: 1px; opacity: .18; transition: opacity .25s; }
.net-eq-bars i.g { background: #30d158; }
.net-eq-bars i.y { background: #ffd60a; }
.net-eq-bars i.r { background: #ff453a; }
.net-eq-bars i.on { opacity: 1; }
.net-eq-bars i.pk { opacity: 1; box-shadow: 0 0 0 1px rgba(255,255,255,.55); }
.net-eq-v { font-size: 10.5px; color: var(--text-2); font-variant-numeric: tabular-nums; white-space: nowrap; min-width: 78px; }
@media (max-width: 760px) {
  .net-gauge { width: 104px; }
  .net-gauge svg { width: 104px; height: 99px; }
  .net-eq-bars i { width: 6px; }
}
@media (max-height: 500px) {
  #net-bar { display: none !important; }
}
/* two 10 px lines: the compact row stays the height of the storage one */
.net-eq-row { height: 10px; }
.net-eq-l, .net-eq-v { line-height: 10px; }

/* Network focus: top talkers popup (static/net-header.js) */
.net-gauge, .net-eq-row { cursor: pointer; touch-action: manipulation; }
.tk-pop { position: fixed; z-index: 3500; width: min(560px, calc(100vw - 16px)); display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 18px 48px rgba(0,0,0,.42); animation: tk-in .14s ease-out; }
@keyframes tk-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.tk-h { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 12px; background: var(--surface2); border-bottom: 1px solid var(--border);
  border-top: 2px solid var(--tk-acc); }
.tk-seg { display: inline-flex; gap: 2px; padding: 2px; border-radius: 9px; background: var(--surface); }
.tk-seg button { display: inline-flex; align-items: center; gap: 5px; border: 0; background: none; color: var(--text-3); font-size: 12px; font-weight: 600;
  padding: 5px 9px; border-radius: 7px; cursor: pointer; white-space: nowrap; }
.tk-seg button.on { background: var(--surface2); color: var(--text); box-shadow: inset 0 0 0 1px var(--tk-acc); }
.tk-total { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-3); white-space: nowrap; }
.tk-total b { color: var(--text); font-variant-numeric: tabular-nums; }
.tk-pulse { width: 7px; height: 7px; border-radius: 50%; background: #30d158; display: inline-block; animation: tk-pulse 1.6s infinite; flex: none; }
@keyframes tk-pulse { 50% { opacity: .3; } }
.tk-list { overflow-y: auto; flex: 1; min-height: 0; -webkit-overflow-scrolling: touch; }
.tk-row { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-bottom: 1px solid var(--border); }
.tk-row:last-child { border-bottom: 0; }
.tk-av { width: 30px; height: 30px; border-radius: 9px; flex: none; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 700; font-size: 14px; }
.tk-main { flex: 1; min-width: 0; }
.tk-l1 { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.tk-name { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.tk-rate { font-size: 11px; color: var(--text-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
.tk-rate b { font-size: 15px; color: var(--text); }
.tk-l2 { display: flex; gap: 8px; font-size: 11px; color: var(--text-3); margin-top: 1px; min-width: 0; }
.tk-ip { font-family: var(--font-mono, monospace); flex: none; }
.tk-fqdn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.tk-bar { height: 4px; border-radius: 3px; background: rgba(128,128,128,.18); margin: 5px 0 4px; overflow: hidden; }
.tk-bar i { display: block; height: 100%; border-radius: 3px; transition: width .4s; }
.tk-l3 { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; font-size: 10.5px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.tk-st { display: inline-flex; align-items: center; gap: 3px; white-space: nowrap; }
.tk-dl svg { color: #64d2ff; } .tk-ul svg { color: #bf5af2; }
.tk-sh { margin-left: auto; color: var(--text-2); font-weight: 600; }
.tk-spark { flex: none; display: block; }
.tk-msg { padding: 26px 18px; text-align: center; font-size: 13px; color: var(--text-2); line-height: 1.5; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.tk-f { padding: 6px 12px; font-size: 10.5px; color: var(--text-3); border-top: 1px solid var(--border); background: var(--surface2); }
@media (max-width: 520px) { .tk-spark { display: none; } }
/* one line at any width: the summary gives way first */
body.net-header #net-title { white-space: nowrap; }
body.net-header:not(.compact-header) #net-summary { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
body.net-header .topbar-inner { column-gap: 12px; }
/* the squares take all the width the header has left */
.net-eq-row { width: 100%; min-width: 0; }
.net-eq-bars { flex: 1; min-width: 0; overflow: hidden; }
.net-eq-bars i { flex: none; }
.net-eq-v { flex: none; min-width: 172px; text-align: left; }
@media (max-width: 760px) { .net-eq-v { min-width: 0; font-size: 9.5px; } }
.net-eq-bars { justify-content: space-between; }
/* a phone has room for the squares only: the popup gives the numbers */
@media (max-width: 560px) { .net-eq-v { display: none; } }

/* Manage InterLink: health */
.il-head{display:flex;align-items:center;gap:6px;font-weight:600;font-size:13px;margin-bottom:6px}
.il-row{display:flex;gap:12px;padding:7px 0;border-top:1px solid var(--border);align-items:flex-start}
.il-name{width:110px;flex:none;font-weight:600;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.il-chips{display:flex;flex-wrap:wrap;gap:4px 5px;align-items:center;min-width:0}
.il-chip{font-size:11px;padding:1px 7px;border-radius:9px;white-space:nowrap}
.il-chip.ok{color:#16a34a;background:rgba(34,197,94,.12)}
.il-chip.warn{color:#d97706;background:rgba(245,158,11,.14)}
.il-chip.bad{color:#ef4444;background:rgba(239,68,68,.13);font-weight:600}
.il-fix{font-size:11px!important;padding:1px 7px!important;margin-right:4px}
.il-grid .il-fix{margin:0 0 0 6px}
.il-scroll{overflow-x:auto}
.il-grid{border-collapse:collapse;font-size:12px;width:100%}
.il-grid th,.il-grid td{border:1px solid var(--border);padding:4px 7px;text-align:center;white-space:nowrap}
.il-grid th{font-weight:600}
.il-grid tbody th{text-align:left}
.il-self{background:var(--border)}
.il-good{color:#16a34a}
.il-warnc{color:#d97706}
.il-badc{color:#ef4444;font-weight:600}
.il-na{color:var(--text-3)}
