.container {
  width: 100%;
}

@media (min-width: 640px) {
  .container {
    max-width: 640px;
  }
}

@media (min-width: 768px) {
  .container {
    max-width: 768px;
  }
}

@media (min-width: 1024px) {
  .container {
    max-width: 1024px;
  }
}

@media (min-width: 1280px) {
  .container {
    max-width: 1280px;
  }
}

@media (min-width: 1536px) {
  .container {
    max-width: 1536px;
  }
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: 2.5rem;
  padding: 0 1rem;
  border: 0;
  border-radius: var(--radius-control);
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.25;
  cursor: pointer;
  transition: background-color 160ms var(--ease-out), color 160ms var(--ease-out), transform 160ms var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}

.btn[hidden], .icon-btn[hidden] {
  display: none;
}

.btn-lg {
  height: 3rem;
  padding: 0 1.25rem;
  font-size: 1.125rem;
}

.btn-xl {
  height: 3.875rem;
  padding: 0 1.5rem;
  font-size: 1.25rem;
}

.btn-primary {
  background: var(--accent);
  color: var(--on-accent);
}

.btn-primary:not(:disabled):hover {
  background: var(--accent-hover);
}

.btn-primary:not(:disabled):active {
  background: var(--accent-pressed);
  transform: scale(0.985);
}

.btn-secondary {
  border: 1px solid var(--hairline-strong);
  background: var(--popover);
  color: var(--ink);
}

.btn-secondary:not(:disabled):hover {
  border-color: var(--hairline-hover);
  background: var(--popover-hover);
}

.btn-secondary:not(:disabled):active {
  border-color: var(--hairline-strong);
  background: var(--page);
  transform: scale(0.985);
}

.btn-ghost {
  background: transparent;
  color: var(--secondary);
}

.btn-ghost:not(:disabled):hover {
  background: var(--wash);
  color: var(--ink);
}

.btn-ghost:not(:disabled):active {
  background: var(--wash-strong);
  color: var(--ink);
}

.btn:disabled {
  cursor: not-allowed;
  color: var(--disabled-ink);
}

.btn-secondary:disabled {
  background: var(--popover);
  border-color: var(--hairline);
}

/* A disabled primary stays the accent, dimmed, so the same button reads the same whoever may
       press it. The fill is opaque, so the label keeps its contrast over any frame behind it. */

.btn-primary:disabled {
  background: color-mix(in srgb, var(--accent) 40%, var(--panel));
  color: var(--ink-soft);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background-color 160ms var(--ease-out), color 160ms var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}

.icon-btn-sm {
  width: 2rem;
  height: 2rem;
}

.icon-btn:not(:disabled):hover {
  background: var(--wash);
}

.icon-btn:not(:disabled):active {
  background: var(--wash-strong);
}

.icon-btn:disabled {
  cursor: not-allowed;
  color: var(--disabled-ink);
}

.icon-btn[data-popup-open] {
  background: var(--wash);
}

.icon-btn-chip {
  border: 1px solid var(--hairline-strong);
  border-radius: 50%;
  background: var(--popover);
}

.icon-btn-chip:not(:disabled):hover, .icon-btn-chip[data-popup-open] {
  border-color: var(--hairline-hover);
  background: var(--popover-hover);
}

.icon-btn-chip:not(:disabled):active {
  border-color: var(--hairline-strong);
  background: var(--page);
}

.field {
  display: block;
  width: 100%;
  height: 2.5rem;
  padding: 0 0.875rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  transition: border-color 160ms var(--ease-out);
}

.field-lg {
  height: 3rem;
}

.field-xl {
  height: 3.875rem;
  padding: 0 1.25rem;
  font-size: 1.125rem;
}

.field::-moz-placeholder {
  color: var(--placeholder);
  opacity: 1;
}

.field::placeholder {
  color: var(--placeholder);
  opacity: 1;
}

.field:not(:disabled):hover {
  border-color: var(--hairline-strong);
}

.field[aria-invalid="true"] {
  border-color: var(--warning);
}

.field:disabled {
  cursor: not-allowed;
  color: var(--disabled-ink);
}

.card {
  background: var(--panel);
  border-radius: var(--radius);
  padding: 1rem;
}

/* Base UI portals the popup to body; the positioner needs a stack level above the page. */

.popover-positioner {
  z-index: 50;
}

.popover {
  min-width: 17rem;
  padding: 1rem;
  border-radius: var(--radius);
  background: var(--popover);
  color: var(--ink);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.3);
  transform-origin: var(--transform-origin);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}

.popover[data-starting-style], .popover[data-ending-style] {
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
}

.popover-label {
  margin-bottom: 0.625rem;
  font-size: 0.875rem;
  font-weight: 600;
}

/* The sync dot's tooltip: the popover's surface, two lines, the second quieter. */

.tooltip {
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius);
  background: var(--popover);
  font-size: 0.8125rem;
  line-height: 1.35;
  white-space: nowrap;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.3);
  transform-origin: var(--transform-origin);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}

.tooltip[data-starting-style], .tooltip[data-ending-style] {
  opacity: 0;
  transform: translateY(-2px);
}

.tooltip-head {
  color: var(--ink);
  font-weight: 600;
}

