/* The phone is a monitor. Everything here stays out of the way of the picture. */
:root {
  --ink: #E6E9ED;
  --muted: #8A93A0;
  --tally: #FF4D3D;
  --scope: #3DB7DA;
  /* The design plan's semantic warning. It was being used by the Status chip's unsettled states
     and by the Playback chip while scrubbing, but never defined — so those rules were invalid and
     silently fell back to plain text. A missing custom property fails quietly, which is exactly
     how it survived. */
  --warning: #E5A83A;
  /* The link is up. Green rather than tally red, because a healthy connection is not "on air".
     Tally is left for a failed colour-check patch, where critical is what it means. */
  --ok: #4CC38A;
  --surface: rgba(23, 26, 32, 0.94);
  --rule: #2A303A;
  --sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* Any author `display` rule outranks the browser's own styling for [hidden], so an element
   with both stays on screen. This keeps the attribute meaning what it says. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: #000;
  color: var(--ink);
  font: 15px/1.45 var(--sans);
  overscroll-behavior: none;
}

body { overflow: hidden; -webkit-user-select: none; user-select: none; }

.stage {
  position: fixed;
  inset: 0;
  height: 100dvh;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: none;
}

#video, .freeze {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
  transform-origin: center center;
  will-change: transform;
}

.freeze { position: absolute; inset: 0; }

.stage.is-reconnecting #video,
.stage.is-reconnecting .freeze { opacity: 0.35; transition: opacity 0.4s ease; }

/* A held frame should be obvious at a glance, with or without the HUD up.
   The border is drawn *into* a canvas the same size as the picture, which then letterboxes
   itself exactly as the video does. Calculating the picture's rectangle in CSS and drawing a
   box there was always a few pixels out, because it has to reproduce the browser's own
   object-fit arithmetic; this way the browser does it, on the same terms, for both. */
.frozen-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  transform-origin: center center;
}

.overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  padding: 24px;
  pointer-events: none;
}
.overlay[hidden] { display: none; }
.overlay__text { margin: 0; font: 500 17px/1.3 var(--sans); }
.overlay__hint { margin: 0; font: 13px/1.4 var(--mono); color: var(--muted); max-width: 32ch; }

/* Freezing is local to this phone, and saying so is the whole point of the note. */
/* Tags over the top of the picture: short statements of fact, stacked so two can be true at
   once. A frozen picture that has lost contact is both of them, and hiding either would be a
   choice about which fact matters that this page is not in a position to make. */
