/* ── Foundations ──────────────────────────────────────────────────────── */

:root {
  --bg: #0a0b12;
  --surface: rgba(255, 255, 255, 0.035);
  --surface-hi: rgba(255, 255, 255, 0.06);
  --line: rgba(255, 255, 255, 0.09);
  --line-hi: rgba(255, 255, 255, 0.16);
  --text: #f4f6fb;
  /* Secondary text. This used to be #8b91a8, which against the backdrop was
     under 4:1 and read as switched off rather than as quieter. */
  --muted: #b9c0d4;
  --accent: #7c6cff;
  --accent-2: #22d3ee;
  --danger: #ff5a72;
  --ok: #3ddc97;
  --radius: 16px;
  --shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.7);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/*
 * The interface face.
 *
 * Apple's own San Francisco cannot be shipped here - its licence covers use on
 * Apple platforms, and Beacon runs on Windows and Linux. `-apple-system` only
 * resolves on a Mac, so on every machine that actually runs this it would fall
 * through to Segoe and nothing would change.
 *
 * Inter is the answer to exactly that problem: drawn as a free stand-in for San
 * Francisco, same humanist-grotesque skeleton, same tall x-height and open
 * apertures, and under the SIL Open Font License so it can be bundled. One
 * variable file covers every weight the app uses, and it is loaded from disk
 * rather than a CDN, so it is there before the first paint and works offline.
 */
@font-face {
  font-family: "Inter";
  src: url("fonts/InterVariable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: block;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: "Inter", -apple-system, BlinkMacSystemFont,
               "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
  /* Inter's own alternates for a rounder, less mechanical lowercase - a single
     storey a is what makes it read as San Francisco rather than as Helvetica. */
  font-feature-settings: "cv05" 1, "cv08" 1, "ss03" 1;
  font-size: 15px;
  /* A touch above regular. Inter is a variable font, so 450 is a real weight
     rather than the browser smearing 400 into something bolder. */
  font-weight: 450;
  line-height: 1.55;
  overflow: hidden;
  user-select: none;
  -webkit-font-smoothing: antialiased;
}

/* Slow drifting light, so the dark UI is not flat. */
.aurora {
  position: fixed;
  inset: -30%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(38% 44% at 22% 18%, rgba(124, 108, 255, 0.30), transparent 70%),
    radial-gradient(34% 40% at 82% 26%, rgba(34, 211, 238, 0.18), transparent 70%),
    radial-gradient(46% 50% at 60% 92%, rgba(180, 90, 255, 0.14), transparent 70%);
  filter: blur(28px);
  animation: drift 34s var(--ease) infinite alternate;
}

@keyframes drift {
  from { transform: translate3d(-2%, -1%, 0) scale(1); }
  to   { transform: translate3d(3%, 2%, 0) scale(1.1); }
}

h1, h2, h3 { margin: 0; font-weight: 620; letter-spacing: -0.02em; }
h1 { font-size: 27px; }
h2 { font-size: 21px; }
h3 { font-size: 14px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
p  { margin: 0; }

.sub { color: var(--muted); font-size: 14px; }
.muted { color: var(--muted); font-weight: 450; }

code {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 0.92em;
  background: var(--surface-hi);
  padding: 1px 5px;
  border-radius: 5px;
}

/* ── View switching ───────────────────────────────────────────────────── */

.view {
  position: relative;
  z-index: 1;
  display: none;
  flex-direction: column;
  height: 100%;
  padding: 26px 30px;
  gap: 22px;
  animation: rise 0.4s var(--ease) both;
}

.view.active { display: flex; }
.view.centered { align-items: center; justify-content: center; }

@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ── Buttons & inputs ─────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: 11px;
  padding: 11px 20px;
  font: inherit;
  font-weight: 560;
  color: var(--text);
  background: var(--surface-hi);
  cursor: pointer;
  transition: transform 0.16s var(--ease), filter 0.16s, background 0.16s, border-color 0.16s;
}

.btn:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.12); }
.btn:active:not(:disabled) { transform: translateY(0) scale(0.985); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

.btn.primary {
  background: linear-gradient(120deg, var(--accent), #a06cff 55%, var(--accent-2));
  color: #0b0a18;
  font-weight: 640;
  box-shadow: 0 10px 26px -12px rgba(124, 108, 255, 0.9);
}

/* Leave room and Stop sharing. These end something, so they should read at a
   glance rather than sitting back as a tinted outline: solid fill, white text,
   the same visual weight as the primary button. */
.btn.danger {
  background: linear-gradient(180deg, #f2415c, #d92b48);
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.18);
  font-weight: 640;
  box-shadow: 0 8px 20px -10px rgba(217, 43, 72, 0.9);
}
.btn.danger:hover:not(:disabled) {
  background: linear-gradient(180deg, #ff5570, #e63552);
  border-color: rgba(255, 255, 255, 0.3);
}
.btn.ghost { background: transparent; border-color: var(--line); color: var(--muted); }
.btn.ghost:hover:not(:disabled) { border-color: var(--line-hi); color: var(--text); }
.btn.small { padding: 7px 14px; font-size: 13.5px; }
.btn.block { width: 100%; }

.btn svg { width: 17px; height: 17px; }

.icon-btn {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  transition: color 0.16s, border-color 0.16s, background 0.16s;
}

.icon-btn:hover { color: var(--text); border-color: var(--line-hi); background: var(--surface-hi); }
.icon-btn svg { width: 19px; height: 19px; }
.icon-btn.ghost { background: transparent; }

/* The settings cog is the only way into Settings, and at --muted against the
   backdrop it was hard to find. It gets full white and a heavier stroke. */
#btn-settings { color: #ffffff; }
#btn-settings svg { stroke-width: 2.1; }

.link {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--accent-2);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.input {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: rgba(0, 0, 0, 0.28);
  color: var(--text);
  font: inherit;
  padding: 11px 14px;
  outline: none;
  user-select: text;
  transition: border-color 0.16s, box-shadow 0.16s;
}

.input::placeholder { color: #5e6479; }
.input:focus {
  border-color: rgba(124, 108, 255, 0.7);
  box-shadow: 0 0 0 3.5px rgba(124, 108, 255, 0.16);
}

.input.big {
  font-size: 19px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  letter-spacing: 0.03em;
  text-align: center;
  padding: 15px;
}

label {
  display: block;
  font-size: 12.5px;
  font-weight: 560;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 7px;
}

.segmented {
  display: inline-flex;
  padding: 3px;
  gap: 3px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: rgba(0, 0, 0, 0.28);
}

.segmented button {
  border: 0;
  border-radius: 8px;
  padding: 8px 15px;
  font: inherit;
  font-size: 13.5px;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  transition: color 0.16s, background 0.16s;
}

.segmented button:hover { color: var(--text); }
.segmented button.active { background: var(--surface-hi); color: var(--text); }
.segmented.small button { padding: 6px 11px; font-size: 12.5px; }

/* ── Home ─────────────────────────────────────────────────────────────── */

.brand {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-top: 14px;
}

.brand h1 { line-height: 1.15; }
.brand > div:nth-child(2) { flex: 1; }
.tagline { color: var(--muted); font-size: 14px; }

.mark {
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  border-radius: 15px;
  color: #0b0a18;
  background: linear-gradient(140deg, var(--accent), var(--accent-2));
  box-shadow: 0 14px 34px -14px rgba(124, 108, 255, 0.95);
}

.mark svg { width: 27px; height: 27px; }

.choices {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px;
  align-content: center;
}

.choice {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 11px;
  text-align: left;
  padding: 32px;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: var(--surface);
  backdrop-filter: blur(20px);
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: transform 0.28s var(--ease), border-color 0.28s, background 0.28s;
}

.choice:hover {
  transform: translateY(-5px);
  border-color: var(--line-hi);
  background: var(--surface-hi);
}

.choice-glow {
  position: absolute;
  inset: -55% 30% 55% -30%;
  background: radial-gradient(circle, rgba(124, 108, 255, 0.35), transparent 62%);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
}

.choice:hover .choice-glow { opacity: 1; }
.choice:nth-child(2) .choice-glow { background: radial-gradient(circle, rgba(34, 211, 238, 0.28), transparent 62%); }

.choice-icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 15px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.05);
  color: var(--accent-2);
  margin-bottom: 6px;
}

.choice:first-child .choice-icon { color: #a99cff; }
.choice-icon svg { width: 26px; height: 26px; }
.choice h2 { font-size: 22px; }
.choice p { color: var(--muted); font-size: 14px; max-width: 30ch; }

.choice-go {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  font-size: 14px;
  font-weight: 580;
  color: var(--accent-2);
}

.choice-go svg { width: 16px; height: 16px; transition: transform 0.24s var(--ease); }
.choice:hover .choice-go svg { transform: translateX(4px); }

.relay-line {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  color: var(--muted);
  padding-bottom: 4px;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--muted);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.05);
}

.dot.on { background: var(--ok); box-shadow: 0 0 0 3px rgba(61, 220, 151, 0.14); }

/* ── Source picker ────────────────────────────────────────────────────── */

.topbar { display: flex; align-items: center; gap: 16px; }
.topbar > div { flex: 1; }
.topbar .back { flex: none; }

.tabs { display: flex; gap: 7px; }

.tab {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 16px;
  font: inherit;
  font-size: 13px;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  transition: all 0.16s;
}

.tab:hover { color: var(--text); border-color: var(--line-hi); }
.tab.active { background: var(--surface-hi); color: var(--text); border-color: var(--line-hi); }

.source-grid {
  flex: 1;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 14px;
  padding: 4px;
  align-content: start;
}

.source {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  text-align: left;
  font: inherit;
  color: inherit;
  transition: transform 0.2s var(--ease), border-color 0.2s, box-shadow 0.2s;
}

.source:hover { transform: translateY(-3px); border-color: var(--line-hi); }

.source.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(124, 108, 255, 0.45), 0 16px 34px -18px rgba(124, 108, 255, 0.8);
}

