/* ============================================================================
   fxcam-modern.css — broadcast-grade reskin for fxcam.html (fan mobile).
   Layered ON TOP of the existing styles; targets real elements only, changes
   NO ids/markup, reversible by removing the <link>.

   Every typeface, size and colour is sourced from the per-game JSON theme
   (--font-family / --secondary-font-family / --font-size / --secondary-font-size
   / --*-color, set on <body>). The values below are only FALLBACKS — the aicam
   look is the default, the client theme wins. Tokens live on <body> so they can
   read those body-set vars.
   ============================================================================ */

body {
  --fx-sans: var(--font-family, "Archivo", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  --fx-mono: var(--secondary-font-family, "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace);
  --fx-base: var(--font-size, 1rem);
  --fx-label: var(--secondary-font-size, 0.72rem);
  --fx-ink: var(--background-color, #0b0e14);
  --fx-text: var(--primary-color, #eceef2);
  --fx-amber: var(--secondary-color, #ffb02e);
  --fx-tally: #ff2e3f;   /* semantic LIVE — not themeable */
  --fx-ok: #3ddc84;
  --fx-dim: color-mix(in srgb, var(--fx-text) 55%, transparent);
  --fx-panel: color-mix(in srgb, var(--fx-ink) 86%, var(--fx-text) 6%);
  --fx-line: color-mix(in srgb, var(--fx-text) 16%, transparent);
  --fx-glass: color-mix(in srgb, var(--fx-ink) 58%, transparent);
  --fx-ink-2: color-mix(in srgb, var(--fx-ink) 92%, var(--fx-text) 4%);

  /* The fan page's key visual (mobile_image) has to be spelled out here: this
     rule is `background: … !important`, so without the var(--background-image)
     layer it wiped main.css's own background-image and left every real handset
     flat — the artwork only ever showed inside the desktop phone frame (the
     @media rule below). */
  background:
    radial-gradient(120% 60% at 50% -10%, color-mix(in srgb, var(--fx-amber) 8%, transparent), transparent 55%),
    /* the back office's "darken" knob, wired the way main.css wires it:
       0 by default, so the artwork shows as uploaded */
    linear-gradient(rgba(0,0,0,var(--bg-darken,0)),rgba(0,0,0,var(--bg-darken,0))),
    var(--background-image, none),
    var(--fx-ink) !important;
  background-size: cover, cover, cover, auto !important;
  background-position: center top !important;
  background-repeat: no-repeat !important;
  background-attachment: fixed !important;
  color: var(--fx-text);
  font-family: var(--fx-sans);
  font-size: var(--fx-base);
  -webkit-font-smoothing: antialiased;
}

/* On desktop the page is a phone column floating on the game's key visual, and
   that column carries the artwork too — the way the landing's .frame does.
   It has to be spelled out as a full shorthand here: the rule above is
   `background: … !important`, which resets background-image wholesale and threw
   away mobile-frame.css's own var(--background-image) layer, leaving the frame
   flat while the backdrop behind it carried the picture. Same cover/fixed
   geometry as that sheet, so frame and backdrop stay in register. */
@media (min-width: 520px) {
  body.mobile-frame {
    background:
      radial-gradient(120% 60% at 50% -10%, color-mix(in srgb, var(--fx-amber) 8%, transparent), transparent 55%),
      /* the back office's "darken" knob, wired the way main.css wires it for
         the page background: 0 by default, so the artwork shows as uploaded */
      linear-gradient(rgba(0,0,0,var(--bg-darken,0)),rgba(0,0,0,var(--bg-darken,0))),
      var(--background-image, none),
      var(--fx-ink) !important;
    background-size: cover, cover, cover, auto !important;
    background-position: center top !important;
    background-repeat: no-repeat !important;
    background-attachment: fixed !important;
  }
}

/* ---- Nickname form → clean glass card ---- */
#formContainer form{
  padding:0.5rem;
}
#formContainer.form, #formContainer {
  background: var(--fx-panel);
  border: 1px solid var(--fx-line);
  border-radius: 18px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
  color: var(--fx-text);
}
#formContainer label { color: var(--fx-dim); font-weight: 600; }
#formContainer input[type="text"] {
  background: var(--fx-ink-2);
  border: 1px solid var(--fx-line);
  border-radius: 12px;
  color: var(--fx-text);
  font-family: var(--fx-sans);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
#formContainer input[type="text"]:focus {
  outline: none;
  border-color: var(--fx-amber);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-amber) 22%, transparent);
}

