/* ═══════════════════════════════════════════════════════════════════════
   Poker Ledger

   The :root block below is lifted verbatim from the Claude Design project
   "Poker Ledger Design System" (Poker Ledger Visual Language.dc.html). Treat
   it as the source of truth: when the design changes, replace that block and
   leave everything under it alone. Every rule in this file reads from it and
   hard-codes nothing.

   The rule the whole system rests on: green and red mean money and nothing
   else. Not a button, not a border, not a live indicator. Everything else
   that needs to stand out uses the accent — slate by default, user-pickable.
   ═══════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Archivo';
  src: url('fonts/archivo-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* ─── design tokens — from the design system, do not edit by hand ─────── */

:root{
  /* ─────────── GROUND & SURFACES ─────────── */
  --paper:               #FBF9F5;   /* app background — warm off-white, kinder than pure white at 1am */
  --surface:             #FFFFFF;   /* cards, sheets, anything floating above the paper */
  --surface-sunken:      #F3F0E9;   /* input wells, empty states, segmented-control track */
  --surface-inverse:     #1B1A17;   /* settle-up bar, toasts, "Settled" chip */
  --hairline:            #E9E4DA;   /* default 1px divider between list rows */
  --hairline-strong:     #D8D1C3;   /* input and secondary-button borders */

  /* ─────────── INK ─────────── */
  --ink:                 #1B1A17;   /* all money and all primary labels */
  --ink-secondary:       #6A665E;   /* venue, stakes, timestamps, helper copy */
  --ink-quiet:           #9B958B;   /* eyebrows, units, placeholder text */
  --ink-disabled:        #B7B1A6;   /* text inside anything switched off */
  --ink-on-accent:       #FFFFFF;   /* label on a filled accent surface */
  --ink-on-inverse:      #FBF9F5;   /* label on the dark surface */

  /* ─────────── ACCENT — never confusable with profit or loss ─────────── */
  /* Each choice supplies a light-ground and a dark-ground value; the theme
     picks which. Every hue sits between blue and magenta, well clear of both
     money colors. `--accent-*` are derived from --surface/--ink rather than
     hard white/black so they follow the theme instead of fighting it. */
  --accent-l:            #47566B;   /* slate — the default since 2026-08-27 (was violet #4A3AFF) */
  --accent-d:            #A9B6CB;
  --accent:              var(--accent-l);
  --accent-hover:        color-mix(in oklab, var(--accent) 86%, var(--ink));
  --accent-press:        color-mix(in oklab, var(--accent) 72%, var(--ink));
  --accent-wash:         color-mix(in oklab, var(--accent) 11%, var(--surface));
  --accent-edge:         color-mix(in oklab, var(--accent) 30%, var(--surface));
  --focus-halo:          color-mix(in oklab, var(--accent) 30%, transparent);
  --focus-ring:          0 0 0 3px var(--focus-halo);

  /* ─────────── MONEY — the only place green and red are permitted ─────────── */
  --profit:              #0A8A50;   /* a positive figure. Never a button, never a border, never decoration */
  --profit-wash:         color-mix(in oklab, var(--profit) 13%, var(--surface));
  --loss:                #D2301D;   /* a negative figure, and money you owe someone */
  --loss-wash:           color-mix(in oklab, var(--loss) 13%, var(--surface));
  --even:                #6A665E;   /* exactly break-even gets no color at all */

  /* ─────────── SHADOW ─────────── */
  --shadow-card:         0 1px 1px rgba(27,26,23,.04), 0 3px 10px -2px rgba(27,26,23,.07);
  --shadow-raised:       0 2px 4px rgba(27,26,23,.06), 0 12px 28px -8px rgba(27,26,23,.14);
  --shadow-sheet:        0 -8px 44px -10px rgba(27,26,23,.24);

  /* ─────────── TYPE ─────────── */
  --font-money: "Archivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI Variable Display", "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-ui:    ui-sans-serif, system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-code:  ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  --text-jumbo:   56px;  /* the amount you are typing into a money field */
  --text-hero:    40px;  /* the one number that answers the screen */
  --text-display: 30px;  /* stat-tile figures and screen titles */
  --text-title:   23px;  /* sheet titles, headline money on a card */
  --text-heading: 18px;  /* section headings, money on a list row */
  --text-body:    16px;  /* the smallest size money is ever set at */
  --text-label:   14px;  /* buttons, list labels, field labels */
  --text-caption: 13px;  /* venue, stakes, timestamps */
  --text-micro:   11px;  /* uppercase eyebrows only, never running copy */

  --leading-tight: 1.02;  /* jumbo and hero money */
  --leading-snug:  1.22;  /* titles */
  --leading-body:  1.45;  /* labels and list rows */
  --leading-loose: 1.6;   /* paragraphs */

  --track-money:  -0.022em;  /* pulls big figures together so a column reads as one shape */
  --track-tight:  -0.014em;  /* headings */
  --track-normal:  0em;
  --track-caps:    0.09em;   /* uppercase eyebrows */

  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;   /* every money figure */
  --weight-bold:     700;   /* hero money only */

  /* ─────────── SPACE ─────────── */
  --space-hair:   2px;
  --space-tiny:   4px;
  --space-small:  8px;
  --space-medium: 12px;
  --space-base:   16px;   /* the default gap and the screen gutter on a phone */
  --space-large:  24px;
  --space-xlarge: 32px;
  --space-huge:   48px;
  --space-giant:  72px;   /* gap between major sections on desktop */

  /* ─────────── TOUCH ─────────── */
  --tap-minimum:     44px;  /* nothing interactive is ever smaller than this */
  --tap-comfortable: 52px;  /* standard button and list-row height */
  --tap-thumb:       60px;  /* the one-handed, one-eye-open primary action */

  /* ─────────── SHAPE ─────────── */
  --radius-small:  8px;    /* chips and small buttons */
  --radius-medium: 12px;   /* buttons and text inputs */
  --radius-large:  16px;   /* cards and money inputs */
  --radius-xlarge: 22px;   /* stat tiles */
  --radius-sheet:  28px;   /* top corners of a bottom sheet */
  --radius-pill:   999px;
  --border-hair:   1px;
  --border-thick:  1.5px;  /* inputs and secondary buttons, so they hold up in bad light */

  /* ─────────── MOTION ─────────── */
  --ease-out:   cubic-bezier(.2,.8,.3,1);
  --speed-fast: 110ms;   /* color and shadow changes */
  --speed-base: 190ms;   /* things that move */

  /* ─────────── LAYOUT ─────────── */
  --page-max:       1680px;  /* desktop content column, incl. the sidebar */
  --phone-max:      390px;   /* the phone we design to first */
  --sign-display:   inline;  /* set to none to hide the leading + on profits */
}

/* ─── accents & themes (added on top of the design system) ────────────── */

/* The accent never colors a figure — money is the only thing that gets green
   or red — so these only have to avoid *being* green or red themselves. Teal is
   pushed to cyan and amber to yellow-gold for exactly that reason. Each supplies
   a value for a light ground and one for a dark; the theme picks. Mirrored in
   js/theme.js, which the picker reads so the swatches can't go stale. */
:root[data-accent='slate']    { --accent-l: #47566B; --accent-d: #A9B6CB; }
:root[data-accent='violet']   { --accent-l: #4A3AFF; --accent-d: #9A8CFF; }
:root[data-accent='blue']     { --accent-l: #1668D6; --accent-d: #6FB1F5; }
:root[data-accent='teal']     { --accent-l: #067A8E; --accent-d: #45C3DA; }
:root[data-accent='purple']   { --accent-l: #7A2ED6; --accent-d: #C094F7; }
:root[data-accent='magenta']  { --accent-l: #C81E8C; --accent-d: #F275C0; }
:root[data-accent='amber']    { --accent-l: #9A6B00; --accent-d: #E8B84B; }
:root[data-accent='ink']      { --accent-l: #1B1A17; --accent-d: #F2EFE9; }

/* Dark: warm charcoal, because the light theme is a warm off-white — a blue
   black next to it would read as a different product. Money colors lift so
   they stay legible on a dark ground. */
:root[data-theme='dark'] {
  color-scheme: dark;
  --paper:           #14120F;
  --surface:         #1C1A16;
  --surface-sunken:  #24211B;
  --surface-inverse: #F2EFE9;
  --hairline:        #2E2A23;
  --hairline-strong: #423B31;
  --ink:             #F2EFE9;
  --ink-secondary:   #ABA396;
  --ink-quiet:       #7E776B;
  --ink-disabled:    #5B554C;
  --ink-on-accent:   #14120F;
  --ink-on-inverse:  #1B1A17;
  --accent:          var(--accent-d);
  --profit:          #35C97F;
  --loss:            #FF6F5B;
  --even:            #ABA396;
  --shadow-card:     0 1px 1px rgba(0, 0, 0, .3), 0 3px 10px -2px rgba(0, 0, 0, .4);
  --shadow-raised:   0 2px 4px rgba(0, 0, 0, .35), 0 12px 28px -8px rgba(0, 0, 0, .55);
  --shadow-sheet:    0 -8px 44px -10px rgba(0, 0, 0, .6);
}

/* Follow the system only when the person hasn't chosen for themselves. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    color-scheme: dark;
    --paper:           #14120F;
    --surface:         #1C1A16;
    --surface-sunken:  #24211B;
    --surface-inverse: #F2EFE9;
    --hairline:        #2E2A23;
    --hairline-strong: #423B31;
    --ink:             #F2EFE9;
    --ink-secondary:   #ABA396;
    --ink-quiet:       #7E776B;
    --ink-disabled:    #5B554C;
    --ink-on-accent:   #14120F;
    --ink-on-inverse:  #1B1A17;
    --accent:          var(--accent-d);
    --profit:          #35C97F;
    --loss:            #FF6F5B;
    --even:            #ABA396;
    --shadow-card:     0 1px 1px rgba(0, 0, 0, .3), 0 3px 10px -2px rgba(0, 0, 0, .4);
    --shadow-raised:   0 2px 4px rgba(0, 0, 0, .35), 0 12px 28px -8px rgba(0, 0, 0, .55);
    --shadow-sheet:    0 -8px 44px -10px rgba(0, 0, 0, .6);
  }
}

/* ─── app chrome measurements (not part of the design system) ─────────── */

:root {
  color-scheme: light;
  --tabs-h: 60px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --sidebar-w: 244px;
}

/* ─── reset ───────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  /* Reserve the scrollbar gutter permanently. Without it a tall tab shows a
     classic scrollbar and a short one doesn't, the viewport changes width
     between them, and anything centred jumps sideways on every tab change.
     Root cause of the drifting title — fixed here rather than per-element. */
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* No overscroll-behavior here on purpose: it would disable pull-to-refresh,
     and on a phone that gesture is the one thing everyone already knows how to
     do when they suspect the screen is stale. The page updates by itself, but
     the pull should still work. */
}

h1, h2, h3, p, figure { margin: 0; }
input, button, textarea, select { font: inherit; color: inherit; }
ul { margin: 0; padding: 0; list-style: none; }
::selection { background: var(--accent-wash); color: var(--ink); }
[hidden] { display: none !important; }

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-small);
}

@keyframes pl-blink { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }

/* ─── type helpers ────────────────────────────────────────────────────── */

.micro {
  font-size: var(--text-micro);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-quiet);
}

/* Every money figure in the product goes through .money. */
.money, .num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}
.money {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--track-money);
}

.up   { color: var(--profit); }
.down { color: var(--loss); }
.even { color: var(--even); }

.muted { color: var(--ink-secondary); }
.dim   { color: var(--ink-quiet); }

/* ─── shell ───────────────────────────────────────────────────────────── */

.app { min-height: 100dvh; display: flex; flex-direction: column; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: var(--space-medium);
  padding: var(--space-medium) var(--space-base);
  background: color-mix(in oklab, var(--paper) 88%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: var(--border-hair) solid var(--hairline);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-small);
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-heading);
  letter-spacing: var(--track-tight);
  color: var(--ink);
}
.mark {
  width: 24px;
  height: 24px;
  flex: none;
  display: inline-block;
  background: currentColor;
  -webkit-mask: url('icons/mark.svg') center / contain no-repeat;
  mask: url('icons/mark.svg') center / contain no-repeat;
}
.topbar-spacer { flex: 1; }

/* Which ledger you're in — top right on a phone. */
.scope-name {
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
  color: var(--ink-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 48vw;
}

/* The centred app name on desktop. Hidden on a phone, where the top bar
   already carries it. */
.masthead { display: none; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-tiny);
  height: 26px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  border: var(--border-hair) solid var(--hairline);
  font-size: var(--text-micro);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-secondary);
  white-space: nowrap;
}
/* Live is the accent, never green — green is spoken for. */
.chip.is-live {
  background: var(--accent-wash);
  border-color: var(--accent-edge);
  color: var(--accent);
}
.chip.is-admin {
  background: var(--surface-inverse);
  border-color: var(--surface-inverse);
  color: var(--ink-on-inverse);
}
/* Offline is a degraded state, so it reads as greyed-out rather than alarming.
   It can't be red — red means money. */