.source img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  background: #05060b;
}

/* Shown only when the preload bridge failed and the app cannot run at all. */
.fatal {
  max-width: 460px;
  margin: 18vh auto 0;
  padding: 28px 30px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface);
  text-align: center;
}

.fatal h2 { margin-bottom: 10px; }
.fatal p { color: var(--muted); font-size: 14px; line-height: 1.6; }

/* Stands in for a preview Windows would not hand over. Same box as .source img
   so the grid keeps its rhythm. */
.source-nopreview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 16 / 10;
  background: #05060b;
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.04em;
}

.source-meta { padding: 10px 12px; display: flex; align-items: center; gap: 8px; }

.source-name {
  flex: 1;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.badge {
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 8px;
}

.setup-bar {
  display: flex;
  /* Was flex-end, which lined the controls up along the bottom. That worked
     while every field was the same height; the quality field is taller (a
     caption under the slider) and dragged Size and Frames down with it, so the
     row read as broken. Aligned from the top instead, with each field a fixed
     height, so the three sit level whatever the caption says. */
  align-items: flex-start;
  /* Three choices, a summary line, the sound box and the button do not fit on
     one row at the minimum window width, so let it fold rather than clip. */
  flex-wrap: wrap;
  gap: 20px;
  row-gap: 14px;
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface);
  backdrop-filter: blur(20px);
}

.setup-bar .btn.primary { margin-left: auto; padding: 13px 26px; }

/* Every field the same height, so the controls line up across the row. */
.setup-bar .field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-height: 104px;
}
.setup-bar .field > label { margin: 0; }
.setup-bar .field .quality-slider { margin: 2px 0 0; }

/* What the current choices add up to, in bandwidth per viewer. */
.quality-note {
  margin: 0;
  flex-basis: 100%;
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ── Hosting ──────────────────────────────────────────────────────────── */

.live-layout {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 20px;
  min-height: 0;
}

.stage {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

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

.live-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 680;
  letter-spacing: 0.1em;
  color: #ffb3bf;
  background: rgba(255, 90, 114, 0.13);
  border: 1px solid rgba(255, 90, 114, 0.3);
  border-radius: 999px;
  padding: 5px 13px;
}

.rec-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
  animation: pulse 1.9s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(255, 90, 114, 0.55); }
  50%      { opacity: 0.65; box-shadow: 0 0 0 6px rgba(255, 90, 114, 0); }
}

.btn.primary .rec-dot { background: #2a1840; animation: none; }

.stat-inline {
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

video {
  width: 100%;
  height: 100%;
  min-height: 0;
  object-fit: contain;
  background: #05060b;
  border: 1px solid var(--line);
  border-radius: 16px;
}

.panel {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
}

.code-card {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface);
  backdrop-filter: blur(20px);
  padding: 18px;
}

.code {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 21px;
  letter-spacing: 0.02em;
  text-align: center;
  padding: 14px 8px;
  margin-bottom: 12px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.34);
  border: 1px dashed var(--line-hi);
  color: #cfc7ff;
  user-select: text;
  word-break: break-all;
}

