/* Components — atomic UI library, every value via tokens.css.
 *
 * Rules:
 *   - No hardcoded hex codes. No fallbacks in var() calls.
 *   - Each component has a doc-comment above explaining purpose + variants.
 *   - Components compose via classes — no nested utility classes.
 *   - State (hover, active, disabled, focus) handled by :pseudo selectors,
 *     not by separate classes (.btn:hover, not .btn--hover).
 *
 * Inventory (alphabetical):
 *   .alert            — banner / flash message with semantic variants
 *   .badge            — inline pill, semantic colors, optional pulse dot
 *   .bot-log-panel    — collapsible recent log lines panel
 *   .bot-status       — top hero showing bot running/stopped state
 *   .btn              — button base + variants (primary, ghost, danger,
 *                       success, sm, lg)
 *   .btn-inline-toggle — inline checkbox-style toggle (in toolbars)
 *   .card             — generic container, hover-lifts via shadow + 1px translate
 *   .divider          — 1px horizontal rule
 *   .dot-*            — semantic dot color helpers (success/error/warn/info)
 *   .drawer           — slide-in panel from right (used by Fleet detail)
 *   .empty-state      — composed empty: icon + title + body + CTA
 *   .file-btn         — styled <label> wrapping <input type="file">
 *   .filter-btn       — toolbar pill button with .active state
 *   .htmx-indicator   — visible only while a request is in flight
 *   .input            — text inputs, textarea, select
 *   .kbd              — inline mono "code" tag for keys, IDs, paths
 *   .kpi              — stat card for the Overview KPI strip
 *   .kpi-strip        — auto-fit grid container for .kpi cards
 *   .label            — form label + .label-hint helper
 *   .modal            — full-screen / centered modal shell
 *   #nprogress        — top-of-page HTMX progress bar
 *   .sidebar-meta     — non-interactive informational footer in sidebar
 *   .skeleton         — shimmering placeholder for loading data
 *   .skip-link        — keyboard a11y "skip to content"
 *   .spinner          — rotating circle loader
 *   .stat             — legacy stat tile (kept for back-compat, prefer .kpi)
 *   .status-pill      — small pulse-able status dot + label
 *   .table            — data table with sortable headers
 *   .tabs             — horizontal tab bar
 *   .toast            — transient notification (slide-in from right)
 *   .toggle           — accessible switch with brand-yellow checked state
 *   .toolbar          — filter / search / action row above tables
 */

/* =========================================================================
   CARD — generic container.
   Subtle top-edge highlight via ::before suggests light catching the
   surface. Border at 1px subtle, hover lifts it to default.
   ========================================================================= */
.card {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--duration-normal) var(--ease-standard),
              transform var(--duration-normal) var(--ease-standard);
  position: relative;
  overflow: hidden;
}
/* Hover: subtle 1px lift + shadow boost. The translate is small enough to
   read as "active" without distracting; the shadow does most of the work.
   Linear/Vercel use this exact combo on dashboard cards. */
.card:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.card-title {
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--text-primary);
  letter-spacing: var(--tracking-snug);
  line-height: var(--leading-tight);
}
.card-subtitle {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  margin-top: 4px;
  line-height: var(--leading-snug);
}
.card-body {
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--text-secondary);
}
.card-body strong, .card-body b {
  color: var(--text-primary);
  font-weight: var(--font-semibold);
}
.card-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-1);
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border-subtle);
}

/* Compact card variant — tighter padding for dense displays */
.card--inset {
  padding: var(--space-2);
}

/* Ghost card — no background, just outline. For nesting cards inside a
   parent card without compounding the elevation. */
.card--ghost {
  background: transparent;
  border-color: var(--border-subtle);
  box-shadow: none;
}

/* =========================================================================
   KPI — stat card for dashboard top strip.
   Label (uppercase eyebrow) + big tabular value + delta hint + sparkline
   slot. Designed for the Overview "fleet at a glance" row.
   ========================================================================= */
