/* The two families, served from here.
 *
 * They used to come from fonts.googleapis.com as a render-blocking stylesheet.
 * Measured: with a warm cache and a working service worker, if that request
 * never answered the app never painted at all. A third-party CSS file in the
 * head is a single point of failure for an app whose whole promise is that it
 * works on a bad connection.
 *
 * Latin and latin-ext only. latin-ext carries the Yoruba diacritics the site
 * sets; cyrillic, greek and vietnamese were being shipped for nothing.
 */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/source-assets/fonts/inter-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/source-assets/fonts/inter-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/source-assets/fonts/inter-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/source-assets/fonts/inter-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/source-assets/fonts/inter-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/source-assets/fonts/inter-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/source-assets/fonts/jost-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/source-assets/fonts/jost-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* The Ekiti Experience — delegate app
 * =============================================================================
 * Built against app-bar.md, measured off dice.fm's mobile product surface on
 * 26 Sep 2026. Seven mechanisms, every number read from a live DOM.
 *
 * The previous version of this file was a light app: white cards on grey, soft
 * shadows, four radius tiers, eight type sizes. It was competent and it was
 * generic — it failed six of the seven mechanisms outright. What changed here
 * is the bones, not the decoration.
 *
 *   1  pure black ground; three surfaces; zero shadow on content
 *   2  one accent, one job, never twice in a row
 *   3  two radius tiers — 8px content, fully round controls
 *   4  type tops out at 25px, four sizes
 *   5  a list row is a 96px square and a text column, not a card
 *   6  controls are circles pinned to both gutters; nothing centred at the top
 *   7  hierarchy inside one line, carried by colour
 *
 * The grammar is rebuilt; the values are the campaign's. Gold rather than
 * DICE's acid yellow, the spiral rather than their wordmark, our photographs.
 * Their typeface is licensed to them.
 * ========================================================================== */

:root {
  /* Mechanism 1. Measured on the bar as #000 / #111 / #222, and that is the
     entire depth system: no elevation in this interface, only steps. */
  --ground:  #000000;
  --surface: #111111;
  --raised:  #222222;
  --line:    #262626;

  /* Every ink value carries text, so every one clears AA on #000 and on #222,
     the two grounds text ever sits on. */
  --ink:       #FFFFFF;   /* 21.0:1 on black */
  --ink-muted: #CFCFD6;   /* 13.5:1 on black, 10.3:1 on #222 — set by daylight */
  --ink-low:   #7E7E83;   /*  5.2:1 on black,  4.7:1 on #111 — recessive TEXT */
  --bad:       #FF8A73;   /*  9.0:1 on black — the only colour besides the accent */
  --mark:      #626268;   /*  3.5:1 on black — marks only, never letterforms */

  /* Mechanism 2. The campaign's gold, 7.46:1 on black, and it marks time.
     Never a fill, never a surface, never twice in one row.
     The poster's blue and magenta cannot live here: measured at 1.97:1 and
     2.96:1 on black, they are not colours on this ground, they are stains. */
  --accent: #E3C273;

  /* Mechanism 3. Content 8px, controls round. The 12-20px middle ground is
     precisely what makes an interface read as a generic card app. */
  --r-content: 8px;
  --r-round: 999px;

  /* Mechanism 4. Four sizes, ceiling 25px. */
  --t-head:  1.5625rem;  /* 25 */
  --t-title: 1rem;       /* 16 */
  --t-meta:  0.875rem;   /* 14 */
  --t-label: 0.75rem;    /* 12 */

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --display: "Jost", var(--sans);

  /* Mechanisms 5 and 6, measured off the reference. */
  --gutter: 16px;
  --thumb: 96px;
  --thumb-gap: 12px;
  --control: 40px;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;

  --z-tabbar: 20; --z-sheet: 40; --z-toast: 50;
  --dur-1: 120ms; --dur-2: 180ms; --dur-3: 260ms;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; background: var(--ground); }
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-title);
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}
h1, h2, h3, p, dl, dd, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
[hidden] { display: none !important; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

/* SHELL --------------------------------------------------------------------- */
.shell {
  min-height: 100svh;
  max-width: 34rem;
  margin-inline: auto;
  padding-bottom: calc(76px + env(safe-area-inset-bottom));
}
.screen { padding-inline: var(--gutter); }
.stack { display: flex; flex-direction: column; gap: var(--s5); }
.stack--tight { gap: var(--s3); }

/* Mechanism 6 — identity hard left, dates hard right, both on the gutter. */
.masthead {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4);
  padding: calc(var(--s4) + env(safe-area-inset-top)) var(--gutter) var(--s2);
}
.masthead__mark { display: flex; align-items: center; gap: var(--s2); }
.masthead__name {
  font-family: var(--display);
  font-size: var(--t-meta); font-weight: 700;
  letter-spacing: 0.02em; text-transform: uppercase;
}
.masthead__when { font-size: var(--t-label); font-weight: 500; color: var(--accent); font-variant-numeric: tabular-nums; }
.seal { width: 26px; height: 26px; flex: none; filter: brightness(0) invert(1); }

/* Mechanisms 4 and 7 — one heading size, hierarchy by colour on one line. */
.screen__head { padding-block: var(--s4) var(--s3); }
.screen__title { font-size: var(--t-head); font-weight: 500; letter-spacing: -0.02em; line-height: 1.12; }
.screen__title .quiet, .section__title .quiet { color: var(--ink-low); }
.screen__note { font-size: var(--t-meta); color: var(--ink-muted); }
.section__title { font-size: var(--t-meta); font-weight: 500; padding-bottom: var(--s3); }

/* MECHANISM 5 — THE ROW ------------------------------------------------------
   A square and a text column. No background, no border, no radius of its own.
   Rows are separated by a hairline and nothing else.
   -------------------------------------------------------------------------- */
.group { display: flex; flex-direction: column; }
.row {
  display: flex; align-items: center; gap: var(--s4);
  width: 100%; min-height: 52px;
  padding: var(--s3) 0;
  text-align: left;
  border-top: 1px solid var(--line);
}
.group > .row:first-child, .group > .session:first-child, .group > .venue:first-child { border-top: 0; }
.row__label { flex: 0 1 auto; min-width: 0; font-size: var(--t-meta); color: var(--ink-muted); }
.row__value { flex: 1 1 auto; min-width: 0; font-size: var(--t-meta); font-weight: 500; text-align: right; overflow-wrap: anywhere; }
.row__sub { display: block; font-weight: 400; font-size: var(--t-label); color: var(--ink-muted); }

/* A native select, dressed as a row value. The OS picker is the right control
   on a cheap Android: it is reachable with a screen reader, it needs no
   JavaScript to open, and it cannot render off-screen the way a custom sheet
   can. Only the chrome is removed. */
