/* ============================================================================
 * app.css - phone first, thumb reachable, readable in a tournament hall
 * ==========================================================================*/

:root {
  --bg: #f4f5f7;
  --surface: #ffffff;
  --surface-2: #eef0f4;
  --line: #d6dae2;
  --line-strong: #aeb6c4;
  --ink: #22262c;
  --ink-dim: #5d6675;
  --ink-faint: #8f98a8;
  --accent: #800000;   /* the maroon in the Asian Skat Masters logo */
  --accent-ink: #ffffff;
  --pos: #17693f;
  --pos-bg: #e4f3ea;
  --neg: #a32020;
  --neg-bg: #fbe8e8;
  --warn: #8a5a00;
  --warn-bg: #fdf1d8;
  --radius: 12px;
  --tap: 48px;
  --shadow: 0 1px 2px rgba(16, 22, 33, .08), 0 4px 14px rgba(16, 22, 33, .06);
  --mono: ui-monospace, "SF Mono", "Roboto Mono", Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14171c;
    --surface: #1c2027;
    --surface-2: #242932;
    --line: #333a45;
    --line-strong: #4a5361;
    --ink: #e8ebf0;
    --ink-dim: #a6afbd;
    --ink-faint: #737d8c;
    /* The logo red, lightened.
       #800000 on a dark ground is a hole in the screen rather than a colour,
       which is why this was blue for a while. Lightened until it carries dark
       text on a button and reads as a link at 12px, and no further: it still
       has to be the red of the logo above it. */
    --accent: #d65c5c;
    --accent-ink: #0d1116;
    --pos: #5dd39e;
    --pos-bg: #17301f;
    --neg: #ff8a8a;
    --neg-bg: #33191a;
    --warn: #e5b95c;
    --warn-bg: #322815;
    --shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 4px 14px rgba(0, 0, 0, .3);
  }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0; padding: 0;
  background: var(--bg); color: var(--ink);
  font: 16px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overscroll-behavior-y: contain;
}

body { padding-bottom: env(safe-area-inset-bottom); }

/* --- app shell ---------------------------------------------------------- */

/* The boot placeholder. Delayed rather than immediate: a load that finishes in
   80 ms should look like nothing happened, not like a flash of "Lädt". */
.boot {
  margin: 48px auto; text-align: center; color: var(--ink-faint); font-size: 13px;
  opacity: 0; animation: boot-in .25s ease .5s forwards;
}
@keyframes boot-in { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .boot { animation: none; opacity: 1; }
}

#app { max-width: 900px; margin: 0 auto; padding: 0 12px calc(env(safe-area-inset-bottom) + 32px); }

/* Header and tabs are one sticky strip. They used to be two, both pinned to
   top: 0, so they landed on top of each other as soon as the page scrolled and
   whichever lost the z-index fight became unclickable. That is what made the
   back button on a sheet impossible to hit. */
.topbar {
  position: sticky; top: 0; z-index: 30;
  margin: 0 -12px 12px;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
}

/* The rule sits under the wordmark rather than under the whole strip: it
   closes off the brand, and the title and the tabs below it read as part of
   the page rather than as more furniture. */
header.bar {
  position: static; z-index: auto;
  display: flex; align-items: center; gap: 10px;
  padding: calc(env(safe-area-inset-top) + 10px) 12px 8px;
  background: none;
  border-bottom: 1px solid var(--line);
}
/* The bar carries the brand and whatever the screen offers; the title sits
   under it, centred, on its own line. */
header.bar #headerActions { margin-left: auto; }

/* One rule, one padding.
 *
 * There were two: a `padding-top: 16px` here and a `padding: 0 12px 10px`
 * three lines further down, which is a shorthand and reset it to zero. The
 * title has been sitting on the grey rule ever since. */
.topbar > h1#title {
  margin: 0; padding: 20px 12px 10px;
  font-size: 17px; font-weight: 650; letter-spacing: -.01em;
  text-align: center; min-width: 0;
}
.topbar > h1#title:empty { display: none; }
.topbar > h1#title .sub {
  font-size: 12px; color: var(--ink-dim); font-weight: 400; display: block;
}

/* The tab bar sits under the header rather than at the foot of the screen.
   It scrolls away with the page on a phone, which keeps the whole viewport for
   a protocol table; the header above it stays. */
/* The tabs sit on a card of their own, so the navigation reads as a thing to
   press rather than as a strip of text under the title. */