.hint { font-size: 12.5px; color: var(--muted); margin-top: 10px; }

.viewers {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface);
  padding: 16px;
}

.viewers-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }

.count {
  min-width: 26px;
  text-align: center;
  font-size: 12.5px;
  font-weight: 620;
  border-radius: 999px;
  padding: 2px 9px;
  background: var(--surface-hi);
  font-variant-numeric: tabular-nums;
}

.viewer-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1; }

.viewer-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 4px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  font-size: 13.5px;
  animation: rise 0.3s var(--ease) both;
}

.viewer-list li:last-child { border-bottom: 0; }
.viewer-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viewer-state { font-size: 11.5px; color: var(--muted); }
.viewer-state.live { color: var(--ok); }

.avatar {
  display: grid;
  place-items: center;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  font-size: 11.5px;
  font-weight: 640;
  color: #0b0a18;
  background: linear-gradient(140deg, var(--accent), var(--accent-2));
}

.kick {
  border: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 6px;
  opacity: 0;
  transition: opacity 0.16s, color 0.16s;
}

.viewer-list li:hover .kick { opacity: 1; }
.kick:hover { color: var(--danger); }

.empty { font-size: 13px; color: var(--muted); text-align: center; padding: 18px 6px; }

/* ── The people panel, shrunk to a rail ───────────────────────────────────
 *
 * Everything with words in it goes, and the faces stay. At this width the
 * avatar has to be the control as well as the label, so it grows, takes a
 * pointer, and says who is sharing with a ring rather than a separate dot -
 * there is no room for two marks on one 34px circle.
 *
 * The column width lives on the grid parent, which is why this reaches up with
 * :has() rather than setting a width on the panel: a grid item cannot argue
 * with its track.
 */
.room-layout:has(.panel.collapsed) { grid-template-columns: 1fr 76px; }

.panel.collapsed { gap: 10px; }
.panel.collapsed .viewers { padding: 12px 8px; }
.panel.collapsed .viewers-head { justify-content: center; margin-bottom: 10px; }
.panel.collapsed .viewers-head h3,
.panel.collapsed .viewers-head .count,
.panel.collapsed .viewer-name,
.panel.collapsed .watch-btn,
.panel.collapsed .kick,
.panel.collapsed .quality-live,
.panel.collapsed .btn-label { display: none; }

.panel.collapsed .viewer-list li {
  justify-content: center;
  gap: 0;
  padding: 7px 0;
  border-bottom: 0;
}

/* The sharing dot is redundant once the avatar itself turns red. */
.panel.collapsed .viewer-list li > .rec-dot { display: none; }

.panel.collapsed .avatar { width: 34px; height: 34px; font-size: 13.5px; }
.panel.collapsed .self-controls { padding: 10px 8px; }
.panel.collapsed #btn-share,
.panel.collapsed #btn-leave-room { padding: 10px 0; justify-content: center; }

.panel:not(.collapsed) .leave-mark { display: none; }
.leave-mark { width: 17px; height: 17px; }

/* The panel toggle points the way it will move the edge. */
.panel-toggle svg { width: 15px; height: 15px; }
.panel-toggle.on svg { transform: rotate(180deg); }

/* ── An avatar that is also a button ──────────────────────────────────────
 *
 * Red means this person is sharing. It is the same red as the recording dot
 * elsewhere, deliberately: one colour for "a picture is going out" wherever it
 * appears. The ring rather than a fill keeps the initial readable.
 */
button.avatar {
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  transition: transform 0.16s var(--ease), box-shadow 0.16s var(--ease);
}

button.avatar:hover { transform: scale(1.08); }
button.avatar:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px; }

