/* afirka data viewer, minimal, scannable */
@import url("/static/vendor/exo2.css");   /* self-hosted Exo 2, app-wide */
:root {
  --bg: #fafaf7;
  --fg: #1f1f1f;
  --dim: #6e6e6e;
  --line: #e2e2dd;
  --accent: #2f6f4f;       /* afirka green */
  --accent-soft: #e1efe7;
  --warn: #b75e2a;
  --eval: #7a3aa6;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --sans: "Exo 2", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --app-fs: 17px;          /* app-wide base, compact/legible dashboard density */

  /* ── ONE shared card + ground system (Tabler-derived). Every page -
     admin, review workspace, reviewer, draws these instead of its own.
     Change here to reskin the whole app. ── */
  --ground: #f5f6f8;                      /* page background under cards */
  --card: #ffffff;                        /* card surface */
  --card-line: rgba(4, 32, 69, 0.08);     /* card / table border */
  --card-radius: 10px;
  --card-shadow: 0 1px 2px rgba(15, 23, 42, 0.04),
                 0 1px 3px rgba(15, 23, 42, 0.03);
  --card-shadow-md: 0 4px 12px rgba(15, 23, 42, 0.07),
                    0 1px 3px rgba(15, 23, 42, 0.04);

  /* One universal content width, every nav bar inner and page body is
     centred to this, so all pages line up at the same size. */
  --content-max: 1280px;

  /* Fixed-footer height, the footer, the body's bottom reserve, and any
     bottom-sticky bar (rate-view action bar) all reference this so they
     sit flush with no gap for content to spill through. */
  --footer-h: 42px;
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
/* Always show the vertical scrollbar (and reserve its gutter) so centred
   (1280) content doesn't jump sideways when navigating between a page
   that scrolls and one that doesn't (/review ↔ /review/manage/batches). */
html { overflow-y: scroll; scrollbar-gutter: stable; }
body {
  font-family: var(--sans);
  background: var(--bg); color: var(--fg);
  font-size: var(--app-fs); line-height: 1.55;
}
/* Opt-in grey ground for card-based pages so white cards read against it,
   filling the full viewport (behind the fixed footer too). */
body.page-ground { background: var(--ground); }

header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 18px; border-bottom: 1px solid var(--line);
  background: white;
  /* Own stacking context above page content so the menu / user / storage
     dropdowns always overlay cards (below modals at z-index 200). */
  position: relative; z-index: 90;
}
.brand { font-weight: 600; font-size: 16px; display: flex; align-items: center; gap: 10px; }
.brand-dim { color: var(--dim); font-weight: 400; margin-left: 4px; }

/* Brand wordmark, name + optional tagline. Tagline lives next to the
   name on a small dim line. Empty tagline collapses out of the layout
   so the name sits exactly where it would without one. */
/* Two-line brand lockup: wordmark on top, tagline as a small muted line
   beneath it. Reads as a subtitle instead of crowding the name inline -
   and stays tidy as the app font scales up. */
.brand-link {
  display: inline-flex; flex-direction: column; align-items: flex-start;
  gap: 0; line-height: 1.1;
  text-decoration: none; color: var(--fg);
  border-bottom: 2px solid transparent;
}
.brand-link:hover .brand-name { color: var(--accent); }
.brand-name {
  font-weight: 600; font-size: 17px; letter-spacing: -0.3px;
  color: var(--fg);
}
.brand-tagline {
  font-size: 10.5px; color: var(--dim); font-weight: 400;
  letter-spacing: 0.02em; font-style: normal;
  margin-top: 1px;
}
.brand-tagline:empty { display: none; }
@media (max-width: 640px) {
  .brand-tagline { display: none; }   /* keep the header tight on mobile */
}
/* Per-page suffix (/ datasets, / admin / users, etc.), same look as
   the old .brand-dim but tied to a specific element so each page can
   stay in sync without competing rules. */