nav.tabs {
  display: flex; justify-content: space-around;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin: 0 12px 12px;
  padding: 2px 4px;
}
nav.tabs button {
  flex: 1; border: 0; background: none; color: var(--ink-dim);
  font-size: 11px; padding: 6px 2px; min-height: var(--tap);
  display: flex; flex-direction: column; align-items: center; gap: 3px; cursor: pointer;
  border-radius: 10px;
}
nav.tabs button .ic { font-size: 19px; line-height: 1; }
nav.tabs button[aria-current="page"] { color: var(--accent); font-weight: 600; }

/* --- generic ------------------------------------------------------------ */

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 14px; margin: 0 0 12px;
}
.card > h2 { font-size: 14px; margin: 0 0 10px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-dim); }
.row { display: flex; gap: 8px; align-items: center; }
.row.wrap { flex-wrap: wrap; }
.spread { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.grow { flex: 1; }
.muted { color: var(--ink-dim); }
.faint { color: var(--ink-faint); }
.small { font-size: 13px; }
.tiny { font-size: 11px; }
.num { font-variant-numeric: tabular-nums; font-family: var(--mono); }
.pos { color: var(--pos); }
.neg { color: var(--neg); }
.center { text-align: center; }
.stack > * + * { margin-top: 8px; }
.hidden { display: none !important; }

/* The `hidden` attribute, made to mean it.
 *
 * A tab bar button is `display: flex` from a class rule, which beats the one
 * line of user-agent stylesheet that hides [hidden] elements - so a viewer
 * kept seeing "Preisgeld" and "Mehr", inert but there. Anything that sets
 * display in this file has to lose to hidden, and this is the one place to say
 * so rather than at each rule. */
[hidden] { display: none !important; }

button, .btn {
  font: inherit; font-weight: 550;
  min-height: var(--tap); padding: 0 16px;
  border-radius: 10px; border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink);
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  /* No double-tap zoom on anything you press.
     Safari waits 300 ms after a tap on a button to see whether a second
     one is coming, and if it is, zooms instead of pressing. Two quick
     taps on + is how you enter mit/ohne 3, and how you used to end up
     looking at a magnified sheet. Pinch zoom is untouched. */
  touch-action: manipulation;
}
button:active { transform: scale(.985); }
button.primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
button.ghost { border-color: transparent; background: transparent; color: var(--accent); }
button.danger { color: var(--neg); border-color: color-mix(in srgb, var(--neg) 40%, transparent); }
button.sm { min-height: 36px; padding: 0 10px; font-size: 13px; }
button[disabled] { opacity: .45; pointer-events: none; }

input, select, textarea {
  font: inherit; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: 10px;
  min-height: var(--tap); padding: 0 12px; width: 100%;
}
textarea { padding: 10px 12px; min-height: 84px; line-height: 1.4; }
label.field { display: block; margin-bottom: 10px; }
label.field > span { display: block; font-size: 12px; color: var(--ink-dim); margin-bottom: 4px; letter-spacing: .02em; }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 600; letter-spacing: .03em;
  padding: 3px 9px; border-radius: 999px;
  background: var(--surface-2); color: var(--ink-dim);
}
.pill.ok { background: var(--pos-bg); color: var(--pos); }
.pill.bad { background: var(--neg-bg); color: var(--neg); }
.pill.warn { background: var(--warn-bg); color: var(--warn); }
.tiny.warn, .small.warn { color: var(--warn); }

/* --- choice grids ------------------------------------------------------- */

.choices { display: grid; gap: 6px; }
.choices.c2 { grid-template-columns: repeat(2, 1fr); }
.choices.c3 { grid-template-columns: repeat(3, 1fr); }
.choices.c4 { grid-template-columns: repeat(4, 1fr); }
.choices.c5 { grid-template-columns: repeat(5, 1fr); }
.choices.c6 { grid-template-columns: repeat(6, 1fr); }
.choices button {
  min-height: 52px; padding: 0 6px; font-size: 14px; line-height: 1.15;
  flex-direction: column; gap: 1px;
}
.choices button .sub { font-size: 10px; color: var(--ink-faint); font-weight: 400; }
.choices button[aria-pressed="true"] {
  background: var(--accent); color: var(--accent-ink); border-color: transparent;
}
.choices button[aria-pressed="true"] .sub { color: color-mix(in srgb, var(--accent-ink) 75%, transparent); }
.choices button.suit-red[aria-pressed="false"] { color: var(--neg); }

/* The six games. Nothing under the symbol, so the symbol can be the size of
   the thing being pressed - G is Grand and N is Null, and by the second round
   nobody is reading them as letters. */
