:root {
  --bg: #0b0e14;
  --panel: rgba(16, 20, 30, 0.86);
  --accent: #ffcc33;
  --text: #eef2f8;

  /* The bottom-right corner is a stack, and everything in it is measured from
     the minimap: the shelf of round buttons sits 10px above it, the toasts
     40px above those, and the implant column above them again. The map's size
     is a clamp, not a constant — 86px on a phone, 216px on a desktop — so an
     element that hard-codes an offset instead of measuring from here is an
     element that lands on the QR button the moment the viewport grows. Named
     once so that cannot happen twice.
     (Scales with the shorter viewport edge: vh alone left portrait full-size.) */
  --minimap-size: clamp(86px, 26.4vmin, 216px);
  --hud-bottom: max(14px, env(safe-area-inset-bottom));
  --hud-right: max(14px, env(safe-area-inset-right));
  /* Row 1: the round-button shelf directly above the map. */
  --hud-shelf: calc(var(--hud-bottom) + var(--minimap-size) + 10px);
  /* Row 2: the transient pills that pop above the shelf. */
  --hud-toasts: calc(var(--hud-bottom) + var(--minimap-size) + 52px);
  /* Row 3: clear of the lot, for anything that stays on screen. */
  --hud-stack-top: calc(var(--hud-bottom) + var(--minimap-size) + 92px);
}

/* No text/element selection anywhere: long-press-and-drag is a game
   gesture (hold to walk), so selection callouts, loupes and tap
   highlights must never trigger. The chat input re-enables it below. */
* {
  box-sizing: border-box;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

input, textarea {
  -webkit-user-select: text;
  user-select: text;
}

img {
  -webkit-user-drag: none;
  pointer-events: none;
}

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

#game {
  position: fixed;
  inset: 0;
  /* A <canvas> is a replaced element, so `inset: 0` alone won't stretch it —
     it needs an explicit size. On an installed iOS PWA `height: 100%` (and
     `100vh`) resolve to the shorter layout viewport, leaving a black strip
     over the home indicator; `dvh`/`dvw` cover the physical screen, safe
     areas included. The vh/vw line is the fallback for iOS < 15.4. */
  width: 100vw;
  height: 100vh;
  width: 100dvw;
  height: 100dvh;
  display: block;
  touch-action: none;
  background: #151724;
}

/* Character select overlay */
/* Splash: logo floating over the live city backdrop. */
#splash {
  position: fixed;
  inset: 0;
  z-index: 12;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  cursor: pointer;
  background: radial-gradient(120% 120% at 50% 30%, rgba(11,14,20,0.0) 40%, rgba(11,14,20,0.55));
}
#splash-logo {
  width: min(560px, 78vw);
  max-height: 55vh;
  object-fit: contain;
  filter: drop-shadow(0 10px 40px rgba(0,0,0,0.65));
}
#splash-hint {
  color: #fff;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-shadow: 0 2px 10px rgba(0,0,0,0.8);
  animation: splashPulse 1.6s ease-in-out infinite;
}
@keyframes splashPulse {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 1; }
}

/* The shell: full-bleed backdrop that centres the panel and clips nothing
   itself. All the content lives in #menu-scroll, because a `justify-content:
   center` column with more content than viewport overflows off the top AND
   the bottom, and neither end can be reached — on a phone that hid the whole
   lower half of the menu, links included. #editor-box does the same thing. */
#menu {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: radial-gradient(120% 120% at 50% 20%, rgba(11,14,20,0.55), rgba(11,14,20,0.9));
  text-align: center;
  z-index: 10;
}
#menu-scroll {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
  /* Never taller than the viewport, so there is always somewhere to scroll
     to; dvh tracks the mobile browser chrome (vh fallback for older
     engines), and the safe-area padding keeps the last link clear of the
     home indicator rather than under it. */
  max-height: 100vh;
  max-height: 100dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* The menu is its own scroll region: reaching the end must not hand the
     scroll to the page behind it and bounce the whole game. */
  overscroll-behavior: contain;
  padding:
    calc(env(safe-area-inset-top) + 16px) 20px
    calc(env(safe-area-inset-bottom) + 16px);
}

#menu-logo {
  width: min(300px, 56vw);
  object-fit: contain;
  filter: drop-shadow(0 6px 22px rgba(0,0,0,0.55));
}

#menu h1 {
  margin: 0;
  font-size: clamp(38px, 9vw, 84px);
  letter-spacing: 3px;
  font-weight: 800;
  color: var(--accent);
  text-shadow: 0 4px 24px rgba(0,0,0,0.6);
}

.tagline {
  margin: 0 0 14px;
  opacity: 0.85;
  font-size: clamp(14px, 3.5vw, 18px);
}

.cards {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  justify-content: center;
}

/* City picker: smaller and quieter than the character cards, because it is
   chosen once and the characters are the thing the menu is really for. The
   city in play is outlined the way a selected card is. */
.cities {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  margin: 0 0 18px;
}
.city {
  min-width: 150px;
  padding: 9px 16px;
  background: var(--panel);
  border: 2px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  color: var(--text);
  font: inherit;
  text-align: center;
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease;
  backdrop-filter: blur(6px);
}
.city:hover, .city:focus {
  transform: translateY(-2px);
  border-color: var(--accent);
  outline: none;
}
.city.on {
  border-color: var(--accent);
  cursor: default;
}
.city.on:hover { transform: none; }
.city-name { font-weight: 700; font-size: 15px; }
.city-blurb { opacity: 0.6; font-size: 12px; margin-top: 1px; }
/* How busy each city is. Reserved height even when empty (and while offline,
   when it says nothing at all) so the row of buttons doesn't jump as players
   come and go. Quiet until there is somebody there, which is the only state
   worth walking across the menu for. */
.city-count {
  min-height: 15px;
  margin-top: 3px;
  font-size: 11px;
  font-weight: 700;
  opacity: 0.45;
}
.city-count.has-players { color: #7ee787; opacity: 1; }

.card {
  width: 150px;
  padding: 16px 14px;
  background: var(--panel);
  border: 2px solid rgba(255,255,255,0.08);
  border-radius: 16px;
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
  backdrop-filter: blur(6px);
}
.card:hover, .card:focus {
  transform: translateY(-4px);
  border-color: var(--accent);
  box-shadow: 0 10px 30px rgba(0,0,0,0.45);
  outline: none;
}

.avatar {
  width: 84px;
  height: 84px;
  margin: 0 auto 10px;
  border-radius: 12px;
}
/* Blocky avatar previews echoing the in-game character colors. */
.avatar-0 {
  background:
    linear-gradient(#ec3459 0 55%, #262838 55% 100%);
  box-shadow: inset 0 0 0 6px rgba(0,0,0,0.15);
}
.avatar-1 {
  background:
    linear-gradient(#268c80 0 55%, #33333d 55% 100%);
  box-shadow: inset 0 0 0 6px rgba(0,0,0,0.15);
}

.char-name { font-weight: 700; font-size: 18px; }
.char-role { opacity: 0.6; font-size: 13px; margin-top: 2px; }

/* Multiplayer lobby roster */
#lobby {
  margin-top: 14px;
  min-width: 240px;
  max-width: 340px;
  padding: 10px 16px 12px;
  background: var(--panel);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  backdrop-filter: blur(6px);
}
#lobby-status {
  font-size: 13px;
  font-weight: 700;
  opacity: 0.85;
}
#lobby-status.online { color: #7ee787; opacity: 1; }
#lobby-list {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  font-size: 13px;
  text-align: left;
  max-height: 120px;
  overflow-y: auto;
}
#lobby-list li {
  padding: 2px 0;
  opacity: 0.9;
}
#lobby-list li .who { font-weight: 700; }
#lobby-list li .what { opacity: 0.65; }

.hint {
  margin-top: 18px;
  max-width: 520px;
  font-size: clamp(11px, 3vw, 14px);
  line-height: 1.6;
  opacity: 0.8;
}
.hint b { color: var(--accent); }

.fatal {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  text-align: center;
  font-size: 18px;
  color: #ff8080;
}

/* In-game HUD. The status and the prompt ride side by side while the pair
   still fits in the left half of the screen; the moment they don't, the
   prompt drops onto its own line under the status rather than running on
   toward the clock. The cap is what forces the break — the row is a flex
   line no wider than half the viewport, and neither pill ever breaks its own
   text. (Shown as `flex` by the UI; `none` here until you're in the city.) */
#hud {
  position: fixed;
  left: max(14px, env(safe-area-inset-left));
  top: max(14px, env(safe-area-inset-top));
  z-index: 5;
  display: none;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px;
  max-width: 50vw;
  pointer-events: none;
}
#hud-status {
  padding: 8px 14px;
  background: var(--panel);
  border-radius: 10px;
  font-weight: 700;
  font-size: clamp(13px, 3.5vw, 16px);
  white-space: nowrap;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
}
#hud-prompt {
  display: none;
  padding: 6px 12px;
  background: var(--accent);
  color: #201a00;
  border-radius: 8px;
  font-weight: 700;
  font-size: clamp(12px, 3.2vw, 15px);
  white-space: nowrap;
}

/* Clock / weather / online count, top-right */
#hud-clock {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  top: max(14px, env(safe-area-inset-top));
  z-index: 5;
  display: none;
  padding: 8px 14px;
  background: var(--panel);
  border-radius: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 600;
  font-size: clamp(12px, 3.2vw, 15px);
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  pointer-events: none;
}
/* Link-health tints for the clock pill (see net.dart slow/lostConnection).
   Amber = slow heartbeat, red = dropped/reconnecting. */
