:root {
  --bg: #0e0f12;
  --panel: #16181d;
  --border: #262a33;
  --text: #d7dae0;
  --dim: #7d8492;
  --accent: #4f8cff;
  --good: #3fbf6f;
  --bad: #e05555;
  --wait: #c9a227;
}

* { box-sizing: border-box; }

/* Author display rules (e.g. flex) override the hidden attribute's UA
   style; this restores it unconditionally. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  max-height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif,
    "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", "Twemoji Mozilla", "EmojiOne Color";
}

main {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding: 24px;
}

/* ---- landing ---- */

#landing h1 {
  font-size: 20px;
  font-weight: 500;
  letter-spacing: 4px;
  color: var(--dim);
  margin: 0;
}

#reloadReport {
  max-width: 36rem;
  width: 100%;
  text-align: left;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 14px;
  line-height: 1.4;
}
#reloadReport strong { display: block; margin-bottom: 8px; }
#reloadReportWhy {
  margin: 0 0 10px;
  white-space: pre-wrap;
  color: var(--text);
}
#reloadReportTrail {
  margin: 0 0 10px;
  max-height: 10rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 11px;
  color: var(--dim);
}

.buttons {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}

button {
  font: inherit;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 18px;
  cursor: pointer;
}

button:hover { border-color: var(--accent); }

button.big {
  font-size: 18px;
  padding: 22px 36px;
  border-radius: 14px;
}

button.danger:hover { border-color: var(--bad); }

button.mini {
  font-size: 11px;
  padding: 2px 10px;
  border-radius: 6px;
  color: var(--dim);
  margin-left: 8px;
}

.error { color: var(--bad); }

/* ---- session ---- */

#session {
  justify-content: flex-start;
  align-items: stretch;
  gap: 14px;
  box-sizing: border-box;
  min-height: 0;
  height: 100%;
  max-height: 100%;
  /* Scroll vertically when the video + controls exceed the viewport (portrait
     phones especially), so controls below the video stay reachable. Combined
     and fullscreen layouts override this back to hidden since they fill the
     viewport exactly by design. */
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
body.combined #session,
body.fake-fullscreen #session {
  overflow: hidden;
}

/* Xbox Edge / TV: extra inset so the stream isn't chewed by panel overscan. */
body.xbox-watch #session,
body.combined.xbox-watch #session {
  --overscan: max(3.5vmin, 16px);
  padding-top: max(var(--overscan), env(safe-area-inset-top, 0px)) !important;
  padding-right: max(var(--overscan), env(safe-area-inset-right, 0px)) !important;
  padding-bottom: max(var(--overscan), env(safe-area-inset-bottom, 0px)) !important;
  padding-left: max(var(--overscan), env(safe-area-inset-left, 0px)) !important;
}

#statusBar {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--dim);
  font-size: 14px;
}

#statusDot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--wait);
}

#statusDot[data-state="good"] { background: var(--good); }
#statusDot[data-state="bad"] { background: var(--bad); }
#statusDot[data-state="waiting"] { background: var(--wait); }

#videoWrap {
  position: relative;
  width: 100%;
  max-width: min(1400px, 100%);
  margin-inline: auto;
  flex: 1 1 auto;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 0;
  gap: 6px;
  /* dvh avoids console/browser chrome making 70vh taller than the visible area. */
  height: min(70dvh, 70vh);
  max-height: min(70dvh, 70vh);
  overflow: hidden; /* dual-pane only; single-pane overrides below */
}

/* Each pane is independently sized (px from JS). Drag grips on edges. */
.video-pane {
  position: relative;
  flex: 0 0 auto;
  min-width: 80px;
  min-height: 80px;
  overflow: hidden;
  background: #000;
  border-radius: 8px;
  border: 1px solid var(--border);
}

.video-pane video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  background: #000;
  object-fit: contain;
  object-position: center;
  border: 0;
  border-radius: 0;
}

/* Single-pane: CSS-fill the wrap. JS pixel sizes + overflow:hidden were
   racing resizes and shearing the right/bottom edge for a few seconds. */
#videoWrap:not(.has-camera) {
  overflow: visible;
}
#videoWrap:not(.has-camera) .screen-pane {
  flex: 1 1 auto;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  overflow: visible;
  background: transparent;
  border-color: transparent;
}
#videoWrap:not(.has-camera) .screen-pane video {
  border-radius: 8px;
}

#videoWrap:not(.has-camera) .camera-pane {
  display: none;
}

/* Fullscreen is requested on #videoWrap. Without this, max-height:70dvh
   stays in effect: JS sizes the pane to the monitor, overflow:hidden crops it. */
#videoWrap:fullscreen,
#videoWrap:-webkit-full-screen,
body.combined #videoWrap:fullscreen,
body.combined #videoWrap:-webkit-full-screen {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: #000;
  overflow: hidden;
}
#videoWrap:fullscreen .video-pane,
#videoWrap:fullscreen .video-pane video,
#videoWrap:-webkit-full-screen .video-pane,
#videoWrap:-webkit-full-screen .video-pane video {
  border-radius: 0;
}
#remoteVideo:fullscreen,
#remoteVideo:-webkit-full-screen {
  position: static;
  inset: auto;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
  border-radius: 0;
}