.avatar.sharing {
  box-shadow: 0 0 0 2px var(--bg, #0a0b12), 0 0 0 4px var(--danger);
}

/* Already on screen: the accent, matching the focus and fullscreen buttons, so
   "you are looking at this" reads the same everywhere. */
.avatar.watching {
  box-shadow: 0 0 0 2px var(--bg, #0a0b12), 0 0 0 4px var(--accent-2);
}
/* The compact copy of the same three choices, in the room sidebar. Each
   segmented row fills the column so the buttons stay big enough to hit. */
.quality-live { display: flex; flex-direction: column; gap: 6px; }
.quality-live label { margin-bottom: 0; }
.quality-live label:not(:first-child) { margin-top: 6px; }
.quality-live .segmented { display: flex; }
.quality-live .segmented button { flex: 1; padding: 6px 4px; }
.quality-live .quality-note { margin-top: 8px; line-height: 1.45; }

/* ── Join ─────────────────────────────────────────────────────────────── */

.card {
  border: 1px solid var(--line);
  border-radius: 20px;
  background: var(--surface);
  backdrop-filter: blur(22px);
  box-shadow: var(--shadow);
  padding: 32px;
}

.join-card { width: 400px; display: flex; flex-direction: column; gap: 12px; }
.join-card h2 { margin-bottom: 2px; }
.join-card .sub { margin-bottom: 8px; }
.join-card .btn { margin-top: 6px; }

.floating-back { position: absolute; top: 26px; left: 30px; }

.error {
  font-size: 13px;
  color: #ffa6b3;
  background: rgba(255, 90, 114, 0.1);
  border: 1px solid rgba(255, 90, 114, 0.26);
  border-radius: 10px;
  padding: 9px 12px;
  text-align: center;
}

/* ── Watching ─────────────────────────────────────────────────────────── */

.view.watch { padding: 0; }
.view.watch video { border: 0; border-radius: 0; height: 100%; }

.watch-overlay {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 22px;
  background: linear-gradient(transparent, rgba(5, 6, 11, 0.92));
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}

.view.watch:hover .watch-overlay,
.watch-overlay.show { opacity: 1; transform: none; }

.watch-status { display: flex; align-items: center; gap: 11px; font-size: 14px; color: var(--muted); }
.watch-controls { margin-left: auto; display: flex; align-items: center; gap: 12px; }

/* Centred "connecting" state, before any frame has arrived. */
.view.watch.connecting .watch-overlay {
  inset: 0;
  flex-direction: column;
  justify-content: center;
  background: rgba(5, 6, 11, 0.94);
  opacity: 1;
  transform: none;
}

.view.watch.connecting .watch-status { font-size: 16px; color: var(--text); }

.spinner {
  width: 17px;
  height: 17px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.16);
  border-top-color: var(--accent-2);
  animation: spin 0.9s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ── Modal & toast ────────────────────────────────────────────────────── */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  background: rgba(5, 6, 11, 0.72);
  backdrop-filter: blur(6px);
  animation: rise 0.24s var(--ease) both;
}

.modal-backdrop[hidden] { display: none; }

.modal {
  width: 430px;
  border: 1px solid var(--line);
  border-radius: 20px;
  background: #12141f;
  box-shadow: var(--shadow);
  padding: 28px;
  /* Cap the height so a long settings panel never runs off the screen; the body
     scrolls inside while the header and the Save/Cancel row stay put. */
  display: flex;
  flex-direction: column;
  max-height: min(88vh, 760px);
}

/* The scrolling middle of a modal. Negative margins + matching padding let the
   scrollbar sit at the panel's edge instead of biting into the content, and the
   padding keeps focus rings and the glass lens from being clipped at the seam. */
.modal-scroll {
  overflow-y: auto;
  overflow-x: hidden;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0 -28px;
  padding: 0 28px;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.modal-scroll::-webkit-scrollbar { width: 10px; }
.modal-scroll::-webkit-scrollbar-thumb {
  background: var(--line);
  border-radius: 10px;
  border: 3px solid transparent;
  background-clip: padding-box;
}
.modal-scroll::-webkit-scrollbar-thumb:hover { background: var(--muted, #6b7280); background-clip: padding-box; }

.modal .sub { margin: 8px 0 20px; }
.modal label { margin-top: 4px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; flex: 0 0 auto; }

.toast {
  position: fixed;
  left: 50%;
  bottom: 34px;
  z-index: 30;
  transform: translateX(-50%);
  padding: 11px 20px;
  border-radius: 12px;
  border: 1px solid var(--line-hi);
  background: #161826;
  box-shadow: var(--shadow);
  font-size: 14px;
  animation: toast-in 0.3s var(--ease) both;
}

.toast[hidden] { display: none; }
.toast.bad { border-color: rgba(255, 90, 114, 0.4); color: #ffa6b3; }

@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, 14px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ── Scrollbars ───────────────────────────────────────────────────────── */

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.1);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.2); background-clip: content-box; }
::-webkit-scrollbar-track { background: transparent; }

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

/* ── Room: video grid ─────────────────────────────────────────────────── */

.room-layout {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 20px;
  min-height: 0;
}

.grid-area {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  min-height: 0;
}

.room-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 14px;
}

.room-badge b {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  color: #cfc7ff;
  font-weight: 600;
  user-select: text;
}

.stage-head .stat-inline { margin-left: auto; }

/* Columns and rows are solved in layoutGrid() so the pictures come out as
   large as the space allows; the grid only needs to hold the result. */
.video-grid {
  flex: 1;
  min-height: 0;
  display: grid;
  gap: 12px;
  align-content: stretch;
  justify-content: stretch;
  overflow: hidden;
  padding: 2px;
}

.video-grid[hidden] { display: none; }

.tile {
  position: relative;
  min-height: 0;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
  background: #05060b;
  animation: rise 0.35s var(--ease) both;
}



.tile video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border: 0;
  border-radius: 0;
  background: #05060b;
  display: block;
}

.tile:fullscreen { border: 0; border-radius: 0; aspect-ratio: auto; }
.tile:fullscreen video { object-fit: contain; }

.tile-waiting {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  font-size: 14px;
  color: var(--muted);
  background: rgba(5, 6, 11, 0.9);
}

.tile-waiting[hidden] { display: none; }

.tile-bar {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  background: linear-gradient(transparent, rgba(5, 6, 11, 0.92));
  opacity: 0;
  transition: opacity 0.25s var(--ease);

  /* ABOVE the drawing canvas, which is z-index 1 and covers the whole tile.
     Without this the bar sits underneath it, and once drawing is switched on
     every click meant for fullscreen, focus, volume or stop-watching lands on
     the canvas and draws a dot instead. Below .tile-draw (3), which must stay
     on top of everything. */
  z-index: 2;

  /* But the bar is a full-width strip across the bottom of the picture, so
     claiming those clicks wholesale would carve a dead band out of the drawing
     surface. The bar itself is transparent to the pointer and only its buttons
     take clicks back - draw over the gradient, press the buttons. */
  pointer-events: none;
}

.tile-bar > button { pointer-events: auto; }

.tile:hover .tile-bar, .tile:focus-within .tile-bar { opacity: 1; }

.tile-name {
  font-size: 13.5px;
  font-weight: 560;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 40%;
}

.tile-stat {
  flex: 1;
  font-size: 11.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.icon-btn.tiny {
  width: 27px;
  height: 27px;
  border-radius: 8px;
  font-size: 12px;
  flex: none;
}

.icon-btn.on {
  color: var(--danger);
  border-color: rgba(255, 90, 114, 0.35);
  background: rgba(255, 90, 114, 0.12);
}

.grid-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 1px dashed var(--line-hi);
  border-radius: 16px;
}

.grid-empty[hidden] { display: none; }
.grid-empty b { color: var(--text); font-weight: 600; }

.self-controls {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface);
}

.check {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  margin: 0;
  cursor: pointer;
  user-select: none;
}

.check[hidden] { display: none; }
.check input { width: 17px; height: 17px; accent-color: var(--accent); cursor: pointer; }
.check:hover { color: var(--text); }

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

@media (max-width: 1040px) {
  .choices { grid-template-columns: 1fr; }
  .live-layout, .room-layout { grid-template-columns: 1fr 280px; }
}

/* ── Stream audience ──────────────────────────────────────────────────── */

/* Overlapping initials in the corner of a stream: who is watching it. */
.tile-watchers {
  position: absolute;
  top: 10px;
  right: 12px;
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  pointer-events: auto;
  z-index: 2;
}

.tile-watchers[hidden] { display: none; }

.watcher {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin-left: -9px;          /* the overlap */
  border-radius: 50%;
  border: 2px solid rgba(8, 9, 16, 0.92);
  background: hsl(var(--tint, 250deg) 62% 52%);
  color: #0b0a18;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1;
  box-shadow: 0 3px 10px -3px rgba(0, 0, 0, 0.85);
  transition: transform 0.16s var(--ease);
}

/* row-reverse stacks the first person on top, which reads better. */
.watcher:hover { transform: translateY(-2px) scale(1.08); z-index: 1; }