#hud-clock.net-warn {
  color: #ffd23f;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4), inset 3px 0 0 #ffb020;
}
#hud-clock.net-alert {
  color: #ff8a7a;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4), inset 3px 0 0 #ff4d4d;
}

/* Cash balance, top-right under the clock */
#hud-cash {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  top: calc(max(14px, env(safe-area-inset-top)) + 44px);
  z-index: 5;
  display: none;
  padding: 6px 14px;
  background: var(--panel);
  border-radius: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 700;
  font-size: clamp(12px, 3.2vw, 15px);
  color: #7ddf7d;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  pointer-events: none;
}
/* ?perf readout. Top-right, in the row below the wanted stars: the bottom-left
   corner belongs to the chat / camera / siren / radio button row and the
   bottom-right to the minimap, and an instrument that covers a control is
   worse than no instrument. Deliberately not styled like the rest of the HUD —
   it is not part of the game, and it has to stay legible on a phone held at
   arm's length while driving. */
#hud-perf {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  top: calc(max(14px, env(safe-area-inset-top)) + 116px);
  z-index: 7;
  text-align: right;
  display: none;
  padding: 6px 10px;
  background: rgba(0,0,0,0.86);
  border-radius: 8px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 600;
  font-size: clamp(10px, 2.6vw, 13px);
  line-height: 1.45;
  color: #8ef0c0;
  white-space: pre;
  pointer-events: none;
}

/* Wanted stars, under the cash. Blue while the heat is on; red/green flash on
   a bust / clean getaway. */
#hud-wanted {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  top: calc(max(14px, env(safe-area-inset-top)) + 80px);
  z-index: 6;
  display: none;
  padding: 6px 14px;
  background: var(--panel);
  border-radius: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 700;
  font-size: clamp(12px, 3.2vw, 15px);
  letter-spacing: 0.08em;
  white-space: nowrap;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  pointer-events: none;
}
/* Busted cut-scene: a full-screen black fade with the verdict. Opacity is set
   each frame from _game.bustedCutMs, so no CSS animation to keep in sync. */
#busted-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #000;
  opacity: 0;
  pointer-events: none;
}
#busted-overlay .busted-title {
  color: #ff5d4d;
  font-family: system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(38px, 10vw, 92px);
  letter-spacing: 0.12em;
}
#busted-overlay .busted-sub {
  margin-top: 14px;
  color: rgba(255, 255, 255, 0.72);
  font-family: system-ui, sans-serif;
  font-weight: 600;
  font-size: clamp(13px, 3vw, 20px);
  letter-spacing: 0.05em;
}

#hud-wanted.on { color: #ffd23f; }
#hud-wanted.busted {
  color: #ff5d4d;
  box-shadow: 0 4px 22px rgba(255,80,60,0.5);
}
#hud-wanted.evaded { color: #4fdc7a; }

/* The handbrake, for touch players — held, not toggled. It sits just left of
   the minimap, where the right thumb already is, and lights up while it's
   pulled. Bigger than the other buttons: it's a control you grab in a hurry. */
#handbrake {
  position: fixed;
  right: calc(var(--hud-right) + var(--minimap-size) + 12px);
  bottom: max(14px, env(safe-area-inset-bottom));
  z-index: 30;
  display: none; /* shown when a touch player is behind a wheel — as flex */
  align-items: center;
  justify-content: center;
  width: 58px;
  height: 58px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.18);
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  cursor: pointer;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
  transition: transform 0.09s ease-out, background 0.09s ease-out;
}
/* The lever is drawn, not typed: let every press land on the button itself. */
#handbrake img {
  display: block;
  pointer-events: none;
  transition: transform 0.09s ease-out;
}
/* Grab it and the hand drives the lever down — straight along it, the way the
   thing is actually worked. Down-and-left now the drawing is mirrored. */
#handbrake.on img { transform: translate(-2px, 1.6px); }
/* Pulled: it lights up and swells under the thumb, rather than shrinking away
   from it — a control being hauled on should grow, not retreat. */
#handbrake.on {
  background: #b3402e;
  border-color: #ff8a7a;
  transform: scale(1.09);
}

/* Drift chronometer, top-center (hidden unless the car is sideways) */
#hud-drift {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  top: max(14px, env(safe-area-inset-top));
  z-index: 5;
  display: none;
  padding: 7px 16px;
  background: var(--panel);
  border-radius: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 700;
  font-size: clamp(13px, 3.4vw, 17px);
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--accent);
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  pointer-events: none;
}
/* A new record: gold, and a beat of celebration as it lands. */
#hud-drift.best {
  color: #ffd23f;
  box-shadow: 0 4px 22px rgba(255,178,32,0.45);
  animation: drift-pop 0.45s ease-out;
}

/* The race clock owns the top-centre slot while a race is on. */
#hud-race {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  top: max(14px, env(safe-area-inset-top));
  z-index: 6;
  display: none;
  padding: 7px 16px;
  background: var(--panel);
  border-radius: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 700;
  font-size: clamp(13px, 3.4vw, 17px);
  letter-spacing: 0.04em;
  white-space: nowrap;
  text-align: center;
  color: var(--accent);
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  pointer-events: none;
}
#hud-race .race-splits {
  margin-top: 3px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0;
  color: rgba(255,255,255,0.72);
}
#hud-race.done { color: #ffd23f; box-shadow: 0 4px 22px rgba(255,178,32,0.45); }

/* Courier readout: a row below the drift/race slot, and tappable (unlike the
   drift clock) so touch players can take or clock off a run without a keyboard. */
#hud-courier {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  top: max(56px, calc(env(safe-area-inset-top) + 44px));
  z-index: 6;
  display: none;
  padding: 7px 16px;
  background: var(--panel);
  border-radius: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 700;
  font-size: clamp(13px, 3.4vw, 17px);
  letter-spacing: 0.04em;
  white-space: nowrap;
  text-align: center;
  color: var(--accent);
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  cursor: pointer;
  pointer-events: auto;
}
/* Idle "take a run" chip: quieter than an active run. */
#hud-courier.idle { opacity: 0.72; font-weight: 600; }
/* Clock running low, or a late drop: red. */
#hud-courier.urgent { color: #ff5d4d; box-shadow: 0 4px 22px rgba(255,80,60,0.45); }
/* A delivery just banked: green. */
#hud-courier.win { color: #4fdc7a; box-shadow: 0 4px 22px rgba(60,210,110,0.4); }
#hud-courier .sub {
  margin-top: 3px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0;
  color: rgba(255,255,255,0.72);
}

/* The 3-2-1 overlay. */
#race-count {
  position: fixed;
  left: 50%;
  top: 42%;
  transform: translate(-50%, -50%);
  z-index: 24;
  display: none;
  font-weight: 900;
  font-size: clamp(64px, 18vw, 160px);
  color: var(--accent);
  text-shadow: 0 6px 30px rgba(0,0,0,0.6);
  pointer-events: none;
}
/* Its own keyframes, not drift-pop: that one only carries translateX,
   because #hud-drift hangs off the top edge and never needed a Y shift.
   Borrowing it here would drop the -50% Y for the length of the animation
   and snap the digit back up when it ended. Any transform an animation
   touches must restate the base transform in full. */
#race-count.pop { animation: race-count-pop 0.4s ease-out; }
@keyframes race-count-pop {
  from { transform: translate(-50%, -50%) scale(1.5); opacity: 0.4; }
  to { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}

/* World-space checkpoint marker, projected each frame. */
#race-marks {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  overflow: hidden;
}
/* Sits over the 3D world, so it lets the scene through rather than
   punching a solid hole in it. */
.race-mark {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 4px 9px;
  border-radius: 999px;
  background: rgba(255,204,51,0.55);
  border: 1px solid rgba(255,204,51,0.75);
  backdrop-filter: blur(2px);
  color: #201a00;
  font-weight: 800;
  font-size: 13px;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(255,236,180,0.55);
  box-shadow: 0 3px 14px rgba(0,0,0,0.35);
}
.race-mark .dist { font-weight: 600; opacity: 0.8; margin-left: 4px; }

/* Race lobby: invite picker, then the roster. */
#race-lobby {
  position: fixed;
  inset: 0;
  z-index: 31;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 7, 12, 0.7);
}
#race-lobby-box {
  width: min(400px, 92vw);
  max-height: 76vh;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  border-radius: 14px;
  background: #171a24;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  color: #fff;
}
#race-lobby-title { font-weight: 800; font-size: 16px; }
#race-lobby-sub { font-size: 11px; opacity: 0.6; }
#race-lobby-list {
  flex: 1 1 auto;
  min-height: 60px;
  overflow-y: auto;
  margin: 4px 0;
}
.race-lobby-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 6px;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}
.race-lobby-row:hover { background: rgba(255,255,255,0.06); }
.race-lobby-row .who {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.race-lobby-row .st { font-size: 11px; opacity: 0.55; }
.race-lobby-row .st.ready { color: #7ee787; opacity: 1; }
#race-lobby-actions { display: flex; gap: 8px; }
#race-lobby-actions button {
  flex: 1;
  padding: 9px 0;
  border-radius: 10px;
  border: none;
  font-weight: 700;
  cursor: pointer;
  background: rgba(255,255,255,0.12);
  color: var(--text);
}
#race-lobby-go { background: var(--accent); color: #201a00; }
#race-lobby-go:disabled { opacity: 0.45; cursor: default; }

/* An invite, as a bar rather than a modal: it must not stop you driving. */
#race-invite {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  top: calc(max(14px, env(safe-area-inset-top)) + 52px);
  z-index: 23;
  display: none;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 12px;
  background: var(--panel);
  box-shadow: 0 4px 18px rgba(0,0,0,0.5);
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
#race-invite button {
  padding: 6px 12px;
  border-radius: 8px;
  border: none;
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
  background: rgba(255,255,255,0.12);
  color: var(--text);
}
#race-invite-yes { background: var(--accent); color: #201a00; }

