/* app-gra.css, zawartosc widoku Gra (faza 22): karty graczy, rzad akcji,
 * ekran wyniku.
 *
 * Zrodlo wygladu: MAKIETA-APP-2026-08-31.md sekcje 4.5 i 4.6 oraz jezyk
 * wizualny zatwierdzonego prototypu ekranu Start. Zaden kolor, promien ani
 * odstep nie jest tu literalem. Wszystko idzie przez var() z css/base.css,
 * bo powloka aplikacji i strony tresci czytaja te same tokeny.
 *
 * Czego ten plik NIE robi: ekonomii nagrod (faza 23, patrz D-03), zegarow
 * z limitem czasu (D-01: licznik idzie w GORE i niczym nie grozi), ani
 * pozostalych widokow (Nauka, Zadania, Profil to osobne kroki).
 *
 * ⚠️ GEOMETRIA PLANSZY. Pas nad plansza ma na desktopie STALA wysokosc
 * (--board-head z css/app-desktop.css). Karta gracza mieszka w tym pasie
 * i NIE MOZE go podwyzszyc, bo plansza jest kotwica ukladu (D-10) i pilnuje
 * tego test/e2e/planszaKotwica.spec.js. Dlatego wszystkie stany karty
 * (tura, namysl, koniec partii) zmieniaja kolor i tlo, a nigdy wysokosc.
 */

/* ================= DOMYSLKI PRZED PROGIEM 900 PX =========================
 * Elementy, ktore istnieja WYLACZNIE dla ukladu desktopowego, musza byc
 * niewidoczne zanim jakikolwiek blok `min-width` je wlaczy. Inaczej kadr
 * 390x844 przestaje byc identyczny ze stanem sprzed fali. Ten sam wzorzec, co
 * `.start-hero` w css/app-desktop.css i `.zadanie-strip` w css/app-zadania.css.
 *
 * Obie klasy maja WLASNA regule `display` w bloku desktopowym, wiec obok kazdej
 * stoi wariant `[hidden]`: w tym repozytorium atrybut `hidden` przegrywa
 * z klasa (znana pulapka).
 */
/* Dwie klasy w selektorze, a nie jedna: `.game-action { display: grid }` stoi
   nizej w tym samym pliku i przy rownej specyficznosci wygralaby kolejnoscia. */
.game-action.game-action--desktop { display: none; }
.game-action.game-action--desktop[hidden] { display: none; }
.gra-preview { display: none; }
.gra-preview[hidden] { display: none; }

/* Fala grupy A (03.09.2026). Prawa kolumna ekranu „Gra z silnikiem" i zielona
   pigulka „Zagraj" w galerii botow istnieja WYLACZNIE na desktopie, wiec
   domyslka `display: none` musi stac tutaj, przed pierwszym blokiem
   `min-width`. Kazda z tych klas dostaje w bloku desktopowym wlasna regule
   `display`, wiec obok kazdej stoi wariant `[hidden]`. */
.silnik-stake { display: none; }
.silnik-stake[hidden] { display: none; }
.silnik-why { display: none; }
.silnik-why[hidden] { display: none; }
.silnik-mini { display: none; }
.silnik-mini[hidden] { display: none; }
.bots-play { display: none; }
.bots-play[hidden] { display: none; }

/* Tekst wylacznie dla czytnika ekranu, ten sam wzorzec co .rail__expand-label. */
.gra-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ================= KARTY GRACZY (makieta 4.5, punkty 1 i 3) ================= */

.player-card {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 56px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
  transition: border-color var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease);
}

/* Karta przeciwnika i karta gracza sa podswietlone, gdy trwa ICH ruch.
   Klase `is-turn` przelacza renderCaptured() w js/ui/render.js: jedno
   zrodlo prawdy wspolne z gra lokalna. */
.player-card.is-turn {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-raised));
}

/* Awatar bierze rozmiar z .player-avatar (css/computer.css); tu tylko monogram
   gracza, ktory nie ma portretu, i dlatego dostaje inicjal z profilu. */
.player-card__mono {
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 700;
  color: var(--text-dim);
  user-select: none;
}
/* [hidden] ma ta sama specyficznosc co regula z `display`. Bez tej linii
   monogram byłby widoczny takze po stronie przeciwnika (ta sama pulapka co
   przy .thinking-dots w css/computer.css). */
.player-card__mono[hidden] { display: none; }

.player-card__id {
  display: grid;
  min-width: 0;
  line-height: 1.15;
}

.player-card .player-name {
  min-width: 0;
  overflow: hidden;
  font-size: 14px;
  font-weight: 650;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.player-card__rating {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.player-card__rating[hidden] { display: none; }

/* Zbite figury i przewaga materialna sa juz stylowane w css/panel.css.
   Tutaj tylko odsuwamy je od nazwy i nie pozwalamy im rozpychac karty. */
.player-card .captured {
  flex: 0 1 auto;
  min-width: 0;
  margin-left: auto;
  overflow: hidden;
}
.player-card .material-diff { flex: none; }

/* ---- licznik czasu (D-01) ----
   Liczy w GORE, nie ma limitu i nie ma konsekwencji. Dlatego nie zmienia
   koloru na ostrzegawczy przy zadnej wartosci i nie jest regionem `aria-live`:
   tykajacy licznik czytany na glos co sekunde bylby halasem, a nie informacja.
   Czytnik ekranu dostaje go z etykieta na zadanie. */
.player-card__clock {
  flex: none;
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  font-family: var(--font-mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.player-card__clock[hidden] { display: none; }
.player-card.is-turn .player-card__clock {
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-sunken));
  color: var(--text);
}

/* Na desktopie karta mieszka w pasie o stalej wysokosci. Wszystko sie kurczy,
   zeby plansza nie drgnela ani o piksel. */
@media (min-width: 900px) {
  .play-layout .player-card {
    min-height: var(--board-head);
    padding: 4px 8px;
  }
  .play-layout .player-card .player-avatar { width: 32px; height: 32px; }
  .play-layout .player-card .player-name { font-size: 13px; }
  .play-layout .player-card__clock { padding: 3px 8px; font-size: 12px; }
}

/* ================= RZAD AKCJI (makieta 4.5, punkt 4) ================= */

.game-actions {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2);
}

.game-action {
  appearance: none;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 5px;
  /* 44 px to minimum WCAG 2.2 AA (2.5.8); ikona nad etykieta i tak potrzebuje
     wiecej, wiec zapas jest realny, a nie deklarowany. */
  min-height: 66px;
  padding: 8px 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease);
}

.game-action__ico {
  display: grid;
  place-items: center;
  color: var(--text-dim);
  transition: color var(--dur-base) var(--ease);
}

.game-action__label {
  display: block;
  min-width: 0;
  overflow-wrap: anywhere;
}

.game-action:hover:not(:disabled) { border-color: var(--accent); }
.game-action:hover:not(:disabled) .game-action__ico { color: var(--accent); }
.game-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.game-action:disabled { opacity: .45; cursor: not-allowed; }

/* Ostatnia akcja czyta sie jako nieodwracalna. Czerwien jest tu ostrzezeniem
   przed skutkiem, a nie kara za wynik, dlatego dotyczy WYLACZNIE poddania. */
.game-action--danger { color: var(--danger); }
.game-action--danger .game-action__ico { color: var(--danger); }
.game-action--danger:hover:not(:disabled) {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, var(--bg-raised));
}
.game-action--danger:focus-visible { outline-color: var(--danger); }

/* Na telefonie rzad akcji stoi TUZ POD karta gracza, czyli na gorze panelu,
   ktory na waskim ekranie schodzi pod plansze. Na desktopie zostaje w kolumnie
   kontekstowej w kolejnosci z sekcji 3.3 makiety (notacja, akcje, pomoce). */
@media (max-width: 899px) {
  .side-panel > .game-actions { order: -1; }

  /* Kotwica planszy (paczka 4/szlif4, 03.09.2026): na Partii/Silniku/Play
     `.board-column` jest `display:flex; flex-direction:row`, wiec pasek
     oceny (`.eval-bar`, 18 px + odstep 8 px) zabieral plansze z srodka i
     przesuwal ja o 26 px w prawo wzgledem Zadania/Lekcji/Analizy, mimo
     tego samego tokenu `--board-mobile-w`. Pasek wychodzi z przeplywu i
     siedzi w marginesie po lewej — `.board-stack` zostaje jedynym
     uczestnikiem `justify-content: center`, wiec centruje sie identycznie
     jak na kazdym innym ekranie. */
  .play-layout .board-column { position: relative; }
  .play-layout .board-column > .eval-bar {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
  }
}

/* ================= EKRAN WYNIKU (makieta 4.6) ================= */

/* Puchar (wygrana), flaga (porazka) albo uscisk dloni (remis) siedzi w rogu
   portretu przeciwnika. Postac zostaje bohaterem ekranu: osiemnascie
   awatarow to caly charakter tego widoku. */
.gameover-emblem {
  position: absolute;
  right: calc(50% - 46px);
  top: 46px;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--bg-raised);
  background: var(--bg-sunken);
  color: var(--text-dim);
}
.gameover-box--win .gameover-emblem {
  background: var(--reward);
  /* --reward jest WYPELNIENIEM; tresc na nim bierze --reward-ink (css/base.css). */
  color: var(--reward-ink);
  border-color: var(--bg-raised);
}
.gameover-box--draw .gameover-emblem { color: var(--text); }

.gameover-verdict {
  margin: 0 0 var(--sp-3);
  color: var(--text-dim);
  font-size: var(--fs-meta);
  line-height: 1.45;
}