.chip.is-offline {
  background: var(--surface-sunken);
  border-color: var(--hairline-strong);
  color: var(--ink-secondary);
}
.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.chip.is-live .dot { animation: pl-blink 2s var(--ease-out) infinite; }

.content {
  flex: 1;
  width: 100%;
  max-width: 62rem;
  margin: 0 auto;
  padding: var(--space-large) var(--space-base);
  padding-bottom: calc(var(--tabs-h) + var(--tap-thumb) + var(--safe-b) + var(--space-xlarge));
}

.stack > * + * { margin-top: var(--space-large); }

/* ─── the waterline ───────────────────────────────────────────────────── */

.waterline {
  height: 1px;
  background: var(--hairline-strong);
  margin: var(--space-medium) 0;
}

/* ─── hero ────────────────────────────────────────────────────────────── */

.hero { padding-top: var(--space-tiny); }

.hero-value {
  display: block;
  margin: var(--space-small) 0 var(--space-medium);
  font-family: var(--font-money);
  font-weight: var(--weight-bold);
  font-size: clamp(var(--text-hero), 12vw, var(--text-jumbo));
  line-height: var(--leading-tight);
  letter-spacing: var(--track-money);
  font-variant-numeric: tabular-nums;
}

.hero-sub {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-tiny) var(--space-base);
  font-size: var(--text-caption);
  color: var(--ink-secondary);
}
.hero-sub b {
  font-weight: var(--weight-semibold);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* ─── cards ───────────────────────────────────────────────────────────── */

.card {
  background: var(--surface);
  border: var(--border-hair) solid var(--hairline);
  border-radius: var(--radius-large);
  box-shadow: var(--shadow-card);
  padding: var(--space-base);
  min-width: 0;
}
.card.flush { padding: 0; overflow: hidden; }

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-medium);
  margin-bottom: var(--space-medium);
}

/* ─── stat tiles ──────────────────────────────────────────────────────── */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-medium);
}
.stat {
  background: var(--surface);
  border: var(--border-hair) solid var(--hairline);
  border-radius: var(--radius-xlarge);
  box-shadow: var(--shadow-card);
  padding: var(--space-base);
}
.stat-value {
  display: block;
  margin-top: var(--space-tiny);
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-display);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-money);
  font-variant-numeric: tabular-nums;
}
.stat-note { font-size: var(--text-caption); color: var(--ink-quiet); }

/* ─── charts ──────────────────────────────────────────────────────────── */

/* max-width matters: charts.js writes an explicit pixel width onto the <svg>,
   so without this a shrinking container can never pull the chart back in —
   the SVG holds the column open and the ResizeObserver never sees a change. */
.chart { width: 100%; max-width: 100%; display: block; overflow: visible; }
.chart-axis { stroke: var(--hairline-strong); stroke-width: 1; }
.chart-grid { stroke: var(--hairline); stroke-width: 1; }
.chart-label {
  font-family: var(--font-ui);
  font-size: 10px;
  font-weight: var(--weight-semibold);
  letter-spacing: .04em;
  fill: var(--ink-quiet);
}
.chart-line { fill: none; stroke-width: 2.25; stroke-linejoin: round; stroke-linecap: round; }
.chart-dot { stroke: var(--surface); stroke-width: 2; }
.chart-empty {
  display: grid;
  place-items: center;
  min-height: 8rem;
  color: var(--ink-quiet);
  font-size: var(--text-caption);
  text-align: center;
  padding: var(--space-base);
}

/* ─── diverging bars ──────────────────────────────────────────────────── */

.bars { display: flex; flex-direction: column; }
.bar-row {
  display: grid;
  grid-template-columns: minmax(6rem, 1.4fr) minmax(3.5rem, 1fr) auto;
  align-items: center;
  gap: var(--space-medium);
  padding: var(--space-medium) 0;
  border-bottom: var(--border-hair) solid var(--hairline);
}
.bar-row:last-child { border-bottom: 0; }

.bar-name {
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bar-sub {
  display: block;
  font-size: var(--text-caption);
  color: var(--ink-quiet);
  font-weight: var(--weight-regular);
  overflow: hidden;
  text-overflow: ellipsis;
}

.bar-track { position: relative; height: 24px; display: flex; align-items: center; }
.bar-track::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--hairline-strong);
}
.bar-fill {
  position: absolute;
  height: 10px;
  border-radius: 2px;
  top: 50%;
  transform: translateY(-50%);
}
.bar-fill.is-up { left: 50%; background: var(--profit); }
.bar-fill.is-down { right: 50%; background: var(--loss); }

.bar-value {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-heading);
  letter-spacing: var(--track-money);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* ─── list rows ───────────────────────────────────────────────────────── */

.rows { display: flex; flex-direction: column; }

.row {
  display: flex;
  align-items: center;
  gap: var(--space-medium);
  width: 100%;
  min-height: var(--tap-comfortable);
  padding: var(--space-medium) var(--space-base);
  background: var(--surface);
  border: 0;
  border-bottom: var(--border-hair) solid var(--hairline);
  text-align: left;
  cursor: pointer;
  transition: background var(--speed-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.row:last-child { border-bottom: 0; }
.row:hover { background: var(--surface-sunken); }
.row:active { background: var(--surface-sunken); }

.row-main { flex: 1; min-width: 0; }
.row-title {
  font-weight: var(--weight-semibold);
  font-size: var(--text-label);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row-sub {
  font-size: var(--text-caption);
  color: var(--ink-quiet);
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row-value {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-heading);
  letter-spacing: var(--track-money);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.row-chevron { color: var(--ink-quiet); flex: none; display: flex; }

/* ─── player card (live night) ────────────────────────────────────────── */

.player {
  background: var(--surface);
  border-bottom: var(--border-hair) solid var(--hairline);
  padding: var(--space-base);
}
.player:last-child { border-bottom: 0; }

.player-top {
  display: flex;
  align-items: baseline;
  gap: var(--space-medium);
  margin-bottom: var(--space-hair);
}
.player-name {
  flex: 1;
  min-width: 0;
  font-weight: var(--weight-semibold);
  font-size: var(--text-heading);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.player-net {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-title);
  letter-spacing: var(--track-money);
  font-variant-numeric: tabular-nums;
}
.player-meta {
  font-size: var(--text-caption);
  color: var(--ink-secondary);
  font-variant-numeric: tabular-nums;
}
.player-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-small);
  margin-top: var(--space-medium);
}
.player.is-you { box-shadow: inset 3px 0 0 var(--accent); }

/* ─── balance guard ───────────────────────────────────────────────────── */

.guard { display: grid; gap: var(--space-small); }
.guard-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: var(--text-caption);
  color: var(--ink-secondary);
}
.guard-line b {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.guard-total { font-size: var(--text-label); }
.guard-total b { font-size: var(--text-title); letter-spacing: var(--track-money); }
.guard.is-off .guard-total b { color: var(--loss); }
.guard.is-ok .guard-total b { color: var(--profit); }

/* ─── buttons ─────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-small);
  min-height: var(--tap-comfortable);
  padding: 0 var(--space-large);
  background: var(--surface);
  color: var(--ink);
  border: var(--border-thick) solid var(--hairline-strong);
  border-radius: var(--radius-medium);
  font-size: var(--text-label);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--track-tight);
  cursor: pointer;
  transition:
    background var(--speed-fast) var(--ease-out),
    border-color var(--speed-fast) var(--ease-out),
    transform var(--speed-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { background: var(--surface-sunken); border-color: var(--ink-quiet); }
.btn:active { transform: scale(.98); }
.btn:disabled {
  background: var(--surface-sunken);
  border-color: var(--hairline);
  color: var(--ink-disabled);
  cursor: not-allowed;
  transform: none;
}

.btn-primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--ink-on-accent);
  box-shadow: 0 1px 2px rgba(27, 26, 23, .12);
}
.btn-primary:hover { background: var(--accent-hover); border-color: transparent; }
.btn-primary:active { background: var(--accent-press); }
.btn-primary:disabled { background: var(--surface-sunken); border-color: var(--hairline); box-shadow: none; }

.btn-danger { background: var(--loss-wash); border-color: transparent; color: var(--loss); }
.btn-danger:hover { background: var(--loss-wash); border-color: var(--loss); }

.btn-sm {
  min-height: 36px;
  padding: 0 14px;
  font-size: var(--text-label);
  border-width: var(--border-hair);
  border-radius: var(--radius-small);
}
.btn-block { width: 100%; }
.btn-ghost { border-color: transparent; background: transparent; }
.btn-ghost:hover { background: var(--surface-sunken); border-color: transparent; }

.btn-row { display: flex; gap: var(--space-small); flex-wrap: wrap; }

/* ─── dock: primary action + tabs ─────────────────────────────────────── */

.dock {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 25;
  background: color-mix(in oklab, var(--paper) 90%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-top: var(--border-hair) solid var(--hairline);
  padding-bottom: var(--safe-b);
}
/* Desktop only. A phone gets no floating action at all: not at the bottom,
   where the tab bar already owns the scarcest space on the screen, and not in
   the header either. The Tonight tab carries both actions in context — "Start
   tonight's game" in its empty card, "Add a player" under the players list —
   which is where they read as part of the page rather than furniture. */
.dock-action {
  padding: var(--space-medium) var(--space-base);
  display: none;
  justify-content: center;
}
/* The one-handed, one-eye-open action. */
.dock-action .btn {
  width: 100%;
  max-width: var(--phone-max);
  min-height: var(--tap-thumb);
  border-radius: var(--radius-large);
  font-size: 17px;
  box-shadow: 0 2px 6px rgba(27, 26, 23, .14);
}

.tabs {
  display: flex;
  height: var(--tabs-h);
  border-top: var(--border-hair) solid var(--hairline);
}
.tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--ink-quiet);
  font-size: var(--text-micro);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  position: relative;
  -webkit-tap-highlight-color: transparent;
}
.tab[aria-selected='true'] { color: var(--accent); }
.tab[aria-selected='true']::after {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 28px;
  height: 2px;
  background: var(--accent);
  border-radius: 0 0 2px 2px;
}
.tab svg { width: 20px; height: 20px; }

/* ─── forms ───────────────────────────────────────────────────────────── */

.field { display: block; }
.field + .field { margin-top: var(--space-base); }
.field > .micro { display: block; margin-bottom: var(--space-small); }

.input {
  width: 100%;
  min-height: var(--tap-comfortable);
  padding: 0 var(--space-base);
  background: var(--surface);
  border: var(--border-thick) solid var(--hairline-strong);
  border-radius: var(--radius-medium);
  color: var(--ink);
  font-size: var(--text-body);
  font-variant-numeric: tabular-nums;
  appearance: none;
  transition:
    border-color var(--speed-fast) var(--ease-out),
    box-shadow var(--speed-fast) var(--ease-out);
}
.input:focus-visible { border-color: var(--accent); box-shadow: var(--focus-ring); }
.input::placeholder { color: var(--ink-quiet); }
textarea.input { min-height: 5rem; padding: var(--space-medium) var(--space-base); resize: vertical; }

/* The amount you are typing is the biggest thing on the sheet. */
.input-money {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-display);
  letter-spacing: var(--track-money);
  min-height: 68px;
  border-radius: var(--radius-large);
  background: var(--surface-sunken);
  padding-left: 2.1em;
}
.money-wrap { position: relative; }
.money-wrap::before {
  content: '$';
  position: absolute;
  left: var(--space-base);
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-display);
  color: var(--ink-quiet);
  pointer-events: none;
}