.kpi-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.kpi {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-2) var(--space-2-5, var(--space-3));
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  transition: border-color var(--duration-normal) var(--ease-standard);
}
.kpi:hover { border-color: var(--border-default); }
.kpi-label {
  font-size: var(--text-2xs);
  font-weight: var(--font-emphasis);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-widest);
}
.kpi-value {
  font-size: var(--text-2xl);
  font-weight: var(--font-semibold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.kpi-delta {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.kpi-delta--up   { color: var(--color-success); }
.kpi-delta--down { color: var(--color-error); }

/* =========================================================================
   STAT — legacy tile (used in some places). Prefer .kpi for new code.
   ========================================================================= */
.stat {
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  padding: var(--space-2) var(--space-3);
  position: relative;
  overflow: hidden;
  transition: border-color var(--duration-normal) var(--ease-standard);
}
.stat:hover { border-color: var(--border-default); }
.stat-label {
  color: var(--text-tertiary);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-widest);
  font-weight: var(--font-semibold);
}
.stat-value {
  font-size: var(--text-3xl);
  font-weight: var(--font-semibold);
  letter-spacing: var(--tracking-display);
  line-height: 1.1;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.stat-hint { font-size: var(--text-xs); color: var(--text-tertiary); }
.stat.success .stat-value { color: var(--color-success); }
.stat.warning .stat-value { color: var(--color-warning); }
.stat.error   .stat-value { color: var(--color-error); }
/* Brand variant — keeps the .brand class for backward compat but
   renders the value in primary dark text (Stripe/Linear/Vercel
   pattern). Tinting numbers a brand color is dated — modern dashboards
   keep numerals neutral so the eye reads them as data, not as accent. */
.stat.brand .stat-value { color: var(--text-primary); }

/* =========================================================================
   BUTTONS
   .btn          — base ghost button (secondary actions)
   .btn-primary  — Snap yellow gradient (primary CTA, max one per region)
   .btn-danger   — destructive red (delete, kill, etc.)
   .btn-ghost    — bare, transparent (tertiary actions)
   .btn-sm       — compact size for inline / table actions
   .btn-lg       — for hero areas
   ========================================================================= */
/* Buttons — chunky 3D relief ("SNES chip") look across all variants.
   The relief is built with 4 layered shadows :
     1. inset top edge highlight (white-ish) — the "lit top" of the chip
     2. inset bottom edge shadow (dark) — the "underside" of the chip
     3. outer 1px floor shadow — anchors it to the surface below
     4. outer ambient shadow — gives weight, picks up at hover
   Pressing inverts the gradient: highlight moves to bottom, shadow inset
   at top, transform translateY(1px). The variants (primary/success/
   danger) keep their colored backgrounds and borders but inherit the
   same relief layering, scaled to the variant's color tone.

   Ghost stays flat — it's a tertiary action that should NOT compete
   visually with the relief buttons around it. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  height: 38px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface-raised);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 0 rgba(0, 0, 0, 0.05),
    0 1px 0 rgba(0, 0, 0, 0.06),
    0 2px 3px rgba(0, 0, 0, 0.05);
  transition:
    background var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard);
  text-decoration: none;
  white-space: nowrap;
}
.btn:hover {
  color: var(--text-primary);
  transform: translateY(-1px);
  text-decoration: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    inset 0 -2px 0 rgba(0, 0, 0, 0.06),
    0 2px 0 rgba(0, 0, 0, 0.06),
    0 4px 6px rgba(0, 0, 0, 0.08);
}
.btn:active {
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.10),
    inset 0 -1px 0 rgba(255, 255, 255, 0.40),
    0 0 0 rgba(0, 0, 0, 0);
}
.btn:focus-visible { outline: none; box-shadow: var(--state-focus-ring); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }
.btn:disabled:hover { transform: none; box-shadow: var(--shadow-sm); }

/* PRIMARY — Snap yellow chip. Yellow gradient + warm tinted highlights. */
.btn-primary {
  background: var(--brand-gradient);
  border-color: var(--brand-700);
  color: var(--text-on-brand);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.60),
    inset 0 -2px 0 rgba(0, 0, 0, 0.10),
    0 1px 0 rgba(0, 0, 0, 0.10),
    var(--shadow-brand-sm);
}
.btn-primary:hover {
  background: var(--brand-gradient);
  border-color: var(--brand-800);
  color: var(--text-on-brand);
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.75),
    inset 0 -2px 0 rgba(0, 0, 0, 0.12),
    0 2px 0 rgba(0, 0, 0, 0.10),
    var(--shadow-brand-md);
}
.btn-primary:active {
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.18),
    inset 0 -1px 0 rgba(255, 255, 255, 0.30);
}

