/*
  One stylesheet, phone-first, dark only (`PX`, `D6`).

  `docs/archive/02-product/22-ui-overview.md` in short: "It reads as a scoreboard, not a bill." Phone-first, quick to
  scan, no sober financial framing, **no red** — so money is gold rather than alarming,
  and nothing on the page is styled as a demand. Since `R66` it is drawn as a pixel-art
  game screen: a deep navy page, gold for euros, framed panels with square corners.

  Three rules hold the look together, and each is easy to break by accident:

  - **Every colour is a token** on `:root`, below. Nothing further down names a colour,
    and neither does `sprites.svg`: a sprite's pixels are `var(--coin-gold)` and friends,
    resolved on the page that draws it. One palette, one place to change it.
  - **The pixel font is for headings and numbers only** (`D8`). Press Start 2P is wide and
    blocky; a sentence in it is a puzzle and a form in it is unusable. Body text, forms
    and tables keep the system font stack and its 16px floor.
  - **Nothing loads from another host.** The font is served from `/static/fonts/` with its
    OFL licence beside it, so a phone opening the board through Funnel (ADR 026) calls
    nobody else. `test_pages.py` walks every page for it.

  `docs/archive/06-api-and-data/63-non-functional-requirements.md` on accessibility asks for readable text sizes,
  sufficient contrast, and controls usable one-handed on a phone. Every text token below
  is at least 6.5:1 on every panel colour it sits on; the 16px floor and rows as tap
  targets are unchanged. There is one theme, so there is no `prefers-color-scheme`: a
  light phone gets the dark board, which is the look being asked for (`D6`).
*/

@font-face {
  font-family: "Press Start 2P";
  src: url("/static/fonts/PressStart2P-Regular.ttf") format("truetype");
  /* Text shows at once in the fallback and swaps when the font lands: a scoreboard that
     is blank for a second on a slow connection reads as broken. */
  font-display: swap;
}

:root {
  color-scheme: dark;

  /* The page. */
  --bg: #0b1026;            /* deep navy, the page itself */
  --card: #151d3d;          /* a panel on the page */
  --card-raised: #1d2852;   /* a panel picked out: the viewer's own row, a hover */
  --line: #2e3c72;          /* frames, rules, dividers */
  --shadow: #050815;        /* the hard pixel shadow under a panel, and outlines */

  /* Text. */
  --ink: #eef2ff;           /* body text */
  --soft: #a4afd6;          /* secondary text: counts, dates, labels */
  --accent: var(--coin-gold); /* links, the current tab, the banner's edge: warm, not a warning */
  --on-accent: var(--bg);   /* text on a gold button */

  /* What a euro figure does not include, and the quiet marker. Never red. */
  --kind: #6fdba5;          /* settled in kind */
  --open: #c3a8ff;          /* no amount yet */
  --stale: #d6c08f;         /* behind, or possibly outdated */

  /* Sprites (`sprites.svg`). A coin is euros and nothing else (`D7`). */
  --coin-gold: #f5c542;     /* a coin's face; also every euro figure */
  --coin-light: #fff1a6;    /* a coin's highlight */
  --coin-shade: #c98a1b;    /* a coin's shadowed side */
  --coin-edge: #6e4708;     /* a coin's outline */
  --gem: #63d8f2;           /* the crown's stones */
  --wood: #94602e;          /* the chest */
  --wood-light: #b57c42;    /* the chest's lid */
  --wood-dark: #5b3716;     /* the chest's seams */
  --outline: var(--shadow); /* a sprite's outline where it is not a coin's */

  /* 06, the fine calendar (`R72`, `D21`): call-out fines per day in five fixed steps,
     navy to ice. Not gold, because a count is not euros (`D7`); dark ink on the two
     brightest, the only ones a number is printed on. */
  --heat-0: #222c58;        /* no call-out fines */
  --heat-1: #24506e;        /* 1–2 */
  --heat-2: #2f7f98;        /* 3–5 */
  --heat-3: #4cb6cf;        /* 6–10 */
  --heat-4: #9ff0ff;        /* 11 or more */

  /* Type and the pixel grid. */
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-pixel: "Press Start 2P", ui-monospace, SFMono-Regular, Menlo, monospace;
  --px: 2px;                /* one "pixel" of a frame, a shadow or a sprite */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* 16px floor: anything smaller is a phone asking to be pinch-zoomed. */
  font: 16px/1.5 var(--font-body);
  color: var(--ink);
  background: var(--bg);
  /* Room for the fixed footer, so the last row is never under it. */
  padding-bottom: 3.5rem;
}

a { color: inherit; text-decoration: none; }
a:focus-visible, button:focus-visible, input:focus-visible, summary:focus-visible,
select:focus-visible, textarea:focus-visible {
  outline: var(--px) solid var(--accent); outline-offset: var(--px);
}
main { max-width: 34rem; margin: 0 auto; padding: 0.75rem; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em; }

/* --- the pixel font: headings and numbers ------------------------------------ */

/*
  Press Start 2P is drawn on an 8px grid and is sharpest at multiples of 8; on a phone's
  2–3× screen 10 and 12 hold up too. It is about as wide as it is tall, so a heading is
  set far smaller than a system-font heading would be, and wraps rather than scrolls.

  `.pixel` is the class for a heading or a figure that is not an `h1`/`h2`. Numbers on
  the team's screens are named below by their own classes rather than by `.money`
  everywhere, because `.money` is also a form on the Boetemeister's payment screen and a
  cell in the settlement table, which keep the plain font (`D6`).
*/
h1, h2, .pixel {
  font-family: var(--font-pixel);
  font-weight: 400;          /* the face has one weight; a faux bold smears it */
  letter-spacing: 0;
  line-height: 1.6;
  overflow-wrap: anywhere;   /* a long name wraps instead of pushing the page sideways */
}
h1 {
  font-size: 1rem; margin: 0.75rem 0;
  color: var(--accent); text-shadow: var(--px) var(--px) 0 var(--shadow);
}
h2 { font-size: 0.75rem; margin: 1.75rem 0 0.6rem; color: var(--soft); }
@media (min-width: 40rem) {
  h1 { font-size: 1.5rem; }
  h2 { font-size: 0.875rem; }
}