.field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-medium); }

.hint { font-size: var(--text-caption); color: var(--ink-secondary); margin-top: var(--space-small); }
.error { font-size: var(--text-caption); color: var(--loss); margin-top: var(--space-small); }

/* quick picks */
.picks { display: flex; gap: var(--space-small); flex-wrap: wrap; margin-top: var(--space-small); }
.pick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  padding: 0 14px;
  background: var(--accent-wash);
  color: var(--accent);
  border: var(--border-hair) solid var(--accent-edge);
  border-radius: var(--radius-small);
  font-family: var(--font-money);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-label);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  transition:
    background var(--speed-fast) var(--ease-out),
    border-color var(--speed-fast) var(--ease-out);
}
.pick:hover { background: color-mix(in oklab, var(--accent) 16%, #FFF); }
.pick[aria-pressed='true'] { background: var(--accent); border-color: var(--accent); color: var(--ink-on-accent); }

/* segmented control */
.segmented {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  background: var(--surface-sunken);
  border-radius: var(--radius-medium);
}
.segmented button {
  border: 0;
  background: none;
  border-radius: 9px;
  min-height: 40px;
  padding: 0 var(--space-base);
  font-size: var(--text-label);
  font-weight: var(--weight-semibold);
  color: var(--ink-secondary);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--speed-fast) var(--ease-out), color var(--speed-fast) var(--ease-out);
}
.segmented button[aria-pressed='true'] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-card);
}

/* ─── sheet ───────────────────────────────────────────────────────────── */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(27, 26, 23, .34);
  animation: fade var(--speed-base) var(--ease-out);
  touch-action: none;              /* nothing behind a sheet moves */
  padding-bottom: var(--kb, 0px);  /* the keyboard's height on the web; 0 in the app */
}
.sheet-panel { touch-action: pan-y; }
@keyframes fade { from { opacity: 0 } }

.sheet-panel {
  width: 100%;
  max-width: 34rem;
  max-height: calc(92dvh - var(--kb, 0px));
  overflow-y: auto;
  /* Contain the scroll chaining so flicking inside a sheet doesn't drag the
     page behind it — but this stays off the body so pull-to-refresh survives. */
  overscroll-behavior: contain;
  background: var(--paper);
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  box-shadow: var(--shadow-sheet);
  padding: var(--space-large) var(--space-base) calc(var(--space-large) + var(--safe-b));
  animation: rise 220ms var(--ease-out);
}
@keyframes rise { from { transform: translateY(3%) } }

.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-medium);
  margin-bottom: var(--space-large);
}
.sheet-head h2 {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-title);
  letter-spacing: var(--track-tight);
  line-height: var(--leading-snug);
}

.sheet-actions { display: flex; gap: var(--space-small); margin-top: var(--space-large); }
.sheet-actions .btn { flex: 1; }

.icon-btn {
  display: grid;
  place-items: center;
  width: var(--tap-minimum);
  height: var(--tap-minimum);
  flex: none;
  border-radius: var(--radius-pill);
  border: var(--border-hair) solid var(--hairline);
  background: var(--surface);
  color: var(--ink-secondary);
  cursor: pointer;
}
.icon-btn:hover { background: var(--surface-sunken); }

/* ─── PIN pad ─────────────────────────────────────────────────────────── */

.pin-display {
  display: flex;
  justify-content: center;
  gap: var(--space-medium);
  margin: var(--space-large) 0 var(--space-xlarge);
}
.pin-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: var(--border-thick) solid var(--hairline-strong);
  background: transparent;
  transition: background var(--speed-fast) var(--ease-out), border-color var(--speed-fast) var(--ease-out);
}
.pin-dot.is-filled { background: var(--accent); border-color: var(--accent); }

.keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-medium); }
.key {
  min-height: var(--tap-thumb);
  border: var(--border-hair) solid var(--hairline);
  border-radius: var(--radius-medium);
  background: var(--surface);
  font-family: var(--font-money);
  font-size: var(--text-title);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background var(--speed-fast) var(--ease-out), transform var(--speed-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.key:active { background: var(--surface-sunken); transform: scale(.97); }
.key:disabled { color: var(--ink-disabled); }
.key.is-blank { border-color: transparent; background: none; cursor: default; }

/* ─── banners & notices ───────────────────────────────────────────────── */

.banner {
  padding: var(--space-medium) var(--space-base);
  font-size: var(--text-caption);
  text-align: center;
  background: var(--surface-sunken);
  color: var(--ink-secondary);
  border-bottom: var(--border-hair) solid var(--hairline);
}
.banner strong { color: var(--ink); }
.banner button {
  border: 0;
  background: none;
  text-decoration: underline;
  cursor: pointer;
  font-weight: var(--weight-semibold);
  color: var(--accent);
}

.notice {
  border-radius: var(--radius-medium);
  padding: var(--space-medium) var(--space-base);
  font-size: var(--text-caption);
  line-height: 1.5;
}
.notice.is-warn { background: var(--loss-wash); color: color-mix(in oklab, var(--loss) 80%, #000); }
.notice.is-ok { background: var(--profit-wash); color: color-mix(in oklab, var(--profit) 80%, #000); }
.notice.is-quiet { background: var(--surface-sunken); color: var(--ink-secondary); }

/* ─── empty states ────────────────────────────────────────────────────── */

.empty { text-align: center; padding: var(--space-huge) var(--space-base); color: var(--ink-secondary); }
.empty h3 {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-title);
  letter-spacing: var(--track-tight);
  color: var(--ink);
  margin-bottom: var(--space-small);
}
.empty p { font-size: var(--text-caption); max-width: 34ch; margin: 0 auto var(--space-large); line-height: var(--leading-loose); }

/* ─── leaderboard layout ────────────────────────────────────────────────
   Phone: one column, everything stacked.
   Desktop: two, so the page fills its width instead of running a single
   narrow ribbon down the left with dead space beside it. */

.board-stats,
.board-archive {
  display: grid;
  gap: var(--space-base);
  min-width: 0;
}
/* Standings and running totals are a matched pair — same row, same height, so
   neither reads as the more important of the two. */
.board-stats { align-items: stretch; }
.board-panel { display: flex; flex-direction: column; min-width: 0; }
.board-panel .bars,
.board-panel .spark-grid { flex: 1; }
.board-head { padding: var(--space-base) var(--space-base) var(--space-small); margin: 0; }

.hero-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-medium);
  flex-wrap: wrap;
  margin: var(--space-small) 0 var(--space-medium);
}
.hero-heading { font-size: var(--text-display); letter-spacing: var(--track-money); }

/* ── running totals: one small chart per player ───────────────────────── */

.spark-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: var(--space-small);
}
.spark-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-tiny);
  padding: var(--space-medium);
  background: var(--surface-sunken);
  border: var(--border-hair) solid transparent;
  border-radius: var(--radius-medium);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--speed-fast) var(--ease-out),
              background var(--speed-fast) var(--ease-out);
  min-width: 0;
}
.spark-card:hover { border-color: var(--hairline-strong); }
.spark-card.is-on { border-color: var(--accent); background: var(--accent-wash); }

.spark-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-small); }
.spark-name {
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
  color: var(--ink-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.spark-card.is-on .spark-name { color: var(--accent); }
.spark-net {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-label);
  letter-spacing: var(--track-money);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.spark-plot { display: block; width: 100%; }
.spark { display: block; width: 100%; }

/* one player, full panel */
.focus-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-medium);
  margin-bottom: var(--space-small);
}
.focus-name {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-title);
  letter-spacing: var(--track-tight);
}
.focus-net {
  font-family: var(--font-money);
  font-weight: var(--weight-bold);
  font-size: var(--text-display);
  letter-spacing: var(--track-money);
  font-variant-numeric: tabular-nums;
}
/* ── the figures under the running totals ─────────────────────────────────
   A label over its number, repeated across the panel. Deliberately not a flex
   run of "4 nights · 100% won" sentences: ten of those read as a paragraph,
   whereas a grid lets you find "avg buy-in" by position and compare two
   columns down the page. Money keeps its own face and tabular figures so the
   numbers line up regardless of digit count.

   These are the design system's .stat tiles, only smaller and quieter: there
   are eight or ten of them rather than a KPI row of three, and they sit inside
   a card directly under the sparkline cards, so they take the same sunken
   ground those do instead of floating a second raised surface on a raised one. */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: var(--space-small);
  margin: var(--space-base) 0 0;
  padding-top: var(--space-base);
  border-top: var(--border-hair) solid var(--hairline);
}
.stat-grid .stat {
  min-width: 0;
  padding: var(--space-medium);
  background: var(--surface-sunken);
  border-color: transparent;
  border-radius: var(--radius-medium);
  box-shadow: none;
}
.stat-grid dt { margin-bottom: var(--space-tiny); }
.stat-grid .stat-value {
  margin: 0;
  font-size: var(--text-label);
  color: var(--ink);
  white-space: nowrap;
}
/* .up / .down carry their own color and must win — a figure that is a profit
   or a loss says so here exactly as it does in the standings. */
.stat-grid .stat-value.up   { color: var(--profit); }
.stat-grid .stat-value.down { color: var(--loss); }

/* a selected standings row */
.bar-row[data-player] {
  background: none;
  border-left: 0;
  border-right: 0;
  border-top: 0;
  width: 100%;
  text-align: left;
  cursor: pointer;
  padding-left: var(--space-small);
  padding-right: var(--space-small);
  border-radius: var(--radius-small);
}
.bar-row[data-player]:hover { background: var(--surface-sunken); }
.bar-row[data-player].is-on { background: var(--accent-wash); }
.bar-row[data-player].is-on .bar-name { font-weight: var(--weight-bold); color: var(--accent); }

