/* ── Google Fonts ──────────────────────────────────────────────────────────── */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');

/* ── Design tokens — kept in sync with tb3-pipeline-designer/style.css ─────── */
:root {
  --bg:          #0e1118;
  --bg2:         #151922;
  --bg3:         #1c2333;
  --bg4:         #222d3d;

  --border:      #2a3547;
  --border2:     #374761;

  --blue:        #4f8ef7;
  --blue-dim:    #1e3a6e;
  --blue-pale:   rgba(79,142,247,0.10);
  --blue-glow:   rgba(79,142,247,0.20);

  --teal:        #2dd4bf;
  --teal-dim:    #0d4a42;
  --teal-pale:   rgba(45,212,191,0.10);

  --amber:       #f59e0b;
  --amber-dim:   #4a3008;
  --amber-pale:  rgba(245,158,11,0.10);

  --red:         #ef4444;
  --red-dim:     #4a1010;
  --red-pale:    rgba(239,68,68,0.10);

  --green:       #4ade80;
  --green-pale:  rgba(74,222,128,0.10);

  --text:        #e2e8f0;
  --text-dim:    #7d8d9e;
  --text-mute:   #7a8a9a;

  --ui:   'Inter', system-ui, sans-serif;
  --mono: 'JetBrains Mono', 'Fira Code', monospace;

  --r-sm: 4px;
  --r:    6px;
  --r-lg: 10px;
  --r-xl: 14px;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }

html, body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-size: 13px;
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

::-webkit-scrollbar       { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }

/* ── App shell ─────────────────────────────────────────────────────────────── */
#app {
  display: grid;
  grid-template-rows: 52px 1fr;
  min-height: 100vh;
}

.app-header {
  background: var(--bg2);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 20px;
}
.app-logo { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.app-tb-logo {
  height: 22px; width: auto;
  display: block;
}
.app-logo-title {
  font-size: 14px; font-weight: 600; color: var(--text);
}

/* Global health summary in the header — replaces the orphan
   "backend health" subtitle with three small chips that answer
   "is anything broken?" at a glance. Hidden until first fetch
   populates it (avoids a flash of "0 0 0"). */
.health-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 12px;
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
  margin-left: 4px;
}
.hs-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--text-dim);
  font-family: var(--ui);
}
.hs-chip .hs-count {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}
.hs-chip .hs-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  display: inline-block;
}
.hs-pass .hs-dot { background: var(--teal); }
.hs-fail .hs-dot { background: var(--red); }
.hs-idle .hs-dot { background: var(--text-mute); }

.header-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.header-meta {
  font-size: 11px;
  color: var(--text-dim);
  font-family: var(--mono);
  white-space: nowrap;
}
.header-version {
  font-size: 11px;
  color: var(--text-mute);
  font-family: var(--mono);
  padding: 0 4px;
  border-left: 1px solid var(--border);
  white-space: nowrap;
  letter-spacing: 0.02em;
}
.header-version:empty { display: none; }
.header-user { font-size: 11px; color: var(--text-dim); font-family: var(--mono); }

/* ── Buttons ───────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font-family: var(--ui); font-size: 13px; font-weight: 500;
  padding: 8px 16px; border-radius: var(--r);
  border: 1px solid transparent; cursor: pointer;
  transition: all 0.15s; white-space: nowrap; user-select: none;
}
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-outline { background: transparent; color: var(--text); border-color: var(--border2); }
.btn-outline:hover { border-color: var(--text-dim); background: var(--bg3); }
.btn-sm { font-size: 11px; padding: 5px 10px; }

/* ── Dashboard ─────────────────────────────────────────────────────────────── */
.dashboard {
  padding: 24px;
  max-width: 1400px;
  margin: 0 auto;
  width: 100%;
}

.server-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 16px;
}

/* ── Server card ───────────────────────────────────────────────────────────── */
.server-card {
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 0;          /* status band needs to bleed to the edge */
  overflow: hidden;    /* ensure the band's bg gets clipped to rounded corners */
  display: flex;
  flex-direction: column;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.server-card:hover { border-color: var(--border2); background: var(--bg3); }
.server-card:focus-visible {
  outline: none;
  border-color: var(--blue-dim);
  box-shadow: 0 0 0 2px var(--blue-pale);
}

/* ── Status band (top of every card) ──────────────────────────────────
   Full-width colored strip — the dominant visual element. Failing
   servers shout from the dashboard; healthy ones fade into the rhythm.
   Color matches the existing status-dot/pill palette so the system feels
   consistent. */
.card-status-band {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  border-bottom: 1px solid;
}
.card-status-band.pass  { background: rgba(45,212,191,0.18);  color: var(--teal);  border-bottom-color: var(--teal-dim); }
.card-status-band.warn  { background: rgba(245,158,11,0.18);  color: var(--amber); border-bottom-color: var(--amber-dim); }
.card-status-band.fail  { background: rgba(239,68,68,0.18);   color: var(--red);   border-bottom-color: var(--red-dim); }
.card-status-band.error { background: rgba(239,68,68,0.18);   color: var(--red);   border-bottom-color: var(--red-dim); }
.card-status-band.running {
  background: rgba(79,142,247,0.18); color: var(--blue);
  border-bottom-color: var(--blue-dim);
}
.card-status-band.none {
  background: var(--bg3); color: var(--text-mute);
  border-bottom-color: var(--border);
}
.card-status-band-label {
  /* the actual word: PASS / FAIL / NO DATA / etc. */
  text-transform: uppercase;
}
.card-status-band-meta {
  margin-left: auto;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.02em;
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* ── Card body ──────────────────────────────────────────────────────── */
.card-body {
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Identity block: title + URL + version stacked, with chevron+gear in
   the corner. No surrounding box — identity is supporting context, not
   a primary section. */
.card-identity {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}
.card-identity-text {
  flex: 1;
  min-width: 0;
}
.card-identity-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.card-title { font-size: 16px; font-weight: 600; }
.card-url {
  font-family: var(--mono); font-size: 11px; color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Chevron affordance — sits next to the gear in the corner now. */
.card-chevron {
  font-size: 16px;
  color: var(--text-mute);
  line-height: 1;
  user-select: none;
}
.server-card:hover .card-chevron { color: var(--text-dim); }

/* ── Sub-sections (Health checks / Activity) ──────────────────────────
   Each gets a subtle dark background and tiny ALL-CAPS label, so the
   two concerns are visually distinct. The boxed treatment is what
   makes this layout read as a structured dashboard rather than a
   flat list of fields. */
.card-section {
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.card-section-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.card-section-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-mute);
  font-weight: 600;
}
.card-section-meta {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.card-section-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
/* Push the sparkline to the right of the row when chips are also present.
   When chips are empty (no health data), the sparkline still sits on its
   own line and looks fine. */
.card-section-row .spark { margin-left: auto; }

/* Status dot — used both in the band and (smaller) elsewhere */
.status-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-mute); flex-shrink: 0;
}
.status-dot.pass    { background: var(--teal);  box-shadow: 0 0 8px var(--teal-pale); }
.status-dot.warn    { background: var(--amber); }
.status-dot.fail    { background: var(--red);   box-shadow: 0 0 8px var(--red-pale); }
.status-dot.error   { background: var(--red);   box-shadow: 0 0 8px var(--red-pale); }
.status-dot.running { background: var(--blue);  animation: pulse 1.4s ease-in-out infinite; }
.status-dot.none    { background: var(--text-mute); }

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--blue-glow); }
  50%      { box-shadow: 0 0 0 6px transparent; }
}