/* The podium ceremony. */
#race-podium {
  position: fixed;
  inset: 0;
  z-index: 32;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 7, 12, 0.72);
}
#race-podium-box {
  width: min(460px, 92vw);
  padding: 18px;
  border-radius: 16px;
  background: #171a24;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  color: #fff;
  text-align: center;
}
#race-podium-title {
  font-weight: 800;
  font-size: 18px;
  margin-bottom: 14px;
}
#race-pedestals {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 10px;
  min-height: 150px;
  margin-bottom: 12px;
}
.pedestal { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.pedestal .av {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
  background-color: rgba(255,255,255,0.1);
  border: 2px solid rgba(255,255,255,0.25);
}
.pedestal .who {
  font-size: 12px;
  font-weight: 700;
  max-width: 96px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pedestal .t {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  opacity: 0.75;
}
.pedestal .block {
  width: 84px;
  border-radius: 6px 6px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 900;
  font-size: 20px;
  color: #201a00;
}
.pedestal.p1 .block { height: 78px; background: #ffcc33; }
.pedestal.p2 .block { height: 56px; background: #c8ced8; }
.pedestal.p3 .block { height: 40px; background: #cd8b52; }
#race-results { font-size: 12px; text-align: left; margin-bottom: 12px; }
.race-result-row {
  display: flex;
  gap: 8px;
  padding: 4px 6px;
  border-radius: 6px;
}
.race-result-row.me { background: rgba(255,204,51,0.14); }
.race-result-row .rank { width: 24px; opacity: 0.65; }
.race-result-row .who { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.race-result-row .t {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  opacity: 0.85;
}
#race-podium-close {
  padding: 9px 22px;
  border-radius: 10px;
  border: none;
  background: var(--accent);
  color: #201a00;
  font-weight: 700;
  cursor: pointer;
}
@keyframes drift-pop {
  from { transform: translateX(-50%) scale(1.28); }
  to { transform: translateX(-50%) scale(1); }
}
#hud-drift .best-time {
  color: #cfd4dc;
  font-weight: 600;
}

/* Minimap, bottom-right */
#minimap {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  bottom: max(14px, env(safe-area-inset-bottom));
  z-index: 5;
  display: none;
  cursor: pointer; /* tap/click (or M) cycles the zoom level */
  touch-action: none;
  width: var(--minimap-size);
  height: var(--minimap-size);
  border-radius: 12px;
  border: 2px solid rgba(255,255,255,0.18);
  box-shadow: 0 4px 16px rgba(0,0,0,0.45);
  opacity: 0.92;
}

/* QR share: toggle button perched above the minimap corner, the code
   canvas swapping into the minimap's exact spot. */
#qr-toggle {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  bottom: var(--hud-shelf);
  z-index: 6;
  display: none; /* shown while playing */
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
#qr-toggle svg { display: block; margin: 0 auto; }
#qr-toggle.on { background: #d9a11c; color: #1a1a1a; }
#qr-map {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  bottom: max(14px, env(safe-area-inset-bottom));
  z-index: 5;
  display: none; /* swapped in for the minimap */
  cursor: pointer; /* click copies the link */
  width: var(--minimap-size);
  height: var(--minimap-size);
  border-radius: 12px;
  border: 2px solid rgba(255,255,255,0.18);
  box-shadow: 0 4px 16px rgba(0,0,0,0.45);
  background: #fff;
}
#qr-toast {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  bottom: var(--hud-toasts);
  z-index: 6;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(20, 22, 32, 0.92);
  color: #7ee787;
  font: 600 13px system-ui, sans-serif;
  opacity: 0;
  transition: opacity 0.25s;
  pointer-events: none;
}

/* Character editor */
.edit-btn {
  margin-top: 10px;
  padding: 5px 10px;
  width: 100%;
  background: rgba(255,255,255,0.08);
  color: var(--text);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 8px;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.edit-btn:hover { border-color: var(--accent); color: var(--accent); }

/* Character-slot extras: cash line + the empty "new character" card */
.char-cash {
  margin-top: 2px;
  font-size: 12px;
  font-weight: 700;
  color: #7ddf7d;
}
.card.empty {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 180px;
  font-size: 15px;
  font-weight: 700;
  opacity: 0.7;
  border-style: dashed;
}
.card.empty:hover { opacity: 1; }

/* The editor's name field */
#ed-name {
  flex: 1;
  padding: 6px 10px;
  border-radius: 9px;
  border: 2px solid #4a4f63;
  background: #262a3a;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  min-width: 0;
}
#ed-name:focus { outline: none; border-color: var(--accent); }

#editor {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  background: rgba(5, 7, 12, 0.7);
}
#editor-box {
  width: min(92vw, 380px);
  /* Never taller than the viewport, so the top/bottom borders and the
     pinned actions below always stay on-screen; dvh tracks the mobile
     browser chrome (vh fallback for older engines). */
  max-height: calc(100vh - 24px);
  max-height: calc(100dvh - 24px);
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 16px;
  backdrop-filter: blur(8px);
  text-align: center;
  overflow: hidden; /* let the inner scroller own the rounded corners */
}
#editor-scroll {
  flex: 1 1 auto;
  min-height: 0; /* allow the flex child to shrink and scroll */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 18px 20px 4px;
}
#editor-title { font-weight: 800; font-size: 20px; margin-bottom: 10px; }
#editor-preview {
  width: 200px;
  height: 200px;
  /* Give the rest of the form room on short screens. */
  max-width: 42vh;
  max-height: 42vh;
  margin: 0 auto 12px;
  border-radius: 12px;
  background-size: cover;
  background-position: center;
  background-color: #10131c;
}
.ed-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 0;
}
.ed-label {
  width: 46px;
  text-align: right;
  font-size: 13px;
  opacity: 0.75;
  flex-shrink: 0;
}
.swatches { display: flex; gap: 6px; flex-wrap: wrap; }
.sw {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  border: 2px solid rgba(255,255,255,0.18);
  cursor: pointer;
  padding: 0;
}
.sw.sel { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(255,204,51,0.35); }
/* Text option buttons (height / build). */
.opts { display: flex; gap: 6px; flex-wrap: wrap; }
.opt {
  padding: 4px 12px;
  border-radius: 9px;
  border: 2px solid #4a4f63;
  background: #262a3a;
  color: #cfd3e0;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.opt.sel {
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 0 0 2px rgba(255,204,51,0.35);
}
#editor-actions {
  flex: 0 0 auto; /* pinned below the scroller — always reachable */
  display: flex;
  gap: 10px;
  justify-content: center;
  padding: 12px 20px;
  border-top: 1px solid rgba(255,255,255,0.10);
}
#editor-actions button {
  padding: 8px 18px;
  border-radius: 10px;
  border: none;
  font-weight: 700;
  cursor: pointer;
}
#ed-shuffle { background: rgba(255,255,255,0.12); color: var(--text); }
#ed-done { background: var(--accent); color: #201a00; }

/* Short screens (e.g. phones in landscape): tighten the editor so most of
   it fits without scrolling; anything left over scrolls inside the box. */
@media (max-height: 560px) {
  #editor-title { font-size: 17px; margin-bottom: 6px; }
  #editor-preview { margin-bottom: 8px; }
  .ed-row { margin: 6px 0; }
  #editor-scroll { padding-top: 12px; }
  #editor-actions { padding: 10px 20px; }
}

/* --- Vehicle customization shop ---------------------------------------- */

/* In the bay you are shopping, not driving: the bottom-left cluster (chat,
   camera, siren, taxi, race, courier, radio) has nothing to act on while the
   car sits on the lift, and on a phone it crowds the panel. The radio's own
   head unit goes with it — a panel whose toggle is gone can't be closed.
   !important because the HUD callback writes these displays inline, and it
   keeps firing behind the overlay every time the clock ticks. The handbrake
   is bottom-right, but it's the same idea: no wheel, no lever. */
body.shopping #chat-open, body.shopping #cam-toggle,
body.shopping #siren-toggle, body.shopping #taxi-call,
body.shopping #race-btn, body.shopping #courier-btn,
body.shopping #radio-toggle, body.shopping #radio-panel,
body.shopping #handbrake {
  display: none !important;
}

#shop {
  position: fixed;
  top: 12px;
  right: 12px;
  bottom: 12px;
  width: min(78vw, 300px);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  background: var(--panel);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 16px;
  backdrop-filter: blur(8px);
}
#shop-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
#shop-title { font-weight: 800; font-size: 18px; }
#shop-cash { color: var(--accent); font-weight: 700; }
#shop-tabs { display: flex; gap: 5px; flex-wrap: wrap; }
#shop-tabs .opt { font-size: 12px; padding: 3px 9px; }
#shop-opts {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-content: flex-start;
}
#shop-opts .opt {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  text-align: left;
}
#shop-opts .price { opacity: 0.7; }

