/* The app's own palette (FastestLapApp.swift): the demo should look like the
   thing it is a demo of. */
:root {
  --bg-deep: #131923;
  --carbon: #0b0e12;
  --ink: #f2f5f9;
  --muted: #8b939f;
  --teal: #0ed3c0;
  --danger: #f24540;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  overflow: hidden;
  /* Every touch on this page is aimed at the car, never at the document: there
     is nothing to scroll (overflow is hidden) and nothing to zoom into. Set
     here rather than only on the controls because it is the element UNDER the
     thumb that gets a say, and a tap can land on the HUD, the gantry or the
     banner as easily as on a pad. */
  touch-action: none;
  background: var(--bg-deep);
  color: var(--ink);
  font: 400 16px/1.4 ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

#stage { position: fixed; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }

/* The plate every HUD block sits on. Nearly opaque, and it has to be: the world
   under the HUD is not a wash but hard-edged geometry, and a road edge crossing
   behind the telemetry drew a visible line straight through the rev strip. */
.plate {
  background: rgba(11, 14, 18, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 7px;
}

#chrome {
  position: fixed;
  inset: 0;
  padding: max(12px, env(safe-area-inset-top)) 12px 12px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s ease 0.2s;
}
body.ready #chrome { opacity: 1; }

/* Readability shadow, per block. NOT on #chrome: a filter on a screen-sized
   element rasterises the whole overlay into one composited layer above the
   canvas, and anything inside it that fades to opacity 0 is still painted —
   which left the gantry's shadow on the track after the lights went out. */
#band, #cues, #keys, #tools { filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.45)); }

#band { display: flex; align-items: flex-start; gap: 12px; }
#right-col { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }

/* ---- timing (top left) ------------------------------------------------- */

#timing { display: flex; overflow: hidden; }

/* The status spine: the colour the last completed lap earned, held flush
   against the panel edge — it keeps the result visible after the banner fades. */
.spine { display: block; width: 3px; align-self: stretch; background: rgba(255, 255, 255, 0.55); }

.timing-body { display: flex; flex-direction: column; gap: 5px; padding: 7px 8px; align-items: flex-start; }

.lap {
  font: 600 32px/1 var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.lap.invalid { color: var(--danger); text-decoration: line-through; text-decoration-color: var(--danger); }

.delta {
  font: 700 15px/1.2 var(--mono);
  font-variant-numeric: tabular-nums;
  padding: 2px 7px;
  border-radius: 4px;
}

.sectors { display: flex; gap: 3px; border-radius: 3px; overflow: hidden; }
.sector { width: 48px; background: rgba(255, 255, 255, 0.05); padding-bottom: 3px; }
.sector.current { background: rgba(255, 255, 255, 0.12); }
.sector i { display: block; height: 3px; margin-bottom: 3px; }
.sector span {
  display: block;
  text-align: center;
  font: 400 11px/1.2 var(--mono);
  font-variant-numeric: tabular-nums;
}
.sector span.done { font-weight: 600; }

.best-line { display: flex; align-items: center; gap: 5px; }
.best { font: 500 12px/1.2 var(--mono); font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, 0.8); }

.label {
  font: 700 10px/1.2 ui-sans-serif, sans-serif;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

/* ---- telemetry (top right) --------------------------------------------- */

/* Every row is measured against one content width, so the rev strip, the ERS
   bar and the gear box share one left edge and one right edge — four readouts
   each ending somewhere different reads as unrelated widgets, not an
   instrument. */
#telemetry { width: 128px; padding: 7px 8px; display: flex; flex-direction: column; gap: 5px; }

.tele-head { display: flex; align-items: flex-end; justify-content: space-between; }

/* Deliberately smaller than the speed beside it: gear is glanced at, speed is
   read. */
.gear {
  width: 24px; height: 26px;
  display: grid; place-items: center;
  font: 700 18px/1 var(--mono);
  font-variant-numeric: tabular-nums;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 4px;
}

.rpm-line { display: flex; align-items: baseline; gap: 3px; }
.rpm-line b { font: 600 10px/1.2 var(--mono); font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, 0.7); }
.rpm-line .label { font-size: 9px; color: rgba(255, 255, 255, 0.45); }

/* The rev lights off a steering wheel: green through amber to red as the revs
   climb, then the whole strip flashing blue at the shift point. */
.rev-strip { display: flex; gap: 2px; }
.rev-strip i { flex: 1; height: 10px; border-radius: 1px; background: rgba(255, 255, 255, 0.14); }

.speed-row { display: flex; align-items: baseline; justify-content: flex-end; gap: 3px; }
.speed-row b { font: 700 34px/1 var(--mono); font-variant-numeric: tabular-nums; }
.speed-row .label { color: rgba(255, 255, 255, 0.6); }

.ers { display: flex; flex-direction: column; gap: 3px; }
.ers-head { display: flex; align-items: baseline; justify-content: flex-end; gap: 4px; }
.ers-label { font: 700 9px/1.2 var(--mono); letter-spacing: 0.06em; color: rgba(255, 255, 255, 0.6); }
.ers-pct { font: 600 9px/1.2 var(--mono); font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, 0.6); }
.ers-bar {
  height: 5px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
  transition: height 0.12s ease-out;
}
.ers-bar.deploying { height: 7px; box-shadow: 0 0 5px var(--teal); }
.ers-bar i { display: block; height: 100%; background: var(--teal); border-radius: 2px; }