/* ---- karta statystyk: cztery wiersze (makieta 4.6, punkt 3) ---- */
.gameover-stats {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  text-align: left;
}

.gameover-stats__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
}

.gameover-stats__title {
  margin: 0;
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.gameover-stats__list { display: grid; gap: 6px; margin: 0; }

.gameover-stat {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.gameover-stat__dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--text-dim);
}
/* Paleta klasyfikacji ruchow jest standardem branzowym wspolnym z lichess
   i chess.com. Czytamy ja, nigdy nie zmieniamy (makieta, sekcja 5). */
.gameover-stat--best .gameover-stat__dot { background: var(--cls-best); }
.gameover-stat--mistake .gameover-stat__dot { background: var(--cls-mistake); }
.gameover-stat--miss .gameover-stat__dot { background: var(--cls-blunder); }
.gameover-stat--accuracy .gameover-stat__dot { background: var(--accent); }

.gameover-stat__label {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: var(--fs-meta);
  color: var(--text-dim);
}

.gameover-stat__value {
  flex: none;
  margin: 0;
  min-width: 46px;
  font-family: var(--font-mono);
  font-size: var(--fs-meta);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* Stan ladowania jest SZKIELETEM, nie kolkiem w srodku tresci (D-02).
   Wynik partii i nagroda sa znane od razu; te cztery liczby dolatuja. */
.gameover-stat__value[data-loading] {
  display: block;
  height: 12px;
  border-radius: var(--radius-xs);
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--text-dim) 16%, transparent) 25%,
    color-mix(in srgb, var(--text-dim) 30%, transparent) 50%,
    color-mix(in srgb, var(--text-dim) 16%, transparent) 75%
  );
  background-size: 240% 100%;
  animation: gra-skeleton 1.3s linear infinite;
}

@keyframes gra-skeleton {
  from { background-position: 120% 0; }
  to   { background-position: -120% 0; }
}

.gameover-stats__note {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-micro);
  line-height: 1.4;
  color: var(--text-dim);
}

/* ---- karta nagrody (makieta 4.6, punkt 4) ---- */
.gameover-reward {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  text-align: left;
}

.gameover-reward__ico {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-pill);
  /* Bursztyn jako WYPELNIENIE, tusz na nim z --reward-ink. */
  background: var(--reward);
  color: var(--reward-ink);
}

.gameover-reward__body { min-width: 0; }

.gameover-reward__value {
  display: block;
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.gameover-reward__note {
  display: block;
  margin-top: 2px;
  font-size: var(--fs-micro);
  line-height: 1.4;
  color: var(--text-dim);
}

/* Wiersze ekonomii na karcie nagrody (faza 23): etykieta po lewej, liczba po prawej. */
.gameover-reward__rows {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px var(--sp-3);
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-micro);
  line-height: 1.4;
}

.gameover-reward__rows dt { color: var(--text-dim); }

