  /* AdminEZ — design system tokens + base styles */

:root {
  /* type */
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* density */
  --gap: 16px;
  --gap-sm: 12px;
  --gap-lg: 24px;
  --pad: 16px;
  --pad-sm: 12px;
  --row: 36px;
  --topbar: 56px;
  --sidebar: 280px;
  --radius: 3px;
  --radius-sm: 3px;
  --radius-pill: 999px;

  /* slate scale */
  --slate-50:  #f8fafc;
  --slate-100: #f1f5f9;
  --slate-150: #ebeff4;
  --slate-200: #e2e8f0;
  --slate-300: #cbd5e1;
  --slate-400: #94a3b8;
  --slate-500: #64748b;
  --slate-600: #475569;
  --slate-700: #334155;
  --slate-800: #1e293b;
  --slate-900: #0f172a;
  --slate-950: #020617;

  /* cobalt — primary accent */
  --cobalt:        #1e40af;
  --cobalt-light:  #3b6fe0;
  --cobalt-lighter:#7ea3ec;
  --cobalt-deep:   #1e3a8a;
  --cobalt-darker: #172554;
  --cobalt-bg:     #eaf0fb;
  --cobalt-bg2:    #dbe5f8;

  /* status */
  --ok:    #047857;
  --ok-bg: #d8f1e6;
  --warn:  #d97706;
  --warn-bg: #fff4e6;
  --crit:  #b91c1c;
  --crit-bg: #fbdcdc;
  --mute:  #64748b;
  --mute-bg: #e9eef4;

  --violet: #6d28d9;

  /* surfaces */
  --bg:        #fcfdfe;       /* content area — barely off-white */
  --bg-deep:   #f4f6f9;
  --surface:   #ffffff;       /* cards */
  --surface-2: #f8fafc;
  --chrome:    #f7f9fc;       /* topbar + sidebar */
  --chrome-2:  #eef2f7;
  --ink-900:   var(--slate-900);
  --text:      var(--slate-800);
  --text-secondary: var(--slate-600);
  --text-muted: var(--slate-500);
  --border:    #e3e8ef;
  --gridline:  rgba(15, 23, 42, 0.07);

  /* shadows */
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04);
  --shadow:    0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.06);
  --shadow-lg: 0 8px 24px rgba(15, 23, 42, 0.10);
}

[data-density="compact"] {
  --gap: 10px;
  --gap-sm: 8px;
  --gap-lg: 14px;
  --pad: 10px;
  --pad-sm: 8px;
  --row: 30px;
  --topbar: 48px;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; padding: 0; }
body {
  font-family: var(--font);
  font-size: 13px;
  font-feature-settings: 'cv11', 'ss01';
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
.mono { font-family: var(--font-mono); font-feature-settings: 'zero', 'ss02'; }
.muted { color: var(--text-muted); }
.warn  { color: var(--warn); }
.crit  { color: var(--crit); }
.xs    { font-size: 11px; }

button, input { font-family: inherit; font-size: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; padding: 0; }
a { color: inherit; text-decoration: none; }
a:hover { text-decoration: none; }
/* prose links — opt-in underline */
.lnk, .auth-foot a { color: var(--cobalt); }
.lnk:hover, .auth-foot a:hover { text-decoration: underline; }

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 currentColor; }
  70%  { box-shadow: 0 0 0 6px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@keyframes spin { from { transform: rotate(0); } to { transform: rotate(360deg); } }

/* ───────── app shell ───────── */
.app {
  display: grid;
  grid-template-rows: var(--topbar) 1fr;
  height: 100vh;
  background: var(--bg);
}
.app-main {
  display: grid;
  grid-template-columns: auto 1fr;
  min-height: 0;
}
.app-main.no-sidebar { grid-template-columns: 1fr; }
.app-content {
  overflow: auto;
  min-width: 0;
  background: var(--bg);
}
.app-content-inner { padding: var(--gap-lg); display: flex; flex-direction: column; gap: var(--gap); }

/* ───────── topbar ───────── */
.topbar {
  display: grid;
  /* Column 1 sizes to fit brand + nav (now sidebar-width wide + ~340px
     of menu items) — `auto` lets the content claim what it needs.
     Middle column = search, capped at 320. Right = user menu, takes
     whatever's left. */
  grid-template-columns: auto minmax(220px, 320px) 1fr;
  align-items: center;
  background: var(--chrome);
  border-bottom: 1px solid var(--border);
  /* Left padding tuned so the brand's logo lands at the same x as the
     sidebar's "Hosts" header text (sidebar-h padding-left is 12px). */
  padding: 0 16px 0 12px;
  gap: 16px;
  height: var(--topbar);
  position: relative;
  z-index: 5;
}

/* Turbo / form-submit loader bar pinned to the topbar's bottom border.
   Fills L→R like a progress bar — quickly to ~30% (the request is on the wire),
   then asymptotically toward 90% (we don't know the actual remaining time).
   On completion the .done state snaps to 100% briefly, then fades out. */
.turbo-bar {
  /* Pinned to the TOP of the topbar (not the bottom) per user feedback —
     fills L→R as a progress hint above the brand/nav so the eye catches
     it during navigation without competing with the topbar's own bottom
     border. */
  position: absolute;
  left: 0; right: 0; top: 0; height: 2px;
  background: transparent;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  transition: opacity .2s;
  z-index: 6;
}
.turbo-bar::before {
  content: '';
  position: absolute; top: 0; left: 0; height: 100%;
  background: var(--cobalt);
  width: 0;
  transition: width .2s ease-out;
}
.turbo-bar.busy {
  opacity: 1;
}
.turbo-bar.busy::before {
  animation: turbo-fill 8s cubic-bezier(.05, .55, .35, 1) forwards;
}
.turbo-bar.done {
  opacity: 1;
}
.turbo-bar.done::before {
  width: 100% !important;
  animation: none;
  transition: width .15s ease-out;
}
@keyframes turbo-fill {
  0%   { width: 0%;  }
  20%  { width: 35%; }
  50%  { width: 65%; }
  100% { width: 90%; }
}
/* Two-column grid so the brand block matches the sidebar's width exactly —
   that lines the mainnav's border-left up flush with the sidebar's right
   edge as one continuous vertical rule. Column 1 = sidebar width minus
   the topbar's own left padding (12px to match sidebar-h padding-left
   so the brand logo lands flush with the sidebar's Hosts text).
   Mobile (≤768px) hides sidebar AND mainnav so this only kicks in on
   desktop. */
.topbar-left   {
  display: grid;
  grid-template-columns: calc(var(--sidebar) - 12px) 1fr;
  align-items: center;
  min-width: 0;
}
.topbar-mid    { display: flex; align-items: center; justify-content: center; }
.topbar-right  { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
.topbar-sep    { color: var(--slate-300); font-weight: 300; }
.brand {
  display: flex; align-items: center; gap: 8px;
  font-weight: 600; letter-spacing: -0.01em;
  font-size: 14px;
  color: var(--ink-900);
  /* No left padding — brand logo aligns flush with the sidebar's
     content edge (combined with the topbar's left padding of 12). */
  padding: 4px 8px 4px 0; border-radius: var(--radius-sm);
  align-self: center;
}
.brand:hover { background: rgba(15, 23, 42, 0.04); }
.brand-mark { display: inline-flex; }

.mainnav {
  display: flex; align-items: center; gap: 2px;
  /* The topbar-left grid puts mainnav at column 2's left edge — which
     equals the sidebar's right edge (the same x). With box-sizing:
     border-box, the sidebar's border-right paints in pixel column 279
     while a border-left here would paint in column 280 — adjacent
     columns producing a doubled visual line. Pull the mainnav 1px to
     the left so its border-left lands in column 279 too, overlapping
     the sidebar's border-right exactly. The 12px padding-left then
     puts the first nav item ~12px to the right of where it was. */
  margin-left: -1px;
  padding-left: 12px;
  border-left: 1px solid var(--border);
  /* 37px tall (was 32; +5 to give the menu items more vertical room
     per user request), still centered in the 56px topbar — short
     inline divider, NOT a full-bleed stroke. The sidebar's right
     border continues the line below the topbar; the two are visually
     aligned at the same x and read as one separator system with a
     small gap at the topbar's bottom edge. */
  height: 37px;
}
.mainnav-i {
  display: inline-flex; align-items: center; gap: 6px;
  /* +5px overall height (was 6px top/bottom = 12px vertical pad; now
     ~8.5px each side = 17px vertical pad). Uses the spare space in
     the 56px topbar without growing the bar itself. */
  padding: 8.5px 10px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  border-radius: 3px;
  position: relative;
}
.mainnav-i:hover { color: var(--ink-900); background: rgba(15, 23, 42, 0.04); }
.mainnav-i.active {
  color: var(--cobalt);
  font-weight: 600;
}
.mainnav-i.active::after {
  content: '';
  position: absolute;
  left: 10px; right: 10px;
  bottom: -2px;
  height: 2px;
  background: var(--cobalt);
  border-radius: 2px 2px 0 0;
}
.topbar-crumb { display: flex; align-items: center; gap: 4px; margin-left: 6px; min-width: 0; overflow: hidden; }
.topbar-crumb .crumb { padding: 4px 6px; font-size: 12.5px; }
.topbar-crumb .mono { color: var(--ink-900); font-weight: 600; }
.mainnav-badge {
  font-family: var(--font-mono);
  font-size: 10px;
  background: var(--crit);
  color: white;
  font-weight: 600;
  padding: 0 5px;
  min-width: 16px; height: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-pill);
  line-height: 1;
}
.crumb { font-weight: 500; color: var(--text-secondary); padding: 4px 8px; border-radius: var(--radius-sm); display: inline-flex; align-items: center; gap: 6px; }
.crumb:hover { background: var(--slate-100); }
.crumb.current { color: var(--ink-900); font-weight: 600; }

.iconbtn {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  position: relative;
}
.iconbtn:hover { background: var(--slate-100); color: var(--ink-900); }
.iconbtn:disabled,
.iconbtn[disabled] {
  /* Show clearly-not-clickable state so disabled action buttons read as
     deliberately gated rather than broken. cursor:not-allowed makes the
     intent obvious on hover. */
  opacity: 0.4;
  cursor: not-allowed;
}
.iconbtn:disabled:hover,
.iconbtn[disabled]:hover { background: transparent; color: var(--text-secondary); }
.iconbtn.sm { width: 24px; height: 24px; }
.relative { position: relative; }
.badge {
  position: absolute; top: 3px; right: 3px;
  background: var(--crit); color: white;
  font-size: 9px; font-weight: 600;
  min-width: 14px; height: 14px; padding: 0 3px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-pill);
  border: 1.5px solid var(--surface);
}

.avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--cobalt);
  color: white;
  font-weight: 600; font-size: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
}

.searchbox {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  width: 100%;
  transition: border-color .12s, background .12s, box-shadow .12s;
}
.searchbox:focus-within {
  background: var(--surface);
  border-color: var(--cobalt-light);
  box-shadow: 0 0 0 3px var(--cobalt-bg);
}
.searchbox.sm { padding: 4px 8px; }
.searchbox-i { display: inline-flex; color: var(--text-muted); }
.searchbox input {
  flex: 1; border: 0; outline: 0; background: transparent;
  font-size: 12.5px;
}
.searchbox input::placeholder { color: var(--text-muted); }
.kbd {
  font-family: var(--font-mono);
  font-size: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 3px;
  padding: 1px 5px;
  color: var(--text-muted);
}

.rangepick {
  display: inline-flex; padding: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  gap: 1px;
}
.rangepick-b {
  font-family: var(--font-mono); font-size: 11px;
  padding: 4px 8px; border-radius: 3px;
  color: var(--text-muted); font-weight: 500;
}
.rangepick-b:hover { color: var(--ink-900); }
.rangepick-b.active { background: var(--chrome); color: var(--cobalt); }
.rangepick.sm .rangepick-b { padding: 3px 6px; font-size: 10px; }

/* Alerts page filter bar — host + severity selects, date pickers,
   Apply / Clear buttons. Sits above the rangepick chip row. */
.alert-filters {
  display: flex; flex-wrap: wrap; align-items: end;
  gap: 10px;
  margin-bottom: var(--gap);
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
/* Fixed width (not min-width) so the control doesn't reflow as the
   selected option's label changes length. */
.alert-filter { display: flex; flex-direction: column; gap: 4px; width: 140px; }
/* Host filter takes ~2x the regular width so long display names
   ("Postgres primary (EU)", "Aylin · MBP M3") fit without truncation. */
.alert-filter-host { width: 280px; }
.alert-filter-l {
  font-size: 10.5px; font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.alert-filter select,
.alert-filter input[type="date"] {
  height: 32px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink-900);
  font-size: 12.5px;
  font-family: inherit;
}
.alert-filter select:focus,
.alert-filter input[type="date"]:focus {
  outline: 0;
  border-color: var(--cobalt);
  box-shadow: 0 0 0 3px var(--cobalt-bg);
}

/* The range picker takes a bit more horizontal real estate than the
   single-day inputs it replaced — give it room to breathe. The wrap
   is a relative container so the calendar icon can sit absolutely
   inside the input's right edge without affecting layout flow. */
.alert-filter-range { min-width: 220px; }
.alert-filter-range-wrap { position: relative; display: block; }
.alert-filter-range input[type="text"] {
  height: 32px;
  /* Right padding bumped to make space for the icon — keeps text
     from sliding underneath it. */
  padding: 0 32px 0 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink-900);
  font-size: 12.5px;
  font-family: inherit;
  cursor: pointer;
  width: 100%;
}
.alert-filter-range input[type="text"]:focus {
  outline: 0;
  border-color: var(--cobalt);
  box-shadow: 0 0 0 3px var(--cobalt-bg);
}
.alert-filter-range-icon {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex; align-items: center;
  color: var(--text-muted);
  pointer-events: none;
}

/* ───────── Tom Select — AdminEZ skin ─────────
   Tom Select ships a generic theme that doesn't match our slate/cobalt
   palette. Override only what's needed; everything else stays from the
   vendor CSS so future minor upgrades don't break us. */
/* Make the wrapper fill the .alert-filter column so the control width
   tracks the parent (fixed 140 or 280px) instead of growing/shrinking
   with the selected option's label length. */
.alert-filter .ts-wrapper { width: 100% !important; }
.adminez-ts-control,
.alert-filter .ts-control {
  min-height: 32px !important;
  padding: 0 28px 0 8px !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-sm) !important;
  background: var(--surface) !important;
  font-size: 12.5px !important;
  font-family: inherit !important;
  color: var(--ink-900) !important;
  box-shadow: none !important;
  /* Lay the selected-item label + the typed-search input out side-by-
     side in a single flex line. Tom Select's default flow positions
     them inline, but with our shrunk control height the inputs were
     overlapping the label. Centering the row and clipping overflow
     keeps things visually clean. */
  display: flex !important;
  align-items: center !important;
  flex-wrap: nowrap !important;
  overflow: hidden !important;
}
.alert-filter .ts-control > .item {
  /* Don't let a long host name push the typed input out of view —
     allow ellipsis on the rendered label. */
  display: inline-flex; align-items: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 100%;
}
.alert-filter .ts-control > input {
  font-size: 12.5px !important;
  margin: 0 !important;
  padding: 0 !important;
  color: var(--ink-900) !important;
  /* When user is typing a search query, hide the previously-selected
     label so the typed text doesn't overlap on top of it. Tom Select
     adds `.has-items` on the wrapper when a value is set; combined
     with `.dropdown-active` (open), we know we're actively searching. */
}
.alert-filter .ts-wrapper.dropdown-active.has-items .ts-control > .item {
  display: none !important;
}
.alert-filter .ts-wrapper.focus .ts-control,
.alert-filter .ts-wrapper.single.focus .ts-control {
  border-color: var(--cobalt) !important;
  box-shadow: 0 0 0 3px var(--cobalt-bg) !important;
}
.alert-filter .ts-wrapper.single .ts-control:after {
  /* Chevron — replace Tom Select's default arrow with our own slate one
     using a CSS triangle so we don't depend on the vendor sprite. */
  content: '';
  position: absolute;
  right: 10px;
  top: 50%;
  width: 0; height: 0;
  border: 4px solid transparent;
  border-top-color: var(--text-muted);
  transform: translateY(-2px);
  pointer-events: none;
}
.alert-filter .ts-wrapper.single.dropdown-active .ts-control:after {
  transform: translateY(-6px) rotate(180deg);
}

.adminez-ts-dropdown,
.alert-filter .ts-dropdown {
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-sm) !important;
  background: var(--surface) !important;
  box-shadow: 0 6px 24px rgba(15, 23, 42, 0.10) !important;
  margin-top: 4px !important;
  font-size: 12.5px !important;
  color: var(--ink-900) !important;
}
/* Breathing room at the top + bottom of the option list — without this
   the first option's hover ring touched the dropdown's top edge. */
.alert-filter .ts-dropdown .ts-dropdown-content {
  padding: 6px 0 !important;
}
.alert-filter .ts-dropdown .option {
  padding: 6px 10px !important;
  color: var(--ink-900) !important;
  border-radius: 3px !important;
  margin: 1px 4px !important;
}
.alert-filter .ts-dropdown .option.active,
.alert-filter .ts-dropdown .option:hover {
  background: var(--cobalt-bg) !important;
  color: var(--cobalt) !important;
}
.alert-filter .ts-dropdown .option.selected {
  background: var(--slate-100) !important;
}
.alert-filter .ts-dropdown .no-results {
  padding: 8px 10px !important;
  color: var(--text-muted) !important;
}

/* ───────── Flatpickr — AdminEZ skin ─────────
   Same approach: keep the vendor layout, replace colors + radii so the
   calendar looks like it belongs in the dashboard. */
.flatpickr-calendar {
  background: var(--surface) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.12) !important;
  font-family: inherit !important;
  /* 12px breathing room on all four sides so the day grid + header
     don't sit flush against the border. */
  padding: 12px !important;
  /* Default Flatpickr width assumes its content is flush — add the
     left+right padding to keep the week row from clipping. */
  width: 332px !important;
}
.flatpickr-calendar.arrowTop:before,
.flatpickr-calendar.arrowTop:after { display: none !important; }
.flatpickr-months,
.flatpickr-weekdays,
.flatpickr-month { background: var(--surface) !important; color: var(--ink-900) !important; }
.flatpickr-current-month,
.flatpickr-monthDropdown-months,
.flatpickr-current-month .cur-year { color: var(--ink-900) !important; font-weight: 600 !important; font-size: 13px !important; }
.flatpickr-weekday { color: var(--text-muted) !important; font-weight: 600 !important; font-size: 10.5px !important; text-transform: uppercase; letter-spacing: 0.06em; }
.flatpickr-day { color: var(--ink-900) !important; border-radius: var(--radius-sm) !important; font-size: 12.5px !important; }
.flatpickr-day.today { border-color: var(--cobalt) !important; color: var(--cobalt) !important; }
.flatpickr-day:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover { background: var(--slate-100) !important; }
/* Range fills the in-between days with a tinted band; ends are solid. */
.flatpickr-day.inRange,
.flatpickr-day.inRange:hover {
  background: var(--cobalt-bg) !important;
  color: var(--cobalt) !important;
  border-color: var(--cobalt-bg) !important;
  box-shadow: -5px 0 0 var(--cobalt-bg), 5px 0 0 var(--cobalt-bg) !important;
}
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected,
.flatpickr-day.startRange.endRange {
  background: var(--cobalt) !important;
  color: #fff !important;
  border-color: var(--cobalt) !important;
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay { color: var(--text-muted) !important; }
.flatpickr-prev-month,
.flatpickr-next-month { fill: var(--text-muted) !important; }
.flatpickr-prev-month:hover svg,
.flatpickr-next-month:hover svg { fill: var(--cobalt) !important; }

/* Stack month above year on two parallel rows, each shaped exactly the
   same: [<] label [>]. Flatpickr's default has the prev/next-month
   arrows flanking the WHOLE strip (single row); JS moves those onto
   the month row only and inserts a matching year row underneath. */
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-months {
  height: auto !important;
  /* Outer .flatpickr-calendar now owns the 12px outer padding; only
     give the months section a small bottom margin so it doesn't
     touch the day grid. */
  padding: 0 0 6px !important;
}
/* Flatpickr's default .flatpickr-month is fixed 34px tall with
   `overflow: hidden` — that clips our second (year) row out of view.
   Override to let it expand vertically. */
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-month {
  height: auto !important;
  overflow: visible !important;
}
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-current-month {
  display: flex !important;
  flex-direction: column !important;
  gap: 4px;
  padding: 0 !important;
  position: static !important;
  height: auto !important;
  font-size: 13px !important;
  width: 100% !important;
}
/* The two rows share their geometry exactly — month and year sit on
   identical-height bars with arrows on both sides. */
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-monthrow,
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-yearrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 28px;
}
/* Flatpickr's prev/next-month spans were absolutely positioned — reset
   to static layout so they sit inline on the month row instead of
   flanking the outer strip. Same for our custom year arrows. */
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-prev-month,
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-next-month,
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-arrow {
  position: static !important;
  width: 24px !important;
  height: 24px !important;
  padding: 0 !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--text-muted) !important;
  border-radius: var(--radius-sm);
  fill: currentColor !important;
}
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-prev-month:hover,
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-next-month:hover,
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-arrow:hover {
  background: var(--slate-100);
  color: var(--cobalt) !important;
}
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-prev-month svg,
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-next-month svg {
  width: 14px !important; height: 14px !important;
  fill: currentColor !important;
}
/* Month picker — the native <select> is taken out of layout (kept in
   the DOM so Flatpickr's internal value writes still target it) and
   we render a custom button + popover instead. That way the chrome
   stays inside AdminEZ's palette, no OS dropdown peeking out. */