.tooltip-sub {
  color: var(--secondary);
}

/* The dialog is the popover surface fixed at the centre. `translate` and `scale` are
       their own properties, so the popover's enter and exit `transform` still composes with
       the centring. */

.dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(0, 0, 0, 0.6);
  transition: opacity 160ms var(--ease-out);
}

.dialog-backdrop[data-starting-style], .dialog-backdrop[data-ending-style] {
  opacity: 0;
}

.dialog-popup {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 51;
  width: min(22rem, calc(100vw - 2rem));
  padding: 1.5rem;
  translate: -50% -50%;
}

.dialog-title {
  margin: 0 0 1rem;
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
}

.segmented {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius-control);
  background: var(--page);
}

.segment {
  flex: 1 1 0;
  height: 2.25rem;
  padding: 0 1rem;
  border: 0;
  border-radius: var(--radius-inner);
  background: transparent;
  color: var(--secondary);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 160ms var(--ease-out), color 160ms var(--ease-out);
}

.segment:not(:disabled):not([aria-pressed="true"]):hover {
  background: var(--wash);
  color: var(--ink);
}

.segment[aria-pressed="true"] {
  background: var(--accent);
  color: var(--on-accent);
}

.segment:disabled {
  cursor: not-allowed;
  color: var(--disabled-ink);
}

.segment:disabled[aria-pressed="true"] {
  background: var(--wash-strong);
  color: var(--ink);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

.visible {
  visibility: visible;
}

.static {
  position: static;
}

.mt-3 {
  margin-top: 0.75rem;
}

.flex {
  display: flex;
}

.hidden {
  display: none;
}

.min-h-5 {
  min-height: 1.25rem;
}

.w-full {
  width: 100%;
}

.min-w-0 {
  min-width: 0px;
}

.flex-1 {
  flex: 1 1 0%;
}

.shrink-0 {
  flex-shrink: 0;
}

.gap-3 {
  gap: 0.75rem;
}

.text-sm {
  font-size: 0.875rem;
  line-height: 1.25rem;
}

.text-warning {
  color: var(--warning);
}

.outline {
  outline-style: solid;
}

.ring {
  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}

/* Served from this origin, never a font service (12e design.md D3); the system stack stands
     in until it loads. */

@font-face {
  font-family: "Instrument Sans";

  src: url("/fonts/InstrumentSans[wdth,wght].woff2") format("woff2");

  font-weight: 400 700;

  font-stretch: 75% 100%;

  font-style: normal;

  font-display: swap;
}

:root {
  color-scheme: dark;
  --page: #0b0b0d;
  --panel: #141418;
  --popover: #1c1c21;
  --popover-hover: #33333b;
  /* Member chip fills, each carrying white initials at 4.5:1 or better. */
  --chip-host: #2f6fed;
  --chip-1: #108377;
  --chip-2: #d33670;
  --chip-3: #8b3fe0;
  --chip-4: #a4670f;
  --chip-5: #1b7caa;
  /* One radius scale: surfaces, controls, and what sits inside a control. Pills and chips are
       999px or 50% in place. */
  --radius: 16px;
  --radius-control: 12px;
  --radius-inner: 8px;
  --ink: #ffffff;
  --ink-soft: #d4d4d8;
  --secondary: #a1a1aa;
  --placeholder: #8a8a94;
  --disabled-ink: #7a7a84;
  --accent: #2ec4b6;
  --accent-hover: #4bd6c9;
  --accent-pressed: #25a89b;
  --on-accent: #04201d;
  --warning: #ff6b5a;
  --sync-ok: #3ddc84;
  --sync-correcting: #f5b544;
  --dot-idle: var(--secondary);
  --dot-idle-ring: rgba(0, 0, 0, 0.6);
  --hairline: rgba(255, 255, 255, 0.08);
  --hairline-strong: rgba(255, 255, 255, 0.16);
  --hairline-hover: rgba(255, 255, 255, 0.28);
  --dash: rgba(255, 255, 255, 0.24);
  --wash: rgba(255, 255, 255, 0.07);
  --wash-strong: rgba(255, 255, 255, 0.12);
  --text-shade: 0 1px 2px rgba(0, 0, 0, 0.6), 0 2px 16px rgba(0, 0, 0, 0.5);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --font-sans: "Instrument Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, Menlo, monospace;
  --header-h: 64px;
  --room-top: calc(var(--header-h) + 1rem);
  accent-color: var(--accent);
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}

* {
  box-sizing: border-box;
}

/* The scrollbar's room is kept whether or not the page overflows, so opening Stats does not
     narrow the grid (12e design.md D12). */

html {
  scrollbar-gutter: stable;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-sans);
  line-height: 1.5;
}

::-moz-selection {
  background: rgba(46, 196, 182, 0.35);
  color: var(--ink);
}

::selection {
  background: rgba(46, 196, 182, 0.35);
  color: var(--ink);
}

input, textarea {
  caret-color: var(--accent);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.tabular, #time {
  font-variant-numeric: tabular-nums;
}

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

body {
  min-height: 100vh;
  min-height: 100dvh;
}

/* The room is one grid. #player is static in room.html; the React children of #screens
     (display: contents) take the other areas, so each is placed beside it without moving
     the media element. */