#minimap { display: block; }

/* ---- coaching cues (centred, under the corner blocks) ------------------ */

#cues { margin-top: 12px; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.cue-row { display: flex; gap: 8px; align-items: center; min-height: 28px; }
.cue-slot { display: flex; }

/* One chassis for every cue, so BRAKE, DEPLOY and the wildlife call read as the
   same instrument speaking. */
.cue {
  display: inline-flex;
  align-items: stretch;
  border-radius: 999px;
  overflow: hidden;
  border: 1px solid;
}
/* BRAKE is an order and arrives like one; the cues that only inform fade in.
   Both are one-shot on appearance — the figure inside a cue is rewritten in
   place (see #setCue), so a ticking countdown never replays either. */
.cue.solid { animation: cue-pop 0.15s ease-out; }
.cue.outline { animation: cue-fade 0.15s ease-out; }
.cue-word {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px;
  font: 800 15px/1.2 ui-sans-serif, sans-serif;
  letter-spacing: 0.3px;
}
.cue-word svg { width: 11px; height: 11px; fill: currentColor; }
.cue-value {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  padding: 5px 9px;
  border-left: 1px solid;
}
.cue-value b { font: 800 15px/1.2 var(--mono); font-variant-numeric: tabular-nums; }
.cue-value em { font-style: normal; font-size: 8px; font-weight: 700; letter-spacing: 0.1em; opacity: 0.75; }

/* Solid is an order; outlined is information. The tinted cues sit on carbon
   rather than straight on the world — a bare tint is a window, and what showed
   through it was a kerb. */
.cue.solid { background: var(--tint); color: #fff; border-color: rgba(255, 255, 255, 0.25); box-shadow: 0 0 8px color-mix(in srgb, var(--tint) 50%, transparent); }
.cue.solid .cue-value { background: rgba(0, 0, 0, 0.22); border-left-color: rgba(255, 255, 255, 0.3); }
.cue.outline { background: color-mix(in srgb, var(--tint) 20%, rgba(11, 14, 18, 0.85)); color: var(--tint); border-color: color-mix(in srgb, var(--tint) 55%, transparent); }
.cue.outline .cue-value { background: rgba(0, 0, 0, 0.13); border-left-color: rgba(255, 255, 255, 0.18); }

@keyframes cue-pop { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: none; } }
@keyframes cue-fade { from { opacity: 0; } to { opacity: 1; } }

/* ---- the starting gantry ----------------------------------------------- */

/* Hung above the car rather than over it: a recovery counts down mid-lap with
   the track still worth seeing. */
#lights {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, calc(-50% - 76px)) scale(1.12);
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  /* visibility flips only once the fade is over, so the beam is gone from the
     paint entirely between starts instead of being transparent but still drawn. */
  visibility: hidden;
  transition: opacity 0.22s ease-out, transform 0.22s ease-out, visibility 0s linear 0.22s;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.5));
}
#lights.show {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, calc(-50% - 76px)) scale(1);
  transition: opacity 0.22s ease-out, transform 0.22s ease-out, visibility 0s;
}

.beam { width: 288px; height: 9px; border-radius: 999px; background: linear-gradient(#9e9e9e, #424242); }
.light-row { display: flex; gap: 10px; }
.light-col { width: 44px; display: flex; justify-content: center; }
.posts i { display: block; width: 5px; height: 9px; background: #575757; }

.housing {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 7px 0;
  width: 44px;
  align-items: center;
  background: var(--carbon);
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 9px;
}

.bulb {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.55);
  background: radial-gradient(circle at 38% 32%, rgba(255, 255, 255, 0.05), rgba(0, 0, 0, 0.55));
  transition: background 0.09s ease-out, box-shadow 0.09s ease-out;
}
/* The glow is what carries at a glance. */
.bulb.on {
  background: radial-gradient(circle at 38% 32%, #ff9e8f, #f24540);
  box-shadow: 0 0 11px rgba(242, 69, 64, 0.9);
}

/* ---- lap banner --------------------------------------------------------- */

#banner {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font: 700 clamp(28px, 6vw, 52px)/1 var(--mono);
  font-variant-numeric: tabular-nums;
  opacity: 0;
  visibility: hidden;
  text-shadow: 0 4px 24px rgba(0, 0, 0, 0.6);
}
#banner.show { visibility: visible; animation: banner 2.6s ease forwards; }
@keyframes banner {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.94); }
  12% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  72% { opacity: 1; }
  100% { opacity: 0; }
}

