/* Pixel-art Outgo — rdzeń szaty wspólny dla wszystkich web-klientów.
   Świadomy wyjątek od „jedna gra = jeden plik HTML" (jak sceneria.js czy
   pytanie.js): te same kroje i kontrolki przepisane po piętnastu grach
   byłyby gorsze niż jeden arkusz. Lustro `core/pixel/` w apce.

   Ładuje się `<link rel="stylesheet" href="/pixel.css">` w <head>, przed
   arkuszem gry — żeby gra mogła nadpisać, co zechce.

   Kroje są **lokalne** (`/pixel/*.ttf`), więc nie łamią „zero zewnętrznych
   zasobów": to te same pliki, które apka wozi w assetach (pubspec: rodziny
   `Jersey25` i `Titillium`). */

/* ── kroje ──────────────────────────────────────────────────────────────
   Jersey 25 — nazwy gier, deski nagłówków, klawisze. Jedna waga (400):
   pogrubiania nie ma i **nie wolno go udawać** (`font-weight:700` na tym
   kroju daje syntetyczny pogrubiacz, który rozjeżdża piksele).
   Titillium Web — wszystko, co się czyta zdaniami: 400 opisy, 600 podpisy
   i pigułki, 700 nagłówki. Komplet trzech wag jest lustrem pubspeca. */
@font-face{font-family:'Jersey 25';src:url(/pixel/jersey25.ttf) format('truetype');
  font-weight:400;font-display:swap}
@font-face{font-family:'Titillium Web';src:url(/pixel/titillium.ttf) format('truetype');
  font-weight:400;font-display:swap}
@font-face{font-family:'Titillium Web';src:url(/pixel/titillium-semibold.ttf) format('truetype');
  font-weight:600;font-display:swap}
@font-face{font-family:'Titillium Web';src:url(/pixel/titillium-bold.ttf) format('truetype');
  font-weight:700;font-display:swap}

:root{
  --px-disp:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
  --px-jersey:'Jersey 25',var(--px-disp);
  --px-tekst:'Titillium Web',var(--px-disp);
}

/* Jersey **tylko wersalikami** — minuskuła tego kroju leży (litery siadają
   poniżej linii bazowej i gubią piksele). Klasa niesie transformację razem
   z krojem, żeby nie dało się wziąć jednego bez drugiego. Tam, gdzie napis
   idzie na canvas albo liczy się `letter-spacing` znak po znaku, tekst
   trzeba dodatkowo podnieść `toUpperCase()` w JS: `text-transform` zmienia
   rysunek, nie zawartość. */
.px-jersey{font-family:var(--px-jersey);font-weight:400;text-transform:uppercase}

/* ── barwy gier ──────────────────────────────────────────────────────────
   Te same rodziny co ramki ikonek z arkusza. Dotąd siedziały jako `--k`
   na id-ach kart w katalogu, więc żadna gra nie mogła sięgnąć po swoją
   barwę u siebie. Hak `[data-gra]` kładzie się na czymkolwiek — karcie
   w katalogu albo korzeniu strony gry. */
:root{
  --k-kalambury:#1E9C8F; --k-panstwa:#C93A66;  --k-telefon:#CE4234;
  --k-artysta:#63A832;   --k-splot:#D8A31C;    --k-saper:#8E4EC6;
  --k-sudoku:#3B76D8;    --k-statki:#3E8FB0;   --k-pilkarzyki:#E07626;
  --k-cztery:#2F9E6E;    --k-klocki:#D6488F;   --k-katapulty:#B9741F;
  --k-bomby:#5E48C8;     --k-skrzynie:#7FA030; --k-mafia:#B3342A;
  /* niebo z góry `pixel/tlo.png` — kolor pod spodem, żeby wolne łącze nie
     mignęło niczym obcym, zanim dojedzie obrazek */
  --px-niebo:#0C0A1E;
}
[data-gra="kalambury"]{--k:var(--k-kalambury)}
[data-gra="panstwa"]{--k:var(--k-panstwa)}
[data-gra="telefon"]{--k:var(--k-telefon)}
[data-gra="artysta"]{--k:var(--k-artysta)}
[data-gra="splot"]{--k:var(--k-splot)}
[data-gra="saper"]{--k:var(--k-saper)}
[data-gra="sudoku"]{--k:var(--k-sudoku)}
[data-gra="statki"]{--k:var(--k-statki)}
[data-gra="pilkarzyki"]{--k:var(--k-pilkarzyki)}
[data-gra="cztery"]{--k:var(--k-cztery)}
[data-gra="klocki"]{--k:var(--k-klocki)}
[data-gra="katapulty"]{--k:var(--k-katapulty)}
[data-gra="bomby"]{--k:var(--k-bomby)}
[data-gra="skrzynie"]{--k:var(--k-skrzynie)}
[data-gra="mafia"]{--k:var(--k-mafia)}

/* ── schodkowa rama (RamaKafelka) ────────────────────────────────────────
   Trzy współśrodkowe warstwy bez grafiki: tło = ciemny obrys, ::before =
   rama w barwie gry (`--k`), ::after = wnętrze z gradientem. Rogi ścina
   schodkowo ten sam wzór na każdej warstwie, tylko z mniejszym progiem —
   dzięki temu ramy wyglądają na równoległe.

   Wzór stoi w jednej zmiennej i czyta `--prog-duzy` / `--prog-maly`
   **w miejscu użycia**, więc każda warstwa podstawia własne progi zamiast
   powtarzać dwudziestowierzchołkowy wielokąt trzy razy. Te same schodki
   w mniejszej skali biorą kafelki wyboru, ramka ikonki i płytka kodu. */
/* Wzór stoi w jednej deklaracji, ale **na wszystkich trzech warstwach
   naraz** — i to jest tu cała sztuczka. Zmienna nie może być zadeklarowana
   tylko na rodzicu: wartość własnej właściwości wylicza się na elemencie,
   w którym stoi, więc `var(--prog-duzy)` zamieniłoby się tam na 10px,
   a warstwy odziedziczyłyby gotowy wielokąt z progiem rodzica. Wypisana
   dla każdej warstwy, rozwiązuje się u każdej z osobna — jeden zapis
   wielokąta, trzy różne progi. */
