/* site/app/screens/players.css — Players.
 * Depends on tokens.css, ui.css and shell.css. Reuses .list .row .tag .btn .avatar .empty
 * .sec .sec-head .segmented .chev .screen-head .pane-scroll — nothing here reinvents one.
 *
 * ⚠ EVERY VALUE IS A TOKEN. A raw hex in this file is a bug, not a shortcut: it does not
 *   follow the palette into dark mode, and that exact mistake is the direct cause of two
 *   shipped defects on this project. There is no sanctioned raw colour in this file.
 *
 * ⚠ EVERY NAME IS PREFIXED `pl-`. This project has already shipped two stylesheets that both
 *   invented `.pcard`, and whichever loaded second broke the other silently. Load order
 *   decides nothing here because no two files can collide.
 */

/* ⚠ THE SCREEN IS ITS OWN SIZE CONTAINER. The wide-screen rule at the bottom of this file
   must ask the APP FRAME how wide it is, not the browser window — the app is drawn inside a
   390px phone frame, so a plain media query fires on a tablet while the frame it is sizing
   is still phone-width. A container query asks the frame, and a container has to be an
   element, which is why render() stamps `.pl` on the pane. */
.pl { container: pl-screen / inline-size; }

/* ═══ 1 · THE PREVIEW BAR — not part of the app ══════════════════════════════ */