.row__select {
  appearance: none; -webkit-appearance: none;
  background: transparent; border: 0;
  padding: 0 calc(var(--s3) + 10px) 0 0;
  font: inherit; font-weight: 500; color: var(--ink);
  text-align: right; text-align-last: right;
  min-height: 24px;
}
.row__select:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }
.row--wants .row__select { color: var(--accent); }
/* The caret, drawn rather than shipped, since it is two lines. */
.row:has(> .row__value > .row__select) { position: relative; }
.row__value:has(> .row__select)::after {
  content: ""; position: absolute; right: 0; top: 50%;
  width: 7px; height: 7px; margin-top: -6px;
  border-right: 1.5px solid var(--mark); border-bottom: 1.5px solid var(--mark);
  transform: rotate(45deg); pointer-events: none;
}
.row__value:has(> .row__select) { position: relative; padding-right: var(--s3); }
button.row:active, a.row:active, button.session:active, .passcard:active { opacity: .6; }
.row__go, .row__copy { flex: none; margin-left: auto; width: 18px; height: 18px; color: var(--mark); }

/* MECHANISM 5 — A ROW IS A 96px SQUARE AND A TEXT COLUMN, NOT A CARD --------
   The square sits flush to the 16px gutter, the text column starts at 124px on
   every row without exception, and the row itself has no background, no border
   and no radius of its own. Rows are separated by a hairline and nothing else.

   Where we hold an honest photograph of the session, the square is that
   photograph. Where we do not, it is a plain numbered tile of exactly the same
   size — so the text column never shifts, and the app never pretends to own a
   picture of a debate it has no picture of. */
.session {
  display: flex; gap: var(--thumb-gap);
  width: 100%; padding: var(--s3) 0;
  text-align: left; align-items: center;
  border-top: 1px solid var(--line);
}
.session__art {
  flex: none; width: var(--thumb); height: var(--thumb);
  border-radius: var(--r-content); overflow: hidden;
  background: var(--surface);
}
.session__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The tile for a session we hold no photograph of: its place in the day, which
   is the one temporal fact that exists for every session on this programme. */
.session__tile {
  display: grid; place-items: center;
  width: 100%; height: 100%;
  font-size: var(--t-head); font-weight: 500;
  font-variant-numeric: tabular-nums; letter-spacing: -0.03em;
  color: var(--ink-low);
  border: 1px solid var(--line);
  border-radius: var(--r-content);
}
.session__body { flex: 1 1 auto; min-width: 0; display: grid; gap: 2px; }
.session__time {
  font-size: var(--t-meta); font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--accent);          /* mechanism 2, doing its one job */
}
.session__time--none { color: var(--ink-low); font-weight: 400; letter-spacing: 0.04em; }
.session__name { display: block; font-size: var(--t-title); font-weight: 400; }
.session__where { display: block; font-size: var(--t-meta); color: var(--ink-muted); }
.session--past { opacity: .42; }
.session--now .session__name { font-weight: 500; }
.session__go { flex: none; width: 18px; height: 18px; color: var(--mark); }

/* MECHANISM 3 — CONTROLS ARE ROUND, CONTENT IS 8px --------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 44px; padding: 0 var(--s5);
  border-radius: var(--r-round);
  font-size: var(--t-meta); font-weight: 500;
  background: var(--ink); color: var(--ground);
  transition: opacity var(--dur-1) var(--ease);
}
.btn:active { opacity: .7; }
.btn:disabled { opacity: .35; pointer-events: none; }
.btn--quiet { background: var(--raised); color: var(--ink); }
.btn--plain { background: none; color: var(--ink-muted); padding-inline: var(--s3); }
.btn--block { width: 100%; }

.circle {
  display: grid; place-items: center; flex: none;
  width: var(--control); height: var(--control);
  border-radius: var(--r-round);
  background: var(--raised); color: var(--ink);
}
.circle svg { width: 18px; height: 18px; }

.pill {
  display: inline-flex; align-items: center; gap: var(--s2);
  padding: 4px 10px; border-radius: var(--r-round);
  background: var(--raised); color: var(--ink-muted);
  font-size: var(--t-label); font-weight: 500;
}
.pill--on { background: var(--ink); color: var(--ground); }

/* TABS ---------------------------------------------------------------------- */
.tabbar {
  position: fixed; z-index: var(--z-tabbar); inset: auto 0 0 0;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  max-width: 34rem; margin-inline: auto;
  padding: var(--s2) var(--s2) calc(var(--s2) + env(safe-area-inset-bottom));
  background: var(--ground);
  border-top: 1px solid var(--line);
}
.tab {
  display: grid; justify-items: center; gap: 4px;
  padding: var(--s2) 2px; min-height: 44px;
  font-size: var(--t-label); color: var(--ink-low);
  transition: color var(--dur-1) var(--ease);
}
.tab svg { width: 20px; height: 20px; stroke-width: 1.6; color: var(--mark); }
.tab[aria-current="page"] svg { color: inherit; }
.tab[aria-current="page"] { color: var(--ink); font-weight: 500; }

/* NOW ----------------------------------------------------------------------- */
.now { background: var(--surface); border-radius: var(--r-content); padding: var(--s5) var(--s4); }
.now__kicker { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-label); font-weight: 500; color: var(--ink-muted); }
.now__pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); animation: pulse 2.4s var(--ease) infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .62; } }
.now__what { margin-top: var(--s3); font-size: var(--t-head); font-weight: 500; letter-spacing: -0.02em; line-height: 1.12; }
.now__where { margin-top: var(--s2); font-size: var(--t-meta); color: var(--ink-muted); }
.now__time { color: var(--accent); font-weight: 500; font-variant-numeric: tabular-nums; }
.now__acts { display: flex; gap: var(--s2); margin-top: var(--s4); flex-wrap: wrap; }
.count { display: flex; align-items: baseline; gap: var(--s2); }
.count__n { font-size: var(--t-head); font-weight: 500; font-variant-numeric: tabular-nums; letter-spacing: -0.03em; }
.count__unit { font-size: var(--t-meta); color: var(--ink-muted); }

