/* Event check-in console.
   Built for a phone held at a door: large touch targets, high contrast for a
   dim venue, and a dark ground so the screen is not a torch at 2am. */

:root {
  color-scheme: dark;

  /* PolyAI deep dark theme */
  --bg:        #000000;
  --surface:   #0B132B; /* midnight navy */
  --surface-2: #1C2541; /* deep violet-blue */
  --line:      #2A3A5C;
  --text:      #FFFFFF;
  --muted:     #8C9BAB;

  --accent:    #00FFFF; /* electric cyan */
  --ok:        #0F8A53; /* muted dark green */
  --danger:    #B8331F; /* muted dark red */
  /* The same two as text. --ok and --danger are fills, sized for white text on
     top of them; used as ink on these near-black surfaces they come out at
     about 3:1 and 2:1. These are the readable versions, ~12:1 and ~9:1. */
  --ok-ink:     #7EE2A8;
  --danger-ink: #FF9B8A;
  --warn:      #B37D2A;

  --radius: 14px;
  --tap: 56px;                    /* minimum comfortable touch target */
}

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

/* The hidden attribute must beat any author display rule below - otherwise
   .btn { display: inline-flex } keeps hidden buttons on screen. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Keep content clear of the notch and the home indicator. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}

.view { min-height: 100dvh; }
.view--center { display: grid; place-items: center; padding: 24px; }
.view[hidden] { display: none; }

/* ── Cards ─────────────────────────────────────────────────────────── */

.card {
  background: rgba(11, 19, 43, 0.7); /* glassmorphism surface */
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}
.card--login { width: 100%; max-width: 380px; text-align: center; }
.label--left { text-align: left; margin-bottom: 4px; font-size: 0.85rem; }
.card--login .text-input--block { margin-bottom: 14px; }

.title { margin: 0 0 6px; font-size: 1.5rem; letter-spacing: -0.02em; }
.subtitle { margin: 0 0 22px; color: var(--muted); font-size: 0.95rem; }
.placeholder { margin: 0; color: var(--muted); text-align: center; }

/* ── Code entry ────────────────────────────────────────────────────── */

.code-input {
  width: 100%;
  height: 68px;
  margin-bottom: 16px;
  padding: 0 12px;
  background: var(--surface-2);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 2rem;
  letter-spacing: 0.5em;
  text-align: center;
  text-indent: 0.5em;             /* re-centre despite the letter-spacing */
  text-transform: lowercase;
}
.code-input:focus {
  outline: none;
  border-color: var(--accent);
}
.code-input.is-invalid { border-color: var(--danger); }

/* ── Buttons ───────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--tap);
  padding: 0 18px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
  background: var(--surface-2);
  cursor: pointer;
  transition: transform 0.15s ease, background 0.2s ease, box-shadow 0.2s ease;
}
.btn:active { transform: scale(0.97); }
.btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; transform: none; }

.btn--primary { background: var(--accent); color: #000000; box-shadow: 0 4px 14px rgba(0, 255, 255, 0.2); }
.btn--primary:hover { background: #33FFFF; box-shadow: 0 6px 20px rgba(0, 255, 255, 0.4); }
.btn--ghost { background: transparent; border-color: var(--line); color: var(--text); }
.btn--ghost:hover { background: rgba(255, 255, 255, 0.05); }
/* 44px, not 40: this is the size of the Deactivate/Edit/Remove buttons in a
   worker row and of every "Sign out", and they are tapped with a thumb. */
.btn--small { width: auto; min-height: 44px; font-size: 0.9rem; }

/* ── Feedback ──────────────────────────────────────────────────────── */

.error {
  margin: 0 0 14px;
  padding: 10px 12px;
  background: #fdecea;
  border: 1px solid #f0bfb8;
  border-radius: 10px;
  color: #93291d;
  font-size: 0.92rem;
}
.error[hidden] { display: none; }

/* ── Console chrome ────────────────────────────────────────────────── */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px 12px;
  /* A phone cannot fit the event name and every action on one line, and a
     no-wrap row put "Sign out" past the right-hand edge where it could not be
     reached. The actions drop to their own line instead. */
  flex-wrap: wrap;
  padding: 14px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
/* The name block takes the slack and gives it back: it may shrink to a third
   of the bar before the actions are pushed down. */
.topbar__who { flex: 1 1 11rem; min-width: 0; }
.topbar__event { font-weight: 600; overflow-wrap: anywhere; }
/* Read as a sentence, so it wraps like one. As a flex row each word-run was a
   separate item and "Signed in as" broke across three lines on a narrow phone. */
.topbar__meta { color: var(--muted); font-size: 0.85rem; }
.topbar__meta .badge { vertical-align: middle; }
.topbar__label { font-weight: 600; color: var(--text); }

