/* Mondo — Phase 1. Mobile first, 360px and up (FR-6.1). */

:root {
  color-scheme: light dark;
  --bg: #fbfbf9;
  --fg: #16171a;
  --muted: #6b6f76;
  --line: #dfdfd9;
  --card: #f1f1ec;
  --ink: #1d1e22;          /* silhouette fill */
  --ok: #1c7c4a;
  --warn: #8a6d00;
  --err: #b3261e;
  --band-0: #d9d9d3;
  --band-1: #ffd39a;
  --band-2: #ffb454;
  --band-3: #7fd39a;
  --band-4: #2fb36b;

  /* The one colour that means "do this". It cannot be orange or green: the
     five bands above already own warm-to-cold, and a green button beside a
     green "you are close" bar would be saying two different things with one
     colour. Blue is the only hue the game has not spent. */
  --accent: #1f4e8c;
  --accent-ink: #ffffff;
  /* A field is lighter than the page, not the same as it. The old inputs took
     --bg and were invisible until focused. */
  --field: #ffffff;

  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #131417;
    --fg: #e9e9e6;
    --muted: #9aa0a6;
    --line: #2c2e33;
    --card: #1c1d22;
    --ink: #e9e9e6;
    --ok: #6ddc9b;
    --warn: #e6c46a;
    --err: #f2a9a2;
    --band-0: #3a3b40;
    --band-1: #7a5a1e;
    --band-2: #b37a22;
    --band-3: #2f7d4f;
    --band-4: #3fb36f;
    --accent: #7aa9e0;
    --accent-ink: #10131a;
    --field: #1a1b20;
  }
}

/* D-18: an explicit choice made on lisecki.dev wins over the system setting. */
html.light-mode {
  --bg: #fbfbf9; --fg: #16171a; --muted: #6b6f76; --line: #dfdfd9; --card: #f1f1ec; --ink: #1d1e22;
  --ok: #1c7c4a; --warn: #8a6d00; --err: #b3261e;
  --band-0: #d9d9d3; --band-1: #ffd39a; --band-2: #ffb454; --band-3: #7fd39a; --band-4: #2fb36b;
  --accent: #1f4e8c; --accent-ink: #ffffff; --field: #ffffff;
  color-scheme: light;
}
html.dark-mode {
  --bg: #131417; --fg: #e9e9e6; --muted: #9aa0a6; --line: #2c2e33; --card: #1c1d22; --ink: #e9e9e6;
  --ok: #6ddc9b; --warn: #e6c46a; --err: #f2a9a2;
  --band-0: #3a3b40; --band-1: #7a5a1e; --band-2: #b37a22; --band-3: #2f7d4f; --band-4: #3fb36f;
  --accent: #7aa9e0; --accent-ink: #10131a; --field: #1a1b20;
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; } /* wins over display:flex/grid on hidden sections */