/* What the part does behind the wheel, under its name. */
#shop-opts .lbl {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
#shop-opts .fx {
  font-size: 11px;
  line-height: 1.35;
  opacity: 0.72;
  font-weight: 400;
}
/* A figure and its unit stay together on whatever line they land. */
#shop-opts .fx .m { white-space: nowrap; }

/* The stat sheet: how the car in the window drives, with the change the
   parts you haven't bought yet would make to it. */
#shop-stats:empty { display: none; }
#shop-stats .sheet {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 8px 10px;
  border-radius: 10px;
  background: rgba(255,255,255,0.05);
  font-size: 12px;
}
#shop-stats .stat {
  display: flex;
  justify-content: space-between;
  gap: 10px;
}
#shop-stats .stat > span:first-child { opacity: 0.6; }
#shop-stats .val {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 700;
}
/* A change for the better reads green, one for the worse red — in the sheet
   and on the option rows alike. */
#shop .up { color: #7ddf7d; font-style: normal; }
#shop .down { color: #ff8a7a; font-style: normal; }
#shop-opts .swatches { padding: 2px 0; }
#shop-actions { display: flex; gap: 10px; }
#shop-actions button {
  flex: 1;
  padding: 9px 0;
  border-radius: 10px;
  border: none;
  font-weight: 700;
  cursor: pointer;
}
#shop-buy { background: var(--accent); color: #201a00; }
#shop-buy:disabled { opacity: 0.45; cursor: default; }
#shop-done { background: rgba(255,255,255,0.12); color: var(--text); }
#shop-repair {
  padding: 9px 0;
  border-radius: 10px;
  border: none;
  font-weight: 700;
  cursor: pointer;
  background: #b3402e;
  color: #fff;
}
#shop-repair:disabled { opacity: 0.45; cursor: default; }

/* --- Chat + speech bubbles -------------------------------------------- */
#chat-open {
  position: fixed;
  left: 12px;
  bottom: 12px;
  z-index: 30;
  display: none; /* shown in-game */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
#cam-toggle {
  position: fixed;
  left: 64px;
  bottom: 12px;
  z-index: 30;
  display: none; /* shown in-game */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
#cam-toggle.on { background: var(--accent); }
/* Siren toggle: only visible inside a vehicle that has one. */
#siren-toggle {
  position: fixed;
  left: 116px;
  bottom: 12px;
  z-index: 30;
  display: none; /* shown in siren vehicles */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
#siren-toggle.on {
  background: var(--accent);
  animation: sirenPulse 0.6s linear infinite;
}
/* Taxi call: shares the cam button's spot (cam is vehicle-only, taxi is
   on-foot-only). */
#taxi-call {
  position: fixed;
  left: 64px;
  bottom: 12px;
  z-index: 30;
  display: none; /* shown on foot */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
/* Street races: next to the taxi, shown only behind a wheel. Shares 116
   with the siren, which wins there — see the hand-off in hud_ui's onHud. */
#race-btn {
  position: fixed;
  left: 116px;
  bottom: 12px;
  z-index: 30;
  display: none; /* shown while playing */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}

/* Courier button: sits beside the race one — both are jobs worked from a car.
   Tap it to take or clock off a delivery run (the touch equivalent of G). */
#courier-btn {
  position: fixed;
  left: 168px;
  bottom: 12px;
  z-index: 30;
  display: none; /* shown while behind a wheel */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
/* On a run: an amber ring so it reads as active. */
#courier-btn.on {
  background: rgba(255, 138, 61, 0.92);
  box-shadow: 0 2px 14px rgba(255, 138, 61, 0.5);
}

/* --- Car radio ------------------------------------------------------- */
/* Toggle sits after the siren in the bottom bar; the head unit opens above
   it. Only ground vehicles have a radio. */
#radio-toggle {
  position: fixed;
  left: 168px;
  bottom: 12px;
  z-index: 30;
  display: none; /* shown in a car */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
/* Lit whenever the radio is playing — the one indication that survives the
   head unit being collapsed. Two rings ripple outward, half a cycle apart,
   so the waves overlap instead of pulsing on and off. */
#radio-toggle.playing {
  background: var(--accent);
  color: #1a1a1a;
}
#radio-toggle.playing::before,
#radio-toggle.playing::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--accent);
  /* The rings overlap the neighbouring buttons; never let them eat a tap. */
  pointer-events: none;
  animation: radioWave 1.8s ease-out infinite;
}
#radio-toggle.playing::after { animation-delay: 0.9s; }
@keyframes radioWave {
  from { transform: scale(1); opacity: 0.65; }
  to   { transform: scale(1.85); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  #radio-toggle.playing::before,
  #radio-toggle.playing::after { animation: none; opacity: 0; }
}
/* A ring, not a fill, says the head unit is expanded; the two states are
   independent and stack. */
#radio-toggle.open {
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 2px 10px rgba(0,0,0,0.4);
}

#radio-panel {
  position: fixed;
  left: 12px;
  bottom: 64px;
  z-index: 30;
  width: 256px;
  padding: 10px;
  border-radius: 14px;
  background: rgba(20, 22, 32, 0.86);
  box-shadow: 0 4px 16px rgba(0,0,0,0.5);
  color: #fff;
}
/* Passengers ride along with the driver's picks. */
/* A passenger can't work the transport — the car's radio belongs to whoever
   is driving — but the star is theirs: it saves the station to their own
   favourites, and changes nothing about what the car is playing. */
#radio-panel.passenger #radio-controls {
  opacity: 0.4;
  pointer-events: none;
}
#radio-now {
  display: flex;
  align-items: center;
  gap: 9px;
}
#radio-art {
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 7px;
  background: rgba(255,255,255,0.09) center/cover no-repeat;
}
#radio-meta { flex: 1; min-width: 0; }
#radio-title {
  font-size: 13px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#radio-sub {
  font-size: 10px;
  opacity: 0.65;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#radio-sub.error { color: #ff8a72; opacity: 1; }
#radio-fav {
  flex: none;
  border: none;
  background: none;
  color: var(--accent);
  font-size: 17px;
  cursor: pointer;
  padding: 0 2px;
}
#radio-controls {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  margin-top: 9px;
}
#radio-controls button {
  flex: 1;
  height: 32px;
  border: none;
  border-radius: 8px;
  background: rgba(255,255,255,0.09);
  color: #fff;
  font-size: 14px;
  cursor: pointer;
}
#radio-controls button:hover { background: rgba(255,255,255,0.17); }

/* Browse picker */
#radio-picker {
  position: fixed;
  inset: 0;
  z-index: 31;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 7, 12, 0.7);
}
#radio-picker-box {
  width: min(440px, 92vw);
  /* A settled height, sized to the viewport: the box opens showing
     "Loading…" and would otherwise snap from one row tall to full height the
     moment the station list arrives. */
  height: min(560px, 76vh);
  min-height: min(320px, 70vh);
  display: flex;
  flex-direction: column;
  padding: 14px;
  border-radius: 14px;
  background: #171a24;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  color: #fff;
}
#radio-tabs { display: flex; gap: 6px; }
#radio-tabs button {
  flex: 1;
  padding: 7px 0;
  border: none;
  border-radius: 8px;
  background: rgba(255,255,255,0.07);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
#radio-tabs button.on { background: var(--accent); color: #1a1a1a; }
#radio-search-row { display: flex; gap: 6px; margin-top: 10px; }
#radio-search {
  flex: 1;
  min-width: 0;
  padding: 7px 10px;
  border: none;
  border-radius: 8px;
  background: rgba(255,255,255,0.09);
  color: #fff;
  font-size: 13px;
}
#radio-close {
  /* Keeps Close on the right on the Recent / Favourites tabs, where the
     search box is hidden and would otherwise leave it stranded left. */
  margin-left: auto;
  border: none;
  border-radius: 8px;
  padding: 0 12px;
  background: rgba(255,255,255,0.09);
  color: #fff;
  font-size: 12px;
  cursor: pointer;
}
#radio-list {
  /* Takes the slack in the fixed-height box, so tabs and search stay put and
     only the list scrolls. */
  flex: 1 1 auto;
  min-height: 0;
  margin-top: 10px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.radio-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: 9px;
  cursor: pointer;
}
.radio-row:hover { background: rgba(255,255,255,0.07); }
.radio-row.on { background: rgba(255, 204, 51, 0.16); }
.radio-row-art {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 6px;
  background: rgba(255,255,255,0.09) center/cover no-repeat;
}
.radio-row-meta { flex: 1; min-width: 0; }
.radio-row-title {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.radio-row-sub {
  font-size: 10px;
  opacity: 0.6;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#radio-list .empty {
  padding: 24px 8px;
  text-align: center;
  font-size: 12px;
  opacity: 0.6;
}

/* Taxi destination map */
#bigmap {
  position: fixed;
  inset: 0;
  z-index: 22;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 7, 12, 0.7);
  /* The backdrop spans the viewport, so without this Safari takes a pinch
     over the map as a document zoom and a double-tap as a page zoom. */
  touch-action: none;
}
#bigmap-box {
  padding: 14px;
  background: var(--panel);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 16px;
  backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Long-press on the map would otherwise drag-select the whole box. */
  user-select: none;
  -webkit-user-select: none;
}
#bigmap-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-weight: 800;
  font-size: 16px;
}
#bigmap-fare { color: var(--accent); font-weight: 700; }
#bigmap-view { position: relative; display: flex; }
#bigmap-canvas {
  width: min(74vmin, 540px);
  height: min(74vmin, 540px);
  border-radius: 12px;
  border: 2px solid rgba(255,255,255,0.18);
  cursor: grab;
  /* Load-bearing for pinch: without it the browser eats the second finger. */
  touch-action: none;
}
#bigmap-canvas:active { cursor: grabbing; }
#bigmap-zoom {
  position: absolute;
  right: 8px;
  bottom: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