/* ---- the key legend ----------------------------------------------------- */

#keys { position: fixed; right: 12px; bottom: 12px; padding: 8px 10px; }
.keys-title {
  font: 700 9px/1.2 ui-sans-serif, sans-serif;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.4);
  margin-bottom: 6px;
}
#keys dl { display: grid; grid-template-columns: auto auto; gap: 4px 10px; align-items: center; }
#keys dt { display: flex; gap: 2px; }
#keys dd { font-size: 11px; color: rgba(255, 255, 255, 0.72); }
#keys dd b { font-weight: 600; color: var(--teal); }
/* The one thing on the overlay that takes a click, so it opts back in. */
#sound-toggle {
  pointer-events: auto;
  cursor: pointer;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
}
#sound-toggle:hover b { text-decoration: underline; }
#sound-toggle.blocked b { color: var(--danger); }
/* Hidden together, or the label sits beside nothing. */
#sound-row[hidden], #sound-row[hidden] + dd { display: none; }

kbd {
  display: inline-block;
  min-width: 20px;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-bottom-width: 2px;
  font: 600 10px/1.4 var(--mono);
  text-align: center;
  color: var(--ink);
}

/* The way back to the site. Top left, under the timing panel's eye line, and
   the second thing on the overlay that takes a click. */
#home {
  position: fixed;
  left: 12px;
  bottom: 12px;
  pointer-events: auto;
  padding: 6px 10px;
  border-radius: 7px;
  background: rgba(11, 14, 18, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.72);
  font-size: 12px;
  text-decoration: none;
}
#home:hover { color: var(--ink); border-color: rgba(255, 255, 255, 0.22); }

/* ---- touch: the app's controls, on a page with no keyboard --------------- */

/* Nothing here applies until main.js decides this is a device driven with
   thumbs (touch.js#touchLikely), so a desktop page is byte for byte what it
   was. The layout is DrivingControls.swift's: the cell and the brake paddle in
   one bottom corner, the mirrored rim grips in the other, the middle clear. */

#drive {
  --pad: 78px;
  --cell-w: 66px;
  --cell-h: 98px;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  padding: 0 22px calc(12px + env(safe-area-inset-bottom));
  pointer-events: none;
  /* The thumbs are on the glass the car is drawn on: no scroll, no zoom, no
     double-tap, and no text selection when a press is held. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
#drive[hidden] { display: none; }

/* A phone held sideways has no room for 98 px of cell above a 78 px paddle, and
   the camera would have to give up most of the frame to clear them. */
@media (max-height: 560px) {
  #drive { --pad: 62px; --cell-w: 54px; --cell-h: 78px; }
}

.group.systems { display: flex; flex-direction: column; gap: 12px; }
.group.steering { display: flex; gap: 10px; }

.pad {
  position: relative;
  width: var(--pad);
  height: var(--pad);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  touch-action: none;
  cursor: pointer;
  /* The silhouette is painted, so the shadow has to follow the alpha rather
     than a box — the cell is not a rectangle. */
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
  transition: transform 0.08s ease-out, filter 0.08s ease-out;
}
.pad.cell { width: var(--cell-w); height: var(--cell-h); }
.pad.down { transform: scale(0.965); }
.pad.down { filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3)) drop-shadow(0 0 9px var(--accent)); }

.pad .face { position: absolute; inset: 0; width: 100%; height: 100%; }
.pad .body {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.pad .glyph { width: 19px; height: 19px; }

/* Accent per control: the cell's is set from JS because it tracks the charge. */
.pad.brake { --accent: var(--danger); }
.pad.steer { --accent: #fff; }
.pad.cell { --accent: var(--teal); }

/* LEDs mark a control carrying system state; knurl marks one you only hold. */
.leds {
  position: absolute;
  top: 9px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: 3px;
}
.leds i {
  width: 7px;
  height: 3px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 45%, transparent);
}
.pad.down .leds i { background: var(--accent); box-shadow: 0 0 4px var(--accent); }

.knurl { display: flex; flex-direction: column; gap: 3px; margin-top: 7px; }
.knurl i { width: 20px; height: 2px; border-radius: 999px; background: rgba(255, 255, 255, 0.2); }
.pad.down .knurl i { background: rgba(255, 255, 255, 0.42); }

/* Etched legend: a hard dark edge under the glyph, as laser etching catches
   light (EngravedLabel). */
.engraved {
  font: 700 9px/1.2 var(--mono);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.65);
}

