/* ==========================================================================
   Precision Instrument — riptidal theme
   --------------------------------------------------------------------------
   Dark mode first. Apple-inspired minimalism. Premium and understated.
   Deep observatory blue dominates; brass appears only on important actions
   and highlights. No gradients, no glow, no startup candy.

   Visual reference: vintage audio equipment, watch movements, astronomy,
   precision manufacturing.

   LAYER 1 (tokens) and LAYER 2 (primitives) are the shared house style and
   are byte-identical across ripqueue, riptidal and ripdisc. Never edit them
   in one project alone -- a change there is a change to all three. LAYER 3
   is riptidal's own.
   ========================================================================== */

/* ==========================================================================
   LAYER 1 — Tokens
   ========================================================================== */

:root {
  /* Brand ------------------------------------------------------------- */
  --primary:          #213A5A;   /* deep observatory blue — dominant */
  --primary-hover:    #2D4E77;
  --accent:           #B08D57;   /* brass — important actions only */
  --accent-hover:     #C09A60;

  /* Surfaces ---------------------------------------------------------- */
  --bg:               #0E1218;
  --surface:          #171D25;
  --surface-elevated: #202833;
  --border:           #304050;

  /* Text -------------------------------------------------------------- */
  --text:             #E8EDF3;
  --text-secondary:   #AAB5C3;
  --text-muted:       #758293;

  /* Semantic ---------------------------------------------------------- */
  --success:          #4F8A63;
  --warning:          #C69243;
  --error:            #B45454;
  --info:             #4C78A8;

  /* Semantic, low-emphasis. Tints of the above for outline treatments.
     Kept as explicit values rather than opacity so they composite
     identically over --surface and --surface-elevated. */
  --success-dim:      #7FB894;
  --success-line:     #3D6B4D;
  --success-wash:     rgba(79, 138, 99, 0.12);
  --warning-dim:      #DBB477;
  --warning-line:     #6E5427;
  --warning-wash:     rgba(198, 146, 67, 0.12);
  --error-dim:        #D08A8A;
  --error-line:       #6E3535;
  --error-wash:       rgba(180, 84, 84, 0.12);
  --info-dim:         #7FA5CC;
  --info-line:        #3A5A7C;
  --info-wash:        rgba(76, 120, 168, 0.10);
  --accent-wash:      rgba(176, 141, 87, 0.12);

  /* Typography -------------------------------------------------------- */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
          system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "SF Mono", ui-monospace, SFMono-Regular, "JetBrains Mono",
          Menlo, Consolas, monospace;

  --fs-xs:   11px;
  --fs-sm:   12.5px;
  --fs-base: 14px;
  --fs-md:   15px;
  --fs-lg:   18px;
  --fs-xl:   24px;
  --fs-2xl:  32px;

  --lh-tight: 1.25;
  --lh-base:  1.55;

  /* Section eyebrows: uppercase, tracked out, muted. Used for every
     section label in the app so scanning feels instrument-like. */
  --eyebrow-size: 10.5px;
  --eyebrow-track: 0.09em;

  /* Geometry ---------------------------------------------------------- */
  --r-sm: 5px;
  --r-md: 7px;
  --r-lg: 10px;
  --r-xl: 14px;

  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 22px;
  --sp-6: 32px;
  --sp-7: 48px;

  /* Elevation. Shadows are deep and soft, never colored — the eye should
     read depth, not decoration. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.40);
  --shadow-lg: 0 18px 48px rgba(0, 0, 0, 0.55);

  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--info);

  --transition: 120ms cubic-bezier(0.32, 0.72, 0, 1);
}

/* This app is dark-mode first and does not ship a light palette. Declaring
   the scheme keeps form controls, scrollbars and the UA caret dark. */
:root { color-scheme: dark; }


/* ==========================================================================
   LAYER 2 — Primitives
   ========================================================================== */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Numbers must never shift width — queue positions, years, runtimes,
   timestamps and counts all sit in columns. */
.tnum, table, input[type="number"], time, .pos, .count {
  font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4 {
  margin: 0 0 var(--sp-3);
  line-height: var(--lh-tight);
  letter-spacing: -0.011em;
  font-weight: 600;
}
h1 { font-size: var(--fs-2xl); letter-spacing: -0.021em; }
h2 { font-size: var(--fs-xl);  letter-spacing: -0.017em; }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }

p { margin: 0 0 var(--sp-3); }

a {
  color: var(--info-dim);
  text-decoration: none;
  transition: color var(--transition);
}
a:hover { color: var(--text); }

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

hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: var(--sp-5) 0;
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--r-sm);
}