body {
  margin: 0;
  /* The bottom padding clears the phone's tab bar, which is fixed. Above 60rem
     the bar is gone and the padding goes back to what it was. */
  padding: 0 1rem calc(4.75rem + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  min-height: 100dvh;
}

header, main, footer { width: 100%; max-width: 30rem; margin: 0 auto; }

/* The bar: a wordmark, the destinations, and everything else behind the avatar.
   It used to carry up to seven links and wrapped onto a second line on a 360px
   phone (found 2026-09-12). Below 60rem the destinations move to the tab bar at
   the bottom of the screen, where a thumb is, and only the wordmark and the
   avatar stay up here. */
header { display: flex; align-items: center; gap: 2rem; padding: .75rem 0; }
.wordmark { font-family: var(--serif); font-size: 1.65rem; font-weight: 500; letter-spacing: -.015em; color: inherit; text-decoration: none; }
header nav { display: none; gap: 1.4rem; align-items: center; }
header nav a, button.link { color: var(--muted); background: none; border: 0; padding: 0; font: inherit; cursor: pointer; text-decoration: none; }
header nav a:hover, button.link:hover { color: var(--fg); text-decoration: underline; }
header nav a[aria-current="page"] { color: var(--fg); font-weight: 600; padding-bottom: .1rem; border-bottom: 2px solid var(--accent); }
header nav a[aria-current="page"]:hover { text-decoration: none; }
/* The wordmark and the destinations read as one group, so the gap between them
   is fixed and it is the account that is pushed to the far edge. */
.head-right { display: flex; align-items: center; gap: .6rem; margin-left: auto; }

/* The streak, where it can be read before the day is over rather than only in
   the panel that appears once it is. */
.streak { display: flex; align-items: baseline; gap: .35rem; margin: 0; padding: .3rem .65rem; background: var(--card); border-radius: 999px; font-size: .8rem; color: var(--muted); white-space: nowrap; }
.streak b { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; color: var(--fg); }

/* The account menu — a <details>, because the platform already ships a
   disclosure that works with a keyboard and closes on Escape. The only script
   is the click-outside in auth-ui.js. */
/* FR-6.6 — the theme switch, sized as the avatar's twin so the two read as a
   pair in the corner rather than a button next to an avatar. */
.theme-btn { flex: none; display: grid; place-items: center; width: 2.25rem; height: 2.25rem; padding: 0; border: 1px solid var(--line); border-radius: 50%; background: var(--card); color: var(--muted); cursor: pointer; }
.theme-btn:hover { border-color: var(--fg); color: var(--fg); }
.theme-btn svg { width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
/* One icon at a time, chosen by the class theme.js toggles. `hidden` is no use
   here: it is an HTMLElement property and an <svg> is not one, so setting it
   from script reflects to nothing. */
.theme-btn .sun { display: none; }
.theme-btn.is-dark .sun { display: block; }
.theme-btn.is-dark .moon { display: none; }

.account { position: relative; flex: none; }
.account > summary { list-style: none; display: grid; place-items: center; width: 2.25rem; height: 2.25rem; border: 1px solid var(--line); border-radius: 50%; background: var(--card); font-size: .8rem; font-weight: 600; cursor: pointer; }
.account > summary::-webkit-details-marker { display: none; }
.account > summary:hover, .account[open] > summary { border-color: var(--fg); }
.account-menu { position: absolute; right: 0; top: calc(100% + .4rem); z-index: 20; min-width: 11rem; display: flex; flex-direction: column; align-items: stretch; gap: .1rem; padding: .35rem; background: var(--bg); border: 1px solid var(--line); border-radius: .6rem; box-shadow: 0 8px 24px rgb(0 0 0 / .18); }
.account-menu a, .account-menu button { font: inherit; text-align: left; padding: .5rem .6rem; border: 0; border-radius: .35rem; background: none; color: inherit; text-decoration: none; cursor: pointer; }
.account-menu a:hover, .account-menu button:hover { background: var(--card); color: inherit; text-decoration: none; }
.account-menu hr { width: 100%; margin: .25rem 0; border: 0; border-top: 1px solid var(--line); }

/* The phone's tab bar: the two things a phone comes here for (FR-6.1). Groups,
   tournaments and the admin panel are one menu away and off the bar on purpose
   — nobody opens Mondo on a bus to rename a group. */
.tabbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 15; display: grid; grid-template-columns: repeat(2, 1fr); padding: .35rem .5rem calc(.35rem + env(safe-area-inset-bottom, 0px)); background: var(--bg); border-top: 1px solid var(--line); }
.tabbar a { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .15rem; min-height: 2.9rem; padding: .25rem; color: var(--muted); text-decoration: none; font-size: .7rem; }
.tabbar a[aria-current="page"] { color: var(--accent); font-weight: 600; }
.tabbar svg { width: 1.35rem; height: 1.35rem; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }

p.sub { margin: 0 0 1.5rem; color: var(--muted); }
p.fine { font-size: .85rem; color: var(--muted); margin: .5rem 0; }
a { color: inherit; }

/* One filled button per screen, and it is the only thing wearing the accent.
   Everything that is a real action but not THE action takes .secondary, which
   is what .primary used to look like. */
button.primary, a.primary {
  font: inherit;
  display: inline-block;
  padding: .6rem 1.1rem;
  border: 1px solid var(--accent);
  border-radius: .5rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}
button.primary:hover:not(:disabled), a.primary:hover { filter: brightness(1.1); }
button.secondary {
  font: inherit;
  padding: .6rem 1rem;
  border: 1px solid var(--line);
  border-radius: .5rem;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
button.secondary:hover:not(:disabled) { border-color: var(--fg); }
button:disabled { opacity: .5; cursor: default; }

/* Silhouette (FR-6.2). The ARTWORK is still a fixed square, so every country is
   drawn at the same visual size; what changed is the card around it, which is
   4:3 because a 30rem square is 480px tall and on a laptop that pushed the
   guess field off the bottom of the screen. */
.silhouette {
  margin: 0 0 1rem; padding: 1rem;
  display: grid; place-items: center; aspect-ratio: 4 / 3;
  background: var(--card); border-radius: .75rem;
}
.silhouette svg { display: block; width: auto; height: 100%; max-width: 100%; aspect-ratio: 1; }
.silhouette svg path { fill: var(--ink); }

/* A flag brings its own colours, so nothing here paints its paths. The box takes
   the flag's own proportions (renderFlag sets aspect-ratio), so the border marks
   where the artwork ends — Yemen's black band and Poland's white one otherwise
   run into the page. */
.flag { margin: 0 0 1rem; }
.flag svg {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  border: 1px solid var(--line);
  border-radius: .25rem;
}

/* D-78 — the photograph of a `person` challenge. A fixed box with the picture
   contained inside it, because these are not a normalised set the way the
   silhouettes are: a 3:4 painted portrait and a 16:9 press photograph both
   arrive here, and a box that resized itself to each would make the prompt
   jump every challenge. `object-fit: contain` on a neutral ground shows the
   whole picture and crops nobody's head off. */
.portrait {
  margin: 0 0 1rem; padding: 1rem;
  display: grid; justify-items: center; gap: .5rem;
  background: var(--card); border-radius: .75rem;
}
.portrait img {
  display: block; width: 100%; height: auto; max-height: 45vh;
  aspect-ratio: 4 / 3; object-fit: contain;
  border-radius: .25rem;
}
.portrait figcaption { text-align: center; }

/* FR-8.7 / D-64, D-72 — the eight options of a multiple-choice challenge:
   flags, or silhouettes.

   Two columns on a phone and four on a desktop: eight flags in one row would be
   under 7rem each at 360px, which is too small to tell Chad from Romania, and
   telling them apart IS the question. The aspect box is the flag's own
   (renderFlag sets it), so a 2:3 vertical flag does not get stretched into the
   shape of its neighbours.

   A struck option keeps its artwork and loses its colour: the player needs to
   remember what they already ruled out, and greying it is how every other
   eliminated thing on a screen looks. The ✗ is there because colour is never
   the only channel (FR-6.7). */
.options { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: .5rem; grid-template-columns: repeat(2, 1fr); }
.option {
  display: block; width: 100%; aspect-ratio: 3 / 2; padding: .4rem;
  background: var(--card); border: 1px solid var(--line); border-radius: .5rem;
  cursor: pointer; font: inherit; color: inherit;
  position: relative;
}
.option svg { display: block; width: 100%; height: 100%; border-radius: .2rem; }
/* A silhouette carries no colour of its own — it is a bare path — so the option
   box paints it exactly as .silhouette does, and without this it would be black
   on a black card in the dark theme. A flag paints itself and is left alone. */
.option svg.shape path { fill: var(--ink); }
.option:not(:disabled):hover, .option:not(:disabled):focus-visible { outline: 2px solid var(--fg); }
/* The artwork dims, the ✗ does not: opacity on the button would fade the mark
   along with the flag under it, and the mark is the part that has to read. */
.option.struck { cursor: default; }
.option.struck svg { opacity: .25; }
.option.struck::after {
  content: "✗"; position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: 2rem; color: var(--fg);
}
/* The reveal: the one that was right, marked the way a solved challenge is. */
.option.right { outline: 2px solid var(--ok); opacity: 1; }
.option:disabled:not(.struck):not(.right) { cursor: default; opacity: .55; }

/* The names under every option, once the challenge is over (D-76). The caption
   is a sibling of the button rather than part of it, so `.option`'s aspect-ratio
   still measures the artwork alone and the tiles do not shrink to make room.
   Long names WRAP rather than truncate: this grid exists to teach, and half of
   "República Democrática do Congo" teaches the wrong string. */
.options li { display: grid; gap: .25rem; align-content: start; }
.option-name {
  margin: 0; font-size: .75rem; line-height: 1.2; text-align: center;
  color: var(--muted); overflow-wrap: anywhere; text-wrap: balance;
}
.option-name.right { color: var(--ok); font-weight: 600; }

/* Guess rows: name · distance · arrow · percent. Colour is never alone (FR-6.7). */
.guesses { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: .4rem; }
.guess {
  display: grid;
  grid-template-columns: 1fr auto 1.5rem 3.2rem;
  gap: .5rem;
  align-items: center;
  padding: .5rem .75rem;
  border-radius: .5rem;
  background: var(--card);
  border-left: .4rem solid var(--band-0);
  font-variant-numeric: tabular-nums;
}
.guess.band-1 { border-left-color: var(--band-1); }
.guess.band-2 { border-left-color: var(--band-2); }
.guess.band-3 { border-left-color: var(--band-3); }
.guess.band-4 { border-left-color: var(--band-4); }
.guess .name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.guess .dist, .guess .pct { font-family: var(--mono); color: var(--muted); text-align: right; }
.guess .dir { text-align: center; font-size: 1.2rem; }

/* Input + listbox (FR-6.3, FR-6.4). */
#guess-form { display: flex; gap: .5rem; align-items: stretch; }
.combo { position: relative; flex: 1; }
#guess-input {
  width: 100%;
  font: inherit;
  padding: .7rem .85rem;
  border: 1px solid var(--line);
  border-radius: .55rem;
  background: var(--field);
  color: inherit;
}
#guess-input:focus { outline: 2px solid var(--fg); outline-offset: 1px; }
/* The number field stands in for the whole combo, so it takes the same room —
   and since D-54 it carries "US$" inside its own border, because the figure was
   always in dollars and nothing on screen said so. `min-width: 0` is what keeps
   the digits from pushing the unit off a 360px viewport (FR-6.1). */
.money { flex: 1; display: flex; align-items: center; gap: .4rem; padding: 0 .85rem; border: 1px solid var(--line); border-radius: .55rem; background: var(--field); }
.money:focus-within { outline: 2px solid var(--fg); outline-offset: 1px; }
.money > span { color: var(--muted); }
.money input { flex: 1; min-width: 0; font: inherit; padding: .6rem 0; border: 0; background: transparent; color: inherit; }
.money input:focus { outline: none; }
#guess-list {
  position: absolute; left: 0; right: 0; top: calc(100% + .25rem); z-index: 2;
  margin: 0; padding: .25rem; list-style: none;
  background: var(--bg); border: 1px solid var(--line); border-radius: .5rem;
  box-shadow: 0 8px 24px rgb(0 0 0 / .15);
  max-height: 16rem; overflow-y: auto;
}
#guess-list li { padding: .5rem .6rem; border-radius: .35rem; cursor: pointer; }
#guess-list li small { color: var(--muted); }
#guess-list li[aria-selected="true"], #guess-list li:hover { background: var(--card); }

#status { min-height: 1.5rem; margin: .5rem 0; white-space: pre-wrap; }
#status.ok { color: var(--ok); }
#status.warn { color: var(--warn); }
#status.err { color: var(--err); }
.err { color: var(--err); }

.result { padding: 1rem; background: var(--card); border-radius: .75rem; }
.result p:first-child { margin-top: 0; font-family: var(--serif); font-size: 1.5rem; font-weight: 500; letter-spacing: -.01em; }

/* The reveal between challenges (D-55) and the "?" beside the counter (D-56). */
.prompt-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.prompt-head p { margin: 0; }
.help-btn {
  flex: none; width: 1.6rem; height: 1.6rem; padding: 0; cursor: pointer;
  font: inherit; font-weight: 600; line-height: 1;
  border: 1px solid var(--line); border-radius: 50%;
  background: transparent; color: var(--muted);
}
.help-btn:hover, .help-btn:focus-visible { color: var(--fg); border-color: var(--fg); }
.reveal { padding: 1rem; margin-bottom: 1rem; background: var(--card); border-radius: .75rem; }
.reveal p { margin: 0 0 .75rem; font-family: var(--serif); font-size: 1.35rem; font-weight: 500; }