.series-row {
  border-left: 3px solid transparent;
  padding-left: calc(var(--space-base) - 3px);
}
.series-row.is-on {
  border-left-color: var(--accent);
  background: var(--accent-wash);
}
.series-row.is-on .row-title { font-weight: var(--weight-bold); color: var(--accent); }
.series-row.is-on:hover { background: var(--accent-wash); }
.series-row .row-chevron[data-act] {
  font-size: var(--text-heading);
  line-height: 1;
  padding: 0 var(--space-small);
  border-radius: var(--radius-small);
}
.series-row .row-chevron[data-act]:hover { background: var(--surface); color: var(--ink); }

/* A picked night is marked the same way a picked series is — accent wash and a
   rule down the left — because they do the same thing: choose what the
   standings and the running totals are counting. */
.night.is-on {
  background: var(--accent-wash);
  border-left: 3px solid var(--accent);
}
.night.is-on > summary { background: transparent; padding-left: calc(var(--space-base) - 3px); }
.night.is-on[open] > summary { background: transparent; }
.night.is-on > summary .row-title { color: var(--accent); }
.night.is-on > summary:hover { background: transparent; }
.night.is-on .night-body { padding-left: calc(var(--space-base) - 3px); }

/* ─── night history ───────────────────────────────────────────────────── */

.night { border-bottom: var(--border-hair) solid var(--hairline); }
.night:last-child { border-bottom: 0; }
.night > summary {
  list-style: none;
  cursor: pointer;
  padding: var(--space-medium) var(--space-base);
  display: flex;
  align-items: center;
  gap: var(--space-medium);
  min-height: var(--tap-comfortable);
}
/* A night row carries a big figure on the right, so the date and the caption
   go on their own lines with real air between them. They're spans, and spans
   are inline — without display:block the caption sits beside the date and the
   margin does nothing at all. */
.night > summary .row-title,
.night > summary .row-sub { display: block; }
.night > summary .row-sub { margin-top: var(--space-small); white-space: normal; }
.night > summary .row-value { align-self: flex-start; }
.night > summary { align-items: flex-start; }

.night > summary::-webkit-details-marker { display: none; }
.night > summary:hover { background: var(--surface-sunken); }
.night[open] > summary { background: var(--surface-sunken); }

/* The one part of the row that opens it. Everything else picks the night, so
   this needs a tap target of its own rather than inheriting the row's — and it
   has to LOOK like a control. A bare chevron sitting where every other list row
   in the app puts a decorative one reads as decoration, and nobody presses it.
   So it borrows the button's own border, ground and radius. */
.night-toggle {
  align-self: flex-start;
  display: grid;
  place-items: center;
  width: var(--tap-minimum);
  height: var(--tap-minimum);
  margin-top: calc(var(--space-tiny) * -1);
  background: var(--surface);
  color: var(--ink);
  border: var(--border-thick) solid var(--hairline-strong);
  border-radius: var(--radius-medium);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--speed-fast) var(--ease-out),
              border-color var(--speed-fast) var(--ease-out),
              transform var(--speed-fast) var(--ease-out);
}
.night-toggle:hover { background: var(--surface-sunken); border-color: var(--ink-quiet); }
.night-toggle:active { transform: scale(.94); }
/* The arrow turns, not the button — .btn:active already owns `transform` here,
   and one of the two would cancel the other. */
.night-toggle svg { transition: transform var(--speed-base) var(--ease-out); }
.night[open] > summary .night-toggle svg { transform: rotate(90deg); }
/* Top padding, not --space-small: the summary row carries a large tabular-nums
   figure on the right, and 4px under it read as cramped against the stats
   that follow. */
.night-body { padding: var(--space-base) var(--space-base) var(--space-large); }

.tbl { width: 100%; border-collapse: collapse; font-size: var(--text-caption); }
.tbl th {
  text-align: right;
  font-size: var(--text-micro);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-quiet);
  padding: var(--space-small) 0;
  border-bottom: var(--border-hair) solid var(--hairline-strong);
}
.tbl th:first-child, .tbl td:first-child { text-align: left; }
.tbl td {
  text-align: right;
  padding: var(--space-small) 0;
  border-bottom: var(--border-hair) solid var(--hairline);
  font-variant-numeric: tabular-nums;
}
.tbl tr:last-child td { border-bottom: 0; }
.tbl td.net { font-family: var(--font-money); font-weight: var(--weight-semibold); }

.transfers { display: grid; gap: var(--space-small); margin-top: var(--space-base); }
.transfer {
  display: flex;
  align-items: center;
  gap: var(--space-small);
  font-size: var(--text-caption);
  padding: var(--space-small) var(--space-medium);
  background: var(--surface-sunken);
  border-radius: var(--radius-small);
  font-variant-numeric: tabular-nums;
}
.transfer b { font-weight: var(--weight-semibold); }
.transfer .arrow { color: var(--ink-quiet); }

/* ─── pull to refresh (touch devices only, added by js/pull-refresh.js) ── */

#ptr {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 40;
  transform: translate3d(-50%, 0, 0);
  opacity: 0;
  pointer-events: none;
  padding-top: var(--space-small);
}
#ptr.is-animating { transition: transform 220ms var(--ease-out), opacity 220ms var(--ease-out); }

.ptr-circle {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--surface);
  border: var(--border-hair) solid var(--hairline);
  box-shadow: var(--shadow-raised);
  color: var(--ink-quiet);
}
.ptr-arrow {
  width: 19px;
  height: 19px;
  transition: transform 180ms var(--ease-out);
}

/* Past the trigger point: the arrow flips and it goes accent, so you can feel
   the commit point without reading anything. */
#ptr.is-ready .ptr-circle { color: var(--accent); border-color: var(--accent-edge); }
#ptr.is-ready .ptr-arrow { transform: rotate(180deg); }

/* Committed: the arrow gives way to a spinning ring. The animated mark is used
   everywhere else the app loads, but not here — at 38px across, its cut and its
   outlined half are too fine to read, and it just looks like a smudge. */
#ptr.is-loading .ptr-arrow { display: none; }
#ptr.is-loading .ptr-circle {
  border-color: var(--hairline);
  border-top-color: var(--accent);
  border-width: 2px;
  animation: ptrSpin .7s linear infinite;
}
@keyframes ptrSpin { to { transform: rotate(360deg); } }

/* ─── loading ─────────────────────────────────────────────────────────── */

.boot {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-xlarge);
  color: var(--ink-quiet);
  font-size: var(--text-caption);
}

/* Letterspaced caps, the design sheet's treatment — set in the app's own face
   rather than the sheet's serif, which the app doesn't load. */
.boot-word {
  font-family: var(--font-money);
  font-weight: var(--weight-medium);
  font-size: var(--text-heading);
  letter-spacing: .34em;
  text-indent: .34em;          /* puts the trailing letterspace back on the left */
  text-transform: uppercase;
  color: var(--ink);
}

/* A hairline that travels rather than fills: it says "working", not "62%". */
.boot-hair {
  width: 180px;
  height: 2px;
  background: var(--hairline);
  overflow: hidden;
}
.boot-hair span {
  display: block;
  width: 33%;
  height: 100%;
  background: var(--ink);
  animation: plHair 1.1s cubic-bezier(.45, 0, .55, 1) infinite;
}
@keyframes plHair {
  from { transform: translateX(-105%); }
  to   { transform: translateX(305%); }
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* ─── the loading mark ─────────────────────────────────────────────────────
   Four directions from the Claude Design project "Poker Ledger Loader", drawn
   on the app's own spade (js/loader.js builds the shape; this drives it).
   Indeterminate, one 4s loop, monochrome. The keyframes are namespaced `pl…`
   on purpose — a bare `spin` is exactly the kind of name a stylesheet this size
   ends up with twice.

   The air either side of the bar is PAINTED in --pl-ground, not cut out of the
   mark, so --pl-ground has to match whatever is actually behind the loader or
   the gap reads as a stripe. Every placement sets it. */

.pl-loader {
  /* Short on purpose: a loading mark that takes four seconds to make its point
     reads as a stall. One pass of any direction lands well inside the wait. */
  --pl-loop: 2.2s;
  --pl-ink: var(--ink);
  --pl-ground: var(--paper);
  width: var(--pl-size, 48px);
  height: var(--pl-size, 48px);
  display: block;
  flex: none;
  /* No `overflow: visible` — the bar is deliberately longer than the mark and
     the viewBox is what trims it to a corner-to-corner strike, exactly as it
     is trimmed in icons/mark.svg. Let it overflow and it runs off across
     whatever is beside the loader. */
}

.pl-loader .pl-fill  { fill: var(--pl-ink); }
.pl-loader .pl-out   { fill: none; stroke: var(--pl-ink); stroke-width: 5.5; stroke-linejoin: round; }
/* 16 wide against a 6-wide blade leaves 5 units of air either side — the same
   proportions the static mark in icons/mark.svg is cut to. */
.pl-loader .pl-air   { stroke: var(--pl-ground); stroke-width: 16; }
.pl-loader .pl-blade { stroke: var(--pl-ink); stroke-width: 6; }

/* Everything that moves turns about the middle of the mark. */
.pl-loader .pl-breathe,
.pl-loader .pl-bar,
.pl-loader .pl-upper,
.pl-loader .pl-lower,
.pl-loader .pl-clip-fill,
.pl-loader .pl-clip-out {
  transform-box: view-box;
  transform-origin: 50px 50px;
}

/* Every direction is the same markup — see js/loader.js. What follows is the
   whole difference between them: which parts move, and which merely sit at a
   fixed angle. That is what lets one be picked at random, or swapped on a mark
   already on screen, by writing `data-loader` and nothing else. */

/* ── sweep: the cut travels across and solidifies everything behind it ── */
[data-loader='sweep'] .pl-clip-fill,
[data-loader='sweep'] .pl-clip-out,
[data-loader='sweep'] .pl-bar {
  animation: plSweep var(--pl-loop) cubic-bezier(.65, 0, .35, 1) infinite;
}
[data-loader='sweep'] .pl-breathe { animation: plBreathe var(--pl-loop) ease-in-out infinite; }

@keyframes plSweep {
  0%, 8%    { transform: rotate(-45deg) translateY(-52px); }
  44%, 56%  { transform: rotate(-45deg) translateY(52px); }
  92%, 100% { transform: rotate(-45deg) translateY(-52px); }
}
@keyframes plBreathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}

/* ── pivot: the cut sweeps round like a clock hand, four ticks a loop ── */
[data-loader='pivot'] .pl-clip-fill,
[data-loader='pivot'] .pl-clip-out,
[data-loader='pivot'] .pl-bar {
  animation: plPivot var(--pl-loop) cubic-bezier(.65, 0, .35, 1) infinite;
}
[data-loader='pivot'] .pl-breathe { animation: plTick var(--pl-loop) ease-in-out infinite; }

/* Ends a full turn from where it starts, so the loop has no seam — which is
   what makes this one safe for a wait of unknown length. */
@keyframes plPivot {
  0%        { transform: rotate(-45deg); }
  22%, 26%  { transform: rotate(45deg); }
  47%, 51%  { transform: rotate(135deg); }
  72%, 76%  { transform: rotate(225deg); }
  97%, 100% { transform: rotate(315deg); }
}
@keyframes plTick {
  0%, 26%, 51%, 76%, 100% { transform: scale(1); }
  12%, 37%, 62%, 87%      { transform: scale(1.03); }
}