/* DANGER — red. Subtle red border + red text, neutral chip body. */
.btn-danger {
  color: var(--color-error);
  border-color: var(--color-error-border);
  background: var(--surface-raised);
}
.btn-danger:hover {
  background: var(--color-error-bg);
  border-color: var(--color-error);
  color: var(--color-error);
}

/* SUCCESS — green-tinted. Used for the "✓ Active" mark + similar. */
.btn-success {
  color: var(--color-success);
  border-color: var(--color-success-border);
  background: var(--color-success-bg);
}
.btn-success:hover {
  background: var(--color-success-bg);
  border-color: var(--color-success);
  color: var(--color-success);
}

/* GHOST — explicitly flat. No relief, no shadow — it's a tertiary
   action. Used when we want the surrounding context to dominate. */
.btn-ghost {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
.btn-ghost:hover {
  background: var(--state-hover);
  border-color: var(--border-subtle);
  transform: none;
  box-shadow: none;
}
.btn-ghost:active { transform: translateY(1px); box-shadow: none; }

.btn-sm { height: 30px; padding: 0 var(--space-1-5); font-size: var(--text-xs); }
.btn-lg { height: 44px; padding: 0 var(--space-3); font-size: var(--text-base); }

/* File-input button — styled <label> wrapping a hidden <input type="file">.
   The native file input is unstyle-able cross-browser; the label's click
   forwards to it through native HTML behavior, no JS needed. */
.file-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.file-btn input[type="file"] {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.file-btn:hover { background: var(--state-hover); border-color: var(--border-strong); }
.file-btn:active { transform: translateY(1px); }

/* HTMX indicator pattern — visible only while a request is in flight. */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-flex; }

/* =========================================================================
   SPINNER — rotating circle loader. Single source of truth.
   ========================================================================= */
.spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid var(--border-subtle);
  border-top-color: var(--brand-500);
  border-radius: var(--radius-pill);
  animation: spinner-rotate 0.8s linear infinite;
  vertical-align: middle;
}
@keyframes spinner-rotate { to { transform: rotate(360deg); } }

/* =========================================================================
   INPUTS, TEXTAREA, SELECT
   ========================================================================= */
.input, textarea, select {
  width: 100%;
  height: 38px;
  padding: 0 12px;
  background: var(--surface-base);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--text-sm);
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}
.input::placeholder, textarea::placeholder { color: var(--text-tertiary); }
.input:hover, textarea:hover, select:hover { border-color: var(--border-strong); }
.input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--brand-500);
  box-shadow: var(--state-focus-ring);
}

textarea {
  height: auto;
  min-height: 140px;
  padding: 12px;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  resize: vertical;
}

.label {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--text-primary);
  margin-bottom: 8px;
  letter-spacing: 0.01em;
}
.label-hint {
  display: block;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  margin-top: 6px;
  line-height: 1.4;
}

/* =========================================================================
   KBD — inline mono "code" tag.
   For keys, account IDs, file paths, license keys (always masked). One
   class instead of repeated inline `style="padding: 4px 8px; background: ..."`.
   ========================================================================= */
.kbd {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.85em;
  padding: 2px 8px;
  background: var(--surface-overlay);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  letter-spacing: 0;
  line-height: 1.5;
}
.kbd--sm { padding: 1px 6px; font-size: 0.78em; }

/* =========================================================================
   TOGGLE — accessible switch
   ========================================================================= */
