/* ───────────────────────────────────────────────────────────────────────────
   KARTING FISHER — pit wall

   The design language is the pit board: the one object in this sport built to
   be read in a fraction of a second, from a distance, by someone who cannot
   look twice. Numbers are the content; words are subordinate to them.

   Colour is the sport's own vocabulary, not a brand palette. Purple is a
   session best, green an improvement, yellow a caution, red a stop. Those
   meanings are already learned by anyone standing at a pit wall, so nothing
   here invents a new code for them — and nothing uses them decoratively.

   One variable font, two voices, from its width axis: wide heavy figures for
   what is read at a glance, normal width for everything else. Self-hosted,
   because track mobile data is poor and a font that fails to load is a real
   risk at the only moment this screen matters.
   ─────────────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Archivo';
  font-style: normal; font-weight: 100 900; font-stretch: 62% 125%;
  font-display: swap;
  src: url(fonts/archivo-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal; font-weight: 100 900; font-stretch: 62% 125%;
  font-display: swap;
  src: url(fonts/archivo-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Asphalt at dusk: cool, desaturated, never neutral black. */
  --asphalt:#0E1114;
  --kerb:#171C22;
  --kerb-lit:#212831;
  --line:#2B333C;
  --chalk:#F0F4F7;
  --dust:#8E9BA6;

  /* The sport's own code. Each one means exactly one thing. */
  --best:#B38CFF;
  --gain:#35D48A;
  --warn:#FFB224;
  --stop:#FF5347;

  --tap:56px;              /* one thumb, sometimes in gloves */
  --gut:16px;
  --radius:12px;
}

* { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
html { color-scheme:dark; }
body {
  margin:0; background:var(--asphalt); color:var(--chalk);
  font-family:'Archivo', system-ui, -apple-system, sans-serif;
  font-size:17px; line-height:1.4; font-weight:450;
  font-variant-numeric:tabular-nums;
  -webkit-text-size-adjust:100%;
  padding-bottom:calc(var(--tap) + 18px + env(safe-area-inset-bottom));
}

/* Figures that get read at a glance take the width axis out wide. A pit board
   is wide and heavy for the same reason. */
.fig { font-variation-settings:'wdth' 116, 'wght' 740; letter-spacing:-0.01em;
       font-variant-numeric:tabular-nums; }
.fig-sm { font-variation-settings:'wdth' 110, 'wght' 680; font-variant-numeric:tabular-nums; }

main { max-width:34rem; margin:0 auto; padding:0 var(--gut); }
section[hidden] { display:none; }

/* A demo must never be mistakable for a race, so this sits above everything
   and does not scroll away. Amber, because it is a caution and not an error. */
.demoband {
  position:sticky; top:0; z-index:6;
  background:var(--warn); color:#241700;
  font-size:.8125rem; font-weight:650; text-align:center;
  padding:6px 12px;
}
.demoband[hidden] { display:none; }
.demoband + .strip { top:28px; }

/* ── the strip: flag and race clock, always visible ───────────────────────── */
.strip {
  position:sticky; top:0; z-index:5;
  display:flex; align-items:center; gap:10px;
  /* A full-width bar that keeps its content in the same column as main. */
  padding:10px max(var(--gut), calc((100% - 34rem) / 2 + var(--gut)));
  margin:0 0 14px;
  background:color-mix(in srgb, var(--asphalt) 92%, transparent);
  backdrop-filter:blur(8px);
  border-bottom:1px solid var(--line);
}
.flagdot { width:12px; height:12px; border-radius:50%; background:var(--dust); flex:none; }
.flagdot[data-flag="green"]{ background:var(--gain); }
.flagdot[data-flag="yellow"]{ background:var(--warn); }
.flagdot[data-flag="red"]{ background:var(--stop); }
.flagdot[data-flag="chequered"]{
  background:
    repeating-conic-gradient(var(--chalk) 0 25%, #1b1b1b 0 50%) 0 0/6px 6px;
}
.strip .where { color:var(--dust); font-size:.875rem; min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Something is wrong with what you are looking at — say so where the eye
   already goes, not in a box somewhere else. */
.strip .where.warn { color:var(--warn); }
.strip .clock { margin-left:auto; font-size:1.0625rem; }
/* A new version is waiting. The worker never swaps itself in mid-race, so this
   is the operator's decision — offered where the eye already goes, and quiet
   enough to ignore until the stint is over. */
/* 44px, not smaller: this project set that floor and then had to fix a 36px
   gear for breaking it. A secondary control is still a thumb target. */
.strip .upd { font-size:.75rem; padding:0 10px; min-height:44px;
  border-color:var(--gain); color:var(--gain); background:transparent;
  border-radius:var(--radius); white-space:nowrap; }
/* The race clock, when it is one we wound by hand rather than the board's.
   Dotted underneath, so the number still reads at a glance but nobody takes
   it for what the results will be based on. */
.strip .clock.manual { color:var(--warn);
  text-decoration:underline dotted; text-underline-offset:3px; }

/* ── the hero: the pit window ─────────────────────────────────────────────── */
.window { padding:4px 0 20px; }
.window .lede { color:var(--dust); font-size:.9375rem; margin:0 0 2px; }
.window .count {
  font-size:clamp(3.75rem, 21vw, 6.5rem); line-height:.92; margin:0;
  font-variation-settings:'wdth' 120, 'wght' 780; letter-spacing:-0.02em;
  font-variant-numeric:tabular-nums;
}
.window[data-state="not-yet"] .count { color:var(--dust); }
.window[data-state="open"]    .count { color:var(--chalk); }
.window[data-state="closing"] .count { color:var(--warn); }
.window[data-state="late"]    .count { color:var(--stop); }
.window[data-state="done"]    .count { color:var(--gain); }
.window .under { color:var(--dust); font-size:.9375rem; margin:8px 0 0; }

/* The stint as a window, not a number: where this stint began, where it may
   end, and where we are in it. This is the one piece of ornament-free
   information design the screen is built around. */
.stintbar { margin-top:16px; }
.stintbar .track {
  position:relative; height:12px; border-radius:6px;
  background:var(--kerb); border:1px solid var(--line); overflow:hidden;
}
.stintbar .open {
  position:absolute; top:0; bottom:0; background:color-mix(in srgb, var(--gain) 26%, transparent);
}
.stintbar .now {
  position:absolute; top:-5px; width:3px; height:20px; border-radius:2px;
  background:var(--chalk);
}
.stintbar .marks {
  display:flex; justify-content:space-between; margin-top:7px;
  color:var(--dust); font-size:.75rem;
}

/* ── rows: a pit board is a stack of rows ─────────────────────────────────── */
.rows { display:flex; flex-direction:column; gap:6px; margin:0 0 22px; }
.row {
  display:flex; align-items:center; gap:12px; min-height:var(--tap);
  padding:10px 14px; background:var(--kerb);
  border:1px solid var(--line); border-radius:var(--radius);
}
.row .num { font-size:1.6rem; min-width:2.6ch; }
.row .body { min-width:0; flex:1; }
.row .body b { font-weight:600; display:block;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.row .body small { color:var(--dust); font-size:.8125rem; }
.row .tail { margin-left:auto; text-align:right; white-space:nowrap; }
.row .tail .big { font-size:1.25rem; display:block; }
.row .tail small { color:var(--dust); font-size:.75rem; }
.row.ours { border-color:var(--chalk); }
.row.degraded { border-style:dashed; }
/* A team that can no longer fit its remaining stops into the time left. Not a
   forecast — the bare minimum pit time no longer fits — so it is marked on the
   row itself rather than tucked into the sub-line. */
.row.short { border-color:var(--stop); }
/* The stint-limit countdown: a third line in the tail, under the stint clock.
   Shown on our row always and on a rival's only once it is tight, because a
   countdown on thirty-six rows is how a real warning gets missed. */
.row .tail small.limit { display:block; margin-top:1px; font-size:.6875rem; }

h2 { font-size:1.0625rem; font-weight:600; margin:0 0 10px;
     font-variation-settings:'wdth' 104, 'wght' 620; }
.note { color:var(--dust); font-size:.875rem; margin:0 0 14px; }
.none { color:var(--dust); padding:18px 2px; }

/* Semantic text colours. Each is the sport's meaning, never decoration. */
.best{color:var(--best)} .gain{color:var(--gain)}
.warn{color:var(--warn)} .stop{color:var(--stop)} .dust{color:var(--dust)}

/* ── controls ─────────────────────────────────────────────────────────────── */
button {
  font:inherit; font-weight:620; min-height:var(--tap);
  border-radius:var(--radius); border:1px solid var(--line);
  background:var(--kerb); color:var(--chalk); cursor:pointer;
  padding:0 18px;
}
button:active { background:var(--kerb-lit); }
/* The one unmissable action is inverted rather than tinted: maximum contrast
   in daylight, and no accent colour spent on a button. */
button.go { background:var(--chalk); color:var(--asphalt); border-color:var(--chalk); }
button.go:active { background:#cfd6db; }
button:disabled { opacity:.42; cursor:default; }
button.quiet { background:transparent; }
button.quiet.warn { color:var(--warn); border-color:var(--warn); }
:focus-visible { outline:3px solid var(--best); outline-offset:2px; }

/* ── pit lanes ────────────────────────────────────────────────────────────── */
.lane { margin-bottom:10px; }
.lane .head { display:flex; align-items:baseline; gap:8px; margin:0 2px 6px; }
.lane .head b { font-weight:600; }
.lane .head small { color:var(--dust); font-size:.8125rem; }
.slots { display:flex; gap:6px; flex-wrap:wrap; min-height:var(--tap);
  padding:6px; background:var(--kerb); border:1px solid var(--line);
  border-radius:var(--radius); align-items:center; }
.slots.over { border-color:var(--chalk); background:var(--kerb-lit); }
.kart {
  min-width:52px; height:44px; display:flex; align-items:center; justify-content:center;
  background:var(--asphalt); border:1px solid var(--line); border-radius:9px;
  font-size:1.25rem; cursor:grab; touch-action:none;
}
.kart[data-next="1"] { border-color:var(--chalk); }
.kart.dragging { opacity:.4; }
.slots .empty { color:var(--dust); font-size:.8125rem; padding:0 8px; }

/* ── setup ────────────────────────────────────────────────────────────────── */
.gear { min-height:44px; min-width:44px; padding:0; margin-left:6px; font-size:1.125rem;
  background:transparent; border-color:transparent; color:var(--dust); }
.gear[aria-current="page"] { color:var(--chalk); }
.form { display:grid; gap:10px; margin:0 0 24px; }
.form label { display:grid; gap:5px; color:var(--dust); font-size:.9375rem; }
.form input {
  min-height:var(--tap); padding:0 14px; font:inherit; font-size:1.0625rem;
  font-variant-numeric:tabular-nums; color:var(--chalk); background:var(--kerb);
  border:1px solid var(--line); border-radius:var(--radius);
}
.form input::placeholder { color:#5b6670; }
.actions { display:flex; gap:8px; margin:0 0 24px; }
.actions button { flex:1; }
.lanerow { display:grid; grid-template-columns:1fr 1.3fr var(--tap); gap:8px; align-items:end; }
/* Grid children refuse to shrink below their content width unless told to,
   and an input's default size is wide enough to push the row off screen. */
.lanerow > * { min-width:0; }
.lanerow input { width:100%; }
.lanerow button { padding:0; }
/* A refusal and a confirmation are not the same thing, so they do not share
   a colour: amber is a caution, and saving something is not one. */
.problem { margin:0 0 18px; color:var(--warn); }
.problem[data-kind="ok"] { color:var(--dust); }

/* ── tabs: thumb reach, as many destinations as the role has ───────────── */
.tabs {
  position:fixed; left:0; right:0; bottom:0; z-index:10;
  /* However many tabs this role can see. A fixed column count wrapped the
     sixth tab onto a second row for a spotter and left a driver's two
     squeezed into the left of five. */
  display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
  background:color-mix(in srgb, var(--asphalt) 94%, transparent);
  backdrop-filter:blur(10px);
  border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
}
.tabs button {
  min-height:var(--tap); border:0; border-radius:0; background:none;
  color:var(--dust); font-size:.8125rem; font-weight:560; padding:0;
}
.tabs button[aria-current="page"] { color:var(--chalk); box-shadow:inset 0 2px 0 var(--chalk); }

@media (prefers-reduced-motion: no-preference) {
  .row, .kart, button { transition:border-color .12s ease, background-color .12s ease; }
}

/* ── signing in ─────────────────────────────────────────────────────────── */
/* Over everything, because a signed-out app has nothing to show. */
#gate { position:fixed; inset:0; z-index:50; background:var(--asphalt);
  display:grid; place-items:center; padding:24px 16px; }
/* An explicit `display` in author CSS beats the `hidden` attribute, so without
   this the gate never goes away and the app renders underneath it. The same
   defect this project already fixed once on the strip — found by looking at a
   screenshot, which the DOM check could not see. */
#gate[hidden] { display:none; }
.gatebox { width:100%; max-width:340px; text-align:center; }
.gatelogo { width:96px; height:96px; margin:0 auto 12px; display:block; }
.gatebox h1 { font-size:1.375rem; margin:0 0 4px;
  font-variation-settings:'wdth' 108, 'wght' 680; }
#f-gate { display:flex; flex-direction:column; gap:10px; margin-top:16px; }
#f-gate input { font-size:1.75rem; text-align:center; letter-spacing:.4em;
  padding:14px 10px; min-height:var(--tap); font-variant-numeric:tabular-nums; }

/* ── the driver's screen ────────────────────────────────────────────────── */
/* Landscape, big boxes, one number each. Pages swipe with scroll snapping
   rather than a gesture handler: fewer moving parts, and it still works with
   a thumb in a glove. */
.drive { padding:0; }
.dash { display:flex; overflow-x:auto; scroll-snap-type:x mandatory;
  gap:0; -webkit-overflow-scrolling:touch; scrollbar-width:none; }
.dash::-webkit-scrollbar { display:none; }
/* The height is measured and set by renderDrive, not guessed: the demo band
   and the strip sit above this and a `calc(100vh - something)` was wrong by
   exactly their height, which put the bottom box under the tab bar. */
.dpage { flex:0 0 100%; scroll-snap-align:start; display:grid; gap:6px;
  height:var(--dash-h, min(62vh, 420px)); }
/* The grids from LapMate's dash spec: one big and two, and a two-by-two. */
.grid-c { grid-template-columns:2fr 1fr; grid-template-rows:1fr 1fr; }
.grid-c > :first-child { grid-row:1 / 3; }
.grid-d { grid-template-columns:1fr 1fr; grid-template-rows:1fr 1fr; }

.dbox { background:var(--kerb); border:1px solid var(--line);
  border-radius:var(--radius); padding:8px 10px; min-width:0;
  display:flex; flex-direction:column; justify-content:center; overflow:hidden; }
.dbox .blabel { color:var(--dust); font-size:.75rem; text-transform:lowercase;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.dbox .bval { font-variation-settings:'wdth' 118, 'wght' 760; line-height:.95;
  letter-spacing:-0.02em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.dbox .bunit { color:var(--dust); font-size:.75rem; margin-top:2px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.dbox .bnone { color:var(--dust); font-size:.9375rem; }
.dbox.s-hero .bval { font-size:clamp(2.5rem, 11vw, 5rem); }
.dbox.s-big  .bval { font-size:clamp(2rem, 8vw, 3.25rem); }
.dbox.s-med  .bval { font-size:clamp(1.5rem, 6vw, 2.25rem); }
.dbox .bval.gain { color:var(--gain); }
.dbox .bval.stop { color:var(--stop); }
.dbox .bval.best { color:var(--best); }
.dbox.gain { border-color:var(--gain); }
.dbox.stop { border-color:var(--stop); }
.dashnote { padding:10px 2px 0; margin:0; }

/* Landscape is where this screen belongs: the phone is sideways on the kart,
   so the boxes take the whole height and the tab bar gets out of the way. */
@media (orientation:landscape) and (max-height:520px) {
  /* The dash takes the whole glass. Everywhere else a centred column is
     right, but the phone is sideways on the kart here and a 540px strip down
     the middle of it wastes half the screen the driver is glancing at. */
  body[data-screen="drive"] main { max-width:none; margin:0; padding:0 8px; }
  .drive .dashnote { display:none; }
  .dbox.s-hero .bval { font-size:clamp(3rem, 16vh, 7rem); }
  .dbox.s-big  .bval { font-size:clamp(2.25rem, 11vh, 4.5rem); }
  .dbox.s-med  .bval { font-size:clamp(1.75rem, 8vh, 3rem); }
}