#bigmap-zoom button {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(12,15,22,0.78);
  color: var(--text);
  font-size: 17px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
#bigmap-zoom button:hover { background: rgba(32,38,52,0.9); }
#bigmap-actions { display: flex; gap: 10px; }
#bigmap-actions button {
  flex: 1;
  padding: 9px 0;
  border-radius: 10px;
  border: none;
  font-weight: 700;
  cursor: pointer;
}
#bigmap-go { background: var(--accent); color: #201a00; }
#bigmap-go:disabled { opacity: 0.45; cursor: default; }
#bigmap-stay { background: rgba(255,255,255,0.12); color: var(--text); }

/* --- Race editor / preview: the same box, a different header + actions --- */
#bigmap-head-edit, #bigmap-head-view {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  font-weight: 800;
  font-size: 16px;
}
#race-view-sub { font-size: 11px; font-weight: 600; opacity: 0.65; }
#race-name {
  flex: 1;
  min-width: 0;
  padding: 7px 9px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(0,0,0,0.3);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}
#race-laps-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}
#race-laps { font-size: 12px; min-width: 52px; text-align: center; }
#race-laps-wrap button {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(255,255,255,0.1);
  color: var(--text);
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
#bigmap-actions-edit, #bigmap-actions-view { display: flex; gap: 10px; }
#bigmap-actions-edit button, #bigmap-actions-view button {
  flex: 1;
  padding: 9px 0;
  border-radius: 10px;
  border: none;
  font-weight: 700;
  cursor: pointer;
  background: rgba(255,255,255,0.12);
  color: var(--text);
}
#race-save, #race-host { background: var(--accent); color: #201a00; }
#race-save:disabled { opacity: 0.45; cursor: default; }
#race-undo:disabled { opacity: 0.45; cursor: default; }
#race-hint {
  position: absolute;
  left: 8px;
  bottom: 8px;
  max-width: 70%;
  padding: 5px 9px;
  border-radius: 8px;
  background: rgba(12,15,22,0.82);
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  pointer-events: none;
}

/* --- The race panel: the radio picker's shape, with race rows --- */
#race-panel {
  position: fixed;
  inset: 0;
  /* Above the bigmap (22): the panel opens the map, so if it sat below,
     the map it opened would cover it. */
  z-index: 31;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 7, 12, 0.7);
}
#race-panel-box {
  width: min(440px, 92vw);
  height: min(560px, 76vh);
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border-radius: 14px;
  background: #171a24;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  color: #fff;
}
#race-tabs { display: flex; gap: 6px; }
#race-tabs button {
  flex: 1;
  padding: 7px 0;
  border-radius: 8px;
  border: none;
  background: rgba(255,255,255,0.1);
  color: var(--text);
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
}
#race-tabs button.on { background: var(--accent); color: #1a1a1a; }
#race-panel-row { display: flex; gap: 8px; }
#race-panel-row button {
  padding: 8px 12px;
  border-radius: 8px;
  border: none;
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
  background: rgba(255,255,255,0.12);
  color: var(--text);
}
#race-new { flex: 1; background: var(--accent); color: #201a00; }
#race-close { margin-left: auto; }
#race-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.race-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: 10px;
  cursor: pointer;
}
.race-row:hover { background: rgba(255,255,255,0.07); }
.race-row-meta { flex: 1; min-width: 0; }
.race-row-title {
  font-size: 13px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.race-row-sub {
  font-size: 10px;
  opacity: 0.6;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#race-list .empty { padding: 18px 8px; opacity: 0.5; font-size: 12px; }
.race-star {
  flex: none;
  border: none;
  background: none;
  color: var(--accent);
  font-size: 17px;
  line-height: 1;
  padding: 4px;
  cursor: pointer;
}

/* Full-screen fade for screen transitions */
#fade {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}
@keyframes sirenPulse {
  0%, 100% { box-shadow: 0 0 10px 2px rgba(255, 60, 60, 0.8); }
  50% { box-shadow: 0 0 10px 2px rgba(80, 120, 255, 0.8); }
}
#chat {
  position: fixed;
  left: 12px;
  bottom: 12px;
  z-index: 31;
  width: min(340px, calc(100vw - 24px));
  display: flex;
  flex-direction: column;
  gap: 6px;
}
#chat-suggest {
  display: flex;
  flex-direction: column-reverse;
  gap: 4px;
  max-height: 40vh;
  overflow-y: auto;
}
.chat-phrase {
  text-align: left;
  padding: 7px 12px;
  border: none;
  border-radius: 10px;
  background: rgba(20, 22, 32, 0.82);
  color: #e8eaf2;
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.chat-phrase:hover { background: rgba(60, 64, 84, 0.9); }
#chat-bar {
  display: flex;
  gap: 6px;
}
#chat-input {
  flex: 1;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid #4a4f63;
  background: rgba(16, 18, 26, 0.92);
  color: #fff;
  font: inherit;
  font-size: 15px;
  outline: none;
}
#chat-input:focus { border-color: var(--accent); }
#chat-send {
  width: 44px;
  border-radius: 10px;
  border: none;
  background: var(--accent);
  color: #222;
  font-size: 17px;
  cursor: pointer;
}
#bubbles {
  position: fixed;
  inset: 0;
  z-index: 25;
  pointer-events: none;
  overflow: hidden;
}

/* Crew bar: round face portraits + names of everyone sharing your ride,
   bottom center; the driver's card gets an accent ring and tag. */
#crew {
  position: fixed;
  left: 50%;
  bottom: max(12px, calc(env(safe-area-inset-bottom) + 6px));
  transform: translateX(-50%);
  z-index: 20;
  display: none; /* flex while riding with others */
  gap: 12px;
  pointer-events: none;
}
.crew-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.crew-face {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.4);
  background-size: cover;
  background-position: center;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
}
.crew-card.driver .crew-face { border-color: var(--accent); }
/* Out of health: the card greys out, so a mate on the floor reads as one. */
.crew-card.down .crew-face { filter: grayscale(1) brightness(0.55); }
.crew-card.down .crew-name { opacity: 0.55; text-decoration: line-through; }
/* Health strip, shown only once a mate has taken a hit. */
.crew-hp {
  width: 42px;
  height: 3px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.55);
  overflow: hidden;
}
.crew-hp > div {
  height: 100%;
  background: linear-gradient(90deg, #ff5a4a, #ffd166);
  transition: width 0.2s ease-out;
}
.crew-name {
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.85);
  max-width: 68px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.crew-tag {
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.09em;
  color: var(--accent);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
}

/* "Rotate to landscape" hint: a phone icon that tips over sideways.
   Sits above the crew bar's row so the two never overlap. */
#rotate-hint {
  position: fixed;
  left: 50%;
  bottom: max(104px, calc(env(safe-area-inset-bottom) + 92px));
  transform: translateX(-50%);
  z-index: 30;
  padding: 10px 12px;
  background: var(--panel);
  border-radius: 12px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.45);
  color: var(--accent);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s, visibility 0.4s;
}
#rotate-hint.show {
  opacity: 1;
  visibility: visible;
}
#rotate-hint svg {
  display: block;
  width: 36px;
  height: 36px;
}
#rotate-hint.show svg {
  animation: rotateHint 1.8s ease-in-out infinite;
}
@keyframes rotateHint {
  0%, 18% { transform: rotate(0deg); }
  55%, 82% { transform: rotate(-90deg); }
  100% { transform: rotate(0deg); }
}
.bubble {
  position: absolute;
  transform: translate(-50%, -100%);
  max-width: 220px;
  padding: 6px 11px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.75);
  color: #16181f;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  box-shadow: 0 2px 8px rgba(0,0,0,0.35);
  white-space: pre-wrap;
  word-break: break-word;
}
.bubble::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -6px;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: rgba(255, 255, 255, 0.75);
  border-bottom: none;
}
.bubble .who {
  display: block;
  font-size: 11px;
  font-weight: 700;
  color: #7a5b12;
}

/* --- In-game 3D map editor ------------------------------------------------ */

/* While editing, the gameplay HUD gets out of the way. */
body.editing #hud, body.editing #hud-clock, body.editing #hud-cash,
body.editing #hud-wanted,
body.editing #hud-drift, body.editing #minimap, body.editing #qr-toggle,
body.editing #qr-map, body.editing #chat-open, body.editing #cam-toggle,
body.editing #siren-toggle, body.editing #taxi-call, body.editing #radio-toggle,
body.editing #handbrake, body.editing #crew, body.editing #bubbles,
body.editing #radio-panel, body.editing #race-btn, body.editing #courier-btn,
body.editing #race-panel, body.editing #config-open,
body.editing #hud-race, body.editing #hud-courier,
body.editing #race-marks, body.editing #race-lobby,
body.editing #race-invite, body.editing #race-count {
  display: none !important;
}