.tags {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  max-width: calc(100% - 24px);
}
.tag {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--rule);
  white-space: nowrap;
  font: 11px/1 var(--mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.tag__title { color: var(--scope); }
.tag__note { color: var(--muted); }
.tag__note::before { content: "\2014  "; }
/* Amber rather than the scope colour: this one is a fault, and the freeze tag beside it is not.
   Spelled out rather than var(--warning), which viewer.css warns at the top does not exist here
   and fails silently when it is asked for. */
.tag--warning .tag__title { color: #e8a33d; }

/* Readouts that stay on the picture after the HUD hides. */
.overlays {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  left: calc(env(safe-area-inset-left, 0px) + 14px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  pointer-events: none;
}
.overlays__item {
  padding: 4px 8px;
  border-radius: 4px;
  background: rgba(15, 17, 21, 0.72);
  color: var(--ink);
  font: 12px/1 var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}
.overlays__item b { font-weight: 500; color: #fff; }

/* ------------------------------------------------------------------ HUD */

.hud {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  padding: 14px calc(env(safe-area-inset-right, 0px) + 16px)
           calc(env(safe-area-inset-bottom, 0px) + 14px)
           calc(env(safe-area-inset-left, 0px) + 16px);
  background: var(--surface);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--rule);
  animation: rise 0.18s ease-out;
  transition: background 0.2s ease;
}

@keyframes rise { from { transform: translateY(100%); } }
@media (prefers-reduced-motion: reduce) { .hud { animation: none; } }

.hud__head { display: flex; align-items: center; gap: 12px; }

/* The sequence name is what tells you where you are; the session code is telemetry. */
.hud__id { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; flex: 1 1 auto; min-width: 0; }
.hud__actions { display: flex; align-items: center; gap: 8px; flex: none; }
.hud__eyebrow { font: 12px/1 var(--sans); color: var(--muted); }
.hud__title {
  max-width: 100%;
  font: 500 18px/1.2 var(--sans);
  overflow: hidden;
}
/* Fits: an ordinary line of text, clipped with an ellipsis if it is a hair over. */
.hud__scroll {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Does not fit: the name reads itself out, sliding to its end and back with a pause at each,
   because an ellipsis hides exactly the part of a sequence name that tells two cuts apart.
   The distance and the duration are measured in hud.ts, so the speed is the same whatever the
   length. `alternate` is what returns it, since a jump back to the start reads as a glitch. */
.hud__title.is-marquee .hud__scroll {
  width: -webkit-max-content;
  width: max-content;
  overflow: visible;
  text-overflow: clip;
  animation: marquee var(--marquee-ms, 6s) ease-in-out infinite alternate;
}
@keyframes marquee {
  0%, 14% { transform: translateX(0); }
  86%, 100% { transform: translateX(var(--marquee-shift, 0)); }
}
@media (prefers-reduced-motion: reduce) {
  .hud__title.is-marquee .hud__scroll {
    width: auto;
    overflow: hidden;
    text-overflow: ellipsis;
    animation: none;
  }
}

.iconbtn {
  flex: none;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 9px;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--muted);
}
.iconbtn[aria-pressed="true"] {
  color: var(--scope);
  border-color: var(--scope);
  background: color-mix(in srgb, var(--scope) 12%, transparent);
}
.hud__state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px 4px 7px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  font: 500 10px/1 var(--mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.hud__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.45;
}
/* Status is the one readout that is a state rather than a number, so it says so in color:
   green when the link is up, the freeze color for a held frame, amber for anything unsettled.
   It does not pulse: a connection is either there or it is not, and a blinking healthy state is
   movement that carries no information. */
.hud__state.is-live {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
  background: color-mix(in srgb, var(--ok) 10%, transparent);
}
.hud__state.is-live .hud__dot {
  opacity: 1;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
}
.hud__state.is-frozen {
  color: var(--scope);
  border-color: color-mix(in srgb, var(--scope) 45%, transparent);
  background: color-mix(in srgb, var(--scope) 10%, transparent);
}
.hud__state.is-frozen .hud__dot {
  opacity: 1;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--scope) 22%, transparent);
}
.hud__state.is-waiting {
  color: var(--warning);
  border-color: color-mix(in srgb, var(--warning) 40%, transparent);
  background: color-mix(in srgb, var(--warning) 10%, transparent);
}
.hud__state.is-waiting .hud__dot { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .hud__state--playback.is-playing .hud__dot { animation: none; } }

/* Playback is the source's state, next to Status which is this phone's. Playing, paused and
   scrubbing have to be told apart across a room, so they differ in color, weight and movement
   rather than in shade.

   Playing is the scope color, the palette's instrument hue. Tally red was tried here and is
   wrong: red reads as recording or as a fault, and a timeline that is simply playing is neither.
   Nothing in the HUD is on air, so nothing in the HUD is tally. The pulse stays, because motion
   is what separates playing from paused at a glance, and a moving picture is what it describes. */
.hud__state--playback.is-playing {
  color: var(--scope);
  border-color: color-mix(in srgb, var(--scope) 45%, transparent);
  background: color-mix(in srgb, var(--scope) 10%, transparent);
}
.hud__state--playback.is-playing .hud__dot {
  opacity: 1;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--scope) 22%, transparent);
  animation: rolling 2s ease-in-out infinite;
}
@keyframes rolling { 50% { opacity: 0.4; box-shadow: 0 0 0 5px color-mix(in srgb, var(--scope) 8%, transparent); } }
.hud__state--playback.is-scrubbing {
  color: var(--warning);
  border-color: color-mix(in srgb, var(--warning) 40%, transparent);
  background: color-mix(in srgb, var(--warning) 8%, transparent);
}
.hud__state--playback.is-scrubbing .hud__dot { opacity: 1; }
.hud__state--playback.is-paused {
  color: var(--muted);
  border-color: color-mix(in srgb, var(--muted) 28%, transparent);
}
.hud__state--playback.is-paused .hud__dot { opacity: 0.3; }