.flatpickr-calendar.adminez-fp-twomonth .flatpickr-monthDropdown-months.adminez-fp-monthsel-hidden {
  position: absolute !important;
  width: 1px !important; height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0 0 0 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
  pointer-events: none !important;
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-btn {
  font-family: inherit;
  font-weight: 600;
  color: var(--ink-900);
  font-size: 13px;
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  min-width: 60px;
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-btn:hover {
  background: var(--slate-100);
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-pop {
  position: absolute;
  top: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 24px rgba(15, 23, 42, 0.10);
  display: none;
  grid-template-columns: 1fr 1fr;
  gap: 2px;
  padding: 6px;
  z-index: 1000;
  min-width: 180px;
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-pop.open { display: grid; }
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-pop-i {
  font: 500 12.5px/1.2 inherit;
  background: transparent;
  border: 0;
  color: var(--ink-900);
  cursor: pointer;
  padding: 6px 10px;
  border-radius: 3px;
  text-align: left;
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-pop-i:hover {
  background: var(--cobalt-bg);
  color: var(--cobalt);
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-month-pop-i.active {
  background: var(--cobalt-bg);
  color: var(--cobalt);
  font-weight: 600;
}
/* Year input — same height + typography as month so the rows mirror.
   We hide the default up/down spinners since the row has its own
   left/right arrows that do the same job. */
.flatpickr-calendar.adminez-fp-twomonth .numInputWrapper {
  width: auto !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  position: relative;
}
.flatpickr-calendar.adminez-fp-twomonth .numInputWrapper span.arrowUp,
.flatpickr-calendar.adminez-fp-twomonth .numInputWrapper span.arrowDown {
  display: none !important;
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-yearrow .cur-year {
  font-weight: 600 !important;
  color: var(--ink-900) !important;
  font-size: 13px !important;
  text-align: center !important;
  /* <input type="number"> ignores width:auto (uses a default browser
     size — ~20 chars). Force it to a small fixed pixel box so the
     year hugs its arrows instead of stretching across the calendar.
     ~52px fits "2026" comfortably + a small horizontal pad. */
  width: 52px !important;
  min-width: 0 !important;
  padding: 2px 4px !important;
  border-radius: var(--radius-sm);
  background: transparent !important;
}
.flatpickr-calendar.adminez-fp-twomonth .adminez-fp-yearrow .cur-year:hover {
  background: var(--slate-100);
}

/* Quick-pick footer: Today / 3 days / 7 days / 2 weeks. Lives below
   the day grid; clicking sets the range + closes the calendar. The
   outer .flatpickr-calendar owns the 12px outer padding, so the
   border-top here extends edge-to-edge via negative horizontal
   margin while the buttons stay inside the padded column. */
.adminez-fp-presets {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
  margin: 8px -12px -12px;
  padding: 8px 12px 12px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  border-bottom-left-radius: var(--radius);
  border-bottom-right-radius: var(--radius);
}
.adminez-fp-preset {
  height: 28px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink-900);
  font-size: 11.5px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
}
.adminez-fp-preset:hover {
  background: var(--slate-100);
  border-color: var(--slate-300);
}
.adminez-fp-preset.is-default {
  /* Today is the suggested default — tinted background hints to the
     operator that this is the most useful starting filter. */
  background: var(--cobalt-bg);
  border-color: var(--cobalt-lighter);
  color: var(--cobalt);
}
.adminez-fp-preset.is-default:hover {
  background: var(--cobalt-bg);
  border-color: var(--cobalt);
  color: var(--cobalt-deep);
}

/* ───────── sidebar ───────── */
.sidebar {
  background: var(--chrome);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
  min-height: 0;
  width: var(--sidebar);
  flex: 0 0 auto;
}
.sidebar.collapsed { width: 44px; align-items: center; gap: 8px; padding-top: 8px; }
.sidebar-rail { display: flex; flex-direction: column; gap: 4px; padding: 4px 0; overflow: auto; }
.rail-host {
  width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
}
.rail-host:hover { background: var(--slate-100); }
.rail-host.active { background: var(--cobalt-bg); box-shadow: inset 2px 0 0 var(--cobalt); }

.sidebar-h {
  padding: 12px 12px 8px;
  border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 8px;
}
.sidebar-h-row { display: flex; align-items: center; justify-content: space-between; }

/* Collapse toggle — small icon button at the top-right of the sidebar
   header. Rotates 180° when collapsed so the chevron points outward
   (right) instead of inward (left), reading as "expand" rather than
   "collapse". */
.sidebar-collapse-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  padding: 0; border: 0;
  background: transparent; cursor: pointer;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  transition: background .12s, color .12s, transform .18s;
}
.sidebar-collapse-btn:hover { background: var(--slate-100); color: var(--ink-900); }
.app[data-sidebar="collapsed"] .sidebar-collapse-btn { transform: rotate(180deg); }

/* When the user collapses the sidebar, --sidebar shrinks for the whole
   .app subtree. The topbar's grid-template-columns: calc(var(--sidebar)
   - 12px) ... naturally tracks this, so the brand "AdminEZ" column
   shrinks in sync with the sidebar rail. */
.app[data-sidebar="collapsed"] { --sidebar: 48px; }
.app[data-sidebar="collapsed"] .sidebar { width: 48px; min-width: 48px; }
.app[data-sidebar="collapsed"] .sidebar-h { padding: 12px 8px 8px; }
.app[data-sidebar="collapsed"] .sidebar-h-row { justify-content: center; }
.app[data-sidebar="collapsed"] .sidebar-title,
.app[data-sidebar="collapsed"] .sidebar-filter,
.app[data-sidebar="collapsed"] .sidebar-pillrow,
.app[data-sidebar="collapsed"] .sidebar-list { display: none; }
/* Hide the brand wordmark when the column shrinks — the logo mark
   alone fits in 48px without overflow. */
.app[data-sidebar="collapsed"] .brand-text { display: none; }
.sidebar-title { font-weight: 600; color: var(--ink-900); font-size: 13px; letter-spacing: -0.005em; display: flex; gap: 6px; align-items: center; }
.sidebar-filter {
  display: flex; align-items: center; gap: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 5px 8px;
}
.sidebar-filter:focus-within {
  background: var(--surface);
  border-color: var(--cobalt-light);
  box-shadow: 0 0 0 3px var(--cobalt-bg);
}
.sidebar-filter-i { color: var(--text-muted); display: inline-flex; }
.sidebar-filter input { border: 0; outline: 0; background: transparent; flex: 1; font-size: 12px; }
.sidebar-pillrow {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 3px;
}
.sb-chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  font-size: 10.5px; padding: 3px 6px;
  border-radius: 3px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-weight: 500;
  min-width: 0;
}
.sb-chip-count {
  font-family: var(--font-mono);
  font-size: 9.5px;
  padding: 1px 4px;
  border-radius: 2px;
  background: rgba(15, 23, 42, 0.06);
  color: inherit;
  opacity: 0.85;
}
/* always-tinted by tone — readable in both selected and unselected states */
.sb-chip.tone-critical { background: #fde4e4; color: var(--crit);    border-color: #f0b6b6; }
.sb-chip.tone-warning  { background: var(--warn-bg); color: var(--warn); border-color: #fbd9b0; }
.sb-chip.tone-healthy  { background: var(--ok-bg);   color: var(--ok);   border-color: #b6e3cf; }
.sb-chip.tone-offline  { background: var(--mute-bg); color: var(--slate-600); border-color: var(--slate-300); }
.sb-chip.tone-all      { background: var(--surface); color: var(--text-secondary); }

.sb-chip:hover { filter: brightness(0.97); }
/* selected = border in own color, weight unchanged */
.sb-chip.active { border-color: currentColor; }
.sb-chip.active.tone-all { background: var(--cobalt-bg); color: var(--cobalt); border-color: var(--cobalt-lighter); }
.sidebar-grouprow { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.lnk { color: var(--text-muted); font-size: 11px; font-weight: 500; padding: 2px 4px; border-radius: 2px; }
.lnk:hover { color: var(--ink-900); background: var(--slate-100); }
.lnk.active { color: var(--cobalt); background: var(--cobalt-bg); }

.sidebar-list {
  overflow: auto;
  flex: 1 1 auto;
  padding: 8px 6px;
  scrollbar-width: thin;
  scrollbar-color: var(--slate-300) transparent;
}
.sidebar-list::-webkit-scrollbar { width: 8px; }
.sidebar-list::-webkit-scrollbar-track { background: transparent; }
.sidebar-list::-webkit-scrollbar-thumb {
  background: transparent;
  border: 2px solid transparent;
  background-clip: padding-box;
  border-radius: 8px;
  min-height: 32px;
}
.sidebar-list:hover::-webkit-scrollbar-thumb { background: var(--slate-300); background-clip: padding-box; }
.sidebar-list::-webkit-scrollbar-thumb:hover { background: var(--slate-400); background-clip: padding-box; }
.sb-group { margin-bottom: 8px; }
.sb-group-h {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 8px 4px 26px;
  font-size: 10.5px; font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}
.sb-group-name { flex: 1; }
.sb-host {
  /* Status dots removed; the row is now a single content column that
     left-flushes to the same x as the host-filter input above. Padding
     keeps a comfortable rhythm against the filter input + adjacent
     rows. */
  display: block;
  padding: 10px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  width: 100%;
  text-align: left;
  position: relative;
}
.sb-host:hover { background: var(--slate-100); }
.sb-host.active {
  background: var(--cobalt-bg);
  border-color: var(--cobalt-lighter);
}
/* Three-row card layout: name+chip, group+ip, last-update.
   Increased gap between rows for breathing room. */
.sb-host-mid { min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.sb-host-row { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.sb-host-row-2 { justify-content: space-between; }
.sb-host-name { font-weight: 500; color: var(--ink-900); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.sb-host-group { font-size: 11px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1; }
.sb-host-group-name { color: var(--ink-900); font-weight: 500; }
.sb-host-seen { font-size: 10.5px; color: var(--text-muted); }
.sb-host-seen-val { color: var(--text-secondary); }
.sb-tag {
  font-size: 9.5px; font-weight: 600;
  padding: 1px 5px; border-radius: 3px;
  background: var(--slate-200); color: var(--slate-700);
  letter-spacing: 0.02em; line-height: 1.4;
  text-transform: lowercase;
}
/* semantic tag colors — same in selected + unselected rows, no grey-on-grey */
.sb-tag[data-tag="prod"]       { background: #dbe5f8; color: var(--cobalt-deep); }
.sb-tag[data-tag="staging"]    { background: var(--warn-bg); color: var(--warn); }
.sb-tag[data-tag="dev"]        { background: #e6e9f5; color: var(--slate-600); }
.sb-tag[data-tag="gpu"]        { background: #ede4fb; color: var(--violet); }
.sb-tag[data-tag="db"]         { background: var(--ok-bg); color: var(--ok); }
.sb-tag[data-tag="cache"]      { background: #e0f0fb; color: #1e6db1; }
.sb-tag[data-tag="build"],
.sb-tag[data-tag="ci"]         { background: #fbe9d6; color: #9a4f00; }
.sb-tag[data-tag="edge"]       { background: #e2eafb; color: var(--cobalt-light); }
.sb-tag[data-tag="monitoring"],
.sb-tag[data-tag="backup"]     { background: var(--slate-200); color: var(--slate-700); }
.sb-host-ip { color: var(--text-muted); }

/* topbar user menu */
.user-menu { position: relative; }
.user-menu-trigger {
  display: inline-flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  user-select: none;
  list-style: none;
}
.user-menu-trigger::-webkit-details-marker { display: none; }
.user-menu-trigger::marker { content: ''; }
.user-menu-trigger:hover { background: var(--slate-100); }
.user-menu-name {
  font-size: 12.5px; font-weight: 500; color: var(--ink-900);
  max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.user-menu-panel {
  position: absolute; top: calc(100% + 4px); right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  min-width: 220px;
  padding: 4px;
  z-index: 50;
}
.user-menu-h { padding: 8px 10px 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.user-menu-h-name { font-weight: 600; font-size: 12.5px; color: var(--ink-900); }
.user-menu-h-sub { margin-top: 2px; }
.user-menu-item {
  display: flex; align-items: center; gap: 8px;
  width: 100%; padding: 7px 10px;
  border-radius: var(--radius-sm);
  background: transparent; border: 0;
  font-size: 12.5px; color: var(--text);
  cursor: pointer; text-align: left;
}
.user-menu-item:hover { background: var(--slate-100); color: var(--ink-900); }
.user-menu-sep { height: 1px; background: var(--border); margin: 4px 0; }
/* Mobile-nav links inside the user menu — hidden on desktop where mainnav is visible */
.user-menu-mobile-nav { display: none; }
@media (max-width: 768px) {
  .user-menu-mobile-nav { display: block; }
}
.brand-text { color: var(--ink-900); font-weight: 600; font-size: 14px; }

/* spinning propeller for sensor fans */
.fan-icon { display: inline-flex; color: var(--slate-400); }
.fan-icon svg { animation: spin 5s linear infinite; }

/* icon-button content gap inside .btn */
.btn .icon { display: inline-block; }
/* SSH button uses a `<span data-ssh-icon>` wrapper around its SVG so the
   JS handler can swap glyphs (plug → clipboard-check) on copy without
   re-fetching markup. The wrapper has to inherit the SVG's centering
   behavior — without this rule the wrapped icon drifts up a pixel
   compared to sibling .btn icons (the span behaves as inline by default
   while the bare SVG would render as inline-block via the rule above). */
.btn [data-ssh-icon] { display: inline-flex; align-items: center; line-height: 0; }

/* sort row on fleet overview */
.sort-row { display: flex; gap: 8px; align-items: center; font-size: 11.5px; color: var(--text-muted); }
.sort-row .lnk { padding: 2px 6px; border-radius: var(--radius-sm); }
.sort-row .lnk:hover { background: var(--slate-100); text-decoration: none; }
.sort-row .lnk.active { background: var(--cobalt-bg); color: var(--cobalt); font-weight: 500; text-decoration: none; }
.dot-sep { color: var(--slate-300); }
.sb-host-chip {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 2px 6px;
  border-radius: 3px;
  white-space: nowrap;
  background: var(--slate-100);
  color: var(--text-muted);
  flex-shrink: 0;
}
.sb-host-chip.tone-crit { background: var(--crit-bg); color: var(--crit); }
.sb-host-chip.tone-warn { background: var(--warn-bg); color: var(--warn); }
.sb-empty { color: var(--text-muted); text-align: center; padding: 24px 12px; font-size: 12px; }

/* ───────── card ───────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.card-h {
  display: flex; align-items: center; justify-content: space-between;
  /* Equal padding on both sides so the title doesn't crowd the card's
     left border. Subtle slate-100 background so the header reads as a
     distinct section vs. the white card body underneath. */
  padding: 10px var(--pad);
  background: var(--chrome);
  border-bottom: 1px solid var(--border);
  gap: 12px;
}
.card-h-l { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.card-h-r { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.card-title { margin: 0; font-size: 12.5px; font-weight: 600; color: var(--ink-900); letter-spacing: -0.005em; }
.card-sub   { font-size: 10.5px; color: var(--text-muted); }
.card-h-stat {
  font-size: 18px; font-weight: 600; color: var(--ink-900);
  letter-spacing: -0.015em;
  display: inline-flex; align-items: baseline; gap: 2px;
}
.card-h-stat .unit { font-size: 13px; color: currentColor; opacity: 0.55; font-weight: 500; margin-left: 2px; }
.card-h-stat.inline-stat { font-size: 13px; }
.dirarrow { font-size: 11px; margin-right: 2px; }
.dirarrow.up   { color: var(--cobalt); }
.dirarrow.down { color: var(--cobalt-light); }
.card-body { flex: 1 1 auto; min-width: 0; padding: var(--pad); }
.card-body.card-body-flush { padding: 0; }
.card-stack { display: flex; flex-direction: column; gap: 10px; }

/* ───────── KPI ───────── */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap);
}
.kpi {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
  display: flex; flex-direction: column; gap: 4px;
}
.kpi-label { font-size: 10.5px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.kpi-value { font-size: 22px; font-weight: 600; color: var(--ink-900); letter-spacing: -0.02em; line-height: 1.1; }
.kpi-sub   { font-size: 11px; color: var(--text-muted); }

/* ───────── CPU widget ───────── */
.cpu-wrap { display: grid; grid-template-rows: auto auto auto; gap: 14px; }
.cpu-cores {
  display: grid;
  grid-template-columns: repeat(var(--cores, 8), 1fr);
  gap: 3px;
  align-items: end;
  height: 56px;
  padding-bottom: 2px;
}
.cpu-core { position: relative; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 2px; }
.cpu-core-bar { width: 100%; background: var(--cobalt); border-radius: 2px 2px 0 0; min-height: 2px; transition: height .4s; }
.cpu-core-lbl { font-size: 9px; color: var(--text-muted); }
.cpu-meta { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding-top: 8px; border-top: 1px dashed var(--border); }
.cpu-meta > div { display: flex; flex-direction: column; gap: 2px; }
.cpu-meta .mono { font-size: 12px; color: var(--ink-900); }

/* ───────── kv grids ───────── */
.kvgrid { display: grid; grid-template-columns: 1fr; gap: 6px; }
.kvgrid.two { grid-template-columns: 1fr 1fr; }
.kvgrid.three { grid-template-columns: repeat(3, 1fr); }
.kvgrid > div { display: flex; flex-direction: column; gap: 2px; }
.kv { display: grid !important; grid-template-columns: 50px 1fr 100px; gap: 8px; align-items: center; }
.kv .mono { text-align: right; }

/* ───────── sensors ───────── */
/* Match the rhythm of the Storage card's disk rows so the right-column
   Sensors don't read as a lighter-weight widget than the disk widget. */
.sensors-grid { display: flex; flex-direction: column; gap: 10px; }
.sensor-row { display: grid; grid-template-columns: 110px 1fr 80px; gap: 10px; align-items: center; }
.sensor-name { font-size: 11.5px; color: var(--text-secondary); }
.sensor-val { font-size: 11px; text-align: right; }
.sensor-val .muted { margin-left: 2px; }
.fans-row { display: flex; flex-wrap: wrap; gap: 8px 16px; padding-top: 10px; margin-top: 10px; border-top: 1px dashed var(--border); }
.fan { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; }
.fan .mono { font-weight: 500; }
.fan .unit { color: var(--text-muted); margin-left: 2px; font-size: 9.5px; }

/* ───────── disks ───────── */
.disks { display: flex; flex-direction: column; gap: 12px; }
.disk { display: flex; flex-direction: column; gap: 6px; }
.disk-h { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.disk-h-l { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.disk-h-r { display: flex; align-items: center; gap: 8px; }
.disk-mount { font-weight: 600; color: var(--ink-900); font-size: 12.5px; }
.disk-meta { display: flex; gap: 12px; font-size: 10.5px; flex-wrap: wrap; }

/* ───────── tables ───────── */
.tbl-wrap { overflow: auto; }
.tbl { width: 100%; border-collapse: collapse; font-size: 12px; }
.tbl thead th {
  text-align: left;
  font-size: 10.5px; font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  background: var(--chrome);
  position: sticky; top: 0;
}
.tbl th.right, .tbl td.right { text-align: right; }
.tbl tbody td { padding: 7px 10px; border-bottom: 1px solid var(--slate-100); vertical-align: middle; }
.tbl tbody tr:hover { background: var(--slate-50); }
.tbl tbody tr:last-child td { border-bottom: 0; }
.row-actions { display: inline-flex; gap: 4px; justify-content: flex-end; }

/* ───────── buttons ───────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  background: var(--cobalt); color: white;
  padding: 7px 12px; border-radius: 3px;
  font-size: 12px; font-weight: 500;
  border: 1px solid var(--cobalt-deep);
  transition: background .12s, transform .04s;
}
.btn:hover { background: var(--cobalt-deep); }
.btn:active { transform: translateY(0.5px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn.sm { padding: 4px 8px; font-size: 11.5px; }
.btn.xs { padding: 3px 6px; font-size: 11px; }
.btn.ghost { background: transparent; color: var(--text-secondary); border-color: transparent; }
.btn.ghost:hover { background: rgba(15, 23, 42, 0.05); color: var(--ink-900); }
.btn.outline { background: var(--surface); color: var(--ink-900); border-color: var(--border); }
.btn.outline:hover { background: var(--surface-2); border-color: var(--slate-300); }

/* ───────── content tabs ───────── */
.tabs {
  display: flex; gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--gap);
  padding: 0;
}
.tab {
  padding: 9px 14px;
  font-size: 13px; font-weight: 500;
  color: var(--text-secondary);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  display: inline-flex; align-items: center; gap: 6px;
}
.tab:hover { color: var(--ink-900); }
.tab.active { color: var(--cobalt); border-bottom-color: var(--cobalt); font-weight: 600; }
.tab-count {
  background: var(--slate-100); color: var(--text-muted);
  font-size: 10px; padding: 1px 5px; border-radius: var(--radius-pill); font-weight: 600;
}
.tab.active .tab-count { background: var(--cobalt-bg); color: var(--cobalt); }

/* ───────── alert banner ───────── */
.alertbanner {
  display: flex; align-items: center; gap: 18px;
  background: #fff4e6;
  border: 1px solid #fbd9b0;
  border-radius: var(--radius);
  padding: 12px 14px;
  box-shadow: 0 1px 2px rgba(217, 119, 6, 0.05);
}
.alertbanner.crit {
  background: #fde4e4;
  border-color: #f0b6b6;
  box-shadow: 0 1px 2px rgba(185, 28, 28, 0.08);
}
/* Left side: icon + title block. Narrowed from 220px to 170px to give the
   per-alert rows more room — "1 active alert" only needs ~110px of text. */
.alertbanner-l {
  display: flex; gap: 10px; align-items: center;
  flex-shrink: 0; width: 170px;
}
.alertbanner-icon {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.alertbanner.crit .alertbanner-icon { color: var(--crit); }
.alertbanner.warn .alertbanner-icon,
.alertbanner:not(.crit):not(.warn) .alertbanner-icon { color: var(--warn); }
/* Title color now tracks the severity instead of being plain black — at a
   glance "1 active alert" reads warning-amber when warn and crit-red when crit. */
.alertbanner-title {
  font-weight: 600; font-size: 13px; line-height: 1.25;
  color: var(--warn);
}
.alertbanner.crit .alertbanner-title { color: var(--crit); }
.alertbanner-sub { font-size: 11px; color: var(--text-secondary); margin-top: 2px; }

/* Right side: per-alert rows. Inline flex — dot + message + (small gap) + ts,
   all hugged together. Timestamp sits next to the message instead of flushed
   to the right edge. */
.alertbanner-list { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.alertbanner-row {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px;
  padding: 1px 0;
  min-width: 0;
}
.alertbanner-row > :first-child { flex-shrink: 0; } /* status dot */
.alertbanner-msg {
  color: var(--ink-900);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  flex-shrink: 1; min-width: 0;
}
.alertbanner-ts  {
  color: var(--text-muted); font-size: 10.5px;
  white-space: nowrap; font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
@media (max-width: 768px) {
  .alertbanner { flex-direction: column; gap: 10px; }
  .alertbanner-l { width: auto; }
}

/* Suppressed-alerts notice — shown under the active banner when there are
   suppressed warnings the operator chose to hide. Quiet, slate-200 ribbon. */
.suppressed-notice {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px;
  padding: 8px 12px;
  background: var(--slate-100);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 12px;
}
.suppressed-notice .lnk { margin-left: 4px; }

/* ───────── alerts list ───────── */
.alert-list { display: flex; flex-direction: column; gap: 8px; }
.card-foot-link { padding-top: 10px; margin-top: 10px; border-top: 1px solid var(--border); text-align: center; }
.card-foot-link .lnk { font-size: 12px; }
.alert { display: grid; grid-template-columns: 12px 1fr auto; gap: 10px; align-items: center; padding: 10px 12px; border-radius: var(--radius-sm); border: 1px solid var(--border); }
.alert.critical { background: #fdeded; border-color: #f0b6b6; }
.alert.warning  { background: #fff4e6; border-color: #fbd9b0; }
.alert.ack      { background: var(--surface-2); border-color: var(--border); opacity: 0.75; }
.alert.suppressed { background: var(--surface-2); border-color: var(--border); opacity: 0.6; }
.alert-actions { display: flex; gap: 6px; align-items: center; }

/* per-alert suppress dropdown */
.suppress-menu { position: relative; }
.suppress-menu summary { list-style: none; }
.suppress-menu summary::-webkit-details-marker { display: none; }
.suppress-menu-panel {
  position: absolute; right: 0; top: calc(100% + 4px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  min-width: 160px; padding: 4px;
  z-index: 50;
  display: flex; flex-direction: column;
}
.suppress-menu-item {
  display: block; width: 100%;
  padding: 6px 10px;
  border: 0; background: transparent;
  font-size: 12.5px; color: var(--text);
  cursor: pointer; text-align: left; border-radius: var(--radius-sm);
}
.suppress-menu-item:hover { background: var(--slate-100); color: var(--ink-900); }

/* live-search dropdown */
.topbar form.searchbox { position: relative; }
.search-dropdown {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  max-height: 70vh;
  overflow-y: auto;
  z-index: 100;
  padding: 6px;
}
.search-dropdown[hidden] { display: none; }
.search-group { padding: 6px 0; border-top: 1px solid var(--border); }
.search-group:first-child { border-top: 0; }
.search-group-h {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-muted);
  padding: 4px 10px 6px;
}
.search-item {
  display: grid; grid-template-columns: 10px 1fr auto;
  gap: 10px; align-items: center;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.search-item:hover { background: var(--slate-100); }
.search-dot { width: 10px; height: 10px; flex-shrink: 0; }
/* Catalog items (Pages & actions) use a glyph instead of a status dot —
   the row has no "status" to convey and the arrow hints at navigation. */
.search-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 10px; height: 10px;
  font-size: 10px; line-height: 1;
  color: var(--cobalt);
  font-family: var(--font-mono);
}
.search-item-mid { min-width: 0; }
.search-item-title {
  font-size: 12.5px; font-weight: 500; color: var(--ink-900);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.search-item-sub { margin-top: 2px; }
.search-item mark { background: var(--cobalt-bg); color: var(--cobalt-deep); padding: 0 1px; border-radius: 2px; }
.search-kind {
  font-family: var(--font-mono);
  font-size: 9.5px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--slate-100); color: var(--slate-600);
}
.search-empty { padding: 16px 10px; text-align: center; }

/* add-host steps */
.steps { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 14px; }
.step-h { font-size: 12.5px; font-weight: 600; color: var(--ink-900); margin-bottom: 6px; }
.codeblock {
  background: var(--slate-50);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--ink-900);
  overflow-x: auto;
  white-space: pre;
  margin: 0;
}
.bullet-list { padding-left: 16px; display: flex; flex-direction: column; gap: 4px; }

/* ───────── modal (native <dialog>) ───────── */
.modal {
  border: 0; padding: 0;
  border-radius: var(--radius);
  background: transparent;
  max-width: 760px; width: 92vw;
  box-shadow: var(--shadow-lg);
}
.modal::backdrop { background: rgba(15, 23, 42, 0.45); }
.modal-body {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px 20px;
  display: flex; flex-direction: column; gap: 14px;
  margin: 0;
}
.modal-h { display: flex; align-items: center; justify-content: space-between; }
.modal-title { font-size: 15px; font-weight: 600; color: var(--ink-900); }
.modal-x {
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: transparent; cursor: pointer;
  font-size: 20px; line-height: 1; color: var(--text-muted);
  border-radius: var(--radius-sm);
}
.modal-x:hover { background: var(--slate-100); color: var(--ink-900); }
.modal-lead { margin: 0; font-size: 12.5px; color: var(--text); }
.modal-warnings {
  margin: 0; padding: 10px 12px 10px 28px;
  background: var(--warn-bg);
  border: 1px solid #fbd9b0;
  border-radius: var(--radius-sm);
  font-size: 11.5px; color: var(--text);
  display: flex; flex-direction: column; gap: 3px;
}
.modal-warnings li { line-height: 1.5; }
.modal-warnings strong { color: var(--ink-900); }
.modal-warnings em { font-style: normal; font-weight: 600; color: var(--ink-900); }

.modal-actions {
  display: flex; gap: 8px; justify-content: flex-end;
  margin-top: 2px;
}

/* ────────── Terminal dock ──────────
   Lives at the bottom of the viewport, marked turbo-permanent so Turbo's
   morph navigation leaves it alone. Hidden by default; opens when a host
   page calls window.AdminEZTerminal.open(hostId). */
#term-dock {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 100;
  background: #0f172a;
  color: #e2e8f0;
  border-top: 1px solid #1e293b;
  box-shadow: 0 -8px 30px rgba(0,0,0,0.25);
  display: flex; flex-direction: column;
  height: 32vh; min-height: 220px;
  transition: height .22s ease, transform .22s ease;
}
#term-dock[data-state="hidden"]    { transform: translateY(100%); height: 32vh; }
#term-dock[data-state="minimized"] { height: 38px; min-height: 38px; }
#term-dock[data-state="docked"]    { height: 32vh; }
#term-dock[data-state="maximized"] { height: 100vh; }

.term-dock-tabs {
  display: flex; align-items: center; gap: 4px;
  padding: 4px 6px 0 8px;
  background: #0b1220;
  border-bottom: 1px solid #1e293b;
  flex-shrink: 0;
  user-select: none;
}
.term-dock-tabstrip { display: flex; gap: 2px; flex: 1; overflow-x: auto; }
.term-dock-tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px;
  font-size: 12px;
  background: transparent; color: #94a3b8;
  border: 0; border-radius: 4px 4px 0 0;
  cursor: pointer; white-space: nowrap;
}
.term-dock-tab:hover { background: rgba(255,255,255,0.04); }
.term-dock-tab.active {
  background: #0f172a; color: #e2e8f0;
  box-shadow: inset 0 2px 0 var(--cobalt);
}
.term-dock-tab-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #10b981; /* green when connected; turns red on WS drop */
  transition: background .15s ease;
}
/* Reconnect pill drives the per-tab dot color too — easier than per-tab
   state since all tabs share one Pusher connection. */
#term-dock[data-ws-state="unavailable"] .term-dock-tab-dot,
#term-dock[data-ws-state="failed"]      .term-dock-tab-dot,
#term-dock[data-ws-state="disconnected"] .term-dock-tab-dot { background: #ef4444; }
#term-dock[data-ws-state="connecting"]   .term-dock-tab-dot { background: #f59e0b; }
.term-dock-tab-close {
  opacity: .55; padding: 0 4px;
}
.term-dock-tab-close:hover { opacity: 1; color: #f87171; }
.term-dock-tab-name.editing {
  outline: 1px solid var(--cobalt);
  background: rgba(30,64,175,0.18);
  border-radius: 2px;
  padding: 0 3px;
  cursor: text;
  /* While editing, ignore the parent's tab-click handler so a stray click
     inside the input doesn't bubble up and toggle activate(). */
  pointer-events: auto;
}

.term-dock-newtab {
  /* Same vertical metrics as a tab so it doesn't jiggle the strip. */
  display: none;
  width: 26px; height: 28px;
  border: 0; background: transparent; cursor: pointer;
  color: #94a3b8; font-size: 18px; line-height: 1;
  border-radius: 4px;
  margin: 0 2px;
}
.term-dock-newtab:hover { background: rgba(255,255,255,0.05); color: #e2e8f0; }
/* Show the + only after at least one tab exists. JS toggles the
   has-tabs class on the dock root. */
#term-dock.has-tabs .term-dock-newtab { display: inline-block; }

.term-dock-ctrls { display: flex; gap: 2px; }
.term-dock-ctrl {
  width: 28px; height: 28px;
  border: 0; background: transparent; cursor: pointer;
  color: #94a3b8; font-size: 14px; line-height: 1;
  border-radius: 4px;
}
.term-dock-ctrl:hover { background: rgba(255,255,255,0.06); color: #e2e8f0; }

/* WebSocket status pill — sits between the tab strip and the dock controls.
   Hidden when the dock has no tabs yet (idle), expands inline with a dot,
   label, and reconnect arrow when something's wrong. */
.term-dock-ws {
  display: inline-flex; align-items: center; gap: 6px;
  height: 22px; padding: 0 8px; margin-right: 6px;
  background: rgba(255,255,255,0.04); border-radius: 11px;
  font: 11px/1 'Inter', system-ui, sans-serif;
  color: #94a3b8;
}
.term-dock-ws-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: #94a3b8;
  transition: background .15s ease;
}
.term-dock-ws-label { letter-spacing: 0.02em; }
.term-dock-ws-btn {
  display: none;
  width: 18px; height: 18px; padding: 0; border: 0;
  background: transparent; cursor: pointer;
  color: #fca5a5; font-size: 13px; line-height: 1;
  border-radius: 4px;
}
.term-dock-ws-btn:hover { background: rgba(239,68,68,0.18); color: #fecaca; }

#term-dock[data-ws-state="idle"] .term-dock-ws       { display: none; }
#term-dock[data-ws-state="connecting"] .term-dock-ws-dot { background: #f59e0b; }
#term-dock[data-ws-state="connected"]  .term-dock-ws-dot { background: #10b981; }
#term-dock[data-ws-state="unavailable"] .term-dock-ws-dot,
#term-dock[data-ws-state="failed"]      .term-dock-ws-dot,
#term-dock[data-ws-state="disconnected"] .term-dock-ws-dot { background: #ef4444; }

#term-dock[data-ws-state="unavailable"] .term-dock-ws,
#term-dock[data-ws-state="failed"] .term-dock-ws,
#term-dock[data-ws-state="disconnected"] .term-dock-ws { color: #fca5a5; }
#term-dock[data-ws-state="unavailable"] .term-dock-ws-btn,
#term-dock[data-ws-state="failed"] .term-dock-ws-btn,
#term-dock[data-ws-state="disconnected"] .term-dock-ws-btn { display: inline-flex; align-items: center; justify-content: center; }

.term-dock-body {
  flex: 1; position: relative; overflow: hidden;
  padding: 6px 8px;
}
#term-dock[data-state="minimized"] .term-dock-body { display: none; }

.term-dock-pane {
  position: absolute; inset: 6px 8px;
  display: none;
}
.term-dock-pane.active { display: block; }
.term-dock-pane .xterm { height: 100%; }
.term-dock-pane .xterm-viewport,
.term-dock-pane .xterm-screen { height: 100% !important; }

/* TOTP enrolment — QR code + secret + verify form. */
.totp-enrol {
  display: flex; align-items: flex-start; gap: 18px; flex-wrap: wrap;
  padding: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.totp-qr { width: 140px; height: 140px; flex-shrink: 0; }
.totp-qr svg { width: 100%; height: 100%; }
.totp-secret { display: flex; flex-direction: column; gap: 4px; min-width: 0; }

/* Recovery codes — only ever shown once after enrolment / regeneration.
   Loud-yellow background so the user knows to save them. */
.recovery-codes {
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px 14px;
  background: var(--warn-bg);
  border: 1px solid #fbd9b0;
  border-radius: var(--radius-sm);
  font-size: 12px;
}
.recovery-codes-h {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--ink-900);
}
.recovery-codes-list {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 4px 16px;
  padding: 8px 10px;
  background: white;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 13px; letter-spacing: 0.02em;
  color: var(--ink-900);
}

/* Sessions table — one row per device, two clickable icon-buttons for
   app/terminal revoke. The icons are intentionally larger than the row
   accent so the click target is generous. */
.session-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--cobalt-bg);
  color: var(--cobalt);
  cursor: pointer;
}
.session-icon:hover  { background: var(--cobalt); color: white; }
.session-icon:disabled,
.session-icon.disabled {
  background: transparent;
  color: var(--slate-400);
  border-color: var(--border);
  cursor: not-allowed;
}
.row-current {
  background: var(--cobalt-bg);
}
.row-current td:first-child {
  box-shadow: inset 3px 0 0 var(--cobalt);
}

/* Inline rename popover next to a device row. */
.rename-pop { position: relative; display: inline-flex; }
.rename-pop[open] > summary { background: var(--slate-200); }
.rename-pop .rename-form {
  position: absolute; right: 0; top: calc(100% + 4px);
  z-index: 5;
  display: flex; gap: 4px; align-items: center;
  background: white; border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px; min-width: 220px;
  box-shadow: var(--shadow-lg);
}

/* First-login banner — shown when the trusted-device cookie was just minted
   and the device hasn't been named yet. Sits above content like the
   onboarding banner. */
.device-name-banner {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  background: var(--cobalt-bg);
  border: 1px solid color-mix(in oklab, var(--cobalt) 25%, transparent);
  border-radius: var(--radius);
  margin-bottom: var(--gap);
}
.device-name-banner-l { flex: 1; display: flex; align-items: center; gap: 10px; }
.device-name-banner form {
  display: flex; gap: 6px; align-items: center;
}
.device-name-banner input[type="text"] {
  min-width: 180px;
  padding: 4px 8px; font-size: 12px;
}

/* The confirm dialog is intentionally narrower than the file-style "Add host"
   modal — it's a yes/no question, not a tray of options. */
.modal-confirm { max-width: 440px; }
.modal-confirm .modal-lead { font-size: 13px; line-height: 1.5; }
.modal-edit    { max-width: 520px; }

/* Host group editor — pick existing OR type a new one. The two inputs share
   space so the user sees both options at once. */
.host-group-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 8px;
  align-items: center;
}

/* Group label that hangs next to the host name. Color is supplied per-group
   via inline `--group-color`; falls back to a slate tint when unset. */
.host-group-chip {
  display: inline-flex; align-items: center;
  padding: 2px 8px;
  font-size: 11px; font-weight: 600;
  background: color-mix(in oklab, var(--group-color, var(--slate-400)) 14%, white);
  color: color-mix(in oklab, var(--group-color, var(--slate-700)) 75%, var(--ink-900));
  border: 1px solid color-mix(in oklab, var(--group-color, var(--slate-400)) 35%, transparent);
  border-radius: var(--radius-pill);
  letter-spacing: 0.01em;
  text-transform: capitalize;
}

.modal-downloads { display: flex; gap: 10px; flex-wrap: wrap; }
.dl-btn {
  flex: 1 1 200px; min-width: 200px;
  display: flex; align-items: center; gap: 12px;
  padding: 14px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  text-decoration: none;
  transition: border-color .12s, background .12s;
}
.dl-btn:hover { border-color: var(--slate-300); background: var(--surface-2); }
.dl-btn.primary {
  background: var(--cobalt);
  border-color: var(--cobalt);
  color: white;
}
.dl-btn.primary:hover { background: var(--cobalt-deep); border-color: var(--cobalt-deep); }
.dl-btn.disabled { opacity: 0.5; cursor: not-allowed; }
.dl-btn-mid { flex: 1; min-width: 0; }
.dl-btn-label { font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 6px; }
.dl-btn-tag {
  font-size: 9.5px; font-weight: 600;
  padding: 1px 5px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.18); color: inherit;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.dl-btn:not(.primary) .dl-btn-tag { background: var(--cobalt-bg); color: var(--cobalt); }
.dl-btn-note { font-size: 11px; opacity: 0.85; margin-top: 2px; }

/* offline state — applied by live.js (body.is-offline, when host goes offline
   live mid-page-view) AND by server-render (.host-view.is-offline, when the
   page loads for an already-offline host). Both selectors apply the same dim. */
.host-card.is-offline,
body.is-offline .rcard,
body.is-offline .kpi,
/* .host-view is the root wrapper around the entire host detail page —
   fleet-h, alertbanner, suppressed-notice, tabs, and the per-tab content
   blocks (resources-row, kpi-row, detail-grid, etc.) are direct children.
   Without a flex/gap context here, those siblings stacked flush against
   each other while everything else in app-content-inner got the expected
   gap. */
.host-view {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  min-width: 0;
}
/* .fleet-h and .tabs each carry a `margin-bottom: var(--gap)` so they
   spaced themselves on pages without a flex parent. Inside .host-view
   those margins compound with the column gap → 32px gaps above + below
   the alert banner instead of the expected 16. Zero the margins here
   so the flex gap is the sole source of vertical rhythm. */
.host-view > .fleet-h,
.host-view > .tabs { margin-bottom: 0; }

.host-view.is-offline .rcard,
.host-view.is-offline .kpi,
.host-view.is-offline .card,
.host-view.is-offline .tbl tbody tr {
  opacity: 0.6;
}
.host-card.is-offline .host-card-metric-v,
body.is-offline [data-live-num],
body.is-offline .kpi-value,
.host-view.is-offline [data-live-num],
.host-view.is-offline .kpi-value {
  color: var(--slate-400) !important;
}
.host-card.is-offline,
.host-view.is-offline .rcard,
.host-view.is-offline .card { filter: grayscale(0.25); }
body.is-offline .rcard-spark,
body.is-offline [data-live-bigchart],
.host-view.is-offline .rcard-spark,
.host-view.is-offline [data-live-bigchart] {
  background: var(--slate-50);
}
#offline-banner { margin-bottom: var(--gap); }

/* Stale-data banner shown above the tabs when viewing an offline host. */
.stale-banner {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  margin-bottom: var(--gap);
  background: var(--slate-100);
  border: 1px solid var(--slate-200);
  border-radius: var(--radius);
  font-size: 12.5px;
  color: var(--text);
}
.stale-banner svg { color: var(--text-muted); flex-shrink: 0; }
.stale-banner strong { color: var(--ink-900); }

/* onboarding — top-of-page banner */
.onboard-banner {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 14px;
  background: var(--cobalt-bg);
  border: 1px solid var(--cobalt-lighter);
  border-radius: var(--radius);
  margin-bottom: var(--gap);
}
.onboard-banner-icon {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--cobalt); color: white;
  border-radius: var(--radius-sm);
}
.onboard-banner-body { flex: 1; min-width: 0; }
.onboard-banner-title { font-weight: 600; color: var(--ink-900); font-size: 13px; }
.onboard-banner-sub { font-size: 11.5px; color: var(--text-secondary); margin-top: 2px; }

/* onboarding — checklist on /welcome */
.onboarding-list { list-style: none; padding: 0; margin: 0; }
.onboarding-step {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  gap: 14px; align-items: center;
  padding: 16px var(--pad);
  border-top: 1px solid var(--border);
}
.onboarding-step:first-child { border-top: 0; }
.onboarding-step.done { opacity: 0.7; }
.onboarding-num {
  width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--slate-100); color: var(--text-secondary);
  font-weight: 600; font-size: 12px;
}
.onboarding-step.done .onboarding-num {
  background: var(--ok); color: white;
}
.onboarding-label { font-weight: 600; color: var(--ink-900); font-size: 13px; }
.onboarding-desc { margin-top: 3px; }
.onboarding-desc code { font-family: var(--font-mono); font-size: 11px; background: var(--slate-100); padding: 1px 4px; border-radius: 2px; }
.alert-msg { font-weight: 500; color: var(--ink-900); font-size: 12.5px; }
.alert-meta { display: flex; gap: 6px; align-items: center; margin-top: 2px; }
/* Severity label inside .alert-meta — plain colored text instead of a
   pill so it left-aligns flush with the .alert-msg above it (the pill's
   internal padding-left used to nudge the label a few px right). */
.alert-sev { font-size: 10.5px; font-weight: 600; letter-spacing: 0.01em; text-transform: capitalize; }
.alert-sev.critical { color: var(--crit); }
.alert-sev.warning  { color: var(--warn); }
.empty { padding: 30px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.empty-icon { width: 40px; height: 40px; background: var(--ok-bg); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; }
.empty-title { font-weight: 600; color: var(--ink-900); }
.empty-sub { color: var(--text-muted); font-size: 12px; }

/* ───────── logs ───────── */
.logs-controls { display: flex; gap: 8px; align-items: center; }
.logs { font-family: var(--font-mono); font-size: 11.5px; max-height: 320px; overflow: auto; padding: 8px 0; background: var(--slate-50); }
.log-line { display: grid; grid-template-columns: 72px 50px 110px 1fr; gap: 8px; padding: 2px 14px; }
.log-line:hover { background: var(--slate-100); }
.log-ts { color: var(--text-muted); }
.log-lvl { font-weight: 600; }
.log-lvl.lvl-info  { color: var(--cobalt); }
.log-lvl.lvl-warn  { color: var(--warn); }
.log-lvl.lvl-error { color: var(--crit); }
.log-lvl.lvl-debug { color: var(--text-muted); }
.log-src { color: var(--violet); }
.log-msg { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.logs-empty { padding: 24px; text-align: center; color: var(--text-muted); }

/* ───────── definition list (host info) ───────── */
.dl { display: grid; grid-template-columns: 100px 1fr; gap: 6px 12px; margin: 0; font-size: 12px; }
.dl dt { color: var(--text-muted); font-size: 11px; }
.dl dd { margin: 0; color: var(--ink-900); }
.taglist { display: flex; gap: 4px; flex-wrap: wrap; }

/* ───────── fleet overview ───────── */
.fleet-h {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: var(--gap);
}
.fleet-h-l h1 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.015em; color: var(--ink-900); }
.fleet-h-l p  { margin: 2px 0 0; color: var(--text-muted); font-size: 12.5px; }
.fleet-h-r { display: flex; gap: 8px; align-items: center; }

.fleet-stats {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--gap);
  margin-bottom: var(--gap);
}
.fstat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
  display: flex; flex-direction: column; gap: 4px;
  position: relative;
}
.fstat-l { font-size: 10.5px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; display: flex; align-items: center; gap: 6px; }
.fstat-v { font-size: 26px; font-weight: 600; color: var(--ink-900); letter-spacing: -0.02em; line-height: 1; font-family: var(--font-mono); }
.fstat-v .unit { font-size: 16px; color: currentColor; opacity: 0.55; margin-left: 3px; font-weight: 500; }
.fstat-s { font-size: 11px; color: var(--text-muted); }
.fstat.crit-tint { background: var(--crit-bg); }
.fstat.warn-tint { background: var(--warn-bg); }
.fstat.ok-tint   { background: var(--ok-bg); }

.fleet-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--gap);
}
.host-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
  display: flex; flex-direction: column; gap: 10px;
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s, transform .04s;
  text-align: left;
}
.host-card:hover { border-color: var(--cobalt-lighter); box-shadow: var(--shadow); }
.host-card:active { transform: translateY(0.5px); }
.host-card.critical { border-color: #f0b6b6; }
.host-card.warning  { border-color: #fbd9b0; }
.host-card.offline  { opacity: 0.65; }
.host-card-h { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.host-card-h-l { display: flex; align-items: center; gap: 8px; min-width: 0; }
.host-card-name { font-weight: 600; color: var(--ink-900); font-size: 13.5px; letter-spacing: -0.005em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.host-card-sub { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.host-card-metrics { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
.host-card-metric { display: flex; flex-direction: column; gap: 1px; }
.host-card-metric-l { font-size: 10px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
.host-card-metric-v { font-size: 13px; font-weight: 600; color: var(--ink-900); font-family: var(--font-mono); letter-spacing: -0.01em; }
.host-card-sparks { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; }
.host-card-spark { display: flex; flex-direction: column; gap: 1px; }
.host-card-spark-l { display: flex; justify-content: space-between; font-size: 9.5px; color: var(--text-muted); }
.host-card-spark-l span:last-child { color: var(--text-secondary); font-family: var(--font-mono); }
.host-card-foot { display: flex; justify-content: space-between; align-items: center; padding-top: 6px; border-top: 1px dashed var(--border); font-size: 10.5px; color: var(--text-muted); }
.host-card.disabled { cursor: default; }

/* ───────── proc-bar (small in table) ───────── */
.proc-bar { width: 60px; }

/* ───────── resources row (system resources at top of host detail) ───────── */
/* New top section: 2:1 split — 6-gauge resource widget on the left,
   load + uptime aux widget on the right. Replaces the old separate
   `.resources-row` (4 rcards) + `.kpi-row` (4 KPIs) stack. */
.host-top {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--gap);
}
.gauges-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  display: flex; flex-direction: column;
  overflow: hidden;
}
.gauges-grid {
  /* Single row, six equal columns — gauges sit side by side. */
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0;
  padding: 14px 16px;
  flex: 1;
}
.gauge {
  position: relative;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 4px;
  padding: 8px 4px;
}
.gauge-svg { width: 80px; height: 80px; display: block; }
.gauge-inner {
  position: absolute;
  inset: 8px 4px auto 4px;
  top: 50%;
  transform: translateY(-60%);   /* nudge up to leave room for label */
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  pointer-events: none;
}
.gauge-val { font-size: 18px; font-weight: 600; color: var(--ink-900); letter-spacing: -0.02em; line-height: 1; }
.gauge-unit { font-size: 10px; line-height: 1; margin-top: 1px; }
.gauge-placeholder { font-size: 10.5px; text-align: center; }
.gauge-label {
  font-size: 10px; font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.06em;
  margin-top: 2px;
}
/* Aux widget — load avg + uptime + last reboot, stacked rows. */
.host-aux {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  display: flex; flex-direction: column;
}
.host-aux-body {
  display: flex; flex-direction: column;
  gap: 12px;
  padding: 14px 16px;
}
.host-aux-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.host-aux-l { font-size: 10.5px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.host-aux-v { font-size: 14px; font-weight: 600; color: var(--ink-900); letter-spacing: -0.01em; }

.resources-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap);
}
.rcard {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  display: flex; flex-direction: column;
  min-width: 0;
  overflow: hidden;
}
.rcard-h {
  /* Equal padding both sides + subtle slate-100 background so the
     header reads as a separate strip from the % + sparkline body. */
  padding: 12px 16px 10px;
  background: var(--chrome);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 6px;
  min-width: 0;
}
.rcard-title {
  font-size: 11px; font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.08em;
}
.rcard-sub {
  font-size: 10.5px;
  color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-width: 0;
}
.rcard-body { padding: 14px 16px; display: flex; flex-direction: column; gap: 12px; flex: 1; }
.rcard-main {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 12px;
}
.rcard-val {
  font-size: 30px; font-weight: 600;
  color: var(--ink-900);
  letter-spacing: -0.025em;
  line-height: 1;
  font-family: var(--font-mono);
  display: inline-flex; align-items: baseline;
}
.rcard-unit { font-size: 20px; opacity: 0.55; font-weight: 500; margin-left: 2px; }
.rcard-spark { min-width: 0; }

/* Big chart partial — SVG plot + HTML y-axis labels.
   Labels live in HTML (not SVG <text>) so they render at a real CSS
   font-size and stay legible regardless of how the SVG is stretched
   by preserveAspectRatio="none". */
.chart {
  position: relative;
  width: 100%;
}
.chart-yaxis {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 32px;
  pointer-events: none;
  z-index: 1;
}
.chart-tick {
  position: absolute;
  right: 6px;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  color: var(--text-muted);
  line-height: 1;
  white-space: nowrap;
  letter-spacing: -0.01em;
}
.chart-plot {
  position: absolute;
  left: 32px; right: 4px; top: 0; bottom: 0;
}
.rcard-foot {
  display: flex; gap: 14px; flex-wrap: wrap;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
  font-size: 11px;
  align-items: center;
}
.rcard-stat { display: inline-flex; align-items: center; gap: 4px; }
.rcard-stat-icon { display: inline-flex; }
.rcard-stat-l { color: var(--text-muted); font-size: 10.5px; }
.rcard-stat-v { font-weight: 500; color: var(--ink-900); font-size: 11.5px; }
.rcard-stat-extra { margin-left: auto; }
.rcard-stat-full { width: 100%; color: var(--text-muted); }

.rcard-empty { opacity: 0.6; }
.rcard-empty-body {
  flex: 1;
  display: flex; align-items: center; justify-content: center;
  padding: 30px 14px;
}
.rcard-empty-msg {
  font-size: 11.5px; color: var(--text-muted);
  display: inline-flex; gap: 6px; align-items: center;
}
.rcard-empty-msg::before {
  content: '';
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--slate-300);
}

/* ───────── grids inside host detail ───────── */
.detail-grid {
  display: grid;
  /* 2:1 split — left column holds the four big metric/storage cards
     (CPU, Memory, GPU, Storage); right column gets the compact Host
     info + Sensors stack. */
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--gap);
}
.detail-grid > .col { display: flex; flex-direction: column; gap: var(--gap); min-width: 0; }
.detail-grid-2 {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap);
}
.detail-grid-3 {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gap);
}

/* ───────── responsive ───────── */
@media (max-width: 1500px) {
  .fleet-stats { grid-template-columns: repeat(3, 1fr); }
  .detail-grid-3 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 1280px) {
  .resources-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 1200px) {
  .detail-grid { grid-template-columns: 1fr; }
  .detail-grid-2 { grid-template-columns: 1fr; }
  .detail-grid-3 { grid-template-columns: 1fr 1fr; }
  .kpi-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
  .topbar { grid-template-columns: 1fr auto; }
  .topbar-mid { display: none; }
  /* Hide bell + settings icon-buttons; their actions are in the user menu */
  .topbar-right .iconbtn { display: none; }
  .user-menu-name { display: none; }
  .sidebar { position: fixed; inset: var(--topbar) auto 0 0; z-index: 20; box-shadow: var(--shadow-lg); transform: translateX(-100%); transition: transform .2s; }
  .sidebar.open { transform: none; }
  .fleet-stats { grid-template-columns: repeat(2, 1fr); }
  .detail-grid-3 { grid-template-columns: 1fr; }
  .kpi-row { grid-template-columns: 1fr 1fr; }
}

/* Mobile fallback — entire app reflows to single column */
.mobile-only { display: none; }
@media (max-width: 768px) {
  .app-main { grid-template-columns: 1fr; }
  .sidebar { display: none; }
  .fleet-stats { grid-template-columns: 1fr 1fr; gap: 8px; }
  .fstat-v { font-size: 18px; }
  .fleet-grid { grid-template-columns: 1fr; }
  .kpi-row { grid-template-columns: 1fr 1fr; }
  .resources-row { grid-template-columns: 1fr 1fr; gap: 8px; }
  .rcard-val { font-size: 22px; }
  .app-content-inner { padding: 12px; gap: 12px; }
  .topbar { padding: 0 10px; }
  .brand-text { display: none; }
  /* Hide the mainnav inline; nav lives in the user-menu panel on mobile */
  .mainnav { display: none; }
  .tabs { overflow-x: auto; flex-wrap: nowrap; }
  .crumb:not(.current) { display: none; }
  .tab { padding: 9px 8px; font-size: 12.5px; white-space: nowrap; }
  .mobile-only { display: block; }
  .tbl thead th, .tbl tbody td { padding: 6px; font-size: 11px; }
  .sensor-row { grid-template-columns: 80px 1fr 50px; }
  .detail-grid { grid-template-columns: 1fr; }
}

/* ───────── auth pages (login) ───────── */
.auth-wrap {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1fr;
  background: var(--bg-deep);
}
.auth-canvas {
  display: flex; align-items: center; justify-content: center;
  padding: 32px 16px;
  background: var(--bg-deep);
}
.auth-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 28px 28px 22px;
  display: flex; flex-direction: column; gap: 18px;
}
.auth-brand {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 600; letter-spacing: -0.01em;
  font-size: 15px; color: var(--ink-900);
}
.auth-h {
  display: flex; flex-direction: column; gap: 4px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}
.auth-h h1 {
  margin: 0; font-size: 18px; font-weight: 600; letter-spacing: -0.015em;
  color: var(--ink-900);
}
.auth-h p { margin: 0; color: var(--text-muted); font-size: 12.5px; }
.auth-form { display: flex; flex-direction: column; gap: 14px; }
.field { display: flex; flex-direction: column; gap: 4px; }
.field-label {
  font-size: 11px; font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0.04em; text-transform: uppercase;
}
.field-input {
  width: 100%;
  height: 36px;
  padding: 0 11px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font);
  font-size: 13px;
  color: var(--ink-900);
  outline: none;
  transition: border-color .12s, background .12s, box-shadow .12s;
}
.field-input:focus {
  border-color: var(--cobalt);
  background: var(--surface);
  box-shadow: 0 0 0 3px rgba(30, 64, 175, 0.12);
}
.field-input::placeholder { color: var(--text-muted); }
.field-error {
  font-size: 11.5px; color: var(--crit);
  display: flex; align-items: center; gap: 4px;
}
.field-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: 12px;
}
.checkbox {
  display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer; user-select: none;
  color: var(--text-secondary);
}
.checkbox input { width: 14px; height: 14px; accent-color: var(--cobalt); margin: 0; }
.auth-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 36px; padding: 0 14px;
  border-radius: var(--radius-sm);
  background: var(--cobalt);
  color: white;
  font-weight: 600;
  font-size: 13px;
  border: 0;
  cursor: pointer;
  transition: background .12s, transform .04s;
}
.auth-btn:hover { background: var(--cobalt-deep); }
.auth-btn:active { transform: translateY(1px); }
.auth-btn:disabled { background: var(--slate-400); cursor: not-allowed; }
.auth-foot {
  display: flex; justify-content: space-between; align-items: center;
  font-size: 11.5px; color: var(--text-muted);
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
.auth-foot a { color: var(--cobalt); text-decoration: none; }
.auth-foot a:hover { text-decoration: underline; }
.auth-error {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px;
  background: var(--crit-bg);
  border: 1px solid #f0b6b6;
  border-radius: var(--radius-sm);
  font-size: 12px; color: #5e1818;
}
.auth-flash {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px;
  background: var(--ok-bg);
  border: 1px solid #b6e3cf;
  border-radius: var(--radius-sm);
  font-size: 12px; color: #0c5235;
}

.brand-word { color: var(--ink-900); }

/* simple table & button helpers used outside the React dashboard */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 30px; padding: 0 12px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 12.5px; font-weight: 500;
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.btn:hover { background: var(--surface-2); border-color: var(--slate-300); }
.btn-primary { background: var(--cobalt); color: white; border-color: var(--cobalt); }
.btn-primary:hover { background: var(--cobalt-deep); border-color: var(--cobalt-deep); }
.btn-danger  { background: var(--crit);   color: white; border-color: var(--crit);   }
.btn-danger:hover  { background: #b91c1c; border-color: #b91c1c; }
.btn-ghost { background: transparent; border-color: transparent; }
.btn-ghost:hover { background: rgba(15,23,42,0.04); }

/* page header for non-dashboard placeholders */
.page-h { margin-bottom: var(--gap-lg); }
.page-h h1 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.015em; }
.page-h p { margin: 4px 0 0; color: var(--text-muted); font-size: 12.5px; }

/* status dots used in fleet grid */
.status-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  flex-shrink: 0;
}
.status-dot.status-healthy  { background: var(--ok); }
.status-dot.status-warning  { background: var(--warn); }
.status-dot.status-critical { background: var(--crit); }
.status-dot.status-offline  { background: var(--slate-400); }

.host-card-h { display: flex; align-items: center; gap: 8px; }
.host-name { font-weight: 600; color: var(--ink-900); font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.host-card { text-decoration: none; color: inherit; }
.host-card-meta, .host-card-foot { color: var(--text-muted); }

/* small fillers — pieces the React mock embedded as inline styles or class
   variants that aren't in the extracted CSS */
.alert-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.alert-dot  { display: inline-flex; align-items: center; }
.sensor-bar { display: flex; align-items: center; }
/* Status pills always start with a capital letter, regardless of how the
   underlying DB value is cased ("offline" → "Offline", "running" → "Running",
   "smart fail" → "Smart Fail"). Tags routed through pills get the same
   treatment; sidebar tags use .sb-tag which keeps its own lowercase rule. */
/* All chip/pill text reads as a label, not a measured value — force
   Inter regardless of the surrounding context. Earlier the sidebar host
   chips and tab-count badges inherited mono from their parent rows when
   the row had `class="mono"` somewhere up the tree. */
/* Codebox — 6-cell numeric entry used by the 2FA step-up flow. Boxes are
   big and squared (44×52px) to read as a deliberate verification UX,
   not a typo-prone text field. Active cell gets a cobalt outline so the
   cursor is unambiguous; filled cells stay neutral. */
.codebox {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin: 4px 0 6px;
}
.codebox-cell {
  width: 44px;
  height: 52px;
  text-align: center;
  font-size: 22px;
  font-weight: 600;
  line-height: 1;
  border: 1.5px solid var(--slate-300);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink-900);
  caret-color: var(--cobalt);
  font-variant-numeric: tabular-nums;
  /* Squish browser default focus ring; we draw our own. */
  outline: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.codebox-cell:focus {
  border-color: var(--cobalt);
  box-shadow: 0 0 0 3px var(--cobalt-bg);
}
.codebox-cell:not(:placeholder-shown) { border-color: var(--slate-400); }

/* Header-only card: no body section, so the header itself should
   carry the rounded bottom corners + provide its own bottom padding.
   Used by the GPU card when the host has no NVIDIA hardware — the
   chart body would just be an empty box otherwise. */
.card.card-header-only > .card-h { border-radius: var(--radius); }
.card.card-header-only > .card-h { padding-bottom: 14px; }
.card.card-header-only           { background: var(--surface); }

.pill,
.chip,
.sb-host-chip,
.host-group-chip,
.tab-count,
.alert-sev,
.alert-filter,
.sb-tag           { font-family: var(--font-sans) !important; }
.pill             { display: inline-flex; text-transform: capitalize; }

/* sidebar chip tones — all five filter-strip variants */
.sb-host-chip.tone-neutral { background: var(--mute-bg); color: var(--slate-700); }