::selection { background: var(--primary-hover); color: var(--text); }

/* Screen-reader only ------------------------------------------------- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Layout -------------------------------------------------------------- */
.container { width: 100%; max-width: 1120px; margin: 0 auto; padding: 0 var(--sp-4); }
.container--narrow { max-width: 720px; }
.container--content { max-width: 940px; }

.stack > * + * { margin-top: var(--sp-3); }
.stack-lg > * + * { margin-top: var(--sp-5); }

.row { display: flex; align-items: center; gap: var(--sp-2); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.spacer { flex: 1 1 auto; }

/* Eyebrow / section label --------------------------------------------- */
.eyebrow {
  font-size: var(--eyebrow-size);
  letter-spacing: var(--eyebrow-track);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-muted);
  margin: 0 0 var(--sp-3);
}

.muted { color: var(--text-muted); }
.secondary { color: var(--text-secondary); }
.small { font-size: var(--fs-sm); }

/* Navigation ---------------------------------------------------------- */
.nav {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 40;
}
.nav-inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  height: 54px;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 var(--sp-4);
}
.nav-brand {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.012em;
}
/* The one brass mark in the chrome: a hairline under the wordmark. */
.nav-brand::after {
  content: "";
  display: block;
  height: 1.5px;
  width: 20px;
  margin-top: 3px;
  background: var(--accent);
  border-radius: 1px;
}
.nav-links { display: flex; align-items: center; gap: var(--sp-1); }
.nav-link {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: 500;
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--r-sm);
  transition: background var(--transition), color var(--transition);
}
.nav-link:hover { background: var(--surface-elevated); color: var(--text); }
.nav-link[aria-current="page"] { background: var(--primary); color: var(--text); }

/* Cards and panels ---------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}
.card--elevated { background: var(--surface-elevated); box-shadow: var(--shadow-md); }
.card--flush { padding: 0; overflow: hidden; }

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.card-body { padding: var(--sp-5); }
.card-footer {
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--bg);
}

/* Buttons -------------------------------------------------------------
   Brass is the single primary action per view. Everything else is quiet. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 550;
  line-height: 1;
  padding: 9px var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface-elevated);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition), border-color var(--transition),
              color var(--transition), opacity var(--transition);
}
.btn:hover { background: var(--border); }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
  font-weight: 600;
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

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

.btn-ghost { background: transparent; border-color: var(--border); color: var(--text-secondary); }
.btn-ghost:hover { background: var(--surface-elevated); color: var(--text); }

.btn-danger { background: transparent; border-color: var(--error-line); color: var(--error-dim); }
.btn-danger:hover { background: var(--error-wash); border-color: var(--error); color: var(--text); }

.btn-sm { font-size: var(--fs-xs); padding: 6px var(--sp-3); border-radius: var(--r-sm); }
.btn-lg { font-size: var(--fs-base); padding: 12px var(--sp-5); }

.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.42;
  cursor: not-allowed;
  pointer-events: none;
}

/* Icon-only button — used for the queue reorder arrows. */
.btn-icon {
  width: 30px; height: 30px;
  padding: 0;
  border-radius: var(--r-sm);
  color: var(--text-muted);
  background: transparent;
}
.btn-icon:hover { background: var(--surface-elevated); color: var(--accent); }

/* Forms ---------------------------------------------------------------- */
.field { display: block; margin-bottom: var(--sp-4); }
.label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 550;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.hint { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 5px; }

.input, .select, .textarea {
  width: 100%;
  font-family: inherit;
  font-size: var(--fs-base);
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 9px var(--sp-3);
  transition: border-color var(--transition), background var(--transition);
}
.input::placeholder, .textarea::placeholder { color: var(--text-muted); }
.input:hover, .select:hover, .textarea:hover { border-color: #3B4E62; }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--info);
  background: var(--surface);
  box-shadow: 0 0 0 3px var(--info-wash);
}
.textarea { min-height: 92px; resize: vertical; line-height: var(--lh-base); }

