/* ============================================================
   platoshin. — Main Stylesheet
   ============================================================ */
* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: 'Rubik', system-ui, -apple-system, sans-serif;
  font-weight: 400;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

body { position: relative; }

/* ============================================================
   HERO BACKGROUND — fixed photo behind everything, with dark
   overlay that darkens edges and keeps the content readable.

   The photo lives at assets/img/hero-bg.jpg. Its position is
   fixed (cover) so scrolling the content doesn't move it, and
   it survives the page-enter fade.
   ============================================================ */
.atmosphere {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
  background-color: var(--bg);
  background-image: url('../img/hero-bg.jpg');
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
}

/* Dark overlay — flat near-opaque neutral black. The photo barely
   shows through, no warm tint. */
.atmosphere::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(10, 10, 11, 0.85);
  pointer-events: none;
}

/* ::after kept as a very subtle vermillion glow accent, top-left.
   Almost invisible — pure mood, no visible "blob" outline. */
.atmosphere::after {
  content: '';
  position: absolute;
  width: 60vw;
  height: 60vw;
  max-width: 700px;
  max-height: 700px;
  top: -30vw;
  left: -15vw;
  border-radius: 50%;
  background: radial-gradient(circle, var(--accent) 0%, transparent 60%);
  opacity: 0.025;
  filter: blur(120px);
  pointer-events: none;
}

/* ============================================================
   APP LAYOUT
   ============================================================ */
.app {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  animation: page-enter 0.55s var(--ease-out) both;
}

@keyframes page-enter {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.atmosphere {
  animation: atmosphere-enter 1.1s ease-out 0.15s both;
}

@keyframes atmosphere-enter {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ============================================================
   STAGGERED ENTRY ANIMATIONS
   Each major panel slides in with a small delay for a polished
   first-paint feel. Video frame gets a subtle scale-in.
   ============================================================ */
.app > .topbar   { animation: slide-down 0.55s var(--ease-out) 0.05s both; }
.app > .stage    { animation: stage-fade 0.65s var(--ease-out) 0.15s both; }
.app > .controls { animation: slide-up 0.55s var(--ease-out) 0.10s both; }

@keyframes slide-down {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes slide-up {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes stage-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.video-frame {
  animation: video-frame-enter 0.65s var(--ease-out) 0.25s both;
}

@keyframes video-frame-enter {
  from { opacity: 0; transform: scale(0.985); }
  to   { opacity: 1; transform: scale(1); }
}

/* ============================================================
   TOPBAR
   ============================================================ */
.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 18px 28px;
  flex-shrink: 0;
  gap: 16px;
  /* No own background — show through to the hero photo / overlay.
     Border is intentionally absent so topbar reads as part of the
     page rather than a separate banner. */
  background: transparent;
  border-bottom: 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

/* ============================================================
   BRAND WORDMARK — text-only logo "platoshin." with vermillion dot.
   Whole wordmark is the clickable trigger for Color Show.
   ============================================================ */
.brand-wordmark {
  display: inline-flex;
  align-items: baseline;
  gap: 0;
  background: transparent;
  border: 0;
  text-decoration: none;
  color: var(--text);
  font-family: 'Rubik', sans-serif;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1;
  position: relative;
  user-select: none;
}

.brand-wordmark-text {
  color: inherit;
}

/* The red dot — actually the "." character, coloured red.
   Same font, weight and size as the wordmark text — it reads as
   a typographic period that happens to be coloured. */
.brand-dot {
  color: var(--accent);
}

/* During connecting: keep the wordmark text pulse (subtle), but the
   dot stays untouched — it's a static glyph, not a status indicator. */
.brand-wordmark.connecting {
  animation: brand-text-pulse 1.6s ease-in-out infinite;
}
@keyframes brand-text-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* ============================================================
   STATUS PILL
   ============================================================ */
.status-pill {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 13px 7px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 100px;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
  transition: all 0.3s var(--ease);
}

.status-pill[data-state="live"] {
  border-color: rgba(220, 38, 38, 0.40);
  color: var(--text);
  background: rgba(220, 38, 38, 0.04);
  position: relative;
  overflow: hidden;
}

/* Live state: dot pulses, no shimmer sweep (per design — pill stays
   visually steady so it reads as a stable transmission indicator). */
.status-pill[data-state="live"] {
  border-color: rgba(34, 197, 94, 0.40);
  background: rgba(34, 197, 94, 0.06);
}

.status-pill[data-state="connected"] {
  border-color: rgba(34, 197, 94, 0.40);
  color: var(--text);
  background: rgba(34, 197, 94, 0.04);
}

.status-pill[data-state="error"] {
  border-color: rgba(220, 38, 38, 0.40);
  color: var(--accent);
  background: rgba(220, 38, 38, 0.06);
}

.status-pill[data-state="connecting"] {
  border-color: rgba(212, 165, 116, 0.40);
  color: var(--text);
  background: rgba(212, 165, 116, 0.04);
}

.status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--muted);
  flex-shrink: 0;
  transition: all 0.2s;
}

.status-pill[data-state="live"] .status-dot {
  background: var(--live);
  box-shadow: 0 0 0 0 var(--live-glow);
  animation: live-pulse 1.6s ease-in-out infinite;
}

.status-pill[data-state="connected"] .status-dot {
  background: var(--ok);
  box-shadow: 0 0 6px var(--ok-glow);
}

.status-pill[data-state="connecting"] .status-dot {
  background: var(--connecting);
  box-shadow: 0 0 6px var(--connecting-glow);
  animation: connecting-pulse 1s ease-in-out infinite;
}

.status-pill[data-state="error"] .status-dot {
  background: var(--error);
  box-shadow: 0 0 6px var(--error-glow);
}

@keyframes connecting-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.5; }
}

@keyframes live-pulse {
  0%   { box-shadow: 0 0 0 0 var(--live-glow); transform: scale(1); }
  50%  { box-shadow: 0 0 0 6px transparent; transform: scale(1.2); }
  100% { box-shadow: 0 0 0 0 transparent; transform: scale(1); }
}

/* ============================================================
   LANG SWITCH
   ============================================================ */
.lang-switch {
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-strong);
  border-radius: 100px;
  padding: 3px;
  position: relative;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.lang-btn {
  background: transparent;
  border: none;
  color: var(--text-mute);
  font-family: 'Rubik', sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: 5px 12px;
  cursor: pointer;
  border-radius: 100px;
  transition: color 0.2s var(--ease);
  position: relative;
  z-index: 2;
  -webkit-tap-highlight-color: transparent;
}

