/* =========================================================================
   Remote — web client styles
   Mobile-first, dark, minimal. Designed for Safari on iPhone/iPad.
   ========================================================================= */

:root {
  --bg: #0b0b0d;
  --bg-elev: #17171b;
  --bg-elev2: #202027;
  --fg: #f2f2f4;
  --fg-dim: #9a9aa3;
  --accent: #4c8dff;
  --accent-press: #2f6fe0;
  --danger: #ff5a5f;
  --ok: #37d67a;
  --warn: #ffb020;
  --border: #2a2a31;
  --bar-h: 44px;

  /* Floating control pill. The expanded stack holds more icons than fit on a
     phone, so it gets a FIXED height of `--ctl-visible` buttons and scrolls.
     The fractional value leaves half an icon peeking at the cut edge, which is
     what tells you there is more to scroll to. */
  --ctl-size: 40px;
  --ctl-gap: 5px;
  --ctl-visible: 5.5;

  /* Safe-area insets (notch / home indicator). Fall back to 0. */
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);
  --sal: env(safe-area-inset-left, 0px);
  --sar: env(safe-area-inset-right, 0px);

  /* Height of the on-screen keyboard, updated at runtime from visualViewport. */
  --kb: 0px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--fg);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  /* Kill native scroll/zoom gestures everywhere; we handle touch ourselves. */
  touch-action: none;
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

#app {
  position: relative;
  height: 100dvh;      /* dynamic viewport height, robust to iOS URL bar */
  width: 100vw;
  overflow: hidden;
}

/* ------------------------- Floating top HUD ---------------------------- */
/* Translucent status strip over the full-bleed video (does not take layout
   space, so the stream reaches the very top of the screen). */
.status-panel {
  position: absolute;
  right: calc(64px + var(--sar));
  bottom: calc(12px + var(--sab));
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  /* Fixed width so multi-digit fps/kb/s never reflow the right-anchored panel;
     the codec/debug line wraps within it instead of being clipped. */
  width: min(300px, calc(100vw - 84px));
  padding: 10px 14px;
  border-radius: 14px;
  background: rgba(20, 20, 26, 0.82);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border: 1px solid rgba(255,255,255,0.10);
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  font-size: 13px; color: var(--fg);
  z-index: 26;
}
.status-panel.hidden { display: none; }
.status-panel > #statusText { font-weight: 600; }

.video-debug {
  font-size: 11px;
  color: var(--fg-dim);
  width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.35;
}
.video-debug:empty { display: none; }

/* Technical connection details (WebRTC ICE pair, ciphers, codec, bitrate…). */
.conn-details {
  width: 100%;
  margin-top: 2px;
  border-top: 1px solid rgba(255,255,255,0.08);
  padding-top: 6px;
  display: flex; flex-direction: column; gap: 2px;
  font-size: 11px; line-height: 1.3;
}
.conn-details:empty { display: none; }
.cd-row { display: flex; gap: 8px; justify-content: space-between; }
.cd-k { color: var(--fg-dim); white-space: nowrap; }
.cd-v {
  color: var(--fg); text-align: right;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Candidate list lines: full-width, left-aligned, slightly dimmer. */
.cd-row.cd-sub .cd-v { text-align: left; color: var(--fg-dim); font-size: 10px; }
.cd-row.cd-sub .cd-k { flex: 0 0 8px; }
.dot {
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--fg-dim);
  box-shadow: 0 0 6px transparent;
}
.dot[data-state="open"]        { background: var(--ok);   box-shadow: 0 0 8px var(--ok); }
.dot[data-state="connecting"]  { background: var(--warn); }
.dot[data-state="reconnecting"]{ background: var(--warn); }
.dot[data-state="closed"]      { background: var(--danger); }

.hud {
  display: flex; gap: 12px;
  font-size: 12px; color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  padding: 4px 8px;
  margin: -4px 0;
  border-radius: 10px;
}
.hud:active { background: rgba(255,255,255,0.08); }
.hud b { color: var(--fg); font-weight: 600; display: inline-block; text-align: right; }
/* Reserve space for the widest value so digit growth doesn't jitter the row. */
#hudFps { min-width: 2ch; }
#hudKbps { min-width: 5ch; }
#hudRtt { min-width: 3ch; }
.hud.graph-on b { color: var(--accent); }

