/* ---------- Sidebar ---------- */
.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: 22px 16px;
  display: flex;
  flex-direction: column;
  gap: 28px;
  overflow: hidden;
  /* Pinned to the actual viewport, not stretched to the grid row's full
     height (which — since .shell only sets min-height: 100vh, not a
     fixed height — grows with whatever makes the PAGE tall, main's
     content included). Confirmed live: on a short page the sidebar
     still rendered taller than one screen and scrolled away with the
     rest of the page instead of staying put. position: sticky keeps it
     pinned to the visible screen while the page around it scrolls. */
  position: sticky;
  top: 0;
  height: 100vh;
}

/* Character used as each nav item's collapsed-state icon — no icon
   library in this project, hidden in expanded mode (the .dot active-state
   indicator shows instead, unchanged from before). Confirmed live: the
   .dot alone at 8px isn't identifiable as anything, so a plain
   icon-rail made the collapsed sidebar unusable — a real (if improvised)
   glyph per item is needed for a collapsed nav to be legible at all. */
.nav-item .icon {
  display: none;
  font-size: 16px;
  line-height: 1;
  width: 18px;
  text-align: center;
  flex-shrink: 0;
}

/* Desktop collapse — separate from the mobile .nav-open reveal below.
   Shrinks to an icon-only rail (glyphs only, labels/text hidden) rather
   than fully hiding the nav, so the page stays navigable collapsed. */
.collapse-toggle {
  margin-left: auto;
  width: 26px;
  height: 26px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--ink-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  font-size: 12px;
  line-height: 1;
}
.collapse-toggle:hover { color: var(--ink); background: var(--border); }

.sidebar.collapsed {
  padding-left: 12px;
  padding-right: 12px;
}
.sidebar.collapsed .brand-name,
.sidebar.collapsed .brand-sub,
.sidebar.collapsed .nav-label,
.sidebar.collapsed .label,
.sidebar.collapsed .sidebar-foot .mono {
  display: none;
}
.sidebar.collapsed .brand {
  flex-direction: column;
  gap: 10px;
  padding: 0;
}
.sidebar.collapsed .collapse-toggle { margin-left: 0; }
.sidebar.collapsed nav { align-items: center; }
.sidebar.collapsed .nav-item {
  justify-content: center;
  padding: 10px;
  width: 40px;
}
.sidebar.collapsed .nav-item .dot { display: none; }
.sidebar.collapsed .nav-item .icon {
  display: block;
  color: var(--ink-dim);
}
.sidebar.collapsed .nav-item.active .icon { color: var(--accent); }
.sidebar.collapsed .sidebar-foot {
  justify-content: center;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 8px;
}

.brand-mark {
  width: 30px;
  height: 30px;
  border-radius: 7px;
  background: var(--accent);
  color: var(--accent-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "IBM Plex Mono", monospace;
  font-weight: 600;
  font-size: 13px;
  flex-shrink: 0;
}

.brand-name {
  font-weight: 700;
  font-size: 15.5px;
  letter-spacing: -0.01em;
}

.brand-sub {
  font-size: 11px;
  color: var(--ink-faint);
  font-family: "IBM Plex Mono", monospace;
  letter-spacing: 0.02em;
}

nav { display: flex; flex-direction: column; gap: 2px; }

.nav-label {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-faint);
  padding: 0 10px;
  margin: 14px 0 6px;
}
.nav-label:first-child { margin-top: 0; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 13.5px;
  color: var(--ink-dim);
  text-decoration: none;
  font-weight: 500;
}

.nav-item .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: 0.35; flex-shrink: 0;
}

.nav-item:hover { background: var(--surface-2); color: var(--ink); }

.nav-item.active {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}
.nav-item.active .dot { background: var(--accent); opacity: 1; }

.sidebar-foot {
  margin-top: auto;
  padding: 10px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--ink-faint);
  font-family: "IBM Plex Mono", monospace;
  display: flex;
  justify-content: space-between;
}
.sidebar-foot .ok { color: var(--good); }
.sidebar-foot .bad { color: var(--bad); }