.lang-btn.active { color: #fff; }
.lang-btn:not(.active):hover { color: var(--text); }

.lang-pill {
  position: absolute;
  top: 3px;
  bottom: 3px;
  background: var(--navy);
  border-radius: 100px;
  transition: all 0.35s var(--ease);
  z-index: 1;
  pointer-events: none;
}

/* ============================================================
   STAGE — neutral grey surround for color-correct viewing
   ============================================================ */
.stage {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  /* No own background — let the hero photo / overlay (atmosphere)
     show through. The video itself has its own black background. */
  background: transparent;
  position: relative;
  min-height: 0;
}

.video-frame {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  position: relative;
  border-radius: 4px;
  /* Note: overflow:hidden moved to .video-frame > video / .plyr so the
     ::after rainbow glow during Color Show can render outside the frame. */
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.10),
    0 30px 60px -20px rgba(15, 23, 42, 0.35),
    0 50px 100px -30px rgba(0, 0, 0, 0.4);
  transition: box-shadow 0.5s var(--ease);
}

.video-frame.has-stream {
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.12),
    0 30px 60px -20px rgba(15, 23, 42, 0.45),
    0 50px 100px -30px rgba(0, 0, 0, 0.45);
}

/* The <video> element fills the frame with letterboxing, never crops */
.video-frame video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: #000;
  object-fit: contain;
  display: block;
}

/* ============================================================
   PLYR PLAYER STYLING (themed to platoshin.)
   ============================================================ */

/* Plyr container fills the frame */
.video-frame .plyr {
  width: 100%;
  height: 100%;
  border-radius: 4px;
  overflow: hidden;
  --plyr-control-spacing: 12px;
}

.plyr--video,
.plyr__poster { background-color: #000 !important; }

/* Big centered play button (when paused) — neutral glass instead
   of vivid accent; reads as a control, not as a brand moment. */
.plyr__control--overlaid {
  background: rgba(255, 255, 255, 0.12) !important;
  border: 1px solid rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45),
              0 0 0 1px rgba(255, 255, 255, 0.05);
  transition: all 0.3s var(--ease);
}

.plyr__control--overlaid:hover,
.plyr__control--overlaid:focus {
  background: rgba(255, 255, 255, 0.20) !important;
  border-color: rgba(255, 255, 255, 0.25);
  transform: translate(-50%, -50%) scale(1.04);
}

/* Bottom control bar */
.plyr--video .plyr__controls {
  padding: 14px 16px 12px;
  background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.85) 100%);
}

/* While the stream hasn't started (the waiting screen is up and the
   frame is not yet marked .has-stream), hide every player control —
   the bottom bar and the big overlaid play button. There's nothing to
   control over a black frame. They fade back in automatically once
   video is playing, since .has-stream is added on the 'playing' event. */
.video-frame:not(.has-stream) .plyr__controls,
.video-frame:not(.has-stream) .plyr__control--overlaid {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 0.3s var(--ease), visibility 0.3s var(--ease);
}

/* Per-control hover — soft neutral fill, never the bright accent.
   The accent is reserved for brand moments (logo dot, error state),
   not for routine controls. */
.plyr--video .plyr__control {
  color: rgba(255, 255, 255, 0.75) !important;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}
.plyr--video .plyr__control:hover {
  background: rgba(255, 255, 255, 0.10) !important;
  color: #ffffff !important;
}
.plyr--video .plyr__control[aria-expanded="true"],
.plyr--video .plyr__control.plyr__control--pressed {
  background: rgba(255, 255, 255, 0.12) !important;
  color: #ffffff !important;
}

/* Range (seek bar + volume) — neutral white fill */
.plyr--full-ui input[type=range] { color: rgba(255, 255, 255, 0.85); }

.plyr--full-ui input[type=range]::-webkit-slider-thumb {
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
}

.plyr__progress__buffer { color: rgba(255, 255, 255, 0.22); }

.plyr__menu__container {
  border: 1px solid var(--border);
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
}

/* Make sure the underlying <video> never crops */
.video-frame video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
}

/* ============================================================
   GO LIVE button — injected into Plyr's bottom control bar.
   Visible only when the player is more than ~2s behind the live edge.
   By using Plyr's own `.plyr__control` class, the button inherits the
   look (size, focus ring, animations) of the other Plyr buttons.
   ============================================================ */
.plyr__control--live {
  /* Hidden by default; revealed via .show class when behind live edge */
  display: none;
  align-items: center;
  gap: 6px;
  padding: 0 10px !important;       /* override Plyr's default square padding */
  margin: 0 4px;
  height: 32px;
  border-radius: 16px !important;
  background: rgba(220, 38, 38, 0.92) !important;
  color: #fff !important;
  font-family: 'Rubik', sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10) inset;
  transition: background 0.18s var(--ease), transform 0.15s var(--ease);
  -webkit-tap-highlight-color: transparent;
}

.plyr__control--live.show {
  display: inline-flex;
}

.plyr--video .plyr__control--live {
  /* Override the muted-neutral control colour above — this is a CTA */
  color: #ffffff !important;
}
.plyr--video .plyr__control--live:hover {
  background: rgba(220, 38, 38, 1) !important;
  color: #ffffff !important;
  transform: translateY(-1px);
}

.plyr__control--live:active {
  transform: translateY(0);
}

.plyr__control--live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--surface);
  flex-shrink: 0;
  box-shadow: 0 0 6px rgba(255, 255, 255, 0.7);
  animation: live-btn-pulse 1.4s ease-in-out infinite;
}

.plyr__control--live-text {
  display: inline-block;
  line-height: 1;
}

@keyframes live-btn-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.55; transform: scale(0.85); }
}

/* ============================================================
   UNMUTE BANNER (after muted autoplay fallback)
   ============================================================ */
.unmute-banner {
  position: absolute;
  top: 14px;
  left: 14px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 14px 8px 12px;
  background: rgba(15, 23, 42, 0.85);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 100px;
  font-family: 'Rubik', sans-serif;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.02em;
  cursor: pointer;
  z-index: 10;
  opacity: 0;
  transform: translateY(-8px) scale(0.92);
  pointer-events: none;
  transition:
    opacity 0.35s var(--ease),
    transform 0.45s var(--ease-out),
    background 0.2s var(--ease);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.40);
  -webkit-tap-highlight-color: transparent;
}

.unmute-banner.show {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
  /* Subtle attention-grabber — once on first show */
  animation: unmute-attention 1.6s ease-out 0.4s 1;
}

