/* ── Live Pages – Shared Styles ─────────────────────────── */
:root {
  color-scheme: light;
  --live-red: #ef4444;
  --live-green: #22c55e;
  --radius: 12px;
  /* Light-theme tokens (also used as page chrome on live pages) */
  --bg: #ffffff;
  --bg-card: #ffffff;
  --bg-subtle: rgba(0, 0, 0, 0.05);
  --bg-hover: rgba(0, 0, 0, 0.03);
  --bg-hover-strong: rgba(0, 0, 0, 0.06);
  --border: rgba(0, 0, 0, 0.1);
  --text: #1a1a1a;
  --muted: #6b7280;
}
[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0b0e11;
  --bg-card: #12151a;
  --bg-subtle: rgba(255, 255, 255, 0.05);
  --bg-hover: rgba(255, 255, 255, 0.04);
  --bg-hover-strong: rgba(255, 255, 255, 0.07);
  --border: rgba(255, 255, 255, 0.06);
  --text: #e8eaed;
  --muted: #8b949e;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; min-height: 100dvh; -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; transition: background-color 0.2s ease, color 0.2s ease; }
a { color: inherit; text-decoration: none; }

/* ── Live header (logo + dark-mode toggle + user menu) ─── */
.live-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 14px 20px;
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
  box-sizing: border-box;
}
.live-header-left { display: flex; align-items: center; gap: 12px; }
.live-header-right { display: flex; align-items: center; gap: 10px; }
.live-header .brand-logo { height: 56px; width: auto; display: inline-block; }
.live-header .brand-logo--dark { display: none; }
[data-theme="dark"] .live-header .brand-logo--light { display: none; }
[data-theme="dark"] .live-header .brand-logo--dark { display: inline-block; }
.live-header .anoncoin-logo { height: 40px; width: auto; margin-left: -4px; }
.live-header .anoncoin-logo--dark { display: none; }
[data-theme="dark"] .live-header .anoncoin-logo--light { display: none; }
[data-theme="dark"] .live-header .anoncoin-logo--dark { display: inline-block; }
.live-header .powered-by { font-size: 13px; color: var(--muted); font-weight: 500; }
.live-header .powered-by-wrap { display: flex; align-items: center; gap: 10px; }

