/*
 * Ditto — one stylesheet, no build step. No inline styles anywhere: the
 * Content-Security-Policy forbids them, so every look lives here.
 *
 * Design language: CheckerAI's (frontend/src/index.css) — OKLCH hues and chroma, with the
 * lightness adjusted so every text pair reaches WCAG AA. src/review/contrast.test.ts reads
 * the three token blocks below and checks every pair it lists, in light and dark: change a
 * lightness, run the test.
 *
 * Themes: light on :root; dark under prefers-color-scheme unless the page says
 * data-theme="light", and always under data-theme="dark". The two dark blocks must stay
 * identical (the test checks).
 *
 * Old token → new token (Phase 5 names):
 *   --primary        → --accent            --card        → --surface
 *   --primary-strong → --accent-hover      --bg-sunk     → --surface-2
 *   --primary-soft   → --accent-light      --line        → --border
 *   --on-primary     → --accent-fg         --line-strong → --border-strong
 *   --text-dim       → --text-2            --text-faint  → --muted
 *   --success-soft   → --success-light     --success-strong → --success-text
 *   --danger-soft    → --danger-light      --danger-strong  → --danger-text
 *   --warning-soft   → --warning-light     --info-soft      → --info-light
 *   --shadow         → --shadow-sm         --shadow-lift    → --shadow-md
 *   --radius         → --radius-lg         --radius-sm (9px) → --radius-md (8px)
 *   --font, --mono   → --font-sans, --font-mono
 * Kept: --d-strong, --d-match, --d-far, --d-na (re-derived, tested), --focus, --thumb-bg.
 * Added: --neutral/-light/-text (the sixth tone), --border-control (≥ 3:1 input edges),
 * --d-fg (the number on a coloured frame distance).
 *
 * Sections: tokens, fonts, base, shell, one per component, then one per page
 * (headed "==== page: <name> ===="). A page's CSS lives only in its own section.
 */

/* ==== tokens ==== */

:root {
  color-scheme: light;

  --bg: oklch(96.5% 0.005 250);
  --surface: oklch(100% 0 0);
  --surface-2: oklch(98% 0.003 250);
  --border: oklch(92% 0.008 250);
  --border-strong: oklch(86% 0.013 255);
  --border-control: oklch(62% 0.013 255);

  --accent: oklch(52% 0.16 273);
  --accent-hover: oklch(46% 0.16 273);
  --accent-light: oklch(95.5% 0.024 273);
  --accent-fg: oklch(100% 0 0);

  --success: oklch(52% 0.15 145);
  --success-light: oklch(95% 0.05 145);
  --success-text: oklch(42% 0.12 145);
  --warning: oklch(70% 0.14 80);
  --warning-light: oklch(96% 0.06 95);
  --warning-text: oklch(46% 0.1 70);
  --danger: oklch(52% 0.2 27);
  --danger-light: oklch(95% 0.03 27);
  --danger-text: oklch(46% 0.18 27);
  --info: oklch(56% 0.19 290);
  --info-light: oklch(95% 0.03 290);
  --info-text: oklch(46% 0.17 290);
  --neutral: oklch(70% 0.008 250);
  --neutral-light: oklch(94% 0.006 250);
  --neutral-text: oklch(38% 0.015 250);

  --text: oklch(15% 0.015 250);
  --text-2: oklch(35% 0.015 250);
  --muted: oklch(51% 0.01 250);
  --muted-2: oklch(70% 0.008 250);

  --focus: oklch(52% 0.16 273);
  --thumb-bg: oklch(18% 0.01 250);

  --d-strong: oklch(50% 0.14 145);
  --d-match: oklch(53% 0.11 65);
  --d-far: oklch(54% 0.01 250);
  --d-na: oklch(88% 0.008 250);
  --d-fg: oklch(100% 0 0);

  --shadow-sm: 0 1px 3px rgb(0 0 0 / 0.06), 0 1px 2px rgb(0 0 0 / 0.04);
  --shadow-md: 0 4px 20px rgb(0 0 0 / 0.1);
  --shadow-lg: 0 8px 32px rgb(0 0 0 / 0.14);

  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;

  --font-sans: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;

  --sidebar-w: 240px;
  --rail-w: 64px;
  --topbar-h: 52px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --bg: oklch(15% 0.01 250);
    --surface: oklch(20% 0.012 250);
    --surface-2: oklch(24% 0.014 250);
    --border: oklch(28% 0.015 250);
    --border-strong: oklch(35% 0.018 250);
    --border-control: oklch(55% 0.018 250);

    --accent: oklch(70% 0.14 273);
    --accent-hover: oklch(76% 0.13 273);
    --accent-light: oklch(27% 0.045 273);
    --accent-fg: oklch(16% 0.01 250);

    --success: oklch(65% 0.16 145);
    --success-light: oklch(27% 0.05 145);
    --success-text: oklch(80% 0.13 145);
    --warning: oklch(72% 0.14 85);
    --warning-light: oklch(27% 0.05 85);
    --warning-text: oklch(82% 0.12 85);
    --danger: oklch(68% 0.19 27);
    --danger-light: oklch(27% 0.06 27);
    --danger-text: oklch(80% 0.11 27);
    --info: oklch(70% 0.17 290);
    --info-light: oklch(27% 0.05 290);
    --info-text: oklch(82% 0.09 290);
    --neutral: oklch(50% 0.008 250);
    --neutral-light: oklch(28% 0.012 250);
    --neutral-text: oklch(84% 0.008 250);

    --text: oklch(95% 0.005 250);
    --text-2: oklch(80% 0.008 250);
    --muted: oklch(68% 0.01 250);
    --muted-2: oklch(50% 0.008 250);

    --focus: oklch(74% 0.14 273);
    --thumb-bg: oklch(10% 0.01 250);

    --d-strong: oklch(72% 0.15 145);
    --d-match: oklch(78% 0.13 80);
    --d-far: oklch(64% 0.01 250);
    --d-na: oklch(36% 0.01 250);
    --d-fg: oklch(16% 0.01 250);

    --shadow-sm: 0 1px 3px rgb(0 0 0 / 0.3), 0 1px 2px rgb(0 0 0 / 0.2);
    --shadow-md: 0 4px 20px rgb(0 0 0 / 0.4);
    --shadow-lg: 0 8px 32px rgb(0 0 0 / 0.5);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: oklch(15% 0.01 250);
  --surface: oklch(20% 0.012 250);
  --surface-2: oklch(24% 0.014 250);
  --border: oklch(28% 0.015 250);
  --border-strong: oklch(35% 0.018 250);
  --border-control: oklch(55% 0.018 250);

  --accent: oklch(70% 0.14 273);
  --accent-hover: oklch(76% 0.13 273);
  --accent-light: oklch(27% 0.045 273);
  --accent-fg: oklch(16% 0.01 250);

  --success: oklch(65% 0.16 145);
  --success-light: oklch(27% 0.05 145);
  --success-text: oklch(80% 0.13 145);
  --warning: oklch(72% 0.14 85);
  --warning-light: oklch(27% 0.05 85);
  --warning-text: oklch(82% 0.12 85);
  --danger: oklch(68% 0.19 27);
  --danger-light: oklch(27% 0.06 27);
  --danger-text: oklch(80% 0.11 27);
  --info: oklch(70% 0.17 290);
  --info-light: oklch(27% 0.05 290);
  --info-text: oklch(82% 0.09 290);
  --neutral: oklch(50% 0.008 250);
  --neutral-light: oklch(28% 0.012 250);
  --neutral-text: oklch(84% 0.008 250);

  --text: oklch(95% 0.005 250);
  --text-2: oklch(80% 0.008 250);
  --muted: oklch(68% 0.01 250);
  --muted-2: oklch(50% 0.008 250);

  --focus: oklch(74% 0.14 273);
  --thumb-bg: oklch(10% 0.01 250);

  --d-strong: oklch(72% 0.15 145);
  --d-match: oklch(78% 0.13 80);
  --d-far: oklch(64% 0.01 250);
  --d-na: oklch(36% 0.01 250);
  --d-fg: oklch(16% 0.01 250);

  --shadow-sm: 0 1px 3px rgb(0 0 0 / 0.3), 0 1px 2px rgb(0 0 0 / 0.2);
  --shadow-md: 0 4px 20px rgb(0 0 0 / 0.4);
  --shadow-lg: 0 8px 32px rgb(0 0 0 / 0.5);
}

:root[data-theme="light"] {
  color-scheme: light;
}

/* ==== fonts ==== */

/*
 * Plus Jakarta Sans — variable, wght axis 200 to 800 (read from the files' fvar table).
 * Copyright 2020 The Plus Jakarta Sans Project Authors (https://github.com/tokotype/PlusJakartaSans).
 * Licensed under the SIL Open Font License, Version 1.1: https://openfontlicense.org
 */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/static/plus-jakarta-sans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/static/plus-jakarta-sans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==== base ==== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: 64px;
}

body {
  margin: 0;
  font: 14px/1.5 var(--font-sans);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
p {
  margin: 0;
}

h1,
h2,
h3 {
  line-height: 1.25;
  letter-spacing: -0.01em;
}

h1 {
  font-size: 24px;
  font-weight: 800;
}

h2 {
  font-size: 15px;
  font-weight: 700;
}

a {
  color: var(--accent);
  text-underline-offset: 2px;
}

a:hover {
  color: var(--accent-hover);
}

code,
kbd,
pre {
  font-family: var(--font-mono);
}

code {
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.icon {
  width: 18px;
  height: 18px;
  flex: none;
  vertical-align: middle;
}

.muted {
  color: var(--muted);
}

.small {
  font-size: 12px;
}

.key {
  font-family: var(--font-mono);
  font-size: 0.92em;
  overflow-wrap: anywhere;
}

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 12px;
  top: -48px;
  z-index: 160;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--accent-fg);
}

.skip-link:focus {
  top: 12px;
}

.inline {
  display: inline;
  margin: 0;
}

.stack > * + * {
  margin-top: 14px;
}

/* Shown only at 768px and wider / only below it. */
@media (max-width: 767.98px) {
  .hide-sm {
    display: none !important;
  }
}

@media (min-width: 768px) {
  .show-sm {
    display: none !important;
  }
}

/* ==== shell ==== */

/* z-index scale: topbar 20, sticky bars 25, sidebar and rail 30, menus 50, toasts 100, dialogs 150. */

.shell {
  min-height: 100vh;
}

.shell-main {
  min-width: 0;
  margin-left: var(--sidebar-w);
}

.main {
  padding: 24px;
}

.page {
  max-width: 1200px;
  margin: 0 auto;
}

.page-wide {
  max-width: 1440px;
}

.page > * + * {
  margin-top: 20px;
}

/* --- sidebar --- */

.sidebar,
.drawer-panel {
  display: flex;
  flex-direction: column;
  background: var(--surface);
}

.sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 30;
  width: var(--sidebar-w);
  border-right: 1px solid var(--border);
  overflow-y: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px;
  color: var(--text);
  text-decoration: none;
}

.brand:hover {
  color: var(--text);
}

.brand-tile {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, #7468ea, #4b34c7);
  color: #fff;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.15);
}

.brand-tile .icon {
  width: 16px;
  height: 16px;
  fill: currentColor;
}

.brand-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.2;
}

.brand-name {
  font-weight: 800;
  font-size: 15px;
}

.brand-sub {
  font-size: 12px;
  color: var(--muted);
}

.nav {
  flex: 1;
  padding: 4px 12px 12px;
}

.nav-group + .nav-group {
  margin-top: 18px;
}

.nav-heading {
  padding: 0 8px 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 2px;
}