.gameover-reward__rows dd {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* Przycisk domykajacy ekran. Nie konkuruje z akcja wiodaca: jest cichy. */
.gameover-action--quiet {
  background: none;
  border-color: transparent;
  color: var(--text-dim);
  font-weight: 600;
}
.gameover-action--quiet:hover { color: var(--text); border-color: var(--border); }

.gameover-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- konfetti: POJEDYNCZY wystrzal, nigdy petla (makieta, sekcja 5) ---- */
.gameover-confetti {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
}
.gameover-confetti span {
  position: absolute;
  top: -12px;
  width: 7px;
  height: 11px;
  border-radius: 1px;
  opacity: 0;
  animation: gra-confetti 1100ms var(--ease) forwards;
}
/* Pozycja, kolor i tor kazdego kawalka sa TUTAJ, a nie w stylu elementu:
   polityka serwisu to `style-src 'self'` bez `'unsafe-inline'` (.htaccess,
   _headers), wiec styl przypisany z JavaScriptu zostalby po cichu zablokowany
   na produkcji. Liczba regul musi zgadzac sie z CONFETTI_PIECES
   w js/ui/gameOverModal.js. */
.gameover-confetti span:nth-child(1) { left: 6.0%; background: var(--accent); --dx: -10px; --dy: 170px; --rot: 180deg; animation-delay: 0ms; }
.gameover-confetti span:nth-child(2) { left: 11.5%; background: var(--reward); --dx: 22px; --dy: 230px; --rot: 217deg; animation-delay: 40ms; }
.gameover-confetti span:nth-child(3) { left: 17.0%; background: var(--accent-strong); --dx: -34px; --dy: 290px; --rot: 254deg; animation-delay: 80ms; }
.gameover-confetti span:nth-child(4) { left: 22.5%; background: var(--crown); --dx: 46px; --dy: 350px; --rot: 291deg; animation-delay: 120ms; }
.gameover-confetti span:nth-child(5) { left: 28.0%; background: var(--accent); --dx: -58px; --dy: 170px; --rot: 328deg; animation-delay: 160ms; }
.gameover-confetti span:nth-child(6) { left: 33.5%; background: var(--reward); --dx: 10px; --dy: 230px; --rot: 365deg; animation-delay: 200ms; }
.gameover-confetti span:nth-child(7) { left: 39.0%; background: var(--accent-strong); --dx: -22px; --dy: 290px; --rot: 402deg; animation-delay: 0ms; }
.gameover-confetti span:nth-child(8) { left: 44.5%; background: var(--crown); --dx: 34px; --dy: 350px; --rot: 439deg; animation-delay: 40ms; }
.gameover-confetti span:nth-child(9) { left: 50.0%; background: var(--accent); --dx: -46px; --dy: 170px; --rot: 476deg; animation-delay: 80ms; }
.gameover-confetti span:nth-child(10) { left: 55.5%; background: var(--reward); --dx: 58px; --dy: 230px; --rot: 513deg; animation-delay: 120ms; }
.gameover-confetti span:nth-child(11) { left: 61.0%; background: var(--accent-strong); --dx: -10px; --dy: 290px; --rot: 550deg; animation-delay: 160ms; }
.gameover-confetti span:nth-child(12) { left: 66.5%; background: var(--crown); --dx: 22px; --dy: 350px; --rot: 587deg; animation-delay: 200ms; }
.gameover-confetti span:nth-child(13) { left: 72.0%; background: var(--accent); --dx: -34px; --dy: 170px; --rot: 624deg; animation-delay: 0ms; }
.gameover-confetti span:nth-child(14) { left: 77.5%; background: var(--reward); --dx: 46px; --dy: 230px; --rot: 661deg; animation-delay: 40ms; }
.gameover-confetti span:nth-child(15) { left: 83.0%; background: var(--accent-strong); --dx: -58px; --dy: 290px; --rot: 698deg; animation-delay: 80ms; }
.gameover-confetti span:nth-child(16) { left: 88.5%; background: var(--crown); --dx: 10px; --dy: 350px; --rot: 735deg; animation-delay: 120ms; }

@keyframes gra-confetti {
  0%   { opacity: 1; transform: translate3d(0, 0, 0) rotate(0deg); }
  100% { opacity: 0; transform: translate3d(var(--dx, 0), var(--dy, 260px), 0) rotate(var(--rot, 320deg)); }
}

/* Ekran wyniku na telefonie zajmuje caly ekran (makieta), na desktopie zostaje
   nakladka nad plansza (sekcja 3.3: „na desktopie wynik pojawia sie w prawej
   kolumnie i jako nakladka nad plansza, nie zabiera calego widoku"). */
@media (max-width: 899px) {
  .gameover-overlay {
    position: fixed;
    z-index: 200;
    border-radius: 0;
    place-items: stretch;
  }
  .gameover-box {
    width: 100%;
    max-height: 100dvh;
    overflow-y: auto;
    border: 0;
    border-radius: 0;
    padding: var(--sp-6) var(--sp-4) var(--sp-5);
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .gameover-emblem { top: calc(var(--sp-6) + 44px); }
}

/* Na desktopie ekran wyniku jest NAKLADKA nad plansza, nie pelnym ekranem
   (makieta, sekcja 3.3). Plansza ma 640 px wysokosci, a tresc ekranu wyniku
   w rozmiarach telefonowych potrzebuje okolo 690 px, wiec przycisk wiodacy
   wychodzil poza kadr. Nakladka jest tu wiec ZWARTA: te same elementy w tej
   samej kolejnosci, mniejsze odstepy. Przewijanie zostaje jako bezpiecznik,
   a nie jako sposob dotarcia do glownej akcji. */
@media (min-width: 900px) {
  .gameover-box {
    max-height: 100%;
    overflow-y: auto;
    padding: var(--sp-4) var(--sp-4) var(--sp-3);
  }
  .gameover-avatar { width: 56px; height: 56px; margin-bottom: 6px; }
  .gameover-emblem {
    top: calc(var(--sp-4) + 32px);
    right: calc(50% - 36px);
    width: 28px;
    height: 28px;
  }
  .gameover-title { font-size: 18px; }
  .gameover-subtitle { margin-bottom: 6px; }
  .gameover-verdict { margin-bottom: var(--sp-2); }
  .gameover-quote { margin-bottom: var(--sp-2); }
  .gameover-stats { margin-bottom: var(--sp-2); padding: var(--sp-2) var(--sp-3); }
  .gameover-reward { margin-bottom: var(--sp-2); padding: var(--sp-2) var(--sp-3); }
  .gameover-actions { gap: 6px; margin-top: 0; }
  .gameover-action { padding: 8px 12px; }
}

/* ======================================================================== *
 * FALA 4 (02.09.2026): EKRAN GRA NA DESKTOPIE W SYSTEMIE ZE STARTU
 * ------------------------------------------------------------------------
 * Kontrakt: MAKIETA-DESKTOP-2026-09-02.md (K3: jeden zielony przycisk na
 * ekran; karty na `--bg-raised`, obwodka 1 px `--border`, promien
 * `--radius-lg`, liczby w Geist Mono), POWLOKA-DESKTOP-2026-09-01.md
 * (kotwica planszy x=342 y=118 w=640 przy 1440x900, pas oceny ZAWSZE
 * zarezerwowany), GRA-2026-09-01.md i DECYZJE-GRA-2026-09-01.md.
 *
 * Slownik jest ten sam, co na Starcie i na ekranie Zadanie: kolumna
 * kontekstowa 340 px z kartami od gory, etykiety w Geist Mono wersalikami,
 * zielen WYLACZNIE na postepie i na jednej akcji pierwszoplanowej.
 *
 * ⚠️ TEN BLOK NIE RUSZA GEOMETRII PLANSZY. Wysokosc pasa `--board-head`,
 * szerokosc `--eval-w` i siatka `.board-column` zostaja w css/app-desktop.css.
 * Karty graczy nadal mieszkaja w pasach o STALEJ wysokosci.
 *
 * ⚠️ PONIZEJ 900 PX NIC SIE NIE ZMIENIA. Zaden selektor z tego bloku nie
 * wychodzi poza `@media (min-width: 900px)`.
 * ======================================================================== */

@media (min-width: 900px) {
  /* Wysokosc kolumny planszy: dwa pasy + plansza + dwie przerwy. Kolumna
     kontekstowa dostaje DOKLADNIE tyle samo, wiec obie konczy sie w tym samym
     miejscu i caly ekran miesci sie w jednym kadrze 900 px. */
  .play-layout {
    --gra-col-h: calc(var(--board-size) + 2 * var(--board-head) + 2 * var(--sp-2));
  }

  /* ---- kolumna kontekstowa: karty od GORY, jak „Dzis" na Starcie ----
     `position: static` zamiast lepkiego `top: 78px` z powloki: ekran partii
     miesci sie w jednym kadrze, a lepkosc spycha kolumne nizej niz pas nad
     plansza (element lepki, ktory jeszcze nie doszedl do swojego `top`, jest
     przez nie PRZESUWANY W DOL). Ta sama poprawka co w fali 3.
     Selektor jest mocniejszy niz `.play-layout .side-panel` z powloki, zeby
     plik powloki zostal nietkniety. */
  #view-computer .play-layout > .side-panel,
  #view-silnik .play-layout > .side-panel,
  #view-play .play-layout > .side-panel,
  #view-friend .play-layout > .side-panel {
    position: static;
    gap: var(--sp-3);
    /* DOKLADNIE tyle, co kolumna planszy, i wlasne przewijanie w srodku:
       partia na 60 ruchow nie moze wypchnac akcji pod zgiecie ani zmusic
       strony do przewijania, bo wtedy plansza wyjezdza z kadru. */
    height: var(--gra-col-h);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
  }

  /* Kolejnosc z kontraktu: status, notacja, pomoce, akcje, zapowiedz nagrody,
     na koncu ciche sterowanie plansza. DOM zostaje w kolejnosci mobilnej. */
  #view-computer .play-layout > .side-panel > .status-card,
  #view-silnik .play-layout > .side-panel > .status-card { order: 1; }
  #view-computer .play-layout > .side-panel > .ranked-badge,
  #view-silnik .play-layout > .side-panel > .ranked-badge { order: 2; }
  #view-computer .play-layout > .side-panel > .moves-card,
  #view-silnik .play-layout > .side-panel > .moves-card { order: 3; }
  #view-computer .play-layout > .side-panel > .aids-card,
  #view-silnik .play-layout > .side-panel > .aids-card { order: 4; }
  #view-computer .play-layout > .side-panel > .game-actions,
  #view-silnik .play-layout > .side-panel > .game-actions { order: 5; }
  #view-computer .play-layout > .side-panel > .gra-preview { order: 6; }
  #view-computer .play-layout > .side-panel > .controls-card,
  #view-silnik .play-layout > .side-panel > .controls-card { order: 7; }

  /* Gra z silnikiem idzie za makieta partii: notacja, pomoce, siatka akcji,
     jedna linia informacyjna. Karta stanu i narzedzia schodza pod nia, bo
     w trakcie partii i tak nie maja nic do powiedzenia. */
  #view-silnik .play-layout > .side-panel > .game-quit { order: 6; }
  #view-silnik .play-layout > .side-panel > .play-ranked { order: 7; }
  #view-silnik .play-layout > .side-panel > .status-card { order: 8; }
  #view-silnik .play-layout > .side-panel > .controls-card { order: 9; }
  #view-silnik .play-layout > .side-panel > .game-quit,
  #view-silnik .play-layout > .side-panel > .play-ranked { flex: none; }

  /* ---- karta statusu ---- */
  #view-computer .play-layout .status-card,
  #view-silnik .play-layout .status-card,
  #view-play .play-layout .status-card,
  #view-friend .play-layout .status-card {
    flex: none;
    padding: var(--sp-3) var(--sp-4);
  }
  #view-computer .play-layout .status-turn,
  #view-silnik .play-layout .status-turn,
  #view-play .play-layout .status-turn,
  #view-friend .play-layout .status-turn { font-size: 15px; }

  /* Odznaka partii rankingowej czyta sie jak etykieta karty, nie jak akcja. */
  #view-computer .play-layout .ranked-badge,
  #view-silnik .play-layout .ranked-badge {
    flex: none;
    margin: 0;
    color: var(--text-dim);
    font: 700 var(--fs-label) var(--font-mono);
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
  }

  /* ---- notacja: JEDYNA karta, ktora rosnie ----
     Dwie kolumny ruchow (biale i czarne) w Geist Mono, numer w trzeciej,
     waskiej. Lista przewija sie w SWOICH granicach, wiec dluga partia nie
     wypycha akcji pod zgiecie ani nie zmusza strony do przewijania. */
  #view-computer .play-layout .moves-card,
  #view-silnik .play-layout .moves-card,
  #view-play .play-layout .moves-card,
  #view-friend .play-layout .moves-card {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    /* Po partii karte statusu zajmuje podsumowanie z lekcja i notacja bez tego
       minimum kurczyla sie do samego naglowka. */
    min-height: 172px;
    padding: var(--sp-3) var(--sp-4) var(--sp-3);
  }

  #view-computer .play-layout .moves-card .card-head h2,
  #view-silnik .play-layout .moves-card .card-head h2,
  #view-play .play-layout .moves-card .card-head h2,
  #view-friend .play-layout .moves-card .card-head h2 {
    font: 700 var(--fs-label) var(--font-mono);
    letter-spacing: var(--ls-label);
  }

  #view-computer .play-layout .move-list,
  #view-silnik .play-layout .move-list,
  #view-play .play-layout .move-list,
  #view-friend .play-layout .move-list {
    flex: 1 1 auto;
    min-height: 96px;
    max-height: none;
    align-content: start;
    font-size: var(--fs-meta);
  }

  #view-computer .play-layout .move-nav,
  #view-silnik .play-layout .move-nav,
  #view-play .play-layout .move-nav,
  #view-friend .play-layout .move-nav { flex: none; margin-top: var(--sp-2); }

  /* ---- karta pomocy do nauki ----
     Pas oceny ZOSTAJE w swojej kolumnie po lewej stronie planszy (kotwica
     ukladu), a jego liczba jest na nim samym. Powtorzenie jej w karcie byloby
     ta sama liczba dwa razy w jednym kadrze, dokladnie ten sam argument, ktory
     w fali 3 schowal odznake „Stawka: postep n / N". Ostrzezenie o bledzie
     zostaje nakladka NAD PLANSZA: jest alertem o ruchu, ktory wlasnie padl,
     i musi stac tam, gdzie patrzy gracz. */
  #view-computer .play-layout .aids-card,
  #view-silnik .play-layout .aids-card,
  #view-play .play-layout .aids-card,
  #view-friend .play-layout .aids-card {
    flex: none;
    padding: var(--sp-3) var(--sp-4);
  }
  #view-computer .play-layout .aids-card .card-head h2,
  #view-silnik .play-layout .aids-card .card-head h2,
  #view-play .play-layout .aids-card .card-head h2 {
    font: 700 var(--fs-label) var(--font-mono);
    letter-spacing: var(--ls-label);
  }
  #view-computer .play-layout .hint-note,
  #view-silnik .play-layout .hint-note,
  #view-play .play-layout .hint-note { font-size: var(--fs-body-sm); }

  /* ---- rzad czterech akcji drugorzednych ----
     Cofnij, Podpowiedz, Poddaj sie, Nowa partia. ZADEN nie jest zielony:
     jedyna akcja pierwszoplanowa partii w toku to ruch na planszy (K3),
     a zielen wraca dopiero na ekranie wyniku. Czwarty przycisk istnieje
     wylacznie tutaj; na telefonie „Nowa partia" zostaje w karcie sterowania. */
  #view-computer .play-layout .game-actions,
  #view-silnik .play-layout .game-actions {
    flex: none;
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  #view-computer .play-layout .game-action--desktop,
  #view-silnik .play-layout .game-action--desktop {
    display: grid;
    align-content: center;
    justify-items: center;
    gap: 5px;
  }
  #view-computer .play-layout .game-action--desktop[hidden],
  #view-silnik .play-layout .game-action--desktop[hidden] { display: none; }
  #view-computer .play-layout .game-action,
  #view-silnik .play-layout .game-action {
    min-height: 62px;
    padding: 8px 2px;
    font-size: 11.5px;
  }
  /* `overflow-wrap: anywhere` z ukladu mobilnego lamalo „Podpowiedz" w polowie
     slowa, bo kolumna ma tu 79 px. Etykieta ma dwa wiersze zapasu, wiec
     wszystkie cztery przyciski maja ikony na jednej wysokosci. */
  #view-computer .play-layout .game-action__label,
  #view-silnik .play-layout .game-action__label {
    overflow-wrap: normal;
    min-height: 2.4em;
  }
  #view-computer .play-layout .game-action svg,
  #view-silnik .play-layout .game-action svg { width: 20px; height: 20px; }

  /* ---- zapowiedz nagrody (ECON-02) ----
     Ta sama liczba, co na ekranie wyboru przeciwnika, wypelniana przez
     `renderGameRewardPreview(playingEl)`. Bursztyn jest kolorem nagrody
     w calym serwisie; zieleni tu nie ma. */
  #view-computer .play-layout .gra-preview {
    display: block;
    flex: none;
    margin: 0;
    padding: 7px var(--sp-3);
    border: 1px solid color-mix(in srgb, var(--reward) 34%, var(--border));
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--reward) 8%, var(--bg-raised));
    color: var(--text-dim);
    font: 700 var(--fs-micro) var(--font-mono);
    font-variant-numeric: tabular-nums;
    text-align: center;
  }
  #view-computer .play-layout .gra-preview[hidden] { display: none; }
  #view-computer .play-layout .gra-preview:empty { display: none; }

  /* ---- ciche sterowanie plansza ----
     Obrot, wspolrzedne i PGN to USTAWIENIA widoku, nie akcje partii, wiec
     traca chrom karty i schodza na dol kolumny jednym rzedem. „Nowa gra"
     z tej karty jest na desktopie schowana: ta sama akcja stoi wyzej,
     w rzedzie czterech, i nie moze byc na ekranie dwa razy. */
  #view-computer .play-layout > .side-panel > .controls-card,
  #view-silnik .play-layout > .side-panel > .controls-card {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-2);
    flex: none;
    padding: 0;
    border: 0;
    background: none;
  }
  #view-computer .play-layout > .side-panel > .controls-card > .controls-summary,
  #view-silnik .play-layout > .side-panel > .controls-card > .controls-summary,
  #view-play .play-layout > .side-panel > .controls-card > .controls-summary { grid-column: 1 / -1; }
  #view-computer .play-layout > .side-panel > .controls-card > .controls-row,
  #view-silnik .play-layout > .side-panel > .controls-card > .controls-row { display: contents; }
  #view-computer .play-layout > .side-panel > .controls-card > .controls-row[hidden],
  #view-silnik .play-layout > .side-panel > .controls-card > .controls-row[hidden] { display: none; }
  #view-computer .play-layout > .side-panel > .controls-card [data-action="new-game"],
  #view-silnik .play-layout > .side-panel > .controls-card [data-action="new-game"] { display: none; }
  #view-computer .play-layout > .side-panel > .controls-card .btn,
  #view-silnik .play-layout > .side-panel > .controls-card .btn {
    min-height: 34px;
    padding: 0 var(--sp-2);
    font-size: var(--fs-micro);
  }
  #view-computer .play-layout > .side-panel > .controls-card [data-action="analyze-game"],
  #view-silnik .play-layout > .side-panel > .controls-card [data-action="analyze-game"] { grid-column: 1 / -1; }

  /* Gra we dwie osoby nie ma rzedu czterech akcji (nie ma tam ani podpowiedzi,
     ani poddania), wiec „Nowa partia" zostaje w karcie sterowania, ale
     przestaje byc zielona. K3 obowiazuje na kazdym ekranie tej sekcji. */
  #view-play .play-layout [data-action="new-game"].btn-primary {
    border-color: var(--border);
    background: var(--bg-sunken);
    color: var(--text);
  }
  #view-play .play-layout [data-action="new-game"].btn-primary:hover:not(:disabled) {
    border-color: var(--accent);
    background: var(--bg-sunken);
  }

  /* ---- pas oceny rowno z plansza ----
     Kolumna pasa oceny ZOSTAJE zarezerwowana na kazdym ekranie (kotwica
     planszy, POWLOKA-DESKTOP sekcja „Plansza jako kotwica"): ten blok NIE
     rusza ani `--eval-w`, ani siatki `.board-column`. Zmienia sie wylacznie
     wysokosc samego paska: rozciagniety na cala kolumne wystawal nad karte
     przeciwnika i pod karte gracza, a jego liczba wisiala obok pustego pasa.
     Teraz konczy sie tam, gdzie plansza. */
  #view-computer .play-layout .board-column > .eval-bar,
  #view-silnik .play-layout .board-column > .eval-bar,
  #view-play .play-layout .board-column > .eval-bar {
    align-self: center;
    /* ⚠️ 03.09.2026 (fala grupy A): samo `var(--board-size)` bylo prawda
       tylko przy 1440 px. W pasmie 900-1179 px plansza jest WEZSZA niz
       `--board-size`, bo tor siatki to `minmax(0, var(--board-size))`, wiec
       pasek wystawal ~130 px pod karte gracza. Wysokosc kolumny minus dwa
       pasy i dwie przerwy to dokladnie wysokosc planszy na KAZDEJ
       szerokosci; przy 1440 px obie wartosci sa rowne 640 px, wiec kadr
       kontrolny partii sie nie zmienia. */
    height: min(var(--board-size), calc(100% - 2 * var(--board-head) - 2 * var(--sp-2)));
    max-height: 100%;
  }

  /* ---- karty graczy w pasach 46 px ----
     Portret, nazwa i ranking do lewej; zbite figury na srodku; zegar do prawej.
     Wszystko sie kurczy, plansza nie drga (patrz naglowek pliku). */
  #view-computer .play-layout .player-card__id,
  #view-silnik .play-layout .player-card__id { gap: 1px; }
  #view-computer .play-layout .player-card__rating,
  #view-silnik .play-layout .player-card__rating { font-size: 11px; }
  #view-computer .play-layout .player-card,
  #view-silnik .play-layout .player-card { border-radius: var(--radius); }

  /* Stopka na czas partii: stan sesji w css/layout.css (D-F1-07). */

  /* ================= EKRAN WYBORU PRZECIWNIKA ================= */

  /* Ekran wyboru jest JEDNYM kadrem: zapowiedz nagrody, pasek rozdzialow,
     mapa i panel spotkania z jednym zielonym „Zagraj". Przed ta fala panel
     spotkania lezal pod zgieciem przy 900 px wysokosci okna, wiec gracz
     widzial mape bez zadnej akcji. */
  #view-computer .setup-layout { padding: 0; }

  /* Karta kampanii traci obwodke i tlo (mapa niesie swoje wlasne), ale ZOSTAJE
     przy swoim wewnetrznym marginesie. Bez niego mapa siegala prawej krawedzi
     pola tresci i jej pigulka „← Drogi" wchodzila pod dwie ikony powloki
     (motyw, ustawienia), ktore sa `fixed` dokladnie w tym rogu. */
  #view-computer .setup-card.is-campaign-wide {
    border: 0;
    background: none;
  }

  /* Zapowiedz nagrody na ekranie wyboru: ta sama plakietka, co w partii. */
  #view-computer .setup-card .setup-reward {
    display: inline-block;
    margin: 0 0 var(--sp-3);
    padding: 7px var(--sp-3);
    border: 1px solid color-mix(in srgb, var(--reward) 34%, var(--border));
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--reward) 8%, var(--bg-raised));
    color: var(--text-dim);
    font: 700 var(--fs-micro) var(--font-mono);
    font-variant-numeric: tabular-nums;
  }
  #view-computer .setup-card .setup-reward:empty { display: none; }

  /* Mapa: obwodka i promien z kontraktu zamiast zlotej ramki i cienia sprzed
     powloki. Panoramiczna scena ZOSTAJE, bo to jest tozsamosc kampanii i pilnuje
     jej campaignVisual.spec.js; zmienia sie chrom wokol niej. */
  #view-computer .campaign-desktop-map {
    border-color: var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg-raised);
    box-shadow: none;
  }

  /* Scena liczy sie z WYSOKOSCI OKNA, nigdy z szerokosci: przy 1440x900
     `52vw` dawalo 720 px i panel spotkania (~150 px) razem z paskiem
     rozdzialow nie miescil sie w kadrze. */
  #view-computer .campaign-desktop-map__scene {
    min-height: min(clamp(430px, 52vw, 720px), calc(100dvh - 328px));
  }

  /* Pasek rozdzialow i pigulka powrotu: te same tokeny, co karty Startu.
     Zloto `#c2a069` z poprzedniej wersji nie jest zadnym tokenem serwisu. */
  #view-computer .campaign-chapters {
    border-bottom-color: var(--border);
    background: var(--bg-raised);
  }
  #view-computer .campaign-desktop-map__back {
    border-color: var(--border);
    background: color-mix(in srgb, var(--bg-sunken) 82%, transparent);
    color: var(--text-dim);
  }

  #view-computer .campaign-desktop-map__foot {
    border-top: 1px solid var(--border);
    background: var(--bg-raised);
  }

  #view-computer .campaign-desktop-encounter span { color: var(--text-dim); }
  #view-computer .campaign-desktop-encounter h3 { color: var(--text); font-size: 20px; }
  #view-computer .campaign-desktop-encounter p { color: var(--accent-strong); font-size: var(--fs-meta); }
  #view-computer .campaign-desktop-encounter small { color: var(--text-dim); }
  #view-computer .campaign-desktop-encounter > img {
    width: 64px;
    height: 64px;
    border-color: var(--accent);
    box-shadow: none;
  }
  #view-computer .campaign-desktop-encounter { grid-template-columns: 64px minmax(0, 1fr) auto; }

  /* JEDYNY zielony przycisk ekranu wyboru (K3). Plaski, w tokenach: gradient
     i cien z poprzedniej wersji byly jedynym takim przyciskiem w serwisie. */
  #view-computer .campaign-desktop-encounter__play {
    min-height: 46px;
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: 700;
    box-shadow: none;
  }
  #view-computer .campaign-desktop-encounter__play:hover { filter: brightness(1.06); }

  /* Lobby kariery: kafel drogi ma promien kart Startu, ale NIE ich tlo.
     ⚠️ Do 03.09 stalo tu `background: var(--bg-raised)` i `--accent-gold`
     na nagrodzie. Oba idą za motywem, a kafel jest ZDJĘCIEM: w motywie
     jasnym podkład robił się biały pod ciemną fotografią, a złota nagroda
     schodziła do #7a5c22 na ciemnym kadrze, czyli 2,99:1 przy progu 4,5:1
     (pomiar: .planning/design/SZLIF-2-KONTRAST.md). Paleta powierzchni
     obrazowej siedzi w css/computer.css, sekcja na górze pliku. */
  #view-computer .campaign-pick {
    border-radius: var(--radius-lg);
    background: var(--gra-surface-media);
  }
  #view-computer .campaign-pick-reward { color: var(--gra-gold-media-strong); }
  #view-computer .campaign-pick-cta {
    border-radius: var(--radius);
    font-weight: 700;
  }

  /* ================= EKRAN WYNIKU PARTII =================
   * ZOSTAJE NAKLADKA nad plansza, a nie karta w kolumnie kontekstowej.
   * Powod jest w kodzie, nie w guscie: `showGameOverModal()` wiesza okno
   * w `.board-wrap`, zaklada w nim pulapke fokusu i wylacza tlo
   * (`isolateBackground`), a kolumne kontekstowa zajmuje juz TRWALY zapis
   * wyniku: karta `.game-summary` z lekcja po partii, ktorej pilnuje
   * `gameSummaryLayout.spec.js`. Dwa opisy tego samego wyniku obok siebie
   * to nie jest jeden ekran. Zmienia sie WYGLAD okna, nie jego miejsce.
   */
  #view-computer .gameover-overlay,
  #view-silnik .gameover-overlay {
    border-radius: var(--radius-xs);
    background: color-mix(in srgb, var(--bg-sunken) 78%, transparent);
    backdrop-filter: blur(2px);
  }

  #view-computer .gameover-box,
  #view-silnik .gameover-box {
    width: min(360px, 94%);
    padding: var(--sp-4) var(--sp-4) var(--sp-3);
    border-radius: var(--radius-lg);
    box-shadow: none;
  }

  /* Okno ma zmiescic sie w 640 px planszy RAZEM z przyciskiem domykajacym.
     Przewijanie zostaje bezpiecznikiem, a nie sposobem dotarcia do akcji. */
  #view-computer .gameover-avatar,
  #view-silnik .gameover-avatar { width: 48px; height: 48px; margin-bottom: 4px; }
  #view-computer .gameover-emblem,
  #view-silnik .gameover-emblem { top: calc(var(--sp-4) + 26px); right: calc(50% - 30px); }
  #view-computer .gameover-title,
  #view-silnik .gameover-title { font-size: 21px; letter-spacing: -.01em; }
  #view-computer .gameover-subtitle,
  #view-silnik .gameover-subtitle { font-size: var(--fs-micro); }
  #view-computer .gameover-verdict,
  #view-silnik .gameover-verdict { font-size: var(--fs-micro); margin-bottom: var(--sp-2); }
  #view-computer .gameover-stats,
  #view-silnik .gameover-stats,
  #view-computer .gameover-reward,
  #view-silnik .gameover-reward { padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-2); }
  /* ⚠️ AKCJE MUSZA BYC WIDOCZNE BEZ PRZEWIJANIA. Komentarz nad tym blokiem
     obiecuje, ze „przewijanie zostaje bezpiecznikiem, a nie sposobem dotarcia
     do akcji", ale w pasmie 900-1179 px tak nie bylo: przy 1000x900 tresc
     okna ma 655 px w kadrze 505 px, wiec „Zobacz raport z partii", „Rewanz"
     i „Zakoncz" lezaly PONIZEJ dolnej krawedzi, bez zadnego sygnalu, ze cos
     jest nizej. Okno wygladalo na kompletne i pozbawione akcji.
     Przyklejona stopka trzyma akcje zawsze na wierzchu, a miekka krawedz nad
     nia mowi, ze tresc pod spodem sie przewija. Znalezione przy szlifie
     paczki 2 (03.09.2026); audyt widzial to samo na modalu ustawien
     (znalezisko 29), ale nie na koncu partii. */
  #view-computer .gameover-actions,
  #view-silnik .gameover-actions {
    gap: 6px;
    position: sticky;
    bottom: calc(-1 * var(--sp-3));
    z-index: 1;
    margin-top: 0;
    padding: var(--sp-3) 0 var(--sp-3);
    margin-bottom: calc(-1 * var(--sp-3));
    background: linear-gradient(to bottom, transparent, var(--bg-raised) 18%);
  }

  /* Etykiety kart w srodku okna mowia tym samym glosem, co „POSTEP SESJI"
     w zadaniu i „DZIS" na Starcie. */
  #view-computer .gameover-stats__title,
  #view-silnik .gameover-stats__title {
    font: 700 var(--fs-label) var(--font-mono);
    letter-spacing: var(--ls-label);
  }
  #view-computer .gameover-stats,
  #view-silnik .gameover-stats,
  #view-computer .gameover-reward,
  #view-silnik .gameover-reward { border-radius: var(--radius); }

  /* Akcja wiodaca na pelna szerokosc, reszta cicho pod nia. Zielony przycisk
     jest tu DOKLADNIE JEDEN. */
  #view-computer .gameover-action,
  #view-silnik .gameover-action {
    min-height: 40px;
    padding: 8px 12px;
    border-radius: var(--radius);
    font-size: var(--fs-body-sm);
  }
  #view-computer .gameover-action--primary,
  #view-silnik .gameover-action--primary { min-height: 44px; font-weight: 700; }

  /* Dopoki okno stoi, tlo jest wylaczone (`isolateBackground`), ale ZIELEN
     w kadrze ma byc jedna (K3): „Analizuj partie" w kolumnie kontekstowej
     czeka na swoja kolej i odzyskuje kolor po zamknieciu okna. */
  #view-computer .play-layout:has(.gameover-overlay) [data-action="analyze-game"],
  #view-silnik .play-layout:has(.gameover-overlay) [data-action="analyze-game"] {
    border-color: var(--border);
    background: var(--bg-sunken);
    color: var(--text);
  }

  /* Trwaly zapis wyniku w kolumnie kontekstowej. Karta „Zacznij" przestaje
     konkurowac z zielonym „Rewanz" w oknie: w kadrze zostaje jedna zielen. */
  #view-computer .play-layout .game-summary-next {
    border-color: var(--border);
    background: var(--bg-sunken);
    color: var(--text);
  }
  #view-computer .play-layout .game-summary-next:hover { border-color: var(--accent); background: var(--bg-sunken); }
}