/* iPhone has no Fullscreen API on arbitrary elements. This fills the viewport
   and hides chrome so Fullscreen still does something in Safari / the PWA. */
body.fake-fullscreen #videoWrap,
body.combined.fake-fullscreen #videoWrap {
  position: fixed;
  inset: 0;
  z-index: 80;
  width: 100dvw;
  max-width: none;
  height: 100dvh;
  max-height: none;
  margin: 0;
  border-radius: 0;
  overflow: hidden;
  background: #000;
}
body.fake-fullscreen #statusBar,
body.fake-fullscreen #viewerPanel,
body.fake-fullscreen #sharerPanel,
body.fake-fullscreen #layoutSwitcher,
body.combined.fake-fullscreen #chat {
  display: none !important;
}

#inboundKeepAudio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

#resumeCapture {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 24px;
  background: rgba(14, 15, 18, 0.86);
  text-align: center;
}
#resumeCapture[hidden] { display: none !important; }
#resumeCapture p { margin: 0; max-width: 22rem; color: var(--text); }

/* Dual-pane: dock only sets row/column order; sizes come from the user. */
#videoWrap.has-camera {
  flex: 1 1 auto;
}

#videoWrap.has-camera[data-camera-dock="right"],
#videoWrap.has-camera[data-camera-dock="left"] {
  flex-direction: row;
}
#videoWrap.has-camera[data-camera-dock="top"],
#videoWrap.has-camera[data-camera-dock="bottom"] {
  flex-direction: column;
}

#videoWrap.has-camera .screen-pane { order: 2; }
#videoWrap.has-camera .camera-pane { order: 3; }
#videoWrap.has-camera[data-camera-dock="left"] .camera-pane { order: 1; }
#videoWrap.has-camera[data-camera-dock="left"] .screen-pane { order: 2; }
#videoWrap.has-camera[data-camera-dock="right"] .screen-pane { order: 1; }
#videoWrap.has-camera[data-camera-dock="right"] .camera-pane { order: 2; }
#videoWrap.has-camera[data-camera-dock="top"] .camera-pane { order: 1; }
#videoWrap.has-camera[data-camera-dock="top"] .screen-pane { order: 2; }
#videoWrap.has-camera[data-camera-dock="bottom"] .screen-pane { order: 1; }
#videoWrap.has-camera[data-camera-dock="bottom"] .camera-pane { order: 2; }

/* Camera-only: hide screen; camera keeps its own size. */
#videoWrap.has-camera.camera-only {
  flex-direction: row;
}
#videoWrap.has-camera.camera-only .screen-pane {
  position: absolute;
  width: 1px !important;
  height: 1px !important;
  margin: 0;
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
}

.pane-grip {
  position: absolute;
  z-index: 4;
  touch-action: none;
  user-select: none;
}
.pane-grip-e {
  top: 0;
  right: 0;
  width: 10px;
  height: calc(100% - 14px);
  cursor: ew-resize;
}
.pane-grip-s {
  left: 0;
  bottom: 0;
  width: calc(100% - 14px);
  height: 10px;
  cursor: ns-resize;
}
.pane-grip-se {
  right: 0;
  bottom: 0;
  width: 18px;
  height: 18px;
  cursor: nwse-resize;
}
.pane-grip-se::after {
  content: '';
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--dim);
  border-bottom: 2px solid var(--dim);
  opacity: 0.85;
}
.video-pane:hover .pane-grip-e,
.video-pane:hover .pane-grip-s {
  background: rgba(79, 140, 255, 0.12);
}
body.resizing-pane {
  cursor: grabbing;
  user-select: none;
}

#stats {
  position: absolute;
  top: 10px;
  right: 10px;
  margin: 0;
  padding: 8px 10px;
  font: 12px/1.5 ui-monospace, Consolas, monospace;
  color: var(--text);
  background: rgba(14, 15, 18, 0.85);
  border: 1px solid var(--border);
  border-radius: 6px;
  pointer-events: none;
}

/* ---- combined watch + chat layout ---- */

#layoutSwitcher {
  position: fixed;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  background: rgba(14, 15, 18, 0.92);
  border: 1px solid var(--border);
  border-radius: 999px;
}

/* Reserve space under the fixed layout bar so it never covers session/chat chrome. */
body.combined,
body.layout-bar {
  padding-top: 48px;
  box-sizing: border-box;
}

#layoutSwitcher .lsLabel {
  font-size: 11px;
  color: var(--dim);
  letter-spacing: 1px;
  margin-right: 2px;
}

#layoutSwitcher button { margin-left: 0; }
#layoutSwitcher button.active { color: var(--text); border-color: var(--accent); }

/* When both views are shown, body becomes the flex container and the two
   full-screen <main>s share the viewport. #landing is [hidden] so it drops
   out of flow. The chat pane size is user-adjustable via the drag handle,
   stored in --chat-w (row layouts) and --chat-h (stacked layout). */
body.combined {
  display: flex;
  align-items: stretch;
  height: 100%;
  max-height: 100%;
  height: 100dvh;
  max-height: 100dvh;
  overflow: hidden;
  --chat-w: 32%;
  --chat-h: 42%;
}