.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 38px;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  color: var(--text-2);
  font-weight: 600;
  text-decoration: none;
  transition: background-color 0.12s, color 0.12s;
}

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

.nav-item.is-active {
  background: var(--accent-light);
  color: var(--accent);
}

.nav-item.is-active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 3px;
  height: 20px;
  border-radius: 0 3px 3px 0;
  background: var(--accent);
  transform: translateY(-50%);
}

.nav-icon {
  display: inline-flex;
}

.nav-label {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nav-count {
  margin-left: auto;
  min-width: 22px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--tone-bg);
  color: var(--tone-text);
  font-size: 12px;
  font-weight: 700;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.sidebar-foot {
  display: grid;
  gap: 8px;
  padding: 12px;
  border-top: 1px solid var(--border);
}

.watcher {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  font-size: 12px;
  font-weight: 600;
  background: var(--neutral-light);
  color: var(--neutral-text);
}

/* One line: the full sentence is in the title. */
.watcher-text {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.watcher-dot {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  background: var(--neutral);
}

.watcher-alive {
  background: var(--success-light);
  color: var(--success-text);
}

.watcher-alive .watcher-dot {
  background: var(--success);
  animation: pulse-dot 2s ease-in-out infinite;
}

.watcher-stopped,
.watcher-stale,
.watcher-never {
  background: var(--danger-light);
  color: var(--danger-text);
}

.watcher-stopped .watcher-dot,
.watcher-stale .watcher-dot,
.watcher-never .watcher-dot {
  background: var(--danger);
}

.sidebar-user {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 2px 2px 2px 4px;
}

.sidebar-username {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.avatar {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-fg);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.rail-toggle {
  display: none;
}

@keyframes pulse-dot {
  50% {
    opacity: 0.45;
  }
}

/* --- topbar --- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 12px;
  height: var(--topbar-h);
  padding: 0 20px;
  border-bottom: 1px solid var(--border);
}

/* The blur sits on a layer behind, not on the bar: backdrop-filter on the bar itself would
   become the containing block of the fixed drawer and menu backdrop inside it. */
.topbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in oklab, var(--surface) 90%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.topbar-title {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
}

.topbar-h {
  font-size: 15px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar-sub {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  color: var(--muted);
}

.topbar-sub .icon {
  width: 14px;
  height: 14px;
}

.topbar-sub-text {
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.topbar-search {
  position: relative;
  display: flex;
  align-items: center;
}

.topbar-search .icon {
  position: absolute;
  left: 10px;
  width: 16px;
  height: 16px;
  color: var(--muted);
  pointer-events: none;
}

.topbar-search .input {
  width: 220px;
  min-height: 36px;
  padding-left: 34px;
}

.upload-off {
  display: inline-flex;
}

@media (max-width: 767.98px) {
  .topbar {
    padding: 0 12px;
    gap: 8px;
  }

  .topbar-sub {
    display: none;
  }
}

/* --- the drawer, below 1024px --- */

.nav-drawer > summary {
  list-style: none;
}

.nav-drawer > summary::-webkit-details-marker,
.menu > summary::-webkit-details-marker {
  display: none;
}

/* The backdrop is part of the summary: a click on it closes the drawer, with no JavaScript. */
.nav-drawer[open] > summary::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  background: rgb(10 12 20 / 0.45);
  cursor: default;
}

.drawer-panel {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 2;
  width: min(288px, 84vw);
  border-right: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  overflow-y: auto;
  animation: drawer-in 0.16s ease-out;
}

@keyframes drawer-in {
  from {
    transform: translateX(-16px);
    opacity: 0;
  }
}

@media (max-width: 1023.98px) {
  .sidebar {
    display: none;
  }

  .shell-main {
    margin-left: 0;
  }
}

@media (min-width: 1024px) {
  .nav-drawer {
    display: none;
  }
}

/* --- focus mode: the rail --- */

.shell[data-focus] .main {
  padding: 16px;
}

@media (min-width: 1024px) {
  .shell[data-focus] .rail-toggle {
    position: absolute;
    top: 22px;
    right: 8px;
    display: inline-grid;
    place-items: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--surface);
    color: var(--muted);
    cursor: pointer;
  }

  .shell[data-focus] .rail-toggle .icon {
    width: 14px;
    height: 14px;
  }

  .shell[data-focus] .sidebar {
    position: fixed;
  }

  .shell[data-focus] .brand {
    padding-right: 36px;
  }
}

/* The rail's rules, twice: collapsed by a person's choice at any width, and by default below 1440px. */
@media (min-width: 1024px) {
  .shell[data-focus][data-rail="collapsed"] .sidebar {
    width: var(--rail-w);
    overflow-x: hidden;
  }

  .shell[data-focus][data-rail="collapsed"] .shell-main {
    margin-left: var(--rail-w);
  }

  .shell[data-focus][data-rail="collapsed"] :is(.brand-text, .nav-heading, .nav-label, .watcher-text, .sidebar-username) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .shell[data-focus][data-rail="collapsed"] .brand {
    justify-content: center;
    padding: 16px 0 36px;
  }

  .shell[data-focus][data-rail="collapsed"] .rail-toggle {
    top: 54px;
    right: 20px;
    transform: rotate(180deg);
  }

  .shell[data-focus][data-rail="collapsed"] :is(.nav, .sidebar-foot) {
    padding-left: 8px;
    padding-right: 8px;
  }

  .shell[data-focus][data-rail="collapsed"] .nav-item {
    justify-content: center;
    padding: 10px 0;
  }

  .shell[data-focus][data-rail="collapsed"] .nav-count {
    position: absolute;
    top: 6px;
    right: 10px;
    min-width: 0;
    width: 8px;
    height: 8px;
    padding: 0;
    font-size: 0;
    background: var(--tone);
  }

  .shell[data-focus][data-rail="collapsed"] :is(.watcher, .sidebar-user) {
    justify-content: center;
    flex-direction: column;
  }
}

@media (min-width: 1024px) and (max-width: 1439.98px) {
  .shell[data-focus]:not([data-rail]) .sidebar,
  .shell[data-focus][data-rail="collapsed"] .sidebar {
    width: var(--rail-w);
    overflow-x: hidden;
  }

  .shell[data-focus]:not([data-rail]) .shell-main {
    margin-left: var(--rail-w);
  }

  .shell[data-focus]:not([data-rail]) :is(.brand-text, .nav-heading, .nav-label, .watcher-text, .sidebar-username) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .shell[data-focus]:not([data-rail]) .brand {
    justify-content: center;
    padding: 16px 0 36px;
  }

  .shell[data-focus]:not([data-rail]) .rail-toggle {
    top: 54px;
    right: 20px;
    transform: rotate(180deg);
  }

  .shell[data-focus]:not([data-rail]) :is(.nav, .sidebar-foot) {
    padding-left: 8px;
    padding-right: 8px;
  }

  .shell[data-focus]:not([data-rail]) .nav-item {
    justify-content: center;
    padding: 10px 0;
  }

  .shell[data-focus]:not([data-rail]) .nav-count {
    position: absolute;
    top: 6px;
    right: 10px;
    min-width: 0;
    width: 8px;
    height: 8px;
    padding: 0;
    font-size: 0;
    background: var(--tone);
  }

  .shell[data-focus]:not([data-rail]) :is(.watcher, .sidebar-user) {
    justify-content: center;
    flex-direction: column;
  }
}

/* --- menus (the account menu) --- */

.menu {
  position: relative;
}

.menu-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 4px 8px 4px 4px;
  border-radius: var(--radius-md);
  list-style: none;
  cursor: pointer;
  color: var(--text-2);
}

.menu-trigger:hover {
  background: var(--surface-2);
}

.menu-trigger .icon {
  width: 16px;
  height: 16px;
}

.menu-name {
  font-weight: 600;
  color: var(--text);
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* An invisible backdrop: a click anywhere else closes the menu, with no JavaScript. */
.menu[open] > summary::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 40;
  cursor: default;
}

.menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 50;
  display: grid;
  gap: 2px;
  min-width: 220px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-md);
}

.menu-head {
  padding: 8px 12px 10px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
}

.menu-head b {
  color: var(--text);
}

.menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  padding: 6px 12px;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-2);
  font: inherit;
  font-weight: 600;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu-item:hover {
  background: var(--surface-2);
  color: var(--text);
}

.menu-item-danger {
  color: var(--danger-text);
}

.menu-item-danger:hover {
  background: var(--danger-light);
  color: var(--danger-text);
}

.menu-item .icon {
  width: 16px;
  height: 16px;
}

/* A choice with a second line under it: what it covers, in numbers. */
.menu-item-text {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.menu-item-sub {
  font-size: 12px;
  font-weight: 400;
  color: var(--muted);
}

.menu-item:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.menu-item:disabled:hover {
  background: transparent;
  color: var(--text-2);
}

/* --- the Queue's Relink: a button that opens two choices --- */

.relink-menu > summary {
  list-style: none;
}

/* The menu's backdrop is fixed inside the summary; a pressed button's shift would move it. */
.relink-menu > .btn:active:not(:disabled) {
  transform: none;
}

.relink-menu[open] > summary {
  background: var(--surface-2);
}

.relink-menu .menu-panel {
  min-width: 290px;
  max-width: calc(100vw - 32px);
}

.relink-menu .menu-head {
  line-height: 1.45;
}

.relink-form {
  margin: 0;
}

/* On a narrow screen the buttons wrap and this one may sit at the left edge, where a panel hung
   from its right edge would leave the screen. There the panel spans the row of buttons instead. */
@media (max-width: 767.98px) {
  .page-actions {
    position: relative;
  }

  .relink-menu {
    position: static;
  }

  .relink-menu .menu-panel {
    left: 0;
    right: 0;
    min-width: 0;
    max-width: none;
  }
}

.theme-form {
  margin: 0;
}

.theme-icon {
  display: inline-flex;
}

/* A display rule beats the hidden attribute's UA style: without this all three icons show. */
/* hidden always hides: a component's own display (.btn is inline-flex) must not show a control
   that is rendered hidden until JavaScript can make it work. */
[hidden] {
  display: none !important;
}

.theme-icon[hidden] {
  display: none;
}

/* --- banners --- */

.banners {
  display: grid;
}

.banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 24px;
  background: var(--tone-bg);
  color: var(--tone-text);
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  font-weight: 600;
}

/* --- toasts, below the topbar --- */

.toasts {
  position: fixed;
  top: calc(var(--topbar-h) + 12px);
  right: 16px;
  z-index: 100;
  display: grid;
  gap: 8px;
  width: min(440px, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 10px 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-md);
  pointer-events: auto;
  animation: toast-in 0.18s ease-out;
  transition: opacity 0.3s, transform 0.3s;
}

.toast-icon {
  display: inline-flex;
  color: var(--tone-text);
}

.toast-text {
  flex: 1;
  min-width: 0;
}

.toast-hide {
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

/* ==== component: tone ==== */

/* The six tones of STAGE_TONE / REASON_TONE. A component reads --tone (a bar, a dot, a chart
   segment), --tone-bg and --tone-text (a badge, a pill, a count). */

.tone-neutral {
  --tone: var(--neutral);
  --tone-bg: var(--neutral-light);
  --tone-text: var(--neutral-text);
}

.tone-info {
  --tone: var(--info);
  --tone-bg: var(--info-light);
  --tone-text: var(--info-text);
}

.tone-success {
  --tone: var(--success);
  --tone-bg: var(--success-light);
  --tone-text: var(--success-text);
}

.tone-warning {
  --tone: var(--warning);
  --tone-bg: var(--warning-light);
  --tone-text: var(--warning-text);
}

.tone-danger {
  --tone: var(--danger);
  --tone-bg: var(--danger-light);
  --tone-text: var(--danger-text);
}

.tone-accent {
  --tone: var(--accent);
  --tone-bg: var(--accent-light);
  --tone-text: var(--accent);
}

/* Charts and legends: SVG shapes and swatches take the tone as their fill. */
svg [class*="tone-"] {
  fill: var(--tone);
}

.swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 3px;
  background: var(--tone, var(--neutral));
}

