/*
 * Design tokens. Taken from the event's design template essentially unchanged -
 * its README offers tokens.css as the whole design system, to be used as-is,
 * and its own comments record the rehearsal findings that shaped it (the
 * white-on-white select popup, the jittering timer, the 16px iOS zoom floor).
 * Those are the same findings this build hit, so it is not a second opinion,
 * it is the same one written down better.
 *
 * Served from /static/tokens.css and shared by the participant page and the
 * facilitator page.
 *
 * Two things here are load-bearing and must not be "tidied":
 *   - every colour has a value in BOTH themes, and form controls set their own
 *     explicitly. `inherit` or `transparent` on a field or an option is what
 *     produced white-on-white dropdowns in desktop dark mode.
 *   - the accent tokens are OVERWRITTEN at runtime by branding.js, which
 *     derives contrast-safe values per theme from whatever hue the facilitator
 *     picked. The values below are the unbranded defaults, not the truth.
 */

/*
 * Design tokens — Passport Stamps.
 *
 * Light and dark are both first-class. The Changshu rehearsal found the app
 * follows the OS theme, so every colour is defined in both and nothing is
 * left to inherit.
 *
 * Theme resolution has three states, matching how a phone actually behaves:
 *   :root                              → light (the default)
 *   :root:not([data-theme="light"]) under prefers-color-scheme: dark → dark
 *   :root[data-theme="dark"|"light"]   → an explicit choice wins over both
 */

:root {
  /* --- Surfaces. Warm paper rather than pure white: this is a passport. --- */
  --bg: #f4f2ed;
  --surface: #fffefb;
  --surface-sunken: #eceae4;
  --border: rgba(20, 24, 33, 0.12);
  --border-strong: rgba(20, 24, 33, 0.22);

  /* --- Text --- */
  --text: #141821;
  --text-dim: rgba(20, 24, 33, 0.6);      /* labels, per spec 60% */
  --text-faint: rgba(20, 24, 33, 0.3);    /* dead clocks, per spec 30% */

  /* --- Accents --- */
  --primary: #2f4bcc;
  --primary-hover: #2540b4;
  --primary-text: #ffffff;
  /* The accent used as *text* rather than as a fill. Held separately because
   * the two have different contrast floors — a fill needs 3:1 against the
   * surface, small text needs 4.5:1 — and because branding lets a facilitator
   * pick the accent, so a colour that is fine on a button can be unreadable
   * as an 11px player number. shared/branding.js derives both. */
  --primary-ink: #2f4bcc;
  --stamp: #b23a20;                        /* the passport stamp ink */
  --stamp-soft: rgba(178, 58, 32, 0.1);
  --ok: #1c7a4f;
  --warn: #9a6410;
  --warn-soft: rgba(154, 100, 16, 0.12);
  --danger: #b2302c;

  /* --- Form controls -------------------------------------------------
   * Named separately and always set explicitly. The rehearsal found
   * white-on-white options in desktop dark mode, because a <select> inherits
   * a light text colour onto a system-drawn white popup. Never `inherit`,
   * never `transparent`, on a field or an option.
   */
  --field: #ffffff;
  --field-text: #141821;
  --field-border: rgba(20, 24, 33, 0.2);
  --field-placeholder: rgba(20, 24, 33, 0.38);
  --field-focus: #2f4bcc;

  /* --- Type --- */
  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --font-num: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas,
              "Liberation Mono", monospace;

  /* --- Metrics from the layout spec --- */
  --frame-w: 390px;
  --frame-h: 844px;
  --band-logo: 64px;
  --band-identity: 96px;
  --tile-h: 72px;
  --safe-area: 34px;
  --pad: 16px;
  --gap: 8px;
  --radius: 12px;
  --radius-sm: 8px;

  --shadow: 0 1px 2px rgba(20, 24, 33, 0.06), 0 8px 24px rgba(20, 24, 33, 0.06);
}

/* System dark, unless the viewer explicitly chose light. */
:root:not([data-theme="light"]) {
  @media (prefers-color-scheme: dark) {
    color-scheme: dark;
  }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d1014;
    --surface: #161a21;
    --surface-sunken: #10141a;
    --border: rgba(255, 255, 255, 0.12);
    --border-strong: rgba(255, 255, 255, 0.22);

    --text: #f1f2f5;
    --text-dim: rgba(241, 242, 245, 0.6);
    --text-faint: rgba(241, 242, 245, 0.3);

    --primary: #6d8bff;
    --primary-hover: #8098ff;
    --primary-text: #0d1014;
    --primary-ink: #6d8bff;
    --stamp: #ff8464;
    --stamp-soft: rgba(255, 132, 100, 0.12);
    --ok: #4cc38a;
    --warn: #e0a850;
    --warn-soft: rgba(224, 168, 80, 0.14);
    --danger: #ff8a85;

    --field: #1c212a;
    --field-text: #f1f2f5;
    --field-border: rgba(255, 255, 255, 0.2);
    --field-placeholder: rgba(241, 242, 245, 0.4);
    --field-focus: #6d8bff;

    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.3);
  }
}