.gametypes button { font-size: 26px; font-weight: 600; }

.toggles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.toggles button { min-height: 46px; font-size: 12.5px; padding: 0 4px; }
.toggles button[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.toggles button[disabled] { opacity: .3; }

/* mit/ohne: the four buttons, and the stepper below them when the number is
   past what the buttons offer. */
.jacks { display: grid; gap: 6px; }
.jacks .choices button { min-height: 52px; font-size: 17px; }

.stepper { display: flex; align-items: center; gap: 0; }
.stepper button { min-width: 52px; border-radius: 10px 0 0 10px; font-size: 22px; }
.stepper button:last-child { border-radius: 0 10px 10px 0; }
.stepper .val {
  flex: 1; text-align: center; min-height: var(--tap); display: flex;
  align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); border-left: 0; border-right: 0;
  font-family: var(--mono); font-size: 20px; font-weight: 600; background: var(--surface);
}

/* Won, lost, and the rare third case. Eingepasst takes only the width of its
   own word, so the two outcomes that happen keep the row. */
.winloss { display: grid; grid-template-columns: 1fr 1fr auto; gap: 8px; }
.winloss button { min-height: 58px; font-size: 16px; font-weight: 650; }
.winloss button[data-v="passed"] { font-size: 13.5px; font-weight: 550; padding: 0 12px; }
.winloss button[aria-pressed="true"][data-v="won"] { background: var(--pos); color: #fff; border-color: transparent; }
.winloss button[aria-pressed="true"][data-v="lost"] { background: var(--neg); color: #fff; border-color: transparent; }
.winloss button[aria-pressed="true"][data-v="passed"] { background: var(--accent); color: var(--accent-ink); border-color: transparent; }

.valuebox {
  display: flex; align-items: baseline; justify-content: center; gap: 10px;
  padding: 14px; border-radius: var(--radius);
  background: var(--surface-2); margin-bottom: 12px;
}
.valuebox .big { font-family: var(--mono); font-size: 38px; font-weight: 700; letter-spacing: -.02em; }
.valuebox .formula { font-size: 12px; color: var(--ink-dim); }

/* --- tables ------------------------------------------------------------- */

.scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 -14px; padding: 0 14px; }
/* A table inside a scroller sizes to its content, not to the card. Left at
   width 100% it stays card-wide and the nowrap cells spill out of the box
   instead of scrolling, which is what the Teamwertung did: one column per
   tournament plus Wertung and Gesamt does not fit a phone. */
.scroll > table { width: auto; min-width: 100%; }
table { border-collapse: collapse; width: 100%; font-size: 13px; }
th, td { padding: 7px 8px; text-align: right; border-bottom: 1px solid var(--line); white-space: nowrap; }
th { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-dim); font-weight: 600; }
th:first-child, td:first-child { text-align: left; }
th.l, td.l { text-align: left; }
tbody tr:last-child td { border-bottom: 0; }
tr.total td { font-weight: 700; border-top: 2px solid var(--line-strong); }
tr.sub td { color: var(--ink-dim); font-size: 12px; }
/* A protocol row that does not add up.
   Named `flagged` and not `flag`: `.flag` is a country flag, and when that
   became an inline-block box sized in em to hold an SVG, every row of the
   Spielprotokoll collapsed into a 1.5 x 1 em strip. One word, two meanings,
   and the table only broke on the screens where a row was wrong. */
tr.flagged td { background: var(--neg-bg); }
tr.me td { background: color-mix(in srgb, var(--accent) 10%, transparent); }
td.num, th.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* --- protocol grid ------------------------------------------------------ */

.protocol td, .protocol th { padding: 5px 6px; font-size: 12.5px; }
.protocol tbody tr { cursor: pointer; }
.protocol tbody tr:active { background: var(--surface-2); }
.protocol .g { color: var(--ink-faint); }

/* --- lists -------------------------------------------------------------- */

.list { display: flex; flex-direction: column; gap: 8px; }
.item {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; min-height: var(--tap);
  cursor: pointer; text-align: left; width: 100%; font: inherit; color: inherit;
  box-shadow: var(--shadow);
}
.item .title { font-weight: 600; }
.item .meta { font-size: 12px; color: var(--ink-dim); }
.item .chev { color: var(--ink-faint); margin-left: auto; }

/* --- issues ------------------------------------------------------------- */

