: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;
}

.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%;
  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;
}

/* 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;
}

/* 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: none;
}
#localCameraPreviewWrap video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
}
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;
}
/* 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; }

#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 {
  --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: 16px;
}
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: 18px;
  line-height: 1.45;
}

#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;
}
.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; }

.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: 70%;
}
.chatPanelBar { display: flex; align-items: center; gap: 8px; padding: 8px 12px; }
.chatPanelBar .spacer { flex: 1; }
#chatMediaGrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 8px;
  padding: 8px;
  overflow-y: auto;
}
.mediaTile { position: relative; min-width: 0; border-radius: 8px; overflow: hidden; background: var(--bg); }
.mediaTile > img, .mediaTile > video {
  display: block;
  width: 100%;
  height: 130px;
  object-fit: cover;
  cursor: pointer;
}
.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; }

#mediaLightbox {
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: rgba(0,0,0,.92);
  touch-action: none;
}
#mediaLightboxStage { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; overflow: hidden; }
#mediaLightboxStage img, #mediaLightboxStage video {
  max-width: 95vw;
  max-height: 92vh;
  object-fit: contain;
  transform-origin: center;
  user-select: none;
}
#mediaLightboxClose {
  position: absolute;
  z-index: 2;
  top: max(12px, env(safe-area-inset-top));
  right: 16px;
  font-size: 30px;
  background: rgba(0,0,0,.6);
  border: 0;
}

#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; }