.toggle {
  --toggle-w: 40px;
  --toggle-h: 22px;
  --toggle-pad: 3px;
  position: relative;
  display: inline-block;
  width: var(--toggle-w);
  height: var(--toggle-h);
  flex-shrink: 0;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle .track {
  position: absolute; inset: 0;
  background: var(--surface-highest);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  transition: background var(--duration-normal) var(--ease-standard),
              border-color var(--duration-normal) var(--ease-standard);
  cursor: pointer;
}
.toggle .thumb {
  position: absolute;
  top: var(--toggle-pad);
  left: var(--toggle-pad);
  width: calc(var(--toggle-h) - var(--toggle-pad) * 2 - 2px);
  height: calc(var(--toggle-h) - var(--toggle-pad) * 2 - 2px);
  background: var(--text-primary);
  border-radius: var(--radius-pill);
  transition: transform var(--duration-normal) var(--ease-spring);
  pointer-events: none;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.toggle input:checked + .track {
  background: var(--brand-gradient);
  border-color: transparent;
  box-shadow: 0 0 12px var(--brand-tint-strong);
}
.toggle input:checked + .track .thumb {
  background: var(--surface-raised);
  transform: translateX(calc(var(--toggle-w) - var(--toggle-h)));
}
.toggle input:focus-visible + .track { box-shadow: var(--state-focus-ring); }

.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) 0;
  gap: var(--space-3);
}
.toggle-row + .toggle-row { border-top: 1px solid var(--border-subtle); }
.toggle-row .info { display: flex; flex-direction: column; gap: 3px; }
.toggle-row .info .name {
  font-weight: var(--font-emphasis);
  font-size: var(--text-sm);
  color: var(--text-primary);
}
.toggle-row .info .desc {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
}

/* =========================================================================
   BADGES — inline status pills. Same visual weight, semantic color via class.
   ========================================================================= */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0-5);
  padding: var(--space-0-5) var(--space-1-5);
  border-radius: var(--radius-pill);
  font-size: var(--text-2xs);
  font-weight: var(--font-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  border: 1px solid transparent;
  line-height: 1.4;
}
.badge .dot {
  width: 6px; height: 6px;
  border-radius: var(--radius-pill);
  background: currentColor;
}
.badge-alive {
  color: var(--color-success);
  background: var(--color-success-bg);
  border-color: var(--color-success-border);
}
.badge-dead {
  color: var(--color-error);
  background: var(--color-error-bg);
  border-color: var(--color-error-border);
}
.badge-challenged {
  color: var(--color-warning);
  background: var(--color-warning-bg);
  border-color: var(--color-warning-border);
}
.badge-brand {
  color: var(--text-brand);
  background: var(--state-selected);
  border-color: var(--brand-border-subtle);
}
.badge-neutral {
  color: var(--text-secondary);
  background: var(--state-hover);
  border-color: var(--border-subtle);
  text-transform: none;
  letter-spacing: 0;
  font-weight: var(--font-emphasis);
}

/* Live pulse only on alive badges */
.badge-alive .dot {
  animation: badge-pulse var(--pulse-duration) var(--ease-standard) infinite;
}
@keyframes badge-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 currentColor; }
  50% { opacity: 0.7; box-shadow: 0 0 0 4px transparent; }
}

/* =========================================================================
   STATUS-PILL — small colored dot + label, designed for fleet tiles.
   Pulse animation on warn / problem variants. Colors matched to
   --color-* tokens.
   ========================================================================= */
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  font-weight: var(--font-emphasis);
}
.status-pill .dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}
.status-pill--ok       .dot { background: var(--color-success); }
.status-pill--warn     .dot {
  background: var(--color-warning);
  animation: status-pulse var(--pulse-duration) var(--ease-standard) infinite;
}
.status-pill--problem  .dot {
  background: var(--color-error);
  animation: status-pulse var(--pulse-duration-urgent) var(--ease-standard) infinite;
}
.status-pill--idle     .dot { background: var(--text-quaternary); }
.status-pill--needs-login .dot {
  background: var(--brand-500);
  box-shadow: 0 0 6px var(--brand-tint-strong);
}
@keyframes status-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