/* ==== component: button ==== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font: inherit;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.12s, border-color 0.12s, color 0.12s, box-shadow 0.12s;
}

.btn .icon {
  width: 16px;
  height: 16px;
}

.btn-sm {
  min-height: 32px;
  padding: 0 10px;
  font-size: 13px;
}

.btn-md {
  min-height: 36px;
}

.btn-lg {
  min-height: 44px;
  padding: 0 18px;
  font-size: 15px;
}

.btn-icon {
  padding: 0;
  width: 36px;
}

.btn-sm.btn-icon {
  width: 32px;
}

.btn-lg.btn-icon {
  width: 44px;
}

.btn-block {
  width: 100%;
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-fg);
}

.btn-primary:hover {
  background: var(--accent-hover);
  color: var(--accent-fg);
}

.btn-secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
  box-shadow: var(--shadow-sm);
}

.btn-secondary:hover {
  background: var(--surface-2);
  border-color: var(--border-control);
  color: var(--text);
}

.btn-ghost,
.btn-quiet {
  background: transparent;
  color: var(--text-2);
}

.btn-ghost:hover,
.btn-quiet:hover {
  background: var(--surface-2);
  color: var(--text);
}

.btn-success {
  background: var(--success);
  color: var(--accent-fg);
}

.btn-success:hover {
  background: var(--success-text);
  color: var(--accent-fg);
}

.btn-danger {
  background: var(--surface);
  border-color: var(--danger);
  color: var(--danger-text);
}

.btn-danger:hover {
  background: var(--danger-light);
  color: var(--danger-text);
}

/* The confirm dialog's danger button, and anywhere a destructive action is the main one. */
.btn-danger-solid {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--accent-fg);
}

.btn-danger-solid:hover {
  background: var(--danger-text);
  border-color: var(--danger-text);
  color: var(--accent-fg);
}

.btn:disabled,
.btn.is-disabled,
.btn[aria-busy="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn.is-disabled {
  pointer-events: none;
}

.btn:active:not(:disabled) {
  transform: translateY(1px);
}

.kbd {
  display: inline-block;
  min-width: 20px;
  padding: 0 5px;
  border: 1px solid currentColor;
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
  opacity: 0.7;
}

.btn .kbd {
  margin-left: 2px;
}

@media (max-width: 767.98px) {
  .btn .kbd {
    display: none;
  }
}

/* ==== component: badge ==== */

.badge,
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--tone-bg, var(--neutral-light));
  color: var(--tone-text, var(--neutral-text));
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  white-space: nowrap;
}

.badge .icon {
  width: 12px;
  height: 12px;
}

.status-pill {
  padding: 3px 10px;
}

.status-dot {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
  background: var(--tone, var(--neutral));
}

.status-dot.is-pulse {
  animation: pulse-dot 1.6s ease-in-out infinite;
}

/* ==== component: card ==== */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-sm);
}

.card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 20px 0;
}

.card-head-text {
  min-width: 0;
}

.card-title {
  font-size: 15px;
  font-weight: 700;
}

.card-meta {
  margin-top: 2px;
  font-size: 12px;
  color: var(--muted);
}

.card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.card-body {
  padding: 16px 20px 20px;
}

.card-body > * + * {
  margin-top: 12px;
}

.card-flush .card-body {
  padding: 12px 0 0;
}

.card-flush .card-head + .card-body {
  padding-top: 12px;
}

.card-foot {
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
}

.block-error {
  display: flex;
  gap: 12px;
  padding: 16px 20px;
  border-color: var(--danger);
  background: var(--danger-light);
  color: var(--danger-text);
}

.block-error-icon {
  display: inline-flex;
}

.block-error-title {
  font-weight: 700;
}

.block-error-text {
  margin-top: 2px;
  font-size: 13px;
}

.block-error code {
  background: transparent;
  border-color: currentColor;
}

/* ==== component: page-header ==== */

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  flex-wrap: wrap;
}

.page-header-text {
  min-width: 0;
}

.page-title {
  font-size: 24px;
  font-weight: 800;
  overflow-wrap: anywhere;
}

.page-subtitle {
  margin-top: 2px;
  max-width: 80ch;
  font-size: 14px;
  color: var(--muted);
}

.page-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* ==== component: stat ==== */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 16px;
}

.stat {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.15s;
}

.stat-has-link:hover {
  box-shadow: var(--shadow-md);
}

.stat-bar {
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--tone);
}

.stat-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  order: 2;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  text-decoration: none;
}

.stat-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.stat-link:hover {
  color: var(--text);
}

.stat-icon {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-md);
  background: var(--tone-bg);
  color: var(--tone-text);
}

.stat-icon .icon {
  width: 16px;
  height: 16px;
}

.stat-value {
  order: 1;
  font-size: 28px;
  font-weight: 800;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.stat-hint {
  order: 3;
  font-size: 12px;
  color: var(--muted);
}

.stat-action {
  order: 4;
  position: relative;
  z-index: 1;
  margin-top: 10px;
}

/* ==== component: table ==== */

.table-wrap {
  overflow-x: auto;
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.table th,
.table td {
  padding: 12px 16px;
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--border);
}

.table th {
  padding-top: 10px;
  padding-bottom: 10px;
  background: var(--surface-2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

.table tbody tr:last-child td {
  border-bottom: 0;
}

.table tbody tr:hover td {
  background: var(--surface-2);
}

.table .cell-end {
  text-align: right;
}

.table .cell-nowrap {
  white-space: nowrap;
}

.sort {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: inherit;
  text-decoration: none;
}

.sort:hover,
.sort.is-sorted {
  color: var(--text);
}

.sort .icon {
  width: 12px;
  height: 12px;
}

th[aria-sort="ascending"] .sort .icon {
  transform: rotate(180deg);
}

/* A whole row opens its link; controls in other cells stay above it. */
.table tr.has-link {
  position: relative;
}

.row-link {
  color: inherit;
  text-decoration: none;
}

.row-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.row-link:focus-visible {
  outline: 0;
}

/* The row's link draws no outline of its own: the whole row shows the focus, at every width —
   a 2px ring in --focus on the row's cells, and the tint as an extra. Keyboard focus only. */
.table tr.has-link:has(.row-link:focus-visible) td {
  background: var(--accent-light);
  box-shadow: inset 0 2px 0 var(--focus), inset 0 -2px 0 var(--focus);
}

.table tr.has-link:has(.row-link:focus-visible) td:first-child {
  box-shadow: inset 2px 2px 0 var(--focus), inset 0 -2px 0 var(--focus);
}

.table tr.has-link:has(.row-link:focus-visible) td:last-child {
  box-shadow: inset -2px 2px 0 var(--focus), inset 0 -2px 0 var(--focus);
}

.cell-value {
  display: block;
  min-width: 0;
}

.table td :is(a:not(.row-link), button, form, input, select) {
  position: relative;
  z-index: 1;
}

.cell-key {
  display: block;
  max-width: 44ch;
  font-family: var(--font-mono);
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A video's name in a cell, over its key in .cell-key.cell-sub. */
.cell-name {
  display: block;
  max-width: 44ch;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cell-sub {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--muted);
}

.card-flush .table-wrap {
  border-top: 1px solid var(--border);
}

.card-flush .card-head + .card-body .table-wrap {
  margin-top: 0;
}

.table-wrap.card {
  overflow: hidden;
}

.is-undone {
  opacity: 0.6;
}

.is-undone b {
  text-decoration: line-through;
}

/* Below 768px a table becomes a stack of cards: each cell shows its column's label. */
@media (max-width: 767.98px) {
  .table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }

  .table,
  .table tbody,
  .table tr,
  .table td {
    display: block;
    width: 100%;
  }

  /* minmax(0, 1fr): a card is as wide as the table, never as wide as its longest nowrap text. */
  .table tbody {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
    padding: 10px;
  }

  .table tr {
    min-width: 0;
    padding: 8px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
  }

  .table td,
  .table tbody tr:last-child td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
    text-align: right;
  }

  .table td:last-child,
  .table tbody tr:last-child td:last-child {
    border-bottom: 0;
  }

  .table td[data-label]::before {
    content: attr(data-label);
    flex: none;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    text-align: left;
  }

  .table td > * {
    min-width: 0;
  }

  /* The value: one flex item beside the label, its parts stacked and right-aligned. */
  .table td > .cell-value {
    display: grid;
    flex: 1 1 auto;
    justify-items: end;
    gap: 2px;
    text-align: right;
  }

  .table td > .cell-value > * {
    max-width: 100%;
  }

  .table tbody tr:hover td,
  .table tr.has-link:has(.row-link:focus-visible) td,
  .table tr.has-link:has(.row-link:focus-visible) td:first-child,
  .table tr.has-link:has(.row-link:focus-visible) td:last-child {
    background: transparent;
    box-shadow: none;
  }

  .table tr.has-link:has(.row-link:focus-visible) {
    outline: 2px solid var(--focus);
  }

  .cell-key,
  .cell-name {
    max-width: 100%;
  }
}

/* ==== component: tabs ==== */

/* The tabs wrap onto a second row rather than scroll: a scrollbar under a tab row reads as broken.
   Each tab draws its own underline, so a wrapped row still looks like tabs. */
.tabnav {
  border-bottom: 1px solid var(--border);
}

.tabnav-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0 2px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.tabnav-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 2px solid transparent;
  color: var(--text-2);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.tabnav-item:hover {
  color: var(--text);
}

.tabnav-item.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.tabnav-item .icon {
  width: 16px;
  height: 16px;
}

.tabnav-count {
  min-width: 22px;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--tone-bg);
  color: var(--tone-text);
  font-size: 12px;
  font-weight: 700;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ==== component: filter-bar ==== */

.filter-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.filter-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 260px;
}

.filter-search .icon {
  position: absolute;
  left: 12px;
  width: 16px;
  height: 16px;
  color: var(--muted);
  pointer-events: none;
}

.filter-search .input {
  padding-left: 36px;
}

.filter-select {
  flex: 0 1 200px;
}

/* A box whose label is shown in front of it: the label stays on one line, the box takes the rest. */
.filter-select.has-label {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 1 280px;
}

.filter-label {
  flex: none;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}

.filter-select.has-label .select {
  flex: 1 1 auto;
  min-width: 0;
}

/* ==== component: pagination ==== */

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  flex-wrap: wrap;
}

.pager-info {
  font-size: 13px;
  color: var(--muted);
}

.pager-info b {
  color: var(--text-2);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.pager-links {
  display: flex;
  align-items: center;
  gap: 2px;
}

.pager-page {
  padding: 0 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
}

.card-flush .pager {
  padding: 10px 16px;
  border-top: 1px solid var(--border);
}

/* ==== component: empty-state ==== */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 56px 24px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-xl);
  background: var(--surface);
  text-align: center;
}

.card .empty {
  border: 0;
  padding: 40px 16px;
}

.empty-icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin-bottom: 14px;
  border-radius: var(--radius-xl);
  background: var(--accent-light);
  color: var(--accent);
}

.empty-icon .icon {
  width: 24px;
  height: 24px;
}

.empty-no-results .empty-icon {
  background: var(--neutral-light);
  color: var(--neutral-text);
}

.empty-title {
  font-size: 17px;
  font-weight: 700;
}