#screens {
  display: contents;
}

.layout {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(15rem, 3fr);
  grid-template-rows: var(--room-top) auto auto auto 1fr;
  grid-template-areas:
      "header header"
      "player column"
      "actions column"
      "stats column"
      ". column";
  -moz-column-gap: 1rem;
       column-gap: 1rem;
  min-height: 100vh;
  min-height: 100dvh;
  padding: 0 1rem 1rem;
}

/* Until the bundle renders there is no route, and the front page must not flash a player. */

.layout:has(#screens:empty) #player {
  visibility: hidden;
}

.room-header {
  grid-area: header;
  align-self: start;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-h);
  margin-inline: -1rem;
  padding-inline: 1rem;
  border-bottom: 1px solid var(--hairline);
}

/* The name, plain white, and the way back to the front page (12e design.md D2, D13). */

.wordmark {
  font-size: 2rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
  text-decoration: none;
  border-radius: var(--radius-inner);
}

/* No display here: the front route closes #player with [hidden], and a display would
     override the user-agent rule that hides it. `align-self: start` keeps this box the video's:
     a grid row stretched by a short page would leave black under the video, and the bar,
     pinned to this box's bottom, would sit below the picture. */

#player {
  --strip-pad-top: 1.25rem;
  --strip-pad-x: 1.5rem;
  --strip-row: 2.75rem;
  grid-area: player;
  align-self: start;
  position: relative;
  min-width: 0;
  overflow: hidden;
  border-radius: var(--radius);
  background: #000;
}

#player:fullscreen {
  border-radius: 0;
}

#player video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  align-self: start;
  background: #000;
}

/* The entry sets data-clickable from the same rule the click uses (13 design.md D16). */

#player video[data-clickable="true"] {
  cursor: pointer;
}

/* While a card has the player, the card sizes it: the video and the card share one grid cell, so
     the player is the taller of the 16:9 picture and the card, and a card that does not fit at
     16:9 grows the player instead of running under the bar. With no card the player is the
     video's box, and a ready title is 16:9 again. The `:not([hidden])` keeps the display off the
     front route's closed player; the one `minmax(0, 1fr)` column keeps a nowrap command from
     widening the player past its column. */

#player:not([hidden]):has(.file-state:not([hidden]), .wait-layer) {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

#player > video, .file-state, .wait-layer {
  grid-area: 1 / 1;
}

/* The host's card replaces the bar (images 05 to 07); every control stays mounted, and
     visibility keeps its box so a driver's read of it still has a size. */

#player:has(.file-state:not([hidden])) .player-bar {
  visibility: hidden;
}

/* The title, the chips and the settings button (theatre only) and the dot, over the video.
     Pointer-transparent except the dot and the button, so the strip never takes a click from the
     video. The gradient is the only shading, and it is drawn only while the strip has something
     to read. */

.player-top {
  --bar-slide: 24px;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: var(--strip-pad-top) var(--strip-pad-x) 2.75rem;
  pointer-events: none;
  opacity: 1;
  transform: translateY(0);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}

.player-top[data-faded="true"] {
  opacity: 0;
  transform: translateY(calc(-1 * var(--bar-slide)));
  pointer-events: none;
}

.player-top:has(#title-name:not(:empty), #sync-dot:not([hidden]), .participants-chips) {
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
}

#title-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-size: clamp(1.125rem, 1.5vw + 0.5rem, 2rem);
  font-weight: 700;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-shadow: var(--text-shade);
}

.player-top-end {
  display: flex;
  flex: none;
  align-items: center;
  gap: 0.75rem;
}

.player-top .participants-chips {
  margin-left: 0;
}

/* No display here: the dot is closed with [hidden] until a title is loaded. The transparent
     border is the hit area; the background is clipped to the 14px dot inside it. Idle is the
     base colour, so a state this sheet does not know reads as claiming nothing, never green.
     Idle's thin dark ring keeps the grey readable on a light frame (owner, 8.5); an inset shadow
     sits at the padding edge, so it rings the dot, not the hit area. */

#sync-dot {
  flex: none;
  box-sizing: border-box;
  width: 1.625rem;
  height: 1.625rem;
  border: 0.375rem solid transparent;
  border-radius: 50%;
  background-color: var(--dot-idle);
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1.5px var(--dot-idle-ring);
  pointer-events: auto;
}

#sync-dot[data-state="in-sync"] {
  background-color: var(--sync-ok);
  box-shadow: none;
}

#sync-dot[data-state="correcting"] {
  background-color: var(--sync-correcting);
  box-shadow: none;
}

/* The bar is one row on a bottom gradient, flush with the player's left, right and bottom. The
     gradient is the only shading, and it bleeds a pixel past those three edges so a fractional
     layout edge leaves no unshaded line of video (the player clips it).
     --bar-btn and --bar-slide are one rule (design.md D7): faded, the bar slides by more than
     half a button, so a box read before a reveal misses the control now there, and its bottom
     padding is as deep as the slide, so every control stays inside the player while faded and a
     coordinate click still lands. Both are px, because drivers read them as numbers. */