/* THE PASS ------------------------------------------------------------------ */
.passcard {
  display: flex; align-items: center; gap: var(--thumb-gap);
  width: 100%; padding: var(--s3) 0; text-align: left;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
/* The same 96px square the bar uses for every list row. */
/* A dark tile like every other square in the app. The white plate belongs on
   the sheet, where a camera actually has to read it. */
.passcard__qr {
  flex: none; width: var(--thumb); height: var(--thumb); padding: 8px;
  border-radius: var(--r-content);
  background: var(--surface); border: 1px solid var(--line);
}
.passcard__qr svg { width: 100%; height: 100%; }
.passcard__text { flex: 1 1 auto; min-width: 0; }
.passcard__name { display: block; font-size: var(--t-title); font-weight: 500; }
.passcard__meta { display: block; font-size: var(--t-meta); color: var(--ink-muted); margin-top: 2px; }
.passcard__go { flex: none; width: 18px; height: 18px; color: var(--mark); }

.sheet {
  position: fixed; z-index: var(--z-sheet); inset: 0;
  display: grid; grid-template-rows: auto 1fr auto;
  background: var(--ground); color: var(--ink);
  padding: calc(var(--s4) + env(safe-area-inset-top)) var(--gutter) calc(var(--s5) + env(safe-area-inset-bottom));
  animation: sheet-in var(--dur-3) var(--ease);
}
@keyframes sheet-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.sheet__top { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
.sheet__body { display: grid; place-content: center; justify-items: center; gap: var(--s4); }
.sheet__qr { width: min(74vw, 300px); aspect-ratio: 1; padding: var(--s4); border-radius: var(--r-content); background: var(--ink); }
.sheet__qr svg { width: 100%; height: 100%; }
.sheet__name { font-family: var(--display); font-size: var(--t-head); font-weight: 700; letter-spacing: -0.02em; text-align: center; }
.sheet__code { font-size: var(--t-meta); font-weight: 500; letter-spacing: 0.1em; font-variant-numeric: tabular-nums; color: var(--ink); }
.sheet__foot { display: grid; gap: var(--s4); }
.sheet__facts { display: flex; justify-content: space-between; gap: var(--s4); }
.sheet__fact b { display: block; font-size: var(--t-meta); font-weight: 500; }
.sheet__fact--when b { color: var(--accent); font-variant-numeric: tabular-nums; }
.sheet__fact span { font-size: var(--t-label); color: var(--ink-muted); }
.arrived { display: inline-flex; align-items: center; gap: var(--s2); font-size: var(--t-meta); font-weight: 500; color: var(--ink); }
.arrived svg { width: 16px; height: 16px; stroke-width: 2.4; }
.sheet__seal img { filter: brightness(0) invert(1); }
.sheet__seal { position: absolute; top: 50%; left: 50%; width: min(78vw, 320px); transform: translate(-50%, -52%); opacity: .06; pointer-events: none; }
.sheet > *:not(.sheet__seal) { position: relative; z-index: 1; }

/* PHOTOGRAPHY --------------------------------------------------------------- */
.shot { position: relative; display: block; overflow: hidden; border-radius: var(--r-content); background: var(--surface); }
.shot img { width: 100%; height: 100%; object-fit: cover; }
.shot__plate {
  position: absolute; inset: auto 0 0 0;
  padding: var(--s6) var(--s4) var(--s4);
  background: linear-gradient(to top, rgba(0,0,0,.92) 0%, rgba(0,0,0,.88) 55%, rgba(0,0,0,.55) 78%, transparent 100%);
}
.shot__title { font-size: var(--t-head); font-weight: 500; letter-spacing: -0.02em; line-height: 1.12; }
.shot__sub { font-size: var(--t-meta); color: var(--ink-muted); margin-top: 2px; }
.shot__when { color: var(--accent); font-weight: 500; }
.arc { aspect-ratio: 21 / 9; }

.venue { display: block; border-top: 1px solid var(--line); padding: var(--s3) 0; }
.venue .shot { aspect-ratio: 16 / 9; }
.venue__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); padding-top: var(--s3); }
.venue__name { font-size: var(--t-meta); font-weight: 500; }
.venue__near { display: block; font-size: var(--t-label); color: var(--ink-muted); font-weight: 400; }

