:root {
  /* Captions' colours (the session's own sound's, a viewer's voice's): the
     talking ring of their camera tiles too. */
  --cap-presenter: #fff;
  --cap-viewer: #ffd84d;
  --bg: #f4f5f7;
  --surface: #ffffff;
  --text: #16181d;
  --muted: #5d6470;
  --border: #d9dce2;
  --accent: #2f6fed;
  --accent-text: #ffffff;
  --danger: #d23b3b;
  --live: #d23b3b;
  --ok: #1f9d55;
  --focus: #2f6fed55;
  --radius: 10px;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1115;
    --surface: #181b21;
    --text: #e8eaef;
    --muted: #9aa1ad;
    --border: #2b3039;
    --accent: #5b8cff;
    --accent-text: #0b0d11;
    --focus: #5b8cff66;
    color-scheme: dark;
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body { background: var(--bg); color: var(--text); line-height: 1.45; }
[hidden] { display: none !important; }
.mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; }
.muted { color: var(--muted); margin-top: 0; }
code { font-size: 0.9em; }

.brand {
  display: flex; align-items: center; gap: 10px;
  font-weight: 650; font-size: 1.2rem;
  max-width: 920px; margin: 0 auto; padding: 24px 16px 8px;
}

.cards {
  display: grid; gap: 16px;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  max-width: 920px; margin: 0 auto; padding: 8px 16px 32px;
  align-items: start;
}
@media (max-width: 720px) { .cards { grid-template-columns: minmax(0, 1fr); } }

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px;
}
.card h1 { font-size: 1.25rem; margin: 0 0 4px; }

form { display: flex; flex-direction: column; gap: 6px; }
label, legend { font-weight: 550; font-size: 0.9rem; margin-top: 10px; }
.hint { font-size: 0.8rem; color: var(--muted); }
/* The presenter's page in a grid: participants by relay. */
.relays table { border-collapse: collapse; font-size: 0.8rem; color: var(--muted); }
.relays caption { text-align: left; font-weight: 600; padding-bottom: 0.25rem; }
.relays th, .relays td { text-align: left; padding: 0.1rem 1rem 0.1rem 0; }
.relays th { font-weight: 600; border-bottom: 1px solid var(--border); }
.relays th:nth-child(n+2), .relays td:nth-child(n+2) { text-align: right; font-variant-numeric: tabular-nums; }
.row { display: flex; gap: 8px; }
.row input { flex: 1; min-width: 0; }

input, select, button {
  font: inherit; color: inherit;
  border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg); padding: 8px 10px; min-height: 40px;
}
input:focus-visible, select:focus-visible, button:focus-visible, summary:focus-visible {
  outline: 3px solid var(--focus); outline-offset: 1px;
}
input:invalid:not(:placeholder-shown):not(:focus) { border-color: var(--danger); }

