/*
 * Component library — minimal scaffold subset.
 * Source: UI-001 §6 (Component Library).
 * Only the components used by the htmx_demo page are styled here; extend
 * as new components are ported.
 */

/* Page header — UI-002 §3 PageHeaderData */
.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.page-header__title {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.02em;
  color: var(--accent-primary);
}

.page-header__subtitle {
  margin-top: var(--space-1);
  font-size: 13px;
  color: var(--text-secondary);
}

.page-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* Card — UI-001 §6.1 */
.card {
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
}

.card-header__title {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--accent-primary);
  letter-spacing: -0.01em;
}

.card-body {
  padding: var(--space-4);
}

.card-footer {
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border-subtle);
  background: var(--bg-base);
}

/* Buttons — UI-001 §6.2 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: 34px;
  padding: 0 var(--space-3);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--transition-fast),
              border-color var(--transition-fast),
              color var(--transition-fast),
              box-shadow var(--transition-fast);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

.btn--primary {
  background: var(--accent-primary);
  color: var(--text-inverse);
}

.btn--primary:hover {
  background: var(--accent-primary-hover);
}

.btn--secondary {
  background: transparent;
  border-color: var(--border-default);
  color: var(--text-primary);
}

.btn--secondary:hover {
  background: var(--hover-overlay);
  border-color: var(--border-strong);
}

.btn--ghost {
  background: transparent;
  color: var(--text-secondary);
}

.btn--ghost:hover {
  background: var(--hover-overlay);
  color: var(--text-primary);
}

.btn--danger {
  background: var(--status-critical);
  color: #ffffff;
}

.btn--danger:hover {
  filter: brightness(0.9);
}

.btn--sm {
  height: 28px;
  font-size: 12px;
  padding: 0 var(--space-2);
}

.btn--lg {
  height: 40px;
  font-size: 14px;
  padding: 0 var(--space-4);
}

/* Icon-only button — used for the per-row action column on
   /devices and other list pages. 28×28, transparent background,
   muted icon colour that brightens on hover. */
.btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: var(--radius-md);
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
  text-decoration: none;
}
.btn--icon:hover {
  background: var(--hover-overlay);
  color: var(--text-primary);
}
.btn--icon svg {
  width: 16px;
  height: 16px;
}
.btn--icon--danger:hover {
  background: var(--status-critical-muted);
  color: var(--status-critical);
}

/* Per-row icon button cluster — keeps the four buttons tight
   on the right side without stealing focus from the row click. */
.device-row__actions {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  justify-content: flex-end;
}

/* Visible qhq-agent version line under the Agent badge in the devices
   list — small muted mono so the tech reads the fleet's agent builds at
   a glance ("—" when no successful check-in stamped a version). */
.device-row__agent-version {
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-tertiary);
}

/* Out-of-date agent (reported build behind the shipped agentversion.Version)
   — danger/red token so stale agents read at a glance. The neutral "—"
   (never checked in / unvalidated install) deliberately does NOT take this. */
.device-row__agent-version--stale {
  color: var(--status-critical);
}

/* Device detail page — definition-list grid for the info cards
   and a flex row for the action buttons. */
.device-detail__grid {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: var(--space-2) var(--space-4);
  margin: 0;
  font-size: 13px;
}
.device-detail__grid dt {
  color: var(--text-tertiary);
  font-weight: 500;
}
.device-detail__grid dd {
  margin: 0;
  color: var(--text-primary);
}
.device-detail__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* Clickable rows — fingers up the cursor and brighten on hover so
   the user knows the whole row is a link. The action-column
   <td> stops propagation so its buttons don't trigger the row
   navigation.

   F19 (Bundle I): real-anchor row-cover. The first <td> of each
   navigable row carries an <a class="row-link"> wrapping the cell's
   visible content. Its ::after pseudo-element absolutely covers the
   parent <tr>, which makes the entire row a real anchor for the
   browser — so right-click "Open in new tab" and middle-click both
   work. Left-click is still handled by the JS data-nav dispatcher
   (interactions.js) so the existing UX is preserved. Cells marked
   data-stop-propagation get a higher stacking context so their
   buttons/links sit above the overlay and remain interactive. */
.data-table__row--clickable {
  cursor: pointer;
  position: relative;
}
.data-table__row--clickable:hover {
  background: var(--hover-overlay);
}
.row-link {
  color: inherit;
  text-decoration: none;
}
.row-link:hover {
  color: inherit;
}
/* Scoped to the clickable-row parent on purpose: the overlay is
   positioned against the nearest positioned ancestor, and only
   .data-table__row--clickable establishes one (position:relative
   above). A bare .row-link outside that scaffolding used to spawn an
   overlay that resolved against the VIEWPORT — inset:0 covered the
   whole page and the last such anchor in DOM order swallowed every
   click (the /assets wrong-photo-on-any-click bug). */
.data-table__row--clickable .row-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}
.data-table__row--clickable td {
  position: relative;
}
.data-table__row--clickable td[data-stop-propagation] {
  z-index: 1;
}

/* Status badge — UI-001 §6.3 */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 22px;
  padding: 2px 8px 2px 6px;
  border-radius: var(--radius-md);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
  line-height: 1;
}

.badge__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.badge--healthy {
  background: var(--status-healthy-muted);
  color: var(--status-healthy);
}

.badge--warning {
  background: var(--status-warning-muted);
  color: var(--status-warning);
}

.badge--critical {
  background: var(--status-critical-muted);
  color: var(--status-critical);
}

.badge--info {
  background: var(--status-info-muted);
  color: var(--status-info);
}

.badge--unknown {
  background: var(--status-unknown-muted);
  color: var(--status-unknown);
}

/* Maintenance — operator-declared, expected-quiet. A distinct violet so a
   suppressed device is never confused with healthy (green) or info (blue). */
.badge--maintenance {
  background: rgba(139, 92, 246, 0.12);
  color: var(--accent-secondary, #a78bfa);
}

.badge--critical .badge__dot {
  animation: pulse-critical 2s ease-in-out infinite;
}

@keyframes pulse-critical {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.5; }
}

/* Processing spinner — used by the onboarding wizard's Configure Device
   step to show the device-config POST (SSH + API key + NAT + channel
   tests, 10-40s) is working. The keyframes can't live in an inline
   style (CSP allows inline style attributes but not @keyframes), so the
   class lives here; the Configure panel composes it with inline layout
   styles. */
.wizard-spinner {
  display: inline-block;
  width: 20px;
  height: 20px;
  flex: none;
  border: 2px solid var(--border-default, #334155);
  border-top-color: var(--accent-primary, #3b82f6);
  border-radius: 50%;
  animation: wizard-spin 0.7s linear infinite;
}

@keyframes wizard-spin {
  to { transform: rotate(360deg); }
}

/* Utility: monospace */
.font-mono {
  font-family: var(--font-mono);
}

/* Utility: spacing (subset of UI-002 §7 utilities.css that the devices
   list needs today — add more as callers arrive). */
.mt-4 { margin-top: var(--space-4); }

/* Utility: visually hidden. Text that must reach a screen reader but must
   not take space on screen — the long form of a caption that had to be
   abbreviated to fit its panel, for one (see .netw-traffic--4). This is the
   DESKTOP shell's copy; mobile.css has its own .m-visually-hidden and is not
   linked by layouts/base.html, so the two cannot be shared. Off-screen rather
   than display:none or hidden, both of which take the text out of the
   accessibility tree along with the layout. */
.u-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Card body flush — table sits edge to edge inside a card. */
.card-body--flush {
  padding: 0;
}

/* Data tables — UI-001 §6.4 */
.table-wrapper {
  width: 100%;
  overflow-x: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.data-table thead tr {
  background: var(--bg-base);
  border-bottom: 1px solid var(--border-default);
}

.data-table th {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
}

.data-table__th--right { text-align: right; }
.data-table__th--center { text-align: center; }

.data-table tbody tr {
  border-bottom: 1px solid var(--border-subtle);
  transition: background-color var(--transition-fast);
}

.data-table tbody tr:last-child {
  border-bottom: none;
}

.data-table tbody tr:hover {
  background: var(--hover-overlay);
}

.data-table td {
  padding: var(--space-3) var(--space-4);
  color: var(--text-primary);
  vertical-align: middle;
}

.data-table__td--right { text-align: right; }
.data-table__td--center { text-align: center; }

.data-table__empty {
  padding: var(--space-8) var(--space-4);
  text-align: center;
  color: var(--text-tertiary);
}

/* Inline meta text — e.g. "metric_name" suffix next to a rule, or
   "source" after an event_type. Rendered in a small muted font so
   it reads as secondary information. */
.data-table__meta {
  color: var(--text-tertiary);
  font-size: 11px;
}

/* Flash / notice banner — .alert + a severity modifier.
   ~20 desktop templates have been emitting this markup since the
   scaffold, but the class had NO rules in any desktop stylesheet: a
   refused save on /assets/new computed to transparent, no border, no
   padding — a plain grey sentence indistinguishable from body copy,
   while the SAME notice on /m/assets got mobile.css's .m-flash--error
   red box (measured, phone smoke 2026-09-06). That asymmetry is what
   let a duplicate-tag refusal read as "nothing happened".
   Treatment mirrors .m-flash so one message looks the same on both
   surfaces, and uses the status tokens rather than literals. */
.alert {
  margin: 0 0 var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: 13px;
}
.alert--ok {
  background: var(--status-healthy-muted);
  border-color: var(--status-healthy);
}
.alert--warn {
  background: var(--status-warning-muted);
  border-color: var(--status-warning);
}
.alert--err {
  background: var(--status-critical-muted);
  border-color: var(--status-critical);
}

/* Toolbar — filter + search row above a table. */
.toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.toolbar__search {
  position: relative;
  flex: 1 1 240px;
  min-width: 200px;
  max-width: 320px;
}

.toolbar__search-icon {
  position: absolute;
  left: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  stroke: var(--text-tertiary);
  stroke-width: 2;
  fill: none;
  pointer-events: none;
}

.toolbar__search-input {
  width: 100%;
  height: 36px;
  padding: 0 var(--space-3) 0 calc(var(--space-3) * 2 + 16px);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 13px;
}

.toolbar__search-input:focus {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: var(--shadow-focus);
}

.toolbar__search-input::placeholder {
  color: var(--text-tertiary);
}

.toolbar__filter {
  height: 36px;
  padding: 0 var(--space-3);
  background: var(--bg-input);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 13px;
  cursor: pointer;
}

.toolbar__filter:focus {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: var(--shadow-focus);
}

.toolbar__count {
  margin-left: auto;
  font-size: 13px;
  color: var(--text-tertiary);
}

/* Metric card (dashboard KPI) — UI-001 §6.6.
 * Compact card used in the top row of a dashboard. Not a full chart —
 * just a label + big value + optional trend + optional sparkline. */
.metric-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-height: 110px;
}

.metric-card__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.metric-card__value {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.metric-card__value--healthy  { color: var(--status-healthy); }
.metric-card__value--warning  { color: var(--status-warning); }
.metric-card__value--critical { color: var(--status-critical); }
.metric-card__value--default  { color: var(--text-primary); }

.metric-card__trend {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 12px;
  color: var(--text-secondary);
}

.metric-card__trend-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-weight: 700;
  background: var(--bg-elevated);
  color: var(--text-tertiary);
}

.metric-card__trend--up   .metric-card__trend-glyph {
  background: var(--status-healthy-muted);
  color: var(--status-healthy);
}

.metric-card__trend--down .metric-card__trend-glyph {
  background: var(--status-critical-muted);
  color: var(--status-critical);
}

.metric-card__sparkline {
  width: 100%;
  height: 24px;
  margin-top: auto;
  color: var(--accent-primary);
  opacity: 0.8;
}

/* Dashboard grid helpers — UI-001 §10.3.
 * KPI row across the top, then a two-column grid of big charts. */
.dashboard-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4);
}

.dashboard-chart-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}

.dashboard-chart-slot {
  padding: var(--space-3);
  min-height: 220px;
}

@media (max-width: 1100px) {
  .dashboard-kpi-grid   { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dashboard-chart-grid { grid-template-columns: 1fr; }
}

@media (max-width: 600px) {
  .dashboard-kpi-grid   { grid-template-columns: 1fr; }
}

/* Pagination — prev/next footer below a table. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.pagination__label {
  font-size: 13px;
  color: var(--text-tertiary);
}

.pagination__buttons {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* -------------------------------------------------------------------------
 * Fleet dashboard v2 (project_dashboard_v03_target.md, 2026-04-12)
 *
 * No donut gauges. Row 1 is three equal-width cards holding a device
 * heatmap, a big-number + breakdown, and a pass/fail checklist. Row 2
 * is four clickable stat tiles. Row 3 is three scrolling panels. All
 * colours use UI-001 tokens — no hardcoded hex outside tokens.css.
 * ------------------------------------------------------------------------- */

.dash-banner {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
}

.dash-banner-card {
  min-height: 220px;
}

/* --- Heatmap (slot 1) --- */

.heatmap {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.heatmap__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
  gap: var(--space-2);
}

.heatmap__tile {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--text-inverse);
  text-decoration: none;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

.heatmap__tile:hover {
  transform: scale(1.05);
  box-shadow: var(--shadow-focus);
}

.heatmap__tile--healthy  { background: var(--status-healthy); }
.heatmap__tile--warning  { background: var(--status-warning); }
.heatmap__tile--critical { background: var(--status-critical); }
.heatmap__tile--unknown  { background: var(--status-unknown); }

.heatmap__label {
  text-transform: lowercase;
  letter-spacing: -0.02em;
}

.heatmap__empty {
  grid-column: 1 / -1;
  padding: var(--space-4);
  text-align: center;
  color: var(--text-tertiary);
  font-size: 13px;
}

.heatmap__legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-size: 12px;
  color: var(--text-secondary);
}

.heatmap__legend a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--text-secondary);
  text-decoration: none;
}

.heatmap__legend a:hover {
  color: var(--text-primary);
}

.heatmap__legend-sep {
  color: var(--text-tertiary);
}

.heatmap__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
}

.heatmap__dot--healthy  { background: var(--status-healthy); }
.heatmap__dot--warning  { background: var(--status-warning); }
.heatmap__dot--critical { background: var(--status-critical); }
.heatmap__dot--unknown  { background: var(--status-unknown); }

/* --- Fleet Score (slot 2) --- */

.dash-fleet-score {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.dash-fleet-score__value {
  font-family: var(--font-mono);
  font-size: 48px;
  font-weight: 700;
  line-height: 1.0;
  letter-spacing: -0.02em;
  text-decoration: none;
}

.dash-fleet-score__value--healthy  { color: var(--status-healthy); }
.dash-fleet-score__value--warning  { color: var(--status-warning); }
.dash-fleet-score__value--critical { color: var(--status-critical); }

.dash-fleet-score__label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
}

.dash-fleet-score__breakdown {
  list-style: none;
  padding: 0;
  margin: var(--space-2) 0 0 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.dash-fleet-score__breakdown a {
  display: flex;
  justify-content: space-between;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 13px;
}

.dash-fleet-score__breakdown a:hover {
  background: var(--hover-overlay);
  color: var(--text-primary);
}

.dash-fleet-score__row-val {
  font-family: var(--font-mono);
  color: var(--text-primary);
}

/* --- Hostnames Health (slot 3) --- */

.dash-dns-checklist {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.dash-dns-checklist__row a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text-secondary);
  font-size: 14px;
}

.dash-dns-checklist__row a:hover {
  background: var(--hover-overlay);
  color: var(--text-primary);
}

.dash-dns-checklist__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  font-weight: 700;
  font-size: 14px;
}

.dash-dns-checklist__row--healthy  .dash-dns-checklist__icon { color: var(--status-healthy); background: var(--status-healthy-muted); }
.dash-dns-checklist__row--warning  .dash-dns-checklist__icon { color: var(--status-warning); background: var(--status-warning-muted); }
.dash-dns-checklist__row--critical .dash-dns-checklist__icon { color: var(--status-critical); background: var(--status-critical-muted); }

.dash-dns-checklist__label {
  flex: 1;
  color: var(--text-primary);
}

.dash-dns-checklist__count {
  color: var(--text-secondary);
  font-size: 13px;
}

/* --- Devices page filter bar --- */

.device-filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
}

.device-filter-bar__field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 140px;
  flex: 1 1 140px;
}

.device-filter-bar__label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
}

.device-filter-bar__input,
.device-filter-bar__select {
  height: 32px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  background: var(--bg-base);
  border: 1px solid var(--border-default);
  color: var(--text-primary);
  font-size: 13px;
  font-family: var(--font-body);
}

.device-filter-bar__input:focus,
.device-filter-bar__select:focus {
  outline: none;
  border-color: var(--accent-primary);
  box-shadow: var(--shadow-focus);
}

.device-filter-bar__select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-secondary) 50%),
                    linear-gradient(135deg, var(--text-secondary) 50%, transparent 50%);
  background-position: calc(100% - 14px) 14px, calc(100% - 9px) 14px;
  background-size: 5px 5px;
  background-repeat: no-repeat;
  padding-right: 28px;
}

/* --- Grouped filter bar (fleet redesign Phase 1, Mark 2026-08-07) ---
   Organization + Site are the PRIMARY scoping axes of a multi-device-
   per-site fleet: they lead the bar on their own prominent row (taller
   selects, accent labels), the per-device narrowing fields follow. */
.device-filter-bar--grouped {
  /* block, NOT a column flex: a wrap-enabled flex row nested in a
     column flex container gets its intrinsic height computed as if
     every field wrapped onto its own line (Chromium), leaving a dead
     band above the controls. Block flow sizes each row normally. */
  display: block;
}

.device-filter-bar__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
}

.device-filter-bar__row--primary {
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-default);
}

.device-filter-bar__row + .device-filter-bar__row {
  margin-top: var(--space-3);
}

.device-filter-bar__field--primary {
  flex: 1 1 240px;
}

.device-filter-bar__label--primary {
  color: var(--accent-primary);
  font-size: 12px;
}

.device-filter-bar__select--primary {
  height: 40px;
  font-size: 14px;
  border-color: var(--accent-primary);
  background-position: calc(100% - 14px) 18px, calc(100% - 9px) 18px;
}

/* --- Sortable table headers (devices list Type/Hostname/IP/Last Seen) --- */
.data-table__sortlink {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: inherit;
  text-decoration: none;
}

.data-table__sortlink:hover {
  color: var(--text-primary);
}

.data-table__sortarrow {
  font-size: 9px;
  opacity: 0.55;
}

.data-table__sortlink.is-active .data-table__sortarrow {
  opacity: 1;
  color: var(--accent-primary);
}