#med {
  position: fixed;
  inset: 0;
  pointer-events: none; /* the canvas keeps the pointer; panels re-enable it */
  z-index: 40;
  font: 13px system-ui, sans-serif;
  color: #dbe2ea;
}
#med-top {
  pointer-events: auto;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  background: rgba(12, 16, 20, 0.67);
  border-bottom: 1px solid #232a33;
}
#med-title { font-weight: 700; margin-right: 8px; }
#med-status {
  margin-left: 10px;
  color: #9aa7b4;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#med-side {
  pointer-events: auto;
  position: absolute;
  top: 46px;
  left: 0;
  bottom: 0;
  width: 218px;
  overflow-y: auto;
  padding: 10px;
  background: rgba(12, 16, 20, 0.67);
  border-right: 1px solid #232a33;
}
#med h3 {
  margin: 12px 0 6px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #8b98a5;
}
#med button {
  font: inherit;
  color: #dbe2ea;
  background: #1a2129;
  border: 1px solid #2c353f;
  border-radius: 6px;
  padding: 5px 9px;
  cursor: pointer;
}
#med button:hover { background: #232c36; }
#med button.on { background: #2e4a72; border-color: #4a6da0; }
#med button:disabled { opacity: 0.45; cursor: default; }
#med-tools, #med-brush, #med-size, #med-time, #med-weather {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
#med-tabs {
  display: flex;
  gap: 0;
  margin: -10px -10px 10px;
  border-bottom: 1px solid #232a33;
}
#med-tabs button {
  flex: 1;
  border: none;
  border-radius: 0;
  background: transparent;
  padding: 9px 0;
  color: #8b98a5;
  border-bottom: 2px solid transparent;
}
#med-tabs button:hover { background: rgba(255, 255, 255, 0.04); }
#med-tabs button.on {
  background: transparent;
  color: #dbe2ea;
  border-bottom-color: #4a6da0;
}
#med-size { margin-top: 8px; flex-direction: column; }
#med-height { margin-top: 8px; display: flex; }
.med-size-row { display: flex; gap: 5px; align-items: center; }
.med-size-row .med-label {
  color: #9aa7b4;
  min-width: 44px;
  text-align: center;
}
#med-palette { display: flex; flex-direction: column; gap: 3px; }
#med-palette .pal-group {
  margin-top: 8px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #8b98a5;
}
#med-palette .pal-hint {
  color: #9aa7b4;
  font-size: 12px;
  line-height: 1.45;
  margin: 4px 0;
}
#med-palette button {
  display: flex;
  align-items: center;
  gap: 7px;
  text-align: left;
  width: 100%;
}
#med-palette .pal-chip {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  flex: none;
}
#med-palette .pal-sub { margin-left: auto; color: #8b98a5; font-size: 11px; }
#med-palette .pal-del { margin-left: auto; color: #e8776a; font-size: 11px; }
#med-hint {
  margin-top: 14px;
  color: #6d7883;
  font-size: 11px;
  line-height: 1.5;
}

/* --- ?steerhud: the live steering readout ------------------------------- */

/* Top-left, out of the way of the bottom bar and the minimap, and deliberately
   narrow: it is read on a phone, one-handed, while the other hand is steering.
   pointer-events off — it must never eat a tap meant for the road. */
#steer-hud {
  position: fixed;
  /* Clear of the status pill, which owns the top-left corner. */
  top: calc(max(10px, env(safe-area-inset-top)) + 46px);
  left: max(10px, env(safe-area-inset-left));
  z-index: 35;
  pointer-events: none;
  width: 240px;
  max-height: 82vh;
  overflow: hidden;
  padding: 8px 10px;
  border-radius: 10px;
  /* Barely there: this covers road the player is driving on, so the panel is a
     tint rather than a slab. What makes it readable over a bright sky or a
     white crosswalk is not the backing but the shadow on the glyphs — a tight
     dark halo, carried on every row, so the text keeps its edge against
     whatever is moving underneath it. */
  background: rgba(8, 11, 17, 0.30);
  border: 1px solid rgba(255,255,255,0.07);
  font: 11px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #e8eef7;
  text-shadow:
    0 1px 2px rgba(0,0,0,0.95),
    0 0 3px rgba(0,0,0,0.9),
    0 0 8px rgba(0,0,0,0.75);
  white-space: pre;
}
/* The two bars sit at the top and never move: on a phone in landscape there
   is no height for the stages below them, so those are what gets clipped —
   never the wheel and pedal the car is being given right now. */
#steer-hud { display: flex; flex-direction: column; gap: 0; }
#steer-hud .bars {
  flex: none;
  /* The one block that carries a little backing of its own. It is the part
     that must read at a glance, and it is also the part that lands on the
     status and prompt pills — which are bright, and which the panel is now
     too sheer to cover on its own. It is three lines: barely any road. */
  background: rgba(8, 11, 17, 0.55);
  border-radius: 6px;
  margin: -4px -5px 4px;
  padding: 4px 5px 6px;
  border-bottom: 1px solid rgba(255,255,255,0.14);
}
/* Below the fold: the explanation. Stacked by default, side by side once the
   screen is too short to hold one column — which landscape always is. */
#steer-hud .cols { display: flex; flex-direction: column; min-height: 0; }
/* Landscape: two narrow columns and a smaller face. Every column of text this
   panel takes is a column of road it covers, so the rows are written short and
   the width is set from them — about a third of a phone screen, not half. */
@media (max-height: 700px) and (min-width: 620px) {
  #steer-hud {
    width: auto;
    padding: 6px 8px;
    font-size: 9px;
    line-height: 1.4;
  }
  #steer-hud .cols { flex-direction: row; gap: 10px; }
  #steer-hud .col { width: 122px; }
}
#steer-hud h4 {
  /* The rows are pre-formatted, but a heading that runs past a narrow column
     should wrap rather than be sliced off mid-word. */
  white-space: normal;
  margin: 7px 0 2px;
  font: 700 9px/1.4 ui-monospace, Menlo, monospace;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  /* All of these run a shade brighter than they did behind the old opaque
     backing: over a lit street the panel no longer supplies the contrast, so
     the ink has to. */
  color: #93a5bd;
}
#steer-hud h4:first-child { margin-top: 0; }
#steer-hud b { color: var(--accent); font-weight: 700; }
/* The stage that is actually deciding the lock, called out by name. */
#steer-hud .hot { color: #8ff0a0; font-weight: 700; }
#steer-hud .cold { color: #a4b4c8; }
/* A bar drawn in text: cheap, and it reads at a glance while moving. */
#steer-hud .bar { color: #74b8f5; }

/* --- Settings: sound levels and drive feel ----------------------------- */
/* The gear keeps the QR button company on the shelf above the minimap, one
   place to its left. The bottom-left bar is for what you are sitting in —
   siren, radio, courier — and this is not that: it is reached the same way on
   foot, in a car and in the air, so it sits where the map does. */
#config-open {
  position: fixed;
  right: calc(max(14px, env(safe-area-inset-right)) + 42px);
  bottom: var(--hud-shelf);
  z-index: 6;
  display: none; /* shown while playing */
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
#config-open.open {
  background: #d9a11c;
  color: #1a1a1a;
}
/* The style stepper: the same button as the gear, one place further along the
   shelf. Hidden until the Graphics pane asks for it, and then only in the
   city — the shelf does not exist on the menu. */
#style-cycle {
  position: fixed;
  right: calc(max(14px, env(safe-area-inset-right)) + 84px);
  bottom: var(--hud-shelf);
  z-index: 6;
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  border: none;
  background: rgba(20, 22, 32, 0.82);
  color: #fff;
  line-height: 0;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
/* A tap turns the wheel, which is the whole idea of the button. */
#style-cycle svg { transition: transform 0.35s ease-out; }
#style-cycle.turn svg { transform: rotate(90deg); }
/* Which look you have just landed on, said once and then gone. Above the
   button, and never in the way of a tap. */
#style-cycle-name {
  position: fixed;
  right: calc(max(14px, env(safe-area-inset-right)) + 42px);
  bottom: var(--hud-toasts);
  z-index: 6;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(20, 22, 32, 0.92);
  color: var(--accent);
  font: 700 13px system-ui, sans-serif;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.25s;
  pointer-events: none;
}

/* The menu's way in, for setting the controls up before the first drive.
   Pinned to the corner rather than left in the column. It was put here
   because the menu could not scroll, so anything low in the column was
   unreachable on a phone; #menu-scroll fixed that, and it stays pinned for
   the reason that outlived the bug — the controls are worth reaching without
   first scrolling past four cities and three character slots. */
#menu-config {
  position: fixed;
  top: max(14px, env(safe-area-inset-top));
  right: max(14px, env(safe-area-inset-right));
  z-index: 11;
  padding: 8px 14px;
  background: var(--panel);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 999px;
  color: var(--text);
  font: inherit;
  font-size: 13px;
  line-height: 1.1;
  cursor: pointer;
  backdrop-filter: blur(6px);
}
#menu-config:hover, #menu-config:focus {
  border-color: var(--accent);
  outline: none;
}

