@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

/* ============================================================
   NodeTrace Dashboard — Enterprise / Professional SaaS Grade
   ============================================================ */

:root {
  /* Dark Two-Tone (Black, Grey, White) */
  --accent:   #3b82f6;
  --accent-h: #2563eb;
  --accent-rgb: 59,130,246;
  --bg:       #000000;
  --surface:  #0a0a0a;
  --surface2: #111111;
  --surface3: #1a1a1a;
  --surface-rgb: 10,10,10;
  --surface2-rgb: 17,17,17;
  --surface3-rgb: 26,26,26;
  --nav-bg:   #000000;
  --nav-text: #ffffff;
  --text:     #ffffff;
  --muted:    #a3a3a3;
  --border:   #262626;
  --shadow:   0 4px 16px rgba(0,0,0,0.8);
  --shadow-sm: 0 1px 4px rgba(0,0,0,0.6);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.7);
  --shadow-lg: 0 16px 40px rgba(0,0,0,0.9);
  --shadow-accent: 0 4px 12px rgba(59,130,246,0.15);
  --ease: cubic-bezier(.2,.8,.2,1);
  --spring: cubic-bezier(.34,1.56,.64,1);
  --spring-soft: cubic-bezier(.3,1.08,.4,1);
  --glass-border: rgba(255,255,255,0.08);
  --glass-shine: rgba(255,255,255,0.02);
  --glass-blur: 16px;
  --red:   #ef4444;
  --green: #10b981;
  --blue:  #3b82f6;
  --amber: #f59e0b;
}



:root[data-theme="light"] {
  /* Azure Classic (Light) */
  --accent:   #3b82f6;
  --accent-h: #2563eb;
  --accent-rgb: 59,130,246;
  --bg:       #f5f6f8;
  --surface:  #ffffff;
  --surface2: #eef1f6;
  --surface3: #e4e8f0;
  --surface-rgb: 255,255,255;
  --surface2-rgb: 238,241,246;
  --surface3-rgb: 228,232,240;
  --nav-bg:   #0f172a;
  --nav-text: #ffffff;
  --text:     #1a2233;
  --muted:    #5b6579;
  --border:   #dfe3ea;
  --shadow:     0 4px 24px rgba(15,23,42,0.08);
  --shadow-sm:  0 1px 3px rgba(15,23,42,0.06);
  --shadow-md:  0 6px 20px rgba(15,23,42,0.08);
  --shadow-lg:  0 16px 48px rgba(15,23,42,0.12);
  --shadow-accent: 0 6px 20px rgba(59,130,246,0.18);
  --glass-border: rgba(15,23,42,0.08);
  --glass-shine:  rgba(255,255,255,0.6);
}