/* ---- Camera as the hero: rounded, framed, glowing ---- */
#videoContainer {
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  background: #05070b;
  /* box-shadow intentionally omitted — the ARCADE SKIN layer sets the neon glow
     with !important, so any value here would be dead. */
}
#deepar-container, #deepar-container canvas { border-radius: 22px; }

#switchCameraBtn {
  background: var(--fx-glass) !important;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--fx-line) !important;
  border-radius: 50% !important;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
}

/* ---- Tally-light LIVE ---- */
.live-badge {
  font-family: var(--fx-mono);
  font-size: var(--fx-label);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(255, 46, 63, 0.16);
  border: 1px solid rgba(255, 46, 63, 0.5);
  border-radius: 999px;
  padding: 5px 11px 5px 9px;
  display: inline-flex; align-items: center; gap: 7px;
  backdrop-filter: blur(8px);
}
.live-badge::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--fx-tally); animation: fxTally 1.8s ease-out infinite;
}

/* ---- Controls + filter filmstrip ---- */
#controls { color: var(--fx-text); }
.gallery-intro {
  font-family: var(--secondary-font-family, var(--fx-mono));
  font-size: var(--secondary-font-size, var(--fx-label));
  line-height: 1;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--fx-dim);
}
.gallery-cell {
  border-radius: 14px !important;
  border: 10px solid transparent !important;
  background: var(--fx-panel);
  color: var(--fx-text);
  font-family: var(--secondary-font-family, var(--fx-mono));
  font-size: var(--secondary-font-size, var(--fx-label));
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 8px;
  /* NB: never transition/override `transform` on the cell — Flickity uses cell
     transforms for wrapAround positioning; touching it breaks/janks the slider.
     Also DON'T transition box-shadow: with wrapAround, Flickity moves .is-selected
     from cell to cell on every scroll, so an animated shadow would repaint the whole
     strip each frame and grind the slider (worse on a DeepAR-loaded phone). The
     applied ring (.filter-on) snaps in instantly on tap — no animation needed. */
  transition: border-color 0.18s ease;
}
/* Flickity stamps .is-selected on the CENTERED cell as you scroll/arrow, and the
   legacy selfiecam.min.css turns that into a big ring (10px border). Reset it to
   the base border (no transform touch) so navigating never looks like applying. */
.gallery-cell.is-selected {
  border: 10px solid transparent !important;
}
.gallery-cell.is-selected img { filter: none !important; }
/* Applied-filter ring (.gallery-cell.filter-on) is defined once, in the ARCADE
   SKIN layer below — it re-set both border-color and box-shadow here, so the
   duplicate base rule was dead and has been removed. */
.gallery-cell.no-filter-cell { color: var(--fx-dim); }
/* Cached filter preview image fills the cell, with the label as a caption.
   pointer-events:none so a swipe/tap goes to the Flickity CELL (not a native
   image-drag) — otherwise the slider won't scroll and taps won't register. */
.gallery-cell .filter-thumb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 12px;
  display: block;
  pointer-events: none;
  -webkit-user-drag: none;
  user-select: none;
}
.gallery-cell .filter-thumb-label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  pointer-events: none;
  user-select: none;
  padding: 10px 6px 5px;
  font-family: var(--secondary-font-family, var(--fx-mono));
  font-size: calc(var(--secondary-font-size, var(--fx-label)) * 0.92);
  line-height: 1;
  text-align: center;
  color: #fff;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.72));
  border-radius: 0 0 12px 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- Capture dock ---- */
#cameraBtn.btn {
  font-weight: 700;
  border-radius: 999px;
  border: none;
  transition: transform 0.15s ease, box-shadow 0.2s ease, filter 0.2s ease;
}
#cameraBtn.btn-start {
  background: var(--fx-amber);
  color: color-mix(in srgb, var(--fx-ink) 88%, #000);
  /* box-shadow set by the ARCADE SKIN layer (!important) — omitted here to avoid dead code. */
}
#cameraBtn.btn-stop {
  background: var(--fx-tally);
  color: #fff;
  /* box-shadow set by the ARCADE SKIN layer (!important) — omitted here to avoid dead code. */
}
#cameraBtn.btn:active { transform: scale(0.97); }

