/* ======================================================
   Arbiter Admin Dashboard — theme

   Both light and dark modes use the same design
   language with shared accent colors (teal, purple, gold).
   Mode-specific palettes defined via --arb-* variables.
   ====================================================== */

/* ---- Shared accent palette ---- */
:root {
  --arb-teal: #00d4ff;
  --arb-teal-dim: #0891b2;
  --arb-purple: #7c3aed;
  --arb-purple-dim: #4c1d95;
  --arb-gold: #d97706;
  --arb-gold-dim: #92400e;

  /* Semantic accents. These were written literally in a dozen rules while two of them
     also existed as theme-scoped tokens, so the palette named a colour it did not
     govern. Both themes share these; the dark theme reaches for the brighter pair. */
  --arb-ok: #16a34a;
  --arb-ok-bright: #34d399;
  --arb-danger: #dc2626;
  --arb-danger-deep: #991b1b;
}

/* ---- Light mode palette ---- */
[data-bs-theme="light"] {
  --bs-body-bg: #f0f2f7;
  --bs-body-color: #1e293b;
  --bs-border-color: #c7cfe0;

  --arb-bg: #f0f2f7;
  --arb-surface: #ffffff;
  --arb-surface-hover: #f1f5f9;
  --arb-nav-active: rgba(14, 116, 144, 0.17);
  --arb-border: #c7cfe0;
  --arb-border-accent: #a78bfa;
  --arb-text: #1e293b;
  --arb-text-muted: #556575;
  --arb-glow: 0 1px 4px rgba(124, 58, 237, 0.08);

  /* Slightly darker accents for legibility on light bg */
  --arb-teal-on-bg: #0e7490;
  --arb-teal-dim: #0b6480;
  --arb-purple-on-bg: #6d28d9;
  --arb-gold-on-bg: #9a4a08;

  --arb-pause-bg: #fde68a;
  --arb-pause-fg: #78350f;
  --arb-pause-border: #f59e0b;

  --arb-danger-bg: #fecaca;
  --arb-danger-fg: #7f1d1d;
  --arb-danger-border: #dc2626;
  --arb-danger-on-bg: #b91c1c;

  --arb-gate-rate-fg: #b45309;
  --arb-gate-rate-border: #fbbf24;
  --arb-gate-conc-fg: #6d28d9;
  --arb-gate-conc-border: #a78bfa;

  --arb-ok-bg: #d1fae5;
  --arb-ok-fg: #065f46;

  --arb-inert-bg: rgba(100, 116, 139, 0.07);
  --arb-inert-fg: #475569;
  --arb-inert-border: #94a3b8;

  --arb-fill-ok: #4ca57a;
  --arb-fill-warn: #c79a3a;
  --arb-fill-bad: #c25b5b;

  --arb-stripe: rgba(124, 58, 237, 0.04);
  --arb-hover: rgba(0, 212, 255, 0.08);
  --arb-input-bg: #ffffff;
  --arb-input-border: #94a3b8;
  --arb-focus-ring: rgba(124, 58, 237, 0.2);
  --arb-focus-outline: var(--arb-teal-on-bg);
}

/* ---- Dark mode palette ---- */
[data-bs-theme="dark"] {
  --bs-body-bg: #0f1320;
  --bs-body-color: #e2e8f0;
  --bs-secondary-bg: #161d2f;
  --bs-tertiary-bg: #1a2234;
  --bs-border-color: #1e293b;

  --arb-bg: #0f1320;
  --arb-surface: #161d2f;
  --arb-surface-hover: #1e2740;
  --arb-nav-active: rgba(0, 212, 255, 0.14);
  --arb-border: #1e293b;
  --arb-border-accent: var(--arb-purple-dim);
  --arb-text: #e2e8f0;
  --arb-text-muted: #94a3b8;
  --arb-glow: 0 0 8px rgba(0, 212, 255, 0.12);

  --arb-teal-on-bg: var(--arb-teal);
  --arb-purple-on-bg: var(--arb-purple);
  --arb-gold-on-bg: var(--arb-gold);

  --arb-pause-bg: #422006;
  --arb-pause-fg: #fde68a;
  --arb-pause-border: #d97706;

  --arb-danger-bg: #450a0a;
  --arb-danger-fg: #fecaca;
  --arb-danger-border: #b91c1c;
  --arb-danger-on-bg: #f87171;

  --arb-gate-rate-fg: #fbbf24;
  --arb-gate-rate-border: #92400e;
  --arb-gate-conc-fg: #c4b5fd;
  --arb-gate-conc-border: #6d28d9;

  --arb-ok-bg: #04291f;
  --arb-ok-fg: #34d399;

  --arb-inert-bg: rgba(148, 163, 184, 0.07);
  --arb-inert-fg: #94a3b8;
  --arb-inert-border: #334155;

  --arb-fill-ok: #15663f;
  --arb-fill-warn: #8a6d1f;
  --arb-fill-bad: #8f2b2b;

  --arb-stripe: rgba(124, 58, 237, 0.04);
  --arb-hover: rgba(0, 212, 255, 0.06);
  --arb-input-bg: #0f172a;
  --arb-input-border: #334155;
  --arb-focus-ring: rgba(0, 212, 255, 0.15);
  --arb-focus-outline: var(--arb-teal-on-bg);
}

/* ---- Theme-agnostic: functional styles ---- */

/* Controls the browser leaves on the arrow. Bootstrap's reboot covers buttons. */
select,
label.queue-sort-label,
.dropdown-item:not(.disabled),
.form-check-label {
  cursor: pointer;
}

.payload-display {
  font-family: monospace;
  font-size: 0.85rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background-color: var(--arb-surface);
  border: 1px solid var(--arb-border);
  color: var(--arb-teal-on-bg);
}

.filter-builder {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.375rem;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.15rem 0.25rem 0.15rem 0.55rem;
  font-size: 0.8rem;
  line-height: 1.2;
  border: 1px solid var(--arb-border-accent);
  border-radius: 999px;
  background-color: var(--arb-surface);
  color: var(--arb-text);
  white-space: nowrap;
  max-width: 16rem;
}

.filter-chip .filter-chip-label {
  color: var(--arb-text-muted);
}

.filter-chip .filter-chip-value {
  overflow: hidden;
  text-overflow: ellipsis;
}

.filter-chip .filter-chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--arb-text-muted);
  font-size: 0.7rem;
  line-height: 1;
  cursor: pointer;
}

.filter-chip .filter-chip-x:hover {
  background-color: var(--arb-surface-hover);
  color: var(--arb-text);
}

.copy-wrap {
  position: relative;
}