.pad.brake .body { gap: 1px; padding-top: 11px; color: var(--danger); }
.pad.brake.down .body { color: #fff; }
.pad.steer .glyph { width: 30px; height: 30px; color: #fff; filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.5)); }

/* ---- the cell ----------------------------------------------------------- */

/* Legend on the carbon face up top, gauge under it, and both inside the body
   clear of the terminal nub (BatteryCell). */
.pad.cell .body {
  position: absolute;
  inset: 14px 11px 11px;
  gap: 5px;
  justify-content: flex-start;
}
.cell-head { display: flex; flex-direction: column; align-items: center; }
.cell-head .glyph { width: 15px; height: 15px; color: var(--accent); filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.7)); }
.cell-head .engraved { color: rgba(255, 255, 255, 0.85); }
.pad.cell.down .cell-head .glyph, .pad.cell.deploying .cell-head .glyph { color: #fff; }

.cell-gauge { flex: 1; width: 100%; display: flex; flex-direction: column; gap: 3px; }
.seg {
  position: relative;
  flex: 1;
  display: flex;
  align-items: flex-end;
  border-radius: 2.5px;
  background: rgba(0, 0, 0, 0.5);
  overflow: hidden;
}
.seg .fill {
  display: block;
  width: 100%;
  height: 0;
  border-radius: 2.5px;
  background: color-mix(in srgb, var(--accent) 90%, transparent);
  box-shadow: 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
  transition: height 0.18s ease-out;
}
.pad.cell.deploying .seg .fill {
  background: var(--accent);
  box-shadow: 0 0 3px color-mix(in srgb, var(--accent) 70%, transparent);
}
/* While harvesting, the next segment up pulses, the way a charging cell does. */
.seg.charging::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  animation: cell-charge 1.5s ease-in-out infinite;
}
@keyframes cell-charge { 0%, 100% { opacity: 0.12; } 50% { opacity: 0.45; } }

/* ---- the header row ----------------------------------------------------- */

/* Out of the box tree entirely with a keyboard, so #home keeps the fixed
   bottom-left corner it has always had and the chips are not there at all. */
#tools { display: contents; }
#tools .chip { display: none; }

body.touch #tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
body.touch #home {
  position: static;
  display: flex;
  align-items: center;
  height: 44px;
  margin-right: auto;
  border-radius: 12px;
}
/* Restart is the most-used input in the game and it is struck twice in a row;
   without this the second tap zooms the page instead. */
body.touch #home, body.touch #tools .chip { touch-action: manipulation; }
/* 44 px, which is the documented minimum: these are struck one-handed while
   driving, without looking away from the track (GameScreen.swift). */
body.touch #tools .chip {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  pointer-events: auto;
  cursor: pointer;
  color: #fff;
  border: 2.5px solid rgba(0, 0, 0, 0.5);
  border-radius: 12px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
  /* Carbon, twill, and the specular top edge — the same face the pads and the
     app's own header chips wear. */
  background:
    linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 50%),
    repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.05) 0 0.7px, transparent 0.7px 4px),
    repeating-linear-gradient(-45deg, rgba(255, 255, 255, 0.03) 0 0.7px, transparent 0.7px 4px),
    rgba(11, 14, 18, 0.58);
}
body.touch #tools .chip:active { background-color: rgba(11, 14, 18, 0.92); }
body.touch #tools .chip[hidden] { display: none; }
#tools .chip svg { width: 21px; height: 21px; }
/* Muted drops the waves for the slash; blocked is the one state that is not a
   preference, so it says so in the colour the HUD keeps for trouble. */
#mute .slash { display: none; }
#mute.muted .waves { display: none; }
#mute.muted .slash { display: block; }
#mute.blocked { color: var(--danger); }

/* The keyboard build's furniture, with no keyboard to serve. */
body.touch #keys { display: none; }

/* ---- boot + failure ----------------------------------------------------- */

#splash {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  background: var(--bg-deep);
  transition: opacity 0.4s ease;
}
body.ready #splash { opacity: 0; pointer-events: none; }

.spinner {
  width: 28px; height: 28px;
  border: 2px solid rgba(255, 255, 255, 0.14);
  border-top-color: var(--teal);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
body.failed .spinner { display: none; }

#error { max-width: 34ch; text-align: center; font: 400 13px/1.5 var(--mono); color: var(--danger); }

@media (max-width: 620px) {
  .lap { font-size: 26px; }
  #keys { display: none; }
}
