:root {
  --bg: #0f1217;
  --surface: #171c24;
  /* Dark UIs convey nesting by going lighter; darker means a recessed field
     you read a value out of, or type into. The old nested panel was #10141a on
     a #171c24 card — a 1.08:1 step, so the grouping it existed to convey was
     invisible and the card read as a pile of loose rows. */
  --surface-2: #1e2532;
  --sunken: #0c0f14;
  --border: #333d4d;
  --border-strong: #46536b;
  --text: #e8ecf1;
  --muted: #93a0b1;
  --accent: #4f8ef7;
  --accent-hover: #6ba1f8;
  --danger: #e5534b;
  --ok: #3fb96f;
  --radius: 10px;
  --gutter: 24px;
  --fs-xs: 0.75rem;
  --fs-sm: 0.8125rem;
  --fs-md: 0.9375rem;
  --fs-lg: 1.125rem;
  color-scheme: dark;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;   /* fallback for engines without dvh */
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.55 system-ui, "Segoe UI", Roboto, sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
}

header {
  width: 100%;
  max-width: 960px;
  padding: 20px var(--gutter) 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
}

header h1 { font-size: 1.15rem; margin: 0; letter-spacing: 0.2px; }
header .tag { color: var(--muted); font-size: 0.85rem; }

main {
  width: 100%;
  max-width: 960px;
  padding: var(--gutter);
  display: flex;
  flex-direction: column;
  gap: 16px;
}

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

.hidden { display: none !important; }

