/* Operator console. Information-dense internal tooling: the job is to read a
   table quickly and be certain which control you just used, not to be
   memorable. Ships as one file with no build step. */

:root {
  --bg: #f2f4f6;
  --surface: #fff;
  --ink: #14181c;
  --ink-soft: #566069;
  --dim: #8b949d;
  --line: #d8dde2;
  --accent: #1f5d8c;
  --ok-bg: #e6f2ea; --ok-ink: #24603f; --ok-line: #bcdcc8;
  --bad-bg: #fbe9e6; --bad-ink: #8c2c1a; --bad-line: #eec3b8;
  --paid-bg: #e8f0f8; --paid-ink: #1f5d8c; --paid-line: #bcd4e8;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #12161a; --surface: #1a1f25; --ink: #e6eaee; --ink-soft: #a6b0ba;
    --dim: #6b757e; --line: #2a323a; --accent: #79aed9;
    --ok-bg: #16261d; --ok-ink: #8fd0a8; --ok-line: #2a4433;
    --bad-bg: #2b1a16; --bad-ink: #e59480; --bad-line: #4b2b22;
    --paid-bg: #16232e; --paid-ink: #8fbde0; --paid-line: #2a4055;
  }
}

* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 15px/1.5 ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}
[hidden] { display: none !important; }

/* ── Gate ─────────────────────────────────────────────────────────── */
.gate { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  padding: 28px; width: min(380px, 100%); display: flex; flex-direction: column; gap: 14px;
}
.card h1 { margin: 0; font-size: 20px; letter-spacing: -0.01em; }
.card .sub { margin: -8px 0 4px; color: var(--ink-soft); font-size: 14px; }
label { display: flex; flex-direction: column; gap: 5px; font-size: 13px; color: var(--ink-soft); }

input, select, button {
  font: inherit; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: 5px; padding: 8px 10px;
}
input:focus-visible, select:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
button { cursor: pointer; }
button:disabled { opacity: .55; cursor: default; }
.card button[type=submit] { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }

/* ── Shell ────────────────────────────────────────────────────────── */
.topbar {
  display: flex; align-items: center; gap: 16px; padding: 0 18px; height: 52px;
  background: var(--surface); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10;
}
.brand { font-weight: 700; letter-spacing: -0.01em; }
.spacer { flex: 1; }
#nav { display: flex; gap: 4px; }
#nav a {
  color: var(--ink-soft); text-decoration: none; padding: 6px 11px;
  border-radius: 5px; font-size: 14px;
}
#nav a.on { background: var(--paid-bg); color: var(--accent); font-weight: 600; }
.whoami { color: var(--dim); font-size: 13px; font-variant-numeric: tabular-nums; }
.ghost { background: transparent; border-color: transparent; color: var(--ink-soft); font-size: 13px; }
.ghost:hover { border-color: var(--line); }

main { padding: 22px 18px 60px; max-width: 1080px; margin: 0 auto; }

/* ── Panel ────────────────────────────────────────────────────────── */
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: 8px; padding: 18px; }
.panel-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.panel-head h2 { margin: 0; font-size: 17px; }
.summary { display: flex; gap: 6px; flex-wrap: wrap; }
.pill {
  font-size: 12px; padding: 3px 9px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--line); color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.filters input[type=search] { flex: 1; min-width: 200px; }