.empty-text {
  max-width: 52ch;
  margin-top: 6px;
  color: var(--muted);
}

.empty-action {
  margin-top: 18px;
}

/* The slim empty state: icon beside the words, left-aligned, for a page whose real content is
 * right below it. The full-size one above is for a page with nothing else on it. */
.empty-compact {
  flex-direction: row;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  text-align: left;
}

.empty-compact .empty-icon {
  flex: none;
  width: 36px;
  height: 36px;
  margin-bottom: 0;
  border-radius: var(--radius-lg);
}

.empty-compact .empty-icon .icon {
  width: 18px;
  height: 18px;
}

.empty-compact .empty-title {
  font-size: 14px;
}

.empty-compact .empty-text {
  max-width: none;
  margin-top: 2px;
  font-size: 13px;
}

.empty-compact .empty-action {
  margin-top: 0;
  margin-left: auto;
}

/* A form laid out in rows of related fields rather than one tall column: Name beside Address,
 * the sign-in pair together, the three post settings on one line. Asked of the CARD's width,
 * so a narrow column or a phone gets one field per line again. */
.form-card {
  container-type: inline-size;
}

.form-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px 16px;
  align-items: start;
}

@container (min-width: 620px) {
  .form-grid.cols-2,
  .form-grid.cols-3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@container (min-width: 900px) {
  .form-grid.cols-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* What Verify found, under the buttons: a sentence, then the three questions ticked or crossed.
 * Before Verify is pressed it is only a hint line, so a greyed-out Add site never stands there
 * with no reason beside it. */
.verify-result {
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  font-size: 13px;
}

.verify-result.is-good {
  border-color: var(--success);
  background: var(--success-light);
}

.verify-result.is-bad {
  border-color: var(--danger);
  background: var(--danger-light);
}

.verify-result.is-hint {
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted);
}

.verify-said {
  font-weight: 600;
}

.verify-result.is-good .verify-said {
  color: var(--success-text);
}

.verify-result.is-bad .verify-said {
  color: var(--danger-text);
}

.verify-result.is-hint .verify-said {
  font-weight: 400;
}

.verify-checks {
  display: grid;
  gap: 4px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.verify-checks li {
  position: relative;
  padding-left: 22px;
  color: var(--text-2);
}

.verify-checks li::before {
  content: '✓';
  position: absolute;
  left: 2px;
  font-weight: 700;
  color: var(--success-text);
}

.verify-checks li.is-bad::before {
  content: '✕';
  color: var(--danger-text);
}

/* Verified, with a field worth a second look: amber, between the green and the red. */
.verify-result.is-warn {
  border-color: var(--warning);
  background: var(--warning-light);
}

.verify-result.is-warn .verify-said {
  color: var(--warning-text);
}

/* Step 2 of the Destinations form: what the site offers, shown once it has answered. The dashed
 * line says "this came from the site", not "this is another form". */
.site-section {
  padding-top: 16px;
  border-top: 1px dashed var(--border-strong);
}

/* Under each of the theme's boxes: what Verify found for it. */
.field-check {
  position: relative;
  margin-top: 6px;
  padding-left: 18px;
  font-size: 12px;
  color: var(--muted);
}

.field-check::before {
  position: absolute;
  left: 0;
  font-weight: 700;
}

.field-check.is-found {
  color: var(--success-text);
}

.field-check.is-found::before {
  content: '✓';
}

.field-check.is-missing {
  color: var(--warning-text);
}

.field-check.is-missing::before {
  content: '!';
}

.field-check.is-bad {
  color: var(--danger-text);
}

.field-check.is-bad::before {
  content: '✕';
}

.field-check.is-unknown::before {
  content: '·';
}

/* ==== component: field ==== */

.field {
  display: grid;
  gap: 6px;
}

.field-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

.field-hint {
  font-size: 12px;
  color: var(--muted);
}

.field-error {
  font-size: 12px;
  font-weight: 600;
  color: var(--danger-text);
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.textarea {
  min-height: 96px;
  padding: 10px 12px;
  resize: vertical;
}

.input::placeholder,
.textarea::placeholder {
  color: var(--muted);
  opacity: 1;
}

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--focus);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--focus) 30%, transparent);
}

.has-error :is(.input, .select, .textarea) {
  border-color: var(--danger);
}

.alert {
  display: flex;
  gap: 10px;
  padding: 11px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  font-size: 14px;
}

.alert-error {
  background: var(--danger-light);
  color: var(--danger-text);
  border-color: var(--danger);
}

.alert-info {
  background: var(--info-light);
  color: var(--text);
  border-color: var(--info);
}

/* ==== component: dialog ==== */

/* margin: auto is what centres a modal dialog, and it has to be said here: a dialog written inside
   a page is a child of .page, whose `> * + *` spacing would otherwise give it a top margin and pin
   it to the top of the window. */
.dialog {
  z-index: 150;
  width: min(460px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  margin: auto;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lg);
}

.dialog::backdrop {
  background: rgb(10 12 20 / 0.5);
}

.dialog[open] {
  animation: dialog-in 0.16s ease-out;
}

@keyframes dialog-in {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }
}

.dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 20px 0;
}

.dialog-title {
  font-size: 16px;
  font-weight: 700;
}

.dialog-body {
  padding: 14px 20px;
}

.dialog-body > * + * {
  margin-top: 14px;
}

/* The hover preview's popup (views/preview.ts): the clip at its own size — 320×180 — on black, so
   a person sees exactly what a visitor of the site sees on hover. */
.dialog-preview {
  width: min(380px, calc(100vw - 32px));
}

.preview-player {
  display: block;
  width: 320px;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  margin-inline: auto;
  border-radius: var(--radius-md);
  background: #000;
}

/* The line under a video's name on the Queue: made, being made, failed. */
.preview-state {
  white-space: nowrap;
}

/* ▶ Preview: small, under "Preview ready" on the Queue and beside the words on a video's page. */
.btn.btn-preview {
  min-height: 24px;
  margin-top: 4px;
  padding: 0 8px;
  gap: 4px;
  font-size: 12px;
}

.btn.btn-preview .icon {
  width: 12px;
  height: 12px;
}

/* On a video's page: the words, then the button, on one line when there is room. */
.vd-preview {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
}

.dialog-scope {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 13px;
}

.dialog-scope:empty {
  display: none;
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  flex-wrap: wrap;
  padding: 0 20px 18px;
}

/* What a dialog is about — a video's title over its key — with its verdict at the end of the line. */
.dialog-subject {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.dialog-subject-text {
  display: grid;
  gap: 2px;
  min-width: 0;
  overflow-wrap: anywhere;
}

.dialog-subject .key {
  color: var(--muted);
  font-size: 12px;
}

.dialog-subject .badge {
  flex: none;
}

/* A dialog that says several things says each under a small heading of its own. */
.dialog-section-title {
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 700;
}

.dialog-section {
  font-size: 13px;
}

.dialog-section .vd-deleted {
  margin-top: 0;
}

.dialog-section .small {
  margin-top: 8px;
}

/* Delete this file? on a video's page: four sentences and a warning need more room than a yes/no. */
.dialog-delete {
  width: min(560px, calc(100vw - 32px));
}

/* ==== component: meter ==== */

.meter-row {
  display: grid;
  gap: 6px;
}

.meter-head,
.bar-head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
}

.meter-label,
.bar-label {
  min-width: 0;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.meter-value,
.bar-value {
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.meter {
  display: block;
  width: 100%;
  height: 8px;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  -webkit-appearance: none;
  appearance: none;
  overflow: hidden;
}

.meter::-webkit-meter-bar {
  height: 8px;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
}

.meter::-webkit-meter-optimum-value,
.meter::-webkit-meter-suboptimum-value,
.meter::-webkit-meter-even-less-good-value {
  border-radius: 999px;
  background: var(--tone);
}

.meter::-moz-meter-bar {
  border-radius: 999px;
  background: var(--tone);
}

.bar-list {
  display: grid;
  gap: 12px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.bar-item {
  display: grid;
  gap: 6px;
}

.bar-track {
  display: block;
  width: 100%;
  height: 8px;
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--border);
  overflow: hidden;
}

/* ==== component: progress ==== */

.progress {
  display: block;
  width: 100%;
  height: 6px;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--accent);
  -webkit-appearance: none;
  appearance: none;
  overflow: hidden;
}

.progress::-webkit-progress-bar {
  border-radius: 999px;
  background: var(--surface-2);
}

.progress::-webkit-progress-value {
  border-radius: 999px;
  background: var(--tone, var(--accent));
  transition: width 0.2s;
}

.progress::-moz-progress-bar {
  border-radius: 999px;
  background: var(--tone, var(--accent));
}

/* ==== component: timeline ==== */

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
}

.step {
  position: relative;
  display: flex;
  gap: 12px;
  padding-bottom: 16px;
}

.step:last-child {
  padding-bottom: 0;
}

.step:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 10px;
  top: 24px;
  bottom: 2px;
  width: 2px;
  background: var(--border);
}

.step-done:not(:last-child)::before {
  background: var(--success);
}

.step-mark {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  background: var(--surface);
  color: var(--accent-fg);
}

.step-mark .icon {
  width: 12px;
  height: 12px;
  stroke-width: 3;
}

.step-done .step-mark {
  border-color: var(--success);
  background: var(--success);
}

.step-current .step-mark {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 4px var(--surface), inset 0 0 0 10px var(--accent);
}

.step-label {
  font-weight: 600;
}

.step-pending .step-label {
  color: var(--muted);
}

.step-time {
  font-size: 12px;
  color: var(--muted);
}

.step-detail {
  margin-top: 4px;
  font-size: 13px;
  color: var(--text-2);
}

/* ==== component: thumb ==== */

.thumb-tile {
  display: grid;
  place-items: center;
  width: 96px;
  aspect-ratio: 16 / 9;
  flex: none;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--thumb-bg);
}

.thumb-tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.thumb-placeholder {
  background: var(--neutral-light);
  color: var(--tone-text, var(--neutral-text));
  border: 1px solid var(--border);
}

.thumb-placeholder .icon {
  width: 18px;
  height: 18px;
}

img.thumb-missing {
  opacity: 0.4;
}

/* ==== page: overview ==== */

/* The page is the refresh root, so its own children need the page's rhythm. */
.overview > * + * {
  margin-top: 20px;
}

.overview-updated {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  white-space: nowrap;
}

.overview-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}

@media (min-width: 1024px) {
  .overview-grid {
    grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr);
  }
}

/* A refresh block is only a wrapper: the card inside it fills the grid cell. */
.overview-block > .card {
  height: 100%;
}

.overview-stats .stat-action .btn {
  margin-top: 8px;
}

/* Charts are used on this page alone: their CSS lives here. Colour comes from .tone-<tone>. */
.chart {
  margin: 0;
}

.chart-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* Per day: the SVG is shapes only, stretched to the width at a fixed height (COLS_H in charts.ts),
   so its labels are HTML at the caption size at every width. */
.chart-plot {
  position: relative;
}

.chart-bars .chart-svg {
  height: 200px;
}

.chart-plot .chart-tick {
  position: absolute;
  top: 0;
  left: 0;
  line-height: 16px;
}

.chart-axis-row {
  display: flex;
  height: 22px;
  margin-top: 4px;
}

.chart-axis-slot {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
}

.chart-axis-slot .chart-axis {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  line-height: 16px;
}

.chart-bar .chart-svg {
  height: 20px;
  border-radius: var(--radius-sm, 6px);
  overflow: hidden;
}

.chart-track {
  fill: var(--bg);
}

.chart-grid {
  stroke: var(--border);
  stroke-dasharray: 3 4;
}