.select {
  appearance: none;
  padding-right: var(--sp-6);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23758293' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
}

.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--error); }
.field-error { font-size: var(--fs-xs); color: var(--error-dim); margin-top: 5px; }

/* Search field — the wizard's centerpiece. */
.search {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0 var(--sp-3);
}
.search:focus-within { border-color: var(--info); box-shadow: 0 0 0 3px var(--info-wash); }
.search input {
  flex: 1;
  border: 0; background: transparent; color: var(--text);
  font-family: inherit; font-size: var(--fs-md);
  padding: 11px 0;
}
.search input:focus { outline: none; }
/* The wrapper already indicates focus via :focus-within. Without this the
   global :focus-visible rule draws a second ring around the input itself,
   which reads as a box inside a box. */
.search input:focus-visible { box-shadow: none; }

/* Tables --------------------------------------------------------------- */

/* Wide tables scroll inside their own box rather than stretching the page.
   This lived only in the mobile media query, which is backwards: a narrow
   screen is not where a wide table causes trouble. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Columns size to their content and give the rest to the widest one, so a
   table does not spread its few columns across the full width with acres
   between them. */
.table { width: 100%; table-layout: auto; border-collapse: collapse; font-size: var(--fs-sm); }
.table td:not(:last-child), .table th:not(:last-child) { white-space: nowrap; }
.table td:first-child, .table th:first-child { width: 40%; }

/* Opt out where a cell holds prose rather than a field. Without this a long
   message widens the table until it scrolls sideways, hiding the one thing
   worth reading on the row. */
/* Matches the nowrap rule's specificity (0,2,1) and comes after it, so it
   wins on a cell as well as on an element inside one. .table .wrap alone is
   (0,2,0) and loses to .table td:not(:last-child) on a <td>. */
.table .wrap,
.table td.wrap, .table th.wrap { white-space: normal; overflow-wrap: break-word; }
.table th {
  text-align: left;
  font-size: var(--eyebrow-size);
  letter-spacing: var(--eyebrow-track);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-muted);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.table td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--surface-elevated);
  color: var(--text-secondary);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover td { background: var(--surface-elevated); }
.table td.title { color: var(--text); font-weight: 500; }
.table td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Queue position marker — brass, because it is the thing the admin
   manipulates and the thing the requester waits on. */