/* DAYS ---------------------------------------------------------------------- */
.days {
  display: flex; gap: var(--s2);
  overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(var(--gutter) * -1);
  padding: 0 var(--gutter) var(--s2);
}
.days::-webkit-scrollbar { display: none; }
.day {
  flex: none; display: grid; align-content: center; justify-items: center; gap: 1px;
  min-width: 44px; min-height: 44px; padding: 0 var(--s3);
  border-radius: var(--r-round);
  background: var(--surface); color: var(--ink-muted);
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.day__dow { font-size: var(--t-label); }
.day__n { font-size: var(--t-meta); font-weight: 500; font-variant-numeric: tabular-nums; color: var(--accent); }
.day[aria-pressed="true"] { background: var(--ink); color: var(--ground); }
.day[aria-pressed="true"] .day__n { color: var(--ground); }
.day[aria-pressed="true"] .day__dow { color: var(--ground); opacity: .7; }


/* FORMS --------------------------------------------------------------------- */
.field { display: grid; gap: var(--s2); }
.field > label { font-size: var(--t-label); color: var(--ink-muted); }
.input {
  width: 100%; min-height: 44px; padding: 0 var(--s4);
  border: 0; border-radius: var(--r-round);
  background: var(--raised); color: var(--ink);
  font-size: var(--t-title);
}
.input::placeholder { color: var(--ink-muted); }
.input:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
textarea.input { padding-block: var(--s3); min-height: 88px; border-radius: var(--r-content); }


/* FEEDBACK ------------------------------------------------------------------ */
.note { font-size: var(--t-meta); color: var(--ink-muted); }
.note--bad { color: var(--bad); }
/* Not an error. Something the delegate has to supply before we can act. */
.note--wants { color: var(--accent); }

/* Mechanism 6 — an empty state is left-aligned like everything else. Centring
   it was the tell that the screen had nothing to say. */
.empty { padding: var(--s6) 0; }
.empty__title { font-size: var(--t-title); font-weight: 500; }
.empty__body { font-size: var(--t-meta); color: var(--ink-muted); margin-top: var(--s2); max-width: 38ch; }


.includes { display: flex; flex-wrap: wrap; gap: var(--s2); padding-bottom: var(--s3); }
.include {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: var(--r-round);
  background: var(--raised); color: var(--ink-muted);
  font-size: var(--t-label);
}
.include svg { width: 12px; height: 12px; stroke-width: 2.6; color: var(--ink-low); }

.toast {
  position: fixed; z-index: var(--z-toast);
  left: var(--gutter); right: var(--gutter);
  bottom: calc(84px + env(safe-area-inset-bottom));
  max-width: 34rem; margin-inline: auto;
  padding: var(--s3) var(--s4);
  border-radius: var(--r-content);
  background: var(--raised); color: var(--ink);
  font-size: var(--t-meta); font-weight: 500;
  animation: toast-in var(--dur-2) var(--ease);
}
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* DETAIL -------------------------------------------------------------------- */
.detail__body { display: block; overflow-y: auto; }
.detail__when { font-size: var(--t-meta); font-weight: 500; color: var(--accent); }
.detail__name { font-size: var(--t-head); font-weight: 500; letter-spacing: -0.02em; line-height: 1.12; margin-top: var(--s2); }
.detail .shot { margin-top: var(--s5); aspect-ratio: 16 / 9; }

/* MOTION -------------------------------------------------------------------- */
@keyframes screen-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.screen[data-enter] { animation: screen-in var(--dur-3) var(--ease); }
@keyframes item-in { from { opacity: 0; } to { opacity: 1; } }
.stagger > * { animation: item-in var(--dur-2) var(--ease) backwards; }
.stagger > *:nth-child(2) { animation-delay: 24ms; }
.stagger > *:nth-child(3) { animation-delay: 48ms; }
.stagger > *:nth-child(4) { animation-delay: 72ms; }
.stagger > *:nth-child(5) { animation-delay: 96ms; }
.stagger > *:nth-child(n+6) { animation-delay: 120ms; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .now__pulse { opacity: 1; }
}

/* PRINT — the pass, in ink on paper ----------------------------------------- */
@media print {
  .shell, .tabbar, .toast { display: none !important; }
  .sheet { position: static; display: block; background: #fff; color: #000; padding: 0; animation: none; }
  .sheet__top, .sheet__seal, [data-sheet-print] { display: none !important; }
  .sheet__body { display: block; text-align: center; }
  .sheet__qr { width: 62mm; height: 62mm; margin: 0 auto 6mm; padding: 0; background: none; }
  .sheet__name { font-size: 18pt; }
  .sheet__code { font-size: 11pt; color: #000; }
  .sheet__facts { margin-top: 8mm; justify-content: center; gap: 10mm; }
  .sheet__fact span { color: #444; }
}

/* A pill in a row is the row's value, so it sits where a value sits. Without
   this it stacks up against the label and the column disappears. */
.row > .pill { margin-left: auto; flex: none; }

/* Mechanism 7 — the greeting and the date are one line, split by colour, not
   a title with a caption parked underneath it. */

/* The way out of a full-screen sheet. The rewrite dropped this rule while the
   markup kept calling it, so the close control rendered as a bare SVG on black
   — the pass had no visible exit. It is the system's control shape, 40px and
   round, like every other control. */
.sheet__close {
  display: grid; place-items: center; flex: none;
  width: var(--control); height: var(--control);
  border-radius: var(--r-round);
  background: var(--raised);
  color: var(--ink);
}
.sheet__close svg { width: 18px; height: 18px; }
.sheet__close:active { opacity: .7; }

/* A session the committee has not timed yet says so. An em-dash reads as the
   app failing to load a time rather than as a time that does not exist. */
.session__time--none { font-size: var(--t-label); }

/* Explains the TBC on a row once, beneath the list it applies to. */
.tbc-note { padding-bottom: var(--s3); font-size: var(--t-label); color: var(--ink-muted); }
.legal { font-size: var(--t-label); color: var(--ink-low); }
.legal a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

  flex: none; align-self: center; margin-left: var(--s3);
  display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: var(--r-round);
  background: var(--raised); color: var(--ink);
}

/* WORD FROM THE COMMITTEE ---------------------------------------------------
   A notification, not a list row. It arrives at the top of Today, says who it
   is from and when, and can be dismissed — which is what makes it credible the
   next time one appears. */
.notices { display: grid; gap: var(--s2); }
.notice {
  display: flex; gap: var(--s3); align-items: flex-start;
  padding: var(--s3) var(--s3) var(--s3) var(--s4);
  background: var(--surface); border-radius: var(--r-content);
  animation: notice-in var(--dur-3) var(--ease);
}
@keyframes notice-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}
.notice.is-going { animation: notice-out var(--dur-2) var(--ease) forwards; }
@keyframes notice-out {
  to { opacity: 0; transform: translateY(-6px); }
}
/* The event's own mark, the way a notification carries the app's icon. */
.notice__mark {
  flex: none; display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: var(--r-round);
  background: var(--raised);
}
.notice__mark img { width: 18px; height: 18px; filter: brightness(0) invert(1); }
.notice__body { flex: 1 1 auto; min-width: 0; }
.notice__meta {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  font-size: var(--t-label); color: var(--ink-muted);
}
.notice__who { font-weight: 500; color: var(--ink-muted); }
.notice__when { color: var(--accent); font-variant-numeric: tabular-nums; }
.notice__text { margin-top: 3px; font-size: var(--t-meta); color: var(--ink); overflow-wrap: anywhere; }
.notice__clock { color: var(--accent); font-weight: 500; font-variant-numeric: tabular-nums; }
/* No ring on an urgent card: the pill already says it, and the only hairline
   bright enough to read was reserved for a control this app no longer has. */
.notice__x {
  flex: none; display: grid; place-items: center;
  width: 44px; height: 44px; margin: -6px -6px 0 0;
  border-radius: var(--r-round); color: var(--ink-low);
}
.notice__x-icon { width: 15px; height: 15px; }
.notice__x:active { opacity: .6; }

@media (prefers-reduced-motion: reduce) {
  .notice, .notice.is-going { animation: none; }
  .notice.is-going { opacity: 0; }
}

/* THE BELL ------------------------------------------------------------------
   Messages are reachable from every screen, and dismissing one on Today moves
   it here rather than destroying it. */
.masthead__right { display: flex; align-items: center; gap: var(--s3); }
.masthead__bell { position: relative; }
.masthead__bell svg { width: 19px; height: 19px; }
.masthead__badge {
  position: absolute; top: -1px; right: -1px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: grid; place-items: center;
  border-radius: var(--r-round);
  background: var(--ink); color: var(--ground);
  font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums;
  border: 2px solid var(--ground);
}

/* A sheet that holds a list rather than one centred object. */
.sheet--list { grid-template-rows: auto 1fr; }
.sheet__heading { font-size: var(--t-head); font-weight: 500; letter-spacing: -0.02em; }
.sheet__heading .quiet { color: var(--ink-low); }
.sheet__scroll {
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  display: grid; gap: var(--s2); align-content: start;
  padding-top: var(--s4);
}
/* The heading wraps to two lines; the control belongs at the top of them. */
.sheet--list .sheet__top { align-items: flex-start; }
.archive__past { padding-top: var(--s4); color: var(--ink-low); }
/* The pass says for itself that it survives no signal. That promise used to
   live only inside a committee message the delegate can dismiss. */
/* Read across a doorway, in a queue, by someone who is not looking for long.
   Larger than the offline stamp below it and in full-strength ink, because
   this is the line that decides whether the delegate comes in. */
.sheet__covers {
  font-size: var(--t-meta); line-height: 1.45; color: var(--ink);
  text-align: center; padding: var(--s3) var(--s4) 0; text-wrap: balance;
}
.sheet__offline { font-size: var(--t-label); color: var(--ink-muted); text-align: center; }
/* A heading whose qualifier is itself a date. The accent is the clock, so the
   clock is where it goes — the same rule as every other date in the app. */
.screen__title .when { color: var(--accent); }
.row__when { display: block; font-size: var(--t-label); font-weight: 400; color: var(--accent); font-variant-numeric: tabular-nums; }

/* WHAT'S NEW ----------------------------------------------------------------
   The same bell, a second stream. A shipped change is not a committee
   announcement, so its mark differs — but it is the same card, because to a
   delegate both are "something I should know". */
.archive__head { padding-top: var(--s2); color: var(--ink-muted); }
.archive__head--later { padding-top: var(--s5); }
.notice__mark--new { background: var(--ink); color: var(--ground); }
.notice__spark { font-size: 15px; line-height: 1; }
.notice__title { margin-top: 3px; font-size: var(--t-title); font-weight: 500; }
.notice--update .notice__text { color: var(--ink-muted); margin-top: 2px; }

/* FIRST RUN -----------------------------------------------------------------
   One thing at a time, in the order it matters, with a way out on every step.
   A first run that cannot be skipped is a first run people learn to resent. */
.onboard {
  grid-template-rows: auto 1fr auto;
  padding: calc(var(--s4) + env(safe-area-inset-top)) var(--gutter)
           calc(var(--s5) + env(safe-area-inset-bottom));
}
.onboard__top { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
.onboard__dots { display: flex; gap: 6px; }
.onboard__dot {
  width: 6px; height: 6px; border-radius: var(--r-round);
  background: var(--mark);
  transition: background var(--dur-2) var(--ease), width var(--dur-2) var(--ease);
}
.onboard__dot.is-done { background: var(--ink-low); }
.onboard__dot.is-here { width: 20px; background: var(--ink); }
.onboard__skip { margin-right: calc(var(--s3) * -1); }

.onboard__body { display: grid; align-content: center; justify-items: start; gap: var(--s3); }
.onboard__art { margin-bottom: var(--s4); }
.onboard__qr {
  display: block; width: 132px; height: 132px; padding: 10px;
  border-radius: var(--r-content); background: var(--ink);
}
.onboard__qr svg { width: 100%; height: 100%; }
.onboard__badge {
  display: grid; place-items: center; width: 56px; height: 56px;
  border-radius: var(--r-round); background: var(--surface); color: var(--ink);
}
.onboard__badge-icon { width: 24px; height: 24px; stroke-width: 1.7; }
.onboard__kicker { font-size: var(--t-label); font-weight: 500; color: var(--ink-low); }
.onboard__title {
  font-size: var(--t-head); font-weight: 500;
  letter-spacing: -0.02em; line-height: 1.12; text-wrap: balance;
}
.onboard__text { font-size: var(--t-meta); color: var(--ink-muted); max-width: 38ch; }
.onboard__foot { display: grid; gap: var(--s2); }

/* Each step arrives rather than cutting, so the sequence reads as one thing. */
.onboard__body, .onboard__foot { animation: onboard-in var(--dur-3) var(--ease); }
@keyframes onboard-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .onboard__body, .onboard__foot { animation: none; }
  .onboard__dot { transition: none; }
}

/* THE LIVE FLOOR ------------------------------------------------------------
   A debate in progress and an open question floor. Both exist only while they
   are happening, and both sit directly under the now card, because a vote you
   have thirty seconds to cast is not something to go looking for. */
.live { background: var(--surface); border-radius: var(--r-content); padding: var(--s5) var(--s4); display: grid; gap: var(--s3); }
.live__kicker { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-label); font-weight: 500; color: var(--ink-muted); }

/* THE QUIZ BOARD -------------------------------------------------------------
   A table a delegate reads from a seat, three rows back, while something else
   is happening on stage. So: the score is the biggest thing on the row, the
   place is quiet, and the leader is the only thing that changes weight. No
   medals or colour for the top three — the quiz is still running and the
   board should not look like it has already decided. */
.qboard { display: grid; }
.qrow {
  display: grid; grid-template-columns: 1.75rem 1fr auto; align-items: baseline;
  gap: var(--s3); padding: var(--s3) 0; border-bottom: 1px solid var(--line);
}
.qrow:last-child { border-bottom: 0; }
.qrow__p { font-size: var(--t-label); color: var(--ink-low); font-variant-numeric: tabular-nums; }
.qrow__n { font-size: var(--t-meta); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qrow--lead .qrow__n { font-weight: 500; color: var(--ink); }
.qrow__s { font-size: var(--t-title); font-weight: 700; font-variant-numeric: tabular-nums; }
/* Gold is time in this app and nothing else, so a score never wears it. */
.qrow--lead .qrow__s { color: var(--ink); }

/* Only when the board is behind. A stamp on a live board would be noise; a
   board with no stamp that is quietly eight minutes old is a lie. */
.qstale { color: var(--ink-low); }
.live__title { font-size: var(--t-title); font-weight: 500; line-height: 1.3; text-wrap: pretty; }
.live__acts { display: flex; flex-wrap: wrap; gap: var(--s2); }

.choice {
  flex: 1 1 auto; min-height: 44px; padding: 0 var(--s4);
  border: 1px solid var(--line); border-radius: var(--r-round);
  background: none; color: var(--ink);
  font: inherit; font-size: var(--t-meta); font-weight: 500;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.choice.is-mine { background: var(--ink); color: var(--ground); border-color: transparent; }
.choice:active { opacity: .7; }

/* The two rounds, side by side, which is the whole point of the format: not
   who won, but which way the room moved. */
.tally { display: grid; gap: var(--s4); padding-top: var(--s2); }
.tally__round { display: grid; gap: var(--s2); }
.tally__label { font-size: var(--t-label); color: var(--ink-low); }
.tally__bar { display: flex; height: 8px; border-radius: var(--r-round); overflow: hidden; background: var(--raised); }
.tally__a { background: var(--ink); }
.tally__b { background: var(--ink-low); }
.tally__legend { display: flex; justify-content: space-between; gap: var(--s3); font-size: var(--t-label); color: var(--ink-muted); }
.tally__swing { font-size: var(--t-meta); color: var(--ink); font-weight: 500; }

/* QUESTIONS ------------------------------------------------------------------ */
.qa__form { display: grid; gap: var(--s2); }
.qa__name { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-label); color: var(--ink-muted); }
.qa__name input { width: 18px; height: 18px; accent-color: #FFFFFF; }
.question { display: flex; gap: var(--s3); align-items: flex-start; padding: var(--s3) 0; border-top: 1px solid var(--line); }
.question__body { flex: 1 1 auto; min-width: 0; }
.question__text { font-size: var(--t-meta); overflow-wrap: anywhere; }
.question__meta { display: flex; align-items: center; gap: var(--s2); margin-top: 2px; font-size: var(--t-label); color: var(--ink-low); }
.question__vote {
  flex: none; display: grid; justify-items: center; gap: 1px;
  min-width: 44px; min-height: 44px; padding: var(--s2) var(--s2);
  border: 1px solid var(--line); border-radius: var(--r-content);
  background: none; color: var(--ink-muted);
  font: inherit; font-size: var(--t-label); font-weight: 500; font-variant-numeric: tabular-nums;
}
.question__vote.is-mine { background: var(--ink); color: var(--ground); border-color: transparent; }
.question__chev { width: 14px; height: 14px; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ACCOUNT HISTORY ------------------------------------------------------------ */
.row--stack { align-items: flex-start; }
.activity__title { display: block; color: var(--ink); font-weight: 500; }
.activity__when { flex: none; font-weight: 400; color: var(--ink-low); font-size: var(--t-label); font-variant-numeric: tabular-nums; }

/* ARRIVALS -------------------------------------------------------------------
   Something landing while the app is open should say so. Push only reaches a
   delegate who granted permission and has signal; a card appearing silently
   above the fold is missed by anyone reading further down. This is neither —
   it slides under the masthead, waits, and leaves on its own. Never a modal:
   nobody mid-task should have to dismiss something to carry on. */
.arrival {
  position: fixed; z-index: var(--z-toast);
  left: var(--gutter); right: var(--gutter);
  top: calc(var(--s3) + env(safe-area-inset-top));
  max-width: 34rem; margin-inline: auto;
  display: flex; align-items: flex-start; gap: var(--s2);
  padding: var(--s3) var(--s3) var(--s3) var(--s4);
  background: var(--raised); border-radius: var(--r-content);
  animation: arrival-in var(--dur-3) var(--ease);
}
.arrival--urgent { outline: 1px solid var(--ink-low); outline-offset: -1px; }
.arrival__in { flex: 1 1 auto; min-width: 0; text-align: left; background: none; color: inherit; }
.arrival__meta {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-label); font-weight: 500; color: var(--ink-muted);
}
.arrival__text { margin-top: 2px; font-size: var(--t-meta); color: var(--ink); overflow-wrap: anywhere; }
.arrival__x {
  flex: none; display: grid; place-items: center;
  width: 44px; height: 44px; margin: -6px -6px 0 0;
  border-radius: var(--r-round); color: var(--ink-low);
}
@keyframes arrival-in { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }
.arrival.is-going { animation: arrival-out var(--dur-2) var(--ease) forwards; }
@keyframes arrival-out { to { opacity: 0; transform: translateY(-8px); } }
@media (prefers-reduced-motion: reduce) {
  .arrival, .arrival.is-going { animation: none; }
  .arrival.is-going { opacity: 0; }
}

/* THE CONFIRMATION STEP ------------------------------------------------------
   An unconfirmed address is not a failed sign-in, so it does not get the error
   colour. It is the one job left, so it gets a surface of its own, the address
   spelled out, and a button that sends the link only when pressed. Nothing is
   sent on its own any more: the old code mailed a fresh link on every sign-in
   attempt and killed the link already sitting in the delegate's inbox. */
.confirm {
  display: grid; gap: var(--s3);
  margin-top: var(--s4); padding: var(--s4);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-content);
}
.confirm__lead { margin: 0; font-size: var(--t-title); font-weight: 500; color: var(--accent); }
.confirm__body { margin: 0; font-size: var(--t-meta); line-height: 1.5; color: var(--ink-muted); }
.confirm__body b { color: var(--ink); font-weight: 500; overflow-wrap: anywhere; }
.confirm__hint { margin: 0; font-size: var(--t-label); color: var(--ink-low); }

/* SEAT CODE ------------------------------------------------------------------
   Sits under the sign-in form for the delegate a rep paid for. It borrows
   .confirm's shape, because it is the same thing structurally: a quiet aside
   under a form explaining why the form is not the answer for you.

   Deliberately not gold. --accent means time in this app and nothing else:
   the countdown, the dates, the deadline. A sign-in aside is not time. */
.seatcode {
  display: grid; gap: var(--s3);
  margin-top: var(--s4); padding: var(--s4);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-content);
}
.seatcode__lead { margin: 0; font-size: var(--t-title); font-weight: 500; color: var(--ink); }
.seatcode__body { margin: 0; font-size: var(--t-meta); line-height: 1.5; color: var(--ink-muted); }
/* The code shape is the one thing they must recognise, so it is the one thing
   that reaches full ink. letter-spacing because it is a code, read character
   by character off a phone screen or a slip of paper, not a word. */
.seatcode__body b { color: var(--ink); font-weight: 500; letter-spacing: 0.04em; overflow-wrap: anywhere; }

/* ONE QUESTION ---------------------------------------------------------------
   The gender prompt. Centred and plain: two buttons the same size, because
   this is a choice with no default and no recommended answer, and anything
   that made one look preferred would be the app nudging a delegate into a
   room. */
.askgender { align-items: center; justify-content: center; }
.askgender__in {
  display: grid; gap: var(--s4);
  width: 100%; max-width: 28rem;
  padding: var(--s5) var(--gutter) calc(var(--s5) + env(safe-area-inset-bottom));
}
.askgender__kicker {
  margin: 0; font-size: var(--t-label); font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--accent);
}
.askgender__title {
  margin: 0; font-family: var(--display); font-weight: 700;
  font-size: clamp(1.75rem, 7vw, 2.25rem); line-height: 1.05;
  letter-spacing: -.03em; text-wrap: balance;
}
.askgender__body { margin: 0; font-size: var(--t-meta); line-height: 1.5; color: var(--ink-muted); }
.askgender__choices { display: grid; gap: var(--s3); margin-top: var(--s2); }