@keyframes unmute-attention {
  0%, 100% { transform: translateY(0) scale(1); }
  20%      { transform: translateY(0) scale(1.04); }
  40%      { transform: translateY(0) scale(1); }
  60%      { transform: translateY(0) scale(1.02); }
}

.unmute-banner:hover {
  background: rgba(15, 23, 42, 0.95);
  transform: translateY(-1px) scale(1.02);
}

.unmute-banner svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* ============================================================
   EMPTY STATE
   ============================================================ */
.empty-state {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  color: rgba(255, 255, 255, 0.7);
  z-index: 5;
  background: #000;
  transition: opacity 0.5s var(--ease);
}

.empty-state.hidden {
  opacity: 0;
  pointer-events: none;
}

/* Scanline — a thin horizontal beam sweeping top→bottom across the
   empty state, evoking a broadcast monitor / test-pattern sweep.
   Only visible in the "waiting for stream" state. */
.scanline {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(220, 38, 38, 0.0) 10%,
    rgba(220, 38, 38, 0.5) 50%,
    rgba(220, 38, 38, 0.0) 90%,
    transparent 100%);
  box-shadow: 0 0 12px rgba(220, 38, 38, 0.4);
  opacity: 0;
  pointer-events: none;
  z-index: 6;
}
.empty-state[data-mode="waiting"] .scanline,
.empty-state[data-mode="broken"] .scanline,
.empty-state[data-mode="connecting"] .scanline {
  opacity: 1;
  animation: scanline-sweep 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes scanline-sweep {
  0%   { top: 0%; opacity: 0; }
  10%  { opacity: 1; }
  90%  { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .empty-state[data-mode="waiting"] .scanline,
  .empty-state[data-mode="broken"] .scanline,
  .empty-state[data-mode="connecting"] .scanline { animation: none; opacity: 0.6; top: 50%; }
}

.gray-bars {
  display: flex;
  width: clamp(180px, 35vw, 320px);
  aspect-ratio: 4 / 1;
  margin-bottom: 32px;
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}

.gray-bars::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.06) 50%, transparent 100%);
  animation: scan 4s ease-in-out infinite;
}

@keyframes scan {
  0%, 100% { transform: translateX(-100%); }
  50%      { transform: translateX(100%); }
}

.gray-bars div { flex: 1; }
.gray-bars div:nth-child(1) { background: #f5f5f5; }
.gray-bars div:nth-child(2) { background: #c8c8c8; }
.gray-bars div:nth-child(3) { background: #999999; }
.gray-bars div:nth-child(4) { background: #707070; }
.gray-bars div:nth-child(5) { background: #4a4a4a; }
.gray-bars div:nth-child(6) { background: #2a2a2a; }
.gray-bars div:nth-child(7) { background: #0a0a0a; }

.empty-title {
  font-size: clamp(15px, 2.4vw, 19px);
  font-weight: 500;
  letter-spacing: -0.005em;
  color: #fff;
  margin-bottom: 10px;
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}

.empty-sub {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  text-align: center;
  padding: 0 20px;
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}
/* Fade-out frame for smooth text swaps between states */
.empty-title.text-swap,
.empty-sub.text-swap {
  opacity: 0;
  transform: translateY(4px);
}

/* ============================================================
   CONTROLS PANEL
   ============================================================ */
.controls {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(10, 10, 11, 0.55);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  padding: 18px 28px 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex-shrink: 0;
  position: relative;
}

.controls::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: rgba(255, 255, 255, 0.06);
}

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

.url-input-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 0 14px;
  transition: all 0.25s var(--ease);
  min-width: 0;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.02), inset 0 1px 2px rgba(0, 0, 0, 0.02);
}

.url-input-wrap:focus-within {
  border-color: var(--accent-bright);
  box-shadow: 0 0 0 3px var(--navy-soft), inset 0 1px 2px rgba(0, 0, 0, 0.02);
}

.url-icon {
  color: var(--accent-bright);
  font-size: 14px;
  line-height: 1;
  user-select: none;
  flex-shrink: 0;
}

.url-input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text);
  font-family: 'Rubik', sans-serif;
  font-size: 13.5px;
  font-weight: 400;
  padding: 12px 0;
  min-width: 0;
}

.url-input::placeholder { color: var(--text-mute); }
.url-input::selection { background: var(--navy-soft); }

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  text-align: center;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-family: 'Rubik', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.005em;
  padding: 11px 20px;
  cursor: pointer;
  border-radius: 8px;
  transition: all 0.2s var(--ease);
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
  flex-shrink: 0;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

.btn:hover {
  border-color: var(--text-mute);
  background: var(--surface-3);
  transform: translateY(-1px);
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
}

.btn:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); }

.btn-primary {
  /* Off-white background, dark text — Vercel-style emphasis */
  background: var(--text);
  border-color: var(--text);
  color: var(--bg);
  font-weight: 600;
  box-shadow: 0 4px 14px -4px rgba(232, 232, 234, 0.25),
              0 1px 0 rgba(255, 255, 255, 0.4) inset;
  transition: background 0.2s var(--ease),
              color 0.2s var(--ease),
              border-color 0.2s var(--ease),
              box-shadow 0.2s var(--ease),
              transform 0.15s var(--ease);
}

.btn-primary:hover {
  /* On hover: dark fill, light text — inverted but still readable */
  background: var(--surface-3);
  border-color: var(--surface-3);
  color: var(--text);
  box-shadow: 0 6px 18px -4px rgba(0, 0, 0, 0.4),
              0 1px 0 rgba(255, 255, 255, 0.06) inset;
}

/* Outline (secondary) — translucent fill + border, lighter weight
   than primary. Mirrors the secondary icon button in the snapshot
   modal so paired actions read the same way across the product. */
.btn-outline {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--border-strong);
  color: var(--text);
  box-shadow: none;
}
.btn-outline:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--text-mute);
  color: var(--text);
  box-shadow: none;
}

/* Brief green "copied" confirmation — used on copy buttons across
   the product (host page, snapshot modal) for a consistent signal. */
.btn-copied,
.btn-copied:hover {
  background: rgba(34, 197, 94, 0.15) !important;
  border-color: rgba(34, 197, 94, 0.5) !important;
  color: #22c55e !important;
  box-shadow: none !important;
}