.badge {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.badge--staff { background: #e5efff; border-color: #a8c7f5; color: #14509f; }
.badge--organizer { background: #f3e8ff; border-color: #d4a8f5; color: #6b21a8; }
.badge--volunteer { background: #e2f5eb; border-color: #a5d9c1; color: #0f5c39; }
.badge--rsvp-confirmed { background: rgba(34, 197, 94, 0.15); border-color: rgba(34, 197, 94, 0.4); color: #22c55e; }
.badge--rsvp-declined { background: rgba(239, 68, 68, 0.15); border-color: rgba(239, 68, 68, 0.4); color: #ef4444; }
.badge--rsvp-pending { background: rgba(234, 179, 8, 0.15); border-color: rgba(234, 179, 8, 0.4); color: #eab308; }

.stack { display: flex; flex-direction: column; gap: 14px; padding: 16px; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ── Mode tabs ─────────────────────────────────────────────────────── */

/* One equal column per tab, however many modes there are - a fixed column
   count leaves an empty slot, and the tabs stop looking centred. */
.modes { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 8px; padding: 12px 16px 0; }

.mode {
  min-height: 52px;
  border: 2px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--muted);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}
.mode.is-active { color: var(--text); }
.mode--entry.is-active { border-color: var(--accent); background: #e5efff; color: #14509f; }
.mode--meal.is-active { border-color: #7c4ddb;       background: #efe8fd; color: #52299b; }
.mode:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ── Scanner ───────────────────────────────────────────────────────── */

.card--scanner { padding: 16px; }

.scanner {
  position: relative;
  aspect-ratio: 4 / 3;
  margin-bottom: 14px;
  background: #000000;
  border-radius: 12px;
  overflow: hidden;
  display: grid;
  place-items: center;
}
.scanner__video { width: 100%; height: 100%; object-fit: cover; display: none; }
.scanner.is-live .scanner__video { display: block; }
.scanner.is-live .scanner__hint { display: none; }

/* A QR code is square, so the guide is too - an oblong tells the volunteer to
   aim for a shape the code will never fill. Sized off the height so it stays
   square whichever aspect ratio the camera box is using. */
.scanner__frame {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  height: 70%;
  aspect-ratio: 1;
  border: 3px solid rgba(0, 255, 255, 0.5);
  border-radius: 14px;
  display: none;
  transition: border-color 120ms linear, box-shadow 120ms linear;
  box-shadow: 0 0 12px rgba(0, 255, 255, 0.3);
}
.scanner.is-live .scanner__frame { display: block; animation: scan-pulse 2s infinite ease-in-out; }
@keyframes scan-pulse {
  0%, 100% { border-color: rgba(0, 255, 255, 0.5); box-shadow: 0 0 12px rgba(0, 255, 255, 0.3); }
  50% { border-color: rgba(0, 255, 255, 1); box-shadow: 0 0 24px rgba(0, 255, 255, 0.6); }
}

/* Three states, so moving between two badges side by side always shows a
   change. Green straight to green would look like nothing had happened.
   White: nothing read. Amber: a code is being held, still settling.
   Green: that code has been taken. */
.scanner.is-seeing .scanner__frame {
  border-color: rgba(179, 125, 42, 0.95);
  box-shadow: 0 0 0 3px rgba(179, 125, 42, 0.25);
  animation: none;
}
.scanner.is-locked .scanner__frame {
  border-color: rgba(15, 138, 83, 0.95);
  box-shadow: 0 0 0 3px rgba(15, 138, 83, 0.28);
  animation: none;
}

/* The torch is a toggle, so it has to look pressed. */
#scan-torch.is-on { background: var(--accent); border-color: var(--accent); color: #ffffff; }

.scanner__hint { margin: 0; padding: 0 24px; color: var(--muted); text-align: center; }

.row { display: flex; flex-wrap: wrap; gap: 10px; }
.status { margin: 12px 0 0; min-height: 1.4em; color: var(--muted); font-size: 0.9rem; text-align: center; }
.status--error { color: var(--danger); }

/* ── Result overlay ────────────────────────────────────────────────── */
/* Full-screen and unmissable: at a busy door the colour is read before the
   words are. */

.result {
  position: fixed;
  inset: 0;
  z-index: 58;                             /* above the sheet, the popups and a profile over the list */
  display: grid;
  padding: 24px;
  text-align: center;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  /* Same reasoning as .modal below. A meal scan with a dietary line is ~480px
     tall; on a phone in landscape, or a portrait one once the URL bar and the
     nav bar are counted, that is taller than the window. Centred with overflow
     visible it lost the top and the bottom at once - including the OK button -
     and no scroll could reach either. "safe" keeps it top-aligned instead of
     centring something it has to clip. */
  overflow-y: auto;
  place-items: safe center;
}
.result[hidden] { display: none; }
/* White on every result colour. Without this the overlay inherits the light
   theme's near-black text, which on saturated green or red is harder to read
   at arm's length than it needs to be. */
.result { color: #ffffff; }
.result--ok   { background: rgba(15, 138, 83, 0.9); }
.result--fail { background: rgba(184, 51, 31, 0.9); }

.result__inner { display: flex; flex-direction: column; align-items: center; gap: 10px; max-width: 460px; width: 100%; }
.result__icon { font-size: 4.5rem; line-height: 1; }
.result__name { margin: 0; font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
.result__detail { margin: 0; font-size: 1.1rem; opacity: 0.92; }

/* Dietary needs are the safety-critical line on this screen: whoever is
   holding the plate must read it without leaning in. Sized to compete with
   the name, not to sit under it politely. */
.result__diet {
  margin: 8px 0 0;
  padding: 14px 22px;
  background: rgba(0, 0, 0, 0.22);
  border: 3px solid #fff;
  border-radius: 12px;
  font-size: 1.9rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}
.result__diet[hidden] { display: none; }
.result .btn { margin-top: 14px; border-color: rgba(255,255,255,0.5); color: #fff; }

/* ── Manual lookup ─────────────────────────────────────────────────── */

.card__title { margin: 0 0 4px; font-size: 1.05rem; }
.card__note { margin: 0 0 14px; color: var(--muted); font-size: 0.88rem; }

.row--search { align-items: stretch; }

.text-input {
  flex: 1;
  min-width: 0;                  /* lets the input shrink inside the flex row */
  min-height: var(--tap);
  padding: 0 14px;
  background: var(--surface-2);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 1.05rem;
}
.text-input:focus { outline: none; border-color: var(--accent); }

/* ── Select dropdown (replaces the number input on the login card) ──── */

.select-input {
  appearance: auto;
  -webkit-appearance: auto;
  cursor: pointer;
  margin-bottom: 14px;
}
.select-input option {
  color: var(--text);
  background: var(--surface);
}

.btn--inline { width: auto; flex: 0 0 auto; }

.hits { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.hits:empty { margin: 0; }

.hit {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: var(--tap);
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  color: var(--text);
  text-align: left;
  font: inherit;
  overflow-wrap: break-word;
  word-break: break-word;
  cursor: pointer;
}
.hit:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.hit > span:first-child { min-width: 0; flex: 1 1 auto; overflow: hidden; }
.hit__name { font-weight: 600; overflow-wrap: anywhere; word-break: break-word; }
.hit__meta { color: var(--muted); font-size: 0.82rem; overflow-wrap: anywhere; word-break: break-word; }
.hit__code {
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  letter-spacing: 0.08em;
}
.hit__state { flex: 0 0 auto; font-size: 0.75rem; }
.hit__state--in { color: var(--ok); }
.hit__state--out { color: var(--muted); }

/* ── Staff controls ────────────────────────────────────────────────── */

.card--staff { border-color: #b9d2f7; }

.field + .field { margin-top: 4px; }
.label { display: block; margin-bottom: 2px; font-weight: 600; }
.rule { margin: 20px 0; border: 0; border-top: 1px solid var(--line); }

.text-input--code {
  width: 100%;
  margin-bottom: 10px;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  letter-spacing: 0.35em;
  text-transform: lowercase;
}

.row--undo { gap: 10px; }
.btn--danger { background: var(--danger); color: #fff; }

/* ── Organiser home ────────────────────────────────────────────────── */
/* Read at a desk rather than at arm's length in a dim room, so this side
   breathes more than the console: wider measure, quieter type. */

.stack--wide { max-width: 640px; margin: 0 auto; width: 100%; }

/* ── Statistics ── */
/* Series colours: first two slots of the reference categorical palette,
   validated together on this white surface (CVD ΔE 24.7, contrast >= 3:1).
   Marks wear them; text never does. */
#view-stats {
  --series-entries: #2a78d6;
  --series-meals: #eb6834;
  --series-registered-online: #a78bfa;
  --series-checked-in: #34d399;
  --meter-track: #dbe8f8;
}
.live {
  display: inline-flex; align-items: center; gap: 5px; margin-left: 6px;
  font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em;
}
.live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--ok); }

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; }
.tile { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tile__label { color: var(--muted); font-size: 0.8rem; }
.tile__value { font-size: 1.7rem; font-weight: 650; line-height: 1.15; color: var(--text); }
.tile__note { color: var(--muted); font-size: 0.78rem; overflow-wrap: anywhere; }

/* The unfilled track is a lighter step of the fill's own hue. */
.meter { height: 10px; margin-top: 16px; background: var(--meter-track); border-radius: 999px; overflow: hidden; }
.meter__fill { height: 100%; width: 0; background: var(--series-entries); border-radius: 999px; transition: width 300ms ease; }
.meter__fill.is-over { background: var(--warn); }
.meter__label { margin: 8px 0 0; color: var(--muted); font-size: 0.85rem; }

.card__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.select-input--compact { width: auto; margin-bottom: 0; padding-top: 6px; padding-bottom: 6px; font-size: 0.9rem; }

.view-toggle { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: rgba(11, 19, 43, 0.6); }
.view-toggle .btn { border-radius: 0; border: none; margin: 0; padding: 4px 12px; font-size: 0.8rem; background: transparent; color: var(--muted); cursor: pointer; transition: background 150ms ease, color 150ms ease; }
.view-toggle .btn:hover { color: var(--text); background: rgba(255, 255, 255, 0.05); }
.view-toggle .btn.is-active { background: var(--surface-2); color: var(--accent); font-weight: 600; }

.chart-legend { display: flex; gap: 14px; margin: 6px 0 4px; font-size: 0.82rem; color: var(--text); }
.chart-key { display: inline-flex; align-items: center; gap: 6px; }
.chart-key::before { content: ""; width: 10px; height: 10px; border-radius: 2px; }
.chart-key--entries::before { background: var(--series-entries); }
.chart-key--meals::before { background: var(--series-meals); }
.chart-key--registered-online::before { background: var(--series-registered-online); }
.chart-key--checked-in::before { background: var(--series-checked-in); }

.chart { position: relative; width: 100%; }
.chart svg { display: block; width: 100%; height: auto; overflow: visible; }
.chart .grid { stroke: var(--line); stroke-width: 1; }
.chart .tick { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.chart .bar--entries { fill: var(--series-entries); }
.chart .bar--meals { fill: var(--series-meals); }
.chart .bar--registered { fill: var(--series-registered-online); }
.chart .bar--checked_in { fill: var(--series-checked-in); }
.chart .bar--cumulative_registered { fill: var(--series-registered-online); }
.chart .bar--cumulative_checked_in { fill: var(--series-checked-in); }
.chart .band { fill: transparent; cursor: default; outline: none; }
.chart .band:hover, .chart .band:focus { fill: rgba(20, 28, 38, 0.05); }
.chart__empty { margin: 10px 0 0; color: var(--muted); font-size: 0.88rem; }

.chart-tip {
  position: absolute; z-index: 2; pointer-events: none; min-width: 120px;
  padding: 8px 10px; background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 6px 18px rgba(20, 28, 38, 0.14); font-size: 0.82rem; color: var(--text);
}
.chart-tip__title { margin: 0 0 4px; color: var(--muted); }
.chart-tip__row { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.chart-tip__row strong { font-variant-numeric: tabular-nums; }
/* Line keys, not boxes, in the tooltip. */
.chart-tip__key { width: 12px; height: 2px; border-radius: 1px; }

.table-scroll { overflow-x: auto; margin-top: 12px; }
.data-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.data-table th, .data-table td { padding: 6px 8px; text-align: right; border-bottom: 1px solid var(--line); }
.data-table th:first-child, .data-table td:first-child { text-align: left; }
.data-table td { font-variant-numeric: tabular-nums; }
.data-table th { color: var(--muted); font-weight: 600; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

.filters { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.log-count { margin: 10px 0 6px; color: var(--muted); font-size: 0.85rem; }
.log { list-style: none; margin: 0; padding: 0; max-height: 420px; overflow-y: auto; }
.log__row {
  display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; align-items: baseline;
  padding: 8px 2px; border-bottom: 1px solid var(--line); font-size: 0.88rem;
}
.log__row.is-new { animation: log-new 1.6s ease-out; }
@keyframes log-new { from { background: #fff4d6; } to { background: transparent; } }
.log__time { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.log__who { min-width: 0; overflow-wrap: anywhere; }
.log__type { display: inline-flex; align-items: center; gap: 5px; margin-right: 6px; color: var(--muted); }
.log__type::before { content: ""; width: 8px; height: 8px; border-radius: 50%; }
.log__type--entry::before { background: var(--series-entries); }
.log__type--meal::before { background: var(--series-meals); }
.log__by { grid-column: 2; color: var(--muted); font-size: 0.8rem; }

@media (max-width: 420px) {
  .tile__value { font-size: 1.35rem; }
  .card__head { flex-wrap: wrap; }
}

.linkish {
  display: block;
  width: 100%;
  min-height: 44px;
  margin-top: 14px;
  padding: 10px 8px;
  background: none;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: 0.9rem;
  text-decoration: underline;
  cursor: pointer;
}
.linkish:hover { color: var(--text); }
.linkish:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.fields { display: flex; flex-direction: column; gap: 6px; }
.fields .label { margin-top: 10px; }
.fields .btn { margin-top: 18px; }
/* Tighter spacing when the fields live inside a modal next to a close button. */
.fields--modal { gap: 4px; }
.fields--modal .label { margin-top: 8px; }
.fields--modal .btn { margin-top: 14px; }

/* Edit modal card — wider than the default so description textarea has room. */
.modal__card--edit { max-width: 480px; display: flex; flex-direction: column; }
.modal__card--edit .row { margin-top: 8px; }
.modal__card--edit .fields { margin-top: 16px; }
.text-input--block { width: 100%; padding: 12px 14px; font-size: 1rem; }
textarea.text-input { min-height: 76px; line-height: 1.5; resize: vertical; }

/* Event list */
.events { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.stat--checked strong { color: var(--ok); }

.event {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 12px;
}
.event__name { font-weight: 600; }
.event__meta { color: var(--muted); font-size: 0.84rem; }
.event__count {
  flex: 0 0 auto;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.event__count strong { display: block; font-size: 1.35rem; line-height: 1.1; }
.event__count span { color: var(--muted); font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.06em; }

/* Codes shown once after creation - treated like an API key */
.card--codes { border-color: #e0b871; background: #fdf6e8; }
.codes { display: flex; flex-direction: column; gap: 10px; margin: 14px 0 18px; }

.codebox {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 12px;
}
.codebox__label { flex: 0 0 74px; color: var(--muted); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em; }
.codebox__value {
  flex: 1;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: 0.24em;
}

/* ── Event overview ────────────────────────────────────────────────── */
/* Where staff land: the numbers they actually want at a glance, then the
   controls, then the way through to scanning. */

/* Shrinkable, or the row cannot wrap. At 0 0 auto this box stayed at its
   max-content width whatever the phone was, so at 390px "Sign out" sat 8px
   past the right-hand edge and the whole page scrolled sideways to reach it.
   min-width: 0 is what actually lets a flex item narrow past its content. */
.topbar__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  flex: 0 1 auto;
  min-width: 0;
  justify-content: flex-end;
}
.topbar__signout { flex: 0 0 auto; }

/* On a phone the bar is two rows, and which button goes where matters: Sign
   out is the one you reach for by position rather than by reading, so it is
   pinned to the far corner opposite the event name. The rest - Overview,
   Statistics, How to Use - are read before they are pressed, so they take the
   row underneath together. Left to itself the wrap put Sign out alone on the
   second row, which made it look like it belonged to neither group. */
@media (max-width: 640px) {
  .topbar__who { order: 1; }
  .topbar__signout { order: 2; }
  .topbar__actions { order: 3; flex: 1 0 100%; justify-content: flex-start; }
  /* Nothing to sit under when every action is hidden, as it is for a
     volunteer: no empty row, and no gap left behind by one. */
  .topbar__actions:empty { display: none; }
}

/* Three across where they fit. "Discord Verified" cannot be squeezed into a
   third of a 320px phone, so below that the row falls to two and wraps rather
   than pushing the page sideways. */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 12px; }

.stat {
  display: block;
  width: 100%;
  font: inherit;
  cursor: pointer;
  padding: 16px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  text-align: center;
}
.stat strong {
  display: block;
  font-size: 2.4rem;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}
.stat:hover { border-color: var(--accent); }
.stat:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.stat span {
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

/* Subtle animation when a value in statistics changes live */
@keyframes stat-value-update {
  0% {
    color: var(--accent);
    text-shadow: 0 0 10px rgba(0, 255, 255, 0.6);
    transform: scale(1.08);
  }
  50% {
    color: var(--accent);
    text-shadow: 0 0 6px rgba(0, 255, 255, 0.3);
    transform: scale(1.04);
  }
  100% {
    color: inherit;
    text-shadow: none;
    transform: scale(1);
  }
}

.stat-updated {
  display: inline-block;
  transform-origin: center;
  animation: stat-value-update 0.8s ease-out;
}

.ev-meta { margin-top: 16px; margin-bottom: 0; }
.ev-description { margin: 10px 0 0; color: var(--text); }
.ev-description[hidden] { display: none; }
/* ── Workers panel ──────────────────────────────────────────────────── */

.card--workers { margin-top: 16px; }
.card__subtitle { margin: 0 0 12px; font-size: 1rem; }

.workers-list { list-style: none; margin: 0 0 12px; padding: 0; }
.workers-empty { color: var(--muted); font-size: 0.9rem; padding: 8px 0; }
.workers-toggle-wrap { display: flex; justify-content: center; margin-top: 8px; margin-bottom: 8px; }
.workers-toggle { width: 100%; text-align: center; padding: 8px; font-weight: 500; }

/* A grid, not a flex row: the name column may shrink to nothing while the code
   and the action buttons keep their own tracks. As a flex row none of the three
   would give way, so the code and the buttons were painted over the name and
   over each other, and the buttons under them could not be tapped. */
.worker {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px 12px;
  padding: 10px 12px;
  background: var(--surface-2);
  border-radius: 10px;
  margin-bottom: 6px;
}
.worker:last-child { margin-bottom: 0; }
.worker__main { grid-column: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.worker__name { font-weight: 600; overflow-wrap: anywhere; }
.worker__role {
  font-size: 0.8rem;
  color: var(--muted);
  text-transform: capitalize;
}
.worker__code {
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 1.2rem;
  /* The trailing letter-space is padding the browser does not know about, so
     the box looked off-centre against its own border. */
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  background: var(--surface);
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid var(--line);
  white-space: nowrap;
}
/* Its own row, right-justified: the status, when it was last used and the
   actions are all secondary to the name and wrap among themselves. */
.worker__right {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 4px 8px;
}
.worker__active { color: var(--ok); font-size: 0.8rem; font-weight: 600; }
.worker__inactive { color: var(--danger); font-size: 0.8rem; font-weight: 600; }
.worker__used { color: var(--muted); font-size: 0.75rem; }

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

.btn--tall { min-height: 68px; font-size: 1.15rem; }

/* ── App shell: sidebar + content ──────────────────────────────────── */

/* Right-justified against the row, beside the name where there is room for
   it and on its own line when there is not. */
.worker__code-row { grid-column: 2; justify-self: end; display: flex; align-items: center; gap: 4px; }
.worker__meta { font-size: 0.8rem; color: var(--muted); }
.view--app { display: grid; grid-template-columns: 232px 1fr; min-height: 100dvh; }

.sidebar {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 22px 18px;
  background: var(--surface);
  border-right: 1px solid var(--line);
}
.sidebar__brand { font-weight: 700; font-size: 1rem; line-height: 1.25; letter-spacing: -0.01em; }
.sidebar__nav { display: flex; flex-direction: column; gap: 4px; }
.sidebar__foot { margin-top: auto; width: 100%; }

.navitem {
  padding: 11px 12px;
  border: 0;
  border-radius: 9px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.navitem:hover { background: var(--surface-2); color: var(--text); }
.navitem.is-active { background: #e5efff; color: #14509f; }
.navitem:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.pane { padding: 26px 28px 60px; max-width: 720px; }
.pane__title { margin: 0 0 20px; font-size: 1.6rem; letter-spacing: -0.02em; }

/* The organiser side is used at a desk, but must not break on a phone. */
@media (max-width: 700px) {
  /* Rows as well as columns. Stacked, the two children fall into two implicit
     rows which split min-height: 100dvh between them - so the bar was handed
     half the phone and centred 46px of nav inside 380px of nothing. */
  .view--app { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
  .sidebar {
    flex-direction: row;
    align-items: center;
    gap: 12px;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding: 12px 14px;
  }
  .sidebar__brand { display: none; }
  .sidebar__nav { flex-direction: row; flex: 1; }
  .sidebar__foot { margin-top: 0; width: auto; }
  .pane { padding: 18px 16px 60px; }
}

/* Events become clickable on the organiser home */
.event--button { width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer; }
.event--button:hover { border-color: var(--accent); }
.event--button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.hint { margin: 14px 0 0; color: var(--muted); font-size: 0.85rem; }

/* ── Code prompt ───────────────────────────────────────────────────── */

.modal {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: opacity 0.2s ease;
}
.modal[hidden] { display: none; }

.modal__card {
  width: 100%;
  max-width: 380px;
  padding: 24px;
  background: rgba(11, 19, 43, 0.85); /* glassmorphism surface */
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.5);
}
/* The attendee list can be long: give the popup room and scroll the rows
   inside it rather than the page behind. */
/* Sized to its container, not the window: 100vw counts the scrollbar and
   ignores the popup's own padding, so the card came out wider than the space
   it was centred in and ran off the right-hand edge. */
.modal__card--list {
  width: 100%;
  max-width: 640px;
  max-height: 80dvh;
  display: flex;
  flex-direction: column;
  padding: 18px;
}
.hits--scroll { overflow-y: auto; margin-top: 4px; }

@media (max-width: 480px) {
  .modal__card--list { padding: 14px; }
  .hit { gap: 8px; padding: 10px 12px; }
}

.modal__card .code-input { margin-top: 16px; }
.modal__card .row { margin-top: 4px; }

/* ── Undo ──────────────────────────────────────────────────────────── */

.result__actions { display: flex; gap: 10px; width: 100%; margin-top: 14px; }
.result__actions .btn { margin-top: 0; }
/* Neither green nor red: the scan no longer stands, but nothing failed. */
.result__actions { display: flex; gap: 10px; width: 100%; margin-top: 14px; }
.result__actions .btn { margin-top: 0; }

/* ── Modal ──────────────────────────────────────────────────────────── */

.modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  background: rgba(20, 28, 38, 0.45);
  padding: 24px;
  /* The edit-worker and create-event cards are taller than a phone in
     landscape; without this their buttons sit below the fold unreachably.
     "safe" so a card taller than the window aligns to the top instead of
     centring with its head clipped where no scroll can reach it. */
  overflow-y: auto;
  place-items: safe center;
}
.modal__card:not(.modal__card--list):not(.modal__card--guide):not(.audit-modal) { width: 100%; max-width: 420px; }
.modal .row { display: flex; gap: 8px; margin-top: 12px; }

/* ── Guide Modal ───────────────────────────────────────────────────── */
.modal__card--guide {
  width: 100%;
  max-width: 580px;
  max-height: 85dvh;
  display: flex;
  flex-direction: column;
}

.modes--guide-lang {
  padding: 8px 0 4px;
  grid-template-columns: 1fr 1fr;
}

.modes--guide-lang .mode {
  min-height: 36px;
  font-size: 0.88rem;
  font-weight: 600;
  border-radius: 8px;
}

.modes--guide {
  padding: 8px 0 14px;
  grid-template-columns: 1fr 1fr;
}

.guide-content {
  overflow-y: auto;
  padding-right: 4px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.guide-section__title {
  font-size: 1.05rem;
  font-weight: 700;
  margin-bottom: 10px;
  color: var(--accent);
}

.guide-step {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 10px;
}

.guide-step h4 {
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: 6px;
  color: var(--text);
}

.guide-step p, .guide-step ul {
  font-size: 0.88rem;
  color: var(--muted);
  line-height: 1.45;
  margin: 0;
}

.guide-step ul {
  padding-left: 18px;
  margin-top: 4px;
}

.guide-step li {
  margin-bottom: 4px;
}

.guide-step code {
  background: rgba(255, 255, 255, 0.1);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: ui-monospace, monospace;
  font-size: 0.85em;
  color: var(--text);
}
.modal .row .btn { flex: 1; }
/* A confirmation answers whatever asked for it, so it sits above everything,
   the scan result flash included. */
.modal--confirm { z-index: 60; }

/* ── Diet marks ── */
/* Beside a name: small enough to scan down a list, never wrapping apart. */
.diet { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px; white-space: nowrap; vertical-align: middle; }
.diet__emoji { font-size: 0.95rem; line-height: 1; }
/* H and K: letters rather than religious symbols, sized to sit with the emojis. */
.diet__badge {
  display: inline-grid;
  place-items: center;
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 50%;
  background: #3b4a5c;
  color: #ffffff;
  font-size: 0.66rem;
  font-weight: 800;
  line-height: 1;
}
.btn--icon { min-width: 44px; padding-left: 0; padding-right: 0; font-size: 1.05rem; }

.legend { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.legend li { display: flex; align-items: center; gap: 10px; }
.legend .diet { margin-left: 0; min-width: 1.6rem; justify-content: center; }
.legend__note { margin: 14px 0 0; color: var(--muted); font-size: 0.85rem; line-height: 1.45; }
.confirm__message { margin: 8px 0 0; color: var(--muted); line-height: 1.45; }
.result--undone { background: #4d5b6c; }
/* A badge scanned twice is not a refusal, so it is not red. */
.result--repeat { background: #9a6a12; }

.undo-last { margin-top: 12px; border-color: var(--warn); color: var(--warn); }

/* ── Attendee profile sheet ────────────────────────────────────────── */
/* Anchored to the bottom so the camera above it stays visible and keeps
   scanning. Capped in height; scrolls rather than pushing into the camera. */

.sheet {
  position: fixed;
  left: 50%;
  bottom: 0;
  z-index: 8;                              /* under the flash, over content */
  transform: translateX(-50%);
  width: 100%;
  max-width: 640px;
  max-height: 50dvh;
  overflow-y: auto;
  padding: 14px 18px calc(16px + env(safe-area-inset-bottom));
  background: rgba(11, 19, 43, 0.9);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: 18px 18px 0 0;
  box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.4);
  transition: transform 0.3s cubic-bezier(0.1, 0.9, 0.2, 1);
}
/* Leave room so the last search results can scroll clear of the sheet. */
body.has-sheet .view { padding-bottom: 52dvh; }
/* While a profile is up, a wider, shorter camera keeps the scanner's own
   buttons above the sheet. A QR code decodes just as well from the crop. */
body.has-sheet .scanner { aspect-ratio: 16 / 9; }

/* Opened from the attendee list, the profile is a dialog stacked over it, so
   the list stays put behind and you can pick the next person straight after. */
.sheet--dialog {
  top: 50%;
  bottom: auto;
  transform: translate(-50%, -50%);
  /* Above the attendee list (.modal, 50) so a name tapped in the list opens on
     top of it; below the scan result (58) and the confirmation (60). */
  z-index: 55;
  /* The same side margins as the popups (.modal padding), so a profile opened
     over the list lines up with it instead of running edge to edge. */
  width: calc(100% - 48px);
  max-height: 78dvh;
  border: 1px solid var(--line);
  border-radius: 18px;
  box-shadow: 0 24px 60px rgba(20, 28, 38, 0.3);
}

/* Phones: tighter, equal margins for every popup and the profile dialog.
   After both .modal rules, so it is not overridden by the later one. */
@media (max-width: 480px) {
  .modal { padding: 12px; }
  .sheet--dialog { width: calc(100% - 24px); }
}

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 54;                             /* just under the profile dialog */
  background: rgba(20, 28, 38, 0.45);
}

.sheet__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.sheet__name { margin: 0; font-size: 1.3rem; font-weight: 700; letter-spacing: -0.01em; }
.sheet__meta { margin: 2px 0 0; color: var(--muted); font-size: 0.85rem; display: flex; gap: 8px; flex-wrap: wrap; }
.sheet__code { font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; letter-spacing: 0.1em; }
.sheet__close {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}
.sheet__close:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* An allergy is the one line nobody may skim past. */
.fact--alert { background: #fdecea; box-shadow: inset 0 0 0 2px var(--danger); }
.fact--alert dt { color: #93291d; }
.fact--alert dd { color: #93291d; font-weight: 800; text-transform: uppercase; letter-spacing: 0.02em; }

/* Short facts share a row equally, whether there are two or three of them,
   and wrap on a narrow phone rather than squeezing; wide ones take a row. */
.facts { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 0; }
.fact { flex: 1 1 5.5rem; padding: 8px 10px; background: var(--surface-2); border-radius: 10px; min-width: 0; }
.fact--wide { flex-basis: 100%; }
.fact dt { color: var(--muted); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; }
.fact dd { margin: 2px 0 0; font-weight: 600; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.fact dd.is-spent { color: var(--danger); }
.fact dd.is-done { color: var(--ok); }


.sheet__more { margin-top: 10px; }
.sheet__more summary { cursor: pointer; color: var(--muted); font-size: 0.88rem; min-height: 44px; padding: 11px 0; }

/* One equal column per action, for the same reason as the mode tabs - but only
   as many as fit. With all four showing ("Check in", "Serve meal", "Remove
   meal", "Uncheck in") four columns on a phone left each label breaking across
   two lines, so they fall into a second row instead. */
.sheet__actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.25rem, 1fr));
  gap: 8px;
  margin-top: 12px;
}
/* Side by side on a phone: tighter padding, and a label may wrap rather than
   shrink below a readable size. */
.action {
  min-height: 52px;
  padding: 6px 8px;
  font-size: 0.95rem;
  line-height: 1.2;
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--text);
}
/* The action matching the mode tab is the likely one, so it stands out. */
.action.is-current { background: var(--accent); border-color: var(--accent); color: #ffffff; }
.action[disabled] { opacity: 0.55; }
.sheet__undo { width: 100%; margin-top: 8px; }
.btn--undo { border-color: var(--warn); color: var(--warn); }

/* The flash clears itself on success; a tap also dismisses it early. */
.result { cursor: pointer; }
/* ── Email management card ────────────────────────────────------------- */

.card--email { margin-top: 16px; }

/* Toggle switches */
.field--toggle {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
.field--toggle .label { flex: 1 1 auto; margin-bottom: 0; }
.field--toggle .field__hint { width: 100%; font-size: 12px; color: #888; margin: 0; }

.toggle {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 24px;
  flex: 0 0 44px;
  cursor: pointer;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle__slider {
  position: absolute;
  inset: 0;
  background: #999;
  border-radius: 24px;
  transition: background 0.2s;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.2);
  border: 1px solid #bbb;
}
.toggle__slider::before {
  content: "";
  position: absolute;
  height: 18px;
  width: 18px;
  left: 3px;
  bottom: 2px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.2s;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.toggle input:checked + .toggle__slider { background: var(--green); border-color: var(--green); }
.toggle input:checked + .toggle__slider::before { transform: translateX(20px); }

/* Whitelist */
.whitelist-list {
  list-style: none;
  padding: 0;
  margin: 8px 0 0;
}
.whitelist-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--line);
}
.whitelist-list li:nth-child(odd) { background: rgba(28, 37, 65, 0.6); }
.whitelist-list li .email { font-size: 13px; word-break: break-all; flex: 1; color: var(--text); }
.whitelist-list li .btn--remove {
  font-size: 18px; margin-left: auto; color: var(--danger); border: none; background: none;
  cursor: pointer; line-height: 1; flex: 0 0 auto;
  /* A bare × at 14px is a 10px target. Give it a thumb's worth of room. */
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; min-width: 44px; height: 44px; padding: 0;
}

.row--whitelist { gap: 8px; }
.row--whitelist .text-input { flex: 1; }
.row--discord { display: flex; gap: 8px; }
.row--discord .text-input { flex: 1; }

/* Dangerous combination glow */
@keyframes pulse-glow {
  0%, 100% { box-shadow: 0 0 8px 2px rgba(220, 38, 38, 0.5); }
  50%      { box-shadow: 0 0 16px 4px rgba(220, 38, 38, 0.8); }
}
.card--email.card--ready-to-send {
  animation: pulse-glow 1.5s ease-in-out infinite;
  border-color: var(--red, #dc2626);
}

/* ── Template editor ──────────────────────────────────------------- */

.template-editor { margin-top: 4px; }
.template-editor summary { cursor: pointer; min-height: 44px; padding: 11px 0; }
.template-editor__textarea {
  font-family: 'Consolas', 'Courier New', monospace;
  font-size: 12px;
  line-height: 1.5;
  resize: vertical;
  min-height: 200px;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.card__subsubtitle {
  font-size: 13px;
  font-weight: 600;
  margin: 12px 0 6px;
  color: var(--muted);
}
.template-preview {
  background: #fff;
  border: 1px solid #e0e0e0;
  border-radius: 6px;
  padding: 16px;
  min-height: 100px;
  overflow-x: auto;
  color: #111827;
}
.template-preview p { margin: 0 0 8px; }
.template-preview :last-child { margin-bottom: 0; }
/* ── Phones ─────────────────────────────────────────────────────────── */
/* Last in the file so these win over the desktop rules they narrow. */

/* iOS zooms the whole page in whenever a focused field is under 16px and never
   zooms back out, which leaves the rest of the page scrolled off to the side.
   Only the two compact fields are affected; everything else is already 16px. */
@media (pointer: coarse) {
  .select-input--compact { font-size: 1rem; }
  .template-editor__textarea { font-size: 16px; }
}

@media (max-width: 480px) {
  /* The name, the count and "Edit" cannot share a line at this width without
     the name breaking after every word. */
  .event { flex-wrap: wrap; gap: 10px; }
  .event > :first-child { flex: 1 1 100%; }

  /* One column: at this width the name track is too narrow to hold a name, and
     the code and the buttons each take a line of their own, right-justified. */
  .worker { grid-template-columns: minmax(0, 1fr); }
  .worker__code-row { grid-column: 1; }
  .worker__code { font-size: 1.05rem; letter-spacing: 0.16em; text-indent: 0.16em; }

  /* The label column is a fixed 74px that the code then has to fit beside. */
  .codebox { flex-wrap: wrap; }
  .codebox__label { flex-basis: 100%; }

  /* Side by side these two run to three lines each ("CHECKED" / "IN"). */
  .stat { padding: 12px; }
  .stat strong { font-size: 2rem; }

  /* Three dropdowns squeezed onto one line truncated to "All s", "All d",
     "Any". Let them keep a readable width and take a second line instead. */
  .filters .select-input--compact { flex: 1 1 9rem; min-width: 9rem; }

  .pane { padding: 16px 16px 48px; }
  .stack { padding: 12px; }
}

@media (max-width: 360px) {
  .stat strong { font-size: 1.7rem; }
  .stat span { letter-spacing: 0.04em; }
  /* Two 44px-tall buttons plus the icon no longer fit beside the event name;
     let them share the second line evenly rather than clip. */
  .topbar__actions { width: 100%; justify-content: flex-start; }
}

/* ── Discord role sync ───────────────────────────────────────────────── */

/* Border left to the themed default, like .card--email. A hardcoded colour
   here is what has left .card--staff and .card--codes still wearing the old
   light theme's blue and cream on a black page. */
.card--discord { margin-top: 16px; }

.discord-report { margin-top: 12px; }
.discord-report[hidden] { display: none; }
.discord-report__summary { margin: 0 0 10px; font-weight: 600; }
.discord-report details { margin-bottom: 6px; }
.discord-report summary { cursor: pointer; min-height: 44px; padding: 11px 0; font-size: 0.9rem; }
.discord-report__list {
  list-style: none;
  margin: 0 0 8px;
  padding: 0 0 0 10px;
  /* A few hundred unmatched names must not push the buttons off the page. */
  max-height: 260px;
  overflow-y: auto;
  border-left: 2px solid var(--line);
}
.discord-report__list li {
  padding: 4px 0;
  color: var(--muted);
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}


/* ── The Discord roster and its filters ──────────────────────────────── */

.discord-filters { display: flex; flex-direction: column; gap: 10px; margin: 10px 0 14px; }
.discord-filters__search { width: 100%; }
.discord-filters__group { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.discord-filters__name {
  flex: 0 0 100%;
  color: var(--muted);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* A chip is a toggle, so it has to look pressed when it is on. */
.discord-chip {
  min-height: 36px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}
.discord-chip:hover { border-color: var(--accent); }
.discord-chip.is-on { background: var(--accent); border-color: var(--accent); color: #000; font-weight: 600; }
.discord-chip:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.discord-roster { list-style: none; margin: 8px 0 0; padding: 0; max-height: 420px; overflow-y: auto; }
.discord-roster__row { border-bottom: 1px solid var(--line); }
.discord-roster__row:last-child { border-bottom: 0; }
.discord-roster__meta { color: var(--muted); font-size: 0.85rem; }

/* The handle and the address. Monospace-ish and breakable, because a long
   address must wrap inside the row rather than push the card sideways. */
.discord-roster__who {
  font-size: 0.82rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.discord-roster__handle { color: var(--accent); }
.discord-roster__email { color: var(--muted); }
.discord-roster__what { color: var(--muted); font-size: 0.85rem; }

/* The diff. Green is a role granted, red one taken away - the same way every
   other review surface says the same thing. Never colour alone: the + and the
   − carry it for anyone who cannot tell these two apart. */

.discord-diff { display: inline-flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.discord-diff__add,
.discord-diff__remove {
  font-size: 0.8rem;
  font-weight: 600;
  padding: 1px 7px;
  border-radius: 999px;
  white-space: nowrap;
}
.discord-diff__add    { color: var(--ok-ink);     background: rgba(126, 226, 168, 0.12); }
.discord-diff__remove { color: var(--danger-ink); background: rgba(255, 155, 138, 0.12); }
.discord-diff__none   { color: var(--muted); font-size: 0.85rem; font-style: italic; }

/* The two panels above the list. Both start shut: what they hold is the
   controls, and their summaries carry the state, so the card opens as a short
   thing you can read rather than a wall of chips and checkboxes. */

/* The Filters button. The count is on the button, so the list you are looking
   at never silently turns out to be a filtered one. */
.discord-filter-btn.is-on {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.discord-plan {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 8px 0 4px;
}
.discord-plan__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}
.discord-plan__totals {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: 0.9rem;
}
.discord-plan__totals[hidden] { display: none; }
.discord-plan__people { color: var(--muted); font-weight: 400; }
.discord-plan__halves { display: flex; flex-wrap: wrap; gap: 14px; }
.discord-plan__half,
.discord-plan__all {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  cursor: pointer;
  font-size: 0.9rem;
}
.discord-plan__half input,
.discord-plan__all input { width: 20px; height: 20px; flex: 0 0 auto; }
.discord-plan__roles { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.discord-chip--role { text-transform: capitalize; }

/* The row the button sits in, with the totals beside it. */
.row--plan { flex-wrap: wrap; align-items: center; gap: 8px; }

/* The whole row is the label, so the name is part of the tap target rather
   than a 20px box beside it. */
.discord-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-height: 44px;
  padding: 4px 0;
  cursor: pointer;
}
.discord-row__pick { width: 20px; height: 20px; margin-top: 11px; flex: 0 0 auto; }
.discord-row--failed { color: var(--danger); }

/* ── Audit Log Redesign ────────────────────────────────────────────────── */

.audit-modal {
  max-width: 900px;
  width: 92vw;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
}

.audit-toolbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.audit-toolbar__group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.audit-toolbar__filters {
  flex: 1 1 auto;
  justify-content: flex-end;
}

.audit-container {
  flex: 1 1 auto;
  max-height: 55vh;
  min-height: 220px;
  overflow-y: auto;
  overflow-x: auto;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px;
  margin-top: 10px;
}

.audit-container--nowrap .audit-list,
.audit-container--nowrap .audit-pre {
  white-space: nowrap;
}

.audit-container--wrap .audit-list,
.audit-container--wrap .audit-pre {
  white-space: pre-wrap;
  word-break: break-all;
}

.audit-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.audit-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: 0.88rem;
  transition: border-color 0.15s ease;
}

.audit-item:hover {
  border-color: rgba(255, 255, 255, 0.25);
}

.audit-item__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
}

.audit-time {
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 0.8rem;
  color: var(--muted);
}

.audit-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid transparent;
}

.audit-badge--db { background: rgba(0, 255, 255, 0.15); border-color: rgba(0, 255, 255, 0.4); color: var(--accent); }
.audit-badge--login { background: rgba(15, 138, 83, 0.2); border-color: rgba(15, 138, 83, 0.5); color: var(--ok-ink); }
.audit-badge--error { background: rgba(184, 51, 31, 0.2); border-color: rgba(184, 51, 31, 0.5); color: var(--danger-ink); }
.audit-badge--warn { background: rgba(179, 125, 42, 0.2); border-color: rgba(179, 125, 42, 0.5); color: #f59e0b; }
.audit-badge--email { background: rgba(139, 92, 246, 0.2); border-color: rgba(139, 92, 246, 0.5); color: #a78bfa; }
.audit-badge--default { background: var(--surface-2); border-color: var(--line); color: var(--muted); }

.audit-user {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 0.78rem;
  font-weight: 600;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
}

.audit-user--organizer { color: #c084fc; border-color: rgba(192, 132, 252, 0.4); }
.audit-user--staff { color: #60a5fa; border-color: rgba(96, 165, 250, 0.4); }
.audit-user--volunteer { color: #34d399; border-color: rgba(52, 211, 153, 0.4); }
.audit-user--system { color: #9ca3af; }

.audit-item__details {
  font-size: 0.85rem;
  color: var(--text);
  overflow-x: auto;
}

.audit-code {
  display: block;
  padding: 6px 10px;
  margin-top: 4px;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 0.8rem;
  color: #e2e8f0;
}

.audit-pre {
  margin: 0;
  padding: 4px;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 0.82rem;
  color: #e0e0e0;
  line-height: 1.5;
}

.log-line {
  display: block;
  padding: 1px 0;
}

.log-ts { color: var(--muted); }
.log-action { font-weight: 700; }
.log-action--db { color: var(--accent); }
.log-action--login { color: var(--ok-ink); }
.log-action--error { color: var(--danger-ink); }
.log-action--warn { color: #f59e0b; }
.log-action--email { color: #a78bfa; }
.log-action--default { color: #9ca3af; }
.log-user { color: #93c5fd; }
.log-details { color: #f3f4f6; }

.audit-modal__footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  margin-top: 12px;
  gap: 10px;
}