.disclosure {
  margin-top: 12px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow: hidden;
}
.disclosure > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 44px;
  padding: 0 14px;
  font: 500 13px/1 var(--sans);
  color: var(--ink);
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::after {
  content: "";
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.15s ease;
}
.disclosure[open] > summary { border-bottom: 1px solid var(--rule); }
.disclosure[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }

.page__head {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 38px;
}
.backbtn {
  flex: none;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  margin-left: -10px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--ink);
}
.page__title { font: 500 17px/1.2 var(--sans); }

#hudSettings .hud__buttons { margin-top: 14px; }
#hudSettings .label { margin: 16px 0 0; }
.switches { display: flex; flex-direction: column; margin-top: 4px; }
.switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 0;
  cursor: pointer;
}
.switch:first-child { border-top: 1px solid var(--rule); }
.switch + .switch { border-top: 1px solid var(--rule); }
/* Kept focusable, but the track is what is seen. */
.switch input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.switch__track {
  flex: none;
  position: relative;
  width: 42px;
  height: 25px;
  border-radius: 999px;
  background: var(--rule);
  transition: background 0.15s ease;
}
.switch__track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  background: var(--muted);
  transition: transform 0.15s ease, background 0.15s ease;
}
.switch input:checked ~ .switch__track { background: color-mix(in srgb, var(--scope) 40%, transparent); }
.switch input:checked ~ .switch__track::after { transform: translateX(17px); background: var(--scope); }
.switch input:focus-visible ~ .switch__track { outline: 2px solid var(--scope); outline-offset: 2px; }
.switch__text { display: flex; flex-direction: column; gap: 2px; font-size: 14px; }
.switch__text small { color: var(--muted); font-size: 12px; }
@media (prefers-reduced-motion: reduce) {
  .switch__track, .switch__track::after, .disclosure > summary::after { transition: none; }
}

/* A footnote: available when something looks wrong, out of the way when it does not. */
.hud__help { margin: 12px 0 0; text-align: right; }
.footnote {
  padding: 4px 0;
  min-height: 28px;
  border: 0;
  background: none;
  color: var(--muted);
  font: 12px/1.3 var(--sans);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.footnote:hover { color: var(--scope); }

.linklike {
  padding: 6px 0;
  min-height: 32px;
  border: 0;
  background: none;
  color: var(--scope);
  font: 14px/1.3 var(--sans);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.hud__tele {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 6px 14px;
  margin: 12px 0 6px;
}
.hud__tele div { display: flex; flex-direction: column; }
.hud__tele dt { font: 10px/1.4 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.hud__tele dd { margin: 0; font: 14px/1.3 var(--mono); }
.hud__tele dd:has(.hud__state) { line-height: 1; }

/* The link the picture is arriving over, with the glyph the panel draws beside the same link. The
   icon is muted because the name is what is being read; it is there so the two screens match at a
   glance rather than to carry the meaning on its own. */
/* The address under the link's name: what was measured, under what was asked for. Muted and
   small, because it is the answer to a question only asked when something looks wrong. */
.hud__linkfrom {
  display: block;
  margin-top: 3px;
  color: var(--muted);
  font-size: 11px;
  letter-spacing: 0.02em;
}
.hud__linkfrom[hidden] { display: none; }

.hud__link { display: flex; align-items: center; gap: 6px; }
.hud__linkicon { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--muted); }

.hud__note { margin: 6px 0 10px; font-size: 12px; color: var(--muted); }

.hud__buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.overlay__action {
  pointer-events: auto;
  margin-top: 6px;
  min-height: 44px;
  padding: 10px 20px;
  border-radius: 8px;
  border: 1px solid var(--scope);
  background: transparent;
  color: var(--scope);
  font: 14px/1 var(--sans);
}

.hud__buttons button, .sheet__close, .sheet__body button {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 10px 14px;
  border-radius: 8px;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink);
  font: 14px/1 var(--sans);
}
.hud__buttons button.is-on { border-color: var(--scope); color: var(--scope); }

/* `.sheet__body button` gives every button in a sheet a border, which outranks the plain-text
   styles by specificity. Text that should read as a link has to say so at least as loudly. */
.sheet__body .footnote,
.sheet__body .linklike {
  border: 0;
  background: none;
  min-height: 28px;
  padding: 4px 0;
  flex: 0 0 auto;
}
.sheet__body .footnote { color: var(--muted); font-size: 12px; }
#hudSettings .switch:first-child { border-top: 1px solid var(--rule); }
.sheet__body .linklike { color: var(--scope); font-size: 14px; }

/* ---------------------------------------------------------------- Sheet */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 10;
  background: #0F1115;
  display: flex;
  flex-direction: column;
  padding: calc(env(safe-area-inset-top, 0px) + 14px) 16px calc(env(safe-area-inset-bottom, 0px) + 16px);
}
.sheet[hidden] { display: none; }
.sheet__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sheet__title { margin: 0; font: 600 18px/1.2 var(--sans); }
.sheet__close { flex: 0 0 auto; min-width: 92px; }
.sheet__body { margin-top: 14px; overflow: auto; -webkit-overflow-scrolling: touch; }