.player-bar {
  --bar-btn: 40px;
  --bar-slide: 24px;
  --bar-gap: clamp(0.25rem, 3.125vw - 1.25rem, 1.75rem);
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: 0 1.25rem var(--bar-slide);
  opacity: 1;
  transform: translateY(0);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}

.player-bar[data-faded="true"] {
  opacity: 0;
  transform: translateY(var(--bar-slide));
}

.player-bar::before {
  content: '';
  position: absolute;
  inset: -2.5rem -1px -1px;
  z-index: -1;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
  pointer-events: none;
}

.bar-row {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

/* The gap follows the viewport, because the player is a share of it: image 02's spacing at a
     wide page, and the old tight pack where the player is narrow enough to need every pixel. */

.bar-group {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  min-width: 0;
  gap: var(--bar-gap);
}

#control-bar .icon-btn {
  flex: none;
  width: var(--bar-btn);
  height: var(--bar-btn);
}

#theatre[aria-pressed="true"] {
  color: var(--accent);
}

#time {
  flex: none;
  margin-left: 0.25rem;
  color: var(--ink);
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.25;
  white-space: nowrap;
}

/* The back and forward arrows carry their "10" as generated content: it adds no text node, so
     the button's textContent stays the engine's text alone. Each arrowhead sits on one side
     (back on the left, forward on the right), so the numeral steps down and away from it. */

.skip-10 {
  position: relative;
  display: inline-flex;
}

.skip-10::after {
  content: '10';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: 2px;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

#back-10 .skip-10::after {
  padding-left: 2px;
}

#forward-10 .skip-10::after {
  padding-right: 2px;
}

/* Native ranges: the track is thin, and the played part and the thumb are the accent. Only the
     seek line sets a display, and it is never closed; #volume sets none, because narrow pages
     close it with display: none. --fraction is written by the component. */

.range {
  --thumb: 14px;
  --track: 4px;
  height: 1.5rem;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
       appearance: none;
}

#seek {
  display: block;
  width: 100%;
  margin-bottom: 0.5rem;
}

#volume {
  flex: none;
  width: 5.5rem;
  margin-inline: 0.25rem;
}

.range::-webkit-slider-runnable-track {
  height: var(--track);
  border-radius: 999px;
  background: linear-gradient(to right, var(--accent) calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--fraction, 0)), rgba(255, 255, 255, 0.35) 0);
}

.range::-webkit-slider-thumb {
  width: var(--thumb);
  height: var(--thumb);
  margin-top: calc((var(--track) - var(--thumb)) / 2);
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  -webkit-transition: transform 160ms var(--ease-out);
  transition: transform 160ms var(--ease-out);
  -webkit-appearance: none;
  appearance: none;
}

.range::-moz-range-track {
  height: var(--track);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.35);
}

.range::-moz-range-progress {
  height: var(--track);
  border-radius: 999px;
  background: var(--accent);
}

.range::-moz-range-thumb {
  width: var(--thumb);
  height: var(--thumb);
  border: 0;
  border-radius: 50%;
  background: var(--accent);
}

.range:not(:disabled):hover::-webkit-slider-thumb {
  transform: scale(1.2);
}

.range:disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

.range:focus-visible {
  outline: none;
}

.range:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 4px var(--accent);
}

.range:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 4px var(--accent);
}

/* The host's file card: the player's whole surface while there is nothing to play, and a grid
     item in the player's one cell, so its height can set the player's (above). It sets a display,
     so it carries its own [hidden] rule. The bar is not drawn over it, so its padding is the
     dashed frame's clearance alone. `margin: auto` on the body centres it and, unlike
     justify-content, still starts from the top when it is taller than the card. */

.file-state {
  position: relative;
  display: flex;
  min-width: 0;
  background: var(--panel);
}

.file-state[hidden] {
  display: none;
}

.file-state[data-kind="choosing"]::before {
  content: '';
  position: absolute;
  inset: 1rem;
  border: 2px dashed var(--dash);
  border-radius: var(--radius-control);
  pointer-events: none;
}

.file-card {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  padding: 2rem 1.5rem;
}

.file-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  width: min(48rem, 100%);
  margin: auto;
  text-align: center;
}

.file-icon {
  flex: none;
  margin-bottom: 0.5rem;
  color: var(--secondary);
}

.file-icon-warn {
  color: var(--warning);
}

.file-title {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.file-note, #file-reason {
  margin: 0;
  color: var(--secondary);
  font-size: 1.0625rem;
  line-height: 1.5;
  text-wrap: pretty;
}

#file-reason:empty {
  display: none;
}

.file-progress {
  width: min(24rem, 100%);
  height: 0.375rem;
  overflow: hidden;
  border-radius: 999px;
  background: var(--wash-strong);
}

.file-progress-fill {
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transform: scaleX(var(--fraction, 0));
  transform-origin: left;
  transition: transform 200ms var(--ease-out);
}

.file-button {
  min-width: 14.375rem;
  margin-top: 0.75rem;
  padding: 0 2.5rem;
}

.file-codebox {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  margin-top: 0.5rem;
  padding: 0.5rem 0.5rem 0.5rem 1rem;
  border-radius: var(--radius-inner);
  background: var(--page);
  text-align: left;
}