.btn-icon {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.btn-icon svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* ============================================================
   STATS ROW
   ============================================================ */
.stats-row {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  padding-top: 4px;
}

.stat {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.stat-label {
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.stat-value {
  color: var(--text);
  font-size: 12.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.005em;
  transition: color 0.4s var(--ease);
}

.stat-value.pulse {
  animation: stat-pulse 0.7s var(--ease-out);
}

@keyframes stat-pulse {
  0%   { color: var(--accent-bright); transform: translateY(-1px); }
  100% { color: var(--text);        transform: translateY(0); }
}

.stats-spacer { flex: 1; min-width: 0; }

/* ============================================================
   TOAST
   ============================================================ */
.toast {
  position: fixed;
  bottom: 32px;
  left: 50%;
  transform: translateX(-50%) translateY(24px) scale(0.95);
  background: var(--text);
  color: var(--bg);
  padding: 12px 22px;
  font-family: 'Rubik', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.005em;
  border-radius: 100px;
  opacity: 0;
  transition:
    opacity 0.35s var(--ease-out),
    transform 0.55s cubic-bezier(0.34, 1.56, 0.64, 1);
  pointer-events: none;
  z-index: 9300;
  box-shadow:
    0 20px 50px -10px rgba(15, 23, 42, 0.30),
    0 0 0 1px rgba(255, 255, 255, 0.05);
  max-width: calc(100vw - 40px);
  text-align: center;
}

.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0) scale(1);
}

/* ============================================================
   FULLSCREEN
   ============================================================ */
:fullscreen .video-frame,
:-webkit-full-screen .video-frame {
  border-radius: 0;
  box-shadow: none;
  aspect-ratio: auto;
}

/* ============================================================
   RESPONSIVE: TABLET
   ============================================================ */
@media (max-width: 900px) {
  .topbar { padding: 14px 18px; }
  .stage { padding: 18px; }
  .controls { padding: 14px 18px 16px; gap: 12px; }
  .brand-tag { display: none; }
  .stats-row { gap: 16px; }
}

/* ============================================================
   RESPONSIVE: MOBILE
   ============================================================ */
@media (max-width: 640px) {
  .topbar {
    padding: 11px 14px;
    gap: 8px;
  }

  .brand-mark { width: 28px; height: 28px; }
  .brand-name { font-size: 14px; }

  .toolbar { gap: 6px; }
  .status-pill {
    padding: 5px 11px 5px 11px;
    font-size: 9.5px;
    letter-spacing: 0.12em;
    gap: 6px;
  }
  /* Keep status text visible on mobile, just compact it */
  .status-pill .status-text {
    max-width: 90px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .lang-btn { padding: 4px 9px; font-size: 10px; }

  .stage { padding: 8px; }

  .controls {
    padding: 12px 14px 14px;
    gap: 12px;
    padding-bottom: max(14px, env(safe-area-inset-bottom));
  }

  .url-row { flex-wrap: wrap; gap: 8px; }

  .url-input-wrap { width: 100%; order: 1; padding: 0 12px; }
  .url-input { font-size: 16px; padding: 11px 0; }   /* 16px prevents iOS zoom on focus */

  .btn {
    flex: 1;
    padding: 11px 14px;
    font-size: 13px;
    order: 2;
  }

  /* On mobile, share button shows only icon to save space */
  #share-btn span { display: none; }
  #share-btn { flex: 0 0 auto; padding: 11px 14px; }
  #share-btn svg { width: 16px; height: 16px; margin: 0; }

  .stats-row {
    gap: 10px 16px;
    padding-top: 2px;
  }
  .stat-label { font-size: 9px; letter-spacing: 0.18em; }
  .stat-value { font-size: 11.5px; }

  /* On mobile call-enabled pages we have a "Show video call" button
     in the same row — hide the Codec stat to keep the button on the
     same line. Codec is rarely actionable info on a phone anyway.
     Single-stream pages keep all stats since they have no button. */
  .controls:has([data-action="toggle-pane"]) .stat[data-key="codec"] {
    display: none;
  }

  .stats-spacer { flex-basis: 100%; height: 0; }

  .gray-bars { width: 220px; margin-bottom: 24px; }
  .empty-title { font-size: 16px; }
  .empty-sub { font-size: 9px; letter-spacing: 0.20em; }

  /* Plyr — compact controls on mobile */
  .plyr--video .plyr__controls { padding: 10px 10px 8px; }
  .plyr__control { padding: 6px !important; }
  .plyr--video .plyr__volume input[type=range] { display: none; }   /* mute toggle is enough */

  /* Unmute banner */
  .unmute-banner { top: 10px; left: 10px; padding: 7px 12px 7px 10px; font-size: 10.5px; gap: 7px; }
  .unmute-banner svg { width: 13px; height: 13px; }

  /* GO LIVE button — slightly more compact on mobile */
  .plyr__control--live { height: 28px; padding: 0 9px !important; font-size: 10px; gap: 5px; }
  .plyr__control--live-dot { width: 6px; height: 6px; }
}

@media (max-width: 420px) {
  .topbar { padding: 10px 12px; }
  /* Brand-text and FPS stat stay visible at all sizes — user requested. */
  /* For very small screens we make brand-text more compact instead. */
  .brand-name { font-size: 14px; }
  .brand-tag { font-size: 10px; }
  /* Compact stats so they don't overflow */
  .stat { padding: 6px 8px; }
  .stat-label { font-size: 9.5px; }
  .stat-value { font-size: 13px; }
}

@media (max-width: 360px) {
  .stat[data-key="latency"] { display: none; }
}

/* Landscape mobile — prioritize video real estate */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
  .topbar { padding: 6px 14px; }
  .brand-tag { display: none; }
  .stage { padding: 4px; }
  .controls { padding: 6px 14px; gap: 6px; }
  .stats-row { display: none; }
  .url-row { flex-wrap: nowrap; }
  .url-row > .btn:not(.btn-primary) { display: none; }
  .url-input-wrap { width: auto; flex: 1; }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ============================================================
   WHEP source — bitrate, codec, fps come from getStats() in
   source-whep.js. Latency is hidden because WebRTC is
   inherently low-latency and the computed value (jitter buffer
   delay + RTT/2) drifts in ways that mislead more than inform.
   ============================================================ */
.video-frame[data-kind="whep"] ~ .controls .stat[data-key="latency"],
.stage:has(.video-frame[data-kind="whep"]) ~ .controls .stat[data-key="latency"] {
  display: none;
}

/* ============================================================
   HOST PAGE — guest link generator (index.html)
   ============================================================ */
body.host-page .topbar { border-bottom: none; }

.host-stage {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  min-height: 0;
}

.host-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 36px 36px 32px;
  width: 100%;
  max-width: 560px;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.03) inset,
    0 30px 60px -20px rgba(0, 0, 0, 0.45),
    0 50px 100px -30px rgba(0, 0, 0, 0.35);
}

