/* AF1 tracking — field UI.
 *
 * SELF-CONTAINED ON PURPOSE. This file does not load Bootstrap, af1.css or any
 * webfont. The marketing site's dark palette is the wrong tool here: this is
 * read at arm's length, outdoors, in direct November sun, by someone wearing
 * gloves. Dark themes lose that fight — a phone cannot out-emit the sky, so
 * the brightest thing available has to be the background, not the text.
 *
 * The brand tie is the near-black AF1 ground on the header bar, the orange
 * rule under it, and heavy condensed uppercase headings. The cost of that tie
 * is zero bytes: see track/README.md for what dropping Bootstrap, af1.css and
 * the webfonts did to the page weight.
 *
 * DISCIPLINE COLOURS ARE VALIDATED, NOT CHOSEN — same rule as the route
 * colours in af1-routes.js. Run the dataviz palette validator on the
 * ALL-PAIRS pairlist before changing one; a list interleaves disciplines, so
 * any two can end up adjacent:
 *
 *   node scripts/validate_palette.js "#0891B2,#7C3AED,#C2410C" --mode light --pairs all
 *   node scripts/validate_palette.js "#0EA5BE,#8B5CF6,#E25410" --mode dark  --pairs all
 *
 * Both pass every check. Worst all-pairs CVD separation is ΔE 15.0 against a
 * floor of 8, so the three stay distinguishable with colour-blindness.
 *
 * Gravel and moto stayed close to the website's teal and orange. RUN HAD TO
 * MOVE off the flyer's olive #a8a80e: against white it manages only 2.47:1
 * contrast, and it collapses into moto's orange at ΔE 4.9 under deuteranopia.
 * Violet is the nearest hue that clears both. Colour is never the only signal
 * anyway — every discipline mark carries its name in text beside it.
 */

:root {
  color-scheme: light;

  --ink:        #101010;   /* body text — near-black, not gray */
  --ink-2:      #3d3d3d;   /* secondary text, still 10:1 on paper */
  --paper:      #ffffff;
  --paper-2:    #f1f1ef;   /* raised row */
  --line:       #101010;   /* borders are INK, not hairlines: a 1px #ddd
                              border is invisible in sunlight */
  --line-soft:  #c8c8c4;

  --brand-ground: #01100f;  /* AF1 near-black, from the flyer */
  --brand-orange: #e07a01;

  --gravel: #0891B2;
  --run:    #7C3AED;
  --moto:   #C2410C;
  /* Ink for text sitting ON a discipline colour. White on the day cyan is
   * only 3.7:1 and fails; dark on it is 5.1:1. Measured, not guessed. */
  --on-gravel: #06100f;
  --on-run:    #ffffff;
  --on-moto:   #ffffff;
  --accent: var(--brand-ground);

  --ok:      #15803d;
  --danger:  #b3200e;

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Condensed heavy uppercase reads as the site's Anton without a download. */
  --font-head: ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;

  --tap: 60px;             /* nothing interactive is smaller than this */
  --gutter: clamp(.85rem, .5rem + 2vw, 1.5rem);
}

/* Night: the crew call is 6am in November and it is pitch dark. Same hues,
 * stepped for a dark surface and validated separately. */
:root[data-theme="night"] {
  color-scheme: dark;
  --ink: #f4f4f2;
  --ink-2: #b9bdbc;
  --paper: #0a1413;
  --paper-2: #16211f;
  --line: #f4f4f2;
  --line-soft: #3a4745;
  --gravel: #0EA5BE;
  --run:    #8B5CF6;
  --moto:   #E25410;
  /* Every night colour is light enough that white text fails on it. */
  --on-gravel: #06100f;
  --on-run:    #06100f;
  --on-moto:   #06100f;
  --accent: #f4f4f2;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;              /* >=16px or iOS zooms on focus */
  line-height: 1.4;
  -webkit-text-size-adjust: 100%;
  padding-bottom: calc(2rem + env(safe-area-inset-bottom, 0px));
}