.watcher.more {
  background: rgba(20, 22, 34, 0.95);
  color: var(--text);
  font-size: 10.5px;
}

/* ── Per-stream volume ────────────────────────────────────────────────── */

/* ── Your own stream's options ────────────────────────────────────────────
 *
 * Same material and placement as the volume popover, because it is the same
 * gesture on the same surface - right-click a picture - and two different
 * looking panels would suggest two different mechanisms.
 */
.stream-menu {
  position: fixed;
  z-index: 40;
  width: 252px;
  padding: 13px 15px 14px;
  border: 1px solid var(--line-hi);
  border-radius: 14px;
  background: rgba(16, 18, 28, 0.97);
  box-shadow: var(--shadow);
  animation: rise 0.16s var(--ease) both;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* The glass rule sets .card and friends to position: relative, which once put
   the volume popover into the page flow and far off screen. This panel is
   positioned the same way, so it needs the same exemption. */
:root[data-glass="on"] .stream-menu { position: fixed; }

.stream-menu-title {
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 4px;
}

.stream-menu label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 4px 0 0;
}

.stream-menu .segmented { display: flex; }
.stream-menu .segmented button { flex: 1; padding: 6px 4px; }
.stream-menu .quality-note { margin: 8px 0 4px; line-height: 1.45; }

.stream-menu-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  text-align: left;
  padding: 9px 10px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--surface-hi);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.16s, border-color 0.16s, color 0.16s;
}

.stream-menu-item svg { width: 17px; height: 17px; flex: 0 0 auto; }
.stream-menu-item:hover { background: rgba(255, 255, 255, 0.08); border-color: var(--line-hi); }
.stream-menu-item:disabled { opacity: 0.55; cursor: default; }
.stream-menu-item.on { color: var(--accent-2); border-color: rgba(34, 211, 238, 0.4); }
.stream-menu-item.danger { color: var(--danger); }
.stream-menu-item.danger:hover { background: rgba(239, 68, 68, 0.14); border-color: rgba(239, 68, 68, 0.4); }

.tile-opts { font-size: 13px; line-height: 1; }

.volume-popover {
  position: fixed;
  z-index: 40;
  min-width: 218px;
  padding: 13px 15px 15px;
  border: 1px solid var(--line-hi);
  border-radius: 14px;
  background: rgba(16, 18, 28, 0.97);
  box-shadow: var(--shadow);
  animation: rise 0.16s var(--ease) both;
}

.volume-name {
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.volume-row { display: flex; align-items: center; gap: 11px; }

.volume-readout {
  min-width: 44px;
  text-align: right;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.volume-popover.silent .volume-readout { color: var(--danger); }

.volume-slider {
  flex: 1;
  appearance: none;
  height: 5px;
  border-radius: 999px;
  outline: none;
  cursor: pointer;
  /* Filled portion up to the thumb, track beyond it. */
  background: linear-gradient(
    to right,
    var(--accent) 0 var(--fill, 100%),
    rgba(255, 255, 255, 0.14) var(--fill, 100%) 100%);
}

.volume-popover.silent .volume-slider {
  background: rgba(255, 255, 255, 0.14);
}

.volume-slider::-webkit-slider-thumb {
  appearance: none;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #fff;
  border: 0;
  box-shadow: 0 2px 7px -1px rgba(0, 0, 0, 0.8);
  cursor: grab;
}

.volume-slider:active::-webkit-slider-thumb { cursor: grabbing; transform: scale(1.1); }

/* ── Access code ──────────────────────────────────────────────────────── */

.welcome-mark {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  margin: 0 auto 14px;
  border-radius: 14px;
  color: #0b0a18;
  background: linear-gradient(140deg, var(--accent), var(--accent-2));
  box-shadow: 0 12px 30px -14px rgba(124, 108, 255, 0.95);
}

.welcome-mark svg { width: 24px; height: 24px; }

#view-welcome .card { text-align: center; }
#view-welcome .sub { margin-bottom: 4px; }

/* ── Agreement ────────────────────────────────────────────────────────── */

.agree {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 4px 0 14px;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: rgba(0, 0, 0, 0.22);
  text-transform: none;
  letter-spacing: 0;
  font-size: 13.5px;
  font-weight: 400;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}

.agree input {
  width: 17px;
  height: 17px;
  margin: 1px 0 0;
  flex: none;
  accent-color: var(--accent);
  cursor: pointer;
}

.agree .link { font-size: inherit; }

/* The agreement itself: a tall, scrollable, monospaced read. */
.terms-modal {
  width: min(860px, 92vw);
  max-width: none;
  display: flex;
  flex-direction: column;
  max-height: 88vh;
}

.terms-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin: 12px 0 4px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.34);
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.62;
  color: #cfd4e8;
  white-space: pre-wrap;
  word-break: break-word;
  user-select: text;
  text-align: left;
}

/* ── Update banner ────────────────────────────────────────────────────── */

.update-chip {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 30;
  padding: 9px 16px;
  border-radius: 999px;
  border: 1px solid var(--line-hi);
  background: rgba(20, 22, 34, 0.97);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 560;
  cursor: pointer;
  box-shadow: 0 16px 36px -18px rgba(0, 0, 0, 0.9);
  animation: rise 0.25s var(--ease) both;
}

.update-chip[hidden] { display: none; }
.update-chip:disabled { cursor: default; }

.update-chip.working { color: var(--muted); }

.update-chip.ready {
  border-color: rgba(61, 220, 151, 0.45);
  background: rgba(61, 220, 151, 0.14);
  color: #9df3cd;
}
.update-chip.ready:hover { filter: brightness(1.15); }

.update-chip.bad {
  border-color: rgba(255, 90, 114, 0.45);
  background: rgba(255, 90, 114, 0.14);
  color: #ffa6b3;
}

/* ── Version, updates and the echo option ─────────────────────────────── */

.version {
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  letter-spacing: 0.01em;
  vertical-align: middle;
  margin-left: 4px;
}

.about {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.about h3 {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted);
}

.about-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 8px;
}

