:root {
  --indigo-50:  #EEF0FF;
  --indigo-100: #DDE0FB;
  --indigo-300: #A5A8F0;
  --indigo-500: #6366F1;
  --indigo-600: #5B5FE3;
  --indigo-700: #4F46E5;
  --indigo-900: #1E1B4B;
  --ink-900:    #0F172A;
  --ink-700:    #334155;
  --ink-500:    #64748B;
  --ink-400:    #94A3B8;
  --ink-200:    #E2E8F0;
  --ink-100:    #F1F5F9;
  --ink-50:     #F8FAFC;
  --green-500:  #22C55E;
  --red-500:    #EF4444;
  --amber-500:  #F59E0B;
  --bg:         #F5F6FB;
  --card-bg:    #FFFFFF;
  --shadow:     0 1px 2px rgba(15, 23, 42, .04), 0 4px 12px rgba(15, 23, 42, .04);
  --radius:     14px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  color: var(--ink-900);
  background: var(--bg);
}
a { color: inherit; text-decoration: none; }
.muted { color: var(--ink-500); }

/* =========================== Topbar =========================== */
.topbar {
  background: #1F2A52;
  background: linear-gradient(180deg, #1F2A52 0%, #232E5E 100%);
  color: #FFF;
}
.topbar-inner {
  max-width: 1400px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  padding: 14px 24px;
  gap: 32px;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 17px;
  color: #FFF;
}
.brand-mark {
  width: 26px; height: 26px; border-radius: 6px;
  background: radial-gradient(circle at 30% 30%, var(--indigo-300), var(--indigo-600));
  box-shadow: inset 0 0 0 2px rgba(255,255,255,.15);
}
.brand-mark.large { width: 36px; height: 36px; }
.topnav {
  display: flex; gap: 6px; flex: 1;
}
.topnav a {
  padding: 8px 14px; border-radius: 8px; color: rgba(255,255,255,.7);
  font-weight: 500; transition: background .15s, color .15s;
}
.topnav a:hover { background: rgba(255,255,255,.06); color: #FFF; }
.topnav a.active { background: var(--indigo-500); color: #FFF; }
.topnav a.muted { color: rgba(255,255,255,.45); }

.avatar {
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--indigo-500);
  display: flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 13px; color: #FFF;
}
.user-name { font-size: 13px; font-weight: 600; }
.user-role { font-size: 11px; color: rgba(255,255,255,.55); }

/* Top-right account dropdown: the user pill is now a button that opens a small
   menu (Change password / Log out). */
.user-menu { position: relative; }
.user-menu-trigger {
  display: flex; align-items: center; gap: 12px;
  background: rgba(255,255,255,.08);
  padding: 6px 12px 6px 8px;
  border: 0; border-radius: 999px; cursor: pointer;
  color: #FFF; font: inherit;
  transition: background .15s;
}
.user-menu-trigger:hover { background: rgba(255,255,255,.14); }
.user-menu-trigger .user-meta {
  display: flex; flex-direction: column; align-items: flex-start;
  line-height: 1.25; white-space: nowrap;
}
.user-menu-caret {
  width: 16px; height: 16px; color: rgba(255,255,255,.55);
  transition: transform .15s ease;
}
.user-menu-trigger[aria-expanded="true"] .user-menu-caret { transform: rotate(180deg); }
.user-menu-dropdown {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  min-width: 220px;
  background: var(--card-bg); border: 1px solid var(--indigo-100);
  border-radius: 12px; box-shadow: 0 14px 32px rgba(15,23,42,.16);
  padding: 6px; display: flex; flex-direction: column; gap: 2px;
}
.user-menu-dropdown[hidden] { display: none; }
.user-menu-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 9px 12px;
  background: transparent; border: 0; border-radius: 8px;
  color: var(--ink-700); font: inherit; font-size: 13px; font-weight: 500;
  text-align: left; text-decoration: none; cursor: pointer;
  transition: background .12s, color .12s;
}
.user-menu-item:hover { background: var(--ink-100); color: var(--ink-900); }
.user-menu-item svg { width: 18px; height: 18px; flex: 0 0 auto; color: var(--ink-400); }
.user-menu-item:hover svg { color: var(--indigo-600); }

/* ---- Header alarm bell ---- */
/* ---- Topbar: Solar agent health pill (mirrors the bell; dark topbar) ---- */
.agent-pill { position: relative; display: flex; align-items: center; }
.agent-pill[hidden] { display: none; }
.agent-pill-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px; border: 0; border-radius: 999px;
  background: rgba(255,255,255,.08); color: #FFF; cursor: pointer;
  font: inherit; transition: background .15s;
}
.agent-pill-btn:hover { background: rgba(255,255,255,.14); }
.agent-pill-text { display: flex; flex-direction: column; line-height: 1.15; text-align: left; }
.agent-pill-title { font-size: 11px; font-weight: 700; }
.agent-pill-status { font-size: 10px; color: rgba(255,255,255,.72); white-space: nowrap; }
.agent-dot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: var(--ink-400);
}
.agent-dot.agent-ok      { background: var(--green-500); box-shadow: 0 0 0 3px rgba(34,197,94,.18); }
.agent-dot.agent-unusual { background: var(--amber-500); box-shadow: 0 0 0 3px rgba(245,158,11,.18); }
.agent-dot.agent-down    { background: var(--red-500);   box-shadow: 0 0 0 3px rgba(239,68,68,.18);
                           animation: agent-pulse 1.6s ease-out infinite; }
/* No heartbeat yet = a problem, same as offline: red, not grey. */
.agent-dot.agent-unknown { background: var(--red-500); box-shadow: 0 0 0 3px rgba(239,68,68,.18); }
@keyframes agent-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(239,68,68,.55); }
  70%  { box-shadow: 0 0 0 8px rgba(239,68,68,0); }
  100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); }
}
@media (prefers-reduced-motion: reduce) { .agent-dot.agent-down { animation: none; } }
.agent-pill-dropdown {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  width: 300px; max-width: min(300px, calc(100vw - 32px));
  background: var(--card-bg); border: 1px solid var(--indigo-100);
  border-radius: 12px; box-shadow: 0 14px 32px rgba(15,23,42,.16); padding: 10px 12px;
}
.agent-pill-dropdown[hidden] { display: none; }
.agent-pill-head {
  display: flex; align-items: baseline; justify-content: space-between;
  padding-bottom: 8px; margin-bottom: 6px; border-bottom: 1px solid var(--ink-100);
}
.agent-pill-head-title { font-size: 13px; font-weight: 700; color: var(--ink-900); }
.agent-pill-head-state { font-size: 11px; color: var(--ink-500); text-transform: capitalize; }
.agent-pill-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.agent-row { display: flex; align-items: flex-start; gap: 8px; padding: 6px; border-radius: 8px; }
.agent-row:hover { background: var(--ink-50); }
.agent-row .agent-dot { margin-top: 3px; }
.agent-row-key { font-size: 12px; font-weight: 600; color: var(--ink-900); word-break: break-all; }
.agent-row-meta { font-size: 11px; margin-top: 2px; }
.agent-empty { font-size: 12px; padding: 8px 6px; }

.bell-root { position: relative; display: flex; align-items: center; }
.bell {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border: 0; border-radius: 999px;
  background: rgba(255,255,255,.08); color: #FFF; cursor: pointer;
  transition: background .15s;
}
.bell:hover { background: rgba(255,255,255,.14); }
.bell svg { width: 19px; height: 19px; display: block; }
.bell-badge {
  position: absolute; top: -3px; right: -3px;
  min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--red-500); color: #FFF;
  font-size: 11px; font-weight: 700; line-height: 1;
  border-radius: 999px; border: 2px solid #232E5E;
}
.bell-badge[hidden] { display: none; }
/* Motion = active alarms need attention (mirrors the map pin-attn pulse). */
.bell.bell-attn { animation: bell-pulse 1.4s ease-in-out infinite; }
@keyframes bell-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(239,68,68,.55); }
  70%  { box-shadow: 0 0 0 8px rgba(239,68,68,0); }
  100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); }
}
@media (prefers-reduced-motion: reduce) { .bell.bell-attn { animation: none; } }

.bell-dropdown {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  width: 320px; max-width: min(320px, calc(100vw - 32px));
  background: var(--card-bg); border: 1px solid var(--indigo-100);
  border-radius: 12px; box-shadow: 0 14px 32px rgba(15,23,42,.16);
  display: flex; flex-direction: column; overflow: hidden;
}
.bell-dropdown[hidden] { display: none; }
.bell-dropdown-head {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 12px 14px 8px; border-bottom: 1px solid var(--ink-100);
}
.bell-dropdown-title { font-size: 13px; font-weight: 700; color: var(--ink-900); }
.bell-dropdown-window { font-size: 11px; }
.bell-list {
  list-style: none; margin: 0; padding: 6px;
  max-height: 360px; overflow-y: auto;
}
.bell-empty { padding: 18px 16px; text-align: center; font-size: 13px; }
.bell-dropdown-foot {
  display: block; padding: 10px 14px; border-top: 1px solid var(--ink-100);
  font-size: 12px; font-weight: 600; color: var(--indigo-600); text-decoration: none;
}
.bell-dropdown-foot:hover { background: var(--ink-100); }

/* Header clock: business timezone (WITA), with the viewer's own local time
   beneath it when their browser timezone differs. Sits in the dark topbar. */
.topclock {
  display: flex; flex-direction: column; align-items: flex-end;
  line-height: 1.25; white-space: nowrap;
}
.topclock-main { font-size: 12.5px; font-weight: 600; color: #FFF; }
.topclock-alt  { font-size: 11px; color: rgba(255,255,255,.6); }
.topclock-alt[hidden] { display: none; }
@media (max-width: 700px) { .topclock { display: none; } }

/* =========================== Page =========================== */
/* The one page container. Its max-width and gutter match .topbar-inner, so page
   titles line up under the brand mark. Only the deliberately full-bleed pages
   (page-fleet, page-chart) may override it — nothing else should re-declare a
   width or padding here, or the pages drift apart. */
.page {
  max-width: 1400px; margin: 0 auto;
  padding: 28px 24px 60px;
}
/* Centers a single auth-style card (e.g. Change password) inside the in-app
   page chrome — unlike body.login-page, the topbar stays visible above it. */
.page-center { display: flex; justify-content: center; padding-top: 24px; }

/* The one page header: Dashboard, Alarms, Briefings and Devices all use this. */
.page-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.page-head h1 {
  margin: 0; font-size: 22px;
  display: flex; align-items: center; gap: 10px;
}
.page-head h1 .badge {
  background: var(--indigo-50); color: var(--indigo-700);
  font-size: 12px; padding: 2px 10px; border-radius: 999px; font-weight: 600;
}

.crumbs { display: flex; gap: 6px; font-size: 13px; margin-bottom: 4px; }
.crumbs a { color: var(--ink-500); }
.crumbs a:hover { color: var(--indigo-600); }
.crumbs .current { color: var(--ink-700); font-weight: 500; }

.header-actions { display: flex; align-items: center; gap: 8px; }
.live-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--green-500);
  box-shadow: 0 0 0 4px rgba(34, 197, 94, .15);
  animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse {
  0%,100% { box-shadow: 0 0 0 4px rgba(34, 197, 94, .15); }
  50%     { box-shadow: 0 0 0 8px rgba(34, 197, 94, .05); }
}

/* =========================== Device list =========================== */
.device-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}
.device-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  padding: 18px 18px 14px;
  box-shadow: var(--shadow);
  transition: transform .15s, box-shadow .15s;
  display: block;
}
.device-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(15,23,42,.08);
}
.device-card-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--ink-500); margin-bottom: 10px;
}
.status-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--ink-200);
}
.status-dot.online  { background: var(--green-500); }
.status-dot.offline { background: var(--ink-400); }
.device-type {
  text-transform: uppercase; letter-spacing: .03em; font-weight: 600;
}
.device-name { font-size: 15px; font-weight: 600; word-break: break-all; }
.device-label { font-size: 12px; margin-top: 2px; word-break: break-all; }
.device-card-foot { font-size: 12px; margin-top: 14px; }
.empty { padding: 40px; text-align: center; color: var(--ink-500); }

/* =========================== Detail panels =========================== */
.panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 16px;
}
.panel {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.panel-head {
  padding: 14px 18px; border-bottom: 1px solid var(--ink-100);
}
.panel-head h2 {
  margin: 0; font-size: 13px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--indigo-700);
}
.panel-body {
  padding: 14px 18px 18px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px;
}
.metric { padding: 4px 0; }
.metric-label { font-size: 11px; color: var(--ink-500); margin-bottom: 4px; }
.metric-value {
  display: flex; align-items: baseline; gap: 4px; line-height: 1;
}
.metric-number {
  font-size: 22px; font-weight: 600; color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}
.metric-unit { font-size: 12px; color: var(--ink-500); font-weight: 500; }
.metric-ts { font-size: 10px; margin-top: 4px; }
.metric-number.flash { animation: flash .8s ease; }
@keyframes flash {
  0%   { background: var(--indigo-100); }
  100% { background: transparent; }
}

.gps-map {
  grid-column: 1 / -1;
  height: 220px; border-radius: 10px;
  background: var(--ink-100);
  margin-top: 6px;
}

/* =========================== Login =========================== */
body.login-page {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  background:
    radial-gradient(1000px 600px at 80% 10%, rgba(99,102,241,.15), transparent 60%),
    radial-gradient(900px 600px at 0% 100%, rgba(99,102,241,.10), transparent 60%),
    var(--bg);
}
.login-card {
  width: 100%; max-width: 380px;
  background: var(--card-bg);
  border-radius: 16px;
  padding: 36px 32px;
  box-shadow: 0 30px 60px rgba(15,23,42,.10);
}
.login-brand {
  display: flex; align-items: center; gap: 10px;
  font-weight: 700; font-size: 18px; margin-bottom: 24px;
}
.login-card h1 { margin: 0 0 4px; font-size: 22px; }
.login-card p  { margin: 0 0 24px; }
.login-card form { display: flex; flex-direction: column; gap: 14px; }
.login-card label { display: flex; flex-direction: column; gap: 4px; }
.login-card label span { font-size: 12px; color: var(--ink-500); }
.login-card input {
  padding: 10px 12px; border-radius: 8px;
  border: 1px solid var(--ink-200); background: var(--ink-50);
  font: inherit; color: var(--ink-900);
}
.login-card input:focus {
  outline: 2px solid var(--indigo-300); outline-offset: 1px;
  border-color: var(--indigo-500); background: #FFF;
}
/* Password field with show/hide eye toggle.
   One eye that opens (revealed) / closes (masked) — the icon's state and color
   ARE the status: closed + grey when hidden, open + indigo when shown. */