/* =========================================================================
   ALERT — banner / flash message
   ========================================================================= */
.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-1-5);
  padding: 12px 16px;
  border-radius: var(--radius-md);
  border: 1px solid;
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--text-primary);
}
.alert::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  margin-top: 7px;
  flex-shrink: 0;
  background: var(--alert-accent);
  box-shadow: 0 0 8px var(--alert-accent);
}
.alert b, .alert strong {
  color: var(--text-primary);
  font-weight: var(--font-semibold);
}
.alert-success {
  --alert-accent: var(--color-success);
  background: var(--color-success-bg);
  border-color: var(--color-success-border);
}
.alert-info {
  --alert-accent: var(--color-info);
  background: var(--color-info-bg);
  border-color: var(--color-info-border);
}
.alert-warning {
  --alert-accent: var(--color-warning);
  background: var(--color-warning-bg);
  border-color: var(--color-warning-border);
}
.alert-error {
  --alert-accent: var(--color-error);
  background: var(--color-error-bg);
  border-color: var(--color-error-border);
}

/* =========================================================================
   TABLE — data table with sticky-header pattern
   ========================================================================= */
.table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.table th, .table td {
  padding: var(--space-1-5) var(--space-1-5);
  text-align: left;
  font-size: var(--text-sm);
}
.table.compact th, .table.compact td { padding: var(--space-1) var(--space-1); }
.table.compact td.num,
.table.compact th.num { font-size: var(--text-xs); }

#accounts-table { table-layout: fixed; }
#accounts-table td { vertical-align: top; }
#accounts-table .truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.table thead th {
  background: var(--surface-overlay);
  color: var(--text-tertiary);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-widest);
  font-weight: var(--font-semibold);
  border-bottom: 1px solid var(--border-default);
}
.table tbody tr {
  transition: background var(--duration-fast) var(--ease-standard);
}
/* Hover row — yellow tint at low alpha to anchor the brand identity
   without making the table itself feel like a CTA. */
.table tbody tr:hover { background: var(--brand-tint-faint); }
.table tbody tr + tr td { border-top: 1px solid var(--border-subtle); }
.table td.num {
  font-variant-numeric: tabular-nums;
  font-family: var(--font-mono);
  color: var(--text-primary);
  font-size: 13px;
}

/* =========================================================================
   TOOLBAR — filter / search / actions row, typically above a table.
   Sticky in scroll for long tables.
   ========================================================================= */
.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  margin-bottom: var(--space-2);
  flex-wrap: wrap;
}
.toolbar-left {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  flex-wrap: wrap;
}
.toolbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: wrap;
}
.toolbar .input {
  width: auto;
  min-width: 220px;
}
/* Filter buttons — extend .btn but with one twist: when this filter is
   the active one (.active class), the chip stays pressed-in. Inherits
   the relief shadows from .btn so we only override the .active state. */
.filter-btn {
  color: var(--text-secondary);
}
.filter-btn:hover { color: var(--text-primary); }
.filter-btn.active {
  /* Selected filter — locked in pressed state with yellow tint. */
  background: var(--state-selected);
  border-color: var(--brand-border-subtle);
  color: var(--text-primary);
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.10),
    inset 0 -1px 0 rgba(255, 255, 255, 0.40);
}
.filter-btn.active:hover {
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.12),
    inset 0 -1px 0 rgba(255, 255, 255, 0.50);
}

/* =========================================================================
   TABS — horizontal tab bar
   ========================================================================= */
.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: var(--space-3);
}
.tab {
  padding: var(--space-1-5) var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: var(--font-emphasis);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
  text-decoration: none;
}
.tab:hover { color: var(--text-primary); text-decoration: none; }
.tab.active {
  color: var(--text-brand);
  border-bottom-color: var(--brand-500);
}

/* =========================================================================
   TOAST — transient notification (slide-in from right)
   ========================================================================= */
