/* DO NOT EDIT — copied from packages/shared/src/design/css/controls.css
   by `npm run tokens`. Edit the source there, regenerate, commit both.
   A vitest in packages/shared fails if this file drifts from the source. */
/* Shared control vocabulary — linked by BOTH web surfaces (public/phone and
   public/landing) after /shared/tokens.css. The desktop app mirrors these in
   React with identical geometry; see apps/desktop/src/components/ui/.
   Hand-written, NOT generated.

   These rules moved here from tdtt.css, which was the only surface that had
   them. Their comments moved with them — the reasoning is load-bearing:

   - .chip is 6px radius, not a pill, because a square bracket cannot follow a
     pill curve.
   - .chip needs position:relative; the .on brackets are absolutely positioned.
   - .chip.on goes border-color:transparent rather than border:none, so the 1px
     border box is retained and an active chip never shifts its neighbours.
   - The active wash mixes into --surface-2, the chip's OWN base surface, never
     into `transparent` — mixing into transparent REPLACES the solid base and
     the page's fixed hex lattice reads straight through the chip.
   - .seg-slab's active segment fills with the section accent, NOT the brand
     gradient, which is reserved for primary CTA buttons so it stays a single
     constant across the app. A chip is a filter; a slab segment is a mode
     switch. Keep the treatments different. */

/* ── chips / filters ── */
.chip-row { display:flex; gap:6px; overflow-x:auto; padding-bottom:10px; scrollbar-width:none; }
.chip-row::-webkit-scrollbar { display:none; }
/* ── Filter chip (binding control system, 2026-07-30) — Codex set filters,
   leaderboard scopes. Tokens only; active state reads the section accent. */
/* 6px radius (not the old 20px pill) so the square bracket corners of the
   selected state can sit flush — a bracket can't follow a pill curve.
   position:relative is REQUIRED here: it used to come from the ticket-mask
   rule, which is gone, and the .chip.on brackets are absolutely positioned. */
.chip { flex:0 0 auto; position:relative; font-family:var(--head); font-size:.54rem; letter-spacing:.05em; text-transform:uppercase;
        padding:8px 13px; border-radius:6px; background:var(--surface-2); border:1px solid var(--border); color:var(--text-dim); }
/* selected = reticle brackets (2026-08-04): a faint wash plus two corner
   brackets that "lock on" to the chip. border-color goes transparent rather
   than border:none so the 1px border box is retained and an active chip never
   shifts its neighbours in the flex row.
   The wash mixes into --surface-2 (the chip's OWN base surface), never into
   `transparent`: mixing into transparent yields a 12%-alpha fill that REPLACES
   the solid base, so the selected chip goes see-through and the fixed hex
   lattice reads straight through it. Mix into the base surface — see the
   "solid active surfaces" note in CLAUDE.md. */
.chip.on {
  background: color-mix(in srgb, var(--sec-accent, var(--cyan)) 12%, var(--surface-2));
  border-color: transparent;
  color: var(--sec-accent-label, var(--sec-accent, var(--cyan)));
}
.chip.on::before, .chip.on::after {
  content: '';
  position: absolute;
  width: 9px;
  height: 9px;
  border: 2px solid var(--sec-accent, var(--cyan));
  filter: drop-shadow(0 0 5px var(--sec-accent, var(--cyan)));
  pointer-events: none;   /* never intercept a tap meant for the chip */
}
/* -1px aligns the brackets to the OUTSIDE of the border box; chip rows use
   gap:7px so the 1px overhang can't collide with the next chip. */
.chip.on::before { left:-1px;  top:-1px;    border-right:none; border-bottom:none; border-radius:5px 0 0 0; }
.chip.on::after  { right:-1px; bottom:-1px; border-left:none;  border-top:none;    border-radius:0 0 5px 0; }

/* ── Segmented slab (Pixel-Arcade filter-bar style "C") ────────────────────────
   One inset track with equal-width segments; the active segment takes the
   brand TABLETOP gradient. Reusable anywhere a chip-row reads as too
   lightweight (leaderboard view tabs, Online/In-Person scope, etc). */
.seg-slab { background:var(--input); border:1px solid var(--border); border-radius:12px;
            padding:4px; display:flex; gap:3px; }
