* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; touch-action: none; }

/* touch-action: none wyłącza gesty na canvasie gry (przewijanie, zoom przy
   dwóch palcach), ale dziedziczy się na CAŁĄ stronę — a Safari na iPhonie
   potrafi przy nim nie wygenerować zdarzenia kliknięcia w formularzu. Efekt:
   przycisk widać, da się go dotknąć i nic się nie dzieje. Okna dialogowe
   i przyciski dostają więc normalną obsługę dotyku z powrotem. */
.overlay, .overlay *, #top-buttons, #top-buttons button, .panel, .panel button,
input, button, label, a { touch-action: manipulation; }
body {
  font-family: ui-rounded, "SF Pro Rounded", system-ui, sans-serif;
  background: #8ed1fc;
  user-select: none;
  -webkit-user-select: none;
  /* iOS: przytrzymanie palca na napisie (GAZ, HAMULEC…) potrafi wywołać
     lupę do zaznaczania tekstu i menu kopiowania — w grze to tylko
     przeszkadza, więc callout znika globalnie. Pola tekstowe mają swoje
     user-select niżej i lupa do wpisywania dalej działa. */
  -webkit-touch-callout: none;
}
#game { position: fixed; inset: 0; width: 100%; height: 100%; display: block; }
.hidden { display: none !important; }

/* --- overlaye / karty --- */
.overlay {
  position: fixed; inset: 0; z-index: 30;
  display: flex; align-items: center; justify-content: center;
  background: rgba(30, 20, 60, 0.45);
  backdrop-filter: blur(4px);
}
.card {
  background: linear-gradient(160deg, #fff, #ffe8f0);
  border-radius: 28px;
  padding: 32px 40px;
  text-align: center;
  box-shadow: 0 18px 50px rgba(30, 20, 60, 0.4);
  max-width: min(92vw, 460px);
  max-height: 88vh;
  /* dvh zamiast vh: na telefonie pasek adresu chowa się i pokazuje, a vh liczy
     się do NAJWIĘKSZEJ wysokości okna — przez to dolna część karty (razem
     z przyciskiem) potrafiła wisieć poza ekranem, niewidoczna i nieklikalna */
  max-height: 88dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Telefon w poziomie: na wysokość zostaje kilkaset pikseli, a sam nagłówek
   zajmował ich ponad sto. Wszystko się zagęszcza, żeby przycisk mieścił się
   na ekranie bez przewijania. */
@media (max-height: 560px) {
  .card { padding: 14px 22px; border-radius: 20px; }
  .card h1 { font-size: 30px; text-shadow: 2px 2px 0 #ffd166; }
  .card h2 { font-size: 22px; margin-bottom: 6px; }
  .tag { margin: 2px 0 10px; font-size: 13px; }
  input { padding: 9px 14px; font-size: 16px; margin-bottom: 8px; }
  button.big { padding: 11px; font-size: 18px; }
  .hint { margin-top: 8px; font-size: 11px; }
  .auth-tabs { margin-bottom: 8px; }
  .auth-tab { padding: 7px 4px; font-size: 11px; }
  .auth-hint { margin: -2px 0 8px; font-size: 11px; }
  .car-list { max-height: 30vh; }
}
.card h1 {
  font-size: 52px; letter-spacing: 2px; color: #118ab2;
  text-shadow: 3px 3px 0 #ffd166;
}
.card h1 span { color: #ff5470; }
.card h2 { font-size: 32px; color: #118ab2; margin-bottom: 12px; }
.tag { color: #666; margin: 6px 0 20px; }
.err { color: #ff5470; font-weight: 700; min-height: 1.4em; margin-top: 8px; }
.hint { color: #888; font-size: 13px; margin-top: 14px; }

input {
  width: 100%; padding: 14px 18px; font-size: 20px; text-align: center;
  border: 3px solid #118ab2; border-radius: 16px; outline: none;
  font-family: inherit; margin-bottom: 14px; background: #fff;
}
button.big {
  width: 100%; padding: 16px; font-size: 24px; font-weight: 800;
  color: #fff; background: linear-gradient(180deg, #ff7a94, #ff5470);
  border: 0; border-radius: 18px; cursor: pointer;
  box-shadow: 0 6px 0 #c93a56; font-family: inherit;
  transition: transform 0.06s;
}
button.big:active { transform: translateY(4px); box-shadow: 0 2px 0 #c93a56; }
button.big:disabled { opacity: 0.6; }
button.big.armed { background: linear-gradient(180deg, #45e08a, #06d6a0); box-shadow: 0 6px 0 #059677; }

/* --- lobby --- */
.panel {
  position: fixed; top: 16px; left: 16px; z-index: 20;
  background: rgba(20, 20, 45, 0.78); color: #fff;
  border-radius: 20px; padding: 18px 20px; width: min(86vw, 320px);
  backdrop-filter: blur(6px);
  /* bez tego na telefonie (mały landscape) treść panelu bywa wyższa niż
     ekran i przycisk GOTOWY wyjeżdża poza widok bez możliwości scrolla */
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
}
.panel h2 { color: #ffd166; font-size: 22px; margin-bottom: 8px; }
.panel ul { list-style: none; max-height: 34vh; overflow-y: auto; margin-bottom: 8px; }
.panel li { padding: 4px 6px; font-size: 16px; border-radius: 8px; }
.panel li.me { background: rgba(255, 213, 102, 0.18); font-weight: 700; }
.panel p { font-size: 13px; color: #cbd; margin-bottom: 10px; }
.panel .hint { color: #99a; }

/* głosowanie trybu wyścigu */
.mode-vote {
  display: grid;
  /* minmax(0,1fr): bez tego guziki nie mogą się skurczyć poniżej szerokości
     tekstu i wyłażą poza panel, nachodząc na siebie (nieklikalne) */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px; margin-bottom: 10px;
}
.mv {
  min-width: 0; padding: 9px 2px; font-size: 11.5px; font-weight: 800;
  font-family: inherit; color: #fff; cursor: pointer;
  background: rgba(255, 255, 255, 0.12);
  border: 2px solid transparent; border-radius: 12px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mv.sel { border-color: #ffd166; background: rgba(255, 209, 102, 0.25); }

/* lampa trybu ŚWIATŁA */
#light-dot {
  position: fixed; top: 44px; left: 50%; transform: translateX(-50%);
  z-index: 16; width: 56px; height: 56px; border-radius: 50%;
  border: 4px solid rgba(20, 20, 45, 0.7); pointer-events: none;
  transition: background 0.12s, box-shadow 0.12s;
}
#light-dot[data-c="g"] { background: #3ef26b; box-shadow: 0 0 26px #3ef26b; }
#light-dot[data-c="y"] { background: #ffd166; box-shadow: 0 0 26px #ffd166; }
#light-dot[data-c="r"] { background: #ff3c50; box-shadow: 0 0 34px #ff3c50; }

#race-timer {
  position: fixed; top: 12px; left: 50%; transform: translateX(-50%);
  z-index: 15; background: rgba(20, 20, 45, 0.65); color: #ffd166;
  padding: 6px 18px; border-radius: 999px; font-size: 17px; font-weight: 800;
  pointer-events: none;
}

/* --- HUD --- */
#race-hud { position: fixed; right: 12px; bottom: 12px; z-index: 15; pointer-events: none; }
#tach { width: 190px; height: 190px; filter: drop-shadow(0 4px 10px rgba(0,0,0,0.4)); }

#progress {
  position: fixed; top: 10px; left: 50%; transform: translateX(-50%);
  width: min(70vw, 700px); height: 14px; z-index: 15;
  background: rgba(20,20,45,0.55); border-radius: 8px;
}
#progress .dot {
  position: absolute; top: 2px; width: 10px; height: 10px; border-radius: 50%;
  background: #ffd166; transform: translateX(-5px);
}
#progress .dot.me { background: #3ef26b; box-shadow: 0 0 6px #3ef26b; }

/* Okrążenie i numer bramki na torze pętlowym. Tuż pod paskiem postępu, bo tam
   gracz i tak zerka po pozycję — a na trzech okrążeniach po tych samych
   ulicach „które to okrążenie" jest pytaniem, na które nie da się odpowiedzieć
   z samego widoku za szybą. */
#okrazenia {
  position: fixed; top: 28px; left: 50%; transform: translateX(-50%);
  z-index: 15; pointer-events: none; white-space: nowrap;
  padding: 3px 12px; border-radius: 999px;
  background: rgba(20,20,45,0.62); color: #ffd166;
  font-size: 13px; font-weight: 800; letter-spacing: 0.3px;
}

/* Licznik jazdy bokiem. Pod licznikiem okrążeń, bo to ta sama półka informacji
   „co się teraz dzieje", a obie naraz widać wyłącznie na torze pętlowym.
   Kolor zielony, nie żółty: żółte na tym HUD-zie jest wszystko, co dotyczy
   wyścigu, a to dzieje się poza nim. */
#bok-licznik {
  position: fixed; top: 56px; left: 50%; transform: translateX(-50%);
  z-index: 15; pointer-events: none; white-space: nowrap;
  padding: 4px 14px; border-radius: 999px;
  background: rgba(20,45,25,0.66); color: #7dfba0;
  font-size: 15px; font-weight: 900; letter-spacing: 0.4px;
  text-shadow: 0 0 12px rgba(62,242,107,0.45);
}
/* Kontra: auto na ułamek sekundy się prostuje, a licznik i tak jeszcze żyje
   (600 ms tolerancji). Przygaszamy go zamiast chować — miganie co dwie
   sekundy byłoby gorsze niż sam brak licznika. */
#bok-licznik.bok-kontra { opacity: 0.55; }

#mode-info {
  position: fixed; top: 15vh; left: 50%; transform: translateX(-50%);
  z-index: 24; max-width: min(90vw, 560px); text-align: center;
  background: rgba(20,20,45,0.72); color: #fff; padding: 12px 22px;
  border-radius: 16px; font-size: 16px; font-weight: 700; line-height: 1.4;
  pointer-events: none;
}

#countdown {
  position: fixed; top: 22vh; left: 0; right: 0; z-index: 25;
  text-align: center; font-size: min(15vw, 96px); font-weight: 900; color: #fff;
  text-shadow: 0 6px 0 rgba(30,20,60,0.5), 0 0 40px rgba(255,213,102,0.7);
  pointer-events: none;
}
#banner {
  position: fixed; top: 38vh; left: 0; right: 0; z-index: 25;
  text-align: center; font-size: min(8vw, 44px); font-weight: 900; color: #ffd166;
  text-shadow: 0 4px 0 rgba(30,20,60,0.6);
  pointer-events: none;
}

/* Wybór trybu startu — nad odliczaniem, żeby palec nie zasłaniał licznika.
   Duże cele dotykowe, bo klika się to w aucie stojącym pod światłami. */
#tryb-startu {
  position: fixed; top: 12vh; left: 50%; transform: translateX(-50%);
  z-index: 24; display: flex; gap: 10px;
  /* Cztery przyciski nie mieszczą się w rzędzie na wąskim telefonie —
     zawijamy do drugiej linii zamiast wypychać skrajne poza ekran. */
  flex-wrap: wrap; justify-content: center; max-width: 96vw;
}
#tryb-startu button {
  border: 2px solid transparent; border-radius: 999px; cursor: pointer;
  background: rgba(20,20,45,0.72); color: #fff;
  padding: 12px 20px; font-size: 17px; font-weight: 800;
  /* iOS Safari: bez tego długie przytrzymanie wywołuje lupę/zaznaczanie */
  -webkit-user-select: none; user-select: none; touch-action: manipulation;
}
#tryb-startu button.sel {
  border-color: #ffd166; color: #ffd166;
  background: rgba(60,48,10,0.8);
}

#spectate-bar {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
  z-index: 15; background: rgba(20,20,45,0.75); color: #fff;
  padding: 10px 22px; border-radius: 999px; font-size: 16px; font-weight: 700;
}

#results table { width: 100%; border-collapse: collapse; font-size: 20px; }
#results td { padding: 8px 10px; text-align: left; color: #333; }
#results tr.me td { background: rgba(255, 213, 102, 0.35); font-weight: 800; border-radius: 8px; }

#top-buttons {
  position: fixed; z-index: 40; display: flex; gap: 10px; align-items: center;
  top: max(14px, env(safe-area-inset-top));
  right: max(14px, env(safe-area-inset-right));
}
#top-buttons button {
  height: 46px; font-size: 21px; border: 0; border-radius: 23px;
  background: rgba(20,20,45,0.6); cursor: pointer; color: #fff;
}
/* Jedyny przycisk HUD-u, więc stać go na słowo obok kreseczek. Samo ☰
   znaczyło „coś tu jest" i nic więcej — a to za mało, żeby ktokolwiek
   sprawdził, czy szukane ustawienie siedzi właśnie tam. */
#menu-btn {
  display: flex; align-items: center; gap: 7px;
  padding: 0 16px; font-family: inherit; font-size: 15px; font-weight: 800;
  letter-spacing: 0.06em;
}
#speed-chip { display: none; }
#lobby-toggle {
  display: flex; width: 100%; border: 0; background: transparent; color: inherit;
  font: inherit; text-align: left; cursor: pointer; padding: 0;
  align-items: center; justify-content: space-between; gap: 8px;
}
#lobby-toggle h2 { margin: 0; }
#lobby-count { color: #ffd166; font-size: 12px; font-weight: 800; }

/* --- menu gry (pełnoekranowy modal) ---
   Zamiast rozwijanej listy przy ☰ — jedno okno na środku, przykrywające grę.
   Miejsca jest wtedy tyle, że każda pozycja mieści nazwę I zdanie o tym, co
   robi; wcześniej wszystko musiało się zmieścić w ikonie, więc znaczenie
   niosły `title`, których na telefonie nikt nigdy nie zobaczy. Podpisy są na
   każdej szerokości — na desktopie dymek trzeba wywołać najechaniem i odczekać,
   a i tak wcześniej nie wiadomo, czy warto. */
/* Nad #top-buttons (40): karta jest szeroka i na telefonie w poziomie sięga
   pod portfel i sam przycisk ☰ MENU — a wtedy ✕ w nagłówku menu leży POD nimi
   i nie da się w niego trafić. Menu i tak przykrywa całą grę, więc nic tu nie
   traci: pieniądze widać z powrotem po zamknięciu. */