/* ── snap: the halves shear apart around the blade and slam back ──
   The split doesn't travel here, so the two clip rects just sit rotated onto
   the diagonal and the halves themselves are what move. */
[data-loader='snap'] .pl-clip-fill,
[data-loader='snap'] .pl-clip-out { transform: rotate(-45deg); }
[data-loader='snap'] .pl-upper { animation: plSnapUp  var(--pl-loop) linear infinite; }
[data-loader='snap'] .pl-lower { animation: plSnapLow var(--pl-loop) linear infinite; }
[data-loader='snap'] .pl-bar   { animation: plSnapBar var(--pl-loop) linear infinite; }

@keyframes plSnapUp {
  0%   { transform: translate(0, 0) rotate(0deg);          animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  12%  { transform: translate(2.2px, 2.2px) rotate(1.6deg); animation-timing-function: cubic-bezier(.12, .9, .2, 1); }
  40%  { transform: translate(-10.6px, -10.6px) rotate(-7deg); }
  54%  { transform: translate(-10.6px, -10.6px) rotate(-7deg); animation-timing-function: cubic-bezier(.78, 0, .18, 1); }
  72%  { transform: translate(0, 0) rotate(0deg);          animation-timing-function: cubic-bezier(.3, 0, .5, 1); }
  80%  { transform: translate(1.4px, 1.4px) rotate(.9deg); animation-timing-function: cubic-bezier(.3, 0, .3, 1); }
  90%, 100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes plSnapLow {
  0%   { transform: translate(0, 0) rotate(0deg);          animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  12%  { transform: translate(-2.2px, -2.2px) rotate(-1.6deg); animation-timing-function: cubic-bezier(.12, .9, .2, 1); }
  40%  { transform: translate(10.6px, 10.6px) rotate(7deg); }
  54%  { transform: translate(10.6px, 10.6px) rotate(7deg); animation-timing-function: cubic-bezier(.78, 0, .18, 1); }
  72%  { transform: translate(0, 0) rotate(0deg);          animation-timing-function: cubic-bezier(.3, 0, .5, 1); }
  80%  { transform: translate(-1.4px, -1.4px) rotate(-.9deg); animation-timing-function: cubic-bezier(.3, 0, .3, 1); }
  90%, 100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes plSnapBar {
  0%   { transform: rotate(-45deg) translateX(0) scaleX(1);      animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  12%  { transform: rotate(-45deg) translateX(-3px) scaleX(.95); animation-timing-function: cubic-bezier(.12, .9, .2, 1); }
  40%  { transform: rotate(-45deg) translateX(5px) scaleX(1.07); }
  54%  { transform: rotate(-45deg) translateX(5px) scaleX(1.07); animation-timing-function: cubic-bezier(.78, 0, .18, 1); }
  72%, 100% { transform: rotate(-45deg) translateX(0) scaleX(1); }
}

/* ── flood: outline draws, blade cuts in, ink floods the upper half ── */
[data-loader='flood'] .pl-breathe {
  animation: plFade var(--pl-loop) linear infinite,
             plDrift var(--pl-loop) linear infinite;
  transform-box: view-box;
  transform-origin: 50px 50px;
}
/* Nothing is cut in half here — the whole outline draws itself, so the lower
   half loses its clip and the blade holds still on the diagonal and arrives by
   drawing on instead of by travelling. */
[data-loader='flood'] .pl-lower { clip-path: none; }
[data-loader='flood'] .pl-out {
  stroke-dasharray: 100;
  animation: plDraw var(--pl-loop) cubic-bezier(.45, 0, .55, 1) infinite;
}
[data-loader='flood'] .pl-bar { transform: rotate(-45deg); }
[data-loader='flood'] .pl-air,
[data-loader='flood'] .pl-blade {
  stroke-dasharray: 100;
  animation: plSlash var(--pl-loop) cubic-bezier(.6, 0, .2, 1) infinite;
}
[data-loader='flood'] .pl-clip-fill {
  animation: plFlood var(--pl-loop) cubic-bezier(.5, 0, .3, 1) infinite;
}

@keyframes plDraw  { 0%, 5% { stroke-dashoffset: 100; } 42%, 100% { stroke-dashoffset: 0; } }
@keyframes plSlash { 0%, 40% { stroke-dashoffset: 100; } 57%, 100% { stroke-dashoffset: 0; } }
@keyframes plFlood {
  0%, 55%   { transform: rotate(-45deg) translateY(-58px); }
  79%, 100% { transform: rotate(-45deg) translateY(0); }
}
@keyframes plFade  { 0%, 88% { opacity: 1; } 98%, 100% { opacity: 0; } }
@keyframes plDrift { 0% { transform: scale(.965); } 100% { transform: scale(1.035); } }

/* Reduced motion gets the mark itself, at rest and whole — not a loader frozen
   mid-cut, which is what the blanket animation floor further down would leave. */
@media (prefers-reduced-motion: reduce) {
  .pl-loader * { animation: none !important; }
  .pl-loader .pl-lower { display: none; }
  .pl-loader .pl-upper { clip-path: none !important; }
  .pl-loader .pl-upper,
  .pl-loader .pl-breathe { transform: none !important; }
  .pl-loader .pl-bar { transform: rotate(-45deg) !important; }
  .pl-loader .pl-out,
  .pl-loader .pl-air,
  .pl-loader .pl-blade { stroke-dasharray: none !important; stroke-dashoffset: 0 !important; }
}

/* ─── desktop ─────────────────────────────────────────────────────────── */

@media (min-width: 56.25rem) {
  :root { --tabs-h: 0px; }

  /* The sidebar is a fixed full-height panel and the content is offset by a
     margin, so .app stays a plain column. Making it a flex row would turn the
     banner and the dock into columns beside the content. */

  .banner { margin-left: var(--sidebar-w); }

  .topbar {
    position: fixed;
    top: 0;
    left: 0;
    width: var(--sidebar-w);
    height: 100dvh;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: var(--space-base);
    padding: var(--space-large);
    border-bottom: 0;
    border-right: var(--border-hair) solid var(--hairline);
    background: var(--paper);
    backdrop-filter: none;
  }
  .topbar-spacer { display: none; }
  /* Not in the sidebar column. The nav below it is pinned at a fixed top, so a
     chip appearing here ("Live" on a night, "Offline" on a train) pushed the
     tracker name down under the nav — only ever visible on a wide screen with a
     night running, which is why it shipped. It lives in the masthead instead,
     top-right of the content, where it has its own room. */
  #status-chips {
    position: fixed;
    top: calc(var(--space-medium) + env(safe-area-inset-top, 0px));
    right: var(--space-large);
    z-index: 31;
  }

  /* Sidebar shows only which tracker you're on — no app mark, no app name.
     Those live in the masthead across the top of the content instead. */
  .topbar .brand { display: none; }
  .scope-name {
    max-width: none;
    font-family: var(--font-money);
    font-size: var(--text-heading);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--track-tight);
    color: var(--ink);
    white-space: normal;
  }

  .masthead {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-medium);
    padding: var(--space-large) var(--space-xlarge) 0;
    font-family: var(--font-money);
    font-weight: var(--weight-semibold);
    font-size: var(--text-title);
    letter-spacing: var(--track-tight);
    color: var(--ink);
  }
  .mark-lg { width: 32px; height: 32px; }

  /* ── one box, shared ──────────────────────────────────────────────────
     The masthead and the content must occupy the exact same box, or the
     centred title sits over the middle of the *leftover space* instead of the
     middle of the content and reads as off-centre. They're sized together
     here so they cannot drift apart.

     --avail is the room beside the sidebar; the column takes that, capped;
     and whatever is left over is split evenly rather than all being dumped on
     the right, which is what made a wide screen feel empty down one side. */
  .content,
  .masthead {
    --avail: calc(100% - var(--sidebar-w));
    --col: min(var(--avail), calc(var(--page-max) - var(--sidebar-w)));
    width: var(--col);
    max-width: none;
    margin-left: calc(var(--sidebar-w) + (var(--avail) - var(--col)) / 2);
    margin-right: 0;
  }

  .content {
    padding: var(--space-xlarge) var(--space-xlarge) var(--space-giant);
  }

  /* Above the sidebar, not below it — .dock's z-index makes a stacking
     context that would otherwise bury the nav and the primary button. */
  .dock {
    position: fixed;
    left: 0;
    right: auto;
    bottom: 0;
    width: var(--sidebar-w);
    z-index: 32;
    background: none;
    backdrop-filter: none;
    border-top: 0;
  }
  .dock-action { display: flex; padding: var(--space-base) var(--space-large); }
  .dock-action .btn { min-height: var(--tap-comfortable); font-size: var(--text-label); }

  .tabs {
    position: fixed;
    left: 0;
    top: 140px;
    width: var(--sidebar-w);
    height: auto;
    flex-direction: column;
    border-top: 0;
    padding: 0 var(--space-medium);
    gap: var(--space-tiny);
  }
  .tab {
    flex: none;
    flex-direction: row;
    justify-content: flex-start;
    gap: var(--space-medium);
    min-height: var(--tap-minimum);
    padding: 0 var(--space-medium);
    border-radius: var(--radius-medium);
    font-size: var(--text-label);
    letter-spacing: var(--track-normal);
    text-transform: none;
    font-weight: var(--weight-medium);
  }
  .tab:hover { background: var(--surface-sunken); }
  .tab[aria-selected='true'] {
    background: var(--accent-wash);
    color: var(--accent);
    font-weight: var(--weight-semibold);
  }
  .tab[aria-selected='true']::after { display: none; }

  /* Standings beside running totals, matched; the archive full width under
     them, series narrow and nights wide. */
  .board-stats { grid-template-columns: 1fr 1fr; gap: var(--space-large); }
  .board-archive { grid-template-columns: minmax(14rem, 1fr) 2fr; gap: var(--space-large); }

  .two-col {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: var(--space-large);
    align-items: start;
  }
  /* Grid items default to min-width:auto, which lets a wide child force the
     track past its fr share and push the page sideways. */
  .two-col > * { margin-top: 0 !important; min-width: 0; }

  .sheet { align-items: center; padding: var(--space-large); }
  .sheet-panel { border-radius: var(--radius-sheet); max-height: 88dvh; padding: var(--space-large); }

  .hero-value { font-size: var(--text-jumbo); }
}

/* ─── quality floor ───────────────────────────────────────────────────── */

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

@media print {
  .topbar, .dock, .tabs { display: none; }
  .content { margin: 0; padding: 0; }
}

/* ─── accent swatches (Settings → Appearance) ──────────────────────────── */

.swatches {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-small);
  margin-top: var(--space-small);
}
.swatch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-small);
  padding: var(--space-small) var(--space-medium);
  background: var(--surface);
  border: var(--border-thick) solid var(--hairline);
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
  color: var(--ink-secondary);
  transition: border-color var(--speed-fast) var(--ease-out),
              color var(--speed-fast) var(--ease-out);
}
.swatch:hover { border-color: var(--hairline-strong); }
.swatch[aria-pressed='true'] { border-color: var(--accent); color: var(--ink); }

.swatch-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  flex: none;
  box-shadow: inset 0 0 0 1px rgba(128, 128, 128, .25);
}
/* The dot shows its own accent's color for whichever theme is on. The values
   come in as inline custom properties from the palette, so adding a colorway
   means editing one list and nothing here. */