button {
  font: inherit;
  /* Transparent rather than none, so .secondary's border does not make it 2px
     taller than the accent and danger buttons standing beside it. */
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 9px 16px;
  min-height: 40px;
  cursor: pointer;
  background: var(--accent);
  color: #fff;
  transition: background 0.15s;
}
button:hover { background: var(--accent-hover); }
button.secondary { background: #232b37; color: var(--text); border-color: var(--border); }
button.secondary:hover { background: #2b3442; }
button.danger { background: var(--danger); }
button.danger:hover { background: #f0655d; }
/* A control that grants a stranger pointer and keyboard access should not sit
   in the same grey as its refusal. */
button.quiet { background: none; border-color: var(--border); color: var(--muted); }
button.quiet:hover { background: #212936; color: var(--text); }
button:focus-visible { outline: 2px solid var(--accent-hover); outline-offset: 2px; }
/* Sharing is disabled on every phone and tablet, and a disabled control that
   renders at full accent blue reads as a broken button rather than a limit. */
button:disabled,
button:disabled:hover {
  background: #232b37;
  color: #5c6675;
  border-color: var(--border);
  cursor: not-allowed;
}
button.danger:disabled { background: #232b37; }

/* A control that is a word in a sentence, not a box. Must follow button:hover:
   equal specificity, later wins. */
.linkish {
  background: none;
  border: 0;
  padding: 0;
  min-height: 0;
  color: var(--muted);
  font: inherit;
  font-size: var(--fs-sm);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.linkish:hover { background: none; color: var(--text); }

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

.link-box {
  flex: 1;
  min-width: 220px;
  background: var(--sunken);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.9rem;
  overflow-wrap: anywhere;
  user-select: all;
}

.status { color: var(--muted); font-size: 0.9rem; }
/* Both forms: nested (a span inside a .status line) and compound (the
   element itself carries the state, e.g. className = 'status bad'). */
.status .ok,
.status.ok { color: var(--ok); }
.status .bad,
.status.bad { color: var(--danger); }

video {
  width: 100%;
  max-height: 70vh;
  background: #000;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

.video-wrap { position: relative; }

.overlay-stats {
  position: absolute;
  left: 10px;
  bottom: 10px;
  background: rgba(10, 12, 16, 0.75);
  border-radius: 6px;
  padding: 4px 10px;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.78rem;
  color: #cfe0ff;
  pointer-events: none;
}

.note { color: var(--muted); font-size: 0.85rem; margin: 8px 0 0; }
.center-msg { text-align: center; padding: 48px 16px; color: var(--muted); }
.center-msg strong { color: var(--text); }

video { object-fit: contain; }

/* --- Remote control: presenter overlay --- */
.pointer-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  border-radius: var(--radius);
}
.rc-pointer {
  position: absolute;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: var(--c);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85), 0 1px 4px rgba(0, 0, 0, 0.5);
  transition: left 0.05s linear, top 0.05s linear, opacity 0.4s;
}
.rc-pointer span {
  position: absolute;
  left: 16px;
  top: -2px;
  white-space: nowrap;
  font: 600 0.72rem/1 Archivo, system-ui, sans-serif;
  background: var(--c);
  color: #fff;
  padding: 2px 6px;
  border-radius: 4px;
}
.rc-pointer.rc-self { width: 12px; height: 12px; margin: -6px 0 0 -6px; opacity: 0.85; }
.rc-click {
  position: absolute;
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  border: 2px solid var(--c);
  pointer-events: none;
  animation: rc-ripple 0.6s ease-out forwards;
}
@keyframes rc-ripple {
  from { transform: scale(0.4); opacity: 0.9; }
  to { transform: scale(3.5); opacity: 0; }
}

/* --- Remote control: presenter viewer list + inbox --- */
.rc-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 4px; }
.rc-viewer {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 8px 12px;
}
.rc-swatch { width: 12px; height: 12px; border-radius: 50%; background: var(--c); flex: none; }
.rc-name { font-weight: 600; }
.rc-viewer-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.rc-viewer-actions button { font-size: var(--fs-sm); }
.rc-badge {
  font-family: ui-monospace, Consolas, monospace;
  font-size: var(--fs-xs);
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(147, 160, 177, 0.14);
  color: var(--muted);
  white-space: nowrap;
}
.rc-badge.ok { background: rgba(63, 185, 111, 0.16); color: var(--ok); }
.rc-badge.want { background: rgba(229, 147, 75, 0.16); color: #e5934b; animation: rc-pulse 1.4s ease-in-out infinite; }
@keyframes rc-pulse { 50% { opacity: 0.45; } }

.rc-inbox-head { display: flex; align-items: baseline; justify-content: space-between; margin-top: 20px; gap: 12px; flex-wrap: wrap; }
.rc-inbox-head h3 { margin: 0; font-size: 1rem; }
.rc-inbox { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.rc-item { background: var(--surface-2); border: 1px solid var(--border-strong); border-radius: 8px; padding: 10px 12px; }
.rc-item-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.rc-item-head button { min-height: 32px; padding: 5px 12px; font-size: var(--fs-sm); }
.rc-who { font: 600 0.78rem/1 Archivo, system-ui, sans-serif; color: var(--c); }
.rc-item-body { white-space: pre-wrap; overflow-wrap: anywhere; font-family: ui-monospace, Consolas, monospace; font-size: 0.9rem; }

/* --- The live session card --- */
.live-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px 16px; flex-wrap: wrap; margin-bottom: 16px;
}
.card h2 { margin: 0 0 4px; font-size: var(--fs-lg); font-weight: 650; }
.live-head h2 { margin: 0; display: flex; align-items: center; gap: 9px; }
.live-dot {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--ok); box-shadow: 0 0 0 3px rgba(63, 185, 111, 0.18);
}
.live-meta { margin: 0; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* The link and the QR are one idea — two ways to hand the session over — so
   they share one raised panel instead of sitting as two loose rows. */
.handoff {
  display: flex; gap: 20px; align-items: stretch;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 16px;
}
.handoff-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.field-label {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
}
.qr-hint { margin: auto 0 0; }

.session-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
}
.rec-group { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.rec-group .status:empty { display: none; }
.session-actions #stopBtn { margin-left: auto; }

.video-wrap { position: relative; margin-top: 16px; }
.fine-print { margin-top: 16px; }
.fine-print summary { cursor: pointer; color: var(--muted); font-size: var(--fs-sm); }
.fine-print summary::marker { color: var(--accent); }

/* --- QR code --- */
.qr-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  flex: none;
}
.qr-row.hidden { display: none; }
/* QR codes need a light quiet zone to scan reliably, so the code sits on white
   regardless of the surrounding dark UI. The box keeps its size in every state,
   so the layout never jumps and an empty slot is never mistaken for a missing
   feature — which is exactly how a slow encoder fetch used to read. */
.qr {
  width: 136px;
  height: 136px;
  flex: none;
  background: #fff;
  border-radius: 8px;
  padding: 8px;
  display: grid;
  place-items: center;
  line-height: 0;
}
.qr svg { display: block; width: 120px; height: 120px; }
.qr::after {
  font: 500 var(--fs-xs)/1.35 system-ui, sans-serif;
  color: #5b6675;
  text-align: center;
  padding: 0 6px;
}
.qr-row.is-pending .qr { background: #e9edf3; animation: qr-wait 1.6s ease-in-out infinite; }
.qr-row.is-pending .qr::after { content: "Generating…"; }
.qr-row.is-failed .qr::after { content: "QR unavailable — the link above still works"; }
@keyframes qr-wait { 50% { opacity: 0.55; } }

.qr-cap { margin: 0; color: var(--muted); font-size: var(--fs-xs); }

/* --- Phones and tablets have no capture API --- */
.cant-capture > h2,
.cant-capture > p.note,
.cant-capture > .row { display: none; }   /* `>` keeps #noCapture's own .row */
.no-capture h3 { margin: 0 0 6px; font-size: var(--fs-lg); }
.btn-link {
  display: inline-flex; align-items: center; min-height: 40px;
  padding: 9px 16px; border-radius: 8px; text-decoration: none;
  background: #232b37; color: var(--text); border: 1px solid var(--border);
}

/* --- Recording --- */
.rec-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--danger);
  flex: none;
  vertical-align: middle;
  animation: rec-blink 1.6s ease-in-out infinite;
}
.rec-dot.paused { animation: none; opacity: 0.5; }
@keyframes rec-blink { 50% { opacity: 0.25; } }

/* --- Remote control: native companion --- */
.rc-companion {
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 12px 0;
}
.rc-companion summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.92rem;
}
.rc-companion summary::marker { color: var(--accent); }
.rc-companion .row { margin-top: 10px; }
.rc-input {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  font: inherit;
  font-size: 0.9rem;
}
.rc-input { flex: 1; min-width: 160px; }
.rc-port { flex: 0 0 84px; min-width: 0; font-family: ui-monospace, Consolas, monospace; }

/* --- Remote control: viewer bar --- */
.rc-bar { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 14px; }
.rc-latency { font-family: ui-monospace, Consolas, monospace; color: var(--muted); }
#controlState.on { color: var(--ok); }
.rc-prompt { display: flex; gap: 8px; margin-top: 10px; align-items: stretch; }
.rc-prompt textarea {
  flex: 1;
  resize: vertical;
  background: var(--sunken);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  font: inherit;
  font-size: 0.92rem;
}
.rc-prompt button { white-space: nowrap; }
/* pinch-zoom, not none: none also surrenders the browser's own pinch, so
   enabling control silently killed zoom. Pans are ours; the pinch stays native. */
video.rc-active { cursor: crosshair; touch-action: pinch-zoom; outline: 2px solid var(--accent); }

@media (max-width: 600px) {
  :root { --gutter: 14px; }
  header { padding-top: 16px; }
  .card { padding: var(--gutter); }
  main { gap: 12px; }

  .handoff { flex-direction: column; align-items: stretch; padding: 14px; gap: 14px; }
  .handoff .row { flex-direction: column; align-items: stretch; }
  .handoff #copyBtn { width: 100%; }
  .qr-row { flex-direction: row; align-items: center; gap: 12px; align-self: flex-start; }
  .qr svg { width: 112px; height: 112px; }

  .session-actions { flex-direction: column; align-items: stretch; }
  .session-actions #stopBtn { margin-left: 0; width: 100%; order: 99; }
  .rec-group { justify-content: space-between; }

  .rc-viewer { flex-wrap: wrap; }
  .rc-viewer-actions { width: 100%; margin-left: 0; flex-wrap: wrap; }
  .rc-input { font-size: 16px; }   /* under 16px, iOS Safari zooms and never zooms back */
  .rc-port { flex: 0 0 96px; }
}

/* Fingers need more than a mouse does. */
@media (pointer: coarse) {
  button { min-height: 44px; }
  .rc-item-head button { min-height: 40px; }
}

/* ==========================================================================
   Viewer — video-first.
   The shared screen is the product; chrome overlays it and gets out of the
   way. On a phone every pixel of chrome is a pixel of someone's screen you
   cannot read.
   ========================================================================== */

body.viewer {
  display: block;
  height: 100dvh;
  overflow: hidden;
  background: #000;
}

.stage {
  position: relative;
  width: 100%;
  height: 100dvh;
  background: #000;
  overflow: hidden;
}

.stage > video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
  border: 0;
  border-radius: 0;
  max-height: none;
  display: block;
}

/* --- heads-up chrome --- */
.hud {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  color: #e8ecf1;
  opacity: 1;
  transition: opacity 0.35s ease;
  z-index: 3;
  pointer-events: none;
}
.hud > * { pointer-events: auto; }
.hud-top {
  top: 0;
  padding-top: max(12px, env(safe-area-inset-top));
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.72), transparent);
}
.hud-bottom {
  bottom: 0;
  padding-bottom: max(12px, env(safe-area-inset-bottom));
  background: linear-gradient(to top, rgba(0, 0, 0, 0.72), transparent);
  flex-wrap: wrap;
}
/* Idle: fade the chrome away so nothing sits on top of the screen you are
   trying to read. Any tap or pointer movement brings it back. */