/* ── User dropdown menu (replaces powered-by on viewer/dashboard) ── */
.live-user-menu { position: relative; display: flex; align-items: center; }
.live-user-avatar-btn {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 50%;
  border: 2px solid var(--border-hi, rgba(255,255,255,0.2));
  background: var(--bg-subtle, #1a1a1a);
  cursor: pointer; padding: 0; overflow: hidden;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.live-user-avatar-btn:hover { border-color: var(--accent, #0070f3); box-shadow: 0 0 0 3px rgba(0,180,255,0.12); }
.live-user-avatar { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
.live-user-avatar-fallback {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700; color: var(--text); background: var(--bg-subtle);
}
.live-user-dropdown {
  position: absolute; top: calc(100% + 10px); right: 0;
  min-width: 220px; background: var(--bg-card, #fff);
  border: 1px solid var(--border, rgba(0,0,0,0.1)); border-radius: 14px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.18);
  padding: 6px 0; z-index: 200;
  animation: liveUserDropIn 0.15s ease;
}
@keyframes liveUserDropIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
[data-theme="dark"] .live-user-dropdown {
  background: #161b22;
  border-color: rgba(255,255,255,0.08);
  box-shadow: 0 12px 40px rgba(0,0,0,0.5);
}
.live-user-dropdown-header {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-light, rgba(0,0,0,0.06));
  text-decoration: none; color: inherit; transition: background 0.12s;
}
.live-user-dropdown-header:hover { background: var(--bg-hover, rgba(0,0,0,0.04)); }
.live-user-dropdown-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.live-user-dropdown-info { min-width: 0; }
.live-user-dropdown-name {
  font-size: 14px; font-weight: 700; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.3;
}
.live-user-dropdown-handle {
  font-size: 12px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.3;
}
.live-user-dropdown-items { padding: 4px 0; }
.live-user-menu-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 10px 16px;
  font-size: 14px; font-weight: 500; color: var(--text);
  background: none; border: none; cursor: pointer; text-align: left;
  transition: background 0.12s;
}
.live-user-menu-item:hover:not(:disabled) { background: var(--bg-hover, rgba(0,0,0,0.04)); }
.live-user-menu-icon { width: 18px; height: 18px; flex-shrink: 0; color: var(--muted); }
.live-user-menu-item--disabled { cursor: default; opacity: 0.6; }
.live-user-coming-soon {
  margin-left: auto; font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); background: var(--bg-subtle);
  border: 1px solid var(--border); padding: 2px 7px; border-radius: 6px; white-space: nowrap;
}
.live-user-menu-item--logout {
  color: var(--red, #dc2626);
  border-top: 1px solid var(--border-light, rgba(0,0,0,0.06));
  margin-top: 2px; padding-top: 10px;
}
.live-user-menu-item--logout .live-user-menu-icon { color: var(--red, #dc2626); }
.live-user-menu-item--logout:hover { background: rgba(220, 38, 38, 0.06); }
.live-user-signin {
  font-size: 13px; font-weight: 600; color: var(--text); text-decoration: none;
  white-space: nowrap; padding: 6px 14px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-subtle);
  transition: background 0.15s, border-color 0.15s;
}
.live-user-signin:hover { background: var(--bg-hover-strong, rgba(0,0,0,0.06)); border-color: var(--border-hi, rgba(255,255,255,0.2)); }

/* Chat-panel logout is replaced by the header user menu — keep the
   element in the DOM so existing wiring doesn't break, just hide it. */
.chat-logout-btn { display: none !important; }
.dark-mode-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-subtle); color: var(--text);
  cursor: pointer; transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
  flex-shrink: 0;
}
.dark-mode-toggle:hover { background: var(--bg-hover-strong); transform: scale(1.05); }
.dm-icon { display: block; }
.dm-icon-sun { display: none; }
[data-theme="dark"] .dm-icon-moon { display: none; }
[data-theme="dark"] .dm-icon-sun { display: block; }
@media (max-width: 600px) {
  .live-header { padding: 10px 14px; gap: 10px; }
  .live-header .brand-logo { height: 44px; }
  .live-header .anoncoin-logo { height: 32px; }
  .live-header .powered-by { display: none; }
}

/* ── Layout ──────────────────────────────────────────────── */
.live-page { max-width: 640px; margin: 0 auto; padding: 40px 20px; }
.live-page-wide { max-width: 1600px; margin: 0 auto; padding: 16px 20px; }
.live-center { text-align: center; padding: 64px 20px; }

/* ── Desktop viewport fit ─────────────────────────────────
   On desktops cap the entire page width (video + chat as one box) so
   the dashboard + viewer pages fit the viewport without a scrollbar
   AND the video frame's right edge stays flush with the chat aside.
   We size the wrapper so video_height × 16/9 + chat_width = max-width,
   keeping the grid a single continuous box that centers on the page.
   The chat aside is JS-synced to the video frame's height (see
   syncHeight() in dashboard.html / viewer.html). */
@media (min-width: 769px) {
  :root {
    /* Site header (logo bar above the page) + .live-page-wide top
       padding. Subtracted from 100dvh when sizing the video frame. */
    --live-header-h: 84px;
    /* Chrome below the video inside .live-page-wide — title line +
       summary row (avatar + name + followers) + paddings + bottom margin.
       Keeps the page fitting 100dvh at 100% browser zoom. */
    --live-fit-chrome: 140px;
    /* Width of the chat aside column in .live-grid (kept in sync with
       the grid-template-columns rule below). */
    --live-chat-w: 380px;
  }
  /* Only resize on the live dashboard / viewer pages so other pages
     that load live.css (go-live form etc.) are untouched. */
  #dashboard.live-page-wide,
  #viewer.live-page-wide {
    /* Cap the WHOLE page so video + chat scale together as one box.
       Falls back to the 1600px global cap on very tall viewports. */
    max-width: min(
      1600px,
      calc((100dvh - var(--live-header-h) - var(--live-fit-chrome)) * 16 / 9 + var(--live-chat-w))
    );
  }
  /* Match the header to the same max-width so the brand logo lines up
     with the left edge of the video frame and the user menu lines up
     with the right edge of the chat aside. */
  body:has(#dashboard.live-page-wide) .live-header,
  body:has(#viewer.live-page-wide) .live-header {
    max-width: min(
      1600px,
      calc((100dvh - var(--live-header-h) - var(--live-fit-chrome)) * 16 / 9 + var(--live-chat-w))
    );
  }
  #dashboard .live-video-wrap,
  #viewer .live-video-wrap {
    max-height: calc(100dvh - var(--live-header-h) - var(--live-fit-chrome));
  }
}

/* ── Form Elements ───────────────────────────────────────── */
.live-input {
  width: 100%; padding: 12px 14px; font-size: 15px; border-radius: 8px;
  border: 1px solid var(--border, #2a2a2a); background: var(--bg-card, #111);
  color: var(--text, #e5e5e5); outline: none; transition: border-color 0.15s;
}
.live-input:focus { border-color: var(--accent, #3b82f6); }
.live-input::placeholder { color: var(--muted, #666); }

.live-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 10px 18px; font-size: 14px; font-weight: 600; border-radius: 8px;
  border: 1px solid var(--border, #2a2a2a); background: var(--bg-card, #111);
  color: var(--text, #e5e5e5); cursor: pointer; transition: all 0.15s;
}
.live-btn:hover:not(:disabled):not(.live-btn-primary):not(.live-btn-danger) { background: var(--bg-hover, #1a1a1a); }
.live-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.live-btn-primary {
  background: var(--accent, #3b82f6); color: #fff; border-color: transparent;
}
.live-btn-primary:hover:not(:disabled) { filter: brightness(1.15); }
.live-btn-danger { background: var(--live-red); color: #fff; border-color: transparent; }
.live-btn-danger:hover { filter: brightness(1.1); }
.live-btn-sm { padding: 6px 12px; font-size: 12px; border-radius: 6px; }
.live-btn-icon {
  width: 40px; height: 40px; padding: 0; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}

/* ── Step Numbers ────────────────────────────────────────── */
.live-step-num {
  width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; font-size: 13px; font-weight: 700; flex-shrink: 0;
}
.live-step-num-active { background: var(--accent, #3b82f6); color: #fff; }
.live-step-num-inactive { background: var(--bg-card, #111); color: var(--text); border: 1px solid var(--border, #2a2a2a); }

/* ── Video Container ─────────────────────────────────────── */
/* ── Studio Frame ────────────────────────────────────────── */
/* Video and chat read as one continuous canvas: only the OUTER edges
   are rounded. Video keeps its left corners, chat keeps its right
   corners, and the meeting edge between them is square. */
.live-video-wrap {
  position: relative; aspect-ratio: 16/9;
  background: #000;
  border-radius: var(--radius) 0 0 var(--radius); overflow: hidden; border: none;
  padding: 0;
  box-sizing: border-box;
  isolation: isolate;
  /* Belt-and-suspenders clipping for the .bcast-stage transform on
     Safari iOS, where `overflow: hidden` alone occasionally lets a
     transformed absolutely-positioned child poke past the wrap edge. */
  contain: paint;
  max-width: 100%;
  min-width: 0;
}
.studio-inner {
  /* Inset the tile area so it sits BETWEEN the broadcast strips
     (top marquee + bottom small/big strips), not behind them. */
  position: absolute;
  top: var(--bcast-top-h, 0px);
  bottom: calc(var(--bcast-small-h, 0px) + var(--bcast-big-h, 0px));
  left: 0; right: 0;
  width: auto; height: auto;
  border-radius: 0; overflow: hidden;
  background: #000;
}
.live-video-wrap.live-video-fill { aspect-ratio: unset; height: 100%; }
/* Animated MP4 backdrop that sits behind the LiveKit tiles inside the
   studio-inner area (between the broadcast strips). When tiles or
   placeholders have transparent / partially transparent backgrounds this
   shows through; otherwise it acts as the empty-stage backdrop. */
video.live-video-bg,
.live-video-bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  background: transparent;
  z-index: 0;
  pointer-events: none;
  border: none;
  display: block;
}
.live-video-wrap video { display: block; }
.live-video-wrap:fullscreen { border: none; border-radius: 0; aspect-ratio: unset; width: 100vw; height: 100vh; height: 100dvh; }
.live-video-wrap:-webkit-full-screen { border: none; border-radius: 0; aspect-ratio: unset; width: 100vw; height: 100vh; }
/* iOS Safari: no native element fullscreen, so we fake it with a fixed
   overlay that covers the viewport. Pairs with `.is-fs` for chrome layout. */
.live-video-wrap.is-fs-ios {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;
  border: none !important;
  border-radius: 0 !important;
  aspect-ratio: unset !important;
  z-index: 9999 !important;
  margin: 0 !important;
  padding: 0 !important;
}
body.body-fs-lock { overflow: hidden !important; touch-action: none; }

/* Portrait fullscreen letterboxing is now handled by the .bcast-stage
   transform (see live-broadcast-chrome.js startStageScaler) which
   centers the scaled stage inside the viewport. No per-element max-
   height needed. */

/* ── Fullscreen overlays (controls + collapsible chat) ───── */
.live-video-wrap.is-fs {
  --fs-chat-w: min(380px, 38vw);
}
.live-video-wrap.is-fs .live-controls {
  bottom: 16px;
  z-index: 30;
  /* When the chat aside is open it overlays the right side of the wrap,
     so the visible video area is only [0 .. width - --fs-chat-w]. Center
     the controls within that visible area by shifting left by half the
     chat width. .is-fs-chat-collapsed override below resets to 50%. */
  left: calc(50% - (var(--fs-chat-w) / 2));
  transition: left 0.25s ease;
}
.live-video-wrap.is-fs .live-overlay-chat {
  bottom: 14px;
}
.live-video-wrap.is-fs .live-overlay-logo-right {
  right: calc(var(--fs-chat-w) + 14px);
  z-index: 30;
}
.live-video-wrap.is-fs #chatAside {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  height: 100%;
  width: min(380px, 38vw);
  background: rgba(20, 20, 20, 0.85);
  backdrop-filter: blur(16px);
  border: none;
  border-left: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 0;
  overflow: hidden;
  z-index: 25;
  display: flex;
  flex-direction: column;
  transition: transform 0.25s ease;
}
.live-video-wrap.is-fs #chatAside > #chatWrap {
  flex: 1 1 0%;
  min-height: 0;
  height: 100% !important;
  display: flex;
  flex-direction: column;
}
.live-video-wrap.is-fs #chatAside #chatBox,
.live-video-wrap.is-fs #chatAside .live-chat-x {
  flex: 1 1 0%;
  min-height: 0;
  height: 100%;
}
.live-video-wrap.is-fs.is-fs-chat-collapsed .live-controls { left: 50%; }
.live-video-wrap.is-fs.is-fs-chat-collapsed .live-overlay-logo-right { right: 14px; }
.live-video-wrap.is-fs.is-fs-chat-collapsed #chatAside {
  transform: translateX(100%);
  pointer-events: none;
}
/* In fullscreen with chat open, keep popups within the video area so they
   don't sit over the chat column. */
.live-video-wrap.is-fs:not(.is-fs-chat-collapsed) .live-join-modal {
  right: var(--fs-chat-w);
}
.fs-chat-toggle {
  position: absolute; z-index: 35;
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(20, 20, 20, 0.85); color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.12);
  display: none; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: 0 4px 16px rgba(0,0,0,0.45);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  bottom: 14px; right: calc(var(--fs-chat-w) + 12px);
  transition: right 0.25s ease, transform 0.15s ease;
}
.live-video-wrap.is-fs .fs-chat-toggle { display: flex; }
.live-video-wrap.is-fs.is-fs-chat-collapsed .fs-chat-toggle { right: 12px; }
.fs-chat-toggle:active { transform: scale(0.95); }

/* ── Unified Control Bar (sits BELOW the video frame) ── */
.live-controls {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 6px 8px;
  background: rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-radius: 9999px; border: 1px solid rgba(255,255,255,0.08);
  width: max-content; max-width: 90%; white-space: nowrap;
  position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%);
  z-index: 20;
  opacity: 1; transition: opacity 0.4s ease;
  pointer-events: auto;
}
.live-controls.live-controls-hidden {
  opacity: 0; pointer-events: none !important;
}
.live-controls.live-controls-hidden * { pointer-events: none !important; }
.live-video-wrap.controls-idle { cursor: none; }

/* ── LIVE badge (Twitch-style) ───────────────────────────── */
.live-badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
  font-size: 13px; font-weight: 800; letter-spacing: 0.04em;
  color: #fff; background: #eb0400; border-radius: 4px;
  flex-shrink: 0; text-transform: uppercase;
}
.live-badge-dot {
  width: 8px; height: 8px; border-radius: 50%; background: #fff;
  animation: pulse-dot 1.5s ease-in-out infinite;
}
@keyframes pulse-dot { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }

/* ── Timer ────────────────────────────────────────────────── */
.live-timer {
  font-size: 13px; font-weight: 600; color: rgba(255,255,255,0.85);
  font-variant-numeric: tabular-nums; flex-shrink: 0;
}

/* ── Volume Slider ───────────────────────────────────────── */
.live-vol-wrap {
  display: flex; align-items: center; gap: 6px; flex-shrink: 0;
}
.live-vol-icon { color: rgba(255,255,255,0.7); flex-shrink: 0; cursor: pointer; }
.live-vol-mute-btn {
  background: none; border: none; padding: 2px; margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  color: rgba(255,255,255,0.7); cursor: pointer; flex-shrink: 0;
  transition: color 0.15s;
}
.live-vol-mute-btn:hover { color: #fff; }
.live-vol-slider {
  -webkit-appearance: none; appearance: none; width: 80px; height: 36px;
  background: transparent; border-radius: 2px; outline: none;
  cursor: pointer; touch-action: none;
}
.live-vol-slider::-webkit-slider-runnable-track {
  background: linear-gradient(to right, #3b82f6 var(--vol-pct, 50%), rgba(255,255,255,0.2) var(--vol-pct, 50%));
  border-radius: 2px; height: 4px;
}
.live-vol-slider::-moz-range-track {
  background: linear-gradient(to right, #3b82f6 var(--vol-pct, 50%), rgba(255,255,255,0.2) var(--vol-pct, 50%));
  border-radius: 2px; height: 4px; border: none;
}
.live-vol-slider::-webkit-slider-thumb {
  -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%;
  background: #3b82f6; cursor: pointer; border: none;
  box-shadow: 0 0 6px rgba(59,130,246,0.4);
  margin-top: -6px;
}
.live-vol-slider::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%;
  background: #3b82f6; cursor: pointer; border: none;
}

/* ── Control Button ──────────────────────────────────────── */
.live-ctrl-btn {
  width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; border: none; background: transparent; color: rgba(255,255,255,0.75);
  cursor: pointer; transition: color 0.15s, background 0.15s; flex-shrink: 0;
}
.live-ctrl-btn:hover { color: #fff; background: rgba(255,255,255,0.1); }
.live-ctrl-btn-active { color: #fff; }
.live-ctrl-btn-off { color: rgba(255,255,255,0.35); }
/* Mobile two-tap arm state for #btnMic: glow ring + amber tint to make
   the user pause and confirm before toggling mute. */
.live-ctrl-btn.is-armed {
  color: #fff;
  background: rgba(245, 158, 11, 0.28);
  box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.85), 0 0 12px rgba(245, 158, 11, 0.6);
  animation: liveCtrlArmPulse 1.1s ease-in-out infinite;
}
@keyframes liveCtrlArmPulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.85), 0 0 8px rgba(245, 158, 11, 0.45); }
  50%      { box-shadow: 0 0 0 3px rgba(245, 158, 11, 1),    0 0 16px rgba(245, 158, 11, 0.75); }
}

/* ── Device selector (cam / mic chevron + dropdown) ──── */
.live-ctrl-group {
  position: relative; display: inline-flex; align-items: center; gap: 0;
}
.live-ctrl-chevron {
  width: 32px; height: 36px; display: flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: rgba(255,255,255,0.55);
  cursor: pointer; transition: color 0.15s, background 0.15s; flex-shrink: 0;
  border-radius: 0 50% 50% 0; margin-left: -4px; padding: 0;
  touch-action: manipulation; /* remove iOS 300ms click delay */
  -webkit-tap-highlight-color: rgba(255,255,255,0.15);
}
.live-ctrl-chevron:hover { color: #fff; background: rgba(255,255,255,0.1); }
.live-ctrl-chevron svg { width: 14px; height: 14px; }
.live-device-menu {
  min-width: 200px; max-width: 300px;
  max-height: calc(100dvh - 120px); overflow-y: auto;
  background: rgba(20,20,24,0.95); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255,255,255,0.12); border-radius: 10px;
  padding: 6px 0; z-index: 9999; box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  animation: deviceMenuIn 0.15s ease;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
@keyframes deviceMenuIn { from { opacity: 0; transform: translateX(-50%) translateY(4px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
.live-device-menu-item {
  display: flex; align-items: center; gap: 8px;
  width: 100%; padding: 10px 14px; border: none; background: none;
  color: rgba(255,255,255,0.8); font-size: 13px; text-align: left; cursor: pointer;
  transition: background 0.12s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  touch-action: manipulation;
}
.live-device-menu-item:hover { background: rgba(255,255,255,0.08); }
.live-device-menu-item.active { color: #1d9bf0; }
.live-device-menu-item .dm-check {
  width: 16px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
}
.live-device-menu-label { overflow: hidden; text-overflow: ellipsis; }

/* Kick button (legacy — replaced by .live-host-actions pill, kept for safety) */
.live-kick-btn {
  position: absolute; top: 6px; left: 6px; z-index: 10;
  width: 24px; height: 24px; border-radius: 50%;
  background: rgba(239,68,68,0.85); color: #fff; border: none;
  font-size: 12px; font-weight: 700; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  opacity: 1; transition: opacity 0.4s ease, transform 0.15s;
}
.live-video-wrap.controls-idle .live-kick-btn { opacity: 0; pointer-events: none; }
.live-kick-btn:hover { background: #ef4444; transform: scale(1.1); }

/* ── Host hover-actions pill (Google-Meet style) ─────────── */
/* Sits centered on a guest tile. Hidden until the tile is hovered.
   Outer wrapper is invisible — only the action button(s) themselves are
   visible (glassmorphic circle on each button). */
.live-host-actions {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%) scale(0.92);
  display: flex; gap: 8px;
  background: transparent;
  border: none;
  box-shadow: none;
  opacity: 0; pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
  z-index: 12;
  /* Keep on a stable compositing layer so the fade-in doesn't promote/
     demote layers every frame — that promotion cycle was forcing the
     control bar, bottom broadcast strip, and BROADCAST-ON-X button to
     re-rasterize on every hover, producing the visible flicker. */
  will-change: opacity, transform;
}
.host-actionable:hover .live-host-actions,
.live-host-actions:focus-within {
  opacity: 1; pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}
.live-video-wrap.controls-idle .live-host-actions,
.live-video-wrap.controls-idle .live-host-actions * { opacity: 0; pointer-events: none !important; }
.live-host-action-btn {
  width: 64px; height: 64px; border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff; border: 1px solid rgba(255, 255, 255, 0.18);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s ease, transform 0.15s ease, color 0.15s ease;
  overflow: hidden; box-sizing: border-box; padding: 0;
  /* No backdrop-filter here: promoting/demoting a backdrop-blur layer on
     hover was triggering a repaint cascade that flickered neighbouring
     blurred surfaces (controls bar, broadcast strip). The opaque dark
     background already provides enough contrast. */
}
.live-host-action-btn svg {
  /* Keep the icon well inside the round bg — ≈44% of the button. */
  width: 44%; height: 44%; flex: 0 0 auto;
}
.live-host-action-btn:hover {
  background: rgba(0, 0, 0, 0.7); transform: scale(1.06);
}
.live-host-action-btn.live-host-action-remove:hover {
  background: rgba(239, 68, 68, 0.85); color: #fff;
  border-color: rgba(239, 68, 68, 0.85);
}
.live-host-action-btn.is-muted {
  background: rgba(239, 68, 68, 0.7);
  border-color: rgba(239, 68, 68, 0.7);
}
.live-host-action-btn.is-muted:hover {
  background: rgba(239, 68, 68, 0.9);
}

/* Participant name overlay shown at the bottom of each video tile */
.live-tile-label {
  position: absolute; left: 10px; bottom: 10px; z-index: 4;
  max-width: calc(100% - 20px);
  box-sizing: border-box;
  padding: 8px 17px;
  font-size: 20px; font-weight: 600;
  line-height: 1.15;
  color: #fff;
  background: rgba(0,0,0,0.6);
  border-radius: 9999px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  letter-spacing: 0;
  pointer-events: none;
}
.viewer-stage-tile > .live-tile-label,
.guest-wrap > .live-tile-label {
  max-width: calc(100% - 52px);
}
.screen-share-side-tile > .live-tile-label {
  max-width: calc(100% - 40px);
  padding: 3px 6px;
  font-size: 11px;
  line-height: 1.2;
}

/* Muted mic indicator on video tiles */
.live-mute-indicator {
  position: absolute; bottom: 8px; right: 8px; z-index: 4;
  width: 26px; height: 26px; min-width: 26px; min-height: 26px;
  max-width: 26px; max-height: 26px;
  border-radius: 50%;
  background: rgba(0,0,0,0.6);
  display: flex; align-items: center; justify-content: center;
  pointer-events: none; flex-shrink: 0;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.live-mute-indicator svg { display: block; width: 14px; height: 14px; }

/* ── End Button ──────────────────────────────────────────── */
.live-ctrl-btn-leave {
  color: #ef4444 !important;
  transition: color 0.15s, transform 0.15s;
}
.live-ctrl-btn-leave:hover { color: #ff6b6b !important; transform: scale(1.1); }

/* ── Hidden status bar (now merged into controls) ────────── */
.live-status { display: none; }

/* ── Audio-Only Tile ─────────────────────────────────────── */
.live-audio-tile,
.viewer-stage-tile,
.guest-wrap {
  /* Transparent so the .live-video-bg looped MP4 shows through behind
     placeholder tiles instead of a flat dark color. */
  background: transparent;
  container-type: size;
}
.host-wrap { container-type: size; }
.host-wrap.is-speaking::after,
.guest-wrap.is-speaking::after,
.viewer-stage-tile.is-speaking::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 3px solid #facc15;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.22), 0 0 18px rgba(250,204,21,0.58);
  pointer-events: none;
  z-index: 9;
}
.host-wrap video::-webkit-media-controls,
.guest-wrap video::-webkit-media-controls,
.viewer-stage-tile video::-webkit-media-controls,
.host-wrap video::-webkit-media-controls-start-playback-button,
.guest-wrap video::-webkit-media-controls-start-playback-button,
.viewer-stage-tile video::-webkit-media-controls-start-playback-button {
  display: none !important;
  -webkit-appearance: none;
}
.live-audio-tile {
  display: flex; align-items: center; justify-content: center;
}
.live-audio-tile-inner {
  width: 100%; box-sizing: border-box; padding: 0 12px;
  display: flex; flex-direction: column; align-items: center; gap: 8px; color: #fff;
  min-width: 0;
}
.live-avatar-circle {
  width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid rgba(255,255,255,0.25);
  background: rgba(255,255,255,0.12); font-size: 40px; font-weight: 700;
}
.live-avatar-circle-img {
  object-fit: cover;
  background: #000;
}
/* Avatar overlay shown on viewer/guest stage tiles when the publisher's
   camera is muted. Sits on top of the (black) video element. */
.viewer-tile-avatar-overlay {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 30% 20%, rgba(0,112,243,0.15), transparent 50%), #0a0a0a;
  z-index: 2;
  pointer-events: none;
}
/* Scale avatar to fit inside small tiles (mobile portrait) */
.viewer-tile-avatar-overlay .live-avatar-circle,
.viewer-tile-avatar-overlay .live-avatar-circle-img {
  width: min(200px, 72%); height: min(200px, 72%);
}
.viewer-tile-avatar-overlay .live-avatar-circle { font-size: min(56px, 10vmin); }
/* NOTE: Earlier versions clamped the camera-off avatar in fullscreen
   (`:fullscreen` / `.is-fs-ios`) to a small fixed-ish circle (≤145px)
   because the avatar was a centered circle that ballooned on huge
   tiles. The current design (see "Camera-off avatar fills the full
   tile frame" block near the bottom of this file) renders the avatar
   edge-to-edge with `object-fit: cover`, so any fullscreen size cap
   would shrink the photo back into a tiny corner. Those clamp rules
   were removed here intentionally — do NOT re-add them without also
   reworking the full-bleed treatment. Label/host-action sizing in
   fullscreen is still adjusted below. */
/* In fullscreen (native or iOS fake), force compact label/host-action
   sizing regardless of any mobile-portrait bumps further down the
   cascade. Tiles are tall+wide here and the cqw-based bumps overshoot.
   NOTE: labels intentionally avoid !important so the JS auto-fit shrink
   in live.js (`fitLiveTextElement`) can still reduce long usernames to
   fit a narrow tile. !important is only used on properties that the JS
   doesn't touch (max-width, padding, weight, button sizes). */
.live-video-wrap:fullscreen .live-avatar-label,
.live-video-wrap.is-fs-ios .live-avatar-label,
.live-video-wrap:fullscreen .live-tile-label,
.live-video-wrap.is-fs-ios .live-tile-label,
.live-video-wrap:fullscreen .live-nametag,
.live-video-wrap.is-fs-ios .live-nametag {
  font-size: clamp(10px, 2.2cqh, 15px);
  padding: 3px 10px !important;
  font-weight: 600 !important;
  max-width: calc(100% - 12px) !important;
  /* Always single-line in fullscreen — long handles must scale down via
     fitLiveTextElement (JS) instead of wrapping to a second row. */
  white-space: nowrap !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  word-break: normal !important;
  overflow-wrap: normal !important;
}
.live-video-wrap:fullscreen .live-host-action-btn,
.live-video-wrap.is-fs-ios .live-host-action-btn {
  width: clamp(28px, 14cqh, 48px) !important;
  height: clamp(28px, 14cqh, 48px) !important;
}
/* When a tile's camera is muted, hide the bottom name tag — the centered
   avatar overlay already shows the participant's @handle. Applies to both
   host dashboard (.guest-wrap) and viewer page (.viewer-stage-tile) tiles. */
.viewer-stage-tile.camera-off > .live-tile-label,
.guest-wrap.camera-off > .live-tile-label { display: none; }
.live-avatar-label {
  border-radius: 9999px; background: rgba(0,0,0,0.55); padding: 7px 16px;
  font-size: clamp(14px, 3.5vmin, 20px); font-weight: 600;
  line-height: 1.15;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  box-sizing: border-box;
  max-width: calc(100% - 24px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  letter-spacing: 0;
}
.screen-share-side-tile .live-audio-tile,
.screen-share-side-tile .viewer-tile-avatar-overlay {
  width: 100%; height: 100%;
}
.screen-share-side-tile .live-audio-tile-inner {
  width: 100%; height: 100%; box-sizing: border-box;
  justify-content: center; gap: 6px; padding: 8px 12px;
  overflow: hidden;
}
.screen-share-side-tile .live-avatar-circle {
  width: 62px; height: 62px;
  min-width: 62px; min-height: 62px;
  max-width: 62px; max-height: 62px;
  aspect-ratio: 1 / 1;
  font-size: 20px;
}
.screen-share-side-tile .live-avatar-label {
  max-width: calc(100% - 8px);
  padding: 3px 7px;
  font-size: 11px;
  line-height: 1.2;
}

/* ── Grid Layout (video + chat) ──────────────────────────── */
.live-grid { display: grid; gap: 0; grid-template-columns: minmax(0,1fr) 380px; align-items: start; }
.live-grid > #leftCol { min-width: 0; max-width: 100%; }
.live-grid #chatAside { align-self: start; max-height: none; min-height: 0; }
.live-grid #chatAside > #chatWrap { min-height: 0; }
.live-grid-fullscreen {
  position: fixed; inset: 0; z-index: 50; background: var(--bg, #0a0a0a); padding: 12px;
  display: grid; height: 100%; grid-template-columns: minmax(0,1fr) 380px; gap: 0;
}

/* ── Chat ────────────────────────────────────────────────── */
.live-chat {
  display: flex; flex-direction: column; height: 100%;
  background: var(--bg-card, #111); border: 1px solid var(--border, #2a2a2a);
  border-radius: var(--radius); overflow: hidden;
}
.live-chat-header {
  padding: 10px 14px; border-bottom: 1px solid var(--border, #2a2a2a);
  font-size: 11px; font-weight: 700; color: var(--muted, #666);
  text-transform: uppercase; letter-spacing: 0.1em;
  background: var(--bg-card, #111);
}
.live-chat-messages {
  flex: 1; overflow-y: auto; padding: 14px 12px 12px;
  display: flex; flex-direction: column; gap: 12px;
  background: color-mix(in srgb, var(--bg-card, #111) 96%, var(--bg, #0a0a0a));
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  touch-action: pan-y;
  will-change: scroll-position;
  transform: translateZ(0);
}
.live-chat-empty { color: var(--muted, #666); font-size: 13px; text-align: center; padding: 32px 0; }

/* Message row: avatar + shell */
.live-chat-msg-row {
  width: 100%; display: flex; align-items: flex-end; gap: 8px;
}
.live-chat-avatar {
  width: 30px; height: 30px; flex: 0 0 30px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.12); background: rgba(255,255,255,0.06);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--muted, #666); font-size: 12px; font-weight: 700;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}
.live-chat-msg-shell {
  flex: 1 1 auto; max-width: calc(100% - 46px); min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
}
.live-chat-sender {
  font-size: 12px; font-weight: 600; color: var(--muted, #666);
  margin-bottom: 2px; padding-left: 2px;
}
.live-chat-sender.is-host { color: var(--accent, #3b82f6); }
.live-chat-bubble {
  position: relative; display: inline-block; max-width: 100%;
  border-radius: 18px; background: #2f3336; color: #fff;
  padding: 8px 14px; box-shadow: none;
}
.live-chat-text {
  display: inline; font-size: 14px; line-height: 1.35;
  color: inherit; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;
}
.live-chat-time {
  display: inline-block; margin-left: 8px; font-size: 11px;
  color: rgba(255,255,255,0.5); white-space: nowrap; vertical-align: baseline;
}

/* Own messages — right-aligned, blue bubble (matches global chat) */
.live-chat-msg-row.is-me {
  flex-direction: row-reverse;
}
.live-chat-msg-row.is-me .live-chat-msg-shell {
  align-items: flex-end;
}
.live-chat-msg-row.is-me .live-chat-sender {
  display: none;
}
.live-chat-bubble.is-me {
  background: #1d9bf0;
  color: #fff;
}
.live-chat-bubble.is-me .live-chat-time {
  color: rgba(255,255,255,0.75);
}
.live-chat-avatar.is-me {
  border-color: #1d9bf0;
  background: rgba(29,155,240,0.15);
  color: #1d9bf0;
}

/* Composer */
.live-chat-form {
  border-top: 1px solid var(--border, #2a2a2a);
  display: flex; align-items: flex-end; gap: 8px;
  padding: 10px 12px; background: var(--bg-card, #111);
  min-height: 56px; position: relative;
}
.live-chat-form input {
  flex: 1 1 auto; width: 100%; min-width: 0; height: 42px;
  border-radius: 9999px; border: 1px solid var(--border, #2a2a2a);
  background: var(--bg, #0a0a0a); color: #fff;
  padding: 11px 16px; font-family: inherit; font-size: 14px; line-height: 1.35;
  outline: none; transition: border-color 0.18s ease;
}
.live-chat-form input::placeholder { color: var(--muted, #666); opacity: 0.75; }
.live-chat-form input:focus { border-color: var(--accent, #3b82f6); }
.live-chat-form button {
  border: 0; background: #1d9bf0; color: #fff; border-radius: 50%;
  padding: 0; width: 36px; height: 36px; min-height: 36px; flex: 0 0 36px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: 18px; font-weight: 700;
  transition: transform 0.15s ease, background 0.15s ease;
}
.live-chat-form button:hover { background: #1a8cd8; transform: translateY(-1px); }
.live-chat-form button:disabled { opacity: 0.5; cursor: default; transform: none; }

/* ── Chat Auth Gate ──────────────────────────────────────── */
.live-chat-auth-gate {
  border-top: 1px solid var(--border, #2a2a2a);
  padding: 14px 16px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-card, #111);
}
.live-chat-closed-note {
  width: 100%; max-width: 300px;
  border: 1px solid color-mix(in srgb, var(--border, #2a2a2a) 85%, transparent);
  background:
    radial-gradient(120% 120% at 0% 0%, rgba(239, 68, 68, 0.12) 0%, transparent 55%),
    radial-gradient(120% 120% at 100% 100%, rgba(29, 155, 240, 0.1) 0%, transparent 52%),
    color-mix(in srgb, var(--bg-card, #111) 92%, #000 8%);
  border-radius: 12px;
  padding: 12px 14px;
  text-align: left;
}
.live-chat-closed-note-title {
  font-size: 12px;
  line-height: 1.2;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #f87171;
  margin-bottom: 6px;
}
.live-chat-closed-note-body {
  font-size: 13px;
  line-height: 1.4;
  color: var(--muted, #71767b);
}
.live-auth-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 24px; border-radius: 9999px;
  background: #1d9bf0; color: #fff;
  font-size: 14px; font-weight: 600;
  text-decoration: none; border: none; cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}
.live-auth-btn:hover {
  background: #1a8cd8; transform: translateY(-1px);
}

/* ── Join Requests Panel ─────────────────────────────────── */
.live-join-panel {
  position: absolute; bottom: 56px; right: 12px; z-index: 25;
  background: rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius);
  padding: 12px; min-width: 220px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.4);
}
.live-join-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 0; font-size: 13px;
  color: #fff;
}
.live-join-panel > div:first-child {
  color: rgba(255,255,255,0.6) !important;
}

/* ── Name Tag (participant label on video) ────────────────── */
.live-nametag {
  position: absolute; bottom: 10px; left: 10px; z-index: 4;
  background: rgba(0, 0, 0, 0.78); color: #fff;
  font-size: 20px; font-weight: 600; padding: 8px 17px;
  border-radius: 6px; pointer-events: none;
  box-sizing: border-box;
  white-space: nowrap; max-width: 60%; overflow: hidden; text-overflow: ellipsis;
  line-height: 1.15;
  letter-spacing: 0;
  display: inline-flex; align-items: baseline; gap: 6px;
}
/* Inner spans for "<Name> <@handle dimmed>" rendering used by both
   .live-nametag and .live-tile-label. The @handle truncates first when
   the tile is narrow. */
.live-nametag .live-label-name,
.live-tile-label .live-label-name {
  min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis;
}
.live-nametag .live-label-handle,
.live-tile-label .live-label-handle {
  min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis;
  opacity: 0.6; font-weight: 500;
}
/* Use inline-flex when the label is showing — but only when not also
   hidden by a higher-priority rule like .camera-off > .live-tile-label. */
.live-tile-label:not([style*="display:none"]) { display: inline-flex; align-items: baseline; gap: 5px; }
.guest-wrap.camera-off > .live-tile-label { display: none; }
.host-wrap > .live-nametag {
  max-width: calc(100% - 52px);
}
.screen-share-side-tile > .live-nametag {
  max-width: calc(100% - 40px);
  padding: 3px 6px;
  font-size: 11px;
  line-height: 1.2;
  border-radius: 9999px;
}

.screen-share-side-tile .live-mute-indicator {
  top: auto;
  right: 6px;
  bottom: 6px;
}

@supports (width: 1cqw) {
  .live-avatar-circle,
  .live-avatar-circle-img {
    width: min(110px, 55cqw, 55cqh);
    height: min(110px, 55cqw, 55cqh);
  }
  .viewer-tile-avatar-overlay .live-avatar-circle,
  .viewer-tile-avatar-overlay .live-avatar-circle-img {
    width: min(110px, 52cqw, 52cqh);
    height: min(110px, 52cqw, 52cqh);
  }
  .screen-share-side-tile .live-avatar-circle,
  .screen-share-side-tile .live-avatar-circle-img {
    /* OVERRIDE the base 62px max/min cap — on mobile portrait the side
       tiles have enough room to fit a much larger avatar. Bias toward
       container-height since side tiles are narrow but tall. */
    width: min(96px, 60cqw, 36cqh) !important;
    height: min(96px, 60cqw, 36cqh) !important;
    min-width: 0 !important;
    min-height: 0 !important;
    max-width: none !important;
    max-height: none !important;
    font-size: clamp(16px, 5.5cqh, 28px) !important;
  }
}

/* ── Stream Summary (viewer) ─────────────────────────────── */
.live-summary {
  margin-bottom: 16px; display: flex; align-items: center; gap: 14px;
  padding: 14px 0;
}
.live-summary-avatar {
  width: 48px; height: 48px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
}
.live-summary-avatar-placeholder {
  width: 48px; height: 48px; border-radius: 50%; background: var(--border, #2a2a2a);
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 700; color: var(--muted, #666); flex-shrink: 0;
}

/* ── Error ────────────────────────────────────────────────── */
.live-error { color: var(--live-red); font-size: 14px; margin-bottom: 16px; }

/* ── Waiting Overlay ─────────────────────────────────────── */
.live-waiting {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px;
  color: rgba(255,255,255,0.6); z-index: 3;
}
.live-spinner {
  width: 32px; height: 32px; border: 3px solid rgba(255,255,255,0.15);
  border-top-color: var(--accent, #3b82f6); border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Video Overlays ──────────────────────────────────────── */
.live-video-wrap {
  --live-frame-logo-top: 14px;
  --live-frame-feed-logo-size: 96px;
  --live-frame-anon-logo-size: 84px;
}
.live-overlay-logo-left {
  position: absolute; top: var(--live-frame-logo-top); left: 14px; z-index: 5;
  height: var(--live-frame-feed-logo-size); width: auto; pointer-events: none;
}
.live-overlay-logo-right {
  position: absolute;
  top: calc(var(--live-frame-logo-top) + var(--live-frame-feed-logo-size) - var(--live-frame-anon-logo-size));
  right: 14px;
  z-index: 5;
  display: flex; align-items: center; gap: 10px;
  pointer-events: none;
  color: rgba(255,255,255,0.48);
  font-size: clamp(10px, 0.9vw, 13px);
  font-weight: 600;
  letter-spacing: 0;
  line-height: 1;
}
.live-overlay-logo-right img {
  width: var(--live-frame-anon-logo-size);
  height: var(--live-frame-anon-logo-size);
  object-fit: contain;
  display: block;
}
/* Broadcast-style floating chat overlay — sits on the right half of
   the video frame, anchored to the very bottom (overlays the bottom
   strips). Text is drawn with a strong shadow instead of a background
   so it reads on top of any video content. */
.live-overlay-chat {
  position: absolute;
  right: 14px;
  /* Anchor to the very bottom of the video wrap — chat visually
     overlays the bottom broadcast strips so the messages always start
     at the bottom of the frame, regardless of viewport / fullscreen. */
  bottom: 14px;
  left: 40%;                       /* allow full right-half width */
  z-index: 12;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;       /* stack from bottom up */
  gap: 4px;
  pointer-events: none;
  max-height: 60%;
  overflow: hidden;
  align-items: flex-end;
}
.live-overlay-chat-msg {
  display: inline-flex; align-items: baseline; gap: 8px;
  padding: 0;
  background: transparent !important;
  border: none;
  border-radius: 0;
  font-size: 15px; line-height: 1.28; color: #fff;
  text-shadow:
    0 1px 2px rgba(0,0,0,0.95),
    0 2px 6px rgba(0,0,0,0.85),
    0 0 1px rgba(0,0,0,1);
  animation: chatSlideIn 0.28s ease-out;
  align-self: flex-end;
  max-width: 100%;
  word-break: break-word; white-space: normal;
  font-weight: 600;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  text-align: right;
  transition: opacity 320ms ease, transform 320ms ease;
}
.live-overlay-chat-msg.is-leaving {
  opacity: 0;
  transform: translateY(-6px);
}
.live-overlay-chat-name {
  font-weight: 900; color: #38bdf8; white-space: nowrap; font-size: 14px;
  text-shadow:
    0 1px 2px rgba(0,0,0,0.95),
    0 2px 6px rgba(0,0,0,0.85);
}
.live-overlay-chat-text {
  word-break: break-word;
}
@keyframes chatSlideIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Join Modal ──────────────────────────────────────────── */
.live-join-modal {
  position: absolute; inset: 0; z-index: 40;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.65);
  /* No backdrop-filter: it would force layer promotion that cascades
     into a repaint of every blurred sibling (control bar, broadcast
     strips, hover action buttons) on every button hover transition,
     producing visible flicker. The opaque dim is enough. */
  will-change: opacity;
}
/* Confirm dialogs (End stream, Leave stage, Remove participant, etc.)
   must sit above the control bar (z-index: 20) and the fullscreen chat
   aside (z-index: 25) or the popup hides behind them. */
.live-confirm-modal { z-index: 45; }
.live-join-modal-card {
  background: rgba(30, 30, 30, 0.9); border: 1px solid rgba(255,255,255,0.12);
  border-radius: 16px; padding: 28px 32px; max-width: 360px; width: 90%;
  text-align: center; box-shadow: 0 24px 60px rgba(0,0,0,0.5);
}
.live-join-modal-btn {
  display: block; width: 100%; padding: 14px 24px;
  border: none; border-radius: 40px; cursor: pointer;
  font-size: 16px; font-weight: 700; letter-spacing: 0.01em;
  background: #facc15; color: #000;
  transition: transform 0.15s, box-shadow 0.15s;
  will-change: transform;
}
.live-join-modal-btn:hover {
  transform: scale(1.03); box-shadow: 0 4px 20px rgba(250, 204, 21, 0.4);
}

/* ── Kick Button ─────────────────────────────────────────── */
.live-kick-btn {
  position: absolute; top: 8px; left: 8px; z-index: 11;
  width: 28px; height: 28px; border-radius: 50%;
  background: rgba(239, 68, 68, 0.85); color: #fff;
  border: none; cursor: pointer; font-size: 14px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  opacity: 1; transition: transform 0.2s, background 0.2s;
  backdrop-filter: blur(4px);
}
.live-kick-btn:hover { background: #ef4444; transform: scale(1.1); }

/* ── Toast Notification ──────────────────────────────────── */
.live-toast {
  position: fixed; bottom: 32px; left: 50%; transform: translateX(-50%);
  background: rgba(30, 30, 30, 0.95); color: #fff;
  padding: 12px 24px; border-radius: 12px; font-size: 14px; font-weight: 500;
  border: 1px solid rgba(255,255,255,0.1);
  box-shadow: 0 8px 32px rgba(0,0,0,0.4);
  backdrop-filter: blur(8px); z-index: 100;
  animation: toastIn 0.3s ease-out;
}
.live-toast-out { animation: toastOut 0.5s ease-in forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateX(-50%) translateY(20px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
@keyframes toastOut { from { opacity: 1; } to { opacity: 0; transform: translateX(-50%) translateY(20px); } }

/* ── Responsive ──────────────────────────────────────────── */
@media (max-width: 768px) {
  /* Phone, non-fullscreen: page should fit in one viewport. We size the
     body to the viewport but keep vertical overflow auto so the browser's
     native pull-to-refresh gesture still works at the top of the page.
     Inner content is sized to fit exactly, so no visible scroll appears,
     but the root is still a scrollable element from the browser's POV.
     Horizontal overflow is hidden to avoid accidental side-scroll. */
  html, body { height: 100dvh; overflow-x: hidden; overflow-y: auto; }

  /* Stack video + chat vertically. */
  .live-grid { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .live-grid > #leftCol { min-width: 0; max-width: 100%; }
  .live-grid #chatAside {
    /* Fixed height equal to remaining viewport (below the video + chrome)
       so .chat-messages inside scrolls independently and the page itself
       stays still — only the chat list scrolls when chat is long. */
    height: calc(100svh - 56vw - 156px) !important;
    min-height: calc(100svh - 56vw - 156px);
    max-height: calc(100svh - 56vw - 156px);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    /* Full-bleed: escape .live-page-wide horizontal padding so the chat
       panel spans the entire viewport width. */
    margin-left: calc(-1 * max(10px, env(safe-area-inset-left)));
    margin-right: calc(-1 * max(10px, env(safe-area-inset-right)));
    /* Cancel the page's bottom padding so the chat surface reaches the
       very bottom of the viewport (behind the fixed composer). */
    margin-bottom: calc(-1 * max(10px, env(safe-area-inset-bottom)));
    padding: 0;
    box-sizing: border-box;
    background: transparent;
    border-top-left-radius: var(--radius, 16px);
    border-top-right-radius: var(--radius, 16px);
  }
  .live-grid #chatWrap {
    /* Fill chatAside completely; chat-messages handles scrolling. */
    flex: 1 1 auto;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: transparent;
    border-top-left-radius: var(--radius, 16px);
    border-top-right-radius: var(--radius, 16px);
  }
  .live-grid #chatBox,
  .live-grid .live-chat-x {
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
    border-top-left-radius: var(--radius, 16px);
    border-top-right-radius: var(--radius, 16px);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  /* When the chat is gated behind X auth (or closed), the auth CTA is
     pinned to the bottom of the viewport (see live-chat-x.css). The chat
     panel itself keeps its fixed mobile height so the message list above
     the CTA scrolls independently instead of pushing the page. */
  /* Reserve room at the bottom of the message list for the fixed auth gate
     (auth) or "Chat closed" note (closed) so the last messages aren't
     hidden underneath. Auth gate is taller than 64px once safe-area is
     added on iOS, and the "Chat closed" note adds two more lines. */
  .live-chat-x[data-chat-state="auth"] .chat-messages {
    padding-bottom: calc(96px + env(safe-area-inset-bottom));
  }
  .live-chat-x[data-chat-state="closed"] .chat-messages {
    padding-bottom: calc(140px + env(safe-area-inset-bottom));
  }
  .live-grid-fullscreen { grid-template-columns: 1fr; grid-template-rows: minmax(0,1fr) 220px; padding: 8px; gap: 0; }
  .live-grid-fullscreen #chatWrap { height: 100% !important; }

  .live-page-wide {
    padding: 10px 12px;
    padding-left: max(10px, env(safe-area-inset-left));
    padding-right: max(10px, env(safe-area-inset-right));
    padding-bottom: max(10px, env(safe-area-inset-bottom));
  }
  .live-page { padding: 24px 16px; }

  /* Compact control bar so all buttons fit on small phones. Allow it to
     scroll horizontally as a last resort instead of overflowing the
     studio frame. */
  .live-controls {
    gap: 6px; padding: 4px 6px;
    max-width: calc(100% - 16px);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .live-controls::-webkit-scrollbar { display: none; }
  .live-ctrl-btn { width: 38px; height: 38px; min-width: 38px; }
  .live-ctrl-btn svg { width: 18px; height: 18px; }
  .live-badge { padding: 4px 8px; font-size: 10px; }
  /* Free up horizontal room on phones */
  .live-timer { display: none; }
  .live-vol-wrap { display: none; }
  /* The viewer/guest control bar renders a bare `.live-vol-slider` (no
     `.live-vol-wrap` parent). Hide it too — on phones the system volume
     keys already cover this, and the slider eats ~90px of bar width which
     pushes the leave/fullscreen buttons off-screen. */
  .live-controls .live-vol-slider { display: none; }
  /* Popout (Document PiP) isn't supported in mobile browsers — hide the
     button so it doesn't waste horizontal space. */
  .live-controls #btnPopout { display: none; }
  /* "Connect X to join" button has inline padding/font-size — clamp it so
     it doesn't blow out the control bar width on phones. */
  .live-controls #btnJoin {
    padding: 6px 12px !important;
    font-size: 12px !important;
    flex-shrink: 1 !important;
    min-width: 0;
    max-width: 50vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Logos shouldn't dominate the small viewport */
  .live-video-wrap {
    --live-frame-logo-top: 10px;
    --live-frame-feed-logo-size: clamp(32px, 9vmin, 59px);
    --live-frame-anon-logo-size: clamp(29px, 8.25vmin, 51px);
  }
  .live-overlay-logo-left { left: 10px; }
  .live-overlay-logo-right { right: 10px; gap: 6px; max-width: 48vw; }
  .live-overlay-logo-right span { font-size: clamp(8px, 2.2vmin, 10px); }

  /* Floating chat overlay sized to viewport */
  .live-overlay-chat {
    left: 30%;
    right: 12px;
    bottom: 12px;
    max-height: 140px;
  }
  .live-overlay-chat-msg { font-size: 12px; padding: 0; }
  .live-overlay-chat-name { font-size: 11px; }

  /* Tile / summary trims */
  .live-summary { padding: 10px 12px; gap: 10px; margin-bottom: 12px; }
  .live-summary-avatar,
  .live-summary-avatar-placeholder { width: 40px; height: 40px; font-size: 16px; }

  /* Touch devices have no hover state. To prevent accidental mutes/removes,
     host-action buttons stay hidden until the user TAPS the guest tile.
     First tap reveals the pill (.is-actions-open is added by live.js);
     second tap actually fires the button. Tapping elsewhere closes it. */
  .live-host-actions {
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, -50%) scale(0.92);
  }
  .host-actionable.is-actions-open > .live-host-actions {
    opacity: 1;
    pointer-events: auto;
    transform: translate(-50%, -50%) scale(1);
  }
  .live-video-wrap.controls-idle .live-host-actions,
  .live-video-wrap.controls-idle .host-actionable.is-actions-open > .live-host-actions {
    opacity: 0;
    pointer-events: none;
  }

  /* Mobile host-actions pill — circular buttons that scale with the
     containing tile so they're tappable in tiny non-fullscreen tiles and
     don't blow out the frame in fullscreen / screen-share layouts.
     Uses container-query units against `.host-wrap` / `.guest-wrap`
     (both declare `container-type: size`). */
  .live-host-actions {
    gap: min(8px, 2.5cqw);
    max-width: calc(100% - 8px);
    box-sizing: border-box;
    padding: 0;
    justify-content: center;
    flex-wrap: nowrap;
  }
  .live-host-action-btn {
    /* Three buttons must fit horizontally inside the tile (3 × width + 2 × gap ≤ 100cqw).
       22cqw × 3 = 66cqw, leaving room for gap + tiny safety margin. */
    width: clamp(32px, 22cqw, 64px);
    height: clamp(32px, 22cqw, 64px);
    min-width: 0;
    min-height: 0;
    flex: 0 1 auto;
    border-radius: 50%;
    padding: 0;
    overflow: hidden;
  }
  .live-host-action-btn svg {
    /* Tighter on mobile so all three icons stay inside their circle bg. */
    width: 42%;
    height: 42%;
    flex: 0 0 auto;
  }

  /* Screen-share side tiles are very narrow — bias the clamp toward the
     tile height instead so the circles stay readable. */
  .screen-share-side-tile .live-host-action-btn {
    width: clamp(26px, 24cqh, 48px);
    height: clamp(26px, 24cqh, 48px);
  }

  /* On mobile, keep camera tiles cropped like desktop; screen-share videos keep contain via inline styles. */
  .viewer-stage-tile video,
  .guest-wrap video {
    object-fit: cover !important;
  }

  /* When the participant's camera is OFF, the avatar-overlay username is
     the only identity signal in the tile. Bump it up so it's readable on
     phones in portrait. (When the camera is ON, `.live-tile-label` /
     `.live-nametag` keep their original sizing — they overlay the video
     and should stay subtle.) */
  .viewer-tile-avatar-overlay .live-avatar-label,
  .camera-off .live-avatar-label,
  .live-audio-tile .live-avatar-label {
    font-size: clamp(16px, 6cqw, 28px) !important;
    padding: 8px 18px;
    line-height: 1.15;
    font-weight: 700;
    max-width: calc(100% - 16px);
  }
  .screen-share-side-tile .live-avatar-label {
    /* iOS Safari fallback first — cqw on Safari 16.x can resolve to 0
       inside transformed/scaled ancestors. vw is always reliable. */
    font-size: clamp(14px, 4.4vw, 22px) !important;
    /* Preferred: scale against the tile width when container queries
       behave (Chromium / Safari 17+). */
    font-size: clamp(14px, max(8cqw, 4.4vw), 22px) !important;
    padding: 5px 8px !important;
    line-height: 1.15 !important;
    font-weight: 700 !important;
    max-width: calc(100% - 4px) !important;
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: clip !important;
    word-break: break-word !important;
    overflow-wrap: anywhere !important;
  }
  /* Camera-ON label (overlays the live video). Match the readable size
     used for camera-off so usernames stay legible on phone-sized tiles. */
  .host-wrap:not(.screen-share-side-tile) > .live-nametag,
  .host-wrap:not(.screen-share-side-tile) > .live-tile-label,
  .guest-wrap:not(.screen-share-side-tile) > .live-nametag,
  .guest-wrap:not(.screen-share-side-tile) > .live-tile-label,
  .viewer-stage-tile:not(.screen-share-side-tile) > .live-tile-label {
    font-size: clamp(16px, 6cqw, 28px);
    padding: 8px 16px;
    line-height: 1.15;
    font-weight: 700;
    max-width: calc(100% - 16px);
  }

  .screen-share-side-tile > .live-tile-label,
  .screen-share-side-tile > .live-nametag {
    left: 4px;
    bottom: 4px;
    /* Don't stretch full width — pill should hug its text like before.
       Cap at tile width minus padding so long handles still wrap inside
       the frame. */
    right: auto;
    max-width: calc(100% - 8px);
    width: auto;
    padding: 5px 10px;
    /* iOS Safari fallback first (vw always works), then cqw-preferred. */
    font-size: clamp(14px, 4.4vw, 22px);
    font-size: clamp(14px, max(8cqw, 4.4vw), 22px);
    line-height: 1.15;
    font-weight: 700;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    word-break: break-word;
    overflow-wrap: anywhere;
  }

  .screen-share-side-tile .live-audio-tile-inner {
    gap: 3px;
    padding: 4px;
  }

  .screen-share-side-tile .live-avatar-circle {
    width: min(38px, 44cqw, 32cqh); height: min(38px, 44cqw, 32cqh);
    min-width: min(38px, 44cqw, 32cqh); min-height: min(38px, 44cqw, 32cqh);
    max-width: min(38px, 44cqw, 32cqh); max-height: min(38px, 44cqw, 32cqh);
    font-size: 14px;
  }

  .screen-share-side-tile .live-avatar-label {
    max-width: calc(100% - 6px);
    padding: 2px 4px;
    font-size: 9px;
    line-height: 1.1;
  }

  .screen-share-side-tile .live-mute-indicator {
    top: 4px;
    right: 4px;
    bottom: auto;
    width: 18px; height: 18px;
    min-width: 18px; min-height: 18px;
    max-width: 18px; max-height: 18px;
  }
  .screen-share-side-tile .live-mute-indicator svg { width: 10px; height: 10px; }

  /* Always show kick button on touch (no hover) */
  .guest-wrap .live-kick-btn { opacity: 1; }

  /* Join request panel + join modal. Float above the control bar on
     phones so the Accept/Decline buttons aren't covered by it. */
  .live-join-panel {
    left: 12px; right: 12px; bottom: 60px; min-width: 0;
    z-index: 25;
    border-radius: var(--radius);
    border-bottom: 1px solid rgba(255,255,255,0.08);
  }
}

/* Mobile fullscreen: no chat panel or chat toggle. */
.live-video-wrap.is-fs.is-fs-mobile .live-controls { left: 50%; }
.live-video-wrap.is-fs.is-fs-mobile .live-overlay-logo-right { right: 14px; }
.live-video-wrap.is-fs.is-fs-mobile #chatAside { display: none !important; }
.live-video-wrap.is-fs.is-fs-mobile #chatEmojiPickerWrap,
.live-video-wrap.is-fs.is-fs-mobile #chatActionMenu { display: none !important; }
.live-video-wrap.is-fs.is-fs-mobile .fs-chat-toggle { display: none !important; }

/* Compact the control bar in mobile fullscreen — phones in landscape are
   often wider than 768px so the @media (max-width: 768px) compact rules
   don't fire. Screen-share layouts especially make a normal-sized control
   bar feel oversized against the shrunken video. */
.live-video-wrap.is-fs.is-fs-mobile .live-controls {
  gap: 6px;
  padding: 3px 6px;
  max-width: calc(100% - 16px);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.live-video-wrap.is-fs.is-fs-mobile .live-controls::-webkit-scrollbar { display: none; }
.live-video-wrap.is-fs.is-fs-mobile .live-ctrl-btn {
  width: 32px; height: 32px; min-width: 32px;
}
.live-video-wrap.is-fs.is-fs-mobile .live-ctrl-btn svg {
  width: 16px; height: 16px;
}
.live-video-wrap.is-fs.is-fs-mobile .live-ctrl-chevron {
  width: 24px; height: 32px;
}
.live-video-wrap.is-fs.is-fs-mobile .live-ctrl-chevron svg {
  width: 12px; height: 12px;
}
.live-video-wrap.is-fs.is-fs-mobile .live-badge {
  padding: 3px 6px; font-size: 9px;
}
.live-video-wrap.is-fs.is-fs-mobile .live-timer,
.live-video-wrap.is-fs.is-fs-mobile .live-vol-wrap,
.live-video-wrap.is-fs.is-fs-mobile .live-controls .live-vol-slider,
.live-video-wrap.is-fs.is-fs-mobile .live-controls #btnPopout {
  display: none;
}

/* ── Tablet (portrait/landscape) ─────────────────────────────────────
   iPad-class viewports (~769px–1024px) are too wide for the phone
   stacked layout but the desktop control bar still overflows because
   the video column is only ~388–644px wide. Shrink the chat column,
   compact the controls, and hide the bits that don't fit. */
@media (min-width: 769px) and (max-width: 1024px) {
  .live-grid {
    grid-template-columns: minmax(0, 1fr) 300px;
  }
  .live-grid #chatAside { width: 300px !important; }

  .live-controls {
    gap: 6px;
    padding: 4px 6px;
    max-width: calc(100% - 16px);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .live-controls::-webkit-scrollbar { display: none; }
  .live-ctrl-btn { width: 36px; height: 36px; min-width: 36px; }
  .live-ctrl-btn svg { width: 18px; height: 18px; }
  /* Hide non-essential controls so the core mic/cam/screen/fullscreen
     /leave buttons always fit. */
  .live-controls .live-vol-wrap,
  .live-controls .live-vol-slider,
  .live-controls #btnPopout,
  .live-controls .live-timer { display: none; }
  .live-controls .live-badge { padding: 3px 7px; font-size: 10px; }
  .live-controls #btnJoin {
    padding: 6px 12px !important;
    font-size: 12px !important;
    max-width: 38vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* Landscape phones — give more room to the video, less to the chat. */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
  .live-grid { grid-template-columns: minmax(0, 1fr) 280px; gap: 8px; }
  .live-grid #chatAside { height: auto !important; }
  .live-grid #chatWrap {
    height: calc(100dvh - 16px) !important;
    min-height: 200px !important;
  }
  .live-summary { display: none; }
  .live-page-wide { padding: 8px; }

  /* The 768px-wide mobile portrait block bumps avatar/label/host-action
     sizes for readability in portrait. In landscape (especially iOS fake
     fullscreen) tiles are much shorter, so those bumps feel oversized.
     Reset them back to compact values here. */
  .viewer-tile-avatar-overlay .live-avatar-label,
  .camera-off .live-avatar-label,
  .live-audio-tile .live-avatar-label {
    font-size: clamp(11px, 3.2cqh, 16px) !important;
    padding: 4px 10px;
    font-weight: 600;
  }
  .host-wrap:not(.screen-share-side-tile) > .live-nametag,
  .host-wrap:not(.screen-share-side-tile) > .live-tile-label,
  .guest-wrap:not(.screen-share-side-tile) > .live-nametag,
  .guest-wrap:not(.screen-share-side-tile) > .live-tile-label,
  .viewer-stage-tile:not(.screen-share-side-tile) > .live-tile-label {
    font-size: clamp(11px, 3.2cqh, 16px);
    padding: 4px 10px;
    font-weight: 600;
  }
  .host-wrap:not(.screen-share-side-tile) .live-avatar-circle,
  .host-wrap:not(.screen-share-side-tile) .live-avatar-circle-img,
  .guest-wrap:not(.screen-share-side-tile) .live-avatar-circle,
  .guest-wrap:not(.screen-share-side-tile) .live-avatar-circle-img,
  .viewer-stage-tile:not(.screen-share-side-tile) .live-avatar-circle,
  .viewer-stage-tile:not(.screen-share-side-tile) .live-avatar-circle-img {
    width: min(96px, 38cqh, 38cqw);
    height: min(96px, 38cqh, 38cqw);
  }
  .live-host-action-btn {
    width: clamp(28px, 16cqh, 44px);
    height: clamp(28px, 16cqh, 44px);
  }
  .live-host-actions { gap: 6px; }
}

/* ── Stream Ended Overlay ────────────────────────────────── */
.live-ended-overlay {
  position: absolute; inset: 0; z-index: 50;
  background: rgba(0,0,0,0.85);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  display: flex; align-items: center; justify-content: center;
}
.live-ended-content {
  text-align: center;
  padding: 40px;
}
.live-ended-icon {
  width: 72px; height: 72px; border-radius: 50%;
  background: rgba(255,255,255,0.08);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 20px;
  color: rgba(255,255,255,0.5);
}

@media (max-width: 420px) {
  .live-ctrl-btn { width: 36px; height: 36px; min-width: 36px; }
  .live-controls { gap: 4px; }
  .live-video-wrap {
    --live-frame-feed-logo-size: clamp(27px, 8.25vmin, 47px);
    --live-frame-anon-logo-size: clamp(26px, 7.5vmin, 42px);
  }
  .live-tile-label { font-size: clamp(16px, 6cqw, 28px); padding: 8px 16px; line-height: 1.15; font-weight: 700; }
  .host-wrap:not(.screen-share-side-tile) > .live-nametag,
  .guest-wrap:not(.screen-share-side-tile) > .live-nametag {
    /* keep desktop sizing; do not enlarge */
  }
  /* Screen-share side tiles: do NOT shrink here. The 768px-wide mobile
     portrait block above (which also applies on phones ≤420px) already
     sets readable sizes using container queries + vw fallback. Restating
     the same values here as !important so any future ancestor specificity
     accidents don't drop us back to 8px. */
  .screen-share-side-tile > .live-tile-label,
  .screen-share-side-tile > .live-nametag {
    max-width: calc(100% - 8px) !important;
    padding: 5px 10px !important;
    font-size: clamp(14px, 4.4vw, 22px) !important;
    font-size: clamp(14px, max(8cqw, 4.4vw), 22px) !important;
    line-height: 1.15 !important;
    font-weight: 700 !important;
  }
  .screen-share-side-tile .live-avatar-circle,
  .screen-share-side-tile .live-avatar-circle-img {
    width: min(96px, 60cqw, 36cqh) !important;
    height: min(96px, 60cqw, 36cqh) !important;
    min-width: 0 !important; min-height: 0 !important;
    max-width: none !important; max-height: none !important;
    font-size: clamp(16px, 5.5cqh, 28px) !important;
  }
  .screen-share-side-tile .live-avatar-label {
    font-size: clamp(14px, 4.4vw, 22px) !important;
    font-size: clamp(14px, max(8cqw, 4.4vw), 22px) !important;
    padding: 5px 8px !important;
    line-height: 1.15 !important;
    font-weight: 700 !important;
  }
  .screen-share-side-tile .live-mute-indicator {
    width: 16px; height: 16px;
    min-width: 16px; min-height: 16px;
    max-width: 16px; max-height: 16px;
  }
  .live-mute-indicator { width: 22px; height: 22px; min-width: 22px; min-height: 22px; }
  .live-mute-indicator svg { width: 12px; height: 12px; }
  /* Host-actions sizing is handled by the mobile (max-width:768px) rule
     above using container queries — do not shrink them again here. */
}

/* ── Document Picture-in-Picture Styles ── */
.pip-body {
  margin: 0 !important;
  padding: 0 !important;
  background: #000 !important;
  overflow: hidden !important;
  width: 100vw !important;
  height: 100vh !important;
  position: relative !important;
}

.pip-body .live-video-wrap {
  position: absolute !important;
  inset: 0 !important;
  width: 100% !important;
  height: 100% !important;
  aspect-ratio: unset !important;
  border-radius: 0 !important;
}

.pip-body #videoContainer {
  position: absolute !important;
  inset: 0 !important;
  width: 100% !important;
  height: 100% !important;
  z-index: 1 !important;
}

/* In PiP the controls live inside the same wrap, so keep them anchored at
   the bottom of the popped window. */
.pip-body .live-controls { bottom: 12px !important; z-index: 30; }


/* ── Go-Live 3-2-1 countdown overlay ──────────────────────────── */
.go-live-countdown {
  position: fixed; inset: 0; z-index: 9998;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at center, rgba(255, 255, 255, 1) 0%, rgba(239, 243, 244, 1) 70%);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  opacity: 1;
  padding: max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right)) max(24px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));
}
[data-theme="dark"] .go-live-countdown {
  background: radial-gradient(circle at center, rgba(20, 20, 28, 1) 0%, rgba(0, 0, 0, 1) 70%);
}
.go-live-countdown.go-live-countdown-out { animation: goLiveCountdownOut 320ms ease-in forwards; }
.go-live-countdown-inner { text-align: center; }
.go-live-countdown-label {
  font-size: 18px; font-weight: 600; letter-spacing: 0.02em;
  color: #536471; margin-bottom: 24px; text-transform: none;
}
[data-theme="dark"] .go-live-countdown-label { color: rgba(255,255,255,0.78); }
.go-live-countdown-number {
  font-size: clamp(120px, 28vw, 220px); font-weight: 800; line-height: 1;
  color: #1d9bf0;
  text-shadow: 0 0 48px rgba(29, 155, 240, 0.45);
  font-variant-numeric: tabular-nums;
  display: inline-block;
}
.go-live-countdown-number.go-live-countdown-pop { animation: goLivePop 900ms cubic-bezier(0.22, 1, 0.36, 1); }
.go-live-countdown-hint {
  margin-top: 18px; font-size: 13px; color: #536471;
  letter-spacing: 0.04em; text-transform: uppercase;
}
[data-theme="dark"] .go-live-countdown-hint { color: rgba(255,255,255,0.45); }
@keyframes goLiveCountdownIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes goLiveCountdownOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes goLivePop {
  0%   { transform: scale(0.6); opacity: 0; }
  35%  { transform: scale(1.15); opacity: 1; }
  60%  { transform: scale(1); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
@media (max-width: 480px) {
  .go-live-countdown-label { font-size: 15px; margin-bottom: 16px; }
  .go-live-countdown-hint { font-size: 11px; margin-top: 12px; }
}

/* ── Screen share popup (host) ───────────────────────────────
   Compact default for desktop AND any fullscreen mode (native +
   iOS fake fullscreen) — must not cover the video. Mobile portrait
   in non-fullscreen gets an enlarged card with bigger tap targets. */
.live-ss-popup-box {
  background: rgba(0,0,0,0.85);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 14px;
  padding: 10px 14px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.45);
  max-width: min(92vw, 380px);
  width: max-content;
  box-sizing: border-box;
}
.live-ss-popup-title {
  font-size: 13px;
  font-weight: 600;
  margin: 0 0 8px 0;
  color: #fff;
  text-align: center;
  line-height: 1.3;
}
.live-ss-popup-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
}
.live-ss-popup-btn {
  border: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  padding: 6px 14px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 9999px;
  min-height: 0;
  color: #fff;
}
.live-ss-popup-btn-dismiss { background: rgba(255,255,255,0.14); }
.live-ss-popup-btn-stop { background: #ef4444; }

/* Mobile portrait, NOT fullscreen → larger card, easier to tap. */
@media (max-width: 768px) and (orientation: portrait) {
  /* Apply enlargement only when popup is NOT inside a fullscreen wrap.
     :not(:fullscreen *) is unreliable across browsers, so we instead
     scope on .live-video-wrap descendants that are NOT .is-fs / .is-fs-ios.
     The popup is always inside videoContainer (.live-video-wrap), so we
     can target it directly and rely on a fullscreen-specific override
     below to shrink it back. */
  .live-ss-popup-box {
    border-radius: 20px;
    padding: clamp(18px, 5vw, 28px) clamp(20px, 5.5vw, 32px);
    max-width: min(96vw, 560px);
    width: min(96vw, 560px);
  }
  .live-ss-popup-title {
    font-size: clamp(17px, 4.8vw, 22px);
    font-weight: 700;
    margin: 0 0 18px 0;
  }
  .live-ss-popup-actions { gap: 12px; }
  .live-ss-popup-btn {
    padding: clamp(14px, 3.6vw, 18px) clamp(22px, 5vw, 30px);
    font-size: clamp(15px, 4.2vw, 18px);
    min-height: 52px;
    min-width: 120px;
    flex: 1 1 auto;
    max-width: 240px;
  }
}

/* Fullscreen (native or iOS fake) — always force compact, even on
   mobile portrait. Mirrors the desktop defaults so the popup stays
   a small top card and never covers the video. */
.live-video-wrap:fullscreen .live-ss-popup-box,
.live-video-wrap.is-fs-ios .live-ss-popup-box,
.live-video-wrap.is-fs .live-ss-popup-box {
  border-radius: 14px;
  padding: 10px 14px;
  max-width: min(92vw, 380px);
  width: max-content;
}
.live-video-wrap:fullscreen .live-ss-popup-title,
.live-video-wrap.is-fs-ios .live-ss-popup-title,
.live-video-wrap.is-fs .live-ss-popup-title {
  font-size: 13px;
  font-weight: 600;
  margin: 0 0 8px 0;
}
.live-video-wrap:fullscreen .live-ss-popup-actions,
.live-video-wrap.is-fs-ios .live-ss-popup-actions,
.live-video-wrap.is-fs .live-ss-popup-actions { gap: 8px; }
.live-video-wrap:fullscreen .live-ss-popup-btn,
.live-video-wrap.is-fs-ios .live-ss-popup-btn,
.live-video-wrap.is-fs .live-ss-popup-btn {
  padding: 6px 14px;
  font-size: 12px;
  min-height: 0;
  min-width: 0;
  flex: 0 0 auto;
  max-width: none;
}


/* ── Camera-off avatar fills the full tile frame ──────────────────────
   When a participant's camera is muted, render the avatar image (or the
   initial-letter fallback) edge-to-edge across the tile instead of the
   small centered circle. Username sits as a bottom-left pill so it
   matches the camera-on `.live-tile-label` placement. Excludes the
   screen-share side tile, which keeps its compact avatar treatment. */
.viewer-stage-tile .viewer-tile-avatar-overlay .live-audio-tile-inner,
.guest-wrap .viewer-tile-avatar-overlay .live-audio-tile-inner,
.host-wrap .viewer-tile-avatar-overlay .live-audio-tile-inner {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  gap: 0;
  display: block;
}
.viewer-stage-tile .viewer-tile-avatar-overlay .live-avatar-circle,
.viewer-stage-tile .viewer-tile-avatar-overlay .live-avatar-circle-img,
.guest-wrap .viewer-tile-avatar-overlay .live-avatar-circle,
.guest-wrap .viewer-tile-avatar-overlay .live-avatar-circle-img,
.host-wrap .viewer-tile-avatar-overlay .live-avatar-circle,
.host-wrap .viewer-tile-avatar-overlay .live-avatar-circle-img {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  min-width: 0 !important;
  min-height: 0 !important;
  max-width: none !important;
  max-height: none !important;
  border-radius: 0;
  border: none;
  background: #0a0a0a;
  object-fit: cover;
  object-position: center;
  font-size: clamp(60px, 18cqw, 180px) !important;
}
.viewer-stage-tile .viewer-tile-avatar-overlay .live-avatar-label,
.guest-wrap .viewer-tile-avatar-overlay .live-avatar-label,
.host-wrap .viewer-tile-avatar-overlay .live-avatar-label {
  position: absolute;
  left: 10px;
  bottom: 10px;
  right: auto;
  z-index: 4;
  max-width: calc(100% - 20px);
  background: rgba(0, 0, 0, 0.6);
  padding: 8px 17px;
  border-radius: 9999px;
  font-size: 20px;
  font-weight: 600;
  line-height: 1.15;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

/* ── Camera-off audio-tile fills the full frame (host go-live + audio-only guests) ──
   Same treatment as `.viewer-tile-avatar-overlay` above, but for participants
   rendered as a `.live-audio-tile` directly inside the wrap (no overlay
   wrapper). This covers:
   - Host's own tile on go-live.html when their camera is off (`.host-audio-tile`)
   - Guest tiles that never published a video track (`.guest-wrap` audio mode)
   - Audio-only viewer-stage tiles built by `ensureAudioOnlyTile()`
*/
.host-wrap > .live-audio-tile,
.guest-wrap > .live-audio-tile {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
}
.host-wrap .live-audio-tile-inner,
.guest-wrap .live-audio-tile-inner,
.viewer-stage-tile .live-audio-tile-inner {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  gap: 0;
  display: block;
}
.host-wrap > .live-audio-tile .live-avatar-circle,
.host-wrap > .live-audio-tile .live-avatar-circle-img,
.guest-wrap > .live-audio-tile .live-avatar-circle,
.guest-wrap > .live-audio-tile .live-avatar-circle-img,
.viewer-stage-tile.live-audio-tile .live-avatar-circle,
.viewer-stage-tile.live-audio-tile .live-avatar-circle-img {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  min-width: 0 !important;
  min-height: 0 !important;
  max-width: none !important;
  max-height: none !important;
  border-radius: 0;
  border: none;
  background: #0a0a0a;
  object-fit: cover;
  object-position: center;
  font-size: clamp(60px, 18cqw, 180px) !important;
}
.host-wrap > .live-audio-tile .live-avatar-label,
.guest-wrap > .live-audio-tile .live-avatar-label,
.viewer-stage-tile.live-audio-tile .live-avatar-label {
  position: absolute;
  left: 10px;
  bottom: 10px;
  right: auto;
  z-index: 4;
  max-width: calc(100% - 20px);
  background: rgba(0, 0, 0, 0.6);
  padding: 8px 17px;
  border-radius: 9999px;
  font-size: 20px;
  font-weight: 600;
  line-height: 1.15;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

/* Fullscreen — match the camera-on `.live-tile-label` shrink so the
   camera-off label is the same size as when the camera is on. The
   camera-off selectors above have higher specificity than the generic
   `.live-video-wrap:fullscreen .live-avatar-label` rule, so we re-state
   the fullscreen override here at matching specificity. */
.live-video-wrap:fullscreen .viewer-stage-tile .viewer-tile-avatar-overlay .live-avatar-label,
.live-video-wrap.is-fs-ios .viewer-stage-tile .viewer-tile-avatar-overlay .live-avatar-label,
.live-video-wrap:fullscreen .guest-wrap .viewer-tile-avatar-overlay .live-avatar-label,
.live-video-wrap.is-fs-ios .guest-wrap .viewer-tile-avatar-overlay .live-avatar-label,
.live-video-wrap:fullscreen .host-wrap .viewer-tile-avatar-overlay .live-avatar-label,
.live-video-wrap.is-fs-ios .host-wrap .viewer-tile-avatar-overlay .live-avatar-label,
.live-video-wrap:fullscreen .host-wrap > .live-audio-tile .live-avatar-label,
.live-video-wrap.is-fs-ios .host-wrap > .live-audio-tile .live-avatar-label,
.live-video-wrap:fullscreen .guest-wrap > .live-audio-tile .live-avatar-label,
.live-video-wrap.is-fs-ios .guest-wrap > .live-audio-tile .live-avatar-label,
.live-video-wrap:fullscreen .viewer-stage-tile.live-audio-tile .live-avatar-label,
.live-video-wrap.is-fs-ios .viewer-stage-tile.live-audio-tile .live-avatar-label {
  font-size: clamp(10px, 2.2cqh, 15px) !important;
  padding: 3px 10px !important;
  max-width: calc(100% - 12px) !important;
}