/* Stretch into the content box under the 48px layout bar — do not use
   height:100% here; that resolves against the border box and clips ~48px. */
body.combined > main {
  min-width: 0;
  min-height: 0;
  height: auto;
  max-height: none;
  padding: 4px;
  align-self: stretch;
}

/* The screen pane takes whatever the chat pane doesn't. */
body.combined #session {
  flex: 1 1 0;
  position: relative;
  gap: 6px;
  height: auto;
  max-height: none;
  min-height: 0;
  padding: 0;
}
body.combined #chat { flex: 0 0 var(--chat-w); gap: 6px; }

/* Chat fills its pane instead of centering a narrow column with big margins. */
body.combined #chat { justify-content: flex-start; height: auto; min-height: 0; }
body.combined #chatRoom {
  max-width: none;
  flex: 1 1 0;
  height: auto;
  max-height: none;
}

/* Combined layout: wrap fills leftover session space under the status bar. */
body.combined #videoWrap {
  max-width: none;
  width: 100%;
  flex: 1 1 0;
  height: auto;
  max-height: none;
  min-height: 0;
}
body.combined #videoWrap.has-camera {
  flex: 1 1 0;
  height: auto;
  max-height: none;
  min-height: 0;
}

/* Drag handle between the two panes. */
#paneResizer {
  flex: 0 0 6px;
  align-self: stretch;
  background: var(--border);
  border-radius: 3px;
  cursor: col-resize;
  order: 2;
}
#paneResizer:hover { background: var(--accent); }
body.combined.layout-bottom #paneResizer { cursor: row-resize; width: 100%; }

/* Orders place the resizer between the two panes for each arrangement. */
body.combined.layout-right #session { order: 1; }
body.combined.layout-right #chat { order: 3; }
body.combined.layout-left #chat { order: 1; }
body.combined.layout-left #session { order: 3; }

/* Stacked: screen on top, chat below, resizer between. */
body.combined.layout-bottom { flex-direction: column; }
body.combined.layout-bottom #session { order: 1; flex: 1 1 0; width: 100%; }
body.combined.layout-bottom #paneResizer { order: 2; }
body.combined.layout-bottom #chat { order: 3; flex: 0 0 var(--chat-h); width: 100%; }

/* In combined mode the quality controls become an on-demand dropdown anchored
   to the screen pane so they never cover or shrink the video. */
body.combined #viewerPanel {
  position: absolute;
  top: 34px;
  left: 6px;
  right: 6px;
  z-index: 20;
  display: none;
  max-height: 78%;
  overflow: auto;
  padding: 12px;
  background: rgba(14, 15, 18, 0.96);
  border: 1px solid var(--border);
  border-radius: 10px;
}
body.combined #session.showControls #viewerPanel { display: flex; }

/* ---- panels ---- */

#sharerPanel, #viewerPanel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

/* Camera-share: compact top chrome only; PiP floats above everything. */
#session.camera-share {
  position: relative;
  gap: 4px;
  justify-content: flex-start;
}
#session.camera-share #statusBar {
  flex: 0 0 auto;
  padding: 2px 0;
}
#session.camera-share #sharerPanel {
  flex: 0 0 auto;
  gap: 4px;
  width: 100%;
  max-width: none;
  align-items: stretch;
  box-sizing: border-box;
  /* Fallback reserve for the floating PiP; JS sets an exact padding-right. */
  padding-right: calc(min(132px, 28vw) + 20px);
}
#session.camera-share #sharerControls {
  gap: 6px;
  justify-content: flex-start;
}
#session.camera-share #sharerControls button {
  font-size: 12px;
  padding: 4px 10px;
}
#session.camera-share #videoWrap {
  display: none;
}

/* Large local cam: sit in the session like a normal stream, under the status bar. */
body.local-cam-large #session.camera-share {
  justify-content: flex-start;
}
body.local-cam-large #session.camera-share #statusBar { order: 1; }
body.local-cam-large #session.camera-share #localCameraPreviewWrap { order: 2; }
body.local-cam-large #session.camera-share #sharerPanel { order: 3; }
body.local-cam-large #session.camera-share #resumeCapture { order: 4; }

/* Mic-share: same compact top chrome as camera-share, no video / PiP. */
#session.mic-share {
  position: relative;
  gap: 4px;
  justify-content: flex-start;
}
#session.mic-share #statusBar {
  flex: 0 0 auto;
  padding: 2px 0;
}
#session.mic-share #sharerPanel {
  flex: 0 0 auto;
  gap: 4px;
  width: 100%;
  max-width: none;
  align-items: stretch;
  box-sizing: border-box;
  padding-right: 0;
}
#session.mic-share #sharerControls {
  gap: 6px;
  justify-content: flex-start;
}
#session.mic-share #sharerControls button {
  font-size: 12px;
  padding: 4px 10px;
}
#session.mic-share #videoWrap,
#session.mic-watch #videoWrap {
  display: none;
}