.toast-container {
  position: fixed;
  bottom: var(--space-3);
  right: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  z-index: 100;
  pointer-events: none;
}
.toast {
  background: var(--surface-modal);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: var(--space-1-5) var(--space-2);
  font-size: var(--text-sm);
  box-shadow: var(--shadow-lg);
  color: var(--text-primary);
  animation: slide-in var(--duration-normal) var(--ease-decelerate);
  min-width: 220px;
  pointer-events: auto;
}
.toast.success { border-left: 3px solid var(--color-success); }
.toast.error   { border-left: 3px solid var(--color-error); }
.toast.warning { border-left: 3px solid var(--color-warning); }
.toast.info    { border-left: 3px solid var(--color-info); }
@keyframes slide-in {
  from { opacity: 0; transform: translateX(20px); }
  to   { opacity: 1; transform: translateX(0); }
}

/* =========================================================================
   EMPTY-STATE — composed empty: icon + title + body + optional CTA list
   Used when a section has no data. Centered, generous whitespace.
   ========================================================================= */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--space-5) var(--space-3);
  max-width: 480px;
  margin: 0 auto;
}
.empty-state-icon {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-lg);
  background: var(--state-hover);
  border: 1px solid var(--border-subtle);
  display: grid;
  place-items: center;
  margin-bottom: var(--space-2);
  color: var(--text-tertiary);
}
.empty-state-title {
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  color: var(--text-primary);
  letter-spacing: var(--tracking-snug);
  margin-bottom: var(--space-1);
}
.empty-state-body {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  margin-bottom: var(--space-3);
}
.empty-state-list {
  text-align: left;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.8;
  padding-left: var(--space-3);
  margin: 0;
}
.empty-state-list strong { color: var(--text-primary); font-weight: var(--font-semibold); }

/* =========================================================================
   DRAWER — slide-in panel from the right, used by Fleet account detail.
   Backdrop dims the rest of the page; clicking it dismisses.
   ========================================================================= */
.drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-normal) var(--ease-standard);
  z-index: 90;
}
.drawer-backdrop.open {
  opacity: 1;
  pointer-events: auto;
}
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--drawer-width);
  max-width: 90vw;
  background: var(--surface-modal);
  border-left: 1px solid var(--border-default);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform var(--duration-normal) var(--ease-emphasized);
  z-index: 91;
}
.drawer.open { transform: translateX(0); }
.drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: var(--space-2);
}

/* =========================================================================
   MODAL — centered modal shell (used by Bulk Import, etc.)
   Use the native <dialog> element for backdrop + focus trap + ESC key.
   ========================================================================= */
.modal {
  background: var(--surface-modal);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: 0;
  max-width: 720px;
  width: 92%;
  box-shadow: var(--shadow-xl);
}
.modal[open] {
  opacity: 0;
  transform: translateY(-8px) scale(0.98);
  transition:
    opacity var(--duration-normal) var(--ease-standard),
    transform var(--duration-normal) var(--ease-standard);
}
.modal[open].is-opening {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.modal[open]::backdrop {
  background: rgba(0, 0, 0, 0);
  backdrop-filter: blur(0);
  -webkit-backdrop-filter: blur(0);
  transition:
    background var(--duration-normal) var(--ease-standard),
    backdrop-filter var(--duration-normal) var(--ease-standard),
    -webkit-backdrop-filter var(--duration-normal) var(--ease-standard);
}
.modal[open].is-opening::backdrop {
  background: rgba(0, 0, 0, 0.40);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.modal-header {
  padding: var(--space-2) var(--space-3) var(--space-1-5);
  border-bottom: 1px solid var(--border-subtle);
}
.modal-title {
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  letter-spacing: var(--tracking-snug);
  margin: 0;
}
.modal-body { padding: var(--space-3); }
.modal-footer {
  padding: var(--space-1-5) var(--space-3) var(--space-2);
  border-top: 1px solid var(--border-subtle);
  display: flex;
  justify-content: flex-end;
  gap: var(--space-1);
}

/* =========================================================================
   BOT STATUS HERO — top of Overview, shows running/stopped state with
   live pulse on the dot.
   ========================================================================= */
/* Bot status hero — clean white card with a subtle yellow accent strip
   on the left edge (when running) to anchor the brand without flooding
   the surface with yellow tint. Calmer than a full overlay glow. */
.bot-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
  position: relative;
}
.bot-status > * { position: relative; z-index: 1; }
.bot-status-left { display: flex; align-items: center; gap: var(--space-2); }
.bot-status-actions { display: flex; align-items: center; gap: var(--space-2); }
.bot-status-actions form {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.bot-state-label {
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  color: var(--text-primary);
  letter-spacing: var(--tracking-snug);
}
.bot-state-meta {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  margin-top: 2px;
}
.bot-dot {
  width: 14px;
  height: 14px;
  border-radius: var(--radius-pill);
  background: var(--text-tertiary);
  box-shadow: 0 0 0 4px var(--state-hover);
  flex-shrink: 0;
}
.bot-dot.running {
  background: var(--color-success);
  box-shadow: 0 0 0 4px var(--color-success-bg), 0 0 16px var(--color-success);
  animation: bot-pulse var(--pulse-duration) var(--ease-standard) infinite;
}
.bot-dot.crashed {
  background: var(--color-error);
  box-shadow: 0 0 0 4px var(--color-error-bg);
}
.bot-dot.stopped {
  background: var(--text-tertiary);
}
@keyframes bot-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.15); }
}
.btn-inline-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
}
.btn-inline-toggle input { accent-color: var(--brand-500); }