#recordingBtn, #screenshotBtn {
  font-family: var(--fx-mono);
  font-size: calc(var(--fx-label) * 1.05);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--fx-glass);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--fx-text);
  border: 1px solid var(--fx-line);
  border-radius: 999px;
  transition: border-color 0.2s ease, transform 0.15s ease;
}
#recordingBtn:active, #screenshotBtn:active { transform: scale(0.97); }
#recordingBtn:hover, #screenshotBtn:hover { border-color: var(--fx-amber); }

/* ---- Status → mono caption ---- */
#status {
  font-family: var(--fx-mono);
  font-size: var(--fx-label);
  letter-spacing: 0.06em;
  color: var(--fx-dim);
}

@keyframes fxTally { 0% { box-shadow: 0 0 0 0 rgba(255, 46, 63, 0.55); } 70%, 100% { box-shadow: 0 0 0 7px rgba(255, 46, 63, 0); } }
@media (prefers-reduced-motion: reduce) { .live-badge::before { animation: none !important; } }

/* ============================================================================
   ARCADE SKIN — neon-glow / CRT layer over the modern base. Accent-driven so it
   still themes from the client colour; kept GPU-light (static scanlines, one
   slow border pulse) because the page is already DeepAR-heavy.
   ============================================================================ */
body {
  --fx-neon: var(--fx-amber);
  --fx-neon-2: color-mix(in srgb, var(--fx-amber) 40%, #00e5ff);  /* cool arcade counter-glow */
}

/* The camera = the arcade CRT: neon bezel, inner vignette, scanlines. */
#videoContainer {
  border: 2px solid var(--fx-neon) !important;
  /* STATIC glow — animating box-shadow forces a full repaint every frame, which
     on a DeepAR-loaded phone can exhaust the GPU and crash the page. */
  box-shadow:
    0 0 14px color-mix(in srgb, var(--fx-neon) 65%, transparent),
    0 0 44px color-mix(in srgb, var(--fx-neon) 28%, transparent),
    inset 0 0 60px rgba(0, 0, 0, 0.55) !important;
}
#videoContainer::after {
  content: "";
  position: absolute; inset: 0; z-index: 6; pointer-events: none;
  border-radius: 22px;
  background:
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.16) 0 1px, transparent 1px 3px),
    radial-gradient(120% 100% at 50% 50%, transparent 62%, rgba(0, 0, 0, 0.4) 100%);
}

/* Filter carousel = neon cartridge selector. */
.gallery-intro {
  color: var(--fx-neon);
  text-shadow: 0 0 8px color-mix(in srgb, var(--fx-neon) 60%, transparent);
}
.gallery-cell {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fx-neon) 26%, transparent), inset 0 0 16px rgba(0, 0, 0, 0.45);
  text-shadow: 0 0 6px color-mix(in srgb, var(--fx-neon) 45%, transparent);
}
/* Applied-filter ring — dedicated class, set only on a real tap (NOT .is-selected,
   which Flickity re-stamps on every scroll frame). Border-color + box-shadow only;
   never touch transform — it would fight Flickity's wrapAround. */
.gallery-cell.filter-on {
  border-color: var(--fx-neon) !important;
  box-shadow:
    0 0 0 2px var(--fx-neon),
    0 0 16px color-mix(in srgb, var(--fx-neon) 75%, transparent),
    0 0 34px color-mix(in srgb, var(--fx-neon) 32%, transparent) !important;
}
.gallery-cell .filter-thumb-label {
  color: #fff;
  text-shadow: 0 0 6px var(--fx-neon), 0 1px 2px #000;
  background: linear-gradient(transparent, color-mix(in srgb, var(--fx-neon) 22%, rgba(0, 0, 0, 0.82)));
}