.chart-baseline {
  stroke: var(--border-strong, var(--border));
}

.chart-tick,
.chart-axis {
  fill: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
}

.chart-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.chart-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 3px;
  background: var(--tone, var(--neutral));
}

.chart-legend-value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.chart-legend-share {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.activity-line {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.activity-icon {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 999px;
  background: var(--tone-bg);
  color: var(--tone-text);
}

.activity-icon .icon {
  width: 15px;
  height: 15px;
}

.activity-text {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* ==== page: review ==== */

/* The filter bar sits at the top of a flush card, above the table. */
.review-filters {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}

.score {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.score b {
  font-size: 15px;
}

/* ==== page: duplicates ==== */

/* The filter bar sits at the top of a flush card, above the table. */
.duplicates-filters {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}

.badge-row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* ==== page: history ==== */

/* ==== page: videos ==== */

.videos-controls {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 16px;
}

.videos-name {
  display: block;
  max-width: 44ch;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* At most two lines: the pill says the stage, the summary only adds to it. */
.videos-summary {
  display: -webkit-box;
  max-width: 36ch;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.videos-table .videos-thumb-cell {
  width: 112px;
}

/* The Video column is sized by its longest name or key: capped here, the seven columns fit the
   card at 1280px beside the sidebar. The full value is in the title and on the video's page. */
.videos-table .videos-name,
.videos-table .cell-key {
  max-width: 32ch;
}

/* A row's first cell sits inside its inline link, which max-width cannot bound: in the stacked
   layout the link lays its parts out like the value wrapper does. */
@media (max-width: 767.98px) {
  .table td > .cell-value > .row-link {
    display: grid;
    justify-items: end;
    gap: 2px;
    min-width: 0;
    max-width: 100%;
  }

  .table td > .cell-value > .row-link > * {
    max-width: 100%;
  }
}

@media (max-width: 767.98px) {
  .videos-table .videos-thumb-cell .thumb-tile {
    width: 100%;
    max-width: 240px;
  }

  /* The cell's value wrapper bounds the width now; the parts only need to fit inside it. */
  .videos-table .videos-name,
  .videos-table .cell-key,
  .videos-summary {
    max-width: 100%;
  }
}

/* Where a scraped video came from, under its name: the site, and the page's host as a link out. */
.videos-source {
  max-width: 32ch;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The run a list is narrowed to, named as its card on Scrape names it, with the way back to it. */
.videos-run {
  margin-bottom: 16px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--accent-light);
}

.videos-run > * + * {
  margin-top: 8px;
}

.videos-run-name {
  margin: 0;
  font-size: 14px;
}

.videos-run-note {
  margin: 0;
  color: var(--text-2);
  font-size: 13px;
}

@media (max-width: 767.98px) {
  .videos-source {
    max-width: 100%;
  }
}

/* ==== page: video-detail ==== */

.vd-title {
  overflow-wrap: anywhere;
}

.vd-key-line {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 100%;
}

.vd-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  align-items: stretch;
}

@media (min-width: 1100px) {
  .vd-grid {
    grid-template-columns: minmax(0, 1fr) 340px;
  }
}

.vd-below {
  margin-top: 16px;
}

.vd-player {
  max-width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--thumb-bg);
}

.vd-player video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--thumb-bg);
}

.vd-frames {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
}

.vd-frame .thumb-tile {
  width: 100%;
}

.vd-frame.is-flat .thumb-tile {
  opacity: 0.55;
}

.vd-frame-cap {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.vd-facts {
  margin: 0;
}

.vd-fact {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}

.vd-fact:last-child {
  border-bottom: 0;
}

.vd-fact dt {
  flex: none;
  font-size: 13px;
  color: var(--muted);
}

.vd-fact dd {
  min-width: 0;
  margin: 0;
  text-align: right;
  overflow-wrap: anywhere;
}

.vd-summary {
  margin: 0;
  color: var(--text-2);
}

.vd-verdict-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 10px 0 0;
}

.vd-failure-facts {
  margin: 0 0 6px;
}

.vd-failure {
  max-height: 220px;
  margin: 0;
  overflow: auto;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  font-family: var(--font-mono);
  font-size: 12px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* A deleted file is a deliberate, safe act, not a fault: muted, not amber. What it has to say
   is that the fingerprint stayed behind, so it reads as information rather than an alarm. */
.vd-deleted {
  margin: 12px 0 0;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 13px;
}

/* A new upload being made into an .mp4 before it is checked: information, the same quiet note.
   Above the player, so it says why the stage is Queued before anyone wonders. */
.vd-note {
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 13px;
}

.vd-warning {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--warning-light);
  color: var(--warning-text);
  font-size: 13px;
}

/* Where it came from, heading the page: the site's name, then the page it was taken from. */
.vd-source-line {
  display: inline-block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}

.vd-source-line .icon {
  width: 13px;
  height: 13px;
  margin-left: 4px;
  vertical-align: -1px;
}

/* The videos beside this one — its copies, or its original: a picture, a name, where and when. */
.vd-members {
  margin: 0;
  padding: 0;
  list-style: none;
}

.vd-member {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
  padding: 8px 0;
}

.vd-member + .vd-member {
  border-top: 1px solid var(--border);
}

.vd-member-text {
  display: block;
  min-width: 0;
}

.vd-member-title {
  display: block;
  overflow: hidden;
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vd-member-title:hover {
  text-decoration: underline;
}

.vd-member-side {
  color: var(--muted);
  font-size: 12px;
  text-align: right;
}

.vd-sub {
  margin: 18px 0 0;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
}

@media (max-width: 559.98px) {
  .vd-member {
    grid-template-columns: 72px minmax(0, 1fr);
  }

  .vd-member .thumb-tile {
    width: 72px;
  }

  .vd-member-side {
    grid-column: 2;
    text-align: left;
  }
}

/* Where it is posted: one line per site, what the group has there on the right. */
.vd-posts {
  margin: 0;
  padding: 0;
  list-style: none;
}

.vd-post-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px 12px;
  padding: 10px 0;
}

.vd-post-line + .vd-post-line {
  border-top: 1px solid var(--border);
}

.vd-post-site {
  min-width: 0;
}

.vd-post-site b {
  display: block;
}

.vd-post-state {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

/* What the button on a site's line does, said once under the list. */
.vd-post-help {
  margin: 10px 0 0;
}

.vd-post-refusal {
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 13px;
}

/* Frames, matches and history: folded under one line, as wide as the cards above it. */
.vd-more {
  margin-top: 16px;
}

.vd-more > summary {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  padding: 14px 20px 14px 42px;
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  list-style: none;
  cursor: pointer;
}

.vd-more > summary::-webkit-details-marker {
  display: none;
}

.vd-more > summary:hover {
  border-color: var(--border-strong);
}

.vd-more > summary:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* A chevron drawn in borders: pointing right while folded, down once open. */
.vd-more > summary::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 20px;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: translateY(-70%) rotate(-45deg);
  transition: transform 0.15s ease;
}

.vd-more[open] > summary::before {
  transform: translateY(-70%) rotate(45deg);
}

.vd-more-title {
  font-weight: 700;
}

.vd-more-body {
  margin-top: 16px;
}

@media (prefers-reduced-motion: reduce) {
  .vd-more > summary::before {
    transition: none;
  }
}

/* ==== page: queue ==== */

/* The bar above the Queue's table: how many rows are ticked, and the verbs that act on them all. */
.queue-bulk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin-bottom: 12px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.queue-bulk-all {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.queue-bulk-count {
  flex: 1;
  min-width: 14ch;
}

.queue-table .queue-select-cell {
  width: 40px;
}

/* The Videos list's bar is the Queue's bar (the queue-bulk classes). Add to Queue asks for the
   sites in a popup: one tick box per site, a line each. */
.queue-sites {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  border: 0;
}

.queue-site {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.queue-site:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-light);
}

.queue-site input {
  width: 16px;
  height: 16px;
  margin: 0;
}

/* Settings → API keys: what a new key is for — the Queue popup's rows, with a line under each name. */
.key-scopes {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  border: 0;
}

.key-scopes legend {
  padding: 0;
  margin-bottom: 4px;
}

.key-scope {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 14px;
  cursor: pointer;
}

.key-scope:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-light);
}

.key-scope input {
  width: 16px;
  height: 16px;
  margin: 2px 0 0;
  flex: none;
}

.key-scope-text {
  display: grid;
  gap: 2px;
}

.videos-table .videos-select-cell {
  width: 40px;
}

/* Delete, or Deleting, at the end of a row: as narrow as its one control. */
.videos-table .videos-actions-cell {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

/* The picture a row is recognised by, the size the Videos list gives it. */
.queue-table .queue-thumb-cell {
  width: 112px;
}

.queue-box {
  width: 16px;
  height: 16px;
  margin: 0;
  cursor: pointer;
}

.queue-pending {
  color: var(--accent);
  font-weight: 600;
}

/* ==== page: attention ==== */

.status-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
}

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

.status-list .status-dot {
  width: 8px;
  height: 8px;
  background: var(--neutral);
}

.status-alive .status-dot {
  background: var(--success);
  box-shadow: 0 0 0 4px var(--success-light);
}

.status-stale .status-dot,
.status-stopped .status-dot,
.status-never .status-dot {
  background: var(--danger);
  box-shadow: 0 0 0 4px var(--danger-light);
}

.attention-text {
  max-width: 80ch;
  color: var(--text-2);
}

/* The explanation above an edge-to-edge table keeps the card's padding. */
.card-note {
  padding: 0 20px 12px;
}

.attention-tiles {
  margin-bottom: 16px;
}