.host-title {
  margin: 0 0 8px;
  font-family: 'Rubik', sans-serif;
  font-size: 26px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.5px;
}

.host-sub {
  margin: 0 0 22px;
  font-family: 'Rubik', sans-serif;
  font-size: 14px;
  font-weight: 400;
  color: var(--text-dim);
  line-height: 1.5;
}

.host-input-row {
  /* Block, not flex — flex inside an animating max-height can
     occasionally hitch on the final frame as flex re-resolves
     children. We only have one child anyway. */
  margin-bottom: 14px;
}

.host-input-row .url-input-wrap {
  width: 100%;
}

.host-detect {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-family: 'Rubik', sans-serif;
  font-size: 13px;
  margin-bottom: 18px;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease);
}

.host-detect-label {
  color: var(--text-dim);
  font-weight: 500;
}

.host-detect-value {
  color: var(--text-mute);
  font-weight: 600;
}

.host-detect[data-state="hls"] {
  background: rgba(34, 197, 94, 0.10);
  border-color: rgba(34, 197, 94, 0.4);
}
.host-detect[data-state="hls"] .host-detect-value {
  color: var(--live);
}

.host-detect[data-state="whep"] {
  background: rgba(34, 197, 94, 0.10);
  border-color: rgba(34, 197, 94, 0.4);
}
.host-detect[data-state="whep"] .host-detect-value {
  color: var(--live);
}

.host-detect[data-state="invalid"] {
  background: rgba(220, 38, 38, 0.10);
  border-color: rgba(220, 38, 38, 0.4);
}
.host-detect[data-state="invalid"] .host-detect-value {
  color: var(--error);
  font-weight: 500;
  font-size: 12.5px;
}

.host-link-row {
  display: flex;
  gap: 8px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}

.host-link-input {
  flex: 1;
  min-width: 0;
  padding: 11px 14px;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  background: var(--surface-2);
  font-family: 'Rubik', sans-serif;
  font-size: 13px;
  color: var(--text);
  cursor: text;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.host-link-input:focus,
.host-link-input:hover {
  outline: none;
  border-color: var(--accent-bright);
  background: var(--surface);
}

.host-link-row .btn {
  flex-shrink: 0;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

.host-hint {
  margin: 0;
  font-family: 'Rubik', sans-serif;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-mute);
  line-height: 1.5;
}

@media (max-width: 640px) {
  .host-card { padding: 24px 18px; }
  .host-title { font-size: 22px; }
  .host-sub { font-size: 13px; }
  .host-link-row { flex-direction: column; }
  .host-link-row .btn { width: 100%; justify-content: center; }
}

/* ============================================================
   GSAP-STYLE EFFECTS
   ============================================================ */

/* ---- SMPTE Colour Bars in empty state ---- */
.smpte-bars {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  pointer-events: none;
  opacity: 0.32;             /* sit behind the No Signal text */
  z-index: 0;
  /* Subtle scanline shimmer */
  background:
    repeating-linear-gradient(
      0deg,
      rgba(0,0,0,0) 0,
      rgba(0,0,0,0) 2px,
      rgba(0,0,0,0.06) 2px,
      rgba(0,0,0,0.06) 3px
    );
}
.smpte-bars-top { flex: 0 0 66%; display: flex; }
.smpte-bars-mid { flex: 0 0  9%; display: flex; }
.smpte-bars-bot { flex: 0 0 25%; display: flex; }
.smpte-bars-top > *,
.smpte-bars-mid > *,
.smpte-bars-bot > * { flex: 1 1 0; }

/* The overlaid No-Signal text needs to sit above the bars */
.empty-state > *:not(.smpte-bars) { position: relative; z-index: 1; }

/* Make the empty state plate transparent so bars show through */
.empty-state {
  background: rgba(0, 0, 0, 0.55) !important;
}

@media (max-width: 640px) {
  .smpte-bars { opacity: 0.28; }
}

/* ---- Breathing frame while live (extremely subtle) ---- */
.video-frame.breathing {
  animation: frame-breathe 4.5s ease-in-out infinite;
}
@keyframes frame-breathe {
  0%, 100% {
    box-shadow:
      0 0 0 1px rgba(0, 0, 0, 0.12),
      0 30px 60px -20px rgba(15, 23, 42, 0.45),
      0 50px 100px -30px rgba(0, 0, 0, 0.45);
  }
  50% {
    box-shadow:
      0 0 0 1px rgba(0, 0, 0, 0.12),
      0 32px 66px -20px rgba(15, 23, 42, 0.50),
      0 56px 110px -30px rgba(0, 0, 0, 0.5);
  }
}

/* ---- Status pill state-change ripple ---- */
.status-pill {
  position: relative;
}
.status-pill.state-change::after {
  content: '';
  position: absolute;
  left: 8px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: currentColor;
  opacity: 0.7;
  pointer-events: none;
  animation: status-ripple 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes status-ripple {
  0%   { transform: translate(-50%, -50%) scale(0.8); opacity: 0.7; }
  100% { transform: translate(-50%, -50%) scale(4.5); opacity: 0;   }
}
/* Smooth tween between state colours on the pill border/background */
.status-pill {
  transition:
    background 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    color 0.45s cubic-bezier(0.16, 1, 0.3, 1),
    border-color 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}
.status-pill .status-dot {
  transition: background 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---- Reduced motion — kill everything decorative ---- */
@media (prefers-reduced-motion: reduce) {
  .video-frame.breathing,
  .status-pill.state-change::after {
    animation: none !important;
  }
}

/* ============================================================
   HOST CALL TOGGLE — "with call" switch + Jitsi URL field
   ============================================================ */
.host-call-toggle {
  margin: 16px 0 18px;
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  user-select: none;
  font-family: 'Rubik', sans-serif;
  font-size: 14px;
  color: var(--text-dim);
}

.toggle-input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.toggle-track {
  position: relative;
  flex-shrink: 0;
  width: 38px;
  height: 22px;
  background: #cbd5e1;
  border-radius: 12px;
  transition: background 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  background: var(--surface);
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.2);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),
              background 0.3s ease;
}
.toggle-input:checked + .toggle-track {
  background: var(--navy-bright);
}
.toggle-input:checked + .toggle-track .toggle-thumb {
  transform: translateX(16px);
}
.toggle-input:focus-visible + .toggle-track {
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.toggle-label {
  flex: 1;
  line-height: 1.4;
}

/* host-input-row and host-call-url-wrap.

   Plyr-style fade: a short opacity transition. On disappear, the
   element fades out then collapses out of the flow with display:none
   after the fade completes. On appear, we flip display first then
   fade in.

   We use the modern `transition-behavior: allow-discrete` plus
   `@starting-style` so that `display` itself participates in the
   transition. Supported in Chrome 117+, Safari 17.4+, Firefox 129+
   — all stable as of 2025. Older browsers fall back to instant
   show/hide which is still acceptable. */
.host-input-row,
.host-call-url-wrap {
  opacity: 1;
  transition:
    opacity 0.2s ease,
    display 0.2s allow-discrete;
}
@starting-style {
  .host-input-row,
  .host-call-url-wrap {
    opacity: 0;
  }
}
.host-input-row.is-hidden,
.host-call-url-wrap.is-hidden {
  opacity: 0;
  display: none;
}

.host-call-url-wrap {
  margin-top: 12px;
  margin-bottom: 14px;
}

.host-call-url-wrap {
  margin-top: 12px;
  margin-bottom: 14px;
}

/* ============================================================
   DUAL PANE — viewer-call / viewer-whep-call layouts
   ============================================================ */
.dual-pane {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  overflow: hidden;
}

.pane {
  position: relative;
  flex: 1 1 50%;
  min-width: 0;
  height: 100%;
  transition: flex-basis 0.45s cubic-bezier(0.16, 1, 0.3, 1),
              opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.pane[data-pane="player"] {
  /* Player pane keeps the existing video-frame styles; nothing extra */
}

.pane[data-pane="call"] {
  background: rgba(10, 10, 11, 0.65);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.pane[data-pane="call"] .jitsi-container {
  flex: 1;
  width: 100%;
  height: 100%;
  background: transparent;
}
.pane[data-pane="call"] .jitsi-container iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.pane[data-pane="call"] .call-loading {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 14px;
  color: rgba(255, 255, 255, 0.7);
  font-family: 'Rubik', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.4s ease;
}
.pane[data-pane="call"].loaded .call-loading {
  opacity: 0;
}
.call-loading-dot {
  width: 32px;
  height: 32px;
  border: 3px solid rgba(255, 255, 255, 0.15);
  border-top-color: rgba(255, 255, 255, 0.7);
  border-radius: 50%;
  animation: brand-spin 1.1s linear infinite;
}

/* ---- Desktop: side-by-side split with toggle ---- */
.dual-pane-desktop .pane[data-pane="player"] { flex: 1 1 65%; }
.dual-pane-desktop .pane[data-pane="call"]   { flex: 1 1 35%; margin-left: 12px; }

.dual-pane-desktop.split-collapsed .pane[data-pane="player"] { flex: 1 1 100%; }
.dual-pane-desktop.split-collapsed .pane[data-pane="call"]   {
  flex: 0 0 0%;
  margin-left: 0;
  opacity: 0;
  pointer-events: none;
}

/* ---- Mobile: tabs (one pane visible, other off-screen) ---- */
.dual-pane-mobile {
  width: 100%;
  position: relative;
}
.dual-pane-mobile .pane {
  position: absolute;
  inset: 0;
  flex: 1 1 100%;
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1),
              opacity 0.4s ease;
}
.dual-pane-mobile.pane-player-active .pane[data-pane="player"] {
  transform: translateX(0);
  opacity: 1;
}
.dual-pane-mobile.pane-player-active .pane[data-pane="call"] {
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
}
.dual-pane-mobile.pane-call-active .pane[data-pane="player"] {
  transform: translateX(-100%);
  opacity: 0;
  pointer-events: none;
}
.dual-pane-mobile.pane-call-active .pane[data-pane="call"] {
  transform: translateX(0);
  opacity: 1;
}

/* ---- Pane toggle button (in stats bar) ---- */
.pane-toggle-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px 6px 10px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 100px;
  font-family: 'Rubik', sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition:
    background 0.2s var(--ease),
    border-color 0.2s var(--ease),
    transform 0.15s var(--ease);
  flex-shrink: 0;
}
.pane-toggle-btn:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--text-mute);
  transform: translateY(-1px);
}
.pane-toggle-btn:active {
  transform: translateY(0);
}
.pane-toggle-btn-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* ============================================================
   NAME MODAL — first-visit ask-for-name dialog
   ============================================================ */