/* THE ALERTS ASK -------------------------------------------------------------
   Same shape as the gender prompt, because they are the same kind of moment:
   one question, two answers, no small print. "Not now" is a real answer and
   is recorded as one, which is what keeps this from becoming a nag. */
.askalerts { align-items: center; justify-content: center; }
.askalerts__in {
  display: grid; gap: var(--s4);
  width: 100%; max-width: 28rem;
  padding: var(--s5) var(--gutter) calc(var(--s5) + env(safe-area-inset-bottom));
}
.askalerts__kicker {
  margin: 0; font-size: var(--t-label); font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--accent);
}
.askalerts__title {
  margin: 0; font-family: var(--display); font-weight: 700;
  font-size: clamp(1.75rem, 7vw, 2.25rem); line-height: 1.05;
  letter-spacing: -.03em; text-wrap: balance;
}
.askalerts__body { margin: 0; font-size: var(--t-meta); line-height: 1.5; color: var(--ink-muted); }
.askalerts__acts { display: grid; gap: var(--s2); margin-top: var(--s2); }

/* The inline offer at the top of the messages sheet. A row, not a dialog: the
   delegate opened this to read, and reading is not interrupted. */
.offer {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
  margin: 0 var(--gutter) var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-content);
}
.offer__text { margin: 0; flex: 1 1 14rem; min-width: 0; font-size: var(--t-meta); color: var(--ink-muted); }
.btn--small { min-height: 36px; padding: 0 var(--s4); font-size: var(--t-label); }