/* Eight tiles: two full rows of four where they fit, instead of five and a ragged three. */
@media (min-width: 1200px) {
  .attention-tiles {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* A video in a row, by the name a person knows it by. The key is in its title. */
.attention-name {
  display: block;
  max-width: 44ch;
  overflow: hidden;
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.attention-name:hover {
  text-decoration: underline;
}

.attention-better .attention-thumb-cell {
  width: 112px;
}

/* "and N more": below an edge-to-edge table, so it needs its own air above. */
.attention-more {
  padding-top: 12px;
}

/* The tiles link to these blocks; they land below the sticky topbar. */
.attention-sections > [id] {
  scroll-margin-top: 72px;
}

.attention-sound-counts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  margin: 16px 0;
}

.attention-sound-counts div {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}

.attention-sound-counts dt {
  font-size: 12px;
  color: var(--muted);
}

.attention-sound-counts dd {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.attention-hints {
  display: grid;
  gap: 6px;
  margin: 0;
  padding-left: 18px;
  color: var(--text-2);
  overflow-wrap: anywhere;
}

/* ==== page: uploads ==== */

/* upload.js reveals these; the attribute must win over their own display. */
[data-upload-controls][hidden],
[data-upload-nojs][hidden],
[data-upload-session][hidden] {
  display: none;
}

.uploader > * + * {
  margin-top: 14px;
}

.upload-controls {
  display: grid;
  gap: 16px;
}

.dropzone {
  display: grid;
  justify-items: center;
  gap: 6px;
  padding: 28px 16px;
  border: 2px dashed var(--border-control);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.dropzone:hover,
.dropzone.is-dragover {
  border-color: var(--accent);
  background: var(--accent-light);
}

.upload-input:focus-visible + .dropzone {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.dropzone-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--accent-light);
  color: var(--accent);
}

.dropzone-icon .icon {
  width: 22px;
  height: 22px;
}

.dropzone-title {
  font-weight: 700;
  color: var(--text);
}

.dropzone-link {
  color: var(--accent);
  text-decoration: underline;
}

.dropzone-hint {
  font-size: 12px;
  color: var(--muted);
}

.upload-fields {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

.upload-fields .textarea {
  min-height: 40px;
}

.upload-quota b {
  color: var(--text);
}

.upload-off-body {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.upload-off-icon {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--tone-bg);
  color: var(--tone-text);
}

.upload-rows {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.upload-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 16px;
  padding: 12px 0;
  border-top: 1px solid var(--border);
}

.upload-row:first-child {
  border-top: 0;
}

.upload-row-head {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}

.upload-row-name {
  min-width: 0;
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.upload-row-size {
  flex: none;
}

.upload-row-actions {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.upload-row .progress {
  grid-column: 1 / -1;
  width: 100%;
}

.upload-row-status {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  min-width: 0;
  font-size: 13px;
}

/* upload.js keeps these wrappers so a row updates in place; their children lay out as before. */
.upload-row-lead,
.upload-row-rest {
  display: contents;
}

.upload-row-detail {
  color: var(--text-2);
  overflow-wrap: anywhere;
}

.upload-row-error {
  color: var(--danger-text);
}

.card-flush .recent-tabs {
  padding: 0 20px;
}

.upload-file {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.upload-file-name {
  overflow-wrap: anywhere;
}

@media (min-width: 768px) {
  .uploads-table .cell-summary {
    min-width: 220px;
  }
}

/* ==== page: api-keys ==== */

/* .code-block and .code-row are styled under api-docs: the reveal's curl line shares them. */

.key-reveal {
  border-color: var(--success);
}

.reveal-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

.reveal-row .field {
  flex: 1;
  min-width: 0;
}

.reveal-row .input {
  font-family: var(--font-mono);
  font-size: 13px;
}

.reveal-try > * + * {
  margin-top: 6px;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.api-keys tr.is-muted td {
  color: var(--muted);
}

/* ==== page: api-docs ==== */

.api-docs {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
}

@media (min-width: 1100px) {
  .api-docs {
    grid-template-columns: 200px minmax(0, 1fr);
    align-items: start;
  }

  .docs-toc {
    position: sticky;
    top: 68px;
  }
}

.docs-toc {
  padding: 14px 16px;
}

.docs-toc-title {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.docs-toc ul {
  display: grid;
  gap: 4px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

@media (max-width: 1099.98px) {
  .docs-toc ul {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
  }
}

.docs-toc a {
  font-size: 13px;
  color: var(--text-2);
  text-decoration: none;
}

.docs-toc a:hover {
  color: var(--accent);
}

.docs-h2 {
  font-size: 18px;
  font-weight: 800;
}

.docs-endpoint .card-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.docs-path {
  padding: 0;
  border: 0;
  background: transparent;
  font-size: 14px;
  overflow-wrap: anywhere;
}

.docs-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.docs-label + .code-row {
  margin-top: 6px;
}

.docs-prose > * + * {
  margin-top: 8px;
}

.docs-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 6px;
  margin: 0;
  padding-left: 20px;
}

/* minmax(0, 1fr): an implicit grid column is sized to its widest item, and a step holding a
   code block would stretch it to the longest line — the block must scroll, not the page. */
.docs-steps {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  margin: 0;
  padding-left: 22px;
}

.docs-steps .code-row {
  margin-top: 8px;
}

.code-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.code-block {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 12px 14px;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.55;
  white-space: pre;
}

.code-block code {
  padding: 0;
  border: 0;
  background: transparent;
  font-size: inherit;
}

/* ==== page: auth ==== */

.auth-shell {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: 24px 16px;
}

.auth-main {
  width: 100%;
  display: grid;
  place-items: center;
}

.auth-card {
  width: min(420px, 100%);
  box-shadow: var(--shadow-md);
}

.auth-card .card-body {
  padding: 32px;
}

.auth-card .card-body > * + * {
  margin-top: 18px;
}

@media (max-width: 479.98px) {
  .auth-card .card-body {
    padding: 24px 20px;
  }
}

.auth-brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

.auth-brand .brand-tile {
  width: 36px;
  height: 36px;
}

.auth-head > * + * {
  margin-top: 4px;
}

.auth-title {
  font-size: 24px;
  font-weight: 800;
}

.password-field {
  position: relative;
}

.password-field .input {
  padding-right: 44px;
}

/* On the input's right edge: the input is the field's last child, 40px high. */
.password-toggle {
  position: absolute;
  right: 4px;
  bottom: 4px;
}

.password-toggle[aria-pressed="true"] {
  color: var(--accent);
}

/* ==== page: errors ==== */

.error-card {
  width: min(560px, 100%);
  margin: 40px auto 0;
  text-align: center;
}

.error-card .card-body {
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 36px 32px;
}

.error-card .card-body > * + * {
  margin-top: 0;
}

.auth-main .error-card {
  margin-top: 0;
}

.error-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--tone-bg);
  color: var(--tone-text);
}

.error-icon .icon {
  width: 26px;
  height: 26px;
}

.error-code {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.error-title {
  font-size: 24px;
  font-weight: 800;
}

.error-message {
  max-width: 46ch;
  color: var(--text-2);
}

.error-ref {
  font-size: 13px;
  color: var(--muted);
}

.error-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 8px;
}

.details {
  justify-self: stretch;
  max-height: 260px;
  overflow: auto;
  padding: 12px;
  border-radius: var(--radius-md);
  background: var(--surface-2);
  font-size: 12px;
  text-align: left;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ==== page: pair ==== */

.pair-head > * + * {
  margin-top: 8px;
}

.crumbs {
  display: flex;
  gap: 8px;
  color: var(--muted);
  font-size: 13px;
}

.crumbs a {
  color: var(--text-2);
  text-decoration: none;
}

.crumbs a:hover {
  color: var(--accent);
}

.pair-title {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.pair-title h1 {
  font-size: 20px;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.pair-explain {
  max-width: 80ch;
  color: var(--text-2);
}

.pair-nav {
  display: flex;
  gap: 8px;
}

.tabs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 2px;
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  max-width: 340px;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-2);
  text-decoration: none;
}

/* The name on top, cut with an ellipsis; the frames and the sound under it, never cut. Side by
   side, the two badges left the name no room at all. */
.tab-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.tab-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: 600;
}

.tab-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}

.tab.is-active {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 30%, transparent);
  color: var(--text);
}

.tab-n {
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: none;
  border-radius: var(--radius-sm);
  background: var(--accent-light);
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
}

.players {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.video-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
}

.video-missing {
  display: grid;
  place-items: center;
  min-height: 240px;
}

.video-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.video-role {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.video-name {
  font-size: 15px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.video-key {
  margin-top: -8px;
  font-size: 12px;
  color: var(--muted);
}

.meta {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px 16px;
  margin: 0;
}

.meta dt {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}

.meta > div {
  min-width: 0;
}

/* A long value with no spaces — an account id, a key — wraps in its own column. Left to overflow,
   it ran across the column beside it and the two texts sat on top of each other. */
.meta dd {
  margin: 1px 0 0;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.meta-wide {
  grid-column: 1 / -1;
}

.sound-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  border-left: 4px solid var(--d-na);
}

.sound-card .muted {
  margin-top: 3px;
}

.sound-sentence {
  font-weight: 600;
}

.sound-mark {
  width: 12px;
  height: 12px;
  flex: none;
  margin-top: 5px;
  border-radius: 50%;
  background: var(--d-na);
}

.sound-same {
  border-left-color: var(--d-strong);
}

.sound-same .sound-mark {
  background: var(--d-strong);
}

.sound-different {
  border-left-color: var(--d-far);
}

.sound-different .sound-mark {
  background: var(--d-far);
}

.sound-unsure {
  border-left-color: var(--d-match);
}

.sound-unsure .sound-mark {
  background: var(--d-match);
}

/* A duplicate whose sound matches no other copy in its group: the list's "Sound differs". */
.sound-differs {
  border-left-color: var(--warning);
}

.sound-differs .sound-mark {
  background: var(--warning);
}

.tab-sound {
  padding: 0 6px;
  border-radius: var(--radius-sm);
  background: var(--success-light);
  color: var(--success-text);
  font-size: 12px;
  font-weight: 700;
}

.frames-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
}

.frames-summary {
  display: flex;
  gap: 14px;
  font-size: 13px;
  color: var(--text-2);
}

.frames-summary b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  list-style: none;
  margin: 12px 0 16px;
  padding: 0;
  font-size: 12px;
  color: var(--text-2);
}

.legend li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.dot {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 3px;
}

.d-strong .frame-dist,
.dot.d-strong {
  background: var(--d-strong);
}

.d-match .frame-dist,
.dot.d-match {
  background: var(--d-match);
}

.d-far .frame-dist,
.dot.d-far {
  background: var(--d-far);
}

.d-na .frame-dist,
.dot.d-na {
  background: var(--d-na);
}

.frames {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  gap: 12px;
}

.frame {
  margin: 0;
  padding: 6px;
  border: 1px solid var(--border);
  border-top: 3px solid var(--d-na);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
}

.frame.d-strong {
  border-top-color: var(--d-strong);
}

.frame.d-match {
  border-top-color: var(--d-match);
}

.frame.d-far {
  border-top-color: var(--d-far);
}

.frame-cap {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 2px 6px;
  font-size: 12px;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.frame-no {
  font-weight: 700;
  color: var(--text);
}

.frame-pct {
  flex: 1;
}

.frame-dist {
  min-width: 26px;
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  color: var(--d-fg);
  font-weight: 700;
  text-align: center;
}

.d-na .frame-dist {
  color: var(--text-2);
}

.frame-img {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--thumb-bg);
  aspect-ratio: 16 / 9;
}

.frame-img + .frame-img {
  margin-top: 4px;
}

.frame-img img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.frame-img.is-flat::after {
  content: "blank";
  position: absolute;
  right: 5px;
  bottom: 5px;
  padding: 0 5px;
  border-radius: 4px;
  background: rgb(0 0 0 / 0.6);
  color: #fff;
  font-size: 11px;
}

.decision-bar {
  position: sticky;
  bottom: 12px;
  z-index: 25;
  padding: 12px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-xl);
  background: color-mix(in oklab, var(--surface) 94%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-md);
}

.actions,
.decided {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.actions .btn {
  min-height: 44px;
}

.actions-hint {
  flex: 1 1 280px;
  margin-left: 6px;
  color: var(--text-2);
  font-size: 13px;
  line-height: 1.45;
}

.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
}

.timeline li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-top: 1px solid var(--border);
}

.timeline li:first-child {
  border-top: 0;
}

.timeline-what {
  flex: 1;
}

@media (max-width: 900px) {
  .players {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  .frames {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  .decision-bar {
    bottom: 8px;
    padding: 10px;
  }

  .actions .btn {
    flex: 1 1 auto;
  }

  .actions-hint {
    display: none;
  }
}

/* ==== the comparison player (§13.5) ==== */
/* Everything here is driven from JavaScript by attribute, property and class — never by an
   inline style attribute in the markup (golden rule #15). data-view picks the view, data-fit
   and data-zoom the alignment; --blink-ms, the transforms and the wipe's clip-path are set
   from player.js through el.style, which the CSP allows and the html tag never writes. */

.compare {
  display: grid;
  gap: 16px;
}

.compare > * {
  min-width: 0;
}

/* The stage owns the pictures. `isolation` keeps difference blending inside it: without it
   the top pane would blend with the page behind the stage, not with the video under it. */
.compare-stage {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  isolation: isolate;
}

.compare-pane {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--thumb-bg);
}

/* Unlinked: the pane the bar, the time and the frame step belong to (§13.2). player.js sets
   it; without a mark, the next press of the play button is a guess. */
.compare-pane[data-driving] {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.compare-pane:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.compare-pane video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: var(--thumb-bg);
  transform-origin: 0 0;
}

.compare-pane-missing {
  display: grid;
  place-items: center;
  min-height: 240px;
  padding: 16px;
  background: var(--surface-2);
  text-align: center;
}

.pane-cap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 10px;
  background: var(--surface);
  color: var(--text-2);
  font-size: 12px;
}

.pane-role {
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.pane-time {
  font-variant-numeric: tabular-nums;
}

/* Held: this one's target is outside its own timeline — it waits at its start, or it has
   ended — while the other plays on. Dimmed, so nobody reads a frozen frame as the moment. */
.compare-pane[data-held] video {
  opacity: 0.45;
}

.pane-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 10px;
  padding: 16px;
  background: color-mix(in oklab, black 62%, transparent);
  color: #fff;
  text-align: center;
}

.pane-overlay-text {
  max-width: 36ch;
  font-weight: 600;
}

.pane-spinner {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border: 3px solid rgb(255 255 255 / 0.3);
  border-top-color: #fff;
  border-radius: 50%;
  animation: player-spin 800ms linear infinite;
}

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

/* --- wipe, difference and blink: both videos in one black stage --- */

.compare-stage:not([data-view="side"]) {
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: #000;
}

/* No z-index of their own: the panes stay in the stage's stacking context, so the top one
   can blend with the one under it. DOM order is what puts the match on top. */
.compare-stage:not([data-view="side"]) .compare-pane {
  position: absolute;
  inset: 0;
  border-radius: 0;
  background: transparent;
}

.compare-stage:not([data-view="side"]) .compare-pane video {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  background: transparent;
}

.compare-stage:not([data-view="side"]) :is(.pane-cap, .pane-overlay, .compare-pane-missing) {
  display: none;
}

.compare-stage[data-view="difference"] .compare-pane[data-pane="candidate"] {
  mix-blend-mode: difference;
}

/* Blink is NOT an animation. The motion block at the foot of this file sets
   `animation: none !important` on everything under `prefers-reduced-motion: reduce`, and a
   Blink view that silently stops swapping shows the match alone — on top of an upload that is
   never revealed — under a note reading "anything that moves is a difference". A reviewer
   reads that stillness as sameness and files a different video as a copy, which is the one
   thing this view exists to catch. So player.js flips `data-blink` on a timer of its own
   (`data-blink-ms`), and all this does is say what each half of the cycle looks like. */
.compare-stage[data-view="blink"] .compare-pane[data-pane="candidate"] {
  opacity: 1;
}

.compare-stage[data-view="blink"][data-blink="off"] .compare-pane[data-pane="candidate"] {
  opacity: 0;
}

/* --- the wipe handle --- */

.wipe {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.wipe-line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: rgb(255 255 255 / 0.85);
  box-shadow: 0 0 6px rgb(0 0 0 / 0.6);
}

.wipe-range {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 8px;
  width: auto;
  margin: 0;
  pointer-events: auto;
  accent-color: #fff;
}

/* --- zoom and panning --- */
/* The transform itself is player-core's; the stage only has to stop the browser from
   treating a drag as a scroll while the picture is larger than its pane. */
.compare-stage[data-zoom="2"] {
  touch-action: none;
  cursor: grab;
}

.compare-stage[data-zoom="2"]:active {
  cursor: grabbing;
}

.view-note {
  margin: 0;
  color: var(--muted);
  font-size: 12px;
}

/* --- the bar --- */

.player-bar {
  display: grid;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.player-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.player-row-main {
  gap: 2px;
}

.player-btn .icon {
  width: 16px;
  height: 16px;
}

.player-play .icon {
  width: 18px;
  height: 18px;
}

.player-icon {
  display: inline-flex;
}

.player-time {
  margin: 0 8px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.player-time-sep {
  margin: 0 4px;
  color: var(--muted-2);
}

/* The seek bar is a native range: keyboard, screen reader and touch all come for free. Its
   own track is transparent, and the track under it draws what is buffered and where the 16
   compared frames are. */
.player-seek {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 260px;
  min-width: 160px;
  height: 28px;
}

/* Inset by half a thumb: the native range moves its 14px thumb between 7px and width−7px, so
   a track drawn to the very edges put every tick and the elapsed fill out of step with the
   thumb everywhere but the exact middle. */
.seek-track {
  position: absolute;
  left: 7px;
  right: 7px;
  top: 50%;
  height: 6px;
  margin-top: -3px;
  overflow: hidden;
  border-radius: 999px;
  background: var(--border);
}

.seek-buffered,
.seek-elapsed {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
}

.seek-buffered {
  background: var(--border-strong);
}

.seek-elapsed {
  background: var(--accent);
}

.seek-ticks {
  position: absolute;
  inset: 0;
}

.seek-tick {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 2px;
  margin-left: -1px;
  background: var(--surface);
  opacity: 0.85;
}

.seek-range {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  width: 100%;
  height: 28px;
  margin: 0;
  background: transparent;
}

.seek-range::-webkit-slider-runnable-track {
  height: 6px;
  background: transparent;
}

.seek-range::-moz-range-track {
  height: 6px;
  background: transparent;
}

.seek-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -4px;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--accent);
}

.seek-range::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--accent);
}

.seek-range:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 999px;
}