/* ── Table ────────────────────────────────────────────────────────── */
table { width: 100%; border-collapse: collapse; font-size: 14px; }
thead th {
  text-align: left; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim); font-weight: 700; padding: 0 10px 8px; border-bottom: 1px solid var(--line);
}
tbody td { padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
.num { font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.dim { color: var(--dim); }
td select { padding: 4px 6px; font-size: 13px; }

/* Where a plan came from, beside the plan menu (views/users.js). */
.pill.plan-source { background: var(--bg); color: var(--dim); margin-left: 6px; }

/* The weekly hours guide (views/users.js, views/events.js). Amber, not red:
   it is a guide, and going over it blocks nothing. */
.hours { font-size: 13px; color: var(--ink-soft); margin: -6px 0 12px; }
.hours.over, .hours-cell .over { color: var(--warn-ink, #8a5a00); font-weight: 600; }
.hours-cell { font-size: 13px; white-space: nowrap; }
.hours-guide { font-size: 12px; color: var(--dim); margin-left: 6px; }
.hours-limit { width: 4.5em; padding: 2px 4px; font-size: 12px; }

.pager { display: flex; align-items: center; gap: 12px; margin-top: 14px; font-size: 13px; }
.pager button { font-size: 13px; padding: 5px 12px; }

/* ── Feedback ─────────────────────────────────────────────────────── */
.empty { color: var(--dim); padding: 20px 0; margin: 0; }
.msg { margin: 14px 0 0; padding: 9px 12px; border-radius: 5px; font-size: 13.5px;
       background: var(--ok-bg); color: var(--ok-ink); border: 1px solid var(--ok-line); }
.msg.bad, .error { background: var(--bad-bg); color: var(--bad-ink); border: 1px solid var(--bad-line);
                   padding: 9px 12px; border-radius: 5px; font-size: 13.5px; margin: 14px 0 0; }

@media (max-width: 620px) {
  .topbar { gap: 10px; padding: 0 12px; }
  .whoami { display: none; }
  table { font-size: 13px; }
}

/* ── Deletion review (§10.1) ───────────────────────────────────────── */

.hint { margin: 0 0 14px; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); }
.row-actions { display: flex; gap: 6px; }
.row-actions button { font-size: 12.5px; padding: 4px 10px; }
.due-soon { color: var(--bad-ink); font-weight: 600; }

.pill.policy-delete { background: var(--bad-bg); color: var(--bad-ink); border-color: var(--bad-line); font-weight: 600; }
.pill.policy-anon { background: var(--paid-bg); color: var(--paid-ink); border-color: var(--paid-line); font-weight: 600; }

/* Two columns, because the entire point of this screen is the contrast
   between what goes and what stays. Stacked they read as one long list and
   the distinction that matters is lost. */
.inv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 6px; }
.inv-block { background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 14px 16px; }
.inv-block.inv-empty { opacity: .7; }
.inv-block h3 {
  margin: 0 0 10px; font-size: 11.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--dim);
}
.inv-rows { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.inv-rows li { display: flex; justify-content: space-between; font-size: 13.5px; }
.inv-rows span { color: var(--ink-soft); }
.inv-rows b { font-variant-numeric: tabular-nums; }
.inv-why { margin: 0 0 10px; font-size: 12.5px; line-height: 1.5; color: var(--ink-soft); }

.inv-files summary { cursor: pointer; font-size: 13px; color: var(--accent); }
.inv-files ul {
  list-style: none; margin: 8px 0 0; padding: 0; max-height: 240px; overflow-y: auto;
  font-size: 12px; display: flex; flex-direction: column; gap: 3px;
}
.inv-files li { display: flex; justify-content: space-between; gap: 10px; }
.inv-files code { color: var(--ink-soft); word-break: break-all; }
.inv-files li span { color: var(--dim); font-variant-numeric: tabular-nums; white-space: nowrap; }

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

/* ── Streamer: events and encoder setup (§14 Q9) ───────────────────── */

main { display: flex; flex-direction: column; gap: 18px; }

.primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
.danger  { background: var(--bad-bg); border-color: var(--bad-line); color: var(--bad-ink); }

.create { border: 1px solid var(--line); border-radius: 6px; padding: 14px 16px; margin-bottom: 14px; background: var(--bg); }
.create-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.create-actions { display: flex; gap: 8px; margin-top: 14px; }

.pill.status-live  { background: var(--bad-bg); color: var(--bad-ink); border-color: var(--bad-line); font-weight: 700; }
.pill.status-ended { background: var(--bg); color: var(--dim); }
.pill.status-idle  { background: var(--paid-bg); color: var(--paid-ink); border-color: var(--paid-line); }

/* Users: an address nobody has confirmed. Informational rather than an error —
   most clippers never need to — so it stays neutral. */
.pill.unverified { background: var(--bg); color: var(--dim); margin-left: 6px; }

.encoder { border-top: 1px solid var(--line); padding-top: 14px; margin-top: 14px; }
.encoder:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.encoder h3 { margin: 0 0 10px; font-size: 12px; letter-spacing: .07em; text-transform: uppercase; color: var(--dim); }

/* Label above value, because the values are long URLs and a two-column
   layout would either wrap them mid-path or squeeze the labels to nothing. */
.kv { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.kv dt { font-size: 13px; color: var(--ink-soft); }
.kv dd { margin: 3px 0 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.kv code {
  background: var(--bg); border: 1px solid var(--line); border-radius: 5px;
  padding: 6px 9px; font-size: 13px; word-break: break-all; user-select: all;
}
.copy { font-size: 12px; padding: 4px 10px; }
/* "Open ↗" sits in a row of Copy buttons and should read as one of them. */
a.copy { color: var(--accent); text-decoration: none; }
a.copy:hover { text-decoration: underline; }
.row-actions { white-space: nowrap; text-align: right; }
.row-actions button + button { margin-left: 4px; }

.note { margin: 10px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--dim); }
.warn-box {
  margin: 0; padding: 11px 13px; border-radius: 6px; font-size: 13.5px; line-height: 1.55;
  background: var(--bad-bg); color: var(--bad-ink); border: 1px solid var(--bad-line);
}

/* The one thing on this screen a streamer is actually watching. */
.probe { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 13.5px; }
.probe-state {
  font-size: 12px; font-weight: 700; letter-spacing: .06em;
  padding: 5px 11px; border-radius: 999px; border: 1px solid transparent;
}
.probe-connected    { background: var(--ok-bg);   color: var(--ok-ink);   border-color: var(--ok-line); }
.probe-live-no-data { background: var(--bad-bg);  color: var(--bad-ink);  border-color: var(--bad-line); }
.probe-idle         { background: var(--bg);      color: var(--dim);      border-color: var(--line); }

.detail-actions { display: flex; align-items: center; gap: 8px; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); }

@media (max-width: 620px) {
  .create-grid { grid-template-columns: 1fr; }
  .detail-actions { flex-wrap: wrap; }
}

/* ── Gate: register / forgot / pending ─────────────────────────────── */

.gate-links { display: flex; gap: 14px; flex-wrap: wrap; margin: 2px 0 0; font-size: 13px; }
.gate-links a { color: var(--accent); text-decoration: none; }
.gate-links a:hover { text-decoration: underline; }
.field-hint { font-size: 12px; color: var(--dim); }
.card .msg { margin: 4px 0 0; }

.pending { max-width: 560px; }
.pending h2 { margin: 0 0 10px; font-size: 17px; }
.pending p { margin: 0 0 14px; font-size: 14px; line-height: 1.55; color: var(--ink-soft); }
.pending .kv { background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 14px 16px; }


/* Upload progress. The only thing in this console that reports on work
   measured in minutes, so it gets a bar rather than a spinner: a streamer
   moving two gigabytes over rink wifi needs to know it is still moving. */
.panel-actions { display: flex; gap: 8px; align-items: center; }

.upload-progress { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }

.upload-bar {
  height: 6px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}

.upload-bar-fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--accent);
  transition: width 160ms linear;
}