.copy-wrap .copy-btn {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 2;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
  font-size: 0.9rem;
  border: 1px solid var(--arb-border);
  border-radius: 4px;
  background-color: var(--arb-surface);
  color: var(--arb-text-muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.copy-wrap:hover .copy-btn,
.copy-wrap .copy-btn:focus-visible {
  opacity: 1;
}

.copy-wrap .copy-btn:hover {
  color: var(--arb-teal-on-bg);
  border-color: var(--arb-border-accent);
  box-shadow: var(--arb-glow);
}

.copy-btn::before {
  content: "\29C9";
}

.copy-btn.copied {
  opacity: 1;
  color: var(--arb-teal-on-bg);
  border-color: var(--arb-border-accent);
}

.copy-btn.copied::before {
  content: "\2713";
}

.event-log {
  max-height: 500px;
  overflow-y: auto;
}

.inline-error {
  display: inline-block;
  padding: .25rem .5rem;
  font-size: .875rem;
  line-height: 1.5;
}

.format-toggle-cell {
  cursor: pointer;
  border-bottom: 1px dashed var(--arb-text-muted);
  opacity: 0.8;
  padding-bottom: 1px;
}

/* Nothing to reformat, so drop the affordance: the underline reads as a second
   dash beneath the em dash that stands in for the missing value. */
.format-toggle-cell.has-no-value {
  border-bottom-color: transparent;
}

.format-toggle-cell:hover {
  opacity: 1;
  color: var(--arb-teal-on-bg);
  border-bottom-color: var(--arb-teal-dim);
}

/* SSE status indicator: shape + color distinguish each state */
/* One pip vocabulary, shared by the health indicator and the events stream.
   Shape carries the state, so neither reads by colour alone. */
.status-pip {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}

.status-dot {
  width: 11px;
  height: 11px;
  display: inline-block;
  box-sizing: border-box;
  flex-shrink: 0;
  border-radius: 50%;
  background-color: var(--arb-text-muted);
}

/* Filled circle: the stream is live, or the server is healthy. */
.status-pip.connected .status-dot,
.status-pip.ok .status-dot {
  background-color: var(--arb-ok);
}

/* Hollow ring: a stream attempt is in flight. */
.status-pip.connecting .status-dot {
  background-color: transparent;
  border: 2px solid var(--arb-text-muted);
}

/* Pulsing ring: the stream dropped, or the server cannot reach its database. */
.status-pip.disconnected .status-dot,
.status-pip.down .status-dot {
  background-color: transparent;
  border: 2px solid var(--arb-danger);
  animation: pulse-dot 1.5s infinite;
}

/* Square: the server serves no stream. */
.status-pip.polling .status-dot {
  border-radius: 2px;
  background-color: #d97706;
}

/* Bar: switched off here. */
.status-pip.off .status-dot {
  height: 3px;
  border-radius: 1px;
  background-color: var(--arb-text-muted);
}

@keyframes pulse-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* Theme toggle button */
.nav-theme-toggle {
  font-size: 1.1rem;
  line-height: 1;
  padding: 0.25rem 0.4rem;
  border: none;
  background: transparent;
  color: var(--arb-text-muted);
  opacity: 0.7;
  transition: opacity 0.15s;
}

.nav-theme-toggle:hover {
  opacity: 1;
}

/* ======================================================
   Component styles — shared structure, mode via variables
   ====================================================== */

/* ---- Navbar ---- */
[data-bs-theme="light"] .navbar {
  background: linear-gradient(135deg, #eef0f8 0%, #f0f2f7 50%, #e8ecf5 100%);
  border-bottom: 1px solid var(--arb-border-accent);
  box-shadow: 0 1px 6px rgba(124, 58, 237, 0.06);
}

[data-bs-theme="dark"] .navbar {
  background: linear-gradient(135deg, #12101f 0%, #0f1320 50%, #0d1117 100%);
  border-bottom: 1px solid var(--arb-border-accent);
  box-shadow: 0 1px 12px rgba(124, 58, 237, 0.12);
}

.navbar-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--arb-teal-on-bg) !important;
  font-weight: 700;
  font-family: 'Electrolize', sans-serif;
  letter-spacing: 0.08em;
  margin-right: 1.5rem;
}

.brand-mark {
  flex: none;
  width: 2.4rem;
  height: 2.4rem;
}

[data-bs-theme="dark"] .brand-mark {
  filter: drop-shadow(0 0 6px rgba(0, 212, 255, 0.25));
}

/* ---- Nav tabs ---- */
.nav-tabs {
  border-bottom-color: var(--arb-border-accent) !important;
}

.nav-tabs .nav-link {
  color: var(--arb-text-muted) !important;
}

.nav-tabs .nav-link:hover {
  color: var(--arb-teal-on-bg) !important;
  border-color: transparent transparent var(--arb-teal-dim) !important;
}

.nav-tabs .nav-link.active {
  color: var(--arb-teal-on-bg) !important;
  background-color: var(--arb-surface) !important;
  border-color: var(--arb-border-accent) var(--arb-border-accent) var(--arb-surface) !important;
}

[data-bs-theme="dark"] .nav-tabs .nav-link.active {
  text-shadow: 0 0 6px rgba(0, 212, 255, 0.2);
}

/* ---- Cards ---- */
.card {
  background-color: var(--arb-surface) !important;
  border: 1px solid var(--arb-border-accent) !important;
  box-shadow: var(--arb-glow);
}

/* Names a card or a section. One style, whichever role it plays. */
.card-title,
.stat-section-label {
  color: var(--arb-text-muted) !important;
  text-transform: uppercase;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
}

.card .display-6 {
  color: var(--arb-teal-on-bg);
}

[data-bs-theme="dark"] .card .display-6 {
  text-shadow: 0 0 8px rgba(0, 212, 255, 0.2);
}

/* Section heading over a group of stat cards. */
/* A section label stands off from the rows above it. */
.stat-section-label {
  margin: 1.5rem 0 0.75rem;
}

/* Stat cards link to the filtered job list. No underline: the whole card lifts
   and glows on hover, and presses in on click. */
.stat-card {
  position: relative;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

.stat-card:hover {
  transform: translateY(-2px);
  border-color: var(--arb-teal-dim) !important;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18), 0 0 14px rgba(0, 212, 255, 0.14) !important;
}

.stat-card:active {
  transform: translateY(0);
}

.stat-card:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 2px;
}

.stat-card.is-danger {
  border-color: var(--arb-danger-border) !important;
}

/* A secondary count under a stat, in the same voice as the queue card's blocked figure. */
.stat-card-sub {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.45rem;
  margin: 0;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--arb-purple-on-bg);
  font-variant-numeric: tabular-nums;
}

/* ---- Tables ---- */
.table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--arb-text);
  --bs-table-border-color: var(--arb-border);
  --bs-table-striped-bg: var(--arb-stripe);
  --bs-table-hover-bg: var(--arb-hover);
}

.table td,
.table th {
  vertical-align: middle;
}

.table thead th {
  color: var(--arb-teal-dim);
  text-transform: uppercase;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  border-bottom: 2px solid var(--arb-border-accent) !important;
  /* Keep the label and its sort caret on one line for every column. */
  white-space: nowrap;
}

/* Jobs and DLQ carry many columns. Give them a floor width so headers never
   compress and overlap; below it the table scrolls (as it already does on
   mobile) rather than squeezing columns narrower than their headers. */
#tab-jobs table {
  min-width: 1150px;
}

#tab-dlq table {
  min-width: 1300px;
}

/* Rate-limit and concurrency policy tables (not the narrow drill-downs) get a
   smaller floor so they scroll rather than crush their columns on mobile. */
#tab-ratelimits table.table-striped,
#tab-concurrency table.table-striped {
  min-width: 900px;
}

/* Cron tables carry timestamps and a two-button action column; give them a
   floor so those columns keep their width and the table scrolls on mobile. */
#tab-cron table,
#tab-cron-overview table {
  min-width: 1100px;
}

#tab-workers-overview table {
  min-width: 1100px;
}