.about-version {
  font-size: 14px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

#update-status { margin: 0; }

/* Sound options sit together so the echo choice reads as part of sharing
   sound rather than as an unrelated switch. */
.audio-fields { display: flex; flex-direction: column; gap: 8px; max-width: 300px; }
/* display:flex above beats the hidden attribute, which is how "Share sound too"
   showed on machines that cannot share sound at all (macOS, Linux without
   PipeWire, every phone). */
.audio-fields[hidden] { display: none; }

/* The zoom row's magnifier in the stream menu, sized to the text beside it. */
.stream-menu label svg { width: 14px; height: 14px; vertical-align: -2px; }
.audio-fields .hint { margin: 0; font-size: 12px; line-height: 1.45; }

/* ── Getting back in after a blip ─────────────────────────────────────────
 *
 * Amber, not red: nothing has gone wrong yet, the app is handling it. The line
 * about the capture matters - the fear when a call drops is that your share
 * died with it, and saying otherwise is most of the reassurance.
 */
.reconnect-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-radius: 12px;
  border: 1px solid rgba(245, 158, 11, 0.35);
  background: rgba(245, 158, 11, 0.12);
  color: #fcd34d;
  font-size: 13px;
  animation: rise 0.25s var(--ease) both;
}

.reconnect-banner[hidden] { display: none; }
.reconnect-note { color: var(--muted); font-size: 12.5px; margin-left: auto; }

.reconnect-spinner {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 2px solid rgba(245, 158, 11, 0.3);
  border-top-color: #fcd34d;
  animation: reconnect-spin 0.8s linear infinite;
}

@keyframes reconnect-spin { to { transform: rotate(360deg); } }

/* ── One stream filling the stage ─────────────────────────────────────── */

/* .icon-btn.on is the red "muted" treatment, which would read as a warning
   here. Focus is a view choice, not a problem, so it takes the accent. */
.tile-focus.on {
  color: var(--accent-2);
  border-color: rgba(34, 211, 238, 0.4);
  background: rgba(34, 211, 238, 0.12);
}

.tile-focus { font-size: 13px; line-height: 1; }

/* Fullscreen is the same kind of choice as focus, so it gets the same accent
   rather than the red that .icon-btn.on would otherwise give it. */
.tile-full { font-size: 13px; line-height: 1; }
.tile-full.on {
  color: var(--accent-2);
  border-color: rgba(34, 211, 238, 0.4);
  background: rgba(34, 211, 238, 0.12);
}

/* In fullscreen the bar is the only way back, and a bar that appears only on
   hover is a bar nobody knows is there. Show it, then fade it out of the way
   once the pointer settles somewhere else. */
.tile:fullscreen .tile-bar { opacity: 1; }
.tile:fullscreen:not(:hover) .tile-bar {
  opacity: 0;
  transition: opacity 0.6s var(--ease) 2s;
}

/* A quiet edge so it is obvious the other streams are set aside rather than
   gone, without stealing attention from the picture. */
#video-grid.focused .tile {
  box-shadow: 0 0 0 1px rgba(34, 211, 238, 0.28);
}

/* ── Liquid glass ─────────────────────────────────────────────────────── */

/*
 * An optional material, with a strength slider in Settings.
 *
 * Everything sits behind [data-glass="on"] and is additive, so dragging the
 * slider to zero restores exactly the flat surfaces the app had before.
 *
 * Four things make it, and only the first is unusual:
 *
 *   1. REFRACTION. `--lg-lens` is a displacement filter built for that exact
 *      panel by glass.js, from the panel's own rounded rectangle: a hard band
 *      in the last pixels of the rim, a wider gather behind it, an extra
 *      squeeze at the corners, and nothing at all across the middle. That is
 *      the shape a real lens has, and what a blur alone can never fake.
 *      glass.js sets its strength from the slider.
 *   2. A HIGHLIGHT along the rim, drawn as a white gradient masked to the
 *      border: brightest across the top, fading down the sides. One pixel,
 *      no colour - the edge should look like light catching glass, not like
 *      an outline someone drew around the panel.
 *   3. A bevel: a bright line along the top where light gathers, a dimmer one
 *      below, and a soft inward glow at the sides.
 *   4. Blur and saturate, so colour blooms through rather than going grey.
 *
 * Deliberately NOT on .tile or video. A displacement filter over a playing
 * stream is by far the most expensive thing here, and obscuring the picture is
 * the one place the effect would hurt rather than help. Small controls get the
 * blur and the rim but not the refraction: on a 38px button the bend is
 * invisible and the filter is not free.
 */

:root[data-glass="on"] {
  /* --lg is 0..1, written by the slider. These all scale from it. */
  --lg-blur: calc(var(--lg, 0.65) * 24px);
  --lg-sat: calc(100% + var(--lg, 0.65) * 85%);
  --lg-fill: rgba(255, 255, 255, calc(var(--lg, 0.65) * 0.075));
  --lg-fill-hi: rgba(255, 255, 255, calc(0.03 + var(--lg, 0.65) * 0.09));

  --lg-sheen: linear-gradient(
    138deg,
    rgba(255, 255, 255, 0.09) 0%,
    rgba(255, 255, 255, 0.02) 26%,
    rgba(255, 255, 255, 0) 52%,
    rgba(255, 255, 255, 0.03) 100%
  );

  /* Light gathers where the glass curves away from you. */
  --lg-bevel:
    inset 0 2px 1px -1px rgba(255, 255, 255, calc(0.18 + var(--lg, 0.65) * 0.5)),
    inset 0 -1.5px 1px -1px rgba(255, 255, 255, calc(0.04 + var(--lg, 0.65) * 0.14)),
    inset 7px 0 16px -14px rgba(255, 255, 255, calc(var(--lg, 0.65) * 0.5)),
    inset -7px 0 16px -14px rgba(255, 255, 255, calc(var(--lg, 0.65) * 0.5));

  --lg-lift: 0 20px 48px -24px rgba(0, 0, 0, 0.85);
}

/*
 * Glass needs something worth bending.
 *
 * This is the whole difference between glass that reads and glass that does
 * not. A lens can only reveal structure that is already behind it, and the flat
 * app sits on a near-black gradient - bending black gives black, which looks
 * exactly like a blur. The reference implementations all put their glass over
 * photographs, which is why theirs pops and a straight port of the maths does
 * not. So when the glass is on, the light behind it is turned up to match, in
 * step with the slider: at Off this is the quiet backdrop the flat app has
 * always had, and at Full it is bright enough to bend.
 */