/* Status pill — saturation bumped per UX redesign item F. The pill
   now uses ~18% bg with saturated text instead of the previous 10%
   muted version. Pops more without being garish. */
.status-pill {
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
  padding: 2px 8px; border-radius: 10px; border: 1px solid;
}
.status-pill.pass    { color: var(--teal);  border-color: var(--teal-dim);  background: rgba(45,212,191,0.18); }
.status-pill.warn    { color: var(--amber); border-color: var(--amber-dim); background: rgba(245,158,11,0.18); }
.status-pill.fail    { color: var(--red);   border-color: var(--red-dim);   background: rgba(239,68,68,0.18); }
.status-pill.error   { color: var(--red);   border-color: var(--red-dim);   background: rgba(239,68,68,0.18); }
.status-pill.running { color: var(--blue);  border-color: var(--blue-dim);  background: rgba(79,142,247,0.18); }
.status-pill.none    { color: var(--text-dim); border-color: var(--border2); }

.card-meta {
  display: flex; gap: 12px; flex-wrap: wrap;
  font-size: 11px; color: var(--text-dim);
  border-top: 1px solid var(--border); padding-top: 10px;
}
.card-meta b { color: var(--text); font-weight: 500; }

.card-warn-banner {
  font-size: 11px; color: var(--amber);
  background: var(--amber-pale); border: 1px solid var(--amber-dim);
  border-radius: var(--r-sm); padding: 6px 10px;
  cursor: pointer; transition: background 0.12s;
}
.card-warn-banner:hover { background: rgba(245,158,11,0.18); }

/* Gear icon — top-right of card */
.card-gear {
  background: transparent; border: none;
  color: var(--text-dim); cursor: pointer;
  width: 24px; height: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); font-size: 14px;
  transition: all 0.12s;
}
.card-gear:hover { color: var(--text); background: var(--bg3); }

/* ── Empty state ───────────────────────────────────────────────────────────── */
.empty {
  text-align: center; padding: 60px 20px;
  color: var(--text-dim); font-size: 13px;
}

/* ── Slide-over panel ──────────────────────────────────────────────────────── */
.so-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: 100;
  opacity: 0; pointer-events: none;
  transition: opacity 0.18s ease;
}
.so-overlay.open { opacity: 1; pointer-events: auto; }

.so-panel {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: min(440px, 100vw);
  background: var(--bg2);
  border-left: 1px solid var(--border);
  box-shadow: -8px 0 24px rgba(0,0,0,0.4);
  z-index: 101;
  display: flex; flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.22s ease;
}
.so-panel.open { transform: translateX(0); }

.so-header {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--bg3);
  flex-shrink: 0;
}
.so-title { font-size: 14px; font-weight: 600; flex: 1; }
.so-sub   { font-size: 11px; color: var(--text-dim); font-family: var(--mono); }
.so-close {
  background: transparent; border: none; color: var(--text-dim);
  cursor: pointer; font-size: 18px; line-height: 1;
  width: 28px; height: 28px; border-radius: var(--r-sm);
}
.so-close:hover { color: var(--text); background: var(--bg4); }

.so-body {
  flex: 1; overflow-y: auto;
  padding: 18px; display: flex; flex-direction: column; gap: 16px;
}

.so-field { display: flex; flex-direction: column; gap: 5px; }
.so-field label {
  font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-dim);
}
.so-field input[type="text"],
.so-field input[type="password"],
.so-field input[type="number"],
.so-field input[type="email"] {
  background: var(--bg3); border: 1px solid var(--border);
  border-radius: var(--r-sm); color: var(--text);
  font-family: var(--ui); font-size: 13px;
  padding: 8px 10px; outline: none;
  transition: border-color 0.15s;
}
.so-field input:focus {
  border-color: var(--blue);
  box-shadow: 0 0 0 2px var(--blue-pale);
}
.so-field-hint {
  font-size: 11px; color: var(--text-mute);
}

.so-checkbox {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; cursor: pointer; user-select: none;
}
.so-checkbox input { width: 14px; height: 14px; cursor: pointer; }

.so-section-divider {
  border: none; border-top: 1px solid var(--border); margin: 4px 0;
}

.so-test-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
  background: var(--bg3); border: 1px solid var(--border);
  border-radius: var(--r); font-size: 12px;
}
.so-test-row.ok    { border-color: var(--teal-dim);  background: var(--teal-pale);  color: var(--teal); }
.so-test-row.fail  { border-color: var(--red-dim);   background: var(--red-pale);   color: var(--red); }
.so-test-row.busy  { color: var(--text-dim); }

.so-footer {
  display: flex; gap: 8px; justify-content: flex-end;
  padding: 14px 18px;
  border-top: 1px solid var(--border);
  background: var(--bg3);
  flex-shrink: 0;
}