/* --- Row 1 — Network Health traffic-light row ---
   The card-body is a flex column so the three status panels and the
   total-devices badge share the available vertical space evenly:
   both halves grow proportionally (flex: 1) and the gap between them
   matches the gap between the three panels side-by-side. */
.dash-banner-card .card-body:has(.netw-traffic) {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  height: 100%;
}

.netw-traffic {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  flex: 1;
}

/* Four-panel variant: the /health Uptime card adds an ungraded "no data"
   panel beside the three traffic lights. Without its own track count the
   fourth child wrapped onto a second row and left the card lopsided. */
.netw-traffic--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.netw-traffic__panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  padding: var(--space-4) var(--space-2);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: inherit;
  background: var(--bg-base);
  border: 1px solid var(--border-default);
  transition: transform var(--transition-fast), border-color var(--transition-fast);
}

.netw-traffic__panel:hover {
  transform: translateY(-1px);
  border-color: var(--border-strong);
}

.netw-traffic__panel--online    { border-top: 3px solid var(--status-healthy); }
.netw-traffic__panel--degraded  { border-top: 3px solid var(--status-warning); }
.netw-traffic__panel--offline   { border-top: 3px solid var(--status-critical); }
/* Ungraded fourth panel: "no data" is not a verdict, so it carries the
   neutral border and the default text colour rather than a traffic light. */
.netw-traffic__panel--unknown   { border-top: 3px solid var(--border-strong); }

.netw-traffic__count {
  font-family: var(--font-mono);
  font-size: 40px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
}

.netw-traffic__panel--online  .netw-traffic__count { color: var(--status-healthy); }
.netw-traffic__panel--degraded .netw-traffic__count { color: var(--status-warning); }
.netw-traffic__panel--offline .netw-traffic__count { color: var(--status-critical); }

.netw-traffic__label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-secondary);
  /* Captions vary in length across the four cards, so a wrap is still
     possible on the three-panel ones ("Up to date"). Centre it and keep the
     caption from stretching the track: the panel is a column, so a second
     line grows downward and never pushes the count sideways. The Uptime
     card's four-panel variant below opts out of wrapping entirely. */
  text-align: center;
  min-width: 0;
}

/* Four-panel captions have a quarter of a third of the banner row to sit in.
   The worst case is NOT the widest viewport: .dash-banner stays three-up
   until it collapses at 1100px (the media query at line 1447 of this file),
   so the tightest layout that ever renders is a 1101px viewport, where
   each panel has 42.7px of INNER width (~93px at 1600px, ~57.6px at 1280px).
   The captions are single short words (uptimeStableCaption and friends at
   pages_uptime.go:145-161, budget pinned at 9 characters by
   uptimeCardCaptionMax, pages_uptime.go:168), so they cannot WRAP; this rule
   is what keeps the longest of them from overflowing the panel instead. The
   two halves are a pair: the 9-character pin is only safe BECAUSE of the
   sizing below, and the sizing below is only enough BECAUSE of the pin —
   change one and re-measure the other.

   Measured with a headless Chromium render of this stylesheet at 1101px, the
   caption font falling back to the platform sans:

     11px, .01em, sentence case (the r5 rule)  "Degraded"  51.6px  OVERFLOWS
     11px, .01em, sentence case                "Unstable"  47.3px  OVERFLOWS
     10px, 0,     UPPERCASE                    "UNSTABLE"  53.7px  OVERFLOWS
      9px, 0,     UPPERCASE                    "UNSTABLE"  48.3px  OVERFLOWS
      8px, 0,     UPPERCASE                    "UNSTABLE"  43.0px  OVERFLOWS
      9px, 0,     sentence case (this rule)    "Unstable"  39.5px  fits

   So the uppercase variant that would match the other three banner cards
   does not fit at any legible size — even at 8px it is 0.3px over — and the
   4-up variant keeps `text-transform: none`. What it spends instead is the
   font size, scaled with the viewport: clamp() holds the caption at the
   family's 11px from ~1294px up and eases it to 9px at the 1101px worst case,
   where the widest token ("Unstable") measures 39.5px inside 42.7px. Side
   padding is tightened for the same reason. The three-panel cards are
   untouched — matching their uppercase is a follow-up, not something to buy
   with an overflow. overflow/text-overflow is the belt-and-braces: a caption
   that somehow exceeds the budget is clipped with an ellipsis INSIDE its own
   panel rather than painted over the neighbour's border. */
.netw-traffic--4 .netw-traffic__panel { padding-left: var(--space-1); padding-right: var(--space-1); }

.netw-traffic--4 .netw-traffic__label {
  text-transform: none;
  letter-spacing: 0;
  font-size: clamp(9px, 0.85vw, 11px);
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.netw-traffic__total {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-2);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  text-decoration: none;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.netw-traffic__total:hover {
  border-color: var(--border-default);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-primary);
}

.netw-traffic__total-count {
  font-family: var(--font-mono);
  font-size: 40px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
}

.netw-traffic__total-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-tertiary);
}

/* --- Row 2 — four operational stat cards --- */

.dash-summary-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4);
}

/* .dash-stat-card is a <div> wrapper — a real anchor inside (.dash-stat-card__main)
   wraps the label+value, so we keep clean single-anchor semantics without
   violating HTML5's no-nested-anchors rule. */
.dash-stat-card {
  display: block;
  color: inherit;
  transition: transform var(--transition-fast), border-color var(--transition-fast);
}

.dash-stat-card:hover {
  border-color: var(--border-strong);
  transform: translateY(-1px);
}

.dash-stat-card__main {
  display: block;
  text-decoration: none;
  color: inherit;
}

.dash-stat-card__label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
}

.dash-stat-card__value {
  font-family: var(--font-mono);
  font-size: 32px;
  font-weight: 700;
  line-height: 1.1;
  color: var(--text-primary);
  margin-top: var(--space-2);
}

.dash-stat-card__secondary {
  margin-top: var(--space-2);
  font-size: 12px;
  color: var(--text-secondary);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.dash-stat-card__secondary a {
  color: var(--text-secondary);
  text-decoration: none;
}

.dash-stat-card__secondary a:hover {
  color: var(--text-primary);
  text-decoration: underline;
}

.dash-stat-card__sep {
  color: var(--text-tertiary);
}

/* --- Row 3 — three-column panel row --- */

.dash-panels-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
}

.dash-panel-third {
  min-height: 320px;
}

.dash-scroll {
  max-height: 320px;
  overflow-y: auto;
}

.dash-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.dash-list__row {
  border-bottom: 1px solid var(--border-subtle);
}

.dash-list__row:last-child {
  border-bottom: none;
}

.dash-list__row a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  text-decoration: none;
  color: var(--text-primary);
  font-size: 13px;
}

.dash-list__row a:hover {
  background: var(--hover-overlay);
}

.dash-list__time {
  color: var(--text-tertiary);
  font-size: 11px;
  font-family: var(--font-mono);
  min-width: 64px;
}

.dash-list__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  flex: 0 0 auto;
}

.dash-list__dot--healthy  { background: var(--status-healthy); }
.dash-list__dot--warning  { background: var(--status-warning); }
.dash-list__dot--critical { background: var(--status-critical); }
.dash-list__dot--unknown  { background: var(--status-unknown); }

.dash-list__main {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-list__meta {
  color: var(--text-secondary);
  font-size: 12px;
}

.dash-list__empty {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--text-tertiary);
  font-size: 13px;
}

/* --- Row 4 — Predictive Failures --- */

.dash-predictive {
  list-style: none;
  padding: 0;
  margin: 0;
}

.dash-predictive__row {
  border-bottom: 1px solid var(--border-subtle);
}

.dash-predictive__row:last-child {
  border-bottom: none;
}

.dash-predictive__row a {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  text-decoration: none;
  color: var(--text-primary);
}

.dash-predictive__row a:hover {
  background: var(--hover-overlay);
}

.dash-predictive__host {
  font-size: 13px;
  min-width: 180px;
}

.dash-predictive__reason {
  flex: 1;
  color: var(--text-secondary);
  font-size: 13px;
}

/* --- Row 5 — feeds row (TLDR 60% / OPNsense 40%) --- */

.dash-feeds-row {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: var(--space-4);
}

.dash-feed-card {
  display: flex;
  flex-direction: column;
  max-height: 1440px;
  min-height: 840px;
}

.dash-feed-card .card-body {
  flex: 1;
  overflow-y: auto;
}

/* When the card embeds an upstream page via iframe, the card-body
   hands the scroll to the iframe itself (TLDR's own chrome is what
   scrolls) and the card-body just acts as a sized container. */
.dash-feed-card--embed .card-body {
  overflow: hidden;
  padding: 0;
}

.dash-feed-iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: #fff;
}

.dash-feed-placeholder {
  color: var(--text-tertiary);
  font-size: 13px;
  margin: 0;
}

.dash-feed-stale {
  color: var(--text-tertiary);
  font-size: 13px;
  margin: 0;
  padding: var(--space-3);
}

.dash-feed-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.dash-feed-list__row {
  padding: var(--space-3);
  border-bottom: 1px solid var(--border-default);
}

.dash-feed-list__row:last-child {
  border-bottom: none;
}

.dash-feed-list__time {
  display: inline-block;
  color: var(--text-tertiary);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  margin-right: var(--space-2);
}

.dash-feed-list__title {
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
}

.dash-feed-list__title:hover {
  text-decoration: underline;
}

.dash-feed-list__summary {
  margin: 4px 0 0 0;
  color: var(--text-secondary);
  font-size: 12px;
  line-height: 1.5;
}

/* --- Responsive collapse --- */