/* A reference list: what to change, where it is, and why it matters. */
.check { list-style: none; margin: 0 0 4px; padding: 0; }
.check li {
  display: grid;
  gap: 3px;
  padding: 14px 0 14px 14px;
  border-top: 1px solid var(--rule);
  border-left: 2px solid var(--scope);
  margin-left: 1px;
}
.check li:first-child { border-top: 0; }
.check__title { font-weight: 500; }
.check__where { font: 12px/1.5 var(--mono); color: var(--scope); }
.check__why { font-size: 13px; color: var(--muted); }

.result { margin: 0 0 16px; padding: 0; list-style: none; }
.result li { padding: 8px 0; border-bottom: 1px solid var(--rule); font-size: 14px; }

.patches { width: 100%; border-collapse: collapse; font: 12px/1.5 var(--mono); }
.patches th, .patches td { text-align: right; padding: 5px 6px; border-bottom: 1px solid var(--rule); }
.patches th:first-child, .patches td:first-child { text-align: left; }
.patches th { color: var(--muted); font-weight: 400; text-transform: uppercase; letter-spacing: 0.06em; }
.patches .swatch { display: inline-block; width: 12px; height: 12px; border-radius: 2px; margin-right: 6px; vertical-align: -1px; }
.patches .bad { color: var(--tally); }

.sheet__body p { font-size: 14px; }
.sheet__body .muted { color: var(--muted); font-size: 13px; }
.sheet__body code { font-family: var(--mono); color: var(--scope); word-break: break-all; }

/* Landscape on a phone leaves about 375 px of height: the HUD has to stay a strip. */
@media (max-height: 520px) {
  .hud { padding-top: 10px; padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 10px); }
  .hud__title { font-size: 15px; }
  /* Thirteen readouts at four columns is four rows, which eats a landscape phone. Let them
     find their own column count so the strip stays a strip. */
  .hud__tele { grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 4px 12px; margin: 8px 0 4px; }
  .hud__tele dd { font-size: 12px; }
  .hud__note { display: none; }
  .disclosure { margin-top: 8px; }
  .disclosure > summary { min-height: 40px; }
  .switch { padding: 9px 0; }
  .hud__buttons button { min-height: 40px; padding: 8px 10px; font-size: 13px; }
}

/* Shown only when the clipboard is unavailable, which is the normal case on a plain-http LAN page. */
.report-text {
  margin: 1rem 0 0;
  padding: .75rem;
  background: #0B0D10;
  border: 1px solid var(--rule);
  border-radius: 6px;
  font: 11px/1.5 var(--mono);
  color: var(--ink);
  white-space: pre;
  overflow-x: auto;
  -webkit-user-select: text;
  user-select: text;
}