/* --- the second row: a row here, an overflow menu on a phone (§13.4) --- */

.player-more {
  min-width: 0;
}

.player-more-trigger {
  display: none;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 4px 10px;
  border-radius: var(--radius-md);
  list-style: none;
  cursor: pointer;
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
}

.player-more-trigger::-webkit-details-marker {
  display: none;
}

.player-more-trigger:hover {
  background: var(--surface-2);
  color: var(--text);
}

.player-row-more {
  gap: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

/* A group is never wider than the row it sits in. `.player-row` wraps BETWEEN groups, which
   is enough on a wide screen; on a phone a single four-button group (Side by side · Wipe ·
   Difference · Blink) is wider than the whole viewport, and an inline-flex that cannot wrap
   pushes the document sideways — measured at 375px, 47px of horizontal scroll. The offset
   controls and their sentence are the same case. */
.player-group {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  max-width: 100%;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}

.player-choice,
.player-toggle {
  gap: 6px;
}

.player-choice-label {
  font-size: 12px;
  font-weight: 600;
}

.player-choice[aria-pressed="true"],
.player-toggle[aria-pressed="true"] {
  background: var(--accent-light);
  color: var(--accent);
}

.player-menu-trigger {
  gap: 6px;
  min-height: 32px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 12px;
  font-weight: 600;
}

.player-menu .menu-panel {
  right: auto;
  left: 0;
  top: auto;
  bottom: calc(100% + 6px);
  min-width: 120px;
}

.player-menu .menu-item[aria-pressed="true"] {
  color: var(--accent);
}

.player-offset {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  max-width: 100%;
  gap: 2px;
}

.player-offset-label {
  margin-left: 4px;
  color: var(--text-2);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* --- the frame tiles: each one jumps to the moment it was cut from (§13.8b) --- */

.frame-tile {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.frame.is-current {
  box-shadow: 0 0 0 2px var(--accent);
}

/* --- the shortcuts dialog, opened from the bar and by ? --- */

.dialog-shortcuts {
  width: min(560px, calc(100vw - 32px));
}

.shortcuts {
  display: grid;
  gap: 6px;
  margin: 0;
}

.shortcuts > div {
  display: grid;
  grid-template-columns: 160px 1fr;
  gap: 12px;
  align-items: baseline;
}

.shortcuts dt {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.shortcuts dd {
  margin: 0;
  color: var(--text-2);
  font-size: 13px;
}

@media (max-width: 480px) {
  .shortcuts > div {
    grid-template-columns: 1fr;
  }
}

/* --- fullscreen, and the fallback where there is none (iPhone) --- */

.compare:fullscreen,
.compare[data-expanded] {
  padding: 12px;
  overflow: auto;
  background: var(--bg);
}

.compare[data-expanded] {
  position: fixed;
  inset: 0;
  z-index: 90;
}

.compare:fullscreen :is(.players, .sound-card, .frames-card),
.compare[data-expanded] :is(.players, .sound-card, .frames-card) {
  display: none;
}

.compare:fullscreen .compare-stage:not([data-view="side"]),
.compare[data-expanded] .compare-stage:not([data-view="side"]) {
  max-height: calc(100vh - 180px);
}

@media (max-width: 767.98px) {
  .compare-stage {
    grid-template-columns: 1fr;
  }

  .player-more-trigger {
    display: flex;
  }

  .player-row-more {
    flex-direction: column;
    align-items: flex-start;
  }

  .player-time {
    margin: 0 4px;
    font-size: 12px;
  }
}

/* ==== small screens ==== */

@media (max-width: 767.98px) {
  .main {
    padding: 16px;
  }

  .card-head {
    padding: 14px 16px 0;
  }

  .card-body {
    padding: 14px 16px 16px;
  }

  .banner {
    padding: 9px 16px;
  }

  .page-title {
    font-size: 20px;
  }
}

/* ==== motion ==== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* --- Settings: sections of rows -------------------------------------------------------------
 *
 * The page was a card per setting: nine of them, different heights, and a grid that had to put
 * a lone one at a third of the width with nothing beside it. Cards in a grid want equal,
 * interchangeable contents. Settings are not that — descriptions differ in length and every
 * control is the same size.
 *
 * So the card is the SECTION and each setting is a row inside it: what it is on the left, the
 * control on the right, a hairline between. One column of full-width cards, no orphans, no
 * holes, and a long description never makes its neighbour taller. */

/* The section owns its spacing as a flex gap, not as margins on its children.
 *
 * The first version said `.card + .card`, which never matched: the settings card is wrapped in
 * a form, so the selector saw `.card + form` and the two cards sat against each other with the
 * panel's footer note touching the next card's edge. A gap on the container cannot be defeated
 * by a wrapper somebody adds later, which is the whole reason to put it there.
 *
 * 16px inside a section, 40px between sections: tight between things that belong together, real
 * air between groups, which is what makes the grouping visible rather than merely present. */
.settings-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.settings-section + .settings-section {
  margin-top: 40px;
}

.settings-section-title {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}

.settings-section-blurb {
  color: var(--muted);
  font-size: 13px;
  margin: 4px 0 0;
  max-width: 78ch;
}

.setting-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  padding: 18px 20px;
}

.setting-row + .setting-row {
  border-top: 1px solid var(--border);
}

/* The control column is fixed so every control in a section lines up, however long the
 * description beside it runs. Below this the row stacks, which is the right answer on a phone:
 * a 240px control beside a paragraph is unreadable. */
@media (min-width: 900px) {
  .setting-row {
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 32px;
    align-items: start;
  }
}

@media (min-width: 1400px) {
  .setting-row {
    grid-template-columns: minmax(0, 1fr) 360px;
  }
}

.setting-row-label {
  font-size: 14px;
  font-weight: 650;
  margin: 0;
}

.setting-row-help {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.55;
  margin: 3px 0 0;
  max-width: 82ch;
}

.setting-row-control {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

.setting-row-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
}

.setting-row-form .field {
  width: 100%;
}

.setting-row-readonly {
  font-size: 14px;
  font-weight: 600;
  margin: 0;
}

/* The four facts on one line, under the description rather than under the control: they are
 * about the setting, not about the box you type in. */
.setting-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 14px;
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.5;
}

.setting-fact {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  min-width: 0;
}

.setting-fact-wide {
  flex-basis: 100%;
}

.setting-fact-label {
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.setting-fact b {
  color: var(--text);
  font-weight: 600;
}

/* One Save for a section, in the card's footer. It was one per row: six identical buttons down
 * the AI section, each its own form, each saving one value. Nobody changes one model and stops
 * — they adjust what they came for and expect one button to mean "that is what I want now". */
.settings-card-form {
  display: block;
}

.settings-save {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.settings-saved {
  color: var(--success-text);
  font-size: 13px;
  font-weight: 600;
}

/* Test, then Save — in that order, because that is the order they are meant to be pressed.
 * A little more air than the usual form row: these two buttons mean different things, and the
 * second one is held shut by the first (static/storage.js). .btn:disabled already greys it. */
.form-actions-test {
  gap: 12px;
  align-items: center;
}

/* What the test said, under the buttons. It replaces a greyed-out button with no explanation,
 * which is the failure this whole arrangement would otherwise introduce. */
.storage-test-note {
  margin-top: 8px;
}

.storage-test-note.is-good {
  color: var(--success-text);
  font-weight: 600;
}

.storage-test-note.is-bad {
  color: var(--danger-text);
  font-weight: 600;
}

/* ==== a card with sub-tabs (tabbedCard, app.js) ==== */

/* The strip sits in the card's head, on its bottom border, the way a settings page in any tool
 * you would copy does it. Named .subtab, not .tab: .tab is the review page's candidate pills,
 * and restyling those by accident is exactly the kind of change nobody would think to check. */
.tab-card-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 4px 12px;
  flex-wrap: wrap;
  padding: 0 20px;
  border-bottom: 1px solid var(--border);
}

.subtabs {
  display: flex;
  gap: 4px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  /* The selected underline lies on the head's border rather than above it. */
  margin-bottom: -1px;
}

.subtab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 14px 12px 12px;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
}

.subtab:hover {
  color: var(--text);
}

.subtab[aria-selected="true"] {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.subtab:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -4px;
  border-radius: var(--radius-sm);
}

/* Something on that tab needs a person — and the one-word state in the header says what. */
.subtab-dot {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 999px;
  background: var(--danger);
}

.tab-card-head .card-actions {
  padding: 10px 0;
}

/* Too narrow for both on one line, the actions go ABOVE the tabs rather than wrapping under
 * them: under, the selected tab's underline would float above the divider instead of lying on
 * it, and the strip would stop reading as the edge of the panel below.
 *
 * Asked of the card, not the screen. What wraps the head is the card's own width — a sidebar,
 * a drawer or a narrow column changes that without the window changing at all — so a rule keyed
 * to the viewport would be right only by coincidence. */
.tab-card {
  container-type: inline-size;
}

@container (max-width: 560px) {
  .tab-card-head {
    padding-top: 12px;
  }

  .tab-card-head .card-actions {
    order: -1;
    width: 100%;
    padding: 0;
  }
}

.subtab-panel {
  padding: 16px 20px 20px;
}

.subtab-panel.is-flush {
  padding: 0;
}

/* Without app.js the panels are one under another, each under its heading, and need a line
 * between them. With it only one shows, and the heading repeats the selected tab. */
.subtab-panel + .subtab-panel {
  border-top: 1px solid var(--border);
}

.subtab-panel-title {
  margin-bottom: 12px;
  font-size: 14px;
  font-weight: 700;
}

.subtab-panel.is-flush > .subtab-panel-title {
  margin-bottom: 0;
  padding: 16px 20px 0;
}

.is-tabbed .subtab-panel + .subtab-panel {
  border-top: 0;
}

.is-tabbed .subtab-panel-title {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.subtab-intro {
  font-size: 13px;
  color: var(--text-2);
}

.subtab-empty {
  padding: 18px 20px;
  font-size: 13px;
  color: var(--muted);
}

/* The Options tab's Save: the same bar a settings card's footer is, inside the tab it saves. */
.subtab-save {
  padding: 14px 20px;
  border-top: 1px solid var(--border);
}

/* Options, in small groups: a label, then its rows, a line between groups. */
.setting-subgroup + .setting-subgroup {
  border-top: 1px solid var(--border);
}

.setting-subhead {
  padding: 16px 20px 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.setting-subgroup .setting-row {
  padding-top: 12px;
}

.setting-subgroup .setting-row + .setting-row {
  padding-top: 18px;
}

/* One card per provider: its key, where it sits in the order, and the model it uses. */
.provider-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: 16px;
}

.provider-card {
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.provider-card > * + *,
.provider-card form > * + * {
  margin-top: 10px;
}

.provider-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}

.provider-card-name {
  font-size: 14px;
  font-weight: 700;
}

.provider-card-badges {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.provider-card-model {
  font-size: 12px;
  color: var(--muted);
}

/* The heading above the keys on the page of every attempt, where the panel is one card. */
.card-subhead {
  margin-top: 8px;
  font-size: 13px;
  font-weight: 700;
}

/* ==== page: collect ==== */

/* One box, one button, and the runs below it. Every colour is a token, so dark mode is the same
   page; every bar is sized by an attribute, never by a style="" (golden rule #15). */

.collect > * + * {
  margin-top: 20px;
}

.collect-box .card-body {
  padding: 20px 22px 18px;
}

.collect-form {
  display: grid;
  gap: 12px;
}

.collect-row {
  display: flex;
  gap: 10px;
}

.collect-url {
  flex: 1;
  min-width: 0;
  min-height: 48px;
  padding: 0 14px;
  font-size: 16px;
}

.collect-url.has-error {
  border-color: var(--danger);
}

.collect-go {
  flex: none;
  min-height: 48px;
}

.collect-error {
  margin: -4px 0 0;
}

.collect-detect {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  min-height: 26px;
  font-size: 13px;
  color: var(--muted);
}

.collect-detect b {
  font-weight: 700;
  color: var(--text);
}

.kind-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--accent-light);
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
  line-height: 18px;
}

.kind-pill .icon {
  width: 14px;
  height: 14px;
}

.collect-auto {
  font-size: 12px;
}

.collect-site.is-new b {
  color: var(--warning-text);
}

.collect-site.is-off,
.collect-site.is-off b {
  color: var(--danger-text);
}

.collect-options {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
}

.collect-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
}

/* What the run will do — "Just this page: every video on it." — beside Read as. */
.collect-kind-note {
  margin: 0;
  font-size: 12px;
  color: var(--muted);
}

.collect-read-as {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.collect-read-as .select {
  width: auto;
  min-width: 190px;
  min-height: 36px;
  font-size: 13px;
}

/* --- the runs --- */

.collect-section-title {
  margin: 0 0 10px;
  font-size: 15px;
  font-weight: 700;
}

.collect-runs {
  display: grid;
  gap: 16px;
}

.run-card {
  display: grid;
  gap: 14px;
  padding: 18px 20px;
  container-type: inline-size;
}

.run-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.run-kind {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-lg);
  background: var(--accent-light);
  color: var(--accent);
}

.run-kind .icon {
  width: 18px;
  height: 18px;
}

.run-head-text {
  flex: 1;
  min-width: 0;
}

.run-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.run-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 6px;
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.run-actions {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

.run-steps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
}

.run-step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
}