/* Pasmo posrednie 900-1179 px: kolumna kontekstowa ma 300 px (powloka), wiec
   cztery akcje z ikona nad etykieta robia sie ciasne. Napisy schodza o klase
   nizej zamiast sie lamac. */
@media (min-width: 900px) and (max-width: 1179px) {
  #view-computer .play-layout .game-action,
  #view-silnik .play-layout .game-action { font-size: 11px; min-height: 58px; }
  #view-computer .play-layout .game-action svg,
  #view-silnik .play-layout .game-action svg { width: 18px; height: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .gameover-stat__value[data-loading] { animation: none; background-position: 0 0; }
  .player-card { transition: none; }
  .game-action { transition: none; }
}

/* ======================================================================== *
 * FALA GRUPY A (03.09.2026): GRA Z SILNIKIEM, GRAJ WE DWIE OSOBY, BOTY
 * ------------------------------------------------------------------------
 * Kontrakt: `.planning/design/KONTRAKT-MAKIET-2026-09-03.md` sekcje 2.1, 2.2
 * i 2.6 (makieta daje uklad, kontrakt wygrywa tam, gdzie sie roznia; dane
 * wygrywaja z obrazem wszedzie, gdzie obraz podaje liczby).
 *
 * Slownik jest ten sam, co na Starcie, w Zadaniu i w partii z fal 1 do 7:
 * karty na `--bg-raised`, obwodka 1 px `--border`, promien `--radius-lg`,
 * etykiety w Geist Mono wersalikami, liczby w Geist Mono, zielen wylacznie
 * na postepie i na JEDNEJ akcji pierwszoplanowej ekranu (K3).
 *
 * TEN BLOK NIE RUSZA GEOMETRII PLANSZY. `--board-size`, `--board-head`,
 * `--eval-w` i `--ctx-w` zostaja w css/app-desktop.css i sa tu tylko CZYTANE.
 * Kotwica planszy (x=342 y=106 w=640 przy 1440x900) obowiazuje takze
 * w `#view-play`.
 *
 * Wszystkie selektory sa zakresowane identyfikatorem widoku. Klasy
 * `.level-card`, `.engine-level-card`, `.setup-card` i `.color-choice` sa
 * WSPOLNE z ekranem kariery (`#view-computer`), ktorego ta fala nie dotyka.
 * ======================================================================== */