/* D-81 — who that was, under the answer. The reveal's own <p> is the verdict,
   set large in serif; this is prose and has to read as prose, so it steps back
   to body size and sits on its own ground to say it came from somewhere else.
   The block is absent, not empty, for the 24 people with no article. */
.reveal .about {
  margin: 0 0 .75rem; padding: .85rem 1rem;
  display: grid; justify-items: start; gap: .6rem;
  background: var(--bg); border: 1px solid var(--line); border-radius: .5rem;
}
.reveal .about p {
  margin: 0;
  font-family: inherit; font-size: 1rem; font-weight: 400; line-height: 1.55;
}
.reveal .about .credit { color: var(--muted); font-size: .8rem; }
/* `.primary` already answered to both a button and a link; `.secondary` only
   ever wore a button until the bio needed one that navigates. */
a.secondary {
  font: inherit; display: inline-block;
  padding: .5rem .9rem;
  border: 1px solid var(--line); border-radius: .5rem;
  background: transparent; color: inherit; text-decoration: none;
}
a.secondary:hover { border-color: var(--fg); }

/* Purely decorative, so reduced motion removes it rather than slowing it. */
.confetti { position: fixed; inset: 0; z-index: 10; overflow: hidden; pointer-events: none; }
.confetti i { position: absolute; top: -5vh; width: .5rem; height: .75rem; border-radius: 1px; animation: confetti-fall linear forwards; }
@keyframes confetti-fall {
  from { transform: translate3d(0, 0, 0) rotate(0deg); opacity: 1; }
  to { transform: translate3d(var(--drift), 105vh, 0) rotate(var(--spin)); opacity: .25; }
}
@media (prefers-reduced-motion: reduce) { .confetti { display: none; } }

footer { margin-top: 2rem; font-size: .85rem; color: var(--muted); }
/* SEC-18: reCAPTCHA's floating badge would sit on the phone's tab bar. Google
   allows hiding it when the page says so in the user flow — the footer line. */
.grecaptcha-badge { visibility: hidden; }
footer .recaptcha { display: block; margin-top: .4rem; font-size: .75rem; }

/* Profile dialog (FR-1.3). */
dialog { border: 1px solid var(--line); border-radius: .75rem; background: var(--bg); color: var(--fg); padding: 1.25rem; width: min(90vw, 22rem); }
dialog::backdrop { background: rgb(0 0 0 / .4); }
dialog h2 { margin: 0 0 .75rem; font-size: 1.1rem; }
dialog label { display: block; font-size: .9rem; margin-bottom: .25rem; }
dialog input { width: 100%; font: inherit; padding: .5rem .6rem; border: 1px solid var(--line); border-radius: .5rem; background: var(--field); color: inherit; }
dialog .actions { display: flex; justify-content: flex-end; gap: 1rem; margin-top: 1rem; align-items: center; }

/* Help dialog (FR-6.9). Wider than the profile one because it is prose, and the
   only way out is the × — which is the point (D-56). */
dialog.help { width: min(92vw, 32rem); }
dialog.help h2 { padding-right: 1.5rem; }
dialog .close {
  position: absolute; top: .5rem; right: .6rem; cursor: pointer;
  font: inherit; font-size: 1.4rem; line-height: 1; padding: .1rem .4rem;
  border: 0; border-radius: .35rem; background: transparent; color: var(--muted);
}
dialog .close:hover, dialog .close:focus-visible { color: var(--fg); background: var(--card); }
#help-body { margin: 0; white-space: pre-wrap; }

/* Prose pages (regras, privacidade). */
main.prose h2 { font-size: 1.15rem; margin-top: 1.75rem; }
/* The scoring table is eight columns; below ~380px it does not fit and scrolled
   the whole page sideways rather than itself (FR-6.1). The wrap is the fix the
   boards already use. */
main.prose .table-wrap { overflow-x: auto; }
main.prose table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
main.prose th, main.prose td { border: 1px solid var(--line); padding: .4rem .6rem; text-align: center; }
main.prose td:first-child, main.prose th:first-child { text-align: left; }

/* Phase 2 — groups, boards, admin (FR-4.6, FR-4.11, FR-7). */
.cards { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: .5rem; }
/* Wrap rather than overflow, like the nav: a tournament called "Almoço de
   terça" beside "em andamento · 3 jogadores · Rodada 1 de 3" is wider than a
   375px card, and `.meta` cannot break (FR-6.1). Found 2026-09-12 — it needed a
   tournament to exist before it showed. */
.cards li a { display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; flex-wrap: wrap; padding: .75rem; background: var(--card); border-radius: .5rem; text-decoration: none; }
.cards li a:hover { outline: 1px solid var(--fg); }
/* `nowrap` kept "3 jogadores" from breaking mid-phrase, but a running
   tournament's meta is three phrases and does not fit a 375px card on one line.
   Breaking at a "·" is better than a horizontal scrollbar (FR-6.1). */