.run-step .icon {
  width: 15px;
  height: 15px;
}

.run-step-sep {
  display: inline-flex;
  color: var(--border-strong);
}

.run-step.is-current {
  color: var(--text);
  font-weight: 600;
}

.run-step.is-current .run-step-mark {
  color: var(--accent);
}

.run-step.is-done {
  color: var(--success-text);
}

.run-step.is-failed {
  color: var(--danger-text);
}

.run-progress {
  display: grid;
  gap: 6px;
}

.run-bar {
  display: block;
  width: 100%;
  height: 12px;
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--border);
  overflow: hidden;
}

.run-bar-seg {
  transition: width 0.6s ease;
}

.seg-finished {
  fill: var(--success);
}

.seg-failed {
  fill: var(--danger);
}

.seg-working {
  fill: var(--accent);
}

.swatch.seg-finished {
  background: var(--success);
}

.swatch.seg-failed {
  background: var(--danger);
}

.swatch.seg-working {
  background: var(--accent);
}

.swatch.seg-waiting {
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

.run-bar-text {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  font-weight: 600;
}

.run-eta {
  font-weight: 500;
  color: var(--muted);
  text-align: right;
}

.run-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 12px;
  color: var(--muted);
}

.run-legend li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.run-counts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}

.run-count {
  display: grid;
  align-content: start;
  gap: 1px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--surface-2);
}

.run-count-n {
  font-size: 22px;
  font-weight: 800;
  line-height: 1.15;
  color: var(--tone-text);
  font-variant-numeric: tabular-nums;
}

.run-count-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
}

.run-count-hint {
  font-size: 11px;
  color: var(--muted);
}

.run-notes {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.run-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  background: var(--tone-bg);
  font-size: 13px;
  color: var(--text);
}

.run-note > .icon {
  flex: none;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  color: var(--tone-text);
}

.run-rows {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--border);
}

.run-row {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr) minmax(170px, 240px);
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.run-row-thumb {
  display: grid;
  place-items: center;
  width: 64px;
  aspect-ratio: 16 / 9;
  border-radius: 6px;
  overflow: hidden;
  background: var(--thumb-bg);
}

.run-row-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.run-row-thumb.is-empty {
  background: var(--surface-2);
  color: var(--muted);
}

.run-row-thumb .icon {
  width: 16px;
  height: 16px;
}

.run-row-main {
  min-width: 0;
}

.run-row-title,
.run-row-address {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.run-row-title {
  font-size: 13px;
  font-weight: 600;
}

.run-row-title a {
  color: inherit;
}

.run-row-address {
  margin-top: 2px;
  font-size: 12px;
  color: var(--muted);
}

.run-row-status {
  display: grid;
  justify-items: start;
  gap: 4px;
  min-width: 0;
}

.run-row-bar {
  height: 5px;
}

.run-row-detail {
  margin: 0;
  font-size: 12px;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.run-foot {
  margin: 0;
  font-size: 12px;
  color: var(--muted);
}

.collect.is-live .run-refresh {
  display: none;
}

@container (max-width: 620px) {
  .run-head {
    flex-wrap: wrap;
  }

  .run-actions {
    width: 100%;
    justify-content: flex-start;
  }

  .run-counts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .run-row {
    grid-template-columns: 48px minmax(0, 1fr);
  }

  .run-row-thumb {
    width: 48px;
  }

  .run-row-status {
    grid-column: 2;
  }
}

/* --- earlier runs --- */

.earlier-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.earlier-row {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) minmax(120px, 170px);
  align-items: center;
  gap: 12px;
  padding: 10px 20px;
  border-top: 1px solid var(--border);
}

.earlier-main {
  min-width: 0;
}

.earlier-title {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}

.earlier-title:hover {
  text-decoration: underline;
}

.earlier-sum {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--muted);
}

.earlier-side {
  display: grid;
  justify-items: end;
  gap: 4px;
}

.run-bar-mini {
  height: 6px;
}

.earlier-when {
  font-size: 12px;
  color: var(--muted);
}

.collect-agents-link {
  text-decoration: none;
}

.collect-advanced {
  font-size: 12px;
  color: var(--muted);
}

/* Keyed on the box, not the window: the same page sits in a phone, a narrow window, or a split
   screen beside the player, and only the width it actually has decides. */
.collect-box .card-body,
.collect-earlier .card-body {
  container-type: inline-size;
}

@container (max-width: 560px) {
  .collect-row {
    flex-direction: column;
  }

  .collect-go {
    justify-content: center;
  }

  .collect-options {
    flex-direction: column;
    align-items: stretch;
  }

  .collect-read-as .select {
    flex: 1;
    min-width: 0;
  }

  .earlier-row {
    grid-template-columns: 28px minmax(0, 1fr);
  }

  .earlier-side {
    grid-column: 2;
    justify-items: start;
  }
}