/* Floating PiP — fixed overlay; JS sets width/height to the camera aspect. */
#localCameraPreviewWrap {
  position: fixed;
  top: 56px;
  right: 12px;
  width: min(132px, 28vw);
  z-index: 60;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: #000;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
  pointer-events: auto;
  cursor: pointer;
}
#localCameraPreviewWrap video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
  pointer-events: none;
}

/* Optional large preview — same box as a normal stream. Watcher is unchanged. */
body.local-cam-large #localCameraPreviewWrap {
  position: relative;
  top: auto !important;
  right: auto;
  left: auto;
  width: 100% !important;
  max-width: min(1400px, 100%);
  height: min(70dvh, 70vh) !important;
  max-height: min(70dvh, 70vh);
  margin-inline: auto;
  z-index: 2;
  border-radius: 8px;
  box-shadow: none;
  cursor: default;
  flex: 1 1 auto;
  min-height: 0;
}
body.combined.local-cam-large #localCameraPreviewWrap {
  width: 100% !important;
  max-width: none;
  height: auto !important;
  max-height: none;
  flex: 1 1 0;
}
#localCameraPreviewWrap:fullscreen,
#localCameraPreviewWrap:-webkit-full-screen,
body.fake-fullscreen #localCameraPreviewWrap {
  position: fixed;
  inset: 0;
  top: 0 !important;
  right: 0;
  width: 100% !important;
  max-width: none;
  height: 100% !important;
  max-height: none;
  margin: 0;
  border: 0;
  border-radius: 0;
  z-index: 80;
}
body.fake-fullscreen #localCameraPreviewWrap {
  width: 100dvw !important;
  height: 100dvh !important;
}
body.combined #localCameraPreviewWrap,
body.layout-bar #localCameraPreviewWrap {
  top: 56px; /* sits under the layout switcher */
}

/* Settings chrome clears the PiP; message list / media grid can sit under it. */
body.has-camera-pip {
  --pip-reserve: calc(min(132px, 28vw) + 20px);
}
body.has-camera-pip #chatHeader,
body.has-camera-pip #chatSettings,
body.has-camera-pip #chatSearchBar,
body.has-camera-pip #chatMedia .chatPanelBar {
  padding-right: max(12px, var(--pip-reserve)) !important;
  box-sizing: border-box;
}

/*
 * Camera-share + chat: controls stay a thin top strip; chat fills the rest.
 * PiP overlays on top. No side-by-side split that wastes half the screen.
 */
body.combined.camera-share-dock,
body.combined.mic-share-dock {
  flex-direction: column;
}
body.combined.camera-share-dock #session,
body.combined.mic-share-dock #session {
  order: 1;
  flex: 0 0 auto;
  width: 100%;
  height: auto;
  max-height: none;
}
body.combined.camera-share-dock #chat,
body.combined.mic-share-dock #chat {
  order: 3;
  flex: 1 1 0;
  width: 100%;
  min-height: 0;
}
body.combined.camera-share-dock #paneResizer,
body.combined.mic-share-dock #paneResizer {
  display: none;
}

/* Large local cam + chat: use the normal split so the preview can fill like a stream. */
body.combined.local-cam-large.camera-share-dock {
  flex-direction: row;
}
body.combined.local-cam-large.camera-share-dock.layout-bottom {
  flex-direction: column;
}
body.combined.local-cam-large.camera-share-dock #session {
  flex: 1 1 0;
  height: auto;
  min-height: 0;
}
body.combined.local-cam-large.camera-share-dock #chat {
  flex: 0 0 var(--chat-w);
}
body.combined.local-cam-large.camera-share-dock.layout-bottom #chat {
  flex: 0 0 var(--chat-h);
  width: 100%;
}
body.combined.local-cam-large.camera-share-dock #paneResizer {
  display: block;
}
body.combined.local-cam-large #session.camera-share #sharerPanel {
  padding-right: 0;
}

/* Camera/mic-share keep their control strip visible in combined mode (not a dropdown). */
body.combined #session.camera-share #sharerPanel,
body.combined #session.mic-share #sharerPanel {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  display: flex;
  z-index: 2;
  max-height: none;
  overflow: visible;
  padding: 0;
  background: transparent;
  border: 0;
}
body.combined #session.camera-share #btnControls,
body.combined #session.mic-share #btnControls {
  display: none;
}

.hint {
  color: var(--dim);
  font-size: 14px;
  margin: 0;
  text-align: center;
}

.controls {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}

.controls label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--dim);
}

select {
  font: inherit;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
}

.bitrate { min-width: 180px; }

input[type="range"] { accent-color: var(--accent); }

/* ---- chat ---- */

#chat {
  justify-content: center;
  gap: 16px;
}

#chatGate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 320px;
}

#chatGate h1 {
  font-size: 20px;
  font-weight: 500;
  letter-spacing: 4px;
  color: var(--dim);
  margin: 0;
}

input[type="text"], input[type="password"], input:not([type]), textarea {
  font: inherit;
  /* 16px minimum: iOS Safari zooms the page when focusing a field under that. */
  font-size: 16px;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
}

/* Give the chat view a definite height so #chatMessages (flex:1; min-height:0)
   becomes the scroll container instead of growing the page. */
#chat { height: 100%; min-height: 0; }