.seg-slab .t {
  flex:1; text-align:center; padding:10px 4px; border-radius:9px;
  font-family:var(--head); font-size:.62rem; letter-spacing:.08em; text-transform:uppercase;
  color:var(--muted); border:none; background:none; cursor:pointer;
  display:flex; align-items:center; justify-content:center; gap:5px;
  -webkit-tap-highlight-color:transparent;
}
.seg-slab .t svg { width:12px; height:12px; }
/* active segment fills solid with the section accent (binding system,
   2026-07-30) — NOT the brand gradient, which is reserved for primary CTA
   buttons only so it stays a single constant across the app. */
.seg-slab .t.on {
  background: var(--sec-accent, var(--cyan));
  color: var(--sec-accent-ink, #04121f);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--sec-accent, var(--cyan)) 45%, transparent);
}
/* "who won" report pickers (tournament.html, play.html match report) mark the
   selected winner with a green semantic regardless of section accent — a
   functional state (win), not a brand/section color. */
.seg-slab .t.on.win {
  background: var(--green); color: #00210f;
  box-shadow: 0 4px 14px rgba(0,230,118,.4);
}

/* ── Page header ──────────────────────────────────────────────────────────
   The web twin of the desktop app's PlateHeader — "underline blade" style:
   an optional eyebrow, the title, an optional subtitle, and a short accent
   blade beneath. The desktop component's `tone` names were deliberately kept
   through its own redesign so its 16 call sites never changed; this mirrors
   its geometry, not its API.

   Bakes in NO horizontal padding — the page owns its gutter. (Same reasoning
   as the Tailwind pb-after-py rule on the desktop side: a component that
   bakes in padding silently overrides its caller.) */
.tdtt-page-header { display:flex; align-items:flex-end; justify-content:space-between;
                    gap:16px; padding:0 0 10px; }
.tdtt-page-header .eyebrow { display:block; font-size:.52rem; letter-spacing:.14em;
                             text-transform:uppercase; color:var(--muted); margin-bottom:4px; }
.tdtt-page-header .title { font-family:var(--head); font-weight:900; font-size:1.5rem;
                           line-height:1.05; color:var(--text); margin:0; }
.tdtt-page-header .sub { display:block; font-size:.74rem; color:var(--text-dim); margin-top:5px; }
.tdtt-page-header .blade { height:3px; width:54px; border-radius:2px; margin-top:9px;
                           background:var(--sec-accent, var(--cyan)); }
.tdtt-page-header .actions { display:flex; align-items:center; gap:8px; flex:0 0 auto; }
@media (max-width: 640px) {
  .tdtt-page-header { flex-direction:column; align-items:stretch; }
  .tdtt-page-header .title { font-size:1.2rem; }
}

/* ── Stat tile ────────────────────────────────────────────────────────────
   A value over a label, with a short accent underline. The reference design
   uses a row of three of these inside each podium card (record / win rate /
   rating) and again in the table header group. */
.tdtt-stat { display:flex; flex-direction:column; gap:3px; min-width:0; }
.tdtt-stat .v { font-family:var(--head); font-size:.95rem; font-weight:700; color:var(--text); line-height:1.1; }
.tdtt-stat .l { font-size:.52rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
/* The underline is the tile's only colour cue, so it carries the section
   accent rather than a per-tile colour — keeps a row of tiles reading as one
   group instead of a rainbow. */
.tdtt-stat .u { height:2px; width:22px; border-radius:2px; background:var(--sec-accent, var(--cyan)); opacity:.75; }

/* ── Hero strip (a player's top heroes, with records) ─────────────────────
   Sits under the player name on both the podium cards and the dense rows.

   FIXED 5-COLUMN GRID, NOT flex:1 -- this is a bug the desktop leaderboard
   already hit and documented: with a growing flex child, a player with a
   single hero got one portrait blown up across the entire row. Fixed columns
   keep the tile identical whether the player has 1 hero or 5.

   Portraits are .jpg for most heroes and .png for nine of the 44, so every
   <img> needs the .jpg -> .png onerror fallback; see the harness markup. */
.tdtt-hero-strip { display:flex; justify-content:center; gap:6px; width:100%; }
/* flex-basis is a fixed FRACTION of the row (one fifth, minus its share of the
   four gaps) with flex-grow:0 -- so a tile is the same size whether the player
   has one hero or five, and a short strip centres under the name instead of
   hugging the left edge of a centred card. A plain 5-column grid also keeps the
   size constant but cannot centre a partial row. */
.tdtt-hero-strip .h { flex:0 0 calc((100% - 24px) / 5); }
.tdtt-hero-strip .h { position:relative; aspect-ratio:2/3; border-radius:9px; overflow:hidden;
                      background:var(--surface-2); border:1px solid var(--border); min-width:0; }
.tdtt-hero-strip .h img { width:100%; height:100%; object-fit:cover; object-position:center; display:block; }
/* Scrim so the record stays legible over light portrait art. Alpha is correct
   here -- it sits over an image, not over the page canvas. */
.tdtt-hero-strip .h::after { content:''; position:absolute; inset:0;
                             background:linear-gradient(to top, rgb(0 0 0 / .92) 18%, rgb(0 0 0 / .15) 55%, transparent); }
.tdtt-hero-strip .h .cap { position:absolute; left:0; right:0; bottom:2px; z-index:1;
                           display:flex; flex-direction:column; align-items:center; gap:1px; padding:0 2px; }
.tdtt-hero-strip .h .hn { font-size:.46rem; color:rgb(255 255 255 / .72); max-width:100%;
                          overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tdtt-hero-strip .h .rec { font-family:var(--mono); font-size:.55rem; font-weight:700; line-height:1;
                           text-shadow:0 1px 3px rgb(0 0 0 / .9); }
/* Win-rate bands, identical to the thresholds the desktop leaderboard and the
   phone site already use (>=55 strong, >=45 even, below that losing). */
.tdtt-hero-strip .rec.good { color:var(--green); }
.tdtt-hero-strip .rec.even { color:var(--gold); }
.tdtt-hero-strip .rec.bad  { color:var(--red); }
.tdtt-hero-strip .empty { grid-column:1 / -1; font-size:.6rem; color:var(--muted); text-align:center; padding:6px 0; }

/* Compact density for the dense rows: square thumbnails at a fixed size, laid
   out in a row. Explicit widths (not 1fr) for the same reason as above -- the
   tile must not resize with the hero count. */
/* Compact density for the dense rows. The record sits BELOW the thumbnail
   rather than on top of it: at 32px a two-number record overlaid on portrait
   art is unreadable no matter how heavy the scrim, and the scrim large enough
   to fix it would hide the portrait it is captioning. Explicit tile widths
   (not 1fr) for the same reason as above -- the tile must not resize with the
   hero count. */
/* Centring is a podium-card concern only. In a dense row the strip sits under
   the player name in the flexible identity column, so it must start at the
   left edge like the name above it -- inheriting justify-content:center
   floats the thumbnails into the middle of the row, away from the name they
   belong to. */
.tdtt-hero-strip.sm { display:flex; justify-content:flex-start; gap:7px; width:auto; }
.tdtt-hero-strip.sm .h { width:32px; aspect-ratio:1; flex:0 0 auto; border-radius:8px;
                         overflow:visible; border:none; background:none; }
.tdtt-hero-strip.sm .h img { width:32px; height:32px; border-radius:8px;
                             border:1px solid var(--border); background:var(--surface-2); }
.tdtt-hero-strip.sm .h::after { content:none; }
.tdtt-hero-strip.sm .h .cap { position:static; padding:0; margin-top:2px; }
.tdtt-hero-strip.sm .h .hn { display:none; }
.tdtt-hero-strip.sm .h .rec { font-size:.5rem; text-shadow:none; letter-spacing:-.02em; }
.tdtt-hero-strip.sm .empty { font-size:.56rem; padding:0; text-align:left; }

/* ── Rank row (dense leaderboard table) ───────────────────────────────────
   A row, not a <tr>, so the same markup works in a grid on desktop and a
   stacked list on a phone. Hairline separators, no zebra striping. */
.tdtt-rank-row { display:flex; align-items:center; gap:12px; padding:9px 12px;
                 border-bottom:1px solid var(--border); min-width:0; }
.tdtt-rank-row:last-child { border-bottom:none; }
.tdtt-rank-row:hover { background:color-mix(in srgb, var(--cyan) 5%, var(--surface)); }
/* The signed-in viewer's own row. Mixes into --surface, never transparent. */
.tdtt-rank-row.me { background:color-mix(in srgb, var(--sec-accent, var(--cyan)) 10%, var(--surface)); }
.tdtt-rank-row .pl { font-family:var(--head); font-weight:900; width:34px; text-align:center; color:var(--text-dim); flex:0 0 auto; }
.tdtt-rank-row .nm { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text); }
/* Fixed, SHARED column widths. The header says these labels sit above these
   values, so header and row must use one width -- with flex:0 0 auto each side
   sized to its own content and the labels drifted off their columns. */
.tdtt-rank-row .cell, .tdtt-rank-head .cell { flex:0 0 auto; width:64px; text-align:right; }
.tdtt-rank-row .cell { font-family:var(--mono); font-size:.72rem; color:var(--text-dim); }
.tdtt-rank-row .elo, .tdtt-rank-head .elo { flex:0 0 auto; width:52px;
                                            display:flex; justify-content:flex-end; }
/* Identity cell: name on top, hero strip beneath. This is the flexible column,
   so it owns min-width:0 -- .nm inside it can then ellipsis instead of forcing
   the row wider than its container. */
.tdtt-rank-row .who { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:4px; }
.tdtt-rank-row .who .nm { flex:none; }

/* Record, split so a glance reads wins against losses. Gold wins / red losses
   matches what the desktop leaderboard already renders. */
.tdtt-record { font-family:var(--mono); font-size:.72rem; white-space:nowrap; }
.tdtt-record .w { color:var(--gold); font-weight:700; }
.tdtt-record .l { color:var(--red); }
.tdtt-record .sep { color:var(--muted); margin:0 2px; }

/* Column header for the dense list. Same padding and column widths as the rows
   below it, or the labels do not line up with what they label. */
.tdtt-rank-head { display:flex; align-items:center; gap:12px; padding:7px 12px;
                  border-bottom:1px solid var(--border);
                  background:var(--surface-2);
                  font-size:.52rem; letter-spacing:.09em; text-transform:uppercase; color:var(--muted); }
.tdtt-rank-head .pl { width:34px; text-align:center; flex:0 0 auto; }
.tdtt-rank-head .who { flex:1 1 auto; min-width:0; }

/* Below ~640px the dense row cannot hold five columns: place + avatar +
   record + win rate + ELO leave the identity column too narrow for the name,
   which silently ellipses to nothing while the hero strip overflows it. Drop
   the two derived columns -- record and win rate are recoverable from the
   hero strip and the profile -- and keep place, identity and the rating. The
   header drops the same cells so it keeps describing what is actually below. */
@media (max-width: 640px) {
  .tdtt-rank-row .cell, .tdtt-rank-head .cell { display:none; }
  .tdtt-rank-row, .tdtt-rank-head { gap:9px; padding-left:8px; padding-right:8px; }
  .tdtt-hero-strip.sm { flex-wrap:wrap; }
}

/* ── Avatar ───────────────────────────────────────────────────────────────
   Square-with-radius, not a circle — matches the desktop app's portrait
   treatment. Falls back to an initial when there is no image. */
.tdtt-avatar { flex:0 0 auto; width:32px; height:32px; border-radius:9px; overflow:hidden;
               background:var(--surface-2); border:1px solid var(--border);
               display:flex; align-items:center; justify-content:center;
               font-family:var(--head); font-size:.7rem; color:var(--text-dim); }
.tdtt-avatar img { width:100%; height:100%; object-fit:cover; }
.tdtt-avatar.lg { width:44px; height:44px; border-radius:12px; font-size:.9rem; }
.tdtt-avatar.xl { width:66px; height:66px; border-radius:16px; font-size:1.3rem; }
/* Rank ring. Drawn with box-shadow rather than a thicker border so the avatar's
   box size does not change between a ringed and an unringed one -- a border
   width change would shift every neighbour in the row by 2px. --rank is set by
   the podium card, so the ring cannot disagree with the medal or the badge. */
.tdtt-avatar.ring { box-shadow:0 0 0 2px var(--rank, var(--cyan)), 0 0 14px color-mix(in srgb, var(--rank, var(--cyan)) 35%, transparent); }

/* ── Rank badge (4th and beyond) ──────────────────────────────────────────*/
.tdtt-rank-badge { display:inline-flex; align-items:center; justify-content:center;
                   min-width:26px; height:26px; padding:0 7px; border-radius:999px;
                   font-family:var(--head); font-weight:900; font-size:.66rem;
                   background:color-mix(in srgb, var(--cyan) 12%, var(--surface-2));
                   color:var(--cyan); }