/* Indeterminate top-bar loader for a view's first data load. */
.top-loader {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 2000;
  background:
    linear-gradient(90deg, transparent, var(--arb-teal-on-bg), transparent)
    no-repeat;
  background-size: 40% 100%;
  animation: top-loader-slide 1.1s ease-in-out infinite;
}

@keyframes top-loader-slide {
  0% { background-position: -40% 0; }
  100% { background-position: 140% 0; }
}

/* Spinning refresh glyph while a poll is in flight. */
.spin {
  display: inline-block;
  animation: spin 0.8s linear infinite;
}

/* The Refresh button is disabled mid-poll, which would dim it to Bootstrap's
   disabled opacity and then brighten the instant the spin ends. The spinner
   already signals activity, so keep it full opacity to avoid that flash. */
.btn:disabled:has(.spin) {
  opacity: 1;
}

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

/* Action columns sit flush-right on desktop. On phones they flip left so they
   read at the start of the horizontally scrolling row. */
.cell-actions {
  text-align: right;
}

/* A flex child that truncates rather than pushing its siblings out of the cell. */
.cell-shrink {
  min-width: 0;
}

/* The wrapper shrinks to its glyph, so the rest of the cell still belongs to the row. */
.cell-actions .dropdown {
  display: inline-block;
  vertical-align: middle;
}

@media (max-width: 768px) {
  .cell-actions {
    text-align: left;
  }
}

/* ---- Primary nav (top-level sections) ---- */
.nav-primary {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  /* Clears the edge fade, so scrolling a link into view never parks it under one. */
  scroll-padding-inline: 2.5rem;
}

.nav-primary-link {
  background: transparent;
  border: 0;
  color: var(--arb-text-muted);
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  padding: 0.4rem 0.8rem;
  border-radius: 0.35rem;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s ease, background-color 0.15s ease;
}

.nav-primary-link:hover {
  color: var(--arb-teal-on-bg);
  background: var(--arb-surface-hover);
}

.nav-primary-link.active {
  color: var(--arb-teal-on-bg);
  background: var(--arb-nav-active);
}

[data-bs-theme="dark"] .nav-primary-link.active {
  text-shadow: 0 0 8px rgba(0, 212, 255, 0.25);
}

.nav-primary-link:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 1px;
}

/* ---- Horizontal scrollers: fade the edge that has more to reveal ----
   The mask sits in the element's own box, so it stays pinned while the content
   scrolls under it. Without a fade a clipped nav or table looks complete. */
.scroll-edge-start {
  mask-image: linear-gradient(to right, transparent 0, #000 2.25rem);
}

.scroll-edge-end {
  mask-image: linear-gradient(to right, #000 calc(100% - 2.25rem), transparent 100%);
}

.scroll-edge-start.scroll-edge-end {
  mask-image: linear-gradient(to right, transparent 0, #000 2.25rem, #000 calc(100% - 2.25rem), transparent 100%);
}

/* ---- Breadcrumb (queue detail header) ---- */
.breadcrumb {
  --bs-breadcrumb-divider-color: var(--arb-text-muted);
  margin-bottom: 0;
  font-size: 0.95rem;
}

.breadcrumb-item a {
  color: var(--arb-teal-on-bg);
  text-decoration: none;
}

.breadcrumb-item a:hover {
  text-decoration: underline;
}

.breadcrumb-item.active {
  color: var(--arb-text);
  font-family: 'Electrolize', sans-serif;
  letter-spacing: 0.02em;
}

.breadcrumb-item + .breadcrumb-item::before {
  color: var(--arb-text-muted);
}

/* Breathing room below the last content row (matters most on mobile). */
.container-fluid.mt-3 {
  padding-bottom: 2rem;
}

/* ---- Queue-list landing: a grid of queue cards, the app's hero surface ---- */
.queue-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.35rem;
}

.queue-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1.35rem;
  padding: 1.5rem 1.6rem 1.65rem;
  background-color: var(--arb-surface);
  border: 1px solid var(--arb-border-accent);
  border-radius: 0.6rem;
  box-shadow: var(--arb-glow);
  color: var(--arb-text);
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.queue-card:hover {
  border-color: var(--arb-teal-dim);
  box-shadow: 0 0 14px rgba(0, 212, 255, 0.12);
}

.queue-card.is-throttled {
  border-color: var(--arb-gold-dim);
}

.queue-card.is-paused {
  border-color: var(--arb-pause-border);
}

.queue-card.is-failing {
  border-color: var(--arb-danger-border);
}

/* Always occupies its line, so hero counts share a baseline across the grid. */
.queue-card-flags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  min-height: 1.35rem;
  margin-top: -0.75rem;
  margin-bottom: -0.35rem;
}

.queue-card-pause {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.15rem 0.5rem;
  border-radius: 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.queue-card-dlq {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.5rem;
  border-radius: 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--arb-danger-fg);
  background-color: var(--arb-danger-bg);
}

.queue-card-dlq:hover {
  color: var(--arb-danger-fg);
  filter: brightness(1.15);
}

/* Pause-bars icon (two vertical strokes). */
.queue-card-pause::before {
  content: "";
  width: 0.4rem;
  height: 0.56rem;
  border-left: 2px solid currentColor;
  border-right: 2px solid currentColor;
}

.queue-card-pause.is-some {
  color: #6b7280;
  background-color: rgba(148, 163, 184, 0.14);
}

[data-bs-theme="dark"] .queue-card-pause.is-some {
  color: #cbd5e1;
}

.queue-card-pause.is-queue,
.queue-card-pause.is-workers {
  color: var(--arb-pause-fg);
  background-color: var(--arb-pause-bg);
}

/* Header links to the queue overview. Each count below links to its filtered
   Jobs view. All are real anchors, so cmd/middle-click opens a new tab. */
.queue-card-head,
.queue-card-hero,
.queue-card-stat {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}

/* The header link stretches over the whole card as a fallback target (the queue
   overview). The counts sit above it and capture their own clicks. */
.queue-card-head::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.queue-card-hero,
.queue-card-stat {
  position: relative;
  z-index: 2;
}

.queue-card-head:focus-visible,
.queue-card-hero:focus-visible,
.queue-card-stat:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 3px;
  border-radius: 3px;
}

.queue-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.queue-card-name {
  font-family: 'Electrolize', sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  color: var(--arb-teal-on-bg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

[data-bs-theme="dark"] .queue-card-name {
  text-shadow: 0 0 8px rgba(0, 212, 255, 0.2);
}

.queue-card-arrow {
  flex-shrink: 0;
  font-size: 1.1rem;
  color: var(--arb-text-muted);
  transition: transform 0.15s ease, color 0.15s ease;
}

.queue-card-head:hover .queue-card-arrow {
  color: var(--arb-teal-on-bg);
  transform: translateX(3px);
}

.queue-card-body {
  display: flex;
  flex-direction: column;
  gap: 1.2rem;
}

.queue-card-hero {
  display: inline-flex;
  align-self: flex-start;
  align-items: baseline;
  gap: 0.5rem;
}

.queue-card-ready {
  font-size: 2.1rem;
  line-height: 1;
  font-weight: 600;
  color: var(--arb-teal-on-bg);
  font-variant-numeric: tabular-nums;
  transition: filter 0.15s ease, text-shadow 0.15s ease;
}

[data-bs-theme="dark"] .queue-card-ready {
  text-shadow: 0 0 12px rgba(0, 212, 255, 0.2);
}

/* The ready number is already teal, so hover brightens it and lights up the
   otherwise-muted label rather than relying on a color swap. */
.queue-card-hero:hover .queue-card-ready {
  filter: brightness(1.12);
}

[data-bs-theme="dark"] .queue-card-hero:hover .queue-card-ready {
  text-shadow: 0 0 18px rgba(0, 212, 255, 0.45);
}

/* The waiting hero over its ready and blocked split. Both lines always render so
   every card is the same height. */
.queue-card-headline {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.queue-card-split {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  white-space: nowrap;
}

.queue-card-split-ready,
.queue-card-split-blocked {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
}

.queue-card-split-blocked {
  color: var(--arb-purple-on-bg);
}

.queue-card-split .qc-val {
  font-weight: 600;
}

.queue-card-split-blocked .qc-val {
  color: inherit;
}

.queue-card-ready-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--arb-text-muted);
  transition: color 0.15s ease;
}

.queue-card-hero:hover .queue-card-ready-label {
  color: var(--arb-teal-on-bg);
}

.queue-card-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.7rem 1.25rem;
  margin: 0;
  padding-top: 1.1rem;
  border-top: 1px solid var(--arb-border);
}

