/*
 * The call surface.
 *
 * A separate stylesheet rather than more of `styles.css`, because this is the
 * one page on the site that is not a document. It is an application: it fills
 * the viewport exactly, it does not scroll, and the thing it is arranging is
 * moving video rather than text. Mixing those rules into the design system
 * would mean every other page carrying the weight of a layout none of them use.
 *
 * The tokens are the site's own, so a call still looks like NoteTheBoat and
 * still follows the visitor's theme — with one deliberate exception. **The
 * stage is dark in both themes.** Video is emissive: a bright surround makes
 * every face look under-lit and every shared screen look washed out, which is
 * why every product in this category is dark here regardless of preference.
 * The panels around it follow the theme normally.
 */

/*
 * `hidden` has to win, and on this page it needs help to.
 *
 * The attribute is styled by the browser as `display: none` at the weakest
 * possible specificity, so any class rule that sets `display` beats it. Every
 * region here — the four views, the side panel, the sheets — is a flex or grid
 * container, which means each one of them silently overrode `hidden` and stayed
 * on screen. The visible symptom was not a stray panel: it was the call stage,
 * fixed to the viewport and invisible, swallowing every click meant for the
 * page behind it.
 */
[hidden] {
  display: none !important;
}

/* ------------------------------------------------------------- the shell */

body[data-stage='call'] {
  /* The masthead and footer are for a page you are reading, not a call you are
     in. They come back the moment the call ends. */
  overflow: hidden;
}

body[data-stage='call'] .masthead,
body[data-stage='call'] .colophon,
/*
 * And the cookie bar. It is anchored to the bottom of the viewport, which
 * during a call is where the microphone button is — so a visitor who had not
 * yet answered it joined a meeting with a consent dialog sitting on top of
 * mute, camera and hang-up. Deferred rather than dismissed: no choice is
 * recorded, and the bar is waiting again the moment the call ends.
 */
body[data-stage='call'] #consent {
  display: none;
}

.call {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: oklch(0.16 0.02 265);
  color: oklch(0.97 0.01 265);
  z-index: 40;
}

/* ---------------------------------------------------------------- top bar */

.call-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem clamp(0.75rem, 2vw, 1.25rem);
  border-block-end: 1px solid oklch(1 0 0 / 0.08);
  flex-wrap: wrap;
}

.call-top-left,
.call-top-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-inline-size: 0;
}

.call-top strong {
  font-size: 0.98rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-inline-size: 40vw;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.28rem 0.6rem;
  border-radius: 999px;
  border: 1px solid oklch(1 0 0 / 0.16);
  background: oklch(1 0 0 / 0.06);
  color: inherit;
  font-size: 0.8rem;
  font-family: inherit;
  cursor: default;
}

button.chip {
  cursor: pointer;
}

button.chip:hover {
  background: oklch(1 0 0 / 0.13);
}

.chip--quiet {
  opacity: 0.72;
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
}

.chip--rec {
  border-color: oklch(0.6 0.2 25 / 0.7);
  background: oklch(0.6 0.2 25 / 0.18);
  color: oklch(0.86 0.13 25);
}

.chip--rec svg {
  animation: rec-pulse 1.6s ease-in-out infinite;
}

@keyframes rec-pulse {
  50% {
    opacity: 0.25;
  }
}

@media (prefers-reduced-motion: reduce) {
  .chip--rec svg {
    animation: none;
  }
}

/* ------------------------------------------------------------------ stage */

.call-body {
  display: grid;
  grid-template-columns: 1fr auto;
  min-block-size: 0;
}

.stage {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--columns, 1), 1fr);
  /*
   * Rows share the height rather than each taking what a 16:9 tile needs.
   *
   * With a fixed aspect on the tiles, two people in a wide window sat in a
   * band across the middle with a third of the stage black above and below —
   * the layout was correct and the screen was mostly empty, which is not what
   * a call looks like anywhere else. The columns are chosen against the real
   * dimensions of this box (see `reflow`), so a cell is already close to 16:9;
   * letting it fill the row closes the gap without stretching anyone's face.
   */
  grid-auto-rows: minmax(0, 1fr);
  gap: 0.6rem;
  padding: 0.75rem;
  align-content: stretch;
  justify-content: stretch;
  min-block-size: 0;
  overflow: hidden;
}

/*
 * Spotlight: whatever is shared takes the room and the faces line up beside it.
 *
 * The shared thing is almost always text, and text is what stops being readable
 * first — so it gets the space and the faces get enough to read a nod from.
 */
