:root {
  --bg: #fbfbfa;
  --surface: #ffffff;
  --border: #e2e0dc;
  --text: #22201d;
  --muted: #6f6a63;
  --accent: #2f6f4f;
  --accent-text: #ffffff;
  --warn: #8a6100;
  --warn-bg: #fdf5e2;
  --error: #a32c22;
  --error-bg: #fcf0ee;
  --ok: #2f6f4f;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  --radius: 8px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #16181a;
    --surface: #1e2124;
    --border: #32363b;
    --text: #e6e3de;
    --muted: #9a948c;
    --accent: #5fb98a;
    --accent-text: #10231a;
    --warn: #e0b04a;
    --warn-bg: #2b2517;
    --error: #e88b80;
    --error-bg: #2d1c1a;
    --ok: #5fb98a;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

body.centered {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: 24px;
}

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

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

/* ---- top bar ---- */

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

.brand {
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  letter-spacing: -0.01em;
  font-size: 17px;
}

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

.mode {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 14px 7px 11px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.12s, background 0.12s;
}
.mode:hover { border-color: var(--accent); }
.mode.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}
.mode.active .mode-text small { color: var(--accent-text); opacity: 0.8; }

.mode-icon { font-size: 15px; line-height: 1; opacity: 0.85; }
.mode-text { display: flex; flex-direction: column; line-height: 1.2; }
.mode-text b { font-size: 13.5px; font-weight: 600; }
.mode-text small { font-size: 11px; color: var(--muted); }

.mode.secondary { padding: 7px 12px; }
.mode.secondary:not(.active) { background: transparent; border-color: transparent; }
.mode.secondary:not(.active):hover { border-color: var(--border); }

.account { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.who { color: var(--muted); font-size: 13px; }

.usage { display: flex; gap: 18px; flex-wrap: wrap; font-size: 13px; }
.usage-cell { display: flex; align-items: center; gap: 6px; color: var(--muted); }
.usage-cell b { color: var(--text); font-weight: 600; }
.usage-cell .money { color: var(--text); font-variant-numeric: tabular-nums; }
.usage-cell.live .money { color: var(--accent); font-weight: 600; }
.usage-cell.prices.stale { color: var(--warn); }
.usage-cell.prices.critical, .usage-cell.prices.unknown { color: var(--error); }

.meter {
  display: inline-block;
  width: 70px; height: 6px;
  background: var(--border);
  border-radius: 3px;
  overflow: hidden;
}
.meter i { display: block; height: 100%; background: var(--ok); }
.meter.warn i { background: var(--warn); }
.meter.over i { background: var(--error); }

.badge {
  padding: 2px 7px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.badge-dry { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn); }

/* ---- layout ---- */

.page { max-width: 940px; margin: 0 auto; padding: 28px 20px 80px; }
h1 { font-size: 26px; margin: 0 0 6px; letter-spacing: -0.02em; }
h2 { font-size: 17px; margin: 0 0 12px; }
.lede { color: var(--muted); margin: 0 0 22px; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 22px;
}

.auth-card { width: min(400px, 100%); }

.row { display: flex; gap: 16px; flex-wrap: wrap; }
.row .field { flex: 1 1 200px; }

.field { display: block; margin-bottom: 16px; }
.label { display: block; font-weight: 600; font-size: 13px; margin-bottom: 5px; }
.hint { color: var(--muted); font-size: 12px; display: block; margin-top: 4px; }
.check { display: flex; gap: 8px; align-items: center; color: var(--muted); margin-bottom: 14px; }

input[type=text], input[type=email], input[type=password], input[type=number],
select, textarea {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
}
textarea { font-family: var(--mono); font-size: 13px; resize: vertical; }
select:disabled, option:disabled { color: var(--muted); }

.actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 6px; }

.btn {
  padding: 8px 16px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
}
.btn:hover:not(:disabled) { border-color: var(--muted); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn.primary { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
.btn.wide { width: 100%; }
.link { background: none; border: none; color: var(--muted); cursor: pointer; font: inherit; padding: 0; }
.link:hover { color: var(--text); }
.inline { display: inline; }

/* ---- alerts ---- */

.alert { padding: 10px 14px; border-radius: 6px; margin-bottom: 16px; font-size: 14px; }
.alert.error { background: var(--error-bg); color: var(--error); border: 1px solid var(--error); }
.alert.warn { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn); }

/* ---- estimate ---- */

.estimate-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 16px;
  margin-bottom: 14px;
}
.estimate-grid b { display: block; font-size: 20px; font-variant-numeric: tabular-nums; }
.estimate-grid small { color: var(--muted); font-size: 12px; }
.notes { color: var(--muted); font-size: 13px; padding-left: 18px; margin: 0 0 14px; }

/* ---- job page ---- */