.stage.idle .hud { opacity: 0; }
.stage.idle { cursor: none; }

.hud-brand { font-weight: 600; font-size: 0.95rem; letter-spacing: 0.2px; }
.hud-room {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.78rem;
  color: #b9c6ce;
}
.hud-stats {
  margin-left: auto;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.72rem;
  color: #b9c6ce;
  white-space: nowrap;
}
.hud-state {
  margin-left: auto;
  font-size: 0.75rem;
  color: #b9c6ce;
  white-space: nowrap;
}

.hud-btn {
  background: rgba(22, 28, 36, 0.82);
  color: #e8ecf1;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px;
  padding: 9px 16px;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  backdrop-filter: blur(8px);
  min-height: 40px; /* comfortable touch target */
}
.hud-btn:hover { background: rgba(38, 46, 58, 0.9); }
.hud-btn.primary { background: var(--accent); color: #fff; border-color: transparent; }

/* --- centred status --- */
.stage-msg {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  text-align: center;
  padding: 32px 24px;
  background: #0f1217;
  color: var(--muted);
  font-size: 1rem;
}
.stage-msg.hidden { display: none; }
.stage-msg strong { color: var(--text); font-size: 1.05rem; font-weight: 600; }
.stage-msg button {
  background: var(--accent);
  color: #fff;
  border: 0;
  border-radius: 8px;
  padding: 11px 20px;
  font: inherit;
  cursor: pointer;
  min-height: 44px;
}

/* --- control sheet --- */
.sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-radius: 14px 14px 0 0;
  padding: 16px 16px max(16px, env(safe-area-inset-bottom));
  box-shadow: 0 -18px 50px -20px #000;
  max-height: 70dvh;
  overflow-y: auto;
}
.sheet.hidden { display: none; }
.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}
.sheet .row { margin-top: 12px; }
.sheet .rc-prompt { margin-top: 12px; }