#chatGate input { width: 100%; }

#chatRoom {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 760px;
  height: 100%;
  max-height: 100%;
  flex: 1;
  min-height: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  overflow: hidden;
}

#chatHeader {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  color: var(--text);
}

@media (max-width: 480px) {
  #chatHeader { gap: 6px; padding: 8px; }
  #chatHeader .mini { padding: 6px 8px; }
}

#chatHeader .spacer { flex: 1; }

/*
 * Buttons whose label changes while they work. Without a locked box, the
 * longer in-progress text widens them and shoves every button after it (the
 * header wraps, so the whole row can jump). Clip instead of growing.
 */
#chatRefresh,
#chatNotifyToggle {
  flex: 0 0 auto;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#chatRefresh { width: 6em; }
#chatNotifyToggle { width: 9em; }

#chatStatusDot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--wait);
}
#chatStatusDot[data-state="good"] { background: var(--good); }
#chatStatusDot[data-state="bad"] { background: var(--bad); }

#chatSettings {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

#chatSettings label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--dim);
}

#chatSettings label.chatToggleRow {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding-bottom: 2px;
  cursor: pointer;
  user-select: none;
}
#chatSettings label.chatToggleRow input {
  width: auto;
  margin: 0;
}

/* High-contrast scheme for reading chat outdoors in sunlight. */
body.chat-outside {
  /* Multiplier for outside-mode text, driven by the settings slider. 1 keeps
     the sizes below; the slider raises it for reading at arm's length. */
  --outside-font-scale: 1;
  --bg: #f2f4f6;
  --panel: #ffffff;
  --border: #1c1c1c;
  --text: #0a0a0a;
  --dim: #2a2a2a;
  --accent: #0046c8;
  --good: #00e83a;
  --bad: #b00000;
  --wait: #7a5600;
  background: var(--bg);
  color: var(--text);
}
body.chat-outside .avatarDot.online {
  color: #00ff4a;
  filter: drop-shadow(0 0 2px #00ff4a) drop-shadow(0 0 1px #003d12);
}
body.chat-outside #chatStatusDot[data-state="good"] {
  background: #00ff4a;
  box-shadow: 0 0 6px #00ff4a, 0 0 0 1px #003d12;
}
body.chat-outside #chat {
  font-size: calc(16px * var(--outside-font-scale));
}
body.chat-outside #chatHeader,
body.chat-outside #chatSettings,
body.chat-outside #chatComposer,
body.chat-outside #chatUploadBar,
body.chat-outside #chatSearchBar,
body.chat-outside #chatMedia .chatPanelBar {
  background: var(--panel);
  border-color: var(--border);
}
body.chat-outside #chatInput,
body.chat-outside #chatPass,
body.chat-outside #chatUser,
body.chat-outside #chatUserEdit,
body.chat-outside #chatSearchInput {
  background: #fff;
  color: var(--text);
  border: 1px solid var(--border);
}
body.chat-outside button {
  color: var(--text);
}
body.chat-outside button.mini,
body.chat-outside #chatComposer button {
  border-color: var(--border);
  background: #e8eaee;
}
body.chat-outside .msg .body {
  color: var(--text);
}
body.chat-outside .msg .msgBody {
  font-size: calc(18px * var(--outside-font-scale));
  line-height: 1.45;
}

/* Maximum legibility when iOS dims the screen on its own (thermal throttling or
   Attention Aware Features). We cannot raise brightness from a web page, so the
   fallback is pure-black-on-pure-white and heavier strokes, which stay readable
   several brightness steps down. */
body.chat-outside {
  --bg: #ffffff;
  --text: #000000;
  --dim: #1a1a1a;
}
body.chat-outside .msg .msgBody,
body.chat-outside .msg .body {
  font-weight: 600;
  color: #000;
}
body.chat-outside #chatInput {
  font-weight: 600;
}

/* The font-size slider is only meaningful in outside mode, so it stays hidden
   until that theme is on. */
#chatFontScaleRow {
  display: none;
}
body.chat-outside #chatFontScaleRow {
  display: flex;
  align-items: center;
  gap: 8px;
}
#chatFontScaleRow input[type="range"] {
  flex: 1;
  min-width: 90px;
}
#chatFontScaleVal {
  min-width: 3ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Outside mode also means "probably hot and on battery": drop decorative
   animation so the GPU does less work, which reduces the thermal dimming that
   prompted this in the first place. */
body.chat-outside *,
body.chat-outside *::before,
body.chat-outside *::after {
  animation: none !important;
}

/* 180° screen flip, for reading the site with the phone physically upside down
   (iPhones never auto-rotate to portrait-upside-down). Purely local: the
   outgoing camera track is untouched, so this never changes what they see.
   180° is the safe rotation - the rotated box is identical to the original, so
   nothing clips and position:fixed children stay inside the viewport. Safe here
   specifically because html/body are locked to 100% height with internal
   scrolling, so the page itself never scrolls. */
body.flip180 {
  transform: rotate(180deg);
  transform-origin: 50% 50%;
}

#chatMessagesWrap {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
}

#chatMessages {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

