/* Default look for broadcast.html and watch.html.
 *
 * This file belongs to the project: it changes when the client changes, and re-downloading a
 * release overwrites it. Do not edit it to restyle your own copy — put your rules in custom.css,
 * which loads after this file, ships empty, and is never modified upstream.
 *
 * Everything visual is expressed as a custom property on :root below, and the rules underneath
 * only ever read those properties. That is deliberate: it means a self-hoster can retheme the
 * whole client by redefining a dozen variables in custom.css, without having to out-specify any
 * selector or copy rules out of this file. If you add a rule here, take its colours and sizes
 * from a variable rather than hard-coding them, or you quietly remove that ability.
 *
 * DARK IS THE DEFAULT (since Aug 2026), matching earthseed.live's landing page and vivoh.earth.
 * Light is opt-in via a `light` class on <html>, set by the header toggle and remembered in
 * localStorage. The OS preference is deliberately not consulted on a first visit: a video page
 * that flashes white before painting is worse than one that is simply always dark until asked.
 * Only the COLOUR variables are redeclared under :root.light — every rule below is written once.
 *
 * Both pages share almost everything; the few rules that genuinely differ are scoped by the body
 * class (.page-broadcast / .page-watch). */

:root {
  /* ── Colour ─────────────────────────────────────────────────────────────── */
  --es-bg: #0a0e16;              /* page background */
  --es-surface: #141a24;         /* cards, panels, the stream info bar */
  --es-surface-2: #1b2330;       /* buttons, inputs, the raised bits of a card */
  --es-border: #222b38;
  --es-border-strong: #3a475a;
  --es-fg: #e0e0e0;              /* main text */
  --es-muted: #a8b0bd;           /* status line */
  --es-hint: #8a93a3;            /* explanatory small print */
  --es-link: #8a93a3;            /* footer links, secondary actions */
  --es-link-hover: #34d399;
  --es-accent: #34d399;          /* the one colour that means "this is yours / this is live" */
  --es-accent-dim: #10241c;
  --es-danger: #f87171;

  /* Text inputs. Set explicitly so the dark theme reaches them — otherwise the fields stay
     white however the rest is themed. */
  --es-field-bg: #0d1119;
  --es-field-fg: #e0e0e0;
  --es-field-border: #2c3644;
  --es-media-bg: #000;           /* behind the video/canvas, seen while letterboxed */
  --es-media-border: #222b38;

  /* Buttons. --es-button-border doubles as the accent for secondary controls; setting just this
     and --es-button-bg is usually enough to make the client look like somewhere else. */
  --es-button-bg: var(--es-surface-2);
  --es-button-fg: #e0e0e0;
  --es-button-border: #2c3644;
  --es-button-disabled-opacity: 0.4;
  /* The primary action — "Go live" — is the one place a gradient earns its keep. */
  --es-button-primary-bg: linear-gradient(135deg, #2fb88a 0%, #149a6b 100%);
  --es-button-primary-fg: #04180f;

  /* Brand. The gradient is the vivoh.earth wordmark treatment (blue → green → violet), reused
     here so the client and the marketing site read as one product. */
  --es-brand-grad: linear-gradient(135deg, #60a5fa 0%, #34d399 50%, #a78bfa 100%);
  --es-brand-dot: linear-gradient(135deg, #f97316 0%, #ef4444 100%);
  --es-brand-mark-size: 38px;

  /* ── Type ───────────────────────────────────────────────────────────────── */
  --es-font: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --es-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;   /* ids and the passcode */
  --es-font-size-h1: 1.5rem;
  --es-font-size-small: 0.85rem;             /* hints and footer links */
  --es-font-size-status: 0.9rem;

  /* ── Layout ─────────────────────────────────────────────────────────────── */
  --es-content-width: 900px;     /* column width */
  --es-page-padding: 22px 18px 48px;
  --es-row-gap: 8px;             /* between controls on a row */
  --es-control-padding: 9px 11px;
  --es-button-padding: 9px 16px;
  --es-radius: 8px;              /* corner rounding on inputs, buttons and media */
  --es-radius-lg: 12px;          /* cards and panels */
  --es-media-max-height: 70vh;
}

/* Light mode. Colours only — no rule below is duplicated. */
:root.light {
  --es-bg: #ffffff;
  --es-surface: #f7f8fa;
  --es-surface-2: #eef0f4;
  --es-border: #e2e5ea;
  --es-border-strong: #c3c9d2;
  --es-fg: #171717;
  --es-muted: #4b5563;
  --es-hint: #6b7280;
  --es-link: #6b7280;
  --es-link-hover: #00997e;
  --es-accent: #00997e;
  --es-accent-dim: #e6f6f1;
  --es-danger: #b91c1c;
  --es-field-bg: #ffffff;
  --es-field-fg: #171717;
  --es-field-border: #c3c9d2;
  --es-media-bg: #171717;
  --es-media-border: #e2e5ea;
  --es-button-bg: #ffffff;
  --es-button-fg: #171717;
  --es-button-border: #c3c9d2;
  --es-button-primary-fg: #ffffff;
  /* The wordmark gradient is bright on black and washes out on white; deepen it. */
  --es-brand-grad: linear-gradient(135deg, #2563eb 0%, #00997e 50%, #7c3aed 100%);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--es-bg);
  color: var(--es-fg);
  font-family: var(--es-font);
  transition: background-color 0.3s ease, color 0.3s ease;
}

.wrap {
  max-width: var(--es-content-width);
  margin: 0 auto;
  padding: var(--es-page-padding);
}

h1 { font-size: var(--es-font-size-h1); font-weight: 600; margin: 0 0 16px; letter-spacing: -0.01em; }

/* Applied by earthseed.js and by the pages' own markup. Wins over [hidden] and over any
   display set by a component rule, which is what makes it safe to toggle anything. */
.hidden { display: none !important; }

/* ── Header ─────────────────────────────────────────────────────────────────
   Logo, tagline and the controls that belong to the page rather than the stream. Matches the
   landing page and vivoh.earth so the three read as one product. */
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 20px;
}
.brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; }
.brand-mark { width: var(--es-brand-mark-size); height: var(--es-brand-mark-size); display: block; }
.brand-name {
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  background: var(--es-brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.brand-name .dot {
  background: var(--es-brand-dot);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.tagline {
  font-style: italic;
  font-size: var(--es-font-size-small);
  background: var(--es-brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  opacity: 0.75;
}
.header-right { display: flex; align-items: center; gap: 8px; }

/* ── Buttons ─────────────────────────────────────────────────────────────── */
button, .btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: var(--es-button-padding);
  border: 1px solid var(--es-button-border);
  border-radius: var(--es-radius);
  background: var(--es-button-bg);
  color: var(--es-button-fg);
  font: inherit;
  font-size: 0.9rem;
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease, transform 0.1s ease;
}
button:hover, .btn:hover { border-color: var(--es-border-strong); }
button:active, .btn:active { transform: translateY(1px); }
button:disabled { opacity: var(--es-button-disabled-opacity); cursor: not-allowed; transform: none; }
.btn svg, button svg { width: 16px; height: 16px; flex-shrink: 0; }

/* "Go live" — deliberately the only gradient control, so there is never a question about which
   button starts the broadcast. */
.btn-primary, #go {
  background: var(--es-button-primary-bg);
  border-color: transparent;
  color: var(--es-button-primary-fg);
  font-weight: 600;
}
.btn-primary:hover, #go:hover { border-color: transparent; filter: brightness(1.08); }
/* Live: the same control now stops the stream, so it stops looking like the one that starts it. */
#go.is-live {
  background: var(--es-button-bg);
  border-color: var(--es-danger);
  color: var(--es-danger);
  filter: none;
}

.theme-toggle { padding: 8px; }
.theme-toggle svg { width: 18px; height: 18px; }
/* One icon at a time: the moon offers dark (shown while light), the sun offers light. */
.theme-toggle .sun-icon { display: none; }
:root.light .theme-toggle .sun-icon { display: block; }
:root.light .theme-toggle .moon-icon { display: none; }

/* ── The stream info bar (broadcast page) ───────────────────────────────────
   Everything about THIS broadcast in one card, above the video: its name, the link, the
   passcode, and whether it is live. */
.stream-info {
  background: var(--es-surface);
  border: 1px solid var(--es-border);
  border-radius: var(--es-radius-lg);
  padding: 14px 16px;
  margin-bottom: 16px;
  transition: background-color 0.3s ease, border-color 0.3s ease;
}
.stream-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.stream-label { color: var(--es-hint); font-size: var(--es-font-size-small); }
.stream-id {
  font-family: var(--es-font-mono);
  font-size: 0.9rem;
  color: var(--es-accent);
  background: var(--es-accent-dim);
  padding: 4px 8px;
  border-radius: 5px;
  /* A node id is one unbroken 52-character token; let it break rather than overflow.
     min-width:0 is required for a flex item to wrap at all. */
  min-width: 0;
  overflow-wrap: anywhere;
  user-select: all;
}
.icon-btn {
  background: none;
  border: none;
  color: var(--es-hint);
  padding: 5px;
  border-radius: 5px;
  flex-shrink: 0;
}
.icon-btn:hover { color: var(--es-accent); border: none; }

/* Live pill. Only ever shown while actually publishing — a status light that can lie is worse
   than no status light. */
.live-pill {
  display: none;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--es-accent-dim);
  color: var(--es-accent);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.live-pill.on { display: inline-flex; }
.live-pill::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  animation: es-pulse 2s ease-in-out infinite;
}
@keyframes es-pulse { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .live-pill::before { animation: none; } }

.row { display: flex; gap: var(--es-row-gap); flex-wrap: wrap; align-items: center; margin: 12px 0; }
.row:last-child { margin-bottom: 0; }

input, select, textarea {
  flex: 1;
  min-width: 180px;
  padding: var(--es-control-padding);
  border: 1px solid var(--es-field-border);
  border-radius: var(--es-radius);
  background: var(--es-field-bg);
  color: var(--es-field-fg);
  font: inherit;
  font-size: 0.9rem;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--es-accent); outline-offset: -1px; }
textarea { resize: vertical; }
/* The checkbox is drawn by the browser and is transparent by default; the fill and text colour
   above would repaint it as a white square. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  min-width: 0;
  width: 16px;
  height: 16px;
  padding: 0;
  border-radius: 4px;
  border: 1px solid var(--es-field-border);
  background: var(--es-surface-2);
  position: relative;
  cursor: pointer;
}
input[type="checkbox"]:checked { background: var(--es-accent); border-color: var(--es-accent); }
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 1px;
  width: 4px;
  height: 9px;
  border: solid var(--es-bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
label.check {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--es-muted);
}
label.check:hover { color: var(--es-fg); }

/* ── Passcode — the optional second secret, deliberately kept out of the share link. ───────── */
.passcode-box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  padding: 12px 14px;
  border: 1px solid var(--es-border);
  border-radius: var(--es-radius);
  background: var(--es-bg);
}
#passcode[readonly] {
  flex: 0 0 auto;
  min-width: 0;
  width: auto;
  border: none;
  background: none;
  padding: 0;
  font-family: var(--es-font-mono);
  font-size: 1.15rem;
  letter-spacing: 0.14em;
  color: var(--es-accent);
  user-select: all;
}
.passcode-box button { padding: 5px 11px; font-size: 0.8rem; }
.passcode-box .hint { flex-basis: 100%; margin: 0; }