.issue {
  border-left: 3px solid var(--neg); background: var(--neg-bg);
  padding: 9px 12px; border-radius: 0 8px 8px 0; font-size: 13px; margin-bottom: 6px;
}
.issue.warn { border-left-color: var(--warn); background: var(--warn-bg); }
.issue .code { font-family: var(--mono); font-size: 10px; opacity: .65; display: block; }
/* One row can raise several complaints and they are shown together under a
   single Zeile label, so the second and later lines need their own air. */
.issue div + div { margin-top: 4px; }

/* --- modal -------------------------------------------------------------- */

.sheetmodal {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(10, 14, 20, .5);
  display: flex; align-items: flex-end; justify-content: center;
}
.sheetmodal > .body {
  background: var(--bg); width: 100%; max-width: 900px;
  border-radius: 18px 18px 0 0; padding: 8px 14px calc(env(safe-area-inset-bottom) + 20px);
  max-height: 92vh; overflow-y: auto;
}
.sheetmodal .grab { width: 40px; height: 4px; border-radius: 2px; background: var(--line-strong); margin: 8px auto 12px; }

/* The action row of a sheet stays put while the form above it scrolls. The
   game editor is taller than a phone screen, so a save button at the end of
   the content is below the fold for the whole time it is being filled in. */
.sheetmodal .actionbar {
  position: sticky; bottom: 0; z-index: 2;
  margin: 8px -14px calc(env(safe-area-inset-bottom) * -1 - 20px);
  padding: 10px 14px calc(env(safe-area-inset-bottom) + 14px);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--line);
  gap: 8px;
}

.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom) + 78px); z-index: 70;
  background: var(--ink); color: var(--bg);
  padding: 11px 18px; border-radius: 999px; font-size: 14px; font-weight: 550;
  box-shadow: var(--shadow); max-width: 90vw;
}

.stage { display: flex; align-items: center; gap: 10px; padding: 8px 0; font-size: 14px; }
.stage .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line-strong); flex: none; }
.stage.active .dot { background: var(--accent); animation: pulse 1s infinite; }
.stage.done .dot { background: var(--pos); }
@keyframes pulse { 50% { opacity: .3; } }

.photo { width: 100%; border-radius: 10px; border: 1px solid var(--line); display: block; }

.empty { text-align: center; padding: 40px 20px; color: var(--ink-dim); }
.empty .ic { font-size: 40px; display: block; margin-bottom: 10px; opacity: .5; }

@media (min-width: 700px) {
  .toggles { grid-template-columns: repeat(6, 1fr); }
  #app { padding-bottom: 40px; }
}

@media print {
  nav.tabs, header.bar, button { display: none !important; }
  .card { box-shadow: none; break-inside: avoid; }
  body { background: #fff; }
}

/* --- branding ----------------------------------------------------------- */

/* The wordmark, set rather than drawn.
   Three words run together with the initial of each one carrying the accent
   and standing taller than the rest, which is how the logo is built. It works
   as type because the coloured capitals are what separate the words, so no
   spaces are needed and the mark stays compact next to the context line. */
#brand {
  flex: none; display: flex; align-items: baseline; line-height: 1;
  font-size: 11px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-dim); white-space: nowrap;
  user-select: none;
}
#brand .hl {
  color: var(--accent);
  font-size: 14px;
  /* The taller capitals would otherwise push the letterspacing open around
     themselves. */
  letter-spacing: .06em;
}
#brand .brand-short { display: none; }

/* On a phone the space belongs to which table and which round, not to the
   full name of the series. */
@media (max-width: 520px) {
  #brand .brand-full { display: none; }
  #brand .brand-short { display: inline; }
}
/* A long tournament name costs an ellipsis rather than the round number under
   it, which is the part somebody is actually looking for. */
#titleText { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A table row that opens something. */
tr.clickrow { cursor: pointer; }
tr.clickrow:hover td { background: var(--surface-2); }

/* --- key/value table, for things that are shown but not editable -------- */

table.kv { width: 100%; border-collapse: collapse; }
table.kv td { padding: 3px 0; vertical-align: top; }
table.kv td:first-child { width: 45%; padding-right: 10px; }

/* --- highlights --------------------------------------------------------- */

/* The best round of a tournament, in the table. Green for the same reason the
   banner below is: it is the high score, not a problem. */
td.bestcell {
  background: color-mix(in srgb, var(--pos) 16%, transparent);
  border-radius: 6px;
}

/* A box with a fact in it.
 *
 * It used to be maroon whatever it said, and maroon on a tinted box reads as
 * an error message - which "best tournament" is the opposite of. So the colour
 * now means something: grey for a plain statement, green for an achievement,
 * amber for something deliberately held back. */