/* ATTRIBUTION ----------------------------------------------------------------
   On every page and every screen, and quiet on all of them. A credit that
   competes with a delegate's room number has misjudged whose screen it is, so
   this is the smallest type in the system, the most recessive ink, and it sits
   after everything else rather than beside it. The links stay underlined,
   because a credit nobody can tell is clickable is not a credit. */
.credit {
  margin: var(--s6) 0 0;
  padding-bottom: env(safe-area-inset-bottom);
  font-size: var(--t-label); line-height: 1.5; color: var(--ink-low);
  text-align: center;
}
.credit a {
  color: var(--ink-muted); text-decoration: underline;
  text-decoration-color: var(--mark); text-underline-offset: 3px;
  padding: 2px 0;
}
.credit a:hover { color: var(--ink); text-decoration-color: currentColor; }
.credit a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }
/* The app's screens end above a fixed tab bar. */
[data-screen] > .credit { margin-bottom: var(--s5); }

/* Inside the first-run tour the credit sits under the buttons, smaller and
   quieter than the note above it, and only on the final card. */
.credit--onboard { margin-top: var(--s4); padding-bottom: 0; }
.credit--onboard[hidden] { display: none; }

/* THE MEDAL TABLE ------------------------------------------------------------
   A live league table in the same card as the quiz board and the debate:
   .live surface, padding and gap, .live__kicker over a .live__title, the
   16/14/12 type scale, the app's greys. Medal colour is an accent and only an
   accent: the 3px place stripe, the two column discs, the legend dots.
   Nothing else on this card is gold.

   - The table bleeds to the card's edges so the place stripe sits on the left
     edge, where a league table draws its qualification bar. Stripes are keyed
     to place (1 gold, 2 silver, 3 bronze tints), never on a row still on zero.
   - Rows are 48px on a phone, 52px wider; hairlines; tabular numerals.
   - Your MSA: a faint neutral lift plus a small "You" label. No colour.
   - Folded (default): podium + your MSA, a quiet "…" row between them when
     places are skipped. Folded rows are display:none, so they are neither
     drawn nor read aloud. Opening animates the clip's measured height (JS,
     WAAPI, 260ms on --ease); .is-closing keeps the rows drawn while it shrinks.
   - Fixed layout with explicit widths: nothing scrolls sideways at 360.
   - The fixed tab bar is reserved by .shell's bottom padding.
   -------------------------------------------------------------------------- */