#config {
  position: fixed;
  inset: 0;
  /* Above the radio picker and the race panel (31): it can be opened from
     the menu, which sits above both. */
  z-index: 34;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 7, 12, 0.7);
  /* The box is centered, but a notch is not: in landscape it eats one side of
     the screen, so keep the whole dialog inside the safe area. */
  padding: max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right))
           max(8px, env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left));
}
/* Docked to a side: the box moves over and the backdrop stops being one.
   Judging a look through a dimmed screen with the city hidden behind a box in
   the middle of it is guesswork, so this is how the Graphics pane is meant to
   be used — the world keeps running, undimmed, next to the sliders shaping it.
   The backdrop takes no clicks while docked (so the game still gets them, and
   a click beside the box no longer closes it); the box itself takes its own. */
#config.dock-left, #config.dock-right {
  background: none;
  pointer-events: none;
}
#config.dock-left { justify-content: flex-start; }
#config.dock-right { justify-content: flex-end; }
#config.dock-left #config-box, #config.dock-right #config-box {
  pointer-events: auto;
  /* Nothing dims the city behind it any more, so the box says where it ends. */
  box-shadow: 0 8px 40px rgba(0,0,0,0.75);
}
#config-box {
  width: min(460px, 100%);
  /* dvh, not vh: on an iOS PWA the toolbars make vh the taller, wrong number,
     and the actions row would sit under the home indicator. */
  height: min(600px, 100dvh);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border-radius: 14px;
  background: #171a24;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  color: #fff;
}
#config-tabs { display: flex; gap: 6px; }
#config-tabs button {
  flex: 1;
  padding: 7px 0;
  border: none;
  border-radius: 8px;
  background: rgba(255,255,255,0.1);
  color: var(--text);
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
}
#config-tabs button.on { background: var(--accent); color: #1a1a1a; }
/* Presets: a whole pane in one tap, above the sliders they move. Quieter than
   the tabs — outlined rather than filled — because the pane you are on is a
   bigger fact than the preset you are on. */
#config-preset-row { display: flex; gap: 6px; }
#config-preset-row button {
  flex: 1;
  min-width: 0;
  padding: 7px 2px;
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 8px;
  background: rgba(255,255,255,0.05);
  color: var(--text);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
#config-preset-row button.on {
  border-color: var(--accent);
  background: rgba(255, 204, 51, 0.16);
  color: var(--accent);
}
/* Nothing tuned by hand yet: there is no "back" to go back to. */
#config-preset-row button:disabled {
  opacity: 0.38;
  cursor: default;
}
#config-preset-hint {
  margin-top: 5px;
  font-size: 11px;
  line-height: 1.35;
  opacity: 0.6;
}
#config-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* A flick that runs off the end of the list must not start dragging the
     page (or the pull-to-refresh) behind it. */
  overscroll-behavior: contain;
}
.cfg-row {
  padding: 9px 8px 11px;
  border-radius: 10px;
}
.cfg-row + .cfg-row { border-top: 1px solid rgba(255,255,255,0.06); }
/* The knobs that belong to whichever look is chosen, set in from the rows
   that are always there — they are the style's, not the pane's. */
.cfg-group {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0 2px;
  padding: 0 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.5;
}
.cfg-group span { color: var(--accent); opacity: 0.9; }
/* Puts this look back without touching the other eleven. */
.cfg-group button {
  margin-left: auto;
  padding: 4px 10px;
  border: 1px solid rgba(255,255,255,0.16);
  border-radius: 999px;
  background: none;
  color: inherit;
  font: 700 10px inherit;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}
/* Nothing moved: there is nothing to put back. */
.cfg-group button:disabled { opacity: 0.35; cursor: default; }
.cfg-row.cfg-sub {
  margin-left: 8px;
  padding-left: 10px;
  border-left: 2px solid rgba(255,204,51,0.35);
  border-radius: 0 10px 10px 0;
}
/* The set-in rows are one block, so they are not ruled off from each other. */
.cfg-row.cfg-sub + .cfg-row.cfg-sub { border-top: none; }
.cfg-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 13px;
  font-weight: 700;
}
.cfg-value {
  margin-left: auto;
  font: 700 12px ui-monospace, Menlo, monospace;
  color: var(--accent);
}
/* Says at a glance which rows are no longer the game's own numbers. */
.cfg-row.custom .cfg-value::after {
  content: ' •';
  color: #7ee88c;
}
.cfg-hint {
  margin-top: 1px;
  font-size: 11px;
  line-height: 1.35;
  opacity: 0.6;
}
.cfg-row input[type=range] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  margin-top: 8px;
  height: 22px; /* the touch target, not the track */
  background: none;
  cursor: pointer;
  /* The slider spans the row, so a finger that means to scroll the list very
     often lands on one. `pan-y` hands the vertical flick back to the list and
     keeps the sideways drag — the one that moves the value — for the slider. */
  touch-action: pan-y;
}
.cfg-row input[type=range]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: rgba(255,255,255,0.16);
}
.cfg-row input[type=range]::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: rgba(255,255,255,0.16);
}
.cfg-row input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  margin-top: -7px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 1px 4px rgba(0,0,0,0.5);
}
.cfg-row input[type=range]::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 1px 4px rgba(0,0,0,0.5);
}
/* The row of buttons a one-of-N setting gets: every option visible at once. */
.cfg-row .cfg-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.cfg-row .cfg-choice {
  padding: 6px 11px;
  border: none;
  border-radius: 999px;
  background: rgba(255,255,255,0.10);
  color: inherit;
  font: 700 12px inherit;
  cursor: pointer;
}
.cfg-row .cfg-choice.on {
  background: var(--accent);
  color: #12131a;
}

/* The switch a 0/1 setting gets instead of a two-position slider. */
.cfg-row .cfg-toggle {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-top: 8px;
  padding: 6px 14px 6px 6px;
  border: none;
  border-radius: 999px;
  background: rgba(255,255,255,0.10);
  color: inherit;
  font: 700 12px inherit;
  cursor: pointer;
}
.cfg-row .cfg-toggle .knob {
  position: relative;
  flex: none;
  width: 34px;
  height: 18px;
  border-radius: 999px;
  background: rgba(255,255,255,0.22);
  transition: background 0.15s;
}
.cfg-row .cfg-toggle .knob::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  transition: left 0.15s;
}
.cfg-row .cfg-toggle.on .knob { background: var(--accent); }
.cfg-row .cfg-toggle.on .knob::after { left: 18px; }

#config-actions { display: flex; gap: 8px; }
#config-actions button {
  padding: 8px 14px;
  border: none;
  border-radius: 8px;
  background: rgba(255,255,255,0.12);
  color: var(--text);
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
}
/* Where the box sits: three little pictures of a screen with the box in it,
   left, middle and right. Drawn in CSS rather than lettered — "Left" next to
   Reset and Done reads as a third verb, and this is a place, not an action. */
#config-dock {
  display: flex;
  gap: 4px;
  margin-left: auto;
  padding: 3px;
  border-radius: 9px;
  background: rgba(255,255,255,0.06);
}
#config-actions #config-dock button {
  padding: 5px 6px;
  border-radius: 6px;
  background: none;
  line-height: 0;
}
#config-actions #config-dock button.on { background: rgba(255,255,255,0.16); }
/* The screen. */
#config-dock button i {
  position: relative;
  display: block;
  width: 20px;
  height: 14px;
  border: 1px solid currentColor;
  border-radius: 2px;
  opacity: 0.55;
}
#config-dock button.on i { opacity: 1; color: var(--accent); }
/* The box in it. */
#config-dock button i::after {
  content: '';
  position: absolute;
  top: 2px;
  bottom: 2px;
  width: 6px;
  background: currentColor;
  border-radius: 1px;
}
#config-dock button[data-side=left] i::after { left: 2px; }
#config-dock button[data-side=right] i::after { right: 2px; }
#config-dock button[data-side=''] i::after { left: 50%; margin-left: -3px; }

/* Both ids: `#config-actions button` above is the more specific selector, so
   a bare `#config-close` would lose its colour to it. */
#config-actions #config-close {
  margin-left: auto;
  background: var(--accent);
  color: #201a00;
}
/* A thumb is not a mouse pointer: on a touch screen the grab target grows,
   and the row it lives in gets enough height to be hit without care. */
@media (pointer: coarse) {
  .cfg-row input[type=range] { height: 30px; }
  .cfg-row input[type=range]::-webkit-slider-thumb {
    width: 24px;
    height: 24px;
    margin-top: -10px;
  }
  .cfg-row input[type=range]::-moz-range-thumb { width: 24px; height: 24px; }
  #config-tabs button { padding: 10px 0; }
  #config-preset-row button { padding: 10px 2px; }
  #config-actions button { padding: 11px 16px; }
}
/* A phone held sideways has about 320px of height to give the box, and the
   rows as written would show three of them. Everything shrinks a little, the
   hints go quiet, and the list keeps the space that is left. */
@media (max-height: 560px) {
  #config-box { padding: 10px; gap: 7px; border-radius: 12px; }
  #config-tabs button { padding: 6px 0; }
  #config-preset-row button { padding: 6px 2px; }
  #config-preset-hint { margin-top: 3px; font-size: 10px; }
  .cfg-row { padding: 6px 6px 7px; }
  .cfg-head { font-size: 12px; }
  .cfg-hint { font-size: 10px; }
  .cfg-row input[type=range] { margin-top: 4px; }
  #config-actions button { padding: 7px 12px; }
}
/* …and it has width to spare instead. Past 640px the rows pair up, so a
   landscape phone shows six or eight at a time rather than three. */