.pos {
  display: inline-block;
  min-width: 2.1em;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* Dialogs -------------------------------------------------------------- */
dialog, .modal {
  background: var(--surface-elevated);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  padding: 0;
  width: min(560px, calc(100vw - 2 * var(--sp-4)));
}
dialog::backdrop { background: rgba(4, 7, 11, 0.68); }
.modal-header {
  padding: var(--sp-5) var(--sp-5) var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.modal-title { font-size: var(--fs-lg); font-weight: 600; margin: 0; }
.modal-body { padding: var(--sp-5); }
.modal-footer {
  display: flex; justify-content: flex-end; gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--bg);
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}

/* Banners -------------------------------------------------------------- */
.banner {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface-elevated);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.banner-info    { border-color: var(--info-line);    background: var(--info-wash); }
.banner-success { border-color: var(--success-line); background: var(--success-wash); }
.banner-warning { border-color: var(--warning-line); background: var(--warning-wash); }
.banner-error   { border-color: var(--error-line);   background: var(--error-wash); }

/* Wizard stepper ------------------------------------------------------- */
.stepper { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.step { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--text-muted); }
.step-mark {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border);
  font-size: var(--fs-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.step[aria-current="step"] { color: var(--text); }
.step[aria-current="step"] .step-mark { border-color: var(--accent); color: var(--accent); }
.step[data-complete] { color: var(--text-secondary); }
.step[data-complete] .step-mark { border-color: var(--success-line); color: var(--success-dim); background: var(--success-wash); }
.step-sep { width: 22px; height: 1px; background: var(--border); }

/* Charts ---------------------------------------------------------------
   Ordered categorical ramp. Brass sits first because in this app the
   highlighted series is the one the viewer came for. */
:root {
  --chart-1: #B08D57;
  --chart-2: #4C78A8;
  --chart-3: #4F8A63;
  --chart-4: #7FA5CC;
  --chart-5: #C69243;
  --chart-6: #6E8299;
  --chart-grid: #202833;
  --chart-axis: #758293;
}

/* Empty state ----------------------------------------------------------- */
.empty {
  text-align: center;
  padding: var(--sp-7) var(--sp-5);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}


/* ==========================================================================
   LAYER 3 — riptidal status chips  (project-specific)
   --------------------------------------------------------------------------
   Eleven statuses, four semantic colors. Progression along the normal path is
   carried by treatment (outline -> solid) within a hue rather than by
   inventing new hues, so the happy path reads as one calm gradient and only
   genuine exceptions interrupt a scan.

     requested    muted               entered, awaiting an administrator
     approved     info     outline    cleared to download
     queued       info     outline    waiting on the worker
     downloading  info     solid      in flight
     review       accent   solid      metadata needs a person
     installing   info     solid      uploading and swapping
     installed    success  solid      terminal; live in Plex
     kept         muted               terminal; what was there was good enough
     failed       error    solid      terminal unless retried
     rejected     error    solid      administrator declined
     withdrawn    muted               requester canceled

   `review` is the only status that takes brass. It is the one state that will
   not advance without a person, so it should be the thing the eye finds.
   ========================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-xs);
  font-weight: 550;
  letter-spacing: 0.01em;
  line-height: 1;
  padding: 4px 11px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  white-space: nowrap;
}

.chip--requested,
.chip--kept,
.chip--withdrawn   { color: var(--text-secondary); border-color: var(--border);       background: var(--surface); }
.chip--approved,
.chip--queued      { color: var(--info-dim);       border-color: var(--info-line);    background: var(--info-wash); }
.chip--downloading,
.chip--installing  { color: var(--bg);             border-color: var(--info);         background: var(--info); }
.chip--review      { color: var(--bg);             border-color: var(--accent);       background: var(--accent); }
.chip--installed   { color: var(--bg);             border-color: var(--success);      background: var(--success); }
.chip--failed,
.chip--rejected    { color: var(--bg);             border-color: var(--error);        background: var(--error); }

/* Quality badge. The two writable tiers read as distinct at a glance; the
   lossy ones exist only to be shown greyed out, never chosen. */
.tier {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 3px 7px;
  border-radius: 4px;
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.tier--lossless { color: var(--info-dim); border-color: var(--info-line); }
.tier--max      { color: var(--accent);   border-color: var(--accent); background: var(--accent-wash); }
.tier--lossy    { color: var(--text-muted); border-color: var(--border); opacity: 0.6; }

/* Tier picker. A row is a <label>; unselectable tiers carry no control at all
   rather than a disabled one that silently does nothing when clicked. */
.pick { display: block; }
.pick input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }

.pick-body {
  display: flex;
  gap: var(--sp-3);
  align-items: baseline;
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg);
  transition: border-color var(--transition), background var(--transition),
              opacity var(--transition);
}
.pick--available { cursor: pointer; }
.pick--available:hover .pick-body { border-color: #3B4E62; }
.pick:has(input:checked) .pick-body { border-color: var(--accent); background: var(--accent-wash); }
.pick:has(input:focus-visible) .pick-body { box-shadow: var(--focus-ring); }

/* Unavailable: still listed, visibly out of reach. An option quietly absent
   reads as a bug; a greyed one with a reason explains itself. */
.pick--unavailable .pick-body { opacity: 0.45; }
.pick-name   { font-weight: 550; min-width: 3.5em; }
.pick-detail { color: var(--text-secondary); font-size: var(--fs-sm); }
.pick-why    { color: var(--text-muted); font-size: var(--fs-xs); margin-left: auto; }

/* Progress. Deliberately plain: a filled track, no stripes, no animation. */
.progress {
  height: 4px;
  border-radius: 2px;
  background: var(--surface-elevated);
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  background: var(--info);
  transition: width var(--transition);
}

/* htmx request indicators */
.htmx-indicator { opacity: 0; transition: opacity var(--transition); }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

/* Staleness marker for cache-served results. Deliberately quiet —
   informative, not alarming. */
.stale {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-style: italic;
}

/* ==========================================================================
   Motion and responsive
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media (max-width: 640px) {
  :root { --fs-2xl: 26px; --fs-xl: 20px; }
  .card { padding: var(--sp-4); border-radius: var(--r-md); }
  .card-header, .card-body, .card-footer { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .nav-inner { gap: var(--sp-3); }
  .nav-link { padding: var(--sp-1) var(--sp-2); }
  .stepper { gap: var(--sp-2); }
  .step-label { display: none; }
}


/* Alphabet index. Letters with nothing behind them stay in place, dimmed, so
   the row does not reflow as a search narrows it — a jumping index is harder
   to use than a mostly-dim one. */
.alphabet {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-bottom: var(--sp-4);
}
.alpha {
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 550;
  line-height: 1;
  min-width: 1.9em;
  padding: 6px 4px;
  text-align: center;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.alpha:hover { background: var(--surface-elevated); color: var(--text); }
.alpha--on {
  background: var(--accent-wash);
  border-color: var(--accent);
  color: var(--accent);
}
.alpha--off { color: var(--text-muted); opacity: 0.35; cursor: default; }
.alpha--off:hover { background: none; color: var(--text-muted); }


/* Front-page figures. A row of plain numbers, no cards or borders: the point
   is the figure, and framing each one adds furniture around four short
   strings. */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-5);
  margin: var(--sp-5) 0;
}
.stat-value {
  font-size: var(--fs-2xl);
  font-weight: 600;
  line-height: 1.1;
  color: var(--text);
}
.stat-label {
  margin-top: 2px;
  font-size: var(--fs-xs);
  letter-spacing: var(--eyebrow-track);
  text-transform: uppercase;
  color: var(--text-muted);
}


/* A table of prose rather than figures. The Layer 2 default is nowrap,
   because a column of numbers that wraps is unreadable; History has no
   numbers in it, so every cell may wrap and the table can never widen past
   its box. .table.table--wrap matches the (0,2,1) of the nowrap rule and
   comes after it. */
.table.table--wrap td, .table.table--wrap th {
  white-space: normal;
  overflow-wrap: break-word;
}

/* The header is a row of links and a button, and below roughly 800px the
   button was pushed past the right edge, scrolling the whole page sideways.
   Let it wrap onto a second line instead. (Layer 2 owns .nav-inner; this
   overrides it here rather than editing a file three projects share.) */
.nav-inner { flex-wrap: wrap; height: auto; min-height: 54px; row-gap: var(--sp-2); }

/* Below a phone's width even a fully wrapped table cannot fit five columns,
   and the answer is fewer columns rather than a sideways scroll. Cells marked
   .on-wide carry context, not the answer, so they go first. */
@media (max-width: 640px) {
  .table td.on-wide, .table th.on-wide { display: none; }
}

/* A candidate's track listing, folded away until asked for. Native
   disclosure rather than script: the summary is the control. */
.tracklist > summary {
  cursor: pointer;
  padding: var(--sp-2) var(--sp-3);
  color: var(--text-secondary);
  border-radius: var(--r-sm);
  list-style-position: inside;
}
.tracklist > summary:hover { color: var(--text); background: var(--surface-elevated); }
.tracklist[open] > summary { color: var(--text); }
.tracklist > div { padding: 0 var(--sp-3) var(--sp-2); }

/* A list that reads as prose rather than as a bulleted list: used where each
   line explains one control, and the bullets would be louder than the text. */
.plain-list { list-style: none; padding: 0; }
.plain-list li { margin-bottom: var(--sp-2); }
.plain-list li:last-child { margin-bottom: 0; }

/* Waiting. A lookup crosses MusicBrainz, Discogs and Tidal and can take
   several seconds, and a page that does nothing visible for several seconds
   reads as broken. htmx toggles .htmx-request on the indicator; everything
   here is the style that makes it visible. */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { display: flex; }

.working {
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) 0;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
}
.spinner {
  width: 15px;
  height: 15px;
  flex: none;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* A control being waited on says so rather than looking clickable. */
.btn:disabled, .btn[disabled] { opacity: 0.55; cursor: progress; }

@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
}

/* Settings. Each row is a control with a sentence under it saying where the
   value came from, so the note has to read as part of the field rather than
   as body text that happens to sit nearby. */
.setting { padding-bottom: var(--sp-4); margin-bottom: var(--sp-4);
           border-bottom: 1px solid var(--border); }
.setting:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.setting-note { margin-top: var(--sp-2); display: flex; flex-wrap: wrap;
                gap: var(--sp-2); align-items: center; }