/* ================= GRA Z SILNIKIEM: EKRAN WYBORU POZIOMU ================= */

/* Pojemniki kolumn. Na telefonie znikaja z UKLADU (`display: contents`), wiec
   kolejnosc w DOM zostaje kolejnoscia na ekranie i kadr 390 px rozni sie od
   punktu wyjscia tylko o to, co dokladamy swiadomie.
   UWAGA: `display: contents` usuwa pojemnik z ukladu, ale NIE z drzewa
   dokumentu: kombinator dziecka nadal widzi go jako rodzica (pulapka z fali
   5). Dlatego reguly ponizej celuja w klasy, a nie w `.setup-card > *`. */
#view-silnik .silnik-main,
#view-silnik .silnik-side,
#view-silnik .silnik-seat { display: contents; }

#view-silnik .silnik-head { display: grid; gap: 4px; }
#view-silnik .silnik-lead {
  margin: 0;
  color: var(--text-dim);
  font-size: var(--fs-meta);
  line-height: 1.5;
}

/* ---- karta poziomu: numer, nazwa, Elo, jedno zdanie ----
   Nazwy, Elo i opisy ida z `js/engineLevels.js`, nie z makiety. Poziomow jest
   JEDENASCIE, nie dziesiec jak na obrazku, wiec siatka ma piec kolumn i tyle
   rzedow, ile wychodzi z danych. */