.queue-card-stat {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  min-width: 0;
}

.qc-val {
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  color: var(--arb-text);
}

.queue-card-stat:hover .qc-val {
  color: var(--arb-teal-on-bg);
}

.qc-lbl {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--arb-text-muted);
}

.queue-card-stat:hover .qc-lbl {
  color: var(--arb-text);
}

.queue-card-ages {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem 1rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--arb-border);
}

.queue-card-age {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}

.qa-lbl {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--arb-text-muted);
  transition: color 0.15s ease;
}

.qa-val {
  font-size: 0.85rem;
  color: var(--arb-text);
  font-variant-numeric: tabular-nums;
}

.queue-card-age:hover .qa-lbl {
  color: var(--arb-text);
}

.queue-card-age:hover .qa-val {
  color: var(--arb-teal-on-bg);
}

.queue-card-age:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 3px;
  border-radius: 3px;
}

.is-zero {
  color: var(--arb-text-muted) !important;
  opacity: 0.55;
}

[data-bs-theme="dark"] .queue-card-ready.is-zero {
  text-shadow: none;
}

/* ---- Queue landing: instance roll-up + layout toolbar ---- */
/* A view that had a roll-up last time holds its slot while the load runs, so the
   numbers fade into place rather than pushing the table down. A view that had
   none holds nothing, so an empty one never opens a gap and closes it again. */
.is-pending {
  opacity: 0;
  visibility: hidden;
}

.queue-summary {
  transition: opacity 0.18s ease-out;
  min-height: 3.25rem;
  box-sizing: border-box;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 1.6rem;
  margin-bottom: 1rem;
  padding: 0.7rem 1.1rem;
  background-color: var(--arb-surface);
  border: 1px solid var(--arb-border);
  border-radius: 0.6rem;
}

.qs-item {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
}

.qs-val {
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--arb-text);
  font-variant-numeric: tabular-nums;
}

.qs-lbl {
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--arb-text-muted);
}

.qs-sep {
  width: 1px;
  align-self: stretch;
  background-color: var(--arb-border);
}

/* Every view's control strip. One definition, so gap and wrapping cannot drift
   between the tabs the way stacked utility classes did. */
.toolbar,
.queue-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1rem;
}

.queue-toolbar {
  transition: opacity 0.18s ease-out;
}

.queue-toolbar-end {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-left: auto;
}

.queue-search {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 15rem;
  height: 2rem;
  padding: 0 0.55rem;
  gap: 0.45rem;
  background-color: var(--arb-surface);
  border: 1px solid var(--arb-border);
  border-radius: 0.45rem;
  color: var(--arb-text-muted);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.queue-search:focus-within {
  border-color: var(--arb-teal-dim);
  box-shadow: 0 0 0 0.18rem var(--arb-focus-ring);
  color: var(--arb-teal-on-bg);
}

.queue-search-icon {
  flex: none;
  width: 0.85rem;
  height: 0.85rem;
}

.queue-search-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--arb-text);
  font-size: 0.85rem;
}

.queue-search-input:focus {
  outline: none;
}

.queue-search-input::placeholder {
  color: var(--arb-text-muted);
  opacity: 0.75;
}

.queue-search-input::-webkit-search-cancel-button {
  appearance: none;
}

.queue-search-clear {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.05rem;
  height: 1.05rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--arb-text-muted);
  font-size: 0.7rem;
  line-height: 1;
  cursor: pointer;
}

.queue-search-clear:hover {
  background-color: var(--arb-surface-hover);
  color: var(--arb-text);
}

.queue-sort {
  display: flex;
  align-items: center;
  height: 2rem;
  padding: 0 0.3rem 0 0.65rem;
  gap: 0.4rem;
  background-color: var(--arb-surface);
  border: 1px solid var(--arb-border);
  border-radius: 0.45rem;
}

.queue-sort:focus-within {
  border-color: var(--arb-teal-dim);
  box-shadow: 0 0 0 0.18rem var(--arb-focus-ring);
}

.queue-sort-label {
  margin: 0;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--arb-text-muted);
}

.queue-sort-select {
  padding: 0 0.2rem;
  border: 0;
  background: transparent;
  color: var(--arb-text);
  font-size: 0.85rem;
  cursor: pointer;
}

.queue-sort-select:focus {
  outline: none;
}

.queue-sort-select option {
  background-color: var(--arb-surface);
  color: var(--arb-text);
}

.queue-sort-dir {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: 0.3rem;
  background: transparent;
  color: var(--arb-teal-on-bg);
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
}

.queue-sort-dir:hover {
  background-color: var(--arb-surface-hover);
}

.view-toggle {
  display: inline-flex;
  align-items: center;
  height: 2rem;
  padding: 0.15rem;
  gap: 0.15rem;
  background-color: var(--arb-surface);
  border: 1px solid var(--arb-border);
  border-radius: 0.45rem;
}

.view-toggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: 0.3rem;
  background: transparent;
  color: var(--arb-text-muted);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.view-toggle-btn svg {
  width: 0.85rem;
  height: 0.85rem;
}

.view-toggle-btn:hover {
  color: var(--arb-text);
  background-color: var(--arb-surface-hover);
}

.view-toggle-btn.active {
  color: var(--arb-teal-on-bg);
  background-color: var(--arb-hover);
  box-shadow: inset 0 0 0 1px var(--arb-teal-dim);
}

.view-toggle-btn:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 1px;
}