.swatch-dot { background: var(--dot-l); }
:root[data-theme='dark'] .swatch-dot { background: var(--dot-d); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .swatch-dot { background: var(--dot-d); }
}

/* ─── collapsible desktop sidebar ──────────────────────────────────────── */

/* Hidden on a phone: there is no sidebar to collapse there. */
.sidebar-toggle { display: none; }
.scope-icon { display: none; }

@media (min-width: 56.25rem) {
  /* One token drives everything — the content margin, the masthead and the
     dock all read --sidebar-w, so nothing else needs to know about this. */
  :root[data-sidebar='collapsed'] { --sidebar-w: 78px; }

  .sidebar-toggle {
    display: grid;
    place-items: center;
    width: var(--tap-minimum);
    height: var(--tap-minimum);
    border: 0;
    background: none;
    border-radius: var(--radius-medium);
    color: var(--ink-quiet);
    cursor: pointer;
    order: -1;              /* sits above the brand slot */
    align-self: flex-start;
  }
  .sidebar-toggle:hover { background: var(--surface-sunken); color: var(--ink); }
  .sidebar-toggle svg { width: 20px; height: 20px; }

  .scope-icon {
    display: none;
    color: var(--ink-secondary);
  }
  .scope-icon svg { width: 22px; height: 22px; }

  .topbar { padding: var(--space-medium); }

  /* ── collapsed ── */
  :root[data-sidebar='collapsed'] .topbar { align-items: center; }
  :root[data-sidebar='collapsed'] .scope-name { display: none; }
  :root[data-sidebar='collapsed'] .scope-icon { display: block; }
  :root[data-sidebar='collapsed'] #status-chips { display: none; }

  :root[data-sidebar='collapsed'] .tabs {
    width: var(--sidebar-w);
    padding: 0 var(--space-small);
    align-items: center;
    top: 128px;
  }
  :root[data-sidebar='collapsed'] .tab {
    justify-content: center;
    width: var(--tap-comfortable);
    height: var(--tap-comfortable);
    padding: 0;
    gap: 0;
  }
  :root[data-sidebar='collapsed'] .tab span:not([class]) { display: none; }

  :root[data-sidebar='collapsed'] .dock { width: var(--sidebar-w); }
  :root[data-sidebar='collapsed'] .dock-action { padding: var(--space-medium); }
  /* Square with a plus in it — the label has nowhere to go. */
  :root[data-sidebar='collapsed'] .dock-action .btn {
    width: var(--tap-comfortable);
    height: var(--tap-comfortable);
    min-height: 0;
    padding: 0;
    border-radius: var(--radius-medium);
    margin: 0 auto;
  }
  :root[data-sidebar='collapsed'] .dock-label { display: none; }
  :root[data-sidebar='collapsed'] .banner { margin-left: var(--sidebar-w); }
}

/* ─── trackers (Settings) ─────────────────────────────────────────────────
   One row per ledger you can open: the shared one everybody lands on, any
   tracker you've been invited to, and your own. The row is the switch; the
   button on its right is the only thing that opens the manage sheet, so
   tapping a tracker never does something you didn't ask for.
   The current one is marked with the accent, never with green — green is
   money in this app and nothing else. */

.tracker-list { border-top: var(--border-hair) solid var(--hairline); }

.tracker-row {
  padding: 0;
  gap: 0;
  cursor: default;
}
.tracker-row:hover { background: var(--surface); }
.tracker-row.is-current { background: var(--accent-wash); }
.tracker-row.is-current:hover { background: var(--accent-wash); }

.tracker-pick {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-medium);
  min-height: var(--tap-comfortable);
  padding: var(--space-medium) var(--space-base);
  background: none;
  border: 0;
  text-align: left;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.tracker-pick:hover { background: color-mix(in oklab, var(--ink) 4%, transparent); }

.tracker-icon {
  flex: none;
  width: 22px;
  height: 22px;
  color: var(--ink-quiet);
  display: flex;
}
.tracker-icon svg { width: 100%; height: 100%; }
.tracker-row.is-current .tracker-icon { color: var(--accent); }

.tracker-mark {
  flex: none;
  width: 18px;
  color: var(--accent);
  display: flex;
}

/* The name and what it is sit one above the other. Both are spans, so the
   column has to be made here rather than assumed. */
.tracker-pick .row-main { display: flex; flex-direction: column; }

/* No border and no fill: it belongs to the row it's in, not on top of it. */
.tracker-manage {
  flex: none;
  margin-right: var(--space-small);
  color: var(--ink-quiet);
  background: none;
  border-color: transparent;
}
.tracker-manage:hover { background: color-mix(in oklab, var(--ink) 6%, transparent); }

.card-foot { padding: var(--space-base); }
.card-foot .hint { margin-top: var(--space-medium); }

/* The invite code is the thing that actually gets passed around — read out at a
   table, typed into a phone. It's set in the money face for its tabular figures
   and its wide, even color, but never in a money color. */
.invite {
  margin: var(--space-base) 0;
  padding: var(--space-base);
  background: var(--surface-sunken);
  border-radius: var(--radius-medium);
}
.invite-code {
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  font-size: var(--text-display);
  letter-spacing: 0.12em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin: var(--space-small) 0 var(--space-medium);
  word-break: break-word;
}
.invite-hint {
  font-size: var(--text-caption);
  color: var(--ink-secondary);
  line-height: 1.5;
  margin-bottom: var(--space-base);
}

.input-code {
  font-family: var(--font-money);
  font-size: var(--text-heading);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ── Inside the iOS app ───────────────────────────────────────────────────
   The website has never needed a TOP safe-area inset. In a browser tab the
   chrome sits above the document, and on the Home Screen iOS keeps the status
   bar out of it (apple-mobile-web-app-status-bar-style: default). The native
   shell is edge-to-edge, so without this the header runs under the clock and
   the Dynamic Island.

   `data-native` is written onto <html> by scripts/build-native.mjs when it
   stages the app bundle. It never exists on the website, so every rule in this
   block is inert there — this file stays one stylesheet with one source. */
html[data-native] .topbar {
  padding-top: calc(var(--space-medium) + env(safe-area-inset-top, 0px));
}

/* ── Accounts, members, and paying to host (2026-08-21) ─────────────────── */

/* The face at the table. Color names map onto the existing accent ramps
   where one exists, so the palette stays the design system's, not a new one. */
.avatar {
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  font-family: var(--font-money);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.02em;
  color: var(--ink-on-accent);
  background: var(--accent);
  flex: none;
  user-select: none;
}
/* A photo fills the same circle; the color stays as the ring behind a
   transparent or slow-decoding image so the layout never jumps. */
.avatar.has-photo { overflow: hidden; }
.avatar.has-photo img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit; }