#menu-modal { z-index: 42; }
.menu-karta {
  width: min(96vw, 760px); max-width: min(96vw, 760px);
  max-height: 92dvh;
  display: flex; flex-direction: column; gap: 10px;
  /* przewija się środek (.menu-zwoj), nie cała karta — nagłówek i ZAMKNIJ
     mają zostać na ekranie, bo wyjście z menu nie może wymagać scrolla */
  overflow: hidden;
  padding: 18px 20px 16px; text-align: left;
}
.menu-pasek { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.menu-pasek h2 { margin: 0; font-size: 26px; }
#menu-x {
  flex: 0 0 auto; width: 44px; height: 44px; border: 0; border-radius: 50%;
  background: #f1e7ee; color: #118ab2; cursor: pointer;
  font-family: inherit; font-size: 18px; font-weight: 800;
}
.menu-zwoj {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding-right: 4px;
}
.menu-naglowek {
  margin: 14px 0 6px; font-size: 12px; font-weight: 800; letter-spacing: 0.12em;
  text-transform: uppercase; color: #8a93a5;
}
.menu-naglowek:first-child { margin-top: 2px; }
#menu-done { flex: 0 0 auto; }

/* Lista akcji. Na szerokim ekranie dwie kolumny — jedna rozciągnięta na
   760 px byłaby pasem tekstu z pustką po prawej. */
#hud-wiecej { display: grid; grid-template-columns: 1fr; gap: 8px; }
@media (min-width: 700px) { #hud-wiecej { grid-template-columns: 1fr 1fr; } }
#hud-wiecej button {
  display: grid; grid-template-columns: 40px 1fr; align-items: center;
  column-gap: 12px; row-gap: 1px;
  width: 100%; min-height: 60px; padding: 10px 14px;
  border: 2px solid #e3dbe8; border-radius: 16px;
  background: #fff; color: #1b2030; cursor: pointer;
  font-family: inherit; text-align: left;
}
#hud-wiecej button:active { background: #e2f4fa; border-color: #118ab2; }
#hud-wiecej button > .hud-ikona {
  grid-column: 1; grid-row: 1 / span 2; font-size: 26px; font-weight: 400; text-align: center;
}
#hud-wiecej button > span { grid-column: 2; grid-row: 1; font-size: 17px; font-weight: 800; }
#hud-wiecej button > i {
  grid-column: 2; grid-row: 2; font-style: normal;
  font-size: 12.5px; color: #6b7280; line-height: 1.3;
}

/* Ustawienia: opis po lewej, przełącznik po prawej. */
#menu-ustawienia { display: flex; flex-direction: column; gap: 8px; }
.ust {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 14px; border: 2px solid #e3dbe8; border-radius: 16px; background: #fff;
}
.ust-tekst { flex: 1 1 220px; min-width: 0; }
.ust-tekst b { display: block; font-size: 16px; font-weight: 800; color: #1b2030; }
.ust-tekst i {
  display: block; font-style: normal; font-size: 12.5px; color: #6b7280;
  line-height: 1.35; margin-top: 2px;
}
/* Komunikat żyroskopu (np. „brak zgody na czujniki"). Baner gry leci pod
   modalem, więc odpowiedź na kliknięcie musi być TU, przy przełączniku. */
.ust-tekst i.ust-stan { color: #118ab2; font-weight: 700; }
.ust-stan:empty { display: none; }
/* 44 px wysokości: dolna granica celu, w który kciuk trafia bez celowania. */
.ust-przel {
  flex: 0 0 auto; min-width: 116px; min-height: 44px; padding: 0 16px;
  border: 2px solid #118ab2; border-radius: 14px;
  background: rgba(17, 138, 178, 0.1); color: #118ab2; cursor: pointer;
  font-family: inherit; font-size: 14px; font-weight: 800; white-space: nowrap;
}
.ust-przel.active { background: #06d6a0; border-color: #06d6a0; color: #fff; }
.ust-przel.wyciszone { background: #fff1c2; border-color: #e0a800; color: #8a6100; }
/* zyro.js nie zna napisów — zapala przyciskowi tylko klasę .active, więc
   podmianę „WŁĄCZ"/„WYŁĄCZ" robi CSS i nie trzeba ruszać modułu żyroskopu */
.ust-przel .gdy-on { display: none; }
.ust-przel.active .gdy-off { display: none; }
.ust-przel.active .gdy-on { display: inline; }
.ust-wybor { display: flex; flex: 0 0 auto; gap: 6px; }
.ust-wybor button {
  min-width: 78px; min-height: 44px; padding: 0 12px;
  border: 2px solid #ddd6e2; border-radius: 14px; background: #fff; color: #667;
  font-family: inherit; font-size: 14px; font-weight: 800; cursor: pointer;
}
.ust-wybor button.sel { border-color: #118ab2; background: #e2f4fa; color: #0d6f8f; }

/* Suwak czułości żyroskopu: własny wiersz na pełną szerokość karty ustawienia.
   Wysokość 44 px jak przyciski — kciuk łapie uchwyt bez celowania, a `accent-color`
   robi za całą stylizację (własne ::-webkit-slider-thumb rozjeżdżało się między
   iOS a Chrome i nie było czego z tego wyciągnąć). */
.ust-suwak { flex: 1 1 100%; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ust-suwak label { font-size: 12.5px; font-weight: 800; color: #1b2030; }
.ust-suwak input[type="range"] {
  flex: 1 1 120px; min-width: 110px; height: 44px; accent-color: #118ab2; cursor: pointer;
}
.ust-suwak i { flex: 1 1 100%; font-style: normal; font-size: 12.5px; color: #6b7280; }
.zyro-podglad-znak { flex: 0 0 auto; font-size: 14px; line-height: 1; opacity: 0.75; }
/* Podgląd wychyłu przy suwaku — ten sam ruch co pasek nad przyciskami jazdy,
   tylko w rozmiarze wiersza ustawień. --zakres = (szer − wys) / 2. */
#zyro-podglad {
  --zakres: 34px; position: relative; flex: 0 0 auto; width: 82px; height: 14px;
  pointer-events: none;
  border-radius: 7px; background: #efe9f2; box-shadow: inset 0 0 0 2px #ddd6e2;
}
#zyro-podglad .zyro-kulka {
  position: absolute; top: 2px; left: 50%; width: 10px; height: 10px; margin-left: -5px;
  border-radius: 50%; background: #06d6a0;
  transform: translateX(calc(var(--skret, 0) * var(--zakres)));
}
/* Żyro wyłączone: kropka stoi na środku i jest szara — inaczej wyglądałaby
   na zepsutą („ustawiam suwak, a nic się nie rusza"). */
body:not(.zyro) #zyro-podglad .zyro-kulka { background: #c9c2cf; }

/* Telefon w poziomie: na wysokość zostaje ~390 px, więc menu się zagęszcza —
   ale cele dotykowe zostają przy 44 px. */
@media (max-height: 560px) {
  .menu-karta { max-height: 96dvh; padding: 10px 14px 12px; gap: 6px; }
  .menu-pasek h2 { font-size: 20px; }
  #menu-x { width: 38px; height: 38px; font-size: 16px; }
  .menu-naglowek { margin: 8px 0 4px; font-size: 11px; }
  /* Lista musi kończyć się PONIŻEJ krawędzi tak, żeby nagłówek USTAWIENIA
     i początek pierwszego wiersza były widać bez przewijania — inaczej
     żyroskop znów znika z oczu, a to jest dokładnie ten problem, który
     przebudowa menu miała rozwiązać. Stąd ciasne wiersze; 44 px zostaje,
     bo poniżej kciuk zaczyna chybiać. */
  #hud-wiecej { gap: 5px; }
  #hud-wiecej button {
    min-height: 44px; padding: 5px 10px; grid-template-columns: 30px 1fr; column-gap: 9px;
  }
  #hud-wiecej button > .hud-ikona { font-size: 20px; }
  #hud-wiecej button > span { font-size: 14px; }
  #hud-wiecej button > i { font-size: 11px; }
  .ust { padding: 8px 10px; gap: 10px; }
  .ust-tekst b { font-size: 14px; }
  .ust-tekst i { font-size: 11px; }
  .ust-przel, .ust-wybor button { min-height: 44px; font-size: 13px; }
  .ust-suwak { gap: 8px; }
  .ust-suwak label, .ust-suwak i { font-size: 11px; }
  #menu-done { padding: 11px; font-size: 17px; }
}

/* --- czat --- */
#chat-log {
  position: fixed; z-index: 14; pointer-events: none;
  left: max(12px, env(safe-area-inset-left));
  bottom: max(14px, env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 4px;
  max-width: min(46vw, 340px);
}
#chat-log .msg {
  background: rgba(20,20,45,0.65); color: #fff; font-size: 13px;
  padding: 4px 10px; border-radius: 10px; word-break: break-word;
}
#chat-log .msg b { color: #ffd166; }
/* Dotyk: lewy dolny róg to kierownica, prawy pedały — log idzie na środek,
   nad rząd 🧭💡TYŁ (dół 22px + 56px przycisku + oddech). pointer-events:none
   z reguły bazowej: dymki nigdy nie mogą przechwycić tapnięcia w jezdnię. */
body.touch #chat-log {
  left: 50%; transform: translateX(-50%); align-items: center;
  bottom: 92px; max-width: min(46vw, 340px);
}
/* Pieszo kontrolki jazdy znikają i dolny środek jest wolny — log schodzi na
   sam dół, żeby nie wisiał na pigułce interakcji („obejrzyj" przy aucie). */
body.touch.pieszo #chat-log { bottom: max(14px, env(safe-area-inset-bottom)); }

#chat-box {
  position: fixed; z-index: 26; left: 50%; transform: translateX(-50%);
  bottom: max(14px, env(safe-area-inset-bottom));
  display: flex; gap: 8px; align-items: center;
  width: min(70vw, 420px);
}
.chat-wiersz { display: flex; gap: 8px; align-items: center; width: 100%; }
#chat-box input {
  /* 16px to nie estetyka: poniżej tej wielkości iOS Safari przy fokusie
     dozoomowuje całą stronę i po zamknięciu klawiatury kadr zostaje ucięty */
  flex: 1; min-width: 0; margin: 0; padding: 10px 14px; font-size: 16px;
  border: 2px solid #118ab2; border-radius: 14px; background: #fff;
}
#chat-box button {
  width: 42px; height: 42px; border: 0; border-radius: 50%;
  font-size: 16px; cursor: pointer; color: #fff; flex-shrink: 0;
}
#chat-send { background: #06d6a0; }
#chat-close { background: #ff5470; }
/* Szybkie odpowiedzi tylko na dotyku — z klawiaturą fizyczną szybciej napisać. */
#chat-szybkie { display: none; }
body.touch #chat-szybkie { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
#chat-szybkie button {
  /* width:auto nadpisuje kółko 42px z reguły `#chat-box button` wyżej */
  width: auto; height: 40px; padding: 0 14px; border: 0; border-radius: 999px;
  white-space: nowrap; /* „dobry start!" ma być pigułką, nie kolumienką */
  font-family: inherit; font-size: 14px; font-weight: 700; color: #fff;
  background: rgba(20,20,45,0.85); box-shadow: inset 0 0 0 2px rgba(255,255,255,0.3);
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
/* Dotyk: kompozytor u GÓRY, nie na dole — klawiatura ekranowa w landscape
   zjada dolną połowę ekranu (iOS nie zmniejsza layout viewportu, tylko
   zasłania), więc pasek przypięty do dołu znikał dokładnie w chwili pisania.
   Kolumna: wiersz z polem, pod nim chipy — bliżej kciuków. */
body.touch #chat-box {
  top: max(8px, env(safe-area-inset-top)); bottom: auto;
  flex-direction: column; width: min(92vw, 560px);
  /* nad #top-buttons (40): otwarty kompozytor przykrywa portfel i ☰ —
     to stan chwilowy, a ➤/✕ nie mogą lądować pod cudzymi przyciskami */
  z-index: 41;
}

/* --- dotyk --- */
#touch-controls { position: fixed; inset: 0; z-index: 18; pointer-events: none; }
#touch-controls button {
  pointer-events: auto; touch-action: none;
  /* jawnie, nie tylko z dziedziczenia: lupa/zaznaczanie na trzymanym
     gazie to prosty przepis na zacięty pedał na iOS */
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
  border: 0; border-radius: 20px; font-family: inherit; font-weight: 800;
  color: #fff; background: rgba(20,20,45,0.7);
  backdrop-filter: blur(3px);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,0.4), 0 2px 8px rgba(0,0,0,0.3);
}
#touch-controls button.active { background: rgba(255, 84, 112, 0.75); }
.tc-left {
  position: absolute; left: max(12px, env(safe-area-inset-left));
  bottom: max(14px, env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.tc-steer { display: flex; gap: 12px; align-items: center; }
.tc-steer button { width: 100px; height: 100px; font-size: 40px; }

/* Żyroskop zamiast ◀▶: przyciski znikają, w ich miejscu pasek z kropką
   pokazującą aktualny skręt (pomaga wyczuć kalibrację i martwą strefę). */
#zyro-pasek {
  --zakres: 89px; /* maks. wychył kropki od środka = (szer − wys) / 2 */
  display: none; position: relative; width: 212px; height: 34px;
  border-radius: 17px; background: rgba(20,20,45,0.55);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,0.25);
  pointer-events: none;
}
#zyro-kulka {
  position: absolute; top: 4px; left: 50%; width: 26px; height: 26px;
  margin-left: -13px; border-radius: 50%; background: rgba(6, 214, 160, 0.9);
  transform: translateX(calc(var(--skret, 0) * var(--zakres)));
}
body.zyro .tc-steer button { display: none; }
body.zyro #zyro-pasek { display: block; }
.tc-left #btn-up, .tc-left #btn-dn { width: 212px; height: 56px; font-size: 17px; }
.tc-right {
  position: absolute; right: max(12px, env(safe-area-inset-right));
  bottom: max(14px, env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: center; gap: 12px;
}
/* Pedały jak w aucie: pionowe prostokąty, hamulec szerszy i niższy, gaz
   wyższy. Kciuk trafia z pamięci mięśniowej, a nie celuje w kółeczko. */
.tc-pedaly { display: flex; gap: 14px; align-items: flex-end; }
.tc-pedaly #btn-gas { width: 100px; height: 160px; font-size: 20px; border-radius: 18px; background: rgba(6, 214, 160, 0.55); }
.tc-pedaly #btn-gas.active { background: rgba(6, 214, 160, 0.9); }
.tc-pedaly #btn-brake { width: 112px; height: 136px; font-size: 16px; border-radius: 18px; background: rgba(255, 84, 112, 0.5); }
/* Hydrołapa leży w poprzek nad oboma pedałami — szarpie się całą dłonią. */
.tc-right #btn-handbrake {
  align-self: stretch; height: 54px; font-size: 14px; letter-spacing: 0.06em;
  border-radius: 16px; background: rgba(255, 209, 102, 0.55); color: #1b2030;
}
.tc-right #btn-handbrake.active { background: rgba(255, 209, 102, 0.95); }