.name-modal-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(12px) saturate(1.2);
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
  opacity: 0;
  z-index: 9000;
  transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.name-modal-overlay.show { opacity: 1; }
.name-modal-overlay.hide { opacity: 0; }

/* Card uses glassy frosted look — semi-transparent dark fill, thin
   light border, and an inset top-highlight that mimics the way
   light catches the edge of frosted glass. */
.name-modal-card {
  background: rgba(22, 22, 24, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 18px;
  padding: 28px 28px 24px;
  width: min(420px, 92vw);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.06) inset,
    0 30px 60px -15px rgba(0, 0, 0, 0.5),
    0 50px 100px -30px rgba(0, 0, 0, 0.4);
  transform: translateY(14px) scale(0.96);
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
  font-family: 'Rubik', sans-serif;
}
.name-modal-overlay.show .name-modal-card {
  transform: translateY(0) scale(1);
}
.name-modal-overlay.hide .name-modal-card {
  transform: translateY(-8px) scale(0.97);
}

.name-modal-title {
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.5px;
  margin-bottom: 6px;
}
.name-modal-sub {
  font-size: 13.5px;
  font-weight: 400;
  color: var(--text-dim);
  line-height: 1.5;
  margin-bottom: 18px;
}

/* Input — translucent fill to match the glass card */
.name-modal-input {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.04);
  font-family: 'Rubik', sans-serif;
  font-size: 16px;          /* prevent iOS zoom */
  font-weight: 500;
  color: var(--text);
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
  -webkit-appearance: none;
}
.name-modal-input::placeholder { color: var(--text-mute); }
.name-modal-input:focus,
.name-modal-input:hover {
  outline: none;
  border-color: var(--text-mute);
  background: rgba(255, 255, 255, 0.06);
}
.name-modal-input.shake {
  animation: name-shake 0.4s ease-out;
  border-color: var(--error);
}
@keyframes name-shake {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-5px); }
  50%      { transform: translateX(5px); }
  75%      { transform: translateX(-3px); }
}

.name-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
}
.name-modal-actions .btn {
  min-width: 120px;
}

/* Mic toggle inside the name modal — a square button with an
   animated mic glyph. No physical switch; the button itself is
   the control. State is reflected via aria-pressed. */