/* ── Media ──────────────────────────────────────────────────────────────────
   The broadcaster previews a live <video>; the viewer paints decoded frames into a <canvas>. */
.stage {
  background: var(--es-surface);
  border: 1px solid var(--es-border);
  border-radius: var(--es-radius-lg);
  padding: 14px;
  transition: background-color 0.3s ease, border-color 0.3s ease;
}
video, canvas {
  display: block;
  width: 100%;
  margin: 0 auto;
  max-height: var(--es-media-max-height);
  object-fit: contain;
  background: var(--es-media-bg);
  border: 1px solid var(--es-media-border);
  border-radius: var(--es-radius);
}
video { min-height: 240px; }

/* Go live sits above the preview with the status line beside it, so the control and what it is
   telling you are in the same glance. */
.stage-controls { margin: 0 0 12px; align-items: center; }
.stage-controls #status { margin: 0; }

/* The status line is how a broadcaster knows the stream is actually live, and the hints are
   where the passcode guidance lives. Restyle them, but keep them legible. */
#status { color: var(--es-muted); font-size: var(--es-font-size-status); margin-top: 12px; }
.hint { color: var(--es-hint); font-size: var(--es-font-size-small); line-height: 1.5; }
.hint a { color: var(--es-link-hover); }
.stage .hint, .page-watch .hint { margin-top: 10px; }

