/* =========================================================================
   Workmates — HUD / overlay styling (retro pixel arcade look)
   The Phaser canvas sits full-screen behind everything; all DOM UI here is
   an overlay layer (pointer-events selectively re-enabled per widget).
   ========================================================================= */

:root {
  --bg: #0f1410;
  --panel: #1a221b;
  --panel-2: #222d24;
  --ink: #e8ecf5;
  --muted: #f2f2f2;
  --accent: #ffd23f;
  --accent-2: #35d0a5;
  --danger: #ff5a5a;
  --warn: #ff9f40;
  --border: #324235;
  --chrome-shadow: #0a0e0b;
  --chrome-input: #0d120e;
  --chrome-video: #060806;
  --pixel: 'Google Sans Code', Helvetica, Arial, sans-serif;
  --term: 'Google Sans Code', Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--term);
  font-size: 12.6px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
}

input, textarea {
  -webkit-user-select: text;
  -moz-user-select: text;
  user-select: text;
}

button { font-family: var(--pixel); cursor: pointer; text-transform: uppercase; }
/* Everything clickable gets the hand cursor — real links/buttons and
   whatever's inside them, form controls you click, and the elements the
   code wires up as clickable via data attributes. Disabled → not-allowed. */
a[href], a[href] *, button, button *, select, summary, label[for],
input[type="checkbox"], input[type="radio"], input[type="color"], input[type="range"], input[type="file"],
[role="button"], [data-close], [data-act], [data-tab], [data-place], [data-del-msg], [data-edit], [data-del] { cursor: pointer; }
button:disabled, button:disabled *, [aria-disabled="true"] { cursor: not-allowed; }
canvas { image-rendering: pixelated; }
p { padding: 4px 0 8px; margin: 0; line-height: 1.3; }

#game-root { position: fixed; inset: 0; z-index: 0; }
#game-root canvas { display: block; width: 100%; height: 100%; }
/* The small copy of the map session replay records (see startReplayMirror in
   game.js) — directly behind the real canvas, so it's never seen in play. */
#game-root canvas { position: relative; z-index: 1; }
/* Session replay (game.js's startReplayMirror): a small copy of the map,
   behind the game canvas, stretched to fill the same area. */
#game-root canvas.replay-mirror { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* The real game canvas is excluded from replays (ph-no-capture), which plays
   back as a grey striped placeholder covering the copy above. A live canvas
   always has a width attribute; the replay's placeholder doesn't — so this
   only ever hides the placeholder, letting the copy show through. */
#game-root canvas.ph-no-capture:not([width]) { display: none !important; }

/* ---------- Overlays / panels ------------------------------------------- */

.overlay {
  background: #000;
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}

#room-overlay, #avatar-overlay, #permission-modal,
#avatar-modal, #credits-modal, #object-modal, #agenda-modal, #agenda-view-modal, #online-modal,
#connect4-modal, #wheelfortune-modal, #wf-rules-modal,
#clubradio-modal, #priceisright-modal, #familiarfeud-modal, #ff-rules-modal, #game-join-modal {
  background: rgba(6, 8, 14, 0.78);
  backdrop-filter: blur(2px);
}
/* Above the game show it's about (see the markup in index.html). */
#game-join-modal { z-index: 60; }
/* A help modal opened from on top of another one (Wheel of Fortune's rules,
   over the table itself) needs its own darker wash so it still reads as a
   distinct layer, not just more of the same dim. */
#wf-rules-modal { background: rgba(6, 8, 14, 0.9); }

.panel {
  background: var(--panel);
  border: 3px solid var(--border);
  box-shadow: 0 0 0 3px var(--chrome-shadow), 8px 8px 0 rgba(0,0,0,0.5);
  padding: 22px;
  max-width: 460px;
  width: 100%;
  max-height: 95vh;
  overflow: auto;
}