:root[data-glass="on"] .aurora {
  background:
    radial-gradient(38% 44% at 22% 18%,
      rgba(124, 108, 255, calc(0.30 + var(--lg, 0.65) * 0.62)), transparent 70%),
    radial-gradient(34% 40% at 82% 26%,
      rgba(34, 211, 238, calc(0.18 + var(--lg, 0.65) * 0.52)), transparent 70%),
    radial-gradient(46% 50% at 60% 92%,
      rgba(180, 90, 255, calc(0.14 + var(--lg, 0.65) * 0.48)), transparent 70%),
    radial-gradient(30% 34% at 8% 74%,
      rgba(255, 138, 76, calc(var(--lg, 0.65) * 0.34)), transparent 72%),
    /* The panels sit across the middle, and the four blobs above all skirt it.
       Without this the cards are the one part of the screen with nothing
       behind them, which is the one part where it matters. */
    radial-gradient(52% 38% at 50% 52%,
      rgba(96, 165, 250, calc(var(--lg, 0.65) * 0.40)), transparent 76%);
  /* Less blur than the flat app: a lens has nothing to grip on pure haze. */
  filter: blur(calc(30px - var(--lg, 0.65) * 12px)) saturate(calc(120% + var(--lg, 0.65) * 60%));
}

/* ── Panels: the full material ────────────────────────────────────────── */

:root[data-glass="on"] .card,
:root[data-glass="on"] .modal,
:root[data-glass="on"] .toast,
:root[data-glass="on"] .volume-popover,
:root[data-glass="on"] .setup-bar,
:root[data-glass="on"] .code-card,
:root[data-glass="on"] .viewers,
:root[data-glass="on"] .self-controls,
:root[data-glass="on"] .choice,
:root[data-glass="on"] .source {
  position: relative;
  background-color: var(--lg-fill);
  background-image: var(--lg-sheen);
  /* --lg-lens is set per panel by glass.js; empty until it has measured one,
     which leaves an ordinary blur rather than a broken filter. */
  backdrop-filter: var(--lg-lens, ) blur(var(--lg-blur)) saturate(var(--lg-sat));
  -webkit-backdrop-filter: var(--lg-lens, ) blur(var(--lg-blur)) saturate(var(--lg-sat));
  box-shadow: var(--lg-bevel), var(--lg-lift);
}

/*
 * The volume slider is not a panel in the layout - it floats at the cursor and
 * is positioned by fixed left/top. The glass rule above gives every panel
 * position: relative, which for this one drops it into page flow, far off
 * screen: the slider was being created every time and simply painted where
 * nobody could see it. Keep the glass material, restore the fixed placement.
 */
:root[data-glass="on"] .volume-popover { position: fixed; }

/*
 * The rim. Masked to the border so only the edge itself is painted.
 *
 * This used to be a conic gradient running the full spectrum, on the theory
 * that a real edge splits light. It does, but at this size it read as a solid
 * rainbow outline drawn around the panel rather than as anything glass was
 * doing - so it is a plain highlight now: brightest along the top where light
 * would catch, fading down the sides, gone at the bottom.
 */
:root[data-glass="on"] .card::after,
:root[data-glass="on"] .modal::after,
:root[data-glass="on"] .toast::after,
:root[data-glass="on"] .volume-popover::after,
:root[data-glass="on"] .setup-bar::after,
:root[data-glass="on"] .code-card::after,
:root[data-glass="on"] .viewers::after,
:root[data-glass="on"] .self-controls::after,
:root[data-glass="on"] .choice::after,
:root[data-glass="on"] .source::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  padding: 1px;
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.62) 0%,
    rgba(255, 255, 255, 0.20) 32%,
    rgba(255, 255, 255, 0.08) 68%,
    rgba(255, 255, 255, 0.14) 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: calc(0.35 + var(--lg, 0.65) * 0.5);
}

/* The selection ring has to survive the bevel replacing its box-shadow. */
:root[data-glass="on"] .source.selected {
  box-shadow:
    var(--lg-bevel),
    0 0 0 2px rgba(124, 108, 255, 0.45),
    0 16px 34px -18px rgba(124, 108, 255, 0.8);
}

/* Behind a modal, bend the whole app rather than just darkening it. */
:root[data-glass="on"] .modal-backdrop {
  background: rgba(5, 6, 11, 0.5);
  backdrop-filter: blur(calc(var(--lg, 0.65) * 22px)) saturate(130%);
  -webkit-backdrop-filter: blur(calc(var(--lg, 0.65) * 22px)) saturate(130%);
}

/* ── Bars over video ──────────────────────────────────────────────────── */

/*
 * Darker than the panels, because these sit over a bright screen share and a
 * pale panel over white slides is unreadable. No refraction, and the blur only
 * switches on while hovered - these live at opacity 0 the rest of the time,
 * and filtering a hidden layer over playing video is the one cost worth
 * refusing outright.
 */
:root[data-glass="on"] .tile-bar,
:root[data-glass="on"] .watch-overlay {
  background-color: rgba(8, 10, 18, 0.52);
  background-image: var(--lg-sheen);
  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, calc(0.08 + var(--lg, 0.65) * 0.16));
}

:root[data-glass="on"] .tile:hover .tile-bar,
:root[data-glass="on"] .tile:focus-within .tile-bar,
:root[data-glass="on"] .view.watch:hover .watch-overlay,
:root[data-glass="on"] .watch-overlay.show {
  backdrop-filter: blur(calc(var(--lg, 0.65) * 18px)) saturate(150%);
  -webkit-backdrop-filter: blur(calc(var(--lg, 0.65) * 18px)) saturate(150%);
}

:root[data-glass="on"] .view.watch.connecting .watch-overlay {
  background-color: rgba(5, 6, 11, 0.82);
}

/* ── Controls: blur and rim, no refraction ────────────────────────────── */

/*
 * Plain buttons only. Primary is a gradient, ghost is meant to be invisible
 * and danger carries its own red - glass on any of them erases the thing that
 * tells them apart.
 */
:root[data-glass="on"] .btn:not(.primary):not(.ghost):not(.danger),
:root[data-glass="on"] .icon-btn:not(.ghost),
:root[data-glass="on"] .segmented,
:root[data-glass="on"] .tab,
:root[data-glass="on"] .room-badge,
:root[data-glass="on"] .count,
:root[data-glass="on"] .badge {
  background-color: var(--lg-fill);
  background-image: var(--lg-sheen);
  backdrop-filter: blur(calc(var(--lg, 0.65) * 14px)) saturate(160%);
  -webkit-backdrop-filter: blur(calc(var(--lg, 0.65) * 14px)) saturate(160%);
  box-shadow:
    inset 0 1px 0 0 rgba(255, 255, 255, calc(0.06 + var(--lg, 0.65) * 0.24)),
    inset 0 -1px 0 0 rgba(255, 255, 255, calc(var(--lg, 0.65) * 0.06));
}