.btn-primary {
  background: var(--blue); color: #fff; border-color: var(--blue);
}
.btn-primary:hover { background: #6b9ef8; box-shadow: 0 0 20px var(--blue-glow); }

.btn-danger {
  background: transparent; color: var(--red); border-color: var(--red-dim);
}
.btn-danger:hover { background: var(--red-pale); border-color: var(--red); }

/* ── Slide-over tabs ───────────────────────────────────────────────────────── */
.so-tabs {
  display: flex;
  border-bottom: 1px solid var(--border);
  background: var(--bg2);
  flex-shrink: 0;
}
.so-tab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 500;
  padding: 10px 16px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: color 0.12s, border-color 0.12s;
}
.so-tab:hover { color: var(--text); }
.so-tab.active {
  color: var(--text);
  border-bottom-color: var(--blue);
}
.so-tab-count {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--text-mute);
  padding: 1px 5px;
  border-radius: 8px;
  background: var(--bg3);
}
.so-tab.active .so-tab-count {
  color: var(--blue);
  background: var(--blue-pale);
}

.so-tab-panel {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.so-tab-panel[hidden] { display: none; }

/* ── Capabilities tab ──────────────────────────────────────────────────────── */
.so-cap-intro {
  font-size: 11.5px;
  color: var(--text-dim);
  line-height: 1.55;
  margin: 0;
}
.so-cap-intro b { color: var(--text); font-weight: 500; }

.so-detect-msg {
  font-size: 11px;
  color: var(--text-dim);
}
.so-detect-msg.busy { color: var(--blue); }
.so-detect-msg.ok   { color: var(--teal); }
.so-detect-msg.fail { color: var(--red); }

.so-cap-groups {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.so-cap-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.so-cap-group-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}

.so-cap-item {
  display: grid;
  grid-template-columns: 16px 1fr;
  column-gap: 8px;
  align-items: baseline;
  padding: 5px 0;
  font-size: 12.5px;
  cursor: pointer;
  user-select: none;
  border-radius: var(--r-sm);
  padding-left: 4px;
}
.so-cap-item:hover:not(.disabled) { background: var(--bg3); }
.so-cap-item.child { padding-left: 24px; }
.so-cap-item.disabled {
  cursor: not-allowed;
  opacity: 0.45;
}
.so-cap-item input { cursor: inherit; }
.so-cap-label { color: var(--text); }
.so-cap-hint {
  grid-column: 2;
  font-size: 10.5px;
  color: var(--text-mute);
  font-style: italic;
}


/* ── Card body extras ──────────────────────────────────────────────────────── */

/* Count chips (item H) — one chip per status, only rendered for
   non-zero counts. Replaces the previous mashed monospace summary
   string. */
.count-chips {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.count-chip {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
}
.count-chip.pass {
  color: var(--teal);
  background: rgba(45,212,191,0.10);
  border-color: var(--teal-dim);
}
.count-chip.warn {
  color: var(--amber);
  background: rgba(245,158,11,0.10);
  border-color: var(--amber-dim);
}
.count-chip.fail {
  color: var(--red);
  background: rgba(239,68,68,0.10);
  border-color: var(--red-dim);
}
.count-chip.skip {
  color: var(--text-dim);
  background: var(--bg3);
  border-color: var(--border);
}

/* Version + stage line (item I) on its own row, monospace, dim. */
.card-version {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--text-mute);
  letter-spacing: 0.02em;
  margin-top: 2px;
  /* Long version strings (e.g. "staging-20260423-070309-pipelines · 12 stages")
     can blow out the identity block. Allow break + ellipsis. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.fail-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Item K: 2-line wrap on detail strings. The fail-item itself is now
   icon + a vertical text block (name on top, detail below). The detail
   uses a 2-line clamp via -webkit-line-clamp; longer text is truncated
   with an ellipsis but the title attribute (set in the markup) gives
   the full string on hover. */
.fail-item {
  display: grid;
  grid-template-columns: 14px 1fr;
  column-gap: 8px;
  align-items: baseline;
  font-size: 11px;
  line-height: 1.4;
}
.fail-item.fail .fail-icon, .fail-item.error .fail-icon { color: var(--red); }
.fail-item.warn .fail-icon                              { color: var(--amber); }
.fail-icon  { font-weight: 700; text-align: center; }
.fail-text  { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.fail-name  { color: var(--text); font-weight: 500; }
.fail-detail {
  color: var(--text-mute);
  font-size: 10.5px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-clamp: 2;
  overflow: hidden;
  word-break: break-word;
}
.fail-more {
  font-size: 10.5px;
  color: var(--text-dim);
  padding-left: 22px;
  font-style: italic;
}

/* ── Sparkline ─────────────────────────────────────────────────────────────── */
.spark {
  display: flex;
  gap: 2px;
  align-items: center;
}
.spark-empty {
  /* Reserve the same vertical space as a populated spark line so cards
     with no run history don't visually jump compared to those with. */
  height: 14px;
}
.spark-dot {
  width: 6px; height: 14px;
  border-radius: 2px;
  background: var(--text-mute);
}
.spark-dot.pass  { background: var(--teal); }
.spark-dot.warn  { background: var(--amber); }
.spark-dot.fail  { background: var(--red); }
.spark-dot.error { background: var(--red); opacity: 0.7; }
.spark-dot.skip  { background: var(--border2); }
.spark-dot:hover { transform: scaleY(1.15); }

/* ── Detail modal ──────────────────────────────────────────────────────────── */
.dm-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.55);
  z-index: 200;
  opacity: 0; pointer-events: none;
  transition: opacity 0.18s ease;
}
.dm-overlay.open { opacity: 1; pointer-events: auto; }

.dm-modal {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%) scale(0.97);
  width: min(880px, 95vw);
  max-height: 85vh;
  background: var(--bg2);
  border: 1px solid var(--border2);
  border-radius: var(--r-lg);
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
  z-index: 201;
  display: flex;
  flex-direction: column;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.dm-modal.open {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}

.dm-header {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--bg3);
  border-top-left-radius: var(--r-lg);
  border-top-right-radius: var(--r-lg);
  flex-shrink: 0;
}
.dm-title { font-size: 14px; font-weight: 600; }
.dm-sub   { font-size: 11px; color: var(--text-dim); font-family: var(--mono); }
.dm-close {
  background: transparent; border: none; color: var(--text-dim);
  cursor: pointer; font-size: 18px; line-height: 1;
  width: 28px; height: 28px; border-radius: var(--r-sm);
}
.dm-close:hover { color: var(--text); background: var(--bg4); }