/* The photo control: the face is the button, a small camera badge says so. */
.profile-top { display: flex; align-items: center; gap: var(--space-base); margin-bottom: var(--space-small); }
.photo-btn {
  position: relative; display: inline-grid; place-items: center;
  border: 0; background: none; padding: 0; cursor: pointer; border-radius: 50%;
  color: inherit; font: inherit; -webkit-tap-highlight-color: transparent;
}
.photo-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.photo-badge {
  position: absolute; right: -3px; bottom: -3px; width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center; background: var(--surface); color: var(--ink-secondary);
  border: var(--border-hair) solid var(--hairline); box-shadow: var(--shadow-card);
}
.photo-badge svg { width: 13px; height: 13px; }
.photo-actions { display: flex; gap: var(--space-small); flex-wrap: wrap; margin-bottom: var(--space-base); }
.gate .photo-actions { justify-content: center; margin-bottom: var(--space-base); }
.gate .gate-photo { display: grid; place-items: center; color: inherit; }
.avatar-sm { width: 26px; height: 26px; font-size: 11px; }
.avatar-md { width: 36px; height: 36px; font-size: 13px; }
.avatar-lg { width: 56px; height: 56px; font-size: 20px; }
.avatar[data-color="violet"] { background: #4A3AFF; color: #fff; }
.avatar[data-color="ocean"]  { background: #0E7490; color: #fff; }
.avatar[data-color="moss"]   { background: #3F6212; color: #fff; }
.avatar[data-color="clay"]   { background: #9A3412; color: #fff; }
.avatar[data-color="plum"]   { background: #701A75; color: #fff; }
.avatar[data-color="slate"]  { background: #334155; color: #fff; }

.provider-row { display: grid; gap: var(--space-small); }
.provider-btn { justify-content: center; gap: var(--space-small); }
.provider-btn svg { width: 18px; height: 18px; }

.or {
  display: flex; align-items: center; gap: var(--space-small);
  margin: var(--space-base) 0;
  color: var(--ink-quiet); font-size: var(--text-micro);
  text-transform: uppercase; letter-spacing: 0.08em;
}
.or::before, .or::after { content: ''; flex: 1; height: var(--border-hair); background: var(--hairline); }

/* A role, said as a chip. Uses the accent — never green or red, which are money. */
.role-chip {
  display: inline-flex; align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: var(--text-micro);
  font-weight: var(--weight-semibold);
  text-transform: uppercase; letter-spacing: 0.06em;
  background: var(--surface-sunken);
  color: var(--ink-secondary);
}
.role-chip.is-owner  { background: color-mix(in oklab, var(--accent) 16%, transparent); color: var(--accent); }
.role-chip.is-cohost { background: color-mix(in oklab, var(--accent) 9%, transparent); color: var(--accent); }

/* The owner's permission table: one row per thing, a select on the right. */
.perm-rows { display: grid; gap: 2px; }
.perm-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-base);
  align-items: center;
  padding: var(--space-small) 0;
  border-bottom: var(--border-hair) solid var(--hairline);
}
.perm-row:last-child { border-bottom: 0; }
.perm-label { font-size: var(--text-label); color: var(--ink); }
.perm-row select.input { width: auto; min-width: 9.5em; padding-block: 6px; }

/* The member list row reuses .tracker-row; this is the extra on the right. */
.member-right { display: flex; align-items: center; gap: var(--space-small); }

/* Tiers on the paywall. The suggested one gets the accent hairline — not a
   fill, so the "current" marker and the price stay legible. */
.tiers { display: grid; gap: var(--space-small); margin: var(--space-base) 0; }
.tier {
  display: grid; gap: 4px;
  text-align: left;
  padding: var(--space-base);
  border: var(--border-hair) solid var(--hairline);
  border-radius: var(--radius-card, 14px);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.tier:hover { border-color: color-mix(in oklab, var(--accent) 40%, var(--hairline)); }
.tier.is-suggested { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
.tier.is-current { background: var(--surface-sunken); }
.tier-top { display: flex; justify-content: space-between; align-items: baseline; }
.tier-name { font-family: var(--font-money); font-weight: var(--weight-semibold); font-size: var(--text-heading); }
.tier-price { font-family: var(--font-money); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }
.tier-sub { font-size: var(--text-caption); color: var(--ink-secondary); }

/* The account card's one-line identity row. */
.me-row { display: flex; align-items: center; gap: var(--space-base); }
.me-row .row-main { min-width: 0; }

/* ── the sign-in gate (iOS app only; app.js never shows it on the web) ──── */
#gate[hidden] { display: none; }
.gate {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: calc(var(--space-large) + env(safe-area-inset-top, 0px)) var(--space-large)
           calc(var(--space-large) + env(safe-area-inset-bottom, 0px));
  background: var(--paper);
}
.gate-card { width: 100%; max-width: 380px; text-align: center; }
.gate-mark { width: 56px; height: 56px; margin: 0 auto var(--space-base); color: var(--ink); }
.gate-mark svg { width: 100%; height: 100%; }
.gate-title {
  font-family: var(--font-money); font-size: var(--text-display);
  font-weight: var(--weight-semibold); letter-spacing: var(--track-tight); margin: 0 0 var(--space-small);
}
.gate-sub { color: var(--ink-secondary); margin: 0 0 var(--space-large); line-height: 1.5; }
.gate .provider-row { display: grid; gap: var(--space-small); }
.gate-foot { color: var(--ink-quiet); font-size: var(--text-caption); margin-top: var(--space-large); }
.gate .swatches { justify-content: center; }
.gate .gate-mark .avatar { width: 56px; height: 56px; font-size: var(--text-title); }
.gate .field { display: block; margin: 0; }

/* ── the app, not the web: no double-tap zoom, no text-size zoom ──────── */
html[data-native] { touch-action: manipulation; -webkit-text-size-adjust: 100%; }
/* WKWebView keeps a stale layer of the page under an animated sheet and blends it
   through (2026-08-24, bisected in the simulator): no sheet animation in the app. */
html[data-native] .sheet, html[data-native] .sheet-panel { animation: none; }
html[data-native] body { touch-action: manipulation; }

/* ── Settings: the player card, then the rest a row away (2026-08-24) ──── */
.settings-hero { padding-top: var(--space-tiny); margin-bottom: var(--space-base); }
.settings-nav > * + * { margin-top: var(--space-base); }
.settings.has-page .settings-nav { display: none; }
.settings-page[hidden] { display: none; }

.id-card {
  --id: #334155;
  position: relative;
  background: linear-gradient(180deg, color-mix(in oklab, var(--id) 16%, var(--surface)) 0, var(--surface) 60%);
  border: var(--border-hair) solid color-mix(in oklab, var(--id) 24%, var(--hairline));
  border-radius: var(--radius-xlarge);
  padding: var(--space-large) var(--space-base) var(--space-base);
  box-shadow: var(--shadow-card);
}
.id-card[data-color="violet"] { --id: #4A3AFF; }
.id-card[data-color="ocean"]  { --id: #0E7490; }
.id-card[data-color="moss"]   { --id: #3F6212; }
.id-card[data-color="clay"]   { --id: #9A3412; }
.id-card[data-color="plum"]   { --id: #701A75; }
.id-card[data-color="slate"]  { --id: #334155; }
.id-top { display: flex; align-items: center; gap: var(--space-base); }
.avatar.avatar-xl { width: 64px; height: 64px; font-size: var(--text-title); }
.id-who { min-width: 0; }
.id-name {
  font-family: var(--font-money); font-size: var(--text-title); font-weight: var(--weight-semibold);
  letter-spacing: var(--track-tight); line-height: var(--leading-snug); margin: 0;
  overflow-wrap: anywhere;
}
.id-how { color: var(--ink-secondary); font-size: var(--text-caption); margin: var(--space-hair) 0 0; }
.id-chips { display: flex; flex-wrap: wrap; gap: var(--space-small); margin-top: var(--space-base); }
.id-chip {
  font-size: var(--text-caption); font-weight: var(--weight-medium);
  padding: var(--space-tiny) var(--space-medium); border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--ink) 6%, transparent); color: var(--ink-secondary);
}
.id-chip.is-plan { background: color-mix(in oklab, var(--id) 18%, var(--surface)); color: var(--ink); }
.id-stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-small);
  margin: var(--space-base) 0 0; padding-top: var(--space-base);
  border-top: var(--border-hair) solid var(--hairline);
}
.id-stats dt { font-size: var(--text-micro); text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--ink-quiet); }
.id-stats dd { margin: 2px 0 0; font-family: var(--font-money); font-size: var(--text-heading); font-weight: var(--weight-semibold); }

.nav-group {
  background: var(--surface); border: var(--border-hair) solid var(--hairline);
  border-radius: var(--radius-large); overflow: hidden; box-shadow: var(--shadow-card);
}
.nav-row {
  display: flex; align-items: center; gap: var(--space-medium); width: 100%;
  min-height: var(--tap-comfortable); padding: var(--space-medium) var(--space-base);
  background: none; border: 0; border-top: var(--border-hair) solid var(--hairline);
  color: var(--ink); font: inherit; text-align: left; cursor: pointer;
  transition: background var(--speed-fast) var(--ease-out);
}
.nav-row:first-child { border-top: 0; }
.nav-row:hover { background: var(--surface-sunken); }
.nav-row[aria-current="page"] { background: var(--accent-wash); }
.nav-row:disabled { color: var(--ink-disabled); cursor: default; }
.nav-row:disabled .nav-icon, .nav-row:disabled .nav-value { color: var(--ink-disabled); }
.nav-icon { width: 22px; height: 22px; flex: none; color: var(--ink-secondary); display: inline-flex; }
.nav-icon svg { width: 100%; height: 100%; }
.nav-label { flex: 1; font-weight: var(--weight-medium); font-size: var(--text-label); }
.nav-value { color: var(--ink-quiet); font-size: var(--text-caption); max-width: 48%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-chev { color: var(--ink-quiet); display: inline-flex; min-width: 18px; }
.nav-row.is-quiet .nav-label { color: var(--ink-secondary); font-weight: var(--weight-regular); }

.page-head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-tiny); }
.page-back {
  display: inline-flex; align-items: center; gap: var(--space-tiny);
  background: none; border: 0; padding: var(--space-small) 0; margin-left: -4px;
  color: var(--accent); font: inherit; font-weight: var(--weight-medium); cursor: pointer;
}
.page-back svg { width: 18px; height: 18px; }
.page-title {
  font-family: var(--font-money); font-size: var(--text-display); font-weight: var(--weight-semibold);
  letter-spacing: var(--track-tight); line-height: var(--leading-snug); margin: 0;
}

@media (min-width: 56.25rem) {
  .settings { display: grid; grid-template-columns: minmax(300px, 380px) minmax(0, 1fr); gap: var(--space-xlarge); align-items: start; }
  .settings.has-page .settings-nav { display: block; }
  .page-back { display: none; }
  .settings-page { padding-top: var(--space-tiny); }
}

/* ── Plans: pick a period, pick a plan, one button (2026-08-24) ─────────── */
.sheet-panel.is-tall { max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 24px); display: flex; flex-direction: column; padding-bottom: 0; }
.sheet-panel.is-tall .sheet-body { flex: 1; }
.plans-lead { color: var(--ink-secondary); line-height: var(--leading-body); margin: 0 0 var(--space-base); }
.plans-period { margin-bottom: var(--space-base); }
.plans-period button { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-small); }
.save-pill {
  font-size: var(--text-micro); font-weight: var(--weight-semibold); letter-spacing: .02em;
  padding: 2px 8px; border-radius: var(--radius-pill); background: var(--accent); color: var(--ink-on-accent);
}
.plans .tiers { margin: 0; }
.plans .tier {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-medium);
  width: 100%; text-align: left; padding: var(--space-base);
  border: var(--border-thick) solid var(--hairline); border-radius: var(--radius-large);
  background: var(--surface); color: var(--ink); cursor: pointer; font: inherit;
  transition: border-color var(--speed-fast) var(--ease-out), background var(--speed-fast) var(--ease-out);
}
.plans .tier.is-selected { border-color: var(--accent); background: var(--accent-wash); box-shadow: inset 0 0 0 1px var(--accent); }
.tier-radio { width: 20px; height: 20px; border-radius: 50%; border: var(--border-thick) solid var(--hairline-strong); position: relative; flex: none; }
.tier.is-selected .tier-radio { border-color: var(--accent); }
.tier.is-selected .tier-radio::after { content: ''; position: absolute; inset: 4px; border-radius: 50%; background: var(--accent); }
.tier-body { display: grid; gap: 2px; min-width: 0; }
.tier-tag { font-size: var(--text-micro); text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--accent); font-weight: var(--weight-semibold); margin-top: 2px; }
.tier-tag[hidden] { display: none; }
.tier-cost { display: grid; justify-items: end; text-align: right; }
.plans .tier-price { font-size: var(--text-title); letter-spacing: var(--track-money); line-height: 1; }
.tier-per { font-size: var(--text-caption); color: var(--ink-secondary); }
.tier-note { font-size: var(--text-micro); color: var(--ink-quiet); margin-top: 2px; white-space: nowrap; }
.plans-includes { list-style: none; padding: 0; margin: var(--space-base) 0 0; display: grid; gap: var(--space-tiny); color: var(--ink-secondary); font-size: var(--text-caption); }
.plans-includes li { position: relative; padding-left: 20px; }
.plans-includes li::before { content: ''; position: absolute; left: 4px; top: 7px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.plans-cta {
  position: sticky; bottom: 0; margin-top: var(--space-large);
  padding: var(--space-medium) 0 calc(var(--space-base) + var(--safe-b));
  background: var(--paper);
  box-shadow: 0 -12px 12px -8px color-mix(in oklab, var(--ink) 8%, transparent);
}
.plans-after { padding: var(--space-base) 0 var(--space-small); }
.plans-cta .plans-legal { margin-top: var(--space-small); }
.plans-legal { font-size: var(--text-micro); color: var(--ink-quiet); line-height: var(--leading-body); margin: var(--space-medium) 0 0; text-align: center; }
.plans-links { display: flex; justify-content: center; flex-wrap: wrap; gap: var(--space-small); margin-top: var(--space-small); }
/* Apple 3.1.2 — the purchase flow links the Terms of Use and Privacy Policy
   (2026-09-02, the 1.0 rejection). Same legal type, underlined, in the accent. */
.plans-legal-links { margin-top: var(--space-small); }
.plans-legal-links a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
/* A switch Apple has queued for the next renewal — worth noticing, so the accent. */
.plans-queued { color: var(--accent); }
/* The plan's status line (next payment / ends on / a payment being retried).
   The ordinary case stays quiet; anything the person should act on takes the accent. */
.plan-status.is-ends, .plan-status.is-grace, .plan-status.is-queued, .plan-status.is-lapsed,
.plans-status.is-ends, .plans-status.is-grace { color: var(--accent); }

/* A long email (Profile) wraps instead of pushing the page sideways (2026-08-24).
   Text that cannot break — an email, a code, a URL — is what widens a page: the
   element's box stays put while the run spills past it. So every card and sheet
   may break anywhere. */
.me-row .row-sub, .id-how { white-space: normal; overflow-wrap: anywhere; }
.card, .sheet-panel, .notice, .hint, .id-card { overflow-wrap: anywhere; min-width: 0; }
.settings, .settings-nav, .settings-page { min-width: 0; max-width: 100%; }

/* ── names and accounts (2026-08-25) ─────────────────────────────────────── */
.avatar.avatar-xs { width: 18px; height: 18px; font-size: 9px; margin-right: 6px; vertical-align: -3px; }
.pick.is-me { border-color: var(--accent); }
.pick.is-me .micro { margin-left: 4px; color: var(--accent); }
.pick.is-quiet { border-style: dashed; color: var(--ink-secondary); }
.whoami { border-color: var(--accent-edge); background: var(--accent-wash); }
.input-prefix { display: flex; align-items: center; gap: 0; }
.input-prefix .prefix { padding: 0 var(--space-small); color: var(--ink-quiet); font-weight: var(--weight-semibold); }
.input-prefix .input { flex: 1; }
[data-username-hint][data-state="ok"] { color: var(--accent); }
[data-username-hint][data-state="bad"] { color: var(--loss); }
.linked-to { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-secondary); font-size: var(--text-caption); }
.results-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-small); margin-top: var(--space-base); }
.results-grid .stat { padding: var(--space-medium); background: var(--surface-sunken); border-radius: var(--radius-medium); }
.results-grid .stat dt { font-size: var(--text-micro); text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--ink-quiet); }
.results-grid .stat dd { margin: 2px 0 0; font-family: var(--font-money); font-size: var(--text-heading); font-weight: var(--weight-semibold); }