/* Inviting someone: the QR is the part that works without a secure context. */
.qr {
  width: min(260px, 70vw);
  margin: 16px auto;
  padding: 12px;
  background: #fff;
  border-radius: 8px;
}
.qr svg { display: block; width: 100%; height: auto; }

.sharelink {
  font: 12px/1.5 var(--mono);
  color: var(--scope);
  word-break: break-all;
  -webkit-user-select: text;
  user-select: text;
}

/* The magnification and the way back out, held at the top of the picture for as long as it is
   zoomed. It is the only thing on screen that says the picture zooms at all, and it is a button,
   so getting back to the whole frame needs no remembered gesture. Shaped like the frozen tag,
   which sits in the same place; when both are up this one drops below it. */
.zoom-tag {
  position: absolute;
  z-index: 6;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--rule);
  color: var(--ink);
  white-space: nowrap;
  font: 11px/1 var(--mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}
.zoom-tag__scale { color: var(--scope); }
.zoom-tag__reset { color: var(--muted); }
.zoom-tag__reset::before { content: "\2014  "; }
.stage:has(.tag:not([hidden])) .zoom-tag { top: calc(env(safe-area-inset-top, 0px) + 46px); }
/* Both tags up at once: one more row to clear. */
.stage:has(.tag:not([hidden]) ~ .tag:not([hidden])) .zoom-tag { top: calc(env(safe-area-inset-top, 0px) + 80px); }

/* Two glyphs in one button, one of them hidden: the speaker shows what the sound is doing. */
.iconbtn__glyph { display: inline-flex; }
.iconbtn__glyph[hidden] { display: none; }

/* The one note under the sound switch, for what the page cannot do anything about. */
.sound-note {
  margin: 6px 2px 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.35;
}

/* The gestures, written down in Settings, because the picture cannot say them itself. */
.gestures { margin: 0; }
.gestures div { display: flex; gap: 10px; align-items: baseline; padding: 5px 0; }
.gestures dt { flex: 0 0 82px; color: #E6E9ED; font-size: 12px; }
.gestures dd { margin: 0; color: #8A93A0; font-size: 12px; }

/* Joining by code rather than by QR.
   Outside the stage and above everything, including the HUD and the sheet, because until this is
   answered there is no session: no picture, and nothing worth reading about one. It started
   inside the stage, where the HUD -- a later sibling of the stage -- painted straight over it. */
.join {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 24px;
  /* Flat black, like the stage behind it, and written out rather than taken from a custom
     property: `--ground` is the panel's palette, not this one, and a custom property that does
     not exist fails silently -- this screen came out transparent, with the overlay underneath
     reading through its own text. The same trap is noted against --warning at the top of this
     file. */
  background: #000;
  text-align: center;
}
.join__title { margin: 0; font: 500 19px/1.3 var(--sans); color: var(--ink); }
.join__hint { margin: 0; max-width: 30ch; font: 13px/1.45 var(--sans); color: var(--muted); }
/* Wide tracking and a big target: this is read off a screen across the room and typed with one
   thumb. The characters are the code's own alphabet, which has no I, O, 0 or 1 in it. */
.join__code {
  width: 100%;
  max-width: 260px;
  margin-top: 6px;
  padding: 14px 12px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
  color: var(--ink);
  font: 600 30px/1 var(--mono);
  letter-spacing: 0.34em;
  text-align: center;
  text-indent: 0.34em;  /* the tracking is added after the last character; this re-centres it */
  text-transform: uppercase;
}
.join__code:focus { outline: 2px solid var(--scope); outline-offset: 2px; }
.join__go {
  width: 100%;
  max-width: 260px;
  min-height: 48px;
  padding: 12px;
  border: 0;
  border-radius: 10px;
  background: var(--scope);
  color: #06181F;
  font: 500 15px var(--sans);
  cursor: pointer;
}
.join__go:disabled { opacity: 0.45; cursor: default; }
.join__error { margin: 0; max-width: 32ch; font: 13px/1.45 var(--sans); color: var(--warning); }