.engine-level-index {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-raised);
  color: var(--text-dim);
  font: 700 var(--fs-micro)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.engine-level-index[hidden] { display: none; }
.engine-level-elo[hidden] { display: none; }
.engine-level-card.is-selected .engine-level-index {
  border-color: var(--accent);
  color: var(--accent-strong);
}

/* Strzalka wiersza (kadr telefoniczny). Rysowana obwodka, zeby nie dokladac
   pliku do `APP_SHELL`. */
.engine-level-go {
  display: block;
  width: 7px;
  height: 7px;
  border-top: 2px solid var(--text-dim);
  border-right: 2px solid var(--text-dim);
  transform: rotate(45deg);
}
.engine-level-go[hidden] { display: none; }

/* ---- „Grasz jako": glify pionkow zamiast kolek ----
   Na obrazie przy kazdej opcji stoi FIGURA, nie kolorowa kropka. Kropka nie
   mowi, ze chodzi o strone przy szachownicy. Reguly sa zakresowane widokiem,
   bo `.color-swatch` dzieli z ekranem kariery (`#view-computer`), ktorego ta
   fala nie dotyka. */
/* ⚠️ `css/friend.css` ustawia NIEZAKRESOWANE `.color-btn { flex-direction:
   column }` i wycieka tym do dwoch innych widokow. Na obrazie ikona stoi OBOK
   napisu, wiec tu wracamy do wiersza. Zrodlo wycieku zostaje nietkniete:
   nalezy do ekranu gry ze znajomym, ktorego ta fala nie dotyka. */
#view-silnik .color-btn { flex-direction: row; gap: var(--sp-2); }

/* Rozwiniecie dokladnej sily. `summary` z `display: flex` gubi domyslny
   znacznik, wiec strzalka jest rysowana obwodka (bez pliku w APP_SHELL). */
#view-silnik summary.engine-elo-head { list-style: none; }
#view-silnik summary.engine-elo-head::-webkit-details-marker { display: none; }
#view-silnik summary.engine-elo-head::after {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin-left: 2px;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: rotate(45deg) translate(-2px, -2px);
}
#view-silnik .engine-elo-picker[open] > summary.engine-elo-head::after {
  transform: rotate(-135deg) translate(-2px, -2px);
}
#view-silnik .engine-elo-value { margin-left: auto; }

#view-silnik .color-swatch {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border: 0;
  border-radius: 0;
  background: none;
}
#view-silnik .color-swatch--w { color: var(--pc-white-fill); }
#view-silnik .color-swatch--b { color: var(--pc-black-fill); }
#view-silnik .color-swatch--random { color: var(--text-dim); }
/* Motyw jasny: biala figura na jasnym przycisku znika, wiec bierze obrys. */
:root[data-theme="light"] #view-silnik .color-swatch--w svg {
  stroke: var(--pc-black-edge);
  stroke-width: 1.6;
  stroke-linejoin: round;
}

/* ---- wiersz "Pomoce do nauki" pod siatka (oba uklady) ---- */
.silnik-aids {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
}
.silnik-aids[hidden] { display: none; }
.silnik-aids__text { display: grid; gap: 2px; min-width: 0; }
.silnik-aids__label {
  font: 700 var(--fs-label) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-dim);
}
.silnik-aids__desc { font-size: var(--fs-micro); color: var(--text-dim); line-height: 1.4; }
.silnik-aids .aids-toggle { margin-left: auto; flex: none; }

/* ---- prawa kolumna: stawka, "Co daje ten tryb", pozycja startowa ---- */
/* Karta stawki. Ksztalt z obrazu: naglowek mono, pod nim wiersze z ikona po
   lewej, drobnym kluczem i grubym odczytem. Tresc jest prawdziwa (patrz
   `STAKE_ROWS` w js/views/silnik.js), wiec odczyt jest STONOWANY, nie zloty:
   tryb niczego nie wyplaca, a zloto obiecywalo nagrode. */
.silnik-stake {
  padding: var(--sp-3) var(--sp-4);
}
.silnik-stake:empty { display: none; }
.silnik-stake__label {
  display: block;
  margin-bottom: var(--sp-3);
  font: 700 var(--fs-label) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-dim);
}
.silnik-stake__list { display: grid; gap: var(--sp-3); margin: 0; padding: 0; list-style: none; }
.silnik-stake__row { display: flex; align-items: center; gap: var(--sp-3); }
.silnik-stake__ico {
  display: grid;
  place-items: center;
  flex: none;
  width: 32px;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  color: var(--text-dim);
}
.silnik-stake__body { display: grid; gap: 1px; min-width: 0; }
.silnik-stake__key { font-size: var(--fs-micro); color: var(--text-dim); line-height: 1.2; }
.silnik-stake__value { font-size: 15px; font-weight: 650; color: var(--text); line-height: 1.2; }

.silnik-why,
.silnik-mini {
  padding: var(--sp-3) var(--sp-4);
}
.silnik-why__label,
.silnik-mini__label {
  display: block;
  margin-bottom: var(--sp-3);
  font: 700 var(--fs-label) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-dim);
}
.silnik-why__list { display: grid; gap: var(--sp-3); margin: 0; padding: 0; list-style: none; }
.silnik-why__list li {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  font-size: var(--fs-micro);
  color: var(--text-dim);
  line-height: 1.45;
}
.silnik-why__ico {
  display: grid;
  place-items: center;
  flex: none;
  width: 26px;
  height: 26px;
  margin-top: 1px;
  color: var(--accent-strong);
}
.silnik-why__body { display: block; min-width: 0; }
.silnik-why__list strong {
  display: block;
  color: var(--text);
  font-size: var(--fs-meta);
  font-weight: 650;
}
/* Miniatura pozycji startowej. Host NIE ma klasy `.board` swiadomie:
   `#view-silnik .board` jest lokatorem partii w test/e2e (resignConfirm,
   analysisHandoff), a drugi element tej klasy w tym samym widoku wywraca
   Playwright w trybie strict. Kopiujemy wiec sam POJEMNIK siatki; pola
   (.sq) i figury (.piece) maja wlasne klasy i dzialaja bez zmian. */
.silnik-mini__board {
  display: grid;
  position: relative;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  grid-template-rows: repeat(8, minmax(0, 1fr));
  aspect-ratio: 1;
  width: 100%;
  margin-inline: auto;
  overflow: hidden;
  border: 2px solid color-mix(in srgb, var(--accent) 20%, var(--bg-raised));
  border-radius: var(--radius-xs);
  cursor: default;
  user-select: none;
}
.silnik-mini__board[hidden] { display: none; }
.silnik-mini__board .coord { display: none; }

/* ================= PARTIA: BELKA OCENY Z PODPISAMI =================
   Ksztalt z makiety partii: pasek stoi przy lewej krawedzi planszy, a liczba
   NIE siedzi w srodku paska (22 px nie miesci „+0.43"), tylko nad nim i pod
   nim. Podpisy wylewaja sie poza swoje pudelko (`overflow: visible`,
   `nowrap`), wiec kolumna `--eval-w` ma dalej te sama szerokosc i kotwica
   planszy sie nie rusza. */
.eval-bar--capped {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 3px;
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: none;
}
.eval-bar--capped .eval-bar-track {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--pc-black-fill);
}
/* Podpisy: mono, wyrownane do srodka kolumny, bez wplywu na uklad. */
.eval-bar--capped .eval-cap {
  flex: none;
  min-height: 13px;
  color: var(--text-dim);
  font: 700 11px/1.2 var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: center;
  white-space: nowrap;
  overflow: visible;
}
/* ⚠️ `.eval-bar-label` z css/aids.css jest pozycjonowany absolutnie i skacze
   na gore przy `.is-negative`. W tym ksztalcie podpis stoi TAM, GDZIE STOI:
   dolny nalezy do gracza z dolu i nigdzie nie jedzie. */
.eval-bar--capped .eval-bar-label,
.eval-bar--capped .eval-bar-label.is-negative {
  position: static;
  top: auto;
  bottom: auto;
  color: var(--text-dim);
  font-size: 11px;
}

/* ================= PARTIA: KARTA STANU MILCZY W TRAKCIE =================
   Kolumna ma na makiecie CZTERY bloki. Karta stanu wraca do kadru dopiero
   z wynikiem partii albo z podgladem przewijanej notacji (patrz
   `quietStatusCard` w js/views/play.js). */
#view-play .status-card.is-quiet,
#view-silnik .status-card.is-quiet { display: none; }