/* Chunky glowing arcade buttons (a hard bottom edge = the "cabinet key"). */
#cameraBtn.btn {
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
#cameraBtn.btn-start {
  color: #14100a;
  box-shadow:
    0 6px 0 color-mix(in srgb, var(--fx-neon) 45%, #000),
    0 0 20px color-mix(in srgb, var(--fx-neon) 70%, transparent),
    inset 0 0 12px rgba(255, 255, 255, 0.35) !important;
}
#cameraBtn.btn-stop {
  box-shadow:
    0 6px 0 rgba(120, 12, 20, 0.9),
    0 0 20px rgba(255, 46, 63, 0.6),
    inset 0 0 12px rgba(255, 255, 255, 0.25) !important;
}
#cameraBtn.btn:active { transform: translateY(4px) !important; }
#recordingBtn, #screenshotBtn {
  text-shadow: 0 0 6px color-mix(in srgb, var(--fx-neon) 45%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fx-neon) 35%, transparent), 0 0 12px color-mix(in srgb, var(--fx-neon) 22%, transparent);
}
#recordingBtn:hover, #screenshotBtn:hover {
  box-shadow: inset 0 0 0 1px var(--fx-neon), 0 0 18px color-mix(in srgb, var(--fx-neon) 55%, transparent);
}

/* Neon LIVE tally with a slight arcade flicker. */
.live-badge {
  box-shadow: 0 0 10px var(--fx-tally), 0 0 22px color-mix(in srgb, var(--fx-tally) 45%, transparent);
  text-shadow: 0 0 8px var(--fx-tally);
  animation: fxFlicker 4s steps(1) infinite;
}
#status { text-shadow: 0 0 6px color-mix(in srgb, var(--fx-neon) 40%, transparent); }

/* ---- Duotone (v2 mockup): a static cyan underline on the "choose your filter"
   label. NB: intentionally NO per-cell "centered" highlight — with wrapAround the
   is-selected cell changes on every scroll frame, so any style change there repaints
   the strip and janks the slider on the DeepAR-heavy phone. The applied amber ring
   (.filter-on, tap-only) is the sole cell state. ---- */
.gallery-intro::after {
  content: "";
  display: block;
  width: 34px;
  height: 2px;
  margin: 6px auto 0;
  border-radius: 2px;
  background: var(--fx-neon-2);
  box-shadow: 0 0 8px color-mix(in srgb, var(--fx-neon-2) 70%, transparent);
}

@keyframes fxArcadeGlow {
  0%, 100% { box-shadow: 0 0 12px color-mix(in srgb, var(--fx-neon) 55%, transparent), 0 0 40px color-mix(in srgb, var(--fx-neon) 22%, transparent), inset 0 0 60px rgba(0,0,0,.55); }
  50% { box-shadow: 0 0 20px color-mix(in srgb, var(--fx-neon) 85%, transparent), 0 0 60px color-mix(in srgb, var(--fx-neon) 40%, transparent), inset 0 0 60px rgba(0,0,0,.55); }
}
@keyframes fxFlicker {
  0%, 92%, 100% { opacity: 1; }
  94% { opacity: 0.72; }
  96% { opacity: 1; }
  98% { opacity: 0.85; }
}
@media (prefers-reduced-motion: reduce) {
  #videoContainer, .live-badge { animation: none !important; }
}

/* ============================================================================
   JOIN SCREEN — form stage.
   The card opens on the fan's spot on the big screen with the camera OFF: a dark
   16:10 "screen" with crop marks, a status chip (Camera off → Ready), a
   camera-off icon with a caption naming the next step, and the jumbotron
   nameplate along the bottom — which is the nickname field itself. The name
   visibly comes first, and nothing suggests the camera is already on.
   Colours come from the card's own text/ink tokens: the shared form CSS paints
   labels and #notice with --secondary-color (!important), invisible on this
   dark card for many themes. State colours (standby amber, ready green) are
   fixed semantics, like the viewfinder's tally.
   ============================================================================ */