/* ---------- Theme Popout Menu ---------- */
.theme-menu-wrapper {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.theme-popout {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 10px;
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: row;
  gap: 8px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
  z-index: 100;
}
.theme-menu-wrapper:hover .theme-popout {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* ---------- Tier Filter Popout (click-toggled, not hover — holds checkboxes) ---------- */
.tier-filter-wrapper {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.tier-filter-popout {
  position: absolute;
  top: calc(100% + 4px);
  /* Anchored from the right, not the left: .tier-filter-wrapper sits at the
     right edge of .site-tabs-row (justify-content:space-between), so a
     left:0 popout opened rightward off the edge of narrow viewports --
     found via a real mobile-width browser check, not just visually clipped
     but the actual open-state content was unreachable. */
  right: 0;
  min-width: 140px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 6px;
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
  z-index: 100;
}
.tier-filter-popout.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.theme-swatch {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform 0.2s var(--spring);
  box-shadow: var(--shadow-sm);
  background-clip: padding-box;
}
.theme-swatch:hover {
  transform: scale(1.15);
}
.theme-swatch.active {
  border-color: var(--text);
  transform: scale(1.15);
}

* { margin:0; padding:0; box-sizing:border-box; }
*:focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:4px; }
html { scroll-behavior:smooth; }

body {
  font-family:'Inter',sans-serif;
  background:var(--bg);
  color:var(--text);
  min-height:100vh;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  position:relative;
  overflow-x:hidden;
  transition:background-color 0.3s ease, color 0.3s ease;
}

body::before, body::after {
  content:''; position:fixed; border-radius:50%; filter:blur(100px); opacity:0.06;
  pointer-events:none; z-index:0;
}
body::before { width:500px; height:500px; background:var(--accent); top:-250px; left:-150px; }
body::after  { width:600px; height:600px; background:var(--surface3); bottom:-300px; right:-150px; }

@keyframes orbFloat { 0%,100% { transform:translate(0,0) scale(1); } 50% { transform:translate(30px,20px) scale(1.1); } }
@keyframes fadeUp { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
@keyframes overlayIn { from { opacity:0; } to { opacity:1; } }
@keyframes modalIn { from { opacity:0; transform:translateY(10px) scale(0.96); } to { opacity:1; transform:translateY(0) scale(1); } }
@keyframes pulseWarn { 0% { opacity: 1; text-shadow: 0 0 8px rgba(251,113,133,0.5); } 50% { opacity: 0.65; text-shadow: none; } 100% { opacity: 1; text-shadow: 0 0 8px rgba(251,113,133,0.5); } }
.pulse-warn { animation: pulseWarn 2s infinite ease-in-out; }

::-webkit-scrollbar { width:10px; height:10px; }
::-webkit-scrollbar-track { background:transparent; }
::-webkit-scrollbar-thumb { background:var(--surface3); border-radius:20px; border:2px solid var(--bg); background-clip:padding-box; }
::-webkit-scrollbar-thumb:hover { background:var(--accent); background-clip:padding-box; }
* { scrollbar-width:thin; scrollbar-color:var(--surface3) transparent; }

/* ---------- App shell layout (NodeTrace-specific structure) ---------- */

.app-shell {
  position:relative; z-index:1;
  display:grid; grid-template-columns:68px 1fr; gap:0;
  min-height:100vh;
}
.app-main-col { grid-column:2; display:flex; flex-direction:column; min-width:0; }

/* ---------- Sidebar ---------- */

.sidebar {
  position:sticky; top:0; height:100vh;
  width:68px; overflow:hidden; z-index:1000;
  transition:width 0.3s var(--spring-soft), background 0.3s var(--ease);
  background:var(--nav-bg); color:var(--nav-text);
  backdrop-filter:blur(var(--glass-blur)) saturate(1.5); -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(1.5);
  border-right:1px solid var(--glass-border);
  padding:20px 14px;
  display:flex; flex-direction:column; gap:24px;
}
.sidebar:hover { width:248px; }

.brand { display:flex; align-items:center; gap:12px; padding:0 6px; }
.brand-mark {
  width:32px; height:32px; border-radius:8px;
  background:var(--accent);
  display:flex; align-items:center; justify-content:center;
  font-weight:700; font-size:14px; color:#fff;
  flex-shrink: 0;
}
.brand-text { font-family:'Rajdhani',sans-serif; font-size:22px; color:var(--text); line-height:1.1; white-space:nowrap; }

.nav-group { display:flex; flex-direction:column; gap:4px; }
.nav-label { font-size:10.5px; font-weight:700; color:var(--muted); text-transform:uppercase; letter-spacing:0.8px; margin:8px 12px 4px; white-space:nowrap; }

.nav-item {
  display:flex; align-items:center; gap:8px;
  padding:8px 12px; border-radius:6px;
  color:var(--muted); font-size:13px; font-weight:500; white-space:nowrap;
  cursor:pointer; transition:all 0.15s var(--ease); border:1px solid transparent;
}
.nav-item svg { width:16px; height:16px; opacity:0.85; flex-shrink:0; transition:none; }
.nav-item span, .brand-text span, .nav-label { opacity:0; pointer-events:none; transition:opacity 0.2s var(--ease); white-space: nowrap; overflow: hidden; }
.sidebar:hover .nav-item span, .sidebar:hover .brand-text span, .sidebar:hover .nav-label { opacity:1; pointer-events:auto; transition-delay:0.1s; }
.nav-item:hover { 
  background:var(--surface2); color:var(--text);
}
.nav-item:hover svg { color:var(--text); }
.nav-item.active {
  background:rgba(var(--accent-rgb),0.1); 
  border-color:rgba(var(--accent-rgb),0.2);
  color:var(--accent);
}

/* ---------- Topbar (mirrors YEIPL's <header>) ---------- */

.topbar {
  height:56px; padding:0 24px; display:flex; align-items:center; justify-content:space-between;
  background:var(--nav-bg); color:var(--nav-text);
  border-bottom:1px solid var(--glass-border);
  padding:0 24px; height:58px;
  display:flex; align-items:center; justify-content:space-between;
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
  box-shadow:var(--shadow-sm), inset 0 1px 0 var(--glass-shine), inset 0 -2px 0 var(--accent);
  position:sticky; top:0; z-index:100;
  backdrop-filter:blur(var(--glass-blur)) saturate(1.5); -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(1.5);
}

.site-select {
  display:none !important; /* superseded by .site-tabs-row (2026-08-02) — the
  <select> inside is kept alive purely as a hidden data model, see dashboard.js */
  align-items:center; gap:9px;
  background:var(--surface2); border:1px solid var(--border); border-radius:20px;
  padding:5px 13px; font-size:12px; color:var(--muted); font-weight:500;
}

/* ---------- Site Tabs Row (replaces the .site-select dropdown) ---------- */
.site-tabs-row {
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:10px 24px; background:var(--nav-bg); color:var(--nav-text);
  border-bottom:1px solid var(--glass-border);
}
.site-tabs-list { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.site-tab {
  background:var(--surface2); border:1px solid var(--border); border-radius:20px;
  padding:6px 14px; font-size:12px; font-weight:600; color:var(--muted); cursor:pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease), border-color 0.15s var(--ease);
}
.site-tab:hover { color:var(--text); border-color:var(--accent); }
.site-tab.active { background:var(--accent); color:#fff; border-color:var(--accent); }
.site-tab-more-wrapper { position:relative; display:inline-flex; }
.site-tab-more-popout {
  position:absolute; top:calc(100% + 4px); left:0; min-width:180px; max-height:280px; overflow-y:auto;
  background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:6px;
  box-shadow:var(--shadow-lg); display:flex; flex-direction:column;
  opacity:0; visibility:hidden; transform:translateY(-4px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
  z-index:100;
}
.site-tab-more-popout.open { opacity:1; visibility:visible; transform:translateY(0); }
.site-tab-more-item { padding:7px 10px; border-radius:8px; font-size:13px; cursor:pointer; color:var(--text); white-space:nowrap; }
.site-tab-more-item:hover { background:var(--surface2); }
.site-tab-more-item.active { color:var(--accent); font-weight:700; }
.site-select .dot { width:7px; height:7px; border-radius:50%; background:var(--green); box-shadow:0 0 8px rgba(34,197,94,0.6); }

/* margin-left:auto (not just the parent .topbar's justify-content:space-
   between) is what actually anchors this to the right edge -- a single-site
   client login hides its sibling .site-select entirely (display:none, see
   dashboard.js applyRoleUI()), and space-between with only one flex child
   left pins that child to the START of the row, not the end. The auto
   margin keeps this block right-aligned regardless of whether .site-select
   is present, so admin and client accounts get the identical topbar layout. */
.topbar-right { display:flex; align-items:center; gap:12px; margin-left:auto; }

/* Hamburger: hidden on desktop, shown only under the mobile breakpoint
   below (::860px). The sidebar there is a hover-expand rail with no way
   for a touchscreen to trigger :hover, so it needs a real open/close
   control instead — see dashboard.js setupMobileNav(). */
.mobile-nav-toggle { display:none; }

/* Add-to-Home-Screen button -- mobile only (desktop browsers already have
   their own install affordance, e.g. Chrome's omnibox icon) and only ever
   shown at all once setupPwaInstall() (dashboard.js) confirms installing is
   actually possible/relevant on this device. `button.pwa-install-btn` (not
   just `.pwa-install-btn`) so this beats .theme-toggle's own
   `display:flex` on specificity -- same class-only specificity, and
   .theme-toggle's rule sits later in this file, so source order alone
   would otherwise let display:flex win and show the button unconditionally
   (caught via a real headless-browser check, not by reading the CSS). */
button.pwa-install-btn { display:none; }

.sidebar-overlay {
  display:none; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:999;
  opacity:0; pointer-events:none; transition:opacity 0.25s var(--ease);
}
.sidebar-overlay.active { opacity:1; pointer-events:auto; }

.license-pill {
  display:flex; align-items:center; gap:7px;
  padding:5px 12px; border-radius:20px; font-size:11px; font-weight:700;
  background:var(--surface2); border:1px solid var(--border);
}
.license-pill.active { color:var(--green); }
.license-pill.grace_period { color:var(--amber); }
.license-pill.locked { color:var(--red); }
.license-pill .dot { width:7px; height:7px; border-radius:50%; background:currentColor; box-shadow:0 0 8px currentColor; }

.avatar {
  min-width:34px; height:34px; border-radius:17px; padding:0 10px;
  background:var(--surface2); border:1.5px solid var(--border);
  display:flex; align-items:center; justify-content:center;
  font-size:12px; font-weight:700; color:var(--accent);
  transition:border-color 0.2s var(--ease);
  white-space:nowrap;
}
.avatar:hover { border-color:var(--red); color:var(--red); }

/* ---------- Theme toggle ---------- */

.theme-toggle {
  width:34px; height:34px; border-radius:50%;
  background:var(--surface2); border:1.5px solid var(--border);
  display:flex; align-items:center; justify-content:center;
  color:var(--muted); cursor:pointer; padding:0;
  transition:border-color 0.2s var(--ease), color 0.2s var(--ease), transform 0.15s var(--spring);
}
.theme-toggle svg { width:17px; height:17px; }
.theme-toggle:hover { border-color:var(--accent); color:var(--accent); }
.theme-toggle:active { transform:scale(0.92); }
.theme-toggle.active { border-color:var(--accent); color:var(--accent); background:rgba(var(--accent-rgb),0.12); }

/* ---------- View density (Comfortable default / Dense) ----------
   Toggled via #densityToggle, stored in localStorage, applied as
   [data-density] on <html> (see index.html head script + dashboard.js
   setDensity()). Comfortable uses the base spacing defined above/below;
   this block only overrides the gaps/padding that make the dashboard feel
   "tighter" in dense mode -- font sizes and colors are untouched. */
:root[data-density="dense"] .main { gap:10px; padding:12px 16px 40px; }
:root[data-density="dense"] .kpi-grid { gap:6px; }
:root[data-density="dense"] .kpi-card { padding:8px; }
:root[data-density="dense"] .content-grid { gap:6px; }
:root[data-density="dense"] .panel { padding:10px; }
:root[data-density="dense"] .panel-header { margin-bottom:8px; }
:root[data-density="dense"] .device-table th,
:root[data-density="dense"] .device-table td { padding:6px 10px; }
:root[data-density="dense"] .saas-table th,
:root[data-density="dense"] .saas-table td { padding:7px 12px; }
:root[data-density="dense"] .kanban { gap:8px; }
:root[data-density="dense"] .kanban-col-body,
:root[data-density="dense"] .kanban-col > .glass { gap:5px; padding:5px 4px 10px; }
:root[data-density="dense"] .ticket-card { padding:7px 10px; }

/* ---------- Main content ---------- */

.main { grid-column:2; display:flex; flex-direction:column; gap:20px; padding:20px 24px 80px; }

.view { display:none; flex-direction:column; gap:20px; }
.view.active { display:flex; animation:fadeUp 0.3s var(--ease); }

.section-title { font-family:'Rajdhani',sans-serif; font-size:24px; font-weight:700; letter-spacing:0.2px; margin:0 0 2px; color:var(--text); }
.section-sub { font-size:13px; color:var(--muted); margin:0; }

/* ---------- KPI / summary cards (mirrors .inv-summary-card) ---------- */

.kpi-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:8px; }

.kpi-card {
  background:var(--surface);
  border:1px solid var(--border); border-radius:8px;
  padding:12px; position:relative; overflow:hidden;
  display:flex; flex-direction:column; justify-content:center;
  transition:box-shadow 0.2s var(--ease), border-color 0.2s var(--ease);
}
.kpi-card:hover {
  border-color:var(--accent);
  box-shadow:var(--shadow-md);
}
.kpi-card.clickable { cursor:pointer; }
.kpi-card.clickable:active { transform:scale(0.98); }

.kpi-top { display:flex; align-items:center; justify-content:space-between; }
.kpi-icon { width:32px; height:32px; border-radius:6px; display:flex; align-items:center; justify-content:center; background:rgba(var(--accent-rgb),0.1); color:var(--accent); }
.kpi-icon svg { width:16px; height:16px; }

.kpi-value { font-size:24px; font-weight:600; color:var(--text); letter-spacing:-0.5px; }
.kpi-label { font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.5px; color:var(--muted); }
.kpi-trend { font-size:11px; font-weight:600; }
.kpi-trend.up { color:var(--green); }
.kpi-trend.down { color:var(--red); }

/* ---------- Content grid ---------- */

.content-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; align-items:stretch; }

.panel {
  background:var(--surface);
  border:1px solid var(--border); border-radius:10px;
  padding:16px; box-shadow:var(--shadow-sm);
  display:flex; flex-direction:column;
  min-width:0; /* prevent grid blowout */
}
/* Table-bearing panels (Devices/Tickets/Inventory/Top-N) get their own
   horizontal scroll region instead of relying on the page-level scroll —
   `body`'s `overflow-x:hidden` above would otherwise just clip a wide
   table's extra columns off-screen on narrow viewports with no way to
   reach them. */
.panel:has(table) { overflow-x:auto; -webkit-overflow-scrolling:touch; }

.panel-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; }
.panel-header h3 { margin:0; font-size:15px; font-weight:600; color:var(--text); letter-spacing:0; }

.chip-row { display:flex; gap:6px; }
.chip {
  padding:5px 12px; border-radius:8px; font-size:11px; font-weight:700;
  color:var(--muted); background:transparent; border:1.5px solid var(--border);
  cursor:pointer; transition:all 0.18s var(--ease);
}
.chip:hover { border-color:var(--accent); color:var(--accent); }
.chip.active { background:var(--accent); color:#fff; border-color:var(--accent); box-shadow:0 3px 10px rgba(255,107,0,0.3); }

.chart-wrap { flex:1; position:relative; height:230px; width:100%; display: flex; flex-direction: column; justify-content: center; }

/* Highcharts always draws invisible 3D "frame" wall panels over the whole
   plot area when options3d.enabled is true, even for pie charts that have
   no axes. They paint on top of the pie slices and default to
   pointer-events:fill, silently swallowing hover/click before it reaches
   the slices underneath -- breaking the hover slice-out effect in 3D mode. */
#categoryChart .highcharts-3d-frame { pointer-events: none; }

/* ---------- License state demo ---------- */

.license-demo { display:flex; flex-direction:column; gap:12px; }
.license-state-row { display:flex; gap:8px; }
.license-btn {
  flex:1; padding:8px 8px; border-radius:8px; font-size:11px; font-weight:700; text-align:center;
  border:1.5px solid var(--border); color:var(--muted); background:transparent; cursor:pointer;
  transition:all 0.18s var(--ease);
}
.license-btn:hover { border-color:var(--accent); color:var(--accent); }
.license-btn.selected { background:var(--accent); color:#fff; border-color:var(--accent); box-shadow:0 3px 10px rgba(255,107,0,0.3); }

.license-status-box { border-radius:8px; padding:14px; font-size:12.5px; line-height:1.55; border:1px solid var(--border); }
.license-status-box b { font-family:'Rajdhani',sans-serif; display:block; font-size:14px; margin-bottom:4px; }
.license-status-box.active { color:var(--green); background:rgba(34,197,94,0.08); border-color:rgba(34,197,94,0.3); }
.license-status-box.grace_period { color:var(--amber); background:rgba(251,146,60,0.08); border-color:rgba(251,146,60,0.3); }
.license-status-box.locked { color:var(--red); background:rgba(239,68,68,0.08); border-color:rgba(239,68,68,0.3); }

/* ---------- Tables (mirrors .tb-list-table) ---------- */

.device-table { width:100%; border-collapse:collapse; font-size:13px; }
.device-table th {
  text-align:left; padding:10px 12px; font-size:11px; font-weight:600;
  color:var(--muted); border-bottom:1px solid var(--border);
}
.device-table td { padding:10px 12px; border-bottom:1px solid var(--border); vertical-align:middle; transition:background 0.1s; color:var(--text); }
.device-table tbody tr:nth-child(even) td { background:rgba(255,255,255,0.015); }
.device-table tr:hover td { background:var(--surface2) !important; color:var(--text); }

.status-dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:8px; vertical-align:middle; }
.status-dot.up { background:var(--green); box-shadow:0 0 8px rgba(34,197,94,0.55); }
.status-dot.down { background:var(--red); box-shadow:0 0 8px rgba(239,68,68,0.55); }

.advanced-col { display: none; }
body.show-advanced .advanced-col { display: table-cell; }

.badge {
  display:inline-flex; padding:2px 9px; border-radius:10px; font-size:10px; font-weight:700;
  text-transform:capitalize; background:var(--surface3); color:var(--muted); border:1px solid var(--border);
}
.method-tag { font-size:10px; color:var(--muted); font-weight:600; }

/* ---------- Tickets kanban (mirrors kb-lane / kb-card) ---------- */

.kanban { display:flex; gap:12px; align-items:flex-start; overflow-x:auto; padding-bottom:8px; }
.kanban-col { width:242px; flex-shrink:0; display:flex; flex-direction:column; }

.kanban-col-header {
  padding:9px 12px; border-radius:8px 8px 0 0; display:flex; align-items:center; justify-content:space-between;
  border:1px solid var(--border); border-bottom:none; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.5px;
  background:rgba(var(--surface2-rgb),0.7);
}
.kanban-count { font-size:10px; font-weight:700; padding:1px 8px; border-radius:10px; background:var(--surface3); color:var(--muted); }

.kanban-col > .glass,
.kanban-col-body {
  display:flex; flex-direction:column; gap:8px; min-height:80px; max-height:calc(100vh - 300px);
  overflow-y:auto; padding:8px 4px 16px; border:1px solid var(--border); border-top:none;
  border-radius:0 0 8px 8px; background:rgba(var(--surface-rgb),0.45);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
}

.ticket-card {
  background:rgba(var(--surface2-rgb),0.65);
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  border:1px solid var(--glass-border); border-radius:10px; padding:11px 13px;
  transition:box-shadow 0.2s var(--ease), border-color 0.2s var(--ease), transform 0.2s var(--spring);
}
.ticket-card:hover { border-color:var(--accent); box-shadow:var(--shadow-accent), inset 0 1px 0 var(--glass-shine); transform:translateY(-3px) scale(1.01); }
.ticket-card .t-title { font-size:12px; font-weight:600; color:var(--text); line-height:1.4; }
.ticket-card .t-meta { font-size:10.5px; color:var(--muted); display:flex; justify-content:space-between; margin-top:5px; }

/* ---------- Assets ---------- */

.asset-warranty-warn td { background:rgba(251,146,60,0.08) !important; }
.asset-warranty-warn .warranty-cell { color:var(--amber); font-weight:700; }

/* ---------- Buttons (mirrors .btn / .btn-primary) ---------- */

.btn {
  padding:8px 16px; border-radius:6px; font-family:'Inter',sans-serif; font-size:13px; font-weight:500;
  cursor:pointer; border:none;
  transition:all 0.15s var(--ease);
}
.btn:active { transform:scale(0.98); }
.lock-btn, .btn-primary {
  background:var(--accent); color:#fff;
  padding:8px 20px; border-radius:6px; border:none; font-size:13px; font-weight:500; cursor:pointer;
  transition:background 0.15s var(--ease);
}
.lock-btn:hover, .btn-primary:hover { 
  background:var(--accent-h);
}

/* ---------- Section header row (page title + primary action button,
   e.g. Tickets/Assets "+ Add" buttons) ---------- */

.section-header-row { display:flex; align-items:flex-start; justify-content:space-between; gap:8px; flex-wrap:wrap; }
/* The row itself already wraps (above), but its toolbar child (search box +
   filters + export/add buttons, e.g. the Devices view) is its own flex
   container with a fixed-width search input -- without this it stayed on
   one unbroken line and pushed the whole page wider than the viewport on
   mobile (body's overflow-x:hidden then silently clipped the overflow
   instead of showing it). Letting it wrap too keeps every control reachable. */
.section-header-row > div { flex-wrap:wrap; }

/* ---------- Secondary/primary button variants for .btn ----------
   .btn (base, defined above) has no background of its own so it can be
   reused as either a subtle secondary action (Cancel) or, with
   .btn-primary added, the accent gradient CTA (same look as .lock-btn,
   just not full-width). */

.btn { background:var(--surface2); color:var(--text); border:1px solid var(--border); }
.btn:hover { background:var(--surface3); }

.btn-edit { }
.btn-edit:hover { background:var(--surface3); color:var(--text); }

/* ---------- Generic modal (Add Ticket / Add Asset forms) ----------
   Same glass-card recipe as .lock-card, just sized for a form instead of
   a centered message, plus a form-field layout (mirrors .login-input). */

.modal-overlay {
  position:fixed; inset:0; z-index:9500; display:none; align-items:center; justify-content:center;
  background:rgba(7,10,18,0.6); backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
  padding:20px;
}
.modal-overlay.show { display:flex; animation:overlayIn 0.18s var(--ease); }

/* .login-screen is a fixed full-screen overlay at z-index:9999 (above the
   default .modal-overlay z-index:9500) — any modal that needs to open ON
   TOP of the login screen (e.g. Forgot Password) needs a higher z-index or
   it renders invisible/unclickable behind it. */
#forgotPasswordModal { z-index:10000; }

.modal-card {
  width:100%; max-width:420px;
  background:rgba(var(--surface-rgb),0.85); border:1px solid var(--glass-border); border-radius:14px;
  padding:24px; box-shadow:var(--shadow-lg), inset 0 1px 0 var(--glass-shine);
  backdrop-filter:blur(24px) saturate(1.6); -webkit-backdrop-filter:blur(24px) saturate(1.6);
  animation:modalIn 0.28s var(--spring);
}
.modal-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; }
.modal-header h2 { font-family:'Rajdhani',sans-serif; font-size:19px; font-weight:700; color:var(--text); margin:0; }
.modal-close {
  background:none; border:none; cursor:pointer; color:var(--muted); font-size:22px; line-height:1;
  padding:0; width:28px; height:28px; display:flex; align-items:center; justify-content:center; border-radius:6px;
  transition:background 0.15s var(--ease), color 0.15s var(--ease);
}
.modal-close:hover { background:var(--surface2); color:var(--text); }

.form-label { font-size:11.5px; font-weight:600; color:var(--muted); margin:8px 0 4px; display:block; }
.form-label:first-of-type { margin-top:0; }

/* Two-column field layout for long forms (Add Client modal) — each direct
   child is a label+input pair, so it lays out in two per row instead of one
   long vertical column. .full-width breaks out to span both columns (e.g. a
   section heading). Collapses to one column on narrow screens. */
.form-grid { display:grid; grid-template-columns:1fr 1fr; column-gap:16px; }
.form-grid > .full-width { grid-column:1 / -1; }
@media (max-width:520px) {
  .form-grid { grid-template-columns:1fr; }
}

.modal-actions { display:flex; justify-content:flex-end; gap:8px; margin-top:14px; }

/* ---------- Toast (brief success indication after a create) ---------- */

.toast {
  position:fixed; top:20px; left:50%; z-index:9999;
  transform:translateX(-50%) translateY(-16px);
  background:rgba(34,197,94,0.95); color:#fff; font-size:13px; font-weight:600;
  padding:10px 20px; border-radius:8px; box-shadow:var(--shadow-lg);
  opacity:0; pointer-events:none; transition:opacity 0.25s var(--ease), transform 0.25s var(--ease);
}
.toast.show { opacity:1; transform:translateX(-50%) translateY(0); }

/* ---------- Lock overlay (license-locked full screen, mirrors .modal-overlay) ---------- */

.lock-overlay {
  position:fixed; inset:0; z-index:9000; display:none; align-items:center; justify-content:center;
  background:rgba(7,10,18,0.6); backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
}
.lock-overlay.show { display:flex; animation:overlayIn 0.18s var(--ease); }
.lock-card {
  width:420px; padding:36px 32px; text-align:center; display:flex; flex-direction:column; align-items:center; gap:12px;
  background:rgba(var(--surface-rgb),0.72); border:1px solid var(--glass-border); border-radius:14px;
  box-shadow:var(--shadow-lg), inset 0 1px 0 var(--glass-shine);
  backdrop-filter:blur(24px) saturate(1.6); -webkit-backdrop-filter:blur(24px) saturate(1.6);
  animation:modalIn 0.28s var(--spring);
}
.lock-icon {
  width:54px; height:54px; border-radius:14px;
  background:linear-gradient(135deg,var(--red),var(--accent));
  display:flex; align-items:center; justify-content:center; box-shadow:0 8px 22px rgba(239,68,68,0.4);
}
.lock-icon svg { width:24px; height:24px; }
.lock-card h2 { font-family:'Rajdhani',sans-serif; margin:4px 0 0; font-size:20px; color:var(--text); }
.lock-card p { margin:0; font-size:13px; color:var(--muted); line-height:1.6; }

/* ---------- Login screen (mirrors #loginOverlay / .login-card) ---------- */

.login-screen {
  position:fixed; inset:0; z-index:9999;
  background:linear-gradient(135deg,#06090f 0%,#0b1124 55%,#101a35 100%);
  display:flex; align-items:center; justify-content:center; padding:20px; overflow:hidden;
}
.login-screen::before, .login-screen::after {
  content:''; position:absolute; border-radius:50%; filter:blur(60px); opacity:0.25;
  pointer-events:none; animation:orbFloat 12s ease-in-out infinite;
}
.login-screen::before { width:340px; height:340px; background:var(--accent); top:-80px; left:-60px; }
.login-screen::after  { width:300px; height:300px; background:var(--blue); bottom:-80px; right:-40px; animation-delay:-6s; }

.login-card {
  position:relative; z-index:1;
  background:rgba(var(--surface-rgb),0.6); border:1px solid var(--glass-border); border-radius:18px;
  padding:40px; width:100%; max-width:420px;
  box-shadow:0 24px 64px rgba(0,0,0,0.6), inset 0 1px 0 var(--glass-shine);
  backdrop-filter:blur(28px) saturate(1.6); -webkit-backdrop-filter:blur(28px) saturate(1.6);
  animation:modalIn 0.45s var(--spring);
  display:flex; flex-direction:column; gap:6px;
}

.login-input {
  width:100%; padding:8px 11px; border:1.5px solid var(--border); border-radius:7px;
  font-family:'Inter','DM Sans',sans-serif; font-size:13px; outline:none;
  background:var(--surface2); color:var(--text);
  transition:border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
  margin-bottom:8px;
}
.login-input::placeholder { color:var(--muted); }
.login-input:hover { border-color:var(--muted); }
.login-input:focus { border-color:var(--accent); box-shadow:0 0 0 3px rgba(var(--accent-rgb),0.15); }

.login-error {
  background:rgba(220,38,38,0.1); border:1px solid rgba(220,38,38,0.4); color:#f87171;
  border-radius:6px; padding:9px 13px; font-size:12.5px; margin:0; min-height:0;
}
.login-error:empty { display:none; }

/* ---------- Password requirements checklist (Session 19) ----------
   Sits under a new-password field; each <li data-rule="..."> turns green
   with a check as the corresponding rule is met (see passwordRules.js). */
.pw-checklist { list-style:none; margin:2px 0 10px; padding:0; display:flex; flex-direction:column; gap:3px; }
.pw-checklist li {
  font-size:11.5px; color:var(--muted); padding-left:18px; position:relative;
  transition:color 0.15s var(--ease);
}
.pw-checklist li::before {
  content:'○'; position:absolute; left:0; top:0; font-size:9px; color:var(--muted);
}
.pw-checklist li.met { color:var(--green); }
.pw-checklist li.met::before { content:'✓'; font-size:12px; top:-1px; color:var(--green); font-weight:700; }

/* ---------- Clients tree (admin onboarding list) ---------- */

.client-grid-row {
  display:grid;
  grid-template-columns: minmax(160px, 1.4fr) minmax(180px, 1.2fr) 140px 130px 130px;
  align-items:center;
  gap:16px;
}
.client-grid-header {
  padding:0 16px 10px;
  border-bottom:1px solid var(--border);
  font-size:11px; font-weight:700; letter-spacing:0.03em; text-transform:uppercase; color:var(--muted);
}
.client-grid-header .client-col-count,
.client-grid-header .client-col-status,
.client-grid-header .client-col-expiry { text-align:right; }
.client-tree-row { padding:14px 16px; border-bottom:1px solid var(--border); cursor:pointer; transition:background 0.15s; }
.client-tree-row:last-child { border-bottom:none; }
.client-tree-row:hover { background:rgba(var(--surface2-rgb), 0.5); }
.client-col-name { display:flex; align-items:center; gap:8px; font-weight:600; font-size:15px; min-width:0; flex-wrap:wrap; }
.client-col-name .client-name-text { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; }
.client-col-tiers { display:flex; gap:6px; flex-wrap:wrap; }
.client-col-count { font-size:12px; color:var(--muted); text-align:right; white-space:nowrap; }
.client-col-status { font-size:12px; font-weight:600; text-transform:capitalize; text-align:right; white-space:nowrap; }
.client-col-expiry { font-size:12px; color:var(--muted); text-align:right; white-space:nowrap; }

@media (max-width:860px) {
  .client-grid-row { grid-template-columns: 1fr; gap:6px; }
  .client-grid-header { display:none; }
  .client-col-count, .client-col-status, .client-col-expiry { text-align:left; }
}

/* ---------- Responsive ---------- */

@media (max-width:1180px) {
  .content-grid { grid-template-columns:1fr; }
}
  @media (max-width:860px) {
    .app-shell { grid-template-columns:1fr; }
    /* .app-main-col is hardcoded to grid-column:2 for the desktop 2-column
       layout, but the line above only declares ONE explicit track here --
       grid auto-generates an implicit column 2 sized to fit the content's
       max-content width instead of the viewport, and that width then
       cascades down through topbar/main/everything inside it. Confirmed via
       a real headless-browser check (topbar/main both rendered ~485px wide
       on a 390px viewport, well past what the wrap fixes above alone
       explained). The sidebar is position:fixed at this breakpoint (out of
       grid flow), so the single 1fr track is exactly what this column
       should occupy. */
    .app-main-col { grid-column:1; }
    .mobile-nav-toggle { display:flex; }

    /* Off-canvas drawer instead of the desktop hover-expand rail — a tap
       can't hover, so labels must be visible unconditionally and the open
       state is driven by the .mobile-open class (dashboard.js), not :hover. */
    .sidebar {
      position:fixed; top:0; left:0; height:100vh; width:248px;
      transform:translateX(-100%);
      transition:transform 0.3s var(--spring-soft);
      border-right:1px solid var(--glass-border); border-bottom:none;
    }
    .sidebar.mobile-open { transform:translateX(0); }
    .sidebar:hover { width:248px; }
    .sidebar .nav-item span, .sidebar .brand-text span, .sidebar .nav-label {
      opacity:1 !important; pointer-events:auto !important;
    }
    .sidebar-overlay { display:block; }

    /* Topbar's right-hand icon cluster (theme/density/notif toggles +
       license pill + avatar) plus the new hamburger was wider than a phone
       viewport -- confirmed via a real headless-browser check, body's
       scrollWidth exceeded the viewport and "License Active" was visibly
       cut off at the right edge. Tighten spacing and drop the license
       pill's text label (the dot + color already conveys status). */
    .topbar { padding:0 12px; }
    .topbar-right { gap:6px; }
    #topbarLicenseText { display:none; }
    .license-pill { padding:5px 8px; }

    .kpi-grid { grid-template-columns:repeat(2,1fr); }
    .login-card { padding:28px 20px; }

    /* Devices table -> stacked cards on mobile instead of a horizontally-
       scrolling table. Was fine at 5 columns, but ticking "Advanced
       Features" adds 5 more (CPU/RAM/Disk/Traffic/Latency = 11 total) and
       the horizontal-scroll fallback just turned into a cramped desktop-
       style table you had to drag sideways through -- "goes pc view", per
       report. <thead> is hidden and each <td> supplies its own label via
       data-label (see deviceRow() in dashboard.js) instead. */
    #deviceTableFull thead { display:none; }
    #deviceTableFull, #deviceTableFull tbody, #deviceTableFull tr, #deviceTableFull td {
      display:block; width:100%;
    }
    #deviceTableFull tr {
      border:1px solid var(--border); border-radius:10px;
      margin-bottom:10px; padding:4px 12px; background:var(--surface2);
    }
    #deviceTableFull td {
      display:flex; align-items:center; justify-content:space-between; gap:12px;
      padding:8px 0; border-bottom:1px solid var(--border); text-align:right;
    }
    #deviceTableFull td:last-child { border-bottom:none; }
    #deviceTableFull td::before {
      content:attr(data-label); flex-shrink:0; text-align:left;
      font-size:11px; font-weight:700; color:var(--muted);
      text-transform:uppercase; letter-spacing:0.5px;
    }
    /* advanced-col's own display:table-cell (base rule) no longer applies
       once the cell is display:flex above -- redeclare show/hide here at
       matching specificity so the Advanced Features toggle still works. */
    #deviceTableFull td.advanced-col { display:none; }
    body.show-advanced #deviceTableFull td.advanced-col { display:flex; }

    button.pwa-install-btn.installable { display:flex; }
  }