.banner {
  background: var(--surface-2);
  border-left: 3px solid var(--line-strong);
  padding: 8px 10px; border-radius: 8px;
}
.banner.good {
  background: var(--pos-bg);
  border-left-color: var(--pos);
}
.banner.hold {
  background: var(--warn-bg);
  border-left-color: var(--warn);
}
button.linky {
  border: 0; background: none; padding: 0; margin: 0; font: inherit; color: inherit;
  text-align: left; cursor: pointer; text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  text-underline-offset: 2px; min-height: 0;
}
button.linky:hover { color: var(--accent); }

/* --- profiles ----------------------------------------------------------- */

.avatar { border-radius: 50%; object-fit: cover; flex: none; }
.avatar.ph {
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
  color: var(--accent); font-weight: 650; letter-spacing: .02em;
}
.big { font-size: 22px; font-weight: 650; line-height: 1.1; }

/* The full logo, on the login screen only. prepare-logo.js cuts the white
   rectangle away, so it sits on any background - but what is left is black
   line art: a wordmark, a pagoda, a skyline. Transparent is not the same as
   legible, and on the dark theme black on near-black is what it sounds like.
   
   A plate rather than a second, inverted copy of the file: the four Jacks are
   the point of this logo and they are red, green, blue and gold. Inverting
   them to make the text readable would cost more than it buys. When the
   designer's own dark version arrives it goes in here as a <picture>. */
.splash {
  display: block; width: 100%; max-width: 320px; height: auto;
  margin: 0 auto 4px;
}

@media (prefers-color-scheme: dark) {
  .splash {
    background: #f2f3f5;
    border-radius: var(--radius);
    padding: 12px 16px;
    max-width: 344px;
  }
}

/* --- the projector ------------------------------------------------------
   A board window has no chrome and no column: the hall reads it from the back
   of the room, so every pixel is for names and numbers. Sized in vw with a
   floor and a ceiling, so it fills a projector without becoming absurd on a
   laptop being used to check it. */

body.presenting #app { max-width: none; padding: 0 clamp(12px, 2vw, 40px) 24px; }
body.presenting .topbar { display: none; }

.boardhead {
  display: flex; align-items: baseline; gap: clamp(8px, 1.5vw, 20px);
  padding: clamp(10px, 2vh, 28px) 0 clamp(8px, 1.5vh, 20px);
  border-bottom: 2px solid var(--line);
  margin-bottom: clamp(10px, 2vh, 24px);
}
.boardtitle { font-size: clamp(20px, 3.2vw, 54px); font-weight: 700; letter-spacing: -.01em; }
.boardsub { font-size: clamp(14px, 2vw, 32px); color: var(--ink-dim); font-weight: 600; }

/* The seating grid gets as many columns as fit and grows the names with the
   window rather than with the font size of the page. */
body.presenting .beamer { grid-template-columns: repeat(auto-fit, minmax(clamp(200px, 22vw, 420px), 1fr)); gap: clamp(8px, 1.2vw, 20px); }
body.presenting .beamer-table { padding: clamp(10px, 1.4vw, 26px); }
body.presenting .beamer-no { font-size: clamp(16px, 1.9vw, 34px); margin-bottom: .3em; }
body.presenting .beamer-seats li { font-size: clamp(15px, 1.9vw, 34px); line-height: 1.45; }

/* Results. Tabular figures so the columns line up from a distance. */
.boardtable { width: 100%; border-collapse: collapse; }
.boardtable th, .boardtable td {
  padding: clamp(3px, .6vh, 10px) clamp(4px, .7vw, 14px);
  font-size: clamp(14px, 1.7vw, 30px);
}
.boardtable th { text-align: right; color: var(--ink-dim); font-weight: 600; font-size: clamp(11px, 1.1vw, 18px); }
.boardtable th.l, .boardtable td.l { text-align: left; }
.boardtable td.num { font-variant-numeric: tabular-nums; font-family: var(--mono); text-align: right; }
.boardtable tbody tr { border-top: 1px solid var(--line); }

/* The head stays while the rows go past.
 *
 * The board scrolls itself every ten seconds, and on the second screenful the
 * columns had no names on them - a wall of numbers with nothing to say which
 * is which. Sticky rather than a second copy of the head, because the row it
 * belongs to has to keep lining up with the columns underneath it.
 *
 * Opaque, or the rows scroll through it. The bar underneath is what stops the
 * first row appearing to touch the header when it slides under. */