.mt { --mt-gold: #D4AF37; --mt-silver: #C0C0C8; --mt-bronze: #CD7F32; --mt-band: rgba(255, 255, 255, 0.03); }

.mt__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--s1) var(--s3); }
.mt__titles { display: grid; gap: var(--s1); min-width: 0; flex: none; }
.mt__live {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  padding-bottom: 2px;
  font-size: var(--t-label); font-weight: 500; color: var(--ink-low);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.mt__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-low); }
.mt__live.is-live .mt__dot { background: #4CC38A; }   /* a mark, not text */

.mt__legend { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s4); font-size: var(--t-label); color: var(--ink-low); }
.mt__legend li { display: inline-flex; align-items: center; gap: 6px; }
.mt__key { width: 8px; height: 8px; border-radius: 50%; }
.mt__key--1 { background: var(--mt-gold); }
.mt__key--2 { background: var(--mt-silver); }
.mt__key--3 { background: var(--mt-bronze); }
.mt__note { font-size: var(--t-meta); color: var(--ink-muted); }

/* The clip is what animates; the table inside it bleeds to the card edges. */
.mt__clip { margin-inline: calc(-1 * var(--s4)); }
.mtable {
  width: 100%;
  table-layout: fixed;
  border-collapse: separate; border-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.mtable th, .mtable td { padding: 0; vertical-align: middle; font-weight: inherit; }
.mtable .mt-pos  { width: 44px; padding-left: var(--s4); text-align: center; }
.mtable .mt-team { padding-left: var(--s2); padding-right: var(--s2); text-align: left; }
.mtable .mt-g, .mtable .mt-s { width: 44px; text-align: right; }
.mtable .mt-s { padding-right: 6px; }
.mtable .mt-tot  { width: 52px; padding-right: 6px; text-align: right; }
.mtable .mt-pts  { width: 60px; padding-right: var(--s4); text-align: right; background: var(--mt-band); }

.mtable thead th {
  position: sticky; top: 0; z-index: 1;
  height: 32px; background: var(--surface);
  border-bottom: 1px solid var(--line);
  font-size: var(--t-label); font-weight: 500; color: var(--ink-low);
}
.mtable thead .mt-pts { background: linear-gradient(var(--mt-band), var(--mt-band)), var(--surface); }

/* The medal discs that head the Gold and Silver columns. */
.mt-disc {
  display: inline-block; width: 12px; height: 12px; border-radius: 50%;
  vertical-align: middle;
}
.mt-disc--gold   { background: radial-gradient(circle at 35% 30%, #F4DE8A 0, #D4AF37 55%, #A88622 100%); }
.mt-disc--silver { background: radial-gradient(circle at 35% 30%, #F2F2F6 0, #C0C0C8 55%, #8E8E98 100%); }

/* Rows. Every row draws its own top hairline, so whichever rows are showing,
   folded or open, the lines are right without a last-child rule. */
.mtable tbody td, .mtable tbody th { height: 52px; border-top: 1px solid var(--line); }
.mtable tbody tr:first-child > * { border-top: 0; }

.mtable tbody .mt-pos { font-size: var(--t-meta); font-weight: 500; color: var(--ink-low); }
.mtable tbody .mt-row--p1 > .mt-pos { box-shadow: inset 3px 0 0 var(--mt-gold); }
.mtable tbody .mt-row--p2 > .mt-pos { box-shadow: inset 3px 0 0 var(--mt-silver); }
.mtable tbody .mt-row--p3 > .mt-pos { box-shadow: inset 3px 0 0 var(--mt-bronze); }
.mtable tbody .mt-row--p1 > .mt-pos, .mtable tbody .mt-row--p2 > .mt-pos, .mtable tbody .mt-row--p3 > .mt-pos { color: var(--ink); font-weight: 700; }

.mt-team__in { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.mt-badge {
  display: grid; place-items: center; flex: none;
  width: 28px; height: 28px; border-radius: 50%;
  font-size: 0.625rem; font-weight: 700;
  letter-spacing: 0.04em; color: #F5F5F7;
}
.mt-team__words { display: grid; min-width: 0; gap: 1px; }
.mt-team__line { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.mt-team__msa {
  font-size: var(--t-meta); font-weight: 500; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mt-team__name {
  font-size: var(--t-label); color: var(--ink-low);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.mtable tbody .mt-g, .mtable tbody .mt-s, .mtable tbody .mt-tot { font-size: var(--t-meta); font-weight: 500; color: var(--ink); }
.mtable tbody .mt-pts { font-size: var(--t-title); font-weight: 700; color: var(--ink); }
.mtable tbody .is-zero { color: var(--ink-low); font-weight: 400; }
.mtable tbody .mt-pts.is-zero { font-weight: 500; }
.mtable tbody .mt-row--nil .mt-team__msa { color: var(--ink-muted); }

/* Your MSA: a faint neutral lift and a hairline, never a colour. Translucent
   so a medal flash still shows through. On the lift --ink-low falls to 4.2:1,
   so recessive text in this row uses #8F8F95 (5.3:1) instead. */
.mtable tbody .mt-row--mine > * { background-color: rgba(255, 255, 255, 0.04); }
.mtable tbody .mt-row--mine > .mt-pts { background-color: rgba(255, 255, 255, 0.06); }
.mtable tbody .mt-row--mine > *,
.mtable tbody .mt-row--mine + tr > * { border-top-color: #3A3A3A; }
.mtable tbody .mt-row--mine .mt-team__msa { font-weight: 700; }
.mtable tbody .mt-row--mine .is-zero, .mtable tbody .mt-row--mine .mt-team__name { color: #8F8F95; }
.mtable tbody .mt-row--mine:not(.mt-row--p1, .mt-row--p2, .mt-row--p3) > .mt-pos { color: #8F8F95; }
.mt-you {
  flex: none; padding: 0 6px; border-radius: var(--r-round);
  border: 1px solid #4A4A4A; color: var(--ink);
  font-size: var(--t-label); font-weight: 500; line-height: 18px;
}

/* The fold. Folded rows are gone (not drawn, not read); the "…" row only
   shows while folded. */
.mt:not(.is-open):not(.is-closing) .mt-row--extra { display: none; }
.mt.is-open .mt-gap, .mt.is-closing .mt-gap { display: none; }
.mtable tbody .mt-gap > td {
  height: 24px; padding: 0;
  text-align: center; vertical-align: middle;
  font-size: var(--t-meta); line-height: 1; letter-spacing: 0.2em; color: var(--ink-low);
}

.mt__toggle {
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
  width: 100%; min-height: 44px;
}
.mt__chev { flex: none; color: var(--ink-muted); }
.mt.is-open .mt__chev { transform: rotate(180deg); }

@media (prefers-reduced-motion: no-preference) {
  .mt__live.is-live .mt__dot { animation: pulse 2.4s var(--ease) infinite; }
  .mt__chev { transition: transform 260ms var(--ease); }
}

@media (max-width: 480px) {
  /* Acronym only; every pixel back to the team column so "EKSUMSA" and its
     "You" label sit on one line at 360. */
  .mt-team__name { display: none; }
  .mtable tbody td, .mtable tbody th { height: 48px; }
  .mtable .mt-pos { width: 36px; }
  .mtable .mt-team { padding-right: var(--s1); }
  .mtable .mt-g, .mtable .mt-s { width: 34px; }
  .mtable .mt-tot { width: 40px; }
  .mtable .mt-pts { width: 46px; }
  .mt-badge { width: 26px; height: 26px; }
  .mt-team__in { gap: var(--s2); }
  .mt-team__line { gap: 6px; }
  .mt-you { padding: 0 5px; }
}
@media (max-width: 359px) {
  .mtable .mt-pos { width: 32px; }
  .mtable .mt-g, .mtable .mt-s { width: 30px; }
  .mtable .mt-tot { width: 34px; }
  .mtable .mt-pts { width: 40px; }
  .mt-badge { width: 24px; height: 24px; }
}

/* WHAT TO BRING -- a folded list on Today. */
.bring { gap: 0; }
.bring__sum { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); cursor: pointer; list-style: none; }
.bring__sum::-webkit-details-marker { display: none; }
.bring__sum::after { content: "+"; font-size: var(--t-title); color: var(--ink-low); margin-left: var(--s2); }
.bring[open] .bring__sum::after { content: "\2212"; }
.bring__n { margin-left: auto; font-size: var(--t-label); color: var(--ink-low); }
.bring__list { list-style: none; margin: var(--s3) 0 0; padding: 0; display: grid; font-size: var(--t-meta); line-height: 1.45; }
.bring__item { display: grid; grid-template-columns: 22px 1fr; gap: var(--s3); align-items: start; padding: var(--s2) 0; min-height: 44px; cursor: pointer; }
.bring__item input { width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--ink); }
.bring__item input:checked + span { color: var(--ink-low); text-decoration: line-through; text-decoration-color: var(--ink-low); }

/* A session with no photograph carries the convention's mark, quietly. */
.session__tile--mark { display: grid; place-items: center; }
.session__tile--mark img { width: 38%; height: auto; opacity: 0.55; }

/* Which scores: a drop-down, centred under the title. */
.qpick { position: relative; justify-self: center; width: min(100%, 22rem); }
.qpick select {
  appearance: none; -webkit-appearance: none; width: 100%; min-height: 48px;
  padding: 0 44px 0 18px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--ground, transparent); color: var(--ink); font: inherit; font-weight: 600; text-align: center; text-align-last: center; cursor: pointer;
}
.qpick select:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.qpick::after {
  content: ""; position: absolute; right: 18px; top: 50%; width: 8px; height: 8px; pointer-events: none;
  border-right: 2px solid var(--ink); border-bottom: 2px solid var(--ink); transform: translateY(-70%) rotate(45deg);
}
.live[data-quiz] .live__title { text-align: center; }
.live[data-quiz] .live__kicker { justify-self: center; }
/* League-table movement since the last question. */
.qrow { grid-template-columns: 1.5rem 2.25rem 1fr auto; }
.qrow__m { font-size: var(--t-label); font-weight: 700; color: var(--ink-low); font-variant-numeric: tabular-nums; }
.qrow__m--up { color: var(--good, #4CC27A); }
.qrow__m--down { color: var(--bad); }
.qrow--lead .qrow__n { font-weight: 700; }