/* The app's own spade on the sign-in screen (2026-08-25). */
.mark.mark-gate { width: 64px; height: 64px; }

.btn.btn-ghost.is-danger { color: var(--loss); }
.btn.btn-ghost.is-danger:hover { background: var(--loss-wash); }

.id-as { display: flex; align-items: center; justify-content: space-between; gap: var(--space-small); margin: var(--space-base) 0 0; font-size: var(--text-caption); color: var(--ink-secondary); }

.me-row-board { justify-content: flex-end; margin-top: var(--space-base); padding-top: var(--space-base); border-top: var(--border-hair) solid var(--hairline); }
.member-right { display: inline-flex; align-items: center; gap: var(--space-small); }

/* ── My results (2026-08-25): you, across every table ────────────────────── */
/* Your standing in each tracker is drawn the way a standing is drawn on the
   Leaderboard — the same bar about the same zero — so the two read as one
   thing. Tapping a row puts you in that tracker. */
.me-who { display: inline-flex; align-items: center; gap: var(--space-medium); min-width: 0; }
.me-who .hero-heading { overflow-wrap: anywhere; }
.me-hero .hero-value { margin-top: var(--space-medium); }
.me-hero .hero-value.up { color: var(--profit); }
.me-hero .hero-value.down { color: var(--loss); }
.me-hero .hero-value.is-quiet {
  font-size: var(--text-display); font-weight: var(--weight-semibold);
  letter-spacing: var(--track-tight); color: var(--ink-secondary);
}
/* Not ".me-row" — that is the Profile card's avatar row (display:flex) and
   it would win over .bar-row's grid, collapsing the bar column to nothing. */
button.bar-row.me-tracker {
  background: none; border-left: 0; border-right: 0; border-top: 0;
  width: 100%; text-align: left; cursor: pointer; color: inherit; font: inherit;
  padding-left: var(--space-small); padding-right: var(--space-small);
  border-radius: var(--radius-small);
  -webkit-tap-highlight-color: transparent;
}
button.bar-row.me-tracker:hover { background: var(--surface-sunken); }
button.bar-row.me-tracker:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.me-tracker .bar-name, .me-tracker .bar-sub { white-space: normal; }
.me-tracker.is-quiet .bar-name { color: var(--ink-secondary); font-weight: var(--weight-regular); }
/* Four tabs now; a two-word label must not stand taller than the others. */
.tab span { white-space: nowrap; }
@media (max-width: 22.5rem) { .tab { letter-spacing: 0; font-size: 10px; } }
.me-chev { color: var(--ink-quiet); display: inline-flex; justify-self: end; }
.me-foot { margin: 0; }
.results-grid .stat dd.up { color: var(--profit); }
.results-grid .stat dd.down { color: var(--loss); }
.results-grid .stat-note { display: block; margin-top: 2px; font-size: var(--text-micro); color: var(--ink-quiet); overflow-wrap: anywhere; }

/* Settings → Profile is the editor itself (2026-08-25): no sheet, no button. */
.profile-inline .profile-top { margin-top: var(--space-base); }
.profile-inline .sheet-actions { margin-top: var(--space-base); }

/* ── the tracker switcher (2026-08-25): the name in the header IS the control ── */
.scope-switch {
  display: inline-flex; align-items: center; gap: var(--space-tiny);
  min-width: 0; max-width: 58vw;
  padding: var(--space-tiny) var(--space-small); margin-right: calc(-1 * var(--space-small));
  background: none; border: 0; border-radius: var(--radius-medium);
  color: inherit; font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.scope-switch:hover { background: var(--surface-sunken); }
.scope-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.scope-switch[aria-expanded="true"] { background: var(--accent-wash); }
.scope-switch .scope-name { max-width: none; min-width: 0; flex: 0 1 auto; }   /* the button bounds it now */
.scope-caret { display: inline-flex; flex: none; color: var(--ink-quiet); }
.scope-caret svg { width: 14px; height: 14px; }
.scope-switch[aria-expanded="true"] .scope-caret { transform: rotate(180deg); }

.menu {
  position: fixed; z-index: 65;
  min-width: 232px; max-width: min(320px, calc(100vw - 16px));
  overflow: auto; padding: var(--space-small);
  background: var(--surface); border: var(--border-hair) solid var(--hairline);
  border-radius: var(--radius-large); box-shadow: var(--shadow-raised);
  animation: menu-in var(--speed-fast) var(--ease-out);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
html[data-native] .menu { animation: none; }
.menu-item {
  display: flex; align-items: center; gap: var(--space-medium); width: 100%;
  min-height: var(--tap-minimum); padding: var(--space-small) var(--space-medium);
  background: none; border: 0; border-radius: var(--radius-medium);
  color: var(--ink); font: inherit; text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.menu-item:hover, .menu-item:focus-visible { background: var(--surface-sunken); outline: none; }
.menu-item[aria-checked="true"] { color: var(--accent); font-weight: var(--weight-semibold); }
.menu-item.is-quiet { color: var(--ink-secondary); }
.menu-icon { width: 20px; height: 20px; flex: none; display: inline-flex; color: var(--ink-secondary); }
.menu-icon svg { width: 100%; height: 100%; }
.menu-item[aria-checked="true"] .menu-icon { color: var(--accent); }
.menu-label { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: var(--text-label); overflow-wrap: anywhere; }
.menu-sub { font-size: var(--text-caption); color: var(--ink-quiet); font-weight: var(--weight-regular); }
.menu-check { width: 18px; flex: none; display: inline-flex; color: var(--accent); }
.menu-sep { height: 1px; background: var(--hairline); margin: var(--space-small) var(--space-medium); }

@media (min-width: 56.25rem) {
  .scope-switch { display: flex; width: 100%; max-width: none; margin: 0; padding: var(--space-small); gap: var(--space-small); text-align: left; }
  .scope-switch .scope-name { flex: 1; }
  :root[data-sidebar='collapsed'] .scope-switch { width: var(--tap-comfortable); height: var(--tap-comfortable); justify-content: center; padding: 0; margin: 0 auto; }
  :root[data-sidebar='collapsed'] .scope-caret { display: none; }
}

/* A hand-over candidate who cannot take the tracker: the row says why and does nothing. */
.tracker-pick:disabled { opacity: .55; cursor: default; }
.tracker-pick:disabled:hover { background: none; }

/* ── The way up: hosting offers on the free surfaces (2026-08-27) ─────────
   One rule (billing.hostOffer) draws every one of these. Accent only — the
   money colors stay money — and the price is set like any other figure. */
.tonight-empty { display: grid; gap: var(--space-large); min-width: 0; }
.host-offer {
  display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto auto; gap: var(--space-small);
  border-color: var(--accent-edge);
  background: linear-gradient(180deg, var(--accent-wash) 0, var(--surface) 72%);
}
.host-offer-mark { width: 28px; height: 28px; color: var(--accent); display: inline-flex; }
.host-offer-mark svg { width: 100%; height: 100%; }
.host-offer-title {
  font-family: var(--font-money); font-size: var(--text-heading); font-weight: var(--weight-semibold);
  letter-spacing: var(--track-tight); line-height: var(--leading-snug); margin: 0;
}
.host-offer-line { color: var(--ink-secondary); font-size: var(--text-caption); line-height: var(--leading-body); margin: 0; max-width: 40ch; }
.host-offer-deal { display: flex; align-items: center; justify-content: space-between; gap: var(--space-base); flex-wrap: wrap; margin-top: var(--space-medium); }
.host-offer-price { display: grid; line-height: 1; }
.host-offer-price .money { font-size: var(--text-display); }
.host-offer-per { font-size: var(--text-micro); text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--ink-quiet); margin-top: var(--space-tiny); }
.host-offer-dismiss { justify-self: end; color: var(--ink-quiet); }

.host-offer-row {
  display: flex; align-items: center; gap: var(--space-medium); width: 100%; min-height: var(--tap-comfortable);
  padding: var(--space-medium) var(--space-base); text-align: left; font: inherit; cursor: pointer;
  border: var(--border-hair) solid var(--accent-edge); border-radius: var(--radius-large);
  background: var(--surface); color: var(--ink); box-shadow: var(--shadow-card);
  transition: background var(--speed-fast) var(--ease-out);
}
.host-offer-row:hover { background: var(--accent-wash); }
.host-offer-icon { width: 20px; height: 20px; flex: none; color: var(--accent); display: inline-flex; }
.host-offer-icon svg { width: 100%; height: 100%; }
.host-offer-label { flex: 1; min-width: 0; font-weight: var(--weight-medium); font-size: var(--text-label); }
.host-offer-sub { font-size: var(--text-caption); color: var(--accent); font-weight: var(--weight-medium); white-space: nowrap; }
.host-offer-chev { width: 18px; height: 18px; color: var(--ink-quiet); display: inline-flex; flex: none; }
.host-offer-chev svg { width: 100%; height: 100%; }

/* One line under "Start the game" on the private ledger. */
.sheet-offer { margin-top: var(--space-medium); color: var(--ink-secondary); font-weight: var(--weight-regular); }
.sheet-offer strong { color: var(--accent); font-weight: var(--weight-semibold); }

/* The identity card's one chip that is a button. */
.id-chip.is-upgrade {
  border: 0; font: inherit; font-size: var(--text-caption); font-weight: var(--weight-semibold); cursor: pointer;
  background: var(--accent); color: var(--ink-on-accent);
  transition: background var(--speed-fast) var(--ease-out);
}
.id-chip.is-upgrade:hover { background: var(--accent-hover); }
.id-chip.is-upgrade:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Settings row values that are the way up ("Free · from $2.99/mo", a full plan). */
.nav-value.is-accent { color: var(--accent); font-weight: var(--weight-medium); }

/* The switcher's "Host a shared tracker" item. */
.menu-item.is-accent { color: var(--accent); font-weight: var(--weight-medium); }
.menu-item.is-accent .menu-icon { color: var(--accent); }

/* A paused tracker is a state, not a loss: accent wash, and the owner's Renew button beside the words. */
.notice.is-accent { background: var(--accent-wash); color: var(--ink); }
.notice-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-base); flex-wrap: wrap; }
.notice-row > span { flex: 1 1 24ch; min-width: 0; }

@media (min-width: 56.25rem) {
  /* Beside the empty card on a wide screen, so the right side of the page is not empty. */
  .tonight-empty.has-offer { grid-template-columns: 1fr 1fr; align-items: stretch; }
  .tonight-empty.has-offer .empty { padding-top: var(--space-large); padding-bottom: var(--space-large); }
}

@media (min-width: 33rem) {
  /* A wide, tall sheet — roomier on anything but the smallest phone. */
  .sheet-panel.is-wide { max-width: 40rem; }
  .sheet-panel.is-wide.is-tall .sheet-body { padding: var(--space-small) var(--space-medium) 0; }
}