body {
  --fx-join-accent: var(--primary-color, #ffb02e);
  --fx-join-standby: #ffb020;
  --fx-join-ready: var(--fx-ok, #3ddc84);
  /* Long copy in the secondary face: the primary one is often a heavy display
     font that is hard to read over several lines. */
  --fx-join-body: var(--secondary-font-family, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
}

#formContainer form {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: 0.75rem 0.75rem 0.9rem;
}

.fx-join-visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
}

/* ---- The screen ---- */
.fx-join-screen {
  position: relative;
  aspect-ratio: 4 / 3;
  min-height: 220px;
  max-height: 46vh;
  max-width: 100%;
  border-radius: 14px;
  overflow: hidden;
  background:
    radial-gradient(90% 70% at 50% 40%, color-mix(in srgb, var(--fx-text) 7%, transparent), transparent 70%),
    repeating-linear-gradient(0deg, color-mix(in srgb, black 22%, transparent) 0 1px, transparent 1px 3px),
    color-mix(in srgb, var(--fx-ink) 70%, black);
  border: 1px solid var(--fx-line);
  box-shadow: inset 0 0 40px color-mix(in srgb, black 60%, transparent);
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.fx-join-screen[data-state="ready"] {
  border-color: color-mix(in srgb, var(--fx-join-ready) 70%, transparent);
  box-shadow:
    inset 0 0 40px color-mix(in srgb, black 60%, transparent),
    0 0 0 3px color-mix(in srgb, var(--fx-join-ready) 18%, transparent);
}

/* Corner crop marks (one element, four gradient corners). */
.fx-join-screen__marks {
  position: absolute;
  inset: 8px;
  pointer-events: none;
  --m: color-mix(in srgb, var(--fx-text) 45%, transparent);
  background:
    linear-gradient(var(--m), var(--m)) top left / 14px 2px,
    linear-gradient(var(--m), var(--m)) top left / 2px 14px,
    linear-gradient(var(--m), var(--m)) top right / 14px 2px,
    linear-gradient(var(--m), var(--m)) top right / 2px 14px,
    linear-gradient(var(--m), var(--m)) bottom left / 14px 2px,
    linear-gradient(var(--m), var(--m)) bottom left / 2px 14px,
    linear-gradient(var(--m), var(--m)) bottom right / 14px 2px,
    linear-gradient(var(--m), var(--m)) bottom right / 2px 14px;
  background-repeat: no-repeat;
}

/* Status chip, top-left. */
.fx-join-screen__status {
  position: absolute;
  inset-block-start: 18px;
  inset-inline-start: 20px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 10px 4px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, black 55%, transparent);
  font-family: var(--fx-mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fx-join-standby);
}
.fx-join-screen__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  animation: fxJoinBlink 1.6s steps(1) infinite;
}
.fx-join-screen[data-state="ready"] .fx-join-screen__status { color: var(--fx-join-ready); }
.fx-join-screen[data-state="ready"] .fx-join-screen__dot { animation: none; }

/* Per-state texts: all in the markup (translated by data-i18n), one shown. */
.fx-join-screen [data-when] { display: none; }
.fx-join-screen:not([data-state="ready"]) .fx-join-screen__status [data-when="off"],
.fx-join-screen[data-state="ready"] .fx-join-screen__status [data-when="ready"],
.fx-join-screen[data-step="nickname"] .fx-join-screen__caption [data-when="nickname"],
.fx-join-screen[data-step="post"] .fx-join-screen__caption [data-when="post"],
.fx-join-screen[data-step="consent"] .fx-join-screen__caption [data-when="consent"],
.fx-join-screen[data-step="ready"] .fx-join-screen__caption [data-when="ready"] { display: inline; }

/* Centre: camera-off icon + what to do next. */
.fx-join-screen__center {
  position: absolute;
  inset: 48px 0 72px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 1.2rem;
  text-align: center;
}
.fx-join-screen__icon {
  width: 42px;
  height: 42px;
  fill: none;
  stroke: color-mix(in srgb, var(--fx-text) 60%, transparent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.fx-join-screen__icon-slash {
  stroke: var(--fx-join-standby);
  stroke-width: 2;
  transition: opacity 0.2s ease;
}
.fx-join-screen[data-state="ready"] .fx-join-screen__icon { stroke: var(--fx-join-ready); }
.fx-join-screen[data-state="ready"] .fx-join-screen__icon-slash { opacity: 0; }
.fx-join-screen__caption {
  margin: 0;
  max-width: 22ch;
  font-family: var(--fx-join-body);
  font-size: 0.86rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--fx-text);
  text-transform: none;
}

/* Nameplate along the bottom = the nickname field. */
.fx-join-screen__plate.form-group {
  position: absolute;
  inset-inline: 20px;
  inset-block-end: 20px;
  margin: 0 !important;
  display: flex;
  align-items: stretch;
  height: 44px;
  border-radius: 8px;
  overflow: hidden;
  background: color-mix(in srgb, black 78%, transparent);
  border: 1px solid color-mix(in srgb, var(--fx-join-accent) 55%, transparent);
  border-inline-start: 4px solid var(--fx-join-accent);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.fx-join-screen__plate.form-group:focus-within {
  border-color: var(--fx-join-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-join-accent) 25%, transparent);
}
.fx-join-screen__tag {
  display: flex;
  align-items: center;
  padding-inline: 10px 4px;
  font-family: var(--fx-mono);
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--fx-join-accent);
}
/* !important throughout: the shared form CSS sizes, uppercases and boxes every
   input with !important, and the returning-fan code hides this one inline —
   here it stays visible so a returning fan sees (and can change) their name. */