.entry-panel { max-width: 480px; }
/* Even vertical rhythm for the entry / lobby modals. */
.entry-panel { display: flex; flex-direction: column; gap: 12px; }
.entry-panel > * { margin: 0; }
.entry-panel form { display: flex; flex-direction: column; gap: 10px; }
.entry-panel form label { margin: 0; }
.entry-panel h4 { margin: 4px 0 0; color: #fff; }

#entry-room-id { color: #fff; }

.logo {
  font-family: var(--pixel);
  font-weight: 700;
  font-size: 1.6667rem;
  letter-spacing: 2px;
  color: var(--accent);
  text-shadow: 3px 3px 0 var(--chrome-shadow);
}
/* The Workmates wordmark on the opening screens (create a space / join),
   in place of the old text heading — the website's own logo, in the app's
   yellow (public/img/logo.svg). */
.logo .entry-logo { display: block; height: 24px; width: auto; max-width: 100%; }

h1 { text-transform: uppercase; }
h1, h4, legend, .stage-dock-label { font-family: var(--pixel); }
h4 { font-size: 0.9444rem; color: #fff; padding: 16px 0 6px; }
.tab-pane h4 { border-top: 1px dashed var(--border); margin-top: 8px; }
.tab-pane h4:first-of-type { border-top: none; margin-top: 0; }
hr {
  border: none; border-bottom: 2px solid var(--border); padding: 12px 0;
}

.muted { color: var(--muted); line-height: 1.6; }
.tiny  { font-size: 0.8889rem; }
.hint  { color: var(--accent-2); min-height: 1.2em; font-size: 0.8333rem; }
.error { color: var(--danger); font-size: 0.8333rem; }

/* ---------- Forms ------------------------------------------------------- */
form label,
.avatar-controls label {
  display: block;
  margin: 10px 0;
  font-family: var(--term);
  font-size: 0.8889rem;
  color: var(--muted);
}

input[type=text], input[type=password], input[type=email], input[type=url],
input[type=number], input[type=search], input[type=date], input[type=time],
textarea, select {
  width: 100%;
  margin-top: 6px;
  padding: 8px 14px;
  background: var(--chrome-input);
  color: var(--ink);
  border: 2px solid var(--border);
  font-family: var(--term);
  font-size: 0.9444rem;
  line-height: 1.2;
}

select {
  appearance: none; -webkit-appearance: none;
  padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath fill='%238a93a8' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
}

/* Colour fields: a dropdown-styled button showing the colour, opening a
   grid of preset swatches (ui.js openSwatchPop). */
.swatch-select {
  display: flex; align-items: center; gap: 10px;
  width: 100%; margin-top: 6px; padding: 8px 36px 8px 14px;
  background: var(--chrome-input) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath fill='%238a93a8' d='M1 1l5 5 5-5'/%3E%3C/svg%3E") no-repeat right 13px center;
  color: var(--ink); border: 2px solid var(--border);
  font-family: var(--term); font-size: 0.9444rem; line-height: 1.2;
  text-align: left; cursor: pointer;
  text-transform: none; letter-spacing: normal; font-weight: normal;
}
.swatch-chip {
  flex: none; width: 16px; height: 16px; margin: -4px 0;   /* (doesn't grow the line — same height as a select) */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
.swatch-pop {
  position: fixed; z-index: 100000;
  display: grid; grid-template-columns: repeat(6, 28px); gap: 6px;
  padding: 10px;
  background: var(--chrome-input); border: 2px solid var(--border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
.swatch-pop[hidden] { display: none; }
.swatch {
  width: 28px; height: 28px; padding: 0; cursor: pointer;
  border: 0; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
}
.swatch:hover { outline: 2px solid var(--muted); outline-offset: 1px; }
.swatch.on { outline: 2px solid var(--accent); outline-offset: 2px; }
.swatch:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

input[type=color] {
  width: 100%; height: 42px; margin-top: 6px; padding: 2px;
  background: var(--chrome-input); border: 2px solid var(--border);
}

textarea { resize: vertical; }

.field-label {
  display: block; margin: 0;
  font-family: var(--term); font-size: 0.8889rem; color: var(--muted);
}
input:focus, select:focus, button:focus-visible, a.btn-primary:focus-visible,
a.btn-secondary:focus-visible, a.btn-tertiary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* ---------- Buttons — one size, three emphasis tiers -------------------- */
.btn-primary, .btn-secondary, .btn-tertiary {
  display: inline-block;
  padding: 8px 22px;
  font-family: var(--pixel);
  font-weight: 700;
  font-size: 0.9444rem;
  line-height: 1.2;
  text-transform: uppercase;
  text-align: center;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  background: none;
  color: var(--accent);
  border: 2px solid transparent;
  border-radius: 4px;
}
.btn-primary   { background: var(--accent); border-color: var(--accent); color: #1a1400; }
.btn-secondary { border-color: var(--accent); }

.btn-primary:hover { filter: brightness(1.08); }
/* Outline buttons fill with a wash of their own colour (yellow, red, amber). */
.btn-secondary:hover:not(:disabled):not(.active) { background: color-mix(in srgb, currentColor 14%, transparent); }
.btn-tertiary:hover { background: var(--panel-2); }
.btn-primary:active, .btn-secondary:active { transform: translateY(2px); }
.btn-secondary.active { background: var(--warn); border-color: var(--warn); color: #2a1400; }
.btn-tertiary:active { transform: translateY(1px); }

/* Semantic colour — combine with any tier. */
.btn-primary.danger { background: var(--danger); border-color: var(--danger); color: #2a0000; }
.btn-primary.warn   { background: var(--warn);   border-color: var(--warn);   color: #2a1400; }
.btn-secondary.danger, .btn-tertiary.danger { border-color: var(--danger); color: var(--danger); }
.btn-secondary.warn,   .btn-tertiary.warn   { border-color: var(--warn);   color: var(--warn); }
.btn-primary.danger, .btn-primary.warn { box-shadow: none; }

.btn-primary:disabled, .btn-secondary:disabled, .btn-tertiary:disabled,
.btn-primary[disabled], .btn-secondary[disabled], .btn-tertiary[disabled] {
  opacity: 0.45; cursor: not-allowed; filter: none; transform: none;
}

/* ---------- Entry / lobby modals ------------------------------------- */
.entry-cta { justify-content: flex-end; align-items: stretch; margin-top: 6px; flex-wrap: wrap; }
.entry-cta .btn-primary,
.entry-cta .btn-secondary,
.entry-cta .btn-tertiary,
.entry-cta a { flex: 0 0 auto; min-width: 132px; }

/* Three choices (Not now / Mic only / Cam & mic) need to share one row. */
#permission-modal .entry-cta { justify-content: flex-end; gap: 6px; }
#permission-modal .entry-cta button { min-width: 0; padding-left: 14px; padding-right: 14px; }

/* "Art & licences" sits apart from the action buttons. */
.credits-btn { margin-right: auto; min-width: 0 !important; }
.credits-list { list-style: none; margin: 4px 0 0; padding: 0; max-height: 46vh; overflow-y: auto; }
.credits-list li { padding: 5px 0; border-bottom: 1px dashed var(--border); font-size: 0.8889rem; }
.credits-list li:last-child { border-bottom: none; }
.rules-list { list-style: none; margin: 4px 0 0; padding: 0; }
.rules-list li { padding: 8px 0; border-bottom: 1px dashed var(--border); font-size: 0.8889rem; line-height: 1.4; }
.rules-list li:last-child { border-bottom: none; }

/* Readable text links inside panels (browser-default blue is invisible on dark). */
.panel p a, .credits-list a {
  color: var(--accent-2); text-decoration: underline; text-underline-offset: 2px;
}
.panel p a:hover, .credits-list a:hover { color: var(--ink); }

/* ---------- Avatar editor --------------------------------------------- */
.avatar-editor { border: 2px solid var(--border); padding: 12px; margin-top: 10px; }
.avatar-editor legend { font-size: 0.8333rem; color: var(--accent); padding: 0 6px; }
.avatar-preview-wrap {
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  background: var(--chrome-input); border: 2px solid var(--border);
  padding: 6px; margin-bottom: 10px;
}
.avatar-rand-link {
  background: none; border: none; margin-top: 2px; padding: 2px 4px;
  color: var(--accent); font-family: var(--term); font-size: 0.8333rem;
  text-transform: none; cursor: pointer; text-decoration: underline;
}
.avatar-rand-link:hover { color: var(--ink); }

#avatar-preview, #avatar-preview-2 { image-rendering: pixelated; display: block; }
.avatar-controls {
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px;
}

/* Preview on the left, customisation in 3 columns on the right. */
.avatar-wide { max-width: 820px; }
.avatar-layout { display: flex; gap: 16px; align-items: flex-start; margin-top: 4px; }
.avatar-layout .avatar-preview-wrap { flex: 0 0 auto; margin-bottom: 0; position: sticky; top: 0; }
.avatar-layout .avatar-controls {
  flex: 1 1 auto; grid-template-columns: repeat(3, 1fr); gap: 6px 12px;
}

.avatar-layout .avatar-preview-col {
  flex: 0 0 190px; position: sticky; top: 0;
  display: flex; flex-direction: column; gap: 16px;
}
.avatar-layout .avatar-preview-col .avatar-preview-wrap { position: static; }
/* Nickname field — same label + box style as the dropdowns on the right. */
.avatar-name-field {
  display: block; margin: 8px 0;
  font-family: var(--term); font-size: 0.8889rem; color: var(--muted);
}
.controls-list { display: flex; flex-direction: column; gap: 4px; margin: 0; }
.controls-list > div { display: flex; align-items: baseline; gap: 8px; }
.controls-list dt {
  font-family: var(--pixel); font-size: 0.7778rem; color: var(--ink);
  background: var(--panel-2); border: 2px solid var(--border); box-shadow: 0 2px 0 var(--chrome-shadow);
  padding: 2px 7px; min-width: 58px; text-align: center; white-space: nowrap;
}
.controls-list dd { margin: 0; color: var(--muted); font-size: 0.7778rem; }

.row-buttons { display: flex; gap: 8px; align-items: flex-end; margin-top: 12px; flex-wrap: wrap; }
/* Avatar modal actions — pinned bottom-right, not stretched. */
.avatar-actions { justify-content: flex-end; }
.avatar-actions .btn-primary,
.avatar-actions .btn-secondary,
.avatar-actions .btn-tertiary { flex: 0 0 auto; }

/* Raised-hand roster, top-right */
.hand-list {
  position: absolute; top: 10px; right: 10px; z-index: 22; pointer-events: none;
  background: var(--panel); border: 2px solid var(--accent-2);
  box-shadow: 0 0 0 3px var(--chrome-shadow);
  padding: 8px 12px; min-width: 150px; max-width: 240px;
}
.hand-list .hl-head { font-family: var(--pixel); font-size: 0.7778rem; color: var(--accent-2); margin-bottom: 5px; }
.hand-list .hl-name { font-family: var(--term); font-size: 0.8889rem; line-height: 1.3; color: var(--ink); }
/* nudge the right-hand sidebars down so the hand list has room */
body.has-hands #chat-sidebar,
body.has-hands #admin-sidebar { top: 120px; }

/* ---------- HUD ------------------------------------------------------- */
#hud { position: fixed; inset: 0; z-index: 23; pointer-events: none; }
#hud .host-only { display: none; }
#hud.is-host .host-only { display: flex; }

/* Toolbar tooltip — one bubble, placed above the hovered button (ui.js
   wireActionTips); --arrow-x points the arrow at the button's centre. */
.ab-tip {
  position: fixed; z-index: 10000; pointer-events: none;   /* above windows, modals and screen shares */
  max-width: 220px; padding: 8px 12px; text-wrap: balance;   /* no one-word last lines */
  background: var(--chrome-shadow); color: var(--ink);
  border: 2px solid var(--border); border-radius: 6px;
  font-family: var(--term); font-size: 0.8333rem; line-height: 1.35; text-align: center;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}
.ab-tip::after {
  content: ''; position: absolute; top: 100%; left: var(--arrow-x, 50%);
  transform: translateX(-50%);
  border: 7px solid transparent; border-top-color: var(--border);
}
.ab-tip[hidden] { display: none; }
/* Shown underneath its control (no room above): the arrow points up. */
.ab-tip.below::after { top: auto; bottom: 100%; border-top-color: transparent; border-bottom-color: var(--border); }

/* Bottom action bar */
.action-bar {
  position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
  display: flex; gap: 4px; align-items: stretch;
  background: var(--panel); border: 3px solid var(--border);
  box-shadow: 0 0 0 3px var(--chrome-shadow);
  padding: 6px; pointer-events: auto;
  max-width: 96vw; overflow-x: auto;
  border-radius: 6px;
}
.ab-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  width: 56px; height: 56px; padding: 6px 4px;
  background: var(--panel-2); border: 2px solid var(--border);
  color: var(--ink); font-size: 0.8889rem; line-height: 1;
  border-radius: 4px;
}
.ab-btn .ab-label { font-size: 0.8333rem; font-weight: 700; text-transform: uppercase; font-family: var(--term); color: var(--muted); }
.ab-btn:hover { background: var(--border); }
a.ab-btn { text-decoration: none; }
/* Chunky low-res emoji so they sit with the pixel styling — the emoji is
   redrawn onto a small canvas by pixelateEmoji() and upscaled with hard pixels. */
/* Shout's one-second arming hold (see wireShoutHold in ui.js): the button
   fills from the bottom while held, and goes live when it's full. */
.ab-btn[data-act="spotlight"] { position: relative; overflow: hidden; }
.ab-btn[data-act="spotlight"]::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 0;
  background: var(--accent-2); opacity: 0.35; pointer-events: none;
}
.ab-btn[data-act="spotlight"].charging::before { animation: shout-charge 1s linear forwards; }
@keyframes shout-charge { from { height: 0; } to { height: 100%; } }
.ab-emoji { display: inline-flex; line-height: 1; padding-bottom: 4px; }
.ab-emoji canvas { display: block; image-rendering: pixelated; }
/* A darker green ring around the green fill, so "on" buttons show the same
   ring-and-fill edge as the dark ones instead of reading as a bigger block. */
.ab-btn.active { background: var(--accent-2); border-color: #23987a; color: #06251d; }
.ab-btn.active .ab-label {color: #06251d; }
.ab-btn.danger { background: var(--danger); border-color: var(--danger); color: #2a0000; }
.ab-btn:disabled { opacity: 0.4; cursor: not-allowed; }
/* Shown but not usable yet (e.g. Share before taking a podium) — still
   clickable so it can explain why, unlike :disabled. */
.ab-btn.unavailable { opacity: 0.4; }
.ab-btn.unavailable:hover { background: var(--panel-2); }
.ab-sep { display: block; width: 2px; background: var(--border); margin: 4px 3px; }

/* Interaction prompt */
.interact-prompt {
  position: absolute; left: 50%; bottom: 96px; transform: translateX(-50%);
  background: var(--accent); color: #1a1400;
  font-family: var(--pixel); font-size: 1rem;
  padding: 8px 12px; border: 2px solid #1a1400;
  pointer-events: none; white-space: nowrap;
}

/* Toast */
.toast {
  background: var(--panel); border: 2px solid var(--accent);
  color: var(--ink); font-family: var(--pixel); font-size: 1rem;
  padding: 10px 14px; pointer-events: none; max-width: 90vw; text-align: center;
}

.toast a { pointer-events: auto; color: var(--accent-2); text-decoration: underline; cursor: pointer; }

/* FIFA-style goal celebration — full-screen flash + a huge bouncing banner. */
.goal-banner {
  position: absolute; inset: 0; z-index: 300; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  background: #0F2B23; transition: background 0.5s ease-out;
}
.goal-banner.show { background: rgba(15, 43, 35, 0.5); }
.goal-banner-text {
  font-family: var(--pixel); font-weight: bold; letter-spacing: 3px;
  font-size: clamp(.6667rem, 10vw, 6.6667rem); color: #ffd23f;
  text-shadow: 0 8px 0 #7a1c10, 0 0 40px rgba(255, 140, 20, 0.85), 0 0 80px rgba(255, 210, 63, 0.5);
  background: rgba(15, 43, 35, 0.9); border-radius: 0.3em; padding: 0.35em 0.7em;
  transform: scale(0); opacity: 0;
  text-transform: uppercase;
}

.goal-banner.show .goal-banner-text { animation: goal-pop 2s cubic-bezier(.16, 1, .3, 1) forwards; }
@keyframes goal-pop {
  0%   { transform: scale(0); opacity: 0; }
  8%   { opacity: 1; }
  42%  { transform: scale(1.08); opacity: 1; }
  55%  { transform: scale(1); opacity: 1; animation-timing-function: ease-in; }
  85%  { transform: scale(1); opacity: 1; }
  100% { transform: scale(1); opacity: 0; }
}

/* Stage video dock */
.stage-dock {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  background: var(--panel); border: 3px solid var(--accent);
  box-shadow: 0 0 0 3px var(--chrome-shadow);
  padding: 6px; pointer-events: auto; max-width: 92vw;
}
.stage-dock-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.stage-dock-label { font-size: 0.7222rem; color: var(--accent); }
.stage-tiles { display: flex; flex-wrap: nowrap; gap: 4px; overflow-x: auto; scrollbar-width: thin; scrollbar-color: var(--accent) transparent; }
.stage-tiles::-webkit-scrollbar { height: 6px; }
.stage-tiles::-webkit-scrollbar-thumb { background: var(--accent); }
.stage-tiles::-webkit-scrollbar-track { background: transparent; }
.stage-tiles .tile {
  position: relative; width: 160px; height: 90px; flex: 0 0 auto;
  background: var(--chrome-video); border: 2px solid var(--border); overflow: hidden;
  transition: opacity 0.3s ease;   /* ambient camera tiles fade in/out by distance */
}
.stage-tiles .tile.screen { width: 240px; }
/* video and portrait fully overlap (rather than one being display:none)
   so the <video> keeps decoding incoming frames in the background even
   while the portrait covers it — hiding a <video> via display:none can
   stall frame decoding in some browsers, turning "wait for the first
   frame" into a deadlock that never resolves. */
.stage-tiles video, .dock-cam-portrait { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: var(--chrome-video); }
.stage-tiles .dock-cam-portrait[hidden] { display: none; }
.stage-tiles .cap {
  position: absolute; left: 0; bottom: 0; right: 0;
  display: flex; align-items: center; gap: 3px;
  font-family: var(--pixel); font-size: 0.6667rem; background: rgba(0,0,0,0.7);
  padding: 2px 4px; color: var(--ink);
}
.dock-mic-icon { display: inline-flex; flex: 0 0 auto; color: var(--ink); }
.dock-mic-icon.muted { color: var(--danger); }
.stage-tiles .muted-dot { position: absolute; top: 3px; right: 3px; font-size: 0.6667rem; }

/* Sidebars */
.sidebar {
  position: absolute; top: 10px; right: 10px; bottom: 92px; width: 390px; max-width: calc(100vw - 20px);
  background: var(--panel); border: 3px solid var(--border);
  box-shadow: 0 0 0 3px var(--chrome-shadow);
  display: flex; flex-direction: column; pointer-events: auto;
  overflow: hidden;
}
.sidebar header {
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; row-gap: 4px;
  font-family: var(--pixel); font-weight: 700; font-size: 1.1111rem; color: var(--accent);
  text-transform: uppercase;
  padding: 10px 12px;
}
/* Chat header: "3 people online" link between the title and ✕. */
.sidebar header .chat-online {
  margin-left: auto; margin-right: 12px; font-family: var(--term); font-weight: 400;
  font-size: 0.7778rem; text-transform: none; color: var(--accent-2);
  transition: color 0.15s ease;
  display: inline-flex; align-items: center; gap: 6px;
}
/* A slow pulsing "live" dot — the club radio's (.clubradio-live-dot), a touch smaller. */
.sidebar header .chat-online::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--accent-2); flex-shrink: 0;
  box-shadow: 0 0 6px var(--accent-2); animation: clubradio-pulse 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .sidebar header .chat-online::before { animation: none; } }
.sidebar header .chat-online:hover, .sidebar header .chat-online:focus-visible { color: var(--ink); }
.online-list { list-style: none; margin: 8px 0 0; padding: 0; max-height: 60vh; overflow-y: auto; }
.online-list li { padding: 8px 0; border-bottom: 1px dashed var(--border); }
.online-list li:last-child { border-bottom: none; }
/* Give the first control breathing room below the header. */
.sidebar .admin-block { padding: 16px 12px 12px; }
.sidebar .x, .object-header .x {
  background: transparent; border: none; color: var(--muted);
  font-size: 1rem; line-height: 1; font-family: var(--term);
}
.sidebar h4:first-of-type { margin-top: 6px; }
.sidebar > *:not(header) { padding: 0 10px; }
.sidebar ol, .sidebar ul { overflow-y: auto; }

/* ---- Manage Space: tabbed, single-scroll body --------------------------- */
.tab-bar { display: flex; gap: 4px; margin: 4px 0 10px; border-bottom: 2px solid var(--border); }
.tab-btn {
  flex: 1; padding: 9px 0px; cursor: pointer;
  margin-bottom: -2px;
  background: transparent; border: none; border-bottom: 2px solid transparent;
  font-family: var(--pixel); font-weight: 700; font-size: 0.8333rem; text-transform: uppercase;
  color: var(--muted);
}
.tab-btn:hover:not(.active) { color: var(--ink); border-bottom-color: color-mix(in srgb, var(--ink) 35%, transparent); }
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }

#admin-sidebar .admin-scroll { flex: 1; overflow-y: auto; padding-top: 12px; padding-bottom: 16px; }
#admin-sidebar .admin-scroll ul { overflow: visible; }
/* Tight gap within a section; a bigger margin above each heading pushes the
   sections apart so a title reads as belonging to the content right below it. */
#admin-sidebar .tab-pane { display: flex; flex-direction: column; gap: 7px; }
#admin-sidebar .tab-pane[hidden] { display: none; }
#admin-sidebar .tab-pane h4 { padding: 16px 0 0 0; margin: 16px 0 0 0; }
#admin-sidebar .tab-pane > :first-child { margin-top: 0; }
#admin-sidebar .tab-pane .admin-block { padding: 0; }
#admin-sidebar .tab-pane .user-list,
#admin-sidebar .tab-pane .object-list { padding: 0; margin: 0; }

/* Any "field + button" row: the field must have no margin of its own, or
   align-items: stretch can't make it exactly match the button's height. */
.admin-linkrow { display: flex; gap: 6px; align-items: stretch; }
.admin-linkrow code {
  flex: 1; min-width: 0; font-family: var(--term); font-size: 0.8889rem;
  background: var(--chrome-input); border: 2px solid var(--border);
  padding: 9px 12px; overflow-x: auto; white-space: nowrap;
}
.admin-linkrow input { flex: 1; min-width: 0; margin: 0; }
.admin-linkrow .btn-secondary { flex: 0 0 auto; }

/* ---------- Map theme picker (Customise tab) ----------------------- */
.theme-picker { display: flex; flex-wrap: wrap; gap: 6px; }
.theme-swatch {
  display: flex; align-items: center; gap: 6px;
  background: var(--chrome-input); border: 2px solid var(--border); color: var(--muted);
  font-family: var(--term); font-size: 0.8333rem; text-transform: none;
  padding: 6px 10px; cursor: pointer;
}
.theme-swatch:hover { color: var(--ink); border-color: var(--muted); }
.theme-swatch.active { color: var(--ink); border-color: var(--accent); }
.theme-dot { flex: none; width: 14px; height: 14px; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25); }

/* ---------- Room chat ----------------------------------------------- */
.chat-log { list-style: none; margin: 8px 0; padding: 0 10px; flex: 1; }
.chat-log li { position: relative; margin: 4px 0 12px; padding-right: 20px; font-size: 0.8889rem; line-height: 1.5; word-break: break-word; transition: background-color 0.4s ease; border-radius: 4px; }
/* Each message is a Teams-style card: name and time across the top, the
   message below, reactions underneath. Your own get an accent bar. */
.chat-log li.msg {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px;
  padding: 8px 10px; margin: 0 0 26px;   /* room below for the reaction pills hanging off it */
}
.chat-log li.msg.me { border-left: 3px solid var(--accent); }
.chat-log .c-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 8px; padding-right: 20px; margin-bottom: 2px; }
.chat-log .c-head .c-name, .chat-log .c-head .c-time { margin-right: 0; }
/* Hover tools, top right: delete (your own messages). */
.chat-log .c-tools {
  position: absolute; top: 4px; right: 4px; display: flex; align-items: center; gap: 2px;
  opacity: 0; transition: opacity 0.1s;
}
.chat-log li:hover .c-tools, .chat-log li:focus-within .c-tools, .chat-log li.hosts-only .c-tools { opacity: 1; }   /* the hosting tips' ✕ always shows */
.chat-log .c-del {
  background: none; border: none; color: var(--muted); font-family: var(--term);
  font-size: 0.7778rem; line-height: 1; padding: 3px; border-radius: 4px; cursor: pointer;
  display: inline-flex; align-items: center;
}
.chat-log .c-del:hover { color: var(--danger); }
/* (the react button itself is styled with the pills below) */
/* Reaction chips under a message — yours outlined in the accent colour. */
/* Like Teams: the pills hang off the bottom edge of the message, half in
   and half out — each reaction (with a count once there's more than one),
   then the round react button. */
.chat-log .c-reacts { position: relative; z-index: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 8px 0 -24px; }
.chat-log .c-react, .chat-log .c-react-add {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  height: 28px; min-width: 0; padding: 0 7px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 15px;
  color: var(--ink); font-family: var(--term); font-size: 0.7778rem; line-height: 1; cursor: pointer;
}
.chat-log .c-react { font-size: 1.05rem; gap: 4px; }
.chat-log .c-react span { font-size: 0.7778rem; }
.chat-log .c-react-add { color: var(--muted); padding: 0 7px; }
.chat-log .c-react:hover, .chat-log .c-react-add:hover { border-color: var(--muted); color: var(--ink); }
.chat-log .c-react.mine { border-color: var(--accent-2); background: linear-gradient(rgba(53, 208, 165, 0.14), rgba(53, 208, 165, 0.14)), var(--panel); }   /* tint over a solid fill, so the card's edge doesn't show through */
/* Posted images and GIFs, shown inline (a link to the full size). */
.chat-log a.c-img-link { display: inline-block; margin-top: 4px; line-height: 0; }
.chat-log img.c-img { max-width: 100%; max-height: 220px; border-radius: 4px; display: block; }
/* Matebot's hosting tips (hosts and co-hosts only). */
.chat-log .c-hosts-only { display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 4px; background: rgba(255, 255, 255, 0.08); color: var(--muted); font-family: var(--term); font-size: 0.6667rem; vertical-align: middle; }
.chat-log ul.c-tips { margin: 6px 0 0; padding-left: 18px; }
.chat-log ul.c-tips li { margin: 0 0 4px; padding: 0; border: none; background: none; }
/* Matebot's welcome GIF, under the first welcome message. */
.chat-log img.c-welcome { width: 100%; height: auto; margin-top: 8px; }
/* The shared emoji picker opened from a message's react button. */
.react-picker {
  position: fixed; z-index: 9000; display: flex; gap: 2px; padding: 4px;
  background: var(--chrome-shadow); border: 2px solid var(--border); border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}
.react-picker[hidden] { display: none; }
.react-picker button {
  background: none; border: none; font-size: 1.2rem; line-height: 1; padding: 4px; border-radius: 6px; cursor: pointer;
  transition: transform 0.1s ease, background-color 0.1s ease;
}
.react-picker button:hover { background: rgba(255, 255, 255, 0.1); transform: scale(1.15); }
.chat-log .c-name { color: var(--accent-2); font-family: var(--pixel); font-weight: 700; font-size: 0.8889rem; margin-right: 6px; }
.chat-log li.me .c-name { color: var(--accent); }
.chat-log li.bot { background: rgba(255, 210, 63, 0.04); }
.chat-log li.bot .c-name { color: #ffd23f; }
/* The host's @everyone: a tinted row like Matebot's, the mention in the accent colour. */
.chat-log li.everyone { background: rgba(255, 210, 63, 0.08); }
.chat-log .c-mention { color: #ffd23f; font-weight: 700; }
.chat-log li.bot strong { color: var(--accent-2); }
.chat-log .c-time { color: var(--muted); font-family: var(--term); font-size: 0.6667rem; margin-right: 6px; }
.chat-log a { color: var(--accent); word-break: break-all; transition: color 0.15s ease; }
.chat-log a:hover, .chat-log a:focus-visible { color: var(--ink); }
/* Matebot links are words, not long URLs, so wrap them between words. */
.chat-log .bot a { word-break: normal; }
/* New messages that arrived while chat was closed, until it's opened
   (markChatRead removes this the same moment it clears the ab-count badge). */
.chat-log li.unread { background: rgba(255, 255, 255, 0.08); }
.chat-log a.matebot-agenda-link { font-weight: bold; word-break: normal; }
/* ---------- Icebreaker poll (top of chat) -------------------------- */
.sidebar .poll-box {
  list-style: none; margin: 4px 0 12px; padding: 12px 12px 14px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 2px solid var(--border); border-radius: 4px;
}
.poll-top { display: flex; align-items: center; justify-content: space-between; }
.poll-tag {
  font-family: var(--pixel); font-weight: 700; font-size: 0.8333rem;
  text-transform: uppercase; color: var(--accent-2);
}
.poll-tools { display: flex; gap: 4px; }
.poll-mini {
  background: transparent; border: 2px solid var(--border); color: var(--muted);
  font-family: var(--term); font-size: 0.7778rem; line-height: 1; padding: 3px 7px;
  border-radius: 3px; cursor: pointer;
}
.poll-mini:hover { color: var(--ink); border-color: var(--accent-2); }
.poll-q { margin: 6px 0 10px; padding: 0; color: var(--muted); font-size: 0.8889rem; }
.poll-opts { display: flex; flex-direction: column; gap: 8px; }
.poll-opt {
  display: block; width: 100%; text-align: left; cursor: pointer;
  font-family: var(--term); font-size: 0.8889rem; color: var(--ink); text-transform: none;
  background: var(--chrome-input); border: 2px solid var(--border); border-left-width: 5px;
  padding: 10px 12px; border-radius: 3px; transition: transform .08s, border-color .12s, background .12s;
}
.poll-opts .poll-opt:nth-child(1) { border-left-color: var(--accent); }
.poll-opts .poll-opt:nth-child(2) { border-left-color: var(--accent-2); }
.poll-opt:hover { background: #151b16; transform: translateX(2px); }

.poll-results { margin-top: 10px; }
.poll-res { padding: 2px 0; }
.poll-res + .poll-res { margin-top: 10px; }
.poll-res-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.poll-res-label { flex: 1; min-width: 0; font-size: 0.8889rem; color: var(--ink); }
.poll-res-pct { flex: 0 0 auto; white-space: nowrap; font-family: var(--pixel); font-size: 0.8889rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.poll-bar {
  margin: 5px 0 3px; height: 14px; background: var(--chrome-input);
  border: 2px solid var(--border); border-radius: 3px; overflow: hidden;
}
.poll-bar-fill {
  display: block; height: 100%; min-width: 2px;
  background-image: repeating-linear-gradient(45deg, rgba(255,255,255,.14) 0 6px, transparent 6px 12px);
  background-color: var(--accent);
  transition: width .6s cubic-bezier(.22, 1, .36, 1);
}
.poll-bar-b { background-color: var(--accent-2); }
.poll-res-count { font-family: var(--term); font-size: 0.8889rem; color: var(--muted); }
.poll-mine-tag { color: var(--accent-2); white-space: nowrap; }
.poll-res.is-mine .poll-bar { border-color: var(--accent-2); }
.poll-res.is-lead .poll-res-pct { color: var(--ink); font-weight: 700; }
.poll-foot { margin: 10px 0 0; padding: 0; font-size: 0.8889rem; color: var(--muted); }

.sidebar .chat-form { display: flex; gap: 6px; align-items: stretch; padding: 8px 10px 12px; border-top: 2px solid var(--border); }
.sidebar .chat-form input { flex: 1; margin: 0; min-width: 0; }
.sidebar .chat-form .btn-primary { flex: 0 0 auto; padding: 9px 16px; }
.ab-dot {
  position: absolute; top: 2px; right: 2px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--danger); border: 1px solid #2a0000;
}
.ab-count {
  position: absolute; top: -4px; right: -4px;
  background: var(--danger); color: #2a0000;
  font-family: var(--pixel); font-size: 0.6111rem;
  padding: 2px 3px; border: 1px solid #2a0000; border-radius: 2px;
}
.ab-btn { position: relative; }
/* A button carrying a notification dot/count gets a lighter red outline
   than the badge itself, so the darker badge still pops against it. */
.ab-btn:has(.ab-dot), .ab-btn:has(.ab-count) { border-color: #ff9d9d; }

/* ---------- Auditorium-only controls + status pills ---------------- */
#hud .aud-only { display: none; }
#hud.in-auditorium .aud-only { display: flex; }
/* Status pills + toasts all stack vertically in one place instead of each
   claiming the same top-center spot — see #hud-top-stack. */
#hud-top-stack {
  position: absolute; left: 50%; top: 12px; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  z-index: 26; pointer-events: none;
}
.mode-pill {
  background: var(--panel); border: 2px solid var(--accent-2); color: var(--ink);
  font-family: var(--pixel); font-size: 1rem; padding: 7px 12px; pointer-events: none;
  white-space: nowrap;
}
.mode-pill.live { border-color: var(--danger); color: var(--danger); }
.follow-pill { pointer-events: auto; border-color: var(--accent); }
.follow-pill a { color: var(--accent); }
.context-menu .cm-sep { height: 1px; background: var(--border); margin: 4px 0; }
.context-menu button:disabled { opacity: 0.4; cursor: not-allowed; }
.context-menu button:disabled:hover { background: transparent; color: inherit; }

/* "Broadcast to everyone" — persistent while a host is on the PA. */
.announce-pill {
  background: var(--panel); border: 2px solid var(--warn); color: var(--warn);
  font-family: var(--pixel); font-size: 1rem; padding: 7px 12px; pointer-events: none;
  white-space: nowrap;
}

/* ---------- Collaborative-file window (FigJam / Figma / Miro) ------- */
/* Opened from a chat link shared in the main hall or a side room (ui.js
   openCollab). Laid out exactly like a screen share (see the takeover rules
   below): below the camera dock, above the toolbar, clear of the open chat;
   your own camera bubble steps aside the same way. Dragging/resizing pins
   it with inline styles, which win over these rules. */
.collab-window {
  position: fixed; z-index: 41;
  top: 150px; bottom: 96px; left: 14px; right: 14px;
  background: var(--panel); border: 3px solid var(--accent-2);
  box-shadow: 0 0 0 3px var(--chrome-shadow), 6px 6px 0 rgba(0,0,0,0.5);
  display: flex; flex-direction: column; pointer-events: auto;
}
.collab-window[hidden] { display: none; }
body.chat-open .collab-window:not(.minimized) { right: 416px; }
body.collab-open:not(.collab-minimized):not(.im-presenting) .self-cam { display: none; }
.collab-window iframe { flex: 1; width: 100%; min-height: 0; border: 0; background: #fff; }
.collab-window .sw-bar { color: var(--accent-2); }
.collab-window .sw-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-right: 10px; }
.collab-window .open-tab-btn { font-size: 0.7222rem; padding: 5px 10px; margin-right: 6px; }
.cw-min, .cw-close { background: transparent; border: none; color: var(--muted); font-size: 0.8889rem; font-family: var(--term); cursor: pointer; }
.cw-close:hover { color: var(--danger); }
/* Minimised: a small live thumbnail in the bottom-right corner, like the
   screen share's — still draggable and resizable; click it to restore.
   The iframe stays loaded, so you don't lose your place. */
.collab-window.minimized { top: auto; left: auto; bottom: 96px; right: 16px; width: 280px; height: 170px; }
.collab-window.minimized iframe { pointer-events: none; }
.cw-thumb-cover { display: none; position: absolute; left: 0; right: 0; bottom: 0; top: 32px; cursor: pointer; }
.collab-window.minimized .cw-thumb-cover { display: block; }
/* The chat's badge for a link that opens in the collab window. */
.chat-log a.collab-link, .agenda-desc a.collab-link, .agenda-desc a.collab-link:visited { display: inline-flex; gap: 4px; align-items: center; margin-top: 6px; padding: 6px 10px; border: 2px solid var(--accent-2); border-radius: 4px; color: var(--accent-2); text-decoration: none; font-weight: 600; word-break: normal; }
.chat-log a.collab-link:hover, .agenda-desc a.collab-link:hover { color: var(--ink); border-color: var(--ink); }
/* Chat messages can be selected and copied (right-click works there too —
   see ui.js's contextmenu handler). */
.chat-log { -webkit-user-select: text; -moz-user-select: text; user-select: text; }

/* ---------- Screen-share window (draggable + resizable) ------------ */
.screen-window {
  position: fixed; top: 60px; right: 16px; width: 480px; height: 300px;
  z-index: 40; background: var(--chrome-video);
  border: 3px solid var(--accent); box-shadow: 0 0 0 3px var(--chrome-shadow), 6px 6px 0 rgba(0,0,0,0.5);
  display: flex; flex-direction: column; pointer-events: auto;
}
.sw-bar {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--panel); border-bottom: 2px solid var(--border);
  font-family: var(--pixel); font-size: 0.7778rem; color: var(--accent);
  padding: 6px 8px; cursor: move; touch-action: none; user-select: none;
}
.sw-controls { display: flex; align-items: center; gap: 6px; }
.sw-close, .sw-stop { background: transparent; border: none; color: var(--muted); font-size: 0.8889rem; font-family: var(--term); cursor: pointer; }
.sw-stop:hover { color: var(--danger); }
/* Only the presenter can actually stop the share — a viewer only ever gets
   the minimize button, same as before this was added. */
.sw-stop { display: none; }
body.im-presenting .sw-stop { display: inline; }
.screen-window video { flex: 1; width: 100%; min-height: 0; object-fit: contain; background: var(--chrome-video); }
/* Reactions floating up from the bottom of the shared screen (media.js's
   showScreenReaction) — over the video, never catching clicks. */
.screen-reaction {
  position: absolute; bottom: 10px; z-index: 2; pointer-events: none;
  font-size: 28px; line-height: 1; transform: translateX(-50%);
  animation: screen-reaction-rise 2.4s ease-out forwards;
}
@keyframes screen-reaction-rise {
  0%   { opacity: 0; transform: translate(-50%, 0) scale(0.6); }
  12%  { opacity: 1; transform: translate(-50%, -12px) scale(1.05); }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: translate(calc(-50% + var(--drift, 0px)), -140px) scale(1); }
}
.sw-resize {
  position: absolute; right: 0; bottom: 0; width: 24px; height: 24px;
  cursor: nwse-resize; touch-action: none; z-index: 2;
}
/* Invisible handles along every edge and corner (see wireEdgeResize in
   ui.js) — drag any side of the window to resize it. */
.sw-edge { position: absolute; z-index: 2; touch-action: none; }
.sw-edge-n, .sw-edge-s { left: 12px; right: 12px; height: 6px; }
.sw-edge-e, .sw-edge-w { top: 12px; bottom: 12px; width: 6px; }
.sw-edge-n { top: 0; } .sw-edge-s { bottom: 0; }
.sw-edge-e { right: 0; } .sw-edge-w { left: 0; }
.sw-edge-ne, .sw-edge-nw, .sw-edge-sw { width: 12px; height: 12px; }
.sw-edge-ne { top: 0; right: 0; } .sw-edge-nw { top: 0; left: 0; } .sw-edge-sw { bottom: 0; left: 0; }
/* A classic corner grip: a triangle of ridged dark grey stripes,
   brightening on hover — drawn on a pseudo-element so the whole 24px
   square stays grabbable. */
.sw-resize::after {
  content: ''; position: absolute; right: 2px; bottom: 2px; width: 16px; height: 16px;
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  background: repeating-linear-gradient(135deg, transparent 0 2px, #4a4f5c 2px 4px);
  opacity: 0.85; transition: opacity 0.15s ease, transform 0.15s ease;
}
.sw-resize:hover::after { opacity: 1; transform: scale(1.15); transform-origin: bottom right; }

/* ---------- Screen-share takeover ---------------------------------- */
/* While someone shares, the window occupies most of the viewport by
   default (sized to the content's own aspect ratio within that area — see
   below). A viewer sees the presenter's camera through the normal SPEAKERS
   dock at top (see recalcCam in media.js) since the sharer is always
   isBroadcasting (required to start a share) — the dock is essentially
   always up during a share — so the window starts below it (same 150px
   clearance #hud-top-stack already uses) rather than under it. The
   presenter doesn't appear in their own dock (that only ever shows remote
   participants), so they instead keep their normal self-cam bubble, moved
   up next to where the dock sits (see .self-cam rules below) — same
   "cam up top, screen below" arrangement either way. */
body.screen-sharing:not(.screen-minimized) .screen-window {
  top: 150px; bottom: 96px; left: 14px; right: 14px;
  width: auto; height: auto;
}
/* The rule above defines the AVAILABLE area, not the window's final size —
   Media.fitScreenWindow() measures it, then writes real inline left/top/
   width/height sized to the shared content's actual aspect ratio and
   centered within that area, rather than letterboxing inside a box
   stretched to fill it edge to edge. */
/* Leave room for the open chat sidebar instead of running underneath it. */
body.screen-sharing:not(.screen-minimized).chat-open .screen-window { right: 416px; }
/* Your own camera bubble is redundant while the share fills the screen for
   everyone ELSE watching — but the presenter still wants to see their own
   feed, so it moves up next to the SPEAKERS dock instead of hiding, giving
   the same "cam up top, screen below" arrangement a viewer already gets via
   the dock (see recalcCam in media.js). */
body.screen-sharing:not(.screen-minimized):not(.im-presenting) .self-cam { display: none; }
body.screen-sharing:not(.screen-minimized).im-presenting .self-cam { position: fixed; top: 10px; left: 14px; bottom: auto; }
/* Minimized: shrink to a small corner thumbnail — click it (or the button
   again) to restore. A purely local, per-viewer choice (M.screenMinimized),
   never synced to anyone else. */
body.screen-sharing.screen-minimized .screen-window {
  top: auto; left: auto; bottom: 96px; right: 16px;
  width: 280px; height: 170px;
  cursor: pointer;
}
/* The SPEAKERS dock now doubles as "who's watching this share with you" —
   push the status-pill stack below it so they don't overlap. */
#stage-dock:not([hidden]) ~ #hud-top-stack { top: 150px; }

/* ---------- Self camera (bottom-left) ------------------------------- */
.self-cam {
  position: fixed; left: 12px; bottom: 84px; z-index: 22;
  width: 168px; height: 126px;
  background: var(--chrome-video); border: 3px solid var(--accent);
  box-shadow: 0 0 0 3px var(--chrome-shadow);
  pointer-events: auto; overflow: hidden; cursor: context-menu;
}
.self-cam video { width: 100%; height: 100%; object-fit: cover; transform: scaleX(-1); }
.self-cam-portrait { display: none; width: 100%; height: 100%; }
/* Camera off: show the avatar portrait canvas instead of the (stopped) video. */
.self-cam.cam-off video { display: none; }
.self-cam.cam-off .self-cam-portrait { display: block; }
.self-cam-label {
  position: absolute; left: 0; bottom: 2px;
  display: flex; align-items: center; gap: 3px;
  font-family: var(--pixel); font-size: 0.6667rem;
  background: rgba(0,0,0,0.7); color: var(--ink); padding: 2px 4px;
}
.self-cam-mic-icon { display: inline-flex; color: var(--ink); }
.self-cam-mic-icon.muted { color: var(--danger); }

.admin-block { padding: 10px; display: flex; flex-direction: column; gap: 8px; }

/* Room-expiry countdown + extend link, sitting in the Manage Space header. */
.admin-head-right { display: flex; align-items: center; gap: 10px; }
.admin-head-right .expiry-countdown {
  font-family: var(--term); font-size: 0.8333rem;
  color: var(--muted); text-transform: none;
}
.link-btn {
  background: none; border: none; padding: 0;
  font-family: var(--term); font-size: 0.8333rem;
  color: var(--accent); text-transform: none; text-decoration: underline; cursor: pointer;
}
/* Inline links keep their colour (yellow, red remove…) and get a soft wash
   of it behind them, plus a heavier underline. */
.link-btn:hover:not(:disabled) {
  background: color-mix(in srgb, currentColor 16%, transparent);
  box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 16%, transparent);
  text-decoration-thickness: 2px;
  border-radius: 2px;
}
.switch-row { display: flex; gap: 8px; align-items: center; font-family: var(--term); font-size: 0.8333rem; color: var(--ink); }

.user-list, .object-list { list-style: none; margin: 0 0 10px; padding: 0 10px 10px; }
.user-list li {
  display: flex; align-items: center; gap: 6px; justify-content: space-between;
  padding: 6px 0; font-size: 0.8889rem;
}
.user-list .u-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-list .u-name .badge { color: var(--accent); font-size: 0.7778rem; }
.u-status {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 10px;
  font-family: var(--term); font-size: 0.6667rem; letter-spacing: 0.3px; white-space: nowrap;
  border: 1px solid var(--border); background: var(--panel-2);
  pointer-events: none; user-select: none;
}
.u-status.u-idle { color: var(--muted); }
.u-status.u-active { color: var(--accent-2); border-color: var(--accent-2); }
/* Admin participant rows: name on its own line, actions as a line of links below. */
#admin-user-list li { flex-wrap: wrap; gap: 4px; align-items: flex-start; }
#admin-user-list .u-name { flex: 1 0 100%; white-space: normal; }
#admin-user-list .u-actions { display: flex; flex-wrap: wrap; gap: 4px 12px; flex: 1 0 100%; }
#admin-user-list .u-actions .link-btn.danger { color: var(--danger); }

.object-list li { padding: 24px 0; border-bottom: 1px dashed var(--border); }
/* Manage › Objects: Office / Arcade / Mini game / Club as collapsible sections. */
#admin-object-list > li.o-section { padding: 0 12px; border: 1px solid var(--border); margin: 4px 0; }
#admin-object-list > li.o-section summary {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 12px 0; cursor: pointer;
  list-style: none; font-weight: 700; font-size: 0.9444rem; color: var(--ink);
}
#admin-object-list > li.o-section summary::-webkit-details-marker { display: none; }
/* Chevron on the right: points down when closed, up when open. */
#admin-object-list > li.o-section summary::after {
  content: ''; width: 4px; height: 4px; flex: 0 0 auto; margin: 0 4px 3px 0;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform 0.15s;
}
#admin-object-list > li.o-section details[open] > summary::after { transform: rotate(-135deg); margin-bottom: -3px; }
#admin-object-list > li.o-section .object-list { padding: 0 0 8px; }
#admin-object-list > li.o-section .object-list > li:last-child { border-bottom: none; }
/* A dashed line between a section's heading and its first object too, so
   every object (e.g. Pool Table) sits between lines like the rest. */
#admin-object-list > li.o-section .object-list > li:first-child { border-top: 1px dashed var(--border); padding-top: 24px; }
.object-list li:first-child { padding-top: 0; }
.object-list .o-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 4px; }
.object-list .o-type { font-size: 0.7222rem; text-transform: uppercase; }
.object-list .o-remove { color: var(--danger); text-transform: none;}
.object-list li.o-disabled .o-remove { color: var(--accent); }
.object-list .o-name { width: 100%; margin-bottom: 6px; }
.object-list .o-row { display: flex; gap: 6px; margin-top: 6px; align-items: stretch; }
.object-list .o-row input { flex: 1; min-width: 0; margin: 0; }
.object-list .o-row .btn-secondary { flex: 0 0 auto; }
.object-list li.o-disabled .o-name,
.object-list li.o-disabled .o-row { opacity: 0.5; }
/* Manage › Sounds: what the club music is doing right now (see ui.js). */
.club-status { margin: 6px 0 2px; color: var(--muted); line-height: 1.4; }
.club-status[data-state="playing"] { color: var(--accent-2); }
/* Just a station name → one line, cut with "…" (full name on hover).
   Explanations (not on the island, a station failing…) still wrap. */
.club-status[data-state="playing"], .club-status[data-state="tuning"], .club-status[data-state="buffering"] {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.club-status[data-state="problem"], .club-status[data-state="none"], .club-status[data-state="blocked"] { color: var(--accent); }
/* A section heading with a link action on its right (Sounds › DJ booth audio). */
#admin-sidebar .tab-pane h4.h4-with-action { display: flex; justify-content: space-between; align-items: baseline; }
.h4-with-action .link-btn { font-size: 0.8333rem; font-weight: normal; }
.h4-with-action .o-remove { color: var(--danger); }   /* same red as the Objects list's remove… */
.h4-with-action .o-remove.is-restore { color: var(--accent); }   /* …and yellow for restore, like it too */
.object-list .o-volume-row.is-off { opacity: 0.5; }
.club-next { display: block; font-size: 0.8333rem; padding: 0; margin: 0 0 4px; }
/* DJ volume: label centred on the slider, on one line. */
.object-list .o-volume-row { align-items: center; }
.object-list .o-volume-row .o-type { flex: 0 0 auto; margin: 0; white-space: nowrap; }

#agenda-view-list { padding: 0; margin: 0; }

/* Agenda items reuse the object-list rhythm (dashed dividers, o-head layout). */
.agenda-time { font-size: 0.7222rem; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.agenda-title { font-family: var(--pixel); font-size: 0.8333rem; font-weight: 700; }
.agenda-desc { margin: 4px 0 0; color: var(--muted); font-size: 0.8333rem; }
/* The 📍 location link — the accent colour like chat links, never the browser's blue/visited purple. */
.agenda-desc a, .agenda-desc a:visited { color: var(--accent); word-break: break-all; }
/* Finished items stay listed but greyed out — colour only (not opacity), so
   the 📍 location link keeps its own link colour and stays easy to click. */
.agenda-past .agenda-time, .agenda-past .agenda-title, .agenda-past .agenda-desc { color: #6b7280; }
.agenda-empty { color: var(--muted); padding: 12px 0; }
.agenda-now-pill {
  display: inline-flex; align-items: center;
  padding: 2px 8px; border-radius: 10px; margin-left: 2px;
  font-family: var(--term); font-size: 0.6667rem; letter-spacing: 0.3px;
  color: var(--accent-2); border: 1px solid var(--accent-2); background: var(--panel-2);
  vertical-align: middle;
}

.banner-preview {
  border: 1px dashed var(--border); border-radius: 6px; padding: 6px;
  display: flex; justify-content: center;
  background:
    repeating-conic-gradient(#0003 0% 25%, transparent 0% 50%) 0 0 / 16px 16px;
}
.banner-preview img {
  display: block; width: auto; max-width: 100%; max-height: 150px; height: auto;
  image-rendering: pixelated;
}
.banner-error { color: var(--danger); }

/* ---------- Landing page --------------------------------------------- */
/* Plain black for now — this page is getting a full SaaS-style redesign later. */
.landing-body {
  display: flex; align-items: center; justify-content: center;
  min-height: 100%; padding: 24px 16px;
  background: #000;
}
.landing-panel { max-width: 460px; display: flex; flex-direction: column; gap: 10px; }
.landing-panel h4 { margin: 6px 0 0; }
.landing-panel h4:first-of-type { margin-top: 6px; }
.landing-panel .created .btn-primary { margin-top: 4px; }
.landing-panel .user-list { max-height: 42vh; overflow-y: auto; padding: 0; }
.landing-panel .user-list li .btn-secondary { flex: 0 0 auto; }

.created { border: 2px solid var(--accent-2); padding: 16px 12px; margin: 6px 0 4px; }
/* Join screen: which room you're joining + what happens next (you'll be
   host / how many are here / link expired), as one notice. */
.entry-notice { border: 2px solid var(--accent-2); background: rgba(53, 208, 165, 0.06); padding: 12px 14px; margin: 14px 0 18px; }
.entry-notice p { margin: 0; padding: 0; }
.entry-notice .hint { margin-top: 8px; min-height: 0; font-size: inherit; }
.entry-notice .entry-url { word-break: break-all; }
.entry-notice .hint:empty { display: none; }
.created-row { display: flex; gap: 6px; align-items: stretch; margin: 6px 0 10px; }
.created-row code {
  flex: 1; min-width: 0; font-family: var(--term); font-size: 0.8333rem;
  background: var(--chrome-input); border: 2px solid var(--border);
  padding: 9px 12px; overflow-x: auto; white-space: nowrap;
}
.created-row .btn-secondary { flex: 0 0 auto; }

/* Context menu */
.context-menu {
  /* Higher than any overlay in the app — now a body-level element (see
     index.html), so this always has to win outright, not just within some
     ancestor's own context. */
  position: absolute; z-index: 600; min-width: 150px;
  background: var(--panel); border: 3px solid var(--border);
  box-shadow: 4px 4px 0 rgba(0,0,0,0.6);
  pointer-events: auto;
}
.context-menu button {
  display: block; width: 100%; text-align: left;
  padding: 9px 12px; font-size: 0.7778rem;
  text-transform: none;
  background: transparent; border: none; color: var(--ink);
}
.context-menu button:hover { background: var(--accent); color: #1a1400; }
.context-menu .cm-title {
  padding: 8px 12px; font-family: var(--pixel); font-size: 0.7778rem;
  color: var(--accent); border-bottom: 2px solid var(--border);
}

/* Object iframe modal */
.object-panel { max-width: 1400px; width: 96vw; height: 95vh; display: flex; flex-direction: column; padding: 0; }
.object-header {
  display: flex; justify-content: space-between; align-items: center;
  font-family: var(--pixel); font-weight: 700; font-size: 1rem; color: var(--accent);
  text-transform: uppercase;
  padding: 4px 12px;
}

.object-header.no-padding { padding: 0; }
.object-header .open-tab-btn { font-size: 0.7222rem; padding: 5px 10px; margin-right: 6px; }
.object-header-title { display: flex; align-items: baseline; gap: 12px; min-width: 0; }
/* Spectator count badge in a minigame modal's header (see ui.js's
   UI.setWatching) — deliberately plain/muted so it reads as a small aside
   next to the title, not competing with it. */
.watching-badge { font-family: var(--pixel); font-size: 0.6667rem; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--muted); white-space: nowrap; }
.object-title-status { display: flex; align-items: center; gap: 6px; min-width: 0; }
#object-title-status-text {
  font-family: var(--term); font-size: 0.7778rem; font-weight: 400; text-transform: none; letter-spacing: normal;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 400px;
}
#object-title-status-text.is-link { color: var(--accent); text-decoration: underline; cursor: pointer; }
#object-title-status-text.is-link:hover { color: var(--ink); }
.icon-btn {
  background: none; border: none; padding: 2px; margin: 0; line-height: 0; cursor: pointer;
  color: var(--muted); opacity: 0.7;
}
.icon-btn:hover { opacity: 1; color: var(--ink); }

/* Whoever has this same arcade cabinet / doc embed open shares a cam strip,
   same treatment as a Connect 4 pair or a Wheel of Fortune table. */
.object-cams {
  display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;
  padding: 0 12px; background: var(--panel-2); border-bottom: 2px solid var(--border);
}
.object-cam-tile { position: relative; width: 150px; height: 120px; flex: 0 0 auto; background: var(--chrome-video); border: 2px solid var(--border); overflow: hidden; }
.object-cam-tile video, .object-cam-tile .oc-cam-portrait { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.object-cam-tile .oc-cam-portrait[hidden] { display: none; }
.object-cam-tile .oc-cam-label {
  position: absolute; left: 0; bottom: 0px; right: 0;
  display: flex; align-items: center; gap: 3px;
  font-family: var(--pixel); font-size: 0.75rem; background: rgba(0,0,0,0.7);
  padding: 2px 2px; color: var(--ink);
}
.oc-cam-mic-icon { display: inline-flex; flex: 0 0 auto; color: var(--ink); }
.oc-cam-mic-icon.muted { color: var(--danger); }
.object-cams-names {
  display: flex; gap: 6px 16px; flex-wrap: wrap; justify-content: center;
  padding: 8px 12px; background: var(--panel-2); border-bottom: 2px solid var(--border);
  font-family: var(--pixel); font-size: 0.7778rem; color: var(--muted);
}

/* Dark, not white — a fallback in case some future embed's CSS zoom (see
   ui.js openObjectModal) doesn't fill the frame exactly on every browser;
   matching our own dark chrome there reads as intentional letterboxing
   instead of a broken white gap. */
.object-frame-wrap { flex: 1; background: var(--panel); overflow: hidden; }
#object-frame { display: block; width: 100%; height: 100%; border: 0; }

/* Connect 4 modal */
.connect4-panel { max-width: 820px; width: 94vw; padding: 0; display: flex; flex-direction: column; }
.connect4-scoreboard {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  padding: 10px 12px; background: var(--panel-2); border-bottom: 2px solid var(--border);
}
.c4-name { font-family: var(--pixel); font-size: 0.8889rem; min-width: 80px; text-align: center; opacity: 0.55; transition: opacity 0.15s ease; }
.c4-name.c4-turn { opacity: 1; }
.c4-name.c4-red { color: #e3342f; }
.c4-name.c4-yellow { color: #ffd23f; }
.c4-turn-label { font-family: var(--pixel); font-size: 0.6667rem; color: var(--muted); min-width: 90px; text-align: center; }
.connect4-board-wrap { position: relative; background: var(--chrome-video); display: flex; justify-content: center; padding: 10px; }
#connect4-canvas { width: 100%; max-width: 740px; height: auto; border-radius: 12px; display: block; cursor: pointer; transition: width 0.15s ease, height 0.15s ease; }
.connect4-winner-text { font-size: 1.8889rem; }

@media (max-width: 640px) {
  .sidebar { width: 78vw; }
  .ab-btn .ab-label { display: none; }
  .stage-tiles .tile { width: 108px; height: 62px; }
  .stage-tiles .tile.screen { width: 150px; }
  body.screen-sharing:not(.screen-minimized) .screen-window { top: 118px; left: 8px; right: 8px; bottom: 96px; }
  /* Same source-order trick as the desktop rule above, just matched to the
     mobile sidebar's own geometry (.sidebar { right: 10px; width: 78vw; }
     at this breakpoint) -- otherwise this rule's right:8px, tying in
     specificity and coming later in the cascade, would always win and chat
     would never actually push the window over on a small screen. */
  body.screen-sharing:not(.screen-minimized).chat-open .screen-window { right: calc(78vw + 20px); }
  body.screen-sharing:not(.screen-minimized):not(.im-presenting) .self-cam { display: none; }
  body.screen-sharing:not(.screen-minimized).im-presenting .self-cam { position: fixed; top: 6px; left: 8px; bottom: auto; }
  body.screen-sharing.screen-minimized .screen-window { width: 180px; height: 110px; }
}
@media (max-width: 760px) {
  .avatar-layout { flex-direction: column; }
  .avatar-layout .avatar-preview-wrap { align-self: center; position: static; }
  .avatar-layout .avatar-preview-col { align-self: center; position: static; align-items: center; }
  .avatar-layout .avatar-controls { grid-template-columns: repeat(2, 1fr); }
}