/* These outrank the plain :hover rules above, so restate the feedback. */
:root[data-glass="on"] .btn:not(.primary):not(.ghost):not(.danger):hover:not(:disabled),
:root[data-glass="on"] .icon-btn:not(.ghost):hover,
:root[data-glass="on"] .tab:hover,
:root[data-glass="on"] .tab.active {
  background-color: var(--lg-fill-hi);
}

/* Typed text needs a darker bed than a panel does. */
:root[data-glass="on"] .input {
  background-color: rgba(0, 0, 0, 0.24);
  background-image: var(--lg-sheen);
  backdrop-filter: blur(calc(var(--lg, 0.65) * 14px)) saturate(150%);
  -webkit-backdrop-filter: blur(calc(var(--lg, 0.65) * 14px)) saturate(150%);
  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, calc(0.05 + var(--lg, 0.65) * 0.12));
}

:root[data-glass="on"] .input:focus {
  box-shadow:
    inset 0 1px 0 0 rgba(255, 255, 255, calc(0.05 + var(--lg, 0.65) * 0.12)),
    0 0 0 3.5px rgba(124, 108, 255, 0.16);
}

/* ── The strength slider in Settings ──────────────────────────────────── */

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

.glass-slider {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(90deg,
    var(--accent) 0%, var(--accent) var(--fill, 65%),
    rgba(255, 255, 255, 0.12) var(--fill, 65%), rgba(255, 255, 255, 0.12) 100%);
  outline: none;
  cursor: pointer;
}

.glass-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: #fff;
  border: 0;
  cursor: grab;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.55);
  transition: transform 0.12s var(--ease);
}

.glass-slider:active::-webkit-slider-thumb { cursor: grabbing; transform: scale(1.12); }

.glass-readout {
  min-width: 62px;
  text-align: right;
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.setting-note {
  font-size: 12.5px;
  color: var(--muted);
  margin-top: 9px;
  line-height: 1.5;
}

/*
 * Reduced motion also means reduced expense. Displacement is the costly half
 * and the sheen is decoration; keep the tint and the rim so panels still read
 * as panels, and drop the rest.
 */
@media (prefers-reduced-motion: reduce) {
  :root[data-glass="on"] .card,
  :root[data-glass="on"] .modal,
  :root[data-glass="on"] .toast,
  :root[data-glass="on"] .volume-popover,
  :root[data-glass="on"] .setup-bar,
  :root[data-glass="on"] .code-card,
  :root[data-glass="on"] .viewers,
  :root[data-glass="on"] .self-controls,
  :root[data-glass="on"] .choice,
  :root[data-glass="on"] .source {
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    background-image: none;
  }
}

/*
 * A faint lattice behind everything, so the lens has something to bend.
 *
 * Refraction can only reveal structure that is already there. Apple's glass
 * sits over photographs and maps; Beacon's sits over a smooth gradient, and a
 * lens over a flat colour looks exactly like a blur - which is the whole
 * complaint the refraction was added to answer. This lays down a grid at three
 * per cent, invisible as a pattern on its own, but the panel edges pinch and
 * bend it and that is what reads as glass.
 *
 * Not inside .aurora: that layer is blurred, and a blurred grid is no grid.
 */
:root[data-glass="on"] body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.035) 0 1px, transparent 1px 46px),
    repeating-linear-gradient(0deg,  rgba(255, 255, 255, 0.028) 0 1px, transparent 1px 46px);
  opacity: calc(var(--lg, 0.65) * 0.95);
}

/* ── Drawing on a stream ──────────────────────────────────────────────────
 *
 * The canvas sits over the picture and under the tile's own bar, so the
 * buttons stay clickable while the rest of the tile takes strokes. It is only
 * given pointer events while drawing is actually allowed - otherwise it would
 * swallow the double-click that focuses a tile and the right-click that opens
 * the menu.
 */
.tile-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  cursor: crosshair;
  touch-action: none;          /* or a drag scrolls the page instead of drawing */
}
.tile-canvas[hidden] { display: none; }

.tile-draw {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 9px;
  border-radius: 999px;
  border: 1px solid var(--line-hi);
  background: rgba(12, 14, 22, 0.86);
  backdrop-filter: blur(10px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  /* Out of the way until wanted: a toolbar parked over the bottom of a game
     is worse than one that takes a moment to find. */
  opacity: 0.35;
  transition: opacity 0.15s var(--ease);
}
.tile:hover .tile-draw, .tile-draw:hover, .tile-draw:focus-within { opacity: 1; }
.tile-draw[hidden] { display: none; }

.draw-colour {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
}
.draw-colour.on { border-color: #fff; transform: scale(1.12); }

.draw-size {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid var(--line-hi);
  background: transparent;
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
}
.draw-size span { background: currentColor; border-radius: 50%; display: block; }
.draw-size.on { background: var(--accent); color: #fff; border-color: transparent; }

.draw-act {
  border: 1px solid var(--line-hi);
  background: transparent;
  color: inherit;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  cursor: pointer;
}
.draw-act:hover { background: rgba(255, 255, 255, 0.08); }

/* ── The quality slider ─────────────────────────────────────────────────── */

.quality-slider {
  width: 100%;
  margin: 4px 0 2px;
  accent-color: var(--accent);
  cursor: pointer;
}

/*
 * The slider must not resize as it is dragged.
 *
 * The setup bar is a flex row whose items are sized by their content, and the
 * caption under the slider changes with every stop - so a longer sentence made
 * the whole field wider and the slider grew and shrank under the cursor as it
 * moved. Measured before the fix: 399px at one end of the scale, 276px in the
 * middle. The field is given a fixed width, and the caption a fixed height, so
 * nothing moves but the handle.
 */
.setup-bar .field.quality-field,
.quality-live .quality-field { flex: 0 0 300px; width: 300px; max-width: 100%; }

.quality-field .quality-slider { width: 100%; }

.quality-ends {
  display: flex;
  justify-content: space-between;
  font-size: 11.5px;
  opacity: 0.6;
  margin-top: -2px;
}
.quality-hint, .field-hint {
  font-size: 12px;
  line-height: 1.35;
  opacity: 0.68;
  margin: 4px 0 0;
  /* Two lines' worth, reserved whether or not this stop needs them: the
     captions differ in length and the panel jumped as the slider moved. */
  min-height: 2.7em;
}
label b[data-quality-label] { color: var(--accent-2); font-weight: 650; }