.nav-toggle {
  display: none;
  margin-left: auto;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
}
.nav-toggle span {
  width: 16px;
  height: 2px;
  background: var(--ink);
  border-radius: 1px;
}

/* Kept here (not in base.css) so these rules load after — and win the
   cascade over — the base .nav-toggle/.sidebar rules defined above. */
@media (max-width: 980px) {
  .sidebar {
    padding: 12px 16px;
    gap: 0;
    align-self: start;
    border-right: none;
    border-bottom: 1px solid var(--border);
    /* Below this breakpoint the sidebar becomes a horizontal top bar
       (see base.css's own 980px override collapsing .shell to a single
       column). Sticky, not static — a short top bar pinned to the top
       of the viewport while the page scrolls under it, same as any
       normal mobile app header; NOT the same thing as the old bug where
       height: 100vh made the whole desktop-style column stick (that's
       what height: auto below actually fixes — a short bar, not a
       full-height one). */
    position: sticky;
    top: 0;
    height: auto;
    z-index: 20;
  }
  .sidebar .brand { padding: 0; }
  .nav-toggle { display: flex; }
  /* Mobile already fully hides/reveals the nav via the hamburger above —
     the desktop icon-rail collapse is redundant here, and .shell's fixed
     column width doesn't apply below this breakpoint anyway (see
     base.css's own 980px override). */
  .collapse-toggle { display: none; }
  .sidebar.collapsed .brand-name,
  .sidebar.collapsed .brand-sub,
  .sidebar.collapsed .label {
    display: initial;
  }
  .sidebar.collapsed .brand { flex-direction: row; }
  .sidebar.collapsed .nav-item { width: auto; justify-content: flex-start; }

  .sidebar nav,
  .sidebar .sidebar-foot {
    display: none;
  }
  .sidebar.nav-open nav,
  .sidebar.nav-open .sidebar-foot {
    display: flex;
  }
  .sidebar.nav-open nav { margin-top: 18px; }
  .sidebar.nav-open .sidebar-foot { margin-top: 18px; }
}

/* ---------- Buttons ---------- */
.btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  padding: 9px 16px;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.btn:hover { background: var(--surface-2); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.btn-primary:hover { filter: brightness(1.06); }

/* ---------- Stat cards ---------- */
.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  margin-bottom: 22px;
}

.stat {
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
  box-shadow: var(--mono-shadow);
  text-decoration: none;
  transition: border-color 0.12s ease, transform 0.12s ease;
}
a.stat:hover {
  border-color: var(--accent);
  transform: translateY(-1px);
}

.stat-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-faint);
  font-family: "IBM Plex Mono", monospace;
  margin-bottom: 8px;
}

.stat-value {
  font-family: "IBM Plex Mono", monospace;
  font-size: 26px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.stat-value.accent { color: var(--accent); }
.stat-value.good { color: var(--good); }
.stat-value.faint { color: var(--ink-faint); }

.stat-hint {
  font-size: 12px;
  color: var(--ink-faint);
  margin-top: 4px;
}

/* ---------- Panels ---------- */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--mono-shadow);
  margin-bottom: 22px;
  overflow: hidden;
}

/* Plain padded content area for a standalone .panel card (Jobs, Settings,
   Language Rules, Bazarr Connection) — unlike .engine-config, this has NO
   background of its own, since it IS the card's body, not an inset
   expansion nested within a longer cascade row (see .engine-config). */
.panel-body { padding: 16px 20px 20px; }
.panel-body .panel-title { margin-bottom: 2px; }
.panel-body .panel-title-sub { margin-bottom: 14px; }

a.panel,
a.panel:hover,
a.panel:visited,
a.panel * {
  text-decoration: none;
  color: inherit;
}
a.panel {
  display: block;
  transition: border-color 0.15s ease;
}
a.panel:hover {
  border-color: var(--accent);
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}

.panel-title { font-weight: 700; font-size: 14.5px; }
.panel-title-sub { font-size: 12px; color: var(--ink-faint); margin-top: 2px; }
.panel-body { padding: 20px; }