.dm-body {
  flex: 1; overflow-y: auto;
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.dm-section { display: flex; flex-direction: column; gap: 10px; }
.dm-section-label {
  font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-dim);
}

.dm-run-head { display: flex; gap: 6px; flex-wrap: wrap; }
.dm-run-meta { font-size: 11px; color: var(--text-dim); font-family: var(--mono); }
.dm-error-box {
  font-family: var(--mono); font-size: 11px;
  color: var(--red); background: var(--red-pale);
  border: 1px solid var(--red-dim); border-radius: var(--r-sm);
  padding: 8px 10px; white-space: pre-wrap; word-break: break-word;
}

.dm-empty {
  padding: 30px 20px; text-align: center;
  color: var(--text-dim); font-size: 12px;
}
.dm-empty.dm-error { color: var(--red); }

/* Item rows */
.dm-group { display: flex; flex-direction: column; }
.dm-group-label {
  font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-dim);
  padding: 8px 0 6px; border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.dm-item {
  display: grid;
  grid-template-columns: 18px 1fr 110px 60px;
  column-gap: 10px;
  align-items: baseline;
  padding: 6px 0;
  font-size: 12px;
  border-bottom: 1px solid transparent;
}
.dm-item:hover { background: var(--bg3); }
.dm-item-icon  { text-align: center; font-weight: 700; }
.dm-item-name  { color: var(--text); font-weight: 500; }
.dm-item-detail{ grid-column: 2; color: var(--text-dim); font-size: 11px; line-height: 1.5; }
.dm-item-dur   { font-family: var(--mono); font-size: 10.5px; color: var(--text-mute); text-align: right; }
.dm-item.fail .dm-item-icon, .dm-item.error .dm-item-icon { color: var(--red); }
.dm-item.warn .dm-item-icon                                { color: var(--amber); }
.dm-item.pass .dm-item-icon                                { color: var(--teal); }
.dm-item.skip .dm-item-icon                                { color: var(--text-mute); }

/* Per-test history strip — one cell per recent run, oldest-first.
   Empty cells (test wasn't in that run) render as a faint outline. */
.dm-item-strip {
  display: inline-flex;
  gap: 1px;
  align-self: center;
  height: 12px;
}
.dm-strip-cell {
  width: 4px;
  height: 12px;
  border-radius: 1px;
  flex-shrink: 0;
}
.dm-strip-cell.pass  { background: var(--teal); }
.dm-strip-cell.warn  { background: var(--amber); }
.dm-strip-cell.fail,
.dm-strip-cell.error { background: var(--red); }
.dm-strip-cell.skip  { background: var(--border2); opacity: 0.6; }
.dm-strip-cell.none  { background: transparent; outline: 1px solid var(--border); outline-offset: -1px; }

/* Recent runs table */
.dm-runs {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.dm-runs th {
  text-align: left;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
}
.dm-runs td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.dm-runs-row { cursor: pointer; transition: background 0.1s; }
.dm-runs-row:hover { background: var(--bg3); }
.dm-runs-row.active { background: var(--blue-pale); }
.dm-runs-row.active td { color: var(--text); }
.dm-runs-rel { color: var(--text-dim); font-family: var(--mono); }
.dm-runs-summary {
  color: var(--text-dim); font-family: var(--mono);
  max-width: 320px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* ── Modal tabs ─────────────────────────────────────────────────────────────── */
.dm-tabs {
  display: flex;
  border-bottom: 1px solid var(--border);
  background: var(--bg2);
  flex-shrink: 0;
}
.dm-tab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 500;
  padding: 10px 16px;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s;
}
.dm-tab:hover { color: var(--text); }
.dm-tab.active {
  color: var(--text);
  border-bottom-color: var(--blue);
}
.dm-tab-panel { display: flex; flex-direction: column; gap: 18px; }
.dm-tab-panel[hidden] { display: none; }

/* ── Card activity row ──────────────────────────────────────────────────────── */
/* Lives inside .card-section-activity, which already provides a bordered
   bg. No top border or extra padding needed here. */
.card-activity {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  color: var(--text-dim);
  flex-wrap: wrap;
  row-gap: 4px;
}
.card-activity b { color: var(--text); font-weight: 500; }
.card-activity .dim { color: var(--text-mute); }

/* "Active now" pill — promoted from a plain green text label. Matches
   the status-pill family visually (compact, mono, uppercase isn't
   appropriate here so we keep mixed case). The dot pulses once when
   the card mounts and then settles — restraint matters; constantly
   pulsing dots feel anxious and erode trust. */
.act-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--teal);
  background: rgba(45, 212, 191, 0.18);
  border: 1px solid var(--teal-dim);
  padding: 2px 8px;
  border-radius: 10px;
  white-space: nowrap;
}
.act-pill-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--teal);
  display: inline-block;
  /* One-shot pulse on mount: 3 cycles of opacity, then stays solid.
     Restrained — no infinite animation. */
  animation: act-pulse 1.6s ease-in-out 3;
}
@keyframes act-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(45, 212, 191, 0.6); }
  50%      { opacity: 0.55; box-shadow: 0 0 0 4px rgba(45, 212, 191, 0); }
}

/* Idle variant — rendered when sessions_last_hour == 0 so cards stay
   visually aligned. Quiet by design: muted gray, no pulse, slightly
   smaller dot. Acts as a layout placeholder, not as an active signal —
   the teal pill should still pop when there's something to see. */
.act-pill.act-pill-idle {
  color: var(--text-mute);
  background: var(--bg3);
  border-color: var(--border);
}
.act-pill.act-pill-idle .act-pill-dot {
  background: var(--text-mute);
  width: 5px; height: 5px;
  animation: none;       /* no pulse on the idle state */
  box-shadow: none;
}