.pl-preview {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 16px 18px; padding: 9px 12px;
  background: var(--bg-sunken); border: 1px dashed var(--rule); border-radius: var(--r-md);
}
.pl-preview-lab {
  font: 600 11px/1 var(--font-ui); letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-tertiary);
}
.pl-preview-group { display: flex; gap: 6px; flex: 1; flex-wrap: wrap; }
.pl-preview-btn {
  border: 1px solid var(--rule); background: var(--bg-raised); color: var(--ink-secondary);
  border-radius: var(--r-full); padding: 7px 12px; cursor: pointer;
  font: 500 12px/1 var(--font-ui);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.pl-preview-btn[aria-pressed="true"] {
  background: var(--selected-soft-bg); color: var(--selected-soft-ink);
  border-color: var(--selected-edge); font-weight: 600;
}

/* ═══ 2 · SECTION FURNITURE ══════════════════════════════════════════════════ */

.pl-sec { margin: 0 0 24px; }
.pl-sec .list { margin: 0 16px; }
.pl-sec .empty { margin: 0 16px; }

/* The snapshot's period, sitting opposite the section title. `leaderboard_snapshot` is
   immutable per period, so naming the period is naming which board this is. */
.pl-period {
  font: 600 12px/1 var(--font-ui); letter-spacing: .04em;
  color: var(--ink-tertiary); flex: none;
}
.pl-count {
  font: 600 12px/1 var(--font-ui); color: var(--ink-tertiary);
  font-variant-numeric: tabular-nums; flex: none;
}
.pl-caption {
  margin: 0 20px 12px;
  font: 400 12px/1.4 var(--font-ui); color: var(--ink-tertiary);
}

/* Thumb width is a calc of the track, so it never needs measuring: 2px padding each side
   plus one 2px gap = 6px of furniture, and the rest splits two ways. */
.pl-seg { margin: 0 16px 12px; }
.pl-seg .thumb { width: calc((100% - 6px) / 2); }
/* ⚠ 30px PAINTED. ui.css's .segmented button is 8px of padding around a 14px line, and its
   own comment says the invisible overlay carries the 44px floor — this restates the floor on
   the control itself so the segment is never short even if that overlay is ever clipped by
   an ancestor. Same line is in cafe.css and history's filter for the same reason. */
.pl-seg button { min-height: var(--tap-min); }
.pl-seg button[aria-pressed="true"] { font-weight: 600; color: var(--ink); }

/* ═══ 3 · ROWS ══════════════════════════════════════════════════════════════ */

/* ⚠ THE ROW IS A <button>, SO IT HAS TO BE UN-BUTTONED. A <button> inherits none of the
   page's font, centres its text, draws a border and paints its own background — all four of
   which make it look nothing like the .row beside it that happens to be a <div>. It is a
   button because the whole row is the tap target: the chevron is 13px of decoration and
   ui.css keeps it pointer-transparent, so a thumb landing on the arrow — which is exactly
   where people aim — must still hit the row. */
.pl-row {
  width: 100%; text-align: left; border: 0; cursor: pointer;
  background: var(--bg-raised); color: inherit; font: inherit;
  border-bottom: 1px solid var(--rule);
  -webkit-tap-highlight-color: transparent;
}
.pl-row:last-child { border-bottom: 0; }
.pl-chev { color: var(--ink-tertiary); }

.pl-name-line { display: flex; align-items: center; gap: 7px; min-width: 0; }
.pl-name-line .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One line each, ellipsised. ⚠ Not `white-space: normal`: a wrapped @username turns a 60px
   list row into a 78px one for that player alone, and a board whose rows are different
   heights stops reading as a ranked column. */
.pl-row .meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pl-board-row .meta { font-size: 12.5px; }

/* ═══ 4 · THE BOARD ═════════════════════════════════════════════════════════ */

/* ⚠ TIGHTER THAN A PLAIN .row, AND IT IS MEASURED. A board row carries six things where an
   ordinary row carries three: rank, avatar, name, @username + match count, rating, movement,
   chevron. At .row's 16px padding and 12px gaps that left about 140px for the name line on a
   390px phone, and "@ton.alvarez · 198 matches" — a perfectly ordinary handle with a dot in
   it — was cut to "198 m…". Four pixels off each gap and each side of the padding is
   invisible and buys back most of a word. */
.pl-board-row { gap: 8px; padding-left: 12px; padding-right: 12px; align-items: center; }

/* ⚠ FIXED WIDTH AND TABULAR, so #1 and #128 sit on the same left edge and the column of
   names below them does not step sideways as the numbers grow a digit. 1.9em fits three
   digits at this weight; the board is capped at 186 players. */
.pl-rank {
  flex: none; width: 1.9em; text-align: right;
  font-family: var(--font-display); font-weight: 600;
  font-size: 17px; line-height: 1; letter-spacing: -.015em;
  color: var(--ink-secondary); font-variant-numeric: tabular-nums;
}

.pl-right {
  flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px;
}
.pl-rating {
  font-family: var(--font-display); font-weight: 600;
  font-size: 17px; line-height: 1; letter-spacing: -.02em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* The provisional row's third line: the standing word, then the floor it is short of. */
.pl-standing { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.pl-standing .tag { flex: none; }
.pl-floor { font: 400 11.5px/1.2 var(--font-ui); color: var(--ink-tertiary); }
/* ⚠ The tinted "me" row repaints its own ink, and --ink-tertiary would vanish into it. */
.pl-board-row.is-me .pl-floor { color: var(--selected-soft-ink); opacity: .82; }
/* Three lines, so the row aligns from the top rather than centring the avatar against a
   block that is now taller than it. */
.pl-board-row.is-prov { align-items: flex-start; }
.pl-board-row.is-prov .pl-rank,
.pl-board-row.is-prov .avatar,
.pl-board-row.is-prov .pl-chev { margin-top: 2px; }

/* ── Movement ─────────────────────────────────────────────────────────────── */
/* ⚠ NOT --ok AND NOT --danger, AND THIS IS THE RULE, NOT A PREFERENCE.
   --ok means "this worked" — a paid ticket, a landed save. Climbing a leaderboard is not a
   success and dropping is not a failure; green-and-red turns a neutral fact into a verdict on
   a person, and it spends the one colour the app reserves for "the write went through".
   --chartreuse is out for the same kind of reason (DESIGN.md §2: energy, never status).
   So direction is carried by THE WORD ("Up 3" / "Down 2" / "Even" / "New") and the arrow,
   with ink weight as a third, quietest channel. Read the board in greyscale: it still reads. */
.pl-move {
  display: inline-flex; align-items: center; gap: 3px;
  font: 500 11.5px/1 var(--font-ui); letter-spacing: .005em;
  color: var(--ink-tertiary); white-space: nowrap;
}
.pl-move.is-up { color: var(--ink); font-weight: 600; }
.pl-move-ico { flex: none; display: block; }
/* A first appearance is not a movement of zero, so it does not wear the movement's shape at
   all — it is a neutral tag, which is what the schema's NULL rank_change actually means. */
.pl-move-new { padding: 4px 7px; font-size: 10px; letter-spacing: .06em; }

/* ── The viewer's own row ─────────────────────────────────────────────────── */
/* ⚠ A TINT *AND* THE WORD "Me". A tint alone is the first thing to disappear in direct sun on
   a court, which is where this screen is read; the tag is what survives it. */
.pl-board-row.is-me { background: var(--selected-soft-bg); }
.pl-board-row.is-me .title,
.pl-board-row.is-me .pl-rank,
.pl-board-row.is-me .pl-rating { color: var(--selected-soft-ink); }
.pl-board-row.is-me .meta { color: var(--selected-soft-ink); opacity: .78; }
.pl-board-row.is-me .pl-move { color: var(--selected-soft-ink); }
.pl-me {
  flex: none; padding: 3px 7px; font-size: 10px; letter-spacing: .07em;
  background: var(--selected-bg); color: var(--selected-ink);
}

/* The pinned row is a separate list, set apart from the board above it — the viewer is not
   in that slice, and running the two together would read as position 13. */
.pl-pin { margin-top: 10px; }
.pl-pin::before {
  content: ""; display: block; height: 1px; margin: 0 20px 10px; background: var(--rule);
}

/* ═══ 5 · EMPTY STATES ══════════════════════════════════════════════════════ */

/* ⚠ A LABEL AND A CONTROL, NOT A CONSOLATION. shell.css's .empty centres a single line of
   tertiary ink; all this adds is room for the one button that changes the state. */
.pl-empty { display: grid; justify-items: center; gap: 14px; }
.pl-empty-btn { font-size: 15px; padding: 11px 18px; }
.pl-btn-ico { flex: none; display: block; }

/* ═══ 6 · SEARCH ════════════════════════════════════════════════════════════ */

.pl-field {
  display: flex; align-items: center; gap: 8px;
  margin: 0 16px; padding: 0 14px; min-height: 50px;
  background: var(--bg-sunken); border: 1px solid var(--rule); border-radius: var(--r-md);
}
/* ⚠ focus-within, NOT :focus ON THE INPUT. The ring belongs to the box the person sees; put
   it on the input and it draws inside the box — a rounded rectangle inset in another one. */
.pl-field:focus-within {
  border-color: var(--focus);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus) 22%, transparent);
}
.pl-search-ico { flex: none; color: var(--ink-tertiary); display: block; }
.pl-at { flex: none; font: 500 16px/1 var(--font-ui); color: var(--ink-tertiary); }

/* ⚠ 16px MINIMUM, AND IT IS NOT A TASTE CALL. Safari zooms the whole page in when a focused
   input's text is under 16px, and it does not zoom back out — the player is left on a
   sideways-scrolling page wondering what they broke. */
.pl-input {
  flex: 1; min-width: 0; width: 100%;
  border: 0; background: transparent; outline: none; padding: 14px 0;
  font: 400 16px/1.35 var(--font-ui); letter-spacing: -.006em; color: var(--ink);
}
.pl-input::placeholder { color: var(--ink-tertiary); }

.pl-rules {
  margin: 8px 20px 0; max-width: 44ch;
  font: 400 12px/1.5 var(--font-ui); color: var(--ink-tertiary);
}

.pl-out { margin-top: 12px; }
.pl-out .list { margin: 0 16px; }

/* ⚠ THE REFUSAL CARRIES A WORD AS WELL AS A POSITION ON THE SCREEN. "Refused" / "Too many" /
   "No match" / "Keep typing" — the tag is the state, the line beside it is what to do. An
   empty result area with nothing in it reads as a broken search box. */
.pl-refuse {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 16px; padding: 13px 15px;
  background: var(--bg-sunken); border-radius: var(--r-md);
}
.pl-refuse .tag { flex: none; }
.pl-refuse p {
  margin: 0; flex: 1; min-width: 0;
  font: 400 13px/1.45 var(--font-ui); color: var(--ink-tertiary);
}

/* ═══ 7 · FOCUS ═════════════════════════════════════════════════════════════ */

/* ⚠ EVERY CONTROL HERE IS A <button>, SO NOTHING GIVES IT A RING FOR FREE ONCE THE UA
   default is overridden by the background and border above. This app has a public sign-up:
   assume every input method arrives. --focus is theme-aware (navy in light, orange in dark);
   there is no --focus-ring, and writing one resolves to nothing and silently uses a
   fallback — which is how the profile lost its ring in dark mode once already. */
.pl-row:focus-visible,
.pl-preview-btn:focus-visible,
.pl-empty-btn:focus-visible,
.pl-seg button:focus-visible {
  outline: 2px solid var(--focus); outline-offset: -2px; border-radius: var(--r-sm);
}

/* ═══ 8 · WIDE SCREENS ══════════════════════════════════════════════════════ */

/* A desktop browser gets the tablet layout in a readable column, per DESIGN.md — not a
   stretched phone with a leaderboard row a metre wide. */
@container pl-screen (min-width: 700px) {
  .pl-sec .list,
  .pl-sec .empty,
  .pl-seg,
  .pl-field,
  .pl-out .list,
  .pl-refuse,
  .pl-preview { max-width: 620px; margin-left: auto; margin-right: auto; }
  .pl-caption, .pl-rules { max-width: 620px; margin-left: auto; margin-right: auto; }
}

/* ═══ 9 · ACCESSIBILITY ═════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  /* The scope switch's thumb is the only moving thing in this file and it is driven by
     spring() in motion.js, which reads the preference at use time and jumps straight to the
     target. Named here anyway, per DESIGN.md §8: the next animation added to this file needs
     somewhere obvious to be turned down, and an absent block is where one gets forgotten. */
  .pl-seg .thumb { transition: none; }
}

@media (prefers-contrast: more) {
  .pl-preview { border-color: var(--ink); }
  .pl-field { border-color: var(--ink); }
  .pl-board-row.is-me { box-shadow: inset 3px 0 0 var(--selected-edge); }
}