/* The capture notice: what happened to the camera, in the broadcaster's own words rather than
   the browser's. Literal amber because this palette has no warning token, and a translucent tint
   over the tokenised background reads in both the dark and light themes. */
.capture-notice {
  margin: 0 0 12px;
  padding: 9px 13px;
  border: 1px solid #b8860b;
  border-radius: 8px;
  background: rgba(184, 134, 11, 0.12);
  color: var(--es-fg);
  font-size: var(--es-font-size-small);
  line-height: 1.45;
}
.capture-notice[hidden] { display: none; }

/* ── Footer ─────────────────────────────────────────────────────────────────
   Links that reveal a panel in place rather than navigating away, so nothing here can interrupt
   a live broadcast. */
.site-footer {
  margin-top: 26px;
  padding-top: 16px;
  border-top: 1px solid var(--es-border);
  text-align: center;
  color: var(--es-hint);
  font-size: var(--es-font-size-small);
}
.site-footer a { color: var(--es-link); text-decoration: none; }
.site-footer a:hover { color: var(--es-link-hover); text-decoration: underline; }
.site-footer p { margin: 0; }
.panel {
  margin-top: 14px;
  padding: 16px 18px;
  border: 1px solid var(--es-border);
  border-radius: var(--es-radius-lg);
  background: var(--es-surface);
  text-align: left;
  color: var(--es-muted);
  line-height: 1.6;
}
.panel p { margin: 0 0 0.9em; }
.panel p:last-child { margin-bottom: 0; }
.panel strong { color: var(--es-fg); }
.panel code {
  font-family: var(--es-font-mono);
  font-size: 0.9em;
  background: var(--es-bg);
  border: 1px solid var(--es-border);
  border-radius: 3px;
  padding: 1px 5px;
}
.panel table { width: 100%; border-collapse: collapse; }
.panel td { padding: 5px 8px; border-bottom: 1px solid var(--es-border); }
.panel tr:last-child td { border-bottom: none; }
.panel td:first-child { color: var(--es-fg); }
.dot-ok, .dot-no, .dot-partial {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 7px;
  vertical-align: middle;
}
.dot-ok { background: var(--es-accent); }
.dot-no { background: var(--es-danger); }
.dot-partial { background: #d4a04a; }

/* ── The report control (watch page) ────────────────────────────────────────
   Built in earthseed.js rather than in watch.html. Understated on purpose: findable by someone
   who needs it, and not competing with the video for attention. */
.linkish {
  background: none;
  border: none;
  padding: 0;
  color: var(--es-link);
  font-size: var(--es-font-size-small);
  text-decoration: underline;
  cursor: pointer;
}
.linkish:hover { color: var(--es-link-hover); border: none; }
/* It is mounted inside the footer, which centres its text — so both of these opt back out. A
   form whose labels and prose are centred reads as a notice rather than something to fill in. */
.report-open { justify-content: center; }
.report-panel {
  border: 1px solid var(--es-border);
  border-radius: var(--es-radius-lg);
  background: var(--es-surface);
  padding: 14px 16px;
  margin-top: 14px;
  text-align: left;
}
.report-title { font-weight: 600; margin-bottom: 6px; }

/* The publish key row on the broadcast page. Hidden until go-live finds no key, so a
   broadcaster who already has one never meets it. */
#keyrow input { font-family: var(--es-font-mono); font-size: 0.85rem; }