.panel-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 60px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--mono-shadow);
  margin-bottom: 22px;
  color: var(--ink-faint);
  font-size: 12.5px;
}
.loading-spinner {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2.5px solid var(--border);
  border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ---------- Progress ---------- */
.progress-row { display: flex; align-items: center; gap: 16px; }

.progress-track {
  flex: 1;
  height: 8px;
  background: var(--surface-2);
  border-radius: 99px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), #6f95bd);
  border-radius: 99px;
  transition: width 0.4s ease;
}

.progress-figs {
  font-family: "IBM Plex Mono", monospace;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-dim);
  white-space: nowrap;
}
.progress-figs b { color: var(--ink); font-weight: 600; }

.progress-meta {
  display: flex;
  gap: 22px;
  margin-top: 16px;
  font-size: 12.5px;
  color: var(--ink-dim);
}
.progress-meta span { font-family: "IBM Plex Mono", monospace; color: var(--ink); font-weight: 500; }

.progress-empty {
  color: var(--ink-faint);
  font-size: 13px;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 99px;
  font-size: 11.5px;
  font-weight: 600;
  font-family: "IBM Plex Mono", monospace;
}
.pill-running { background: var(--accent-soft); color: var(--accent); }
.pill-running .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: pulse 1.6s ease-in-out infinite; }
.pill-idle { background: var(--surface-2); color: var(--ink-faint); }
.pill-good { background: var(--good-soft); color: var(--good); }
.pill-good .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--good); }
.pill-warn { background: var(--warn-soft); color: var(--warn); }
.pill-warn .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--warn); }

.notice-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  margin-bottom: 16px;
  border-radius: 8px;
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  font-size: 13px;
  color: var(--ink);
}
.notice-banner-text b { color: var(--warn); }
.notice-banner a { color: var(--accent); font-weight: 600; white-space: nowrap; }

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
  .pill-running .dot { animation: none; }
}

/* ---------- Two column layout ---------- */
.grid-2 {
  display: grid;
  grid-template-columns: 340px 1fr;
  gap: 16px;
  align-items: start;
}

/* ---------- Engine cascade (list of engine cards inside ONE panel) ---------- */
.engine-cascade {
  padding: 6px 18px 14px;
}

.engine-card {
  width: 100%;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.engine-card:last-child { border-bottom: none; }

/* Vue TransitionGroup's FLIP-based move animation — fires automatically
   whenever a card's position in the list changes (e.g. a neighbor
   reordering live during a drag), giving the "cards jump out of the way"
   feedback so it's always clear whether the dragged card is about to
   land above or below a given neighbor. Only .engine-card-move-move
   (Vue's convention: <name>-move) needs a transition; enter/leave aren't
   used since cards are never actually added/removed during a reorder. */
.engine-card-move-move {
  transition: transform 220ms cubic-bezier(0.2, 0, 0.2, 1);
}

.engine-card.dragging {
  /* No transition here — this card's position is driven directly by the
     pointer via an inline transform every pointermove frame; animating
     that would make it visibly lag behind the actual finger/cursor. */
  position: relative;
  z-index: 5;
  box-shadow: 0 6px 20px rgb(0 0 0 / 18%);
  border-radius: 8px;
  cursor: grabbing;
}

.engine-card .panel-title { margin-bottom: 2px; }
.engine-card .panel-title-sub { margin-bottom: 14px; }

.engine-card-meta { display: flex; flex-direction: column; gap: 8px; }
.engine-card-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--ink-faint);
}
a.engine-card-line { color: var(--accent); text-decoration: none; }
a.engine-card-line:hover { text-decoration: underline; }
.engine-card-line .dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.engine-card-line .dot-good { background: var(--good); }
.engine-card-line .dot-warn { background: var(--warn); }

.upload-callout {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin: 14px 20px 20px;
  padding: 14px 16px;
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  border-radius: var(--radius);
  text-decoration: none;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.upload-callout:hover { transform: translateY(-1px); box-shadow: var(--mono-shadow); }

.upload-callout-text { display: flex; flex-direction: column; }
.upload-callout-num {
  font-family: "IBM Plex Mono", monospace;
  font-size: 30px;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--warn);
}
.upload-callout-label {
  font-size: 13px;
  color: var(--ink);
  margin-top: 2px;
}
.upload-callout-arrow {
  font-size: 20px;
  color: var(--warn);
  flex-shrink: 0;
}