.stage[data-layout='spotlight'] {
  grid-template-columns: 1fr;
  grid-auto-rows: minmax(0, 1fr);
}

.stage[data-layout='spotlight'] .tile {
  display: none;
}

.stage[data-layout='spotlight'] .tile[data-kind='screen'] {
  display: block;
  grid-row: 1 / span 5;
}

.stage[data-layout='spotlight'] .tile:not([data-kind='screen']) {
  display: block;
  position: absolute;
  inline-size: clamp(120px, 14vw, 200px);
  aspect-ratio: 16 / 9;
  inset-block-end: 0.9rem;
  z-index: 2;
}

.stage[data-layout='spotlight'] .tile:not([data-kind='screen']):nth-of-type(1) {
  inset-inline-end: 1rem;
}
.stage[data-layout='spotlight'] .tile:not([data-kind='screen']):nth-of-type(2) {
  inset-inline-end: calc(clamp(120px, 14vw, 200px) + 1.6rem);
}
.stage[data-layout='spotlight'] .tile:not([data-kind='screen']):nth-of-type(3) {
  inset-inline-end: calc(2 * clamp(120px, 14vw, 200px) + 2.2rem);
}

.tile {
  position: relative;
  margin: 0;
  /*
   * No fixed aspect: the tile is the cell, and the camera inside it is cropped
   * to fit rather than letterboxed. Cropping a face slightly is invisible;
   * black bars around every participant are the thing that makes a call look
   * like a prototype.
   */
  min-block-size: 0;
  border-radius: var(--radius-lg, 16px);
  overflow: hidden;
  background: oklch(0.22 0.03 268);
  box-shadow: inset 0 0 0 1px oklch(1 0 0 / 0.06);
  transition: box-shadow 140ms var(--ease-out, ease);
}

.tile video {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
  background: oklch(0.22 0.03 268);
}

.tile[data-kind='screen'] video {
  /* Contain, not cover: cropping a shared screen removes the toolbar, the tab
     bar, or whichever edge the person was pointing at. */
  object-fit: contain;
  background: oklch(0.12 0.02 265);
}

/*
 * The mirror, and the only place it may live.
 *
 * Your own tile and your own preview, flipped in CSS after the fact — never the
 * frames themselves. Doing it in the pipeline mirrored what every other
 * participant received, so anything with writing on it reached the call
 * backwards: a name badge, a book, a page held up to the camera.
 */
.is-mirrored video,
video.is-mirrored {
  transform: scaleX(-1);
}

/* The ring, which is how a call tells you who is talking without a label. */
.tile.is-speaking {
  box-shadow:
    inset 0 0 0 2px var(--accent, oklch(0.7 0.18 255)),
    0 0 0 3px oklch(0.7 0.18 255 / 0.25);
}

.tile-avatar {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  font-size: clamp(1.6rem, 6cqi, 3rem);
  font-weight: 700;
  color: oklch(0.8 0.1 275);
  background: oklch(0.24 0.04 268);
}

.tile.is-camera-off .tile-avatar {
  display: grid;
}

.tile.is-camera-off video {
  visibility: hidden;
}

.tile figcaption {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  font-size: 0.82rem;
  color: oklch(0.98 0 0);
  background: linear-gradient(to top, oklch(0.1 0.02 265 / 0.78), transparent);
}

.tile-badges {
  display: inline-flex;
  gap: 0.3rem;
  align-items: center;
}

.badge {
  font-style: normal;
  font-size: 0.78rem;
}

.badge--rec {
  color: oklch(0.7 0.2 25);
}

.badge--net {
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
  background: oklch(0.6 0.02 265);
}

.badge--net[data-quality='good'] {
  background: oklch(0.7 0.16 150);
}
.badge--net[data-quality='fair'] {
  background: oklch(0.78 0.15 80);
}
.badge--net[data-quality='poor'] {
  background: oklch(0.65 0.2 25);
}

.reaction-fly {
  position: absolute;
  inset-block-end: 12%;
  inset-inline-start: 50%;
  font-size: 2.4rem;
  pointer-events: none;
  animation: fly 3s var(--ease-out, ease-out) forwards;
}

@keyframes fly {
  to {
    transform: translateY(-42vh) scale(1.4);
    opacity: 0;
  }
}

/* ------------------------------------------------------------------ panel */

.panel {
  inline-size: min(360px, 86vw);
  display: flex;
  flex-direction: column;
  background: var(--surface, oklch(0.2 0.02 265));
  color: var(--text, inherit);
  border-inline-start: 1px solid var(--border, oklch(1 0 0 / 0.1));
  min-block-size: 0;
}