/* One line where it fits, cut with an ellipsis where it does not; the full command stays in the
     DOM, and the phone wraps it (below), because a clipped command is not one to copy by eye. */

#file-command {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: 0.9375rem;
  font-weight: 400;
  line-height: 1.5;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-codebox .btn {
  flex: none;
  height: 2.25rem;
  padding: 0 0.875rem;
}

#file-note {
  color: var(--secondary);
  font-size: 0.9375rem;
}

/* Over the host's playing video, in the title's strip under the name, and it takes no click.
     The strip's own variables place it, so it follows the title at every width. The component
     sets `data-phase="fading"` when its visible time is up and unmounts it once the fade has
     run; the global reduced-motion rule shortens the transition to nothing, so there it is
     just gone. */

.file-hint {
  position: absolute;
  top: calc(var(--strip-pad-top) + var(--strip-row));
  left: var(--strip-pad-x);
  right: var(--strip-pad-x);
  z-index: 1;
  color: var(--secondary);
  font-size: 0.8125rem;
  line-height: 1.25rem;
  text-shadow: var(--text-shade);
  pointer-events: none;
  opacity: 1;
  transition: opacity 400ms var(--ease-out);
}

.file-hint[data-phase="fading"] {
  opacity: 0;
}

/* The viewer's waiting card and the title's end are each one layer: the whole picture,
     positioned with no z-index, so the strip and the bar (both 1) stay over it, and it takes no
     pointer event. The wait card is a grid item in the player's cell, so a card taller than 16:9
     grows the player; the end is over a playing title and never does. The bottom padding is as
     deep as the bar, so no word sits under the controls. Each is mounted only while it applies,
     so none is ever closed with [hidden]. */

.wait-layer, .ended-layer {
  position: relative;
  display: flex;
  min-width: 0;
  padding: 1.5rem 1.5rem 5.5rem;
  pointer-events: none;
  animation: layer-in 240ms var(--ease-out) both;
}

.ended-layer {
  position: absolute;
  inset: 0;
}

@keyframes layer-in {
  from {
    opacity: 0;
  }
}

.wait-layer {
  background: var(--panel);
}

.wait-body, .ended-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: min(32rem, 100%);
  margin: auto;
  text-align: center;
}

.wait-body {
  gap: 0.75rem;
}

.wait-title {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.wait-note {
  margin: 0;
  color: var(--secondary);
  font-size: 1.0625rem;
  line-height: 1.5;
}

.wait-dots {
  display: flex;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.wait-dots span {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  background: var(--accent);
  animation: wait-pulse 1.2s ease-in-out infinite both;
}

.wait-dots span:nth-child(2) {
  animation-delay: 0.2s;
}

.wait-dots span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes wait-pulse {
  0%, 100% {
    opacity: 0.4;
  }

  50% {
    opacity: 1;
  }
}

/* The global reduced-motion rule shortens a duration but would leave an infinite loop running. */

@media (prefers-reduced-motion: reduce) {
  .wait-dots span {
    animation: none;
  }
}

.ended-layer {
  background: rgba(0, 0, 0, 0.55);
}

.ended-body {
  gap: 1.25rem;
}

.ended-title {
  margin: 0;
  font-size: clamp(2rem, 5vw, 3.75rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-shadow: var(--text-shade);
}

.ended-button {
  min-width: 12rem;
  padding: 0 2rem;
  pointer-events: auto;
}

/* The pill sits at the top centre, below the title's row when there is a title, so a long name
     never runs under it. The amber is the sync dot's own. */

.reconnect-pill {
  position: absolute;
  top: 1rem;
  left: 50%;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  translate: -50% 0;
  padding: 0.5rem 1rem;
  border: 1px solid var(--hairline-strong);
  border-radius: 999px;
  background: rgba(20, 20, 24, 0.88);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.25rem;
  white-space: nowrap;
  pointer-events: none;
  animation: layer-in 200ms var(--ease-out) both;
}

#player:has(#title-name:not(:empty)) .reconnect-pill {
  top: 4rem;
}

.reconnect-dot {
  flex: none;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 50%;
  background: var(--sync-correcting);
}

.room-actions {
  grid-area: actions;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.room-actions .btn {
  height: 3.5rem;
  padding: 0 1.5rem;
  font-size: 1.125rem;
}

/* The open panel's button keeps its ink label and takes a 1.5px accent edge: the border's
     1px and a half pixel of inset ring, so the box does not move when it toggles. */

#stats[aria-expanded="true"] {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 0.5px var(--accent);
}

#invite {
  min-width: 13.5rem;
  margin-left: auto;
  text-decoration: none;
}

/* No display here either: the panel is closed with [hidden], and #overlay inside it keeps
     its own [hidden] in step. The flex layout lives on .stats-body. Monospace is this
     panel's alone. */

.stats-panel {
  grid-area: stats;
  min-width: 0;
  margin-top: 0.75rem;
  padding: 1rem 1.125rem;
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: 0.9375rem;
  font-weight: 400;
  line-height: 1.65;
  overflow-wrap: anywhere;
}

.stats-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.stats-body :is(#host-file-status, #keyframe-notice, #host-preparing):empty {
  display: none;
}

.stats-caption {
  margin: 0.5rem 0 0;
  font: inherit;
  font-weight: 500;
  color: var(--secondary);
}

#clock-line, #drift-line, #bytes-line, #controller-line, #status-line {
  color: var(--ink-soft);
}