.add-engine-wrap {
  position: relative;
  display: inline-block;
}

.engine-opt {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 8px;
  border-radius: 8px;
  cursor: pointer;
}
.engine-opt:hover { background: var(--surface-2); }

.engine-opt.selected {
  background: var(--accent-soft);
}
.engine-opt.disabled { opacity: 0.55; cursor: not-allowed; }

.engine-radio {
  width: 16px; height: 16px;
  border-radius: 50%;
  border: 2px solid var(--ink-faint);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.engine-opt.selected .engine-radio { border-color: var(--accent); }
.engine-radio .fill { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }

.engine-info { flex: 1; min-width: 0; }
.engine-name-row {
  /* Name + badges share one row on desktop, where there's usually
     plenty of horizontal room — only the mobile media query below
     forces .engine-badges onto its own separate line, since that's
     where a long name + 2 badges genuinely stops fitting on one line. */
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  row-gap: 4px;
}
.engine-name { font-weight: 600; font-size: 13.5px; }
.engine-badges { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.engine-badges:empty { display: none; }
.engine-meta { font-size: 11.5px; color: var(--ink-faint); font-family: "IBM Plex Mono", monospace; margin-top: 3px; }

.badge {
  font-size: 10px;
  font-family: "IBM Plex Mono", monospace;
  padding: 1.5px 6px;
  border-radius: 4px;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.badge-local { background: var(--good-soft); color: var(--good); }
.badge-external { background: var(--warn-soft); color: var(--warn); }
.badge-free { background: var(--good-soft); color: var(--good); }
.badge-paid { background: var(--surface-2); color: var(--ink-faint); }

.engine-status {
  font-size: 11px;
  font-family: "IBM Plex Mono", monospace;
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--good);
  flex-shrink: 0;
}
.engine-status .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.engine-status.off { color: var(--ink-faint); }
.engine-status.error { color: var(--bad); }

.engine-config {
  margin-top: 10px;
  padding: 14px 16px 16px;
  border: 1px solid var(--surface-2);
  border-radius: 8px;
}

.drag-handle {
  cursor: grab;
  color: var(--ink-faint);
  font-size: 15px;
  flex-shrink: 0;
  /* Without this, a touch-drag starting on the handle is interpreted as
     a page scroll gesture by the browser before JS ever gets a chance to
     preventDefault() it — confirmed the reason drag didn't work at all
     in the mobile PWA (native HTML5 dnd has no touch equivalent, and
     even the pointer-events replacement needs this to stop scroll from
     winning the gesture race). */
  touch-action: none;
}
.drag-handle:active { cursor: grabbing; }

.separator-row {
  cursor: default;
  align-items: center;
  padding: 8px 8px;
  background: var(--surface-2);
  border: 1px dashed var(--border);
}
.separator-label {
  flex: 1;
  font-size: 11.5px;
  color: var(--ink-faint);
  font-family: "IBM Plex Mono", monospace;
  text-align: left;
}

.add-engine-menu {
  position: absolute;
  top: 100%;
  left: 18px;
  margin-top: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--mono-shadow);
  z-index: 10;
  display: flex;
  flex-direction: column;
  min-width: 220px;
  overflow: hidden;
}
.add-engine-opt {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 14px;
  background: none;
  border: none;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
  font-family: inherit;
}
.add-engine-opt:hover { background: var(--surface-2); }

/* ---------- Forms ---------- */
.field { margin-bottom: 12px; }
.field label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-family: "IBM Plex Mono", monospace;
  margin-bottom: 5px;
}
.field input, .field select, .inline-input {
  width: 100%;
  padding: 8px 10px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--ink);
  font-size: 13px;
  font-family: "IBM Plex Mono", monospace;
}
.field input:focus, .field select:focus, .inline-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.field-hint { font-size: 11.5px; color: var(--ink-faint); margin-top: 10px; }
.field-hint a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.field-hint a:hover, .field-hint a:focus-visible { text-decoration-thickness: 2px; }

.field-row { display: flex; gap: 10px; align-items: center; margin-top: 12px; }
.field-row .btn { flex-shrink: 0; padding: 8px 14px; font-size: 12.5px; }