/* ------------------------- Net graph overlay --------------------------- */
/* CS-style realtime graph: legend with live values + scrolling sparklines.
   Floats bottom-left, above the safe area; never intercepts touches. */
.net-graph {
  position: absolute;
  left: calc(10px + var(--sal));
  bottom: calc(64px + var(--sab));
  width: min(340px, calc(100vw - 20px));
  height: 96px;
  display: flex;
  padding: 8px 10px;
  gap: 10px;
  background: rgba(12, 12, 15, 0.66);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  pointer-events: none;
  z-index: 24;
}
.net-graph.hidden { display: none; }
.ng-legend {
  display: flex; flex-direction: column; justify-content: center;
  gap: 6px;
  font-size: 11px; color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ng-item { display: flex; align-items: baseline; gap: 5px; }
.ng-item i {
  width: 8px; height: 8px; border-radius: 2px;
  align-self: center;
}
.ng-item b { color: var(--fg); font-weight: 700; font-size: 13px; min-width: 34px; text-align: right; }
.ng-fps i  { background: var(--ok); }
.ng-kbps i { background: var(--accent); }
.ng-ping i { background: var(--warn); }
.ng-canvas { flex: 1; height: 100%; }

/* ------------------------------ Buttons -------------------------------- */
.btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg-elev2);
  color: var(--fg);
  font-size: 14px;
  padding: 8px 14px;
  border-radius: 10px;
  cursor: pointer;
  touch-action: manipulation;
}
.btn:active { background: #2c2c34; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.primary:active { background: var(--accent-press); }
.btn.primary:disabled { opacity: 0.4; }
.btn.ghost { background: transparent; }
.btn.key { padding: 8px 0; min-width: 40px; font-size: 15px; }

/* --------------------- Floating control pill --------------------------- */
.controls {
  position: absolute;
  right: calc(10px + var(--sar));
  /* Vertically centred on the right edge — the easiest place to reach with a
     thumb, and it lets the stack grow in BOTH directions instead of only up.
     Centring an absolutely positioned box via `top/bottom: 0` + auto margins
     needs a non-auto height (or it would stretch to fill), hence `max-content`;
     doing it with a transform instead would offset the box from where `bottom`
     puts it, which breaks the drag-to-move maths below. Dragging sets
     `top: auto`, which cancels the centring on its own. */
  top: calc(var(--sat) + 8px);
  bottom: calc(var(--sab) + 8px);
  height: max-content;
  margin: auto 0;
  display: flex; flex-direction: column; align-items: center; gap: var(--ctl-gap);
  padding: 5px;
  /* Cap to the viewport so the stack never runs off the top or bottom (e.g.
     landscape, where height is small); the actions scroll while the toggle and
     the persistent buttons stay pinned. */
  max-height: calc(100dvh - var(--sat) - var(--sab) - 16px);
  background: rgba(28, 28, 34, 0.72);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 999px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  z-index: 25;
}
.controls.hidden { display: none; }

/* Collapsible: the action buttons stack above an always-visible toggle. When
   collapsed only the toggle shows (default), keeping the stream unobstructed.
   The stack is capped at `--ctl-visible` buttons and scrolls beyond that, so
   adding icons never turns the pill into a full-height column. */
.ctl-actions {
  display: flex; flex-direction: column; align-items: center; gap: var(--ctl-gap);
  max-height: calc(var(--ctl-visible) * var(--ctl-size) +
                   (var(--ctl-visible) - 1) * var(--ctl-gap));
  overflow-y: auto; min-height: 0;
  overscroll-behavior: contain;   /* don't hand the scroll to the page/stream */
  scrollbar-width: none; -ms-overflow-style: none;
}
.ctl-actions::-webkit-scrollbar { display: none; }
/* Scroll affordance: the icon at the clipped edge DISSOLVES instead of being cut
   off, which reads as “there is more” rather than “the pill ends here”. Which
   edges fade is driven from JS (`data-overflow`), because CSS can't see scroll
   position — so a stack that fits keeps crisp edges and looks deliberate. */
.ctl-actions[data-overflow='bottom'] {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 30px), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 30px), transparent);
}
.ctl-actions[data-overflow='top'] {
  -webkit-mask-image: linear-gradient(to top, #000 calc(100% - 30px), transparent);
          mask-image: linear-gradient(to top, #000 calc(100% - 30px), transparent);
}
.ctl-actions[data-overflow='both'] {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 30px, #000 calc(100% - 30px), transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 30px, #000 calc(100% - 30px), transparent);
}
.controls.collapsed .ctl-actions { display: none; }
.ctl-toggle .ico { transition: transform .2s ease; }
.controls:not(.collapsed) .ctl-toggle .ico { transform: rotate(180deg); }
.ctl-toggle { touch-action: none; }   /* drag handle: don't let the browser scroll while moving the pill */

.ctl-btn {
  appearance: none;
  flex: 0 0 auto;
  width: var(--ctl-size); height: var(--ctl-size);
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.ctl-btn:active { background: rgba(255,255,255,0.14); }
.ctl-btn.active { background: rgba(76,141,255,0.28); color: var(--accent); }
.ctl-btn .ico { width: 22px; height: 22px; }
.ico { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.ico.dots { fill: currentColor; stroke: none; }

/* -------------------- Shortcut macro strip ----------------------------- */
/* Bottom-left strip (clears the pill on the right), horizontally scrollable. */
.macro-bar {
  position: absolute;
  left: calc(10px + var(--sal));
  right: calc(72px + var(--sar));
  bottom: calc(10px + var(--sab));
  display: flex; gap: 6px;
  padding: 6px;
  overflow-x: auto; overscroll-behavior: contain;
  background: rgba(28, 28, 34, 0.72);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 14px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  z-index: 24;
  scrollbar-width: none; -ms-overflow-style: none;
}
.macro-bar::-webkit-scrollbar { display: none; }
.macro-bar.hidden { display: none; }
.macro-btn {
  appearance: none; flex: 0 0 auto;
  min-width: 44px; height: 38px; padding: 0 10px;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 9px;
  background: rgba(255,255,255,0.14);
  color: var(--fg);
  font-size: 14px; line-height: 1; white-space: nowrap;
  font-variant-numeric: tabular-nums;
  cursor: pointer; touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.macro-btn:active { background: var(--accent); color: #fff; }

.reconnect-float {
  position: absolute;
  left: 50%; bottom: calc(64px + var(--sab));
  transform: translateX(-50%);
  appearance: none; cursor: pointer;
  background: var(--accent); color: #fff;
  border: none; border-radius: 999px;
  padding: 10px 18px; font-size: 14px; font-weight: 600;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  z-index: 26;
}
.reconnect-float.hidden { display: none; }

/* --------------------- Control role (spectator) ------------------------ */
.role-banner {
  position: absolute;
  top: calc(8px + var(--sat)); left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: center;
  max-width: calc(100vw - 24px);
  padding: 10px 14px;
  background: rgba(28, 28, 34, 0.95);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  font-size: 13px; z-index: 30;
}
.role-banner.hidden { display: none; }
.role-actions { display: flex; gap: 8px; }
.role-banner .btn { padding: 6px 12px; font-size: 13px; }
.spectate-chip {
  position: absolute;
  top: calc(8px + var(--sat)); left: 50%;
  transform: translateX(-50%);
  appearance: none; cursor: pointer;
  background: rgba(28, 28, 34, 0.85); color: var(--fg);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid var(--border); border-radius: 999px;
  padding: 6px 14px; font-size: 12px; z-index: 30;
}
.spectate-chip.hidden { display: none; }

/* ---------------- Foreground alert banner (Web Push) ------------------- */
.alert-toast {
  position: absolute;
  top: calc(8px + var(--sat)); left: 50%;
  transform: translateX(-50%);
  max-width: calc(100vw - 24px);
  padding: 10px 14px;
  background: rgba(28, 28, 34, 0.92);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  color: var(--fg); z-index: 40;
  pointer-events: none;
}
.alert-toast-title { font-size: 13px; font-weight: 600; }
.alert-toast-body { font-size: 12px; color: var(--fg-dim); margin-top: 2px; }
/* Carries a window to jump to: make it read as tappable. */
.alert-toast.tappable { cursor: pointer; border-color: rgba(76,141,255,0.5); }
.alert-toast.tappable:active { background: rgba(76,141,255,0.22); }

/* ---------------- Quick-switch label (Cmd-Tab style) ------------------- */
.switch-toast {
  position: absolute;
  left: 50%; bottom: calc(76px + var(--sab));
  transform: translateX(-50%) translateY(6px);
  max-width: 80vw;
  padding: 8px 16px;
  background: rgba(20, 20, 26, 0.92);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid var(--border); border-radius: 999px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  color: var(--fg); font-size: 14px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  pointer-events: none; z-index: 40;
  opacity: 0; transition: opacity .15s ease, transform .15s ease;
}
.switch-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* -------------------- Special-keys accessory bar ----------------------- */
/* Sits directly above the iOS soft keyboard (bottom = keyboard height). */
.key-accessory {
  position: absolute;
  left: 0; right: 0;
  bottom: var(--kb);
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px;
  padding: 6px calc(6px + var(--sar)) 6px calc(6px + var(--sal));
  background: rgba(38, 38, 44, 0.92);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  border-top: 1px solid rgba(255,255,255,0.08);
  z-index: 40;
}
.key-accessory.hidden { display: none; }
.key-cluster { display: flex; align-items: center; gap: 5px; }
.kbtn {
  appearance: none;
  min-width: 32px; height: 38px;
  padding: 0 6px;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 8px;
  background: rgba(255,255,255,0.14);
  color: var(--fg);
  font-size: 15px; line-height: 1;
  cursor: pointer; touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  box-shadow: 0 1px 0 rgba(0,0,0,0.3);
}
.kbtn:active { background: rgba(255,255,255,0.28); }
.kbtn.mod.armed { background: var(--accent); color: #fff; }

/* ------------------------------- Stage --------------------------------- */
#stage {
  position: absolute;
  /* Keep the shared content clear of the iOS status bar (clock/Wi-Fi) at the top
     so the shared window's own title bar stays visible. --sat is 0 outside a
     safe-area context (desktop), so this is a no-op there. */
  top: var(--sat);
  right: 0;
  bottom: 0;
  left: 0;
  background: #000;
  overflow: hidden;
  z-index: 0;
}

#view, #cursor {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
#cursor { pointer-events: none; z-index: 5; }

.empty-hint {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 14px; color: var(--fg-dim);
  z-index: 4;
}
.empty-hint.hidden { display: none; }

.lock-overlay {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 8px; text-align: center; padding: 24px;
  background: rgba(11, 11, 13, 0.92);
  z-index: 6;
}
.lock-overlay.hidden { display: none; }
.lock-badge { font-size: 44px; }
.lock-title { margin: 4px 0 0; font-size: 18px; font-weight: 600; }
.lock-sub { margin: 0; max-width: 320px; color: var(--fg-dim); font-size: 13px; }

/* --------------------------- Link-down overlay -------------------------- */
/* Dims the last frame rather than replacing it with black: keeping the picture
   visible tells you WHERE you were, and the card explains WHY it stopped. */
.link-overlay {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  background: rgba(11, 11, 13, 0.62);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  z-index: 7;
}
.link-overlay.hidden { display: none; }
.link-card {
  display: flex; flex-direction: column; align-items: center;
  gap: 8px; text-align: center;
  min-width: 240px; max-width: 340px;
  padding: 18px 20px;
  background: rgba(28, 28, 34, 0.92);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 16px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.link-spinner {
  width: 26px; height: 26px;
  border: 2.5px solid rgba(255,255,255,0.18);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: link-spin .8s linear infinite;
}
@keyframes link-spin { to { transform: rotate(360deg); } }
/* A dead link is not "in progress": stop the spinner so it doesn't imply work. */
.link-overlay[data-state='closed'] .link-spinner {
  animation: none; border-top-color: var(--danger); opacity: .5;
}
.link-title { margin: 2px 0 0; font-size: 16px; font-weight: 600; }
.link-sub { margin: 0; color: var(--fg-dim); font-size: 12px; }
.link-sub:empty { display: none; }
.link-card .conn-details { text-align: left; }
.link-card .btn { margin-top: 4px; }

@media (prefers-reduced-motion: reduce) {
  .link-spinner { animation: none; }
}

/* ------------------------- Region selection ---------------------------- */
.overlay { position: absolute; inset: 0; z-index: 30; }
.overlay.hidden { display: none; }

#regionOverlay { touch-action: none; background: rgba(0,0,0,0.15); }
.region-rect {
  position: absolute;
  border: 2px solid var(--accent);
  background: rgba(76, 141, 255, 0.18);
  box-shadow: 0 0 0 9999px rgba(0,0,0,0.35);
  pointer-events: none;
}
.region-rect.hidden { display: none; }
.region-bar {
  position: absolute;
  left: 50%; bottom: calc(16px + var(--sab));
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 8px 12px;
  z-index: 31;
}
.region-hint { font-size: 12px; color: var(--fg-dim); }

/* --------------------------- Input panel ------------------------------- */
/* ---------------------- Hidden keyboard driver ------------------------- */
/* Off-screen input that raises the iOS soft keyboard when focused on tap.
   Must stay focusable (no display:none / visibility:hidden). font-size >=16px
   prevents iOS from zooming the page on focus. */
.hidden-input {
  position: fixed;
  bottom: 0; left: 0;
  width: 1px; height: 1px;
  padding: 0; margin: 0; border: 0;
  opacity: 0;
  font-size: 16px;
  color: transparent;
  background: transparent;
  caret-color: transparent;
  pointer-events: none;
  z-index: -1;
}

/* --------------------------- Source picker ----------------------------- */
.sheet {
  background: rgba(0, 0, 0, 0.5);
  display: flex; align-items: stretch;
}
.sheet-panel {
  width: 100%;
  height: 100dvh; max-height: 100dvh;
  background: var(--bg-elev);
  display: flex; flex-direction: column;
  padding-bottom: var(--sab);
}
.sheet-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px;
  padding-top: calc(14px + var(--sat));
  padding-left: calc(16px + var(--sal));
  padding-right: calc(16px + var(--sar));
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
}
.sheet-head h2 { margin: 0; font-size: 17px; }
.sheet-actions { display: flex; gap: 8px; }
.sheet-body {
  flex: 1 1 auto; overflow-y: auto;
  padding: 10px 16px 24px;
  padding-left: calc(16px + var(--sal));
  padding-right: calc(16px + var(--sar));
  -webkit-overflow-scrolling: touch; touch-action: pan-y;
}
.sheet-body h3 { color: var(--fg-dim); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; margin: 14px 0 6px; }

/* Windows subheading with the sort toggle on the right. */
.sheet-subhead { display: flex; align-items: center; justify-content: space-between; }
.sheet-subhead h3 { margin: 14px 0 6px; }
.btn.tiny { padding: 3px 10px; font-size: 12px; border-radius: 8px; }

/* Controls guide (first-run + Help): fullscreen list explaining each icon. */
.guide-overlay { background: rgba(0,0,0,0.55); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); display: flex; align-items: stretch; }
.guide-panel {
  width: 100%; height: 100dvh; max-height: 100dvh;
  background: var(--bg-elev); display: flex; flex-direction: column;
  padding-bottom: var(--sab);
}
.guide-head {
  position: sticky; top: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px; padding-top: calc(14px + var(--sat));
  padding-left: calc(16px + var(--sal)); padding-right: calc(16px + var(--sar));
  background: var(--bg-elev); border-bottom: 1px solid var(--border);
}
.guide-head h2 { margin: 0; font-size: 17px; }
.guide-body {
  flex: 1 1 auto; overflow-y: auto;
  padding: 8px 16px 24px;
  padding-left: calc(16px + var(--sal)); padding-right: calc(16px + var(--sar));
  -webkit-overflow-scrolling: touch;
}
.guide-row { display: flex; align-items: center; gap: 14px; padding: 11px 4px; border-bottom: 1px solid var(--border); }
.guide-row:last-child { border-bottom: none; }
.guide-ico {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 11px;
  display: grid; place-items: center;
  background: var(--bg-elev2); border: 1px solid var(--border); color: var(--fg);
}
.guide-ico .ico { width: 22px; height: 22px; }
.guide-text { min-width: 0; }
.guide-text .t { font-weight: 600; font-size: 14px; }
.guide-text .d { color: var(--fg-dim); font-size: 12.5px; line-height: 1.35; margin-top: 1px; }

.window-search {
  appearance: none; -webkit-appearance: none;
  width: 100%;
  margin: 0 0 8px;
  padding: 9px 12px;
  font-size: 16px;   /* >=16px avoids iOS zoom-on-focus */
  color: var(--fg);
  background: var(--bg-elev2);
  border: 1px solid var(--border);
  border-radius: 10px;
  outline: none;
}
.window-search:focus { border-color: var(--accent); }
.window-search::placeholder { color: var(--fg-dim); }

.source-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.source-item {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-elev2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
}
/* Unified-display row: highlighted variant of a source-item at the top. */
.unified-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-elev2);
  border: 1px solid var(--accent);
  border-radius: 10px;
  padding: 10px 12px;
}
.unified-row .meta { flex: 1 1 auto; min-width: 0; }
.unified-row .app { font-weight: 600; font-size: 14px; }
.unified-row .title { color: var(--fg-dim); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.unified-row .btn { flex: 0 0 auto; }
.clip-row {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px;
}
.clip-label { color: var(--fg-dim); font-size: 12px; margin-right: auto; }
.clip-row .btn { flex: 0 0 auto; padding: 6px 12px; font-size: 13px; }

/* Unified-display scale slider. */
.scale-row {
  display: flex; align-items: center; gap: 10px;
  margin-top: 10px;
}
.scale-row .clip-label { margin-right: 0; flex: 0 0 auto; }
.scale-slider {
  flex: 1 1 auto; min-width: 0;
  accent-color: var(--accent);
  height: 28px;
}
.scale-out {
  flex: 0 0 auto;
  min-width: 84px; text-align: right;
  color: var(--fg); font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.scale-hint {
  color: var(--fg-dim); font-size: 11px; line-height: 1.35;
  margin-top: 2px;
}
.list-hint {
  margin: 0 0 8px; color: var(--fg-dim); font-size: 11px; line-height: 1.4;
}
.list-hint b { color: var(--fg); font-weight: 600; }
.source-item .meta { flex: 1 1 auto; min-width: 0; }
.source-item .app { font-weight: 600; font-size: 14px; }
.source-item .title { color: var(--fg-dim); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.source-item .pick { flex: 0 0 auto; }
/* Share shape: how this device frames the window.
     Fit         = resize the window to the device, show all of it
     Tall        = keep its size, fill the width, scroll up/down
     Left/Right  = keep its size, fill the height from that edge, swipe sideways
   A segmented control rather than three separate toggles, because the options
   are mutually exclusive and the current one has to be readable at a glance. */
.source-item .shape {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 2px; padding: 2px;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 8px;
}
.source-item .shape .seg {
  appearance: none; -webkit-appearance: none;
  background: transparent; border: 0; border-radius: 6px;
  color: var(--fg-dim); font: inherit; font-size: 11px; line-height: 1;
  padding: 6px 2px; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.source-item .shape .seg.on {
  background: var(--accent); color: #fff; font-weight: 600;
}
.source-item .region-btn { flex: 0 0 auto; }

/* ---- Thumbnail grid ------------------------------------------------------
   A column of one-line rows leaves most of a sheet empty, and a title alone is a
   poor way to tell two editor windows apart. Same markup, relaid out via grid
   areas: the preview spans the top, the Recent badge floats over its corner.

   Two tiles per row on a phone; wider screens fit as many as 240px allows. The
   preview resolution follows the tile size (the client measures it and asks the
   host for exactly that), so two-up is not a trade against sharpness. */
.source-list.grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
@media (min-width: 700px) {
  .source-list.grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
}
.source-list.grid .source-item {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "thumb thumb"
    "meta  meta"
    "shape shape"
    "pick  pick";
  align-content: start;
  gap: 8px;
  padding: 10px;
  cursor: pointer;
}
.source-list.grid .source-item:active { border-color: var(--accent); }
.source-list.grid .thumb {
  grid-area: thumb;
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: 7px;
  overflow: hidden;
  background: var(--bg-elev) center / 22px no-repeat;
  border: 1px solid var(--border);
}
.source-list.grid .thumb img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
  display: block;
  opacity: 0; transition: opacity .18s ease;
}
/* Only a loaded image fades in, so an empty tile stays a neutral placeholder
   instead of flashing a broken-image glyph. */
.source-list.grid .thumb img[src] { opacity: 1; }
.source-list.grid .thumb.empty { opacity: .45; }
.source-list.grid .meta { grid-area: meta; }
.source-list.grid .shape { grid-area: shape; }
.source-list.grid .pick { grid-area: pick; }
/* Anything sitting ON the preview needs lifting above it. The tile is
   `position: relative`, and a positioned element paints above non-positioned
   siblings whatever the DOM order says, so a plain grid item in the same area
   ends up UNDER the screenshot: invisible, and worse, a tap lands on the tile
   and shares the window instead. */
.source-list.grid .recent-badge,
.source-list.grid .fav {
  position: relative;
  z-index: 1;
}
.source-list.grid .recent-badge {
  grid-area: thumb;
  align-self: start; justify-self: end;
  margin: 6px;
}
/* Favourite star, opposite corner to the Recent badge so the two never collide.
   Over the preview rather than in a row of its own: it costs no height, and the
   tile is where the eye already is. Sized for a fingertip, not for the glyph. */
.source-list.grid .fav {
  grid-area: thumb;
  align-self: start; justify-self: start;
  margin: 2px;
  appearance: none; -webkit-appearance: none;
  background: rgba(0, 0, 0, .45);
  border: 0; border-radius: 8px;
  color: #fff; font-size: 15px; line-height: 1;
  min-width: 32px; min-height: 32px;
  cursor: pointer;
}
.source-list.grid .fav.on { color: #ffcc44; }
/* Two lines of title, so tiles in a row stay the same height instead of one long
   window name stretching the whole row. */
.source-list.grid .title {
  white-space: normal;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

/* A half-width phone tile has no room for full-size chrome: tighten the type and
   the button so the shape control still reads as three distinct choices. */
@media (max-width: 699px) {
  .source-list.grid .source-item { padding: 8px; gap: 6px; }
  .source-list.grid .app { font-size: 13px; }
  .source-list.grid .title { font-size: 11px; }
  .source-list.grid .shape .seg { font-size: 10px; padding: 5px 1px; }
  .source-list.grid .pick { padding: 5px 10px; font-size: 12px; }
}
.source-item .recent-badge {
  flex: 0 0 auto;
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 2px 6px;
}
.source-empty { color: var(--fg-dim); font-size: 13px; padding: 6px 2px; }

/* ------------------------------ PIN unlock ----------------------------- */
.pin-overlay {
  background: rgba(0, 0, 0, 0.8);
  display: flex; align-items: center; justify-content: center;
  z-index: 50;
}
.pin-panel {
  display: flex; flex-direction: column; gap: 12px;
  width: min(320px, 80vw);
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 14px; padding: 22px;
  text-align: center;
}
.pin-panel h2 { margin: 0; font-size: 18px; }
.pin-error { margin: 0; font-size: 13px; color: var(--danger); line-height: 1.4; }
/* The Not-paired screen reuses this element for a friendly hint (not an error),
   so soften it to the dim colour there. */
.pin-error.is-hint { color: var(--fg-dim); }
/* Informational fingerprint of the paired Mac's key (first chars of the
   public key) so the user knows which Mac they're unlocking. */
.pin-fingerprint {
  margin: 0; font-size: 12px; color: var(--fg-dim);
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
.pin-fingerprint code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--fg); background: var(--bg-elev2);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 1px 6px; letter-spacing: 0.08em;
}
/* Big, legible numeric code entry. */
.pin-input {
  appearance: none; -webkit-appearance: none;
  text-align: center; letter-spacing: 0.35em;
  font-size: 30px; font-weight: 600; line-height: 1.2;
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 14px 12px;
  color: var(--fg); background: var(--bg-elev2);
  border: 1px solid var(--border); border-radius: 12px; outline: none;
}
.pin-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(76, 141, 255, 0.25); }
/* ---- Pairing controls (Not-paired screen) --------------------------------
   Scanning the QR in-app is the short path back from a lost pairing; pasting the
   link is the one that works on devices without a barcode API (iOS Safari keeps
   it behind a feature flag). */
.pair-box { display: flex; flex-direction: column; gap: 10px; }
.pair-video {
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  background: #000; border-radius: 12px;
}
.pair-link {
  appearance: none; -webkit-appearance: none;
  font: inherit; font-size: 13px; text-align: center;
  padding: 10px; color: var(--fg); background: var(--bg-elev2);
  border: 1px solid var(--border); border-radius: 10px; outline: none;
}
.pair-link:focus { border-color: var(--accent); }

/* Small "How to pair" affordance that reopens the onboarding wizard. */
.pin-help {
  align-self: center; margin-top: 2px;
  font-size: 13px; padding: 6px 12px;
}

.pin-note { margin: 0; font-size: 13px; color: var(--fg-dim); line-height: 1.4; }
.paste-field {
  appearance: none; -webkit-appearance: none;
  width: 100%; box-sizing: border-box; resize: none;
  font-size: 16px; padding: 12px;
  color: var(--fg); background: var(--bg-elev2);
  border: 1px solid var(--border); border-radius: 10px; outline: none;
}
.paste-field:focus { border-color: var(--accent); }

/* ------------------------- First-run wizard ---------------------------- */
.wizard-overlay {
  background: rgba(0, 0, 0, 0.82);
  display: flex; align-items: center; justify-content: center;
  z-index: 60;   /* above the PIN overlay (50) so "?" works on that screen */
  padding: calc(var(--sat) + 16px) 16px calc(var(--sab) + 16px);
}
.wizard-panel {
  position: relative;
  display: flex; flex-direction: column; gap: 12px;
  width: min(360px, 88vw);
  max-height: 86dvh; overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 16px; padding: 26px 22px 20px;
  text-align: center;
}
.wizard-close {
  position: absolute; top: 8px; right: 10px;
  appearance: none; -webkit-appearance: none; border: 0; background: transparent;
  color: var(--fg-dim); font-size: 26px; line-height: 1;
  width: 36px; height: 36px; border-radius: 10px;
  cursor: pointer; touch-action: manipulation;
}
.wizard-close:active { background: var(--bg-elev2); }
.wizard-dots { display: flex; gap: 7px; align-self: center; margin-top: 4px; }
.wizard-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--border); transition: background 0.15s, transform 0.15s;
}
.wizard-dot.is-active { background: var(--accent); transform: scale(1.25); }
.wizard-icon { font-size: 40px; line-height: 1; margin-top: 6px; }
.wizard-title { margin: 0; font-size: 19px; }
.wizard-text { margin: 0; font-size: 14px; color: var(--fg-dim); line-height: 1.5; }
.wizard-text b { color: var(--fg); font-weight: 600; }
.wizard-action-slot:empty { display: none; }
.wizard-action { margin-top: 2px; }
.wizard-nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 8px;
}
.wizard-nav .btn { flex: 1 1 0; min-height: 44px; }

.hidden { display: none !important; }