#keyframe-notice {
  color: var(--secondary);
}

#host-file-status, #host-preparing {
  color: var(--secondary);
}

#roster, #notices {
  list-style: none;
  margin: 0;
  padding: 0;
  color: var(--secondary);
}

#notices {
  max-height: 220px;
  overflow-y: auto;
}

#notices li.gap {
  color: var(--warning);
}

/* The column fills the rows it spans, capped at the viewport so it stays sticky when the
     left side grows. Size containment keeps its content (a long activity log) out of the grid's
     row sizing: without it the 1fr row grows to fit the column and the page scrolls. */

.room-column {
  grid-area: column;
  align-self: stretch;
  position: sticky;
  top: 0.75rem;
  display: flex;
  flex-direction: column;
  min-height: 0;
  max-height: calc(100vh - var(--room-top) - 1rem);
  max-height: calc(100dvh - var(--room-top) - 1rem);
  contain: size;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--panel);
}

.participants {
  flex: none;
}

.participants-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 1.125rem 1rem;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 1.25rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background-color 160ms var(--ease-out);
}

.participants-trigger:hover {
  background: var(--wash);
}

.participants-trigger:focus-visible {
  outline-offset: -2px;
}

.participants-trigger[data-panel-open] {
  border-bottom-color: transparent;
}

.participants-title {
  flex: none;
}

.participants-chevron {
  flex: none;
  color: var(--secondary);
  transition: transform 200ms var(--ease-out);
}

.participants-trigger[data-panel-open] .participants-chevron {
  transform: rotate(180deg);
}

.participants-panel {
  overflow: hidden;
  height: var(--collapsible-panel-height);
  transition: height 200ms var(--ease-out);
}

.participants-panel[data-starting-style], .participants-panel[data-ending-style] {
  height: 0;
}

/* Initials in a chip whose fill is its tone class; the host's carries an ink ring. The
     header's chips overlap, earlier ones on top, and show only while the list is closed. */

.member-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  color: var(--ink);
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.member-chip-sm {
  width: 2.5rem;
  height: 2.5rem;
  font-size: 1rem;
}

.member-chip-tone-host {
  background: var(--chip-host);
}

.member-chip-tone-1 {
  background: var(--chip-1);
}

.member-chip-tone-2 {
  background: var(--chip-2);
}

.member-chip-tone-3 {
  background: var(--chip-3);
}

.member-chip-tone-4 {
  background: var(--chip-4);
}

.member-chip-tone-5 {
  background: var(--chip-5);
}

.member-chip-host {
  box-shadow: 0 0 0 2px var(--ink);
}

.participants-chips {
  display: flex;
  align-items: center;
  flex: 0 1 auto;
  min-width: 0;
  margin-left: auto;
  padding: 3px;
  overflow: hidden;
}

.participants-trigger[data-panel-open] .participants-chips {
  display: none;
}

.participants-chip-slot {
  position: relative;
  display: inline-flex;
  margin-left: -0.375rem;
}

.participants-chip-slot:first-child {
  margin-left: 0;
}

.participants-chips .member-chip {
  box-shadow: 0 0 0 2px var(--panel);
}

.participants-chips .member-chip-host {
  box-shadow: 0 0 0 2px var(--ink);
}

.member-chip-more {
  margin-left: -0.375rem;
  background: var(--wash-strong);
  color: var(--secondary);
}

.participants-list {
  list-style: none;
  margin: 0;
  padding: 0 1rem 0.75rem;
  max-height: 18rem;
  overflow-y: auto;
  border-bottom: 1px solid var(--hairline);
}

.participant {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 3.875rem;
}