/* ================= PARTIA: NARZEDZIA ZA ROZWINIECIEM ================= */
.controls-summary {
  display: flex;
  align-items: center;
  min-height: 44px;
  cursor: pointer;
  color: var(--text-dim);
  font: 700 var(--fs-label) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  list-style: none;
}
.controls-summary::-webkit-details-marker { display: none; }
.controls-summary::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 8px;
  vertical-align: 2px;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: rotate(45deg);
}
details.controls-card[open] > .controls-summary::after { transform: rotate(-135deg); }
details.controls-card[open] > .controls-summary { margin-bottom: var(--sp-2); }

/* ================= PARTIA: PODDANIE OSOBNO I CICHO ================= */
.game-quit {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  min-height: 34px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: none;
  color: var(--text-dim);
  font-size: var(--fs-micro);
  font-weight: 600;
  cursor: pointer;
}
.game-quit[hidden] { display: none; }
.game-quit:hover { border-color: var(--danger); color: var(--text); }
.game-quit__ico { display: grid; place-items: center; flex: none; }

/* ================= GRAJ WE DWIE OSOBY ================= */

/* Odczyt oceny w karcie pomocy. Liczba jest TA SAMA, co na pasku po lewej
   (rozdzielacz w js/views/play.js), a zdanie pod nia jest tym, czego pasek nie
   mowi. To jedyny ekran sekcji Gra, ktory ma prawo do tego powtorzenia:
   kontrakt makiet 2.6 wymienia "karte pomocy z ocena pozycji" wprost. */
.play-eval {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 2px var(--sp-2);
}
.play-eval[hidden] { display: none; }
.play-eval__label { font-size: var(--fs-micro); color: var(--text-dim); }
.play-eval__value {
  font: 700 var(--fs-meta) var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  text-align: right;
}
.play-eval__value:empty::after { content: "\2014"; color: var(--text-dim); }
.play-eval__note {
  grid-column: 1 / -1;
  font-size: var(--fs-micro);
  color: var(--text-dim);
  line-height: 1.4;
}

/* Cztery akcje w siatce dwa na dwa (kontrakt makiet 2.6). Dwie klasy
   w selektorze, bo `.game-actions { grid-template-columns: repeat(3, ...) }`
   stoi wyzej w tym pliku. */
.game-actions.play-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Zdanie o rankingu jest informacja, nie ostrzezeniem: bez koloru alarmu. */
.play-ranked {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  padding: var(--sp-3) var(--sp-4);
  color: var(--text-dim);
  font-size: var(--fs-micro);
  line-height: 1.4;
}
.play-ranked[hidden] { display: none; }
.play-ranked__ico { display: grid; place-items: center; flex: none; color: var(--text-dim); }

/* ================= GALERIA BOTOW ================= */

.bots-head { display: grid; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.bots-head[hidden] { display: none; }
.bots-head .setup-title { margin: 0; }
.bots-head .bots-lead { margin: 0; }
.bots-head .bots-counter { margin: 0; }

.bots-progress {
  width: 100%;
  height: 8px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--bg-sunken);
  color: var(--accent);
}
.bots-progress[hidden] { display: none; }
.bots-progress::-webkit-progress-bar { border-radius: var(--radius-pill); background: var(--bg-sunken); }
.bots-progress::-webkit-progress-value { border-radius: var(--radius-pill); background: var(--accent); }
.bots-progress::-moz-progress-bar { border-radius: var(--radius-pill); background: var(--accent); }

.bots-scale { margin: 0; font-size: var(--fs-micro); color: var(--text-dim); }
.bots-scale[hidden] { display: none; }

/* Odznaka "Pokonany" jest ZLOTA, bo zloto jest w tym serwisie kolorem nagrody
   (korona poziomu, kropki trudnosci). Zielen zostaje przy postepie i akcji. */
#view-boty .level-card-badge {
  align-self: flex-start;
  padding: 2px 8px;
  border: 1px solid color-mix(in srgb, var(--accent-gold) 40%, var(--border));
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--accent-gold) 12%, var(--bg-raised));
  color: var(--accent-gold);
  font: 700 var(--fs-label) var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}
#view-boty .level-card.is-defeated .level-card-badge { display: inline-block; }

/* Klodka na zaslonie portretu. Znaczenie niesie ZDANIE w karcie, ten znak
   jest ozdobnikiem, wiec zyje w pseudo-elemencie. */
#view-boty .level-card.is-locked .level-card-avatar--hidden { position: relative; }
#view-boty .level-card.is-locked .level-card-avatar--hidden::after {
  content: "\1F512";
  position: absolute;
  right: 4px;
  bottom: 2px;
  font-size: 13px;
  line-height: 1;
}

/* ---- telefon: drabina poziomow jest LISTA WIERSZY (kontrakt makiet 2.1) ----
   Numer, nazwa, Elo i strzalka w jednym wierszu. Opis poziomu schodzi z kadru,
   bo w wierszu na 44 px nie ma na niego miejsca; nie ginie, bo suwak
   dokladnej sily pokazuje `tagline` WYBRANEGO poziomu tuz pod siatka. */
@media (max-width: 899px) {
  #view-silnik .engine-level-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 6px;
  }
  #view-silnik .engine-level-card {
    display: grid;
    grid-template-columns: 24px minmax(0, 1fr) auto auto;
    align-items: center;
    gap: var(--sp-3);
    padding: 10px 12px;
  }
  /* Nazwa i Elo wchodza wprost w wiersz: pojemnik znika z ukladu. */
  #view-silnik .engine-level-head { display: contents; }
  #view-silnik .engine-level-name { font-size: 14px; }
  #view-silnik .engine-level-elo { font-size: var(--fs-micro); color: var(--text-dim); }
  #view-silnik .engine-level-desc { display: none; }
  #view-silnik .silnik-aids { padding: var(--sp-3); }
}