.test-result {
  font-size: 11.5px;
  font-family: "IBM Plex Mono", monospace;
  color: var(--good);
  display: flex;
  align-items: center;
  gap: 5px;
}
.test-result.error { color: var(--bad); }

.tag-input-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 99px;
  background: var(--surface-2);
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  font-weight: 600;
}
.tag-chip .remove {
  cursor: pointer;
  color: var(--ink-faint);
  font-weight: 700;
}
.tag-chip .remove:hover { color: var(--bad); }

/* ---------- Queue table ---------- */
.queue-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  row-gap: 10px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.chip {
  padding: 5px 11px;
  border-radius: 99px;
  border: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-dim);
  cursor: pointer;
  background: var(--surface);
}
.chip.active { background: var(--ink); color: var(--bg); border-color: var(--ink); }

.table-scroll { width: 100%; min-width: 0; overflow-x: auto; }
/* width: max-content, not 100%/min-width: 100% — with table-layout: auto
   (the default), a percentage-based width still lets the browser shrink
   columns down to fit THAT percentage before it ever considers actually
   overflowing, so cell text kept wrapping badly on a narrow screen even
   after switching width: 100% to min-width: 100% (confirmed live on
   History's expanded run-detail table on a phone: "One / Outs", "episo
   / de" wrapped mid-word regardless). max-content tells the browser to
   size the table to its columns' natural, unwrapped widths — small on
   a wide screen (no different from before), but on a narrow screen this
   is what actually makes the table wider than its .table-scroll
   container, which is what gives overflow-x: auto something real to
   scroll to instead of squeezing columns to fit. */
table { width: max-content; min-width: 100%; border-collapse: collapse; font-size: 13px; }

thead th {
  text-align: left;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-family: "IBM Plex Mono", monospace;
  font-weight: 600;
  padding: 10px 20px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

tbody td {
  padding: 11px 20px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: none; }

.title-cell { font-weight: 600; max-width: 300px; }
.title-cell .badge { margin-left: 8px; vertical-align: middle; }
.title-type { font-size: 11.5px; color: var(--ink-faint); font-weight: 400; font-family: "IBM Plex Mono", monospace; margin-top: 1px; }

.lang-tag {
  display: inline-flex;
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--ink-dim);
}

.arrow { color: var(--ink-faint); margin: 0 5px; font-size: 11px; }
.lang-cell { white-space: nowrap; }

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 5px;
  white-space: nowrap;
}
.status-pill .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.status-done { background: var(--good-soft); color: var(--good); }
.status-translating, .status-working { background: var(--accent-soft); color: var(--accent); }
.status-queued, .status-pending { background: var(--surface-2); color: var(--ink-faint); }
.status-failed { background: var(--bad-soft); color: var(--bad); }
.status-skipped_no_source { background: var(--surface-2); color: var(--ink-faint); }
.status-translated_pending_upload { background: var(--warn-soft); color: var(--warn); }

/* ---------- Compare tool diff highlighting ---------- */
/* Both sides are independent translations, not a before/after edit of one
   document — a word that doesn't line up between them isn't "added" or
   "removed," it's just where the two engines chose different wording, so
   both sides use the SAME neutral highlight rather than a red/green
   subtract-vs-add pair. */
.diff-row-changed td { background: var(--surface-2); }
.diff-diff { background: var(--warn-soft); color: var(--warn); border-radius: 3px; }
.diff-same { color: inherit; }
/* Overrides the shared `table { width: max-content }` rule (right for
   short-cell tables like Queue/History, wrong here) — the two subtitle-
   text columns must split the available width evenly and WRAP long
   lines, not keep stretching the table wider to stay unwrapped. */
.diff-table { table-layout: fixed; width: 100%; }
.diff-table td { white-space: normal; overflow-wrap: break-word; vertical-align: top; }

/* ---------- Page tabs ---------- */
.page-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}

.page-tab {
  background: none;
  border: none;
  padding: 9px 14px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-faint);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.page-tab:hover { color: var(--ink); }
.page-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

/* ---------- Simple horizontal bar chart (no charting library) ---------- */
.bar-chart { display: flex; flex-direction: column; gap: 10px; }