.participant-label {
  min-width: 0;
  overflow: hidden;
  font-size: 1.0625rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.host-tag {
  flex: none;
  padding: 0 0.625rem;
  border-radius: 999px;
  background: var(--wash-strong);
  color: var(--ink-soft);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.75rem;
}

.room-activity {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  padding: 1.25rem 1rem;
}

.activity-line {
  margin: 0 0 1rem;
  text-align: center;
  color: var(--secondary);
  font-size: 0.9375rem;
  line-height: 1.5rem;
  overflow-wrap: anywhere;
}

.activity-line:last-child {
  margin-bottom: 0;
}

.activity-warn {
  color: var(--warning);
}

.activity-chat {
  text-align: left;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

.chat-sender {
  font-weight: 600;
  color: var(--ink);
}

.chat-sender-tone-host {
  color: var(--chip-host);
}

.chat-sender-tone-1 {
  color: var(--chip-1);
}

.chat-sender-tone-2 {
  color: var(--chip-2);
}

.chat-sender-tone-3 {
  color: var(--chip-3);
}

.chat-sender-tone-4 {
  color: var(--chip-4);
}

.chat-sender-tone-5 {
  color: var(--chip-5);
}

.participant-self {
  display: flex;
  flex: 0 1 auto;
  align-items: center;
  gap: 1rem;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.room-compose {
  flex: none;
  padding: 0.75rem;
  border-top: 1px solid var(--hairline);
}

.room-compose .field {
  height: 2.75rem;
  padding: 0 1.125rem;
  border-radius: 999px;
  background: transparent;
}

/* Narrow: one column, as image 10. The header, the video flush under it and edge to edge with
     its one-row bar, the Stats row, the Stats panel, then the column, with no horizontal
     overflow. `align-content: start` stops a short page stretching the auto rows, which would
     pull #player taller than its video. The slider and the theatre button are not displayed; the
     slider stays mounted. */

@media (max-width: 767.98px) {
  .layout {
    --room-top: var(--header-h);
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: var(--room-top) auto auto auto auto;
    grid-template-areas: "header" "player" "actions" "stats" "column";
    align-content: start;
  }

  #player {
    --strip-pad-top: 1rem;
    --strip-pad-x: 1rem;
    --strip-row: 1.75rem;
    margin-inline: -1rem;
    border-radius: 0;
  }

  .player-top {
    padding-bottom: 2.5rem;
  }

  .player-bar {
    padding-inline: 1rem;
  }

  #volume, #theatre {
    display: none;
  }

  #time {
    font-size: 0.8125rem;
  }

  .room-actions .btn {
    font-size: 1rem;
  }

  .file-card {
    padding: 1.75rem 1.5rem;
  }

  .wait-layer, .ended-layer {
    padding: 1rem 1rem 5.5rem;
  }

  .file-icon {
    display: none;
  }

  .file-title, .wait-title {
    font-size: 1.5rem;
  }

  #file-command {
    overflow-wrap: anywhere;
    white-space: normal;
  }

  /* The end is over a 16:9 picture: the title steps down, the button is the medium one, and the
       layer is centred in what is left under the strip and above the bar. With the pill up it
       starts below it and lays its two parts in a row, which is all that fits. The keyframe hint
       gives way to both: there is nowhere left for it. */

  .ended-layer {
    padding-top: 2.75rem;
  }

  .ended-body {
    gap: 0.5rem;
  }

  .ended-title {
    font-size: 2rem;
  }

  .ended-button {
    height: 2.5rem;
    min-width: 0;
    padding: 0 1.25rem;
    font-size: 0.9375rem;
  }

  .ended-button svg {
    width: 1.25rem;
    height: 1.25rem;
  }

  #player:has(.reconnect-pill) .ended-layer {
    padding-top: 3.75rem;
  }

  #player:has(.reconnect-pill):has(#title-name:not(:empty)) .ended-layer {
    padding-top: 5.5rem;
  }

  #player:has(.reconnect-pill) .ended-body {
    flex-direction: row;
    gap: 1rem;
  }

  #player[data-ended] .file-hint, #player:has(.reconnect-pill) .file-hint {
    display: none;
  }

  #player:has(#title-name:not(:empty)) .reconnect-pill {
    top: 3rem;
  }

  .room-column {
    position: static;
    height: 24rem;
    max-height: none;
    margin: 0.75rem -1rem 0;
    border-radius: 0;
  }
}

/* Theatre: the right column, the header, the Stats row and the Stats panel are not displayed
     (they stay mounted, and come back as they were), and the player takes the page, capped so the
     whole picture fits. `--room-top` is then only the least gap above the player, and the first and
     last rows share what is left, so the picture sits centred as image 04 draws it. Nothing is
     re-created or moved; the media element is the same one. */

@media (min-width: 768px) {
  .layout[data-theatre] {
    --room-top: 1rem;
    grid-template-rows: minmax(var(--room-top), 1fr) auto auto auto minmax(1rem, 1fr);
    padding-bottom: 0;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "header" "player" "actions" "stats" ".";
  }

  .layout[data-theatre] :is(.room-column, .room-header, .room-actions, .stats-panel) {
    display: none;
  }

  .layout[data-theatre] #player {
    justify-self: center;
    width: min(100%, calc((100vh - var(--room-top) - 1rem) * 16 / 9));
    width: min(100%, calc((100dvh - var(--room-top) - 1rem) * 16 / 9));
  }
}

/* The front page (12e design.md D1, D6): it spans every row of .layout, and the page scrolls.
     The hero is the first screen: 82vh, so the features start just under the fold. */

.front {
  grid-column: 1 / -1;
  grid-row: 1 / -1;
}

.front-wrap {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 1rem;
}

.front-hero {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: 3.5rem;
  align-items: center;
  min-height: 82vh;
  min-height: 82dvh;
  padding: 4.5rem 0 2rem;
}

.front-headline {
  margin: 0;
  max-width: 30rem;
  text-align: center;
  font-size: clamp(3.2rem, 6vw, 6rem);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.04em;
  color: var(--ink);
}

.front-lede {
  margin: 1.25rem 0 2rem;
  max-width: 30rem;
  font-size: clamp(1.08rem, 1.5vw, 1.3rem);
  color: var(--secondary);
}

.front-actions {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  max-width: 30rem;
}

/* The room, drawn: a player with its bar and dot, and the column as blank bars. */

.front-room {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 34%;
  gap: 0.625rem;
  padding: 0.75rem;
  border-radius: 18px;
  background: var(--panel);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6), 0 0 0 1px var(--hairline);
}