/* --- panels: the framed box everything sits in ------------------------------- */

/*
  A pixel frame has no rounded corners and no anti-aliased border: it is four hard lines
  that stop short of the corners, the way a sprite's box would be drawn, plus a darker
  line under it for depth. Four offset shadows draw it, so it takes no layout space and
  the notched corners come free. `.panel` is the class for a new box; the existing
  screens' boxes are listed here so that they all wear the same frame.
*/
.panel, .board, details.type, .feed > li, .status-list, .own-row-pick, .notice.waiting {
  background: var(--card);
  border: 0; border-radius: 0;
  box-shadow:
    0 calc(-1 * var(--px)) 0 var(--line),
    0 var(--px) 0 var(--line),
    calc(-1 * var(--px)) 0 0 var(--line),
    var(--px) 0 0 var(--line),
    0 calc(3 * var(--px)) 0 var(--shadow);
}

/* --- sprites ------------------------------------------------------------------ */

/*
  `<svg class="sprite coin-10" aria-hidden="true"><use href="/static/sprites.svg#coin-10"/></svg>`.
  The symbol id and the class are the same word. Each symbol's grid size is its `--w` and
  `--h` below, and `--px` is how many screen pixels one grid pixel takes — so a sprite is
  always a whole multiple of its grid and never blurs. Set `--px` on an element to draw a
  sprite bigger (`.sprite.big`), never `width`.

  `crispEdges` and `pixelated` are both set because they cover different renderers:
  `shape-rendering` for SVG geometry, `image-rendering` for anything rasterised on the way.
*/
.sprite {
  display: inline-block; vertical-align: middle; flex: none;
  width: calc(var(--px) * var(--w, 8)); height: calc(var(--px) * var(--h, 8));
  shape-rendering: crispEdges; image-rendering: pixelated;
  overflow: visible;
}
.sprite.big { --px: 4px; }
.sprite.coin-1 { --w: 8; --h: 8; }
.sprite.coin-10 { --w: 12; --h: 12; }
.sprite.crown { --w: 12; --h: 10; }
.sprite.chest { --w: 12; --h: 10; }
/* The pantry's items (`R74`), drawn `.big`. */
.sprite.bitterbal { --w: 12; --h: 12; }
.sprite.speciaalbier { --w: 12; --h: 12; }
.sprite.appeltaart { --w: 12; --h: 9; }
.sprite.appelflap { --w: 12; --h: 11; }
.sprite.pannenkoek { --w: 12; --h: 9; }
.sprite.poffertjes { --w: 12; --h: 6; }
.sprite.voorraad { --w: 12; --h: 11; }

/* --- bars ------------------------------------------------------------------- */

.bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 0.6rem 0.9rem;
  background: var(--card);
  border-bottom: var(--px) solid var(--line);
  box-shadow: 0 var(--px) 0 var(--shadow);
}
.brand {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-pixel); font-size: 0.75rem; line-height: 1;
  color: var(--accent); text-shadow: var(--px) var(--px) 0 var(--shadow);
}
.bar nav { display: flex; gap: 1.1rem; }
.bar nav a { color: var(--soft); padding: 0.2rem 0; }
.bar nav a[aria-current="page"] {
  color: var(--ink); box-shadow: inset 0 calc(-1 * var(--px)) 0 var(--accent);
}

/* The footer is on every screen (AF.4), so it must be quiet enough to live there. */
footer.status {
  position: fixed; bottom: 0; left: 0; right: 0;
  justify-content: center; text-align: center;
  border-bottom: 0; border-top: var(--px) solid var(--line);
  box-shadow: 0 calc(-1 * var(--px)) 0 var(--shadow);
  font-size: 0.85rem; color: var(--soft);
}
/* Behind is said in the quiet marker's colour and a heavier weight: noticeable, and
   still not an alarm. */
footer.status.stale { color: var(--stale); font-weight: 600; }

/* --- notices ------------------------------------------------------------- */

/*
  The unreviewed banner. Non-negotiable #8 — it appears wherever a total does — so it is
  styled to be *read* and not to alarm: no red, no icon, a gold edge on a framed panel.
*/
.notice.waiting {
  margin: 1rem 0; padding: 0.6rem 0.75rem 0.6rem 1rem;
  font-size: 0.92rem;
  background:
    linear-gradient(var(--accent), var(--accent)) left / calc(2 * var(--px)) 100% no-repeat,
    var(--card);
}
.notice.waiting a { color: var(--accent); }
/*
  The centurions the next close will post (ADR 031, `R83`). News, not a warning: the
  banner's framed panel with the crown's gem on its edge rather than the gold, so the
  two are not read as one. A long tie wraps rather than widening the page.
*/
.coming { list-style: none; margin: 1rem 0; padding: 0; }
.coming li {
  margin: 0 0 0.5rem; padding: 0.6rem 0.75rem 0.6rem 1rem;
  font-size: 0.92rem; overflow-wrap: anywhere;
  background:
    linear-gradient(var(--gem), var(--gem)) left / calc(2 * var(--px)) 100% no-repeat,
    var(--card);
}
.coming li:last-child { margin-bottom: 0; }
.note { color: var(--soft); font-size: 0.88rem; margin: 0.5rem 0; }
.empty {
  margin: 1.5rem 0; padding: 1rem; text-align: center;
  color: var(--soft); background: var(--card);
  border: var(--px) dashed var(--line); border-radius: 0;
}