.bar-row { display: flex; align-items: center; gap: 10px; }

.bar-row-label {
  width: 140px;
  flex-shrink: 0;
  font-size: 12.5px;
  color: var(--ink-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bar-row-track {
  flex: 1;
  height: 16px;
  background: var(--surface-2);
  border-radius: 4px;
  overflow: hidden;
}

.bar-row-fill {
  height: 100%;
  background: var(--accent);
  border-radius: 4px;
}

.bar-row-fill.bad { background: var(--bad); }
.bar-row-fill.good { background: var(--good); }

.bar-row-value {
  width: 90px;
  flex-shrink: 0;
  text-align: right;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  color: var(--ink-faint);
}

.events-table td.event-type-sending { color: var(--ink-faint); }
.events-table td.event-type-response { color: var(--ink-dim); }
.events-table td.event-type-item_done { color: var(--good); }
.events-table td.event-type-content_blocked_fallback,
.events-table td.event-type-provider_failed_fallback,
.events-table td.event-type-rate_limited_retry,
.events-table td.event-type-watchdog_timeout { color: var(--warn); }
.events-table td.event-type-item_failed { color: var(--bad); }

.row-time { font-family: "IBM Plex Mono", monospace; font-size: 12px; color: var(--ink-faint); white-space: nowrap; }

th.sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
th.sortable:hover { color: var(--ink); }
th.sortable .sort-indicator {
  display: inline-block;
  width: 10px;
  font-size: 10px;
  color: var(--accent);
}
.sortable-chip { cursor: pointer; }
.row-action { text-align: right; white-space: nowrap; }
.link-btn {
  background: none;
  border: none;
  color: var(--accent);
  font-family: "IBM Plex Mono", monospace;
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  padding: 4px 6px;
}
.link-btn:hover { text-decoration: underline; }
.link-btn:disabled { color: var(--ink-faint); cursor: not-allowed; text-decoration: none; }

.empty-state {
  padding: 40px 20px;
  text-align: center;
  color: var(--ink-faint);
  font-size: 13.5px;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--ink-dim);
}
.pagination .btn { padding: 6px 12px; font-size: 12px; }

/* ---------- Toast ---------- */
.toast-container {
  position: fixed;
  bottom: 24px;
  right: 24px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 1000;
  pointer-events: none;
}

.toast {
  background: var(--ink);
  color: var(--bg);
  padding: 11px 16px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  box-shadow: 0 4px 16px rgba(20, 26, 36, 0.18);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.18s ease, transform 0.18s ease;
  max-width: 420px;
  white-space: normal;
  overflow-wrap: break-word;
  pointer-events: auto;
}

.toast-visible {
  opacity: 1;
  transform: translateY(0);
}

.history-run-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  cursor: pointer;
}
.history-run-main { min-width: 0; flex: 1; }
.history-run-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.history-run-title .badge {
  /* Unlike the Engines page's short static labels ("free tier"), this
     badge holds a dynamically-built engine summary that can run long
     (e.g. "Gemini Main gemini-3.1-flash-lite (+1 via Gemini Secondary
     gemini-3.1-flash-lite)") — .badge's shared white-space: nowrap kept
     it from ever wrapping or shrinking, so on a narrow screen it just
     overflowed underneath .history-run-stats instead, visually
     overlapping the files/ok/failed counts. Overridden back to normal
     wrapping specifically for this badge. */
  white-space: normal;
  text-align: left;
}
.history-run-meta { font-size: 12px; color: var(--ink-faint); margin-top: 6px; font-family: "IBM Plex Mono", monospace; }
.history-run-stats { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.history-stat { font-size: 12.5px; color: var(--ink-faint); white-space: nowrap; }
.history-stat b { color: var(--ink); font-weight: 700; }
.history-stat.good b { color: var(--good); }
.history-stat.bad b { color: var(--bad); }
.expand-arrow {
  display: inline-block;
  transition: transform 0.15s ease;
  color: var(--ink-faint);
  font-size: 13px;
}
.expand-arrow.open { transform: rotate(180deg); }
.history-run-body { border-top: 1px solid var(--border); }

.error-cell {
  cursor: pointer;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 2px;
  /* Overrides the nowrap it inherits from .row-time — a long, space-free
     error message (e.g. a URL-ish string) with nowrap and no room to
     scroll had nowhere to go but wrap one character per line. Capping the
     width and allowing normal wrapping keeps the row a sane height
     instead of blowing out the column or the table's overall width. */
  white-space: normal;
  overflow-wrap: break-word;
  max-width: 200px;
}
.error-cell:hover { color: var(--ink); }

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 20px;
}
.modal-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  max-width: 720px;
  width: 100%;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.modal-head h3 { margin: 0; font-size: 15px; }