@media (max-width: 1100px) {
  .dash-banner       { grid-template-columns: 1fr; }
  .dash-summary-row  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dash-panels-row   { grid-template-columns: 1fr; }
  .dash-feeds-row    { grid-template-columns: 1fr; }
  .netw-traffic      { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .netw-traffic--4   { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 600px) {
  .dash-summary-row  { grid-template-columns: 1fr; }
}

/* ------- tabs bar (used by /settings/* and /admin/* sub-nav) ------- */
.tabs-bar {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border-default, #1e3a5f);
  margin-bottom: 20px;
  overflow-x: auto;
}
.tabs-bar__tab {
  padding: 10px 16px;
  color: var(--text-secondary, #94a3b8);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  cursor: pointer;
  background: transparent;
  border-left: none;
  border-right: none;
  border-top: none;
}
.tabs-bar__tab:hover { color: var(--text-primary, #e2e8f0); }
.tabs-bar__tab--active {
  color: var(--accent-primary, #77BA64);
  border-bottom-color: var(--accent-primary, #77BA64);
}

/* ------- settings form widgets ------- */
.form-card {
  background: var(--bg-surface, #122035);
  border: 1px solid var(--border-default, #1e3a5f);
  border-radius: 12px;
  padding: 24px;
  margin-bottom: 16px;
}
.form-card__title {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary, #94a3b8);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 16px 0;
}
.form-field { display: block; margin-bottom: 12px; }
.form-field__label {
  display: block;
  font-size: 12px;
  color: var(--text-secondary, #94a3b8);
  margin-bottom: 4px;
}
.form-field__input,
.form-field__select,
.form-input,
.form-select,
.input {
  width: 100%;
  background: var(--bg-subtle, #0a1628);
  border: 1px solid var(--border-default, #1e3a5f);
  color: var(--text-primary, #e2e8f0);
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 13px;
}
.form-field__input:focus,
.form-field__select:focus,
.form-input:focus,
.form-select:focus,
.input:focus {
  outline: none;
  border-color: var(--accent-primary, #77BA64);
}
.input--sm {
  width: auto;
  padding: 4px 8px;
  font-size: 12px;
}
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.form-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px;
  background: var(--bg-subtle, #0a1628);
  border-radius: 8px;
}
.form-toggle__switch {
  position: relative;
  width: 36px;
  height: 20px;
  border-radius: 99px;
  border: none;
  cursor: pointer;
  background: #475569;
}
.form-toggle__switch--on { background: var(--accent-primary, #77BA64); }
.form-toggle__switch::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: white;
  transition: transform 0.15s;
}
.form-toggle__switch--on::after { transform: translateX(16px); }

.avatar-circle {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: var(--accent-primary, #77BA64);
  color: var(--text-inverse, #0a1628);
  font-size: 24px;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.avatar-row {
  display: flex;
  align-items: center;
  gap: 20px;
}
.color-swatch {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform 0.15s;
}
.color-swatch:hover { transform: scale(1.1); }
.color-swatch--active { border-color: white; }
.color-swatch-row { display: flex; flex-wrap: wrap; gap: 10px; }

.module-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
@media (max-width: 900px) {
  .module-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 600px) {
  .module-grid { grid-template-columns: 1fr; }
}
.tenant-card-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
@media (max-width: 800px) { .tenant-card-grid { grid-template-columns: 1fr; } }

.notif-matrix { width: 100%; font-size: 13px; }
.notif-matrix th, .notif-matrix td { padding: 8px; }
.notif-matrix th { text-align: left; color: var(--text-secondary); font-weight: 500; }
.notif-matrix td.notif-matrix__cell { text-align: center; }
.notif-cell {
  width: 20px;
  height: 20px;
  border: 1px solid var(--border-default, #1e3a5f);
  border-radius: 4px;
  background: var(--bg-subtle, #0a1628);
}
.notif-cell--on {
  background: var(--accent-primary, #77BA64);
  border-color: var(--accent-primary, #77BA64);
}

.progress-bar {
  width: 100%;
  height: 8px;
  background: var(--bg-subtle, #0a1628);
  border-radius: 99px;
  overflow: hidden;
  margin-top: 4px;
}
.progress-bar__fill {
  height: 100%;
  background: var(--accent-primary, #77BA64);
  border-radius: 99px;
}
.progress-bar__fill--warn { background: #f59e0b; }
.progress-bar__fill--crit { background: #ef4444; }

.flash-banner {
  padding: 12px 16px;
  border-radius: 8px;
  font-size: 13px;
  margin: 16px 0;
  border: 1px solid;
}
.flash-banner--ok {
  background: rgba(52, 211, 153, 0.1);
  border-color: rgba(52, 211, 153, 0.3);
  color: #34d399;
}
.flash-banner--err {
  background: rgba(248, 113, 113, 0.1);
  border-color: rgba(248, 113, 113, 0.3);
  color: #f87171;
}

/* Lab page misconfiguration banner — ERR-8. */
.lab-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 8px;
  border: 1px solid;
  font-size: 13px;
  line-height: 1.45;
}
.lab-banner__icon {
  flex: 0 0 auto;
  font-size: 18px;
  line-height: 1;
  margin-top: 1px;
}
.lab-banner__body { flex: 1 1 auto; }
.lab-banner__title {
  font-weight: 600;
  margin-bottom: 2px;
}
.lab-banner__message { opacity: 0.9; }
.lab-banner--error {
  background: rgba(248, 113, 113, 0.1);
  border-color: rgba(248, 113, 113, 0.35);
  color: #f87171;
}
.lab-banner--info {
  background: rgba(96, 165, 250, 0.1);
  border-color: rgba(96, 165, 250, 0.35);
  color: #60a5fa;
}

/* Network allowlist row direction indicators (task #59).
   here       = row defined at the current scope, editable
   inherited  = row pushed down from a higher scope, read-only
   exposure   = row originating below current scope, read-only */
.allowlist-direction {
  font-size: 11px;
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid;
}
.allowlist-direction--here {
  background: rgba(148, 163, 184, 0.12);
  border-color: rgba(148, 163, 184, 0.35);
  color: #cbd5e1;
}
.allowlist-direction--inherited {
  background: rgba(96, 165, 250, 0.12);
  border-color: rgba(96, 165, 250, 0.4);
  color: #60a5fa;
}
.allowlist-direction--exposure {
  background: rgba(251, 146, 60, 0.12);
  border-color: rgba(251, 146, 60, 0.4);
  color: #fb923c;
}

.allowlist-row--inherited td { background: rgba(96, 165, 250, 0.04); }
.allowlist-row--exposure td  { background: rgba(251, 146, 60, 0.04); }

.allowlist-status {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid;
  text-transform: capitalize;
}
.allowlist-status--ok {
  background: rgba(52, 211, 153, 0.12);
  border-color: rgba(52, 211, 153, 0.4);
  color: #34d399;
}
.allowlist-status--unchecked {
  background: rgba(148, 163, 184, 0.12);
  border-color: rgba(148, 163, 184, 0.35);
  color: #94a3b8;
}
.allowlist-status--resolve_failed,
.allowlist-status--unreachable {
  background: rgba(248, 113, 113, 0.12);
  border-color: rgba(248, 113, 113, 0.4);
  color: #f87171;
}
.allowlist-status--expired {
  background: rgba(251, 146, 60, 0.12);
  border-color: rgba(251, 146, 60, 0.4);
  color: #fb923c;
}

/* Onboarding wizard — 5-step indicator + form shell. Task #81
   port of frontend/src/pages/devices/OnboardingWizard.jsx. */
.wizard-shell {
  max-width: 760px;
  margin: 16px 0;
}
.wizard-steps {
  display: flex;
  align-items: center;
  gap: 0;
  margin-bottom: 8px;
}
.wizard-step {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
}
.wizard-step:last-child { flex: 0 0 auto; }
.wizard-step__dot {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(148, 163, 184, 0.12);
  color: #94a3b8;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid rgba(148, 163, 184, 0.3);
  flex-shrink: 0;
}
.wizard-step--active .wizard-step__dot {
  background: rgba(59, 130, 246, 0.2);
  color: #60a5fa;
  border-color: rgba(59, 130, 246, 0.5);
}
.wizard-step--done .wizard-step__dot {
  background: rgba(52, 211, 153, 0.18);
  color: #34d399;
  border-color: rgba(52, 211, 153, 0.45);
}
.wizard-step__label {
  font-size: 11px;
  color: #94a3b8;
  margin-left: 8px;
  white-space: nowrap;
}
.wizard-step--active .wizard-step__label { color: #e2e8f0; }
.wizard-step__connector {
  flex: 1;
  height: 1px;
  background: rgba(148, 163, 184, 0.25);
  margin: 0 12px;
  min-width: 12px;
}
.wizard-step--done .wizard-step__connector {
  background: rgba(52, 211, 153, 0.4);
}
.wizard-step-label {
  font-size: 12px;
  color: #64748b;
  margin: 4px 0 16px 0;
}
.wizard-body { }
.wizard-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 16px;
}
.wizard-form { max-width: 720px; }

/* internal-chip — toggle for marking a device as internal/lab so it
   gets excluded from fleet metrics. Sits in the device detail page
   Actions card. Uses a native checkbox so CSP doesn't bite, but the
   label draws the visual toggle so the raw <input> is hidden. */
.internal-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--border-subtle);
  border-radius: 9999px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-tertiary);
  cursor: pointer;
  user-select: none;
  transition: all var(--transition-fast);
}
.internal-chip:hover { border-color: var(--accent-primary-muted); color: var(--text-secondary); }
.internal-chip input { accent-color: var(--accent-primary); }
.internal-chip--on {
  background: var(--status-warning-muted);
  border-color: var(--status-warning);
  color: var(--status-warning);
}
.device-detail__flags { display: flex; gap: 8px; flex-wrap: wrap; }

.internal-chip--locked {
  cursor: not-allowed;
  opacity: 0.85;
}
.internal-chip--locked input { pointer-events: none; }

/* ── Platform self-health banner ────────────────────────────────
   Rendered only when something is degraded/down. Red for down,
   amber for degraded. Sits between topbar and main content so it
   never overlaps a tooltip. */
.platform-health-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 20px;
  font-size: 13px;
  color: var(--text-inverse, #0a1628);
  background: var(--status-warning, #f59e0b);
  border-bottom: 1px solid rgba(0, 0, 0, 0.15);
}
.platform-health-banner--down {
  background: var(--status-error, #dc2626);
}
.platform-health-banner__icon {
  font-size: 16px;
  line-height: 1;
}
.platform-health-banner__label {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 11px;
}
.platform-health-banner__detail {
  display: flex;
  gap: 16px;
  font-size: 12px;
  opacity: 0.95;
}
.platform-health-banner__item {
  white-space: nowrap;
}

/* ───────────────────────────────────────────────────────────────
 * Device detail page (templates/pages/device_detail.html)
 *
 * Ports the pre-HTMX 7-tab layout from the Zoe reference onto
 * current design tokens. Tab switching is plain-JS via interactions.js
 * (data-tab-switch / data-tab-panel + .is-active) — no Alpine, no
 * unsafe-eval. Every class is prefixed `.dd-` so nothing here
 * collides with existing card / grid primitives.
 * ───────────────────────────────────────────────────────────────*/
.dd-header {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  padding: 14px 18px;
  margin-top: 8px;
}
.dd-header__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.dd-header__back {
  font-size: 12px;
  color: var(--text-tertiary);
  text-decoration: none;
}
.dd-header__back:hover { color: var(--accent-primary); }
.dd-header__selector {
  background: var(--bg-base);
  color: var(--text-secondary);
  border: 1px solid var(--border-default, var(--border-subtle));
  border-radius: var(--radius-sm, 4px);
  padding: 4px 8px;
  font-size: 12px;
  font-family: var(--font-mono, ui-monospace, monospace);
  cursor: pointer;
  max-width: 260px;
}
.dd-header__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.dd-header__identity { min-width: 0; }
.dd-header__title-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.dd-header__hostname {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.02em;
  font-family: var(--font-mono, ui-monospace, monospace);
}
.dd-header__status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 600;
  text-transform: capitalize;
  padding: 2px 10px;
  border-radius: 9999px;
  color: var(--text-secondary);
  background: var(--bg-base);
  border: 1px solid var(--border-subtle);
}
.dd-header__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}
.dd-header__meta {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 4px;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  font-family: var(--font-mono, ui-monospace, monospace);
}
.dd-header__meta span + span::before {
  content: "·";
  margin-right: 8px;
  color: var(--text-tertiary);
}
.dd-header__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.dd-header__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--bg-base);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
}
.dd-header__btn:hover {
  color: var(--accent-primary);
  border-color: var(--accent-primary);
}
.dd-header__chip { display: inline-flex; align-items: center; }

.dd-infobar {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  padding: 14px 18px;
  margin-top: 12px;
}
.dd-infobar__item label {
  display: block;
  font-size: 11px;
  color: var(--text-tertiary);
  margin-bottom: 2px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dd-infobar__value {
  font-size: 13px;
  color: var(--text-primary);
}

.dd-tabs {
  margin-top: 12px;
}
.dd-tabs__bar {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border-subtle);
  background: transparent;
  overflow-x: auto;
  padding: 0;
  margin-bottom: 14px;
}
.dd-tabs__btn {
  appearance: none;
  border: none;
  background: none;
  padding: 9px 14px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-tertiary);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  font-family: inherit;
}
.dd-tabs__btn:hover {
  color: var(--text-secondary);
  background: var(--hover-overlay, rgba(255,255,255,0.04));
}
.dd-tabs__btn.is-active {
  color: var(--accent-primary);
  border-bottom-color: var(--accent-primary);
}
.dd-tabs__panel {
  display: none;
}
.dd-tabs__panel.is-active {
  display: block;
}

/* Stat grid — 3 equal columns by default, collapses to 1 on narrow.
   dd-statgrid--5 / --6 variants are used on the Monitoring tab for
   the hardware-sensor row (5 cards) and the top summary row (6
   cards) respectively. */
.dd-statgrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 14px;
}
.dd-statgrid--5 { grid-template-columns: repeat(5, 1fr); }
.dd-statgrid--6 { grid-template-columns: repeat(6, 1fr); }
.dd-stat {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  padding: 14px 16px;
}
.dd-stat__label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}
.dd-stat__value {
  font-size: 24px;
  font-weight: 700;
  font-family: var(--font-mono, ui-monospace, monospace);
  color: var(--text-primary);
  line-height: 1;
}
.dd-stat__value--ok   { color: var(--status-healthy, #34d399); }
.dd-stat__value--warn { color: var(--status-warning, #fbbf24); }
.dd-stat__value--err  { color: var(--status-critical, #f87171); }
.dd-stat__value--muted { color: var(--text-tertiary); font-size: 18px; font-weight: 500; }

/* dd-stat--text is the identity-card variant used for the CPU card
   on the Monitoring tab. It presents a compact two-column key/value
   grid instead of the big numeric readout the sibling cards use. */
.dd-stat--text .dd-stat__value { display: none; }
.dd-stat--text {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-auto-rows: min-content;
  row-gap: 2px;
  column-gap: 10px;
  align-items: baseline;
}
.dd-stat--text .dd-stat__label { grid-column: 1 / -1; margin-bottom: 2px; }
.dd-stat__kv {
  font-size: 11px;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dd-stat__kv__value {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 12px;
  color: var(--text-primary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dd-stat__note {
  margin-top: 6px;
  font-size: 10px;
  color: var(--text-tertiary);
  font-style: italic;
}

/* Sensor row — one-line compact readout. Used on the Monitoring
   tab so that CPU identity, temperature, disk life, and three
   stoplight indicators (SMART / CMOS / 7-day trend) share a
   single horizontal line instead of each owning their own card.
   Wraps on narrow viewports. */
.dd-sensorrow {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  align-items: center;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  padding: 10px 14px;
  margin-bottom: 12px;
}
.dd-sensor {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  white-space: nowrap;
}
.dd-sensor__k {
  font-size: 10px;
  font-weight: 600;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dd-sensor__v {
  color: var(--text-primary);
  font-size: 12px;
}
.dd-sensor__v--muted { color: var(--text-tertiary); }
.dd-sensor--dot { cursor: help; }
.dd-sensor__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
  background: var(--text-tertiary);
  box-shadow: 0 0 0 2px rgba(0,0,0,0.2);
}
.dd-sensor__dot--ok       { background: var(--status-healthy, #34d399); }
.dd-sensor__dot--warn     { background: var(--status-warning, #fbbf24); }
.dd-sensor__dot--err      { background: var(--status-critical, #f87171); }
.dd-sensor__dot--unknown  { background: var(--text-tertiary); }

/* Memory/storage hbars container when placed inside a chart-grid
   cell. Keeps the card the same visual height as a chart so the
   two-column row stays tidy. */
.dd-hbars-card { min-height: 160px; }
.dd-hbars-card .dd-hbars { padding: 14px 16px; }

/* Horizontal usage bars inside a card (RAM / Swap / Disk row on
   the Monitoring tab). Grid layout so label, track, value, hint
   line up consistently across rows. */
.dd-hbars {
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.dd-hbar {
  display: grid;
  grid-template-columns: 60px 1fr 60px auto;
  align-items: center;
  gap: 10px;
}
.dd-hbar__label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dd-hbar__track {
  height: 10px;
  background: var(--bg-base);
  border-radius: 9999px;
  overflow: hidden;
}
.dd-hbar__fill {
  height: 100%;
  border-radius: 9999px;
  transition: width 0.4s ease;
  background: var(--accent-primary);
}
.dd-hbar__fill--ok    { background: var(--status-healthy, #34d399); }
.dd-hbar__fill--warn  { background: var(--status-warning, #fbbf24); }
.dd-hbar__fill--err   { background: var(--status-critical, #f87171); }
.dd-hbar__fill--muted { background: rgba(255,255,255,0.15); }
.dd-hbar__value {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 12px;
  color: var(--text-primary);
  text-align: right;
}
.dd-hbar__hint {
  font-size: 10px;
  color: var(--text-tertiary);
  font-style: italic;
}

/* Progress (CPU/Mem/Disk bars on the Monitoring + Health tabs) */
.dd-progress {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  padding: 14px 16px;
}
.dd-progress__top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.dd-progress__label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dd-progress__value {
  font-size: 16px;
  font-weight: 700;
  font-family: var(--font-mono, ui-monospace, monospace);
  color: var(--text-primary);
}
.dd-progress__track {
  width: 100%;
  height: 8px;
  background: var(--bg-base);
  border-radius: 9999px;
  overflow: hidden;
}
.dd-progress__fill {
  height: 100%;
  border-radius: 9999px;
  transition: width 0.4s ease;
  background: var(--accent-primary);
}
.dd-progress__fill--ok   { background: var(--status-healthy, #34d399); }
.dd-progress__fill--warn { background: var(--status-warning, #fbbf24); }
.dd-progress__fill--err  { background: var(--status-critical, #f87171); }

/* Info list — key/value rows with a dotted key column */
.dd-infolist {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  overflow: hidden;
}
.dd-infolist__row {
  display: flex;
  align-items: center;
  padding: 9px 16px;
  border-bottom: 1px solid rgba(255,255,255,0.03);
}
.dd-infolist__row:last-child { border-bottom: none; }
.dd-infolist__key {
  font-size: 12px;
  color: var(--text-tertiary);
  width: 180px;
  flex-shrink: 0;
  cursor: help;
  border-bottom: 1px dotted rgba(255,255,255,0.1);
}
.dd-infolist__value {
  font-size: 12px;
  color: var(--text-primary);
}
/* Site row + "Move to another site" control (components/device_move_site.html,
   C123/C131). The control is a real button beside the site name, in the
   value column like the row's sibling actions (Report in, Reachback);
   the <summary> IS the button, so the disclosure marker is suppressed
   and the org/site form drops in beneath it when opened. */
.dd-infolist--siterow { margin-top: var(--space-3); }
.dd-infolist__row--siterow { align-items: flex-start; }
.dd-infolist__row--siterow > .dd-infolist__key { padding-top: var(--space-2); }
.dd-siterow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.dd-siterow__name {
  align-self: flex-start;
  padding-top: var(--space-2);
}
.dd-siterow__move > summary {
  list-style: none;
  cursor: pointer;
}
.dd-siterow__move > summary::-webkit-details-marker { display: none; }
.dd-siterow__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-2);
  min-width: 260px;
}

/* Card + empty state shared across tabs */
.dd-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  overflow: hidden;
}
.dd-card__header {
  padding: 11px 16px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  border-bottom: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.dd-card__body { padding: 14px 16px; font-size: 13px; color: var(--text-secondary); }
.dd-card__count {
  font-size: 10px;
  font-weight: 700;
  color: var(--accent-primary);
  background: var(--accent-primary-muted, rgba(119,186,100,0.15));
  padding: 1px 6px;
  border-radius: 4px;
}
.dd-empty {
  padding: 18px 16px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: 12px;
}
.dd-empty a { color: var(--accent-primary); }

/* Event list — dot + message + meta row */
.dd-eventlist { padding: 0; }
.dd-event {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid rgba(255,255,255,0.03);
}
.dd-event:last-child { border-bottom: none; }
.dd-event__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 6px;
}
.dd-event__dot--critical { background: var(--status-critical, #f87171); }
.dd-event__dot--warning  { background: var(--status-warning, #fbbf24); }
.dd-event__dot--info     { background: var(--accent-secondary, #60a5fa); }
.dd-event__body { flex: 1; min-width: 0; }
.dd-event__msg {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-primary);
}
.dd-event__meta {
  font-size: 10px;
  color: var(--text-tertiary);
  margin-top: 2px;
  font-family: var(--font-mono, ui-monospace, monospace);
}

/* Monitoring bar above the chart grid */
.dd-monitoring__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  font-size: 12px;
  color: var(--text-tertiary);
}
.dd-chartgrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

/* Health score card — big number + bar */
.dd-health {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  padding: 20px;
}
.dd-health__top {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 14px;
}
.dd-health__score {
  font-size: 40px;
  font-weight: 700;
  font-family: var(--font-mono, ui-monospace, monospace);
  line-height: 1;
}
.dd-health__label {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}
.dd-health__desc {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-top: 2px;
}

/* Firmware current-version banner */
.dd-firmware {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  padding: 16px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.dd-firmware__label {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-bottom: 4px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dd-firmware__version {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 18px;
  color: var(--text-primary);
}

/* Access tab button row */
.dd-access__buttons {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* Console / Serial / WebGUI tabs — shared chrome + xterm/iframe mount.
   The dd-console wrapper is reused for both SSH-over-WS (Console tab)
   and virsh PTY (Serial tab); dd-webgui wraps the iframe. Each has a
   thin bar on top matching the rest of the device-detail cards and a
   fixed-height mount area below so xterm/iframe sizing is stable. */
.dd-console {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  background: var(--bg-surface);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 560px;
}
.dd-console__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--bg-surface-2, var(--bg-surface));
  border-bottom: 1px solid var(--border-subtle);
  flex-wrap: wrap;
}
.dd-console__name {
  color: var(--text-primary);
  font-size: 12px;
}
.dd-console__badge {
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(96, 165, 250, 0.18);
  color: var(--accent-primary, #60a5fa);
}
.dd-console__badge--serial {
  background: rgba(34, 197, 94, 0.18);
  color: var(--status-healthy, #22c55e);
}
.dd-console__status {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-left: auto;
}
.dd-console__popout {
  font-size: 11px;
  text-decoration: none;
  padding: 3px 10px;
  border-radius: 6px;
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
}
.dd-console__popout:hover {
  color: var(--accent-primary);
  border-color: var(--accent-primary);
}
.dd-console__term {
  flex: 1;
  min-height: 520px;
  background: #0a1628;
  padding: 6px 8px;
}
.dd-console__term .xterm {
  height: 100%;
}

.dd-webgui {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg, 8px);
  background: var(--bg-surface);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 720px;
}
.dd-webgui__frame {
  flex: 1;
  width: 100%;
  border: 0;
  background: #ffffff;
  min-height: 680px;
}

/* Responsive collapse */
@media (max-width: 1200px) {
  .dd-statgrid--6 { grid-template-columns: repeat(3, 1fr); }
  .dd-statgrid--5 { grid-template-columns: repeat(5, 1fr); }
}
@media (max-width: 900px) {
  .dd-statgrid,
  .dd-statgrid--5,
  .dd-statgrid--6 { grid-template-columns: 1fr; }
  .dd-infobar  { grid-template-columns: repeat(2, 1fr); }
  .dd-chartgrid { grid-template-columns: 1fr; }
  .dd-hbar { grid-template-columns: 54px 1fr 60px; }
  .dd-hbar__hint { display: none; }
}
@media (max-width: 600px) {
  .dd-infobar  { grid-template-columns: 1fr; }
}

/* =========================================================================
   Monitoring — Real-time / History / Compare
   Ported from zoe-reference monitoring_realtime/history/compare.html.
   Scoped with mon-rt- / mon-hist- / mon-cmp- prefixes so the rules don't
   collide with device-detail (dd-) or dashboard (dash-) layouts.
   ========================================================================= */

/* Real-time */
.mon-rt-counters {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 16px;
}
@media (min-width: 768px) { .mon-rt-counters { grid-template-columns: repeat(4, 1fr); } }
.mon-rt-card {
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 16px 20px;
}
.mon-rt-card__label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.04em; }
.mon-rt-card__value { font-size: 24px; font-weight: 700; font-family: var(--font-mono); color: var(--text-primary); }
.mon-rt-card__value--blue { color: #60a5fa; }
.mon-rt-card__value--green { color: #34d399; }
.mon-rt-card__value--accent { color: var(--accent-primary); }
.mon-rt-card__value--amber { color: #fbbf24; }

.mon-rt-panel {
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 20px; margin-bottom: 16px;
}
.mon-rt-panel__title { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 16px; }

.mon-rt-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;
}
@media (min-width: 640px) { .mon-rt-grid { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1024px) { .mon-rt-grid { grid-template-columns: repeat(6, 1fr); } }
.mon-rt-tile {
  border: 1px solid; border-radius: var(--radius-lg); padding: 12px;
  display: flex; flex-direction: column; gap: 4px;
  text-decoration: none; color: inherit;
  transition: filter var(--transition-fast);
}
.mon-rt-tile:hover { filter: brightness(1.15); }
.mon-rt-tile--up { background: rgba(34,197,94,0.12); border-color: rgba(34,197,94,0.3); }
.mon-rt-tile--down { background: rgba(239,68,68,0.12); border-color: rgba(239,68,68,0.3); }
.mon-rt-tile--unknown { background: rgba(107,114,128,0.12); border-color: rgba(107,114,128,0.3); }
.mon-rt-tile__name { font-size: 12px; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mon-rt-tile__meta { display: flex; }
.mon-rt-tile__status {
  font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 9999px;
  display: inline-block; width: fit-content; text-transform: capitalize;
}
.mon-rt-tile__status--up { background: var(--status-healthy-muted); color: var(--status-healthy); }
.mon-rt-tile__status--down { background: var(--status-critical-muted); color: var(--status-critical); }
.mon-rt-tile__status--unknown { background: rgba(107,114,128,0.2); color: #9ca3af; }
.mon-rt-empty { grid-column: 1 / -1; text-align: center; padding: 40px; color: var(--text-tertiary); }

/* History */
.mon-hist-controls {
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 20px;
  display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end;
  margin-bottom: 16px;
}
.mon-hist-field { display: flex; flex-direction: column; gap: 4px; }
.mon-hist-field__label { font-size: 11px; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.04em; }
.mon-hist-field select, .mon-hist-field input {
  background: var(--bg-base); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 6px 12px; font-size: 13px;
  color: var(--text-primary); min-width: 180px;
}
.mon-hist-field select:focus, .mon-hist-field input:focus {
  outline: none; border-color: var(--accent-primary);
}
.mon-hist-range-btns { display: flex; gap: 4px; }
.mon-hist-range-btn {
  padding: 6px 12px; font-size: 12px; border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle); background: transparent;
  color: var(--text-tertiary); cursor: pointer; transition: all var(--transition-fast);
}
.mon-hist-range-btn:hover { border-color: var(--text-secondary); }
.mon-hist-range-btn--active {
  background: var(--accent-primary-muted); border-color: var(--accent-primary);
  color: var(--accent-primary);
}
.mon-hist-panel {
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 20px; margin-bottom: 16px;
}
.mon-hist-panel__title { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 12px; }
.mon-hist-panel__header {
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;
}
.mon-hist-empty { text-align: center; padding: 48px; color: var(--text-tertiary); font-size: 13px; }
.mon-hist-table-scroll { overflow-x: auto; max-height: 400px; overflow-y: auto; }
.mon-hist-table { width: 100%; font-size: 13px; border-collapse: collapse; }
.mon-hist-table th {
  text-align: left; padding: 8px 12px; font-size: 11px; font-weight: 600;
  color: var(--text-tertiary); border-bottom: 1px solid var(--border-subtle);
  text-transform: uppercase; letter-spacing: 0.04em;
  position: sticky; top: 0; background: var(--bg-surface);
}
.mon-hist-table td {
  padding: 8px 12px; color: var(--text-secondary); font-family: var(--font-mono); font-size: 12px;
  border-bottom: 1px solid rgba(30,37,54,0.5);
}
.mon-hist-table tbody tr:hover { background: var(--hover-overlay); }

/* Compare */
.mon-cmp-controls {
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 20px;
  display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start;
  margin-bottom: 16px;
}
.mon-cmp-field { display: flex; flex-direction: column; gap: 6px; min-width: 220px; }
.mon-cmp-field--grow { flex: 1; }
.mon-cmp-field__label { font-size: 11px; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.04em; }
.mon-cmp-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.mon-cmp-chip {
  padding: 6px 12px; font-size: 12px; border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle); background: transparent;
  color: var(--text-tertiary); cursor: pointer; transition: all var(--transition-fast);
}
.mon-cmp-chip:hover { border-color: var(--text-secondary); }
.mon-cmp-chip--active {
  background: var(--accent-primary-muted); border-color: var(--accent-primary);
  color: var(--accent-primary);
}
.mon-cmp-panel {
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 20px; margin-bottom: 16px;
}
.mon-cmp-panel__title { font-size: 13px; font-weight: 600; color: var(--text-secondary); }
.mon-cmp-panel__header {
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;
}
.mon-cmp-legend { display: flex; gap: 12px; }
.mon-cmp-legend__item { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--text-tertiary); }
.mon-cmp-legend__dot { width: 12px; height: 12px; border-radius: 2px; }
.mon-cmp-empty { text-align: center; padding: 48px; color: var(--text-tertiary); font-size: 13px; }
.mon-cmp-chart { width: 100%; }

.mon-cmp-details {
  display: grid; grid-template-columns: repeat(1, 1fr); gap: 12px; margin-bottom: 16px;
}
@media (min-width: 768px) { .mon-cmp-details { grid-template-columns: repeat(2, 1fr); } }
.mon-cmp-card {
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 20px;
}
.mon-cmp-card__header {
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;
}
.mon-cmp-card__name { font-size: 13px; font-weight: 500; color: var(--text-primary); }
.mon-cmp-card__status {
  display: inline-block; font-size: 10px; font-weight: 600;
  padding: 2px 8px; border-radius: 9999px; text-transform: capitalize;
}
/* Chip vocabulary matches the /devices list and the uptime page: Online /
   Degraded / Offline (C196). The class suffix IS the rendered label — the
   compare script sets both from the same snapshot field and CSS capitalizes
   it — so the Go-emitted strings and these class names change together.
   There is no --unknown: the control-aware headline is total, every device
   buckets into exactly one of the three. */
.mon-cmp-card__status--online { background: var(--status-healthy-muted); color: var(--status-healthy); }
.mon-cmp-card__status--degraded { background: var(--status-warning-muted); color: var(--status-warning); }
.mon-cmp-card__status--offline { background: var(--status-critical-muted); color: var(--status-critical); }
.mon-cmp-card__metrics {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
}
.mon-cmp-card__label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.mon-cmp-card__value { font-family: var(--font-mono); font-size: 18px; color: var(--text-primary); }

/* PR-7 (role-taxonomy v2 + customer-hierarchy): admin polish ------- */

/* The shared <dialog class="modal"> slot itself. Without this the
   dialog renders with UA chrome — a white box on the dark theme. */
dialog.modal {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 0;
}
dialog.modal::backdrop {
  background: rgba(4, 10, 20, 0.6);
}

/* HTMX confirm-modal partial (rendered into the existing dialog
   modal slot used by /admin/tenants, /admin/users, /admin/orgs,
   /admin/sites). Mirrors the form-card padding so confirm + form
   open with the same chrome. */
.confirm-modal {
  padding: 20px 24px;
  min-width: 320px;
  max-width: 480px;
}
.confirm-modal__title { font-size: 16px; color: var(--text-primary); margin-bottom: 8px; }
.confirm-modal__message { font-size: 13px; line-height: 1.4; }

/* Per-page filter bar above admin tables. Compact selects sized to
   match the data-table chrome on /admin/users, /admin/tenants, etc. */
.admin-filter-bar .form-field { display: inline-flex; flex-direction: column; gap: 2px; }
.admin-filter-bar .form-field__input { padding: 4px 8px; font-size: 12px; }

/* Default-admin badge sizing — used inline next to user names on
   /admin/users to mark the ★ Default holder for each role/scope. */
.default-admin-badge {
  display: inline-block;
  font-size: 14px;
  vertical-align: middle;
  cursor: help;
}

/* Bundle Q (2026-05-04) — row-hover summary tooltip. The element is
   appended to <body> by interactions.js so it escapes any overflow
   container; CSS positions it via fixed coords the JS sets each
   mouseenter. Surface + border match the .card token so a tooltip
   feels like a small floating card. Pointer-events: none keeps the
   tooltip from intercepting clicks meant for the row underneath —
   click navigation (Bundle I) still fires when the cursor passes
   through the tooltip area. */
.row-tooltip {
  position: fixed;
  z-index: 1100;
  max-width: 400px;
  max-height: 50vh;
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md, 8px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  padding: var(--space-3, 12px);
  pointer-events: none;
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-primary);
}
.row-tooltip__title {
  font-weight: 600;
  margin-bottom: 2px;
  word-break: break-word;
}
.row-tooltip__subtitle {
  color: var(--text-secondary);
  font-size: 12px;
  margin-bottom: var(--space-2, 8px);
}
.row-tooltip__fields {
  display: grid;
  grid-template-columns: minmax(70px, max-content) 1fr;
  gap: 2px var(--space-3, 12px);
  margin: 0;
}
.row-tooltip__label {
  color: var(--text-secondary);
  font-size: 12px;
  margin: 0;
}
.row-tooltip__value {
  color: var(--text-primary);
  margin: 0;
  word-break: break-word;
}

/* Admin org/site form specifics — the create/edit fragments share
   the form-row layout so a single tweak covers both. */
.admin-org-form .form-row,
.admin-site-form .form-row { margin-bottom: 12px; }

/* Bundle AF — reachability pill row on the device-detail Overview tab.
   Four pills (icmp/ssh/api/snmp) sourced from the canonical
   monitoring.reachability_status row. State drives the colour:
     up      → healthy   (green)
     down    → critical  (red)
     stale   → warning   (amber)
     unknown → unknown   (grey)
   Mirrors the existing --status-* token convention so dark/light
   themes pick up the right shade automatically. */
.dd-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 1;
  border: 1px solid var(--text-tertiary);
  background: var(--status-unknown-muted);
  color: var(--text-secondary);
}
.dd-pill__kind { font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.dd-pill__state { color: var(--text-tertiary); }
.dd-pill__stat { color: var(--text-secondary); font-size: 11px; }
.dd-pill--up      { border-color: var(--status-healthy);  background: var(--status-healthy-muted);  color: var(--status-healthy); }
.dd-pill--down    { border-color: var(--status-critical); background: var(--status-critical-muted); color: var(--status-critical); }
.dd-pill--auth-rejected { border-color: var(--status-critical); background: var(--status-critical-muted); color: var(--status-critical); }
.dd-pill--stale   { border-color: var(--status-warning);  background: var(--status-warning-muted);  color: var(--status-warning); }
.dd-pill--unknown { border-color: var(--status-unknown);  background: var(--status-unknown-muted);  color: var(--status-unknown); }

/* Stage-1 config-delta viewer (Mark 2026-05-19 "do both") — used on
   /config-backups/{id}. The unified-diff content is pre-built by
   renderUnifiedDiffHTML in pages_entity_detail.go; each line is wrapped
   in one of three span classes below. The healthy/critical-muted vars
   already track the platform's dark/light themes, so the diff card
   reads correctly under both. */
.config-diff {
  white-space: pre-wrap;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 12px;
  line-height: 1.5;
  margin: 0;
  background: var(--bg-elevated, #1a1a1a);
  border-radius: 4px;
  padding: 8px;
  max-height: 60vh;
  overflow-y: auto;
}
.config-diff .diff-add { display: block; background: var(--status-healthy-muted);  color: var(--status-healthy); }
.config-diff .diff-del { display: block; background: var(--status-critical-muted); color: var(--status-critical); }
.config-diff .diff-ctx { display: block; color: var(--text-tertiary); }

/* ───────────────────────────────────────────────────────────────────
 * Device-status overview (View 1 — technician-focused redesign).
 * Event-led block at the top of the device-detail Overview tab. Reuses
 * the existing tokens / dd-card / badge / dd-pill / font-mono; only the
 * banner / sparkline / whose-side primitives below are new.
 * ─────────────────────────────────────────────────────────────────── */
.ds-overview { display: flex; flex-direction: column; gap: 16px; margin-bottom: 18px; }
.ds-overview .section-label { font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--text-tertiary); }
.ds-overview .muted { color: var(--text-tertiary); }
.ds-demoted { opacity: .62; }

/* RIGHT-NOW banner */
.now-banner { border-radius: var(--radius-lg); padding: 18px 20px; display: flex; align-items: center; gap: 16px; border: 1px solid var(--border-default); }
.now-banner--ok      { background: linear-gradient(0deg, var(--status-healthy-muted), var(--status-healthy-muted)), var(--bg-surface); border-color: var(--status-healthy); }
.now-banner--alert   { background: linear-gradient(0deg, var(--status-critical-muted), var(--status-critical-muted)), var(--bg-surface); border-color: var(--status-critical); }
.now-banner--unknown { background: linear-gradient(0deg, var(--status-warning-muted), var(--status-warning-muted)), var(--bg-surface); border-color: var(--status-warning); }
.now-banner--maintenance { background: linear-gradient(0deg, rgba(139, 92, 246, 0.10), rgba(139, 92, 246, 0.10)), var(--bg-surface); border-color: rgba(139, 92, 246, 0.55); }
.now-banner__icon { width: 46px; height: 46px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 22px; flex-shrink: 0; }
.now-banner--ok      .now-banner__icon { background: var(--status-healthy-muted);  color: var(--status-healthy); }
.now-banner--alert   .now-banner__icon { background: var(--status-critical-muted); color: var(--status-critical); }
.now-banner--unknown .now-banner__icon { background: var(--status-warning-muted);  color: var(--status-warning); font-weight: 700; }
.now-banner--maintenance .now-banner__icon { background: rgba(139, 92, 246, 0.14); color: #a78bfa; }
.now-banner--maintenance .now-banner__headline { color: #a78bfa; }
.now-banner__main { flex: 1; }
.now-banner__headline { font-family: var(--font-display); font-size: 20px; font-weight: 700; letter-spacing: -.01em; }
.now-banner--ok      .now-banner__headline { color: var(--status-healthy); }
.now-banner--alert   .now-banner__headline { color: var(--status-critical); }
.now-banner--unknown .now-banner__headline { color: var(--status-warning); }
.now-banner__sub { margin-top: 3px; color: var(--text-secondary); font-size: 13px; }
.now-banner__sub b { color: var(--text-primary); }
.now-banner__aside { text-align: right; font-size: 12px; color: var(--text-tertiary); white-space: nowrap; }
.now-banner__aside .big { font-family: var(--font-mono); font-size: 18px; color: var(--text-primary); display: block; }

/* Last-incident inline line */
.ds-lastline { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13px; color: var(--text-secondary); }
.ds-lastline b { color: var(--text-primary); }

/* Per-WAN grid */
.ds-wan-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.ds-wan-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.ds-wan-name { color: var(--text-tertiary); font-size: 12px; }
/* State cluster: the degraded badge sits BESIDE the up/down pill, never
   inside it — a circuit can be up and degraded at the same time. */
.ds-wan-state { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.ds-wan-state .badge { height: 18px; font-size: 10px; padding: 1px 6px; }
.ds-wan-now { display: flex; gap: 18px; margin-top: 12px; flex-wrap: wrap; }
.ds-now-item .ds-now-label { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-tertiary); }
.ds-now-value { font-family: var(--font-mono); font-size: 18px; color: var(--text-primary); display: flex; align-items: center; gap: 5px; margin-top: 2px; }
.trend-up   { color: var(--status-healthy); }
.trend-down { color: var(--status-critical); }
.trend-flat { color: var(--text-secondary); }

/* Sparkline (div-bar approach — mirrors the report sparkline) */
.sparkline { display: flex; flex-direction: column; gap: 6px; }
.sparkline-track { display: flex; align-items: flex-end; gap: 1px; height: 54px; width: 100%; background: var(--bg-base); border-radius: var(--radius-sm); padding: 4px 5px; position: relative; }
.sparkline-bar { flex: 1 1 0; min-width: 0; border-radius: 1px 1px 0 0; background: var(--status-healthy); opacity: .85; }
.sparkline-bar--healthy  { background: var(--status-healthy); }
.sparkline-bar--warning  { background: var(--status-warning); }
.sparkline-bar--critical { background: var(--status-critical); }
.sparkline-bar--unknown  { background: var(--status-unknown); opacity: .45; }
.sparkline-axis { display: flex; justify-content: space-between; font-size: 10px; color: var(--text-tertiary); font-family: var(--font-mono); }
.sparkline-marker { position: absolute; top: -2px; font-size: 10px; line-height: 1; transform: translateX(-50%); }
.ds-legend { display: flex; gap: 14px; font-size: 11px; color: var(--text-tertiary); align-items: center; margin-top: 10px; flex-wrap: wrap; }
.ds-legend span { display: inline-flex; align-items: center; gap: 5px; }
.ds-legend i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }

/* Whose-side row */
.ds-side-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.ds-side-cell { padding: 14px 16px; }
.ds-side-cell + .ds-side-cell { border-left: 1px solid var(--border-subtle); }
.ds-side-cap { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-tertiary); margin-bottom: 6px; }
.ds-side-verdict { font-weight: 600; color: var(--text-primary); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ds-side-stat { font-weight: 400; font-size: 12px; color: var(--text-secondary); }
.ds-side-detail { margin-top: 6px; font-size: 12px; color: var(--text-secondary); }
.ds-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex-shrink: 0; background: var(--status-unknown); }
.ds-dot--healthy  { background: var(--status-healthy); }
.ds-dot--warning  { background: var(--status-warning); }
.ds-dot--critical { background: var(--status-critical); }
.ds-dot--unknown  { background: var(--status-unknown); }

/* Muted footer */
.ds-footnote { font-size: 12px; color: var(--text-tertiary); line-height: 1.6; }

@media (max-width: 900px) {
  .ds-wan-grid { grid-template-columns: 1fr; }
  .ds-side-row { grid-template-columns: 1fr; }
  .ds-side-cell + .ds-side-cell { border-left: none; border-top: 1px solid var(--border-subtle); }
}

/* ════════════════════════════════════════════════════════════════════
   Fleet active-incidents hero (View 3 of the technician-focused
   redesign). The sustained problems happening NOW lead the dashboard;
   the rolled-up Fleet Score + traffic lights are demoted below.
   Reuses .card / .font-mono; new classes are scoped under
   .fleet-incidents so they can't bleed into the rest of the chrome.
   ════════════════════════════════════════════════════════════════════ */
.fleet-incidents { margin-bottom: 18px; }
.fleet-incidents .section-label { display: block; font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--text-tertiary); margin: 22px 0 8px; }
.fleet-incidents .section-label:first-child { margin-top: 0; }
.fleet-incidents .muted { color: var(--text-tertiary); text-transform: none; letter-spacing: 0; font-weight: 400; }

.incident-stack { display: flex; flex-direction: column; gap: 12px; }

/* one incident card — left border keyed to severity */
.incident { display: block; border: 1px solid var(--border-subtle); border-left: 4px solid var(--status-unknown); border-radius: var(--radius-lg); background: var(--bg-surface); overflow: hidden; text-decoration: none; color: inherit; }
.incident:hover { background: var(--hover-overlay, rgba(255,255,255,.02)); }
.incident--critical { border-left-color: var(--status-critical); box-shadow: 0 0 0 1px var(--status-critical-muted); }
.incident--warning  { border-left-color: var(--status-warning); }
.incident__top { display: flex; align-items: flex-start; gap: 14px; padding: 16px 18px; }
.incident__pulse { width: 12px; height: 12px; border-radius: 50%; margin-top: 5px; flex-shrink: 0; background: var(--status-unknown); }
.incident--critical .incident__pulse { background: var(--status-critical); }
.incident--warning  .incident__pulse { background: var(--status-warning); }
.incident__body { flex: 1; min-width: 0; }
.incident__device { font-size: 17px; font-weight: 700; color: var(--text-primary); }
.incident__what { margin-top: 3px; color: var(--text-secondary); font-size: 13px; }
.incident__what b { color: var(--text-primary); }
.incident__dur { text-align: right; white-space: nowrap; }
.incident__dur-big { font-size: 24px; color: var(--status-critical); line-height: 1; }
.incident--warning .incident__dur-big { color: var(--status-warning); }
.incident__dur-lbl { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-tertiary); margin-top: 3px; }
.incident__meta { display: flex; flex-wrap: wrap; gap: 10px 22px; padding: 13px 18px 14px; border-top: 1px solid var(--border-subtle); font-size: 12px; }
.incident__k { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-tertiary); }
.incident__v { color: var(--text-primary); margin-top: 2px; }
.incident__v--link { color: var(--accent-primary); font-weight: 600; }
.incident__action { margin-left: auto; text-align: right; }

/* business/off + whose-side tags */
.ftag { display: inline-block; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; padding: 2px 7px; border-radius: 999px; line-height: 1.4; }
.ftag--biz  { background: var(--status-warning-muted);  color: var(--status-warning); }
.ftag--off  { background: var(--status-unknown-muted);  color: var(--text-secondary); }
.ftag--isp  { background: var(--status-info-muted, var(--accent-primary-muted)); color: var(--status-info, var(--accent-primary)); }
.ftag--ours { background: var(--status-critical-muted); color: var(--status-critical); }

/* honest empty state */
.incident-empty .card-body { display: flex; align-items: center; gap: 10px; color: var(--text-secondary); font-size: 13px; }
.incident-empty__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--status-healthy); flex-shrink: 0; }

/* recently-recovered strip */
.recovered-row { display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--border-subtle); font-size: 13px; text-decoration: none; color: inherit; }
.recovered-row:last-child { border-bottom: none; }
.recovered-row:hover { background: var(--hover-overlay, rgba(255,255,255,.02)); }
.recovered-row__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--status-healthy); flex-shrink: 0; }
.recovered-row__dev { font-weight: 600; color: var(--text-primary); min-width: 120px; }
.recovered-row__desc { color: var(--text-secondary); flex: 1; }
.recovered-row__when { color: var(--text-tertiary); font-size: 12px; white-space: nowrap; }

/* demote the old fleet-score/traffic-light banner below the hero */
.dash-banner--demoted { opacity: .72; }

@media (max-width: 700px) {
  .incident__top { flex-wrap: wrap; }
  .recovered-row__dev { min-width: 90px; }
}

/* ── Device decommission live progress panel ─────────────────────────────
 * fragments/decommission_progress.html — swapped into
 * #decommission-progress on the devices list by the row delete button
 * (or bootstrapped from ?djob= after the device-detail form delete) and
 * self-polling while the background teardown job runs. Step colors use
 * the functional status tokens so ✓/✗/skipped read the same as badges. */
.decom-progress__head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.decom-progress__title {
  font-weight: 600;
  color: var(--text-primary);
}
.decom-progress__steps {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.decom-progress__step {
  display: flex;
  gap: 8px;
  align-items: baseline;
  font-size: 13px;
  color: var(--text-secondary);
}
.decom-progress__icon {
  width: 16px;
  flex: 0 0 16px;
  text-align: center;
  font-weight: 700;
  color: var(--text-tertiary);
}
.decom-progress__step--ok .decom-progress__icon { color: var(--status-healthy); }
.decom-progress__step--failed .decom-progress__icon { color: var(--status-critical); }
.decom-progress__step--failed .decom-progress__label { color: var(--status-critical); }
.decom-progress__step--skipped { opacity: 0.65; }
.decom-progress__step--running .decom-progress__label {
  color: var(--text-primary);
  font-weight: 600;
}
.decom-progress__detail {
  font-size: 12px;
  color: var(--text-tertiary);
}
.decom-progress__final {
  margin-top: 10px;
  font-size: 13px;
  color: var(--text-secondary);
}
.decom-progress__final--success { color: var(--status-healthy); }
.decom-progress__final--warning { color: var(--status-warning); }
.decom-progress__final--error { color: var(--status-critical); }
.decom-progress__spinner {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent-primary);
  border-radius: 50%;
  display: inline-block;
  animation: decom-spin 0.8s linear infinite;
}
@keyframes decom-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .decom-progress__spinner { animation: none; }
}

/* ============================================================================
   QHQ "FLEET CONSOLE" INDUSTRIAL DESIGN SYSTEM (UI-001 rev 2026-07-21)
   Extracted from the operator-approved mockups (qhq-redux mockup #3 +
   device-detail plate, approved 2026-07-20). Two layers:
     · qhq-* components — new, namespaced machined-panel vocabulary
       (panels, steel plates, lamps/LEDs, chips, gauges, slide-over,
       topology SVG, unit cards). Opt-in via class; no collisions with
       existing component classes.
     · bridge reskin — restyles EXISTING class names toward the machined
       look. Visual properties only: no display/position/size changes, so
       layout math, HTMX targets, and interactions.js hooks are untouched.
   Token values live in tokens.css (same UI-001 variable names, so the
   theme.go per-tenant override allowlist keeps working).
   ========================================================================== */

/* Room lighting: two soft radial washes over the base background.
   Overrides reset.css body background (this file loads after it). */
body {
  background:
    radial-gradient(1200px 700px at 78% -8%, rgba(40, 90, 132, 0.18), transparent 60%),
    radial-gradient(900px 600px at -6% 108%, rgba(119, 186, 100, 0.09), transparent 55%),
    var(--bg-base);
}
::selection { background: var(--qhq-green); color: var(--qhq-steel-ink); }
:focus-visible { outline: 2px solid var(--qhq-green); outline-offset: 2px; }

/* ---------- machined panel ---------- */
.qhq-panel {
  position: relative;
  background: linear-gradient(160deg, var(--qhq-panel-a), var(--qhq-panel-c));
  border: 1px solid var(--qhq-line);
  border-radius: 12px;
  box-shadow: inset 0 1px 0 rgba(160, 200, 150, 0.06), 0 10px 26px rgba(0, 0, 0, 0.42);
}
.qhq-panel::before {  /* gradient rail across the bezel top */
  content: "";
  position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--qhq-rail);
  border-radius: 12px 12px 0 0;
  opacity: 0.7; pointer-events: none;
}
.qhq-panel::after {   /* brushed-metal grain */
  content: "";
  position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: repeating-linear-gradient(90deg, rgba(232, 244, 229, 0.012) 0 1px, transparent 1px 3px);
  mix-blend-mode: screen;
}
/* Rivets: <span class="qhq-rivet tl|tr|bl|br"></span> inside a .qhq-riveted panel */
.qhq-rivet {
  position: absolute; width: 5px; height: 5px; border-radius: 50%;
  z-index: 2; pointer-events: none;
  background: radial-gradient(circle at 35% 30%, #9fb6a6, #2a3a4c 70%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.qhq-rivet.tl { top: 8px; left: 8px; }   .qhq-rivet.tr { top: 8px; right: 8px; }
.qhq-rivet.bl { bottom: 8px; left: 8px; } .qhq-rivet.br { bottom: 8px; right: 8px; }

/* Milled recess (inputs, fact plates, search wells) */
.qhq-recess {
  background: linear-gradient(180deg, #060e1c, var(--qhq-inset));
  border: 1px solid var(--qhq-etch);
  border-bottom-color: rgba(119, 186, 100, 0.14);
  border-radius: 8px;
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.55);
}

/* Engraved green-steel plate (section titles, active states) */
.qhq-steelplate {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--qhq-grad);
  color: var(--qhq-steel-ink);
  border: 1px solid rgba(10, 22, 40, 0.6);
  border-top-color: rgba(232, 244, 229, 0.4);
  border-radius: 5px;
  padding: 3px 12px;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 11px;
  letter-spacing: 0.16em; text-transform: uppercase;
  text-shadow: 0 1px 0 rgba(232, 244, 229, 0.28);
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.45), inset 0 -2px 3px rgba(10, 22, 40, 0.4);
}

/* Engraved caption */
.qhq-etched {
  font-family: var(--font-mono);
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.22em;
  font-size: 10.5px; color: var(--text-tertiary);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.7);
}
.qhq-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* Section header inside a panel */
.qhq-sectionhead {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--qhq-line);
}
.qhq-sectionhead .qhq-etched { color: var(--text-secondary); }
.qhq-sectionhead .qhq-tools { margin-left: auto; display: flex; gap: 8px; }

/* ---------- status lamps (physical indicator bulbs) ---------- */
.qhq-lamp {
  display: inline-block; width: 13px; height: 13px; border-radius: 50%; flex: none;
  border: 2px solid #071120;
  box-shadow: 0 0 0 1px #23405a, inset 0 -2px 3px rgba(0, 0, 0, 0.5);
}
.qhq-lamp--ok    { background: radial-gradient(circle at 35% 30%, #c8ecbb, var(--status-healthy) 55%, #2f5c31);
                   box-shadow: 0 0 0 1px #23405a, 0 0 9px rgba(119, 186, 100, 0.55); }
.qhq-lamp--warn  { background: radial-gradient(circle at 35% 30%, #ffe6ad, var(--status-warning) 55%, #8a6414);
                   box-shadow: 0 0 0 1px #23405a, 0 0 9px rgba(232, 182, 68, 0.5); }
.qhq-lamp--bad   { background: radial-gradient(circle at 35% 30%, #ffb7ab, var(--status-critical) 55%, #7c231a);
                   box-shadow: 0 0 0 1px #23405a, 0 0 12px rgba(212, 84, 84, 0.65);
                   animation: qhqLampPulse 1.6s var(--qhq-ease) infinite; }
.qhq-lamp--agent { background: radial-gradient(circle at 35% 30%, #cbe6f2, var(--status-info) 55%, #25506e);
                   box-shadow: 0 0 0 1px #23405a, 0 0 9px rgba(100, 160, 186, 0.55); }
.qhq-lamp--off   { background: radial-gradient(circle at 35% 30%, #4e6070, #2c3c4c 60%, #16222e); }
.qhq-lamp--sm    { width: 10px; height: 10px; }
@keyframes qhqLampPulse { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.55); } }

/* Flat LED dot (tables, feeds) */
.qhq-led {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: none;
  box-shadow: 0 0 8px currentColor; background: currentColor;
}
.qhq-led--on    { color: var(--status-healthy); }
.qhq-led--agent { color: var(--status-info); }
.qhq-led--fail  { color: var(--status-warning); }
.qhq-led--down  { color: var(--status-critical); }
.qhq-led--off   { color: var(--status-unknown); box-shadow: none; }

/* ---------- chips (mono, uppercase, engraved) ---------- */
.qhq-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 3px 10px; border-radius: 99px;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  border: 1px solid var(--qhq-line);
  background: var(--qhq-inset);
  color: var(--text-secondary); white-space: nowrap;
}
.qhq-chip--ok    { color: var(--qhq-ink-ok);    border-color: rgba(119, 186, 100, 0.4); }
.qhq-chip--warn  { color: var(--qhq-ink-warn);  border-color: rgba(232, 182, 68, 0.45); }
.qhq-chip--bad   { color: var(--qhq-ink-bad);   border-color: rgba(212, 84, 84, 0.5);
                   background: linear-gradient(180deg, #241019, #160b12); }
.qhq-chip--agent { color: var(--qhq-ink-agent); border-color: rgba(100, 160, 186, 0.45); }
.qhq-chip--live  { border-radius: 6px; background: rgba(0, 0, 0, 0.18); letter-spacing: 0.08em; }

/* Softer status pill (sentence-case) */
.qhq-pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 3px 10px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600; border: 1px solid transparent;
}
.qhq-pill--on    { color: var(--status-healthy);  background: rgba(119, 186, 100, 0.1);  border-color: rgba(119, 186, 100, 0.25); }
.qhq-pill--agent { color: var(--status-info);     background: rgba(100, 160, 186, 0.1);  border-color: rgba(100, 160, 186, 0.28); }
.qhq-pill--fail  { color: var(--status-warning);  background: rgba(232, 182, 68, 0.1);   border-color: rgba(232, 182, 68, 0.3); }
.qhq-pill--down  { color: var(--qhq-ink-bad);     background: rgba(212, 84, 84, 0.1);    border-color: rgba(212, 84, 84, 0.3); }

/* Status badge with lamp (table Status column) */
.qhq-badge {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px; border-radius: 99px;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  border: 1px solid;
}
.qhq-badge .qhq-lamp { width: 8px; height: 8px; border-width: 1px; }
.qhq-badge--g { color: var(--qhq-ink-ok);   border-color: rgba(119, 186, 100, 0.4); background: rgba(119, 186, 100, 0.08); }
.qhq-badge--y { color: var(--qhq-ink-warn); border-color: rgba(232, 182, 68, 0.45); background: rgba(232, 182, 68, 0.08); }
.qhq-badge--r { color: var(--qhq-ink-bad);  border-color: rgba(212, 84, 84, 0.5);   background: rgba(212, 84, 84, 0.08); }
.qhq-badge--b { color: var(--qhq-ink-agent);border-color: rgba(100, 160, 186, 0.45);background: rgba(100, 160, 186, 0.08); }

/* ---------- buttons ---------- */
.qhq-toolbtn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: 7px;
  border: 1px solid var(--qhq-line);
  background: linear-gradient(180deg, var(--bg-elevated), var(--bg-surface));
  color: var(--text-secondary); cursor: pointer;
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(232, 244, 229, 0.05);
  transition: all 0.14s var(--qhq-ease);
  text-decoration: none;
}
.qhq-toolbtn:hover { color: var(--text-primary); border-color: var(--qhq-line-hi); text-decoration: none; }
.qhq-toolbtn--steel {
  color: var(--qhq-steel-ink);
  background: var(--qhq-grad);
  border-color: rgba(10, 22, 40, 0.6);
  text-shadow: 0 1px 0 rgba(232, 244, 229, 0.3);
  box-shadow: 0 2px 10px rgba(119, 186, 100, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.22);
}
.qhq-toolbtn--steel:hover { filter: brightness(1.07); color: var(--qhq-steel-ink); }
.qhq-toolbtn--danger {
  color: var(--qhq-ink-bad);
  border-color: rgba(212, 84, 84, 0.5);
  background: linear-gradient(180deg, #241019, #160b12);
}
.qhq-toolbtn--danger:hover { border-color: var(--status-critical); color: #f2c4bd; }
.qhq-toolbtn[aria-pressed="true"] {
  color: var(--qhq-steel-ink); background: var(--qhq-grad);
  border-color: rgba(10, 22, 40, 0.6);
  text-shadow: 0 1px 0 rgba(232, 244, 229, 0.3);
  box-shadow: inset 0 2px 4px rgba(10, 22, 40, 0.45);
  transform: translateY(1px);
}

/* ---------- page-link tabs (site/device detail) ---------- */
.qhq-pagetabs {
  display: flex; gap: 4px;
  border-bottom: 2px solid var(--qhq-etch);
  position: relative;
}
.qhq-pagetabs::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: 2px;
  background: linear-gradient(90deg, rgba(40, 90, 132, 0.7), transparent 70%);
  opacity: 0.5;
}
.qhq-ptab {
  padding: 10px 22px 9px; border-radius: 8px 8px 0 0;
  color: var(--text-secondary); position: relative; top: 2px;
  font-family: var(--font-mono); font-size: 12px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  border: 1px solid transparent; background: none; cursor: pointer;
}
.qhq-ptab:hover { color: var(--text-primary); }
.qhq-ptab[aria-selected="true"],
.qhq-ptab.is-active {
  color: var(--qhq-green);
  background: linear-gradient(180deg, var(--qhq-panel-a), var(--qhq-panel-c));
  border-color: var(--qhq-line);
  border-bottom-color: var(--qhq-panel-c);
  box-shadow: inset 0 2px 0 var(--qhq-green);
}

/* ---------- inventory table (opt-in skin: add .qhq-table to a table) ---------- */
.qhq-tablewrap { overflow-x: auto; padding: 4px 8px 10px; }
table.qhq-table { width: 100%; border-collapse: collapse; }
table.qhq-table th {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--qhq-blue-lt); text-align: left;
  padding: 10px 14px; border-bottom: 1px solid var(--qhq-line);
  white-space: nowrap;
}
table.qhq-table th[data-key] { cursor: pointer; user-select: none; }
table.qhq-table th[data-key]:hover { color: var(--qhq-green); }
table.qhq-table td {
  padding: 11px 14px;
  border-bottom: 1px solid rgba(232, 244, 229, 0.045);
  font-size: 13px; vertical-align: middle;
}
table.qhq-table tr.qhq-rowlink { cursor: pointer; transition: background 0.12s var(--qhq-ease); }
table.qhq-table tr.qhq-rowlink:hover { background: rgba(119, 186, 100, 0.045); }
table.qhq-table tr.qhq-rowlink:focus-visible { outline: 2px solid var(--qhq-green); outline-offset: -2px; }
.qhq-devname {
  font-family: var(--font-mono); font-weight: 600; font-size: 13.5px; color: var(--text-primary);
}
.qhq-devrole {
  display: block; font-size: 10px; color: var(--text-tertiary);
  font-family: var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; margin-top: 2px;
}
.qhq-cell-mono { font-family: var(--font-mono); font-size: 12px; color: var(--text-secondary); }
.qhq-cell-dim  { color: var(--text-secondary); font-size: 12.5px; }

/* ---------- unit cards (org / site drill-down) ---------- */
.qhq-cardgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 18px;
}
.qhq-unitcard {
  padding: 18px 18px 16px; cursor: pointer; text-align: left; width: 100%;
  color: inherit; font: inherit; text-decoration: none; display: block;
  transition: transform 0.16s var(--qhq-ease), box-shadow 0.16s var(--qhq-ease);
}
.qhq-unitcard:hover {
  transform: translateY(-2px); text-decoration: none;
  box-shadow: inset 0 1px 0 rgba(160, 200, 150, 0.06), 0 14px 30px rgba(0, 0, 0, 0.5);
}
.qhq-unitcard--warn {
  border-color: rgba(232, 182, 68, 0.45);
  box-shadow: inset 0 1px 0 rgba(160, 200, 150, 0.04), 0 10px 24px rgba(0, 0, 0, 0.38),
              0 0 0 1px rgba(232, 182, 68, 0.22), 0 0 22px rgba(232, 182, 68, 0.10);
}
.qhq-unitcard--bad {
  border-color: rgba(212, 84, 84, 0.5);
  box-shadow: inset 0 1px 0 rgba(160, 200, 150, 0.04), 0 10px 24px rgba(0, 0, 0, 0.38),
              0 0 0 1px rgba(212, 84, 84, 0.25), 0 0 22px rgba(212, 84, 84, 0.12);
}
.qhq-unitcard__toprow { display: flex; align-items: flex-start; gap: 14px; }
.qhq-unitcard__names  { min-width: 0; flex: 1; }
.qhq-unitcard h2, .qhq-unitcard__title {
  margin: 0; font-size: 20px; font-weight: 800; letter-spacing: -0.01em; line-height: 1.15;
  color: var(--text-primary); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5); overflow-wrap: break-word;
}
.qhq-unitcard__meta { margin-top: 3px; font-size: 11.5px; color: var(--text-tertiary); font-family: var(--font-mono); }
.qhq-facetrow { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.qhq-unitcard__go {
  margin-top: 14px; display: flex; justify-content: flex-end; align-items: center; gap: 6px;
  color: var(--qhq-green); font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
}
.qhq-unitcard:hover .qhq-unitcard__go { color: var(--qhq-ink-ok); }

/* Site roster card (sites-page rebuild 2026-08-13): leads with the
   latest state change + time context; device roll-up is the small
   secondary line. */
.qhq-sitecard__event {
  margin-top: 14px; font-size: 15px; line-height: 1.35;
  color: var(--text-primary); overflow-wrap: break-word;
}
.qhq-sitecard__event--issue { font-weight: 600; }
.qhq-sitecard__age {
  margin-top: 2px; font-family: var(--font-mono); font-size: 11px;
  color: var(--text-tertiary); letter-spacing: 0.04em;
}
.qhq-sitecard__summary {
  margin-top: 12px; font-family: var(--font-mono); font-size: 11.5px;
  color: var(--text-secondary);
}

/* Severity note strip (named broken thing on a card) */
.qhq-sevnote {
  margin-top: 12px; display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 7px; font-size: 12.5px;
}
.qhq-sevnote b {
  font-family: var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; font-size: 11px;
}
.qhq-sevnote--bad  { background: linear-gradient(180deg, #241019, #160b12); border: 1px solid rgba(212, 84, 84, 0.5); color: #f2c4bd; }
.qhq-sevnote--bad b { color: var(--qhq-ink-bad); }
.qhq-sevnote--warn { background: #1e1a0d; border: 1px solid rgba(232, 182, 68, 0.4); color: #ecdcae; }
.qhq-sevnote--warn b { color: var(--qhq-ink-warn); }
.qhq-sevnote--ok   { background: #0f2016; border: 1px solid rgba(119, 186, 100, 0.35); color: #b9dcab; }

/* Alert bar (page-level, e.g. device on failover) */
.qhq-alertbar {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 13px 16px; border-radius: 9px; font-size: 13.5px; line-height: 1.5;
}
.qhq-alertbar b {
  font-family: var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase;
  font-size: 11.5px; display: block; margin-bottom: 3px;
}
.qhq-alertbar--warn { background: #1e1a0d; border: 1px solid rgba(232, 182, 68, 0.45); color: #ecdcae; }
.qhq-alertbar--warn b { color: var(--qhq-ink-warn); }
.qhq-alertbar--bad  { background: linear-gradient(180deg, #241019, #160b12); border: 1px solid rgba(212, 84, 84, 0.5); color: #f2c4bd; }
.qhq-alertbar--bad b { color: var(--qhq-ink-bad); }
.qhq-alertbar--ok   { background: #0f2016; border: 1px solid rgba(119, 186, 100, 0.35); color: #b9dcab; }
.qhq-alertbar--ok b { color: var(--qhq-ink-ok); }

/* ---------- command deck: counters + signal feed ---------- */
.qhq-counter { display: flex; align-items: center; gap: 11px; }
.qhq-counter .qhq-lamp { width: 11px; height: 11px; }
.qhq-counter b { font-family: var(--font-mono); font-size: 20px; font-weight: 600; min-width: 34px; text-align: right; }
.qhq-counter span {
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-secondary);
}
.qhq-counter__bar { flex: 1; height: 5px; border-radius: 3px; background: rgba(232, 244, 229, 0.06); overflow: hidden; }
.qhq-counter__bar i { display: block; height: 100%; border-radius: 3px; }

.qhq-ev { display: flex; gap: 11px; align-items: flex-start; }
.qhq-ev__dot {
  width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex: none;
  box-shadow: 0 0 7px currentColor; background: currentColor;
}
.qhq-ev__t { font-size: 12.5px; line-height: 1.4; }
.qhq-ev__t b { color: var(--text-primary); }
.qhq-ev__t span { color: var(--text-secondary); }
.qhq-ev__ts { font-family: var(--font-mono); font-size: 10px; color: var(--text-tertiary); margin-top: 2px; letter-spacing: 0.04em; }

/* ---------- probes (4-way reachability rows) ---------- */
.qhq-probe { display: flex; align-items: center; gap: 11px; font-size: 13px; }
.qhq-probe .qhq-lamp { width: 11px; height: 11px; flex: none; }
.qhq-probe .qhq-probe__k {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-secondary); flex: 1;
}
.qhq-probe b { font-family: var(--font-mono); font-weight: 600; font-size: 12px; }

/* ---------- fact plate (nameplate key/values) ---------- */
.qhq-factplate { padding: 2px 14px; }
.qhq-fact {
  display: flex; justify-content: space-between; gap: 14px;
  padding: 9px 0; border-bottom: 1px solid rgba(232, 244, 229, 0.05); font-size: 12.5px;
}
.qhq-fact:last-child { border-bottom: 0; }
.qhq-fact span {
  color: var(--text-tertiary); font-family: var(--font-mono);
  font-size: 10px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  padding-top: 2px; flex: none;
}
.qhq-fact b {
  font-family: var(--font-mono); font-weight: 500; text-align: right;
  color: var(--text-primary); overflow-wrap: break-word; min-width: 0;
}

/* ---------- timeline (device event history) ---------- */
.qhq-timeline { padding: 14px 16px 18px; display: flex; flex-direction: column; }
.qhq-tlrow { display: flex; gap: 13px; position: relative; padding-bottom: 16px; }
.qhq-tlrow:last-child { padding-bottom: 0; }
.qhq-tlrow::before {
  content: ""; position: absolute; left: 5.5px; top: 16px; bottom: 2px; width: 1px;
  background: rgba(232, 244, 229, 0.08);
}
.qhq-tlrow:last-child::before { display: none; }
.qhq-tlrow .qhq-ev__dot {
  width: 12px; height: 12px; margin-top: 3px; border: 2px solid #071120;
  box-shadow: 0 0 0 1px #23405a, 0 0 8px currentColor; z-index: 1;
}

/* ---------- analog needle gauge ----------
   Markup: <div class="qhq-gauge" data-value="93" data-tone="ok"></div>
   The SVG (bezel arc, bands, ticks, needle, readout) is rendered by
   static/js/qhq-gauges.js (Phase 2/3 — CSP-safe external file that
   re-renders on htmx:afterSwap). These styles cover the container +
   needle animation; without the JS the container renders empty. */
.qhq-gauge { width: 104px; flex: none; }
.qhq-gauge--lg { width: 150px; }
.qhq-gauge--md { width: 128px; }
.qhq-gauge svg { display: block; width: 100%; height: auto; }
.qhq-gauge .qhq-gauge__pct { font-family: var(--font-mono); font-weight: 700; }
.qhq-gauge .qhq-gauge__needle {
  transform-origin: 60px 60px;
  transition: transform 0.9s cubic-bezier(0.25, 1.4, 0.35, 1);
}

/* ---------- sparkline ---------- */
.qhq-sparkblock { padding: 12px 14px 10px; }
.qhq-sparkblock .qhq-etched { display: block; margin-bottom: 8px; }
.qhq-sparkblock svg { display: block; width: 100%; height: 46px; }
.qhq-spark-line { fill: none; stroke: var(--qhq-green); stroke-width: 1.8; stroke-linejoin: round; }
.qhq-spark-line--warn { stroke: var(--qhq-warn); }
.qhq-spark-fill { opacity: 0.7; }
.qhq-spark-dot  { fill: var(--qhq-ink-ok); }
.qhq-sparkmeta {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: 11px; color: var(--text-tertiary); margin-top: 5px;
}

/* ---------- right slide-over (device quick panel; wiring lands Phase 4) ---------- */
.qhq-slideover {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 420px; max-width: 100vw; z-index: 50;
  transform: translateX(calc(100% + 30px));
  transition: transform 0.26s var(--qhq-ease);
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, #17293f, var(--bg-surface) 60%, #0e1c30);
  border-left: 1px solid var(--qhq-line-hi);
  box-shadow: -18px 0 44px rgba(0, 0, 0, 0.55), inset 1px 0 0 rgba(232, 244, 229, 0.05);
}
.qhq-slideover.is-open { transform: none; }
.qhq-slideover::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(90deg, rgba(232, 244, 229, 0.012) 0 1px, transparent 1px 3px);
}
.qhq-so-head { padding: 18px 20px 14px; border-bottom: 1px solid var(--qhq-line); position: relative; }
.qhq-so-kicker { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.qhq-so-close {
  position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; border-radius: 7px;
  display: grid; place-items: center; color: var(--text-secondary); cursor: pointer;
  border: 1px solid var(--qhq-line);
  background: linear-gradient(180deg, var(--bg-elevated), var(--bg-surface));
  z-index: 1;
}
.qhq-so-close:hover { color: var(--text-primary); border-color: var(--qhq-line-hi); }
.qhq-so-title { display: flex; align-items: center; gap: 12px; }
.qhq-so-title h2 { margin: 0; font-family: var(--font-mono); font-size: 23px; font-weight: 700; letter-spacing: 0.02em; }
.qhq-so-sub {
  margin-top: 4px; font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--text-tertiary);
}
.qhq-so-body { flex: 1; overflow-y: auto; padding: 16px 20px 24px; }
.qhq-so-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.qhq-so-actions .qhq-toolbtn { justify-content: center; padding: 10px 8px; }
.qhq-so-actions .qhq-so-wide { grid-column: 1 / -1; }

/* ---------- topology diagram (SVG) ---------- */
.qhq-diagram-wrap { padding: 8px 10px 14px; overflow-x: auto; }
.qhq-diagram { min-width: 660px; }
.qhq-diagram svg { display: block; width: 100%; height: auto; }
.qhq-lnk { stroke: #3d5a6e; stroke-width: 2; fill: none; }
.qhq-lnk--down   { stroke: var(--qhq-bad); stroke-dasharray: 7 6; animation: qhqDashCrawl 1.4s linear infinite; }
.qhq-lnk--dim    { stroke: #28425a; stroke-dasharray: 3 5; }
.qhq-lnk--tunnel { stroke: var(--qhq-blue-lt); stroke-dasharray: 2 6; stroke-width: 1.8; opacity: 0.85;
                   animation: qhqDashCrawl 2.4s linear infinite; }
@keyframes qhqDashCrawl { to { stroke-dashoffset: -26; } }
.qhq-node { cursor: pointer; }
.qhq-node .qhq-node__shape { fill: #152740; stroke: #4a6b84; stroke-width: 1.4; transition: stroke 0.15s var(--qhq-ease); }
.qhq-node:hover .qhq-node__shape,
.qhq-node:focus-visible .qhq-node__shape { stroke: var(--qhq-green); }
.qhq-node .qhq-node__glyph { stroke: #8ba89e; stroke-width: 1.6; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.qhq-node .qhq-node__glyph-fill { fill: #8ba89e; }
.qhq-node--down .qhq-node__shape { stroke: rgba(212, 84, 84, 0.6); }
.qhq-node--down .qhq-node__glyph { stroke: #5f7a72; }
.qhq-nlabel { font: 600 12px var(--font-mono); fill: #cde0c8; text-anchor: middle; }
.qhq-nrole  { font: 700 8.5px var(--font-mono); letter-spacing: 0.18em; text-transform: uppercase; fill: #5f7a72; text-anchor: middle; }
.qhq-slamp { stroke: #071120; stroke-width: 1.6; }
.qhq-slamp--ok   { fill: var(--status-healthy); }
.qhq-slamp--warn { fill: var(--status-warning); }
.qhq-slamp--bad  { fill: var(--status-critical); animation: qhqLampPulse 1.6s var(--qhq-ease) infinite; }
.qhq-slamp--off  { fill: #33475a; }
.qhq-linktag { font: 700 8.5px var(--font-mono); letter-spacing: 0.14em; text-transform: uppercase; fill: var(--qhq-bad); }
.qhq-linktag--steel { fill: var(--qhq-blue-lt); }
.qhq-legend {
  display: flex; flex-wrap: wrap; gap: 16px; align-items: center;
  padding: 10px 16px 14px; color: var(--text-secondary); font-size: 12px;
}
.qhq-legend__key { display: inline-flex; align-items: center; gap: 7px; }
.qhq-legend__swatch { width: 26px; height: 0; border-top: 2px solid #3d5a6e; display: inline-block; }
.qhq-legend__swatch--down   { border-top: 2px dashed var(--qhq-bad); }
.qhq-legend__swatch--tunnel { border-top: 2px dotted var(--qhq-blue-lt); }

/* ---------- toggle switch ---------- */
.qhq-toggle {
  position: relative; width: 46px; height: 24px; flex: none; border-radius: 99px;
  border: 1px solid var(--qhq-etch); cursor: pointer;
  background: linear-gradient(180deg, #060e1c, var(--qhq-inset));
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.6);
  transition: background 0.18s var(--qhq-ease);
}
.qhq-toggle::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: linear-gradient(180deg, #40566a, #25384c);
  border: 1px solid #4a6b84;
  transition: all 0.18s var(--qhq-ease);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.qhq-toggle[aria-checked="true"] { background: linear-gradient(180deg, #26491f, #183417); }
.qhq-toggle[aria-checked="true"]::after { left: 24px; background: var(--qhq-grad-v); border-color: rgba(10, 22, 40, 0.6); }

/* ---------- tooltip surface (data-tip engine; Phase 6) ---------- */
.qhq-tip {
  position: fixed; z-index: 99; max-width: 290px; pointer-events: none;
  opacity: 0; transform: translateY(4px);
  transition: opacity 0.12s var(--qhq-ease), transform 0.12s var(--qhq-ease);
  background: linear-gradient(180deg, #1c3450, var(--bg-surface));
  border: 1px solid rgba(119, 186, 100, 0.4); border-radius: 7px;
  padding: 9px 12px; box-shadow: 0 10px 26px rgba(0, 0, 0, 0.6);
  font-size: 12.5px; line-height: 1.45; color: var(--text-secondary);
}
.qhq-tip.is-show { opacity: 1; transform: none; }
.qhq-tip__title {
  display: block; font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--qhq-green); margin-bottom: 3px;
}

/* ---------- footer stamp ---------- */
.qhq-stamp {
  margin-top: 34px; padding-top: 14px; border-top: 1px solid var(--qhq-line);
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px;
  color: var(--text-tertiary);
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.08em;
}

/* ---------- wordmark (sidebar logo text treatment) ---------- */
.qhq-wordmark b { font-size: 19px; font-weight: 800; letter-spacing: 0.01em; display: block; line-height: 1.1; white-space: nowrap; }
.qhq-wordmark .qhq-wordmark__q  { color: var(--qhq-green); }
.qhq-wordmark .qhq-wordmark__hq { color: var(--qhq-green-dk); }
.qhq-wordmark .qhq-wordmark__sub {
  display: block; font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.3em;
  color: var(--text-tertiary); text-transform: uppercase; margin-top: 3px;
}

/* ============================================================
   BRIDGE — reskin existing components toward the machined look
   (visual properties only; class names, layout metrics, and
   interactions.js hooks untouched)
   ============================================================ */

/* Cards become machined panels */
.card {
  background: linear-gradient(160deg, var(--qhq-panel-a), var(--qhq-panel-c));
  border: 1px solid var(--qhq-line);
  border-radius: 12px;
  box-shadow: inset 0 1px 0 rgba(160, 200, 150, 0.06), 0 10px 26px rgba(0, 0, 0, 0.42);
  position: relative;
}
.card::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--qhq-rail); border-radius: 12px 12px 0 0;
  opacity: 0.7; pointer-events: none;
}
.card-header { border-bottom: 1px solid var(--qhq-line); }
.card-header__title {
  font-family: var(--font-mono); font-weight: 700; font-size: 11px;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-secondary);
}

/* Filter bars become machined panels too */
.device-filter-bar {
  background: linear-gradient(160deg, var(--qhq-panel-a), var(--qhq-panel-c));
  border: 1px solid var(--qhq-line);
  box-shadow: inset 0 1px 0 rgba(160, 200, 150, 0.06), 0 10px 26px rgba(0, 0, 0, 0.42);
}

/* Buttons: primary = green-steel, secondary = machined, danger = hot */
.btn--primary {
  background: var(--qhq-grad);
  color: var(--qhq-steel-ink);
  border: 1px solid rgba(10, 22, 40, 0.6);
  text-shadow: 0 1px 0 rgba(232, 244, 229, 0.3);
  box-shadow: 0 2px 10px rgba(119, 186, 100, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.22);
}
.btn--primary:hover { background: var(--qhq-grad); filter: brightness(1.07); color: var(--qhq-steel-ink); }
.btn--secondary, .btn--ghost {
  background: linear-gradient(180deg, var(--bg-elevated), var(--bg-surface));
  border: 1px solid var(--qhq-line);
  color: var(--text-secondary);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(232, 244, 229, 0.05);
}
.btn--secondary:hover, .btn--ghost:hover { color: var(--text-primary); border-color: var(--qhq-line-hi); }
.btn--danger {
  color: var(--qhq-ink-bad);
  border: 1px solid rgba(212, 84, 84, 0.5);
  background: linear-gradient(180deg, #241019, #160b12);
}
.btn--danger:hover { border-color: var(--status-critical); color: #f2c4bd; }

/* Data tables: engraved mono headers, green row hover */
.data-table th {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--qhq-blue-lt);
  border-bottom: 1px solid var(--qhq-line);
}
.data-table td { border-bottom: 1px solid rgba(232, 244, 229, 0.045); }
.data-table__row--clickable:hover { background: rgba(119, 186, 100, 0.045); }

/* Existing status badges pick up the chip treatment; the badge dot
   becomes a glowing LED (mockup lamp language, zero markup change) */
.badge {
  font-family: var(--font-mono); font-weight: 700; font-size: 10.5px;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.badge__dot { box-shadow: 0 0 7px currentColor; }
.badge--healthy  { color: var(--qhq-ink-ok);    border: 1px solid rgba(119, 186, 100, 0.4);  background: rgba(119, 186, 100, 0.08); }
.badge--warning  { color: var(--qhq-ink-warn);  border: 1px solid rgba(232, 182, 68, 0.45);  background: rgba(232, 182, 68, 0.08); }
.badge--critical { color: var(--qhq-ink-bad);   border: 1px solid rgba(212, 84, 84, 0.5);    background: rgba(212, 84, 84, 0.08); }
.badge--info     { color: var(--qhq-ink-agent); border: 1px solid rgba(100, 160, 186, 0.45); background: rgba(100, 160, 186, 0.08); }
.badge--unknown  { color: var(--text-tertiary); border: 1px solid var(--qhq-line); background: var(--qhq-inset); }
.badge--maintenance { color: var(--status-maintenance, #a78bfa); border: 1px solid rgba(139, 92, 246, 0.45); background: rgba(139, 92, 246, 0.10); }

/* Sidebar → nav rail: chassis gradient, mono uppercase items,
   active item becomes an engraved steel plate */
.sidebar {
  background: linear-gradient(180deg, var(--bg-surface), #0d1a2e 70%, #0b1626);
  border-right: 1px solid var(--qhq-line);
  box-shadow: 4px 0 18px rgba(0, 0, 0, 0.4);
}
.sidebar__logo { border-bottom: 1px solid var(--qhq-line); }
.sidebar__logo-mark { filter: drop-shadow(0 0 6px rgba(119, 186, 100, 0.25)); }
.sidebar__logo-text { font-weight: 800; color: var(--qhq-green); }
.sidebar__group-label {
  font-family: var(--font-mono); letter-spacing: 0.22em;
  color: var(--text-tertiary); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.7);
}
.sidebar__item {
  font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  border-radius: 8px; margin: 0 8px;
  border-left: 3px solid transparent;   /* keep the existing metric */
}
.sidebar__item:hover { background: rgba(119, 186, 100, 0.06); }
.sidebar__item--active {
  color: var(--qhq-steel-ink);
  background: var(--qhq-grad);
  border-left-color: transparent;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5), inset 0 -2px 3px rgba(10, 22, 40, 0.35);
  text-shadow: 0 1px 0 rgba(232, 244, 229, 0.3);
}
.sidebar__item--active .sidebar__icon svg { stroke: var(--qhq-steel-ink); }
.sidebar__subitem { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; }
.sidebar__subitem--active { color: var(--qhq-green); border-left-color: var(--qhq-green); }
.sidebar__badge {
  background: var(--qhq-warn); color: var(--qhq-steel-ink);
  box-shadow: 0 0 8px rgba(232, 182, 68, 0.6);
}

/* Topbar: translucent chassis strip, mono crumbs */
.topbar {
  background: linear-gradient(180deg, rgba(10, 22, 40, 0.97) 70%, rgba(10, 22, 40, 0.88));
  border-bottom: 1px solid var(--qhq-line);
  backdrop-filter: blur(3px);
}
.topbar__breadcrumb {
  font-family: var(--font-mono); font-size: 12.5px;
  letter-spacing: 0.1em; text-transform: uppercase;
}
.topbar__breadcrumb-sep { color: var(--qhq-blue-lt); }
.topbar__breadcrumb-current { color: var(--qhq-green); font-weight: 700; }
.topbar__user-btn {
  border-radius: 8px;
  background: var(--qhq-grad);
  color: var(--qhq-steel-ink);
  border: 1px solid rgba(10, 22, 40, 0.6);
}
.topbar__user-btn:hover { filter: brightness(1.07); background: var(--qhq-grad); color: var(--qhq-steel-ink); }
.topbar__user-menu {
  background: linear-gradient(180deg, #1c3450, var(--bg-surface));
  border: 1px solid var(--qhq-line-hi);
}
.topbar__badge { background: var(--qhq-warn); color: var(--qhq-steel-ink); box-shadow: 0 0 8px rgba(232, 182, 68, 0.6); }

/* Device-detail tabs adopt the ptab treatment (mechanics unchanged:
   interactions.js data-tab-switch still just toggles is-active) */
.dd-tabs__bar { border-bottom: 2px solid var(--qhq-etch); }
.dd-tabs__btn {
  font-family: var(--font-mono); font-size: 12px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-secondary);
  border-radius: 8px 8px 0 0;
}
.dd-tabs__btn:hover { color: var(--text-primary); }
.dd-tabs__btn.is-active {
  color: var(--qhq-green);
  background: linear-gradient(180deg, var(--qhq-panel-a), var(--qhq-panel-c));
  box-shadow: inset 0 2px 0 var(--qhq-green);
}

/* Legacy tabs-bar (health etc.) — same treatment */
.tabs-bar__tab {
  font-family: var(--font-mono); font-size: 12px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
}
.tabs-bar__tab--active { color: var(--qhq-green); box-shadow: inset 0 -2px 0 var(--qhq-green); }

/* Form fields become milled recesses */
.form-field__input, .form-field__select,
.form-input, .form-select, .input,
.device-filter-bar__input, .device-filter-bar__select,
.toolbar__search-input {
  background: linear-gradient(180deg, #060e1c, var(--qhq-inset));
  border: 1px solid var(--qhq-etch);
  border-bottom-color: rgba(119, 186, 100, 0.18);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
  color: var(--text-primary);
  font-family: var(--font-mono);
}
.form-field__input:focus, .form-field__select:focus,
.form-input:focus, .form-select:focus, .input:focus,
.device-filter-bar__input:focus, .device-filter-bar__select:focus,
.toolbar__search-input:focus {
  border-color: rgba(119, 186, 100, 0.45);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(119, 186, 100, 0.15);
  outline: none;
}
.form-field__label, .device-filter-bar__label {
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-tertiary);
}

/* Page headers */
.page-header__title { font-weight: 800; letter-spacing: -0.01em; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.5); }
.page-header__subtitle { color: var(--text-secondary); }

/* Scrollbars (WebKit + Firefox) */
* { scrollbar-color: #23405a var(--qhq-inset); scrollbar-width: thin; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: var(--qhq-inset); }
*::-webkit-scrollbar-thumb { background: #23405a; border-radius: 5px; border: 2px solid var(--qhq-inset); }
*::-webkit-scrollbar-thumb:hover { background: #3d5a6e; }

/* ---------- fleet drill-down (Phase 2: org → site → device) ---------- */
/* Empty-scope stand-in where a gauge would render. */
.qhq-fleet-nodata {
  flex: none; align-self: center; padding: 8px 12px; border-radius: 7px;
  border: 1px dashed var(--qhq-line); color: var(--text-tertiary);
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap;
}

/* Site-detail hero: gauge beside the quick-facts plate. */
.qhq-fleet-hero { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.qhq-fleet-hero__facts { flex: 1; min-width: 260px; }
.qhq-fleet-links { display: flex; gap: 10px; flex-wrap: wrap; }

/* Slide-over internals the fragment needs beyond the Phase-1 shell:
   the swap-target wrapper must carry the head/body column layout, and
   the gauge row + quick stats mirror the mockup's so-gaugerow. */
.qhq-so-fill { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.qhq-so-gaugerow { display: flex; gap: 18px; align-items: center; margin-bottom: 16px; }
.qhq-so-quick { flex: 1; display: grid; gap: 8px; }
.qhq-so-q { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; }
.qhq-so-q span {
  color: var(--text-tertiary); font-family: var(--font-mono);
  font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase; padding-top: 2px;
}
.qhq-so-q b { font-family: var(--font-mono); font-weight: 600; font-size: 12.5px; text-align: right; }
.qhq-so-body .qhq-alertbar { margin-bottom: 16px; }
.qhq-so-body .qhq-alertbar .qhq-lamp { margin-top: 2px; }
.qhq-so-body .qhq-factplate { margin-bottom: 16px; }

/* Device cards on the site detail grid (2026-07-21 review: cards, not a
   table). Same unitcard shell; smaller gauge; lamp + mono name + role. */
.qhq-gauge--sm { width: 84px; }
.qhq-devcard:focus-visible { outline: 2px solid var(--qhq-green); outline-offset: 2px; }
.qhq-devcard__head { display: flex; align-items: flex-start; gap: 12px; }
.qhq-devcard__head .qhq-lamp { flex: none; margin-top: 5px; }
.qhq-devcard .qhq-devname { font-size: 15px; }
.qhq-devcard__foot {
  margin-top: 14px; display: flex; justify-content: space-between; align-items: center; gap: 10px;
}

/* Find-as-you-type filter over a card grid (org / site / device). */
.qhq-filterbar { display: flex; align-items: center; gap: 12px; }
.qhq-filterinput {
  width: 100%; max-width: 340px; padding: 8px 12px;
  background: var(--qhq-inset); border: 1px solid var(--qhq-line); border-radius: 7px;
  color: var(--text-primary); font-family: var(--font-mono); font-size: 12.5px;
}
.qhq-filterinput:focus { outline: 2px solid var(--qhq-green); outline-offset: -1px; }
.qhq-filterinput::placeholder { color: var(--text-tertiary); }
.qhq-filter-hide { display: none !important; }
.qhq-filter-nomatch {
  margin-top: 16px; padding: 10px 14px; border-radius: 7px;
  border: 1px dashed var(--qhq-line); color: var(--text-tertiary);
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.14em; text-transform: uppercase;
}

/* Cards|List segmented view toggle (fleet drill-down toolbars) + the
   view containers it swaps. Hidden-view class is separate from
   qhq-filter-hide so the find-as-you-type filter and the view toggle
   never fight over the same class. */
.qhq-viewtoggle {
  display: inline-flex; flex: none; margin-left: auto;
  background: var(--qhq-inset); border: 1px solid var(--qhq-line);
  border-radius: 7px; overflow: hidden;
}
.qhq-viewtoggle__btn {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: 8px 14px; color: var(--text-tertiary);
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.qhq-viewtoggle__btn + .qhq-viewtoggle__btn { border-left: 1px solid var(--qhq-line); }
.qhq-viewtoggle__btn:hover { color: var(--text-primary); }
.qhq-viewtoggle__btn.is-active { background: var(--qhq-line); color: var(--text-primary); }
.qhq-viewtoggle__btn:focus-visible { outline: 2px solid var(--qhq-green); outline-offset: -2px; }
.qhq-view-hide { display: none !important; }
.qhq-fleet-list__health { white-space: nowrap; }
.qhq-fleet-list .qhq-devrole { display: inline; margin-left: 6px; }

/* Dashboard Organizations strip — org → fleet navigation chips. */
.qhq-orgstrip { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 18px; }
.qhq-orgstrip__label {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-tertiary);
}
.qhq-orgstrip__chip {
  display: inline-flex; align-items: center; gap: 8px; padding: 7px 12px;
  border: 1px solid var(--qhq-line); border-radius: 7px; background: rgba(0, 0, 0, 0.18);
  color: var(--text-primary); text-decoration: none; font-size: 12.5px;
  transition: border-color 0.12s var(--qhq-ease), transform 0.12s var(--qhq-ease);
}
.qhq-orgstrip__chip:hover { border-color: var(--qhq-green); text-decoration: none; transform: translateY(-1px); }
.qhq-orgstrip__name { font-weight: 600; }
.qhq-orgstrip__count { font-family: var(--font-mono); font-size: 11px; color: var(--text-tertiary); }
.qhq-orgstrip__score {
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  padding: 1px 6px; border-radius: 5px; border: 1px solid var(--qhq-line);
  color: var(--text-secondary);
}
.qhq-orgstrip__score--ok   { color: var(--status-healthy); border-color: rgba(119, 186, 100, 0.35); }
.qhq-orgstrip__score--warn { color: var(--status-warning); border-color: rgba(232, 182, 68, 0.35); }
.qhq-orgstrip__score--bad  { color: var(--status-critical); border-color: rgba(212, 84, 84, 0.35); }
.qhq-orgstrip__problems { font-size: 11px; color: var(--status-warning); }
.qhq-orgstrip__ok { font-size: 11px; color: var(--text-tertiary); }
.qhq-orgstrip__empty { font-size: 11px; color: var(--text-tertiary); font-style: italic; }
.qhq-orgstrip__chip--more {
  border-style: dashed; color: var(--qhq-green);
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
}

/* ============================================================
   MOTION + PRINT
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .qhq-lamp--bad, .qhq-slamp--bad, .qhq-lnk--down, .qhq-lnk--tunnel,
  .qhq-gauge .qhq-gauge__needle, .qhq-slideover {
    animation: none !important; transition: none !important;
  }
}

/* Print: only an element marked .qhq-printable is emitted, re-lit for
   paper (white bg, high-contrast strokes). Used by the topology panel's
   "Print diagram" button (Phase 3). The hiding is scoped to
   body.qhq-print-diagram, so default printing is unaffected. */
@media print {
  body.qhq-print-diagram { background: #fff; }
  body.qhq-print-diagram * { visibility: hidden; }
  body.qhq-print-diagram .qhq-printable,
  body.qhq-print-diagram .qhq-printable * { visibility: visible; }
  body.qhq-print-diagram .qhq-printable {
    position: absolute; left: 0; top: 0; width: 100%;
    border: 0; box-shadow: none; background: #fff; border-radius: 0;
  }
  .qhq-printable::before, .qhq-printable::after { display: none; }
  .qhq-printable .qhq-rivet { display: none; }
  .qhq-printable .qhq-sectionhead { border-bottom: 2px solid #222; }
  .qhq-printable .qhq-etched { color: #333; text-shadow: none; }
  .qhq-printable .qhq-toolbtn { display: none; }
  .qhq-printable .qhq-steelplate { background: #fff; color: #111; border: 1.5px solid #111; text-shadow: none; box-shadow: none; }
  .qhq-printable .qhq-legend { color: #333; }
  .qhq-printable .qhq-legend__swatch { border-top-color: #888; }
  .qhq-printable .qhq-legend__swatch--down { border-top-color: #c0392b; }
  .qhq-printable .qhq-legend__swatch--tunnel { border-top-color: #285A84; }
  .qhq-printable .qhq-lnk { stroke: #777; }
  .qhq-printable .qhq-lnk--down { stroke: #c0392b; animation: none; }
  .qhq-printable .qhq-lnk--tunnel { stroke: #285A84; animation: none; }
  .qhq-printable .qhq-lnk--dim { stroke: #aaa; }
  .qhq-printable .qhq-node__shape { fill: #fff; stroke: #333; }
  .qhq-printable .qhq-node__glyph { stroke: #333; }
  .qhq-printable .qhq-node__glyph-fill { fill: #333; }
  .qhq-printable .qhq-nlabel { fill: #111; }
  .qhq-printable .qhq-nrole { fill: #555; }
  .qhq-printable .qhq-slamp { stroke: #333; }
  .qhq-printable .qhq-slamp--ok { fill: #4a8a3c; }
  .qhq-printable .qhq-slamp--warn { fill: #c78a1e; }
  .qhq-printable .qhq-slamp--bad { fill: #c0392b; animation: none; }
  .qhq-printable .qhq-slamp--off { fill: #bbb; }
  .qhq-printable .qhq-linktag { fill: #c0392b; }
  .qhq-printable .qhq-linktag--steel { fill: #285A84; }
  .qhq-printable .qhq-lamp { box-shadow: none; animation: none; }
}

/* Asset details — photo lightbox (assets_detail.html +
   assets-detail.js). The thumbnail is a real <button> (keyboard-
   reachable) stripped of button chrome; the lightbox reuses the shared
   dialog.modal surface, sized so the image fills the viewport minus a
   margin. Theme-aware via the same tokens dialog.modal uses. */
.asset-photo-thumb {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  border-radius: 6px;
}
.asset-photo-thumb:focus-visible { outline: 2px solid var(--qhq-green); outline-offset: 2px; }

dialog.modal--lightbox {
  padding: 8px;
  max-width: calc(100vw - 48px);
  max-height: calc(100vh - 48px);
}
.modal--lightbox__img {
  display: block;
  max-width: calc(100vw - 80px);
  max-height: calc(100vh - 140px);
  border-radius: var(--radius-md, 8px);
  cursor: zoom-out; /* clicking the image closes the viewer */
}
.modal--lightbox__bar {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

/* Asset edit — "Rescan stored photo" panel (assets_edit.html +
   assets-rescan.js). Card-like inset that matches the form chrome. */
.asset-rescan {
  margin-top: 20px;
  padding: 14px 16px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md, 8px);
  background: var(--bg-surface);
}
.asset-rescan__status { font-size: 12px; color: var(--text-tertiary); min-height: 16px; margin-top: 6px; }
.asset-rescan__raw {
  width: 100%;
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md, 8px);
  padding: 8px;
}

/* ════════════════════════════════════════════════════════════════════
   Full-fleet monitoring FIRST CUT (switch / AP / UPS typed pages +
   fleet type rollups) — ported from the 2026-08-04 design mockup.
   Prefix mk- kept 1:1 with the mockup so the two can be diffed while
   the layout is under operator review. Fleet-redesign PHASE 2 (switch
   page) shares this mk- vocabulary; its one additive style (the sortable
   connected-device table) is appended at the end of this block.
   ════════════════════════════════════════════════════════════════════ */

/* ─── fleet type rollup cards (screen 1) ─── */
.mk-typegrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 14px; }
.mk-type { background: var(--bg-surface); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); padding: 14px 16px; position: relative; }
.mk-type__icon { width: 30px; height: 30px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; background: var(--accent-primary-muted); color: var(--accent-primary); margin-bottom: 8px; }
.mk-type__icon svg { width: 18px; height: 18px; }
.mk-type__count { font-size: 26px; font-weight: 700; font-family: var(--font-mono); line-height: 1; }
.mk-type__label { font-size: 11px; font-weight: 600; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: .04em; margin-top: 4px; }
.mk-type__split { margin-top: 8px; display: flex; gap: 10px; font-size: 11px; font-family: var(--font-mono); }
.mk-type__split b { font-weight: 600; }
.ok-txt { color: var(--status-healthy); } .warn-txt { color: var(--status-warning); }
.err-txt { color: var(--status-critical); } .unk-txt { color: var(--status-unknown); }
.mk-healthbar { display: flex; height: 6px; border-radius: var(--radius-full); overflow: hidden; margin-top: 10px; background: var(--bg-base); }
.mk-healthbar i { display: block; height: 100%; }

/* ─── switch front-panel port graphic (screen 2) ─── */
.mk-panel {
  background: linear-gradient(180deg, #16273d, #0d1a2c);
  border-top: 1px solid var(--border-subtle);
  padding: 16px 18px 12px; overflow-x: auto;
}
.mk-panel__row { display: flex; align-items: flex-end; gap: 18px; }
.mk-portblock { display: grid; grid-template-rows: auto auto; grid-auto-flow: row; gap: 7px 6px; }
.mk-sfpblock { display: grid; grid-template-rows: auto auto; gap: 7px 6px; padding-left: 14px; border-left: 1px solid var(--border-subtle); }
.mk-port {
  width: 40px; height: 34px; border-radius: 4px; position: relative; cursor: pointer;
  border: 1px solid var(--border-default); background: var(--bg-base);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 10px; color: var(--text-tertiary);
  transition: box-shadow .12s ease, transform .12s ease;
}
.mk-port::before {
  content: ""; position: absolute; top: 3px; left: 50%; transform: translateX(-50%);
  width: 18px; height: 5px; border-radius: 1px; background: rgba(255,255,255,0.06);
}
.mk-port span { margin-top: 8px; }
.mk-port:hover { transform: translateY(-1px); box-shadow: 0 0 0 2px rgba(119,186,100,.35); }
.mk-port.is-selected { box-shadow: 0 0 0 2px var(--accent-primary); }
.mk-port--1g   { border-color: var(--status-healthy); background: rgba(119,186,100,.16); color: var(--text-primary); }
.mk-port--1g::before { background: var(--status-healthy); }
.mk-port--100m { border-color: var(--status-warning); background: rgba(232,182,68,.14); color: var(--text-primary); }
.mk-port--100m::before { background: var(--status-warning); }
/* State "up": attachment evidence without a reported rate (PoE delivering,
   a client placed on the port, a downstream switch's port-only uplink
   record). Healthy border, no speed tint — the 1g/100m fills are reserved
   for a rate the vendor actually reported. */
.mk-port--up   { border-color: var(--status-healthy); color: var(--text-primary); }
.mk-port--up::before { background: var(--status-healthy); }
.mk-port--dis  { border-style: dashed; opacity: .55; }
.mk-port--dis::after { content: "×"; position: absolute; top: -1px; right: 3px; color: var(--status-unknown); font-size: 11px; }
/* State "unknown": the management plane reported no link state. Visibly
   distinct from both "up" and "link down" — never styled as either. (The
   Omada mapper emits it for an evidence-free port on a row that carries no
   topology reading — a v1 row, or one written while the topology endpoint
   was failing — C149.) */
.mk-port--unk  { border-style: dotted; }
.mk-port--unk::after { content: "?"; position: absolute; top: -1px; right: 3px; color: var(--status-unknown); font-size: 11px; }
/* State modifier "as of": this port's link state came from an EARLIER
   reading than the rest of the row — the Omada collector carried the
   last good topology reading forward across a cycle that produced none
   (C149). A THIRD state, distinct from both a current reading and "not
   reported": the link state is real and was observed at a stated time,
   it simply has not been confirmed since. Composes with every state
   class rather than replacing one (a carried port is still 1g / up /
   down / disabled), so it uses the inset edge bar and leaves the
   ::before speed bar and the ::after corner marks of --dis / --unk
   alone. The age itself is text — the panel banner, the legend entry
   and each square's title/aria-label — never colour alone. */
.mk-port--asof { box-shadow: inset 2px 0 0 var(--status-info); }
.mk-panel__asof {
  margin: 0 0 8px; padding: 5px 9px; font-size: 11px; line-height: 1.45;
  color: var(--text-secondary); background: var(--bg-subtle);
  border-left: 2px solid var(--status-info); border-radius: 3px;
}
.mk-port__poe {
  position: absolute; top: -6px; right: -5px; width: 13px; height: 13px; border-radius: 50%;
  background: var(--status-info); color: var(--text-inverse);
  font-size: 9px; line-height: 13px; text-align: center; font-weight: 700;
}
.mk-portlegend { display: flex; gap: 16px; margin-top: 12px; font-size: 11px; color: var(--text-tertiary); flex-wrap: wrap; align-items: center; }
.mk-portlegend i { width: 12px; height: 9px; border-radius: 2px; display: inline-block; margin-right: 5px; vertical-align: -1px; }
.mk-portdetail {
  border-top: 1px solid var(--border-subtle); background: var(--bg-base);
  padding: 12px 16px; display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px;
  font-size: 12px;
}
.mk-portdetail label { display: block; font-size: 10px; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: .05em; }
.mk-portdetail b { font-family: var(--font-mono); font-weight: 600; color: var(--text-primary); }

/* ─── switch redesign: per-port device-type icons + labeling ─── */
.mk-port { padding-top: 1px; }
.mk-port span { position: absolute; bottom: 1px; left: 0; right: 0; text-align: center; margin-top: 0; font-size: 8px; line-height: 1; }
.mk-port__icon { width: 17px; height: 17px; margin-top: 7px; color: var(--text-primary); opacity: .9; pointer-events: none; }
.mk-port--1g .mk-port__icon, .mk-port--100m .mk-port__icon, .mk-port--up .mk-port__icon { opacity: 1; }
.mk-portlegend__sep { color: var(--text-tertiary); font-style: italic; }

.mk-typecell { display: inline-flex; align-items: center; gap: 6px; }
.mk-typeicon { width: 15px; height: 15px; color: var(--text-secondary); flex: none; }

.mk-portpanels { border-top: 1px solid var(--border-subtle); background: var(--bg-base); padding: 12px 16px 14px; }
.mk-portpanel__head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; }
.mk-portpanel__title { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.mk-portpanel__friendly { color: var(--accent-primary); }
.mk-portpanel__empty { font-size: 12px; color: var(--text-tertiary); font-style: italic; }
.mk-portpanel__table { width: 100%; }
.mk-labelform { display: inline-flex; align-items: center; gap: 6px; }
.mk-labelform__input {
  font-size: 12px; padding: 4px 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-default); background: var(--bg-surface); color: var(--text-primary);
  min-width: 150px;
}
.mk-anchor-note { font-size: 10px; color: var(--text-tertiary); margin-top: 4px; max-width: 320px; }

/* topology map: focus ring shown while wheel-zoom is armed */
[data-topology-svg].is-focused { outline: 2px solid var(--accent-primary); outline-offset: -2px; }

/* ─── AP radio cards + signal bars (screen 3) ─── */
.mk-radio__band { font-family: var(--font-mono); font-size: 15px; font-weight: 700; color: var(--text-primary); display: flex; align-items: center; gap: 8px; }
.mk-kv { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; padding: 3px 0; }
.mk-kv .k { color: var(--text-tertiary); }
.mk-kv .v { font-family: var(--font-mono); color: var(--text-primary); }
.mk-sig { display: inline-flex; align-items: flex-end; gap: 1.5px; height: 12px; margin-right: 6px; }
.mk-sig i { width: 3px; border-radius: 1px; background: var(--status-unknown); display: inline-block; }
.mk-sig i:nth-child(1){height:4px} .mk-sig i:nth-child(2){height:7px} .mk-sig i:nth-child(3){height:10px} .mk-sig i:nth-child(4){height:12px}
.mk-sig--4 i { background: var(--status-healthy); }
.mk-sig--3 i:nth-child(-n+3) { background: var(--status-healthy); }
.mk-sig--2 i:nth-child(-n+2) { background: var(--status-warning); }
.mk-sig--1 i:nth-child(1) { background: var(--status-critical); }

/* ─── UPS gauges (screen 4) ─── */
.mk-gaugerow { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 14px; }
.mk-gauge { background: var(--bg-surface); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); padding: 14px 16px 10px; text-align: center; }
.mk-gauge svg { width: 150px; max-width: 100%; }
.mk-gauge__label { font-size: 11px; font-weight: 600; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: .04em; }
.mk-gauge__note { font-size: 10px; color: var(--text-tertiary); font-style: italic; margin-top: 2px; }

/* ─── shared two/three-column helpers + sparkline info tone ─── */
.mk-cols2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
.mk-cols3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.sparkline-bar--info { background: var(--status-info); }

@media (max-width: 900px) {
  .mk-typegrid, .mk-gaugerow, .mk-cols3 { grid-template-columns: repeat(2, 1fr); }
  .mk-cols2 { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .mk-typegrid, .mk-gaugerow, .mk-cols3 { grid-template-columns: 1fr; }
}

/* mk- addendum: tones/variants the mockup uses that the shipped dd-*
   set didn't have yet. */
.dd-event__dot--ok { background: var(--status-healthy); }
.dd-hbar__fill--info { background: var(--status-info); }
.dd-statgrid--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .dd-statgrid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .dd-statgrid--4 { grid-template-columns: 1fr; } }

/* Fleet-redesign PHASE 2 — sortable connected-device table on the switch
   page: clickable headers + sort indicator (switchdetail.js). */
.data-table[data-sortable] th[data-sort-type] { cursor: pointer; user-select: none; white-space: nowrap; }
.data-table[data-sortable] th[data-sort-type]::after { content: "\2195"; opacity: .35; margin-left: 5px; font-size: 10px; }
.data-table[data-sortable] th[aria-sort="ascending"]::after { content: "\2191"; opacity: .9; }
.data-table[data-sortable] th[aria-sort="descending"]::after { content: "\2193"; opacity: .9; }

/* ── UPS event timeline (device Monitoring tab) ────────────────────────────
   Server-rendered SVG event-timeline: on_battery duration bands + per-type
   markers on one shared time axis, a toggleable legend, and a density strip.
   Colours come from the status tokens so warnings are never green. Toggle
   behaviour is in static/js/upsdetail.js. */
.ups-tl { margin-top: 14px; }
.ups-tl__legend { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.ups-tl__chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px 3px 5px; border-radius: 12px; cursor: pointer;
  font-size: 11px; line-height: 1; color: var(--text-secondary);
  background: var(--bg-elevated); border: 1px solid var(--border-subtle);
  user-select: none;
}
.ups-tl__chip:hover { border-color: var(--border-strong); color: var(--text-primary); }
.ups-tl__chip[aria-pressed="false"] { opacity: .4; text-decoration: line-through; }
.ups-tl__swatch { width: 16px; height: 16px; flex: 0 0 16px; }
.ups-tl__count { color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
.ups-tl__scroll { overflow-x: auto; }
.ups-tl__svg { width: 100%; min-width: 520px; height: auto; display: block; }
.ups-tl__grid { stroke: var(--border-subtle); stroke-width: 1; }
.ups-tl__tick, .ups-tl__lanelabel, .ups-tl__striplabel {
  fill: var(--text-tertiary); font-size: 10px; font-family: var(--font-body);
}
.ups-tl__lanelabel { fill: var(--text-secondary); font-size: 11px; }
.ups-tl__striplabel { fill: var(--text-tertiary); font-size: 10px; }
.ups-tl__bar { fill: var(--status-info); opacity: .78; }

/* Marker + band colour discipline: tone class drives fill (or stroke for the
   cross), so a warning is amber and never green. */
.ups-tl-mark { fill: var(--status-info); }
.ups-tl-mark--stroke { fill: none; stroke-width: 2; stroke-linecap: round; }
.ups-tl--healthy.ups-tl-mark { fill: var(--status-healthy); }
.ups-tl--warning.ups-tl-mark { fill: var(--status-warning); }
.ups-tl--critical.ups-tl-mark { fill: var(--status-critical); }
.ups-tl--info.ups-tl-mark { fill: var(--status-info); }
.ups-tl--neutral.ups-tl-mark { fill: var(--status-unknown); }
.ups-tl-mark--stroke.ups-tl--healthy { stroke: var(--status-healthy); }
.ups-tl-mark--stroke.ups-tl--warning { stroke: var(--status-warning); }
.ups-tl-mark--stroke.ups-tl--critical { stroke: var(--status-critical); }
.ups-tl-mark--stroke.ups-tl--info { stroke: var(--status-info); }
.ups-tl-mark--stroke.ups-tl--neutral { stroke: var(--status-unknown); }

.ups-tl-band { fill: var(--status-warning); opacity: .30; stroke: var(--status-warning); stroke-opacity: .7; stroke-width: 1; }
.ups-tl-band--open { stroke-dasharray: 3 2; }

.ups-tl__hint { margin-top: 8px; font-size: 11px; color: var(--text-tertiary); }

/* ── Direction-B multi-device health card (fleet home) ─────────────────
   Per device-type group, a per-device composite-score mini-bar with a
   term-by-term hover tooltip. Colour is spent only on the score band
   (healthy/at-risk/degraded); the numeric score sits ABOVE the device
   name so it never crowds the label (small-multiples fix, Mark). */
/* let the hover tooltips rise out of the card instead of being clipped by
   the .dd-card overflow:hidden. */
.dd-card.qhq-health { overflow: visible; }
.qhq-health__legend { margin-left: auto; display: inline-flex; gap: 14px; font-size: 11px; color: var(--text-secondary); font-weight: 400; }
.qhq-health__leg { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.qhq-health__swatch { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.qhq-health__swatch--healthy { background: var(--status-healthy); }
.qhq-health__swatch--warning { background: var(--status-warning); }
.qhq-health__swatch--critical { background: var(--status-critical); }

.qhq-health__groups { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 18px; padding: 14px 4px 6px; }
.qhq-hgroup { min-width: 0; }
.qhq-hgroup__head { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; margin-bottom: 10px; border-bottom: 1px solid var(--border-subtle); }
.qhq-hgroup__icon { width: 15px; height: 15px; color: var(--text-tertiary); display: inline-flex; }
.qhq-hgroup__icon svg { width: 15px; height: 15px; }
.qhq-hgroup__label { font-size: 12px; font-weight: 600; color: var(--text-primary); text-transform: uppercase; letter-spacing: .04em; }
.qhq-hgroup__counts { margin-left: auto; font-family: var(--font-mono); font-size: 11px; }
.qhq-hgroup__counts b { font-weight: 700; }

/* mini-bars row — dense; wraps to a new line rather than scrolling so the
   hover tooltip (which rises above a bar) is never clipped by an overflow
   container. */
.qhq-hbars { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px 10px; padding-bottom: 4px; }
.qhq-hbar {
  position: relative; flex: 0 0 auto; width: 46px;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  text-decoration: none; border-radius: var(--radius-sm); padding: 4px 2px;
  transition: background .12s;
}
.qhq-hbar:hover, .qhq-hbar:focus-visible { background: var(--bg-elevated); outline: none; }
/* percent value ABOVE the name (small-multiples fix); light-grey by default,
   band colour on hover — colour discipline. */
.qhq-hbar__pct { font-family: var(--font-mono); font-size: 12px; font-weight: 700; color: var(--text-secondary); line-height: 1; }
.qhq-hbar__track {
  position: relative; width: 20px; height: 76px; border-radius: 3px;
  background: var(--bg-input); overflow: hidden; display: flex; align-items: flex-end;
  box-shadow: inset 0 0 0 1px var(--border-subtle);
}
.qhq-hbar__track i { display: block; width: 100%; border-radius: 3px 3px 0 0; min-height: 3px; }
.qhq-hbar__name {
  font-size: 10px; color: var(--text-tertiary); font-family: var(--font-mono);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center;
}
/* band colours — bar fill + a shape cue (top cap) so status is legible
   beyond colour alone. */
.qhq-hbar--healthy  .qhq-hbar__track i { background: var(--status-healthy); }
.qhq-hbar--warning  .qhq-hbar__track i { background: var(--status-warning); }
.qhq-hbar--critical .qhq-hbar__track i { background: var(--status-critical); }
.qhq-hbar--warning  .qhq-hbar__track i { border-top: 2px dashed var(--bg-input); }
.qhq-hbar--critical .qhq-hbar__track i { border-top: 2px solid var(--bg-input); }
.qhq-hbar:hover .qhq-hbar__pct, .qhq-hbar:focus-visible .qhq-hbar__pct { color: var(--text-primary); }
.qhq-hbar--warning:hover  .qhq-hbar__pct { color: var(--status-warning); }
.qhq-hbar--critical:hover .qhq-hbar__pct { color: var(--status-critical); }

/* hover / focus tooltip — the per-device term breakdown */
.qhq-hbar__tip {
  position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%) translateY(4px);
  z-index: 40; width: 214px; padding: 10px 11px;
  background: var(--bg-overlay); border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0,0,0,.38);
  opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .12s, transform .12s;
  text-align: left;
}
.qhq-hbar:hover .qhq-hbar__tip, .qhq-hbar:focus-visible .qhq-hbar__tip {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}
.qhq-hbar__tiphead { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 8px; padding-bottom: 7px; border-bottom: 1px solid var(--border-subtle); }
.qhq-hbar__tiphead b { font-size: 12px; color: var(--text-primary); font-family: var(--font-mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qhq-hbar__tipscore { font-family: var(--font-mono); font-size: 11px; font-weight: 700; white-space: nowrap; }
.qhq-hbar__tipscore--healthy { color: var(--status-healthy); }
.qhq-hbar__tipscore--warning { color: var(--status-warning); }
.qhq-hbar__tipscore--critical { color: var(--status-critical); }
.qhq-hbar__tiprow { display: grid; grid-template-columns: 68px 26px 1fr; align-items: baseline; gap: 6px; padding: 2px 0; font-size: 10.5px; }
.qhq-hbar__tiprow .k { color: var(--text-secondary); }
.qhq-hbar__tiprow .v { font-family: var(--font-mono); font-weight: 700; color: var(--text-primary); text-align: right; }
.qhq-hbar__tiprow .d { color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qhq-hbar__tiprow.is-dropped { opacity: .5; }
.qhq-hbar__tiprow.is-dropped .v { color: var(--text-tertiary); font-weight: 400; }

/* ============================================================
   Global search palette (topbar Cmd/Ctrl+K) — globalsearch.js.
   Hidden via the `hidden` attribute until opened; a fixed scrim
   plus a centered panel. Colors are design tokens only.
   ============================================================ */
.qhq-search {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
.qhq-search[hidden] { display: none; }
.qhq-search__scrim {
  position: absolute;
  inset: 0;
  background: rgba(4, 10, 20, 0.62);
}
.qhq-search__panel {
  position: relative;
  width: min(640px, calc(100vw - 2 * var(--space-4)));
  margin-top: 12vh;
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.qhq-search__inputrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
}
.qhq-search__icon {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  color: var(--text-tertiary);
}
.qhq-search__input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-size: 1.05rem;
  font-family: inherit;
}
.qhq-search__input::placeholder { color: var(--text-tertiary); }
.qhq-search__esc {
  flex: 0 0 auto;
  padding: 2px 7px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  color: var(--text-tertiary);
  font-family: var(--font-mono);
  font-size: 0.72rem;
}
.qhq-search__results {
  list-style: none;
  margin: 0;
  padding: var(--space-2) 0;
  overflow-y: auto;
}
.qhq-search__group {
  padding: var(--space-2) var(--space-4) var(--space-1);
  color: var(--text-tertiary);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.qhq-search__result {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
}
.qhq-search__result--active {
  background: var(--accent-primary-muted);
}
.qhq-search__result-label {
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qhq-search__result-sub {
  color: var(--text-tertiary);
  font-size: 0.78rem;
  margin-left: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qhq-search__empty {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--text-tertiary);
  font-size: 0.85rem;
}
.qhq-search__empty[hidden] { display: none; }

/* The asset forms' implicit-submission sink (C231): the nameless hidden
   submit button that is deliberately the form's DEFAULT button, so an
   accidental Enter with JS off posts without the save=1 marker and the
   server refuses it. `hidden` alone is a presentation hint any rule
   with a display value overrides — and a visible one-pixel button
   inside the form's flex/grid layout would be a control the operator
   can tap. Pin it shut. */
[data-asset-implicit-sink] { display: none !important; }