body #formContainer .fx-join-screen__plate input#nickname {
  display: block !important;
  flex: 1 1 auto;
  min-width: 0;
  width: auto !important;
  height: 100% !important;
  min-height: 0 !important;
  margin: 0 !important;
  padding: 0 10px 0 4px !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
  color: var(--fx-text) !important;
  font-family: var(--fx-sans) !important;
  font-size: 1.05rem !important;
  font-weight: 800 !important;
  letter-spacing: 0.02em;
  text-align: start !important;
  text-transform: uppercase !important;
  caret-color: var(--fx-join-accent);
}
body #formContainer .fx-join-screen__plate input#nickname::placeholder {
  color: color-mix(in srgb, var(--fx-text) 45%, transparent);
  font-weight: 700;
}
body #formContainer .fx-join-screen__plate input#nickname:focus { outline: none; }
/* The screen's caption says what's missing; the red inline errors and the
   stored-nickname echo would only crowd it. */
body #formContainer #stored-nickname,
body #formContainer #nicknameError,
body #formContainer #consentError { display: none !important; }

/* ---- Notice (info.notice, or the default explanation) ---- */
body #formContainer #notice.fx-join-notice {
  margin: 0;
  padding: 0 0.2rem;
  background: none;
  border: 0;
  color: color-mix(in srgb, var(--fx-text) 82%, transparent) !important;
  /* the shared #notice rule forces uppercase + a 1.5rem display size */
  font-family: var(--fx-join-body) !important;
  font-size: 0.84rem !important;
  font-weight: 500;
  line-height: 1.4 !important;
  text-align: center;
  text-transform: none !important;
  letter-spacing: 0;
}
body #formContainer #notice.fx-join-notice:empty { display: none; }

/* ---- Consent: one compact, readable row ---- */
body #formContainer .disclaimer { margin: 0 !important; padding: 0 !important; }
body #formContainer .disclaimer label {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  margin: 0;
  padding: 0.6rem 0.7rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--fx-text) 5%, transparent);
  text-align: start;
  cursor: pointer;
}
body #formContainer .disclaimer label,
body #formContainer .disclaimer label span {
  color: color-mix(in srgb, var(--fx-text) 85%, transparent) !important;
  font-family: var(--fx-join-body) !important;
  font-size: 0.72rem !important;
  font-weight: 500 !important;
  line-height: 1.35 !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
}
body #formContainer .disclaimer input[type="checkbox"] {
  flex: 0 0 auto;
  width: 22px !important;
  height: 22px !important;
  margin: 0 !important;
  accent-color: var(--fx-join-accent);
}

/* ---- Button ---- */
body #controls #cameraBtn:disabled {
  opacity: 1 !important;
  border: 1px dashed var(--fx-line) !important;
}

/* Pinned call to action (form stage only), so the button never ends up under
   the bottom dock on a short phone. #controls already carries the dock's height
   as padding-bottom (inline <style> in fxcam.html), so bottom:0 parks the button
   right above it. Keyed on the form being visible: the streaming dock (fixed,
   fxcam-viewfinder.css) and the lobby are untouched. */
body:has(#formContainer:not([style*="display: none"])):not(.fxv-streaming) #controls {
  position: sticky;
  bottom: 0;
  z-index: 20;
  padding-top: 1.2rem;
  background: linear-gradient(
    to bottom,
    transparent,
    color-mix(in srgb, var(--fx-ink) 94%, transparent) 1.1rem,
    var(--fx-ink)
  );
}
body:has(#formContainer:not([style*="display: none"])):not(.fxv-streaming) #controls #cameraBtn.btn {
  margin-block: 0 0.5rem !important;
}

@keyframes fxJoinBlink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) {
  .fx-join-screen__dot { animation: none; }
  .fx-join-screen, .fx-join-screen__plate.form-group, .fx-join-screen__icon-slash { transition: none; }
}