#chatJumpLatest {
  position: absolute;
  right: 16px;
  bottom: 12px;
  font-size: 12px;
  padding: 6px 12px;
  border-radius: 16px;
  background: var(--accent);
  color: #fff;
  border: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}

.msg {
  display: flex;
  flex-direction: column;
  padding: 2px 0;
  max-width: 100%;
  position: relative;
}

.msg .msgReply {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--dim);
  margin: 6px 0 0 5px;
  cursor: pointer;
  max-width: 90%;
  overflow: hidden;
  padding: 4px 8px 4px 0;
  border-radius: 4px;
}
.msg .msgReply:hover { background: rgba(255, 255, 255, 0.04); }
.msg .msgReplyBar {
  display: none;
}
/* Anchored to .msgReply itself (not .msg), so the connector's length is fixed
   regardless of how tall the message body/image/reactions below it are. It
   runs from near the bottom of the reply avatar down to the top of the main
   avatar in .msgHead, which immediately follows .msgReply in the DOM. */
.msg.hasReply .msgReply::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 12px;
  height: 30px;
  width: 13px;
  border-left: 2px solid var(--dim);
  border-bottom: 2px solid var(--dim);
  border-bottom-left-radius: 8px;
  opacity: .55;
  pointer-events: none;
}
.msg .msgReplyAvatar {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}
.msg .msgReplyAvatarFallback {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-weight: 700;
  color: #fff;
  flex: none;
  text-transform: uppercase;
}
.msg .msgReplyBody {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.msg .msgReplyUser {
  color: var(--text);
  font-weight: 600;
  flex: none;
}
.msg .msgReplySnippet {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.msg .msgHead {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}

.msg.grouped .msgHead { display: none; }
.msg.grouped .msgBody,
.msg.grouped .msgImg,
.msg.grouped .msgVideo,
.msg.grouped .msgVideoWrap,
.msg.grouped .msgReactions { margin-left: 34px; }

.msgAvatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}
.msgAvatarFallback {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  flex: none;
  text-transform: uppercase;
}

.msg .msgUser { font-weight: 600; font-size: 14px; color: var(--text); }
.msg.mine .msgUser { color: var(--accent); }
.msg .msgTime { font-size: 11px; color: var(--dim); }
.msg .msgEdited { font-size: 11px; color: var(--dim); font-style: italic; }

/* Live presence: true 45° circular stroke outside the pfp (top-right).
   Avatar r=13; 1px gap → inner 14; 3px thick → outer 17; midline r=15.5. */
.msgAvatarWrap {
  position: relative;
  width: 26px;
  height: 26px;
  min-width: 26px;
  min-height: 26px;
  max-width: 26px;
  max-height: 26px;
  flex: none;
  overflow: visible;
  /* Size ignores the overflowing 34px ring so message rows stay 26px tall. */
  contain: size;
}
.avatarDot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  pointer-events: none;
  background: none;
  border: none;
  color: var(--dim);
}
.avatarDot svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.avatarDot.online { color: var(--good); }
.avatarDot.offline { color: var(--dim); }

.msgActions { display: none; gap: 12px; margin-left: 34px; margin-top: 2px; }
.msg.actionsOpen .msgActions { display: flex; }
.msgActions button {
  font-size: 12px;
  padding: 4px 8px;
  color: var(--dim);
  background: none;
  border: none;
}
.msgActions button:hover { color: var(--text); }

.msg.highlight { background: rgba(79, 140, 255, 0.15); border-radius: 6px; }
.msg.composeTarget { background: rgba(255, 255, 255, 0.07); border-radius: 6px; }

.msg .msgBody {
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
  margin-left: 34px;
}

.msg .msgImg {
  margin-top: 4px;
  max-width: 320px;
  max-height: 320px;
  border-radius: 8px;
  border: 1px solid var(--border);
  cursor: pointer;
  width: auto;
  height: auto;
  object-fit: contain;
}

.msg .msgVideoWrap {
  position: relative;
  margin-top: 4px;
  max-width: min(480px, 100%);
  max-height: 360px;
  min-height: 120px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: #000;
  overflow: hidden;
}
.msg .msgVideo {
  display: block;
  max-width: 100%;
  max-height: 360px;
  width: auto;
  height: auto;
  background: #000;
}
/* Cover frame standing in for the clip: nothing else says "clickable". */
.msg img.msgVideo { cursor: pointer; object-fit: contain; }
.msg .msgVideoStatus {
  position: absolute;
  inset: 0;
  margin: auto;
  width: fit-content;
  height: fit-content;
  max-width: 90%;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 8px;
  padding: 8px 12px;
  background: rgba(0, 0, 0, 0.72);
  color: #fff;
  font: inherit;
  cursor: pointer;
}
.msg .msgVideoStatus:disabled {
  opacity: 0.7;
  cursor: default;
}
.msg .msgUploadOverlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  pointer-events: none;
}
.msg .msgUploadOverlay .chatUploadPie {
  width: 44px;
  height: 44px;
}
.msg .msgUploadLabel {
  font-size: 13px;
  text-align: center;
  padding: 0 10px;
}
.msg .msgVideoWrap.is-failed .msgUploadOverlay {
  background: rgba(80, 0, 0, 0.55);
}