/* Wrapped toolbar: controls fill the row rather than hugging the right edge. */
@media (max-width: 640px) {
  .queue-search {
    width: 100%;
    min-width: 0;
  }

  .queue-toolbar-end {
    width: 100%;
    margin-left: 0;
    justify-content: space-between;
  }

  /* A button keeps its label on one line and gives up none of its width: the sort
     control beside it is the flexible one. */
  .queue-toolbar-end > .btn {
    flex: none;
    white-space: nowrap;
  }

  .queue-sort {
    flex: 1 1 auto;
    min-width: 0;
  }

  .queue-sort-select {
    min-width: 0;
  }

  .qs-sep {
    display: none;
  }


  /* A control a finger has to find gets 24px to be found by. Links inside a table
     row are left alone: they read as inline text, and padding them would tear the
     dense layout the tables are for. */
  .queue-card-stat,
  .queue-card-dlq,
  .queue-card-age,
  .queue-sort-select {
    min-height: 24px;
  }

  .queue-toolbar .btn,
  .toolbar .btn,
  .queue-sort-dir {
    min-width: 24px;
    min-height: 24px;
  }

  /* The wrapper draws the box, so the input has to fill it to be the target. */
  .queue-search-input {
    min-height: 24px;
  }

  /* Bootstrap sizes a switch in ems off the label and positions it by floating the
     input under a padded parent. At the size a finger needs, that alignment no longer
     holds, so the pair becomes a flex row and the switch keeps its 2:1 shape. */
  .form-check.form-switch {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-left: 0;
    min-height: 0;
  }

  .form-switch .form-check-input {
    float: none;
    flex: none;
    margin: 0;
    width: 2.75rem;
    height: 1.5rem;
  }

  .form-switch .form-check-label {
    margin: 0;
  }

  .queue-summary {
    gap: 0.3rem 1.1rem;
  }
}

/* ---- Queue landing: list view ---- */
.queue-table {
  table-layout: auto;
  width: 100%;
}

.queue-table th.num,
.queue-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.queue-table td:last-child,
.queue-table th:last-child {
  padding-left: 1.25rem;
}

.queue-row-name {
  font-family: 'Electrolize', sans-serif;
  letter-spacing: 0.02em;
  color: var(--arb-teal-on-bg);
  text-decoration: none;
}

.queue-row-name:hover {
  color: var(--arb-teal-on-bg);
  text-decoration: underline;
}

.queue-row-num {
  color: var(--arb-text);
  text-decoration: none;
}

.queue-row-num:hover {
  color: var(--arb-teal-on-bg);
}

/* Semantic colours for a count, wherever one is shown. */
.qc-val.warn,
.qs-val.warn,
.queue-row-num.warn,
.stat-card .display-6.warn {
  color: var(--arb-gold-on-bg);
}

.qc-val.bad,
.qs-val.bad,
.queue-row-num.bad,
.stat-card .display-6.bad {
  color: var(--arb-danger-on-bg);
}

.queue-row-num.bad {
  font-weight: 600;
}

.queue-list-empty {
  padding: 2rem 0;
  text-align: center;
  color: var(--arb-text-muted);
}

/* ---- Buttons ---- */
/* A disabled button keeps its variant's own colours and reads as disabled through
   opacity alone, rather than falling back to Bootstrap's grey. */
.btn:disabled {
  --bs-btn-disabled-color: var(--bs-btn-color);
  --bs-btn-disabled-bg: var(--bs-btn-bg);
  --bs-btn-disabled-border-color: var(--bs-btn-border-color);
}

.btn-outline-secondary {
  --bs-btn-color: var(--arb-text-muted);
  --bs-btn-border-color: var(--arb-input-border);
  --bs-btn-hover-color: var(--arb-text);
  --bs-btn-hover-bg: var(--arb-surface-hover);
  --bs-btn-hover-border-color: var(--arb-text-muted);
}

.btn-outline-primary {
  --bs-btn-color: var(--arb-teal-on-bg);
  --bs-btn-border-color: var(--arb-teal-dim);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--arb-teal-dim);
  --bs-btn-hover-border-color: var(--arb-teal-on-bg);
}

.btn-primary {
  /* Dark enough that the white label clears the contrast floor on the fill. */
  --bs-btn-bg: #0e7490;
  --bs-btn-border-color: #0e7490;
  --bs-btn-hover-bg: #0b6480;
  --bs-btn-hover-border-color: var(--arb-teal-on-bg);
  --bs-btn-color: #fff;
  --bs-btn-hover-color: #fff;
}

.btn-outline-success {
  --bs-btn-color: var(--arb-ok);
  --bs-btn-border-color: var(--arb-ok);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: #15803d;
  --bs-btn-hover-border-color: #22c55e;
}

[data-bs-theme="dark"] .btn-outline-success {
  --bs-btn-color: var(--arb-ok-bright);
  --bs-btn-border-color: #10b981;
  --bs-btn-hover-bg: #065f46;
}

.btn-outline-danger {
  --bs-btn-color: var(--arb-danger);
  --bs-btn-border-color: var(--arb-danger);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: #b91c1c;
  --bs-btn-hover-border-color: #ef4444;
}

[data-bs-theme="dark"] .btn-outline-danger {
  --bs-btn-color: var(--arb-danger-on-bg);
  --bs-btn-border-color: var(--arb-danger);
  --bs-btn-hover-bg: var(--arb-danger-deep);
}

.btn-outline-warning {
  --bs-btn-color: var(--arb-gold-on-bg);
  --bs-btn-border-color: var(--arb-gold);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--arb-gold-dim);
  --bs-btn-hover-border-color: var(--arb-gold);
}

[data-bs-theme="dark"] .btn-outline-warning {
  --bs-btn-color: #fbbf24;
  --bs-btn-border-color: var(--arb-gold);
  --bs-btn-hover-bg: #78350f;
}



/* ---- Row actions: one quiet glyph per row, not a labelled button per row ---- */
.btn-row-actions {
  --bs-btn-color: var(--arb-text-muted);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: transparent;
  --bs-btn-hover-color: var(--arb-teal-on-bg);
  --bs-btn-hover-bg: var(--arb-surface-hover);
  --bs-btn-hover-border-color: var(--arb-border);
  --bs-btn-active-color: var(--arb-teal-on-bg);
  --bs-btn-active-bg: var(--arb-surface-hover);
  --bs-btn-active-border-color: var(--arb-border);
  --bs-btn-disabled-color: var(--arb-text-muted);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: transparent;
  padding: 0.15rem 0.45rem;
  font-size: 1.1rem;
  line-height: 1.1;
}

/* The glyph lifts out of the muted state once the row is under the pointer. */
tr:hover .btn-row-actions:not(:disabled) {
  color: var(--arb-text);
}

.btn-row-actions:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 1px;
}

/* ---- Badges ---- */
.badge.bg-success {
  background-color: var(--arb-ok-bg) !important;
  color: var(--arb-ok-fg);
}

/* Terminal, inert: outlined instead of filled. The ring is inset so this badge
   keeps the height of every other one. */
.bg-dark-subtle {
  background-color: var(--arb-inert-bg) !important;
  box-shadow: inset 0 0 0 1px var(--arb-inert-border);
}

.text-dark-emphasis {
  color: var(--arb-inert-fg) !important;
}

/* Bootstrap's subtle-success pair reads as its own green next to the palette. */
.bg-success-subtle {
  background-color: var(--arb-ok-bg) !important;
}

.text-success-emphasis {
  color: var(--arb-ok-fg) !important;
}

.badge.bg-danger {
  background-color: var(--arb-danger-deep) !important;
  color: #fca5a5;
}

[data-bs-theme="light"] .badge.bg-danger {
  background-color: #fee2e2 !important;
  color: var(--arb-danger-deep);
}

.badge.bg-primary {
  background-color: var(--arb-purple-dim) !important;
  color: #c4b5fd;
}

[data-bs-theme="light"] .badge.bg-primary {
  background-color: #ede9fe !important;
  color: #5b21b6;
}

.badge.bg-warning {
  background-color: var(--arb-gold-dim) !important;
  color: #fcd34d;
}

[data-bs-theme="light"] .badge.bg-warning {
  background-color: #fef3c7 !important;
  color: var(--arb-gold-dim);
}