.upload-status { margin: 0; font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  .upload-bar-fill { transition: none; }
}

/* ── Usage ──────────────────────────────────────────────────────────────
   Read like a dashboard rather than a table: the four numbers a streamer
   came for, then the shape of the audience underneath them. */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}

.stat {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.stat-k { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--dim); }
.stat-v { font-size: 27px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat-u { font-size: 12.5px; color: var(--ink-soft); }

/* A number we do not have, rather than a number that is zero. Dashed because
   the tile is a placeholder for a fact, not a fact. */
.stat-void { border-style: dashed; }
.stat-void .stat-v { color: var(--dim); }
.stat-void .stat-u { color: var(--dim); }

.band, .band-empty { border-top: 1px solid var(--line); padding-top: 14px; margin-top: 4px; }
.band h3, .band-empty h3 {
  margin: 0 0 10px; font-size: 12px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--dim);
}

.band-svg { display: block; width: 100%; height: auto; }
.band-fill { fill: var(--accent); opacity: .16; }
.band-line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.band-peak { fill: var(--accent); stroke: var(--surface); stroke-width: 2; }
.band-axis { stroke: var(--line); stroke-width: 1; }
.band-grid { stroke: var(--line); stroke-width: 1; stroke-dasharray: 2 3; }
.band-tick { font-size: 10px; fill: var(--dim); font-variant-numeric: tabular-nums; }