.act-tail {
  margin-left: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Recency-coloured timestamp. The whole "5m ago" text takes a different
   color depending on age, so the eye can sort cards by freshness without
   reading each line. Five buckets, mapped in JS via recencyBucket().
   Tooltip carries the absolute ISO timestamp for precise inspection. */
.act-ts {
  font-family: var(--mono);
  letter-spacing: 0.02em;
  font-weight: 500;
  cursor: help;
}
.act-ts.live   { color: var(--teal);     font-weight: 600; }
.act-ts.recent { color: #6dccc1;        }   /* teal, dimmed */
.act-ts.today  { color: var(--text);    }
.act-ts.week   { color: var(--text-dim); font-weight: 400; }
.act-ts.stale  { color: var(--text-mute); font-weight: 400; opacity: 0.75; }

/* ── Slide-over: side-by-side fields ────────────────────────────────────────── */
.so-field-row {
  display: flex;
  gap: 10px;
}
.so-field-row .so-field { min-width: 0; }

/* ── Top nav ────────────────────────────────────────────────────────────────── */
.app-nav {
  display: flex;
  gap: 4px;
  margin-left: 24px;
}
.nav-link {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
  padding: 6px 12px;
  border-radius: var(--r-sm);
  transition: color 0.12s, background 0.12s;
}
.nav-link:hover { color: var(--text); background: var(--bg3); }
.nav-link.active {
  color: var(--text);
  background: var(--bg3);
}

/* ── Pause button warning state ─────────────────────────────────────────────── */
.btn-warning {
  border-color: var(--amber-dim);
  color: var(--amber);
  background: var(--amber-pale);
}

/* ── Users page ─────────────────────────────────────────────────────────────── */
.users-page {
  padding: 0 8px;
  max-width: 1400px;
  margin: 0 auto;
}
.users-toolbar {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}
.users-title {
  font-size: 18px;
  font-weight: 600;
  margin: 0;
  color: var(--text);
}
.users-since {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-dim);
}
.users-since select {
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--ui);
  font-size: 12px;
  padding: 4px 8px;
}
.users-filter {
  margin-left: auto;
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--ui);
  font-size: 12px;
  padding: 6px 10px;
  width: 240px;
}
.users-filter:focus { outline: none; border-color: var(--blue); }
.back-link {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 12px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}
.back-link:hover { color: var(--text); }

.users-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}
.users-table th {
  text-align: left;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.users-table th.num,
.users-table td.num   { text-align: right; font-family: var(--mono); }
.users-table th.time,
.users-table td.time  { color: var(--text-dim); font-family: var(--mono); white-space: nowrap; }
.users-table td {
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
}
.users-table tbody tr:hover { background: var(--bg3); }
/* In-app text links (user names in lists, session IDs in tables, etc.).
   Designed for legibility against the dark surfaces — a brighter, more
   saturated blue with a subtle underline-on-hover. The previous color
   (--blue, #4f8ef7) was too desaturated against the dark backgrounds. */
.user-link,
.session-link {
  color: #7cb7ff;
  text-decoration: none;
  border-bottom: 1px dashed transparent;
  transition: color 0.12s, border-color 0.12s;
}
.user-link:hover,
.session-link:hover {
  color: #a8d0ff;
  border-bottom-color: currentColor;
}
.user-link:visited,
.session-link:visited {
  color: #9aa9c8;
}
.user-link:visited:hover,
.session-link:visited:hover {
  color: #c2d0e8;
}

/* Inside the recent-runs / sessions tables, session-id cells are mono.
   Keep that, but lift the contrast a bit. */
.dm-runs td .session-link {
  font-family: var(--mono);
  font-size: 11px;
}

.users-meta {
  margin-top: 12px;
  font-size: 11px;
  color: var(--text-mute);
}

.users-section {
  margin-bottom: 28px;
}
.users-section-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin-bottom: 10px;
}

/* Empty / error states inside users page */
.users-page .empty {
  padding: 24px;
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
  border: 1px dashed var(--border);
  border-radius: var(--r-md);
}
.users-page .empty.error { color: var(--red); border-color: var(--red-dim); }

/* Hide views by default — router controls visibility */
.view[hidden] { display: none; }

/* ── Test alert row (slide-over) ────────────────────────────────────────────── */
.so-test-alert-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.so-test-alert-msg {
  font-size: 11px;
  color: var(--text-dim);
}
.so-test-alert-msg.busy { color: var(--blue); }
.so-test-alert-msg.ok   { color: var(--teal); }
.so-test-alert-msg.fail { color: var(--red); }

/* ── Session deep-dive page ─────────────────────────────────────────────────── */
.session-page {
  padding: 0 8px;
  max-width: 1100px;
  margin: 0 auto;
}
.session-toolbar {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}
.session-title {
  font-size: 18px;
  font-weight: 600;
  margin: 0;
  color: var(--text);
  flex: 1;
  min-width: 0;
}
.session-server {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-mute);
  margin-left: 8px;
}

.session-section { margin-bottom: 28px; }
.session-section-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin-bottom: 10px;
}

/* Error banner — prominent for ERROR sessions */
.error-banner {
  background: var(--red-pale, rgba(244, 84, 89, 0.08));
  border: 1px solid var(--red-dim, rgba(244, 84, 89, 0.35));
  border-left: 4px solid var(--red);
  border-radius: var(--r-md);
  padding: 14px 18px;
  margin-bottom: 24px;
}
.error-banner-head {
  font-size: 13px;
  font-weight: 600;
  color: var(--red);
  margin-bottom: 6px;
}
.error-banner-reason {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--text);
  margin-bottom: 10px;
  white-space: pre-wrap;
  word-break: break-word;
}
.copy-btn { font-size: 11px; }