.badge.bg-secondary {
  background-color: var(--arb-surface-hover) !important;
  color: var(--arb-text);
}

.badge.bg-info {
  background-color: rgba(0, 212, 255, 0.15) !important;
  color: var(--arb-teal-on-bg);
}

/* Paused state badge: readable in both modes (used by navbar + worker rows). */
.paused-badge {
  background-color: var(--arb-pause-bg);
  color: var(--arb-pause-fg);
  border: 1px solid var(--arb-pause-border);
}

/* ---- Rate-limit fill bars: muted so they don't glare, especially in dark mode ---- */
.progress-bar.bg-success {
  background-color: var(--arb-fill-ok) !important;
}

.progress-bar.bg-warning {
  background-color: var(--arb-fill-warn) !important;
}

.progress-bar.bg-danger {
  background-color: var(--arb-fill-bad) !important;
}

/* ---- Policy tables ---- */
/* A policy holding work back marks its row, so the table scans like the queue
   cards. Bootstrap paints striping and hover with an inset box-shadow, so the
   accent is drawn on top of the cell instead of competing for that property. */
tr.policy-warn td:first-child,
tr.policy-bad td:first-child {
  position: relative;
}

tr.policy-warn td:first-child::before,
tr.policy-bad td:first-child::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 3px;
}

tr.policy-warn td:first-child::before {
  background-color: var(--arb-gold);
}

tr.policy-bad td:first-child::before {
  background-color: var(--arb-danger-on-bg);
}

/* Placeholder row while a prefix's rows are on the way. It fills the height one
   row would, so the panel does not resize when they land. */
.skeleton-bar {
  display: block;
  height: 0.75rem;
  /* Centred in the height a filled row occupies, so nothing moves when it lands. */
  margin: 0.375rem 0;
  border-radius: 0.25rem;
  background: linear-gradient(90deg, var(--arb-surface-hover) 25%, var(--arb-border) 50%, var(--arb-surface-hover) 75%);
  background-size: 200% 100%;
  animation: skeleton-sweep 1.2s ease-in-out infinite;
}

/* Placeholder rows arrive on a short fade, staggered down the table, so a load
   that outlasts the delay does not pop them in all at once. */
.skeleton-row {
  animation: skeleton-in 0.22s ease-out both;
}

.skeleton-row:nth-of-type(2) { animation-delay: 0.04s; }
.skeleton-row:nth-of-type(3) { animation-delay: 0.08s; }
.skeleton-row:nth-of-type(4) { animation-delay: 0.12s; }
.skeleton-row:nth-of-type(5) { animation-delay: 0.16s; }