/* On a phone the prompt box and its button should stack, not squeeze. */
@media (max-width: 560px) {
  .sheet .rc-prompt { flex-direction: column; align-items: stretch; }
  .sheet .rc-prompt button { min-height: 44px; }
  .hud-stats { display: none; } /* the resolution readout is desktop detail */
  .hud-bottom { gap: 8px; }
  .hud-btn { padding: 9px 14px; }
}

/* --- Trackpad cursor ---
   The transient .rc-pointer dot fades, because in direct mode your finger
   already marks the spot. A trackpad cursor must not: it is the only thing
   showing where a tap will land, so it stays put and reads as an aim point
   rather than a trail. */
.rc-pointer.rc-cursor {
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  background: none;
  border: 2px solid var(--c);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(0, 0, 0, 0.55);
  opacity: 1;
  transition: none;   /* the direct-mode dot eases between points; an aim does not */
}
/* A centre dot, so the exact click point is unambiguous at a glance. */
.rc-pointer.rc-cursor::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 4px;
  height: 4px;
  margin: -2px 0 0 -2px;
  border-radius: 50%;
  background: var(--c);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55);
}
/* Claim the gestures only while trackpad control is live, so pinch-zoom and
   scrolling still work on the page the rest of the time. */
/* none, not pinch-zoom: trackpad mode implements its own pinch (a transform
   that also works inside fullscreen, where browser page-zoom is disabled), so
   the browser's must stay out of its way. Direct-tap mode above keeps the
   native pinch. */
video.rc-trackpad { touch-action: none; }

/* The plan badge sits in the same row as the transport badge, so it must read
   as status rather than an advert. The link is the only coloured thing in it. */
#planLabel a { color: var(--accent); text-decoration: none; }
#planLabel a:hover { text-decoration: underline; }
/* A whole sentence, unlike the other badges, so on a phone it must wrap rather
   than push the row off the screen. */
#capNotice { white-space: normal; }

/* Someone was turned away (.refused), or the session being resumed had already
   ended and this is a new one (.ended). Facts about the session rather than
   alarms, so they take the card's own raised surface and the body text colour;
   a link is the one thing that leads somewhere and the only thing coloured. */
.refused,
.ended {
  display: flex; align-items: baseline; gap: 8px 16px; flex-wrap: wrap;
  margin: -4px 0 16px;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-size: var(--fs-sm);
  color: var(--text);
}
.refused a { color: var(--accent); text-decoration: none; }
.refused a:hover { text-decoration: underline; }
.refused .linkish,
.ended .linkish { margin-left: auto; flex: none; }

/* --- Resume after a reload ---
   Sits above the start card because it is the time-limited choice: the room
   it names is only held for minutes. The card's own surface and no new colour
   — an offer, not an alarm; the accent button is the one thing that leads on. */
.resume .row { margin-top: 12px; }