h1, h2 {
  font-family: var(--font-head);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .01em;
  margin: 0;
}
h1 { font-size: clamp(1.5rem, 1.1rem + 2vw, 2.1rem); }
h2 { font-size: 1rem; letter-spacing: .08em; color: var(--ink-2); }

a { color: inherit; }
.muted { color: var(--ink-2); }
.hint  { color: var(--ink-2); font-size: .9rem; margin: .75rem 0 0; }
.error { color: var(--danger); font-weight: 700; margin: .75rem 0 0; }
.stack > * + * { margin-top: 1rem; }

/* ---------- header ---------- */
.bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem;
  background: var(--brand-ground);
  color: #fff;
  border-bottom: 3px solid var(--brand-orange);
  padding: .5rem var(--gutter);
  padding-top: calc(.5rem + env(safe-area-inset-top, 0px));
  position: sticky; top: 0; z-index: 20;
}
.bar__brand {
  display: inline-flex; align-items: center; gap: .5rem;
  text-decoration: none; color: #fff;
  font-family: var(--font-head); font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; font-size: 1rem;
}
/* The wordmark is type, not an image. The site's logo JPEG is 23.7KB for a
 * 28px square — not worth it on a checkpoint's data connection. */
.bar__mark {
  background: var(--brand-orange); color: var(--brand-ground);
  padding: .15rem .4rem; border-radius: 3px; font-size: .95rem;
}
.bar__right { display: flex; align-items: center; gap: .9rem; }
.bar__right a, .bar__right button {
  color: #fff; background: none; border: 0; padding: .35rem;
  font: inherit; font-size: .85rem; cursor: pointer; text-decoration: underline;
}
.bar__right form { margin: 0; display: inline; }

main { padding: 1rem var(--gutter) 0; max-width: 44rem; margin-inline: auto; }

/* ---------- alert ---------- */
.alert {
  padding: calc(.9rem + env(safe-area-inset-top, 0px)) var(--gutter) .9rem;
  background: var(--danger); color: #fff;
  font-weight: 800; text-transform: uppercase; letter-spacing: .03em;
  text-align: center;
}

/* ---------- discipline identity ----------
 * A colour bar plus a text label. Never colour alone.
 *
 * `--stripe` is set INLINE from checkins/palette.py rather than by a class
 * per discipline, because a checkpoint can serve several at once: gravel and
 * moto share every station, so most stripes are two-tone. One mechanism
 * covers one colour, two or three without a combinatorial pile of classes. */
.striped { position: relative; padding-left: calc(.9rem + 8px); }
.striped::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 8px;
  background: var(--stripe, var(--ink-2)); border-radius: 4px 0 0 4px;
}

.tag {
  display: inline-block; flex: none;
  background: var(--tag-bg, var(--ink)); color: var(--tag-ink, var(--paper));
  font-weight: 800; font-size: .7rem; letter-spacing: .09em;
  text-transform: uppercase; padding: .2rem .45rem; border-radius: 3px;
  white-space: nowrap;
}

/* ---------- station picker ---------- */
.group { margin-bottom: 1.6rem; }
.group__head {
  display: flex; align-items: center; gap: .6rem; margin-bottom: .5rem;
  padding-bottom: .35rem; position: relative;
}
.group__head::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--stripe, var(--ink-2));
}
.group__head h2 { color: var(--ink); font-size: 1.05rem; }
.group__count { margin-left: auto; color: var(--ink-2); font-size: .85rem; }

.stations { display: grid; gap: .5rem; }
.station {
  display: flex; align-items: center; gap: .6rem;
  min-height: var(--tap); padding: .7rem .9rem;
  background: var(--paper); color: var(--ink); text-decoration: none;
  border: 2px solid var(--line-soft); border-radius: 5px;
}
.station:hover, .station:focus-visible { background: var(--paper-2); border-color: var(--line); }
.station.is-current { border-color: var(--line); background: var(--paper-2); }
.station__name { font-weight: 700; }
.station__mi { margin-left: auto; color: var(--ink-2); font-size: .85rem; white-space: nowrap; }