.msgReactions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 5px 0 0 34px;
}
.reactionPill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 28px;
  padding: 3px 8px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: rgba(255,255,255,.04);
  color: var(--text);
}
.reactionPill.mine { border-color: var(--accent); background: rgba(79,140,255,.16); }
.reactionPill img { width: 20px; height: 20px; object-fit: contain; }
.reactionPicker {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 4px 0 0 34px;
  padding: 8px;
  width: min(320px, calc(100% - 42px));
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  z-index: 20;
}
.reactionNativeRow { display: flex; gap: 6px; }
.reactionNativeRow input { flex: 1; min-width: 0; font-size: 16px; }
.reactionCustomGrid { display: flex; flex-wrap: wrap; gap: 4px; }
.reactionCustomGrid button { padding: 4px; min-width: 32px; background: transparent; border: 0; font-size: 20px; }
.reactionCustomGrid img { width: 24px; height: 24px; object-fit: contain; }

/* ---- tags ---- */

.msgTags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 5px 0 0 34px;
}
.msg.grouped .msgTags { margin-left: 34px; }
.tagPill {
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: rgba(255,255,255,.04);
  color: var(--dim);
  font-size: 11px;
}
.tagPicker {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 4px 0 0 34px;
  padding: 8px;
  width: min(320px, calc(100% - 42px));
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  z-index: 20;
}
/* Opened from a media tile, where there is no message row to sit under. */
.tagPicker.floating {
  position: fixed;
  margin: 0;
  width: min(320px, calc(100vw - 16px));
  z-index: 10001;
}
.tagNewRow { display: flex; gap: 6px; }
.tagNewRow input { flex: 1; min-width: 0; font-size: 16px; }
.tagKnownGrid { display: flex; flex-wrap: wrap; gap: 4px; }
.tagOption {
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  font-size: 12px;
}
.tagOption.on { border-color: var(--accent); background: rgba(79,140,255,.16); }

#chatMediaTagLabel,
#chatSearchTagLabel {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--dim);
  white-space: nowrap;
  flex: 0 0 auto;
}
#chatMediaTag,
#chatSearchTag {
  max-width: 9em;
  padding: 3px 4px;
  font-size: 12px;
}

.unreadDivider {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--bad);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin: 8px 0;
}
.unreadDivider::before, .unreadDivider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--bad);
  opacity: 0.5;
}

#typingIndicator {
  padding: 2px 14px 6px;
  font-size: 12px;
  font-style: italic;
  color: var(--dim);
  min-height: 0;
}

#chatReplyBanner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--dim);
}
#chatReplyText {
  flex: 1;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

#chatSearch {
  display: flex;
  flex-direction: column;
  border-bottom: 1px solid var(--border);
  max-height: 50%;
}
#chatSearchBar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
}
#chatSearchBar input { flex: 1; }
#chatSearchMinWordsLabel {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--dim);
  white-space: nowrap;
  flex: 0 0 auto;
}
#chatSearchMinWords {
  width: 3.2em;
  flex: 0 0 auto;
  padding: 4px 6px;
  font-size: 12px;
}
#chatSearchCount { font-size: 12px; color: var(--dim); }
#chatSearchResults { overflow-y: auto; padding: 0 8px 8px; }
.searchResult {
  padding: 8px 10px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 13px;
}
.searchResult:hover { background: var(--bg); }
.searchResult .srHead { display: flex; gap: 8px; align-items: baseline; margin-bottom: 2px; }
.searchResult .srUser { font-weight: 600; color: var(--text); }
.searchResult .srTime { font-size: 11px; color: var(--dim); }
.searchResult .srBody { color: var(--dim); white-space: pre-wrap; word-break: break-word; }
.searchResult mark { background: var(--wait); color: #000; border-radius: 2px; }

#chatMedia {
  display: flex;
  flex-direction: column;
  border-bottom: 1px solid var(--border);
  max-height: 78%;
}
.chatPanelBar { display: flex; align-items: center; gap: 8px; padding: 8px 12px; }
.chatPanelBar .spacer { flex: 1; }
.chatMediaHead { flex-shrink: 0; }
#chatMediaPage { font-size: 12px; color: var(--dim); }
.chatMediaTools {
  flex-wrap: wrap;
  border-top: 1px solid var(--border);
  padding-top: 6px;
  padding-bottom: 6px;
}
#chatMediaGrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px;
  padding: 8px;
  overflow-y: auto;
}
.mediaTile { position: relative; min-width: 0; border-radius: 8px; overflow: hidden; background: var(--bg); }
.mediaPlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 999px;
  background: rgba(0,0,0,.45);
  color: #fff;
  font-size: 15px;
  pointer-events: none;
}
.mediaTile > img, .mediaTile > video {
  display: block;
  width: 100%;
  height: auto;
  object-fit: contain;
  object-position: center;
  background: #0a0b0e;
  cursor: pointer;
}
.mediaTile > img { aspect-ratio: 4 / 3; }
/* A video tile uses the clip's own shape. A forced 4:3 box paints a portrait
   video on the left and leaves the rest of the tile black. */