.cards .meta { color: var(--muted); font-size: .9rem; }
.badge { font-size: .75rem; padding: .1rem .4rem; border-radius: .3rem; background: var(--line); color: var(--fg); vertical-align: middle; }
.badge.warn { background: var(--band-1); color: #16171a; }
.row { display: flex; gap: .5rem; align-items: stretch; margin: .5rem 0; flex-wrap: wrap; }
.row input[type="text"] { flex: 1; min-width: 8rem; font: inherit; padding: .6rem .75rem; border: 1px solid var(--line); border-radius: .5rem; background: var(--field); color: inherit; }
.tabs { display: flex; gap: .25rem; margin: 1rem 0 .5rem; }
.tabs button { font: inherit; padding: .4rem .8rem; border: 1px solid var(--line); border-radius: 2rem; background: transparent; color: var(--muted); cursor: pointer; }
.tabs button[aria-selected="true"] { color: var(--fg); border-color: var(--fg); }
.table-wrap { overflow-x: auto; margin: 0 -1rem; padding: 0 1rem; }
table.board { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
table.board th, table.board td { padding: .5rem .5rem; text-align: right; border-bottom: 1px solid var(--line); white-space: nowrap; }
table.board td:not(.name) { font-family: var(--mono); font-size: .9rem; }
table.board th:nth-child(2), table.board td:nth-child(2) { text-align: left; }
table.board th { color: var(--muted); font-weight: 500; font-size: .85rem; }
table.board tr.me td { background: var(--card); font-weight: 600; }
table.board .name { max-width: 10rem; overflow: hidden; text-overflow: ellipsis; }
.today h3 { font-size: .95rem; margin: 1rem 0 .25rem; color: var(--muted); }
.today ul { list-style: none; margin: 0; padding: 0; }
.today li { padding: .3rem 0; border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; gap: .5rem; }
.today li .score { font-family: var(--mono); font-size: .85rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.fast { color: var(--err); font-weight: 600; }
.pending { list-style: none; margin: .5rem 0; padding: 0; font-size: .9rem; }
.pending li { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem; padding: .25rem 0; color: var(--muted); }
.pending li > div { display: flex; gap: .75rem; flex: none; }
/* The invite just minted. A tint rather than a border, so the row does not
   change height and shift the ones under it as it settles. */
.pending li.new { background: var(--card); border-radius: .4rem; padding: .25rem .4rem; }

/* FR-4.12 — the two kinds of invite link, stacked in the dialog. */
.choice { display: flex; gap: .5rem; align-items: baseline; margin-bottom: .6rem; font-size: .95rem; }
.choice input { width: auto; }
select { font: inherit; padding: .2rem .3rem; border: 1px solid var(--line); border-radius: .35rem; background: var(--field); color: inherit; }
/* --- the admin's attempt tables (FR-7.2) ---------------------------------
   A row here is about a person (or about a day), and everything else on it is
   how that went. `.board` gives every cell the same weight and puts column two
   on the left, which was right when column two was the name; here it is the
   score, so both are restated. */
table.board.admin th:first-child, table.board.admin td:first-child { text-align: left; }
table.board.admin th:nth-child(2), table.board.admin td:nth-child(2) { text-align: right; }
table.board.admin td { vertical-align: middle; }
/* The subject of the row, at the weight of a subject, with everything that
   qualifies it rather than measures it underneath. */
table.board.admin td.subject { font-family: inherit; font-size: 1rem; max-width: 16rem; }
td.subject b { font-weight: 600; }
td.subject .sub { display: block; margin-top: .05rem; font-size: .8rem; font-weight: 400; color: var(--muted); }
/* A hairline where the row changes subject: who, then how it went, then what
   you can do about it. */
table.board.admin th.div, table.board.admin td.div { border-left: 1px solid var(--line); }
/* The numbers are read down their columns and line up on the right; a control
   is read from its left edge, like the name is. */
table.board.admin th.ctrl, table.board.admin td.ctrl { text-align: left; }
table.board.admin td.pts { font-size: 1.05rem; font-weight: 600; }
/* A voided day keeps every number it ever had and stops counting, so the row
   has to read as struck rather than as missing (D-82). */
table.board.admin tr.voided > td { color: var(--muted); }
table.board.admin tr.voided > td.pts { text-decoration: line-through; }
/* Muting the row must not mute the evidence. A voided day is exactly the one
   you came to look at, and the fast mark is the reason you are looking. */
table.board.admin tr.voided > td.fast { color: var(--err); }

/* The disclosure that opens the row below. A button rather than a <summary>,
   because <details> cannot span table rows and spanning them is the point. */
.disc { display: inline-flex; align-items: center; gap: .35rem; padding: .25rem 0; border: 0; background: none; font: inherit; font-size: .85rem; color: var(--muted); cursor: pointer; }
.disc:hover { color: var(--fg); }
.disc svg { width: .7rem; height: .7rem; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.disc[aria-expanded="true"] svg { transform: rotate(90deg); }

/* Two actions, one of them destructive. Touching and identical they read as one
   sentence, which is what they were; the rule makes them two and the colour on
   hover makes one of them the dangerous one. */
.acts { display: inline-flex; align-items: center; gap: .6rem; }
.acts .sep { color: var(--line); }
.acts button.danger:hover { color: var(--err); }

/* One row per guess, given the whole width of the row it belongs to rather than
   one cell of it. Every rule still has to say `table.detail` to out-specify
   `table.board td`, which is a descendant selector and reaches in here too. */
table.board.admin tr.detail > td { padding: 0; background: var(--card); }
table.board.admin tr.detail > td > div { padding: .7rem .75rem .9rem; }
table.detail { border-collapse: collapse; width: 100%; }
table.board.admin table.detail th, table.board.admin table.detail td {
  padding: .22rem 1.25rem .22rem 0; text-align: left; border-bottom: 0; white-space: nowrap;
  font-family: inherit; font-size: .85rem;
}
table.board.admin table.detail th { font-weight: 500; color: var(--muted); font-size: .8rem; padding-bottom: .35rem; }
/* A rule where each challenge starts, so the seven blocks are visible without
   the kind having to span rows. */
table.board.admin table.detail tr.start > td { border-top: 1px solid var(--line); }
table.board.admin table.detail td.kind { color: var(--muted); }
/* `.said` and not `.guess`: `.guess` is the game's own guess card, three
   hundred lines up, and it brought its background and its 8px radius in here. */
table.board.admin table.detail td.said { font-family: var(--mono); }
/* The seconds are the column you read down, so they line up and nothing else
   does. This used to be written as `:last-child` and stopped being the seconds
   the moment D-77 added a fifth column beside them. */
table.board.admin table.detail th:nth-child(3), table.board.admin table.detail td.secs {
  text-align: right; padding-right: 1.6rem; font-family: var(--mono); font-variant-numeric: tabular-nums;
}
/* D-77 — the absent claim is the one worth seeing, so it is drawn rather than
   left blank; a claim the clock refuses is drawn louder still. */
table.board.admin table.detail td.claim-none { color: var(--warn); font-style: italic; }
table.board.admin table.detail td.claim-off { color: var(--err); font-weight: 600; }
/* An empty last column takes the slack: four columns handed a whole row spread
   themselves across it, and a guess a hand away from its own time is worse than
   the cramped version this replaced. */
table.board.admin table.detail th:last-child, table.board.admin table.detail td:last-child { width: 100%; padding-right: 0; }

/* The date is this tab's heading, so it sits on the baseline with its picker
   rather than above it. */
.day-head { margin-bottom: .75rem; padding-bottom: .6rem; border-bottom: 1px solid var(--line); }
.day-head .row { align-items: baseline; }
.day-head .title { margin: 0; }
.day-head p { margin: 0; }

/* The long note about how to read the table. It is worth every word the first
   time and nothing after that, so it folds. */
details.note { margin: .25rem 0 1rem; font-size: .9rem; }
details.note > summary { cursor: pointer; color: var(--muted); }
details.note > summary:hover { color: var(--fg); }
details.note p { margin: .5rem 0 0; }

/* Below the breakpoint the board scrolls inside `.table-wrap`, and the first
   thing to go off the left edge is whose row you are reading. */
@media (max-width: 60rem) {
  /* Child combinators throughout: the challenges table is a table inside a cell
     of this one, so a plain descendant selector pins ITS first column too and
     the guesses slide away underneath the kinds. */
  table.board.admin > thead > tr > th:first-child,
  table.board.admin > tbody > tr > td:first-child { position: sticky; left: 0; background: var(--bg); }
  /* The detail row IS its own first cell, and it spans everything: pinning it
     would pin the whole thing to the left edge. */
  table.board.admin > tbody > tr.detail > td { position: static; background: var(--card); }
}
h2.title { font-family: var(--serif); font-size: 1.5rem; font-weight: 500; letter-spacing: -.01em; margin: 0 0 .25rem; }

/* --- tournaments (Phase 3) ------------------------------------------------ */
.stack { display: grid; gap: .5rem; margin: 1rem 0; }
.stack input[type="text"] { font: inherit; padding: .6rem .75rem; border: 1px solid var(--line); border-radius: .5rem; background: var(--field); color: inherit; }
.presets { border: 0; margin: 0; padding: 0; display: grid; gap: .4rem; }
.preset { display: grid; grid-template-columns: auto 1fr; gap: .1rem .5rem; align-items: baseline; padding: .6rem .75rem; background: var(--card); border-radius: .5rem; cursor: pointer; }
.preset input { grid-row: span 2; }
.preset .fine { grid-column: 2; margin: 0; }
.preset:has(input:checked) { outline: 1px solid var(--fg); }

/* Pills that toggle: practice's continents (FR-9.9) and Batata Quente's queue. */
.chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.chips button {
  font: inherit; font-size: .9rem; padding: .3rem .7rem; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px; background: transparent; color: var(--muted);
}
.chips button:hover, .chips button:focus-visible { color: var(--fg); border-color: var(--fg); }
.chips button[aria-pressed="true"] { background: var(--field); border-color: var(--fg); color: var(--fg); font-weight: 600; }
/* "?" and the leave button share the right of the prompt head. */
.head-actions { display: flex; gap: .75rem; align-items: center; }

.capital-prompt { font-family: var(--serif); font-size: 2.1rem; font-weight: 500; letter-spacing: -.01em; text-align: center; padding: 2.25rem 1rem; background: var(--card); border-radius: .75rem; margin: 0 0 1rem; }
.card-items { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .25rem; font-size: .9rem; }
/* An empty guess list and an empty hint line must not hold a day's worth of
   space open once the prompts above them are gone. */
.guesses:empty { margin: 0; }
.card-item { display: grid; grid-template-columns: 1.5rem 1fr auto; gap: .5rem; padding: .3rem 0; border-bottom: 1px solid var(--line); color: var(--muted); }
.card-item.current { color: var(--fg); font-weight: 600; }
.card-item .score { font-family: var(--mono); }
.card-item.solved .score { color: var(--ok); }
.card-item .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#card-form { display: flex; gap: .5rem; align-items: stretch; }
#card-input { width: 100%; font: inherit; padding: .7rem .85rem; border: 1px solid var(--line); border-radius: .55rem; background: var(--field); color: inherit; }
#card-input:focus { outline: 2px solid var(--fg); outline-offset: 1px; }
#card-datalist { position: absolute; z-index: 5; left: 0; right: 0; top: calc(100% + .25rem); list-style: none; margin: 0; padding: .25rem; background: var(--bg); border: 1px solid var(--line); border-radius: .5rem; max-height: 14rem; overflow-y: auto; }
#card-datalist li { padding: .5rem .6rem; border-radius: .35rem; cursor: pointer; }
#card-datalist li small { color: var(--muted); }
#card-datalist li[aria-selected="true"], #card-datalist li:hover { background: var(--card); }
.today h4 { font-size: .95rem; margin: 1rem 0 .25rem; color: var(--muted); font-weight: 600; }

/* Tournament fixtures (docs/06-tournaments.md §6.2). One line per pairing;
   the winner is marked, never the score — under `match` the score does not
   carry, so leading with it would say the wrong thing (D-49). */
.fixtures { list-style: none; margin: 0 0 .8rem; padding: 0; }
.fixtures li { padding: .3rem 0; border-bottom: 1px solid var(--line); font-variant-numeric: tabular-nums; }
#fixture-rounds h4 { margin: .8rem 0 .2rem; color: var(--muted); font-size: .85rem; font-weight: 600; }
/* A knocked-out player stays in the table (consolation, D-47) but should not
   read as if they were still in the bracket. */
.board tr.out td { color: var(--muted); }
.bracket-tag { display: inline-block; min-width: 7.5rem; color: var(--muted); font-size: .8rem; }


/* --- the rail: where you are in what you are doing -------------------------
   One markup, two shapes. On a phone it is a strip of N boxes above the prompt
   — the score if that challenge is done, its number if it is the one you are on,
   a dot if it is still to come. On a desktop the same list is the left column,
   one row per challenge carrying its name as well. The day used to be a list
   UNDER the guess form, which meant scrolling past the prompt to find out where
   you were. */
.rail { min-width: 0; }
.rail-head, .rail-total { display: none; }
.day { list-style: none; margin: 0 0 .9rem; padding: 0; display: grid; gap: .35rem; grid-auto-flow: column; grid-auto-columns: 1fr; }
.step {
  display: grid; place-items: center; min-height: 2.3rem; padding: .2rem;
  background: var(--card); border-radius: .5rem;
  font-family: var(--mono); font-size: .85rem; color: var(--muted);
}
.step .step-name { display: none; }
.step .step-n { display: none; }
.step.current .step-n { display: block; }
.step.current .step-score { display: none; }
.step.pending .step-score::after { content: "\00b7"; }
.step.current { background: var(--field); box-shadow: inset 0 0 0 2px var(--accent); color: var(--accent); font-weight: 600; }
.step.solved .step-score { color: var(--ok); font-weight: 600; }
.step.failed .step-score { color: var(--fg); }

/* A group's heading: name on the left, the owner's two buttons on the right. */
.board-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.board-head .row { margin: 0; }

/* The one tournament in this group that is waiting on somebody, at the top of
   the panel the group page already had. It used to be a link on a line of fine
   print under the board. */
.round-card { display: grid; gap: .25rem; padding: 1rem; margin: 0 0 1.5rem; background: var(--card); border-radius: .75rem; text-decoration: none; color: inherit; }
.round-card:hover { outline: 1px solid var(--fg); }
.round-card .kicker { font-family: var(--mono); font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.round-card .name { font-family: var(--serif); font-size: 1.3rem; font-weight: 500; }
.round-card .meta { font-size: .85rem; color: var(--muted); margin-bottom: .6rem; }
.round-card .cta { padding: .55rem 1rem; border-radius: .5rem; background: var(--accent); color: var(--accent-ink); font-size: .9rem; font-weight: 600; text-align: center; }

/* The sections of a group page (FR-4, FR-5). Tournaments and members were two
   more pages and one more "which group?" screen; they are tabs. */
.subtabs { display: flex; align-items: center; gap: 1.4rem; margin: 1.1rem 0 1.25rem; border-bottom: 1px solid var(--line); overflow-x: auto; }
.subtabs button { font: inherit; white-space: nowrap; padding: 0 0 .65rem; border: 0; border-bottom: 2px solid transparent; background: none; color: var(--muted); cursor: pointer; }
.subtabs button:hover { color: var(--fg); }
.subtabs button[aria-selected="true"] { color: var(--fg); font-weight: 600; border-bottom-color: var(--accent); }
.subtabs .count { font-family: var(--mono); font-size: .8rem; color: var(--muted); margin-left: .35rem; }

/* The day, once it is over: the five answers, which until now were a grey list
   the result screen pushed off the bottom of a phone. */
.recap { list-style: none; margin: .9rem 0 1.1rem; padding: 0; }
.recap li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .75rem; align-items: baseline; padding: .55rem 0; border-bottom: 1px solid var(--line); }
.recap .kind { display: block; font-family: var(--mono); font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.recap .answer { display: block; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recap .score { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; color: var(--muted); }
.recap li.solved .score { color: var(--ok); }

/* ---------------------------------------------------------------------------
   Desktop (D-57, FR-6.11)
   ---------------------------------------------------------------------------
   Everything that changes a LAYOUT lives inside the media query below. Under
   60rem the computed style is what it was before this file grew a second half,
   which is the point: the phone view tested well with real players on
   2026-09-11 and there was no reason to move it.

   The two components the panels need are declared outside the query only to be
   turned OFF; they turn on inside it. */

.side { display: none; }
.stats { display: none; }

@media (min-width: 60rem) {
  /* One wider shell. Prose keeps its 30rem measure — a 480px line length is
     good for reading, and widening it would be the one change here that makes
     something worse. */
  header, main:not(.prose), footer { max-width: 68rem; }

  /* No tab bar up here: the three destinations are back in the top bar, where
     there is room for them. */
  body { padding-bottom: 2rem; }
  header nav { display: flex; }
  .tabbar { display: none; }

  /* The admin panel is four tables of timing data read by one person on a
     desktop, so it gets the widest shell in the app. `:has()` is the whole
     mechanism; where it is unsupported the page falls back to 68rem, which is
     degradation rather than breakage. */
  body:has(#panel) header,
  body:has(#panel) main,
  body:has(#panel) footer { max-width: 84rem; }

  /* A sign-in box stretched to 68rem reads as a bug. */
  #signed-out { max-width: 34rem; }

  /* The three play screens are 15rem of rail, 30rem of column and 19rem of
     panel, which with the gaps is 69rem — not 68. So each gets a shell the
     width of its own content and the bar, the game and the footer line up on
     both edges. */
  body:has(#game) header,
  body:has(#game) main,
  body:has(#game) footer { max-width: 69rem; }
  /* torneios.html is two pages in one: the card wants the game's shell and the
     detail view wants the ordinary one. `:not([hidden])` is what makes that
     live — the same attribute the page already toggles picks the shell. */
  body:has(#card:not([hidden])) header,
  body:has(#card:not([hidden])) main,
  body:has(#card:not([hidden])) footer { max-width: 69rem; }
  #game, #card { max-width: 69rem; }

  /* Rail, game, panel. Exactly three grid children on every play screen, so
     placement is deterministic and nothing auto-flows into the wrong column. */
  #game, #card {
    display: grid;
    grid-template-columns: 15rem minmax(0, 30rem) minmax(16rem, 19rem);
    gap: 2.5rem;
    align-items: start;
  }
  .play-col { min-width: 0; }
  .rail { position: sticky; top: 1rem; }
  /* FR-8.7: four across rather than two. The column is 30rem, so each flag is
     about 7rem — the size the phone gives them at two across. */
  .options { grid-template-columns: repeat(4, 1fr); }

  /* The rail, in its other shape: a row per challenge, with its name. */
  .rail-head { display: block; }
  .rail-head h1 { font-family: var(--serif); font-size: 1.5rem; font-weight: 500; letter-spacing: -.01em; line-height: 1.15; margin: 0; }
  .rail-head .fine { margin: .4rem 0 0; }
  .day { display: flex; flex-direction: column; gap: 0; margin: 1.1rem 0 0; }
  .step {
    grid-template-columns: 1.4rem minmax(0, 1fr) auto; gap: .6rem;
    place-items: center stretch; align-items: center;
    min-height: 0; padding: .5rem 0; background: none; border-radius: 0;
    border-bottom: 1px solid var(--line); font-family: inherit; font-size: .9rem;
  }
  .step .step-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .step .step-name::first-letter { text-transform: uppercase; }
  .step .step-n {
    display: grid; place-items: center; width: 1.4rem; height: 1.4rem;
    border: 1px solid var(--line); border-radius: 50%;
    font-family: var(--mono); font-size: .7rem; color: var(--muted);
  }
  .step.solved .step-n, .step.failed .step-n { background: var(--fg); border-color: var(--fg); color: var(--bg); }
  .step .step-score { display: block; font-family: var(--mono); font-size: .85rem; text-align: right; }
  .step.pending .step-score::after { content: "\2014"; }
  .step.current {
    background: var(--card); box-shadow: none; color: var(--fg); font-weight: 600;
    border-bottom-color: transparent; border-left: 3px solid var(--accent);
    border-radius: 0 .5rem .5rem 0; padding: .5rem .6rem .5rem .75rem; margin-left: -.9rem;
  }
  .step.current .step-n { border: 2px solid var(--accent); color: var(--accent); background: none; }
  .step.current .step-score { color: var(--accent); }
  .rail-total {
    display: flex; align-items: baseline; justify-content: space-between; gap: .5rem;
    margin-top: .9rem; padding-top: .7rem; border-top: 1px solid var(--fg);
    font-size: .9rem; font-weight: 600;
  }
  .rail-total b { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 1.05rem; }



  .side { display: block; min-width: 0; position: sticky; top: 1rem; }
  .side .title { font-family: inherit; font-size: .95rem; color: var(--muted); font-weight: 600; }
  .side .today h3 { margin-top: .75rem; }
  .side select {
    font: inherit; width: 100%; margin: .25rem 0 .5rem;
    padding: .4rem .55rem; border: 1px solid var(--line); border-radius: .5rem;
    background: var(--field); color: inherit;
  }

  /* Four counters, label left and number right, on the same tabular figures
     the boards use so the column edge stays straight. */
  .stats { display: grid; grid-template-columns: 1fr auto; gap: .25rem .5rem; margin: 1rem 0 0; }
  .stats dt { color: var(--muted); font-size: .9rem; }
  .stats dd { margin: 0; font-family: var(--mono); text-align: right; font-variant-numeric: tabular-nums; }

  /* Boards: the tables stop scrolling sideways and simply fit. */
  .table-wrap { overflow-x: visible; margin: 0; padding: 0; }
  table.board .name { max-width: 20rem; }

  /* A group board is a table and a live panel, not a table with a panel under
     it. Same shape as the game, for the same reason. */
  .board-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 20rem);
    gap: 2.5rem;
    align-items: start;
  }
  .board-main { min-width: 0; }
  .board-cols #today { position: sticky; top: 1rem; }

  /* The tournament detail: standings wide, the open round beside them.
     `#round` is FIRST in the DOM because that is where a phone wants it, so
     here it is the one child that gets placed explicitly. Every rule that
     moves it is inside this query; below 60rem the document order stands. */
  .detail-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem);
    gap: 2.5rem;
    align-items: start;
  }
  .detail-cols > #round { grid-column: 2; grid-row: 1; position: sticky; top: 1rem; }
  .detail-main { grid-column: 1; grid-row: 1; min-width: 0; }
}

/* ---------------------------------------------------------------------------
   Versus — FR-12 (sala.html). The room is the play column and a scoreboard;
   the scoreboard stacks under the game on a phone, like practice's run list.
   --------------------------------------------------------------------------- */
.modes { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: .5rem; }
.modes button {
  width: 100%; text-align: left; font: inherit; padding: .75rem 1rem; cursor: pointer; display: grid; gap: .2rem;
  border: 1px solid var(--line); border-radius: .75rem; background: transparent; color: var(--fg);
}
.modes button:hover, .modes button:focus-visible { border-color: var(--fg); }
.modes button[aria-pressed="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.modes .name { font-weight: 600; }

/* The countdown. A bar, because a number that changes ten times a second is
   noise; the seconds are printed beside it once a second. */
.timer { height: .35rem; background: var(--line); border-radius: 1rem; overflow: hidden; margin: 0 0 .5rem; }
.timer span { display: block; height: 100%; width: 100%; background: var(--accent); }
.timer span.low { background: var(--err); }
.clock { font-family: var(--mono); text-align: right; margin: 0 0 .5rem; min-height: 1.2em; }

.scoreboard { list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: .25rem; }
.board-row { display: grid; grid-template-columns: .8rem 1fr auto; gap: .5rem; align-items: center; padding: .35rem 0; border-bottom: 1px solid var(--line); }
.board-row .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-row .score { font-family: var(--mono); }
.board-row.me .name { font-weight: 600; }
.board-row.turn { background: var(--card); }
.board-row.out { color: var(--muted); text-decoration: line-through; }
.dot { width: .8rem; height: .8rem; border-radius: 50%; display: inline-block; }
.card-item .dot { align-self: center; }

/* Batata Quente. The map is the payoff, so it takes the column's full width;
   each named country fades in, in the colour of whoever named it. The borders
   are a hairline in SCREEN pixels, so a small grey gap between two coloured
   neighbours stays visible however far the map is zoomed (D-95). */
.potato-actions { display: flex; justify-content: flex-end; }
.potato-turn { font-family: var(--serif); font-size: 1.3rem; }
.turn-pill { display: flex; align-items: center; gap: .6rem; margin: 0; padding: .75rem .9rem .25rem; }
.turn-pill .dot { flex: none; }
.turn-pill.mine .potato-turn { color: var(--accent); font-weight: 500; }
.turn-pill .secs { margin-left: auto; font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 1.2rem; font-weight: 600; }
.world { position: relative; margin: .5rem 0; background: var(--card); border-radius: .75rem; overflow: hidden; }
.world .timer { position: absolute; top: 0; left: 0; right: 0; margin: 0; height: 4px; border-radius: 0; }
.world svg { display: block; width: 100%; height: auto; }
.world .land { fill: var(--band-0); }
.world .named { stroke: var(--card); stroke-width: .75px; vector-effect: non-scaling-stroke; animation: named-in .6s ease-out; }
.world .named.latest { stroke: var(--fg); stroke-width: 1.5px; stroke-linejoin: round; }
@keyframes named-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .world .named { animation: none; } }
.world .zoom, .world .zoom-hint { display: none; }

/* The dock: one field, the ONE queued name beside it (D-95), and a line that
   says what Enter will do. */
.dock-bar { display: flex; align-items: center; gap: .4rem; padding: .3rem; background: var(--field); border: 1px solid var(--line); border-radius: .75rem; }
.dock.mine .dock-bar { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.dock-bar input { flex: 1; min-width: 0; height: 2.75rem; padding: 0 .4rem; border: 0; outline: 0; background: none; color: inherit; font: inherit; font-size: 1.05rem; }
.dock-bar button[type="submit"] { flex: none; min-height: 2.75rem; }
#potato-hint { margin: .4rem 0 0; }
#potato-hint.warn { color: var(--warn); }
#potato-hint.err { color: var(--err); }
.slot { flex: 0 1 auto; min-width: 0; max-width: 45%; display: flex; align-items: center; gap: .5rem; min-height: 2.75rem; padding: 0 .2rem 0 .7rem; background: var(--card); border: 1px solid var(--line); border-radius: .6rem; }
.slot .glyph { flex: none; display: grid; }
.slot svg { width: 1.1rem; height: 1.1rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.slot .text { min-width: 0; display: flex; flex-direction: column; line-height: 1.15; }
.slot .text .label { font-size: .6rem; }
.slot b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.slot.empty { padding: 0 .75rem; background: none; border-style: dashed; color: var(--muted); }
.slot.empty .label { font: inherit; font-size: .8rem; letter-spacing: 0; text-transform: none; }
.slot.armed .glyph { color: var(--accent); animation: armed 1.6s ease-in-out infinite; }
.slot.replacing { opacity: .6; }
.slot.replacing b { text-decoration: line-through; }
.slot.sending { padding-right: .8rem; background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.slot.sending .label { color: inherit; opacity: .8; }
.slot.landed { padding-right: .8rem; background: color-mix(in srgb, var(--c) 16%, var(--bg)); border-color: var(--c); }
.slot.landed .glyph { color: var(--c); }
.slot .pts { font-family: var(--mono); }
.slot .unqueue { flex: none; display: grid; place-items: center; width: 1.9rem; height: 1.9rem; padding: 0; border: 0; border-radius: .4rem; background: none; color: var(--muted); cursor: pointer; }
.slot .unqueue:hover { color: var(--fg); background: var(--bg); }
.slot .unqueue svg { width: .9rem; height: .9rem; }
@keyframes armed { 50% { opacity: .4; } }
@media (prefers-reduced-motion: reduce) { .slot.armed .glyph { animation: none; } }

/* The side: how far the map has come, the board, and every country named,
   newest first, in a box of its own. */
.rail-only { display: none; }
.potato-head { margin: 0 0 1rem; }
.potato-head p { margin: 0 0 .25rem; }
.potato-title { font-family: var(--serif); font-size: 1.3rem; font-weight: 500; }
.meter { height: 4px; margin-top: .4rem; background: var(--line); border-radius: 4px; overflow: hidden; }
.meter span { display: block; height: 100%; background: var(--fg); }
#feed-title { margin: 1.25rem 0 0; }
.feed { margin-top: .4rem; max-height: 16rem; overflow-y: auto; align-content: start; }
.feed small { color: var(--muted); }
#room-link { flex: 1; min-width: 8rem; font: inherit; font-family: var(--mono); font-size: .85rem; padding: .6rem .75rem; border: 1px solid var(--line); border-radius: .5rem; background: var(--field); color: inherit; }

@media (min-width: 60rem) {
  body:has(#room-cols) header,
  body:has(#room-cols) main,
  body:has(#room-cols) footer { max-width: 69rem; }
  #room-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(16rem, 19rem); gap: 2.5rem; align-items: start; }
  #room-side { position: sticky; top: 1rem; }

  /* D-95 — a Batata Quente game is the whole window: the side on the left, the
     map everywhere else, and the turn, the clock, the zoom and the field
     floating over it. Nothing scrolls but the list of countries. */
  body.potato-room { height: 100dvh; min-height: 0; padding: 0; overflow: hidden; }
  body.potato-room > header, body.potato-room > footer, body.potato-room > .tabbar { display: none; }
  body.potato-room:has(#room-cols) > main { max-width: none; height: 100%; }
  body.potato-room #room-cols { height: 100%; grid-template-columns: 19rem minmax(0, 1fr); gap: 0; align-items: stretch; }
  body.potato-room #room-side { grid-column: 1; grid-row: 1; position: static; min-height: 0; display: flex; flex-direction: column; padding: 1rem 1rem .9rem; border-right: 1px solid var(--line); }
  body.potato-room .rail-only { display: block; }
  body.potato-room #room-side > .wordmark { margin: 0 0 1rem; line-height: 1.2; }
  body.potato-room #board-title { margin: 0; font-family: var(--mono); font-size: .7rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
  body.potato-room .feed { flex: 1 1 0; min-height: 0; max-height: none; }
  body.potato-room .rail-foot { margin-top: auto; padding-top: .8rem; border-top: 1px solid var(--line); font-size: .8rem; color: var(--muted); }
  body.potato-room .rail-foot p { margin: 0 0 .3rem; }
  body.potato-room .rail-foot a { color: var(--muted); }
  body.potato-room .rail-foot a:hover { color: var(--fg); }
  body.potato-room .rail-foot .recaptcha { display: block; font-size: .7rem; line-height: 1.4; }
  body.potato-room .play-col { grid-column: 2; grid-row: 1; position: relative; min-height: 0; }
  body.potato-room #potato { position: absolute; inset: 0; }
  /* In the side's top corner, across from the wordmark. */
  body.potato-room .potato-actions { position: absolute; top: 1.35rem; right: calc(100% + 1rem); z-index: 2; }
  body.potato-room .world { position: absolute; inset: 0; margin: 0; border-radius: 0; cursor: grab; touch-action: none; }
  body.potato-room .world.grabbing { cursor: grabbing; }
  body.potato-room .world svg { height: 100%; }
  body.potato-room .turn-pill,
  body.potato-room .world .zoom,
  body.potato-room .dock-bar { background: color-mix(in srgb, var(--bg) 92%, transparent); border: 1px solid var(--line); border-radius: .75rem; box-shadow: 0 6px 24px rgb(0 0 0 / .1); backdrop-filter: blur(6px); }
  body.potato-room .turn-pill { position: absolute; top: 1.25rem; left: 1.25rem; padding: .55rem 1rem .55rem .85rem; gap: .8rem; cursor: default; }
  body.potato-room .potato-turn { font-size: 1.35rem; }
  body.potato-room .turn-pill .secs { min-width: 2.6ch; text-align: right; font-size: 1.35rem; }
  body.potato-room .world .zoom { position: absolute; top: 1.25rem; right: 1.25rem; display: flex; flex-direction: column; padding: .2rem; }
  body.potato-room .zoom button { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; padding: 0; border: 0; border-radius: .5rem; background: none; color: var(--fg); cursor: pointer; }
  body.potato-room .zoom button:hover { background: var(--card); }
  body.potato-room #zoom-fit { border-top: 1px solid var(--line); border-radius: 0 0 .5rem .5rem; }
  body.potato-room .zoom svg { width: 1.1rem; height: 1.1rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  body.potato-room .world .zoom-hint { display: block; position: absolute; right: 1.5rem; bottom: 7.5rem; margin: 0; font-size: .75rem; color: var(--muted); pointer-events: none; transition: opacity .6s; }
  body.potato-room .world .zoom-hint.used { opacity: 0; }
  body.potato-room .dock { position: absolute; left: 0; right: 0; bottom: 1.5rem; display: flex; flex-direction: column; align-items: center; padding: 0 1.5rem; pointer-events: none; }
  body.potato-room .dock > * { pointer-events: auto; }
  body.potato-room .dock-bar { width: min(47.5rem, 100%); padding: .4rem; border-radius: .9rem; }
  body.potato-room .dock.mine .dock-bar { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent), 0 8px 28px rgb(0 0 0 / .12); }
  body.potato-room #potato-hint { margin-top: .5rem; padding: .1rem .6rem; border-radius: .4rem; background: color-mix(in srgb, var(--card) 85%, transparent); text-align: center; }
  /* The end: the map stays, and the result sits where the field was. */
  body.potato-room #finished { position: absolute; left: 50%; bottom: 1.5rem; z-index: 3; transform: translateX(-50%); width: min(36rem, calc(100% - 3rem)); max-height: calc(100% - 3rem); overflow-y: auto; border: 1px solid var(--line); box-shadow: 0 8px 28px rgb(0 0 0 / .15); }
  body.potato-room #status { position: absolute; top: 1.25rem; left: 50%; z-index: 3; transform: translateX(-50%); margin: 0; }
}

/* ---------------------------------------------------------------------------
   Treinar — FR-9 (praticar.html). Two screens. The chooser is a grid of cards,
   because the kinds keep growing and a list beside the game did not have room
   for them. A run is exactly the height of the window: nothing on it scrolls
   the page, and the two things that grow — the guesses and the history — scroll
   inside their own boxes, newest first.
   --------------------------------------------------------------------------- */
.label { margin: 0; font-family: var(--mono); font-size: .7rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }

.choose-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1rem 2rem; margin: .5rem 0 1.5rem; }
.choose-head h1 { margin: 0 0 .35rem; font-family: var(--serif); font-size: 2.2rem; font-weight: 500; letter-spacing: -.02em; line-height: 1; }
.choose-head .fine { margin: 0; max-width: 34rem; }
.filter .label { margin-bottom: .4rem; }
#choose > h2.label { margin: 1.5rem 0 .6rem; }
.kinds { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(9.5rem, 100%), 1fr)); gap: .6rem; }
.kind-card {
  position: relative; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: flex-start; gap: .4rem;
  padding: .85rem; text-align: left; font: inherit; color: inherit; cursor: pointer;
  background: var(--field); border: 1px solid var(--line); border-radius: .75rem;
  transition: border-color .15s, transform .15s;
}
.kind-card:hover:not(:disabled), .kind-card:focus-visible { border-color: var(--accent); }
.kind-card:hover:not(:disabled) { transform: translateY(-2px); }
.kind-icon { display: grid; place-items: center; width: 2.25rem; height: 2.25rem; border-radius: .5rem; background: var(--card); color: var(--accent); }
.kind-icon svg { width: 1.3rem; height: 1.3rem; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.kind-card .name { font-family: var(--serif); font-size: 1.2rem; font-weight: 500; line-height: 1.15; }
.kind-card .name::first-letter { text-transform: uppercase; }
/* The question a kind asks is the name, said longer: a phone has no room to
   show both, so it is only read out there. */
.kind-card .fine { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; margin: 0; }
.kind-card .tag { margin-top: auto; font-family: var(--mono); font-size: .65rem; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  .kind-card { transition: none; }
  .kind-card:hover:not(:disabled) { transform: none; }
}

/* A run fills the window and no more. The footer and the tab bar step aside
   for it; "← Tipos" is the way out. */
body.playing { height: 100dvh; min-height: 0; display: flex; flex-direction: column; overflow: hidden; padding-bottom: 0; }
body.playing > header { flex: none; }
body.playing > footer, body.playing > .tabbar { display: none; }
body.playing > main { flex: 1; min-height: 0; display: flex; flex-direction: column; }
body.playing #run-cols { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; gap: .6rem; }

.run-bar { display: flex; align-items: center; gap: .6rem; padding-bottom: .6rem; border-bottom: 1px solid var(--line); }
.back { flex: none; display: inline-flex; align-items: center; justify-content: center; gap: .25rem; min-width: 2.75rem; min-height: 2.75rem; border: 1px solid var(--line); border-radius: 999px; color: inherit; text-decoration: none; font-size: .9rem; }
.back:hover { border-color: var(--fg); }
.back svg { width: 1.1rem; height: 1.1rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.back span { display: none; }
.run-title { flex: 1; min-width: 0; }
.run-title h1 { margin: 0; font-family: var(--serif); font-size: 1.3rem; font-weight: 500; line-height: 1.1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.run-title h1::first-letter { text-transform: uppercase; }
.run-title .fine { margin: 0; font-size: .75rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.run-sub #progress { font-family: var(--mono); }
.run-sub #progress:not([hidden]) + #run-where::before { content: " · "; }
.run-bar .head-actions { gap: .5rem; }
.run-bar #leave-btn { display: none; }

body.playing .play-col { min-height: 0; display: flex; flex-direction: column; }
body.playing #status { flex: none; min-height: 0; margin: .25rem 0 0; }
body.playing #status:empty { display: none; }
#train { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: .6rem; }
.stage { flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column; gap: .6rem; overflow-y: auto; }
.stage > * { margin: 0; }
.stage .capital-prompt { padding: 1rem; font-size: 1.4rem; }
.stage .silhouette { aspect-ratio: auto; height: min(32dvh, 18rem); padding: .75rem; }
.stage .flag { width: min(100%, 30dvh * 1.5); margin-inline: auto; }
.stage .portrait { padding: .6rem; }
.stage .portrait img { max-height: 20dvh; }
/* A board has no field and no rows: the options are the answer, and they get
   the room. */
#train.pick .stage { flex: 1 1 auto; }
#train.pick .capital-prompt { padding: .9rem 1rem; font-size: 1.35rem; }
/* The column a guess is typed into never gives up its field, nor the newest
   guess under it (the floor is field + one row + give-up): the stage above
   shrinks first (and scrolls, if a long name and a photo still do not fit).
   It does not scroll itself, or the suggestions under the field would be
   clipped at its edge; only a reveal, which has no field, may. */
.answer-col { flex: 1 1 0; min-height: 11.5rem; display: flex; flex-direction: column; gap: .5rem; }
#train.pick .answer-col { flex: none; min-height: 0; }
#train.revealing .answer-col { overflow-y: auto; }
.answer-col > * { margin: 0; }
.answer-col .guesses { flex: 1 1 0; min-height: 0; overflow-y: auto; align-content: start; }
#train.revealing .answer-col .guesses { flex: none; max-height: 12rem; }
.giveup { flex: none; width: 100%; min-height: 2.75rem; }

/* The run so far. On a phone, a bar under the game that opens as a sheet. */
#run-side { margin: 0 -1rem; border-top: 1px solid var(--line); background: var(--field); }
.run-toggle {
  width: 100%; display: flex; align-items: center; gap: .6rem; min-height: 3.25rem;
  padding: .5rem 1rem calc(.5rem + env(safe-area-inset-bottom, 0px));
  border: 0; background: none; font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.run-toggle .label { flex: none; }
.run-last { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .9rem; color: var(--muted); }
.run-toggle svg { flex: none; width: 1.2rem; height: 1.2rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.run-panel { display: none; }
@media (max-width: 59.99rem) {
  #run-side.open::before { content: ""; position: fixed; inset: 0; z-index: 25; background: rgb(0 0 0 / .35); }
  #run-side.open .run-panel {
    display: flex; flex-direction: column; gap: .75rem;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 26; height: 65dvh;
    padding: 1rem 1rem calc(1rem + env(safe-area-inset-bottom, 0px));
    background: var(--bg); border-radius: 1rem 1rem 0 0; box-shadow: 0 -8px 24px rgb(0 0 0 / .18);
  }
}
.run-panel-head { display: flex; align-items: center; justify-content: space-between; }
.sheet-close { width: 2.75rem; height: 2.75rem; margin: -.5rem -.5rem -.5rem 0; padding: 0; border: 0; background: none; color: var(--muted); font: inherit; font-size: 1.5rem; cursor: pointer; }
.run-stats { margin: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .4rem; }
.run-stats div { min-width: 0; padding: .5rem .6rem; border-radius: .5rem; background: var(--card); }
.run-stats dt { font-family: var(--mono); font-size: .6rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.run-stats dd { margin: 0; font-family: var(--mono); font-size: 1.1rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.run-list { margin: 0; flex: 1; min-height: 0; overflow-y: auto; align-content: start; }
.end-sheet { flex: none; min-height: 2.75rem; }

@media (min-width: 60rem) {
  .choose-head h1 { font-size: 2.75rem; }
  .kinds { grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); gap: 1rem; }
  .kind-card { padding: 1rem 1.1rem; gap: .45rem; }
  .kind-card .fine { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; font-size: .875rem; }
  .kind-card .name { font-size: 1.35rem; }

  body.playing { padding-bottom: 1.25rem; }
  body.playing > header, body.playing > main { max-width: 76rem; }
  body.playing #run-cols { grid-template-columns: minmax(0, 1fr) 19rem; grid-template-rows: auto minmax(0, 1fr); gap: 1.25rem 1.5rem; }
  .run-bar { grid-column: 1 / -1; padding-bottom: .9rem; }
  .back { padding: 0 .9rem 0 .6rem; }
  .back span { display: inline; }
  .run-title h1 { font-size: 1.75rem; }
  .run-title .fine { font-size: .85rem; }
  .run-bar #leave-btn { display: inline-block; }

  #train { display: grid; grid-template-columns: minmax(0, 1fr) minmax(18rem, 22rem); gap: 1.25rem; padding: 1.25rem; border: 1px solid var(--line); border-radius: 1rem; }
  #train.pick { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
  /* A reveal has no artwork left to show (only a board keeps its prompt), so
     the verdict and the guesses that led to it take the whole panel. */
  #train.revealing:not(.pick) { grid-template-columns: minmax(0, 1fr); }
  #train.revealing:not(.pick) .stage { display: none; }
  #train.pick .capital-prompt { padding: 1rem; font-size: 1.6rem; }
  /* The stage is the size the grid gives it, and the artwork is sized from
     that — a flag is as wide as its height allows. */
  .stage { container-type: size; }
  .stage .silhouette, .stage .portrait { flex: 1; min-height: 0; height: auto; }
  .stage .portrait { grid-template-rows: minmax(0, 1fr) auto; }
  .stage .portrait img { height: 100%; max-height: none; aspect-ratio: auto; }
  .stage .flag { width: min(100%, 100cqh * 1.5); margin: auto; }
  /* Two rows of four 3:2 tiles, each with a name under it once revealed, under
     the prompt: as wide as the stage's height lets both rows show. */
  .stage .options { width: min(100%, 3 * (100cqh - 9rem)); margin-inline: auto; }
  .stage .capital-prompt { padding: 2.25rem 1rem; font-size: 2.1rem; }

  #run-side { margin: 0; min-height: 0; display: flex; flex-direction: column; padding: 1rem; border: 1px solid var(--line); border-radius: 1rem; background: none; }
  .run-toggle, .sheet-close, .end-sheet { display: none; }
  .run-panel { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: .75rem; }
  .run-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
  .run-stats div { padding: .65rem .75rem; }
  .run-stats dt { font-size: .65rem; }
  .run-stats dd { font-size: 1.5rem; }
}