/* Długie, kadr w tył i czat: środek dołu, między kierownicą a gazem.
   Szerokość liczy się z podpisu (min-width + padding), a nie z kwadratu —
   ikona bez słowa nie tłumaczy się sama, a `title` na telefonie nie wyskoczy. */
.tc-akcje {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: max(22px, env(safe-area-inset-bottom));
  display: flex; gap: 10px;
}
.tc-akcje button {
  min-width: 56px; height: 56px; padding: 0 12px; font-size: 12px;
  letter-spacing: 0.03em; white-space: nowrap; border-radius: 16px;
}
#btn-dlugie.active { background: rgba(255, 241, 194, 0.9); color: #1b2030; }
#btn-tyl.active { background: rgba(17, 138, 178, 0.85); }

#player-list .pl:not(.me) { cursor: pointer; }
#player-list .pl:not(.me):hover { background: rgba(17, 138, 178, 0.12); }
#player-list .rek-nick-czas { margin-left: 6px; font-size: 12px; color: #8a93a5; }
.wp-rek { margin-top: 8px; font-size: 13px; color: #445; }
.wp-rek-nowy { color: #1b9e4b; font-weight: 700; }

/* Pedały urosły w górę (hydrołapa + gaz ≈ 226px), obrotomierz ucieka wyżej. */
body.touch #race-hud { right: 8px; bottom: 240px; }
body.touch #tach { width: 130px; height: 130px; }

/* Kompakt: telefon, niski ekran albo wąskie okno. Nie zależy od klasy `touch`,
   bo emulacja i tablety potrafią pokazać sterowanie, a nie dostać tej klasy. */
@media (max-width: 1100px), (max-height: 620px), (pointer: coarse) {
  #top-buttons { gap: 6px; }
  #wallet { height: 32px; padding: 0 8px; font-size: 12px; }
  #top-buttons > button { height: 34px; font-size: 16px; border-radius: 17px; }
  #menu-btn { padding: 0 12px; font-size: 12px; gap: 5px; }

  /* Lobby: pigułka. Listę graczy otwierasz tapnięciem. */
  #lobby-toggle { display: flex; }
  #lobby-szczegoly { display: none; }
  #lobby-panel.otwarte #lobby-szczegoly { display: block; }
  #lobby-panel {
    top: max(6px, env(safe-area-inset-top));
    left: 50%; transform: translateX(-50%);
    width: auto; max-width: min(48vw, 220px);
    padding: 4px 10px; border-radius: 999px;
  }
  #lobby-panel.otwarte { border-radius: 14px; padding: 8px 10px; }
  body.ma-minimape #lobby-panel {
    left: 50%; transform: translateX(-50%);
  }

  /* Minimapa zostaje. Schowana jest tylko lista celów — tap w pasek ją rozwija. */
  #minimapa {
    top: max(6px, env(safe-area-inset-top)) !important;
    bottom: auto !important;
    left: max(8px, env(safe-area-inset-left)) !important;
  }
  #minimapa-cel {
    top: max(116px, calc(env(safe-area-inset-top) + 110px)) !important;
    left: max(8px, env(safe-area-inset-left)) !important;
    bottom: auto !important;
    width: auto !important;
    max-width: 38vw;
    height: 26px !important;
    padding: 0 8px !important;
  }
  #minimapa-spis {
    top: max(146px, calc(env(safe-area-inset-top) + 140px)) !important;
    bottom: auto !important;
    left: max(8px, env(safe-area-inset-left)) !important;
  }
  #lobby-panel h2 { font-size: 13px; margin: 0; }
  #lobby-panel .hint { display: none; }
  #lobby-panel ul { max-height: 28vh; margin: 6px 0 4px; }
  #lobby-panel li { font-size: 12px; padding: 2px 4px; }
  #lobby-panel p { font-size: 11px; margin-bottom: 4px; }

  /* Log był tu kiedyś schowany w całości — przez co czat na telefonie był
     głuchy: dało się pisać, ale nie dało się niczego przeczytać. Zostaje
     widoczny, tylko ciaśniejszy. */
  #chat-log { max-width: 38vw; }
  #chat-log .msg { font-size: 12px; padding: 3px 8px; }
  #chat-box { bottom: max(8px, env(safe-area-inset-bottom)); width: min(92vw, 420px); }

  body:not(.w-wyscigu) #tach { display: none; }
  body:not(.w-wyscigu) #speed-chip {
    display: flex; align-items: baseline; gap: 5px;
    padding: 4px 10px; border-radius: 999px;
    background: rgba(20,20,45,0.62); color: #fff;
    font-weight: 800; font-size: 12px; pointer-events: none;
  }
  body:not(.w-wyscigu) #race-hud {
    left: auto; bottom: auto;
    top: max(88px, calc(env(safe-area-inset-top) + 76px));
    right: max(8px, env(safe-area-inset-right));
  }
  body.w-wyscigu #speed-chip { display: none; }
  body.w-wyscigu #race-hud { left: auto; top: auto; right: 6px; bottom: 200px; }

  /* Telefon: środek góry zajmuje pasek postępu i zwinięty panel lobby, lewy
     górny róg minimapa — wyśrodkowany licznik okrążeń chował się za nimi.
     Idzie więc pod rząd przycisków po prawej, gdzie w wyścigu nic nie stoi
     (obrotomierz zjeżdża wtedy na dół, patrz reguła wyżej). */
  #okrazenia {
    top: max(64px, calc(env(safe-area-inset-top) + 52px));
    right: max(8px, env(safe-area-inset-right));
    left: auto;
    transform: none;
  }
  body.w-wyscigu #tach { width: 96px; height: 96px; }

  .tc-steer { gap: 8px; }
  .tc-steer button { width: 72px; height: 72px; font-size: 30px; border-radius: 18px; }
  #zyro-pasek { width: 152px; height: 26px; --zakres: 63px; }
  #zyro-kulka { top: 3px; width: 20px; height: 20px; margin-left: -10px; }
  .tc-left #btn-up, .tc-left #btn-dn { width: 152px; height: 44px; font-size: 13px; }
  .tc-left, .tc-right { gap: 8px; }
  .tc-pedaly { gap: 10px; }
  .tc-pedaly #btn-gas { width: 80px; height: 128px; font-size: 16px; }
  .tc-pedaly #btn-brake { width: 88px; height: 108px; font-size: 13px; }
  .tc-right #btn-handbrake { height: 42px; font-size: 11px; }
  .tc-akcje { bottom: max(10px, env(safe-area-inset-bottom)); gap: 6px; }
  /* 44 px to dolna granica celu, który trafia się kciukiem bez patrzenia —
     niżej nie schodzimy nawet na najciaśniejszym telefonie. */
  .tc-akcje button {
    min-width: 44px; height: 44px; padding: 0 9px; font-size: 10px; border-radius: 12px;
  }

  #join .card > .hint { display: none; }
  body.touch .komis-akcja { padding: 8px 14px; font-size: 14px; bottom: 18px; }
}

/* --- customizacja (krok 2 joina + modal w lobby) --- */
.ghost {
  background: rgba(17, 138, 178, 0.1); color: #118ab2;
  border: 2px solid #118ab2; border-radius: 16px;
  font-family: inherit; font-weight: 800; cursor: pointer;
  padding: 12px 14px; font-size: 18px;
}
.ghost.wide { width: 100%; margin-top: 10px; }
.join-row { display: flex; gap: 10px; align-items: stretch; margin-top: 4px; }
.join-row .big { flex: 1; }
.join-row #join-back { flex: 0 0 auto; width: 58px; font-size: 22px; }
#join-welcome #wb-look { margin-bottom: 16px; font-size: 15px; color: #118ab2; }

#pwa-install { font-size: 15px; padding: 10px 12px; margin-top: 12px; }
#pwa-jak {
  text-align: left; font-size: 13px; color: #555; line-height: 1.4;
  margin-top: 8px;
}
#pwa-jak p { margin: 6px 0; }
@media (max-height: 560px) {
  #pwa-install { padding: 7px 10px; font-size: 12px; margin-top: 8px; }
  #pwa-jak { font-size: 12px; }
}

/* --- webview (Messenger itp.) --- */
/* nad wszystkimi kartami (.overlay ma 30): ekran logowania pod spodem nie może
   przykryć ostrzeżenia, bo logowanie w webview zapisuje konto w złym miejscu */
#webview-overlay { z-index: 60; }
#wv-jak { text-align: left; font-size: 14px; color: #555; line-height: 1.5; margin: 0 0 14px; }
#wv-otworz { font-size: 19px; margin-bottom: 10px; }
/* adres jest w input (nie w <p>), żeby po zablokowanym schowku dało się go
   zaznaczyć i skopiować ręcznie */
#wv-adres { font-size: 13px; padding: 8px 12px; margin: 10px 0 4px; color: #555; }
#wv-mimo {
  background: none; border: 0; color: #aaa; font-family: inherit;
  font-size: 13px; text-decoration: underline; cursor: pointer; margin-top: 10px;
}
@media (max-height: 560px) {
  #wv-jak { font-size: 12px; margin-bottom: 8px; }
  #wv-otworz { font-size: 15px; padding: 9px; margin-bottom: 6px; }
  #wv-kopiuj { margin-top: 6px; padding: 8px 10px; font-size: 14px; }
}

.card.wide { max-width: min(96vw, 720px); }
#wallet {
  display: flex; align-items: center; padding: 0 14px; height: 46px;
  border-radius: 23px; background: rgba(20,20,45,0.6); color: #ffd166;
  font-weight: 800; font-size: 15px; white-space: nowrap;
}