.mediaTile > video {
  aspect-ratio: auto;
  background: #000;
  pointer-events: none;
}
.mediaTile > video::-webkit-media-controls-start-playback-button,
.mediaTile > video::-webkit-media-controls-overlay-play-button {
  display: none !important;
  -webkit-appearance: none;
}
.mediaVideoStatus {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 26px;
  z-index: 1;
  text-align: center;
  font-size: 11px;
  color: #fff;
  background: rgba(0,0,0,.55);
  border-radius: 6px;
  padding: 4px 6px;
  pointer-events: none;
}
.mediaTileMeta { padding: 5px 6px; color: var(--dim); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mediaDelete { position: absolute; top: 5px; right: 5px; padding: 4px 6px; font-size: 10px; }

@media (max-width: 640px) {
  #chatMediaGrid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  #chatMedia { max-height: 82%; }
  #chatMediaGrid { grid-template-columns: 1fr 1fr; }
  .mediaTile > img { aspect-ratio: 1 / 1; }
  .chatMediaHead, .chatMediaTools { padding: 8px; }
}

#mediaLightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: 100dvw;
  max-height: 100dvh;
  overflow: hidden;
  z-index: 10000;
  background: rgba(0,0,0,.92);
  touch-action: none;
}
#mediaLightboxStage {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: calc(56px + env(safe-area-inset-top, 0px)) 12px calc(12px + env(safe-area-inset-bottom, 0px));
}
#mediaLightboxStage img, #mediaLightboxStage video {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transform-origin: center;
  user-select: none;
}
#mediaLightboxClose {
  position: absolute;
  z-index: 2;
  top: max(8px, env(safe-area-inset-top, 0px));
  right: max(8px, env(safe-area-inset-right, 0px));
  width: 44px;
  height: 44px;
  padding: 0;
  font-size: 30px;
  line-height: 1;
  background: rgba(0,0,0,.6);
  border: 0;
  color: #fff;
}

#chatUploadBar {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 8px 12px;
  border-top: 1px solid var(--border);
}
#chatUploadBar[hidden] { display: none !important; }
.chatUploadCopy { min-width: 0; }
.chatUploadCopy span {
  display: block;
  font-size: 12px;
  opacity: 0.7;
  margin-top: 2px;
}
.chatUploadPie {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: conic-gradient(var(--accent) calc(var(--pct, 0) * 1%), rgba(127, 127, 127, 0.35) 0);
}

#chatComposer {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  min-width: 0;
}

#chatComposer #chatInput {
  flex: 1 1 auto;
  min-width: 0;
  resize: none;
  overflow-y: auto;
  /* ~3 lines of text; autoGrowInput grows up to this. */
  max-height: calc(1.35em * 3 + 16px);
  line-height: 1.35;
  field-sizing: content;
}

/* Buttons keep their size; the input shrinks to fit so nothing runs off the
 * edge on narrow phones. On very small screens tighten padding further. */
#chatComposer button { flex: 0 0 auto; white-space: nowrap; }

@media (max-width: 480px) {
  #chatComposer { gap: 4px; padding: 8px; }
  #chatComposer button { padding: 8px 8px; font-size: 13px; }
  #chatAttach { padding: 8px 10px; }
}

#chatAttach {
  font-size: 18px;
  line-height: 1;
  padding: 8px 14px;
}

/* Confetti overlay: full-viewport rain of a custom emoji (or 🎉 fallback). */
.confettiLayer {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 9999;
}
.confettiPiece {
  position: absolute;
  top: -8vh;
  will-change: transform;
  animation-name: confettiFall;
  animation-timing-function: linear;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}
@keyframes confettiFall {
  0% { transform: translateY(-10vh) translateX(0) rotate(0deg); opacity: 1; }
  90% { opacity: 1; }
  100% {
    transform: translateY(115vh) translateX(var(--drift, 0)) rotate(var(--spin, 360deg));
    opacity: 0.9;
  }
}

/* Custom emoji: inline in messages + picker panel above the composer. */
.customEmoji {
  height: 1.4em;
  width: auto;
  vertical-align: -0.3em;
  margin: 0 1px;
}

#chatConfetti { display: flex; align-items: center; justify-content: center; }
.confettiBtnImg { height: 22px; width: auto; display: block; }

#chatEmojiPanel {
  border-top: 1px solid var(--border);
  background: var(--panel);
  padding: 10px 12px;
  max-height: 220px;
  overflow-y: auto;
}
#chatEmojiGrid {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.emojiEmpty { color: var(--dim); font-size: 13px; padding: 4px 0; }
.emojiPick {
  padding: 4px;
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  line-height: 0;
}
.emojiPick:hover { border-color: var(--border); background: var(--bg); }
.emojiPick img { width: 28px; height: 28px; object-fit: contain; }
#chatEmojiAdd { margin-top: 10px; }

/* "Server unreachable" heartbeat banner (shown after a sustained socket drop). */
#chatOfflineBanner {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 9999;
  max-width: 90vw;
  padding: 8px 16px;
  border-radius: 999px;
  background: #b3261e;
  color: #fff;
  font-size: 13px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
#chatOfflineBanner.show { opacity: 1; }