.px-rama,.px-rama::before,.px-rama::after{
  --px-schodek:polygon(
    0 var(--prog-duzy),var(--prog-maly) var(--prog-duzy),
    var(--prog-maly) var(--prog-maly),var(--prog-duzy) var(--prog-maly),
    var(--prog-duzy) 0,
    calc(100% - var(--prog-duzy)) 0,
    calc(100% - var(--prog-duzy)) var(--prog-maly),
    calc(100% - var(--prog-maly)) var(--prog-maly),
    calc(100% - var(--prog-maly)) var(--prog-duzy),100% var(--prog-duzy),
    100% calc(100% - var(--prog-duzy)),
    calc(100% - var(--prog-maly)) calc(100% - var(--prog-duzy)),
    calc(100% - var(--prog-maly)) calc(100% - var(--prog-maly)),
    calc(100% - var(--prog-duzy)) calc(100% - var(--prog-maly)),
    calc(100% - var(--prog-duzy)) 100%,var(--prog-duzy) 100%,
    var(--prog-duzy) calc(100% - var(--prog-maly)),
    var(--prog-maly) calc(100% - var(--prog-maly)),
    var(--prog-maly) calc(100% - var(--prog-duzy)),
    0 calc(100% - var(--prog-duzy)))}
.px-rama{
  --prog-duzy:10px;
  --prog-maly:4px;
  position:relative;
  background:#140B05;
  border:0;
  border-radius:0;
  clip-path:var(--px-schodek);
  /* twardy pikselowy cień pod całą ramą — `filter` idzie PO clip-path,
     więc cień powtarza schodkowy obrys zamiast być prostokątem */
  filter:drop-shadow(0 5px 0 rgba(0,0,0,.35));
}
.px-rama::before,.px-rama::after{content:'';position:absolute;z-index:0;
  clip-path:var(--px-schodek)}
/* rama zeszczuplona po pierwszym oglądzie (2 px obrysu + 4 px koloru —
   gruba wersja 3+6 przytłaczała karty) */