/* Log panel — collapsible recent log lines */
.bot-log-panel {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.bot-log-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-1-5) var(--space-2);
  background: var(--surface-overlay);
  border-bottom: 1px solid var(--border-subtle);
}
.bot-log-title {
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-widest);
}
.bot-log-content {
  margin: 0;
  padding: var(--space-2);
  max-height: 280px;
  overflow: auto;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
  /* Force text selection in pywebview WebKit */
  user-select: text;
  -webkit-user-select: text;
}

/* =========================================================================
   UTILITIES
   ========================================================================= */
.divider { height: 1px; background: var(--border-subtle); margin: var(--space-3) 0; }

/* Margin utilities — replace inline style="margin: ...". */
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }

/* Vertical rhythm — every direct child gets standard spacing above (except first) */
.stack    > * + * { margin-top: var(--space-3); }
.stack-sm > * + * { margin-top: var(--space-2); }
.stack-lg > * + * { margin-top: var(--space-4); }

/* Flex alignment helpers — pair with .flex-row / .flex-col */
.justify-end     { justify-content: flex-end; }
.justify-between { justify-content: space-between; }
.justify-center  { justify-content: center; }
.items-start     { align-items: flex-start; }
.items-center    { align-items: center; }
.items-end       { align-items: flex-end; }
.text-center     { text-align: center; }

/* Width utilities */
.w-full { width: 100%; }
.max-w-form { max-width: 720px; }       /* settings forms, narrow content */
.max-w-prose { max-width: 65ch; }       /* long-form prose */

/* Skip link — visible on focus only, top-left corner */
.skip-link {
  position: absolute;
  top: -40px;
  left: var(--space-2);
  background: var(--surface-modal);
  color: var(--text-primary);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-default);
  z-index: 1000;
  text-decoration: none;
  transition: top var(--duration-normal) var(--ease-standard);
}
.skip-link:focus { top: var(--space-1); }

/* Status dots — semantic color helpers (paired with inline size styles). */
.dot-success { background: var(--color-success); }
.dot-error { background: var(--color-error); }
.dot-warning { background: var(--color-warning); }
.dot-info { background: var(--color-info); }

/* =========================================================================
   SKELETON — placeholder shimmer while data loads.
   Used by HTMX swap targets that haven't received content yet. The
   shimmer animation reads as "something is loading" without a spinner
   monopolizing attention. Stripe/Sentry use this exact pattern.
   ========================================================================= */