.panel-tabs {
  display: flex;
  border-block-end: 1px solid var(--border, oklch(1 0 0 / 0.1));
}

.panel-tabs button {
  flex: 1;
  padding: 0.7rem 0.4rem;
  background: none;
  border: 0;
  border-block-end: 2px solid transparent;
  color: inherit;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

.panel-tabs button.is-on {
  border-block-end-color: var(--accent);
  color: var(--accent);
}

.panel-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-block-size: 0;
  padding: 0.75rem;
  gap: 0.6rem;
  overflow: auto;
}

.chat-log,
.caption-log,
.people {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  overflow: auto;
  flex: 1;
}

.chat-line {
  background: var(--surface-raised, oklch(1 0 0 / 0.06));
  border: 1px solid var(--border, oklch(1 0 0 / 0.08));
  border-radius: var(--radius, 10px);
  padding: 0.45rem 0.6rem;
  font-size: 0.9rem;
}

.chat-line b {
  display: block;
  font-size: 0.76rem;
  opacity: 0.7;
}

.chat-line--mine {
  border-color: var(--accent-line, oklch(0.5 0.1 258));
  background: var(--accent-quiet, oklch(0.3 0.05 258));
}

.chat-form {
  display: flex;
  gap: 0.4rem;
}

.chat-form input {
  flex: 1;
  min-inline-size: 0;
}

.caption-log li {
  font-size: 0.9rem;
  line-height: 1.5;
}

.caption-log b {
  opacity: 0.65;
  font-size: 0.78rem;
  margin-inline-end: 0.3rem;
}

.people li {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.9rem;
  padding: 0.35rem 0;
  border-block-end: 1px solid var(--border, oklch(1 0 0 / 0.06));
}