body.presenting .boardhead {
  position: sticky; top: 0; z-index: 3;
  background: var(--bg);
  margin-bottom: 0;
}
body.presenting .boardtable thead th {
  position: sticky; top: var(--boardhead-h, 0px); z-index: 2;
  background: var(--bg);
  box-shadow: inset 0 -1px 0 var(--line-strong);
}

/* Banded columns rather than banded rows.
 *
 * A row on a projector is a metre wide and the eye has to carry a name across
 * eight numbers to the right one. Rows are what the reader is already
 * following; the columns are what they lose their place in. Faint enough that
 * the numbers stay the loudest thing on the wall. */
body.presenting .boardtable td:nth-child(even),
body.presenting .boardtable th:nth-child(even) {
  background: color-mix(in srgb, var(--ink) 5%, var(--bg));
}
.boardtable tr.podium td { font-weight: 700; }
/* The team, on a wall: quieter than the numbers and cut off rather than
   allowed to push the round columns off the screen. */
.boardtable td.teamcell {
  color: var(--ink-dim); font-weight: 500;
  max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.boardtable .flag { width: 1.6em; height: 1.07em; vertical-align: -.15em; }
.boardtable tr.podium td.l { color: var(--accent); }

/* --- beamer -------------------------------------------------------------
   The seating on a projector at the front of the hall. Sized in vw so it fills
   whatever it is thrown onto, with a floor so it stays sane on a laptop. */

.beamer { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.beamer-table { padding: 16px 18px; }
/* A table on the spectator board opens its whole run, so it is a button. It
   still has to look like the card next to it. */
button.beamer-open {
  display: block; width: 100%; text-align: left; min-height: 0;
  border-color: var(--line); font: inherit;
}
button.beamer-open .beamer-no { display: flex; align-items: baseline; gap: 6px; }
button.beamer-open .chev { margin-left: auto; color: var(--ink-faint); }
button.beamer-open:hover { border-color: var(--line-strong); }
.beamer-no {
  font-size: clamp(18px, 2.2vw, 30px); font-weight: 700;
  color: var(--accent); letter-spacing: .01em; margin-bottom: 6px;
}
.beamer-seats { margin: 0; padding-left: 1.3em; }
.beamer-seats li {
  font-size: clamp(16px, 1.9vw, 26px); line-height: 1.5;
  font-weight: 550;
}

/* --- flags and countries ------------------------------------------------ */

/* Sized in em, so a flag still grows with the text around it the way the emoji
   did and .big-flag still works by setting a font size. 3:2, as drawn. */
.flag {
  display: inline-block; width: 1.5em; height: 1em;
  flex: none; line-height: 1; vertical-align: -.12em;
}
.flag svg {
  display: block; width: 100%; height: 100%; border-radius: 1px;
  /* The hairline is drawn inside the SVG, not shadowed here: a half-pixel
     box-shadow gets rounded to whole device pixels one side at a time, and
     Japan came out with a line on the top and the right and none on the left
     or the bottom. See flags.js. */
}
.big-flag { font-size: 22px; line-height: 1; flex: none; }
.ctry { display: inline-flex; align-items: baseline; gap: 5px; }

/* A tournament row reads left to right and does not centre anything: flag,
   where, when, who won. */
.item.tourney { display: flex; align-items: center; gap: 10px; text-align: left; }

.pmeta { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.teamtag {
  font-size: 11px; font-weight: 600; line-height: 1;
  padding: 3px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  white-space: nowrap;
}

/* --- who deals next ----------------------------------------------------- */

/* A row of the protocol rather than a banner above it: it belongs directly
   under the last game, which is where the table is already looking. */
tr.nextrow > td { padding: 0 !important; background: color-mix(in srgb, var(--accent) 8%, transparent); }
.nextrow-btn {
  display: flex; align-items: center; gap: 10px; width: 100%;
  border: 0; background: none; cursor: pointer; text-align: left;
  padding: 9px 10px; font: inherit; color: inherit;
  border-left: 3px solid var(--accent);
}
.nextrow-btn .chev { margin-left: auto; }

/* --- the seats at a table ----------------------------------------------- */

.seatrow { display: flex; gap: 8px; }
.seatcard {
  flex: 1; min-width: 0; text-align: center;
  padding: 8px 6px; border-radius: 10px; background: var(--surface-2);
}
.seatname { font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seatpts { font-size: 20px; font-weight: 650; line-height: 1.2; }
.nextup-title {
  display: flex; align-items: center; gap: 10px;
  font-size: 20px; font-weight: 650; letter-spacing: -.01em;
  margin: 2px 0 4px;
}

.nextup-no {
  font-weight: 650; color: var(--accent);
  white-space: nowrap;
}

/* One block is one full turn of the deal, so the sheet is ruled the way the
   paper one is: a heavier line every three games at a three seat table, every
   four at a four seat one. */
table.protocol tr.blockend > td { border-bottom: 2px solid var(--line-strong); }

/* --- moving between the tables of a round -------------------------------- */

/* Breadcrumbs. One line, scrolls sideways on a narrow phone rather than
   wrapping into two, so the table view below it does not move. */
.crumbs {
  display: flex; align-items: center; gap: 4px; margin: 0 -12px 10px;
  padding: 0 12px 2px; overflow-x: auto; -webkit-overflow-scrolling: touch;
  font-size: 13px; white-space: nowrap;
}
.crumbs .sep { color: var(--ink-dim); opacity: .6; }
.crumbs .crumb {
  flex: none; border: 0; background: none; padding: 2px 2px; min-height: 0;
  font-size: 13px; color: var(--accent); cursor: pointer; text-decoration: none;
}
.crumbs button.crumb:hover { text-decoration: underline; }
.crumbs .crumb.here { color: var(--ink-dim); cursor: default; }

.tablestrip {
  display: flex; gap: 6px; overflow-x: auto; margin: 0 -12px 12px; padding: 0 12px 2px;
  -webkit-overflow-scrolling: touch;
}
.tablestrip button {
  flex: none; border: 1px solid var(--line); background: var(--surface);
  border-radius: 999px; padding: 6px 14px; font-size: 13px; cursor: pointer;
  color: var(--ink-dim); min-height: 0;
}
.tablestrip button[aria-pressed="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600;
}

/* ---------------------------------------------------------------------------
 * Signing up
 *
 * The public pages. One column and no navigation: whoever opens these has a
 * link and nothing else, and the only thing on the screen is the one thing
 * they came to do.
 * -------------------------------------------------------------------------*/
.signup { max-width: 460px; margin: 24px auto 0; }
.signup .field { align-items: center; }
.signup h2 { font-size: 18px; text-transform: none; letter-spacing: 0; color: var(--ink); }

/* The sign-up worksheet. Inputs sit in table cells, so they have to stop
   looking like form fields and start looking like the row they are in. */
.cell {
  width: 100%; min-width: 90px; min-height: 34px;
  padding: 4px 6px; font-size: 14px;
  background: transparent; border: 1px solid transparent; border-radius: 6px;
}
.cell:hover { border-color: var(--line); }
.cell:focus { background: var(--surface); border-color: var(--accent); }
select.cell { min-width: 140px; }

/* A card that may be wider than the column it sits in.
   Only where there is room: on a phone the column is the screen. */
@media (min-width: 940px) {
  .card.wide {
    width: min(96vw, 1500px);
    margin-inline: calc((100% - min(96vw, 1500px)) / 2);
  }
}

/* The team matrix: what counted, and what was played and did not. */
td.counted { background: color-mix(in srgb, var(--pos) 12%, transparent); }
tr.teamtotal td { border-top: 2px solid var(--line-strong); }

/* Who is signed in, and the way out, in the header bar. The name goes when
   there is no room for it: on a phone the power symbol is the whole button. */
.whoami { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.whoami-name { color: var(--ink-dim); max-width: 14ch; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 520px) { .whoami-name { display: none; } }

/* A row whose name looks like somebody we already have. It cannot be saved
   until it has been answered, so it says so before the save does. */
select.cell.undecided {
  border-color: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
}

/* A password box with an eye in it. The button sits inside the field's box, so
   the field keeps the whole row and the two do not compete for it. */
.peek { position: relative; display: flex; flex: 1; }
.peek input { flex: 1; padding-right: 40px; }
.peek-eye {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; min-height: 0; padding: 0;
  border: 0; background: none; color: var(--ink-faint); cursor: pointer;
}
.peek-eye:hover { color: var(--ink-dim); }

/* A card that folds. The head is the whole row, so the target is the card and
   not a chevron the size of a fingernail. */
.foldhead {
  display: flex; align-items: center; gap: 10px; width: 100%;
  border: 0; background: none; padding: 0; margin: 0; text-align: left;
  color: inherit; font: inherit; cursor: pointer; min-height: 0;
}
.foldhead .sub { font-size: 12px; color: var(--ink-dim); }
.foldhead .chev { color: var(--ink-faint); }

/* The gear on a card head, beside the drawn download arrow.
 *
 * A glyph draws at roughly seven tenths of its font size, so 18px of type next
 * to a 16px icon is the smaller of the two. 23px puts the two shapes at the
 * same height; the flex box keeps them on the same line while it happens. */
/* The gear, nudged.
 *
 * The two boxes are the same and centred; what is not centred is the ink
 * inside the glyph. A character is positioned by the font's baseline and its
 * ascent, not by where the drawing happens to sit in the em square, so ⚙ hangs
 * low in a box that is centred on the line. There is no way to ask CSS for
 * "centre the ink" - this is the correction, and it is why the icons beside it
 * are drawn rather than typed. */
button.gear { font-size: 23px; }
button.gear > span { display: block; transform: translateY(-4px); }

/* The icon buttons on a card head.
 *
 * Both are square boxes that centre whatever is in them, which is what puts
 * them on one line: an inline SVG otherwise sits on the text baseline and a
 * glyph sits in the middle of its line box, and those are not the same height.
 * The line-height of 1 is what stops the glyph's box being taller than the ink
 * in it. */
.cardicons button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; min-height: 0; padding: 0;
  line-height: 1;
}
.cardicons svg { display: block; }

/* --- the footer ---------------------------------------------------------- *
 *
 * One line of small grey type under the login card, and only there. It is a
 * credit, not navigation: on a tournament screen the same line read as the app
 * clearing its throat, and the last inch of a phone belongs to the standings.
 */
#foot {
  margin-top: 20px;
  text-align: center;
  color: var(--ink-faint); font-size: 12px;
}
#foot a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
#foot a:hover { color: var(--accent); }

/* The front door has no header strip.
 *
 * The full logo is on the screen at its proper size; a four millimetre
 * wordmark above it is the same word twice, over an empty title line.
 *
 * Driven by the login screen's own markup rather than by a class on <body>:
 * the ways off that screen are many - signing in, a reset link, a public
 * registration page, the projector - and a flag every one of them has to
 * remember to clear is a flag that will one day be left on, leaving an app
 * with no navigation. What is on the screen decides. A browser too old for
 * :has() shows the strip, which is what it showed yesterday.
 */
.topbar:has(~ main .door) { display: none; }

/* --- Siegerehrung -------------------------------------------------------- *
 *
 * On the projector. One row per place, all of them from the start so the room
 * can see how many are still to come and the board does not jump as names
 * arrive. An uncalled place is its number on an empty line - no ghost of a
 * name, nothing to squint at.
 *
 * Sized in vh rather than vw: this is the one board whose length is known (ten
 * rows), so it can be made to fill the height of whatever it is thrown onto
 * instead of being scaled down to fit afterwards.
 */
body.presenting .ceremony {
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: clamp(4px, .6vh, 12px);
  min-height: calc(100vh - var(--boardhead-h, 0px) - 24px);
}
body.presenting .crow {
  display: grid;
  grid-template-columns: 2.2em 1fr auto auto 3.5em;
  align-items: center; gap: clamp(8px, 1.5vw, 28px);
  padding: clamp(2px, .5vh, 10px) clamp(8px, 1.2vw, 20px);
  border-radius: 12px;
  font-size: clamp(18px, 4.2vh, 52px);
  line-height: 1.15;
  color: var(--ink-faint);
}
body.presenting .crow .cplace { text-align: right; font-variant-numeric: tabular-nums; }
body.presenting .crow .cname { font-weight: 600; color: var(--ink); }
body.presenting .crow .cteam { color: var(--ink-dim); font-size: .62em; }
body.presenting .crow .cpts {
  text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-dim);
  font-size: .78em;
}
/* Not yet called: the number, and the space its name will take. */
body.presenting .crow:not(.called) { opacity: .35; }
body.presenting .crow:not(.called) .cname::after { content: "\00a0"; }

/* The one just called. The room needs telling where to look. */
body.presenting .crow.fresh {
  background: var(--surface); box-shadow: var(--shadow);
  color: var(--ink);
}
body.presenting .crow.fresh .cplace { color: var(--accent); font-weight: 700; }

/* And the winner, when the last envelope is open. */
body.presenting .crow.champion {
  background: var(--accent); color: var(--accent-ink);
  font-size: clamp(22px, 5.4vh, 68px);
}
body.presenting .crow.champion .cname,
body.presenting .crow.champion .cplace,
body.presenting .crow.champion .cteam,
body.presenting .crow.champion .cpts { color: var(--accent-ink); }

/* The organizer's copy of the same list, which does show the names to come. */
.ceremony-list { width: 100%; }
.ceremony-list tr.g td { color: var(--ink-faint); }