@media (min-width: 900px) {
  /* ================= GRA Z SILNIKIEM: DWIE KOLUMNY =================
     Lewa kolumna `1fr`, prawa `--ctx-w` z powloki, odstep `--sp-5`: dokladnie
     ta sama siatka, co `.play-layout` w partii i `.home` na Starcie, wiec
     prawa krawedz tresci nie skacze miedzy ekranami sekcji Gra.
     Karta zbiorcza traci chrom: kartami sa teraz jej sekcje, jak na Starcie. */
  /* ⚠️ Pulapka repozytorium: `[hidden]` z css/computer.css ma specyficznosc
     (0,2,0), a selektor ponizej ja bije (1,1,0), wiec bez tej pary karta
     ustawien zostawala widoczna NAD plansza po kliknieciu „Zagraj" na
     desktopie (znalezisko fali Gra 03.09.2026, zmierzone: computed display
     „block" zamiast „none" na #view-silnik [data-role="setup"] po starcie
     partii przy 1440 px). */
  #view-silnik .setup-layout[hidden] { display: none; }
  #view-silnik .setup-layout { display: block; padding: 0; }
  #view-silnik .setup-card {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--ctx-w);
    gap: var(--sp-5);
    align-items: stretch;
    width: 100%;
    max-width: none;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    overflow: visible;
  }
  #view-silnik .silnik-main {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    min-width: 0;
  }
  #view-silnik .silnik-side {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
  }

  #view-silnik .setup-title { margin: 0;
  }

  /* Piec kolumn z kontraktu. Poziomow jest jedenascie, wiec ostatni rzad ma
     jedna karte: liczba kafli idzie z `js/engineLevels.js`, a nie z obrazka. */
  #view-silnik .engine-level-grid {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: var(--sp-2);
  }
  #view-silnik .engine-level-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    min-height: 138px;
    padding: var(--sp-3) 10px var(--sp-3);
    background: var(--bg-raised);
    border-radius: var(--radius);
    text-align: center;
  }
  /* Jedenasty szczebel bierze caly rzad. Dziesiec pierwszych stoi dwa rzedy
     po piec, dokladnie jak na obrazie; bez tej reguly ostatni rzad mial jeden
     kafel i cztery puste pola. */
  #view-silnik .engine-level-card--full {
    grid-column: 1 / -1;
    flex-direction: row;
    justify-content: center;
    gap: var(--sp-3);
    min-height: 0;
    padding: 10px var(--sp-4);
    text-align: left;
  }
  #view-silnik .engine-level-card--full .engine-level-head {
    flex-direction: row;
    align-items: baseline;
    gap: var(--sp-2);
    width: auto;
  }
  #view-silnik .engine-level-card--full .engine-level-desc { margin: 0; }
  #view-silnik .engine-level-head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    width: 100%;
  }
  #view-silnik .engine-level-name { font-size: 13.5px; line-height: 1.2; }
  /* Elo jest STONOWANE, nie zielone: na obrazie zielen w tej siatce nalezy
     WYLACZNIE do obwodki wybranego kafla. Zielona liczba w kazdym kaflu
     odbierala jej te robote. */
  #view-silnik .engine-level-elo { font-size: var(--fs-micro); color: var(--text-dim); }
  #view-silnik .engine-level-desc {
    margin-top: auto;
    font-size: 11px;
    line-height: 1.35;
    color: var(--text-dim);
  }
  /* Strzalka nalezy do wiersza telefonu; kafel ma numer i obwodke. */
  #view-silnik .engine-level-go { display: none; }

  #view-silnik .engine-elo-picker {
    margin-top: 0;
    padding: 0 var(--sp-4);
    border-radius: var(--radius-lg);
    background: var(--bg-raised);
  }
  #view-silnik .engine-elo-head {
    margin: 0;
    padding: 10px 0;
    cursor: pointer;
  }
  #view-silnik .engine-elo-picker[open] .engine-elo-head { padding-bottom: 0; }
  #view-silnik .engine-elo-label { font: 700 var(--fs-label) var(--font-mono); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--text-dim); }
  #view-silnik .engine-elo-foot { margin-top: 8px; padding-bottom: var(--sp-3); }
  #view-silnik .engine-elo-range { margin-top: var(--sp-2); }

  /* "Grasz jako" jest karta, a nie naglowkiem nad rzedem przyciskow. */
  #view-silnik .silnik-seat {
    display: block;
    padding: var(--sp-3) var(--sp-4);
  }
  #view-silnik .silnik-seat .setup-subtitle {
    margin: 0 0 var(--sp-2);
    font: 700 var(--fs-label) var(--font-mono);
    letter-spacing: var(--ls-label);
  }
  #view-silnik .color-btn { min-height: 40px; padding: 8px; font-size: var(--fs-meta); }

  /* Prawa kolumna. Trzy klasy maja domyslke `display: none` przed progiem
     900 px, wiec tu wracaja do swojego ukladu. */
  #view-silnik .silnik-stake { display: block; }
  #view-silnik .silnik-why,
  #view-silnik .silnik-mini { display: block; }

  /* Jedyny zielony przycisk ekranu (K3), na dole prawej kolumny.
     `position: sticky` z css/computer.css nalezy do dlugiej listy postaci
     w kampanii; tutaj kolumna miesci sie w kadrze i lepkosc nie ma czego
     trzymac. */
  #view-silnik .setup-card [data-action="start"] {
    position: static;
    width: 100%;
    margin: auto 0 0;
    min-height: 46px;
    padding: 12px;
    font-size: 15px;
  }
  #view-silnik .engine-status { margin: 0; text-align: center; }
  /* Wejscie w gre we dwie osoby jest na desktopie w szynie, tuz obok. */
  #view-silnik .setup-alt { display: none; }

  /* ================= GRAJ WE DWIE OSOBY ================= */

  /* Kolejnosc kart w kolumnie kontekstowej. DOM zostaje w kolejnosci mobilnej,
     wiec dopisanie karty bez `order` postawiloby ja na koncu. */
  /* Kolumna z makiety: notacja, pomoce, siatka akcji, linia informacyjna.
     Karta stanu (wynik) i narzedzia stoja PO nich, a nie przed. */
  #view-play .play-layout > .side-panel > .moves-card { order: 2; }
  #view-play .play-layout > .side-panel > .aids-card { order: 3; }
  #view-play .play-layout > .side-panel > .game-actions { order: 4; }
  #view-play .play-layout > .side-panel > .play-ranked { order: 5; }
  #view-play .play-layout > .side-panel > .status-card { order: 6; }
  #view-play .play-layout > .side-panel > .controls-card { order: 7; }

  #view-play .play-layout > .side-panel > .game-actions,
  #view-play .play-layout > .side-panel > .play-ranked { flex: none; }
  #view-play .play-layout .game-action { min-height: 58px; padding: 8px 4px; font-size: 12px; }
  #view-play .play-layout .game-action__label { overflow-wrap: normal; }
  #view-play .play-layout .game-action svg { width: 20px; height: 20px; }

  #view-play .play-layout .player-card__id { gap: 1px; }
  #view-play .play-layout .player-card { border-radius: var(--radius); }
  #view-play .play-layout .player-card .player-name { font-size: 13px; }

  /* Ciche sterowanie plansza schodzi na dol kolumny jednym rzedem malych
     przyciskow: ten sam wzorzec, co w partii z botem (fala 4). */
  #view-play .play-layout > .side-panel > .controls-card {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-2);
    flex: none;
    padding: 0;
    border: 0;
    background: none;
  }
  #view-play .play-layout > .side-panel > .controls-card > .controls-row { display: contents; }
  #view-play .play-layout > .side-panel > .controls-card > .controls-row[hidden] { display: none; }
  #view-play .play-layout > .side-panel > .controls-card .btn {
    min-height: 34px;
    padding: 0 var(--sp-2);
    font-size: var(--fs-micro);
  }
  #view-play .play-layout > .side-panel > .controls-card [data-action="analyze-game"] { grid-column: 1 / -1; }
  #view-play .play-layout > .side-panel > .controls-card .controls-note {
    grid-column: 1 / -1;
    margin: 0;
    font-size: var(--fs-label);
    line-height: 1.4;
  }

  /* ================= GALERIA BOTOW ================= */

  #view-boty .bots-layout { display: block; }
  #view-boty .bots-card {
    width: 100%;
    max-width: none;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
  }
  #view-boty .bots-head { max-width: 760px; margin-bottom: var(--sp-5); }
  #view-boty .setup-title { margin: 0;
  }
  #view-boty .bots-counter {
    color: var(--text-dim);
    font: 700 var(--fs-meta) var(--font-mono);
    font-variant-numeric: tabular-nums;
  }

  #view-boty .level-grid { gap: var(--sp-5); }
  #view-boty .level-band-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--sp-3);
  }
  /* Kontrakt makiet 2.2: portret wiekszy i karta przestronniejsza niz
     kafel kampanii. 64 px jest tym samym rozmiarem, ktorego uzywa juz
     `.level-card-avatar` na telefonie (css/computer.css) — jeden rozmiar
     zamiast trzeciej wartosci wylacznie dla tego pasma. */
  #view-boty .level-card {
    position: relative;
    grid-template-columns: 64px minmax(0, 1fr);
    align-items: start;
    gap: var(--sp-3);
    padding: var(--sp-3);
    border-radius: var(--radius-lg);
    background: var(--bg-raised);
  }
  #view-boty .level-card:hover { transform: none; }
  #view-boty .level-card-avatar { width: 64px; height: 64px; }
  #view-boty .level-card-body { gap: 4px; }
  #view-boty .level-card-head { align-items: baseline; }
  #view-boty .level-card-name { font-size: 14.5px; }
  #view-boty .level-card-elo { font-size: var(--fs-micro); }
  #view-boty .level-card-desc { font-size: 11.5px; line-height: 1.4; }


  /* Zielone "Zagraj" odslania sie na najechaniu i na fokusie karty. Nie
     przesuwa tresci: zajmuje warstwe nad karta i zmienia sie tylko krycie.
     `pointer-events: none`, bo klikalna jest CALA karta. */
  #view-boty .level-card.is-defeated .bots-play {
    display: block;
    position: absolute;
    right: 8px;
    bottom: 8px;
    padding: 3px 12px;
    border-radius: var(--radius);
    background: var(--accent);
    color: var(--accent-contrast);
    font-size: var(--fs-micro);
    font-weight: 700;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-base) var(--ease);
  }
  #view-boty .level-card.is-defeated:hover .bots-play,
  #view-boty .level-card.is-defeated:focus-visible .bots-play { opacity: 1; }
}

/* Pasmo posrednie 900-1179 px: kolumna kontekstowa ma 300 px (powloka), wiec
   lewa kolumna traci 40 px. Piec kafli poziomu i cztery karty botow robia sie
   ciasne, wiec siatki schodza o jedna kolumne. */
@media (min-width: 900px) and (max-width: 1179px) {
  #view-silnik .engine-level-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  #view-boty .level-band-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  #view-play .play-layout .game-action { font-size: 11.5px; min-height: 56px; }
}

@media (prefers-reduced-motion: reduce) {
  .bots-play { transition: none; }
}

/* Nazwa pasma niesie takze rozdzial kampanii, w ktorym te boty sie spotyka.
   Dzieki temu karta zablokowana ma jedno krotkie zdanie zamiast dziesieciu
   kopii tej samej informacji. */
.level-band-place {
  margin-left: 10px;
  color: var(--text-dim);
  font-weight: 500;
  letter-spacing: var(--ls-label);
  text-transform: none;
}

/* Zablokowana karta jest INFORMACJA, nie odmowa (kontrakt makiet 2.2), wiec
   nie gasnie do 55% krycia: nazwa i Elo maja byc czytelne w obu motywach,
   a "zablokowany" niesie zaslonieta podobizna, klodka i zdanie. Zaslona
   bierze tokeny, bo literal #101318 z css/computer.css znikal w motywie
   jasnym razem ze znakiem zapytania. */
#view-boty .level-card.is-locked { opacity: 1; }
#view-boty .level-card.is-locked .level-card-name { color: var(--text-dim); }
#view-boty .level-card-avatar--hidden {
  color: var(--text-dim);
  background:
    repeating-linear-gradient(
      135deg,
      color-mix(in srgb, var(--text) 7%, transparent) 0 6px,
      transparent 6px 12px
    ),
    var(--bg-sunken);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* Klodka jest juz na zaslonie portretu, wiec zdanie ponizej jej nie powtarza
   (css/computer.css dokleja ja pseudo-elementem dla ekranu kariery). */
#view-boty .level-card-lock::before { content: none; }

/* Zdanie o zamku jest INFORMACJA, nie akcja, wiec nie bierze zieleni
   (css/computer.css maluje nia zamek na ekranie kariery). Dotyczy obu
   ukladow: na telefonie to trzy najglosniejsze wiersze karty. */
#view-boty .level-card-lock {
  margin-top: 2px;
  font-size: 11.5px;
  font-weight: 550;
  color: var(--text-dim);
  line-height: 1.35;
}

/* Pasmo posrednie: kolumna kontekstowa ma 300 px, wiec lewa traci 40 px
   i wiersz pomocy schodzil 5 px pod zgiecie 900 px. */
@media (min-width: 900px) and (max-width: 1179px) {
  #view-silnik .silnik-main { gap: 10px; }
  #view-silnik .engine-level-card { min-height: 104px; }
}