.person-name {
  flex: 1;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.host-tools {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.info {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 0.75rem;
  font-size: 0.86rem;
  margin: 0;
}

.info dt {
  opacity: 0.65;
}

.info dd {
  margin: 0;
}

.info .wrap {
  overflow-wrap: anywhere;
  font-family: var(--font-mono);
  font-size: 0.76rem;
}

/* ------------------------------------------------------------------- dock */

.dock {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.55rem 0.75rem calc(0.55rem + env(safe-area-inset-bottom, 0px));
  background: oklch(0.13 0.02 265);
  border-block-start: 1px solid oklch(1 0 0 / 0.08);
  overflow-x: auto;
  scrollbar-width: none;
}

.dock::-webkit-scrollbar {
  display: none;
}

.dock-button {
  position: relative;
  display: grid;
  place-items: center;
  gap: 0.15rem;
  min-inline-size: 4.2rem;
  padding: 0.5rem 0.4rem;
  border: 1px solid transparent;
  border-radius: var(--radius, 10px);
  background: oklch(1 0 0 / 0.06);
  color: oklch(0.97 0.01 265);
  font: inherit;
  font-size: 0.68rem;
  cursor: pointer;
  transition: background 120ms var(--ease-out, ease);
}

.dock-button:hover {
  background: oklch(1 0 0 / 0.14);
}

.dock-button.is-off {
  background: oklch(0.55 0.19 25 / 0.24);
  border-color: oklch(0.6 0.2 25 / 0.5);
  color: oklch(0.9 0.09 25);
}

.dock-button.is-on {
  background: var(--accent, oklch(0.6 0.18 255));
  color: oklch(0.99 0.01 255);
}

/* An unread mark, anchored to the button rather than nudged into place with
   margins — which is what it was, and what put it in a different spot at every
   dock width. */
.dock-button.has-unread::after {
  content: '';
  position: absolute;
  inset-block-start: 0.35rem;
  inset-inline-end: 0.5rem;
  inline-size: 7px;
  block-size: 7px;
  border-radius: 50%;
  background: var(--accent);
}

.dock-button--leave {
  background: oklch(0.5 0.2 25);
  color: oklch(0.99 0.02 25);
}

.dock-button--leave:hover {
  background: oklch(0.56 0.22 25);
}

.dock-emoji {
  font-size: 1.32rem;
  line-height: 1;
}

/* ----------------------------------------------------------- sheets, toasts */

.sheet {
  position: absolute;
  inset-block-end: 5.4rem;
  inset-inline-start: 50%;
  translate: 50% 0;
  inline-size: min(24rem, 92vw);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem;
  border-radius: var(--radius-lg, 16px);
  background: var(--surface, oklch(0.2 0.02 265));
  color: var(--text, inherit);
  border: 1px solid var(--border, oklch(1 0 0 / 0.12));
  box-shadow: 0 18px 48px oklch(0 0 0 / 0.45);
  z-index: 5;
  max-block-size: 60vh;
  overflow: auto;
}

.sheet h3 {
  font-size: 0.95rem;
}

/*
 * A smaller button, which the design system does not have and this surface
 * needs a lot of: chat send, the host's two controls, the knock's approve and
 * decline, the actions on the end screen. All of them sit inside a panel or a
 * toast, where a full-size button is the widest thing in the box.
 */
.btn--sm {
  padding: 0.35rem 0.7rem;
  font-size: 0.8rem;
}

/*
 * Buttons on a dark toast, in either theme.
 *
 * A toast is dark whatever the page is, and `.btn` takes its colours from the
 * page tokens — so in the light theme the knock's "decline" was near-black text
 * on a near-black card. The tokens are re-pointed for this context rather than
 * the toast being lightened, because the toast has to stay legible over video.
 */
.toast .btn {
  --btn-fg: oklch(0.97 0.01 265);
  --btn-border: oklch(1 0 0 / 0.3);
}

.toast .btn--primary {
  --btn-fg: var(--accent-contrast);
}

.effect-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.effect,
.effect-row button {
  padding: 0.34rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border, oklch(1 0 0 / 0.16));
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
}

.effect-row button.is-on,
.effect.is-on {
  background: var(--accent, oklch(0.6 0.18 255));
  border-color: transparent;
  color: var(--accent-contrast, oklch(0.99 0.01 255));
}

.effect--upload {
  display: inline-flex;
  align-items: center;
}

.reactions {
  position: absolute;
  inset-block-end: 5.4rem;
  inset-inline-start: 50%;
  translate: 50% 0;
  display: flex;
  gap: 0.25rem;
  padding: 0.4rem;
  border-radius: 999px;
  background: var(--surface, oklch(0.2 0.02 265));
  border: 1px solid var(--border, oklch(1 0 0 / 0.12));
  z-index: 5;
}

.reactions button {
  font-size: 1.4rem;
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0.2rem 0.35rem;
  border-radius: 999px;
}

.reactions button:hover {
  background: oklch(1 0 0 / 0.1);
}

.toasts {
  position: absolute;
  inset-block-start: 3.6rem;
  inset-inline-end: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  z-index: 6;
  max-inline-size: min(24rem, 88vw);
}

.toast {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.55rem 0.8rem;
  border-radius: var(--radius, 10px);
  background: oklch(0.24 0.03 268);
  color: oklch(0.97 0.01 265);
  border: 1px solid oklch(1 0 0 / 0.12);
  font-size: 0.85rem;
  box-shadow: 0 10px 30px oklch(0 0 0 / 0.35);
}

/*
 * The knock, which is the one toast with buttons in it.
 *
 * Wrapping rather than a fixed row: a long name and two actions do not fit on
 * one line at a phone's width, and the failure mode of not wrapping is an
 * "Approve" button pushed off the edge of the screen — on the notification
 * whose entire purpose is to be answered.
 */
.toast--knock {
  border-color: var(--accent);
  flex-wrap: wrap;
  row-gap: 0.5rem;
}

.toast--knock .button {
  flex: 1 1 auto;
}

.captions-overlay {
  position: absolute;
  inset-block-end: 5.6rem;
  inset-inline: 50%;
  translate: 50% 0;
  inline-size: min(52rem, 92vw);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius, 10px);
  background: oklch(0.1 0.02 265 / 0.82);
  color: oklch(0.99 0 0);
  font-size: clamp(0.95rem, 2.2vw, 1.15rem);
  line-height: 1.5;
  text-align: center;
  pointer-events: none;
  z-index: 4;
}

.captions-overlay b {
  opacity: 0.62;
  font-weight: 600;
  margin-inline-end: 0.35rem;
}

/* ------------------------------------------------------- start and lobby */

.call-entry {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
  gap: 1rem;
  margin-block-start: 2rem;
}

.call-card {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg, 16px);
  background: var(--surface-raised);
}

.call-card h2 {
  font-size: 1.05rem;
}

.call-card-note {
  font-size: 0.82rem;
  color: var(--text-muted);
}

/*
 * The showcase: the product, shown rather than described.
 *
 * Framed as a call window — dark ground, rounded, a bar of the things that are
 * on during the call — because the page above it is a form, and a form is a
 * poor answer to "what is this". The footage is the home page's, so this costs
 * no new bytes to anyone who arrived from there.
 */