@keyframes skeleton-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Matches the height of a filled row in the main tables. */
.skeleton-row td {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

@keyframes skeleton-sweep {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

.drill-table {
  --bs-table-bg: transparent;
  margin-bottom: 0.25rem;
}

.drill-table > thead > tr > th {
  background-color: transparent;
  border-bottom: 1px solid var(--arb-border) !important;
}

.drill-table > tbody > tr > td {
  background-color: transparent;
  border-top: 1px solid var(--arb-border);
  border-bottom: 0;
}

/* The other tables carry a row-actions menu, which sets their row height. This one
   has no actions column, so hold that height rather than sitting shorter. */
#tab-concurrency .detail-row > td {
  height: 2.25rem;
}

/* ---- SSE dot glow in dark mode ---- */
[data-bs-theme="dark"] .status-pip.connected .status-dot,
[data-bs-theme="dark"] .status-pip.ok .status-dot {
  background-color: var(--arb-ok-bright);
  box-shadow: 0 0 6px rgba(52, 211, 153, 0.5);
}

[data-bs-theme="dark"] .status-pip.disconnected .status-dot,
[data-bs-theme="dark"] .status-pip.down .status-dot {
  border-color: #f87171;
  box-shadow: 0 0 6px rgba(248, 113, 113, 0.5);
}

[data-bs-theme="dark"] .status-pip.polling .status-dot {
  background-color: #fbbf24;
}

/* ---- Dropdowns ---- */
.dropdown-menu {
  background-color: var(--arb-surface) !important;
  border-color: var(--arb-border-accent) !important;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}

[data-bs-theme="dark"] .dropdown-menu {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
}

.dropdown-item {
  color: var(--arb-text-muted) !important;
}

.dropdown-item:hover,
.dropdown-item:focus {
  background-color: var(--arb-surface-hover) !important;
  color: var(--arb-teal-on-bg) !important;
}

.dropdown-item.active {
  background-color: var(--arb-purple-dim) !important;
  color: #e2e8f0 !important;
}

/* ---- Forms ---- */
.form-control,
.form-select {
  background-color: var(--arb-input-bg) !important;
  border-color: var(--arb-input-border) !important;
  color: var(--arb-text) !important;
}

.form-control:focus,
.form-select:focus {
  border-color: var(--arb-teal-dim) !important;
  box-shadow: 0 0 0 0.2rem var(--arb-focus-ring) !important;
}

.form-control::placeholder {
  color: var(--arb-text-muted) !important;
  opacity: 0.6;
}

.form-check-input {
  background-color: var(--arb-surface-hover) !important;
  border-color: var(--arb-input-border) !important;
  cursor: pointer;
}

.form-check-input:checked {
  background-color: var(--arb-teal-dim) !important;
  border-color: var(--arb-teal-on-bg) !important;
}

.form-check-input:focus {
  box-shadow: 0 0 0 0.2rem var(--arb-focus-ring) !important;
}

/* Center the select-column checkbox in its header and cells. */
.select-cell {
  text-align: center;
  vertical-align: middle;
}
.select-cell .form-check-input {
  display: block;
  float: none;
  margin: 0 auto;
}

.form-label {
  color: var(--arb-text-muted);
  font-size: 0.85rem;
}

/* ---- Modals ---- */
.modal-content {
  background-color: var(--arb-surface) !important;
  border: 1px solid var(--arb-border-accent) !important;
  box-shadow: 0 0 24px rgba(124, 58, 237, 0.1);
}

/* The action names the modal, the subject sits under it. Aligning to the start keeps
   the close button beside the title rather than centred on the block. */
.modal-header {
  align-items: flex-start;
  gap: 1rem;
  padding: 0.85rem 1.1rem;
  border-bottom-color: var(--arb-border) !important;
}

/* Takes the row, so the close button sits at the far edge rather than beside the
   title, and is bounded, so a long subject ellipsizes instead of wrapping. */
.modal-heading {
  flex: 1;
  min-width: 0;
}

/* The drawer's title sizing, so a panel heading reads the same wherever it opens.
   Left to the h2 default it renders at heading scale and doubles the header's height. */
.modal-header .modal-title {
  font-family: 'Electrolize', sans-serif;
  font-size: 1.05rem;
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: var(--arb-teal-on-bg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

[data-bs-theme="dark"] .modal-header .modal-title {
  text-shadow: 0 0 8px rgba(0, 212, 255, 0.2);
}

.modal-subject {
  margin: 0.25rem 0 0;
  font-family: var(--bs-font-monospace);
  font-size: 0.8rem;
  line-height: 1.2;
  color: var(--arb-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.modal-header .btn-close {
  margin: 0;
  flex: none;
}

.modal dt {
  color: var(--arb-text-muted);
}

/* ---- Detail drawer ---- */
.detail-drawer {
  /* Bootstrap sizes and borders an offcanvas through these variables. */
  --bs-offcanvas-width: var(--arb-drawer-w, 32rem);
  --bs-offcanvas-border-color: var(--arb-border-accent);
  --bs-offcanvas-border-width: 1px;
  --bs-offcanvas-bg: var(--arb-surface);
  width: var(--bs-offcanvas-width);
  max-width: 94vw;
  background-color: var(--bs-offcanvas-bg);
  box-shadow: -12px 0 34px rgba(0, 0, 0, 0.34);
}

/* Narrow enough that the drawer covers the list anyway: full width, backdrop. */
@media (max-width: 1200px) {
  .detail-drawer {
    --bs-offcanvas-width: 100vw;
    max-width: 100vw;
  }

  .drawer-resize {
    display: none;
  }
}

.detail-drawer .offcanvas-header {
  display: block;
  padding: 0;
  border-bottom: 1px solid var(--arb-border);
}

.drawer-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 1.1rem;
}

.drawer-head-text {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.drawer-title {
  font-family: 'Electrolize', sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.04em;
  color: var(--arb-teal-on-bg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

[data-bs-theme="dark"] .drawer-title {
  text-shadow: 0 0 8px rgba(0, 212, 255, 0.2);
}

/* Holds its line when there is no status or position to show. */
.drawer-sub {
  display: flex;
  align-items: center;
  min-height: 1.05rem;
  gap: 0.5rem;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--arb-text-muted);
}

.drawer-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
}

.drawer-nav {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem;
  gap: 0.15rem;
  background-color: var(--arb-bg);
  border: 1px solid var(--arb-border);
  border-radius: 0.45rem;
}

.drawer-actions-btn {
  height: 1.9rem;
  padding: 0 0.55rem;
  border: 1px solid var(--arb-border);
  border-radius: 0.45rem;
  background-color: var(--arb-bg);
  color: var(--arb-text);
  font-size: 0.78rem;
  line-height: 1;
  cursor: pointer;
}

.drawer-actions-btn:hover:not(:disabled) {
  color: var(--arb-teal-on-bg);
  border-color: var(--arb-teal-dim);
}

.drawer-actions-btn:disabled {
  opacity: 0.45;
  cursor: default;
}

.drawer-nav-btn,
.drawer-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 0.3rem;
  background: transparent;
  color: var(--arb-text-muted);
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.drawer-nav-btn {
  width: 1.7rem;
  height: 1.6rem;
  font-size: 0.8rem;
  color: var(--arb-text);
}

.drawer-nav-btn:hover:not(:disabled) {
  color: var(--arb-teal-on-bg);
  background-color: var(--arb-surface-hover);
}

.drawer-nav-btn:disabled {
  color: var(--arb-text-muted);
  opacity: 0.35;
  cursor: default;
}

.drawer-close {
  width: 1.9rem;
  height: 1.9rem;
  font-size: 0.9rem;
}

.drawer-close:hover {
  color: var(--arb-text);
  background-color: var(--arb-surface-hover);
}

.drawer-nav-btn:focus-visible,
.drawer-close:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 1px;
}

/* Label/value rows with their own rhythm, rather than a bare definition list. */
.detail-drawer .offcanvas-body {
  padding: 0.35rem 1.1rem 1.5rem;
}

.detail-drawer dl {
  margin: 0;
}

.detail-drawer dt,
.detail-drawer dd {
  padding: 0.42rem 0;
  border-bottom: 1px solid var(--arb-border);
  margin: 0;
}

.detail-drawer dt {
  color: var(--arb-text-muted);
  font-weight: 500;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1.35;
}

.detail-drawer dd {
  word-break: break-word;
  font-size: 0.88rem;
}

/* A full-width label owns the block below it, so it drops its own divider. */
.detail-drawer dt.col-12 {
  border-bottom: 0;
  padding-bottom: 0.2rem;
  padding-top: 0.9rem;
}

.detail-drawer dd.col-12 {
  border-bottom: 0;
}

/* Override editors, which live in a drawer panel. */
.arb-edit .edit-note {
  color: var(--arb-text-muted);
  font-size: 0.78rem;
  padding: 0.5rem 0 0.4rem;
  margin: 0;
}

.arb-edit .form-check-label {
  color: var(--arb-text-muted);
  font-weight: 500;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.arb-edit .form-control,
.arb-edit .form-select {
  margin-top: 0.45rem;
}

/* A field reverted to its declared default reads as inert. */
.arb-edit .form-control:disabled,
.arb-edit .form-select:disabled {
  opacity: 0.45;
}

.arb-edit .edit-hint {
  display: block;
  margin-top: 0.3rem;
  color: var(--arb-text-muted);
  font-size: 0.75rem;
}

.arb-edit .edit-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  padding-top: 1.1rem;
}

/* In a drawer the fields stack, one per row, on the detail rows' rhythm. */
.drawer-edit .edit-field {
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--arb-border);
}

/* Drag handle on the drawer's leading edge. */
.drawer-resize {
  position: absolute;
  top: 0;
  left: 0;
  width: 6px;
  height: 100%;
  z-index: 3;
  cursor: col-resize;
  background: transparent;
  transition: background-color 0.15s ease;
}

.drawer-resize:hover,
.drawer-resize:focus-visible,
body.drawer-resizing .drawer-resize {
  background-color: var(--arb-teal-dim);
  outline: none;
}

/* A drag must not select the table text it passes over. */
body.drawer-resizing {
  user-select: none;
  cursor: col-resize;
}

body.drawer-resizing .detail-drawer {
  transition: none;
}

/* A job can vanish mid-browse, so this state is routine. */
.drawer-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  min-height: 60%;
  padding: 2rem 1.5rem;
  text-align: center;
  color: var(--arb-text-muted);
}

.drawer-empty-icon {
  width: 2.25rem;
  height: 2.25rem;
  margin-bottom: 0.35rem;
  color: var(--arb-text-muted);
  opacity: 0.55;
}

.drawer-empty-title {
  margin: 0;
  font-size: 0.95rem;
  color: var(--arb-text);
}

.drawer-empty-note {
  margin: 0;
  max-width: 24rem;
  font-size: 0.82rem;
  line-height: 1.45;
}

.drawer-empty-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
}

.detail-row {
  cursor: pointer;
}

/* Two equal tracks, so the rate and concurrency badges line up down the column. */
.gate-badges {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.25rem;
  width: 100%;
}

.gate-badge {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0.1rem 0.4rem;
  border-radius: 0.3rem;
  font-size: 0.7rem;
  font-family: var(--bs-font-monospace);
  border: 1px solid transparent;
  text-align: center;
}

.gate-badge.is-empty {
  visibility: hidden;
}

a.gate-badge {
  text-decoration: none;
  cursor: pointer;
}

a.gate-badge:hover {
  background-color: var(--arb-surface-hover);
}

a.gate-badge.is-empty {
  pointer-events: none;
}

.gate-badge.is-rate {
  color: var(--arb-gate-rate-fg);
  border-color: var(--arb-gate-rate-border);
}

.gate-badge.is-conc {
  color: var(--arb-gate-conc-fg);
  border-color: var(--arb-gate-conc-border);
}

/* Below 1025px the container is its own scroller, where sticky cannot apply. */
@media (min-width: 1025px) {
  /* Direct children only: a drill-down table nested in a row is not the head. */
  .table.sticky-head > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--arb-bg);
  }
}