.job-head { display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; align-items: flex-start; }
.job-actions { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }

.progress { height: 8px; background: var(--border); border-radius: 4px; overflow: hidden; margin-bottom: 6px; }
.progress i { display: block; height: 100%; background: var(--accent); transition: width 0.3s; }
.progress-wrap { margin-bottom: 22px; }
.progress-meta { display: flex; gap: 14px; font-size: 13px; color: var(--muted); }
.progress-meta span:first-child { color: var(--text); font-variant-numeric: tabular-nums; }

.chunk-grid { display: flex; flex-wrap: wrap; gap: 3px; }
.chunk {
  width: 26px; height: 22px;
  display: grid; place-items: center;
  border-radius: 3px;
  font-size: 10px;
  font-family: var(--mono);
  background: var(--border);
  color: var(--muted);
}
.chunk-done { background: var(--ok); color: var(--accent-text); }
.chunk-running { background: var(--accent); color: var(--accent-text); animation: pulse 1.2s ease-in-out infinite; }
.chunk-retried { background: var(--warn); color: #1a1508; }
.chunk-failed { background: var(--error); color: #fff; }
@keyframes pulse { 50% { opacity: 0.45; } }

.status { font-size: 12px; font-weight: 600; padding: 2px 8px; border-radius: 10px; background: var(--border); }
.status-done, .status-ok { background: var(--ok); color: var(--accent-text); }
.status-running, .status-queued { background: var(--accent); color: var(--accent-text); }
.status-failed, .status-budget_stopped { background: var(--error); color: #fff; }
.status-cancelled, .status-interrupted, .status-draft { background: var(--warn); color: #1a1508; }

/* ---- table ---- */

.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th, .table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); }
.table th { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }

/* ---- log ---- */

.log { list-style: none; margin: 0; padding: 0; max-height: 320px; overflow-y: auto; font-family: var(--mono); font-size: 12px; }
.log li { padding: 3px 0; border-bottom: 1px solid var(--border); color: var(--muted); }
.log .log-warning { color: var(--warn); }
.log .log-error { color: var(--error); }

/* ---- rules strip on the tagging page ---- */

.rules {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 14px;
  margin-bottom: 22px;
}
.rule {
  border-left: 3px solid var(--accent);
  padding: 2px 0 2px 12px;
  font-size: 13px;
  color: var(--muted);
  line-height: 1.45;
}
.rule b { display: block; color: var(--text); font-size: 13.5px; margin-bottom: 2px; }

/* ---- tag set display ---- */

.tagset-head { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.tagset-head h2 { margin-bottom: 2px; }
.tagset-head h2 a { text-decoration: none; }
.tagset-counts { display: flex; gap: 14px; align-items: flex-start; }
.count { font-size: 12px; color: var(--muted); }
.count b { display: block; font-size: 18px; color: var(--text); font-variant-numeric: tabular-nums; }

.tag-row { display: flex; gap: 12px; margin: 10px 0; align-items: baseline; }
.tag-row-label {
  flex: 0 0 96px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.tag-row-label small { font-weight: 400; text-transform: none; letter-spacing: 0; }
.tag-row-tags { display: flex; flex-wrap: wrap; gap: 5px; }

.tagset-preview { margin: 4px 0 18px; }
.tagset-preview-inner {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  background: var(--bg);
}
.tagset-preview-head {
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
  margin-bottom: 6px;
}

.pill-yours, .pill-locked {
  font-size: 11px;
  padding: 1px 7px;
  border-radius: 9px;
  font-weight: 600;
}
.pill-yours { background: var(--accent); color: var(--accent-text); }
.pill-locked { background: var(--border); color: var(--muted); }

.editor { font-family: var(--mono); font-size: 12.5px; line-height: 1.5; }
.guidance {
  font-family: var(--mono);
  font-size: 12.5px;
  white-space: pre-wrap;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 12px;
  margin: 0;
  overflow-x: auto;
}

/* ---- tag pills in the review pane ---- */

.tag {
  display: inline-block;
  padding: 0 6px;
  margin: 0 3px;
  border-radius: 10px;
  font-size: 11px;
  font-family: var(--mono);
  vertical-align: middle;
}
.tag-tone { background: #2f6f4f22; color: var(--accent); border: 1px solid var(--accent); }
.tag-motion { background: #2b6cb022; color: #3182ce; border: 1px solid #3182ce; }
.tag-expression { background: #6b46c122; color: #805ad5; border: 1px solid #805ad5; }
.tag-func { background: var(--border); color: var(--muted); border: 1px solid var(--muted); }

/* ---- failure explanation ---- */

.explanation b { display: block; margin-bottom: 4px; font-size: 14.5px; }
.explanation p { margin: 4px 0; }
.explanation-action { font-weight: 500; }
.explanation details { margin-top: 8px; }
.explanation summary { cursor: pointer; font-size: 12px; opacity: 0.75; }
.explanation pre {
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 11.5px;
  margin: 6px 0 0;
  opacity: 0.8;
}


/* ---- mobile -----------------------------------------------------------
   Desktop stays the primary target and is deliberately untouched: every rule
   below sits inside a media query. The aim on a phone is not a redesign, only
   that nothing overflows sideways and every control stays reachable.
*/

/* A table is the one thing here that cannot reflow honestly — squeezing
   columns loses the numbers. Let it scroll inside its own box instead of
   pushing the whole page sideways. */
.table-wrap { overflow-x: auto; }

@media (max-width: 760px) {
  .page { padding: 18px 14px 60px; }

  /* Once the bar wraps to three lines, sticky eats a third of a phone
     screen. It scrolls away instead. */
  .topbar { position: static; gap: 10px; padding: 9px 14px; }
  .brand { font-size: 16px; }

  /* The two modes are the product; they get the full width and split it
     evenly rather than shrinking to fit beside everything else. */
  .modes { width: 100%; gap: 6px; }
  .mode { flex: 1; justify-content: center; padding: 9px 8px; }
  .mode-text { align-items: center; }
  .mode-text small { display: none; }
  .mode.secondary { flex: 0 0 auto; }

  .account { margin-left: 0; width: 100%; justify-content: space-between; }
  .usage { width: 100%; gap: 12px; font-size: 12px; }

  /* iOS zooms the viewport when a field under 16px takes focus, which leaves
     the user scrolled sideways on a page that otherwise fits. */
  input[type=text], input[type=email], input[type=password], input[type=number],
  select, textarea { font-size: 16px; }

  /* ~44px tall, the usual minimum for a reliable tap. */
  .btn { padding: 11px 16px; }
  .actions { gap: 10px; }

  /* The label column is a fixed 96px; stack it rather than crushing the tags. */
  .tag-row { flex-direction: column; gap: 4px; }
  .tag-row-label { flex: none; }

  .job-head { flex-direction: column; gap: 12px; }
  .job-actions { align-items: flex-start; width: 100%; }
  .tagset-head { flex-direction: column; gap: 10px; }

  .estimate-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .estimate-grid b { font-size: 18px; }

  h1 { font-size: 22px; }
  .card { padding: 16px; }
}

@media (max-width: 430px) {
  .mode-icon { display: none; }
  .mode-text b { font-size: 13px; }
  .table { font-size: 13px; }
  .table th, .table td { padding: 6px 8px; }
  .chunk { width: 23px; height: 20px; }
  .usage { gap: 10px; }
  .usage-cell .meter { display: none; }
}

/* ==========================================================================
   The shell: one product, three apps
   --------------------------------------------------------------------------
   Added when voice review, the emotion studio and the tagger moved behind one
   login. The tokens above are unchanged -- these only add the product-level
   chrome that used to be a separate static index page, plus the screens that
   have no equivalent in the standalone tagger (home, admin, no-access).
   ========================================================================== */

.brand { font-weight: 500; color: var(--text); text-decoration: none; letter-spacing: -0.01em; }
.brand b { font-weight: 700; }

/* Product nav. Deliberately quieter than .modes below it: which app you are
   in changes rarely, which mode you are in changes constantly. */
.apps { display: flex; gap: 2px; margin-left: 8px; }
.apps .app {
  padding: 6px 12px;
  border-radius: 6px;
  color: var(--muted);
  text-decoration: none;
  font-size: 14px;
  white-space: nowrap;
}
.apps .app:hover { background: var(--surface); color: var(--text); }
.apps .app.active { background: var(--surface); color: var(--text); font-weight: 600; }

/* Second row, owned by whichever app is mounted. */
.subnav {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 10px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  flex-wrap: wrap;
}
.subnav .modes { flex: 1; }

.account { margin-left: auto; display: flex; align-items: center; gap: 14px; }
.quiet { color: var(--muted); text-decoration: none; font-size: 14px; }
.quiet:hover { color: var(--text); }

/* ---- home ---- */

.app-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.app-card {
  display: block;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  color: inherit;
}
a.app-card:hover { border-color: var(--accent); }
.app-card .card-name { font-weight: 600; }
.app-card .card-what { color: var(--muted); font-size: 14px; margin-top: 4px; }
/* Locked apps are shown, not hidden: someone told to "use the studio" needs to
   see it exists and that they need a grant, not a page that looks truncated. */
.app-card.locked { border-style: dashed; opacity: 0.62; }
.app-card .lock {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--warn);
  border: 1px solid var(--warn);
  background: var(--warn-bg);
  border-radius: 4px;
  padding: 1px 6px;
  margin-left: 6px;
  vertical-align: 1px;
}

.notice { max-width: 34rem; }
.alert.ok { background: var(--surface); border: 1px solid var(--ok); color: var(--ok); }
.btn.small { padding: 4px 10px; font-size: 13px; }
.section { margin-top: 34px; }
.copyable { user-select: all; word-break: break-all; }

/* ---- admin ---- */

.grid { width: 100%; border-collapse: collapse; font-size: 14px; }
.grid th, .grid td { padding: 9px 10px; border-bottom: 1px solid var(--border); text-align: left; }
.grid th { color: var(--muted); font-weight: 500; font-size: 13px; }
.grid .col-app { text-align: center; width: 108px; }
.grid .col-app.all { color: var(--muted); font-style: italic; text-align: left; }
.grid tr.inactive { opacity: 0.5; }
.who-email { font-weight: 500; }
.who-meta { color: var(--muted); font-size: 12px; margin-top: 2px; }

.row-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.row-form input[type="email"] { flex: 1; min-width: 260px; }
.row-form input[type="number"] { width: 76px; }
.checks { display: flex; gap: 14px; align-items: center; font-size: 14px; color: var(--muted); }
.checks label { display: flex; gap: 5px; align-items: center; cursor: pointer; }

@media (max-width: 640px) {
  .topbar { flex-wrap: wrap; gap: 10px; }
  .apps { order: 3; width: 100%; margin-left: 0; overflow-x: auto; }
  .account { gap: 10px; }
  .grid .col-app { width: auto; }
}

/* ==========================================================================
   Voice review
   --------------------------------------------------------------------------
   Ported from the static pack, whose pages carried their own light-only
   palette. Rewritten onto the tokens above so the review pages, the studio and
   the tagger are one product in both themes.
   ========================================================================== */

.subnav strong { font-weight: 600; }
.subnav label { font-size: 14px; color: var(--muted); display: flex; align-items: center; gap: 6px; }
/* Not the tagger's .progress bar above: the review counter is text, and the bar's
   8 px height with overflow hidden cut it down to a struck-through strip. */
.subnav .progress { height: auto; overflow: visible; background: none; border-radius: 0;
                    margin: 0; font-size: 14px; color: var(--muted); }
.server-status { font-size: 13px; color: var(--muted); }
.server-status.warn { color: var(--warn); }

section.group h2 { font-size: 19px; margin: 0 0 4px; }
.meta { color: var(--muted); font-size: 13px; margin-bottom: 10px;
        display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.count { font-weight: 600; color: var(--ok); }
.transcript { font-size: 13px; color: var(--text); background: var(--bg);
              border: 1px solid var(--border); border-radius: 6px;
              padding: 8px 12px; margin: 8px 0; }
audio { width: 100%; max-width: 240px; height: 34px; display: block; margin: 4px 0; }

/* ---- audio player (static/player.js) ----
   Every <audio controls> is drawn as this instead of the browser's own control,
   which ignores the theme. The <audio> stays in the page, hidden. */
audio.ap-native { display: none !important; }
.ap {
  display: flex; align-items: center; gap: 9px;
  width: 100%; max-width: 260px; height: 36px; margin: 4px 0; padding: 0 12px 0 4px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
  transition: border-color .15s, box-shadow .15s;
}
.ap.playing { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.ap.error { border-color: var(--error); }
.ap-btn {
  flex: none; display: grid; place-items: center; width: 28px; height: 28px; padding: 0;
  border: 0; border-radius: 50%; background: var(--accent); color: var(--accent-text); cursor: pointer;
  transition: transform .1s, filter .15s;
}
.ap-btn:hover { filter: brightness(1.1); }
.ap-btn:active { transform: scale(.92); }
.ap-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ap-btn svg { width: 12px; height: 12px; fill: currentColor; }
.ap.error .ap-btn { background: var(--error); }
.ap.loading .ap-btn { animation: ap-pulse 1s ease-in-out infinite; }
@keyframes ap-pulse { 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .ap.loading .ap-btn { animation: none; } }
.ap-seek {
  flex: 1; min-width: 40px; height: 4px; margin: 0; padding: 0;
  -webkit-appearance: none; appearance: none; border-radius: 2px; cursor: pointer;
  background: linear-gradient(to right, var(--accent) var(--pct, 0%), var(--border) var(--pct, 0%));
}
.ap-seek:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.ap-seek::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--accent);
  transform: scale(0); transition: transform .12s;
}
.ap-seek::-moz-range-thumb {
  width: 9px; height: 9px; border-radius: 50%; background: var(--accent);
  border: 2px solid var(--surface); transform: scale(0); transition: transform .12s;
}
.ap-seek::-moz-range-track { background: transparent; }
.ap:hover .ap-seek::-webkit-slider-thumb, .ap.playing .ap-seek::-webkit-slider-thumb,
.ap-seek:focus-visible::-webkit-slider-thumb { transform: scale(1); }
.ap:hover .ap-seek::-moz-range-thumb, .ap.playing .ap-seek::-moz-range-thumb,
.ap-seek:focus-visible::-moz-range-thumb { transform: scale(1); }
.ap-time {
  flex: none; min-width: 5.6em; text-align: right; font-size: 12px;
  font-variant-numeric: tabular-nums; color: var(--muted); white-space: nowrap;
}
.ap.playing .ap-time { color: var(--text); }
.ap.error .ap-time { color: var(--error); }
.em .ap, .prov .ap { max-width: 100%; height: 32px; margin: 0; }
.em .ap-btn, .prov .ap-btn { width: 24px; height: 24px; }
.listen .ap { max-width: none; }
.line-text { font-size: 12px; color: var(--muted); margin: 0 0 6px; }
.voice-id { font-family: var(--mono); font-size: 12px; color: var(--muted); }
.reference { margin: 6px 0 10px; }
.ref-label { font-size: 12px; font-weight: 600; margin-bottom: 2px; }
.ref-badge { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 10px;
             background: var(--bg); border: 1px solid var(--border);
             color: var(--muted); font-size: 11px; white-space: nowrap; }
.missing { color: var(--error); font-size: 12px; }
textarea.notes { width: 100%; min-height: 34px; font: inherit; font-size: 13px;
                 background: var(--bg); color: var(--text);
                 border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; }

.grid.auditions td { vertical-align: top; }
.grid.auditions tr.winner { background: color-mix(in srgb, var(--ok) 12%, transparent); }
input[type="checkbox"].pick { width: 20px; height: 20px; }
.clear-sel[hidden] { display: none; }

/* round 2 */
.voice.card { padding: 14px 18px; }
.voice.rated { border-left: 3px solid var(--ok); }
.voice.ref { border-style: dashed; }
.vname { font-weight: 600; }
.dur { color: var(--muted); font-size: 12px; margin-left: 8px; }
h2.persona { margin: 26px 0 10px; }
.rating { display: flex; align-items: center; gap: 6px; margin: 10px 0; flex-wrap: wrap; }
.rating .lbl { color: var(--muted); font-size: 13px; margin-right: 4px; }
button.score {
  width: 38px; height: 34px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text);
  font: inherit; font-weight: 600; cursor: pointer;
}
button.score:hover { border-color: var(--muted); }
button.score.on { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
.clear-score[hidden] { display: none; }
details.script { margin-top: 10px; }
details.script .lines { margin-top: 8px; line-height: 1.7;
                        background: var(--bg); border: 1px solid var(--border);
                        border-radius: 6px; padding: 8px 10px; }
details.script .lines b { font-weight: 600; margin-right: 6px; text-transform: capitalize; }
/* Round 2: the scene sits beside each voice's player, so the words are next to
   whatever is playing. On a phone it drops under the player, above the score. */
.voice.card.with-script {
  display: grid; column-gap: 24px; align-items: start;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
  grid-template-areas: "head script" "play script" "rating script" "notes script";
  grid-template-rows: auto auto auto 1fr;
}
.with-script > .vhead { grid-area: head; }
.with-script > .vplay { grid-area: play; }
.with-script > .rating { grid-area: rating; }
.with-script > .notes { grid-area: notes; }
.with-script > .voice-script { grid-area: script; margin-top: 0; }
.voice-script summary { font-size: 13px; color: var(--muted); }
.voice-script .lines { font-size: 13px; line-height: 1.6; }
@media (max-width: 720px) {
  .voice.card.with-script {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "head" "play" "script" "rating" "notes";
    grid-template-rows: none;
  }
  .with-script > .voice-script { margin: 6px 0 2px; }
  /* A thumb, not a cursor: five of these still fit a 360px phone. */
  button.score { height: 42px; }
}

/* Narrow screens: the five-column audition table reflows into stacked cards. */
@media (max-width: 720px) {
  .grid.auditions, .grid.auditions tbody, .grid.auditions tr, .grid.auditions td { display: block; width: 100%; }
  .grid.auditions th { display: none; }
  .grid.auditions tr:first-child { display: none; }
  .grid.auditions tr { border: 1px solid var(--border); border-radius: 10px;
                       padding: 10px 12px; margin: 12px 0; }
  .grid.auditions tr.winner { border-color: var(--ok); border-width: 2px; }
  .grid.auditions td { border: none; padding: 6px 0; }
  .grid.auditions td[data-label]::before {
    content: attr(data-label); display: block; font-weight: 600; font-size: 11px;
    text-transform: uppercase; letter-spacing: .03em; color: var(--muted); margin-bottom: 3px;
  }
  .grid.auditions td[data-label="Pick"] { display: flex; align-items: center; gap: 8px; }
  .grid.auditions td[data-label="Pick"]::before { margin-bottom: 0; }
  audio, .ap { max-width: 100%; }
}

/* ==========================================================================
   Emotion studio
   --------------------------------------------------------------------------
   The studio page carried its own near-identical token block (warm neutrals,
   light/dark) — the same palette this file already defines. Mapped onto the
   shared tokens: --bad/--good become --error/--ok, --card becomes --surface.
   ========================================================================== */

.voice-picker { max-width: 26rem; }

.take { display: flex; gap: 1.2rem; flex-wrap: wrap; align-items: center;
        padding: 12px 16px; margin-bottom: 16px; }
.take label { font-size: 13px; color: var(--muted); display: flex; gap: 6px; align-items: center; }
.take select, .take input { width: auto; }
.note { color: var(--muted); font-size: 13px; }

.gate { font-size: 13px; color: var(--muted); margin-bottom: 16px; }
.gate b { color: var(--text); font-variant-numeric: tabular-nums; }
.gate .fail { color: var(--error); }
.gate .pass { color: var(--ok); }

.em { padding: 12px 16px; margin-bottom: 10px; }
.em.flagged { border-color: var(--error); }
.em .top { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.em .name { font-weight: 600; min-width: 6.5rem; }
.em .sim { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.em .sim.low { color: var(--error); }
.em .off { margin-left: auto; font-size: 13px; color: var(--muted); }
.em audio { height: 32px; max-width: 100%; margin: 0; }
/* Which of the two players is which: the clip the pack ships, and what cello
   makes from it. Unlabelled, two players side by side are a coin toss. */
.tag { font-size: 12px; color: var(--muted); text-transform: uppercase;
       letter-spacing: .04em; }
/* Every player says what it says. A clip, its audition and the take speak three
   different texts, and judging any of them without the words is guesswork. */
.reftext.under { flex-basis: 100%; margin: 4px 0 2px; }
.reftext.script { margin-top: 10px; line-height: 1.7; }
.reftext.script b { color: var(--text); font-weight: 600; margin-right: 6px; }

/* Provenance: the chain a clip came from, above the clips themselves. A defect
   heard in a beat is in the source recording, the take, or our cut, and those
   have three different owners -- so the page shows all three in that order. */
.prov { padding: 12px 16px; margin-bottom: 10px; }
.prov .top { display: flex; gap: 12px; align-items: center; }
.prov .name { font-weight: 600; }
.prov .chain { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.prov .step { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.prov .label { font-size: 13px; color: var(--muted); min-width: 13rem; }
.prov .label small { color: var(--muted); font-variant-numeric: tabular-nums; }
.prov audio { height: 32px; max-width: 100%; margin: 0; }
.prov .reftext {
  flex-basis: 100%; font-size: 13px; color: var(--muted);
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 6px 8px;
}

/* The knobs only matter for a beat you have marked wrong, so they stay folded
   away until it is flagged -- fifteen emotions of open controls is a wall. */
.knobs { margin-top: 10px; display: none; gap: 8px; flex-direction: column; }
.em.flagged .knobs { display: flex; }
.knobs label { font-size: 13px; color: var(--muted); }
.knobs input[type="text"], .knobs textarea {
  width: 100%; font: inherit; color: var(--text);
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 6px 8px;
}
.knobs textarea { min-height: 2.4rem; resize: vertical; }

.joblog {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 14px; margin-top: 16px;
  font: 12px/1.5 var(--mono); white-space: pre-wrap;
  max-height: 22rem; overflow: auto;
}
.joblog.err { color: var(--error); }

@media (max-width: 720px) {
  .em .top { align-items: flex-start; flex-direction: column; }
  .em .name { min-width: 0; }
  .em .off { margin-left: 0; }
}
.normalized { color: var(--warn); }

/* ==========================================================================
   Run compare
   --------------------------------------------------------------------------
   Two 16:9 players side by side do not fit the 940px column the other apps
   read in, so this page widens it. The transport sticks to the bottom of the
   window: it is the only clock, and it has to stay reachable while the logs
   are scrolled.
   ========================================================================== */

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

.compare-pick select { width: auto; max-width: min(34rem, 70vw); }

.cmp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.cmp-side { min-width: 0; }
/* Each side's card and player sit on rows shared across both sides, so a card
   with an extra note pushes both players down together. Stacked per column,
   the taller card put one video lower than the other and the two frames no
   longer lined up for comparison. */
.cmp-grid.sides { row-gap: 10px; }
.cmp-grid.sides .cmp-side { display: grid; grid-row: span 2; grid-template-rows: subgrid; }
.cmp-grid.sides .run-card { margin: 0; }
.cmp-grid.sides .stage { align-self: start; }
.compat { margin-bottom: 14px; }

.run-card { padding: 12px 14px; margin-bottom: 10px; font-size: 13px; }
.rc-head { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; margin-bottom: 6px; }
.rc-head b { font-size: 15px; }
.rc-side { font-size: 11px; font-weight: 700; text-transform: uppercase;
           letter-spacing: .05em; color: var(--muted); }
.run-mode { font: 11px var(--mono); padding: 0 6px; border: 1px solid var(--border);
            border-radius: 9px; color: var(--muted); }
.rc-meta { display: grid; grid-template-columns: max-content 1fr; gap: 1px 10px; margin: 0; }
.rc-meta dt { color: var(--muted); }
.rc-meta dd { margin: 0; overflow-wrap: anywhere; }
/* The one thing a comparison exists to show: which of these the two runs disagree on. */
.rc-meta dd.diff { color: var(--warn); font-weight: 600; }
.run-card .alert { margin: 8px 0 0; padding: 6px 10px; font-size: 13px; }
.run-card .note { margin-top: 4px; }

/* The video and the box overlay share one box of the run's viewport aspect, so
   viewport pixels (1280x720) map 1:1 onto the frame at any displayed size. */
.stage { position: relative; aspect-ratio: 16 / 9; background: #000;
         border-radius: var(--radius); overflow: hidden; }
.stage video, .stage .overlay { position: absolute; inset: 0; width: 100%; height: 100%; }
.stage video { object-fit: contain; }
.stage .overlay { pointer-events: none; }
.overlay .box { fill: none; stroke-width: 2px; vector-effect: non-scaling-stroke; }
.overlay text { font: 600 18px system-ui, sans-serif; paint-order: stroke;
                stroke: #000; stroke-width: 4px; }
.overlay .click { fill: rgb(255 255 255 / .3); stroke: #fff; stroke-width: 2px;
                  vector-effect: non-scaling-stroke; }
.overlay .clabel { fill: #fff; }
.stage .offair { position: absolute; left: 0; right: 0; bottom: 0; padding: 6px 10px;
                 color: #fff; background: rgb(0 0 0 / .6); font-size: 13px; }
.stage .offair[hidden] { display: none; }

.transport { position: sticky; bottom: 0; z-index: 5; display: flex; gap: 12px;
             align-items: center; flex-wrap: wrap; padding: 10px 14px; margin: 14px 0; }
.transport #scrub { flex: 1 1 240px; accent-color: var(--accent); }
.transport .clock { font-variant-numeric: tabular-nums; min-width: 5.5em; }
.transport label { font-size: 13px; color: var(--muted); display: flex; gap: 6px; align-items: center; }
.transport select { width: auto; padding: 4px 6px; }

/* Audio source: one click per switch, the pressed one filled. */
.seg { display: inline-flex; align-items: center; }
.seg-label { font-size: 13px; color: var(--muted); margin-right: 6px; }
.seg button { font: inherit; font-size: 13px; padding: 4px 12px; margin-left: -1px;
              border: 1px solid var(--border); background: var(--surface);
              color: var(--text); cursor: pointer; }
.seg button:first-of-type { margin-left: 0; border-radius: 6px 0 0 6px; }
.seg button:last-of-type { border-radius: 0 6px 6px 0; }
.seg button:hover { background: var(--bg); }
.seg button[aria-pressed="true"] { position: relative; background: var(--accent);
                                   border-color: var(--accent); color: var(--accent-text); }
.stage.audible { outline: 3px solid var(--accent); outline-offset: 2px; }

.filters { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 10px; }
.chip { display: inline-flex; align-items: center; gap: 5px; padding: 2px 10px 2px 6px;
        border: 1px solid var(--border); border-left: 3px solid var(--g);
        border-radius: 12px; font-size: 13px; color: var(--muted);
        background: var(--surface); cursor: pointer; }
.chip small { font-variant-numeric: tabular-nums; }

.events { list-style: none; margin: 0; padding: 0; position: relative; min-width: 0;
          max-height: 44vh; overflow-y: auto; font-size: 12.5px;
          background: var(--surface); border: 1px solid var(--border);
          border-radius: var(--radius); }
.events li { display: grid; grid-template-columns: 5.2em 10.5em 1fr 1.8em; gap: 8px;
             padding: 3px 10px 3px 7px; border-left: 3px solid var(--g, transparent);
             border-bottom: 1px solid var(--border); cursor: pointer; }
.events li:hover { background: var(--bg); }
.events li.empty { display: block; color: var(--muted); cursor: default; }
.events .t { font-family: var(--mono); color: var(--muted); text-align: right;
             font-variant-numeric: tabular-nums; }
.events .ty { font-family: var(--mono); color: var(--muted); overflow: hidden;
              text-overflow: ellipsis; white-space: nowrap; }
.events .sum { overflow-wrap: anywhere; }
.events li.now { background: color-mix(in srgb, var(--accent) 18%, transparent); }
/* What has not happened yet on the shared clock. */
.events li.now ~ li { opacity: .55; }

/* Manual sync: ⇆ on one event, then on its match in the other log. */
.events .sync-btn { align-self: start; text-align: center; border-radius: 4px;
                    color: var(--muted); opacity: .35; cursor: pointer; user-select: none; }
.events li:hover .sync-btn, .events li.marked .sync-btn { opacity: 1; }
.events .sync-btn:hover { background: var(--accent); color: var(--accent-text); }
.events li.marked { outline: 2px solid var(--accent); outline-offset: -2px; }
.events li.suggested { outline: 2px dashed var(--accent); outline-offset: -2px; }
.events li.now ~ li:is(.marked, .suggested) { opacity: 1; }
.transport .sync { font-size: 13px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

.g-actions { --g: var(--accent); }
.g-speech { --g: #805ad5; }
.g-detections { --g: #3182ce; }
.g-replay { --g: var(--error); }
.g-run { --g: var(--muted); }
.g-queue, .g-episodes, .g-avatar, .g-provider, .g-other { --g: var(--border); }

@media (max-width: 900px) {
  .cmp-grid { grid-template-columns: 1fr; }
  .seg button { padding: 9px 14px; }
  .events li { grid-template-columns: 4.6em 1fr 1.8em; }
  .events .ty { grid-column: 2; }
  .events .sync-btn { grid-column: 3; grid-row: 1; opacity: .7; }
  .events .sum { grid-column: 1 / -1; }
}

/* ==========================================================================
   LiveKit capture
   --------------------------------------------------------------------------
   Two runs in the compare page's grid and players (.cmp-grid, .stage), but
   each with its own native controls: the runs come from different moments of
   a stream, so there is no shared timeline to drive them from.
   ========================================================================== */

.lk-notes { color: var(--muted); font-size: 14px; margin: -12px 0 22px; padding-left: 1.2em; }
.lk-toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
              padding: 10px 14px; margin-bottom: 14px; }
.lk-pane { min-width: 0; }
.lk-head { display: flex; gap: 10px; align-items: center; margin-bottom: 8px; }
.lk-head select { flex: 1; min-width: 0; width: auto; padding: 6px 8px; }
.lk-head label { display: flex; gap: 6px; align-items: center; font-size: 13px;
                 color: var(--muted); white-space: nowrap; }
.lk-pane .alert { padding: 6px 10px; margin-bottom: 8px; font-size: 13px; }
.lk-desc { margin: -2px 0 8px; font-size: 14px; }
.lk-spec { margin: 8px 0; font-size: 13px; }
.lk-tracks { display: grid; gap: 4px; margin-bottom: 8px; }
.lk-track { display: grid; grid-template-columns: minmax(0, 1fr) minmax(90px, 160px) 3.2em;
            gap: 10px; align-items: center; font-size: 13px; }
.lk-track input[type="range"] { width: 100%; margin: 0; accent-color: var(--accent); }
.lk-track small { color: var(--muted); }
.lk-track .lk-vol { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
.lk-track .missing { grid-column: 2 / -1; }
/* The video is at a point this track has no audio for (it started later, or ended). */
.lk-track.out { opacity: .5; }
.lk-files { margin: 0; font-size: 13px; overflow-wrap: anywhere; }

/* Sized to its content rather than stretched across the wide page: three columns spread over 1400px put a
   row's values too far from its name to read across. */
.lk-table-card { padding: 0; width: fit-content; max-width: 100%; }
.lk-table { width: auto; }
.lk-table th, .lk-table td { padding: 7px 14px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.lk-table td { min-width: 11em; }
/* Run names as written: the shared table style's capitals turn 720p30 into 720P30. */
.lk-table thead th { vertical-align: bottom; font-size: 14px; font-weight: 600; color: var(--text);
                     text-transform: none; letter-spacing: 0; }
.lk-table thead th small { display: block; font-size: 12px; font-weight: 400; color: var(--muted); }
.lk-table tbody th { font-size: 13px; font-weight: 400; color: var(--muted); text-transform: none; letter-spacing: 0; }
/* On a phone the table scrolls sideways; the row names stay in view. */
.lk-table tr > :first-child { position: sticky; left: 0; z-index: 1; background: var(--surface);
                              box-shadow: inset -1px 0 var(--border); }
.lk-table code { font-size: 12px; }
.lk-table .lk-on { background: color-mix(in srgb, var(--accent) 20%, var(--surface)); }
.lk-tag { display: inline-block; margin-right: 4px; padding: 0 5px; border-radius: 4px; font-size: 11px;
          font-weight: 700; background: var(--accent); color: var(--accent-text); }