/* --- logowanie --- */
.auth-tabs { display: flex; gap: 6px; margin-bottom: 10px; }
.auth-tab {
  flex: 1 1 0; padding: 9px 4px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 12px; letter-spacing: 0.5px;
  color: #667; background: #fff; border: 2px solid #ddd6e2; border-radius: 14px;
}
.auth-tab.sel { border-color: #118ab2; background: #e2f4fa; color: #0d6f8f; }
.auth-hint { margin: -4px 0 10px; font-size: 12px; line-height: 1.35; }
#join-pin { letter-spacing: 4px; }
#wb-logout { margin-top: 6px; font-size: 13px; opacity: 0.75; }

/* --- wybór auta ---
   Kilkadziesiąt pozycji, więc lista przewija się we własnym pudełku zamiast
   rozpychać kartę na całą wysokość ekranu (na telefonie w poziomie mamy
   naprawdę mało miejsca w pionie). */
.car-picker { text-align: left; margin: 6px 0 14px; }
.car-list {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 40vh; overflow-y: auto; padding-right: 4px;
  -webkit-overflow-scrolling: touch;
}
.car-card {
  display: grid; grid-template-columns: 1fr auto; gap: 0 8px;
  padding: 8px 10px; cursor: pointer; text-align: left;
  font-family: inherit; color: #445;
  background: #fff; border: 2px solid #ddd6e2; border-radius: 14px;
  transition: transform 0.06s;
}
.car-card:active { transform: translateY(2px); }
.car-card.sel { border-color: #ff5470; background: #ffe1e8; color: #c93a56; }
.car-card b { font-size: 14px; font-weight: 800; }
.car-card .gen {
  grid-column: 1; font-size: 11px; font-weight: 700;
  color: #118ab2; letter-spacing: 0.5px;
}
.car-card.sel .gen { color: #c93a56; }
.car-card .stats { grid-column: 1; font-size: 11px; opacity: 0.75; }
.car-card .price {
  grid-column: 2; grid-row: 1 / span 3; align-self: center;
  font-size: 13px; font-weight: 800; white-space: nowrap;
}
.cust-preview { width: 100%; margin-top: 8px; font-size: 15px; padding: 10px; }

/* --- obróć telefon --- */
#rotate-overlay { display: none; }
@media (orientation: portrait) and (pointer: coarse) {
  #rotate-overlay {
    display: flex; position: fixed; inset: 0; z-index: 100;
    align-items: center; justify-content: center; text-align: center;
    background: #1e143c; color: #ffd166;
    font-size: 30px; font-weight: 800; line-height: 1.6;
  }
}

/* === GARAŻ I GIEŁDA ===========================================================
   Obie panele mieszczą się w `.card`. Zasada z wyboru auta obowiązuje tu tym
   bardziej: każda długa lista przewija się we WŁASNYM pudełku o wysokości
   liczonej w vh, bo na telefonie w poziomie miejsca w pionie jest naprawdę
   mało, a rozpychanie karty wypycha przyciski poza ekran. */

/* --- garaż --- */
.garage { text-align: left; }
.gar-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-bottom: 10px;
}
.gar-balance {
  flex: 1; font-size: 18px; font-weight: 800; color: #118ab2;
}
.gar-plot-note {
  flex: 1 1 100%; margin: 0; font-size: 13px; color: #666; font-weight: 600;
}
.gar-shop-btn { padding: 8px 12px; font-size: 14px; }

.gar-cars {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 26vh; overflow-y: auto; padding-right: 4px;
  -webkit-overflow-scrolling: touch;
}
.gar-car {
  display: grid; grid-template-columns: 1fr auto; gap: 0 8px;
  padding: 8px 10px; cursor: pointer; text-align: left;
  font-family: inherit; color: #445;
  background: #fff; border: 2px solid #ddd6e2; border-radius: 14px;
  transition: transform 0.06s;
}
.gar-car:active { transform: translateY(2px); }
.gar-car.sel { border-color: #118ab2; background: #e2f4fa; color: #0d6f8f; }
.gar-car.active { border-color: #ff5470; }
.gar-car.active.sel { background: #ffe1e8; color: #c93a56; }
.gar-car b { font-size: 14px; font-weight: 800; }
.gar-car .gen {
  grid-column: 1; font-size: 11px; font-weight: 700;
  color: #118ab2; letter-spacing: 0.5px;
}
.gar-car.active.sel .gen { color: #c93a56; }
.gar-car .stats { grid-column: 1; font-size: 11px; opacity: 0.75; }
.gar-modcount {
  grid-column: 2; grid-row: 1; align-self: center;
  font-size: 12px; font-weight: 800; color: #118ab2; white-space: nowrap;
}
.gar-badge {
  grid-column: 2; grid-row: 2 / span 2; align-self: center;
  font-size: 10px; font-weight: 800; letter-spacing: 0.5px;
  color: #fff; background: #ff5470; border-radius: 8px;
  padding: 3px 6px; white-space: nowrap;
}
.gar-empty { font-size: 13px; color: #888; padding: 10px 2px; }

.gar-top { margin: 12px 0 8px; }
.gar-top h3 { font-size: 20px; font-weight: 800; color: #445; }
.gar-sub { display: block; font-size: 12px; color: #667; margin-top: 2px; }
.gar-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.gar-actions .ghost { padding: 8px 12px; font-size: 14px; }
.gar-sell {
  padding: 8px 12px; font-size: 14px; cursor: pointer;
  font-family: inherit; font-weight: 800;
  color: #ff5470; background: rgba(255, 84, 112, 0.1);
  border: 2px solid #ff5470; border-radius: 16px;
}
.gar-sell.armed { color: #fff; background: #ff5470; }

/* Serwis obok sprzedaży: zielony, bo to wydatek NA auto, nie pozbycie się go.
   Wyłączony (brak pieniędzy) zostaje widoczny — cena serwisu to informacja
   potrzebna do planowania, a nie tylko przycisk. */
.gar-serwis {
  padding: 8px 12px; font-size: 14px; cursor: pointer;
  font-family: inherit; font-weight: 800;
  color: #1d9a6c; background: rgba(29, 154, 108, 0.1);
  border: 2px solid #1d9a6c; border-radius: 16px;
}
.gar-serwis.armed { color: #fff; background: #1d9a6c; }
.gar-serwis:disabled { opacity: 0.55; cursor: default; }

/* Ostrzeżenie o wytrzymałości. Żółte, nie czerwone: to nie jest błąd, tylko
   świadomy wybór gracza — jazda na kredyt jest dozwolona i ma być kusząca. */
.gar-warn {
  margin-top: 10px; padding: 8px 10px;
  background: #fff6de; border: 2px solid #ffd166; border-radius: 12px;
  font-size: 12px; color: #6b5514; line-height: 1.45;
}
.gar-warn b { display: block; margin-bottom: 4px; }
.gar-warn ul { list-style: none; }
.gar-warn li { margin-top: 3px; }
.gar-warn li::before { content: '• '; }

/* --- katalog modyfikacji --- */
.gar-mods {
  max-height: 40vh; overflow-y: auto; margin-top: 12px; padding-right: 4px;
  -webkit-overflow-scrolling: touch;
}
/* Informacja o zawężeniu katalogu do auta. Przygaszona — to przypis, a nie
   ostrzeżenie; ma tylko powiedzieć, że reszta części czeka na inne auta. */
.gar-hidden {
  font-size: 11px; line-height: 1.45; color: #7a8290;
  background: #f4f6f9; border-radius: 10px;
  padding: 6px 8px; margin-bottom: 10px;
}
/* Zakładki katalogu części — ten sam kształt co w Rekordach, żeby pasek
   zakładek wyglądał w całej grze tak samo. */
.mod-taby {
  display: flex; gap: 5px; flex-wrap: wrap;
  margin: 0 0 10px; padding-bottom: 8px; border-bottom: 2px solid #eef1f6;
}
.mod-tab {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 11px; border: 2px solid #dde3ec; border-radius: 999px;
  background: #fff; font-size: 12px; font-weight: 800; color: #667;
  cursor: pointer; white-space: nowrap;
}
.mod-tab.sel { border-color: #118ab2; background: #e2f4fa; color: #0d6f8f; }
.mod-tab-ile {
  min-width: 16px; padding: 0 4px; border-radius: 999px;
  background: #06d6a0; color: #fff; font-size: 10px; font-weight: 800;
  text-align: center; line-height: 16px;
}

.mod-cat { margin-bottom: 12px; }
.mod-cat h4 {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 12px; font-weight: 800; letter-spacing: 1px;
  text-transform: uppercase; color: #118ab2;
  padding-bottom: 4px; margin-bottom: 6px;
  border-bottom: 2px solid #e6eef2;
}
.mod-cat-count { font-size: 10px; font-weight: 700; color: #06d6a0; letter-spacing: 0; }

.mod-row {
  background: #fff; border: 2px solid #ddd6e2; border-radius: 14px;
  margin-bottom: 6px; overflow: hidden;
}
.mod-row.on { border-color: #06d6a0; background: #eafaf5; }
.mod-row.open { border-color: #118ab2; }
/* Niepasujące zostają na liście, tylko przygaszone — gracz ma widzieć, że
   dyfer 4.44 istnieje, i przeczytać, czego mu do niego brakuje. */
.mod-row.off { opacity: 0.55; background: #f7f7fa; }

.mod-head {
  display: grid; grid-template-columns: 1fr auto; gap: 0 8px;
  width: 100%; padding: 8px 10px; text-align: left; cursor: pointer;
  font-family: inherit; color: #445; background: none; border: 0;
}
.mod-name { font-size: 14px; font-weight: 800; }
.mod-price {
  grid-column: 2; grid-row: 1; align-self: start;
  font-size: 13px; font-weight: 800; white-space: nowrap; color: #118ab2;
}
.mod-row.on .mod-price { color: #06d6a0; }
.mod-desc { grid-column: 1 / -1; font-size: 11px; line-height: 1.4; opacity: 0.8; margin-top: 2px; }
.mod-why {
  grid-column: 1 / -1; font-size: 11px; font-weight: 700;
  color: #c93a56; margin-top: 4px;
}
.mod-need {
  display: inline; padding: 0; margin: 0; border: 0;
  background: none; font: inherit; font-weight: 800;
  color: #c93a56; text-decoration: underline; text-underline-offset: 2px;
  cursor: pointer;
}
.mod-need:hover { color: #8e1e36; }

.mod-preview { padding: 0 10px 10px; }
.mod-delta {
  background: #f4f8fb; border-radius: 10px; padding: 6px 8px; margin-bottom: 8px;
  font-size: 12px;
}
.delta-row {
  display: grid; grid-template-columns: 62px 1fr 14px 1fr auto;
  align-items: baseline; gap: 4px; padding: 2px 0;
}
.delta-info { padding: 2px 0; color: #667; line-height: 1.4; }
.delta-name { font-weight: 800; color: #667; }
.delta-before { opacity: 0.65; }
.delta-arrow { opacity: 0.5; text-align: center; }
.delta-after { font-weight: 800; }
.delta-diff { font-weight: 800; white-space: nowrap; }
.delta-diff.up { color: #06d6a0; }
.delta-diff.down { color: #ff5470; }
.mod-gain { font-size: 11px; color: #0d6f8f; margin-bottom: 8px; }

.mod-buy {
  width: 100%; padding: 10px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 14px;
  color: #fff; background: #06d6a0; border: 0; border-radius: 14px;
}
.mod-buy:disabled { background: #c9cdd6; cursor: not-allowed; }

/* --- giełda --- */
.shop { text-align: left; }
.shop-head { display: flex; align-items: center; gap: 10px; }
.shop-head h3 { flex: 1; font-size: 20px; font-weight: 800; color: #445; }
.shop-balance { font-size: 16px; font-weight: 800; color: #118ab2; white-space: nowrap; }
.shop-close { padding: 6px 12px; font-size: 16px; }
.shop-hint { font-size: 11px; color: #888; line-height: 1.4; margin: 6px 0 10px; }
.shop-empty { font-size: 13px; color: #888; padding: 10px 2px; }

.shop-list {
  display: flex; flex-direction: column; gap: 8px;
  max-height: 55vh; overflow-y: auto; padding-right: 4px;
  -webkit-overflow-scrolling: touch;
}
.offer {
  display: grid; grid-template-columns: 1fr auto; gap: 8px;
  padding: 10px; background: #fff;
  border: 2px solid #ddd6e2; border-radius: 14px;
}
/* Auto poza zasięgiem zostaje na liście z ceną — pula giełdy celowo nie jest
   dobierana do portfela, więc od pierwszej minuty widać, dokąd to prowadzi. */
.offer.locked { opacity: 0.5; background: #f7f7fa; }
.offer-main { min-width: 0; }
.offer-name { font-size: 15px; font-weight: 800; color: #445; }
.offer-gen {
  display: block; font-size: 11px; font-weight: 700;
  color: #118ab2; letter-spacing: 0.5px;
}
.offer-stats { display: block; font-size: 11px; color: #667; margin-top: 2px; }
.offer-mods { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.chip {
  font-size: 10px; font-weight: 700; padding: 3px 7px; border-radius: 999px;
  color: #0d6f8f; background: #e2f4fa;
}
.chip.risky { color: #8a5a00; background: #fff0cc; }
.offer-warn { font-size: 11px; font-weight: 700; color: #8a5a00; margin-top: 6px; line-height: 1.4; }

.offer-side {
  display: flex; flex-direction: column; align-items: flex-end;
  justify-content: center; gap: 4px; white-space: nowrap;
}
.offer-price { font-size: 17px; font-weight: 800; color: #445; }
.offer-tier { font-size: 10px; font-weight: 700; color: #999; letter-spacing: 0.5px; }
.offer-left { font-size: 11px; font-weight: 700; color: #888; }
.offer-left.soon { color: #ff5470; }
.offer-left.gone { color: #c9cdd6; text-decoration: line-through; }
.offer-buy {
  margin-top: 2px; padding: 8px 14px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 14px;
  color: #fff; background: #118ab2; border: 0; border-radius: 14px;
}
.offer-buy:disabled { background: #c9cdd6; cursor: not-allowed; font-size: 11px; padding: 8px 10px; }

/* Na niskich ekranach (telefon w poziomie) listy dostają mniej miejsca,
   żeby nagłówek i przyciski zostały widoczne bez przewijania karty. */
@media (max-height: 480px) {
  .gar-cars { max-height: 20vh; }
  .gar-mods { max-height: 34vh; }
  .shop-list { max-height: 44vh; }
}

/* === INWENTARZ CZĘŚCI I HANDEL =============================================== */

/* --- inwentarz (w garażu) --- */
.gar-inv { margin-top: 10px; }
.inv-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; cursor: pointer; text-align: left;
  font-family: inherit; font-weight: 800; font-size: 13px; color: #445;
  background: #f4f8fb; border: 2px solid #ddd6e2; border-radius: 14px;
}
.inv-title { flex: 1; }
.inv-total { color: #118ab2; }
.inv-caret { opacity: 0.5; }

.inv-list {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 30vh; overflow-y: auto; margin-top: 6px; padding-right: 4px;
  -webkit-overflow-scrolling: touch;
}
.inv-part {
  display: grid; grid-template-columns: 1fr auto; gap: 8px;
  padding: 9px 10px; background: #fff;
  border: 2px solid #ddd6e2; border-radius: 14px;
}
/* Komplet silnikowy wygląda inaczej, bo jest inny: to nie jedna część,
   tylko silnik razem z całym swoim tuningiem. */
.inv-part.engine { border-color: #ffd166; background: #fffdf6; }
.inv-main { min-width: 0; }
.inv-name { font-size: 14px; font-weight: 800; color: #445; }
.inv-sub {
  display: block; font-size: 11px; font-weight: 700;
  color: #118ab2; letter-spacing: 0.3px;
}
.inv-part.engine .inv-sub { color: #8a5a00; }
.inv-desc { display: block; font-size: 11px; color: #667; opacity: 0.8; margin-top: 2px; line-height: 1.35; }
.inv-with { display: block; font-size: 11px; font-weight: 700; color: #667; margin-top: 5px; }
.inv-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }

.inv-side {
  display: flex; flex-direction: column; align-items: flex-end;
  justify-content: center; gap: 4px; white-space: nowrap;
}
.inv-value { font-size: 15px; font-weight: 800; color: #445; }
.inv-install, .inv-sell {
  padding: 6px 10px; cursor: pointer; font-family: inherit;
  font-weight: 800; font-size: 12px; border-radius: 12px; border: 0;
}
.inv-install { color: #fff; background: #06d6a0; }
.inv-install:disabled { background: #e6e6ee; color: #999; cursor: not-allowed; font-size: 10px; }
.inv-sell { color: #ff5470; background: rgba(255, 84, 112, 0.1); border: 2px solid #ff5470; }

/* --- skutki montażu (podgląd w garażu) --- */
.mod-conseq {
  padding: 8px 10px; margin-bottom: 8px;
  background: #f4f8fb; border-radius: 10px; font-size: 11px; line-height: 1.45;
}
.conseq-title { display: block; font-size: 12px; color: #445; margin-bottom: 4px; }
.conseq-label { display: block; color: #667; }
/* Przy swapie gracz może stracić kilka drogich części naraz — to musi rzucać
   się w oczy mocniej niż reszta podglądu. */
.conseq-out { padding: 6px 8px; margin: 6px 0; background: #fff6de; border-radius: 8px; }
.conseq-out .conseq-label { color: #6b5514; font-weight: 700; }
.conseq-stay { padding: 6px 8px; margin: 6px 0; background: #eafaf5; border-radius: 8px; }
.conseq-stay .conseq-label { color: #0a7a5e; font-weight: 700; }

.mod-uninstall {
  width: 100%; padding: 9px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 13px;
  color: #118ab2; background: rgba(17, 138, 178, 0.1);
  border: 2px solid #118ab2; border-radius: 14px;
}

/* --- przełożenia regulowanej skrzyni (dogbox) --- */
/* Strojenie ma WŁASNE okno (index.html: #przelozenia-modal), więc dostaje całą
   szerokość karty i jedno przewijanie — to, które daje `.card.wide > div`.
   Wcześniej panel siedział na szczycie katalogu części i dziedziczył jego
   wąską kolumnę razem z jego scrollem: na telefonie wychodziło przewijanie
   w przewijaniu, a palec sięgający po suwak przewijał listę.
   Suwak jest NATYWNY (jak widełki ceny w handlu) — na iOS Safari własne
   przeciąganie przez pointer events kończy się utratą uchwytu przy
   przewijaniu palcem. */
.przel-okno { text-align: left; }
.przel {
  background: #fff; border: 2px solid #118ab2; border-radius: 14px;
  padding: 10px 12px 12px;
}
.przel-head { display: flex; align-items: baseline; gap: 8px; }
.przel-head b { flex: 1; font-size: 16px; font-weight: 800; color: #445; }
.przel-tag {
  font-size: 10px; font-weight: 800; letter-spacing: 0.5px; text-transform: uppercase;
  color: #118ab2; background: #e2f4fa; border-radius: 8px; padding: 2px 6px;
}
.przel-skrzynia { font-size: 12px; font-weight: 700; color: #118ab2; margin-top: 1px; }
.przel-hint { font-size: 11px; line-height: 1.4; color: #667; margin: 4px 0 8px; }
.przel-warn {
  font-size: 11.5px; line-height: 1.45; color: #6b5514;
  background: #fff6de; border: 2px solid #ffd166; border-radius: 10px;
  padding: 6px 8px; margin-bottom: 8px;
}
.przel-warn:empty { display: none; }
/* Wiersze biegów układają się w tyle kolumn, ile zmieści karta: na telefonie
   w poziomie siedem suwaków jeden pod drugim to ekran i pół przewijania. */
.przel-rows { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 2px 16px; }
/* Jeden wiersz na DWA piętra:
       [nazwa] [opis: koniec biegu, obroty po zmianie] [wartość]
       [  −  ] [            suwak               ] [  +  ]
   Przyciski dostają wtedy pełne 44 px na bok (w mniejsze palec nie trafia),
   a suwak całą szerokość kolumny zamiast skrawka między napisami. Kolumny są
   STAŁE, bo napis 2.87 → 2.9 nie może przesuwać suwaka pod palcem. */
.przel-row {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr) 46px;
  grid-template-areas:
    "nazwa info  wart"
    "minus suwak plus";
  align-items: center; gap: 0 6px; padding: 3px 0;
  border-bottom: 1px solid #eef1f5;
}
/* Przełożenie główne dotyczy WSZYSTKICH biegów naraz, więc stoi w poprzek
   pod nimi, a nie jako ósmy bieg w drugiej kolumnie. */
.przel-row.final {
  grid-column: 1 / -1;
  margin-top: 4px; border-top: 2px solid #e6eef2; padding-top: 6px; border-bottom: 0;
}
.przel-name { grid-area: nazwa; font-size: 15px; font-weight: 800; color: #667; }
.przel-row.final .przel-name { font-size: 10px; letter-spacing: 0.3px; text-transform: uppercase; }
.przel-slider {
  grid-area: suwak; width: 100%; min-width: 0; margin: 0;
  height: 40px; accent-color: #118ab2;
}
.przel-step {
  width: 46px; height: 44px; padding: 0; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 20px; line-height: 1;
  color: #118ab2; background: #e2f4fa; border: 0; border-radius: 10px;
}
.przel-step.minus { grid-area: minus; }
.przel-step.plus { grid-area: plus; }
.przel-step:active { background: #c6e8f3; }
.przel-val {
  grid-area: wart; text-align: right; font-size: 15px; font-weight: 800; color: #445;
  font-variant-numeric: tabular-nums;
}
.przel-val.changed { color: #0d6f8f; }
.przel-info { grid-area: info; font-size: 10.5px; color: #7a8290; }
.przel-est { margin-top: 8px; }
.przel-est .mod-delta { margin-bottom: 0; }
.przel-err { font-size: 11px; font-weight: 700; color: #c93a56; margin-top: 6px; min-height: 0; }
.przel-err:empty { display: none; }
.przel-ok { font-size: 11.5px; font-weight: 700; color: #0a7a5e; margin-top: 6px; }
.przel-ok:empty { display: none; }
.przel-actions { display: flex; gap: 8px; margin-top: 10px; }
.przel-save {
  flex: 1; min-height: 46px; padding: 10px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 14px;
  color: #fff; background: #118ab2; border: 0; border-radius: 12px;
}
.przel-save:disabled { background: #c9cdd6; cursor: not-allowed; }
.przel-reset { min-height: 46px; padding: 8px 14px; font-size: 14px; }
.przel-reset:disabled { opacity: 0.5; cursor: default; }
/* Wejście do strojenia: przy aucie, nie w katalogu części — przełożeń się nie
   kupuje. Widać go tylko z regulowaną skrzynią (dogbox). */
.gar-przel-btn {
  padding: 8px 12px; font-size: 14px; cursor: pointer;
  font-family: inherit; font-weight: 800;
  color: #0d6f8f; background: #e2f4fa;
  border: 2px solid #118ab2; border-radius: 16px;
}

/* --- handel --- */
.trade { text-align: left; }
.trade-head { display: flex; align-items: center; gap: 10px; }
.trade-head h3 { flex: 1; font-size: 20px; font-weight: 800; color: #445; }
.trade-balance { font-size: 16px; font-weight: 800; color: #118ab2; white-space: nowrap; }
.trade-close { padding: 6px 12px; font-size: 16px; }
.trade-hint { font-size: 11px; color: #888; line-height: 1.4; margin: 6px 0; }

/* Blokada handlu. Żółta jak ostrzeżenia, nie czerwona: to nie jest błąd gracza,
   tylko zabezpieczenie, które sam odblokuje jeżdżąc. */
.trade-lock {
  padding: 12px; margin: 8px 0 12px;
  background: #fff6de; border: 2px solid #ffd166; border-radius: 14px;
  color: #6b5514;
}
.trade-lock b { display: block; font-size: 15px; margin-bottom: 6px; }
.trade-lock p { font-size: 12px; line-height: 1.45; }
.trade-lock-why { margin-top: 8px; opacity: 0.85; }
.trade-progress {
  height: 8px; margin: 10px 0 4px; border-radius: 999px;
  background: rgba(107, 85, 20, 0.15); overflow: hidden;
}
.trade-progress-fill { height: 100%; background: #ffd166; border-radius: 999px; }
.trade-progress-label { font-size: 11px; font-weight: 800; }

.trade-sell { margin: 8px 0 12px; }
.trade-sell h4 {
  font-size: 12px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase;
  color: #118ab2; padding-bottom: 4px; margin-bottom: 6px; border-bottom: 2px solid #e6eef2;
}
.trade-picker {
  display: flex; gap: 6px; overflow-x: auto; padding-bottom: 4px;
  -webkit-overflow-scrolling: touch;
}
.trade-pick {
  flex: 0 0 auto; padding: 7px 10px; cursor: pointer; text-align: left;
  font-family: inherit; color: #445;
  background: #fff; border: 2px solid #ddd6e2; border-radius: 12px;
}
.trade-pick.sel { border-color: #118ab2; background: #e2f4fa; color: #0d6f8f; }
.trade-pick b { display: block; font-size: 13px; font-weight: 800; }
.trade-pick-sub { font-size: 10px; opacity: 0.75; }

.trade-form { margin-top: 10px; }
.trade-quote { display: block; font-size: 11px; color: #667; margin-bottom: 6px; }
.trade-price-line { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.trade-price-val { font-size: 20px; font-weight: 800; color: #445; }
.trade-net { font-size: 11px; font-weight: 700; color: #8a5a00; }
.trade-slider { width: 100%; margin: 8px 0 10px; accent-color: #118ab2; }
.trade-submit {
  width: 100%; padding: 10px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 14px;
  color: #fff; background: #118ab2; border: 0; border-radius: 14px;
}

.trade-list {
  display: flex; flex-direction: column; gap: 8px;
  max-height: 42vh; overflow-y: auto; padding-right: 4px;
  -webkit-overflow-scrolling: touch;
}
.trade-offer.mine { border-color: #118ab2; background: #f4fbfe; }
.trade-seller { display: block; font-size: 11px; font-weight: 700; color: #118ab2; }
.trade-offer.mine .trade-seller { color: #0d6f8f; }
.trade-vs { font-size: 10px; font-weight: 800; color: #999; letter-spacing: 0.3px; }
.trade-vs.good { color: #06d6a0; }
.trade-vs.bad { color: #ff5470; }
.trade-cancel {
  margin-top: 2px; padding: 8px 14px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 13px;
  color: #ff5470; background: rgba(255, 84, 112, 0.1);
  border: 2px solid #ff5470; border-radius: 14px;
}

@media (max-height: 480px) {
  .inv-list { max-height: 24vh; }
  .trade-list { max-height: 34vh; }
}

/* --- minimapa (public/js/minimap.js) ---------------------------------------
   Samo płótno minimapy tworzy i pozycjonuje JS, żeby moduł dało się wpiąć bez
   ruszania index.html. Tutaj zostaje tylko to, czego JS zrobić nie może:
   odsunięcie elementów, którym minimapa weszłaby na głowę. Klasę
   `ma-minimape` moduł dokłada do <body> przy tworzeniu. */

/* Desktop: minimapa siedzi w lewym dolnym rogu, dokładnie na logu czatu.
   Nad kołem mapy jest jeszcze pasek z celem nawigacji, stąd te 268 px. */
body.ma-minimape:not(.touch) #chat-log { bottom: 268px; }

/* Dotyk: dół po lewej zajmuje kierownica, więc minimapa idzie w lewy górny
   róg — a stamtąd musi się usunąć panel lobby. Odsuwamy go o szerokość paska
   celu (150 px), bo ten jest szerszy niż samo koło mapy. */
body.touch.ma-minimape #lobby-panel { left: 172px; }

/* --- komis i tryb pieszy (public/js/onfoot.js) -------------------------------
   Cały HTML tych ekranów tworzy JS (tak samo jak minimapa i przycisk mikrofonu),
   więc index.html o nich nie wie. Tutaj zostaje sam wygląd. */

/* Podpowiedź „podejdź i naciśnij". Siedzi na dole na środku, czyli w jedynym
   pasie ekranu, którego nie zajmuje ani kierownica, ani gaz — na telefonie
   kciuk trafia w nią bez przesuwania dłoni. */
.komis-akcja {
  position: fixed; z-index: 24; left: 50%; transform: translateX(-50%);
  bottom: 84px;
  display: flex; align-items: center; gap: 10px;
  padding: 12px 22px; border-radius: 999px; cursor: pointer;
  background: rgba(20, 20, 45, 0.82); color: #fff;
  font-family: inherit; font-weight: 800; font-size: 16px;
  border: 2px solid rgba(255, 213, 102, 0.9);
  box-shadow: 0 6px 18px rgba(30, 20, 60, 0.45);
  touch-action: manipulation;
  animation: komis-puls 1.6s ease-in-out infinite;
}
.komis-akcja:active { transform: translateX(-50%) translateY(2px); }
.komis-akcja-ikona { font-size: 20px; }
.komis-akcja-klawisz {
  font-family: inherit; font-size: 12px; font-weight: 800;
  padding: 3px 9px; border-radius: 8px;
  background: rgba(255, 213, 102, 0.9); color: #3a2b00;
}
@keyframes komis-puls {
  0%, 100% { box-shadow: 0 6px 18px rgba(30, 20, 60, 0.45); }
  50% { box-shadow: 0 6px 26px rgba(255, 213, 102, 0.55); }
}
/* Na dotyku klawisza nie ma czym nacisnąć, a dół ekranu jest wolny niżej. */
body.touch .komis-akcja { bottom: 26px; font-size: 18px; padding: 14px 26px; }
body.touch .komis-akcja-klawisz { display: none; }

/* Pieszy na telefonie dostaje joystick zamiast klastra przycisków od auta,
   więc cały zestaw kierowcy na czas spaceru znika. */
body.pieszo #touch-controls { display: none; }

/* Przycisk akcji na dotyku wędruje w prawy dolny róg, poza kółko joysticka
   po lewej — inaczej sięgając po „obejrzyj auto" ruszałoby się gałką. */
body.touch.pieszo .komis-akcja {
  left: auto; transform: none;
  right: max(18px, env(safe-area-inset-right));
  bottom: 104px;
}
body.touch.pieszo .komis-akcja:active { transform: translateY(2px); }

/* Karta oglądanego auta. */
.komis-naglowek { display: flex; align-items: center; gap: 10px; }
.komis-naglowek h2 { flex: 1; text-align: left; margin: 0; font-size: 26px; }
.komis-x { padding: 6px 12px; font-size: 15px; flex-shrink: 0; }
.komis-podpis {
  text-align: left; font-size: 12px; font-weight: 700; color: #118ab2;
  letter-spacing: 0.4px; margin: 2px 0 12px;
}
.komis-parametry {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 14px; text-align: left;
}
.komis-param {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px; padding: 6px 10px; border-radius: 10px; background: #f4f6fa;
}
.komis-param-k { font-size: 12px; color: #777; }
.komis-param-v { font-size: 15px; color: #1b2130; }
/* Stan i przebieg to jedyne liczby, po których widać, że auto było męczone. */
.komis-param.slaby { background: #fff0cc; }
.komis-param.slaby .komis-param-v { color: #8a5a00; }
/* Dopisek „ty: …" na karcie cudzego auta — porównanie z własnym.
   Zieleń = Twoja wartość lepsza, czerwień = gorsza, szarość = remis. */
.komis-moje { font-size: 11px; font-weight: 400; color: #8a8f9c; white-space: nowrap; }
.komis-moje.lepiej { color: #1d7a3d; }
.komis-moje.gorzej { color: #b23a3a; }
.komis-blurb {
  text-align: left; font-size: 13px; color: #555; line-height: 1.45;
  margin-top: 10px; font-style: italic;
}
.komis-stopka {
  display: flex; align-items: center; gap: 14px; margin-top: 14px;
}
.komis-cena { font-size: 26px; font-weight: 900; color: #c41f2b; white-space: nowrap; }
.komis-kup { flex: 1; width: auto; font-size: 20px; }

/* Telefon w poziomie: parametry w jednej kolumnie zjadały całą wysokość. */
@media (max-height: 560px) {
  .komis-naglowek h2 { font-size: 20px; }
  .komis-parametry { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px 8px; }
  .komis-param { padding: 4px 8px; }
  .komis-param-v { font-size: 13px; }
  .komis-blurb { display: none; }
  .komis-cena { font-size: 20px; }
  .komis-kup { font-size: 16px; }
}


/* --- joystick pieszego (public/js/touch-stick.js) ---------------------------
   Cały ekran łapie palec od kamery. Gałka stoi na stałe w lewym dolnym
   rogu (jak strzałki kierowcy). Warstwa jest NIŻEJ niż przycisk akcji
   i panele, więc te przechwytują dotyk pierwsze. */
.joy { position: fixed; inset: 0; z-index: 12; pointer-events: none; }
.joy-strefa { pointer-events: auto; touch-action: none; }
.joy-kamera { position: absolute; inset: 0; }
.joy-ruch {
  position: absolute; z-index: 1;
  left: max(12px, env(safe-area-inset-left));
  bottom: max(16px, env(safe-area-inset-bottom));
  width: 156px; height: 156px;
}

.joy-gniazdo, .joy-galka { position: absolute; pointer-events: none; border-radius: 50%; }
.joy-gniazdo {
  inset: 0;
  border: 2px solid rgba(255, 255, 255, 0.38);
  background: rgba(20, 20, 45, 0.32);
}
.joy-galka {
  left: 50%; top: 50%;
  width: 62px; height: 62px;
  transform: translate(-50%, -50%);
  background: rgba(255, 255, 255, 0.34);
  border: 2px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 3px 12px rgba(30, 20, 60, 0.4);
}
.joy-galka-srodek {
  position: absolute; inset: 19px;
  border-radius: 50%; background: rgba(255, 213, 102, 0.9);
}


/* --- zgłoszenia od graczy (public/js/ui/feedback.js) ------------------------
   Panel siedzi w modalu `.card.wide`, tak samo jak handel i lakiernia, więc
   dziedziczy z nich tło i marginesy — poniżej tylko to, co własne. */
.zgl { text-align: left; }
/* Wyjaśnienie „po co to jest". Stoi NAD polem tekstowym i nad nagrywaniem,
   bo ma być przeczytane, zanim gracz zacznie pisać. */
.zgl-po-co { margin: 6px 0 12px; }
.zgl-po-co-lead { font-size: 14px; color: #445; line-height: 1.45; font-weight: 600; }
.zgl-po-co-naglowek {
  margin-top: 10px;
  font-size: 12px; font-weight: 800; letter-spacing: 0.6px; text-transform: uppercase;
  color: #118ab2;
}
.zgl-po-co-lista { list-style: none; margin-top: 5px; }
.zgl-po-co-lista li {
  display: flex; gap: 8px; align-items: baseline;
  padding: 2px 0; font-size: 13px; color: #556; line-height: 1.4;
}
/* Stała szerokość ikony: bez niej teksty ustawiają się schodkami, bo emoji
   mają różne szerokości. */
.zgl-po-co-ikona { flex: 0 0 20px; font-size: 14px; }
/* Skrócona wersja listy powyżej — pokazuje się dopiero na niskim ekranie. */
.zgl-po-co-krotko { display: none; }

.zgl-po-co-glos {
  margin-top: 9px; padding: 8px 10px;
  font-size: 13px; color: #4a3c8c; line-height: 1.45;
  background: #f0edfb; border-radius: 12px;
}

/* Jedyna rzecz w tym oknie, która dzieje się bez pytania gracza o zgodę —
   więc wyróżniona paskiem z boku, a nie schowana drobnym drukiem. */
.zgl-po-co-dane {
  margin-top: 8px; padding: 8px 10px;
  font-size: 12px; color: #6b5a2a; line-height: 1.5;
  background: #fff7e0; border-left: 4px solid #ffd166; border-radius: 0 12px 12px 0;
}
.zgl-po-co-dane b { color: #8a5a00; }

.zgl-pole {
  width: 100%; padding: 10px 12px; resize: vertical; min-height: 84px;
  font-family: inherit; font-size: 15px; color: #1b2130; line-height: 1.4;
  background: #fff; border: 2px solid #ddd6e2; border-radius: 14px;
  /* touch-action wraca do normy: pole tekstowe musi dać się przewinąć palcem,
     a `touch-action: none` z html/body dziedziczy się na całą stronę. */
  touch-action: manipulation;
}
.zgl-pole:focus { outline: none; border-color: #118ab2; }
.zgl-licznik-tekstu { display: block; font-size: 10px; color: #99a; text-align: right; margin-top: 2px; }
.zgl-licznik-tekstu.blisko { color: #c46b00; font-weight: 800; }

.zgl-nagrywanie {
  margin: 10px 0; padding: 10px 12px;
  background: #f6f4fb; border: 2px solid #e6e0f0; border-radius: 16px;
}
.zgl-nagraj {
  width: 100%; padding: 11px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 15px;
  color: #fff; background: #6c5ce7; border: 0; border-radius: 14px;
}
/* W trakcie nagrywania przycisk zmienia kolor na czerwony i pulsuje — z drugiego
   końca pokoju ma być widać, że mikrofon jest włączony. */
.zgl-nagraj.nagrywa { background: #ff5470; animation: zgl-puls 1.1s ease-in-out infinite; }
@keyframes zgl-puls { 50% { opacity: 0.62; } }
@media (prefers-reduced-motion: reduce) { .zgl-nagraj.nagrywa { animation: none; } }

.zgl-stan { font-size: 11px; color: #778; line-height: 1.4; margin-top: 7px; }
.zgl-czas {
  display: block; margin-top: 4px;
  font-size: 15px; font-weight: 800; color: #445;
  font-variant-numeric: tabular-nums;
}
/* Ostatnie dziesięć sekund. Ucięcie bez uprzedzenia wygląda jak awaria, więc
   licznik musi zdążyć zwrócić na siebie uwagę. */
.zgl-czas.koniec { color: #ff5470; }

.zgl-podglad { width: 100%; margin-top: 8px; }
.zgl-kasuj { display: block; width: 100%; margin-top: 6px; font-size: 12px; padding: 7px; }

.zgl-co { margin: 8px 0 12px; font-size: 12px; color: #667; }
.zgl-co summary { cursor: pointer; font-weight: 700; color: #118ab2; }
.zgl-spis { list-style: none; margin-top: 8px; }
.zgl-spis li {
  display: flex; gap: 10px; justify-content: space-between;
  padding: 3px 0; border-bottom: 1px solid #eceaf2;
}
.zgl-spis-k { color: #889; }
.zgl-spis-v { color: #1b2130; font-weight: 700; text-align: right; word-break: break-word; }
.zgl-spis-uwaga {
  display: block; margin-top: 8px; border: 0;
  font-size: 11px; font-style: italic; color: #889;
}

.zgl-wyslij {
  width: 100%; padding: 12px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: 16px;
  color: #fff; background: #118ab2; border: 0; border-radius: 14px;
}
.zgl-wyslij:disabled { background: #c6ccd4; cursor: default; }

.zgl-komunikat { min-height: 18px; margin-top: 8px; font-size: 13px; font-weight: 700; }
.zgl-komunikat.ok { color: #06976f; }
.zgl-komunikat.blad { color: #ff5470; }
.zgl-komunikat.info { color: #8a5a00; }

/* Telefon w poziomie: karta ma wtedy ~300 px wysokości, a pole tekstowe
   i odtwarzacz zjadały ją w całości razem z przyciskiem wysyłki. */
@media (max-height: 560px) {
  /* Telefon w poziomie ma około 340 px na całą kartę, a samo wyjaśnienie zajęłoby
     ją w całości — pole tekstowe wypadłoby pod krawędź i gracz musiałby przewijać,
     żeby zobaczyć, że w ogóle jest gdzie pisać. Zostają więc DWIE rzeczy, których
     nie wolno zgubić: „to idzie do autora" i „lecą dane techniczne". Lista czterech
     powodów zamienia się w jedno zdanie, a zachęta do nagrywania znika, bo dwie
     linijki niżej stoi przycisk „🎙️ nagraj głosem" i mówi dokładnie to samo. */
  .zgl-po-co { margin: 4px 0 8px; }
  .zgl-po-co-lead { font-size: 12px; }
  .zgl-po-co-naglowek, .zgl-po-co-lista, .zgl-po-co-glos { display: none; }
  .zgl-po-co-krotko {
    display: block; margin-top: 5px;
    font-size: 11px; color: #556; line-height: 1.4;
  }
  .zgl-po-co-dane { margin-top: 6px; padding: 6px 8px; font-size: 11px; }
  /* Nagłówek panelu zmniejszamy, ale NIE chowamy: `.trade-head` to flexbox,
     w którym `h3` jest rozpychaczem odsuwającym ✕ do prawej krawędzi.
     Ukryty nagłówek przenosił krzyżyk na lewo, pod tytuł modala. */
  .zgl .trade-head h3 { font-size: 15px; }
  /* `height`, nie samo `min-height`: pole ma `rows="4"`, a atrybut wygrywa
     z minimalną wysokością i zostawiał 108 px zamiast obiecanych 54.
     `resize: vertical` działa dalej, więc gracz i tak może je rozciągnąć. */
  .zgl-pole { min-height: 0; height: 56px; }
  .zgl-nagrywanie { margin: 6px 0; padding: 7px 9px; }
  .zgl-stan { display: none; }
  .zgl-podglad { height: 34px; }
  .zgl-co { margin: 6px 0 8px; }
  .zgl-komunikat { min-height: 0; }
}

/* --- karta wyniku przejazdu (ui/wynik-przejazdu.js) -------------------------
   Pokazywana ZARAZ PO MECIE, kiedy gracz jeszcze jedzie. Stąd układ: góra
   ekranu (w kadrze zza auta jest tam niebo, nie asfalt), wąska kolumna,
   `pointer-events: none` na całości — jedynym klikalnym elementem jest krzyżyk,
   żeby karta nie mogła przechwycić sterowania dotykowego. */
#konfetti { position: fixed; inset: 0; }

.wp {
  position: fixed; z-index: 22; pointer-events: none;
  top: max(34px, calc(env(safe-area-inset-top) + 30px));
  left: 50%; transform: translateX(-50%);
  width: min(92vw, 420px);
  padding: 10px 14px 12px;
  background: rgba(20, 20, 45, 0.82);
  border: 1px solid rgba(255, 209, 102, 0.35);
  border-radius: 16px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  color: #fff; font-size: 14px; line-height: 1.35;
}
.wp.wp-wygrana { border-color: rgba(62, 242, 107, 0.75); }
.wp.wp-dnf { border-color: rgba(255, 84, 112, 0.55); }

/* Wjazd karty: zjeżdża spod górnej krawędzi. Krótko (280 ms), bo to element
   gry, a nie okno dialogowe — ma się pojawić, a nie prezentować. */
@keyframes wp-wjazd {
  from { opacity: 0; transform: translate(-50%, -18px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}
.wp.wp-wjazd { animation: wp-wjazd 280ms cubic-bezier(0.2, 0.9, 0.3, 1); }

.wp-head { display: flex; align-items: center; gap: 8px; }
.wp-tytul { font-weight: 900; letter-spacing: 0.06em; font-size: 15px; color: #ffd166; }
.wp.wp-wygrana .wp-tytul { color: #3ef26b; }
.wp.wp-dnf .wp-tytul { color: #ff8fa3; }
.wp-miejsce { margin-left: auto; font-weight: 800; font-size: 15px; }
.wp-x {
  pointer-events: auto; touch-action: manipulation;
  margin-left: 4px; padding: 2px 7px; border: 0; border-radius: 8px;
  background: rgba(255, 255, 255, 0.12); color: #fff; font-size: 13px; cursor: pointer;
}

/* Czas mety: jedyna liczba, którą widać z odległości metra od ekranu.
   `tabular-nums` jest tu obowiązkowe — bez niego dobijające cyfry mają różne
   szerokości i cały napis drga w poziomie, co przy jeździe wygląda jak usterka. */
.wp-czas {
  font-size: 40px; font-weight: 900; line-height: 1.05; margin: 2px 0 6px;
  font-variant-numeric: tabular-nums; text-shadow: 0 3px 0 rgba(30, 20, 60, 0.55);
}
.wp-czas .wp-jedn { font-size: 20px; font-weight: 700; opacity: 0.65; }

.wp-wiersze { display: flex; flex-direction: column; gap: 3px; }
.wp-w {
  display: flex; gap: 10px; align-items: baseline;
  opacity: 0; transform: translateX(-10px);
  transition: opacity 220ms, transform 220ms;
}
.wp-w.wp-widoczny { opacity: 1; transform: none; }
.wp-l { color: rgba(255, 255, 255, 0.62); font-size: 13px; }
.wp-v { margin-left: auto; font-weight: 800; font-variant-numeric: tabular-nums; }

.wp-stopka { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.wp-kasa {
  padding-top: 7px; border-top: 1px solid rgba(255, 255, 255, 0.14);
  color: #3ef26b; font-size: 15px; font-variant-numeric: tabular-nums;
}
.wp-kasa.wp-bez-kasy { color: #ffb4c0; }
.wp-powod { color: rgba(255, 255, 255, 0.6); font-size: 12px; font-weight: 400; }

.wp-lista { display: flex; flex-direction: column; gap: 2px; font-size: 12px; }
.wp-lw { display: flex; gap: 8px; color: rgba(255, 255, 255, 0.72); }
.wp-lw.wp-ja { color: #ffd166; font-weight: 800; }
.wp-lp { width: 20px; }
.wp-ln { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wp-lt { margin-left: auto; font-variant-numeric: tabular-nums; }
.wp-usterka { color: #ffb4c0; font-size: 12px; }

/* Telefon w poziomie ma ~340 px wysokości: pełna karta zjadłaby połowę widoku
   drogi. Zostają duży czas i wiersze, ale wszystko o klasę mniejsze. */
@media (max-height: 560px) {
  .wp { top: max(28px, env(safe-area-inset-top)); width: min(64vw, 340px); padding: 7px 11px 9px; }
  .wp-czas { font-size: 28px; margin: 0 0 3px; }
  .wp-czas .wp-jedn { font-size: 15px; }
  .wp-w, .wp-l, .wp-v { font-size: 12px; }
  .wp-lista { display: none; }   /* klasyfikacja jest miła, ale nie kosztem drogi */
}

/* Ograniczenie ruchu w systemie: liczby dalej dobijają (to treść, nie ozdoba),
   ale karta i wiersze pojawiają się bez przesuwania. */
@media (prefers-reduced-motion: reduce) {
  .wp.wp-wjazd { animation: none; }
  .wp-w { transform: none; }
}

/* ---------------------------------------------------------------------------
   Tablica rekordów (ui/rekordy.js).
   Liczby czasu ustawiamy w tabelarycznych cyfrach (`tabular-nums`) — bez tego
   przecinki w kolumnie „12,431" i „9,872" nie stoją w jednej linii i tablica
   przestaje się czytać jednym rzutem oka, a po to właśnie się ją otwiera.
   --------------------------------------------------------------------------- */
.rek { text-align: left; }
.rek-head { display: flex; align-items: center; gap: 10px; }
.rek-head h3 { flex: 1; font-size: 20px; font-weight: 800; color: #445; }
.rek-licznik { font-size: 12px; color: #8a93a5; white-space: nowrap; }
.rek-close { padding: 6px 12px; font-size: 16px; }

.rek-taby { display: flex; gap: 6px; margin: 10px 0 8px; flex-wrap: wrap; }
.rek-tab {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 11px; border: 2px solid #dde3ec; border-radius: 999px;
  background: #fff; font-size: 12px; font-weight: 800; color: #667;
  cursor: pointer; white-space: nowrap;
}
.rek-tab-ikona { font-size: 13px; line-height: 1; }
.rek-tab.sel { border-color: #118ab2; background: #e2f4fa; color: #0d6f8f; }

.rek-hint { font-size: 11px; color: #888; line-height: 1.45; margin: 2px 0 10px; }
.rek-sekcja { margin-bottom: 14px; }
.rek-nag { display: flex; align-items: baseline; gap: 8px; margin-bottom: 5px; }
.rek-nag h4 { font-size: 14px; font-weight: 800; color: #445; }
.rek-pod { font-size: 11px; color: #99a; }
.rek-pusto {
  font-size: 12px; color: #8a93a5; line-height: 1.5;
  background: #f4f6fa; border-radius: 10px; padding: 10px 12px;
}

.rek-model { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
.rek-model b { font-size: 16px; color: #223; }

.rek-lista { display: flex; flex-direction: column; gap: 3px; }
.rek-w {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 9px; border-radius: 8px; background: #f4f6fa;
  font-size: 13px; color: #445;
}
/* Własny wiersz musi dać się znaleźć bez czytania — po to gracz tu wchodzi. */
.rek-w.rek-ja { background: #fff3d0; color: #6b4d00; font-weight: 800; }
/* Wynik spoza pierwszej dziesiątki dopisany pod listą: ta sama forma, ale
   oderwany kreską, żeby nie udawał, że gracz jest jedenasty. */
.rek-w.rek-poza { margin-top: 6px; border-top: 2px dashed #e2e6ee; border-radius: 8px; }
.rek-poz { width: 24px; flex: none; font-weight: 800; color: #99a; }
.rek-nick { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rek-auto {
  flex: none; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 11px; color: #8a93a5;
}
.rek-auto.rek-tuning::after { content: ' 🔧'; }
.rek-czas { flex: none; font-variant-numeric: tabular-nums; font-weight: 800; }
.rek-strata {
  flex: none; width: 62px; text-align: right;
  font-variant-numeric: tabular-nums; font-size: 11px; color: #a0a7b4;
}

/* Ogłoszenie pobitego rekordu — pasek na górze ekranu, nad grą.
   Nie łapie kliknięć: pojawia się w trakcie jazdy i nie może zablokować
   sterowania (ten sam powód, co przy karcie wyniku po przejeździe). */
.rek-ogl {
  position: fixed; left: 50%; transform: translateX(-50%);
  top: max(60px, calc(env(safe-area-inset-top) + 56px));
  z-index: 40; pointer-events: none;
  display: flex; align-items: center; gap: 10px;
  max-width: min(92vw, 560px); padding: 9px 16px;
  border-radius: 999px; background: rgba(17, 20, 28, 0.9);
  color: #fff; font-size: 14px; font-weight: 800;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
}
.rek-ogl.rek-ogl-tor { background: linear-gradient(90deg, #b8860b, #ffd166); color: #2b2000; }
.rek-ogl-ikona { font-size: 20px; }
.rek-ogl-tekst { line-height: 1.3; }
.rek-ogl-wjazd { animation: rek-wjazd 340ms cubic-bezier(0.2, 0.9, 0.3, 1.3); }
@keyframes rek-wjazd {
  from { opacity: 0; transform: translate(-50%, -18px) scale(0.94); }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

/* Błysk flesza foto pułapki — cały ekran, gdy to WŁASNE zdjęcie trafiło na
   tablicę. Bez kliknięć i nad wszystkim: to jedna klatka światła, nie okno. */
#foto-blysk {
  position: fixed; inset: 0; z-index: 60;
  background: #fff; opacity: 0; pointer-events: none;
}
#foto-blysk.blysk { animation: foto-blysk 550ms ease-out; }
@keyframes foto-blysk {
  from { opacity: 0.9; }
  to   { opacity: 0; }
}

/* Telefon w poziomie: pasek zjeżdża wyżej i chudnie, żeby nie zasłaniać drogi. */
@media (max-height: 560px) {
  .rek-ogl { top: max(30px, env(safe-area-inset-top)); font-size: 12px; padding: 6px 12px; }
  .rek-ogl-ikona { font-size: 15px; }
}
@media (prefers-reduced-motion: reduce) {
  .rek-ogl-wjazd { animation: none; }
}

/* === KOMPAKT MODALI NA TELEFONIE =============================================
   Garaż (i pozostałe karty `.card.wide`) na telefonie w poziomie: wyśrodkowana
   karta z marginesami marnowała po kilkadziesiąt pikseli z każdej strony,
   a jednokolumnowy układ z listami po 26–40 vh pokazywał trzy wiersze naraz.
   Karta idzie na cały ekran, a garaż w dwie kolumny: auta po lewej, inwentarz
   i katalog po prawej. Progi te same, co `kompaktowyModal` w main.js —
   tam od nich zależy dławienie renderu 3D pod otwartą kartą. */

/* Długie listy (katalog części, giełda, inwentarz): wiersze poza ekranem nie
   liczą layoutu ani malowania — przy kilkudziesięciu pozycjach to większość
   kosztu przewijania. Przeglądarki bez content-visibility po prostu pominą. */
.mod-row, .offer, .inv-part {
  content-visibility: auto;
  contain-intrinsic-size: auto 60px;
}

/* Rozmycie tła pod modalem: nad żywym canvasem WebGL blur kosztuje pełną
   rekompozycję w każdej klatce — na telefonie to widoczne zacięcia przy
   każdym przewinięciu listy. Ciemniejsze tło bez filtra wygląda niemal tak
   samo i nie kosztuje nic. */
@media (pointer: coarse) {
  .overlay { backdrop-filter: none; background: rgba(24, 16, 44, 0.66); }
}

@media (max-height: 620px), (pointer: coarse) and (max-width: 1024px) {
  /* Karta zakrywa cały ekran, więc rozmyte tło i tak nie jest widoczne —
     a kompozytor liczyłby blur mimo to, w każdej klatce. */
  .overlay { backdrop-filter: none; }
  /* Karta na cały ekran. Flex w pionie: nagłówek i ZAMKNIJ stoją, środek
     (mount) dostaje całą resztę wysokości i w razie czego sam się przewija. */
  .overlay .card.wide {
    width: 100%; max-width: none; max-height: none;
    height: 100vh; height: 100dvh;
    border-radius: 0; display: flex; flex-direction: column; gap: 6px;
    padding: 8px max(12px, env(safe-area-inset-right))
             max(8px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
  }
  .overlay .card.wide h2 { font-size: 16px; margin-bottom: 0; flex: none; }
  .overlay .card.wide > div { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
  .overlay .card.wide button.big {
    flex: none; padding: 8px; font-size: 15px; border-radius: 12px;
    box-shadow: 0 3px 0 #c93a56;
  }

  /* --- garaż w dwóch kolumnach --- */
  /* Lewa kolumna: lista aut, a POD nią nagłówek oglądanego auta (nazwa, dane,
     sprzedaż, ostrzeżenia) — czyli wszystko, co nie jest katalogiem. Prawa:
     pasek inwentarza i katalog części na CAŁĄ resztę wysokości. Wcześniej
     nagłówek auta jechał w scrollu razem z katalogiem i zjadał pół ekranu,
     zanim gracz dojechał do pierwszej części. */
  .garage {
    height: 100%;
    display: grid;
    grid-template-columns: minmax(146px, 200px) minmax(0, 1fr);
    grid-template-rows: auto auto minmax(0, 1fr) auto;
    gap: 4px 10px;
  }
  .gar-head { grid-area: 1 / 1 / 2 / 3; margin-bottom: 0; gap: 6px; }
  .gar-balance { font-size: 13px; flex: none; }
  .gar-plot-note { flex: 1 1 auto; font-size: 10px; }
  .gar-shop-btn { padding: 5px 9px; font-size: 12px; border-radius: 12px; }

  .gar-cars {
    grid-area: 2 / 1 / 4 / 2;
    max-height: none; min-height: 0; overscroll-behavior: contain;
  }
  .gar-inv { grid-area: 2 / 2 / 3 / 3; margin-top: 0; }
  /* `display: contents`: sam `.gar-detail` znika z układu, a jego dzieci
     (nagłówek auta i katalog) stają się elementami siatki — dzięki temu DOM
     zostaje w spokoju, a nagłówek ląduje w innej kolumnie niż katalog. */
  .gar-detail { display: contents; }
  .gar-detail > .gar-top {
    grid-area: 4 / 1 / 5 / 2;
    margin: 0; min-height: 0; max-height: 42vh; overflow-y: auto;
    -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  }
  .gar-detail > .gar-mods {
    grid-area: 3 / 2 / 5 / 3;
    max-height: none; min-height: 0; margin-top: 0;
    overscroll-behavior: contain;
  }
  /* Komunikaty bez auta („wybierz auto z listy") idą tam, gdzie stałby katalog. */
  .gar-detail > .gar-empty { grid-area: 3 / 2 / 5 / 3; }

  /* --- gęstsze wiersze --- */
  .gar-car { padding: 5px 8px; border-radius: 10px; }
  .gar-car b { font-size: 12px; }
  .gar-car .gen, .gar-car .stats { font-size: 10px; }
  .gar-modcount { font-size: 10px; }
  .gar-badge { font-size: 9px; padding: 2px 5px; }

  .gar-top h3 { font-size: 14px; }
  .gar-sub { font-size: 10px; }
  /* W wąskiej kolumnie przyciski jeden pod drugim na całą szerokość — łatwiej
     trafić kciukiem, a długie „Sprzedaj za …" nie łamie się w pionową drabinkę. */
  .gar-actions { margin-top: 6px; gap: 4px; flex-direction: column; align-items: stretch; }
  .gar-actions .ghost, .gar-sell, .gar-serwis { padding: 6px 8px; font-size: 11.5px; border-radius: 10px; }
  /* Wejście w przełożenia zostaje większe od reszty przycisków przy aucie:
     to jedyne wejście do strojenia i ma się w nie trafiać kciukiem za
     pierwszym razem. */
  .gar-przel-btn { min-height: 42px; padding: 6px 8px; font-size: 12px; border-radius: 10px; }
  .gar-warn { margin-top: 6px; padding: 6px 8px; font-size: 10px; }

  /* Okno przełożeń: gęściej, ale suwak i przyciski −/+ zostają w rozmiarze
     na kciuk — to jedyne, w co gracz tu naprawdę trafia palcem, a telefon
     w poziomie ma ledwie kilkaset pikseli wysokości. Reszta oszczędności
     idzie z napisów i odstępów, nigdy z celów dotyku. */
  .przel { padding: 8px 10px 10px; border-radius: 10px; }
  .przel-head b { font-size: 14px; }
  .przel-skrzynia { font-size: 11px; }
  .przel-hint { font-size: 10px; margin: 3px 0 6px; }
  .przel-warn { font-size: 10.5px; padding: 5px 7px; margin-bottom: 6px; }
  .przel-row { padding: 2px 0; }
  .przel-info { font-size: 9.5px; }
  .przel-name { font-size: 13px; }
  .przel-val { font-size: 13.5px; }
  .przel-est { margin-top: 6px; }
  .przel-actions { margin-top: 8px; }

  .mod-taby { gap: 4px; margin-bottom: 7px; padding-bottom: 6px; }
  .mod-tab { padding: 5px 9px; font-size: 11px; }
  .mod-cat { margin-bottom: 6px; }
  .mod-cat h4 { font-size: 10.5px; padding-bottom: 3px; margin-bottom: 3px; }
  .mod-row { border-radius: 8px; margin-bottom: 3px; }
  .mod-head { padding: 4px 8px; }
  .mod-name { font-size: 12.5px; }
  .mod-price { font-size: 11.5px; }
  .mod-desc { font-size: 10px; margin-top: 1px; }
  .mod-why { font-size: 10px; margin-top: 1px; }
  /* Zwinięty wiersz to JEDNA linijka: nazwa i cena. Opis pokazuje się dopiero
     po rozwinięciu, a powód niedostępności zwija się do jednej linii z „…" —
     z pełnymi opisami katalog był ścianą tekstu, w której mieściły się trzy
     pozycje na ekran, a i tak nikt nie czyta opisów WSZYSTKICH części naraz. */
  .mod-row:not(.open) .mod-desc { display: none; }
  .mod-row:not(.open) .mod-why {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    min-width: 0; max-width: 100%;
  }
  .mod-row, .offer, .inv-part { contain-intrinsic-size: auto 28px; }
  .mod-preview { padding: 0 8px 6px; }
  .mod-delta { font-size: 11px; padding: 4px 7px; margin-bottom: 4px; }
  .delta-row { grid-template-columns: 52px 1fr 12px 1fr auto; }
  .mod-gain { font-size: 10px; margin-bottom: 4px; }
  .mod-conseq { padding: 5px 8px; margin-bottom: 4px; font-size: 10px; }
  /* Neutralne „gniazdo wolne — nic nie wyleci" znika: brak ostrzeżenia mówi
     to samo, a linijka mniej w podglądzie to linijka więcej katalogu. Zostaje
     wszystko, co OSTRZEGA (zajęte gniazdo, skutki swapu — mają .conseq-title). */
  .mod-conseq:not(:has(.conseq-title)) { display: none; }
  .mod-buy, .mod-uninstall { padding: 7px; font-size: 12px; border-radius: 10px; }
  /* Przypis o zawężeniu katalogu w jednej linii — pełna wersja to trzy linijki
     na samym szczycie listy, czyli w najcenniejszym miejscu ekranu. */
  .gar-hidden {
    font-size: 9.5px; padding: 3px 7px; margin-bottom: 4px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }

  .inv-head { padding: 5px 8px; font-size: 11px; border-radius: 10px; }
  .inv-list { max-height: 32vh; }
  .inv-part { padding: 6px 8px; }
  .inv-name { font-size: 12.5px; }
  .inv-sub, .inv-desc, .inv-with { font-size: 10px; }
  .inv-value { font-size: 12.5px; }
  .inv-install, .inv-sell { padding: 5px 8px; font-size: 11px; }

  /* Giełda i handel dziedziczą pełnoekranową kartę; listom wolno urosnąć. */
  .shop-list { max-height: none; }
  .trade-list { max-height: none; }
}

/* --- co nowego (MOTD po zalogowaniu) ---------------------------------------
   Karta jak reszta okien, ale lista wpisów wyrównana do lewej — czyta się ją
   jak listę, nie jak plakat. Data względna w stałej kolumnie, żeby teksty
   wpisów zaczynały się w jednym pionie. */
.motd-card { max-width: min(94vw, 560px); }
#motd-hej { margin-bottom: 10px; }
#motd-list {
  list-style: none; text-align: left;
  margin: 0 0 16px; max-height: 48vh; max-height: 48dvh; overflow-y: auto;
  overscroll-behavior: contain;
}
#motd-list li {
  display: flex; gap: 10px; align-items: baseline;
  padding: 7px 2px; border-bottom: 2px dashed #ffd7e2;
}
#motd-list li:last-child { border-bottom: 0; }
.motd-data { flex: none; width: 96px; font-size: 12px; font-weight: 800; color: #118ab2; }
.motd-tekst { font-size: 14px; color: #444; }

/* Telefon w poziomie: mało wysokości — karta rośnie wszerz, lista się zagęszcza,
   przycisk ma zostać na ekranie bez przewijania całej karty. */
@media (max-height: 560px) {
  .motd-card { max-width: min(96vw, 640px); }
  #motd-list { max-height: 42dvh; }
  #motd-list li { padding: 4px 2px; }
  .motd-data { width: 84px; font-size: 11px; }
  .motd-tekst { font-size: 12.5px; }
}

/* --- policja ---------------------------------------------------------------
   Plansza „STÓJ" u zatrzymywanego: musi być nie do przeoczenia w trakcie
   jazdy, ale nie może blokować sterowania — pointer-events: none, gracz
   dalej prowadzi (i sam decyduje, czy stanąć, czy uciekać). */
#kontrola {
  position: fixed; top: 20vh; left: 0; right: 0; z-index: 26;
  text-align: center; pointer-events: none;
}
.kontrola-tresc {
  display: inline-block; padding: 14px 26px; border-radius: 18px;
  font-size: min(6.5vw, 40px); font-weight: 900; color: #fff;
  background: rgba(12, 36, 92, 0.88); border: 4px solid #fff;
  animation: kontrola-puls 0.6s steps(2, jump-none) infinite;
}
#kontrola-kto {
  margin-top: 10px; font-size: min(4vw, 20px); font-weight: 700; color: #fff;
  text-shadow: 0 2px 0 rgba(30, 20, 60, 0.6);
}
/* mruganie jak koguty: niebieski/czerwony, skokowo, bez płynnego przejścia */
@keyframes kontrola-puls {
  0% { background: rgba(12, 36, 92, 0.88); }
  100% { background: rgba(150, 22, 28, 0.88); }
}

/* panel służbowy: lista kierowców z akcjami przy każdym wierszu */
#policja-mount { max-height: 52vh; max-height: 52dvh; overflow-y: auto; overscroll-behavior: contain; }
.policja-wiersz {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 8px 2px; border-bottom: 2px dashed #cfd8ea;
}
.policja-wiersz:last-child { border-bottom: 0; }
.policja-nick { font-weight: 800; font-size: 16px; color: #223; }
.policja-dyst { font-size: 12px; font-weight: 700; color: #667; flex: 1; }
.policja-wiersz button {
  border: 0; border-radius: 999px; cursor: pointer; font-weight: 800;
  padding: 7px 12px; font-size: 13px; background: #e8edf6; color: #123;
  -webkit-user-select: none; user-select: none; touch-action: manipulation;
}
.policja-wiersz button.stoj { background: #c62828; color: #fff; }
.policja-wiersz button.mandat { background: #12408f; color: #fff; }
.policja-mandat-form {
  flex-basis: 100%; display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  padding: 6px 0 2px;
}
.policja-mandat-form input {
  border: 2px solid #cfd8ea; border-radius: 10px; padding: 7px 10px;
  font-size: 14px; font-weight: 700;
}
.policja-mandat-form input.kwota { width: 88px; }
.policja-mandat-form input.powod { flex: 1; min-width: 140px; }
.policja-pusto { padding: 14px 4px; font-size: 15px; color: #667; font-weight: 700; }

/* --- odznaki i licencje ---------------------------------------------------
   Panel czyta się na telefonie: jedna kolumna, cele dotykowe 44 px, każda
   pozycja z nazwą I zdaniem opisu (żadnych samych ikon — patrz nagłówek
   ui/odznaki.js). Od 700 px sekcje same się rozsuwają na dwie kolumny. */
.odz { text-align: left; }
.odz-head { display: flex; align-items: center; gap: 10px; }
.odz-head h3 { flex: 1; font-size: 20px; font-weight: 800; color: #445; }
.odz-close { padding: 6px 12px; font-size: 16px; min-width: 44px; min-height: 44px; }
.odz-tresc {
  display: flex; flex-direction: column; gap: 14px;
  max-height: 62vh; overflow-y: auto; padding: 8px 4px 2px 0;
  -webkit-overflow-scrolling: touch;
}

/* Odpowiedź na pytanie, z którym gracz tu wchodzi — więc na samej górze
   i wyraźnie inna od reszty. */
.odz-cel {
  background: linear-gradient(135deg, #123a5c, #1d6a8f);
  color: #fff; border-radius: 16px; padding: 14px 16px;
}
.odz-cel h4 {
  font-size: 11px; font-weight: 800; letter-spacing: 1.2px;
  text-transform: uppercase; color: #9fd8ee; margin-bottom: 6px;
}
.odz-cel-zdanie { font-size: 17px; font-weight: 800; line-height: 1.35; }
.odz-cel-pod { font-size: 12px; color: #cfe8f4; line-height: 1.45; margin-top: 8px; }

.odz-pasek { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.odz-pasek-rynna {
  flex: 1; height: 10px; border-radius: 999px; background: rgba(255, 255, 255, 0.22);
  overflow: hidden;
}
.odz-pasek-fill { height: 100%; border-radius: 999px; background: #ffd166; }
/* Liczby OBOK paska, nie zamiast: sam pasek jest nieczytelny dla kogoś, kto
   nie odróżnia kolorów, i nie mówi, ile dokładnie brakuje. */
.odz-pasek-liczby { font-size: 13px; font-weight: 800; white-space: nowrap; }

.odz-sek { border-top: 2px solid #eef1f6; padding-top: 12px; }
.odz-sek h4 { font-size: 15px; font-weight: 800; color: #445; }
.odz-nag { display: flex; align-items: baseline; gap: 8px; }
.odz-nag h4 { flex: 1; }
.odz-termin {
  font-size: 11px; font-weight: 800; color: #8a5a00; background: #fff0cc;
  padding: 4px 9px; border-radius: 999px; white-space: nowrap;
}
.odz-hint { font-size: 11px; color: #888; line-height: 1.45; margin: 6px 0 10px; }

.odz-wyz {
  background: #f6f8fc; border: 2px solid #e4e9f2; border-radius: 14px;
  padding: 12px; margin-bottom: 10px;
}
.odz-wyz-gora { display: flex; align-items: baseline; gap: 8px; }
.odz-wyz-nazwa { flex: 1; font-size: 16px; font-weight: 800; color: #223; }
.odz-wyz-licznik { font-size: 12px; font-weight: 800; color: #118ab2; white-space: nowrap; }
.odz-wyz-opis { font-size: 12px; color: #667; line-height: 1.45; margin-top: 4px; }
.odz-wyz-best { font-size: 12px; font-weight: 700; color: #445; margin: 8px 0 4px; }

.odz-gdzie { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; }
.odz-gdzie-etykieta { font-size: 11px; font-weight: 800; color: #8a93a5; }
.odz-chip {
  font-size: 12px; font-weight: 800; padding: 5px 10px; border-radius: 999px;
  color: #0d6f8f; background: #e2f4fa;
}

.odz-stopnie, .odz-lista { display: flex; flex-direction: column; gap: 4px; }
.odz-stopien, .odz-poz {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 9px 10px; border-radius: 10px; background: #fff;
  min-height: 44px;
}
.odz-poz { background: #f6f8fc; }
.odz-stopien.zrobione, .odz-poz.zrobione { background: #e9f7ec; }
.odz-znak { font-size: 16px; line-height: 1.3; flex: none; width: 20px; }
.odz-stopien-tekst { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.odz-stopien-tekst b { font-size: 14px; font-weight: 800; color: #223; }
/* Zdanie „co trzeba zrobić" — właściwa treść wiersza. Nigdy w `title`:
   na telefonie dymek nie istnieje. */
.odz-zadanie { font-size: 12px; color: #667; line-height: 1.45; }
.odz-brak { font-size: 12px; font-weight: 700; color: #8a5a00; }
.odz-zdobyta { font-size: 11px; font-weight: 700; color: #2f7d3b; }

@media (min-width: 700px) {
  .odz-tresc { display: grid; grid-template-columns: 1fr 1fr; align-items: start; }
  .odz-cel { grid-column: 1 / -1; }
}
@media (max-height: 480px) {
  .odz-tresc { max-height: 48vh; }
}

/* Brak licencji w ogłoszeniu giełdy: pełne zdanie w treści karty, nie sama
   kłódka na przycisku. */
.offer-blok {
  font-size: 11px; font-weight: 700; color: #8a5a00; background: #fff0cc;
  border-radius: 8px; padding: 6px 8px; margin-top: 6px; line-height: 1.4;
}

/* Tablice skoków wewnątrz panelu odznak. Wiersz niesie trzy liczby naraz
   (długość, wysokość, prędkość na krawędzi), więc na telefonie łamie się
   na dwie linie zamiast ścinać którąkolwiek z nich. */
.odz-tab { margin-bottom: 12px; }
.odz-tab-tytul { font-size: 13px; font-weight: 800; color: #445; margin-bottom: 5px; }
.odz-w {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  padding: 8px 10px; border-radius: 9px; background: #f4f6fa;
  font-size: 13px; color: #445; margin-bottom: 3px; min-height: 44px;
}
.odz-w.odz-ja { background: #fff3d0; color: #6b4d00; font-weight: 800; }
.odz-poz-nr { width: 26px; flex: none; font-weight: 800; color: #99a; }
.odz-nick { flex: 1; min-width: 70px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.odz-auto {
  flex: none; max-width: 42%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 11px; color: #8a93a5;
}
.odz-wynik { flex: none; font-weight: 800; font-variant-numeric: tabular-nums; }
.odz-wynik-pod {
  flex-basis: 100%; padding-left: 26px;
  font-size: 11px; color: #8a93a5; font-variant-numeric: tabular-nums;
/* --- panel admina ---------------------------------------------------------
   Grany kciukiem na telefonie: cele dotykowe 44 px, podpisy słowne przy każdym
   przycisku (ikona bez tekstu nie mówi na dotyku nic), a sekcja „nowy sezon"
   odcina się kolorem od reszty — ma wyglądać na to, czym jest. */
#admin-mount { max-height: 62vh; max-height: 62dvh; overflow-y: auto; overscroll-behavior: contain; }
.adm-sekcja {
  padding: 10px 0; border-bottom: 2px dashed #cfd8ea;
}
.adm-sekcja:last-child { border-bottom: 0; }
.adm-sekcja h4 { margin: 0 0 4px; font-size: 15px; color: #123; text-transform: uppercase; }
.adm-hint { margin: 2px 0 8px; font-size: 13px; font-weight: 700; color: #667; line-height: 1.35; }
.adm-rzad { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.adm-pole {
  border: 2px solid #cfd8ea; border-radius: 10px; padding: 11px 12px;
  font-size: 15px; font-weight: 700; min-height: 44px; flex: 1; min-width: 130px;
  margin: 0 0 8px; box-sizing: border-box; width: 100%;
}
.adm-rzad .adm-pole { margin-bottom: 0; }
.adm-btn {
  border: 0; border-radius: 12px; cursor: pointer; font-weight: 900;
  padding: 12px 16px; min-height: 44px; font-size: 14px;
  background: #e8edf6; color: #123; margin: 8px 6px 0 0;
  -webkit-user-select: none; user-select: none; touch-action: manipulation;
}
.adm-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.adm-glowny { background: #12408f; color: #fff; }
.adm-lekki { background: #e8edf6; color: #123; }
.adm-ostrzegawczy { background: #e07a00; color: #fff; }
.adm-grozny { background: #b3140f; color: #fff; width: 100%; font-size: 16px; }
.adm-kafle { display: flex; gap: 8px; flex-wrap: wrap; }
.adm-kafel {
  background: #f2f5fb; border-radius: 12px; padding: 8px 12px; min-width: 74px;
  display: flex; flex-direction: column; align-items: center;
}
.adm-kafel b { font-size: 18px; color: #123; }
.adm-kafel span { font-size: 11px; font-weight: 800; color: #667; text-transform: uppercase; }
.adm-konto, .adm-dziennik { margin: 6px 0; }
.adm-dziennik { max-height: 34vh; overflow-y: auto; }
.adm-w {
  display: flex; gap: 8px; justify-content: space-between; align-items: baseline;
  padding: 4px 0; border-bottom: 1px solid #eef2f8; font-size: 13px;
}
.adm-etykieta { font-weight: 700; color: #667; white-space: pre; }
.adm-wartosc { font-weight: 800; color: #123; text-align: right; word-break: break-word; }
.adm-komunikat {
  background: #12408f; color: #fff; border-radius: 10px; padding: 10px 12px;
  font-weight: 800; font-size: 14px; margin: 0 0 8px;
}
.adm-groza { background: #fff4f3; border: 2px solid #f0b8b5; border-radius: 12px; padding: 12px; margin-top: 10px; }
.adm-groza h4 { color: #b3140f; }
.adm-ok { background: #f1fbf3; border: 2px solid #a8dcb4; border-radius: 12px; padding: 12px; }
.adm-fraza { text-transform: uppercase; letter-spacing: 2px; text-align: center; }
.adm-sciezka {
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px;
  background: #123; color: #9fe6b0; padding: 10px; border-radius: 8px;
  word-break: break-all; -webkit-user-select: all; user-select: all; margin: 0 0 8px;
}
.adm-checkbox {
  display: flex; gap: 10px; align-items: flex-start; font-size: 13px;
  font-weight: 700; color: #345; padding: 8px 0; min-height: 44px;
}
.adm-checkbox input { width: 22px; height: 22px; flex: none; }

/* przełącznik sezonu w archiwum rekordów */
.rek-epoki { display: flex; gap: 6px; flex-wrap: wrap; margin: 6px 0 10px; }

/* wipe wybiórczy: zestawy, grupy checkboxów, ostrzeżenia */
.adm-zestawy { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; }
.adm-grupa {
  margin: 12px 0 4px; font-size: 11px; font-weight: 900; letter-spacing: 1px;
  color: #8a94a6; text-transform: uppercase;
}
.adm-checkbox b { display: block; font-size: 14px; color: #123; }
.adm-opis { display: block; font-size: 12px; font-weight: 600; color: #667; line-height: 1.3; }
.adm-wymuszony { opacity: 0.75; }
.adm-saldo { text-align: right; font-size: 18px; }
.adm-uwaga {
  background: #fff6e5; border-left: 4px solid #e07a00; border-radius: 6px;
  padding: 8px 10px; font-size: 13px; font-weight: 700; color: #5a3b00; margin: 6px 0;
}
.adm-alarm {
  background: #ffecea; border-left: 4px solid #b3140f; border-radius: 6px;
  padding: 8px 10px; font-size: 13px; font-weight: 800; color: #6d0f0c; margin: 6px 0;
}