/* Metadata key-value grid */
.kv-grid {
  display: grid;
  grid-template-columns: 200px 1fr;
  row-gap: 6px;
  column-gap: 16px;
  margin: 0;
  font-size: 12.5px;
}
.kv-grid dt {
  color: var(--text-dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.kv-grid dd {
  color: var(--text);
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  word-break: break-word;
}

/* Transcript */
.transcript {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg2);
  max-height: 400px;
  overflow-y: auto;
}
.transcript-row {
  display: grid;
  grid-template-columns: 90px auto 1fr;
  gap: 12px;
  padding: 8px 14px;
  font-size: 12.5px;
  border-bottom: 1px solid var(--border);
}
.transcript-row:last-child { border-bottom: none; }
.transcript-time {
  color: var(--text-mute);
  font-family: var(--mono);
  font-size: 11px;
  white-space: nowrap;
}
.transcript-speaker {
  color: var(--blue);
  font-weight: 500;
  font-size: 11.5px;
}
.transcript-text {
  color: var(--text);
  line-height: 1.5;
}

/* Recording cards */
.recording-card {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 14px;
  margin-bottom: 12px;
  background: var(--bg2);
}
.recording-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  margin-bottom: 10px;
  font-size: 11.5px;
}
.recording-meta-row { display: flex; gap: 6px; }
.recording-meta-key { color: var(--text-dim); text-transform: uppercase; font-size: 10px; letter-spacing: 0.04em; align-self: center; }
.recording-meta-val { color: var(--text); font-family: var(--mono); }
.recording-meta-error { width: 100%; color: var(--red); font-size: 11px; }
.recording-audio {
  width: 100%;
  height: 36px;
  margin-bottom: 8px;
  /* dark-theme audio controls — relies on browser support */
  filter: invert(0.85) hue-rotate(180deg);
}

/* Raw JSON */
.raw-toggle {
  background: transparent;
  border: none;
  color: var(--text-dim);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: pointer;
  padding: 0;
}
.raw-toggle:hover { color: var(--text); }
.raw-json {
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 14px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text);
  overflow-x: auto;
  max-height: 600px;
  overflow-y: auto;
  margin-top: 10px;
}

/* Upstream correlation id beneath a transcript-fetch error */
.empty-cid {
  font-size: 11px;
  color: var(--text-mute);
  margin-top: 6px;
  text-align: center;
}
.empty-cid code {
  font-family: var(--mono);
  background: var(--bg2);
  padding: 1px 4px;
  border-radius: var(--r-sm);
  font-size: 10.5px;
}

/* Non-final / partial ASR segments — visually muted to distinguish from
   committed final results. Useful for sessions that ended mid-stream. */
.transcript-row.partial {
  opacity: 0.62;
  font-style: italic;
}
.transcript-partial-badge {
  font-size: 9.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--amber);
  border: 1px solid var(--amber-dim, rgba(255, 184, 77, 0.35));
  border-radius: var(--r-sm);
  padding: 1px 5px;
  margin-left: 8px;
  align-self: center;
  flex-shrink: 0;
}

/* ── Pipeline stage cards ───────────────────────────────────────────────────── */
.pipeline-stages {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.pipeline-card {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 12px 14px;
  background: var(--bg2);
}
.pipeline-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.pipeline-task {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px 8px;
  border-radius: var(--r-sm);
  border: 1px solid currentColor;
}
.pipeline-task-asr { color: var(--teal);  background: var(--teal-pale); }
.pipeline-task-nlp { color: var(--amber); background: var(--amber-pale); }
.pipeline-task-tts { color: #7cb7ff;       background: rgba(124,183,255,0.10); }
/* Fallback for any task type we haven't styled explicitly */
.pipeline-task     { color: var(--text-dim); }

.pipeline-type {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-mute);
}
.pipeline-stage-num {
  margin-left: auto;
  font-size: 10px;
  color: var(--text-mute);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.pipeline-tag {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text);
  word-break: break-all;
  margin-bottom: 4px;
}
.pipeline-tag-breadcrumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-bottom: 10px;
  font-size: 10.5px;
  color: var(--text-mute);
}
.pipeline-tag-part {
  font-family: var(--mono);
  background: var(--bg3, var(--bg));
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 1px 6px;
  cursor: help;
}
.pipeline-tag-sep {
  color: var(--text-mute);
  opacity: 0.6;
}

/* Parameters grid — two columns of key:value rows. Active params get the
   teal tint; inactive (off / null / empty) render muted to fade into the
   background, so the eye lands on what's actually doing something. */
.pipeline-params {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 4px 16px;
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
}
.pipeline-param {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 0;
  color: var(--text-mute);
  font-family: var(--mono);
}
.pipeline-param.active {
  color: var(--text);
}
.pipeline-param.active .pipeline-param-val {
  color: var(--teal);
  font-weight: 600;
}
.pipeline-param-key {
  color: var(--text-dim);
  font-weight: 500;
}
.pipeline-param-val {
  text-align: right;
  word-break: break-word;
}

/* Section-label suffix note (e.g. "same config across all 3 recordings") */
.session-section-note {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  font-size: 11px;
  color: var(--text-mute);
  margin-left: 6px;
}

/* When pipelines differ across recordings, group each under its rec id */
.pipeline-group {
  margin-bottom: 14px;
}
.pipeline-group:last-child { margin-bottom: 0; }
.pipeline-group-head {
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 8px;
  font-family: var(--mono);
}
.pipeline-group-head code {
  background: var(--bg2);
  padding: 1px 5px;
  border-radius: var(--r-sm);
  font-size: 10.5px;
  color: var(--text);
}

/* Recording card highlight when processedMs < recordedMs */
.recording-card.has-processing-gap {
  border-left: 3px solid var(--amber);
}

/* Header user menu — username + sign out button */
.header-user-menu {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: 12px;
  padding-left: 12px;
  border-left: 1px solid var(--border);
}
.header-username {
  font-size: 11px;
  color: var(--text-mute);
  font-family: var(--mono);
}

/* ── Session search filter form (Activity tab) ───────────────────────────── */
.dm-search-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
  padding: 10px 12px;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.dm-search-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-end;
}
.dm-search-row-pills {
  border-top: 1px dashed var(--border);
  padding-top: 8px;
}
.dm-search-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1 1 140px;
  min-width: 100px;
}
.dm-search-field span {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-mute);
  font-weight: 500;
}
.dm-search-field input,
.dm-search-field select {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 6px 8px;
  color: var(--text);
  font-size: 12px;
  font-family: inherit;
}
.dm-search-field input:focus,
.dm-search-field select:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 2px var(--blue-glow);
}