/* ---------- Glass fallback (no backdrop-filter support) ---------- */

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar, .sidebar, .panel, .kpi-card, .ticket-card, .kanban-col-body, .login-card, .lock-card {
    background:var(--surface) !important;
  }
}

/* ---------- SaaS Ticketing & Data Grid UI ---------- */
.search-box {
  position:relative;
  display:flex; align-items:center;
}
.search-box svg {
  position:absolute; left:12px;
  width:16px; height:16px; color:var(--muted);
}
.search-box input {
  background:rgba(var(--surface2-rgb), 0.5);
  border:1px solid var(--border);
  color:var(--text); padding:8px 12px 8px 36px;
  border-radius:8px; font-size:13px; width:220px;
  transition:border-color 0.2s, box-shadow 0.2s;
}
.search-box input:focus {
  border-color:var(--accent); box-shadow:0 0 0 3px rgba(var(--accent-rgb), 0.2); outline:none;
}

.saas-table {
  width:100%; border-collapse:collapse;
  font-size:13.5px;
}
.saas-table th {
  text-align:left; padding:12px 16px;
  color:var(--muted); font-weight:600;
  border-bottom:2px solid var(--border);
  user-select:none; cursor:pointer;
  transition:color 0.2s, background 0.2s;
}
.saas-table th:hover { color:var(--text); background:var(--surface2); }
.saas-table td {
  padding:14px 16px; border-bottom:1px solid var(--border);
  color:var(--text); vertical-align:middle;
}
.saas-table tr:hover td { background:rgba(var(--surface2-rgb), 0.4); }