/* ---------- station header ---------- */
.head { display: flex; align-items: center; gap: .75rem; margin-bottom: 1rem; }
.head h1 { flex: 1 1 auto; }
.change { font-size: .9rem; white-space: nowrap; }

/* ---------- bib entry ---------- */
.label { display: block; font-weight: 700; font-size: .85rem; text-transform: uppercase; letter-spacing: .06em; margin-bottom: .3rem; }
.bib, .search {
  width: 100%; background: var(--paper); color: var(--ink);
  border: 3px solid var(--line); border-radius: 5px;
  padding: .5rem .7rem; font-family: var(--font-head);
}
.bib { font-size: clamp(2rem, 1.2rem + 4vw, 3rem); font-weight: 800; letter-spacing: .06em; }
.search { font-size: 1.15rem; font-weight: 600; }
.bib:focus, .search:focus { outline: 4px solid var(--brand-orange); outline-offset: 1px; }

/* ---------- confirmation card ---------- */
.card {
  margin-top: .9rem; padding: .85rem .95rem;
  background: var(--paper); border: 3px solid var(--line); border-radius: 5px;
}
.card__top { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.card__bib { font-family: var(--font-head); font-weight: 800; font-size: 2rem; line-height: 1; }
.card__name { font-weight: 800; font-size: 1.3rem; text-transform: uppercase; letter-spacing: .01em; margin: .25rem 0 0; }
.card__meta { color: var(--ink-2); font-size: .9rem; margin: .15rem 0 0; }
.card__pos { font-weight: 700; margin: .15rem 0 0; }

/* ---------- IN / OUT / DONE ----------
 * Deliberately MONOCHROME. Colour on this row would be a second categorical
 * scale fighting the discipline one — and OUT's natural amber sits right on
 * top of moto's orange. Fill vs outline separates them instead, which also
 * survives glare and colour-blindness. */
.actions { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; margin-top: .9rem; }
.act {
  min-height: var(--tap); border-radius: 5px; cursor: pointer;
  border: 3px solid var(--line); background: var(--paper); color: var(--ink);
  font-family: var(--font-head); font-weight: 800; font-size: 1.15rem;
  letter-spacing: .05em; text-transform: uppercase;
}
.act--in { background: var(--line); color: var(--paper); }
.act:active { transform: scale(.97); }
.act:focus-visible { outline: 4px solid var(--brand-orange); outline-offset: 2px; }

/* ---------- name-match fallback ---------- */
.matches { display: grid; gap: .4rem; margin-top: .5rem; }
.match {
  display: flex; align-items: center; gap: .5rem; text-align: left;
  min-height: var(--tap); padding: .5rem .8rem; cursor: pointer;
  background: var(--paper); color: var(--ink);
  border: 2px solid var(--line-soft); border-radius: 5px; font: inherit;
}
.match:hover { background: var(--paper-2); }

/* ---------- lists ---------- */
.list { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.row {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .5rem .7rem; border-radius: 4px;
  background: var(--paper); border: 2px solid var(--line-soft);
}
.row.is-void { opacity: .55; }
.row.is-void .row__name, .row.is-void .row__bib { text-decoration: line-through; }
.row__kind {
  font-family: var(--font-head); font-weight: 800; font-size: .75rem;
  letter-spacing: .06em; padding: .15rem .4rem; border-radius: 3px;
  border: 2px solid var(--line); flex: none;
}
.row__kind--in   { background: var(--line); color: var(--paper); }
.row__kind--done { background: var(--line); color: var(--paper); }
.row__bib  { font-family: var(--font-head); font-weight: 800; }
.row__name { flex: 1 1 7rem; font-weight: 600; }
.row__link { display: flex; align-items: center; gap: .5rem; flex: 1 1 9rem; text-decoration: none; color: inherit; }
.row__time { color: var(--ink-2); font-variant-numeric: tabular-nums; font-size: .9rem; }
.row__pos  { flex-basis: 100%; color: var(--ink-2); font-size: .9rem; }
.undo {
  background: none; border: 2px solid var(--line-soft); border-radius: 4px;
  color: var(--ink); font: inherit; font-size: .8rem; font-weight: 700;
  padding: .3rem .55rem; cursor: pointer; min-height: 38px;
}
.undo:hover { border-color: var(--danger); color: var(--danger); }

/* ---------- athlete detail ---------- */
.pii {
  padding: .8rem .95rem; border-radius: 5px;
  background: var(--paper); border: 3px solid var(--danger);
}
.pii h2 { color: var(--danger); }
.pii a { font-weight: 800; font-size: 1.15rem; }

/* ---------- forms ---------- */
.form { display: grid; gap: .4rem; max-width: 21rem; }
.form label { display: block; font-weight: 700; font-size: .85rem; text-transform: uppercase; letter-spacing: .05em; margin-bottom: .25rem; }
.form input {
  width: 100%;
  background: var(--paper); color: var(--ink);
  border: 3px solid var(--line); border-radius: 5px;
  padding: .55rem .7rem; font: inherit; font-size: 1rem;
}
.btn {
  min-height: var(--tap); width: 100%; cursor: pointer;
  background: var(--line); color: var(--paper); border: 3px solid var(--line);
  border-radius: 5px; font-family: var(--font-head); font-weight: 800;
  font-size: 1.05rem; letter-spacing: .05em; text-transform: uppercase;
  margin-top: .6rem;
}

/* ---------- dashboard (phase 2) ----------
 * Unlike the check-in screens this one is for a dispatcher on a laptop, so it
 * may be wider and heavier: it pulls Leaflet and all nine GPX tracks. Keep
 * that asymmetry deliberate — do not let these styles leak onto the station
 * screens, which are budgeted at ~23 KB. */
.dash { max-width: none; }
.dash__head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; }
.dash__stamp { color: var(--ink-2); font-size: .85rem; font-variant-numeric: tabular-nums; }

.banner {
  margin: .75rem 0 1rem; padding: .6rem .8rem;
  border: 2px dashed var(--line-soft); border-radius: 5px;
  color: var(--ink-2); font-size: .9rem;
}

.dash__filters { display: flex; flex-wrap: wrap; gap: .75rem; margin-bottom: .9rem; }
.filt {
  border: 2px solid var(--line-soft); border-radius: 6px;
  padding: .4rem .7rem .6rem; margin: 0; min-width: 12rem;
}
.filt legend { padding: 0 .3rem; }
.filt__all { display: inline-flex; align-items: center; gap: .4rem; cursor: pointer; }
.filt__course {
  display: flex; align-items: center; gap: .45rem;
  padding: .25rem 0; cursor: pointer; font-size: .95rem;
}
.filt__swatch { width: 14px; height: 14px; border-radius: 3px; border: 1px solid var(--line); flex: none; }
.filt__count { margin-left: auto; color: var(--ink-2); font-variant-numeric: tabular-nums; font-size: .85rem; }
.filt--find { display: grid; align-content: start; gap: .3rem; min-width: 16rem; }
.filt--find .search { font-size: 1rem; padding: .4rem .6rem; border-width: 2px; }

.dash__map {
  height: min(62vh, 620px); border: 3px solid var(--line); border-radius: 6px;
  background: var(--paper-2);
}
.dash__feed { margin-top: 1.5rem; }
.dash__feed h2 { margin-bottom: .5rem; }

/* Leaflet draws its own light chrome; keep it legible in night mode without
   inverting the map tiles themselves. */
:root[data-theme="night"] .leaflet-control-attribution,
:root[data-theme="night"] .leaflet-control-zoom a { background: #0a1413; color: #d8dedd; }