.dm-search-pillgroup {
  display: flex;
  align-items: center;
  gap: 4px;
}
.dm-search-pillgroup-label {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-mute);
  margin-right: 4px;
}

.filter-pill {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 3px 9px;
  font-size: 10.5px;
  font-weight: 500;
  font-family: var(--mono);
  letter-spacing: 0.04em;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
}
.filter-pill:hover {
  border-color: var(--text-mute);
  color: var(--text);
}
.filter-pill.on {
  background: var(--teal-pale);
  border-color: var(--teal);
  color: var(--teal);
}

.dm-search-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 12px;
}
.btn-link {
  background: none;
  border: none;
  color: #7cb7ff;
  font-size: 11px;
  cursor: pointer;
  padding: 0;
  font-family: inherit;
}
.btn-link:hover { text-decoration: underline; }

.dm-search-meta {
  font-size: 10.5px;
  color: var(--text-mute);
  margin-bottom: 6px;
  font-family: var(--mono);
}
.dm-search-meta .dm-warn {
  color: var(--amber);
}
.dm-search-count {
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 4px;
}

/* ── Responsive ────────────────────────────────────────────────────────────
   The header packs a lot of elements into a single row. On narrow screens
   we hide the lower-priority ones rather than wrap (wrapping looks worse
   than just dropping things).

   Priority decisions (high → low):
   1. Always visible: logo, nav, Run-all, Sign-out, health summary
   2. Hide < 1100px: Pause button (rare action), "last sweep" chip
   3. Hide < 900px:  username text, version chip
   4. < 720px is treated as a minimum useful width — at that point the
      cards switch to single column too. */

@media (max-width: 1100px) {
  #pauseBtn,
  .header-meta {
    display: none;
  }
  /* Tighten health summary at this width too — labels were eating
     about 100px otherwise. */
  .hs-label { display: none; }
  .health-summary { gap: 8px; padding: 4px 8px; }
}

@media (max-width: 900px) {
  .header-username,
  .header-version {
    display: none;
  }
}

@media (max-width: 720px) {
  /* At this width even a tightened header would push out. Reflow nav
     under the logo: top row = logo + actions, bottom row = nav + summary. */
  .app-header {
    flex-wrap: wrap;
    padding: 6px 12px;
    height: auto;
  }
  .health-summary { order: 5; margin-left: 0; border: none; padding: 4px 0; }
  .app-nav { order: 4; }
  /* Logo and right-side actions stay on the first wrapped row */
}

/* Defensive: keep small action buttons from being squished by the
   flex layout on narrow screens. They're sized for their text and
   shouldn't shrink. */
.btn { flex-shrink: 0; }
.app-logo { flex-shrink: 0; }
.app-nav { flex-shrink: 0; }
.header-user-menu { flex-shrink: 0; }

/* ── User-detail page: chart + filter form ─────────────────────────────── */
.user-chart-host {
  margin-bottom: 12px;
}
.chart-meta {
  font-size: 10.5px;
  color: var(--text-mute);
  font-family: var(--mono);
  margin-bottom: 6px;
}
.chart-note {
  font-size: 10.5px;
  color: var(--amber);
  margin-bottom: 6px;
}
.chart-svg-wrap {
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 8px;
}
.user-chart {
  width: 100%;
  height: auto;
  display: block;
}
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 8px;
  font-size: 10.5px;
  color: var(--text-dim);
}
.chart-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.chart-legend-dot {
  width: 9px; height: 9px;
  border-radius: 2px;
  display: inline-block;
}

/* Segmented buttons (metric / stack / bucket) — sit alongside the
   filter pills in the user-detail filter form. */
.user-chart-controls {
  border-top: 1px dashed var(--border);
  padding-top: 8px;
}
.seg-group {
  display: flex;
  align-items: center;
  gap: 4px;
}
.seg-btn {
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-dim);
  padding: 3px 9px;
  font-size: 10.5px;
  font-family: var(--mono);
  letter-spacing: 0.04em;
  cursor: pointer;
  border-radius: var(--r-sm);
}
.seg-btn:hover:not(:disabled) {
  border-color: var(--text-mute);
  color: var(--text);
}
.seg-btn.on {
  background: var(--blue-pale);
  border-color: var(--blue);
  color: var(--blue);
}
.seg-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Inline "(tokens only)" hint inside a disabled segmented button.
   Smaller, italic, and skips the monospace family so it reads as
   guidance rather than another option in the segmented control. */
.seg-btn-hint {
  font-family: var(--ui);
  font-style: italic;
  font-size: 9.5px;
  letter-spacing: 0;
  margin-left: 3px;
  opacity: 0.85;
}

/* ── Collapsible sections on the user-detail page ──────────────────────── */
.users-section-toggle {
  /* The whole label row is the click target. Hover/focus states make
     the affordance visible without needing a separate button. */
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  padding: 4px 0;
  margin: 0;  /* reset — section-label had its own spacing */
}
.users-section-toggle:hover .users-section-title { color: var(--text); }
.users-section-toggle:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--blue-pale);
  border-radius: var(--r-sm);
}
.users-section-chevron {
  display: inline-block;
  font-size: 11px;
  color: var(--text-mute);
  width: 12px;
  text-align: center;
  transition: transform 0.12s ease;
}
.users-section.collapsed .users-section-chevron {
  transform: rotate(-90deg);
}
.users-section-title {
  /* Reuse the existing users-section-label aesthetic — caps, dim text — but
     scoped to the title span now that the label container is the toggle. */
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  font-weight: 600;
}
.users-section-summary {
  font-size: 10.5px;
  font-family: var(--mono);
  color: var(--text-mute);
  letter-spacing: 0.02em;
  margin-left: auto;  /* push to right edge of the header row */
}
.users-section.collapsed .users-section-body {
  display: none;
}
.users-section-body {
  margin-top: 8px;
}

/* ── Config panel: backfill block ──────────────────────────────────────
   Sits inside the General tab under the Activity Retention field. Shows
   the current/last backfill job's status and offers trigger/cancel
   buttons. The block stays hidden until the panel polls for status — no
   point flashing an empty container before the data loads. */