.name-modal-mic-row {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  display: flex;
  align-items: center;
  gap: 12px;
}

.name-modal-mic-label {
  font-family: 'Rubik', sans-serif;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: 0.01em;
  transition: color 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Square mic button — glassy fill in both states, glyph colour
   carries the on/off meaning (red when muted, white when active). */
.mic-toggle {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: rgba(220, 38, 38, 0.14);
  border: 1.5px solid rgba(220, 38, 38, 0.45);
  border-radius: 12px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    border-color 0.35s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.18s cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 0.35s cubic-bezier(0.16, 1, 0.3, 1);
  /* Mic glyph colour when muted — vivid red, contrasts with dark glass */
  color: #f87171;
}

.mic-toggle:hover {
  transform: translateY(-1px);
  background: rgba(220, 38, 38, 0.20);
  box-shadow: 0 4px 12px -4px rgba(220, 38, 38, 0.35);
}

.mic-toggle:active {
  transform: translateY(0) scale(0.96);
}

.mic-toggle:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.30);
}

/* Pressed (microphone on) state — neutral glass with white glyph */
.mic-toggle[aria-pressed="true"] {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.20);
  color: #ffffff;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.05);
}
.mic-toggle[aria-pressed="true"]:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.30);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.06),
    0 4px 12px -4px rgba(255, 255, 255, 0.20);
}
.name-modal-mic-row:has(.mic-toggle[aria-pressed="true"]) .name-modal-mic-label {
  color: var(--text);
}

/* The icon: line-draw slash on/off + breathing pulse when on */
.mic-icon {
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4));
}
.mic-icon svg {
  width: 100%;
  height: 100%;
  overflow: visible;
  stroke-width: 2.2;          /* thicker strokes for better contrast on dark */
}
.mic-icon .mic-slash {
  stroke-dasharray: 24;
  stroke-dashoffset: 0;     /* drawn = visible (off / muted state) */
  transition: stroke-dashoffset 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.mic-toggle[aria-pressed="true"] .mic-icon .mic-slash {
  stroke-dashoffset: 24;    /* line-draw retract → invisible */
}
.mic-toggle[aria-pressed="true"] .mic-icon .mic-body {
  animation: mic-pulse 2.2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  transform-origin: 12px 8.5px;
}

@keyframes mic-pulse {
  0%, 100% { transform: scale(1);     opacity: 1;   }
  50%      { transform: scale(1.06);  opacity: 0.85; }
}

@media (prefers-reduced-motion: reduce) {
  .mic-toggle[aria-pressed="true"] .mic-icon .mic-body { animation: none; }
  .mic-icon .mic-slash { transition: none; }
  .mic-toggle { transition: none; }
}

/* Mobile-specific tweaks */
@media (max-width: 640px) {
  .pane-toggle-btn {
    padding: 5px 10px 5px 9px;
    font-size: 10px;
    gap: 5px;
  }
  .name-modal-card {
    padding: 22px 20px 20px;
  }
  .name-modal-title { font-size: 19px; }
  .name-modal-sub { font-size: 13px; }
  .toggle-label { font-size: 13px; }
}

/* ============================================================
   HOST TABS — three-mode selector
   ============================================================ */
.host-tabs {
  display: flex;
  gap: 4px;
  padding: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin-bottom: 16px;
}

.host-tab {
  flex: 1;
  padding: 9px 12px;
  border: none;
  background: transparent;
  border-radius: 8px;
  font-family: 'Rubik', sans-serif;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-dim);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background 0.3s cubic-bezier(0.16, 1, 0.3, 1),
    color 0.3s cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.host-tab:hover {
  color: var(--accent);
}
.host-tab.active {
  background: var(--surface-3);
  color: var(--text);
  font-weight: 600;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.05) inset,
    0 1px 3px rgba(0, 0, 0, 0.3);
}
.host-tab:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* host-detect "call" state — green-ish like whep */
.host-detect[data-state="call"] {
  background: rgba(34, 197, 94, 0.10);
  border-color: rgba(34, 197, 94, 0.4);
}
.host-detect[data-state="call"] .host-detect-value {
  color: var(--live);
}

@media (max-width: 640px) {
  .host-tabs { gap: 2px; padding: 3px; }
  .host-tab { padding: 8px 8px; font-size: 11.5px; }
}

/* ============================================================
   CALL PAGE (call.html) — Jitsi only, no player
   ============================================================ */
body.call-page .stage,
body.call-page .controls { display: none; }

.call-stage {
  flex: 1;
  display: flex;
  position: relative;
  min-height: 0;
  padding: 8px 16px 16px;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
}
@media (max-width: 640px) {
  .call-stage { padding: 8px; padding-bottom: max(8px, env(safe-area-inset-bottom)); }
}

.call-fullscreen {
  flex: 1;
  width: 100%;
  height: 100%;
  background: rgba(10, 10, 11, 0.65);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 4px;
  overflow: hidden;
  position: relative;
}
.call-fullscreen iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* Centered loading state — fades out once Jitsi joins */
body.call-page .call-loading {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 14px;
  color: rgba(255, 255, 255, 0.7);
  font-family: 'Rubik', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  pointer-events: none;
  transition: opacity 0.4s ease;
  z-index: 2;
}

body.call-page .call-error {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ef4444;
  font-family: 'Rubik', sans-serif;
  font-size: 14px;
  font-weight: 500;
  background: var(--bg-deep);
  z-index: 3;
}

/* ============================================================
   FIX: [hidden] attribute must override display:flex/grid

   By default the user agent stylesheet has `[hidden] { display: none }`
   with very low specificity, so any class rule with `display: flex`
   wins and a hidden=true element stays visible. We override here.
   ============================================================ */
[hidden] { display: none !important; }

/* Disabled action buttons in host link row — when no valid link
   is ready yet. Visually subdued, no pointer events. */
.host-link-row .btn:disabled,
.host-link-row .btn.btn-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
  filter: grayscale(0.4);
}

/* ============================================================
   SNAPSHOT BUTTON inside Plyr controls

   Sits next to the fullscreen button. Inherits .plyr__control
   hover/focus/sizing — we only style our SVG and add a flash
   animation for the capture moment.
   ============================================================ */
.plyr__control--snapshot svg {
  width: 20px;
  height: 20px;
  fill: none !important;
  stroke: currentColor;
  stroke-width: 2.4;
}
/* Snapshot uses the same colour/opacity as every other Plyr control
   (inherited from `.plyr--video .plyr__control`), so all buttons read
   at identical brightness. No extra opacity override here. */