.call-showcase {
  position: relative;
  margin: 2.5rem 0 0;
  border-radius: var(--radius-lg, 16px);
  overflow: hidden;
  background: oklch(0.16 0.02 265);
  border: 1px solid var(--border);
  box-shadow: 0 24px 60px var(--shadow-tint-far, oklch(0 0 0 / 0.18));
}

.call-showcase-video {
  display: block;
  inline-size: 100%;
  /* 4:3, on the element as well as here — the same lesson the home page
     learned: a stale cached stylesheet once cropped every face at the
     forehead. */
  aspect-ratio: 4 / 3;
  max-block-size: 26rem;
  object-fit: cover;
  object-position: center 30%;
}

.call-showcase-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.6rem 0.75rem;
  background: oklch(0.13 0.02 265);
}

.call-showcase-pill {
  padding: 0.28rem 0.65rem;
  border-radius: 999px;
  border: 1px solid oklch(1 0 0 / 0.16);
  background: oklch(1 0 0 / 0.06);
  color: oklch(0.95 0.01 265);
  font-size: 0.78rem;
}

.call-showcase-pill--rec {
  border-color: oklch(0.6 0.2 25 / 0.7);
  background: oklch(0.6 0.2 25 / 0.2);
  color: oklch(0.86 0.13 25);
}

.call-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 0.75rem;
  margin-block-start: 2rem;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.85rem;
}

.field > span {
  color: var(--text-muted);
}

.field input[type='text'],
.field select {
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius, 10px);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.check {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.86rem;
}

/*
 * A checkbox big enough to hit with a thumb.
 *
 * The browser default is about 13px, which fails the 24px minimum target that
 * `scripts/site-check.mjs` enforces — and fails it in the place it matters
 * most, since the two switches on this page decide whether a room has a
 * waiting room and whether the call is end-to-end encrypted.
 */
.check input[type='checkbox'] {
  inline-size: 1.5rem;
  block-size: 1.5rem;
  flex: none;
  accent-color: var(--accent);
  cursor: pointer;
}

.hint {
  font-size: 0.8rem;
  color: var(--text-muted);
}

.lobby {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: 1.5rem;
  align-items: start;
}

.lobby-preview {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg, 16px);
  overflow: hidden;
  background: oklch(0.2 0.02 265);
}

.lobby-preview video {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}

.lobby-preview-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: oklch(0.85 0.02 265);
  font-size: 0.9rem;
  text-align: center;
  padding: 1rem;
}

.lobby-preview-bar {
  position: absolute;
  inset-block-end: 0.75rem;
  inset-inline: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.round {
  display: grid;
  place-items: center;
  inline-size: 2.6rem;
  block-size: 2.6rem;
  border-radius: 50%;
  border: 1px solid oklch(1 0 0 / 0.18);
  background: oklch(0.15 0.02 265 / 0.75);
  color: oklch(0.98 0 0);
  cursor: pointer;
}

.round.is-off {
  background: oklch(0.5 0.2 25 / 0.85);
  border-color: transparent;
}

.level {
  inline-size: 5rem;
  block-size: 0.4rem;
  border-radius: 999px;
  background: oklch(1 0 0 / 0.18);
  overflow: hidden;
}

.level i {
  display: block;
  block-size: 100%;
  inline-size: 0%;
  background: var(--accent);
  transition: inline-size 90ms linear;
}

.lobby-side {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.lobby-room {
  color: var(--text-muted);
  font-size: 0.9rem;
}

.lobby-devices {
  display: grid;
  gap: 0.6rem;
}

.effects {
  border: 1px solid var(--border);
  border-radius: var(--radius, 10px);
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.effects legend {
  font-size: 0.8rem;
  color: var(--text-muted);
  padding-inline: 0.3rem;
}

.ended-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius, 10px);
  background: var(--surface-raised);
}

.ended-actions {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-block-start: 1rem;
}

/* --------------------------------------------------------------- phones */

@media (max-width: 640px) {
  .call-body {
    grid-template-columns: 1fr;
  }

  /*
   * The panel takes the screen rather than a column of it. A 360px phone split
   * into video and chat gives neither of them enough to be usable, and the
   * person opening chat has decided which one they are looking at.
   */
  .panel {
    position: absolute;
    inset: 3.2rem 0 5rem;
    inline-size: 100%;
    z-index: 7;
  }

  .dock-button {
    min-inline-size: 3.6rem;
    font-size: 0.6rem;
  }

  .dock-button span:not(.dock-emoji) {
    display: none;
  }
}