.px-rama::before{inset:2px;--prog-duzy:8px;--prog-maly:3px;background:var(--k)}
.px-rama::after{inset:6px;--prog-duzy:6px;--prog-maly:2px;
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--k) 62%,#170C04) 0%,
    color-mix(in srgb,var(--k) 40%,#170C04) 100%)}
.px-rama > *{position:relative;z-index:1}

/* ── tabliczka i przycisk paska (PixelTabliczka / PixelPrzycisk) ─────────
   Drewno jak na makiecie: gruba ciemna obwódka, rozjaśniona górna
   i przyciemniona dolna krawędź robią „fazowanie" bez obrazka. Nazwy
   `.pxpanel` / `.pxbtn` zostają — takie już stoją w markupie katalogu. */
.pxpanel{background:#43280F;border:3px solid #1C0F04;border-radius:9px;
  box-shadow:inset 0 3px 0 rgba(255,205,120,.22), inset 0 -3px 0 rgba(0,0,0,.45)}
.pxbtn{width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  color:var(--amber,#FFAE2B);flex:none}
.pxbtn svg{width:24px;height:24px;fill:currentColor}
.pxbtn[disabled],.pxbtn.off{color:#9A8B79}

/* ── deska nagłówka (DeskaNaglowka) ─────────────────────────────────────
   Drewniana deska z choinkami po bokach. Choinka jest tą samą ścieżką SVG
   co w apce, wklejoną jako data-URI — żaden plik z sieci. */
.px-deska{display:flex;align-items:center;justify-content:center;
  gap:9px;padding:7px 14px;
  font-family:var(--px-jersey);font-weight:400;text-transform:uppercase;
  font-size:17px;letter-spacing:.06em;color:#FFC44D;
  text-shadow:0 2px 0 rgba(0,0,0,.5);
  background:#4A2A12;border:3px solid #241203;border-radius:8px;
  box-shadow:inset 0 2px 0 rgba(255,205,120,.18), inset 0 -2px 0 rgba(0,0,0,.4)}
.px-deska::before,.px-deska::after{content:'';width:15px;height:17px;
  flex:none;image-rendering:pixelated;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 14'%3E%3Cpath fill='%233E8F4A' d='M6 0 2 5h2L1 9h3L2 12h8L8 9h3L8 5h2z'/%3E%3Crect x='5' y='12' width='2' height='2' fill='%235C3A1E'/%3E%3C/svg%3E") no-repeat center/contain}

/* ── pasek klatek ───────────────────────────────────────────────────────
   Animacja z jednego obrazka ułożonego w rządek: tło szerokie na N ekranów,
   przesuwane skokowo `steps(N)`. Skok to `100/(N−1)%`, nie `100%` — przy
   ośmiu klatkach ostatnia pozycja to 7/8 szerokości, nie 8/8.
   Klatkowanie jest **skokowe**, bez płynnych przejść: to pixel art. */
.px-klatki{--px-klatek:8;
  background-size:calc(var(--px-klatek) * 100%) 100%;
  image-rendering:pixelated;
  animation:px-klatki var(--px-czas,1.04s) steps(var(--px-klatek)) infinite
    var(--px-faza,0s)}
@keyframes px-klatki{
  from{background-position-x:0}
  to{background-position-x:calc(100% * var(--px-klatek) / (var(--px-klatek) - 1))}}

/* ── nocne niebo (TloPixel) ─────────────────────────────────────────────
   Nocny las na całym ekranie. Kolor pod spodem = niebo z góry obrazka,
   żeby wolne łącze nie mignęło niczym obcym, zanim dojedzie plik.
   Gwiazdy i meteor rysuje canvas nad tłem (pixel.js) — w obrazku
   mogłyby najwyżej dostać poświatę, a mają migotać skokowo. */
.px-niebo{background:var(--px-niebo)}
/* `#tlo` to niebo katalogu; `.px-las` — to samo niebo pod nakładkami,
   które stają NAD katalogiem (skaner, profil, instruktaż). Jedna reguła,
   bo kadr zależy od orientacji ekranu (blok `@media` niżej) i pięć kopii
   tej samej ścieżki rozjechałoby się przy pierwszej zmianie. */
#tlo,.px-las{position:fixed;inset:0;z-index:0;
  background:var(--px-niebo,#0B0919) url(/pixel/tlo.png) center/cover no-repeat;
  image-rendering:pixelated}
#gwiazdy{position:fixed;inset:0;z-index:0;pointer-events:none;
  width:100%;height:100%;image-rendering:pixelated}

/* ── scena ogniska (ScenaOgniska) ───────────────────────────────────────
   Czysta oprawa NAD treścią (`pointer-events:none` — tapnięcia przechodzą).
   Warstwy od wierzchu: chłopiec > lampa > ognisko > namiot > treść > tło.
   Kontener ma zerową wysokość i siedzi przy dole ekranu: dzieci ustawiają
   się szerokością w procentach, więc kompozycja jest jedna na każdy
   telefon. Sufit 560 px trzyma laptopy w ryzach (kolumna kart ma 420).

   Chłopiec jest **parametrem**: ekran podmienia jego kadr, szerokość
   i odsunięcie od lewej (`--px-chlopiec`, `--px-chlopiec-szer`,
   `--px-chlopiec-lewo`) — gablota i skaner mają go większego i dalej
   od krawędzi, bo tam zasłaniał treść. */
.px-scena{
  --px-chlopiec:url(/pixel/chlopiec.png);
  --px-chlopiec-szer:33%;
  --px-chlopiec-lewo:10%;
  position:fixed;left:50%;transform:translateX(-50%);bottom:0;
  width:min(100vw,560px);height:0;pointer-events:none;z-index:40}
/* Warstwy nieruchome mieszczą obrazek w swoim polu; **paski klatek nie** —
   te mają swoją szerokość z `.px-klatki` (N ekranów obok siebie), a
   `contain` upchnąłby cały rządek klatek w kadrze i widać by było
   wszystkie osiem naraz. */
.px-scena .px{position:absolute;bottom:0;background-repeat:no-repeat;
  background-position:bottom center;image-rendering:pixelated}
.px-scena .px:not(.px-klatki){background-size:contain}
/* krzaki: rama pierwszego planu — treść chowa się ZA nie, a ognisko,
   chłopiec, lampa i namiot stoją NA nich (najniższy z-index sceny) */
.px-scena .krzaki{left:-6%;width:112%;aspect-ratio:1536/1024;
  background-image:url(/pixel/krzaki.png);z-index:0}
/* proporcje wg makiety (2026-08-27): namiot mniejszy i dosunięty w lewo
   (ledwo ucięty krawędzią), człowieczek i lampa więksi, ognisko uniesione
   na rozświetloną polanę krzaków */
.px-scena .namiot{right:-2%;width:42%;aspect-ratio:1;
  background-image:url(/pixel/namiot.png);z-index:1;bottom:-2px}
.px-scena .ognisko{left:37%;width:26%;aspect-ratio:543/724;
  background-image:url(/pixel/ognisko.png);z-index:2;bottom:22px;
  --px-czas:1.04s}
.px-scena .lampa{left:.5%;width:18%;aspect-ratio:543/724;
  background-image:url(/pixel/lampa.png);z-index:3;bottom:8px;
  /* wolniej i z przesunięciem — zsynchronizowane migotanie czyta się
     jak usterka */
  --px-czas:1.8s;--px-faza:.3s}
.px-scena .chlopiec{left:var(--px-chlopiec-lewo);width:var(--px-chlopiec-szer);
  aspect-ratio:1;background-image:var(--px-chlopiec);z-index:4;bottom:2px}

/* ── ekran szerszy niż wyższy: desktop, tablet i telefon w poziomie ───────
   Pionowe `tlo.png` pod `cover` pokazuje na monitorze sam środek kadru:
   ścianę choinek od krawędzi do krawędzi, bez księżyca i bez polany,
   a scena ogniska (560 px) urywa się na desktopie twardą krawędzią
   `krzaki.png`. Tryb poziomy bierze więc **drugi świat** z tego samego
   generatora (`web/tools/pixel-panorama.py`): panoramę 1536×1024
   kotwiczoną NA DOLE (kadr ucina górę nieba, nie polanę) i — zamiast
   krzaków — **dolny pas tej samej panoramy z alfą**, rozciągnięty na
   `max(100vw, 150vh)`, czyli dokładnie w skali `cover` (1536/1024 = 1,5).
   Pas leży wtedy pixel w pixel na niebie pod spodem, więc nie ma szwu
   do maskowania, a karty chowają się w krzakach na całą szerokość.
   Rekwizyty zostają w tych samych pikselach (procenty z 560 przeliczone
   na 1000), ale rozsuwają się: lampa na lewo od kolumny kart, namiot
   na prawo od niej, chłopiec z ogniskiem pod jej dołem — jak w pionie.
   Próg 700 px odsiewa telefon w pionie; telefon w poziomie wchodzi,
   bo panorama i tak leży lepiej niż środek pionu. **Ten sam warunek
   siedzi w pixel.js (`POZIOM`)** — mapa gwiazd idzie za obrazkiem. */
@media (orientation:landscape) and (min-width:700px){
  #tlo,.px-las{background-image:url(/pixel/tlo-poziom.png);
    background-position:center bottom}
  .px-scena{width:min(100vw,1000px)}
  .px-scena .krzaki{left:50%;transform:translateX(-50%);
    width:max(100vw,150vh);aspect-ratio:1536/324;
    background-image:url(/pixel/krzaki-poziom.png)}
  .px-scena .namiot{width:24%;right:4%}
  .px-scena .ognisko{width:15%;left:42.5%}
  .px-scena .lampa{width:10.5%;left:12%}
  /* szerokość kadru chłopca dalej podaje wariant (`--px-chlopiec-szer`,
     w skali sceny 560) — tu tylko przeliczona na scenę 1000 */
  .px-scena .chlopiec{width:calc(var(--px-chlopiec-szer) * .56);left:27%}
}

/* ── klawisze (kontrolki.dart) ───────────────────────────────────────────
   Wszystkie biorą ten sam schodkowy obrys co rama kafelka, więc dziedziczą
   `--px-schodek` — progi podaje każdy z osobna. `clip-path` obcina do
   schodków, a `filter:drop-shadow` idzie PO nim, więc cień powtarza
   schodkowy obrys zamiast być prostokątem.

   Napis jedzie Jersey 25 wersalikami. `letter-spacing` liczy się od
   rozmiaru, tak jak w apce (.5 px przy 19). */
.px-klawisz,.px-klawisz::before,
.px-belka,.px-belka::before,
.px-duch,.px-duch::before,.px-duch::after{
  --px-schodek:polygon(
    0 var(--prog-duzy),var(--prog-maly) var(--prog-duzy),
    var(--prog-maly) var(--prog-maly),var(--prog-duzy) var(--prog-maly),
    var(--prog-duzy) 0,
    calc(100% - var(--prog-duzy)) 0,
    calc(100% - var(--prog-duzy)) var(--prog-maly),
    calc(100% - var(--prog-maly)) var(--prog-maly),
    calc(100% - var(--prog-maly)) var(--prog-duzy),100% var(--prog-duzy),
    100% calc(100% - var(--prog-duzy)),
    calc(100% - var(--prog-maly)) calc(100% - var(--prog-duzy)),
    calc(100% - var(--prog-maly)) calc(100% - var(--prog-maly)),
    calc(100% - var(--prog-duzy)) calc(100% - var(--prog-maly)),
    calc(100% - var(--prog-duzy)) 100%,var(--prog-duzy) 100%,
    var(--prog-duzy) calc(100% - var(--prog-maly)),
    var(--prog-maly) calc(100% - var(--prog-maly)),
    var(--prog-maly) calc(100% - var(--prog-duzy)),
    0 calc(100% - var(--prog-duzy)))}

/* Fazowany klawisz — główna akcja. Wnętrze to gradient o **twardych
   progach** (0 / .14 / .86 / 1): trzy pasy, nie płynne przejście — gładki
   gradient rozmyłby fazowanie, a to ma wyglądać jak wytłoczona płytka. */
.px-klawisz{
  --px-baza:#FFB52E; --px-jasny:#FFD983; --px-ciemny:#C77D14;
  --prog-duzy:8px; --prog-maly:3px;
  position:relative;display:flex;align-items:center;justify-content:center;
  height:52px;padding:0 16px;border:0;background:#1C0F04;
  clip-path:var(--px-schodek);
  filter:drop-shadow(0 4px 0 rgba(0,0,0,.35));
  font-family:var(--px-jersey);font-weight:400;text-transform:uppercase;
  font-size:19px;line-height:1;letter-spacing:.5px;color:#3A2306;
  transition:transform .1s}
/* wnętrze odsunięte o 2 px, własne progi 7/3 — jak `r.deflate(2)` w apce */
.px-klawisz::before{content:'';position:absolute;inset:2px;z-index:0;
  --prog-duzy:7px;--prog-maly:3px;clip-path:var(--px-schodek);
  background:linear-gradient(180deg,
    var(--px-jasny) 0 14%,var(--px-baza) 14% 86%,var(--px-ciemny) 86% 100%)}
.px-klawisz > *{position:relative;z-index:1}
.px-klawisz:active{transform:scale(.96)}
.px-klawisz[disabled]{opacity:.55}
/* drewniany — ciemniejszy wariant, napis z cieniem (na bursztynie cień
   brudził litery, na drewnie jest potrzebny) */
.px-klawisz.drewno{--px-baza:#43280F;--px-jasny:#6A431F;--px-ciemny:#2A1708;
  color:#F6EFE0;text-shadow:0 1px 0 rgba(0,0,0,.5)}
/* ostrożny (glina) — akcja, którą warto przemyśleć */
.px-klawisz.glina{--px-baza:#D9603B;--px-jasny:#F0906B;--px-ciemny:#A03A20}

/* Płaska belka — klawisz **delikatniejszy** od bursztynowego, nie cichszy:
   jednolite wypełnienie z lekkim spadkiem, jasna obwódka, ciemny obrys.
   Trzy takie stoją jedna pod drugą jako trzy równorzędne drogi; trzy
   fazowane klawisze czytałyby się jak trzy akcje główne naraz. */
.px-belka{
  --px-baza:#B4762A; --px-obwodka:#E8B14E;
  --prog-duzy:6px; --prog-maly:2px;
  position:relative;display:flex;align-items:center;justify-content:center;
  gap:8px;height:46px;padding:0 14px;border:0;background:#241203;
  clip-path:var(--px-schodek);
  font-family:var(--px-jersey);font-weight:400;text-transform:uppercase;
  font-size:18px;line-height:1;color:#FFF3DC;transition:transform .1s}
.px-belka::before{content:'';position:absolute;inset:2px;z-index:0;
  --prog-duzy:5px;--prog-maly:2px;clip-path:var(--px-schodek);
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--px-baza) 92%,#FFFFFF),
    color-mix(in srgb,var(--px-baza) 88%,#000000));
  box-shadow:inset 0 0 0 2px var(--px-obwodka)}
.px-belka > *{position:relative;z-index:1}
.px-belka svg{width:20px;height:20px;fill:currentColor;flex:none}
.px-belka:active{transform:scale(.96)}
.px-belka[disabled]{opacity:.55}

/* Duch — klawisz najcichszy: **sam pierścień** i ledwo widoczne wnętrze.
   Trzy warstwy jak w kafelku wyboru (obrys 8/3, pierścień 6/2 odsunięty
   o 2, wnętrze 5/2 odsunięte o 5) — pierścień musi zostać pierścieniem,
   zalany na całe pole robi z ducha szarą płytę. */
.px-duch{
  --px-pierscien:rgba(255,243,208,.35);
  --prog-duzy:8px; --prog-maly:3px;
  position:relative;display:flex;align-items:center;justify-content:center;
  height:46px;padding:0 14px;border:0;background:#140B05;
  clip-path:var(--px-schodek);
  font-family:var(--px-jersey);font-weight:400;text-transform:uppercase;
  font-size:18px;line-height:1;color:#F6EFE0;transition:transform .1s}
.px-duch.akcent{--px-pierscien:#FFAE2B}
.px-duch::before,.px-duch::after{content:'';position:absolute;z-index:0;
  clip-path:var(--px-schodek)}
.px-duch::before{inset:2px;--prog-duzy:6px;--prog-maly:2px;
  background:var(--px-pierscien)}
/* W apce te trzy ścieżki są rozłączne (różnice), więc wnętrze `#66120A04`
   kładzie się na **tle strony**, nie na pierścieniu. W CSS warstwy się
   nakładają, a półprzezroczysta zasłona przepuściłaby bursztyn i akcent
   miałby środek w kolorze obwódki. Wpisujemy więc wynik złożenia na
   ciemnym blacie; jasna kartka ma własny wariant niżej. */
.px-duch::after{inset:5px;--prog-duzy:5px;--prog-maly:2px;
  background:#130B05}
.px-duch > *{position:relative;z-index:1}
.px-duch:active{transform:scale(.96)}
/* Wgłębienie na **jasnej kartce**: ciemna obwódka na papierze ginie, więc
   rysunek jest z dwóch pierścieni — obwódka i wnętrze ciemniejsze od
   kartki, żeby pole wyglądało na wycięte, nie doklejone. */
.px-duch.napapierze{--px-pierscien:#B9AC93;color:#3A2306;
  background:var(--px-pierscien)}
.px-duch.napapierze.akcent{--px-pierscien:#B9741F}
.px-duch.napapierze::before{background:#E9E0CC}
.px-duch.napapierze::after{display:none}

/* ── kafelek wyboru (PixelWybor) ────────────────────────────────────────
   Trzy warstwy jak rama kafelka, ale ciaśniejsze: obrys 8/3, rama 6/2
   odsunięta o 2, wnętrze 5/2 odsunięte o 5. Wybrany świeci bursztynem,
   niewybrany chowa się w drewnie. */
.px-wybor,.px-wybor::before,.px-wybor::after{
  --px-schodek:polygon(
    0 var(--prog-duzy),var(--prog-maly) var(--prog-duzy),
    var(--prog-maly) var(--prog-maly),var(--prog-duzy) var(--prog-maly),
    var(--prog-duzy) 0,
    calc(100% - var(--prog-duzy)) 0,
    calc(100% - var(--prog-duzy)) var(--prog-maly),
    calc(100% - var(--prog-maly)) var(--prog-maly),
    calc(100% - var(--prog-maly)) var(--prog-duzy),100% var(--prog-duzy),
    100% calc(100% - var(--prog-duzy)),
    calc(100% - var(--prog-maly)) calc(100% - var(--prog-duzy)),
    calc(100% - var(--prog-maly)) calc(100% - var(--prog-maly)),
    calc(100% - var(--prog-duzy)) calc(100% - var(--prog-maly)),
    calc(100% - var(--prog-duzy)) 100%,var(--prog-duzy) 100%,
    var(--prog-duzy) calc(100% - var(--prog-maly)),
    var(--prog-maly) calc(100% - var(--prog-maly)),
    var(--prog-maly) calc(100% - var(--prog-duzy)),
    0 calc(100% - var(--prog-duzy)))}
.px-wybor{--prog-duzy:8px;--prog-maly:3px;
  position:relative;border:0;background:#140B05;padding:10px 12px;
  text-align:center;clip-path:var(--px-schodek)}
.px-wybor::before,.px-wybor::after{content:'';position:absolute;z-index:0;
  clip-path:var(--px-schodek)}
.px-wybor::before{inset:2px;--prog-duzy:6px;--prog-maly:2px;background:#46311C}
.px-wybor::after{inset:5px;--prog-duzy:5px;--prog-maly:2px;
  background:rgba(28,18,8,.9)}
.px-wybor[aria-pressed="true"]::before{background:#FFAE2B}
.px-wybor[aria-pressed="true"]::after{background:#3A2610}
.px-wybor > *{position:relative;z-index:1}
.px-wybor b{display:block;font-family:var(--px-tekst);font-size:14px;
  font-weight:800;color:#C9BBA4}
.px-wybor small{display:block;font-family:var(--px-tekst);font-size:11px;
  font-weight:700;color:#9A8B79}
.px-wybor[aria-pressed="true"] b{color:#FFAE2B}
.px-wybor[aria-pressed="true"] small{color:#F6EFE0}

/* ── tytuł gry (TytulGryPixel) ──────────────────────────────────────────
   Jersey w granatowym obrysie: osiem twardych cieni po ±2 px daje obwódkę
   bez `-webkit-text-stroke` (ten ścina litery od środka), plus cień pod
   spodem. Wersalikami, jak wszystko pisane tym krojem. */
.px-tytul{font-family:var(--px-jersey);font-weight:400;text-transform:uppercase;
  font-size:44px;line-height:1;color:#FFE9A8;
  text-shadow:
    2px 0 0 #0B0919,-2px 0 0 #0B0919,0 2px 0 #0B0919,0 -2px 0 #0B0919,
    2px 2px 0 #0B0919,-2px 2px 0 #0B0919,2px -2px 0 #0B0919,-2px -2px 0 #0B0919,
    0 4px 0 rgba(26,13,3,.8)}
/* skaner ma go odrobinę większy, niski ekran — mniejszy */
.px-tytul.skaner{font-size:46px}
@media (max-height:640px){.px-tytul{font-size:32px}}
/* Wariant z choinkami (nagłówek stołu): mniejszy i **bez obrysu** — nad
   planszą granatowa obwódka robiła się drugim tytułem obok deski. */
.px-tytul.zchoinkami{display:flex;align-items:center;justify-content:center;
  gap:9px;font-size:34px;text-shadow:0 3px 0 rgba(26,13,3,.8)}
.px-tytul.zchoinkami::before,.px-tytul.zchoinkami::after{content:'';
  width:15px;height:17px;flex:none;image-rendering:pixelated;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 14'%3E%3Cpath fill='%233E8F4A' d='M6 0 2 5h2L1 9h3L2 12h8L8 9h3L8 5h2z'/%3E%3Crect x='5' y='12' width='2' height='2' fill='%235C3A1E'/%3E%3C/svg%3E") no-repeat center/contain}

/* ── fiszka: papier w trzech pasach (FiszkaPixel) ───────────────────────
   Kartka rośnie z treścią, ale **nie rozciąga się byle jak**: górny pas
   (spinacz, zagięcie) i dolny skalują się z **szerokością**, więc zostają
   w proporcjach; rozciąga się tylko środek. To ten sam podział, co
   `drawImageRect` × 3 w apce.

   Pasy dostają wysokość z `aspect-ratio`, a wycinek obrazka — z procentów:
   `background-size` w procentach liczy się od pola pasa, więc
   `--f-h / --f-gora` daje dokładnie tę część obrazka, która ma tam wejść.
   Środek przesuwa się o `--f-gora / (--f-gora + --f-dol)`, bo tyle wynosi
   udział górnego pasa w tym, co z obrazka zostaje poza środkiem.

   Użycie:
     <div class="px-fiszka px-fiszka-karta">
       <div class="px-fiszka-tlo" aria-hidden="true"><i></i><i></i><i></i></div>
       <div class="px-fiszka-tresc">…</div>
     </div> */
/* Papier i treść leżą w **jednej komórce siatki**, nie jedno nad drugim
   w `position:absolute`. Absolutne tło nie dokłada się do wysokości, więc
   kartka była niższa niż jej własne pasy: środek schodził do zera, a z
   papieru zostawały dwa paski. W siatce wiersz jest najwyższy z dwojga,
   czyli nigdy niższy niż górny plus dolny pas. Gdy ktoś narzuci kartce
   niższą wysokość, oba pasy ścisną się proporcjonalnie — tak samo jak
   w apce, gdzie painter dzieli je przez wspólny współczynnik. */
.px-fiszka{display:grid}
.px-fiszka > .px-fiszka-tlo,.px-fiszka > .px-fiszka-tresc{grid-area:1/1}
.px-fiszka-tlo{z-index:0;display:flex;flex-direction:column;
  pointer-events:none}
.px-fiszka-tlo > i{display:block;background-image:var(--f-obraz);
  background-repeat:no-repeat;image-rendering:pixelated}
.px-fiszka-tlo > i:nth-child(1){aspect-ratio:var(--f-w)/var(--f-gora);
  background-size:100% calc(var(--f-h) / var(--f-gora) * 100%);
  background-position:0 0}
.px-fiszka-tlo > i:nth-child(2){flex:1;min-height:0;
  background-size:100% calc(var(--f-h) /
    (var(--f-h) - var(--f-gora) - var(--f-dol)) * 100%);
  background-position:0 calc(var(--f-gora) /
    (var(--f-gora) + var(--f-dol)) * 100%)}
.px-fiszka-tlo > i:nth-child(3){aspect-ratio:var(--f-w)/var(--f-dol);
  background-size:100% calc(var(--f-h) / var(--f-dol) * 100%);
  background-position:0 100%}
/* Marginesy treści **w ułamku szerokości** — inaczej na szerokiej kartce
   napis siada na zagięciu papieru, a na wąskiej odkleja się od niego.
   Procent wyściółki liczy się od szerokości, więc wystarczy jeden zapis. */
.px-fiszka-tresc{position:relative;z-index:1;
  padding:calc(var(--f-gora) / var(--f-w) * var(--f-pad, 45) * 1%)
          calc(var(--f-gora) / var(--f-w) * var(--f-pad, 45) * .8%)}

/* Miary pasów zmierzone na plikach — szerokość i wysokość obrazka,
   potem górny pas i dolny. */
.px-fiszka-karta{--f-obraz:url(/pixel/fiszka.png);
  --f-w:1085;--f-h:1282;--f-gora:250;--f-dol:250}
/* konto i „zaproś" mają górny pas odrobinę niższy — mniej zagięcia nad treścią */
.px-fiszka-konto{--f-obraz:url(/pixel/fiszka.png);
  --f-w:1085;--f-h:1282;--f-gora:240;--f-dol:250}
.px-fiszka-wysoka{--f-obraz:url(/pixel/fiszka-wyzsza.png);
  --f-w:1081;--f-h:1455;--f-gora:250;--f-dol:260}
.px-fiszka-pergamin{--f-obraz:url(/pixel/biurko/pergamin.png);
  --f-w:1024;--f-h:348;--f-gora:70;--f-dol:70}
.px-fiszka-podkladka{--f-obraz:url(/pixel/biurko/podkladka.png);
  --f-w:642;--f-h:865;--f-gora:110;--f-dol:110}
.px-fiszka-plakietka{--f-obraz:url(/pixel/plakietka.png);
  --f-w:1080;--f-h:493;--f-gora:160;--f-dol:100}
.px-fiszka-kartka{--f-obraz:url(/pixel/kartka.png);
  --f-w:960;--f-h:612;--f-gora:120;--f-dol:150}

/* ── 9-slice (SliceObraz / drawImageNine) ───────────────────────────────
   Rama rośnie z treścią, ale rogi zostają w swoich rozmiarach — rozciągają
   się tylko jednolite pasy. `fill` maluje też środek, `pixelated` trzyma
   piksele ostre. Kadry podaje się w pikselach źródła (`--s-*`), a grubość
   ramki w pikselach ekranu (`--s-ramka`) — to ona decyduje, jak duże są
   rogi po narysowaniu. */
.px-slice{border-style:solid;border-color:transparent;
  border-image-source:var(--s-obraz);
  border-image-slice:var(--s-gora) var(--s-prawo) var(--s-dol) var(--s-lewo) fill;
  border-image-repeat:stretch;
  border-width:var(--s-ramka,12px);
  image-rendering:pixelated}

/* ── dymek (core/pixel/dymek.dart) ──────────────────────────────────────
   Rama jest **obrazkiem** i rośnie z treścią jak fiszka: `drawImageNine`
   rozciąga same jednolite pasy. Kadr środkowy to `(55,50)–(700,190)`
   w pikselach pliku (960×312), więc kadry to 50 / 260 / 122 / 55 —
   prawy jest szeroki, bo siedzi w nim ogon, a dolny wysoki, bo pod
   środkiem zostaje zaokrąglenie ramy.

   Plik jest w rozdzielczości ekranu ×3 (jak reszta szaty), więc grubość
   ramki na ekranie to kadry podzielone przez trzy. Minimalna wysokość
   58 px to suma kadru górnego i dolnego — niżej dymek zjadałby własne
   narożniki. */
.px-dymek{
  position:fixed;right:12px;bottom:calc(env(safe-area-inset-bottom) + 14px);
  z-index:120;pointer-events:none;
  min-width:170px;min-height:58px;max-width:320px;
  box-sizing:border-box;
  display:flex;align-items:center;justify-content:center;text-align:center;
  padding:17px 26px 45px;
  border-style:solid;border-color:transparent;
  border-width:16.667px 86.667px 40.667px 18.333px;
  border-image-source:url(/pixel/dymek.png);
  border-image-slice:50 260 122 55 fill;
  border-image-repeat:stretch;
  image-rendering:pixelated;
  /* Bez wersalików: apka pisze dymek tak, jak stoi w słowniku
     (`dymek.dart` nie ma `toUpperCase`), a „MINA! WYPADŁEŚ Z TEJ PLANSZY."
     czytało się jak krzyk zamiast jak zdanie od telefonu. */
  font-family:var(--px-jersey);font-weight:400;
  font-size:22px;line-height:1.1;letter-spacing:.5px;color:#4A2B0E;
  /* dopóki nie ma treści, dymka nie ma wcale */
  opacity:0;visibility:hidden;
  /* „odbicie" easeOutBack rusza **tylko skalę** — fade idzie po prostej */
  transform:scale(.82);transform-origin:100% 100%;
  transition:opacity 180ms linear,
    transform 180ms cubic-bezier(.34,1.56,.64,1),
    visibility 0s linear 180ms}
.px-dymek.widac{opacity:1;visibility:visible;transform:scale(1);
  transition-delay:0s,0s,0s}
/* Na desktopie dymek siada przy prawej krawędzi **kadru** (lobby 560,
   stół min(560, 50vh) — patrz .bk-kadr w biurko.js), nie w rogu okna:
   ma mówić telefon leżący na środku biurka, a nie monitor. */
@media (min-width:700px) and (min-height:500px){
  .px-dymek{right:max(12px,calc(50% - min(280px,25vh)))}}
.px-dymek.znika{opacity:0;transform:scale(1);
  transition:opacity 140ms linear,visibility 0s linear 140ms}

/* ── rama planszy ───────────────────────────────────────────────────────
   Drewniana rama z twardym pikselowym cieniem — plansza zostaje treścią,
   rama robi z niej rekwizyt leżący na kempingu. Lustro `PixelRamaPlanszy`
   (core/pixel/gra.dart). W stopce (pod planszą, wciąż na desce ramy)
   siada to, co należy do stołu, a nie do planszy: obsada, legenda barw,
   licznik rundy. */
.px-plansza{box-sizing:border-box;display:flex;flex-direction:column;
  min-height:0;padding:4px;background:#43280F;
  border:3px solid #1C0F04;border-radius:6px;
  box-shadow:0 5px 0 rgba(0,0,0,.35)}
/* Wnętrze wypełnia ramę **co do piksela** i nic nie ma prawa z niej
   wyjść. Gra, której cząstki mają prawo wylecieć poza planszę (konfetti
   czwórki), dostaje `.bezciecia`: iskry przechodzą wtedy nad ramą
   i blatem, zamiast ucinać się równo z krawędzią drewna — czyli
   dokładnie w chwili, dla której powstały. */
.px-plansza > .px-plansza-wnetrze{min-height:0;flex:0 1 auto;
  border-radius:2px;overflow:hidden}
.px-plansza.bezciecia > .px-plansza-wnetrze{overflow:visible}
.px-plansza > .px-plansza-stopka{padding:5px 2px 1px}

/* ── deska, listwa, pasek stanu, tabliczka ──────────────────────────────
   Lustro `PixelDeska`, `PixelListwa`, `PixelPasekStanu` (gra.dart)
   i `PixelTabliczka` (pixel.dart). Deska nagłówka (`.px-deska` wyżej)
   to ta sama płyta z choinkami po bokach — `bezchoinek` zdejmuje je
   i oddaje krój treści, bo ta wersja bierze dowolną zawartość. */
.px-deska.bezchoinek{padding:7px 12px;gap:0;
  font-family:inherit;font-weight:inherit;text-transform:none;
  font-size:inherit;letter-spacing:normal;color:inherit;text-shadow:none}
.px-deska.bezchoinek::before,.px-deska.bezchoinek::after{display:none}
/* ciemniejsza wersja na podpowiedź — mniej krzyczy niż drewno paska */
.px-deska.bezchoinek.ciemna{background:rgba(22,12,4,.7);
  border:2px solid #43280F;box-shadow:none;
  font-family:var(--px-tekst,inherit);font-size:12px;font-weight:600;
  color:#E8D9B0}

/* Rama listwy jest **obrazkiem**, nie rysunkiem w kodzie: środek jest
   jednolity, więc rozciągnięcie w poziomie niczego nie psuje przy
   proporcjach zwykłego paska. */
.px-listwa{box-sizing:border-box;width:100%;display:flex;align-items:center;
  justify-content:center;padding:10px 18px;
  background:url(/pixel/listwa.png) center/100% 100% no-repeat;
  image-rendering:pixelated}
/* Pasek stanu nad planszą: po lewej to, co gra liczy, po środku napis
   albo chip, po prawej dodatek gry. Przyciski stołu tu **nie siadają** —
   dzwonek, zegar, „?" i ✕ mają swój górny pasek i tak jest w każdej grze. */
.px-pasekstanu{display:flex;align-items:center;width:100%}
.px-pasekstanu > .srodek{flex:1;min-width:0;display:flex;
  align-items:center;justify-content:center}
.px-pasekstanu .napis{font-family:var(--px-jersey);font-size:24px;
  line-height:1.05;text-align:center;color:#F2E9D4;
  text-shadow:0 2px 0 rgba(0,0,0,.6)}
/* bursztyn zapala się, gdy ruch należy do gracza */
.px-pasekstanu .napis.twoj{color:#FFAE2B}

/* Tabliczka: drewno z fazowaniem **gradientem**, nie twardym pasem —
   światło schodzi w 14 % wysokości, cień wchodzi w ostatnich 14 %
   (`foregroundDecoration` w PixelTabliczka). */
.px-tabliczka{position:relative;display:inline-flex;align-items:center;
  gap:6px;padding:0 10px;background:#43280F;border:3px solid #1C0F04;
  border-radius:9px;color:#FFAE2B}
.px-tabliczka::after{content:'';position:absolute;inset:0;border-radius:6px;
  pointer-events:none;background:linear-gradient(180deg,
    rgba(255,205,120,.22) 0%,rgba(255,205,120,0) 14%,
    rgba(0,0,0,0) 86%,rgba(0,0,0,.45) 100%)}
.px-tabliczka > *{position:relative;z-index:1}
.px-tabliczka .liczba{font-family:var(--px-jersey);font-size:21px;
  font-variant-numeric:tabular-nums}

/* ── klawisz gry i pigułka obsady ───────────────────────────────────────
   Klawisz przy planszy (saper: „Kopię" / „Stawiam") — drewno z własnymi
   barwami stanu, więc `.px-tabliczka` nie wystarczy; fazowanie jest
   jednak to samo, co u niej. */
.px-klawiszgry{position:relative;box-sizing:border-box;height:56px;
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  padding:0 14px;background:#43280F;border:3px solid #1C0F04;
  border-radius:9px;cursor:pointer;
  font-family:var(--px-jersey);font-size:24px;line-height:1;color:#F6EFE0;
  text-shadow:0 2px 0 rgba(0,0,0,.5);
  transition:transform 120ms ease-out}
.px-klawiszgry::after{content:'';position:absolute;inset:0;border-radius:6px;
  pointer-events:none;background:linear-gradient(180deg,
    rgba(255,205,120,.22) 0%,rgba(255,205,120,0) 14%,
    rgba(0,0,0,0) 86%,rgba(0,0,0,.45) 100%)}
.px-klawiszgry > *{position:relative;z-index:1}
.px-klawiszgry svg{width:22px;height:22px;fill:currentColor;flex:none;
  color:#CDA972}
.px-klawiszgry:active{transform:scale(.97)}
/* stan włączony — narzędzie, które właśnie trzymasz w ręku */
.px-klawiszgry.wlaczony{background:#7A4A12;border-color:#FFAE2B;color:#FFE1A8}
.px-klawiszgry.wlaczony svg{color:#FFAE2B}

/* Pigułka obsady: portret, kropka w barwie gracza i wynik. Wypadnięty
   z planszy przygasa — zostaje na stole, ale już nie gra. */
.px-pigulka{display:inline-flex;align-items:center;gap:6px;
  padding:3px 8px;background:#43280F;border:2px solid #1C0F04;
  border-radius:8px}
.px-pigulka.twoja{border-color:#FFAE2B}
.px-pigulka.poza{opacity:.45}
.px-pigulka .kropka{width:8px;height:8px;flex:none;border-radius:99px}
/* zerwane gniazdo przygasza samą kropkę, nie całą pigułkę */
.px-pigulka.zerwana .kropka{opacity:.35}
.px-pigulka canvas.av,.px-pigulka img.av{width:22px;height:22px;flex:none;
  image-rendering:pixelated}
.px-pigulka .nm{max-width:92px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-family:var(--px-tekst,inherit);font-size:12px;
  font-weight:700;color:#CDA972}
.px-pigulka.twoja .nm{color:#F6EFE0}
.px-pigulka .wynik{font-family:var(--px-jersey);font-size:17px;line-height:1;
  color:#FFAE2B;font-variant-numeric:tabular-nums}

/* ── przejścia między nakładkami (trasaPixel) ───────────────────────────
   Czysty fade: 260 ms w przód, 200 ms w tył. Zoomu tu **nie ma** —
   dołożony kiedyś (1.03 → 1) skalował razem z warstwą kopię tła i całość
   drżała. Krzywa jak `Curves.easeOutCubic`. */
.px-trasa{transition:opacity 260ms cubic-bezier(.215,.61,.355,1)}
.px-trasa.wstecz{transition-duration:200ms}
.px-trasa.niewidoczna{opacity:0}
/* Kto prosi o mniej ruchu, dostaje podmianę bez animacji — nie skrócony
   fade, tylko żaden. */
@media (prefers-reduced-motion:reduce){
  .px-trasa{transition:none}
}
/* Wariant z tytułem (PixelWyborTytul): napis nad opisem, oba na środku. */
/* z opisem: ciaśniejsza wyściółka, żeby drugi wiersz się mieścił;
   wyrównanie to samo — napis na klawiszu stoi po środku w każdym rządku,
   inaczej „Wszyscy naraz" bez opisu siedział przy lewej, a „Po kolei"
   obok po środku */
.px-wybor.tytul{padding:10px 5px}

/* Atrybut `hidden` niesie `display:none` o **sile atrybutu** i przegrywa
   z każdą regułą klasy, która ustawia `display` — a te komponenty gry
   chowa i pokazuje wprost (klawisz powrotu, deska podpowiedzi). Bez tego
   `el.hidden = true` nie chowa niczego i wygląda to na błąd logiki. */
.px-klawisz[hidden],.px-duch[hidden],.px-deska[hidden],
.px-belka[hidden],.px-wybor[hidden],.px-tabliczka[hidden],
.px-klawiszgry[hidden],.px-pigulka[hidden],.px-listwa[hidden],
.px-pasekstanu[hidden],.px-plansza[hidden]{display:none !important}