.front-room-video {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  border-radius: 10px;
  background: radial-gradient(120% 90% at 70% 30%, color-mix(in srgb, var(--accent) 34%, var(--page)) 0%, color-mix(in srgb, var(--accent) 14%, var(--page)) 40%, var(--page) 75%);
}

.front-room-video::after {
  content: '';
  position: absolute;
  inset: 60% 0 0;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.65));
}

.front-room-dot {
  position: absolute;
  z-index: 1;
  top: 10px;
  right: 10px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--sync-ok);
}

.front-room-track {
  position: absolute;
  z-index: 1;
  left: 12px;
  right: 12px;
  bottom: 26px;
  height: 3px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.25);
}

.front-room-track span {
  display: block;
  width: 42%;
  height: 100%;
  border-radius: 3px;
  background: var(--accent);
}

.front-room-controls {
  position: absolute;
  z-index: 1;
  left: 12px;
  bottom: 9px;
  display: flex;
  gap: 8px;
}

.front-room-controls i {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.85);
}

.front-room-column {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 4px;
}

.front-room-chips, .front-step-chips {
  display: flex;
}

.front-chip {
  display: block;
  width: 18px;
  height: 18px;
  border-radius: 50%;
}

.front-room-chips {
  gap: 4px;
}

.front-line {
  display: block;
  height: 7px;
  border-radius: 4px;
  background: var(--wash-strong);
}

.front-line-centre {
  width: 60%;
  align-self: center;
  background: var(--wash);
}

.front-line-short {
  width: 70%;
}

.front-line-mid {
  width: 85%;
}

.front-room-input {
  display: block;
  height: 20px;
  margin-top: auto;
  border: 1px solid var(--hairline-strong);
  border-radius: 7px;
}

.front-leads, .front-supports {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3.5rem;
}

.front-leads {
  margin-top: 3.5rem;
  padding-top: 2.5rem;
  border-top: 1px solid var(--hairline);
}

.front-supports {
  margin-top: 2.5rem;
}

.front-leads h2 {
  margin: 0 0 0.75rem;
  font-size: clamp(1.6rem, 2.6vw, 2.2rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.front-supports h3 {
  margin: 0 0 0.25rem;
  font-size: 1.12rem;
}

.front-leads p, .front-supports p {
  margin: 0;
  font-size: 1.05rem;
  color: var(--secondary);
}

.front-steps {
  margin-top: 7rem;
}

.front-eyebrow {
  margin: 0 0 1.25rem;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--secondary);
}

.front-steps ol {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.front-steps li {
  padding: 1.25rem;
  border-radius: var(--radius);
  background: var(--panel);
}

.front-steps h3 {
  margin: 0 0 0.25rem;
  font-size: 1.2rem;
}

.front-steps p {
  margin: 0;
  color: var(--secondary);
}

.front-step-picture {
  display: grid;
  place-items: center;
  height: 120px;
  margin-bottom: 1.125rem;
  border-radius: 10px;
  background: var(--page);
  box-shadow: inset 0 0 0 1px var(--hairline);
}

.front-step-file {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--popover);
}

.front-step-file-icon {
  position: relative;
  display: block;
  width: 22px;
  height: 28px;
  border-radius: 3px;
  background: var(--popover-hover);
}

.front-step-file-icon::after {
  content: '';
  position: absolute;
  left: 7px;
  top: 9px;
  border-left: 8px solid var(--accent);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
}

.front-step-file-name {
  width: 4.5rem;
}

.front-step-link {
  padding: 10px 14px;
  border-radius: 999px;
  background: var(--popover);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}

.front-step-play {
  display: flex;
  align-items: center;
  gap: 14px;
}

.front-step-play-button {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--accent);
}

.front-step-play-button::after {
  content: '';
  margin-left: 4px;
  border-left: 14px solid var(--on-accent);
  border-top: 9px solid transparent;
  border-bottom: 9px solid transparent;
}

.front-step-chips .front-chip {
  width: 26px;
  height: 26px;
  margin-left: -6px;
  box-shadow: 0 0 0 2px var(--page);
}

.front-footer {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-top: 7rem;
  padding: 1.75rem 0 2.5rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.95rem;
  color: var(--secondary);
}

.front-footer-name {
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}

@media (max-width: 767.98px) {
  .front-wrap {
    padding: 0 0.25rem;
  }

  .front-hero {
    grid-template-columns: minmax(0, 1fr);
    gap: 2.5rem;
    min-height: 0;
    padding-top: 3.5rem;
  }

  .front-room {
    grid-template-columns: minmax(0, 1fr);
  }

  .front-room-column {
    display: none;
  }

  .front-leads, .front-supports, .front-steps ol {
    grid-template-columns: minmax(0, 1fr);
    gap: 2rem;
  }

  .front-steps ol {
    gap: 1.25rem;
  }
}

.or-divider {
  display: flex;
  align-items: center;
  gap: 1rem;
  color: var(--secondary);
  font-size: 0.875rem;
}

.or-divider::before, .or-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

/* Shared parts (public/room/ui.jsx). Each sets a display, so each carries its own
     [hidden] rule: the engine closes a kept button with `hidden`. */