.so-backfill {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  margin-top: -4px;        /* tighten gap to the retention field above */
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r);
}
.so-backfill-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.so-backfill-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-mute);
  font-weight: 600;
}
.so-backfill-status {
  /* Holds the inline-styled status pill (rendered from JS) */
  display: inline-flex;
}
.so-backfill-progress {
  font-size: 11px;
  color: var(--text-dim);
  font-family: var(--mono);
  letter-spacing: 0.02em;
  min-height: 14px;        /* prevent layout jump when data loads */
}
.so-backfill-window {
  font-size: 10px;
  color: var(--text-mute);
  margin-top: 2px;
}
.so-backfill-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Retry link — visible only for failed/cancelled jobs as contextual
   recovery. Styled as a text link rather than a button so it carries
   less visual weight than the cancel button (which is shown during
   a real active operation). The arrow hints "do it again." */
.so-backfill-retry {
  font-size: 11px;
  color: var(--blue);
  text-decoration: none;
  letter-spacing: 0.02em;
  margin-left: auto;     /* push to the right edge of the actions row */
  cursor: pointer;
  transition: color 0.12s;
}
.so-backfill-retry:hover {
  color: var(--text);
  text-decoration: underline;
}

/* ── Confirm/alert dialog (replaces window.confirm and window.alert) ───
   Visually mirrors the detail modal (dm-*) but smaller, fixed at ~440px,
   with a tighter title/body/footer rhythm. Used by:
     • showConfirmDialog()  — two-button confirm
     • showAlertDialog()    — single-button info/error
   See static/dialog.js. */
.cd-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.55);
  z-index: 250;       /* above detail modal (200) and config panel (?) */
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.cd-overlay.open { opacity: 1; pointer-events: auto; }

.cd-modal {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  width: min(440px, 92vw);
  background: var(--bg2);
  border: 1px solid var(--border2);
  border-radius: var(--r-lg);
  box-shadow: 0 20px 60px rgba(0,0,0,0.6);
  z-index: 251;
  display: flex;
  flex-direction: column;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.cd-modal.open {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}

.cd-header {
  padding: 14px 18px 6px;
}
.cd-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
}
.cd-body {
  padding: 6px 18px 16px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-dim);
  /* let body wrap longer messages without blowing the modal up */
  max-height: 60vh;
  overflow-y: auto;
}
.cd-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px;
  border-top: 1px solid var(--border);
}

/* ── "Show more" pagination footer (user-detail + activity search) ─────
   Sits below the table when there are more rows to load. The button is
   the action; the hint shows how many more there are; the error span
   only fills in if a fetch fails. */
.users-show-more {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-top: 1px dashed var(--border);
  margin-top: 4px;
}
.users-show-more-hint {
  font-size: 11px;
  color: var(--text-mute);
  font-family: var(--mono);
  letter-spacing: 0.02em;
}
.users-show-more-error {
  font-size: 11px;
  color: var(--red);
  margin-left: auto;
}

/* ── Error banner: structured logs from /sessions/{id}/logs?type=ERROR ──
   Shown inside .error-banner (red, fatal) for ERROR sessions, and inside
   .error-banner.non-fatal (amber, advisory) for completed sessions that
   nonetheless logged stage errors. The table is identical in both
   contexts; only the surrounding banner color differs. */
.error-banner.non-fatal {
  background: var(--amber-pale, rgba(245, 158, 11, 0.06));
  border-color: var(--amber-dim, rgba(245, 158, 11, 0.30));
  border-left-color: var(--amber, #f59e0b);
}
.error-banner.non-fatal .error-banner-head {
  color: var(--amber, #f59e0b);
  font-weight: 600;
}
.error-banner-sub {
  font-weight: 400;
  color: var(--text-mute);
  font-size: 11px;
  margin-left: 6px;
}

.error-log-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 12px;
  font-size: 12px;
}
.error-log-table thead th {
  text-align: left;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-mute);
  font-weight: 600;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
}
.error-log-table tbody td {
  padding: 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.error-log-table tbody tr:last-child td {
  border-bottom: none;
}
.error-log-time {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
}
.error-log-msg {
  color: var(--text);
  font-family: var(--mono);
  font-size: 12px;
  word-break: break-word;
}
.error-log-offset {
  display: inline-block;
  margin-left: 8px;
  font-size: 10px;
  color: var(--text-mute);
  font-family: var(--mono);
}

/* Stage badges. Color-coded so the eye scans by stage. Unknown stages
   fall back to .stage-badge's neutral default. */
.stage-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.05em;
  background: var(--bg3);
  color: var(--text-dim);
  border: 1px solid var(--border);
  text-transform: uppercase;
}
.stage-badge.stage-asr {
  background: rgba(79, 142, 247, 0.15);
  color: var(--blue);
  border-color: var(--blue-dim, rgba(79, 142, 247, 0.35));
}
.stage-badge.stage-tts {
  background: rgba(45, 212, 191, 0.15);
  color: var(--teal);
  border-color: var(--teal-dim, rgba(45, 212, 191, 0.35));
}
.stage-badge.stage-mt {
  background: rgba(168, 85, 247, 0.15);
  color: #c084fc;
  border-color: rgba(168, 85, 247, 0.35);
}
.stage-badge.stage-nlu,
.stage-badge.stage-llm {
  background: rgba(245, 158, 11, 0.15);
  color: var(--amber, #f59e0b);
  border-color: rgba(245, 158, 11, 0.35);
}

.error-log-fetch-failed {
  margin-top: 10px;
  font-size: 11px;
  color: var(--text-mute);
  font-style: italic;
}

/* ── Auto-tests tab field helpers ──────────────────────────────────────
   The interval-with-suffix pattern (number input + " minutes" label
   inline) is specific to this tab. The checkbox-row pattern works
   anywhere but we only use it here for now. */
.so-inline-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.so-field-suffix {
  font-size: 12px;
  color: var(--text-dim);
}
.so-checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
}
.so-checkbox-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
  accent-color: var(--blue);
}
.so-checkbox-row span {
  font-size: 13px;
  color: var(--text);
}
.so-test-actions {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  display: flex;
  gap: 8px;
}