/* Visual flash on the button when the shutter "fires" — quick
   white overlay confirms the capture happened. Works inside
   fullscreen because the animation is on the button itself. */
.plyr__control--snapshot.snapshot-flash {
  animation: plyr-snapshot-flash 0.45s ease-out;
}
@keyframes plyr-snapshot-flash {
  0%   { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
         background: transparent; }
  20%  { box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.25);
         background: rgba(255, 255, 255, 0.18); }
  100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
         background: transparent; }
}

/* ============================================================
   SNAPSHOT BUTTON — standalone (kept for backwards compatibility,
   but the in-Plyr button below replaces it on viewer pages)
   ============================================================ */
.snapshot-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px 6px 10px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 100px;
  font-family: 'Rubik', sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition:
    background 0.2s var(--ease),
    border-color 0.2s var(--ease),
    transform 0.15s var(--ease);
  flex-shrink: 0;
}
.snapshot-btn:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--text-mute);
  transform: translateY(-1px);
}
.snapshot-btn:active {
  transform: translateY(0);
}
.snapshot-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.snapshot-btn svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

.snapshot-btn.snapshot-flash {
  animation: snapshot-flash-anim 0.35s ease-out;
}
@keyframes snapshot-flash-anim {
  0%   { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
         background: rgba(255, 255, 255, 0.04); }
  30%  { box-shadow: 0 0 0 8px rgba(255, 255, 255, 0.25);
         background: rgba(255, 255, 255, 0.18); }
  100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
         background: rgba(255, 255, 255, 0.04); }
}

/* Hide on small mobile when the toggle-pane button is also present
   (call-enabled pages) — there's no room. */
@media (max-width: 640px) {
  .snapshot-btn {
    padding: 5px 10px 5px 9px;
    font-size: 10px;
    gap: 5px;
  }
  .snapshot-btn .snapshot-btn-text { display: none; }
  .controls:has([data-action="toggle-pane"]) .snapshot-btn .snapshot-btn-text {
    display: none;
  }
}

/* ============================================================
   SNAPSHOT MODAL — preview + download/copy
   ============================================================ */
.snapshot-modal-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  z-index: 9100;
  transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1);
  padding: 20px;
}
.snapshot-modal-overlay.show { opacity: 1; }
.snapshot-modal-overlay.hide { opacity: 0; }

.snapshot-modal-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 24px;
  width: min(640px, 100%);
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 30px 60px -15px rgba(0, 0, 0, 0.5),
              0 50px 100px -30px rgba(0, 0, 0, 0.45);
  transform: translateY(14px) scale(0.96);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
  font-family: 'Rubik', sans-serif;
}
.snapshot-modal-overlay.show .snapshot-modal-card {
  transform: translateY(0) scale(1);
}
.snapshot-modal-overlay.hide .snapshot-modal-card {
  transform: translateY(-8px) scale(0.97);
}

.snapshot-modal-title {
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.5px;
  margin-bottom: 4px;
}
.snapshot-modal-sub {
  font-size: 13.5px;
  font-weight: 400;
  color: var(--text-dim);
  line-height: 1.5;
  margin-bottom: 16px;
}

.snapshot-preview {
  background: var(--bg-deep);
  border-radius: 12px;
  overflow: hidden;
  position: relative;
  flex: 1;
  min-height: 0;
  margin-bottom: 12px;
  /* SMPTE bars-style border accent on top */
  box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.08);
}
.snapshot-preview img {
  width: 100%;
  height: auto;
  max-height: 50vh;
  display: block;
  object-fit: contain;
  background: var(--bg-deep);
}

.snapshot-modal-meta {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-mute);
  letter-spacing: 0.05em;
  margin-bottom: 16px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.snapshot-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

/* Square icon buttons with hover tooltip */
.snapshot-icon-btn {
  position: relative;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s var(--ease),
              border-color 0.2s var(--ease),
              color 0.2s var(--ease),
              transform 0.15s var(--ease);
}
.snapshot-icon-btn svg {
  width: 20px;
  height: 20px;
}
.snapshot-icon-btn:active { transform: translateY(1px); }

/* Primary (Download): filled off-white */
.snapshot-icon-btn-primary {
  background: var(--text);
  border: 1px solid var(--text);
  color: var(--bg);
  box-shadow: 0 4px 14px -4px rgba(232, 232, 234, 0.25);
}
.snapshot-icon-btn-primary:hover {
  background: var(--surface-3);
  border-color: var(--surface-3);
  color: var(--text);
  transform: translateY(-1px);
}

/* Secondary (Copy): outline */
.snapshot-icon-btn-secondary {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-strong);
  color: var(--text);
}
.snapshot-icon-btn-secondary:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--text-mute);
  transform: translateY(-1px);
}

/* Brief success state on Copy */
.snapshot-icon-btn-ok {
  background: rgba(34, 197, 94, 0.15) !important;
  border-color: rgba(34, 197, 94, 0.5) !important;
  color: #22c55e !important;
}

/* Hover tooltip — small dark bubble above the button */
.snapshot-icon-btn::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: rgba(10, 10, 11, 0.95);
  color: var(--text);
  font-family: 'Rubik', sans-serif;
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  padding: 6px 10px;
  border-radius: 7px;
  border: 1px solid var(--border-strong);
  box-shadow: 0 6px 18px -6px rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
}
.snapshot-icon-btn:hover::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* Close (×) button in the card corner */
.snapshot-modal-close-x {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 8px;
  color: var(--text-mute);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}
.snapshot-modal-close-x svg {
  width: 18px;
  height: 18px;
}
.snapshot-modal-close-x:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
}

/* Mobile tweaks for the modal */
@media (max-width: 640px) {
  .snapshot-modal-card {
    padding: 18px;
    border-radius: 14px;
  }
  .snapshot-modal-title { font-size: 18px; }
  .snapshot-modal-sub { font-size: 12.5px; margin-bottom: 12px; }
  .snapshot-modal-actions .btn { min-width: 0; flex: 1; }
}

/* ============================================================
   PAGE-SPECIFIC OVERLAY INTENSITY
   On stream / call pages the photo is more present (less content
   in the foreground), so it competes for attention with the
   player. We darken the overlay and the topbar slightly on those
   pages to keep focus where it belongs.
   ============================================================ */
body.viewer-page .atmosphere::before,
body.call-page   .atmosphere::before {
  background: rgba(8, 8, 9, 0.92);
}

body.viewer-page .topbar,
body.call-page   .topbar {
  background: rgba(10, 10, 11, 0.45);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