.caveats { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 12px; }
.caveats summary { cursor: pointer; font-size: 13px; color: var(--accent); }
.caveats ul { margin: 10px 0 0; padding-left: 20px; font-size: 13px; line-height: 1.6; color: var(--ink-soft); }
.caveats li { margin-bottom: 6px; }

/* ── Branding screen ─────────────────────────────────────────────────────
   Two columns: the preview on the left, the controls on the right. Collapses
   to one on a narrow window, where the preview still comes first — the whole
   screen exists to show someone what their banner will look like. */
.branding-grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 24px; }
@media (max-width: 860px) { .branding-grid { grid-template-columns: 1fr; } }

.section-label {
  margin: 0 0 10px; font-size: 11.5px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--dim);
}

/* A 16:9 stage the overlay is positioned inside, in percentages, so the
   preview is computing the same fractions of the same frame the worker
   burns in. Nothing here is a pixel measurement. */
.frame { position: relative; width: 100%; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 4px; }
.frame-bg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.frame-overlay { display: block; }

/* ── Plans: what an account's plan leaves out ────────────────────────────
   views/events.js and views/branding.js. The paid palette rather than the
   error one: nothing is broken, the account just does not have this yet, and
   this box is where checkout will sit once billing exists (plans.md §6). */
.plan-lock {
  margin: 0 0 14px; padding: 12px 14px; border-radius: 6px;
  background: var(--paid-bg); border: 1px solid var(--paid-line);
}
.plan-lock h3 { margin: 0 0 6px; font-size: 14px; color: var(--paid-ink); }
.plan-lock p { margin: 0 0 8px; font-size: 13.5px; line-height: 1.55; color: var(--ink-soft); }
.plan-lock p:last-child { margin-bottom: 0; }
.plan-lock-cta { font-weight: 600; color: var(--accent); }
.plan-lock code {
  font-size: 12.5px; background: var(--surface); border: 1px solid var(--paid-line);
  border-radius: 4px; padding: 1px 6px; user-select: all;
}

/* Under the probe: an encoder the API would turn away (lib/goLive.js). Red,
   unlike the lock above, because this one is a stream failing. */
.probe .refused {
  flex-basis: 100%; margin: 0; padding: 8px 11px; border-radius: 5px;
  font-size: 13px; line-height: 1.5;
  background: var(--bad-bg); color: var(--bad-ink); border: 1px solid var(--bad-line);
}

/* Users: after putting a clipper on Teams, the offer to make them a streamer. */
.msg .offer { margin-left: 8px; font-size: 12.5px; padding: 3px 10px; }

/* The optional end date beside a plan (docs/proposals/paywall-journeys.md
   §6.3). Narrow and quiet: a date input at its natural width pushes the Hours
   column off the side of the table, and the date is a detail of the plan
   rather than a column of its own. */
.plan-ends { display: inline-flex; align-items: center; gap: 4px; margin-left: 8px; color: var(--dim); font-size: 12px; }
.plan-ends input { width: 128px; }

/* The followable switch on the Usage screen (paywall-journeys.md §6.3). */
.followable { display: inline-flex; align-items: center; gap: 8px; margin: 4px 0 2px; }