/* Explicit choice beats the system in both directions. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0d1014;
  --surface: #161a21;
  --surface-sunken: #10141a;
  --border: rgba(255, 255, 255, 0.12);
  --border-strong: rgba(255, 255, 255, 0.22);

  --text: #f1f2f5;
  --text-dim: rgba(241, 242, 245, 0.6);
  --text-faint: rgba(241, 242, 245, 0.3);

  --primary: #6d8bff;
  --primary-hover: #8098ff;
  --primary-text: #0d1014;
  --primary-ink: #6d8bff;
  --stamp: #ff8464;
  --stamp-soft: rgba(255, 132, 100, 0.12);
  --ok: #4cc38a;
  --warn: #e0a850;
  --warn-soft: rgba(224, 168, 80, 0.14);
  --danger: #ff8a85;

  --field: #1c212a;
  --field-text: #f1f2f5;
  --field-border: rgba(255, 255, 255, 0.2);
  --field-placeholder: rgba(241, 242, 245, 0.4);
  --field-focus: #6d8bff;

  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.3);
}

:root[data-theme="light"] {
  color-scheme: light;
}

/* ------------------------------------------------------------------ base */

* { box-sizing: border-box; }

/* A component that sets its own `display` would otherwise outrank the UA's
   [hidden] rule and stay on screen. Stated once, here, rather than patched
   per component. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

/* Tabular figures throughout. A number that changes every few seconds must
   not change width, or the whole header jitters. */
.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ------------------------------------------------- form controls, shared */

input,
select,
textarea,
button {
  font-family: inherit;
  font-size: 16px;          /* below 16px iOS Safari zooms the page on focus */
}

/*
 * Text-like fields only. A checkbox or radio must be excluded: `appearance:
 * none` strips its native tick and `width: 100%` stretches it into a bar, so
 * the control renders as a plain rectangle that never visibly changes when
 * toggled. That is what happened to the facilitator's two pre-game switches on
 * 2026-08-25 - they were clickable and did nothing a human could see.
 */
input:not([type="checkbox"]):not([type="radio"]),
select,
textarea {
  width: 100%;
  padding: 13px 14px;
  border-radius: var(--radius-sm);
  /* Explicit, never inherited — see the note in the token block. */
  background-color: var(--field);
  color: var(--field-text);
  border: 1px solid var(--field-border);
  appearance: none;
}

input::placeholder,
textarea::placeholder { color: var(--field-placeholder); }

/* Never `both`. A horizontal drag on a 390px frame pushes the textarea past
   the card edge, and on a phone the handle is only ever hit by accident. */
textarea { resize: vertical; }

input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible {
  outline: 2px solid var(--field-focus);
  outline-offset: 2px;
}

/* The rehearsal bug, fixed at the source: a system-drawn popup gets its own
   colours rather than inheriting a light text colour onto a white surface. */
select {
  background-image: linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
                    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-position: calc(100% - 20px) calc(50% + 2px),
                       calc(100% - 15px) calc(50% + 2px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 40px;
}

select option {
  background-color: var(--field);
  color: var(--field-text);
}

button {
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
}

.btn-primary {
  width: 100%;
  padding: 15px 18px;       /* a large tap target, per the brief */
  background: var(--primary);
  color: var(--primary-text);
  border-color: var(--primary);
  font-size: 15px;
}
.btn-primary:hover:not(:disabled) { background: var(--primary-hover); }

/* A disabled primary stops being primary. Dimming a saturated fill still
   leaves it the loudest thing on screen, which is the wrong signal for a
   facilitator glancing down mid-event — the illegal action should not be the
   one that catches the eye. */
.btn-primary:disabled {
  background: var(--surface-sunken);
  border-color: var(--border);
  color: var(--text-faint);
  cursor: default;
}

.btn-quiet {
  padding: 11px 14px;
  background: var(--surface);
  color: var(--text);
  border-color: var(--border-strong);
  font-size: 14px;
}
.btn-quiet:hover:not(:disabled) { background: var(--surface-sunken); }
.btn-quiet:disabled { opacity: 0.4; cursor: default; }

.btn-danger { color: var(--danger); border-color: var(--danger); }
.btn-danger:hover:not(:disabled) { background: var(--stamp-soft); }

/* --------------------------------------------------------------- shared */

.label {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.hairline { border: 1px solid var(--border); }

/* ------------------------------------------------------------ logo slot
 * A fixed box that holds an event's logo, at five sizes across the two
 * pages. Empty it is a dashed placeholder; filled it gets out of the way.
 *
 * The image is positioned rather than sized with percentages, because a
 * percentage max-height against a centred grid or flex item resolves against
 * an auto-sized track and does not constrain anything — which is how a 256px
 * logo ends up hanging out of a 64px slot. Absolute insets give it a box with
 * definite bounds at every slot size, and `contain` letterboxes inside it:
 * an event's mark is whatever aspect ratio it is, and cropping someone's
 * brand is worse than leaving air around it.
 */
.logo-slot { position: relative; }

.logo-slot img {
  position: absolute;
  inset: 4px;
  width: calc(100% - 8px);
  height: calc(100% - 8px);
  object-fit: contain;
}

.logo-slot[data-empty="false"] {
  border-color: transparent;
  background: none;
}

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