@media (max-height: 560px) and (min-width: 640px) {
  #config-box { width: min(760px, 100%); }
  #config-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 14px;
    align-content: start;
  }
  /* The divider is per-row in one column; in two it would draw down the
     middle of a pair. Give every row its own line instead. */
  .cfg-row + .cfg-row { border-top: none; }
  .cfg-row { border-bottom: 1px solid rgba(255,255,255,0.06); }
}

/* ---------------------------------------------------------------------------
   Cybernetics: the clinic panel, the chrome HUD and the implant buttons.

   The panel deliberately mirrors #shop — same side, same width, same shape —
   because they are the same kind of place and a player who has used one should
   not have to learn the other. What it does not share is the colour: the
   garage is warm and this is not. */

#clinic {
  position: fixed;
  top: 12px;
  right: 12px;
  bottom: 12px;
  width: min(84vw, 340px);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  background: var(--panel);
  border: 1px solid rgba(190, 120, 255, 0.28);
  border-radius: 16px;
  backdrop-filter: blur(8px);
}
#clinic-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
#clinic-title { font-weight: 800; font-size: 18px; color: #e9c6ff; }
#clinic-cash { color: var(--accent); font-weight: 700; }

/* Licence tier: what the ward will show you, and what the next one costs in
   chrome already sunk. The bar is the progress toward it. */
#clinic-licence {
  font-size: 11px;
  line-height: 1.45;
  color: rgba(255,255,255,0.66);
}
#clinic-licence .tier { color: #e9c6ff; font-weight: 700; }
#clinic-licence .bar {
  margin-top: 5px;
  height: 4px;
  border-radius: 3px;
  background: rgba(255,255,255,0.12);
  overflow: hidden;
}
#clinic-licence .bar > span {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #7a4bd0, #d36ce0);
}

#clinic-tabs { display: flex; gap: 5px; flex-wrap: wrap; }
#clinic-tabs .opt { font-size: 12px; padding: 3px 9px; }

#clinic-opts {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-content: flex-start;
}
#clinic-opts .opt {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  text-align: left;
}
/* An implant row: icon, name and its one-line effect, then the price. */
#clinic-opts .imp {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  width: 100%;
  padding: 8px 10px;
  text-align: left;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.10);
  background: rgba(255,255,255,0.045);
  color: inherit;
  font: inherit;
  cursor: pointer;
}
#clinic-opts .imp:hover { background: rgba(255,255,255,0.085); }
#clinic-opts .imp.sel {
  border-color: rgba(211,108,224,0.75);
  background: rgba(211,108,224,0.14);
}
/* Owned at full grade, or locked behind a licence: not buyable, and it says
   so by going quiet rather than by disappearing. */
#clinic-opts .imp.maxed, #clinic-opts .imp.locked {
  opacity: 0.45;
  cursor: default;
}
#clinic-opts .imp .icon { font-size: 19px; line-height: 1.1; }
#clinic-opts .imp .body { flex: 1; min-width: 0; }
/* The three lines are spans (a <button> may not contain <div>), so each has to
   be told to be a line — inline, the name, the grade and the blurb run
   together into one paragraph. */
#clinic-opts .imp .name {
  display: block;
  font-weight: 700;
  font-size: 13px;
}
#clinic-opts .imp .grade {
  display: block;
  font-size: 11px;
  color: #b9f0ff;
  margin-top: 1px;
}
#clinic-opts .imp .fx {
  display: block;
  font-size: 11px;
  opacity: 0.62;
  margin-top: 3px;
  line-height: 1.35;
}
#clinic-opts .imp .price {
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  white-space: nowrap;
}
#clinic-opts .imp .owned { color: #7ddf7d; }
/* Grade pips: three dots, filled to the grade fitted, ringed to the grade
   selected but not yet paid for. */
#clinic-opts .pips { display: flex; gap: 3px; margin-top: 6px; }
#clinic-opts .pips i {
  width: 14px;
  height: 4px;
  border-radius: 2px;
  background: rgba(255,255,255,0.18);
}
#clinic-opts .pips i.own { background: #7ddf7d; }
#clinic-opts .pips i.want { background: #d36ce0; }
#clinic-opts .head {
  font-size: 11px;
  opacity: 0.55;
  margin-top: 4px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* The surgery. Replaces the shelf while the arms are working, so there is
   nothing to click and nothing to misread as still shopping. */
#clinic-surgery {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  text-align: center;
}
#clinic-surgery-name { font-weight: 800; font-size: 15px; color: #e9c6ff; }
#clinic-surgery-bar {
  width: 100%;
  height: 6px;
  border-radius: 4px;
  background: rgba(255,255,255,0.12);
  overflow: hidden;
}
#clinic-surgery-bar > span {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, #4bd0c0, #d36ce0);
}
#clinic-surgery-note {
  font-size: 11px;
  opacity: 0.6;
  letter-spacing: 0.06em;
}

#clinic-actions { display: flex; gap: 8px; }
#clinic-actions button { flex: 1; }

/* While the clinic is open the round buttons go, exactly as they do for the
   garage — same reason, same mechanism (the HUD callback owns their inline
   display and would put them back on the next tick). */
body.clinicking #chat-open, body.clinicking #cam-toggle,
body.clinicking #siren-toggle, body.clinicking #taxi-call,
body.clinicking #race-btn, body.clinicking #courier-btn,
body.clinicking #radio-toggle, body.clinicking #radio-panel,
body.clinicking #handbrake, body.clinicking #chrome-buttons {
  display: none !important;
}

/* --- The chrome HUD ------------------------------------------------------ */

/* Above the chat/taxi button row, not across it: those buttons sit at
   bottom: 12px and are 44px tall, so the stack starts clear of them. */
#hud-chrome {
  position: fixed;
  left: max(14px, env(safe-area-inset-left));
  bottom: calc(max(14px, env(safe-area-inset-bottom)) + 60px);
  z-index: 6;
  display: none;
  flex-direction: column;
  gap: 6px;
  width: min(42vw, 210px);
  pointer-events: none;
}
#hud-health, #hud-jet {
  display: none;
  height: 7px;
  border-radius: 4px;
  background: rgba(0,0,0,0.45);
  border: 1px solid rgba(255,255,255,0.14);
  overflow: hidden;
}
#hud-health > span, #hud-jet > span {
  display: block;
  height: 100%;
  width: 100%;
  transition: width 120ms linear;
}
#hud-health > span { background: linear-gradient(90deg, #e0483c, #6fe07a); }
/* Under a quarter it stops being a gradient and starts being a warning. */
#hud-health.low > span { background: #ff4436; }
#hud-jet > span { background: linear-gradient(90deg, #2f7fd0, #6ee0ff); }

#hud-implants { display: flex; gap: 5px; flex-wrap: wrap; }
#hud-implants .chip {
  padding: 3px 7px;
  border-radius: 8px;
  background: var(--panel);
  border: 1px solid rgba(255,255,255,0.14);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
#hud-implants .chip.hot { color: #6ee0ff; border-color: rgba(110,224,255,0.5); }
#hud-implants .chip.cold { opacity: 0.42; }

#downed-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #000;
  opacity: 0;
  pointer-events: none;
}
#downed-overlay .busted-title {
  color: #6ee0ff;
  font-family: system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(38px, 10vw, 92px);
  letter-spacing: 0.12em;
}
#downed-overlay .busted-sub {
  margin-top: 14px;
  color: rgba(255, 255, 255, 0.72);
  font-family: system-ui, sans-serif;
  font-weight: 600;
  font-size: clamp(13px, 3vw, 20px);
  letter-spacing: 0.05em;
}

/* --- Implant buttons (touch) --------------------------------------------- */

/* The column stands on top of the corner stack — above the map, above the
   shelf the QR and gear buttons sit on, above the toasts. It used to clear a
   flat 168px, which was enough only while the map was phone-sized: on a
   tablet held upright, or any desktop, the map alone is 216px and the column
   came down on the QR button. */
#chrome-buttons {
  position: fixed;
  right: var(--hud-right);
  bottom: var(--hud-stack-top);
  z-index: 7;
  display: none;
  flex-direction: column;
  gap: 7px;
  align-items: flex-end;
}
#chrome-buttons button {
  width: 46px;
  height: 46px;
  padding: 0;
  border-radius: 50%;
  font-size: 20px;
  line-height: 1;
  background: var(--panel);
  border: 1px solid rgba(255,255,255,0.16);
  color: inherit;
  backdrop-filter: blur(6px);
}
#chrome-buttons button.on {
  border-color: rgba(110,224,255,0.8);
  box-shadow: 0 0 16px rgba(110,224,255,0.45);
}
#chrome-buttons button.cold { opacity: 0.38; }

/* A short screen has no room for a column of seven beside the minimap: lay
   them out along the bottom instead, above the pedal row. Along the floor,
   starting left of the map and of the handbrake slot beside it (58px plus its
   gap) — the row shares this edge with a control you grab in a hurry, so it
   begins past it rather than under it. This offset used to be a flat 132px,
   which cleared the map only while the map was phone-sized. */
@media (max-height: 560px) {
  #chrome-buttons {
    flex-direction: row;
    bottom: calc(var(--hud-bottom) + 4px);
    right: calc(var(--hud-right) + var(--minimap-size) + 80px);
  }
  #chrome-buttons button { width: 40px; height: 40px; font-size: 17px; }
}