.brand-suffix {
  color: var(--fg); font-weight: 500; font-size: 16px; margin-left: 18px;
  align-self: center;   /* vertically centered against the 2-line brand */
}
.brand-suffix:empty { display: none; }
.brand-suffix .bc-link {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.brand-suffix .bc-link:hover { border-bottom-color: currentColor; }
.brand-suffix code {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 13.5px;
  padding: 1px 5px;
  background: rgba(0, 0, 0, 0.06);
  border-radius: 4px;
}

/* Datasets sub-nav (SFT / CPT / Stats), lives at the top of every
   datasets-* page so users can jump tracks without bouncing through a
   registry dropdown. */
.ds-subnav {
  display: inline-flex; gap: 4px;
}
.ds-subnav-link {
  font-size: 12px; padding: 4px 10px; border-radius: 999px;
  color: var(--dim); text-decoration: none; border: 1px solid transparent;
  letter-spacing: 0.3px; font-weight: 500;
}
.ds-subnav-link:hover { color: var(--fg); border-color: var(--line); }
.ds-subnav-link.active {
  background: var(--accent); color: white; border-color: var(--accent);
}

/* Sub-nav rendered at the top of the sidebar filters block, track
   switcher (SFT/CPT/Stats) sits above the search/filter controls so
   it's the first thing users see and reach. */
.ds-subnav-sidebar {
  display: flex; gap: 4px; justify-content: space-between;
  padding-bottom: 8px; margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
}
.ds-subnav-sidebar .ds-subnav-link {
  flex: 1; text-align: center;
}

.sidebar-toggle {
  display: none;                          /* mobile only, JS shows it */
  background: none; border: 1px solid var(--line); border-radius: 4px;
  padding: 2px 8px; cursor: pointer; font-size: 16px; line-height: 1;
}

.header-right { display: flex; align-items: center; gap: 10px; position: relative; }

/* Compact storage chip in the header */
.storage-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 4px;
  background: var(--accent-soft); color: var(--accent);
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  cursor: default;
}
.storage-chip.warn { background: #fdf1e3; color: var(--warn); }
.storage-chip.danger { background: #fbe3e3; color: #a02828; }
.storage-chip svg { width: 11px; height: 11px; }
.storage-chip { position: relative; }

/* Header icon-button, same height as .storage-chip so they line up
   visually. Used for /sync; future icon entry-points should pile on
   the same class. */
.header-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 22px;
  border-radius: 4px;
  background: var(--accent-soft); color: var(--accent);
  text-decoration: none;
  transition: background 0.12s ease;
}
.header-icon-btn:hover { background: var(--accent); color: white; }
.header-icon-btn svg { width: 13px; height: 13px; }
.header-icon-btn[aria-current="page"] {
  background: var(--accent); color: white;
}
.storage-popover {
  display: none;
  position: absolute; top: calc(100% + 6px); right: 0;
  min-width: 240px; padding: 10px 14px;
  background: white; border: 1px solid var(--line); border-radius: 6px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  z-index: 110;
  font-family: var(--sans); color: var(--fg);
}
.storage-chip:hover .storage-popover,
.storage-chip:focus-within .storage-popover { display: block; }
.storage-popover .label {
  text-transform: uppercase; letter-spacing: 0.4px; font-size: 10px;
  color: var(--dim); margin-bottom: 6px; font-weight: 600;
}
.storage-popover { min-width: 320px; }
.storage-popover dl {
  display: grid; grid-template-columns: auto 1fr; gap: 2px 10px;
  margin: 0; font-family: var(--mono); font-size: 11px;
}
.storage-popover dt { color: var(--dim); }
.storage-popover dt.dt-section {
  color: var(--fg); font-weight: 600; padding-top: 6px;
  border-top: 1px solid var(--line); margin-top: 4px;
}
.storage-popover dd { margin: 0; }
.storage-popover .storage-where { color: var(--accent, #2563eb); text-decoration: none; }
.storage-popover .storage-where:hover { text-decoration: underline; }
.storage-popover .storage-kind {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  border-radius: 3px; background: rgba(37, 99, 235, 0.1);
  color: var(--accent, #2563eb); font-size: 10px; vertical-align: middle;
}
.storage-popover .storage-note { color: var(--dim); font-size: 10px; }
.sidebar-toggle svg { width: 14px; height: 14px; }

/* User menu trigger + dropdown */
.user-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 1px solid var(--line); border-radius: 4px;
  padding: 4px 10px; cursor: pointer; font-family: inherit; font-size: 12px;
  color: var(--fg);
}
.user-trigger:hover { border-color: var(--accent); }
.user-trigger .avatar {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent); color: white;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600; text-transform: uppercase;
}
.user-trigger .me-email { font-family: var(--mono); font-size: 12px; }
.user-trigger .caret { color: var(--dim); font-size: 9px; margin-left: 2px; }

.user-menu {
  position: absolute; top: calc(100% + 6px); right: 0;
  background: white; border: 1px solid var(--line); border-radius: 6px;
  min-width: 240px; padding: 8px 0; z-index: 100;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  display: none;
}
.user-menu.open { display: block; }
.user-menu .menu-header {
  padding: 8px 14px 10px 14px; border-bottom: 1px solid var(--line);
}
.user-menu .menu-header .menu-email {
  font-family: var(--mono); font-size: 12px; word-break: break-all;
}
.user-menu .menu-header .menu-role {
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 11px; margin-top: 4px;
}
.user-menu .menu-section {
  padding: 8px 14px; border-bottom: 1px solid var(--line);
  font-size: 12px; color: var(--dim);
}
.user-menu .menu-section:last-of-type { border-bottom: none; }
.user-menu .menu-section .label {
  text-transform: uppercase; letter-spacing: 0.4px; font-size: 10px;
  color: var(--dim); margin-bottom: 4px; font-weight: 600;
}
.user-menu .menu-section dl {
  display: grid; grid-template-columns: auto 1fr; gap: 2px 10px;
  margin: 0; font-family: var(--mono); font-size: 11px; color: var(--fg);
}
.user-menu .menu-section dt { color: var(--dim); }
.user-menu .menu-actions { padding: 4px 0; }
.user-menu .menu-actions a, .user-menu .menu-actions button {
  display: block; width: 100%; text-align: left;
  padding: 7px 14px; background: none; border: none; cursor: pointer;
  font-family: inherit; font-size: 13px; color: var(--fg); text-decoration: none;
}
.user-menu .menu-actions a:hover, .user-menu .menu-actions button:hover {
  background: var(--accent-soft); color: var(--accent);
}

/* Filter meta moved from header to sidebar */
.filter-meta {
  font-size: 13px; color: var(--fg); font-family: var(--mono);
  margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--line);
}

.extract-btn {
  padding: 6px 14px; background: var(--accent); color: white;
  border: none; border-radius: 4px; cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: 500;
}
.extract-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.extract-btn.cancel { background: #cb4646; }
.extract-btn.secondary { background: white; color: var(--accent); border: 1px solid var(--accent); }
.extract-btn.danger {
  background: white; color: #a02828; border: 1px solid #f4dada;
  font-weight: 500;
}
.extract-btn.danger:hover:not(:disabled) { background: #fff2f2; border-color: #cb4646; }

/* Danger zone inside the data-ops modal, collapsed by default so it's
   not a footgun, but one click away for admins who need it. */
.run-danger {
  margin-top: 8px; padding-top: 10px; border-top: 1px dashed var(--line);
}
.run-danger > summary {
  cursor: pointer; color: #a02828; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.4px; padding: 4px 0;
}
.run-danger > summary:hover { color: #cb4646; }
.run-danger-actions {
  display: flex; gap: 8px; flex-wrap: wrap; margin: 8px 0;
}

.extract-controls {
  display: inline-flex; align-items: center; gap: 8px;
}

/* Approval state in the dataset summary */
.pill.approval-ok       { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
.pill.approval-stale    { background: #fdf1e3; color: var(--warn); font-weight: 500; }
.pill.approval-missing  { background: var(--ground); color: var(--dim); }
.force-toggle {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: var(--dim); cursor: pointer; user-select: none;
}
.force-toggle input[type="checkbox"] { margin: 0; cursor: pointer; }
.force-toggle:has(input:checked) { color: var(--warn); font-weight: 500; }

/* Global extract-lock banner (shows for everyone when anyone is extracting) */
.global-lock-banner {
  padding: 8px 18px; background: #fdf1e3; color: var(--warn);
  font-size: 13px; border-bottom: 1px solid #f0d8b0;
  display: flex; align-items: center; gap: 10px;
}
.global-lock-banner a {
  color: var(--accent); text-decoration: underline; font-weight: 500;
}

/* The "Run" panel inside the job modal */
.run-panel { display: flex; flex-direction: column; gap: 14px; }
.run-panel .run-actions {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding-bottom: 12px; border-bottom: 1px solid var(--line);
}
.run-panel .run-hint { color: var(--dim); font-size: 12px; margin: 0; }

.job-panel {
  border: 1px solid var(--accent); border-radius: 6px; padding: 14px 16px;
  background: var(--accent-soft); margin-bottom: 0;
}
.job-panel.failed { border-color: #cb4646; background: #fbe3e3; }
.job-panel.cancelled { border-color: #888; background: var(--ground); }
.job-panel .job-head {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: 8px;
}
.job-panel .job-head .status-pill {
  display: inline-block; padding: 2px 8px; border-radius: 3px;
  background: var(--accent); color: white; font-size: 11px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.4px;
}
.job-panel.failed .status-pill { background: #cb4646; }
.job-panel.cancelled .status-pill { background: #888; }
.job-panel.done .status-pill { background: var(--accent); }
.job-panel .job-counters {
  font-family: var(--mono); font-size: 13px; color: var(--fg);
  margin: 6px 0;
}
.job-panel .job-log {
  font-family: var(--mono); font-size: 11px;
  background: #1f1f1f; color: #d0d0c8; padding: 8px 10px; border-radius: 4px;
  max-height: 220px; overflow-y: auto; white-space: pre; line-height: 1.4;
  margin-top: 8px;
}

/* Tool pages (datasets / cpt / stats) lay the body out as a flex column so
   the master-detail grid fills whatever height is left below the header +
   subnav, and the global body padding-bottom (52px) keeps it clear of the
   fixed footer, no fragile per-chrome height math. */
body:has(> main.layout) { display: flex; flex-direction: column; min-height: 100vh; }
.layout {
  display: grid;
  grid-template-columns: 320px 1fr;
  grid-template-rows: 1fr;
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
}

.sidebar {
  border-right: 1px solid var(--card-line);
  background: var(--card);
  display: flex; flex-direction: column;
  overflow: hidden;
  grid-column: 1;                         /* pin: any extra layout child can't push us */
  grid-row: 1;
}

.detail { grid-column: 2; grid-row: 1; }

.sidebar-backdrop {
  display: none;                          /* mobile only, toggled in @media block below */
  position: fixed; inset: 53px 0 0 0;
  background: rgba(0, 0, 0, 0.4); z-index: 50;
}

/* ---- responsive: tablet & smaller -------------------------------------- */
@media (max-width: 900px) {
  .sidebar-toggle { display: inline-flex; }
  .layout { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; top: 53px; bottom: 0; left: 0;
    width: 84%; max-width: 340px;
    z-index: 60;
    transform: translateX(-100%);
    transition: transform 180ms ease;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.08);
    grid-column: auto; grid-row: auto;     /* no longer in the grid */
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-backdrop.open { display: block; }
  .detail { grid-column: 1; grid-row: 1; padding: 16px; }
}

/* ---- responsive: phone ------------------------------------------------- */
@media (max-width: 500px) {
  header { padding: 10px 12px; }
  .brand { font-size: 14px; }
  /* Keep the breadcrumb (.brand-suffix is relocated into .breadcrumb-bar);
     only the tagline/dim wordmark extras collapse on phones. */
  .brand-dim, .brand-tagline { display: none; }
  .breadcrumb-bar { padding: 6px 12px; }
  .storage-chip { font-size: 10px; padding: 2px 6px; }
  .detail h1 { font-size: 18px; }
  .stats-grid { grid-template-columns: 1fr; }
  .summary { gap: 6px; }
  .summary .pill { font-size: 12px; padding: 1px 6px; }
}
.filters {
  padding: 12px; border-bottom: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 8px;
}
.filter-search-row {
  display: flex; align-items: center; gap: 8px;
}
.filters input[type="search"] {
  width: 100%; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: 6px;
  font-size: 13px; font-family: inherit;
}
.filter-search-row input[type="search"] {
  flex: 1 1 auto; min-width: 0;
}
.filter-search-row select {
  flex: 0 0 124px; width: 124px; padding: 8px 9px;
  border: 1px solid var(--line); border-radius: 6px;
  font-size: 12px; font-family: inherit; background: white; color: var(--fg);
}
.filter-select-label {
  display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: center;
  font-size: 12px; color: var(--dim);
}
.filter-select-label select {
  width: 100%; padding: 7px 9px;
  border: 1px solid var(--line); border-radius: 6px;
  font-size: 13px; font-family: inherit; background: white; color: var(--fg);
}
.filter-chips {
  display: flex; align-items: center; gap: 10px;
  font-size: 12px; color: var(--dim);
}
.filter-chips label {
  display: flex; align-items: center; gap: 4px;
  cursor: pointer; white-space: nowrap;
}
.filter-chip-select { margin-left: auto; }
.filter-chip-select select {
  width: 124px; padding: 5px 8px;
  border: 1px solid var(--line); border-radius: 6px;
  font-size: 12px; font-family: inherit; background: white; color: var(--fg);
}

.dataset-list {
  list-style: none; padding: 0; margin: 0;
  overflow-y: auto; flex: 1;
}

/* Approve-samples banner at the top of the samples tab. The point of
   the gate is "I reviewed these and they look right", so the button
   lives here, where the user is looking at the actual data. */
.samples-approve-banner {
  display: flex; gap: 16px; align-items: center; justify-content: space-between;
  padding: 12px 16px; margin-bottom: 14px;
  background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 6px;
}
.samples-approve-banner.approved {
  background: #f0f8f3; border-color: var(--accent);
}
.samples-approve-banner .approve-text {
  font-size: 13px; color: var(--fg); line-height: 1.5;
}
.samples-approve-banner button { white-space: nowrap; }
@media (max-width: 700px) {
  .samples-approve-banner { flex-direction: column; align-items: stretch; }
}

/* Samples-tab count chip, preview sidecars are capped at 10 rows so
   the tab fits on one page and no pagination is needed. The chip just
   tells you how many rows you're looking at. */
.samples-count {
  font-size: 11px; color: var(--dim); font-family: var(--mono);
  padding: 4px 0 10px; letter-spacing: 0.3px;
}

/* Sidebar action button ("+ add dataset") */
.sidebar-action-btn {
  background: var(--accent); color: white; border: none; border-radius: 4px;
  padding: 6px 10px; cursor: pointer; font-family: inherit; font-size: 12px;
  font-weight: 500;
}
.sidebar-action-btn:hover { background: #245a3f; }
/* Anchor variant (e.g. the CSV export link), same pill, secondary look. */
.sidebar-action-link {
  display: block; margin-top: 6px; text-align: center; text-decoration: none;
  background: transparent; color: var(--accent);
  border: 1px solid var(--accent);
}
.sidebar-action-link:hover { background: var(--accent); color: white; }

/* Reusable table pagination bar (paginate.js). */
.pg-bar {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 12px 18px;
  border-top: 1px solid var(--line);
  font-size: 14px; color: var(--dim);
}
.pg-bar .pg-per { display: inline-flex; align-items: center; gap: 8px; }
.pg-bar .pg-select {
  font: inherit; font-size: 14px;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel, #fff); color: var(--fg); cursor: pointer;
}
.pg-bar .pg-info { font-variant-numeric: tabular-nums; font-size: 14px; }
.pg-bar .pg-nav { margin-left: auto; display: inline-flex; gap: 8px; }
.pg-bar .pg-nav button {
  font: inherit; font-size: 14px; font-weight: 600;
  padding: 7px 16px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel, #fff); color: var(--fg); cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.pg-bar .pg-nav button:hover:not(:disabled) {
  border-color: var(--accent); color: var(--accent); background: var(--accent-soft);
}
.pg-bar .pg-nav button:disabled { opacity: 0.4; cursor: not-allowed; }

/* Reusable inline help affordance, a small "i" badge that reveals a
   description on hover/focus. Replaces always-on intro paragraphs so
   headers stay clean. Use: <span class="help-icon" data-tip="…" tabindex="0">i</span>
   (multi-line tips: put real newlines in data-tip). */
.help-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--accent);
  background: var(--accent-soft, rgba(47, 111, 79, 0.12));
  color: var(--accent);
  font-size: 11px; font-weight: 700; font-style: normal;
  font-family: var(--sans, inherit);
  line-height: 1; cursor: help; vertical-align: middle;
  position: relative;
  margin-left: 10px;   /* breathe away from the title it annotates */
  transition: filter 100ms ease;
}
.help-icon:hover, .help-icon:focus-visible {
  filter: brightness(0.92);
  outline: none;
}
.help-icon::after {
  content: attr(data-tip);
  position: absolute; top: calc(100% + 8px); left: 0;
  width: max-content; max-width: 360px;
  background: #21201d; color: #f4f2ec;
  padding: 11px 14px; border-radius: 8px;
  font-size: 15px; font-weight: 400; font-style: normal;
  line-height: 1.5; letter-spacing: normal; text-transform: none;
  white-space: pre-line; text-align: left;
  opacity: 0; pointer-events: none; transform: translateY(-3px);
  transition: opacity 120ms ease, transform 120ms ease;
  z-index: 71; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
}
.help-icon:hover, .help-icon:focus-visible { z-index: 71; }
.help-icon::before {          /* little pointer */
  content: ""; position: absolute; top: calc(100% + 3px); left: 6px;
  border: 5px solid transparent; border-bottom-color: #21201d;
  opacity: 0; pointer-events: none; transition: opacity 120ms ease;
  z-index: 72;
}
.help-icon:hover::after, .help-icon:focus-visible::after,
.help-icon:hover::before, .help-icon:focus-visible::before {
  opacity: 1; transform: translateY(0);
}

/* Modal */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); z-index: 200;
}
.modal {
  position: fixed; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  background: white; border-radius: 8px; z-index: 201;
  width: min(640px, 92vw); max-height: 88vh;
  display: flex; flex-direction: column;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
}
.modal-wide { width: min(900px, 94vw); }

/* Canonical "create / edit" dialog size, applied to every modal whose
   purpose is to take input (add dataset, add tokenizer, add/edit
   handler, invite user, edit roles). Pinned width AND height so the
   dialog doesn't bounce as the form's content density changes (eg
   add-dataset wizard steps, add-handler with CodeMirror), and so the
   shape stays consistent across the app. View-only dialogs (help,
   audit-payload, job-detail) keep their own sizing and may be
   .modal-wide or .modal as appropriate. */
.modal-form {
  width: min(900px, 94vw);
  height: min(720px, 88vh);
  max-height: 88vh;
}
/* Universal: any element with the HTML `hidden` attribute MUST be
   display:none, regardless of any later `display: grid/flex/block` rule
   the element's class might set. The browser's UA stylesheet has the
   right default, but a more-specific class rule typically overrides it.
   The !important here brings hidden back to its intended behaviour. */
[hidden] { display: none !important; }

/* Inline link-style button for opening help inside other UI */
.link-btn {
  background: none; border: none; color: var(--accent); cursor: pointer;
  font: inherit; padding: 0; text-decoration: underline;
}
.link-btn:hover { color: #245a3f; }

/* Rendered-markdown styling, keep close to the rest of the app's tone */
.markdown-body {
  font-size: 14px; line-height: 1.65;
}
.markdown-body h1 { font-size: 22px; margin: 0 0 14px 0; }
.markdown-body h2 { font-size: 17px; margin: 26px 0 10px 0; padding-top: 12px; border-top: 1px solid var(--line); }
.markdown-body h3 { font-size: 14px; margin: 18px 0 8px 0; color: var(--accent); }
.markdown-body h4 { font-size: 13px; margin: 14px 0 6px 0; }
.markdown-body p { margin: 8px 0; }
.markdown-body code {
  font-family: var(--mono); font-size: 12.5px;
  background: var(--ground); padding: 1px 5px; border-radius: 3px;
}
.markdown-body pre {
  background: #1f1f1f; color: #d0d0c8; padding: 12px 14px; border-radius: 5px;
  overflow-x: auto; font-family: var(--mono); font-size: 12px; line-height: 1.5;
}
.markdown-body pre code { background: none; color: inherit; padding: 0; font-size: 12px; }
.markdown-body ul, .markdown-body ol { padding-left: 22px; margin: 8px 0; }
.markdown-body li { margin: 3px 0; }
.markdown-body table {
  border-collapse: collapse; width: 100%; margin: 10px 0;
  font-size: 12.5px;
}
.markdown-body th, .markdown-body td {
  border: 1px solid var(--line); padding: 6px 10px; text-align: left;
}
.markdown-body th { background: var(--ground); font-weight: 600; }
.markdown-body blockquote {
  padding: 8px 14px; margin: 10px 0;
  color: var(--dim); background: var(--accent-soft);
  border-radius: 6px;
}
.markdown-body a { color: var(--accent); }
.modal-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 16px 20px; border-bottom: 1px solid var(--line);
}
.modal-head h2 { margin: 0; font-size: 16px; }
.modal-close {
  background: none; border: none; cursor: pointer; font-size: 18px;
  color: var(--dim); padding: 0 4px; line-height: 1;
}
.modal-close:hover { color: var(--fg); }
.modal-body { padding: 16px 20px; overflow-y: auto; flex: 1; }
.modal-hint {
  font-size: 12px; color: var(--dim); margin: 0 0 16px 0; line-height: 1.5;
}
.modal-hint a { color: var(--accent); }
.modal-foot {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 20px; border-top: 1px solid var(--line);
}
.modal-error {
  margin-top: 10px; padding: 8px 12px; background: #fbe3e3; color: #a02828;
  border-radius: 4px; font-size: 12px; display: none;
}
.modal-error.show { display: block; }

/* Add-dataset form, section cards, clear visual hierarchy */
.add-dataset-form {
  display: flex; flex-direction: column; gap: 14px;
}
.add-dataset-form .form-card {
  border: 1px solid var(--line); border-radius: 8px; background: white;
  overflow: hidden;
}
.add-dataset-form .form-card .card-head {
  padding: 12px 16px;
  background: #fafaf7;
  border-bottom: 1px solid var(--line);
}
.add-dataset-form .form-card .card-head h3 {
  margin: 0; font-size: 14px; color: var(--fg); font-weight: 600;
}
.add-dataset-form .form-card .card-head p {
  margin: 3px 0 0 0; font-size: 12px; color: var(--dim);
}
.add-dataset-form .form-card .card-body {
  padding: 14px 16px; display: flex; flex-direction: column; gap: 12px;
}

.add-dataset-form .field {
  display: flex; flex-direction: column; gap: 4px;
}
.add-dataset-form .field label {
  font-size: 12px; font-weight: 500; color: var(--fg);
  display: inline-flex; align-items: center; gap: 6px;
}
.add-dataset-form .field input,
.add-dataset-form .field select,
.add-dataset-form .field textarea {
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 4px;
  font-family: inherit; font-size: 13px;
  background: white; color: var(--fg); width: 100%;
  box-sizing: border-box;
}
.add-dataset-form .field input:focus,
.add-dataset-form .field select:focus,
.add-dataset-form .field textarea:focus {
  outline: none; border-color: var(--accent);
}
.add-dataset-form .field textarea {
  font-family: var(--mono); font-size: 12px; min-height: 60px; resize: vertical;
}
.add-dataset-form .field input.invalid {
  border-color: #cb4646;
}
.add-dataset-form .field .hint, .add-dataset-form .field small.hint {
  font-size: 11px; color: var(--dim); margin-top: 0;
}
.add-dataset-form .field .field-error {
  color: #cb4646; font-size: 11px; min-height: 0;
}
.add-dataset-form .row-2 {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
}

/* Input + inline action button (used by hf_id "Inspect fields") */
.add-dataset-form .inline-input {
  display: flex; gap: 6px; align-items: stretch;
}
.add-dataset-form .inline-input input { flex: 1; }
.add-dataset-form .inline-btn {
  padding: 6px 12px; white-space: nowrap;
  background: white; color: var(--accent);
  border: 1px solid var(--accent); border-radius: 4px; cursor: pointer;
  font: inherit; font-size: 12px;
}
.add-dataset-form .inline-btn:hover { background: var(--accent-soft); }
.add-dataset-form .inline-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Field-inspector results card, shown after clicking "Inspect fields".
   Each field renders as a click-to-copy chip so the curator can paste
   the column name into the task.mapping textarea. */
.add-dataset-form .field-card {
  margin-top: 6px; padding: 10px 12px;
  background: var(--ground); border: 1px solid var(--line); border-radius: 6px;
  font-size: 12px;
}
.add-dataset-form .field-card .card-head-row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px; margin-bottom: 8px;
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.4px;
  color: var(--dim);
}
.add-dataset-form .field-card .card-head-row .dim {
  font-family: var(--mono); text-transform: none; letter-spacing: 0;
  font-size: 10.5px;
}
.add-dataset-form .field-card .hint.err { color: #a02828; }
.add-dataset-form .field-card .hint.dim { color: var(--dim); margin-bottom: 6px; display: block; }
.add-dataset-form .field-card details > summary {
  cursor: pointer; padding: 4px 0; color: var(--dim); font-size: 11px;
}
.add-dataset-form .field-card details + details { margin-top: 4px; }

.add-dataset-form .field-chips {
  display: flex; flex-wrap: wrap; gap: 4px;
}
/* Inspect → configs section: sits above the fields list, divides from
   it with a hairline so "which configs exist" reads separately from
   "which columns exist". */
.add-dataset-form .inspect-configs {
  margin-bottom: 10px; padding-bottom: 10px;
  border-bottom: 1px dashed var(--line);
}
.add-dataset-form .cfg-chip code { color: var(--accent); }
.add-dataset-form .field-chip {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 3px 8px; background: white; border: 1px solid var(--line);
  border-radius: 12px; cursor: copy; font: inherit;
  text-align: left;
}
.add-dataset-form .field-chip:hover {
  border-color: var(--accent); background: var(--accent-soft);
}
.add-dataset-form .field-chip.copied {
  border-color: var(--accent); background: var(--accent); color: white;
}
.add-dataset-form .field-chip.copied code,
.add-dataset-form .field-chip.copied .field-chip-type,
.add-dataset-form .field-chip.copied .field-chip-info { color: white; }
.add-dataset-form .field-chip code {
  background: none; padding: 0; font-size: 12px; color: var(--fg);
}
.add-dataset-form .field-chip-type {
  font-size: 10px; color: var(--accent);
  text-transform: lowercase; letter-spacing: 0.2px;
}
.add-dataset-form .field-chip-info {
  font-size: 10px; color: var(--dim); font-style: italic;
}

/* "optional" tag, flips the convention from required-asterisk to
   optional-tag, which is less noisy when most fields are required. */
.optional-tag {
  display: inline-block;
  padding: 1px 6px;
  background: var(--ground); color: var(--dim);
  border-radius: 3px; font-size: 10px; font-weight: 400;
  text-transform: uppercase; letter-spacing: 0.4px;
}

/* Mapping example, inline link to fill the textarea */
.mapping-example {
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px; flex-wrap: wrap;
}
.mapping-example .hint { flex: 1; min-width: 0; }
.mapping-example code {
  font-family: var(--mono); font-size: 11px;
  background: var(--ground); padding: 1px 5px; border-radius: 3px;
}
.dataset-list li {
  padding: 10px 14px; cursor: pointer;
  border-bottom: 1px solid #f0f1f3;
  border-left: 3px solid transparent;
  transition: background 100ms ease, border-color 100ms ease;
}
.dataset-list li:hover { background: #fafbfc; }
.dataset-list li.active {
  background: var(--accent-soft); color: var(--accent); font-weight: 600;
  border-left-color: var(--accent);
}
.dataset-list li.not-extracted { opacity: 0.6; }
.dataset-list li.not-extracted:hover { opacity: 1; }
.dataset-list .ds-id { font-weight: 500; font-family: var(--mono); font-size: 13px;
                       display: flex; align-items: center; gap: 6px; }
.ds-audit-btn {
  margin-left: auto;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--dim);
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  padding: 2px 9px;
  border-radius: 999px;
  cursor: pointer;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  transition: all 100ms ease;
}
.ds-audit-btn:hover {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}

/* Audit modal, choice rows + history list */
.audit-choices {
  display: flex; flex-direction: column;
  gap: 6px; margin: 12px 0;
}
.audit-choice {
  display: flex; align-items: flex-start; gap: 9px;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
  cursor: pointer;
  background: #fff;
  font-size: 13.5px;
  transition: border-color 100ms ease, background 100ms ease;
}
.audit-choice:hover { background: #fafaf7; }
.audit-choice:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.audit-choice input { margin-top: 2px; flex-shrink: 0; }
.audit-history {
  margin-top: 18px;
  border-top: 1px dashed var(--line);
  padding-top: 14px;
}
.audit-history h4 {
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim);
}
.audit-history ul { list-style: none; padding: 0; margin: 0;
                    display: flex; flex-direction: column; gap: 10px; }
.audit-history li { font-size: 12.5px; }
.audit-pill {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px 7px;
  border-radius: 999px;
  margin-right: 6px;
}
.audit-pill.audit-accept           { background: #e1efe7; color: #2f6f4f; }
.audit-pill.audit-edit-and-accept  { background: #fbf3cf; color: #8a6a0f; }
.audit-pill.audit-reject           { background: #fbeae5; color: #b04030; }
.audit-by   { color: var(--fg); font-weight: 500; margin-right: 6px; }
.audit-when { color: var(--dim); font-family: var(--mono); font-size: 11px; }
.audit-comment {
  margin: 4px 0 0;
  padding: 6px 10px;
  background: #fafaf7;
  border-radius: 6px;
  color: var(--fg);
  font-size: 12.5px;
}

/* Prior-audit banner shown at the top of a re-audit modal. */
.audit-prior {
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 8px;
}
.audit-prior-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.audit-prior-comment {
  margin: 8px 0 4px;
  font-size: 13px; font-style: italic;
  color: var(--fg);
}
.audit-prior-hint {
  margin: 8px 0 0;
  font-size: 11.5px;
  color: var(--dim);
}

/* History-row layout, a "Mark addressed" button can sit on the
   same line as the audit metadata. */
.audit-history-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.audit-address-btn {
  margin-left: auto;
  padding: 2px 9px !important;
  font-size: 11px !important;
}
.audit-addressed {
  display: inline-flex; align-items: center; gap: 6px;
  margin-left: auto;
  font-size: 11.5px; font-weight: 600;
  color: #2f7d54;
  background: #e1efe7;
  border-radius: 999px;
  padding: 2px 9px;
}
.audit-addressed-note {
  margin: 4px 0 0 4px;
  font-size: 12px;
  font-style: italic;
  color: var(--dim);
}
.dataset-list .ds-id .status-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: 6px; vertical-align: middle;
}
.dataset-list .ds-meta {
  display: flex; gap: 6px; font-size: 11px; color: var(--dim); margin-top: 3px;
  flex-wrap: wrap;
}
/* Test-cache stats, italics with a faint accent to visually separate
   "we have 5 rows from a Quick test" from a real extraction's counts. */
.dataset-list .ds-test {
  font-style: italic; color: var(--accent); opacity: 0.85;
}
.dot-extracted   { background: var(--accent); }
.dot-gated       { background: #c47b21; }
.dot-script      { background: #b75e2a; }
.dot-empty       { background: #aaa; }
.dot-all-dropped { background: #cb4646; }
.dot-error       { background: #cb4646; }
.dot-network     { background: #5c6bc0; }
.dot-unavailable { background: #888; }
.dot-skipped     { background: #888; }
.dot-unknown     { background: #d0d0c8; }

/* Tiny "X/Y cells extracted" badge next to the dataset id in the sidebar.
   Colour bands with manifest.combos status, green = full coverage,
   amber = partial, red = nothing extracted yet. */
.cov-badge {
  display: inline-block; margin-left: 6px;
  padding: 0 5px; border-radius: 8px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  vertical-align: middle;
}
.cov-ok    { background: #e3f0e6; color: #2c5e3f; }
.cov-warn  { background: #fdf1e3; color: var(--warn); }
.cov-err   { background: #fbe3e3; color: #a02828; }
.tag {
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  background: var(--line); color: var(--fg); font-size: 11px;
}
.tag-purpose-train { background: var(--accent-soft); color: var(--accent); }
.tag-purpose-eval  { background: #f0e3f8; color: var(--eval); }

.detail {
  overflow-y: auto; padding: 20px 28px;
  background: var(--ground);   /* grey ground so the white cards read */
}
.empty { color: var(--dim); padding: 60px 20px; text-align: center; }
.detail h1 {
  font-size: 20px; margin: 0;
}
.detail h1 .ds-id-mono { font-family: var(--mono); color: var(--dim); font-weight: 400; font-size: 14px; margin-left: 8px; }
.detail-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 10px;
}
.detail-actions { flex-shrink: 0; }
@media (max-width: 760px) {
  .detail-head { flex-direction: column; gap: 8px; }
  .detail-actions { width: 100%; }
}
.detail .summary {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
  padding: 14px 16px; color: var(--dim); font-size: 13px;
  background: var(--card); border: 1px solid var(--card-line);
  border-radius: var(--card-radius); box-shadow: var(--card-shadow);
  margin-bottom: 16px;
}
.detail .summary .pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px; background: var(--ground); border: 1px solid var(--card-line);
  border-radius: 999px; color: var(--fg);
}

.registry-only {
  border: 1px solid var(--card-line); border-radius: var(--card-radius); padding: 16px 18px;
  background: var(--card); box-shadow: var(--card-shadow); margin-top: 8px;
}
.registry-only h3 {
  margin: 0 0 6px 0; font-size: 13px; color: var(--dim);
  text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600;
}
.registry-only .reason {
  display: inline-block; padding: 4px 10px; border-radius: 4px;
  background: var(--accent-soft); color: var(--accent); font-size: 12px;
  margin-bottom: 12px;
}
.registry-only .reason.warn { background: #fdf1e3; color: var(--warn); }
.registry-only .reason.err  { background: #fbe3e3; color: #a02828; }
.registry-only .kv { display: grid; grid-template-columns: 120px 1fr; gap: 6px 14px; font-size: 13px; }
.registry-only .kv dt { color: var(--dim); font-family: var(--mono); font-size: 12px; }
.registry-only .kv dd { margin: 0; word-break: break-word; }

/* License row badges, small inline pills sized to sit next to a code
   tag. data.licenses.analyze maps the raw metadata to {redistributable,
   non_commercial, share_alike, attribution_required, public_domain,
   gated} which we render here. */
.lic-redist {
  display: inline-block; padding: 1px 7px; border-radius: 3px;
  font-size: 11px; font-weight: 600; margin-left: 6px;
}
.lic-redist.yes { background: #d6f5d6; color: #1d6b34; }
.lic-redist.no  { background: #fbe3e3; color: #8b1a1a; }
.lic-flag {
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  font-size: 10px; font-weight: 600; margin-left: 4px;
  background: rgba(0,0,0,0.05); color: var(--dim);
}
.lic-flag.gated { background: #fef3c7; color: #b45309; }
.lic-flag.nc    { background: #fee2e2; color: #b91c1c; }
.lic-flag.sa    { background: #ede9fe; color: #6d28d9; }
.lic-flag.pd    { background: #d1fae5; color: #047857; }
.lic-flag.attr  { background: rgba(37,99,235,0.10); color: #1d4ed8; }
.registry-only pre {
  font-family: var(--mono); font-size: 12px; background: var(--ground);
  padding: 8px 10px; border-radius: 4px; overflow-x: auto;
}
.registry-only .cmd-hint {
  margin-top: 12px; padding: 10px 12px; background: var(--ground);
  border-radius: 6px; font-family: var(--mono); font-size: 12px;
}

.tabs {
  display: flex; gap: 0; border-bottom: 1px solid var(--line);
  margin-bottom: 16px;
}
.tabs button {
  background: none; border: none; padding: 8px 14px; cursor: pointer;
  font-family: inherit; font-size: 13px; color: var(--dim);
  border-bottom: 2px solid transparent;
}
.tabs button.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 500; }

.tab-panel { min-height: 200px; }

.sample {
  border: 1px solid var(--card-line); border-radius: var(--card-radius);
  background: var(--card); box-shadow: var(--card-shadow);
  padding: 12px 14px; margin-bottom: 12px;
}
.sample .sample-head {
  font-family: var(--mono); font-size: 11px; color: var(--dim); margin-bottom: 8px;
  display: flex; gap: 8px;
}
.sample .msg { margin: 8px 0; }
.sample .msg .role {
  font-size: 11px; color: var(--accent); font-weight: 600; text-transform: uppercase; letter-spacing: 0.4px;
}
.sample .msg .content {
  white-space: pre-wrap; word-break: break-word;
  margin-top: 2px; font-size: 13px;
}
.sample .msg.role-system .role { color: var(--warn); }
.sample .msg.role-assistant .role { color: #2c5cae; }

/* Accordion: clamp the sample body once as a unit, then a show-more
   toggle expands the rest. Two flavours of clamp target:
     - .sample-body[data-text-block]    (SFT, wraps all role turns)
     - .msg .content[data-text-block]   (CPT, single text block)
   The single fade gradient sits at the bottom of whichever element
   carries data-text-block, so multi-message SFT samples no longer
   stack three independent fades on top of each other. */
.sample [data-text-block] {
  max-height: calc(1.5em * 3); overflow: hidden; position: relative;
  line-height: 1.5em;
}
.sample.expanded [data-text-block] { max-height: none; }
.sample:not(.expanded) [data-text-block]::after {
  content: ""; position: absolute; bottom: 0; left: 0; right: 0;
  height: 1.6em; background: linear-gradient(180deg, transparent, var(--card));
  pointer-events: none;
}
.sample .rel-sample-toggle {
  background: none; border: none; cursor: pointer;
  font-size: 11px; color: var(--accent); padding: 6px 0 0 0;
  text-transform: uppercase; letter-spacing: 0.4px; font-weight: 600;
}
.sample .rel-sample-toggle:hover { text-decoration: underline; }
/* Hidden by JS when the sample body fits inside the 3-line clamp -
   nothing to reveal, so no button to dangle. */
.sample .rel-sample-toggle[hidden] { display: none; }

/* Track prefix (sft/ or cpt/), small, dimmed, monospaced so the
   real id stays the visually dominant token. */
.ds-track {
  color: var(--dim); font-family: var(--mono);
  font-weight: 400; letter-spacing: 0.2px;
}

.stats-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 20px;
}
.stat-block {
  border: 1px solid var(--card-line); border-radius: var(--card-radius);
  padding: 12px 14px; background: var(--card); box-shadow: var(--card-shadow);
}
.stat-block h3 { margin: 0 0 8px 0; font-size: 13px; color: var(--dim); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; }
.stat-block ul { margin: 0; padding-left: 0; list-style: none; }
.stat-block li {
  display: flex; justify-content: space-between; align-items: center;
  padding: 3px 0; font-family: var(--mono); font-size: 12px;
}
.stat-block li .bar {
  flex: 1; margin: 0 8px; height: 4px; background: var(--line); border-radius: 2px; position: relative;
}
.stat-block li .bar > span {
  display: block; height: 100%; background: var(--accent); border-radius: 2px;
}

.shards table {
  width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12px;
}
.shards th, .shards td { padding: 6px 10px; text-align: left; border-bottom: 1px solid var(--line); }
.shards th { color: var(--dim); font-weight: 500; }

/* Handler chip-link in the registry block (custom-task datasets) */
.task-edit-btn {
  margin-left: 6px;
  border: none;
  background: transparent;
  color: var(--dim);
  cursor: pointer;
  font-size: 13px;
  padding: 1px 6px;
  border-radius: 4px;
  vertical-align: middle;
  transition: background 100ms ease, color 100ms ease;
}
.task-edit-btn:hover { background: var(--accent-soft); color: var(--accent); }
.task-value { vertical-align: middle; }

.handler-link {
  color: var(--accent); text-decoration: none;
  border-bottom: 1px dashed var(--accent);
}
.handler-link code { background: none; padding: 0; color: inherit; }
.handler-link:hover { border-bottom-style: solid; }

/* Tokenize tab on dataset detail */
.tok-tab-controls {
  display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap;
  padding: 8px 0 12px;
}
.tok-tab-controls label {
  display: flex; flex-direction: column; gap: 3px;
  font-size: 11px; color: var(--dim);
}
.tok-tab-controls select, .tok-tab-controls input {
  padding: 5px 8px; font-size: 12px;
  border: 1px solid var(--line); border-radius: 4px;
  font-family: inherit;
}
.tok-tab-controls #tok-tab-tok-select { min-width: 280px; }
.tok-tab-controls #tok-tab-langs { min-width: 240px; }
.tok-tab-controls #tok-tab-n { width: 80px; }
.tok-tab-controls #tok-tab-n:disabled { background: var(--ground); color: var(--dim); cursor: not-allowed; }
.tok-tab-controls .tok-tab-all {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--fg); cursor: pointer;
}
.tok-tab-controls #tok-tab-run {
  padding: 7px 14px; background: var(--accent); color: white;
  border: 1px solid var(--accent); border-radius: 4px; cursor: pointer;
  font: inherit; font-size: 12px;
}
.tok-tab-controls #tok-tab-run:hover { background: #245a3f; }
.tok-tab-hint { font-size: 11.5px; color: var(--dim); margin: 0 0 12px 0; }

.tok-tab-results { margin-top: 6px; }
.dst-fert-legend {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 6px; font-size: 11px; color: var(--dim);
}
.dst-fert-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 6px; }
.dst-fert-grid { width: 100%; border-collapse: collapse; }
.dst-fert-grid th, .dst-fert-grid td {
  padding: 6px 9px; border-bottom: 1px solid var(--line);
  text-align: center; font-size: 11.5px;
}
.dst-fert-grid thead th {
  background: var(--ground); color: var(--dim); font-weight: 600; font-size: 10.5px;
  text-transform: uppercase; letter-spacing: 0.4px;
}
.dst-fert-grid tbody th { background: var(--ground); text-align: left; font-weight: 500; }
.dst-fert-grid tbody th code { background: none; padding: 0; color: var(--accent); }
.dst-fcell { font-family: var(--mono); }
.dst-fcell-empty { color: var(--dim); background: #f8f8f4; }
.dst-fcpt { font-weight: 600; font-size: 12px; color: var(--fg); }
.dst-ftpw { font-size: 10px; color: var(--dim); margin-top: 1px; }

/* Coverage grid, per-(cfg, split) cells from manifest.combos */
.combo-summary {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-bottom: 14px;
}
.combo-tag {
  display: inline-flex; align-items: center;
  padding: 2px 8px; border-radius: 10px;
  font-family: var(--mono); font-size: 11px;
  background: var(--accent-soft); color: var(--accent);
}
.combo-tag.combo-warn { background: #fdf1e3; color: var(--warn); }
.combo-tag.combo-err  { background: #fbe3e3; color: #a02828; }
.combo-tag.combo-dim  { background: var(--ground); color: var(--dim); }

.combo-grid-wrap { overflow-x: auto; border: 1px solid var(--card-line); border-radius: var(--card-radius); background: var(--card); box-shadow: var(--card-shadow); }
.combo-grid {
  width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12px;
}
.combo-grid th, .combo-grid td {
  padding: 6px 10px; border-bottom: 1px solid var(--line); text-align: center;
  white-space: nowrap;
}
.combo-grid thead th { background: var(--ground); color: var(--dim); font-weight: 600; }
.combo-grid .combo-row-h { text-align: left; color: var(--fg); background: var(--ground); }
.combo-grid .combo-cell { cursor: help; position: relative; }
.combo-grid .combo-flag { display: inline-block; width: 14px; }
.combo-grid .combo-n    { color: var(--dim); margin-left: 4px; }
.combo-grid .combo-retry {
  margin-left: 6px;
  width: 18px; height: 18px; padding: 0;
  background: white; color: var(--accent);
  border: 1px solid var(--accent-soft); border-radius: 50%;
  cursor: pointer; font-size: 11px; line-height: 1;
  opacity: 0; transition: opacity 100ms ease;
}
.combo-grid tr:hover .combo-retry,
.combo-grid .combo-cell:focus-within .combo-retry { opacity: 1; }
.combo-grid .combo-retry:hover { background: var(--accent-soft); border-color: var(--accent); }

.combo-tail-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin: 18px 0 4px 0;
}
.combo-tail-head .combo-tail-h { margin: 0; }
.combo-retry-all {
  padding: 5px 10px;
  background: white; color: var(--accent);
  border: 1px solid var(--accent); border-radius: 4px; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 500;
}
.combo-retry-all:hover { background: var(--accent-soft); }
.combo-grid .combo-ok   { background: rgba(60, 130, 90, 0.06); }
.combo-grid .combo-ok   .combo-flag { color: var(--accent); }
.combo-grid .combo-warn { background: rgba(212, 130, 30, 0.08); }
.combo-grid .combo-warn .combo-flag { color: var(--warn); }
.combo-grid .combo-err  { background: rgba(180, 50, 50, 0.06); }
.combo-grid .combo-err  .combo-flag { color: #a02828; }
.combo-grid .combo-dim  .combo-flag { color: var(--dim); }
.combo-grid .combo-dim  .combo-n    { color: var(--dim); }

.combo-tail-h {
  margin: 18px 0 8px 0; font-size: 13px;
  color: var(--dim); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600;
}
.combo-tail { margin: 0; padding-left: 0; list-style: none; }
.combo-tail li {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 0; font-size: 12px; font-family: var(--mono);
}
.combo-tail .combo-reason { color: var(--dim); flex: 1; }
.combo-allgood {
  margin: 14px 0 0 0; font-size: 12px; color: var(--accent);
}

.query-area {
  display: flex; flex-direction: column; gap: 10px;
}
.query-area textarea {
  width: 100%; min-height: 120px; font-family: var(--mono); font-size: 13px;
  padding: 10px; border: 1px solid var(--line); border-radius: 6px;
  resize: vertical;
}
.query-area .query-controls { display: flex; gap: 8px; align-items: center; }
.query-area button {
  padding: 6px 14px; background: var(--accent); color: white;
  border: none; border-radius: 4px; cursor: pointer; font-family: inherit; font-size: 13px;
}
.query-area button:disabled { opacity: 0.5; cursor: wait; }
.query-area .query-status { font-size: 12px; color: var(--dim); font-family: var(--mono); }
.query-result {
  overflow-x: auto; border: 1px solid var(--card-line); border-radius: var(--card-radius);
  background: var(--card); box-shadow: var(--card-shadow);
}
.query-result table {
  width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12px;
}
.query-result th, .query-result td {
  padding: 6px 10px; text-align: left; border-bottom: 1px solid var(--line);
  vertical-align: top; max-width: 400px; overflow: hidden; text-overflow: ellipsis;
}
.query-result th { background: var(--ground); color: var(--fg); font-weight: 500; }

.qt-banner {
  margin: 10px 0; padding: 8px 12px; border-radius: 4px;
  background: #fdf1e3; color: var(--warn);
  font-size: 13px; line-height: 1.5;
}
.qt-banner code { font-family: var(--mono); font-size: 12px; }
.qt-banner .qt-stale {
  margin-left: 8px; padding: 1px 6px; border-radius: 3px;
  background: #fbe3e3; color: #a02828; font-size: 11px;
}

/* ---- responsive: tables on phone --------------------------------------
   Wide tables (registry catalogue, coverage grid, fertility heat-grid,
   admin tables) get horizontal scroll instead of overflowing the layout
   on phones. `display: block` turns the table into a scroll container
   while its rows still render via internal table layout.
   The combo / fert grids keep their dense numbers readable.            */
@media (max-width: 700px) {
  .detail { padding: 14px 12px; }
  .combo-grid,
  .dst-fert-grid,
  .query-result table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
  }
  .combo-grid th, .combo-grid td,
  .dst-fert-grid th, .dst-fert-grid td {
    padding: 4px 6px; font-size: 11px;
  }
  .stats-grid { gap: 12px; }
}

/* Upload template download links (add-dataset modal, upload source) */
.upload-template-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  padding: 8px 10px; background: var(--accent-soft); border-radius: 4px;
}
.upload-template-row .template-links a {
  color: var(--accent); font-family: var(--mono); font-size: 12px;
  text-decoration: underline; margin-right: 2px;
}

/* ---- add-dataset wizard ----------------------------------------------- */
.wizard-steps {
  display: flex; gap: 0; list-style: none; margin: 0 0 16px 0; padding: 0;
  counter-reset: wstep;
}
.wizard-steps li {
  flex: 1; position: relative; text-align: center;
  font-size: 12px; color: var(--dim); padding: 8px 4px 0;
  counter-increment: wstep; cursor: default;
}
.wizard-steps li::before {
  content: counter(wstep);
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--line); color: var(--fg);
  font-weight: 600; font-size: 11px; margin-bottom: 4px;
}
.wizard-steps li.active { color: var(--accent); font-weight: 600; }
.wizard-steps li.active::before { background: var(--accent); color: white; }
.wizard-steps li.done { color: var(--accent); cursor: pointer; }
.wizard-steps li.done::before { background: var(--accent-soft); color: var(--accent); content: "✓"; }
/* connector line between step bullets */
.wizard-steps li:not(:last-child)::after {
  content: ""; position: absolute; top: 19px; left: 50%; right: -50%;
  height: 2px; background: var(--line); z-index: -1;
}
.wizard-steps li.done:not(:last-child)::after { background: var(--accent-soft); }

.wizard-step { display: block; }
.wizard-foot { display: flex; align-items: center; gap: 8px; }
.wizard-foot-spacer { flex: 1; }

/* CPT ingest, global header chip + spinner. Lives in the shared
   stylesheet so it renders correctly on every page where header.js
   surfaces a running job. */
.cpt-running-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 11.5px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.cpt-running-chip:hover { background: var(--accent); color: white; }

.cpt-spin {
  display: inline-block;
  width: 10px; height: 10px;
  border: 2px solid var(--accent-soft);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: cpt-spin 0.8s linear infinite;
}
.cpt-running-chip .cpt-spin { border-color: rgba(255,255,255,0.4); border-top-color: white; }
.cpt-running-chip:hover .cpt-spin { border-color: rgba(255,255,255,0.5); border-top-color: white; }
@keyframes cpt-spin { to { transform: rotate(360deg); } }

/* Registry selector at the top of the sidebar, switches between SFT
   (/datasets) and CPT (/cpt) registries; both pages share the same
   layout shell so the switch is just navigation. */
.registry-select-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--dim);
  margin-bottom: 8px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.registry-select {
  flex: 1;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--fg);
  text-transform: none;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: white;
}
.registry-select:focus { outline: 2px solid var(--accent-soft); }

/* Tier pill on CPT sidebar entries. */
.tier-pill {
  font-family: var(--mono);
  font-size: 10px;
  background: var(--accent-soft);
  color: var(--accent);
  padding: 1px 5px;
  border-radius: 3px;
  font-weight: 500;
}

/* Global progress modal, opened from the header chip; lists every
   in-flight extract (SFT and CPT) so the user always knows what's
   running, even after navigating around. */
.progress-list { list-style: none; padding: 0; margin: 0; }
.progress-row {
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 10px 12px;
  margin-bottom: 8px;
  background: white;
}
.progress-row-head {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--fg);
}
.progress-row-head:hover { color: var(--accent); }
.progress-row-kind {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  background: var(--accent-soft);
  color: var(--accent);
  padding: 2px 7px;
  border-radius: 3px;
  font-weight: 600;
}
.progress-row-status {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--accent);
}
.progress-row-toks {
  margin-top: 6px;
  font-size: 12px;
  color: var(--dim);
}
.progress-row-toks code {
  font-family: var(--mono);
  background: var(--bg);
  padding: 1px 5px;
  border-radius: 3px;
  margin-right: 4px;
}
.progress-row-event {
  margin-top: 4px;
  font-size: 11.5px;
  color: var(--dim);
  font-style: italic;
}
.progress-foot {
  text-align: center;
  font-size: 11px;
  color: var(--dim);
  margin-top: 12px;
}

/* Detail-panel stats / coverage helpers for CPT (and any future SFT-side
   use). Tight typography to match the rest of the panel. */
.cpt-h4 { margin: 6px 0 8px; font-size: 13px; font-weight: 600; color: var(--fg); }

.stats-table {
  width: 100%; border-collapse: collapse; font-size: 13px;
  margin-bottom: 8px;
}
.stats-table th, .stats-table td {
  padding: 6px 10px; border-bottom: 1px solid var(--line);
  text-align: left;
}
.stats-table th {
  font-weight: 600; color: var(--dim); background: var(--bg);
  border-bottom: 2px solid var(--line);
}
.stats-table td.num, .stats-table th.num {
  text-align: right; font-variant-numeric: tabular-nums;
}
.stats-table code { font-family: var(--mono); }

.cov-row {
  display: grid;
  grid-template-columns: 110px 1fr 200px;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
  font-size: 13px;
  border-bottom: 1px dotted var(--line);
}
.cov-row code { font-family: var(--mono); font-size: 12px; }
.cov-lang { display: flex; align-items: center; gap: 6px; }
.cov-tag-bonus {
  background: #f0e9d9; color: #6a4a00;
  font-size: 9.5px; padding: 1px 5px; border-radius: 3px;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.cov-bar {
  height: 8px; border-radius: 4px;
  background: var(--bg); border: 1px solid var(--line);
  overflow: hidden;
}
.cov-bar > span {
  display: block; height: 100%;
  background: var(--accent);
}
.cov-row-ok   .cov-bar > span { background: var(--accent); }
.cov-row-warn .cov-bar > span { background: #c47b21; }
.cov-row-err  .cov-bar > span { width: 0 !important; }
.cov-row-info .cov-bar > span { background: #6a4a00; }
.cov-meta { color: var(--dim); font-size: 11.5px; text-align: right; }

/* === Compact coverage header ============================================
   Replaced the verbose "by tokenizer" + "by language" tables (which
   duplicated data already shown row-by-row in the bars below) with a
   one-line summary and a small chip-strip for tokenizer totals. */
.cov-summary {
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px 12px; margin-bottom: 14px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 6px;
}
.cov-stats {
  display: flex; gap: 18px; flex-wrap: wrap; font-size: 12.5px; color: var(--dim);
}
.cov-stats strong { color: var(--fg); font-weight: 600; font-family: var(--mono); }
.cov-tok-chips {
  display: flex; gap: 6px; flex-wrap: wrap;
  border-top: 1px solid var(--line); padding-top: 8px;
}
.cov-tok-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 999px;
  background: white; border: 1px solid var(--line);
  font-size: 11px; color: var(--dim);
}
.cov-tok-chip code { color: var(--fg); font-family: var(--mono); font-size: 11px; }

.cov-list { display: flex; flex-direction: column; gap: 4px; }

.cov-extras { margin-top: 18px; }
.cov-extras > summary {
  font-size: 12px; color: var(--dim); cursor: pointer; padding: 4px 0;
  font-weight: 500;
}
.cov-extras > summary:hover { color: var(--accent); }
.cov-extras[open] > summary { color: var(--fg); }

.registry-json {
  font-family: var(--mono); font-size: 12px;
  background: var(--bg); border: 1px solid var(--line);
  padding: 10px 14px; border-radius: 6px;
  white-space: pre-wrap; overflow-x: auto;
  color: var(--fg); line-height: 1.5;
}

/* Target-language overlay editor on /languages. Collapsed by default;
   curators expand to add/remove. Visible to viewers (read-only when
   unauth'd for the curate capability, the API rejects, the UI just
   shows a stale state after the failed call). */
.lang-targets {
  margin: 10px 0 16px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: white;
}
.lang-targets > summary {
  cursor: pointer;
  padding: 8px 12px;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.lang-targets > summary:hover { background: var(--accent-soft); }
.lang-targets[open] > summary { border-bottom: 1px solid var(--line); }
.lang-targets-count { color: var(--dim); font-weight: 400; }
.lang-targets-help {
  margin: 8px 12px 4px;
  color: var(--dim);
  font-size: 12px;
  line-height: 1.5;
}
.lang-targets-help code { font-family: var(--mono); }
.lang-targets-chips {
  display: flex; flex-wrap: wrap; gap: 5px;
  padding: 6px 12px 10px;
}
.lang-target-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 4px 2px 8px;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 999px;
  font-size: 12px;
  font-family: var(--mono);
}
.lang-target-chip.is-overlay {
  background: #fdf1e3; color: var(--warn);
}
.lang-target-chip code { background: transparent; padding: 0; color: inherit; }
.lang-target-remove {
  background: none; border: none; cursor: pointer;
  color: inherit; font-size: 14px; line-height: 1;
  padding: 0 4px;
}
.lang-target-remove:hover { color: #a02828; }
.lang-targets-add {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px 12px;
  border-top: 1px dashed var(--line);
}
.lang-targets-add input[type="search"] {
  flex: 1; padding: 4px 8px; font: inherit;
  border: 1px solid var(--line); border-radius: 4px;
}
.lang-targets-add button {
  font: inherit; padding: 4px 10px;
  background: var(--accent); color: white;
  border: 1px solid var(--accent); border-radius: 4px;
  cursor: pointer;
}
.lang-targets-add button:hover { background: #265d40; }
.lang-targets-status { font-size: 12px; color: var(--dim); }

/* LID consensus badge on sample cards (CPT detail "samples" tab and
   public /release/<lang>). Status is stage_lid's per-record verdict:
     accepted , AfroLID + GlotLID agree
     rewritten, consensus disagreed with the source's declared label
     uncertain, no agreement; kept anyway (require_consensus=false)
     blind    , AfroLID-blind lang (mos/lua/tmh/umb); routed to /review
     rejected , dropped under require_consensus
*/
.lid-badge {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  padding: 1px 6px;
  border-radius: 3px;
  letter-spacing: .02em;
  text-transform: lowercase;
  cursor: help;
}
.lid-accepted   { background: #dceedc; color: var(--accent); }
.lid-rewritten  { background: #fdf1e3; color: var(--warn); }
.lid-uncertain  { background: #fff1cf; color: #6a4a00; }
.lid-blind      { background: #ece6f7; color: var(--eval); }
.lid-rejected   { background: #fbe3e3; color: #8b1a1a; }

/* Per-row tint in the Stats tab's LID consensus table. */
.stats-table td.lid-accepted   { color: var(--accent); }
.stats-table td.lid-rewritten  { color: var(--warn); }
.stats-table td.lid-uncertain  { color: #6a4a00; }
.stats-table td.lid-blind      { color: var(--eval); }
.stats-table td.lid-rejected   { color: #8b1a1a; }

/* Shared module sub-navigation, rendered by header.js right under
   the main header, grouping sibling pages within the active module
   (Datasets / Review / Languages / Training / Release). Provides the
   horizontal-tab IA the rest of the app expected but only Admin had. */
.module-subnav {
  position: sticky;
  top: 0;
  z-index: 5;
  background: #fff;                 /* match the admin app-bar */
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.03);
}
.module-subnav-inner {
  padding: 0 24px;        /* left-aligned to the viewport (tabs at the left) */
  display: flex;
  align-items: stretch;
  gap: 14px;
  min-height: 50px;
}
.module-subnav-label {
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  padding-right: 12px;
  border-right: 1px solid var(--line);
}
.module-subnav-tabs {
  display: flex;
  align-items: stretch;
  gap: 2px;
  flex: 1;
  overflow-x: auto;
}
.module-subnav-tab {
  display: inline-flex;
  align-items: center;
  padding: 0 15px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--dim);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: color 120ms ease, border-color 120ms ease;
}
.module-subnav-tab:hover { color: var(--fg); }
.module-subnav-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
@media (max-width: 640px) {
  .module-subnav-label { display: none; }
  .module-subnav-inner { padding: 0 12px; min-height: 38px; }
  .module-subnav-tab { padding: 0 10px; font-size: 12.5px; }
}

/* Sidebar-layout variant, align the "Datasets" label with the 320px
   sidebar gutter so the tabs (Overview, SFT, CPT, …) start exactly
   at the content column. Drops back to the standard centred bar on
   mobile (sidebar collapses to a drawer there). */
.module-subnav--sidebar .module-subnav-inner {
  max-width: none;
  margin: 0;
  padding: 0;
  gap: 0;
}
.module-subnav--sidebar .module-subnav-label {
  width: 320px;
  flex-shrink: 0;
  padding: 0 18px;
  border-right: 1px solid var(--line);
  background: white;
}
.module-subnav--sidebar .module-subnav-tabs {
  padding-left: 18px;
}
@media (max-width: 900px) {
  .module-subnav--sidebar .module-subnav-label {
    display: none;
  }
  .module-subnav--sidebar .module-subnav-tabs {
    padding-left: 12px;
  }
}

/* Shared footer, injected by header.js on every non-landing page.
   Replaces the page-specific .rel-footer that used to live on /release. */
.app-footer {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  margin: 0;
  height: var(--footer-h);   /* exact height so sticky bars sit flush on it */
  padding: 0 24px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12.5px;
  color: var(--dim);
  background: #fff;
  border-top: 1px solid var(--line);
  box-shadow: 0 -1px 3px rgba(15, 23, 42, 0.04);
}
/* Reserve exactly the footer's height so content scrolls fully clear. */
body { padding-bottom: var(--footer-h); }
.app-footer a { color: var(--accent); text-decoration: none; }
.app-footer a:hover { text-decoration: underline; }
.app-footer-tag { font-style: italic; }

/* ---------------------------------------------------------------
   Button hierarchy, one canonical look, four variants.

   Canonical class is `.btn` (use it on new code). Legacy class names
   (.extract-btn, .cpt-btn, .cpt-ingest-btn, .btn-primary, .btn-secondary)
   are aliases so we don't have to rewrite every existing template.

       .btn               → primary (filled accent)
       .btn.secondary     → outlined accent
       .btn.danger        → outlined red
       .btn.ghost         → no border, link-coloured (subtle row actions)
       .btn.sm            → compact padding for inline / table rows

   All variants share the same height, font, and radius so a row of
   mixed primary + secondary + danger buttons aligns visually. ----- */

.btn,
.extract-btn,
.cpt-ingest-btn,
.cpt-btn.cpt-primary,
button.btn-primary,
a.btn-primary {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;
  padding: 6px 14px;
  background: var(--accent);
  color: white;
  border: 1px solid var(--accent);
  border-radius: 4px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  text-decoration: none;
  cursor: pointer;
  transition: background 100ms ease, border-color 100ms ease, color 100ms ease;
}
.btn:hover:not(:disabled),
.extract-btn:hover:not(:disabled),
.cpt-ingest-btn:hover:not(:disabled),
.cpt-btn.cpt-primary:hover:not(:disabled),
button.btn-primary:hover:not(:disabled),
a.btn-primary:hover { background: #265d40; border-color: #265d40; }
.btn:disabled,
.extract-btn:disabled,
.cpt-ingest-btn:disabled,
button.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

.btn.secondary,
.extract-btn.secondary,
.cpt-btn:not(.cpt-primary),
button.btn-secondary,
a.btn-secondary {
  background: white;
  color: var(--accent);
  border: 1px solid var(--accent);
}
.btn.secondary:hover:not(:disabled),
.extract-btn.secondary:hover:not(:disabled),
.cpt-btn:not(.cpt-primary):hover:not(:disabled),
button.btn-secondary:hover:not(:disabled),
a.btn-secondary:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

.btn.danger,
.extract-btn.danger,
button.btn-danger,
a.btn-danger {
  background: white;
  color: #a02828;
  border: 1px solid #f4dada;
  font-weight: 500;
}
.btn.danger:hover:not(:disabled),
.extract-btn.danger:hover:not(:disabled),
button.btn-danger:hover:not(:disabled),
a.btn-danger:hover { background: #fff2f2; border-color: #cb4646; color: #a02828; }

.btn.ghost,
button.btn-ghost,
a.btn-ghost {
  background: transparent;
  color: var(--accent);
  border: 1px solid transparent;
  padding: 4px 10px;
}
.btn.ghost:hover:not(:disabled),
button.btn-ghost:hover:not(:disabled),
a.btn-ghost:hover { background: var(--accent-soft); }

.btn.sm,
button.btn-sm,
a.btn-sm {
  padding: 3px 10px;
  font-size: 12px;
}

/* `.extract-btn.cancel` was a one-off red used while a job is cancellable;
   keep it but expressed via the same token vocabulary so the rest of the
   palette stays coherent. */
.extract-btn.cancel,
.btn.cancel { background: #cb4646; border-color: #cb4646; color: white; }
.extract-btn.cancel:hover:not(:disabled),
.btn.cancel:hover:not(:disabled) { background: #a83737; border-color: #a83737; }

/* ---------------------------------------------------------------
   Tables, canonical density. Same row height, same uppercase-mono
   header row, same hover state. Apply with `class="std-table"`.
   Admin's #pending-table / #users-table / #jobs-table get the same
   look without rewriting by riding the rule below. ---------------- */
.std-table,
#pending-table,
#users-table,
#jobs-table,
#audit-table,
#handlers-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.std-table th,
.std-table td,
#pending-table th, #pending-table td,
#users-table th,   #users-table td,
#jobs-table th,    #jobs-table td,
#audit-table th,   #audit-table td,
#handlers-table th, #handlers-table td {
  padding: 8px 10px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
.std-table th,
#pending-table th,
#users-table th,
#jobs-table th,
#audit-table th,
#handlers-table th {
  color: var(--dim);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: #fafaf7;
  border-bottom: 1px solid var(--line);
}
.std-table tbody tr:hover,
#pending-table tbody tr:hover,
#users-table tbody tr:hover,
#jobs-table tbody tr:hover { background: var(--accent-soft); }

/* ── Universal menu: inline group nav in the header row (built by
   header.js), each opening a dropdown of its pages. Permission-gated. ── */
.app-menu { display: flex; align-items: center; gap: 2px; margin-left: 22px; }
.header-right { margin-left: auto; }
/* Hamburger, hidden on desktop (inline menu); shown on narrow screens. */
.nav-toggle {
  display: none; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-left: 8px; flex: none;
  border: 1px solid transparent; border-radius: 8px;
  background: transparent; color: var(--fg); cursor: pointer;
}
.nav-toggle:hover { background: var(--ground, #f5f6f8); border-color: var(--card-line); }
.uninav-group { position: relative; display: inline-flex; }
.uninav-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 11px; border-radius: 7px;
  border: none; background: transparent; cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 600; color: var(--dim);
  transition: color 120ms ease, background 120ms ease;
}
.uninav-btn:hover { color: var(--fg); background: var(--ground, #f5f6f8); }
.uninav-btn.active { color: var(--accent); }
.uninav-group.open .uninav-btn { color: var(--accent); background: var(--ground, #f5f6f8); }
/* Chevron drawn from borders, rotates up when its group is open. */
.uninav-caret {
  width: 6px; height: 6px; margin-top: -3px; flex: none;
  border-right: 1.7px solid currentColor; border-bottom: 1.7px solid currentColor;
  transform: rotate(45deg); opacity: 0.55;
  transition: transform 140ms ease, opacity 140ms ease;
}
.uninav-group.open .uninav-caret { transform: rotate(-135deg); margin-top: 2px; opacity: 0.9; }

.uninav-drop {
  position: absolute; top: calc(100% + 6px); left: 0;
  z-index: 60; display: none;
  min-width: 210px;
  background: var(--card, #fff);
  border: 1px solid var(--card-line, var(--line));
  border-radius: 10px;
  box-shadow: 0 12px 30px rgba(15, 23, 42, 0.16);
  padding: 6px;
}
.uninav-group.open .uninav-drop { display: block; }
.uninav-item {
  display: block; padding: 8px 12px; border-radius: 6px;
  font-size: 14px; color: var(--fg); text-decoration: none; white-space: nowrap;
  transition: background 100ms ease, color 100ms ease;
}
.uninav-item:hover { background: var(--ground, #f5f6f8); color: var(--accent); }
.uninav-item.active { color: var(--accent); font-weight: 600; background: var(--accent-soft); }

/* Breadcrumb on its own row below the header (moved there by header.js). */
.breadcrumb-bar {
  padding: 7px 18px; border-bottom: 1px solid var(--line);
  background: #fbfbfc; font-size: 13px; color: var(--dim);
}
.breadcrumb-bar .brand-suffix {
  margin: 0; font-size: 13px; font-weight: 500; align-self: auto;
}
/* ── Mobile menu: collapse the inline group bar behind the hamburger.
   The menu drops as a full-width panel below the header; groups expand
   inline within it. ── */
@media (max-width: 860px) {
  .nav-toggle { display: inline-flex; }
  .app-menu {
    display: none;
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 2px;
    margin: 0; padding: 8px;
    background: var(--card); border-bottom: 1px solid var(--line);
    box-shadow: var(--card-shadow-md);
    max-height: 78vh; overflow-y: auto;
    z-index: 95;
  }
  header.app-header.nav-open .app-menu { display: flex; }
  .uninav-group { display: block; width: 100%; }
  .uninav-btn {
    width: 100%; justify-content: space-between;
    font-size: 15px; padding: 12px 12px; border-radius: 8px;
  }
  .uninav-drop {
    position: static; display: none;
    min-width: 0; margin: 2px 0 8px; padding: 2px 0 2px 12px;
    border: none; border-left: 2px solid var(--card-line); border-radius: 0;
    box-shadow: none;
  }
  .uninav-group.open .uninav-drop { display: block; }
  .uninav-item { padding: 11px 12px; font-size: 15px; }
}

/* ===== Floating AfriCC extraction-progress card (elevated users only) ===== */
#africc-widget {
  position: fixed; right: 16px; bottom: calc(var(--footer-h, 42px) + 14px);
  width: 262px; background: var(--card, #fff);
  border: 1px solid var(--card-line, rgba(4,32,69,0.08)); border-radius: 10px;
  box-shadow: 0 8px 28px rgba(4,32,69,0.16); padding: 12px 13px;
  z-index: 900; font-size: 13px; color: var(--fg, #12212e);
}
#africc-widget[hidden] { display: none; }
.aw-head { display: flex; align-items: center; gap: 7px; margin-bottom: 8px;
  cursor: grab; user-select: none; touch-action: none; }
.aw-head.dragging { cursor: grabbing; }
.aw-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.aw-dot.live { background: var(--accent, #2f6f4f); animation: aw-pulse 1.3s ease-in-out infinite; }
.aw-dot.done { background: #9aa4ad; }
@keyframes aw-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }
.aw-title { font-weight: 650; flex: 1; }
.aw-x { border: none; background: none; cursor: pointer; font-size: 18px; line-height: 1;
  color: var(--dim, #6b7681); padding: 0 2px; }
.aw-x:hover { color: var(--fg, #12212e); }
.aw-bar { height: 6px; background: var(--ground, #f5f6f8); border-radius: 999px;
  overflow: hidden; margin-bottom: 7px; }
.aw-bar span { display: block; height: 100%; background: var(--accent, #2f6f4f);
  border-radius: 999px; transition: width 0.4s ease; }
.aw-line { font-variant-numeric: tabular-nums; }
.aw-line b { color: var(--accent, #2f6f4f); }
.aw-sub { color: var(--dim, #6b7681); font-size: 11.5px; margin-top: 2px; }
.aw-link { display: inline-block; margin-top: 8px; font-size: 12px;
  color: var(--accent, #2f6f4f); text-decoration: none; }
.aw-link:hover { text-decoration: underline; }
@media (max-width: 560px) { #africc-widget { right: 10px; left: 10px; width: auto; } }