.pw-field { position: relative; display: flex; }
.pw-field input { width: 100%; padding-right: 44px; }
.pw-toggle {
  position: absolute; top: 50%; right: 6px;
  transform: translateY(-50%);
  display: grid; place-items: center;
  width: 32px; height: 32px; padding: 0;
  background: transparent; border: 0; cursor: pointer;
  color: var(--ink-400); border-radius: 8px;
  transition: color .15s ease, background .15s ease;
}
.pw-toggle:hover { color: var(--ink-700); background: var(--ink-100); }
.pw-toggle.is-on { color: var(--indigo-600); }
.pw-toggle.is-on:hover { background: var(--indigo-50); }
.pw-toggle:focus-visible {
  outline: 2px solid var(--indigo-300); outline-offset: 2px;
}
.pw-icon { width: 20px; height: 20px; overflow: visible; }
/* The two states share the icon box and cross-fade with a subtle scale. */
.pw-icon-open,
.pw-icon-closed {
  transition: opacity .18s ease, transform .18s ease;
  transform-origin: 12px 12px;
}
.pw-icon-open   { opacity: 0; transform: scale(.65); }
.pw-icon-closed { opacity: 1; transform: scale(1); }
.pw-toggle.is-on .pw-icon-open   { opacity: 1; transform: scale(1); }
.pw-toggle.is-on .pw-icon-closed { opacity: 0; transform: scale(.65); }
@media (prefers-reduced-motion: reduce) {
  .pw-icon-open, .pw-icon-closed { transition: opacity .01s; }
}
.primary {
  margin-top: 6px;
  background: var(--indigo-600); color: #FFF; font-weight: 600;
  padding: 11px 14px; border: 0; border-radius: 8px; cursor: pointer;
  transition: background .15s;
}
.primary:hover { background: var(--indigo-700); }
/* Login error banner. Dedicated class so it doesn't collide with the alarm-list
   `.alert` component defined further down. */
.login-alert {
  display: flex; align-items: center; gap: 8px;
  background: #FEF2F2; color: #B91C1C;
  border: 1px solid #FECACA; border-radius: 8px;
  padding: 10px 12px; font-size: 13px; font-weight: 500;
}
.login-alert svg { flex: 0 0 auto; width: 16px; height: 16px; stroke: #DC2626; }

/* Green confirmation banner (e.g. "Your password has been updated."). */
.form-ok {
  display: flex; align-items: center; gap: 8px;
  background: #ECFDF5; color: #047857;
  border: 1px solid #A7F3D0; border-radius: 8px;
  padding: 10px 12px; font-size: 13px; font-weight: 500;
  margin-bottom: 16px;
}
.form-ok svg { flex: 0 0 auto; width: 16px; height: 16px; stroke: #059669; }

/* Secondary links under the form (Forgot password? / Back to sign in). */
.login-aux { margin-top: 14px; text-align: center; font-size: 13px; }
.login-aux a { color: var(--indigo-600); text-decoration: none; font-weight: 500; }
.login-aux a:hover { text-decoration: underline; }

/* "or" divider separating the password form from the Google button. */
.login-divider {
  display: flex; align-items: center; gap: 12px;
  margin: 18px 0; color: var(--ink-400);
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
}
.login-divider::before,
.login-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--ink-200);
}

/* "Sign in with Google" button — white/bordered so it reads as the secondary
   option next to the indigo primary submit. */
.btn-google {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 11px 14px;
  border: 1px solid var(--ink-200); border-radius: 8px;
  background: #FFF; color: var(--ink-700);
  font: inherit; font-weight: 600; text-decoration: none; cursor: pointer;
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.btn-google:hover { background: var(--ink-50); border-color: var(--ink-400); }
.btn-google:focus-visible { outline: 2px solid var(--indigo-300); outline-offset: 2px; }
.btn-google-logo { flex: 0 0 auto; width: 18px; height: 18px; }

/* Anchor styled as the primary button (password-reset complete page). */
.login-cta { display: block; text-align: center; text-decoration: none; }

/* =========================== Fleet shell (combined list + detail) =========================== */
body.page-fleet .page { max-width: none; padding: 0; }
.fleet-shell {
  display: grid;
  /* Per-side column widths so the two sidebars collapse independently. */
  --col-devices: 340px;
  --col-alerts: 320px;
  grid-template-columns: var(--col-devices) minmax(0, 1fr) var(--col-alerts);
  height: calc(100vh - 64px);
  overflow: hidden;
  transition: grid-template-columns .25s ease;
}
@media (max-width: 1180px) {
  .fleet-shell { grid-template-columns: 340px minmax(0, 1fr); }
  .fleet-alerts-sidebar { grid-column: 1 / -1; border-left: none; border-top: 1px solid var(--ink-100); }
  /* Collapse is a desktop feature; hide the toggle where panels already reflow. */
  .sidebar-toggle { display: none; }
}
@media (max-width: 900px) {
  .fleet-shell { grid-template-columns: 1fr; }
}

/* --- Collapsible side panels (desktop only; below 1180px they already reflow) --- */
.sidebar-head-row {
  display: flex; align-items: flex-start;
  gap: 8px; margin-bottom: 12px;
}
/* Title fills the row, so the toggle sits at whichever end markup order puts it:
   right of "Devices" (left panel), left of "Active alerts" (right panel). */
.sidebar-head-row .sidebar-title { margin: 0; flex: 1; }
.sidebar-toggle {
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 28px; height: 28px; padding: 0;
  background: transparent; border: 1px solid var(--ink-200); border-radius: 8px;
  color: var(--ink-500); cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.sidebar-toggle:hover { background: var(--ink-100); color: var(--ink-700); }
.sidebar-toggle:focus-visible { outline: 2px solid var(--indigo-300); outline-offset: 2px; }
.sidebar-toggle svg { width: 16px; height: 16px; }
/* The slim rail shown in place of a collapsed sidebar. */
.sidebar-rail {
  display: none;                 /* shown only when that side is collapsed (desktop) */
  flex-direction: column; align-items: center; gap: 16px;
  padding-top: 16px; height: 100%;
}
.rail-label {
  writing-mode: vertical-rl; transform: rotate(180deg);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-500); white-space: nowrap;
}
@media (min-width: 1181px) {
  .fleet-shell.is-devices-collapsed { --col-devices: 48px; }
  .fleet-shell.is-alerts-collapsed  { --col-alerts: 48px; }
  .is-devices-collapsed .fleet-sidebar .sidebar-head,
  .is-devices-collapsed .fleet-sidebar .device-list { display: none; }
  .is-devices-collapsed .fleet-sidebar .sidebar-rail { display: flex; }
  .is-alerts-collapsed .fleet-alerts-sidebar .sidebar-head,
  .is-alerts-collapsed .fleet-alerts-sidebar .fleet-alerts-list { display: none; }
  .is-alerts-collapsed .fleet-alerts-sidebar .sidebar-rail { display: flex; }
}
.fleet-sidebar {
  background: #FFFFFF;
  border-right: 1px solid var(--ink-100);
  display: flex; flex-direction: column;
  min-height: 0;
}
.sidebar-head {
  padding: 18px 18px 12px;
  border-bottom: 1px solid var(--ink-100);
  position: sticky; top: 0;
  background: #FFFFFF;
  z-index: 2;
}
.sidebar-title {
  margin: 0 0 12px; font-size: 16px;
  display: flex; align-items: center; gap: 8px;
}
.sidebar-title .badge {
  background: var(--indigo-50); color: var(--indigo-700);
  font-size: 11px; padding: 2px 8px; border-radius: 999px;
  font-weight: 600;
}
.sidebar-search {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--ink-200);
  border-radius: 8px;
  background: var(--ink-50);
  font: inherit; color: var(--ink-900);
}
.sidebar-search:focus {
  outline: 2px solid var(--indigo-300); outline-offset: 1px;
  border-color: var(--indigo-500); background: #FFF;
}
.status-chips {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px;
}
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px;
  background: var(--ink-100); color: var(--ink-700);
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 11px; font-weight: 600; cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.chip .dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--ink-400);
}
.chip-ok      .dot { background: var(--green-500); }
.chip-unusual .dot { background: var(--amber-500); }
.chip-warn    .dot { background: var(--amber-500); } /* legacy freshness bucket, kept for old markup */
/* Solar-device filters: each dot matches its .sd-pill-* so the chip row and the Status
   column agree. */
.chip-problem   .dot { background: var(--red-500); }
.chip-notpolled .dot { background: var(--ink-400); }
.chip-deleted   .dot { background: var(--ink-400); }
.chip-stale   .dot { background: #94A3B8; }          /* legacy freshness bucket, kept for old markup */
.chip-down    .dot { background: var(--red-500); }
.chip:hover { background: var(--ink-200); }
/* Freshness legend ⓘ, sat at the end of the chip row (reuses .tile-info/.tile-tip). */
.status-chips-info { align-self: center; margin-left: 2px; }
.chip.active {
  background: var(--indigo-50);
  border-color: var(--indigo-500);
  color: var(--indigo-700);
}

.device-list {
  list-style: none; margin: 0; padding: 6px 8px 12px;
  overflow-y: auto; flex: 1;
}
.device-row {
  display: grid;
  grid-template-columns: 14px 1fr 12px;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  cursor: pointer;
  transition: background .12s;
}
.device-row:hover { background: var(--ink-100); }
.device-row.selected {
  background: var(--indigo-50);
  box-shadow: inset 3px 0 0 var(--indigo-600);
}
.device-row .status-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--ink-400);
  box-shadow: 0 0 0 3px transparent;
}
.status-dot.status-ok      { background: var(--green-500); box-shadow: 0 0 0 3px rgba(34,197,94,.12); }
.status-dot.status-unusual { background: var(--amber-500); box-shadow: 0 0 0 3px rgba(245,158,11,.12); }
.status-dot.status-warn    { background: var(--amber-500); box-shadow: 0 0 0 3px rgba(245,158,11,.12); } /* legacy */
.status-dot.status-stale   { background: #94A3B8; }                                                      /* legacy */
.status-dot.status-down    { background: var(--red-500); box-shadow: 0 0 0 3px rgba(239,68,68,.12); }
.device-row .row-name { font-size: 13px; font-weight: 600; word-break: break-all; }
.device-row .row-meta { font-size: 11px; margin-top: 2px; }
.device-row .row-chev { color: var(--ink-400); font-size: 16px; }

/* Filter state — hide rows that don't match the active chip / search. */
.device-list[data-active-status="ok"]      .device-row:not([data-status="ok"]),
.device-list[data-active-status="unusual"] .device-row:not([data-status="unusual"]),
.device-list[data-active-status="down"]    .device-row:not([data-status="down"]) { display: none; }
.device-row.hide-search { display: none; }

/* Disabled devices. Listed rather than hidden — disabling is reversible, so the fleet page
   is where you would go looking for one — but they carry no freshness status, so they must
   not read as a fault.

   TEXT ONLY, never the background: .device-row.selected owns `background` and its
   `inset 3px 0 0` left marker, so a row that is both selected and disabled composes into
   muted text on the indigo tint instead of the two fighting over the same property.
   `opacity` is avoided for the same reason plus contrast — and because a faded row reads
   as "loading", not "switched off". The dot needs no rule at all: `status-off` matches no
   `.status-dot.status-*` override, so it falls back to the neutral grey base. */
.device-row[data-status="off"] .row-name { color: var(--ink-500); font-weight: 500; }
.device-row[data-status="off"] .row-off { font-weight: 600; }
.status-chips-off { align-self: center; font-size: 11px; }

/* The solar detail header stacks its actions so the edit link sits under the pill; the
   ThingsBoard panel keeps them side by side. Scoped on the attribute the solar header
   already carries, so neither has to know about the other. */
.detail-head[data-source="solar"] .detail-actions {
  flex-direction: column; align-items: flex-end; gap: 8px;
}
/* Source filter (All / ThingsBoard / Solar Guardian) — same chips as the Home table. */
.device-list[data-active-source="tb"]    .device-row:not([data-source="tb"]),
.device-list[data-active-source="solar"] .device-row:not([data-source="solar"]) { display: none; }
.sidebar-source-filter { margin-left: 0; margin-top: 10px; flex-wrap: wrap; }

/* ---- Area groups (sidebar) ---- */
.device-group { margin-bottom: 2px; }
.device-group-head {
  width: 100%;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px;
  background: none; border: 0; cursor: pointer;
  font: inherit; color: var(--ink-600);
  text-transform: uppercase; letter-spacing: .06em;
  font-size: 11px; font-weight: 700;
  position: sticky; top: 0; z-index: 1;
  background: #FFFFFF;
}
.device-group-head:hover { color: var(--ink-900); }
.device-group-head .group-chev {
  font-size: 10px; color: var(--ink-400);
  transition: transform .12s;
}
.device-group.is-collapsed .group-chev { transform: rotate(-90deg); }
.device-group-head .group-name { flex: 1; text-align: left; word-break: break-word; }
.device-group-head .group-count {
  background: var(--ink-100); color: var(--ink-600);
  font-size: 10px; font-weight: 700; letter-spacing: 0;
  padding: 1px 7px; border-radius: 999px;
}
.device-group-head .group-down {
  background: rgba(239,68,68,.12); color: var(--red-500);
  font-size: 10px; font-weight: 700; letter-spacing: 0;
  padding: 1px 7px; border-radius: 999px;
}
.device-group-list { list-style: none; margin: 0; padding: 0; }
.device-group.is-collapsed .device-group-list { display: none; }
.device-group.group-empty { display: none; }

/* Third level: devices sharing one TCP306 gateway. Only rendered when a gateway holds
   more than one device — see dashboard.views._nest_networks.

   The whole group is indented and bracketed by one left rule, so the header reads as a
   child of the area rather than a sibling of it. The rule lives on the container, not on
   each row: .device-row.selected paints its own `inset 3px 0 0` marker on the left edge,
   and a per-row border would sit under it and fight for the same pixels. */
.device-network {
  list-style: none;
  margin: 2px 0 2px 12px;
  border-left: 2px solid var(--ink-100);
}
.device-network-head {
  width: 100%;
  display: flex; align-items: center; gap: 6px;
  padding: 5px 10px 5px 8px;
  background: none; border: 0; cursor: pointer;
  font: inherit; color: var(--ink-500);
  /* Deliberately smaller and not uppercased: the area headers own that treatment, and
     two levels shouting in the same voice is what made these look like siblings. */
  font-size: 10.5px; font-weight: 700; letter-spacing: .02em;
}
.device-network-head:hover { color: var(--ink-700); }
.device-network-head .group-chev {
  font-size: 9px; color: var(--ink-400);
  transition: transform .12s;
}
/* flex:1 so the count is pushed to the right edge, where the area header's own count
   sits — the group is indented but its right edge is not, so the two line up. */
.device-network-head .network-name {
  flex: 1; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* .device-group-head's pill is scoped to that selector, so this level needs its own. */
.device-network-head .group-count {
  background: var(--ink-100); color: var(--ink-500);
  font-size: 10px; font-weight: 700; letter-spacing: 0;
  padding: 1px 6px; border-radius: 999px;
}
.device-network.is-collapsed .group-chev { transform: rotate(-90deg); }
.device-network-list { list-style: none; margin: 0; padding: 0; }
.device-network.is-collapsed .device-network-list { display: none; }
.device-network.group-empty { display: none; }
.device-network-list .device-row { padding-left: 10px; }

/* =========================== Detail pane =========================== */
.fleet-detail {
  padding: 22px 28px 60px;
  overflow-y: auto;
  background: var(--bg);
  min-width: 0;
}
.detail-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; margin-bottom: 20px;
}
.detail-id {
  font-size: 11px; color: var(--indigo-700); font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
}
.detail-name { margin: 4px 0 4px; font-size: 22px; }
.detail-sub  { font-size: 12px; }
.detail-actions { display: flex; align-items: center; gap: 12px; }
.live-pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: rgba(34,197,94,.1); color: #15803D;
  padding: 5px 10px; border-radius: 999px; font-size: 12px; font-weight: 600;
}
.live-pill .live-dot { width: 8px; height: 8px; }
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border-radius: 8px; font: inherit; font-weight: 600;
  font-size: 13px; cursor: pointer; border: 1px solid transparent;
  text-decoration: none;
}
.btn-secondary {
  background: #FFFFFF; color: var(--indigo-700);
  border-color: var(--ink-200);
}
.btn-secondary:hover {
  background: var(--indigo-50); border-color: var(--indigo-500);
}