.modal-close {
  background: none;
  border: none;
  color: var(--ink-faint);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 8px;
}
.modal-close:hover { color: var(--ink); }
.modal-body {
  padding: 16px 20px;
  overflow-y: auto;
}
.modal-body pre {
  white-space: pre-wrap;
  word-break: break-word;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12.5px;
  margin: 0;
  color: var(--ink);
}

/* ---------- Mobile responsive overrides ---------- */
/* Kept in THIS file (not base.css), which loads AFTER base.css (see
   base.html's <link> order) — with equal selector specificity, the
   stylesheet that loads LAST always wins the cascade regardless of
   which file's media query actually matches at the current viewport
   width. Confirmed live: an earlier version of the .stats override
   below lived in base.css and was silently beaten by THIS file's own
   unrelated, non-media-queried 4-column .stats rule (line ~275) at
   every phone width, since components.css loaded second. */
@media (max-width: 980px) {
  .shell,
  .shell.sidebar-collapsed {
    grid-template-columns: minmax(0, 1fr);
    /* Below this breakpoint .shell stacks into a single column — sidebar
       row, then main row — and CSS Grid's default row sizing stretches
       any leftover height onto that content, not just the LAST row.
       min-height: 100vh (needed on desktop so a short page's sidebar
       still reaches the bottom of the viewport) left a real gap of
       empty space between the sidebar bar and main's actual content on
       a short mobile page — confirmed live via DevTools: main's own box
       was stretched far taller than its content needed. A mobile page
       should just be as tall as its content and scroll normally. */
    min-height: 0;
  }
  main { width: 100%; padding: 20px 16px 40px; }
  .grid-2 { grid-template-columns: 1fr; }
  .stats { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 520px) {
  /* Real phone widths (not just tablet/narrow-desktop, the 980px
     breakpoint above) — 2 stat cards per row is still cramped once each
     card's own padding/label/number is accounted for at ~375-430px. */
  .stats { grid-template-columns: 1fr; }
  .page-head {
    flex-wrap: wrap;
    align-items: flex-start;
  }
  .panel-head {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .progress-meta {
    flex-wrap: wrap;
    row-gap: 6px;
  }
  .engine-opt .engine-info {
    /* Still needs room to breathe against the status pills/arrow
       siblings sharing the row, but no longer forced to its own full
       line — a long instance name/model string wraps within itself
       (min-width: 0 lets it shrink below its own text's natural width
       instead of forcing the row wider than the viewport). */
    min-width: 0;
  }
  .engine-status,
  .expand-arrow {
    flex-shrink: 0;
  }
  .engine-name-row {
    /* On desktop, name + badges share one row (plenty of horizontal
       room there) — on a phone-width screen, a long instance name plus
       2 badges genuinely doesn't fit on one line, so badges are forced
       onto their own row below the name instead of both fighting for
       space on one cramped line. */
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
  }
  .history-run-head {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .history-run-stats {
    /* flex-shrink: 0 (the base rule) is what's needed on a wide screen
       so this block never gets squeezed illegibly thin — but combined
       with .history-run-title's badge overflowing instead of wrapping
       (now fixed above), it was also what forced this block to sit
       pinned at a fixed width fighting for the same row as a title that
       genuinely needs more room on a narrow screen. flex-basis: 100%
       lets it drop to its own full-width row below the title via the
       flex-wrap above once there isn't enough room, instead of both
       blocks cramming onto one row. */
    flex-basis: 100%;
    justify-content: flex-end;
  }
}