button { cursor: pointer; font-weight: 550; }
button.primary { background: var(--accent); color: var(--accent-text); border-color: var(--accent); margin-top: 16px; }
button.secondary { background: var(--surface); }
button.danger { background: var(--danger); border-color: var(--danger); color: #fff; }
button.ghost { background: transparent; width: 40px; }
/* Beside "Start sharing": presenting in place of whoever is. */
button#share-takeover { width: auto; margin-top: 16px; }
button:disabled { opacity: 0.55; cursor: default; }
/* Meeting mode (the user's spec, 2026-10-24): "Start meeting" green; in a
   meeting, the screen button green ("Start sharing") or orange
   ("Interrupt sharing"). */
:root { --go: #1f9d55; --interrupt: #dd6b20; }
button.go { background: var(--go); border-color: var(--go); color: #fff; }
button.interrupt { background: var(--interrupt); border-color: var(--interrupt); color: #fff; }
button#share-meeting { width: auto; margin-top: 16px; }
.start-row { flex-wrap: wrap; }
.mode-switch legend { margin-top: 0; }

.segmented { border: 0; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.segmented legend { padding: 0; margin-bottom: 6px; }
.segmented label { margin: 0; font-weight: 450; }
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.segmented span {
  display: inline-block; padding: 7px 12px; border: 1px solid var(--border);
  border-radius: 999px; background: var(--bg); cursor: pointer; font-size: 0.9rem;
}
.segmented input:checked + span { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
.segmented input:focus-visible + span { outline: 3px solid var(--focus); }

details { margin-top: 8px; }
summary { cursor: pointer; color: var(--muted); font-size: 0.9rem; }
details[open] { display: flex; flex-direction: column; gap: 6px; }
.check { display: flex; gap: 8px; align-items: center; font-weight: 450; }
.check input { min-height: auto; }

.error { color: var(--danger); min-height: 1.2em; margin: 8px 0 0; font-size: 0.9rem; }
.stack { display: grid; gap: 16px; align-content: start; }
.native-note { margin: 0; padding: 32px 16px; text-align: center; color: var(--muted); }
.result { min-height: 1.2em; margin: 8px 0 0; font-size: 0.9rem; }
.result.ok { color: var(--ok); }
.result.bad { color: var(--danger); }
.notice { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; font-size: 0.9rem; }

/* Publisher */
.bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 16px; background: var(--surface); border-bottom: 1px solid var(--border);
}
.bar .spacer { flex: 1; }
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px; background: var(--border); color: var(--text);
}
.badge.live { background: var(--live); color: #fff; }
.badge.live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; animation: pulse 1.6s infinite; }
.badge.warn { background: #b7791f; color: #fff; }
@keyframes pulse { 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { .badge.live::before { animation: none; } }

.share-main { max-width: 1100px; margin: 0 auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.link-box { display: flex; gap: 8px; }
.link-box input { flex: 1; min-width: 0; font-family: ui-monospace, Menlo, Consolas, monospace; }
.preview-wrap { background: #000; border-radius: var(--radius); overflow: hidden; aspect-ratio: 16 / 9; display: grid; place-items: center; }
.preview-wrap video { width: 100%; height: 100%; object-fit: contain; }
.controls { display: flex; gap: 8px; flex-wrap: wrap; }
.controls select { flex: 1; min-width: 180px; }
/* The microphone's level on the sharing page, with its meter. */
.mic-gain { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.mic-gain label { margin: 0; }
/* A microphone's level (−60…0 dBFS, see showLevel in web/app.js): a bar
   of our own — Safari's <meter> stayed an empty grey bar for every value
   (the user's Mac and iPad, 2026-11-05), whatever this page set; this draws
   as /mictest's bars, which those devices show. Green where a voice should
   be, amber under −40 or over −4 dBFS. */
.level-bar { display: inline-block; vertical-align: middle; width: 140px; height: 10px; border-radius: 5px;
  background: var(--bg); border: 1px solid var(--border); overflow: hidden; }
.level-bar[hidden] { display: none; }
.level-bar > span { display: block; height: 100%; width: 0; background: var(--ok); transition: width 80ms linear; }
.level-bar.off > span { background: #d39b22; }
/* "Resume sharing" is primary while paused: not a form's button pushed down. */
.controls button { margin-top: 0; }
.stats { color: var(--muted); font-size: 0.8rem; margin: 0; min-height: 1.2em; }

/* Viewer */
.stage { position: fixed; inset: 0; background: #000; color: #e8eaef; overflow: hidden; }
.stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; image-rendering: auto; }
.stage.actual { overflow: auto; }
.stage.actual canvas { position: static; width: auto; height: auto; display: block; margin: auto; }

.overlay {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px;
  background: rgba(0, 0, 0, 0.55); text-align: center; padding: 16px;
  pointer-events: none;
}
.overlay p { margin: 0; font-size: 1.05rem; max-width: 36em; }
.spinner {
  width: 32px; height: 32px; border-radius: 50%;
  border: 3px solid rgba(255,255,255,0.25); border-top-color: #fff;
  animation: spin 0.9s linear infinite;
}
.overlay.static .spinner { display: none; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 3s; } }

/* Too narrow for all its buttons (a phone): it scrolls sideways, faded
   on the side where more is (see scrollingBar). */
.toolbar {
  position: fixed; right: 12px; bottom: 12px; display: flex; gap: 6px; align-items: center;
  padding: 6px; border-radius: 12px; background: rgba(20, 22, 27, 0.75);
  transition: opacity 0.3s; opacity: 1;
  max-width: calc(100% - 24px);
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
}
.toolbar > * { flex: none; }
.toolbar .badge { white-space: nowrap; }
.toolbar.more-right { -webkit-mask-image: linear-gradient(to left, transparent, #000 28px); mask-image: linear-gradient(to left, transparent, #000 28px); }
.toolbar.more-left { -webkit-mask-image: linear-gradient(to right, transparent, #000 28px); mask-image: linear-gradient(to right, transparent, #000 28px); }
.toolbar.more-left.more-right {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
}
.stage.idle .toolbar { opacity: 0; }
.fs-hint {
  position: fixed; left: 50%; top: 12px; transform: translateX(-50%); margin: 0;
  padding: 6px 12px; border-radius: 12px; background: rgba(20, 22, 27, 0.75); color: #fff;
  font-size: 0.85rem; pointer-events: none; transition: opacity 0.3s;
}
.stage.idle .fs-hint { opacity: 0; }
.toolbar .badge { background: rgba(255,255,255,0.15); color: #fff; }
.toolbar .badge.live { background: var(--live); }
button.icon {
  min-width: 36px; min-height: 36px; padding: 4px 8px;
  background: rgba(255,255,255,0.1); border-color: transparent; color: #fff; font-size: 0.9rem;
}
/* The presenters' link for viewers (🌐): a link, looking as the buttons
   beside it. */
a.icon {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; min-height: 36px; padding: 4px 8px; border-radius: 8px; border: 1px solid transparent;
  background: rgba(255,255,255,0.1); color: #fff; font-size: 0.9rem; text-decoration: none;
}
a.icon:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
a.icon[hidden] { display: none; }
/* The sharing page's link for viewers: one row, wrapping. */
.weblink-row { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; }
.weblink-row label { margin: 0; }
#pub-weblink-url { flex: 2 1 16em; min-width: 0; }
#pub-weblink-text { flex: 1 1 12em; min-width: 0; }
.weblink-row button { margin: 0; }
.weblink-row button[hidden] { display: none; }
.weblink-row .hint:empty { display: none; }
/* Its page shown to viewers while nobody presents: right after it. */
.linkpage-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 6px 0; }
.linkpage-row[hidden] { display: none; }
.linkpage-row .hint:empty { display: none; }
/* A VNC connection to share (the start page, the sharing page's row). */
.vnc-choice { display: flex; flex-direction: column; gap: 6px; }
.vnc-choice label { margin: 0; }
.vnc-choice .row input { flex: 1; min-width: 0; }
.vnc-choice .row button { margin: 0; white-space: nowrap; }
.vnc-row { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; }
.vnc-row label { margin: 0; }
.vnc-row input { flex: 1 1 14em; min-width: 0; }
.vnc-row button { margin: 0; }
.vnc-row .hint { flex-basis: 100%; }
.vnc-row .hint:empty { display: none; }
.vnc-row .hint.error { color: var(--danger, #c0392b); }
.stats-panel {
  position: fixed; left: 12px; top: 12px; margin: 0; padding: 10px 12px;
  background: rgba(20, 22, 27, 0.8); border-radius: 8px; font-size: 0.75rem; color: #e8eaef;
}

/* Viewer takeover */
.guest-banner {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px; border-radius: var(--radius);
  background: #b7791f22; border: 1px solid #b7791f; 
}
.guest-banner span { flex: 1; min-width: 12em; font-weight: 550; }
/* A guest's stream on the presenter's page: fitted (as the viewer's), or
   at actual size and scrolled; full screen either way. Fitted, the canvas
   is pinned to the box: as a grid item, a picture larger than the box (an
   iPad's 1546x1080 on a tablet) made its row grow, and the box cut it off.
   The presenter's own picture (the video) too: a screen not 16:9 (the
   tablet's 2304x1440, one held upright) was cut at the bottom. */
.preview-wrap { position: relative; }
.preview-wrap canvas, .preview-wrap video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.preview-wrap .toolbar { position: absolute; }
.preview-wrap.actual { display: block; overflow: auto; }
.preview-wrap.actual canvas { position: static; width: auto; height: auto; max-width: none; display: block; margin: auto; }
.preview-wrap:fullscreen { aspect-ratio: auto; border-radius: 0; width: 100%; height: 100%; }
.preview-wrap:-webkit-full-screen { aspect-ratio: auto; border-radius: 0; width: 100%; height: 100%; }

.switch { display: flex; align-items: center; gap: 10px; cursor: pointer; font-weight: 500; margin: 0; }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch .track {
  flex: none; width: 40px; height: 22px; border-radius: 999px; background: var(--border);
  position: relative; transition: background 0.15s;
}
.switch .track::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px;
  border-radius: 50%; background: #fff; transition: transform 0.15s;
}
.switch input:checked + .track { background: var(--accent); }
.switch input:checked + .track::after { transform: translateX(18px); }
.switch input:focus-visible + .track { outline: 3px solid var(--focus); outline-offset: 2px; }
.switch:has(input:disabled) { cursor: not-allowed; opacity: 0.55; }

/* Not affected by the toolbar's idle fade: the offer must stay visible. */
.share-fab {
  position: fixed; left: 16px; bottom: 16px; z-index: 5;
  background: var(--accent); color: var(--accent-text); border-color: var(--accent);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); padding: 10px 16px;
}
.float-panel { position: fixed; inset: auto 16px 16px 16px; z-index: 6; display: contents; }
#view-share-panel, #view-presenting, #view-talk-panel, #view-name-panel, #view-cam-panel, #view-cc-panel, #view-cc-lang-panel {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 6;
  width: min(420px, calc(100vw - 32px));
  background: var(--surface); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
  display: flex; flex-direction: column; gap: 8px;
}
#view-share-panel[hidden], #view-presenting[hidden], #view-talk-panel[hidden], #view-name-panel[hidden], #view-cam-panel[hidden], #view-cc-panel[hidden], #view-cc-lang-panel[hidden] { display: none; }
.cc-langs { border: 0; margin: 0 0 12px; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 16px; }
.cc-langs label { display: flex; align-items: center; gap: 8px; font-weight: 400; margin: 0; padding: 4px 0; cursor: pointer; }
.cc-langs label.spoken { grid-column: 1 / -1; }
.cc-langs input { width: auto; margin: 0; }
#view-cc-lang { font-size: 13px; font-weight: 700; letter-spacing: 0.02em; }
/* Captions (captions.js): over the picture, at the bottom (above the bar)
   or the top; the session's own sound's in white, a viewer's voice with
   its name, in its own colour (voiceColour, set on the line; yellow
   without a slot). */
.captions {
  position: fixed; left: calc(50% - var(--chat-shift, 0px) / 2); transform: translateX(-50%); z-index: 4;
  width: min(1100px, calc(100vw - 32px - var(--chat-shift, 0px)));
  display: flex; flex-direction: column; align-items: center; gap: 4px; pointer-events: none;
}
.captions[hidden] { display: none; }
.captions.bottom { bottom: 72px; }
.captions.top { top: 56px; }
.captions p {
  margin: 0; padding: 2px 10px; border-radius: 6px; background: rgba(0, 0, 0, 0.75); color: var(--cap-presenter);
  text-align: center; line-height: 1.3; overflow-wrap: anywhere; text-shadow: 0 1px 2px #000;
}
.captions p.viewer { color: var(--cap-viewer); }
.captions p .who { font-weight: 700; }
.captions.size-s p { font-size: clamp(14px, 1.7vw, 20px); }
.captions.size-m p { font-size: clamp(17px, 2.3vw, 28px); }
.captions.size-l p { font-size: clamp(21px, 3.1vw, 38px); }
.captions.size-xl p { font-size: clamp(26px, 4.2vw, 52px); }
button.icon.cc { font-weight: 700; letter-spacing: 0.02em; }
button.icon.cc[aria-pressed="true"] { background: var(--accent); color: var(--accent-text); }
/* The presenter's page: captions shown there too (CC beside the switch,
   their language), over its preview box — sized by the box, not the
   window; above the guest's tools when they show. */
.captions-row { display: flex; flex-direction: column; gap: 8px; }
.captions-row[hidden] { display: none; }
.cc-here { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin-left: 50px; }
.cc-here[hidden] { display: none; }
.cc-line { display: flex; align-items: center; gap: 8px 12px; flex-wrap: wrap; }
.cc-here button, .cc-here select { width: auto; margin: 0; }
.cc-here select[hidden] { display: none; }
.cc-here button.cc { font-weight: 600; }
.cc-here button.cc[aria-pressed="true"] { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
.preview-wrap .cc-layer { position: absolute; inset: 0; container-type: size; pointer-events: none; z-index: 2; }
.cc-layer .captions { position: absolute; left: 50%; width: calc(100% - 16px); z-index: auto; }
.cc-layer .captions.bottom { bottom: 8px; }
.cc-layer .captions.top { top: 8px; }
.preview-wrap:has(> .toolbar:not([hidden])) .cc-layer .captions.bottom { bottom: 60px; }
.cc-layer .captions.size-s p { font-size: 13px; font-size: clamp(12px, 2.2cqi, 22px); }
.cc-layer .captions.size-m p { font-size: 15px; font-size: clamp(14px, 2.8cqi, 28px); }
.cc-layer .captions.size-l p { font-size: 18px; font-size: clamp(16px, 3.6cqi, 36px); }
.cc-layer .captions.size-xl p { font-size: 21px; font-size: clamp(18px, 4.6cqi, 46px); }
.float-panel h2 { font-size: 1.1rem; margin: 0; display: flex; align-items: center; gap: 8px; }
.float-panel p { margin: 0; color: var(--muted); font-size: 0.9rem; }
.float-panel .row { margin-top: 8px; }
.float-panel .row button { margin-top: 0; }
.toast {
  position: fixed; left: 50%; top: 16px; transform: translateX(-50%); z-index: 7; margin: 0;
  background: rgba(20, 22, 27, 0.9); color: #fff; padding: 10px 14px; border-radius: 8px;
}

/* Browser notifications for a session: the offer, under the toasts. */
.push-offer {
  position: fixed; left: 50%; top: 64px; transform: translateX(-50%); z-index: 7;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px 12px;
  width: max-content; max-width: calc(100vw - 32px); box-sizing: border-box;
  background: rgba(20, 22, 27, 0.92); color: #fff; padding: 10px 14px; border-radius: 8px;
}
.push-offer[hidden] { display: none; }
.push-offer .row { display: flex; gap: 8px; }
.push-offer button { margin: 0; }
/* Its own colours on the dark bar: a secondary button's are the page's
   surface — white in the light theme, under the bar's white text (the
   user's Mac: "one of the buttons is shown blank"). */
.push-offer button.secondary { background: transparent; color: #fff; border-color: rgba(255, 255, 255, 0.55); }

/* Peer-to-peer indicators */
.p2p-note { color: var(--muted); font-weight: 450; }
.toolbar .badge.p2p { background: #1f9d55; color: #fff; text-transform: none; letter-spacing: 0; cursor: help; }

/* How the page reaches the server: HTTP/3 or WebSocket */
.badge.net { cursor: help; letter-spacing: 0.06em; }
.badge.net.h3 { background: var(--accent); color: var(--accent-text); }
.toolbar .badge.net { background: rgba(255,255,255,0.15); color: #fff; }
.toolbar .badge.net.h3 { background: #2f6fed; color: #fff; }

.badge.net.http { background: #b7791f; color: #fff; }
.toolbar .badge.net.http { background: #b7791f; color: #fff; }

/* Viewers talking: the presenter's list, the viewers' badge. */
.talkers { display: flex; flex-direction: column; gap: 6px; }
.talkers .row { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.talkers ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.talkers li { display: flex; align-items: center; gap: 8px; }
.talkers li .name { flex: 1; }
.talkers li.speaking .name { font-weight: 650; }
.talkers li button { width: auto; min-height: 30px; padding: 2px 10px; }
.talkers-badge .speaking { font-weight: 700; }
#view-mic.on { background: var(--live); }
#view-talk-panel select { width: 100%; }
#view-talk-panel .mic-gain select { width: auto; flex: 1; }
.fs-hint.talk-hint { top: 52px; background: var(--live); }
.stage.idle .fs-hint.talk-hint { opacity: 1; }

/* Cameras: the presenter's in a corner, viewers' in a column on the side
   (on the sharing page: a strip). Small, never in the way of the screen. */
.cam-tile {
  position: relative; background: #000; border-radius: 8px; overflow: hidden;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); aspect-ratio: 16 / 9;
}
.cam-tile canvas { width: 100%; height: 100%; object-fit: cover; display: block; }
.cam-tile .cam-name {
  position: absolute; left: 6px; bottom: 4px; font-size: 0.75rem; color: #fff;
  text-shadow: 0 0 3px #000; max-width: calc(100% - 12px); overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
/* Who talks now (web/speaking.js): a ring inside the tile, over the
   picture, in the colour of their captions (the presenter's white; each
   viewer's own, voiceColour, set on the tile), a dark line inside it for a
   light picture; in a meeting's grid too. */
.cam-tile { --talk: var(--cap-viewer); }
.cam-presenter, .cam-tile.voice-presenter { --talk: var(--cap-presenter); }
.cam-tile::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 0 3px var(--talk), inset 0 0 0 4px rgba(0, 0, 0, 0.6);
  opacity: 0; transition: opacity 0.2s ease;
}
.cam-tile.talking::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .cam-tile::after { transition: none; } }
.cam-presenter { position: fixed; top: 12px; right: 12px; width: min(24vw, 280px); z-index: 4; }
.cam-presenter[hidden] { display: none; }
.cam-hide, .cam-size {
  position: absolute; top: 4px; right: 4px; width: 26px; min-height: 26px; padding: 0;
  border-radius: 50%; background: rgba(0, 0, 0, 0.55); color: #fff; border: 0; font-size: 1rem; line-height: 1;
}
/* The presenter's camera, larger at the viewer's wish: 860 px wide (the
   user: 800-900), less where the window is smaller, and back. */
.cam-size { left: 4px; right: auto; display: grid; place-items: center; }
.cam-size svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.cam-size .shrink, .cam-presenter.large .cam-size .grow { display: none; }
.cam-presenter.large .cam-size .shrink { display: block; }
.cam-presenter { transition: width 0.2s ease; }
.cam-presenter.large { width: min(860px, calc(100vw - 24px), calc((100vh - 96px) * 16 / 9)); }
@media (prefers-reduced-motion: reduce) { .cam-presenter { transition: none; } }
.cam-column {
  position: fixed; left: 12px; top: 12px; display: flex; flex-direction: column; gap: 8px; z-index: 4;
  width: min(18vw, 200px);
}
.cam-strip { display: flex; flex-wrap: wrap; gap: 8px; }
.cam-strip .cam-tile { width: 180px; }
#view-cam.on { background: var(--live); }
@media (max-width: 640px) {
  .cam-presenter { width: 34vw; }
  .cam-presenter.large { width: min(calc(100vw - 24px), calc((100vh - 96px) * 16 / 9)); }
  .cam-column { width: 26vw; }
}
/* Our own camera, from the camera itself: mirrored, as in a call. */
.cam-tile video { width: 100%; height: 100%; object-fit: cover; display: block; }
.cam-self video, .cam-self canvas { transform: scaleX(-1); }
.cam-self { outline: 2px solid rgba(255, 255, 255, 0.5); }
/* Sharing paused: our camera goes nowhere meanwhile. */
.cam-self.held video, .cam-self.held canvas { opacity: 0.35; }

/* Every camera tile hidden by the viewer (👥) while a screen is shown:
   the presenter's, viewers', our own, and 👤 with them. */
#viewer.cams-hidden #view-cam-presenter, #viewer.cams-hidden #view-cam-tiles, #viewer.cams-hidden #view-cam-show { display: none; }

/* A meeting with no screen shown: the cameras fill the screen, each placed
   by the page (web/camgrid.js) — the viewer's whole window, the sharing
   page's preview box. Under the bar and whatever floats over the picture. */
.stage.meeting #view-canvas { visibility: hidden; }
.stage.meeting .cam-column { inset: 0; width: auto; display: block; z-index: 0; }
.stage.meeting .cam-column .cam-tile { position: absolute; }
.stage.meeting .cam-presenter { z-index: 0; transition: none; }
.stage.meeting .cam-presenter .cam-hide, .stage.meeting .cam-presenter .cam-size { display: none; }
.cam-presenter .presenter-name { display: none; }
.stage.meeting .cam-presenter .presenter-name { display: block; }
.stage.meeting .cam-tile .cam-name, .preview-wrap.grid .cam-tile .cam-name { font-size: 0.9rem; left: 8px; bottom: 6px; }
.meeting-note { position: absolute; left: 50%; top: 45%; transform: translate(-50%, -50%); margin: 0; color: #c8ccd4; text-align: center; max-width: 80%; }
.preview-wrap.grid > video, .preview-wrap.grid > canvas, .preview-wrap.grid > .native-note { display: none; }
/* The preview full size elsewhere (Publisher.openFull): its button in the
   box's top right corner, over anything shown there; while that window is
   open, the box's pictures hidden and a note in their place (a meeting's
   camera tiles stay). */
.preview-wrap .full-open {
  position: absolute; top: 8px; right: 8px; z-index: 4; min-width: 32px; min-height: 32px; padding: 0;
  display: grid; place-items: center; background: rgba(0, 0, 0, 0.55); border: 1px solid rgba(255, 255, 255, 0.35);
  opacity: 0.85;
}
.preview-wrap .full-open:hover, .preview-wrap .full-open:focus-visible { opacity: 1; background: rgba(0, 0, 0, 0.75); }
.preview-wrap.full-elsewhere > video, .preview-wrap.full-elsewhere > canvas,
.preview-wrap.full-elsewhere > .native-note { visibility: hidden; }
.preview-wrap .full-note {
  position: absolute; left: 12px; right: 52px; top: 10px; z-index: 3; margin: 0;
  color: #fff; font-size: 0.9rem; text-shadow: 0 1px 2px #000;
}
.preview-wrap .full-note[hidden] { display: none; }
#full { position: fixed; inset: 0; background: #000; }
#full canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
#full .full-status {
  position: absolute; left: 16px; right: 16px; top: 50%; transform: translateY(-50%); margin: 0;
  text-align: center; color: #ddd; font-size: 1.1rem;
}
#full .full-status[hidden] { display: none; }
/* The window's grid (a meeting showing no screen), as a viewer's meeting:
   every tile placed by the page over the whole window. Otherwise the screen
   alone, as the preview shows it. */
#full:not(.grid) .cam-presenter, #full:not(.grid) .cam-column { display: none; }
#full.grid #full-canvas { visibility: hidden; }
#full.grid .cam-column { inset: 0; width: auto; display: block; z-index: 0; }
#full.grid .cam-column[hidden] { display: none; }
#full.grid .cam-column .cam-tile { position: absolute; }
#full.grid .cam-presenter { z-index: 0; transition: none; }
#full .cam-presenter .cam-hide { display: none; }
#full.grid .cam-presenter .presenter-name { display: block; }
#full.grid .cam-tile .cam-name { font-size: 0.9rem; left: 8px; bottom: 6px; }
/* Its captions: over the screen or the grid, no bar under them. */
#full .captions { position: absolute; left: 50%; width: min(1400px, calc(100% - 32px)); }
#full .captions.bottom { bottom: 24px; }
#full .captions.top { top: 24px; }
.preview-wrap .cam-strip.grid { position: absolute; inset: 0; display: block; }
.preview-wrap .cam-strip.grid .cam-tile { position: absolute; }

/* The camera's background: a picture chosen on this device. */
.bg-choice { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.bg-label { width: 100%; font-weight: 550; font-size: 0.9rem; }
.bg-thumb { width: 96px; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); }
.bg-note { width: 100%; }
.bg-edge-row { display: inline-flex; align-items: center; gap: 6px; font-size: 0.9rem; }
.bg-edge-row[hidden], .bg-edge-hint[hidden] { display: none; }
.bg-edge-row select { width: auto; margin: 0; }
.bg-edge-hint { width: 100%; }
/* Framing and lighting (web/framing.js): each on a line of its own. */
.bg-look { width: 100%; display: flex; align-items: center; gap: 6px; font-size: 0.9rem; margin: 0; }
.bg-look input { width: auto; margin: 0; }
.bg-note:empty { display: none; }
label.button {
  position: relative; display: inline-flex; align-items: center; margin: 0; cursor: pointer;
  border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; min-height: 40px;
  background: var(--surface); font-weight: 550; font-size: 1rem;
}
label.button input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
label.button:focus-within { outline: 3px solid var(--focus); outline-offset: 1px; }

/* Recording: the presenter's switch, the indicator everybody sees, and the
   download pages. */
.rec-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.viewers-sound-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 6px 0; }
.viewers-sound-row button { width: auto; margin: 0; }
.viewers-sound-row button.on { border-color: var(--danger); color: var(--danger); }
.badge.rec { background: #c53030; color: #fff; }
.badge.rec::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; animation: pulse 1.6s infinite; }
.badge.rec.paused { background: #718096; }
.badge.rec.paused::before { animation: none; }
@media (prefers-reduced-motion: reduce) { .badge.rec::before { animation: none; } }
.rec-corner { position: fixed; left: 12px; top: 12px; z-index: 5; }
.cards.single { grid-template-columns: minmax(0, 1fr); max-width: 760px; }
.rec-days { list-style: none; padding: 0; margin: 12px 0 0; display: flex; flex-direction: column; gap: 10px; }
.rec-days li { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; }
.rec-days .day { font-weight: 600; min-width: 9.5em; }
.rec-days .meta { color: var(--muted, inherit); opacity: 0.8; font-size: 0.9rem; flex: 1 1 10em; }
.rec-days .state { font-size: 0.85rem; flex-basis: 100%; }
.rec-session { margin-top: 16px; }
.rec-session h2 { font-size: 1rem; margin: 0 0 6px; display: flex; gap: 8px; align-items: center; }
.rec-session h2 a { font-weight: 400; font-size: 0.85rem; }
.spacer { flex: 1; }
/* Buttons side by side: none pushed down by button.primary's form margin. */
.rec-days button, #recadmin-list .row button { margin-top: 0; }
.rec-files-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.rec-files-row .meta { color: var(--muted); font-size: 0.9rem; }
a.button.primary {
  display: inline-flex; align-items: center; min-height: 40px; padding: 8px 14px; border-radius: 8px;
  background: var(--accent); color: var(--accent-text); border: 1px solid var(--accent);
  text-decoration: none; font-weight: 550;
}
#recadmin-list .row, #filesadmin-list .row { align-items: center; }
#filesadmin-list .row button { margin-top: 0; }
.files-admin .meta { color: var(--muted, inherit); opacity: 0.8; font-size: 0.9rem; margin: 0 0 4px 26px; }
.files-admin h2 .badge { font-size: 0.7rem; }
.files-admin .files-open { margin-left: 26px; font-size: 0.9rem; }
.files-tree { list-style: none; padding: 0; margin: 8px 0 0; border: 1px solid var(--border); border-radius: var(--radius); }
.files-tree li { display: flex; align-items: center; gap: 8px; padding: 5px 10px; border-top: 1px solid var(--border); min-width: 0; }
.files-tree li:first-child { border-top: none; }
.files-tree li input { min-height: auto; flex: none; }
.files-tree .name { overflow-wrap: anywhere; min-width: 0; }
.files-tree .meta { margin: 0 0 0 auto; white-space: nowrap; font-size: 0.85rem; }
/* /recordings: every session's chat, and its Clear. */
.chats-admin { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; }
.chats-admin h2 { flex-wrap: wrap; }
.chats-admin h2 .badge { font-size: 0.7rem; }
.chats-admin h2 button { margin: 0 0 0 auto; font-size: 0.9rem; font-weight: 550; }
.chats-admin .meta { color: var(--muted, inherit); opacity: 0.8; font-size: 0.9rem; margin: 0 0 2px; overflow-wrap: anywhere; }
.chats-admin .chat-clear-confirm { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.chats-admin .chat-clear-confirm p { margin: 0; flex: 1 1 16em; }
.chats-admin .chat-clear-confirm button { margin-top: 0; }
/* /recordings: every session's attendance, by day and name. */
.attend-admin { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; }
.attend-admin h2 { flex-wrap: wrap; }
.attend-admin h2 .badge { font-size: 0.7rem; }
.attend-admin h2 button { margin: 0; font-size: 0.9rem; font-weight: 550; }
.attend-admin h2 .attend-pdf { margin-left: auto; }
.attend-admin .attend-clear-confirm { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0; }
.attend-admin .attend-clear-confirm p { margin: 0; flex: 1 1 16em; }
.attend-admin .attend-clear-confirm button { margin-top: 0; }
.attend-admin .meta { color: var(--muted, inherit); opacity: 0.8; font-size: 0.9rem; margin: 0 0 2px; overflow-wrap: anywhere; }
.attend-admin .attend-open { font-size: 0.9rem; }
.attend-day { margin-top: 12px; }
.attend-day h3 { font-size: 0.95rem; margin: 0 0 4px; display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
.attend-day h3 .meta { font-weight: 400; margin: 0; }
.attend-people { list-style: none; padding: 0; margin: 0; border: 1px solid var(--border); border-radius: var(--radius); }
.attend-people li { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 12px; padding: 5px 10px; border-top: 1px solid var(--border); min-width: 0; }
.attend-people li:first-child { border-top: none; }
.attend-people label.check { flex: 1 1 12em; min-width: 0; margin: 0; }
.attend-people label.check input { min-height: auto; flex: none; }
.attend-people .name { overflow-wrap: anywhere; min-width: 0; }
.attend-people li.out .name { text-decoration: line-through; opacity: 0.6; }
.attend-people .when, .attend-people .total { font-size: 0.9rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.attend-people .when { color: var(--muted, inherit); }
.attend-people .total { font-weight: 600; min-width: 6.5em; text-align: right; }

/* Raised hands: the viewers' button, the word over the picture, the hands
   in the bottom left corner (free: cameras and the toolbar are elsewhere);
   the presenter's list. */
#view-hand.on { background: #d69e2e; }
.hand-note {
  position: fixed; left: 50%; top: 40%; transform: translate(-50%, -50%); z-index: 6; margin: 0;
  padding: 14px 22px; border-radius: 14px; background: rgba(20, 22, 27, 0.88); color: #fff;
  font-size: 1.4rem; font-weight: 600; text-align: center; max-width: 80vw; pointer-events: none;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
/* The presenter's capture stopped for now: its last picture stays. */
.held-note {
  position: fixed; left: 50%; top: 12px; transform: translateX(-50%); z-index: 5; margin: 0;
  padding: 6px 14px; border-radius: 999px; background: #b7791f; color: #fff;
  font-size: 0.9rem; font-weight: 600; text-align: center; max-width: calc(100vw - 32px); pointer-events: none;
}
/* The link for viewers' page while nobody presents (relay/linkpage.rs):
   the relay's picture of all of it, fitted into the window over whatever
   was shown before; the waiting said above it. */
.stage .linkpage {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000;
}
.stage:not(.actual) .linkpage, .stage.actual .linkpage { width: calc(100% - var(--chat-shift)); }
.linkpage-note {
  position: fixed; left: 50%; top: 12px; transform: translateX(-50%); z-index: 5; margin: 0;
  padding: 6px 14px; border-radius: 999px; background: rgba(20, 22, 27, 0.8); color: #fff;
  font-size: 0.9rem; font-weight: 600; text-align: center; max-width: calc(100vw - 32px); pointer-events: none;
}
/* A subsession: which, and whether the session sees or hears it now. */
.sub-note {
  position: fixed; left: 50%; top: 12px; transform: translateX(-50%); z-index: 5; margin: 0;
  padding: 6px 14px; border-radius: 999px; background: rgba(20, 22, 27, 0.8); color: #fff;
  font-size: 0.9rem; font-weight: 600; text-align: center; max-width: calc(100vw - 32px); pointer-events: none;
}
.sub-note .shared { color: #f6ad55; }
/* The presenter's subsessions: one line each, its actions at the end. */
.subs { display: flex; flex-direction: column; gap: 8px; }
.subs table { border-collapse: collapse; width: 100%; }
.subs caption { text-align: left; font-weight: 600; padding-bottom: 4px; }
.subs th, .subs td { text-align: left; padding: 4px 8px 4px 0; vertical-align: middle; }
.subs td.actions { display: flex; flex-wrap: wrap; gap: 6px; }
.subs td .names { display: block; color: var(--muted); font-size: 0.9em; }
.subs td .names:empty { display: none; }
#pub-subs-confirm { flex-wrap: wrap; align-items: center; }
.subs td.actions button[aria-pressed="true"] { background: var(--accent); color: var(--accent-text); }
.hands-corner {
  position: fixed; left: 12px; bottom: 12px; z-index: 5; list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 6px; max-width: 40vw;
  transition: opacity 0.6s;
}
/* Hidden by the viewer (👁): shown for 5 s on a change, then fading away. */
.hands-corner.faded { opacity: 0; visibility: hidden; transition: opacity 0.6s, visibility 0s linear 0.6s; }
#view-hands-toggle, #view-cams-toggle { position: relative; }
#view-hands-toggle.off, #view-cams-toggle.off { opacity: 0.6; }
#view-hands-toggle.off::after, #view-cams-toggle.off::after {
  content: ""; position: absolute; left: 20%; right: 20%; top: 50%;
  border-top: 2px solid #fff; transform: rotate(-40deg); pointer-events: none;
}
.hands-corner li {
  padding: 6px 12px; border-radius: 12px; background: rgba(214, 158, 46, 0.92); color: #1a1a1a;
  font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.hands h3 { margin: 0; font-size: 1rem; }
.hands .row button { width: auto; min-height: 30px; padding: 2px 10px; margin: 0; }
.hands li .name { font-weight: 600; }
.hands-corner li.react-only { background: rgba(20, 22, 27, 0.85); color: #fff; }
.hands-corner li:has(.react-icon) { display: flex; align-items: center; gap: 2px; }
.hands-corner li .react-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; padding-left: 4px; }
.hands li .react-icons { display: inline-flex; gap: 4px; flex-wrap: wrap; }
.hands li button.react-clear {
  width: auto; min-width: 34px; min-height: 30px; padding: 2px 6px; margin: 0;
  background: transparent; border-color: var(--border); color: var(--text);
}
/* Reactions (2026-10-13): the drawer behind the viewer's ✋, their icons. */
.react-icon { display: inline-flex; align-items: center; justify-content: center; width: 1.25em; height: 1.25em; line-height: 1; vertical-align: -0.2em; }
.react-icon svg { width: 100%; height: 100%; }
.react-drawer {
  position: fixed; z-index: 6; display: flex; flex-direction: column; gap: 2px; padding: 6px;
  border-radius: 12px; background: rgba(20, 22, 27, 0.94); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
.react-drawer[hidden] { display: none; }
.react-item {
  display: flex; align-items: center; justify-content: flex-start; gap: 10px; width: auto; min-height: 40px;
  margin: 0; padding: 6px 12px; background: transparent; border-color: transparent; color: #fff;
  font-weight: 500; text-align: left; white-space: nowrap;
}
.react-item:hover { background: rgba(255, 255, 255, 0.1); }
.react-item.on { background: rgba(214, 158, 46, 0.35); }
.react-item.on::after { content: "✓"; margin-left: auto; padding-left: 14px; }
.react-item .react-icon { font-size: 1.2rem; }
#viewer:has(#view-share:not([hidden])) .hands-corner { bottom: 68px; }
@media (max-width: 640px) {
  /* The toolbar spans the bottom: the hands go above it. */
  .hands-corner, #viewer:has(#view-share:not([hidden])) .hands-corner { bottom: 68px; }
}

/* Shared files: the presenter's panel on its page, the viewers' over the
   picture (the folder button opens it). */
.files-panel { display: flex; flex-direction: column; gap: 8px; }
.files-panel[hidden] { display: none; }
.files-panel.inline { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px; background: var(--surface); }
.files-panel.inline.drop { border-color: var(--accent); outline: 3px solid var(--focus); }
.files-panel.float {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 6;
  width: min(720px, calc(100vw - 32px)); max-height: min(80vh, calc(100vh - 32px)); overflow: auto;
  background: var(--surface); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
.files-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.files-head h3 { margin: 0; font-size: 1rem; }
.files-head .files-sum { flex: 1; min-width: 8em; }
.files-head button { margin: 0; }
.files-panel .files-close { background: transparent; color: var(--text); border-color: var(--border); }
.files-crumbs { font-size: 0.9rem; color: var(--muted); overflow-wrap: anywhere; }
button.link { background: none; border: none; padding: 0; min-height: 0; color: var(--accent); cursor: pointer; font: inherit; text-align: left; }
button.link:hover { text-decoration: underline; }
.files-tools { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.files-tools button { margin: 0; }
.files-tools .files-where { flex: 1 1 12em; }
.files-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.files-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 6px 4px; border-top: 1px solid var(--border); }
.files-row:first-child { border-top: none; }
.files-row.drop { background: var(--focus); border-radius: 6px; }
.files-row.adding { color: var(--muted); }
.files-name { flex: 1 1 12em; min-width: 0; overflow-wrap: anywhere; }
.files-size { min-width: 5em; text-align: right; }
.files-state { min-width: 7em; }
.files-state.here { color: var(--ok); }
.files-row button, .files-row .button { margin: 0; min-height: 30px; padding: 2px 10px; }
.files-row a.button {
  display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: inherit; text-decoration: none; font-weight: 550;
}
.files-panel button.icon.files-act { background: transparent; color: var(--text); border-color: var(--border); min-width: 30px; }
.files-edit-row { flex-basis: 100%; }
.files-edit { display: flex; flex-direction: row; gap: 8px; flex-wrap: wrap; align-items: center; }
.files-edit input, .files-edit select { flex: 1 1 12em; min-width: 0; }
.files-edit button { margin: 0; }
.files-empty { padding: 8px 4px; }
.files-select { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-height: 34px; }
.files-select button { margin: 0; }
.files-select-all { display: inline-flex; align-items: center; gap: 6px; }
.files-select input[type=checkbox], .files-pick { width: 20px; height: 20px; margin: 0; flex: none; accent-color: var(--accent); }
.files-saving-what { flex: 1 1 12em; }
.files-note { margin: 0; }
#view-files.busy { box-shadow: inset 0 -3px 0 var(--accent); }
/* Something shared this viewer has not looked at yet. */
#view-files { position: relative; }
#view-files.new::after {
  content: ''; position: absolute; top: 2px; right: 2px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--live); box-shadow: 0 0 0 2px rgba(20, 22, 27, 0.9);
}
.files-new {
  font-size: 0.7rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.04em;
  color: #fff; background: var(--live); border-radius: 6px; padding: 1px 6px;
}
@media (max-width: 640px) {
  .files-size, .files-state { min-width: 0; }
  .files-panel.float { padding: 12px; }
}

/* The session's chat (2026-10-18): a panel at the side of the viewer's page
   and the sharing page. Where there is room the page makes way for it (the
   picture, the bar, the presenter's camera move over); on a phone it covers
   the page until closed. */
:root { --chat-w: clamp(300px, 28vw, 400px); }
@media (max-width: 760px) { :root { --chat-w: 100vw; } }
#viewer, #sharing { --chat-shift: 0px; }
#viewer.chat-open, #sharing.chat-open { --chat-shift: var(--chat-w); }
@media (max-width: 760px) { #viewer.chat-open, #sharing.chat-open { --chat-shift: 0px; } }
#sharing.chat-open { padding-right: var(--chat-shift); }
.stage:not(.actual) #view-canvas { width: calc(100% - var(--chat-shift)); }
.stage.actual { padding-right: var(--chat-shift); }
#viewer .overlay { right: var(--chat-shift); }
#view-toolbar { right: calc(12px + var(--chat-shift)); max-width: calc(100% - 24px - var(--chat-shift)); }
#view-cam-presenter { right: calc(12px + var(--chat-shift)); }
#view-cam-presenter.large { width: min(860px, calc(100vw - 24px - var(--chat-shift)), calc((100vh - 96px) * 16 / 9)); }
#viewer .fs-hint, #viewer .held-note, #viewer .sub-note, #viewer .linkpage-note, #viewer .hand-note, #viewer .toast,
#view-share-panel, #view-presenting, #view-talk-panel, #view-name-panel, #view-cam-panel, #view-files-panel {
  left: calc(50% - var(--chat-shift) / 2);
}
.chat-panel {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 5; width: var(--chat-w);
  display: flex; flex-direction: column;
  background: var(--surface); color: var(--text); border-left: 1px solid var(--border);
  box-shadow: -4px 0 24px rgba(0, 0, 0, 0.25);
}
.chat-panel[hidden] { display: none; }
.chat-head { display: flex; align-items: center; gap: 8px; padding: 8px 8px 8px 14px; border-bottom: 1px solid var(--border); }
.chat-head h2 { margin: 0; font-size: 1rem; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-close { width: 36px; min-height: 36px; padding: 0; font-size: 1.4rem; line-height: 1; background: transparent; border-color: transparent; color: var(--text); }
.chat-body { position: relative; flex: 1; min-height: 0; }
.chat-log { position: absolute; inset: 0; overflow-y: auto; overscroll-behavior: contain; padding: 8px 12px 12px; }
.chat-log:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }
.chat-top { text-align: center; margin: 4px 0 10px; }
.chat-msgs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.chat-msg { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.4; padding: 3px 8px; border-radius: 8px; }
.chat-name { font-weight: 650; color: var(--accent); white-space: normal; }
.chat-name::after { content: ":"; }
.chat-msg.presenter .chat-name { color: var(--live); }
.chat-msg.own { background: var(--bg); }
.chat-msg.pending { opacity: 0.65; }
.chat-msg.failed { opacity: 1; box-shadow: inset 3px 0 0 var(--danger); }
.chat-state { white-space: normal; }
.chat-state button.link { font-size: 0.8rem; }
.chat-day {
  align-self: center; margin: 10px 0 4px; padding: 1px 10px; border-radius: 999px;
  font-size: 0.75rem; color: var(--muted); background: var(--bg);
}
/* Scrolled back: new messages no longer bring the panel down; this does. */
.chat-down {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
  width: 40px; height: 40px; min-height: 40px; padding: 0; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: var(--accent-text); border-color: var(--accent);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}
.chat-down[hidden] { display: none; }
.chat-down svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.chat-form { flex-direction: row; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--border); }
.chat-form textarea {
  flex: 1; min-width: 0; resize: none; overflow-y: hidden;
  font: inherit; line-height: 1.4; color: inherit;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg);
  padding: 8px 10px; min-height: 40px;
}
.chat-form textarea:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
.chat-form button { margin: 0; }
.chat-error { margin: 0 12px 8px; }
.chat-error[hidden] { display: none; }
/* 💬 on the bars: on while the panel is open; the messages not read yet. */
.chat-btn { position: relative; }
button.secondary.chat-btn { width: auto; min-height: 36px; padding: 4px 12px; }
button.secondary.chat-btn.on { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
#view-chat-btn.on { background: var(--accent); }
.chat-btn[data-unread]::after {
  content: attr(data-unread); position: absolute; top: -6px; right: -6px;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  background: var(--live); color: #fff; font-size: 0.7rem; font-weight: 700; line-height: 18px; text-align: center;
  box-shadow: 0 0 0 2px rgba(20, 22, 27, 0.9);
}
.toast.clickable { cursor: pointer; }

/* Forms to fill in (2026-10-20): the presenter's switch on a PDF's row,
   the responses' folder only presenters see, a viewer's Respond. */
.files-fill { display: inline-flex; align-items: center; gap: 4px; margin: 0; font-size: 0.85rem; font-weight: 550; }
.files-fill input { width: 18px; height: 18px; min-height: 0; margin: 0; accent-color: var(--accent); }
.files-tag {
  font-size: 0.7rem; font-weight: 650; letter-spacing: 0.02em; white-space: nowrap;
  color: var(--muted); border: 1px solid var(--border); border-radius: 6px; padding: 1px 6px;
}
.files-row button.files-respond { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
/* A shared file the relay plays: its buttons on its row, the banner's. */
.files-media { display: inline-flex; align-items: center; gap: 4px; }
.files-panel button.icon.files-act:disabled { opacity: 0.4; }
#pub-media-note .media-tools { display: inline-flex; gap: 6px; flex: none; min-width: 0; }
/* Its slider, beside its name (the banner's below it on a phone), and the
   playing row's in Shared files. */
#pub-media-note .media-seek-row { display: inline-flex; align-items: center; gap: 8px; flex: 2 1 14em; min-width: 12em; font-weight: 550; }
.media-seek { flex: 1 1 auto; min-width: 6em; margin: 0; accent-color: var(--accent); }
.media-at { flex: none; font-variant-numeric: tabular-nums; white-space: nowrap; }
#pub-media-note .media-at { flex: none; min-width: 0; }
#pub-media-note #pub-media-text { flex: 0 1 auto; }
.files-media .media-seek { width: 9em; flex: 1 1 9em; }
.files-media-where { display: inline-flex; align-items: center; gap: 6px; }
.files-media-where.playing .media-at { color: var(--live); font-weight: 600; }
.files-media { flex-wrap: wrap; }
#pub-media-note .media-tools button { margin: 0; }
/* A private folder (presenters only), and the note inside one. */
.files-tag.private { color: var(--accent); border-color: var(--accent); }
.files-private-here { margin: 0; padding: 6px 8px; border-radius: 8px; background: var(--bg); }
/* The app: the response page inside the viewer's page (no tabs there). */
.respond-overlay { position: fixed; inset: 0; z-index: 2000; background: var(--bg); }
.respond-overlay iframe { width: 100%; height: 100%; border: 0; display: block; }

/* The response page (web/respond.html), laid out as the patient-forms
   system's form page: the bar, then the document fitted to the width. */
body.respond { display: flex; flex-direction: column; height: 100vh; height: 100dvh; }
.respond-bar {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.respond-bar button { margin: 0; min-height: 44px; padding: 8px 18px; }
.respond-what { flex: 1; min-width: 0; text-align: center; }
.respond-what b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.respond-name { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 16px; background: var(--surface); border-bottom: 1px solid var(--border); }
.respond-name label { margin: 0; }
.respond-name input { flex: 1 1 12em; min-width: 0; max-width: 24em; }
.respond-msg { margin: 8px 16px; padding: 8px 12px; border-radius: 8px; background: color-mix(in srgb, var(--danger) 15%, var(--surface)); color: var(--text); }
.respond-msg.ok { background: color-mix(in srgb, var(--ok) 15%, var(--surface)); }
#respond-wrap { flex: 1; position: relative; min-height: 0; }
#respond-container { position: absolute; inset: 0; overflow: auto; background: #cfd4dc; }
#respond-container .pdfViewer .page { margin: 12px auto; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); }
/* PDF.js sizes its page frame for content-box (it sets border-box itself
   where it wants it): the page's border-box would shift the fields off
   the drawing (seen in the patient-forms system). :where() keeps every
   PDF.js rule ahead. */
:where(.pdfViewer, .pdfViewer *) { box-sizing: content-box; }
body.respond.sending #respond-wrap { pointer-events: none; opacity: 0.6; }
/* The page's own fields' look stays out of the PDF's: its fields as PDF.js
   draws them, on paper (light), whatever the page's theme. */
.pdfViewer { color-scheme: light; }
.annotationLayer :is(input, select, textarea, button) {
  min-height: revert; padding: revert; border-radius: revert;
  background-color: revert; color: revert; font-weight: revert;
}
/* The panel's changes kept on this device until the relay has them. */
.files-waiting { margin: 0; padding: 6px 10px; border-radius: 8px; font-size: 0.85rem; background: color-mix(in srgb, var(--accent) 14%, var(--surface)); }
/* The standby page: its switch and its text, below it. */
.standby { display: flex; flex-direction: column; gap: 8px; }
.standby[hidden] { display: none; }
.standby textarea {
  width: 100%; box-sizing: border-box; resize: vertical;
  font: inherit; line-height: 1.4; color: inherit;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg);
  padding: 8px 10px; min-height: 48px;
}
.standby textarea:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
/* Viewers' microphone and camera held while the standby page is shown. */
.toolbar button.held { opacity: 0.45; }
/* The microphone test (/mictest): what to say, the level, the results. */
:root { --mictest-with: #dd6b20; --mictest-plain: #2f6fed; }
@media (prefers-color-scheme: dark) { :root { --mictest-plain: #5b8cff; } }
.mictest-device { display: block; width: 100%; }
.mictest-run { margin-top: 16px; }
.mictest-say { font-size: 1.25rem; font-weight: 600; margin: 4px 0 10px; }
.mictest-meter { height: 10px; border-radius: 5px; background: var(--bg); border: 1px solid var(--border); overflow: hidden; }
.mictest-meter > div { height: 100%; width: 0; background: var(--ok); transition: width 80ms linear; }
.mictest-error { color: var(--danger); }
.mictest-chart { display: block; width: 100%; margin: 8px 0 4px; }
.mictest-key { display: inline-block; width: 14px; height: 3px; vertical-align: middle; margin: 0 4px 0 8px; }
.mictest-key.with { background: var(--mictest-with); }
.mictest-key.plain { background: var(--mictest-plain); }
.mictest-wide { overflow-x: auto; }
.mictest-table { border-collapse: collapse; font-size: 0.85rem; width: 100%; margin-top: 8px; }
.mictest-table th, .mictest-table td { text-align: left; vertical-align: top; padding: 4px 8px 4px 0; border-bottom: 1px solid var(--border); }
.mictest-table td.mictest-what { color: var(--muted); width: 34%; }
.mictest-table th:nth-child(2) { color: var(--mictest-with); }
.mictest-table th:nth-child(3) { color: var(--mictest-plain); }
.mictest-listen { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.mictest-listen a { color: var(--accent); }