.skeleton {
  background: linear-gradient(
    90deg,
    var(--surface-sunken) 0%,
    var(--surface-overlay) 50%,
    var(--surface-sunken) 100%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s linear infinite;
  border-radius: var(--radius-sm);
  height: 14px;
  width: 100%;
}
.skeleton--lg { height: 28px; }
.skeleton--xl { height: 40px; border-radius: var(--radius-md); }
.skeleton--circle {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
}
@keyframes skeleton-shimmer {
  to { background-position: -200% 0; }
}

/* =========================================================================
   NPROGRESS — top-of-page progress bar shown during HTMX requests.
   Connected via Alpine.js or HTMX events: add .htmx-active to <body>
   when a request is in flight; the bar fills toward 90% and snaps to
   100% on completion.
   ========================================================================= */
#nprogress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--brand-500);
  box-shadow: 0 0 8px var(--brand-tint-strong);
  width: 0;
  transition: width var(--duration-slow) var(--ease-decelerate),
              opacity var(--duration-fast) var(--ease-standard);
  z-index: 200;
  pointer-events: none;
  opacity: 0;
}
body.htmx-active #nprogress {
  width: 90%;
  opacity: 1;
}
body.htmx-done #nprogress {
  width: 100%;
  opacity: 0;
}

/* =========================================================================
   SIDEBAR META — non-interactive informational footer of the sidebar.
   Distinct from .nav-item so screen readers don't announce it as a link.
   ========================================================================= */
.sidebar-meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-1);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  margin-top: auto;
}

/* =========================================================================
   LIVE VIEW — fleet thumbnail grid (Median Web v2 port, light-theme adapted)
   Reference: Median Web `components-v2.css` :1354-1462. Tokens swapped to
   Snap's light palette so tiles read on white instead of dark crust.
   ========================================================================= */
.live-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
}
.live-tile {
  display: flex;
  flex-direction: column;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: border-color var(--duration-fast) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}
.live-tile:hover {
  border-color: var(--border-strong);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
}
/* Outer link wrapping the thumbnail — separate from the meta strip so
   the Pause button below isn't part of the same anchor. */
.live-tile-thumb-link {
  display: block;
  text-decoration: none;
  color: inherit;
}
.live-tile-thumb-link:hover { text-decoration: none; }

.live-tile-thumb {
  position: relative;
  width: 100%;
  /* 16:9 matches common Snap web viewports (1366×768, 1920×1080);
     object-fit:cover crops minimally to fill the tile. */
  aspect-ratio: 16 / 9;
  background: var(--surface-sunken);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.live-tile-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}
.live-tile-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}
/* Age pill — semi-opaque dark on top of (always colorful) screenshot.
   Kept dark in light theme too because screenshot backgrounds vary;
   a white pill would disappear over bright Snap UI. */
.live-tile-age {
  position: absolute;
  bottom: var(--space-1);
  right: var(--space-1);
  background: rgba(26, 26, 24, 0.78);
  color: rgba(250, 250, 248, 0.95);
  padding: 1px var(--space-1);
  border-radius: var(--radius-pill);
  font-size: var(--text-2xs);
  font-weight: var(--font-semibold);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-wide);
}
.live-tile-meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-1-5);
  border-top: 1px solid var(--border-subtle);
}
.live-tile-id {
  flex: 1;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
/* Status modifiers — banned tiles fade until hovered. */
.live-tile.status-banned { opacity: 0.55; }
.live-tile.status-banned:hover { opacity: 1; }
.live-tile.is-paused { border-color: var(--color-warning-border); }
.live-tile.is-login-failed { border-color: var(--color-error-border); }
/* Paused / failed overlay sits centered on the screenshot, dimming it. */
.live-tile-pause-overlay {
  position: absolute;
  inset: 0;
  background: rgba(26, 26, 24, 0.55);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-0-5);
  pointer-events: none;
}

/* Picker toolbar — flex row used in card-header for filters + counter.
   Mirror of Web v2 components-v2.css:767. */
.picker-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: wrap;
}
.picker-toolbar .input { flex: 1; min-width: 180px; height: 32px; }
.picker-toolbar .counter {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