/* ---- Child rows (inline expand) ---- */
.child-row {
  border-left: 2px solid var(--arb-teal-dim);
}

.expand-arrow {
  font-size: 0.65rem;
  color: var(--arb-teal-on-bg);
  transition: transform 0.15s;
}

th.sortable {
  cursor: pointer;
  user-select: none;
}

/* Fixed box: the caret glyphs differ in width. */
.sort-caret {
  display: inline-block;
  width: 0.85em;
  margin-left: 0.3em;
  text-align: center;
  font-size: 0.85em;
  opacity: 0.75;
}

th.sortable:hover .sort-caret {
  opacity: 1;
}

th.sortable:hover {
  color: var(--arb-teal-on-bg);
}

th.sortable:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: -2px;
}

.format-toggle-cell:focus-visible {
  outline: 2px solid var(--arb-focus-outline);
  outline-offset: 2px;
  opacity: 1;
}

.table-responsive {
  overflow: visible;
}

/* ---- Scrollbar (thin, themed for both modes via theme variables) ---- */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--arb-input-border) var(--arb-bg);
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: var(--arb-bg);
}

::-webkit-scrollbar-thumb {
  background: var(--arb-input-border);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--arb-purple-dim);
}

/* Brand on the left, queue picker + controls clustered on the right. */
.navbar .container-fluid {
  gap: 0.75rem;
}

/* ---- Narrow screens (phones + tablets, incl. iPad Pro portrait) ---- */
@media (max-width: 1024px) {
  /* Denser text overall. */
  body {
    font-size: 0.875rem;
  }
  .table {
    font-size: 0.8rem;
  }

  /* Wide tables size columns to their content and scroll horizontally, rather
     than squeezing fixed percentage widths until the headers overlap. */
  .table-responsive {
    overflow-x: auto;
    margin-bottom: 0.75rem;
  }
  .table-responsive table[style*="table-layout: fixed"] {
    table-layout: auto !important;
    width: auto !important;
    min-width: 100%;
  }
  /* Headers stay on one line, so the sort caret stays beside the label. */
  .table-responsive th {
    white-space: nowrap;
  }

  /* Tab strip: one horizontally scrollable row. overflow-y: hidden stops the
     stray vertical scrollbar the overflow-x quirk otherwise adds. */
  .nav-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-padding-inline: 2.5rem;
  }
  .nav-tabs .nav-item {
    white-space: nowrap;
  }
}

/* ---- Phones only: stack the navbar (tablets/laptops have room for one row) ---- */
@media (max-width: 768px) {
  .navbar .container-fluid {
    flex-wrap: wrap;
  }
  .navbar-brand {
    font-size: 1rem;
  }
  /* Brand + controls share the top row (controls pushed right by the brand's
     auto margin). The primary nav drops to its own scrollable row below. */
  .nav-controls {
    order: 2;
  }
  .nav-primary {
    order: 3;
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .status-pip.disconnected .status-dot,
  .status-pip.down .status-dot {
    animation: none;
  }
}

/* ---- Drill link: a subtle "go to the rows behind this value" affordance ---- */
.drill-link {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  align-self: flex-start;
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--arb-border);
  border-radius: 0.3rem;
  font-size: 0.7rem;
  line-height: 1.5;
  white-space: nowrap;
  color: var(--arb-text-muted);
  text-decoration: none;
  transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.drill-link:hover {
  color: var(--arb-teal-on-bg);
  border-color: var(--arb-teal-on-bg);
  background: var(--arb-surface-hover);
}

/* ---- Light-mode contrast ----
   Bootstrap's outline variants take their text colour from the mid-weight palette,
   which is tuned for a dark ground. On the light one they fall under the 4.5:1 floor,
   so the light theme names darker ones. */
[data-bs-theme="light"] .btn-outline-success {
  --bs-btn-color: #157347;
  --bs-btn-border-color: #157347;
}

[data-bs-theme="light"] .btn-outline-primary {
  --bs-btn-color: #0a58ca;
  --bs-btn-border-color: #0a58ca;
}

[data-bs-theme="light"] .btn-outline-danger {
  --bs-btn-color: #b02a37;
  --bs-btn-border-color: #b02a37;
}

[data-bs-theme="light"] code {
  color: #a1265f;
}

/* The same, for the one variant a dark ground gets wrong. */
[data-bs-theme="dark"] .btn-outline-primary {
  --bs-btn-color: #6ea8fe;
  --bs-btn-border-color: #3d8bfd;
}

/* Bootstrap's link blue is tuned for a white ground and falls short on this one. */
[data-bs-theme="light"] {
  --bs-link-color-rgb: 10, 88, 202;
  --bs-link-hover-color-rgb: 8, 66, 152;
}

/* ---- Phones: undo the tablet density ----
   The tablet block above cuts the base size, and the derived label sizes then land
   under 11px on a phone, which reads at arm's length. 16px on the controls also stops
   iOS zooming the page when one takes focus. Tables scroll rather than squeeze, so the
   larger text costs no layout. */
@media (max-width: 640px) {
  body {
    font-size: 1rem;
  }

  .table {
    font-size: 0.875rem;
  }

  .form-control,
  .form-select,
  .queue-search-input {
    font-size: 1rem;
  }

  .qs-lbl,
  .qc-lbl,
  .qa-lbl,
  .queue-card-ready-label,
  .queue-sort-label,
  .stat-section-label,
  .card-title {
    font-size: 0.8rem;
  }

  .small,
  small {
    font-size: 0.875rem;
  }

  .sort-caret {
    font-size: 0.95em;
  }

  .badge {
    font-size: 0.75rem;
  }

  /* The event-type filters are a row of chips: let them wrap rather than push the
     page sideways once the labels are full size. */
  .toolbar .d-flex.gap-2 {
    flex-wrap: wrap;
  }
}

/* ---- Phones: tables show what identifies a row ----
   The column registry drops the rest (narrow: false), and the select cell goes with
   them: it is unconditional in the markup, so the registry cannot reach it.
   The width floors above exist so a full column set scrolls rather than compresses.
   A reduced set has nothing to compress, so the floor comes off and the table fits. */
@media (max-width: 640px) {
  .table .select-cell {
    display: none;
  }

  #tab-jobs table,
  #tab-dlq table,
  #tab-archive table {
    min-width: 0;
  }
}

/* ---- Empty state: the first screen a new install shows ---- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  max-width: 34rem;
  margin: 0 auto;
  padding: 6rem 1.5rem 2rem;
  text-align: center;
  color: var(--arb-text-muted);
}

.empty-state-icon {
  width: 2.5rem;
  height: 2.5rem;
  color: var(--arb-teal-on-bg);
  opacity: 0.7;
}

.empty-state-title {
  margin: 0.25rem 0 0;
  font-family: 'Electrolize', sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.04em;
  color: var(--arb-text);
}

.empty-state-note {
  margin: 0;
  line-height: 1.6;
}

/* The same panel says a load failed, in the one colour a failure is drawn in. */
.empty-state-icon.is-error {
  color: var(--arb-danger-on-bg);
  opacity: 0.85;
}

.empty-state-action {
  margin-top: 0.75rem;
}

.empty-state-link {
  margin-top: 0.5rem;
  font-size: 0.9rem;
  text-decoration: none;
}

.empty-state-link:hover {
  text-decoration: underline;
}