.sort-icon { display:inline-block; margin-left:4px; font-size:11px; opacity:0.5; }
.sort-icon.asc::after { content:"\25B2"; opacity:1; }
.sort-icon.desc::after { content:"\25BC"; opacity:1; }

.status-pill {
  padding:4px 10px; border-radius:12px; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.5px;
}
.status-pill.open { background:rgba(var(--accent-rgb),0.15); color:var(--accent); }
.status-pill.in_progress { background:rgba(245,158,11,0.15); color:#f59e0b; }
.status-pill.resolved { background:rgba(16,185,129,0.15); color:#10b981; }
.status-pill.closed { background:rgba(148,163,184,0.15); color:#94a3b8; }

.priority-badge {
  padding:4px 8px; border-radius:6px; font-size:11px; font-weight:700;
  border:1px solid transparent;
}
.priority-badge.critical { background:rgba(239,68,68,0.1); color:#ef4444; border-color:rgba(239,68,68,0.2); }
.priority-badge.high { background:rgba(249,115,22,0.1); color:#f97316; border-color:rgba(249,115,22,0.2); }
.priority-badge.medium { background:rgba(59,130,246,0.1); color:#3b82f6; border-color:rgba(59,130,246,0.2); }
.priority-badge.low { background:rgba(148,163,184,0.1); color:#94a3b8; border-color:rgba(148,163,184,0.2); }

.avatar-sm {
  width:28px; height:28px; border-radius:50%; background:var(--surface3);
  display:inline-flex; align-items:center; justify-content:center;
  font-size:12px; font-weight:700; color:var(--text); border:1px solid var(--glass-border);
}

/* Slide Panel */
.slide-panel-overlay {
  position:fixed; top:0; left:0; right:0; bottom:0;
  background:rgba(0,0,0,0.5); backdrop-filter:blur(4px);
  opacity:0; visibility:hidden; z-index:2000;
  transition:opacity 0.3s var(--ease);
}
.slide-panel-overlay.active { opacity:1; visibility:visible; }

.slide-panel {
  position:fixed; top:0; right:-500px; width:100%; max-width:480px; bottom:0;
  background:var(--surface); box-shadow:-8px 0 32px rgba(0,0,0,0.5);
  z-index:2001; transition:right 0.3s var(--spring-soft);
  display:flex; flex-direction:column; border-left:1px solid var(--border);
}
.slide-panel.active { right:0; }
.slide-panel-header {
  padding:20px 24px; border-bottom:1px solid var(--border);
  display:flex; align-items:center; justify-content:space-between;
  background:rgba(var(--surface2-rgb), 0.5);
}
.slide-panel-body { padding:24px; overflow-y:auto; flex:1; display:flex; flex-direction:column; gap:20px; }
.ticket-detail-group { display:flex; flex-direction:column; gap:8px; }
.ticket-detail-label { font-size:12px; color:var(--muted); font-weight:600; text-transform:uppercase; letter-spacing:0.5px; }
.ticket-detail-value { font-size:14px; color:var(--text); }
.ticket-desc-box { background:var(--surface2); padding:16px; border-radius:8px; border:1px solid var(--border); font-size:14px; line-height:1.5; white-space:pre-wrap; }

/* ---------- Network Map (vis-network hierarchical topology diagram) ---------- */

#networkMapPanel { gap:12px; min-height:0; }
#networkMapContainer {
  width:100%; height:calc(100vh - 320px); min-height:420px;
  border:1px solid var(--border); border-radius:8px;
  background:var(--surface2);
}
.network-map-legend {
  display:flex; flex-wrap:wrap; gap:6px 16px;
  font-size:11.5px; color:var(--muted); padding-bottom:4px; border-bottom:1px solid var(--border);
}
.network-map-legend-item { display:inline-flex; align-items:center; gap:5px; white-space:nowrap; }
.network-map-legend-line { display:inline-block; width:20px; height:0; border-top:2px solid var(--muted); }
.network-map-legend-line.dashed { border-top-style:dashed; }