/* KPI strip on detail pane */
.kpi-strip {
  display: grid;
  /* auto-fit reflows by the panel's own width (not the viewport), so the strip
     wraps correctly even when it sits in the narrow fleet-detail column. */
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin: 0 0 14px;
}

.kpi-tile {
  position: relative;
  border-radius: 12px;
  padding: 12px 14px;
  border: 1px solid transparent;
  display: flex; flex-direction: column; gap: 6px;
  min-width: 0;
  transition: background .25s ease, border-color .25s ease, color .25s ease;
}
.kpi-tile-head { display: flex; align-items: center; gap: 8px; }
.kpi-tile-icon { width: 18px; height: 18px; flex: 0 0 18px; }
.kpi-tile-label {
  font-size: 10px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpi-tile-value {
  display: flex; align-items: baseline; gap: 6px;
  line-height: 1;
  min-width: 0;
}
.kpi-tile-number {
  font-size: 26px; font-weight: 700;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Battery-state shows a text status ("Charging"/"Discharging"), not a number,
   so use a smaller size that fits a narrow tile without spilling. */
.kpi-tile[data-slug="batt_state"] .kpi-tile-number { font-size: 18px; }
.kpi-tile-unit { font-size: 12px; font-weight: 600; opacity: .85; flex: 0 0 auto; }
.kpi-tile-sub { font-size: 11px; }
.kpi-tile-number.flash { animation: flash .8s ease; }

/* Tinted themes — soft pastel surface, accent text for icon + label + sub */
.kpi-tile-solar    { background: #FEF3C7; border-color: #FDE68A; color: #92400E; }
.kpi-tile-load     { background: #EDE9FE; border-color: #DDD6FE; color: #5B21B6; }
.kpi-tile-ok       { background: #ECFDF5; border-color: #BBF7D0; color: #047857; }
.kpi-tile-warn     { background: #FEF3C7; border-color: #FDE68A; color: #8A5A06; }
.kpi-tile-danger   { background: #FEE2E2; border-color: #FECACA; color: #B91C1C; }
.kpi-tile-thermal  { background: #DBEAFE; border-color: #BFDBFE; color: #1E40AF; }
.kpi-tile-neutral  { background: var(--ink-100); border-color: var(--ink-200); color: var(--ink-700); }

.kpi-tile .kpi-tile-sub.muted { color: inherit; opacity: .75; }

/* ---- Battery health card (device detail) ---- */
.battery-health {
  background: var(--card-bg);
  border: 1px solid var(--ink-200);
  border-left: 4px solid var(--ink-400);
  border-radius: 12px;
  padding: 14px 16px;
  margin: 0 0 14px;
}
.battery-health.band-ok       { border-left-color: #22C55E; }
.battery-health.band-watch    { border-left-color: #EAB308; }
.battery-health.band-warn     { border-left-color: #F59E0B; }
.battery-health.band-critical { border-left-color: #EF4444; }

.bh-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.bh-title { font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; margin: 0; color: var(--ink-700); }
.bh-status { font-size: 11px; font-weight: 700; padding: 2px 9px; border-radius: 999px; }
.bh-status-good { background: #ECFDF5; color: #047857; }
.bh-status-attention { background: #FEE2E2; color: #B91C1C; }
/* Nothing measurable — deliberately neutral, not a health verdict. */
.bh-status-none { background: #F1F5F9; color: #475569; }
.bh-asof { font-size: 11px; margin-left: auto; }

.bh-body { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 14px; align-items: end; }
.bh-metric { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.bh-metric-label { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-500); }
.bh-metric-value { font-size: 22px; font-weight: 700; line-height: 1.1; color: var(--ink-900); font-variant-numeric: tabular-nums; }
.bh-metric-value.band-ok       { color: #047857; }
.bh-metric-value.band-watch    { color: #A16207; }
.bh-metric-value.band-warn     { color: #8A5A06; }
.bh-metric-value.band-critical { color: #B91C1C; }
.bh-unit { font-size: 13px; font-weight: 600; opacity: .8; }
.bh-metric-sub { font-size: 11px; }
.bh-na { color: var(--ink-400); font-weight: 600; }
/* "not measurable" is a phrase, not a figure — step it down from the metric size. */
.bh-na-sm { font-size: 15px; letter-spacing: -0.01em; }
.bh-yes { color: #047857; }
.bh-no  { color: #B91C1C; }

.bh-spark { display: flex; flex-direction: column; gap: 3px; color: #6366F1; min-width: 0; }
.bh-spark-svg { width: 100%; height: 30px; display: block; }
.bh-spark-label { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; }

.bh-reasons { list-style: none; margin: 12px 0 0; padding: 10px 0 0; border-top: 1px solid var(--ink-100); display: flex; flex-direction: column; gap: 5px; }
.bh-reason { font-size: 12px; color: var(--ink-700); padding-left: 16px; position: relative; }
.bh-reason::before { content: "!"; position: absolute; left: 2px; color: #B91C1C; font-weight: 800; }

/* ---- Battery-health list page ---- */
.bh-list-flagged { margin-left: 10px; font-size: 13px; font-weight: 700; color: #B91C1C; background: #FEE2E2; border-radius: 999px; padding: 3px 10px; }
.bh-list-note { margin: 0 0 14px; font-size: 12px; max-width: 720px; }
.bh-table-wrap { overflow-x: auto; background: var(--card-bg); border: 1px solid var(--ink-200); border-radius: 12px; box-shadow: var(--shadow); }
.bh-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.bh-table th { text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-500); font-weight: 700; padding: 10px 12px; border-bottom: 1px solid var(--ink-200); white-space: nowrap; }
.bh-table td { padding: 10px 12px; border-bottom: 1px solid var(--ink-100); vertical-align: top; }
.bh-table tbody tr:last-child td, .bh-table tr:last-child td { border-bottom: none; }
.bh-table .num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.bh-row td:first-child { border-left: 3px solid transparent; }
.bh-row.is-attention.band-critical td:first-child { border-left-color: #EF4444; }
.bh-row.is-attention.band-warn     td:first-child { border-left-color: #F59E0B; }
.bh-row.is-attention.band-watch    td:first-child { border-left-color: #EAB308; }
.bh-cell-name a { font-weight: 600; color: var(--ink-900); text-decoration: none; }
.bh-cell-name a:hover { text-decoration: underline; }
/* Marks a row as coming from the solar fleet, whose assessment method differs. */
.bh-src-tag { font-size: 10px; font-weight: 700; letter-spacing: .02em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 999px; background: #FEF3C7; color: #92400E; margin-left: 6px; }
.bh-badge { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; white-space: nowrap; }
.bh-badge-good { background: #ECFDF5; color: #047857; }
.bh-badge-attention { background: #FEE2E2; color: #B91C1C; }
/* Nothing measurable — deliberately neutral, not a health verdict. */
.bh-badge-none { background: #F1F5F9; color: #475569; }
/* Phrase in a numeric column: shrink so it doesn't wrap the SoH cell. */
.bh-nm { font-size: 11px; white-space: nowrap; font-weight: 600; }
.bh-cell-notes { color: var(--ink-700); max-width: 340px; }
.bh-note-line { margin: 1px 0; }

/* Alarm-tile severity dots */
.kpi-alarms-breakdown { display: flex; align-items: center; gap: 10px; font-size: 11px; flex-wrap: wrap; }
.kpi-sev { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; color: var(--ink-900); }
.kpi-sev .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.kpi-sev-critical      .dot { background: #EF4444; }
.kpi-sev-major         .dot { background: #F59E0B; }
.kpi-sev-minor         .dot { background: #EAB308; }
.kpi-sev-warning       .dot { background: #3B82F6; }
.kpi-sev-indeterminate .dot { background: #94A3B8; }

.detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 16px;
  align-items: start;
}
@media (max-width: 1180px) {
  .detail-grid { grid-template-columns: 1fr; }
}

/* =========================== Alerts feed (sidebar on detail + history) =========================== */
.alerts-feed, .alerts-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  position: sticky; top: 16px;
}
.alerts-head, .card-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--ink-100);
}
.alerts-head h2, .card-head h2 {
  margin: 0; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--indigo-700);
}
.alerts-head .badge, .card-head .badge {
  background: var(--ink-100); color: var(--ink-700);
  font-size: 11px; padding: 2px 8px; border-radius: 999px; font-weight: 600;
}
.alerts-list {
  list-style: none; margin: 0; padding: 6px;
  max-height: 480px; overflow-y: auto;
}
.alert {
  background: transparent !important;
  color: var(--ink-900) !important;
  display: grid;
  grid-template-columns: 10px 1fr;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 13px;
  transition: background .12s, opacity .25s;
}
.alert:hover { background: var(--ink-100) !important; }
.alert.is-cleared { opacity: .45; }
.alert-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--ink-400);
  margin-top: 5px;
}
/* Severity dot colors — must match the "Alarms by severity" chart palette
   (home.js SEV_COLOR / .dot-* / .kpi-sev-* / .summary-sev-*).
   NOT the map pins: those encode freshness, never severity. */
.alert-critical      .alert-dot { background: #EF4444; box-shadow: 0 0 0 3px rgba(239,68,68,.12); }
.alert-major         .alert-dot { background: #F59E0B; box-shadow: 0 0 0 3px rgba(245,158,11,.12); }
.alert-minor         .alert-dot { background: #EAB308; box-shadow: 0 0 0 3px rgba(234,179,8,.12); }
.alert-warning       .alert-dot { background: #3B82F6; box-shadow: 0 0 0 3px rgba(59,130,246,.12); }
.alert-indeterminate .alert-dot { background: #94A3B8; }
.alert-type {
  font-weight: 600;
}
.alert-meta { font-size: 11px; margin-top: 2px; }
.alert-sev {
  text-transform: uppercase; font-weight: 600; letter-spacing: .03em;
}
.alerts-empty {
  padding: 14px 16px; text-align: center; font-size: 13px;
}
/* "All clear" state — green, like the map's floating device summary
   (.summary-card-sev-clear). Shared by the Devices sidebar and the header bell. */
.alerts-empty-clear { color: #16A34A; font-weight: 600; }
.alert.is-new { animation: alertSlideIn .35s ease; }
@keyframes alertSlideIn {
  0%   { background: var(--indigo-50) !important; transform: translateX(-8px); opacity: 0; }
  100% { background: transparent !important; transform: translateX(0); opacity: 1; }
}

/* =========================== History page =========================== */
.history-header {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; margin-bottom: 22px;
}
.crumb-back {
  display: inline-block; font-size: 12px; color: var(--ink-500); margin-bottom: 6px;
}
.crumb-back:hover { color: var(--indigo-600); }
.history-name { margin: 0 0 4px; font-size: 26px; }
.history-sub  { font-size: 13px; margin: 0; }
.history-status { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.status-badge {
  font-size: 11px; font-weight: 700; padding: 4px 10px; border-radius: 999px;
  text-transform: uppercase; letter-spacing: .04em;
}
.status-badge.status-ok      { background: rgba(34,197,94,.12); color: #15803D; }
.status-badge.status-unusual { background: rgba(245,158,11,.15); color: #B45309; }
.status-badge.status-warn    { background: rgba(245,158,11,.15); color: #B45309; } /* legacy */
.status-badge.status-stale   { background: rgba(148,163,184,.2); color: #475569; } /* legacy */
.status-badge.status-down    { background: rgba(239,68,68,.12); color: #B91C1C; }

.range-toggle {
  display: inline-flex; background: #FFF; border: 1px solid var(--ink-200);
  border-radius: 10px; padding: 2px; box-shadow: var(--shadow);
}
.range-btn {
  border: 0; background: transparent; color: var(--ink-500); font-weight: 600;
  font: inherit; font-size: 12px; padding: 7px 16px; border-radius: 8px; cursor: pointer;
}
.range-btn:hover { color: var(--ink-900); }
.range-btn.active {
  background: var(--indigo-600); color: #FFF;
}

.kpi-cards {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin-bottom: 18px;
}
.kpi-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  padding: 14px 16px 6px;
  box-shadow: var(--shadow);
  position: relative;
}
.kpi-card header {
  display: flex; justify-content: space-between; align-items: center;
}
.kpi-card-title {
  font-size: 12px; color: var(--ink-500); font-weight: 600;
}
.kpi-card-pill {
  font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 999px;
  text-transform: uppercase; letter-spacing: .03em;
}
.pill-ok   { background: rgba(34,197,94,.12);  color: #15803D; }
.pill-warn { background: rgba(245,158,11,.15); color: #B45309; }
.pill-down { background: rgba(239,68,68,.12);  color: #B91C1C; }
.kpi-card-value {
  display: flex; align-items: baseline; gap: 4px; margin-top: 8px;
}
.kpi-card-number {
  font-size: 30px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--ink-900);
}
.kpi-card-number.flash { animation: flash .8s ease; }
.kpi-card-unit { font-size: 13px; color: var(--ink-500); font-weight: 500; }
.kpi-spark {
  display: block; width: 100% !important; height: 42px !important;
  margin-top: 8px;
}

.history-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 16px;
  margin-bottom: 18px;
}
@media (max-width: 1180px) {
  .history-main { grid-template-columns: 1fr; }
  .kpi-cards    { grid-template-columns: repeat(2, 1fr); }
}

.chart-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px 18px 12px;
}
.chart-head {
  display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;
}
.chart-head h2 { margin: 0; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--indigo-700); }
.chart-head-range { font-size: 11px; font-weight: 500; margin-left: 4px;
  text-transform: none; letter-spacing: 0; color: var(--ink-500); }
.chart-legend {
  display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: var(--ink-700);
}
.chart-legend .lg { display: inline-flex; align-items: center; gap: 6px; }
.chart-legend .lg-swatch { width: 12px; height: 3px; border-radius: 2px; display: inline-block; }
.chart-canvas-wrap { position: relative; height: 380px; }
.chart-canvas-wrap canvas { width: 100% !important; height: 100% !important; }

.history-bottom {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 16px;
}
@media (max-width: 1180px) {
  .history-bottom { grid-template-columns: 1fr; }
}
.cells-card, .energy-card, .location-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.cells-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
  padding: 16px;
}
.cell {
  text-align: center; padding: 10px 6px;
  background: var(--ink-50); border-radius: 10px;
}
.cell-v {
  font-size: 18px; font-weight: 700; color: var(--ink-900); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.cell-l { font-size: 10px; color: var(--ink-500); margin-top: 4px; font-weight: 600; }
.energy-rows { padding: 12px 16px 16px; display: flex; flex-direction: column; gap: 10px; }
.energy-row { display: flex; justify-content: space-between; align-items: baseline; }
.energy-label { font-size: 12px; }
.energy-value { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.energy-value em { font-style: normal; font-size: 12px; color: var(--ink-500); font-weight: 500; }
.location-map { height: 180px; }

/* ===== Per-category drill-down + single-chart popout ===== */

/* Make panel headers a flex row so the drill link sits to the right. */
.panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.panel-drill {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  color: var(--indigo-700); padding: 4px 8px; border-radius: 6px;
  background: transparent; transition: background .15s;
}
.panel-drill:hover { background: var(--indigo-50); color: var(--indigo-700); }

/* History page Power chart + category chart cards: legend + popout to the right. */
.chart-head-right {
  display: flex; align-items: center; gap: 12px;
}
.chart-popout {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 6px;
  font-size: 14px; color: var(--ink-500);
  background: transparent; transition: background .15s, color .15s;
}
.chart-popout:hover { background: var(--indigo-50); color: var(--indigo-700); }

/* ----- Category page ----- */
.category-header {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 24px; margin-bottom: 20px;
}
.category-name {
  margin: 8px 0 0; font-size: 22px; font-weight: 700; color: var(--ink-900);
}
.category-name .muted { font-weight: 500; }
.category-sub { margin: 4px 0 0; font-size: 13px; }
.category-actions { display: flex; gap: 12px; align-items: center; }
.category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(540px, 1fr));
  gap: 16px;
}
@media (max-width: 900px) {
  .category-grid { grid-template-columns: 1fr; }
  .category-header { flex-direction: column; align-items: stretch; }
}
/* Slightly shorter chart on the category grid so multiple fit per viewport. */
.page-category .chart-canvas-wrap { height: 300px; }

/* ----- Single-chart popout page -----
 * Lock the page to the viewport so the canvas always shows its x-axis.
 * Body becomes a flex column; main fills the remaining viewport; canvas
 * needs `min-height: 0` so flex: 1 can actually shrink it (default
 * `min-height: auto` would otherwise let it overflow). */
body.page-chart {
  height: 100vh;
  display: flex; flex-direction: column;
  overflow: hidden;
}
.page-chart main.page {
  flex: 1; min-height: 0;
  max-width: none; width: 100%; margin: 0; padding: 0;
  display: flex; flex-direction: column;
}
.chart-page {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  padding: 14px 24px 18px;
  gap: 10px;
  background: var(--card-bg);
}
.chart-page-head {
  flex: 0 0 auto;
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 16px; flex-wrap: wrap;
}
.chart-page-title {
  margin: 6px 0 0; font-size: 22px; font-weight: 700; color: var(--ink-900);
}
.chart-page-sub { margin: 4px 0 0; font-size: 13px; }
.chart-page-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.chart-page-legend {
  flex: 0 0 auto;
  display: flex; flex-wrap: wrap; gap: 16px; font-size: 13px; color: var(--ink-700);
  padding: 0 4px;
}
.chart-page-legend .lg { display: inline-flex; align-items: center; gap: 6px; }
.chart-page-legend .lg-swatch { width: 18px; height: 3px; border-radius: 2px; display: inline-block; }
.chart-page-canvas {
  flex: 1 1 auto; min-height: 0; position: relative;
}
.chart-page-canvas canvas { width: 100% !important; height: 100% !important; }
.fullscreen-toggle { font-size: 16px; padding: 6px 10px; }

/* ----- Series picker popover ----- */
.series-picker-wrap { position: relative; }
.series-picker {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 50;
  width: 480px; max-width: calc(100vw - 24px); max-height: 70vh;
  background: var(--card-bg); border: 1px solid var(--indigo-100);
  border-radius: 10px; box-shadow: 0 14px 32px rgba(15, 23, 42, .14);
  display: flex; flex-direction: column;
}
/* Restore the [hidden] semantics. `display: flex` above (and the same default
 * applies to dialog/details elements) outranks the user-agent
 * `[hidden] { display: none }` rule because of equal specificity, so the
 * picker would render even when the JS set picker.hidden = true. */
.series-picker[hidden] { display: none; }
.series-picker-search {
  padding: 10px; border-bottom: 1px solid var(--ink-100);
  position: sticky; top: 0; background: var(--card-bg);
}
.series-picker-search input {
  width: 100%; padding: 8px 10px; border-radius: 6px;
  border: 1px solid var(--ink-200); background: var(--ink-50);
  font: inherit; color: var(--ink-900);
}
.series-picker-body { overflow-y: auto; padding: 6px 12px; flex: 1; min-height: 0; }
.series-group { padding: 8px 0; }
.series-group + .series-group { border-top: 1px solid var(--ink-100); }
.series-group-title {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--ink-500); margin-bottom: 6px;
}
.series-item {
  display: grid; grid-template-columns: 18px 1fr auto;
  align-items: center; gap: 10px; padding: 6px 6px;
  border-radius: 6px; font-size: 13px; cursor: pointer;
}
.series-item:hover { background: var(--indigo-50); }
.series-item input { margin: 0; accent-color: var(--indigo-600); }
.series-item-text {
  display: flex; flex-direction: column; gap: 1px; min-width: 0;
}
.series-item-label {
  color: var(--ink-900); font-weight: 500; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.series-item-sub {
  font-size: 11px; font-variant-numeric: tabular-nums; line-height: 1.2;
}
.series-item-unit { font-size: 11px; white-space: nowrap; }
.series-picker-foot {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 10px; border-top: 1px solid var(--ink-100);
}
.series-picker-foot .primary { padding: 6px 14px; border-radius: 6px; font-size: 13px; cursor: pointer; border: 0; }
.series-picker-foot .btn-secondary { padding: 6px 14px; border-radius: 6px; font-size: 13px; cursor: pointer; }

/* Multi-unit chart stack (popout only). JS picks single / dual / stacked. */
.chart-stack {
  display: flex; flex-direction: column;
  gap: 8px;
  min-height: 0;
}
.chart-stack > .chart-stack-row {
  flex: 1 1 0;
  min-height: 0;
  position: relative;
}
.chart-stack > .chart-stack-row > canvas {
  width: 100% !important;
  height: 100% !important;
}
.chart-mode-chip {
  display: inline-block;
  padding: 2px 8px;
  margin-left: 8px;
  border-radius: 999px;
  background: var(--indigo-50);
  color: var(--indigo-700);
  font-size: 12px; font-weight: 500;
  vertical-align: middle;
}
.chart-mode-chip-block {
  background: #FEF3C7;   /* amber-100 */
  color:      #92400E;   /* amber-900 */
}

/* Block (fixed-window) picker — sits next to the range toggle. */
.block-picker-wrap { position: relative; display: inline-block; }
.block-toggle.active {
  background: #FEF3C7;
  color: #92400E;
  border-color: #FDE68A;
}
.block-picker {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  min-width: 280px;
  padding: 12px;
  background: var(--bg, #fff);
  border: 1px solid var(--border, #E5E7EB);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.block-picker[hidden] { display: none; }
.block-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--muted, #6B7280);
}
.block-field input[type="datetime-local"] {
  padding: 6px 8px;
  border: 1px solid var(--border, #E5E7EB);
  border-radius: 4px;
  font-size: 13px;
}
.block-picker-hint {
  font-size: 11px;
  margin-top: -2px;
}
.block-picker-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}

/* =========================== Fleet alerts sidebar (right side of Home) =========================== */
.fleet-alerts-sidebar {
  background: #FFFFFF;
  border-left: 1px solid var(--ink-100);
  display: flex; flex-direction: column;
  min-height: 0;
}
.fleet-alerts-sidebar .sidebar-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
}
.sidebar-link {
  font-size: 12px;
  color: var(--indigo-700);
  text-decoration: none;
  white-space: nowrap;
}
.sidebar-link:hover { text-decoration: underline; }
.fleet-alerts-list {
  list-style: none; margin: 0; padding: 8px;
  overflow-y: auto;
  flex: 1 1 auto;
}
.fleet-alerts-list .alert { background: transparent !important; }
.fleet-alerts-list .alert:hover { background: var(--ink-100) !important; }
.alert-cleared { opacity: .55; }
.alert-device {
  text-transform: none;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink-700);
}

/* Make the whole alert row a clickable anchor without breaking its grid layout. */
.alert-link {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  gap: 10px;
  color: inherit; text-decoration: none;
}
/* Fallback for browsers without grid-template-columns: subgrid. */
@supports not (grid-template-columns: subgrid) {
  .alert-link {
    display: grid;
    grid-template-columns: 10px 1fr;
    align-items: flex-start;
  }
}
/* On the standalone Alerts page, rows use 5 columns instead of the sidebar's 2. */
.alerts-table .alert.alert-row { grid-template-columns: 110px minmax(140px, 1.4fr) minmax(140px, 1.2fr) 110px 140px; }
.alerts-table .alert-link      { grid-template-columns: 110px minmax(140px, 1.4fr) minmax(140px, 1.2fr) 110px 140px; }

/* =========================== Devices page (standalone list) =========================== */
.devices-page-controls {
  display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap; justify-content: flex-end;
}
.devices-page-controls .sidebar-search { width: 240px; }

.device-table {
  list-style: none; margin: 0; padding: 0;
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.device-table-head,
.device-table-link {
  display: grid;
  grid-template-columns: minmax(200px, 1.4fr) minmax(120px, 1fr) minmax(120px, 1fr) 110px 130px;
  align-items: center;
  gap: 12px;
}
.device-table-head {
  padding: 10px 14px;
  font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-500);
  background: var(--ink-50);
  border-bottom: 1px solid var(--ink-100);
}
/* Row is a single-child container; its <a> child carries the column grid. */
.device-table-row { display: block; border-bottom: 1px solid var(--ink-100); }
.device-table-row:last-child { border-bottom: none; }
.device-table-link {
  padding: 12px 14px;
  color: inherit; text-decoration: none;
}
.device-table-link:hover { background: var(--ink-100); }
.device-table .cell.name { font-weight: 600; display: inline-flex; align-items: center; gap: 8px; }
.device-table .cell.type { font-size: 13px; color: var(--ink-700); }
.device-table .cell.label { font-size: 13px; }
.device-table .cell.last { font-size: 12px; color: var(--ink-700); font-variant-numeric: tabular-nums; }
.device-table-row.hide-search { display: none; }
.device-table[data-active-status="ok"]      .device-table-row:not([data-status="ok"]),
.device-table[data-active-status="unusual"] .device-table-row:not([data-status="unusual"]),
.device-table[data-active-status="down"]    .device-table-row:not([data-status="down"]) { display: none; }

/* =========================== Alerts page (standalone table) =========================== */
/* Alarms and Briefings both render into this shell: title → filter bar → table →
   pager. They share it so the two pages compose identically. */
.alerts-page { display: flex; flex-direction: column; }
.alerts-table {
  list-style: none; margin: 0; padding: 0;
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.alerts-table-head {
  display: grid;
  grid-template-columns: 110px minmax(140px, 1.4fr) minmax(140px, 1.2fr) 110px 140px;
  gap: 12px;
  padding: 0 14px;
  min-height: 36px;
  align-items: center;
  font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-500);
  background: var(--ink-50);
  border-bottom: 1px solid var(--ink-100);
}
.alerts-table .alert.alert-row {
  border-bottom: 1px solid var(--ink-100);
  padding: 0;
  border-radius: 0;
  margin: 0;
}
.alerts-table .alert.alert-row:last-child { border-bottom: none; }
/* Row height is DECLARED, not emergent. Briefings' cells hold pill badges (padding
   + border) while the Alarms cells are plain text, so a content-driven row height
   drifts between the two pages. min-height, not height, so a long device name still
   wraps and grows the row rather than overflowing it. */
.alerts-table .alert.alert-row .alert-link {
  padding: 0 14px;
  min-height: 48px;
  align-items: center;
}
.alerts-table .alert.alert-row .alert-link:hover { background: var(--ink-100); }
.alerts-table .cell.sev { font-weight: 600; display: inline-flex; align-items: center; gap: 8px; text-transform: uppercase; font-size: 12px; }
.alerts-table .cell.type    { font-weight: 600; }
.alerts-table .cell.device  { color: var(--ink-700); }
/* Fixed-format timestamps must never wrap: a wrapped cell grows the row past its
   declared min-height, which is exactly how the two tables drift out of step. */
.alerts-table .cell.started { font-size: 12px; color: var(--ink-700); font-variant-numeric: tabular-nums; white-space: nowrap; }
.alerts-table .cell.state   { font-size: 12px; text-transform: uppercase; color: var(--ink-500); letter-spacing: .04em; }

/* ---- Alarms filter bar (multi-select chips + device search) ----
   Chips are <a> links whose href the view precomputes, so filtering needs no JS. */
.filter-bar {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 12px 14px;
  margin-bottom: 14px;
  display: flex; flex-direction: column; gap: 8px;
}
.filter-bar-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.filter-search  { display: flex; align-items: center; gap: 8px; }
.filter-search input[type="search"] {
  font: inherit; font-size: 13px; padding: 6px 10px; min-width: 240px;
  border: 1px solid var(--ink-200); border-radius: 8px;
  background: var(--card-bg); color: var(--ink-900);
}
.filter-clear {
  font-size: 12px; font-weight: 600; color: var(--indigo-700);
  text-decoration: none; padding: 4px 8px; border-radius: 6px;
}
.filter-clear:hover { background: var(--ink-100); }

/* Label column is fixed so the three chip rows line up into a grid. */
.filter-group { display: flex; align-items: baseline; gap: 12px; }
.filter-group-label {
  flex: 0 0 110px;
  font-size: 10px; font-weight: 700; color: var(--ink-500);
  text-transform: uppercase; letter-spacing: .06em;
}
.filter-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.filter-chips .chip { text-decoration: none; }
/* Status/ack chips carry no colour of their own — only severity is colour-coded,
   and it uses the canonical severity palette. */
.filter-chips .chip-active .dot        { background: var(--red-500); }
.filter-chips .chip-cleared .dot       { background: var(--ink-400); }
.filter-chips .chip-unacked .dot       { background: var(--amber-500); }
.filter-chips .chip-acked .dot         { background: var(--green-500); }
.filter-chips .chip-critical .dot      { background: #EF4444; }
.filter-chips .chip-major .dot         { background: #F59E0B; }
.filter-chips .chip-minor .dot         { background: #EAB308; }
.filter-chips .chip-warning .dot       { background: #3B82F6; }
.filter-chips .chip-indeterminate .dot { background: #94A3B8; }
/* Briefings chips. Dots take the same hues as the .kind-badge / .brief-status pills
   they filter, so the chip and the row it selects read as the same thing. */
.filter-chips .chip-intraday .dot      { background: #1f4f99; }
.filter-chips .chip-daily .dot         { background: #3f6249; }
.filter-chips .chip-ok .dot            { background: var(--green-500); }
.filter-chips .chip-error .dot         { background: var(--red-500); }
.filter-chips .chip-no_data .dot       { background: var(--ink-400); }

/* ---- Sortable column headers ----
   Used by both Alarms and Briefings; .is-sortable-row is the opt-in, so a head row
   with plain <span> cells gets no carets. The caret is driven purely by aria-sort,
   the same trick as the fleet table's th[aria-sort]::after. */
.alerts-table-head.is-sortable-row .cell.is-sortable {
  color: inherit; text-decoration: none; cursor: pointer;
  user-select: none; display: inline-flex; align-items: center;
}
.alerts-table-head.is-sortable-row .cell.is-sortable:hover { color: var(--ink-700); }
.alerts-table-head.is-sortable-row .cell.is-sortable::after {
  content: "↕"; opacity: .3; margin-left: 4px; font-size: 10px;
}
.alerts-table-head.is-sortable-row .cell[aria-sort="ascending"]::after  { content: "↑"; opacity: 1; }
.alerts-table-head.is-sortable-row .cell[aria-sort="descending"]::after { content: "↓"; opacity: 1; }
.alerts-table-head.is-sortable-row .cell[aria-sort] { color: var(--ink-900); }

/* The .panels container had an unused alongside-aside; the helper class is just
 * a hook for any future override. The base `.panels` rule (grid + gap: 16px) is
 * what produces the spacing between Battery / Solar Panel / Grid cards. */
.panels-fullwidth { width: 100%; }

/* =========================== Alarm 4-state pill =========================== */
/* Lifecycle state — see https://thingsboard.io/docs/user-guide/alarms/#alarm-states */
.state-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px; font-weight: 700;
  letter-spacing: .04em;
  border: 1px solid transparent;
}
.state-active_unack   { background: #fde7e9; color: #a01024; border-color: #f3b7bf; }
.state-active_ack     { background: #fef0d6; color: #8a5a06; border-color: #f6d291; }
.state-cleared_unack  { background: #e2eefc; color: #1f4f99; border-color: #b6d3f3; }
.state-cleared_ack    { background: #e8efe9; color: #3f6249; border-color: #c2d4c8; }

/* =========================== Alerts pager =========================== */
.alerts-pager {
  display: flex; align-items: center; justify-content: center;
  gap: 14px;
  margin: 16px 0 0;
  font-size: 13px;
}
.alerts-pager .pager-link {
  text-decoration: none; color: var(--indigo-700);
  padding: 6px 12px; border-radius: 8px;
  border: 1px solid var(--ink-100);
}
.alerts-pager .pager-link:hover { background: var(--ink-100); }
.alerts-pager .pager-link.disabled { color: var(--ink-500); border-color: var(--ink-100); cursor: default; }

/* Numbered pages. The alarms list runs to a dozen-plus pages, so prev/next alone
   would mean a click per page to reach the end. Server-rendered links, like the chips. */
.alerts-pager .pager-nums { display: flex; align-items: center; gap: 4px; }
.alerts-pager .pager-num {
  min-width: 32px; padding: 6px 8px;
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none; color: var(--ink-700);
  border: 1px solid transparent; border-radius: 8px;
  font-variant-numeric: tabular-nums;
}
.alerts-pager .pager-num:hover { background: var(--ink-100); }
.alerts-pager .pager-num.is-current {
  background: var(--indigo-50); border-color: var(--indigo-500);
  color: var(--indigo-700); font-weight: 700;
}
.alerts-pager .pager-gap { color: var(--ink-400); padding: 0 2px; }

/* =========================== Briefings list + detail =========================== */
/* Sits inside .filter-search (which supplies the flex row), so this only styles
   the date controls — the shell is shared with the Alarms filter bar. */
.brief-filter { font-size: 12px; color: var(--ink-500); }
.brief-filter label { display: inline-flex; align-items: center; gap: 4px; }
.brief-filter input[type="date"] {
  font: inherit; font-size: 13px; color: var(--ink-900);
  padding: 6px 10px; border: 1px solid var(--ink-200); border-radius: 8px; background: var(--card-bg);
}

/* Briefings table — reuses the alarms-table shell and mirrors its column rhythm
   (leading badge = 110px like Severity; trailing time column = 150px), so the two
   list pages read as siblings. */
.briefs-table .alerts-table-head,
.briefs-table .alert.alert-row,
.briefs-table .alert-link {
  /* Trailing column is 170px, not 150px: "Jul 13, 2026 01:18 WITA" wrapped to two
     lines at 150px, which grew the row past its declared height and desynchronised
     it from the alarms table. */
  grid-template-columns: 110px minmax(160px, 1.6fr) 90px 120px 170px;
}
.briefs-table .cell.window    { font-variant-numeric: tabular-nums; color: var(--ink-700); }
.briefs-table .cell.devices   { font-variant-numeric: tabular-nums; }
/* Match the alarms "Started" cell typography exactly, nowrap included. */
.briefs-table .cell.generated { font-size: 12px; color: var(--ink-700); font-variant-numeric: tabular-nums; white-space: nowrap; }

.kind-badge {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  border: 1px solid transparent;
}
.kind-daily    { background: #e8efe9; color: #3f6249; border-color: #c2d4c8; }
.kind-intraday { background: #e2eefc; color: #1f4f99; border-color: #b6d3f3; }

.brief-status {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; border: 1px solid transparent;
}
.status-ok      { background: #e8efe9; color: #3f6249; border-color: #c2d4c8; }
.status-error   { background: #fde7e9; color: #a01024; border-color: #f3b7bf; }
.status-no_data { background: var(--ink-100); color: var(--ink-500); border-color: var(--ink-200); }

/* Briefing detail — the report runs full width (a 36-device analysis list does not
   fit a sidebar-narrowed column), with the Details strip below it. */
.brief-detail-page { max-width: 1080px; }
.brief-back { margin: 0 0 12px; font-size: 13px; }
.brief-back a { color: var(--indigo-700); text-decoration: none; font-weight: 600; }
.brief-back a:hover { text-decoration: underline; }
/* Give the summary a touch more presence on the detail page. */
.brief-detail .ai-brief-text { font-size: 14px; line-height: 1.7; }

/* ---- report sections ---- */
.brief-section { margin-top: 20px; }
.brief-section:first-of-type { margin-top: 12px; }
.brief-section-title {
  margin: 0 0 10px; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-500);
  padding-bottom: 6px; border-bottom: 1px solid var(--ink-100);
}
.brief-section .ai-brief-text { margin: 0 0 10px; }

/* ---- per-device analysis ---- */
.brief-devices { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.brief-device {
  border: 1px solid var(--ink-100); border-radius: 10px;
  padding: 10px 12px; background: var(--ink-50);
}
/* Devices needing attention get the rail treatment so they read first. */
.brief-device.has-issues { background: #fff; border-left: 3px solid var(--amber-500); }
.brief-device-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 7px;
}
.brief-device-head .status-dot { display: inline-block; flex: 0 0 8px; }
.brief-device-name { font-size: 13px; color: var(--ink-900); }
.brief-device-status {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px; background: var(--ink-100); color: var(--ink-700);
}
.brief-device-status.status-ok      { background: rgba(34,197,94,.12);  color: #15803D; }
.brief-device-status.status-unusual { background: rgba(245,158,11,.14); color: #92400E; }
/* warn/stale kept so historical briefs (stored with the old categories) still render. */
.brief-device-status.status-warn    { background: rgba(245,158,11,.14); color: #92400E; }
.brief-device-status.status-stale   { background: rgba(148,163,184,.18); color: #475569; }
.brief-device-status.status-down    { background: rgba(239,68,68,.12);  color: #B91C1C; }
.brief-device-alarms {
  font-size: 11px; font-weight: 600; padding: 2px 7px; border-radius: 999px;
  background: rgba(239,68,68,.10); color: #B91C1C;
}
.brief-device-silent { font-size: 11px; color: var(--ink-500); }

.brief-vitals { display: flex; flex-wrap: wrap; gap: 6px; }
.brief-vital {
  font-size: 11px; color: var(--ink-500); background: #fff;
  border: 1px solid var(--ink-100); border-radius: 6px; padding: 2px 7px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.brief-vital b { color: var(--ink-900); font-weight: 600; }
/* Band colours mirror the device gauges, so the report agrees with the gauge. */
.brief-vital.band-warn   { background: rgba(245,158,11,.10); border-color: rgba(245,158,11,.35); }
.brief-vital.band-warn b { color: #92400E; }
.brief-vital.band-danger { background: rgba(239,68,68,.10); border-color: rgba(239,68,68,.35); }
.brief-vital.band-danger b { color: #B91C1C; }
.brief-device-note {
  margin: 7px 0 0; font-size: 12px; line-height: 1.55; color: var(--ink-700);
}
.brief-device:not(.has-issues) .brief-device-note { color: var(--ink-500); }

/* ---- Brief: standing battery-health section ---- */
.brief-section-note { font-size: 11px; font-weight: 500; color: var(--ink-500); text-transform: none; letter-spacing: 0; }
.brief-batteries { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.brief-battery {
  border: 1px solid var(--ink-100); border-left: 3px solid var(--ink-400);
  border-radius: 8px; padding: 8px 12px; background: #fff;
}
.brief-battery.band-watch    { border-left-color: #EAB308; }
.brief-battery.band-warn     { border-left-color: #F59E0B; }
.brief-battery.band-critical { border-left-color: #EF4444; }
.brief-battery-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.brief-battery-name { color: var(--ink-900); }
.brief-battery-soh { font-size: 11px; font-weight: 600; color: var(--ink-700); background: var(--ink-100); border-radius: 6px; padding: 1px 7px; font-variant-numeric: tabular-nums; }
.brief-battery-tag { font-size: 11px; font-weight: 600; color: #B91C1C; background: rgba(239,68,68,.10); border-radius: 6px; padding: 1px 7px; }
.brief-battery-note { margin: 5px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-700); }

/* ---- findings + recommendations ---- */
.brief-findings { margin: 0; padding-left: 18px; }
.brief-findings li {
  font-size: 13px; line-height: 1.6; color: var(--ink-700); margin-bottom: 5px;
}
.brief-recs { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.brief-rec { display: flex; align-items: baseline; gap: 9px; }
.brief-prio {
  flex: 0 0 auto; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; padding: 2px 8px; border-radius: 999px; min-width: 58px;
  text-align: center;
}
.prio-high   { background: rgba(239,68,68,.12);  color: #B91C1C; }
.prio-medium { background: rgba(245,158,11,.14); color: #92400E; }
.prio-low    { background: var(--ink-100);       color: var(--ink-700); }
.brief-rec-text { font-size: 13px; line-height: 1.6; color: var(--ink-700); }

.brief-detail-side {
  margin-top: 16px;
  background: var(--card-bg); border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 16px 18px;
}
.brief-side-title {
  margin: 0 0 12px; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-500);
}
.brief-side-dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 9px 14px; }
.brief-side-dl dt {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-500); align-self: center;
}
.brief-side-dl dd {
  margin: 0; font-size: 13px; color: var(--ink-900); text-align: right;
  font-variant-numeric: tabular-nums;
}
.brief-error {
  margin: 12px 0 0; font-size: 12px; color: #a01024;
  background: #fde7e9; border: 1px solid #f3b7bf; border-radius: 8px;
  padding: 8px 10px; white-space: pre-wrap;
}
@media (max-width: 820px) {
  .brief-side-dl { grid-template-columns: 1fr; gap: 2px 0; }
  .brief-side-dl dd { text-align: left; margin-bottom: 8px; }
}

/* =========================== Home (Fleet Map) page =========================== */
/* No padding here: .page already supplies the gutter, and adding a second one
   inset the title 24px from the brand mark above it. Header is .page-head. */
.home-updated { font-size: 12px; }

.home-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 14px;
}
@media (max-width: 900px) { .home-tiles { grid-template-columns: 1fr; } }

/* ---- Home: Solar status donuts (one compact row) ---- */
.solar-status { margin-bottom: 14px; }
.solar-status[hidden] { display: none; }
.solar-status-title { margin: 4px 0 8px; font-size: 13px; font-weight: 700; color: var(--ink-700); }
.solar-status-title .muted { font-weight: 500; font-size: 12px; }
.solar-status-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 10px;
}
@media (max-width: 1024px) {
  .solar-status-grid { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
}
.solar-donut {
  background: var(--card-bg); border: 1px solid var(--ink-200); border-radius: 12px;
  box-shadow: var(--shadow); padding: 10px 8px;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.solar-donut-title {
  font-size: 10.5px; font-weight: 600; color: var(--ink-600); line-height: 1.2; text-align: center;
  min-height: 2.4em; display: flex; align-items: center; justify-content: center;
}
.solar-donut-chart { width: 68px; }
.solar-donut-legend {
  list-style: none; margin: 0; padding: 0; width: 100%;
  display: flex; flex-direction: column; gap: 2px; font-size: 10px; color: var(--ink-600);
}
.solar-donut-legend li { display: flex; align-items: center; gap: 5px; }
.solar-donut-legend .dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.solar-donut-legend b { margin-left: auto; color: var(--ink-900); }

/* ---- Home: battery-health strip (links to the roll-up) ---- */
.battery-strip {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px; margin-bottom: 14px;
  background: var(--card-bg); border: 1px solid var(--ink-200);
  border-radius: 12px; box-shadow: var(--shadow);
  color: var(--ink-700); text-decoration: none;
  transition: border-color .2s ease, background .2s ease;
}
.battery-strip:hover { border-color: var(--ink-400); }
.battery-strip-icon { display: flex; color: var(--ink-500); }
.battery-strip-icon .kpi-tile-icon { width: 20px; height: 20px; }
.battery-strip-text { font-size: 13px; }
.battery-strip-text b { color: var(--ink-900); }
.battery-strip-cta { margin-left: auto; font-size: 12px; font-weight: 600; color: #4F46E5; white-space: nowrap; }
.battery-strip.has-attention { background: #FEF2F2; border-color: #FECACA; }
.battery-strip.has-attention .battery-strip-icon { color: #DC2626; }
.battery-strip.has-attention .battery-strip-text b { color: #B91C1C; }

.fleet-tile { padding: 12px 16px; }
.fleet-tile-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 6px;
}
.fleet-tile-head .kpi-card-title {
  font-size: 11px;
  color: var(--ink-500);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.fleet-tile-number {
  font-size: 22px; font-weight: 700; color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}
/* Info affordance on the Active/Inactive tiles — explains the freshness bands. */
.fleet-tile-titlewrap { display: inline-flex; align-items: center; gap: 5px; }
.tile-info {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; color: var(--ink-400); cursor: help;
}
.tile-info svg { width: 13px; height: 13px; display: block; }
.tile-info:hover, .tile-info:focus-visible { color: var(--ink-700); outline: none; }
.tile-tip {
  position: absolute; top: calc(100% + 6px); left: -2px; z-index: 20;
  display: none; width: max-content; max-width: 240px;
  padding: 7px 10px; border-radius: 6px;
  background: var(--ink-900); color: #fff; box-shadow: var(--shadow);
  font-size: 11px; font-weight: 500; line-height: 1.5;
  text-transform: none; letter-spacing: 0; white-space: normal;
}
.tile-tip b { font-weight: 700; }
.tile-info:hover .tile-tip, .tile-info:focus-visible .tile-tip { display: block; }
.fleet-tile-body {
  display: flex; align-items: center; gap: 16px;
}
/* Fixed square box keeps the canvas from being stretched by flex.
   Chart.js then renders at 1:1 inside it. */
.fleet-tile-chart-wrap {
  flex: 0 0 84px;
  width: 84px;
  height: 84px;
  position: relative;
}
.fleet-tile-chart {
  display: block;
  width: 100% !important;
  height: 100% !important;
}
/* Chart.js draws its tooltip inside the canvas, and the canvas is only 84px —
   "indeterminate: 3" was getting its corners and its tail sliced off. home.js
   renders this instead, anchored to the wrap above, which nothing clips.
   Matches .tile-tip so every dark tip on the page is the same size and shape. */
.chart-tip {
  position: absolute; z-index: 20;
  transform: translate(-50%, calc(-100% - 10px));
  display: none; width: max-content; max-width: 240px;
  padding: 7px 10px; border-radius: 6px;
  background: var(--ink-900); color: #fff; box-shadow: var(--shadow);
  font-size: 11px; font-weight: 600; line-height: 1.5;
  white-space: nowrap;
  pointer-events: none;  /* must not sit under the cursor, or it flickers */
}
.chart-tip.is-shown { display: block; }
.fleet-tile-legend {
  list-style: none; margin: 0; padding: 0;
  font-size: 12px; color: var(--ink-700);
  display: flex; flex-direction: column; gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
}
.fleet-tile-legend li {
  display: flex; align-items: center; gap: 6px;
  white-space: nowrap;
}
.fleet-tile-legend.fleet-tile-legend-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px;
}
.fleet-tile-legend b {
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  margin-left: auto;
  font-weight: 700;
}
.fleet-tile-legend .dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  flex: 0 0 8px;
}
.dot-ok            { background: #22C55E; }
.dot-unusual       { background: #F59E0B; }
.dot-warn          { background: #F59E0B; } /* legacy freshness bucket */
.dot-stale         { background: #94A3B8; } /* legacy freshness bucket */
.dot-down          { background: #EF4444; }
.dot-critical      { background: #EF4444; }
.dot-major         { background: #F59E0B; }
.dot-minor         { background: #EAB308; }
.dot-warning       { background: #3B82F6; }
.dot-indeterminate { background: #94A3B8; }

/* Full-width fleet map card, sits below the Device Monitoring table. */
.home-map-card {
  margin-top: 14px;
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  height: 52vh;
  min-height: 380px;
  max-height: 560px;
  padding: 8px;
  position: relative;
}
#fleet-map {
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  border-radius: calc(var(--radius) - 4px);
  background: var(--ink-100);
}
.home-map-foot { margin: 8px 4px 2px; font-size: 12px; flex: 0 0 auto; }

/* Key for the two independent pin signals: hue = freshness, motion = attention. */
.map-legend {
  margin: 0 4px 8px; font-size: 11px; flex: 0 0 auto;
  display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: center;
}
.map-legend-group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
.map-legend-key   { display: inline-flex; align-items: center; gap: 4px; }
.map-legend-key .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
/* .pin is sized for the map (14px, block) and is declared LATER in this file, so
   this needs the extra specificity to win. Shrink it to sit inline with the text. */
.map-legend .map-legend-pin { display: inline-block; width: 8px; height: 8px; border-width: 1.5px; }

/* ---- Fleet monitoring table (full-width, below the map + alerts row) ---- */
.fleet-table-card {
  margin-top: 14px;
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px;
}
.fleet-table-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 10px; flex-wrap: wrap;
}
.fleet-table-title { margin: 0; font-size: 14px; color: var(--ink-700); }
/* Source filter chips (All / ThingsBoard / Solar Guardian) — pushed right, sits by search. */
.table-source-filter { display: inline-flex; gap: 4px; margin-left: auto; }
.table-source-filter button {
  font: inherit; font-size: 12px; font-weight: 600;
  padding: 5px 10px; border: 1px solid var(--ink-200); border-radius: 999px;
  background: var(--ink-50); color: var(--ink-600); cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.table-source-filter button:hover { background: var(--ink-100); }
.table-source-filter button.active {
  background: var(--indigo-50); border-color: var(--indigo-500); color: var(--indigo-700);
}
.fleet-table-search {
  font: inherit; font-size: 13px; padding: 6px 10px; min-width: 220px;
  border: 1px solid var(--ink-100); border-radius: 8px;
  background: #fff; color: var(--ink-900);
}
.fleet-table-wrap { overflow: auto; max-height: clamp(300px, 48vh, 560px); }
.fleet-table {
  width: 100%; border-collapse: collapse;
  font-size: 13px; white-space: nowrap;
}
.fleet-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--card-bg);   /* opaque so rows scroll under the header */
}
.fleet-table th {
  text-align: right; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-500); font-weight: 600; padding: 8px 10px;
  border-bottom: 1px solid var(--ink-100);
}
.fleet-table th.col-name, .fleet-table td.col-name { text-align: left; }
/* sortable headers */
.fleet-table th.is-sortable { cursor: pointer; user-select: none; }
.fleet-table th.is-sortable:hover { color: var(--ink-700); }
.fleet-table th.is-sortable::after { content: "↕"; opacity: .3; margin-left: 4px; font-size: 10px; }
.fleet-table th[aria-sort="ascending"]::after  { content: "↑"; opacity: 1; }
.fleet-table th[aria-sort="descending"]::after { content: "↓"; opacity: 1; }
.fleet-table th[aria-sort] { color: var(--ink-900); }
.fleet-table td {
  padding: 9px 10px; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--ink-900);
  border-bottom: 1px solid var(--ink-100);
}
.fleet-table tbody tr:hover { background: var(--ink-50, #F8FAFC); }
.fleet-table-name { font-weight: 600; color: var(--ink-900); text-decoration: none; }
.fleet-table-name:hover { text-decoration: underline; }
.fleet-table-type { display: block; font-size: 11px; color: var(--ink-500); font-weight: 400; }
.fleet-table-empty { padding: 16px 4px; }

/* battery bar — bar pinned left (reads left→right), % text pinned right, so
   bars and numbers each line up in their own column regardless of value width. */
.batt-cell { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.batt-bar {
  width: 64px; height: 8px; border-radius: 4px;
  background: var(--ink-100); overflow: hidden; flex: 0 0 64px;
}
.batt-val { font-variant-numeric: tabular-nums; white-space: nowrap; }
.batt-bar-fill { display: block; height: 100%; border-radius: 4px; }
.batt-bar-fill.is-high { background: #22C55E; }
.batt-bar-fill.is-mid  { background: #F59E0B; }
.batt-bar-fill.is-low  { background: #EF4444; }

/* status */
.fleet-table .status-cell { display: inline-flex; align-items: center; gap: 6px; text-transform: capitalize; }
.fleet-table .status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }

/* ---- In-map device summary panel (Leaflet control, top-right, collapsible) ---- */
.map-summary {
  width: 290px;
  max-width: calc(100vw - 40px);
  background: rgba(255, 255, 255, .96);
  border-radius: 10px;
  box-shadow: 0 4px 16px rgba(15, 23, 42, .18);
  overflow: hidden;
  font: inherit;
}
.map-summary-head {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--ink-100);
  background: #FFFFFF;
}
.map-summary-toggle {
  border: 0; background: transparent; cursor: pointer;
  font-size: 13px; line-height: 1; padding: 2px 4px; color: var(--ink-700);
}
.map-summary-title {
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-700); flex: 1 1 auto;
}
.map-summary-body { padding: 8px; }
.map-summary .summary-list {
  display: flex; flex-direction: column; gap: 8px;
  max-height: 360px; overflow-y: auto;
  margin: 0; padding: 0; list-style: none;
}
.map-summary.is-collapsed .map-summary-body { display: none; }
@media (max-width: 700px) { .map-summary { width: 240px; } }

.summary-clear {
  font-size: 11px; font-weight: 600;
  color: var(--indigo-700); background: transparent; border: 0; cursor: pointer;
  padding: 2px 6px; border-radius: 6px;
}
.summary-clear:hover { background: var(--ink-100); }
.is-hidden { display: none !important; }
.summary-empty { padding: 12px 4px; font-size: 12px; }

.summary-card {
  border: 1px solid var(--ink-100);
  border-left: 3px solid var(--ink-200);
  border-radius: 8px;
  padding: 10px 12px;
  display: flex; flex-direction: column; gap: 6px;
  background: var(--card-bg);
  transition: border-color .15s ease, background .15s ease;
}
.summary-card:hover { background: #FAFBFC; }
.summary-card.status-ok      { border-left-color: #22C55E; }
.summary-card.status-unusual { border-left-color: #F59E0B; }
.summary-card.status-warn    { border-left-color: #F59E0B; } /* legacy */
.summary-card.status-stale   { border-left-color: #94A3B8; } /* legacy */
.summary-card.status-down    { border-left-color: #EF4444; }

.summary-card-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.summary-card-id   { display: flex; align-items: center; gap: 6px; min-width: 0; }
.summary-status-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.summary-card-name { font-size: 13px; color: var(--ink-900); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.summary-card-meta { font-size: 11px; flex: 0 0 auto; }

/* Each dot now spells out its severity, so the count is what should pop — not
   the whole chip. Hence weight 500 here and 700 only on the number. */
.summary-card-alarms { display: flex; flex-direction: column; gap: 4px; }
.summary-alarms-label {
  font-size: 11px; font-weight: 600; color: var(--ink-500);
  letter-spacing: .05em;
}
.summary-card-sev { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 11px; }
.summary-card-sev-clear { color: #16A34A; font-weight: 600; font-size: 11px; }
.summary-sev { display: inline-flex; align-items: center; gap: 4px; font-weight: 500; color: var(--ink-700); }
.summary-sev b { font-weight: 700; }
.summary-sev .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.summary-sev-critical      .dot { background: #EF4444; }
.summary-sev-major         .dot { background: #F59E0B; }
.summary-sev-minor         .dot { background: #EAB308; }
.summary-sev-warning       .dot { background: #3B82F6; }
.summary-sev-indeterminate .dot { background: #94A3B8; }

.summary-card-foot { display: flex; justify-content: space-between; align-items: center; font-size: 11px; }
.summary-card-link { color: var(--indigo-700); font-weight: 600; text-decoration: none; }
.summary-card-link:hover { text-decoration: underline; }

/* =========================== Map pins =========================== */
.pin-wrap { background: transparent; border: 0; }
.pin {
  display: block;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.35);
  box-sizing: content-box;
}
/* Hue = device freshness, and nothing else. --pin-halo is the same colour in
   rgb triplet form, so one keyframe can pulse any of them. */
.pin-ok      { background: #22C55E; --pin-halo: 34,197,94; }
.pin-unusual { background: #F59E0B; --pin-halo: 245,158,11; }
.pin-warn    { background: #F59E0B; --pin-halo: 245,158,11; }  /* legacy */
.pin-stale   { background: #94A3B8; --pin-halo: 148,163,184; } /* legacy */
.pin-down    { background: #EF4444; --pin-halo: 239,68,68; }

/* Motion = needs attention: down, or carrying an active alarm. Pulse is
   the ONLY thing that means this, exactly as hue only ever means freshness. */
.pin-attn { animation: pin-pulse 1.4s ease-in-out infinite; }
@keyframes pin-pulse {
  0%   { transform: scale(1);    box-shadow: 0 1px 3px rgba(0,0,0,.35), 0 0 0 0 rgba(var(--pin-halo), .55); }
  70%  { transform: scale(1.15); box-shadow: 0 1px 3px rgba(0,0,0,.35), 0 0 0 8px rgba(var(--pin-halo), 0); }
  100% { transform: scale(1);    box-shadow: 0 1px 3px rgba(0,0,0,.35), 0 0 0 0 rgba(var(--pin-halo), 0); }
}
@media (prefers-reduced-motion: reduce) { .pin-attn { animation: none; } }

.pin-tooltip {
  background: var(--card-bg) !important;
  color: var(--ink-900) !important;
  border: 1px solid var(--ink-100) !important;
  border-radius: 10px !important;
  box-shadow: var(--shadow) !important;
  padding: 10px 12px !important;
  font-size: 12px !important;
}
.pin-tooltip::before { display: none !important; }
.pin-popup-title { font-weight: 700; color: var(--ink-900); margin-bottom: 2px; }
.pin-popup-meta { font-size: 11px; margin-bottom: 6px; }
.pin-popup-row { display: flex; justify-content: space-between; gap: 12px; line-height: 1.6; }
.pin-popup-row b { font-weight: 600; }
.pin-popup-row .status-ok      { color: #22C55E; }
.pin-popup-row .status-unusual { color: #F59E0B; }
.pin-popup-row .status-warn    { color: #F59E0B; } /* legacy */
.pin-popup-row .status-stale   { color: #94A3B8; } /* legacy */
.pin-popup-row .status-down    { color: #EF4444; }

/* =========================== Dashboard: power-flow diagram =========================== */
.flow-diagram {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px 18px;
  margin: 14px 0;
  /* Query container so the diagram stacks by its own width, not the viewport's. */
  container-type: inline-size;
  container-name: flow;
}

/* Single-line diagram stage: a 3×2 grid of node cards over an SVG wire layer.
   Fixed 5:2 aspect + the matching 500×200 viewBox means uniform scaling, so the
   wire coordinates always line up with the card cells. */
.flow-stage {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  /* minmax(0,…) so a tall Battery card can't stretch the row and grow the stage
     past its 5:2 aspect — that would break the fixed-viewBox SVG wire alignment.
     Any small overflow sits in the card's bottom padding instead. */
  grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-areas:
    "solar inverter load"
    "grid  battery  battery";
  place-items: center;
  aspect-ratio: 5 / 2;
  gap: 22px 6px;
}
/* Pin the bottom-row cards to the top of their row so the (tall) battery grows
   DOWNWARD only — this keeps Grid and Battery tops aligned and leaves a clean
   horizontal channel under the inverter for the Grid trace. */
.flow-node-grid,
.flow-node-battery { align-self: start; }
.flow-wires {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  pointer-events: none;
}
.wire-base {
  fill: none;
  stroke: #F59E0B;            /* amber trace (static) */
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.wire-dashed { stroke-dasharray: 7 7; }
/* Live highlight + flow animation are deferred to a later phase: traces are
   static for now. viz.js still tags live paths with .is-active, so the active
   styling can be re-enabled here without touching the data layer. */

.flow-node {
  position: relative;
  z-index: 1;
  width: 84%;
  background: var(--ink-50);
  border: 1px solid var(--ink-100);
  border-radius: 14px;
  padding: 12px 14px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.flow-node-icon { width: 26px; height: 26px; margin-bottom: 2px; color: var(--ink-500); }
.flow-node-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-700);
  line-height: 1.2;
}
.flow-node-value {
  font-size: 18px;
  font-weight: 700;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
  min-width: 0; max-width: 100%;
  overflow-wrap: anywhere;
}
.flow-node-unit { font-size: 12px; font-weight: 600; color: var(--ink-500); }
.flow-node-sub  { font-size: 12px; font-weight: 600; color: var(--ink-500); }

/* Node placement (grid areas) + tints + icon accents (reuse KPI theme hues). */
.flow-node-solar    { grid-area: solar;    background: #FEF3C7; border-color: #FDE68A; }
.flow-node-grid     { grid-area: grid;     background: #DBEAFE; border-color: #BFDBFE; }
/* Battery bank spans the bottom-right two columns (it carries the most detail);
   the Grid elbow rises in the channel just left of it (see the SVG wire coords). */
.flow-node-battery  { grid-area: battery;  width: 92%; gap: 8px; background: #ECFDF5; border-color: #BBF7D0; }
.flow-node-load     { grid-area: load;     background: #EDE9FE; border-color: #DDD6FE; }
.flow-node-inverter { grid-area: inverter; width: 94%; background: #FFFFFF; border-color: var(--ink-200); box-shadow: 0 2px 8px rgba(15,23,42,.05); }
.flow-node-solar    .flow-node-icon { color: #B45309; }
.flow-node-grid     .flow-node-icon { color: #1D4ED8; }
.flow-node-battery  .flow-node-icon { color: #047857; }
.flow-node-load     .flow-node-icon { color: #6D28D9; }
.flow-node-inverter .flow-node-icon { color: #475569; }

/* --- Battery Bank node internals: Batt #1 — bank — Batt #2, series-linked --- */
/* Short connector segments tie the three squares in series; the bank (middle)
   node is the one wired up to the inverter (dashed, via the main SVG layer). */
.batt-row {
  display: flex; align-items: flex-start; justify-content: center; gap: 0;
}
.batt-conn { flex: 0 0 18px; height: 2px; margin-top: 15px; background: #34D399; }
/* Box is pinned to the square's width so the connectors meet every square (the
   wider V / BATT labels overflow-center below, clear of the connector line). */
.batt-box { display: flex; flex-direction: column; align-items: center; gap: 3px; flex: 0 0 32px; min-width: 0; }
.batt-square {
  display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: 8px;
  background: #FFFFFF; border: 2px solid #34D399;
}
.batt-square svg { width: 18px; height: 18px; color: #047857; }
/* The bank/junction square: solid green, slightly recessed — the bus tie point. */
.batt-box-bank .batt-square { background: #047857; border-color: #047857; }
.batt-box-bank .batt-square svg { color: #FFFFFF; }
/* VB1/VB2 sized to match the other node values (Solar/Grid/Load). */
.batt-volt {
  font-size: 18px; font-weight: 700; color: var(--ink-900);
  font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap;
}
.batt-volt .flow-node-unit { font-size: 12px; }
.batt-name {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-500); line-height: 1;
}

/* Two-part card: batteries (structure) on the left, readings (measurements) on
   the right — keeps the node short while staying the same width as the others. */
.batt-main {
  display: flex; align-items: center; justify-content: center;
  gap: 20px; width: 100%;
}
/* Bank readings: power / voltage / current, as a light divided list on the right. */
.batt-stats {
  display: flex; flex-direction: column;
  min-width: 155px;
  padding-left: 18px; border-left: 1px solid #BBF7D0;
}
.batt-stat {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 5px 0;
}
.batt-stat + .batt-stat { border-top: 1px solid #D1FAE5; }
.batt-stat-label {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--ink-500);
}
.batt-stat-val {           /* same size as the other node values (Solar/Grid/Load) */
  font-size: 18px; font-weight: 700; color: var(--ink-900);
  font-variant-numeric: tabular-nums; white-space: nowrap; line-height: 1;
}
.batt-stat-val .flow-node-unit { font-size: 12px; }

/* Stack to a vertical card list when the panel gets narrow; hide the wires. */
/* Stack to a vertical card list before the Battery card gets too narrow for its
   side-by-side layout. Switching straight to the stack (rather than letting the
   battery card grow tall in the grid) keeps the stage at its 5:2 aspect ratio,
   so the fixed-viewBox SVG wires stay aligned to the cards. Wires hidden here. */
@container flow (max-width: 640px) {
  .flow-stage {
    aspect-ratio: auto;
    grid-template-columns: 1fr;
    grid-template-rows: none;
    grid-template-areas: none;
    place-items: stretch;
    gap: 10px;
  }
  /* Reset the inline grid-area placement, the row-top pin, and any nudge
     (e.g. the Grid's translateY) so cards flow cleanly in DOM order. */
  .flow-stage > .flow-node {
    grid-area: auto; width: 100%; max-width: none;
    align-self: stretch; transform: none !important;
  }
  .flow-wires { display: none; }
}

/* =========================== Dashboard: 4-card viz grid =========================== */
.viz-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin: 0 0 14px;
}
@media (max-width: 1200px) { .viz-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .viz-grid { grid-template-columns: 1fr; } }

.viz-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.viz-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.viz-card-title {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-700);
  text-transform: uppercase;
  letter-spacing: .06em;
}
.viz-status-pill {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.viz-status-ok       { background: #ECFDF5; color: #047857; border-color: #BBF7D0; }
.viz-status-warn     { background: #FEF3C7; color: #8A5A06; border-color: #FDE68A; }
.viz-status-danger   { background: #FEE2E2; color: #B91C1C; border-color: #FECACA; }
.viz-status-neutral  { background: var(--ink-100); color: var(--ink-500); }

/* Gauge ---- SVG arc + center value */
.viz-gauge {
  width: 100%;
  display: flex;
  justify-content: center;
}
.viz-gauge .gauge-svg {
  width: 100%;
  max-width: 220px;
  height: auto;
  display: block;
}
.gauge-bg { stroke: var(--ink-100); stroke-width: 12; stroke-linecap: round; }
.gauge-fg { stroke-width: 12; stroke-linecap: round; transition: stroke .3s ease; }
.gauge-fg.gauge-ok       { stroke: #22C55E; }
.gauge-fg.gauge-warn     { stroke: #F59E0B; }
.gauge-fg.gauge-danger   { stroke: #EF4444; }
.gauge-fg.gauge-neutral  { stroke: var(--indigo-500); }
.gauge-value {
  font: 700 26px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  fill: var(--ink-900);
}
.gauge-unit {
  font: 600 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  fill: var(--ink-500);
  letter-spacing: .04em;
}

/* Secondary stat tiles below the gauge */
.viz-stats {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
  gap: 6px;
}
.viz-stat {
  background: var(--ink-50);
  border: 1px solid var(--ink-100);
  border-radius: 8px;
  padding: 6px 8px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.viz-stat-value {
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink-900);
}
.viz-stat-unit {
  font-size: 10px;
  color: var(--ink-500);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.viz-stat-ok      .viz-stat-value { color: #047857; }
.viz-stat-warn    .viz-stat-value { color: #8A5A06; }
.viz-stat-danger  .viz-stat-value { color: #B91C1C; }

/* =========================== Dashboard: collapsible detail panels =========================== */
.detail-collapse {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-top: 14px;
  overflow: hidden;
}
.detail-collapse > .detail-collapse-summary {
  list-style: none;
  cursor: pointer;
  padding: 14px 18px;
  display: flex;
  align-items: center;
  gap: 10px;
  user-select: none;
}
.detail-collapse > .detail-collapse-summary::-webkit-details-marker { display: none; }
.detail-collapse-chevron {
  display: inline-block;
  transition: transform .2s ease;
  color: var(--ink-500);
  font-size: 12px;
}
.detail-collapse[open] .detail-collapse-chevron { transform: rotate(90deg); }
.detail-collapse-title {
  font-weight: 700;
  color: var(--ink-900);
  font-size: 14px;
}
.detail-collapse-meta {
  font-size: 12px;
}
.detail-collapse > .panels {
  padding: 0 18px 18px;
}
.detail-collapse > .detail-collapse-summary:hover {
  background: var(--ink-50);
}

/* =========================== Critical-alert row highlight =========================== */
.alert.alert-critical {
  background: #FEE2E2;
  border-left: 3px solid #EF4444;
  padding-left: 9px;
}
.alert.alert-critical .alert-type   { color: #7F1D1D; font-weight: 700; }
.alert.alert-critical .alert-meta   { color: #991B1B; }
.alert.alert-critical .alert-sev    { color: #B91C1C; font-weight: 700; }
.alert.alert-critical:hover         { background: #FECACA; }
.alert.alert-critical.alert-cleared { opacity: .55; }

/* =========================== Dashboard analytics charts =========================== */
.dash-charts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  margin: 0 0 14px;
}
.dash-charts-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
@media (max-width: 1100px) { .dash-charts-row { grid-template-columns: 1fr; } }

.dash-chart-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 18px 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.dash-chart-head {
  display: flex; align-items: center; justify-content: space-between;
}
.dash-chart-title {
  margin: 0;
  font-size: 13px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-700);
}
.dash-chart-canvas-wrap {
  position: relative;
  height: 220px;
}
.dash-chart-canvas-wrap canvas { width: 100% !important; height: 100% !important; }

.range-chips {
  display: inline-flex; gap: 4px;
  background: var(--ink-100);
  border-radius: 8px;
  padding: 2px;
}
.range-chips button {
  background: transparent; border: 0;
  padding: 4px 10px; border-radius: 6px;
  font: 600 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--ink-500); cursor: pointer;
}
.range-chips button.active {
  background: var(--card-bg);
  color: var(--ink-900);
  box-shadow: 0 1px 2px rgba(15,23,42,.12);
}

/* ── AI device briefing (Home) ────────────────────────────────────────────
   Two columns between the KPI tiles and the map grid: intraday (left) + daily
   (right). Reuses the existing card tokens; the slim indigo rail is the one
   quiet "AI" signal. Columns stack on narrow viewports. */
.ai-brief-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin-bottom: 6px;
}
.ai-brief-cols .ai-brief { margin-bottom: 0; height: 100%; }
@media (max-width: 900px) {
  .ai-brief-cols { grid-template-columns: 1fr; }
}
.ai-brief-more { margin: 0 0 14px; font-size: 12px; text-align: right; }
.ai-brief-more a { color: var(--indigo-700); font-weight: 600; text-decoration: none; }
.ai-brief-more a:hover { text-decoration: underline; }
.ai-brief {
  display: flex;
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  margin-bottom: 14px;
}
.ai-brief-rail {
  flex: 0 0 4px;
  background: linear-gradient(180deg, var(--indigo-500), var(--indigo-700));
}
.ai-brief-main { flex: 1 1 auto; min-width: 0; padding: 14px 18px 16px; }
.ai-brief-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.ai-brief-titlewrap { display: flex; align-items: center; gap: 8px; }
.ai-brief-title {
  margin: 0; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--indigo-700);
}
.ai-brief-time { font-size: 11px; white-space: nowrap; }
.ai-brief-stale {
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  color: #8A5A06; background: #FEF3C7; border: 1px solid #FDE68A;
  border-radius: 999px; padding: 1px 7px;
}
.ai-brief-text {
  margin: 10px 0 12px; font-size: 13px; line-height: 1.6;
  color: var(--ink-700);
}
.ai-brief-empty { margin: 8px 0 2px; font-size: 13px; }
.ai-brief-chips {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px;
}
.ai-brief-chips .ai-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--ink-700);
  background: var(--ink-50); border: 1px solid var(--ink-100);
  border-radius: 999px; padding: 4px 10px;
}
.ai-brief-chips .ai-chip b { color: var(--ink-900); font-weight: 700; }
.ai-brief-chips .ai-chip-muted {
  color: var(--ink-500); background: transparent; border-color: transparent;
}
.ai-brief-chips .ai-chip-attention {
  background: rgba(245,158,11,.12); border-color: rgba(245,158,11,.35); color: #92400E;
}
.ai-brief-chips .ai-chip-attention b { color: #92400E; }
/* Home card shows the executive summary only; this opens the full report. */
.ai-brief-link { margin: 10px 0 0; font-size: 12px; }
.ai-brief-link a { color: var(--indigo-700); font-weight: 600; text-decoration: none; }
.ai-brief-link a:hover { text-decoration: underline; }

/* ================= Topbar submenu (Devices → children) =================
   The only hover-revealed menu in the app; the other three topbar dropdowns are
   click-toggled with [hidden]. Hover alone would be unreachable by keyboard and
   touch, so :focus-within opens it too. Visuals deliberately mirror
   .user-menu-dropdown / .user-menu-item so it reads as the same component. */
.topnav-group { position: relative; display: inline-flex; }
.topnav-submenu {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 60;
  min-width: 210px;
  background: var(--card-bg); border: 1px solid var(--indigo-100);
  border-radius: 12px; box-shadow: 0 14px 32px rgba(15, 23, 42, .16);
  padding: 6px;
  display: none; flex-direction: column; gap: 2px;
}
/* A gap between the trigger and the panel would close the menu as the pointer
   crosses it; this bridges it without moving the panel. */
.topnav-submenu::before {
  content: ""; position: absolute; left: 0; right: 0; top: -8px; height: 8px;
}
.topnav-group:hover .topnav-submenu,
.topnav-group:focus-within .topnav-submenu { display: flex; }
/* Every selector here is scoped with `.topnav` purely to outrank `.topnav a`, which sets
   white text for the dark topbar. These items sit on a WHITE panel, so losing that fight
   renders the labels white-on-white — visible icons, invisible text. `.topnav a` is
   (0,1,1); a bare `.topnav-submenu-item` is only (0,1,0) and loses. Same for padding and
   the hover colours. */
.topnav .topnav-submenu-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 8px;
  color: var(--ink-700); font-size: 13px; font-weight: 500;
  text-decoration: none;
  transition: background .12s, color .12s;
}
.topnav .topnav-submenu-item:hover { background: var(--ink-100); color: var(--ink-900); }
.topnav .topnav-submenu-item svg {
  width: 18px; height: 18px; flex: 0 0 auto; color: var(--ink-400);
}
.topnav .topnav-submenu-item:hover svg { color: var(--indigo-600); }

/* ===================== Solar device management ===================== */
/* Two rows, the same shape as the Alarms filter bar: search and roster state on one line,
   the status filters on their own. As one flex row the nine chips pushed the roster label
   onto a line of its own, where it read as an orphan. */
.sd-toolbar { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
.sd-toolbar-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.sd-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
/* Status legend. Nine entries do not fit .tile-tip's 240px, and it sits at the right-hand
   end of the chip row, so it opens leftwards to stay on screen. */
.sd-legend { margin-left: 2px; }
.sd-legend-tip { max-width: 340px; left: auto; right: -2px; line-height: 1.65; }
.sd-legend-note {
  display: block; margin-top: 6px; padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, .18); color: var(--ink-400);
}
.sd-toolbar form { display: flex; gap: 8px; }
.sd-toolbar input[type="search"] {
  font: inherit; font-size: 13px; padding: 6px 10px;
  border: 1px solid var(--ink-200); border-radius: 8px; min-width: 220px;
}
.sd-spacer { flex: 1; }
/* Keeps its place on the right when the row wraps on a narrow screen. */
.sd-meta { font-size: 12px; color: var(--ink-500); margin-left: auto; }
.sd-meta code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; }

.sd-table-wrap {
  overflow-x: auto; background: var(--card-bg);
  border: 1px solid var(--ink-200); border-radius: 12px; box-shadow: var(--shadow);
}
.sd-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.sd-table th {
  text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-500); font-weight: 700; padding: 10px 12px;
  border-bottom: 1px solid var(--ink-200); white-space: nowrap;
}
/* Sortable headers. The caret is on the active column only — showing an idle arrow on
   every column makes the sorted one hard to pick out at a glance. */
.sd-table th a { color: inherit; text-decoration: none; white-space: nowrap; }
.sd-table th a:hover { color: var(--indigo-700); }
.sd-table th.is-sorted a { color: var(--indigo-700); }
.sd-table th[aria-sort="ascending"] a::after  { content: " ▲"; font-size: 8px; }
.sd-table th[aria-sort="descending"] a::after { content: " ▼"; font-size: 8px; }
.sd-table td { padding: 10px 12px; border-bottom: 1px solid var(--ink-100); vertical-align: top; }
.sd-table tbody tr:last-child td { border-bottom: none; }
.sd-table tbody tr:hover { background: var(--ink-50); }
.sd-table .num { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* The description ⓘ is .tile-info (see solar/_description_info.html), the same component
   as the freshness legend — only the size differs beside a heading rather than a chip row,
   and a heading's own bold/uppercase must not leak into the tooltip. */
.page-head h1 .tile-info,
.detail-name .tile-info { width: 17px; height: 17px; vertical-align: middle; }
.page-head h1 .tile-info svg,
.detail-name .tile-info svg { width: 16px; height: 16px; }
.tile-info .tile-tip { font-weight: 500; }
/* Disabled fields on a deleted device: readable, obviously not editable. */
.sd-field input:disabled, .sd-field select:disabled, .sd-field textarea:disabled {
  background: var(--ink-50); color: var(--ink-500); cursor: not-allowed;
}
.sd-name { font-weight: 600; color: var(--ink-900); }
.sd-name:hover { color: var(--indigo-700); text-decoration: underline; }
.sd-sub { display: block; font-size: 11px; color: var(--ink-500); margin-top: 2px; }

.sd-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 600; white-space: nowrap;
}
.sd-pill .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
/* Four states, one hue each. The column used to carry nine labels and needed a legend to
   read; the pill now answers "is this working?" and the sub-line beneath it says why. */
.sd-pill-ok        { background: #DCFCE7; color: #15803D; }
.sd-pill-problem   { background: #FEE2E2; color: #B91C1C; }
.sd-pill-notpolled { background: var(--ink-100); color: var(--ink-500); }
/* Deleted is told apart by outline rather than shade — it shares the All tab with the
   grey "not polled" rows, and a second grey fill would be unreadable beside them. */
.sd-pill-deleted   { background: transparent; color: var(--ink-400);
                     border: 1px dashed var(--ink-400); padding: 2px 8px; }

/* The cause, under the pill. Muted so the pill stays the thing you scan. */
.sd-state-detail {
  display: block; font-size: 11px; color: var(--ink-600); margin-top: 3px; max-width: 300px;
}
/* On the device page the pill and its cause sit on one line rather than stacked. */
.sd-status-line { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; }
.sd-status-line .sd-state-detail { margin-top: 0; }

.sd-err-hint { display: block; font-size: 11px; color: var(--ink-500); margin-top: 2px; max-width: 340px; }

/* Form: the app has no shared field styling outside .login-card, so this is its own. */
.sd-form { background: var(--card-bg); border-radius: var(--radius); box-shadow: var(--shadow);
           padding: 20px 22px 22px; max-width: 620px; }
.sd-form h2 { margin: 0 0 4px; font-size: 13px; font-weight: 700;
              text-transform: uppercase; letter-spacing: .06em; color: var(--ink-700); }
.sd-form .sd-form-note { margin: 0 0 16px; font-size: 12px; color: var(--ink-500); }
.sd-field { display: block; margin-bottom: 14px; }
.sd-field > span { display: block; font-size: 12px; font-weight: 600;
                   color: var(--ink-700); margin-bottom: 5px; }
.sd-field input, .sd-field select, .sd-field textarea {
  width: 100%; font: inherit; font-size: 14px; padding: 9px 11px;
  border: 1px solid var(--ink-200); border-radius: 8px; background: #FFF;
}
.sd-field textarea { resize: vertical; min-height: 68px; line-height: 1.5; }
.sd-field input:focus, .sd-field select:focus, .sd-field textarea:focus {
  outline: none; border-color: var(--indigo-500);
  box-shadow: 0 0 0 3px var(--indigo-50);
}
.sd-field .sd-hint { display: block; font-size: 11px; color: var(--ink-500); margin-top: 4px; }
/* Required marker. Its own colour so it reads at a glance against the bold field label,
   and cursor:help pairs with the title attribute. */
.sd-req { color: #DC2626; font-weight: 700; cursor: help; }

/* Advanced: a section inside a card, not a card of its own — so it borrows
   .detail-collapse's rotating chevron but none of its background or shadow. */
.sd-advanced { margin-top: 4px; border-top: 1px solid var(--ink-100); padding-top: 12px; }
.sd-advanced > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-500);
}
.sd-advanced > summary::-webkit-details-marker { display: none; }
.sd-advanced > summary:hover { color: var(--ink-700); }
.sd-advanced-chevron { display: inline-block; transition: transform .2s ease; font-size: 11px; }
.sd-advanced[open] .sd-advanced-chevron { transform: rotate(90deg); }
.sd-advanced > .sd-field { margin-top: 14px; }
.sd-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
@media (max-width: 620px) { .sd-row { grid-template-columns: 1fr; } }
/* Devices on one gateway (network form) and networks in one area (area form).
   A plain list, not .sd-table: these are 1-4 rows in a narrow rail, where a table's
   header and column rules cost more than they explain. */
.sd-bus-list { list-style: none; margin: 12px 0 0; padding: 0; }
.sd-bus-list li {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 0; border-top: 1px solid var(--ink-100);
  font-size: 13px;
}
.sd-bus-list li:first-child { border-top: 0; }
.sd-bus-list .muted { margin-left: auto; font-size: 12px; }
.sd-bus-list .sd-pill { margin-left: 0; }

.sd-form-actions { display: flex; align-items: center; gap: 10px; margin-top: 18px; }
.sd-form-actions .sd-spacer { flex: 1; }
.sd-danger {
  background: #FFF; color: #B91C1C; border: 1px solid #FECACA;
  padding: 8px 14px; border-radius: 8px; font: inherit; font-weight: 600;
  font-size: 13px; cursor: pointer;
}
.sd-danger:hover { background: #FEF2F2; border-color: #EF4444; }
.sd-readonly { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
               font-size: 12px; color: var(--ink-500); }
/* Confirm dialog for enable / disable / adopt. A native <dialog>, so the backdrop, focus
   trap and Escape handling are the browser's; only the panel needs styling. Tokens match
   .user-menu-dropdown so it reads as the same family of surface. */
.sd-confirm {
  width: min(420px, calc(100vw - 32px));
  padding: 18px 20px 16px;
  border: 1px solid var(--indigo-100); border-radius: 12px;
  background: var(--card-bg); color: var(--ink-700);
  box-shadow: 0 18px 44px rgba(15, 23, 42, .22);
}
.sd-confirm::backdrop { background: rgba(15, 23, 42, .42); }
.sd-confirm-title {
  margin: 0 0 8px; font-size: 15px; font-weight: 700; color: var(--ink-900);
}
.sd-confirm-body { margin: 0; font-size: 13px; line-height: 1.55; color: var(--ink-600); }
.sd-confirm-actions {
  display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px;
}
/* .primary carries a top margin from the login form; neutralise it in this row. */
.sd-confirm-actions .primary { margin-top: 0; }

.sd-notice { display: flex; gap: 9px; padding: 10px 12px; border-radius: 8px;
             font-size: 13px; margin-bottom: 14px; }
.sd-notice-ok   { background: #DCFCE7; color: #15803D; }
.sd-notice-warn { background: #FEF3C7; color: #92400E; }

/* ---- Device history timeline ----------------------------------------------
   No timeline component existed anywhere in the app, so this is its own. The row shape
   (dot column + body) matches .alert, but deliberately does not reuse it: .alert forces
   background and colour with !important and its severity classes collide with the action
   names used here. Dot tints come from the .sd-pill-* palette so an event reads the same
   colour as the state it produced. */
/* Device page: form left, history right. The form is intrinsically narrow, so a single
   column left most of the width empty and pushed History below the fold. `align-items:
   start` stops the aside stretching to the form's height, which is what makes `position:
   sticky` on it behave. */
/* Device page: fields left, actions and history right.
   The left track matches `.sd-form`'s own 620px cap exactly. Give it `1fr` instead and the
   track stretches to the full page width while the card inside stays 620px — the leftover
   renders as a gap between the two columns that looks like a layout bug. `justify-content:
   start` keeps the leftover at the page edge, where it reads as a margin. */
.sd-layout {
  display: grid; grid-template-columns: minmax(0, 620px) minmax(300px, 400px);
  gap: 18px; align-items: start; justify-content: start;
}
.sd-main, .sd-side { min-width: 0; }
.sd-side > * + * { margin-top: 14px; }
/* One column before the aside gets too narrow to show a change row without wrapping
   awkwardly; the rail then simply follows the form. */
@media (max-width: 1040px) {
  .sd-layout { grid-template-columns: minmax(0, 620px); }
  .sd-side { margin-top: 14px; }
}

.sd-history h2 { margin-top: 0; }
/* Cap the panel and scroll inside it, so a long history never makes the page taller than
   the form it sits beside. */
.sd-events {
  list-style: none; margin: 0; padding: 0;
  max-height: calc(100vh - 260px); overflow-y: auto;
}
.sd-event {
  display: grid; grid-template-columns: 10px minmax(0, 1fr); gap: 10px;
  padding: 9px 0 9px 2px; border-top: 1px solid var(--ink-100);
}
.sd-event:first-child { border-top: 0; }
.sd-event-body { min-width: 0; }
/* Action and actor wrap as a unit rather than the timestamp orphaning onto its own line. */
.sd-event-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.sd-event-dot {
  width: 8px; height: 8px; border-radius: 50%; margin-top: 5px;
  background: var(--ink-400);
}
.sd-event-created   .sd-event-dot,
.sd-event-enabled   .sd-event-dot,
.sd-event-adopted   .sd-event-dot,
.sd-event-restored  .sd-event-dot { background: #22C55E; }
.sd-event-updated   .sd-event-dot { background: var(--indigo-500); }
.sd-event-discovered .sd-event-dot { background: #7C3AED; }
.sd-event-imported  .sd-event-dot { background: var(--ink-400); }
.sd-event-disabled  .sd-event-dot { background: var(--ink-500); }
.sd-event-deleted   .sd-event-dot { background: #DC2626; }

.sd-event-action { font-size: 13px; font-weight: 600; color: var(--ink-900); }
.sd-event-meta   { font-size: 12px; color: var(--ink-500); }
/* Label above values, not beside: at 340px a side-by-side row wraps mid-value and the
   arrow ends up on its own line. Long values break rather than widen the column. */
.sd-event-change { font-size: 12px; margin-top: 5px; }
.sd-event-field  { display: block; color: var(--ink-500); }
.sd-event-values {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 5px;
  overflow-wrap: anywhere;
}
.sd-event-old   { color: var(--ink-400); text-decoration: line-through; }
.sd-event-arrow { color: var(--ink-400); }
.sd-event-new   { color: var(--ink-900); font-weight: 600; }
/* The shared pager sizes itself for a full-width table; inside a 340px aside it needs to
   wrap and sit tighter. */
.sd-history .alerts-pager {
  flex-wrap: wrap; justify-content: center; gap: 4px; margin-top: 12px;
  padding-top: 12px; border-top: 1px solid var(--ink-100);
}