.tabs { display: flex; gap: 0.75rem; margin: 1rem 0; }
.tabs a {
  flex: 1; text-align: center; padding: 0.5rem 0.4rem;
  color: var(--soft); background: var(--card); font-size: 0.9rem;
  border: var(--px) solid var(--line); border-radius: 0;
  box-shadow: 0 var(--px) 0 var(--shadow);
}
.tabs a[aria-current="page"] {
  color: var(--accent); border-color: var(--accent); font-weight: 600;
  background: var(--card-raised);
}

/* --- the board ----------------------------------------------------------- */

.board { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.board .row { border-bottom: var(--px) solid var(--line); }
.board .row:last-child { border-bottom: 0; }

/* The whole row is the tap target — one-handed on a phone. */
.board .row > a {
  display: grid; grid-template-columns: 1.75rem 1fr auto;
  align-items: baseline; gap: 0.15rem 0.6rem;
  padding: 0.75rem 0.85rem;
}
.board .row > a:hover { background: var(--card-raised); }
.rank { color: var(--soft); font-variant-numeric: tabular-nums; font-size: 0.9rem; }
.who { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.money { font-variant-numeric: tabular-nums; font-weight: 600; }
.count {
  grid-column: 2 / 4; color: var(--soft); font-size: 0.85rem;
  display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: baseline;
}

/* The numbers on the team's screens, in the pixel font and in gold: a euro is a coin. */
.board .rank, .board .money, details.type .money, .fines .money, .feed .money {
  font-family: var(--font-pixel); font-weight: 400; line-height: 1.6;
}
.board .rank { font-size: 0.625rem; }
.board .money, details.type .money { font-size: 0.75rem; color: var(--coin-gold); }
.fines .money, .feed .money { font-size: 0.625rem; color: var(--coin-gold); }

/*
  The viewer's own row. ADR 017: a browser fact, never a server identity — the class is
  added by `boetepot.js` from `localStorage`, and the server never learns who is looking.
*/
.board .row.me {
  background:
    linear-gradient(var(--accent), var(--accent)) left / calc(2 * var(--px)) 100% no-repeat,
    var(--card-raised);
}
.board .row.me .who::after {
  content: " — jij"; color: var(--accent); font-weight: 600; font-size: 0.85rem;
}

.own-row-pick {
  margin: 1rem 0; padding: 0.6rem 0.75rem;
  font-size: 0.9rem; color: var(--soft);
}
.own-row-pick button {
  font: inherit; color: var(--accent); background: none;
  border: 0; padding: 0.35rem 0.3rem; cursor: pointer; text-decoration: underline;
}

/* board in coins */

/*
  Panel 01 (`PX`, `R68`): each row's euro figure drawn in coins under the name, a big coin
  per €10 and a small one per €1 (`D7`); the figure itself stays as text on the right
  (`D12`). How many of each is decided in `api` (`D11`) — nothing here counts.

  A season-end row is the case to design for: €400 is forty big coins, and a 360px phone
  has about 270px under a name. So the big coins **overlap by half**, like a fanned-out pile,
  and wrap onto a second line rather than widening the page. Each coin pulls itself left
  by half its width and the strip is padded by the same amount, so the first coin of every
  line starts flush with the name above it. A coin drawn later sits on top, which is the
  way a pile reads.
*/
.board .coins {
  grid-column: 2 / 4;
  display: flex; flex-wrap: wrap; align-items: flex-end;
  row-gap: calc(var(--px) * 1);
  padding-left: calc(var(--px) * 6);
  margin: 0.1rem 0 0.15rem;
  line-height: 0;
}
.board .coins .sprite.coin-10 { margin-left: calc(var(--px) * -6); }
.board .coins .sprite.coin-1 { margin-left: calc(var(--px) * -3); }
/* A row of ones only starts flush with the name too. */
.board .coins .sprite.coin-1:first-child { margin-left: calc(var(--px) * -6); }
/*
  Air every fifth big coin, and where the tens stop and the ones start, so a pile can be
  read at a glance — five, ten, fifteen — rather than counted. Spacing chosen by position,
  which is a look; the counts themselves are `api`'s.
*/
.board .coins .sprite.coin-10:nth-child(5n + 1):not(:first-child),
.board .coins .sprite.coin-10 + .sprite.coin-1 { margin-left: calc(var(--px) * -2); }

/* The leader's crown, in front of the name and on the name's baseline. */
.board .who .sprite.crown { margin-right: 0.35rem; vertical-align: -0.1rem; }

/* --- chips: what a euro figure does not include ------------------------- */

.chip {
  font-size: 0.76rem; padding: 0.08rem 0.4rem; border-radius: 0;
  border: 1px solid currentColor; white-space: nowrap;
}
.chip.kind { color: var(--kind); }
.chip.open { color: var(--open); }
.chip.stale { color: var(--stale); }
.tag {
  font-size: 0.72rem; color: var(--soft);
  border: 1px solid var(--line); border-radius: 0;
  padding: 0.05rem 0.4rem; white-space: nowrap; font-weight: 400;
  font-family: var(--font-body); text-shadow: none;
}

.total {
  margin: 1rem 0; padding-top: 0.6rem; border-top: var(--px) solid var(--line);
  color: var(--soft); font-size: 0.9rem;
}
.total strong { color: var(--open); }

/*
  What a member still owes this season (`R45`). Under the total and in the same quiet
  register: it is a different figure from the one above it, not a louder one, and the
  member page stays a scoreboard rather than a bill.
*/
.balance { margin: -0.4rem 0 1rem; color: var(--soft); font-size: 0.9rem; }
.balance strong { color: var(--ink); }

/* --- member detail ------------------------------------------------------- */

h1.member { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

details.type { margin-bottom: 0.9rem; }
details.type > summary {
  display: grid; grid-template-columns: 1fr auto auto;
  gap: 0.6rem; align-items: baseline;
  padding: 0.7rem 0.85rem; cursor: pointer;
}
details.type > summary .code { min-width: 0; overflow-wrap: anywhere; }
details.type > summary .count { grid-column: auto; color: var(--soft); }
details.type[open] > summary { border-bottom: var(--px) solid var(--line); }

.fines { list-style: none; margin: 0; padding: 0.4rem 0.85rem 0.7rem; }
.fines > li { padding: 0.5rem 0; border-bottom: 1px dashed var(--line); }
.fines > li:last-child { border-bottom: 0; }
.when { color: var(--soft); font-size: 0.85rem; margin-right: 0.5rem; }

/* The call-out. The only message content anywhere in the product. */
blockquote.callout {
  margin: 0.4rem 0 0; padding: 0.4rem 0 0.4rem 0.7rem;
  border-left: var(--px) solid var(--line);
}
blockquote.callout .said { margin: 0; }
blockquote.callout .said.none, .callout.none {
  color: var(--soft); font-style: italic; margin: 0.4rem 0 0;
  font-size: 0.88rem;
}
.by { color: var(--soft); font-size: 0.82rem; margin: 0.2rem 0 0; }

/*
  ADR 015's notice: the call-out was deleted or edited after it posted. Indented to sit
  under the quotation it qualifies, and given the accent rather than a warning colour —
  the fine is unchanged, so nothing here is an alarm. "No red" holds; a notice is a fact,
  not a demand.
*/
.notice.control {
  margin: 0.35rem 0 0 0.7rem; padding: 0.35rem 0 0.35rem 0.7rem;
  border-left: var(--px) solid var(--accent);
  color: var(--ink); font-size: 0.85rem;
}

/* --- feed ---------------------------------------------------------------- */

.feed { list-style: none; margin: 0; padding: 0; }
.feed > li { padding: 0.65rem 0.85rem; margin-bottom: 0.9rem; }
.feed .what { margin: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: baseline; }
.feed .what a { font-weight: 600; box-shadow: inset 0 -1px 0 var(--line); }
.more { text-align: center; margin: 1rem 0; }
.more a { color: var(--accent); }

/* --- status page --------------------------------------------------------- */

.status-list { margin: 1rem 0; padding: 0.25rem 0.85rem; }
.status-list dt {
  color: var(--soft); font-size: 0.82rem; text-transform: uppercase;
  letter-spacing: 0.04em; margin-top: 0.85rem;
}
.status-list dd { margin: 0.15rem 0 0.85rem; overflow-wrap: anywhere; }
.signout { margin: 1.5rem 0; }
.signout button {
  font: inherit; color: var(--soft); background: var(--card);
  border: var(--px) solid var(--line); border-radius: 0;
  box-shadow: 0 var(--px) 0 var(--shadow);
  padding: 0.55rem 0.9rem; cursor: pointer;
}

/* --- the password box --------------------------------------------------- */

body.centred { display: grid; place-items: center; min-height: 100vh; padding-bottom: 0; }
.gate { width: min(22rem, 90vw); text-align: center; }
.gate .sprite { margin: 0 auto 0.5rem; display: block; }
.gate h1 { margin-top: 0; }
.gate form { display: grid; gap: 0.5rem; margin-top: 1rem; text-align: left; }
.gate label { font-size: 0.85rem; color: var(--soft); }
.gate input {
  font: inherit; padding: 0.65rem; border-radius: 0;
  border: var(--px) solid var(--line); background: var(--card); color: var(--ink);
}
/* A gold game button: a hard shadow under it that it sinks into when pressed. */
.gate button {
  font: inherit; font-weight: 600; padding: 0.65rem; margin-top: 0.25rem;
  border: 0; border-radius: 0; cursor: pointer;
  background: var(--accent); color: var(--on-accent);
  box-shadow: 0 calc(2 * var(--px)) 0 var(--coin-shade);
}
.gate button:active { transform: translateY(var(--px)); box-shadow: 0 var(--px) 0 var(--coin-shade); }
.gate .wrong { color: var(--accent); font-size: 0.88rem; margin: 0.25rem 0 0; }

/* === The Boetemeister's screens ============================================== */

/*
  Everything from here down is the Boetemeister's: the settlement, payment and export
  screens and the review pages. Since `R67` they wear the team's look (`D6`) — the
  palette, the pixel headings, square corners, and a box is a `.panel` in the template
  rather than a border here — but **forms and tables keep the body font**. An amount is
  typed into them and a row is read on a phone at training; Press Start 2P in a form is
  unusable. So nothing below sets `--font-pixel`, and a euro figure here is gold in the
  body font rather than a pixel number.

  Buttons are game buttons: square, with a hard shadow they sink into when pressed.
  The plain one sits on a raised card colour so it still shows on a panel; the one that
  writes is gold, like the password box's.
*/

/* --- settlement, the one screen with controls on it ----------------------- */

/*
  `docs/archive/02-product/22-ui-overview.md`'s "scoreboard, not a bill" is about the team's screens. This one
  *is* a bill, and it is the one place in the product where that is the right register —
  so it gets a table and column headings, which nothing else here has.

  Still no red. The irreversible button is the accent at full strength, which is as loud
  as this palette gets, and the thing that stops a mis-tap is that it does not exist
  until a rehearsal has run (`BF.3` step 4) rather than that it looks dangerous.
*/

/*
  `table-layout: fixed`, and the widths are the point rather than tidiness. Left to
  itself the table sizes to its content, and the longest name plus row 19's "was op de
  lijst" tag pushed it past 375px — so the **page** scrolled sideways on a phone. The
  name column takes what is left and wraps; the euro column is fixed.
*/
.settle { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 0.9rem; }
.settle th, .settle td { text-align: left; padding: 0.55rem 0.4rem; }
/* The table is a panel; the name column, which wraps anyway, gives up the room to keep
   the names off the frame. The fixed columns keep theirs. */
.settle tr > :first-child { padding-left: 0.75rem; }
.settle col.euros { width: 3.4rem; }
/* Wide enough for "Openstaand" in the heading and "tegoed €30" in a cell at 375px;
   anything longer wraps inside the cell rather than widening the table. */
.settle col.balance { width: 5.6rem; }
/* A credit is not a debt with a minus sign, so it does not look like one. */
.settle .credit { color: var(--soft); font-weight: 400; }
/* The tag goes on its own line under a long name rather than widening the column. */
.settle tbody th .tag { display: inline-block; margin-top: 0.15rem; }
/*
  No uppercase and no letter-spacing here, which the rest of the sheet does use. Under
  `table-layout: fixed` a heading wider than its column does not shrink the column — it
  simply hangs over the edge and scrolls the page, which is how a "Betaald" column put
  six pixels of horizontal scroll on a 375px phone.
*/
.settle thead th {
  font-size: 0.75rem; color: var(--soft); font-weight: 600;
  border-bottom: var(--px) solid var(--line);
}
.settle tbody tr { border-bottom: var(--px) solid var(--line); }
.settle tbody tr:last-child { border-bottom: 0; }
/* A euro is gold on every screen; here in the body font, because it is a table. */
.settle td.money { color: var(--coin-gold); }
.settle td .chip { margin-left: 0.3rem; }
.settle tbody th { font-weight: 600; }
.settle td:last-child, .settle th:last-child { text-align: right; }

/* Nothing owed, nothing to bring. Present, because a list that omits the people
   who behaved cannot be read as "everyone is accounted for" — and quiet, because they
   are not what the Boetemeister is here to decide about. */
.settle tr.quiet th, .settle tr.quiet td { color: var(--soft); }

/* An in-kind obligation hangs off the row above it: ADR 021 keys it on
   member × item × period, so it is a separate line and not a separate person. */
.settle tr.obligation { border-bottom: 0; }
/* Left-aligned although it is the row's last cell: it spans both columns, and the
   euro column's right alignment would push the item away from the name it belongs to. */
.settle tr.obligation td {
  padding-top: 0; color: var(--kind); font-size: 0.85rem; text-align: left;
}

.warning, .consequence, .done, .history, .late { margin: 1.25rem 0; }
.consequence, .done { padding: 0.85rem 0.85rem 0.85rem 1rem; }
/* The rehearsal is what the Boetemeister confirms, so it carries the banner's gold edge. */
.consequence {
  background:
    linear-gradient(var(--accent), var(--accent)) left / calc(2 * var(--px)) 100% no-repeat,
    var(--card);
}
/* The pixel heading at its page size: at 16px it took three lines of a phone. */
.consequence h2, .history h2, .late h2 { margin: 0 0 0.6rem; }
.consequence h2 { color: var(--accent); }

.facts { list-style: none; margin: 0.4rem 0; padding: 0; }
.facts > li { padding: 0.25rem 0; }

.choices { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin: 1rem 0; }
.choices button, .find .inline button, details.undo button {
  font: inherit; padding: 0.55rem 0.9rem; border-radius: 0; cursor: pointer;
  border: var(--px) solid var(--line); background: var(--card-raised); color: var(--ink);
  box-shadow: 0 var(--px) 0 var(--shadow);
}
.choices .go, .choices a.go {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
  box-shadow: 0 calc(2 * var(--px)) 0 var(--coin-shade);
  font-weight: 600; text-align: center;
}
.choices button:active, .find .inline button:active, details.undo button:active {
  transform: translateY(var(--px)); box-shadow: none;
}
.choices .go:active { box-shadow: 0 var(--px) 0 var(--coin-shade); }

/* A refusal on a Boetemeister's screen, and the sentence after a write. The accent, not
   red: a refusal is an answer, and "no red" holds even here. */
.notice.wrong, .notice.done-note {
  margin: 1rem 0; padding: 0.6rem 0.75rem 0.6rem 1rem; font-size: 0.92rem;
  background:
    linear-gradient(var(--accent), var(--accent)) left / calc(2 * var(--px)) 100% no-repeat,
    var(--card);
}
.notice.done-note {
  background:
    linear-gradient(var(--kind), var(--kind)) left / calc(2 * var(--px)) 100% no-repeat,
    var(--card);
}
a.back { color: var(--accent); }
/* A name in the settlement table opens the payment form. In the body text's colour with a
   gold pixel line under it, so the gold in the row is the money and not the name too. */
a.payee { color: var(--ink); box-shadow: inset 0 calc(-1 * var(--px)) 0 var(--accent); }

/* --- the payment screen (`R46`) ------------------------------------------------ */

/* One column, full width: every control a thumb-sized target at 375px. */
/* `font-weight: 400` because the form shares its class name with the board's `.money`
   figures, which are bold: without it every label, choice and typed amount was bold. */
form.money {
  display: grid; gap: 0.35rem; margin: 1rem 0; padding: 0.85rem;
  font-weight: 400; font-variant-numeric: normal;
}
form.money label, form.money legend { font-size: 0.85rem; color: var(--soft); }
form.money input, form.money select, form.money textarea, details.undo input {
  font: inherit; width: 100%; padding: 0.6rem; border-radius: 0;
  border: var(--px) solid var(--line); background: var(--bg); color: var(--ink);
}
form.money fieldset { border: 0; margin: 0.4rem 0; padding: 0; display: grid; gap: 0.2rem; }
/* The four choices are rows, so the whole line is the tap target. */
form.money label.pick {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.45rem 0.2rem; color: var(--ink); font-size: 1rem;
}
form.money label.pick input { width: auto; margin: 0; accent-color: var(--accent); }
form.money .note { margin: 0 0 0.4rem; }
form.money .choices { margin: 0.5rem 0 0; }
form.money .choices .go { width: 100%; }

.ledger { list-style: none; margin: 0; padding: 0; }
.ledger > li { padding: 0.6rem 0.85rem; margin-bottom: 0.9rem; }
.ledger .what, .ledger .when { margin: 0; }
.ledger .credit, .history .credit { color: var(--soft); }
details.undo { margin-top: 0.35rem; font-size: 0.88rem; }
details.undo > summary { color: var(--soft); cursor: pointer; padding: 0.25rem 0; }
details.undo form { display: grid; gap: 0.35rem; margin-top: 0.35rem; }
details.undo label { color: var(--soft); }
details.undo button { border-color: var(--accent); }

/* --- the review queue (`SW`) ---------------------------------------------------- */

/*
  A word the parser did not know, marked where it was written (`BF.1`). The accent at a
  low strength rather than a warning colour: the word is the clue to what was meant, not
  a fault in the member who typed it. On the navy page a gold wash alone turns muddy
  brown, so a full-strength gold pixel line under the word carries it; the wash says
  where the word starts and stops. The text stays the body text's colour and font, so a
  marked word reads exactly as the member typed it.
*/
mark {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  box-shadow: inset 0 calc(-1 * var(--px)) 0 var(--accent);
  color: var(--ink); border-radius: 0; padding: 0 0.1rem;
}
/* A call-out can be one unbroken word — a link, a row of emoji — and it must wrap rather
   than push a 375px page sideways. */
blockquote.callout .said { overflow-wrap: anywhere; }

.queue { list-style: none; margin: 0; padding: 0; }
.queue > li > a { display: block; padding: 0.6rem 0.85rem; margin-bottom: 0.9rem; }
.queue > li > a:hover { background: var(--card-raised); }
.queue blockquote.callout { margin: 0; }
.reason { font-size: 0.92rem; margin: 0.6rem 0; }
.queue .reason { margin: 0.45rem 0 0.2rem; font-size: 0.88rem; }
.summary { color: var(--soft); font-size: 0.85rem; margin: 0; }

/* A row of a proposal or a reading: how many, what, who, and the price on the right. It
   wraps onto a second line at 375px rather than widening the page. */
.rows { list-style: none; margin: 0.25rem 0; padding: 0; }
.rows > li {
  display: flex; flex-wrap: wrap; gap: 0.15rem 0.5rem; align-items: baseline;
  padding: 0.45rem 0; border-bottom: 1px dashed var(--line);
}
.rows > li:last-child { border-bottom: 0; }
.rows .count { color: var(--soft); font-variant-numeric: tabular-nums; }
.rows .money { margin-left: auto; }
.rows code { color: var(--soft); }

/* The retype box and *Zoek boete* (`D9`, `D12`), in the payment screen's form style. The
   box is the call-out's own words, so it keeps the page's font rather than a code font. */
form.money textarea { resize: vertical; }
.find { display: grid; gap: 0.35rem; margin-bottom: 0.6rem; }
.find .inline { display: flex; gap: 0.5rem; }
.find .inline input { flex: 1; min-width: 0; }
.matches { list-style: none; margin: 0.2rem 0; padding: 0; font-size: 0.92rem; }
.matches > li { padding: 0.4rem 0; border-bottom: 1px dashed var(--line); }
.matches > li:last-child { border-bottom: 0; }
.matches code, .matches .typed { color: var(--soft); }
.matches .typed { font-size: 0.85rem; }

/* What the typed text would post, under the box it came from. */
.preview { border-top: var(--px) solid var(--line); margin-top: 0.4rem; padding-top: 0.6rem; }
.preview .reading { margin: 0 0 0.3rem; }
.preview blockquote.callout { margin: 0.3rem 0; }
.restate h2 { margin: 1rem 0 0.3rem; }
.changes > li { font-variant-numeric: tabular-nums; }

/* statistieken ------------------------------------------------------------------ */

/*
  `/statistieken` (`PX`, `R69`). One column on a phone, two at desktop width (`D13`), so
  the page gets a wider `main` than the board's. Each panel is a `.panel`; a panel ticket
  adds its own rules under its own heading below, and touches nothing above this block.
*/
main:has(> .panels) { max-width: 64rem; }
.panels { display: grid; gap: 1.25rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 60rem) {
  .panels { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.stats-panel { padding: 0.75rem; min-width: 0; }
.stats-panel h2 { margin-top: 0.25rem; }

/* 02, the towers. A row of columns that scrolls inside its panel rather than widening the
   page: a season is thirty-odd weeks. Coins overlap like a stack on a table. */
.towers {
  list-style: none; margin: 0; padding: 0.25rem 0 0.5rem;
  display: flex; align-items: flex-end; gap: 0.5rem; overflow-x: auto;
}
.tower { display: flex; flex-direction: column; align-items: center; flex: none; min-width: 3.25rem; }
.tower .stack { display: flex; flex-direction: column-reverse; align-items: center; }
.tower .stack .sprite + .sprite { margin-bottom: calc(-2 * var(--px)); }
.tower .stack .sprite.coin-10 + .sprite.coin-10 { margin-bottom: calc(-8 * var(--px)); }
.tower .niveleuro {
  display: flex; flex-direction: column; align-items: center;
  margin-bottom: calc(2 * var(--px)); padding-bottom: calc(2 * var(--px));
  border-bottom: var(--px) dashed var(--line);
  /* Its own colour, from the tokens: a niveleuro is euros, so still a coin, but not play. */
  --coin-gold: var(--gem); --coin-light: var(--ink); --coin-shade: var(--open);
}
/* "niveleuro" over "€22": wrapped at the space, never inside the word. */
.tower .niveleuro .label {
  font-size: 0.5rem; color: var(--gem); text-align: center;
  width: min-content; overflow-wrap: normal;
}
.tower .figure { display: flex; flex-direction: column; align-items: center; margin-top: 0.25rem; }
.tower .week { font-size: 0.75rem; color: var(--soft); }
.tower .euros { font-size: 0.625rem; color: var(--coin-gold); }
.tower .closed {
  display: flex; flex-direction: column; align-items: center;
  min-height: 2.25rem; font-size: 0.7rem; color: var(--soft);
}

/* 06, the fine calendar (`R72`). Weekdays down the side, season weeks across like the
   towers, scrolling inside its panel rather than widening the page. A cell is a square
   of its shade; outside the season so far there is no cell at all. */
.calendar-scroll { overflow-x: auto; padding-bottom: 0.25rem; }
.calendar { border-collapse: separate; border-spacing: var(--px); }
.calendar th { font-weight: 400; color: var(--soft); }
.calendar .week { font-family: var(--font-pixel); font-size: 0.5rem; padding: 0 0 0.25rem; }
.calendar .weekday { font-size: 0.75rem; text-align: left; padding-right: 0.25rem; }
.calendar td { width: 1.75rem; min-width: 1.75rem; height: 1.75rem; padding: 0; text-align: center; }
/* 8px is the pixel font's native size: crisp, and three digits (the team weekend's 106)
   still fit a 28px cell on one line. */
.calendar .tally { font-size: 0.5rem; white-space: nowrap; color: var(--bg); }
.calendar .shade-0, .calendar-legend .shade-0 { background: var(--heat-0); }
.calendar .shade-1, .calendar-legend .shade-1 { background: var(--heat-1); }
.calendar .shade-2, .calendar-legend .shade-2 { background: var(--heat-2); }
.calendar .shade-3, .calendar-legend .shade-3 { background: var(--heat-3); }
.calendar .shade-4, .calendar-legend .shade-4 { background: var(--heat-4); }
/* What a screen reader reads and the grid does not show: each day's name and count. */
.calendar .sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.calendar-legend {
  list-style: none; margin: 0.5rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.25rem 0.75rem; font-size: 0.8rem; color: var(--soft);
}
.calendar-legend li { display: flex; align-items: center; gap: 0.3rem; }
.calendar-legend .swatch { width: 0.875rem; height: 0.875rem; outline: 1px solid var(--line); }
/* The week a period closed: a small chest under its label, as under its tower (`D13`). */
.calendar .week .sprite.chest { --px: 1px; display: block; margin: 0.15rem auto 0; }
.calendar-legend .sprite.chest { --px: 1px; }

/* 05, most wanted. A row per type: its rank, its name and code, then its count as a bar
   of square blocks and the number beside it. Blocks, not coins, and not gold: a coin and
   gold are euros, and this is a count of fines (`D7`). The bar's length is decided in
   `api/statistics.py`; the leader's is twenty blocks, which fits 360px. */
.most-wanted {
  list-style: none; margin: 0; padding: 0; counter-reset: wanted;
  display: grid; gap: 0.6rem;
  --block: var(--gem); --block-edge: var(--shadow);
}
.wanted {
  counter-increment: wanted;
  display: grid; grid-template-columns: 1.75rem minmax(0, 1fr); column-gap: 0.25rem;
}
.wanted::before {
  content: counter(wanted); grid-row: span 2;
  font-family: var(--font-pixel); font-size: 0.75rem; line-height: 1.9; color: var(--soft);
}
.wanted .what { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.5rem; min-width: 0; }
.wanted .name { overflow-wrap: anywhere; }
.wanted .code { font-size: 0.625rem; color: var(--soft); }
.wanted .track { display: flex; align-items: center; gap: 0.5rem; }
.wanted .blocks { display: flex; gap: var(--px); flex: none; }
.wanted .block {
  width: calc(4 * var(--px)); height: calc(4 * var(--px)); background: var(--block);
  box-shadow: inset calc(-1 * var(--px)) calc(-1 * var(--px)) 0 var(--block-edge);
}
.wanted .count { font-size: 0.75rem; color: var(--ink); line-height: 1; }
/* 03, the race (`R70`). Inline SVG scaled to the panel's width, so it never widens the
   page at 360px. The lines are staircases on whole units, so `crispEdges` keeps them hard
   pixels. Three colours for the board's top three, named here from the tokens; every
   other line is faint. The viewer's own line (`.me`, set by `boetepot.js` from the own
   row's remembered name, ADR 017) is thick and in the ink colour, or thick in its own
   colour if it is one of the three. */
.race-panel { --race-1: var(--coin-gold); --race-2: var(--gem); --race-3: var(--open); }
.race-chart {
  display: block; width: 100%; height: auto; margin: 0.25rem 0 0.5rem;
  shape-rendering: crispEdges;
}
.race-chart .race-axis { stroke: var(--line); stroke-width: 2; }
.race-chart .race-grid { stroke: var(--line); stroke-width: 1; stroke-dasharray: 2 2; }
.race-chart .race-close { stroke: var(--wood-light); stroke-width: 1; stroke-dasharray: 2 2; }
.race-chart .race-label { fill: var(--soft); font-family: var(--font-pixel); font-size: 7px; }
.race-chart .race-close-label { fill: var(--wood-light); }
.race-line polyline { fill: none; stroke: var(--line); stroke-width: 2; }
.race-line rect { fill: none; }
.race-line.place-1 polyline { stroke: var(--race-1); }
.race-line.place-2 polyline { stroke: var(--race-2); }
.race-line.place-3 polyline { stroke: var(--race-3); }
.race-line.place-1 rect { fill: var(--race-1); }
.race-line.place-2 rect { fill: var(--race-2); }
.race-line.place-3 rect { fill: var(--race-3); }
.race-line.me polyline { stroke-width: 4; }
.race-line.me:not([class*="place-"]) polyline { stroke: var(--ink); }
.race-line.me:not([class*="place-"]) rect { fill: var(--ink); }

/* The names and totals under the chart, in the board's order: the figures (`D12`). */
.race-legend {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.2rem 0.9rem;
  font-size: 0.85rem; color: var(--soft);
}
.race-legend li { display: flex; align-items: center; gap: 0.35rem; min-width: 0; }
.race-legend .who { font-weight: 400; }
.race-legend .euros { font-size: 0.5625rem; color: var(--soft); }
.race-legend .swatch { width: 0.5rem; height: 0.5rem; background: var(--line); flex: none; }
.race-legend .place-1 .swatch { background: var(--race-1); }
.race-legend .place-2 .swatch { background: var(--race-2); }
.race-legend .place-3 .swatch { background: var(--race-3); }
.race-legend [class*="place-"] { color: var(--ink); }
.race-legend [class*="place-"] .who { font-weight: 600; }
.race-legend [class*="place-"] .euros { color: var(--coin-gold); }
.race-legend .me .swatch { outline: var(--px) solid var(--ink); }
.race-legend .me .who::after { content: " — jij"; color: var(--accent); font-weight: 600; }

/* Every week's figure as text (`D12`), folded away under the chart. Scrolls inside the
   panel, never the page. */
.race-table { margin-top: 0.75rem; font-size: 0.85rem; }
.race-table > summary { color: var(--soft); cursor: pointer; padding: 0.25rem 0; }
.race-table .table-scroll { overflow-x: auto; }
.race-table table { border-collapse: collapse; font-variant-numeric: tabular-nums; }
.race-table th, .race-table td { padding: 0.15rem 0.4rem; text-align: right; white-space: nowrap; }
.race-table th[scope="row"] { text-align: left; font-weight: 400; }
.race-table thead th { color: var(--soft); font-weight: 400; border-bottom: var(--px) solid var(--line); }

/* 09, how final is the number (`R75`). Call-outs in blocks, never coins (`D7`): a bar of
   twenty that stretches to the panel's width, so it fits a 360px phone, and the same
   square beside each count as its key. Final on the left, waiting on the right. */
.finality-bar { display: flex; gap: var(--px); margin: 0.75rem 0; }
.finality-bar .block { flex: 1 1 0; height: 1.25rem; }
.finality .block { display: inline-block; border: var(--px) solid var(--shadow); }
.finality .block.posted { background: var(--gem); }
.finality .block.decided { background: var(--kind); }
.finality .block.waiting { background: var(--stale); }
.finality-counts { list-style: none; margin: 0; padding: 0; }
.finality-counts li { margin: 0.3rem 0; }
.finality-counts .block { width: 0.9rem; height: 0.9rem; vertical-align: middle; margin-right: 0.35rem; }
.finality-counts .pixel { font-size: 0.75rem; }
/* 08, the pantry (`R74`). One shelf per item: its picture in a fixed-width cell so the
   sentences line up whatever the sprite's height, and the sentence beside it, wrapping
   under itself on a narrow phone rather than under the picture. */
.pantry { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.shelf {
  display: grid; grid-template-columns: 3.5rem minmax(0, 1fr); align-items: center;
  gap: 0.75rem; padding: 0.4rem 0; border-top: var(--px) solid var(--line);
}
.shelf:first-child { border-top: 0; }
.shelf .sprite { justify-self: center; }
.shelf .stock { overflow-wrap: anywhere; }
.shelf .incurred { font-size: 0.75rem; color: var(--kind); margin-right: 0.25rem; }
.shelf .brought { color: var(--soft); white-space: nowrap; }
/* 07, who calls out whom. A matrix of numbered squares (`D7`: counts are never coins),
   sized so eleven columns fit a 360px phone with room to spare: 1.375rem a column, an 8px
   pixel figure, labels of two or three letters decided in `api`. A wider team scrolls inside its frame, never
   the page. The diagonal is owning up, so it is outlined rather than filled. */
.matrix-frame { overflow-x: auto; margin: 0.5rem 0; }
.matrix {
  border-collapse: separate; border-spacing: var(--px);
  font-family: var(--font-pixel); font-size: 0.5rem; line-height: 1;
}
.matrix th, .matrix td {
  width: 1.375rem; min-width: 1.375rem; height: 1.375rem;
  padding: 0; text-align: center; vertical-align: middle; font-weight: 400;
}
.matrix th { color: var(--soft); }
.matrix th abbr { text-decoration: none; }
.matrix tbody th { text-align: left; padding-right: calc(2 * var(--px)); width: auto; }
.matrix td { background: var(--bg); color: var(--soft); }
.matrix td.hit { background: var(--card-raised); color: var(--ink); }
.matrix td.own { box-shadow: inset 0 0 0 var(--px) var(--gem); color: var(--gem); }
/* A total is a count, so not gold: gold is euros. */
.matrix .row-total { color: var(--ink); background: transparent; }
.matrix tr.other th { color: var(--soft); }
.matrix-legend {
  display: flex; flex-wrap: wrap; gap: 0.25rem 0.75rem;
  margin: 0.25rem 0; font-size: 0.8rem; color: var(--soft);
}
.matrix-legend div { display: flex; align-items: baseline; gap: 0.3rem; }
.matrix-legend dt { font-family: var(--font-pixel); font-size: 0.5rem; color: var(--ink); }
.matrix-legend dd { margin: 0; }
