/* ============================================================
   KWFoto.pl — GALERIA REPORTAŻU · WYSTAWA AKTÓW
   WSPÓLNY arkusz wszystkich reportaży
   ============================================================
   Ten plik leży w `reportaze/`, a NIE w folderze pary — bo kod ma być
   jeden, a folderów par będzie wiele. Podstrona pary ładuje go ścieżką
   `../galeria.css`, tuż po wspólnym `../../style.css` (paleta, kroje,
   nagłówek, stopka, ziarno, kursor).

   ⚠️ KOPIUJĄC FOLDER PARY, TEGO PLIKU SIĘ NIE KOPIUJE. W folderze pary
   zostaje wyłącznie treść: `index.html` z danymi pary i opisami aktów
   oraz podfoldery `akt-1…akt-N` ze zdjęciami. Instrukcja: `CZYTAJ-TO.txt`.

   Strona ma dwa stany i jedną drogę między nimi:

     WYSTAWA  — ciemna sala, obrazy na ścianie, nad każdym lampa.
                Jeden ekran, bez przewijania.
     AKT      — pełna galeria jednego aktu: kolaż kadrów na całym ekranie.
                Wjeżdża NAD wystawę (`position: fixed`), więc wystawa
                nie musi nic o niej wiedzieć.
     PODGLĄD  — pojedynczy kadr na czarnym tle, strzałki, licznik.

   ⚠️ ŚWIATŁO JEST STANEM DOKUMENTU, nie klasą body: `<html data-zapal>`.
   Ustawia je skrypt wstawiony w `<head>` (jeszcze przed pierwszym
   malowaniem), więc sala jest ciemna od pierwszej klatki — bez mrugnięcia
   jasną stroną. Bez JavaScriptu atrybut nie powstaje i strona jest po
   prostu oświetlona: treść zostaje czytelna dla robotów i czytników.
   ============================================================ */

:root {
  /* --- POKRĘTŁA SALI -------------------------------------------------
     Szerokość obrazu = --jedn * --w (waga wpisana przy akcie w HTML).
     `min(…vw, …vh)` sprawia, że na niskim oknie obrazy same maleją —
     wystawa ma zmieścić się w JEDNYM kadrze, bez przewijania. */
  --jedn: clamp(7.5rem, min(15.5vw, 27vh), 17rem);
  --sciana-odstep: clamp(1rem, 2.6vw, 3.2rem);

  /* --- KINKIET: WYSOKOŚĆ I ODSTĘP OD RAMY -----------------------------
     Wyniesione do zmiennych 2026-09-06, bo ta sama liczba jest potrzebna
     w DWÓCH miejscach: przy samej lampie (`.lampa`) i przy ścianie, która
     musi zarezerwować nad sobą tyle miejsca, ile lampy nad nią wystają
     (`.sciana`, `padding-top`). Rozjazd między nimi znaczyłby lampę
     wchodzącą w tabliczkę z imionami — dokładnie to, co się stało
     u Pauliny i Adriana. */
  --lampa-wys: clamp(1.65rem, 2.7vw, 2.35rem);
  --lampa-luz: 0.5rem;

  /* --- ŚWIATŁO -------------------------------------------------------
     Ciepłe (~2700 K), SUBTELNE i rozproszone — nie punktowe i nie mocne.
     Dwie barwy, bo prawdziwe światło stygnie z odległością od źródła:
       --zar      tuż przy żarniku, prawie białe z ciepłym odcieniem
       --swiatlo  snop i łuny na kamieniu — wyraźnie cieplejsze
     Zmiana nastroju całej sali = zmiana TYCH DWÓCH linijek. */
  --zar: 255, 240, 215;
  --swiatlo: 255, 209, 150;

  /* Metal kinkietów — MOSIĄDZ. Na czarno-szarej ścianie to jedyny ciepły
     akcent poza samym światłem i to on trzyma oprawy przy palecie reszty
     serwisu (`--gold` w `style.css`). Przy ciemnym brązie kinkiety ginęły
     na tle kamienia i wyglądały jak cień, a nie jak lampa. */
  --lampa-metal: #c2a067;

  /* Miękkość światła: im większe rozmycie, tym bardziej rozproszony snop
     i tym mniej widać jego krawędzie. Poniżej ~8 px zaczyna wyglądać
     jak wycięty z papieru. */
  --snop-miekkosc: clamp(16px, 1.9vw, 32px);

  /* Tempo zapalania: ile trwa jedna lampa i co ile odpala następna.
     ⚠️ To wartości WYJŚCIOWE. `galeria.js` losuje z nich kolejność lamp
     i rozrzuca czasy o kilkanaście procent, żeby żadne dwie świetlówki
     nie łapały światła identycznie. */
  --zapal-czas: 1.25s;
  --zapal-odstep: 0.42s;

  /* Wysokość nagłówka — wpisuje ją `galeria.js`, żeby sala wiedziała,
     ile miejsca u góry jest zajęte. Wartość zapasowa na czas przed pomiarem. */
  --glowa: 66px;
}

/* ============================================================
   SALA — ciemne wnętrze, w którym wisi wystawa
   ============================================================ */
body.galeria {
  /* Neutralna czerń — ten sam odcień co kamień, tylko wygaszony.
     Widać ją w szparach wokół sali i pod ścianą. */
  background: #070707;
  overflow-x: hidden;
}

.sala {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: calc(var(--glowa) + clamp(1rem, 3vh, 2.6rem)) clamp(1rem, 3vw, 3rem)
           clamp(1.4rem, 4vh, 3rem);
  overflow: hidden;
}

/* ============================================================
   ŚCIANA — gładka, ciemna, z rozproszoną nakładką światła
   ============================================================
   Zwykła ciemna ściana galerii i miękka warstwa światła na niej.
   ŻADNEJ rzeźbionej faktury — wcześniej stał tu proceduralny łupany
   kamień (`feDiffuseLighting` na mapie wysokości, kilkanaście kroków
   filtra). Wyglądał źle i był drogi: przeglądarka musiała przefiltrować
   kafelek 1400 × 1400 px przy pierwszym malowaniu. Wypadł w całości —
   razem z nim zniknął jedyny poważny koszt malowania tej strony.

   ⚠️ NAKŁADKA ROZPRASZAJĄCA TO SAME GRADIENTY, bez `filter: blur()`.
   Gradient promienisty jest z definicji idealnie gładki — rozmycie nie
   dodałoby mu nic, a kosztowałoby rasteryzację całej warstwy. Miękkość
   bierze się z ROZMIARU plam (56–78% szerokości sali) i z niskiego
   krycia, nie z rozmywania.

   ⚠️ NA CIEMNYCH GRADIENTACH POJAWIA SIĘ PASMOWANIE (banding) — sąsiednie
   odcienie czerni potrafią ułożyć się w widoczne pasy. Ratuje nas wspólne
   ziarno `.grain` ze `style.css` (krycie 0,032 na całej stronie): działa
   jak dithering i rozbija granice pasm. Gdyby ziarno kiedyś znikło,
   ściana zacznie się prążkować.

   Warstwy od góry:
     1. ciepła łuna od kinkietów — wspólna poświata całego rzędu,
     2. dwie miękkie plamy przetarcia — łamią martwą płaskość,
     3. cień pod obrazami,
     4. winieta — zamyka kadr i zbiera wzrok do środka,
     5. sama ściana: pionowy gradient od górnej szarości do czerni.

   POKRĘTŁA: cztery przystanki w warstwie 5 (jasność ściany), krycie
   w warstwach 1–3 (siła nakładki), zasięg winiety w warstwie 4.
   ⚠️ Zmieniając jasność ściany, pamiętaj o ramach: czarna listwa musi
   zostać ciemniejsza od tła, inaczej obrazy zleją się ze ścianą.
   ============================================================ */
.sala-tlo {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    /* 1) ciepła łuna od kinkietów */
    radial-gradient(120% 76% at 50% 26%,
      rgba(var(--swiatlo), 0.1) 0%,
      rgba(var(--swiatlo), 0.045) 34%,
      rgba(var(--swiatlo), 0.012) 58%,
      rgba(0, 0, 0, 0) 76%),
    /* 2) miękkie przetarcia — nierówność oświetlenia, nie faktura */
    radial-gradient(62% 48% at 24% 32%,
      rgba(255, 252, 245, 0.035) 0%, rgba(255, 252, 245, 0) 72%),
    radial-gradient(56% 44% at 79% 27%,
      rgba(255, 252, 245, 0.026) 0%, rgba(255, 252, 245, 0) 74%),
    /* 3) cień pod obrazami */
    radial-gradient(78% 46% at 50% 92%,
      rgba(0, 0, 0, 0.34) 0%, rgba(0, 0, 0, 0) 72%),
    /* 4) winieta */
    radial-gradient(132% 96% at 50% 42%,
      rgba(0, 0, 0, 0) 30%,
      rgba(0, 0, 0, 0.42) 70%,
      rgba(0, 0, 0, 0.86) 100%),
    /* 5) ściana */
    linear-gradient(to bottom, #2c2c2c 0%, #242424 48%, #161616 78%, #0b0b0b 100%);
}

/* ------------------------------------------------------------
   DELIKATNA FAKTURA ŚCIANY + DROBINKI
   ------------------------------------------------------------
   Jedna warstwa, dwa szumy, żadnego reliefu: ledwie wyczuwalne ziarno
   na całej ścianie i rzadkie jasne drobinki, które łapią światło jak
   mika w kamieniu. Ma tylko zdjąć ze ściany martwą gładkość — nie
   udawać materiału. Poprzedni, rzeźbiony kamień poszedł do kosza
   właśnie dlatego, że próbował za dużo.

   ⚠️ SIŁA JEST POLICZONA, nie dobrana na oko. Zmierzone na ścianie
   o jasności 50 (tam gdzie wiszą obrazy): ziarno rusza piksel do
   **48–54**, czyli o dwa–cztery poziomy, a drobinki sięgają **75**.
   Faktury prawie nie widać i o to chodzi; widać drobinki.

   ⚠️ PRÓG DROBINEK JEST ZMIERZONY. `feTurbulence` prawie nigdy nie
   wychodzi powyżej 0,72, więc próg dobrany „gdzieś wysoko" nie daje
   ANI JEDNEGO ziarna — i nie widać tego po niczym poza tym, że kolejne
   warianty dają identyczne liczby. Zmierzony rozkład: mediana 0,502,
   p97,5 0,655; stąd `intercept -32.75` przy nachyleniu 50.
   Pokrycie po zmierzeniu: **1,3% powierzchni**.

   ⚠️ `soft-light` na warstwie wyśrodkowanej wokół 0,5 modyfikuje ścianę
   w obie strony (jedne miejsca ciemniej, inne jaśniej) i NIE zmienia jej
   średniej jasności. Gdyby szum był przesunięty ku jasności, ściana
   zrobiłaby się po prostu jaśniejsza zamiast pofakturowana.

   POKRĘTŁO: `--faktura` (0 = gładka ściana).
   ------------------------------------------------------------ */
.sala-tlo::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='420'%3E%3Cfilter id='f' color-interpolation-filters='sRGB' x='0%25' y='0%25' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.25' numOctaves='3' seed='5' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer result='ziarno'%3E%3CfeFuncR type='linear' slope='0.5' intercept='0.25'/%3E%3CfeFuncG type='linear' slope='0.5' intercept='0.25'/%3E%3CfeFuncB type='linear' slope='0.5' intercept='0.25'/%3E%3CfeFuncA type='linear' slope='0' intercept='1'/%3E%3C/feComponentTransfer%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='1' seed='61' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer result='iskry'%3E%3CfeFuncR type='linear' slope='50' intercept='-32.75'/%3E%3CfeFuncG type='linear' slope='50' intercept='-32.75'/%3E%3CfeFuncB type='linear' slope='50' intercept='-32.75'/%3E%3CfeFuncA type='linear' slope='0' intercept='1'/%3E%3C/feComponentTransfer%3E%3CfeComposite in='ziarno' in2='iskry' operator='arithmetic' k1='0' k2='1' k3='0.5' k4='0'/%3E%3C/filter%3E%3Crect width='420' height='420' filter='url(%23f)'/%3E%3C/svg%3E");
  background-size: 420px 420px;
  mix-blend-mode: soft-light;
  opacity: var(--faktura, 0.4);
}

/* Posadzka — ciemny pas u dołu z ciepłym odbiciem pod obrazami. */
.sala-tlo::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 26%;
  background:
    radial-gradient(80% 100% at 50% 0%,
      rgba(var(--swiatlo), 0.075), rgba(0, 0, 0, 0) 70%),
    linear-gradient(to bottom, rgba(0, 0, 0, 0.32), rgba(0, 0, 0, 0.82));
  border-top: 1px solid rgba(var(--swiatlo), 0.07);
}

/* ⚠️ CELOWO BEZ `z-index`. Ściana i tak leży nad tłem sali (jest później
   w drzewie i jest pozycjonowana), a każdy `z-index` inny niż `auto`
   zamknąłby tu KONTEKST NAKŁADANIA — i wtedy snopy światła, które mają
   `mix-blend-mode: screen`, przestałyby widzieć pod sobą ścianę. Światło
   dodaje się do tła sali tylko wtedy, gdy tło jest w tym samym kontekście. */
.sala-in {
  position: relative;
  width: 100%;
  max-width: 1560px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(1.2rem, 3.4vh, 2.8rem);
}

/* ============================================================
   TABLICZKA — muzealny opis wystawy (imiona pary, data, miejsce)
   ============================================================
   Stoi nad ścianą i zapala się DOPIERO PO obrazach: najpierw ma się
   pojawić to, po co ktoś tu przyszedł. */
.plakietka {
  text-align: center;
  max-width: 46rem;
}
.plakietka .label { margin-bottom: 0.8rem; }
.plakietka h1 {
  font-family: var(--serif);
  font-size: clamp(1.9rem, 4.4vw, 3.4rem);
  line-height: 1.06;
  letter-spacing: 0.01em;
}
.plakietka h1 em { font-style: italic; color: var(--gold); }
.plakietka-meta {
  margin-top: 0.7rem;
  font-family: var(--nav);
  font-size: 0.66rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.plakietka-meta i {
  font-style: normal;
  color: rgba(var(--swiatlo), 0.28);
  margin: 0 0.55rem;
}

/* ============================================================
   ŚCIANA — obrazy obok siebie
   ============================================================
   Zwykły FLEX, nie siatka: obrazy mają różne proporcje i różne wagi,
   a rząd ma się sam wyśrodkować przy dowolnej ich liczbie (2–5).
   Wysokość rzędu robi najwyższy obraz — dlatego przesunięcia w pionie
   dajemy MARGINESEM (`--y`), a nie `transform`: `transform` jest już
   zajęty przez najazd, a dwie rzeczy nie mogą pisać po tej samej
   właściwości (NOWY-WATEK §7.17).
   ============================================================ */
.sciana {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sciana-odstep);
  width: 100%;

  /* 🔴 MIEJSCE NA KINKIETY (2026-09-06). Lampa jest `position: absolute`
     i wisi NAD aktem, więc NIE WCHODZI DO PUDEŁKA ŚCIANY — układ o niej
     nie wie. Dopóki obrazy były mieszane (jeden pion, reszta poziomów),
     mieściła się w odstępie pod tabliczką i nikt tego nie zauważył.
     U Pauliny i Adriana WSZYSTKIE TRZY okładki są pionami 3:4: najszerszy
     akt (`--w: 1.4`) zrobił się przez to najwyższy, a że dostał jeszcze
     unos w górę (`--y: -2.4rem`), jego lampa wjechała na datę i miejsce
     w tabliczce (zmierzone: 55 px nachodzenia).
     Ta wyściółka oddaje ścianie tyle, ile lampa nad nią wystaje. Reszta —
     unos aktów — jest przycinana z `galeria.js` (`--lift`), i tylko wtedy,
     gdy naprawdę brakuje miejsca. */
  padding-top: calc(var(--lampa-wys) + var(--lampa-luz));

  /* --- ILE OBRAZÓW WISI NA ŚCIANIE -----------------------------------
     Liczbę wpisuje `galeria.js` (`data-ile`) po sprawdzeniu okładek —
     akt bez okładki znika, więc dopiero wtedy wiadomo, ile ich naprawdę
     jest. Ta sama ściana obsługuje dwa, trzy, cztery i pięć obrazów.

     ⚠️ SUFIT STAWIA WYSOKOŚĆ OKNA, A NIE LICZBA OBRAZÓW — i to jest tu
     najważniejsza rzecz do zrozumienia. Rozmiar ramy idzie z `--jedn`,
     w którym człon `27vh` już przy czterech aktach dobija do granicy
     kadru. Przy dwóch aktach ubywa SZEROKOŚCI, a nie wysokości: lampa,
     rama i podpis zajmują dokładnie tyle samo pionu, ile zajmowały.
     Dlatego obrazów nie da się powiększyć „bo jest miejsce" — miejsce
     jest tylko w poziomie.

     ⚠️ SKALE SĄ ZMIERZONE, nie zgadnięte. Pierwsza próba (2 akty → 1,42,
     potem 1,26) wypychała wystawę **22 px poza okno** przy 1366 × 768
     i 1280 × 620, a trzy akty przy 1,12 — o 7 px. Zapas nad przyciskiem
     przy czterech aktach wynosi 27 px (1366 × 768), więc na powiększenie
     ramy zostaje tyle i ani piksela więcej. Stąd ostrożne 1,06 i 1,03.
     Zmieniając te liczby, ZMIERZ najciaśniejsze okno.

     Resztę roboty przy małej liczbie obrazów robi ODSTĘP: przy dwóch
     i trzech aktach rząd rozsuwa się szerzej, żeby zająć ścianę tym,
     czego nie da się dołożyć wysokością. */
  --skala: 1;
}
.sciana[data-ile="1"] { --skala: 1.06; }
.sciana[data-ile="2"] { --skala: 1.06; gap: clamp(2rem, 7vw, 8rem); }
.sciana[data-ile="3"] { --skala: 1.03; gap: clamp(1.5rem, 4.4vw, 5.5rem); }
.sciana[data-ile="5"] { --skala: 0.9; gap: clamp(0.8rem, 2vw, 2.4rem); }

.akt {
  /* --w = waga obrazu (szerokość względem sąsiadów), --y = zawieszenie
     wyżej/niżej, --d = kiedy zapala się jego lampa. Wszystkie trzy
     wpisane przy akcie w HTML — to jedyne pokrętła układu ściany. */
  flex: 0 0 auto;
  width: calc(var(--jedn) * var(--skala, 1) * var(--w, 1));
  /* `--lift` (0…1) przycina unos, gdy lampy nie mieszczą się pod tabliczką.
     Wpisuje go `galeria.js` po pomiarze; domyślnie 1, czyli układ dokładnie
     taki, jak wpisany przy akcie. Rytm ściany zostaje — wszystkie unosy
     maleją tak samo, więc obrazy nie zmieniają kolejności ani proporcji. */
  margin-top: calc(var(--y, 0rem) * var(--skala, 1) * var(--lift, 1));
  min-width: 0;
}
/* Slot bez okładki znika — tak samo jak slot rolki na okładce oferty.
   ⚠️ Własna reguła `display` bije atrybut `hidden`, stąd ta linijka
   (NOWY-WATEK §7.5). */
.akt[hidden] { display: none; }

/* ⚠️ AKT POJAWIA SIĘ DOPIERO PO POTWIERDZENIU OKŁADKI (2026-08-26).
   Wcześniej wszystkie akty z HTML-a wisiały na ścianie od pierwszej klatki,
   a te bez pliku znikały dopiero po odpowiedzi 404. Przy zwykłym opóźnieniu
   serwera widać było, jak z czterech obrazów dwa przepadają — a ściana przy
   okazji przeskakiwała, bo `data-ile` przestawia jej skalę i odstępy.
   Teraz jest odwrotnie: pod klasą `sprawdza` widać WYŁĄCZNIE akty, którym
   `galeria.js` potwierdził okładkę klasą `jest`.

   ⚠️ Klasę `sprawdza` zakłada SKRYPT, nie HTML — bez JavaScriptu ściana
   pokazuje wszystkie akty z pliku pary. Elementu, którego nie da się
   z powrotem odsłonić, nie wolno chować (NOWY-WATEK §7.25). */
.sciana.sprawdza .akt { display: none; }
.sciana.sprawdza .akt.jest { display: block; }
.sciana.sprawdza .akt.jest[hidden] { display: none; }

.akt-a {
  display: block;
  position: relative;
  text-decoration: none;
  color: inherit;
}

/* ------------------------------------------------------------
   KINKIET OBRAZOWY — mosiężna belka na dwóch ramionach
   ------------------------------------------------------------
   Wzór: oprawy z makiety od Krzysztofa — długi, wąski profil z mosiądzu,
   podwieszony na dwóch cienkich ramionach, świecący ciepłą kreską spodem.

   ⚠️ DWA RAMIONA, NIE JEDNO. Belka o szerokości ponad połowy obrazu
   podwieszona na jednym pręcie w środku wygląda, jakby miała się obrócić.
   Dwa ramiona rozstawione symetrycznie czytają się jako zamocowanie
   i dopiero wtedy oprawa „stoi" na ścianie.
   HTML się nie zmienia: ramiona robią pseudoelementy `.lampa-stem`,
   a sam element jest tylko niewidoczną rozpórką między nimi.

   ⚠️ MOSIĄDZ, NIE CIEMNY BRĄZ. Na czarno-szarej ścianie ciemna oprawa
   ginęła — czytała się jak cień, nie jak lampa. Mosiądz jest tu jedynym
   ciepłym akcentem poza samym światłem i wiąże kinkiety z paletą reszty
   serwisu.

   ⚠️ Kształt belki robią TRZY rzeczy naraz (bez którejkolwiek wychodzi
   naklejka): przyciemnione końce zamieniające płaski prostokąt w profil,
   gradient korpusu od jasnego grzbietu do ciemnego spodu oraz wąski
   POŁYSK wzdłuż górnej krawędzi — metal poznaje się po jednym ostrym
   odbiciu.
   ------------------------------------------------------------ */
.lampa {
  position: absolute;
  left: 50%;
  bottom: calc(100% + var(--lampa-luz));
  translate: -50% 0;
  width: 56%;
  height: var(--lampa-wys);
  z-index: 3;
  pointer-events: none;
}

/* ------------------------------------------------------------
   GŁĘBIA ŚWIATŁA — dwie warstwy przy samej oprawie
   ------------------------------------------------------------
   Płaskiej lampy nie ratuje mocniejszy blask, tylko dwie rzeczy, które
   robi prawdziwa oprawa: ZASŁANIA światło do góry i ma JĄDRO tuż pod
   kloszem, gdzie światło jest najgorętsze. Dopiero ta para daje wrażenie,
   że oprawa odstaje od ściany, a nie jest na niej namalowana.
   ------------------------------------------------------------ */

/* cień rzucany NA ŚCIANĘ nad oprawą — obudowa nie przepuszcza światła
   w górę, więc tuż nad nią ściana jest ciemniejsza niż wokół */
.lampa::before {
  content: "";
  position: absolute;
  left: -14%; right: -14%;
  bottom: 40%;
  height: 190%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(ellipse 58% 100% at 50% 100%,
    rgba(0, 0, 0, 0.55) 0%,
    rgba(0, 0, 0, 0.22) 42%,
    rgba(0, 0, 0, 0) 72%);
  filter: blur(9px);
}

/* JĄDRO ŚWIATŁA tuż pod kloszem — najjaśniejszy punkt całej sceny.
   ⚠️ `screen`, bo to światło DODAWANE do ściany, a nie plama farby.
   Gaśnie razem z lampą (`--zap`), więc przy zgaszonych światłach znika. */
.lampa::after {
  content: "";
  position: absolute;
  left: 6%; right: 6%;
  top: 82%;
  height: 150%;
  border-radius: 50%;
  background: radial-gradient(ellipse 52% 100% at 50% 0%,
    rgba(var(--zar), 0.28) 0%,
    rgba(var(--swiatlo), 0.12) 38%,
    rgba(var(--swiatlo), 0.04) 62%,
    rgba(var(--swiatlo), 0) 82%);
  filter: blur(7px);
  mix-blend-mode: screen;
  opacity: var(--zap, 1);
}
.lampa i { position: absolute; display: block; }

/* rozpórka: sama nic nie rysuje, wyznacza tylko rozstaw ramion */
.lampa-stem {
  left: 24%; right: 24%;
  top: 0;
  height: 38%;
  background: none;
}
.lampa-stem::before,
.lampa-stem::after {
  content: "";
  position: absolute;
  top: 0;
  width: clamp(1.5px, 0.17vw, 2.5px);
  height: 100%;
  background: linear-gradient(to bottom, #6f5c34 0%, var(--lampa-metal) 100%);
  border-radius: 1px;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
}
.lampa-stem::before { left: 0; }
.lampa-stem::after { right: 0; }

/* BELKA — profil z mosiądzu */
/* PRZEDNIA ŚCIANKA oprawy — ta, którą widzimy na wprost.
   ⚠️ SAMYM CIENIOWANIEM NIE DA SIĘ ZROBIĆ BRYŁY. Poprzednie wersje były
   jednym prostokątem z gradientem i mimo połysku, przyciemnionych końców
   i cienia pod spodem wciąż czytały się płasko. Bryłę widać dopiero
   wtedy, gdy widać DWIE JEJ ŚCIANKI naraz: przednią (tutaj) i górną
   (`::before`, trapez uciekający w głąb). Dopiero dwie płaszczyzny
   spotykające się na wspólnej krawędzi mówią oku „to jest bryła”. */
.lampa-klosz {
  left: 0; right: 0;
  bottom: 13%;
  height: 27%;
  border-radius: 0 0 2px 2px;
  background:
    /* połysk tuż pod krawędzią łączącą obie ścianki */
    linear-gradient(180deg,
      rgba(255, 250, 235, 0) 0%,
      rgba(255, 250, 235, 0.5) 16%,
      rgba(255, 250, 235, 0.14) 34%,
      rgba(255, 250, 235, 0) 52%),
    /* końce w cieniu */
    linear-gradient(to right,
      rgba(0, 0, 0, 0.62) 0%,
      rgba(0, 0, 0, 0.16) 4%,
      rgba(0, 0, 0, 0) 11%,
      rgba(0, 0, 0, 0) 89%,
      rgba(0, 0, 0, 0.16) 96%,
      rgba(0, 0, 0, 0.62) 100%),
    /* korpus przedniej ścianki: ciemnieje ku dołowi */
    linear-gradient(180deg,
      #e0c288 0%, #b8945a 34%, #856835 70%, #3e2f1a 100%);
  box-shadow:
    inset 0 -1px 2px rgba(0, 0, 0, 0.7),
    /* cień RZUCANY DO PRZODU — oprawa stoi przed ścianą, nie na niej */
    0 7px 16px -4px rgba(0, 0, 0, 0.85),
    0 2px 5px rgba(0, 0, 0, 0.6);
}
/* połysk wzdłuż grzbietu */
/* GÓRNA ŚCIANKA — trapez zwężający się ku ścianie.
   ⚠️ To jest CAŁY efekt trójwymiarowości. Ścianka jest węższa u góry
   (przy ścianie) niż u dołu (przy krawędzi od widza), więc czyta się
   jako płaszczyzna uciekająca w głąb. Ciemnieje ku ścianie, bo tam nic
   jej nie oświetla, a rozjaśnia się przy krawędzi wspólnej z przednią
   ścianką — i ta jasna krawędź to szczyt bryły.
   ⚠️ Kąt trapezu (7% z każdej strony) NIE MOŻE BYĆ DUŻY: przy większym
   oprawa zaczyna wyglądać jak widziana z góry, czyli jakby wisiała
   znacznie wyżej niż wzrok. */
.lampa-klosz::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  bottom: 100%;
  height: 68%;
  clip-path: polygon(7% 0, 93% 0, 100% 100%, 0 100%);
  background:
    linear-gradient(to right,
      rgba(0, 0, 0, 0.5) 0%,
      rgba(0, 0, 0, 0.12) 8%,
      rgba(0, 0, 0, 0) 18%,
      rgba(0, 0, 0, 0) 82%,
      rgba(0, 0, 0, 0.12) 92%,
      rgba(0, 0, 0, 0.5) 100%),
    linear-gradient(180deg,
      #5d4e30 0%, #8c7448 30%, #c2a26a 68%, #f0dcac 100%);
}
/* ciepły odblask wnętrza oprawy na jej dolnej krawędzi */
.lampa-klosz::after {
  content: "";
  position: absolute;
  left: 5%; right: 5%;
  bottom: 0;
  height: 26%;
  border-radius: 0 0 2px 2px;
  background: linear-gradient(to top,
    rgba(var(--swiatlo), 0.55) 0%, rgba(var(--swiatlo), 0) 100%);
  opacity: var(--zap, 1);
}

/* ŚWIECĄCA KRESKA POD BELKĄ
   ⚠️ Rozmycie zamiast poświat w `box-shadow`: cienie dawały cztery
   koncentryczne obwódki, im dalej tym wyraźniej widoczne jako narysowane
   kółka. Rozmyty pasek gaśnie tak, jak gaśnie światło. Rozmycie jest tu
   MNIEJSZE niż przy poprzedniej oprawie — na makiecie widać wyraźną
   ciepłą kreskę, a nie samą łunę; rozpraszaniem zajmuje się dopiero
   światło na ścianie niżej. */
.lampa-zar {
  left: 5%; right: 5%;
  bottom: 2%;
  height: 30%;
  border-radius: 999px;
  background: linear-gradient(180deg,
    rgba(var(--zar), 0.92) 0%,
    rgba(var(--swiatlo), 0.55) 60%,
    rgba(var(--swiatlo), 0) 100%);
  filter: blur(clamp(2px, 0.28vw, 4px));
  opacity: var(--zap, 1);
}

/* ------------------------------------------------------------
   ŚWIATŁO NA ŚCIANIE
   ------------------------------------------------------------
   ⚠️ ŻADNEGO STOŻKA WYCIĘTEGO `clip-path`. Trapez — nawet mocno rozmyty —
   ma proste boki i wierzchołek, a oko wyłapuje geometrię natychmiast:
   czytało się to jak reflektor teatralny, nie jak oświetlacz obrazu.
   Teraz światło to ELIPTYCZNY ROZBŁYSK z gładkim spadkiem we wszystkich
   kierunkach — bez jednej prostej krawędzi w całej warstwie.

   Trzy warstwy o różnym zasięgu, wszystkie mocno rozmyte:
     .snop         szeroka kąpiel światła na kamieniu
     .snop::before najszersza, ledwie widoczna poświata w powietrzu
     .snop::after  jaśniejsze jądro tuż pod kloszem

   ⚠️ Leży POD ramą (`z-index: 0` przy ramie 2). Na wierzchu rozjaśniałby
   zdjęcie tam, gdzie akurat wypadnie plama. Obraz rozjaśnia się inaczej —
   zdejmowaną zasłoną nocy (`.rama::after`).
   ⚠️ `mix-blend-mode: screen` — światło ma DODAWAĆ się do kamienia, a nie
   go zakrywać. Dlatego nad ramą nie ma przodka z `opacity` ani `filter`:
   każdy z nich zamknąłby blendowanie w sobie.
   ------------------------------------------------------------ */
.snop {
  position: absolute;
  left: 50%;
  top: calc(-1 * clamp(1rem, 1.8vw, 1.7rem));
  translate: -50% 0;
  width: 340%;
  height: calc(100% + clamp(6rem, 14vh, 12rem));
  z-index: 0;
  pointer-events: none;
  opacity: var(--zap, 1);
  /* DWIE WARSTWY, NIE JEDNA: wąski, jaśniejszy rdzeń w osi lampy
     i szeroka kąpiel światła dookoła. Sam rdzeń czyta się jako smuga
     w powietrzu — to on daje głębię, bo pojedyncza plama wygląda płasko
     bez względu na to, jak miękka jest jej krawędź. */
  background:
    radial-gradient(
      ellipse 15% 50% at 50% 0%,
      rgba(var(--zar), 0.075) 0%,
      rgba(var(--swiatlo), 0.038) 34%,
      rgba(var(--swiatlo), 0.012) 62%,
      rgba(var(--swiatlo), 0) 84%
    ),
    radial-gradient(
      ellipse 34% 58% at 50% 0%,
      rgba(var(--zar), 0.1) 0%,
      rgba(var(--swiatlo), 0.07) 22%,
      rgba(var(--swiatlo), 0.04) 44%,
      rgba(var(--swiatlo), 0.018) 66%,
      rgba(var(--swiatlo), 0.005) 84%,
      rgba(var(--swiatlo), 0) 100%
    );
  mix-blend-mode: screen;
  filter: blur(var(--snop-miekkosc));
}

/* najszersza poświata — sprawia, że rama nie wisi w czarnej próżni */
.snop::before {
  content: "";
  position: absolute;
  left: 50%; top: -4%;
  translate: -50% 0;
  width: 76%; height: 96%;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(var(--swiatlo), 0.06) 0%,
    rgba(var(--swiatlo), 0.026) 48%,
    rgba(var(--swiatlo), 0) 100%);
  filter: blur(46px);
}

/* jaśniejsze jądro tuż pod kloszem — tam kamień świeci najmocniej */
.snop::after {
  content: "";
  position: absolute;
  left: 50%; top: 1%;
  translate: -50% 0;
  width: 30%; height: 46%;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(var(--zar), 0.1), rgba(var(--swiatlo), 0) 100%);
  filter: blur(30px);
}

/* ------------------------------------------------------------
   RAMA, PASSE-PARTOUT, OBRAZ
   ------------------------------------------------------------ */
.rama {
  position: relative;
  z-index: 2;
  display: block;
  /* CZARNA LISTWA — wąska, matowa, bez połysku i bez zaokrągleń.
     Cała ozdoba to jedna ciepła grań na górnej krawędzi: tam pada
     światło z kinkietu. Rama ma być ciemną obwódką wokół zdjęcia,
     a nie ozdobą samą w sobie.

     ⚠️ WĄSKA CELOWO (ok. 8 px przy 1280). Gruba czarna listwa na
     ciemnej ścianie zlewa się z nią w bezkształtną plamę; to jasne
     passe-partout, a nie listwa, ma oddzielać zdjęcie od kamienia.

     ⚠️ Listwa (9–40) leży BLISKO jasności ciemnego kamienia (46–57) —
     to nie jest przeoczenie, tylko konsekwencja ciemnej ściany. Obraz
     odcina od tła przede wszystkim JASNE PASSE-PARTOUT (ok. 235), a nie
     kontrast samej listwy. Dlatego passe-partout nie może zniknąć ani
     pociemnieć: bez niego czarna rama na ciemnym kamieniu rozpływa się
     w jedną plamę. */
  padding: clamp(0.3rem, 0.5vw, 0.56rem);
  background:
    linear-gradient(160deg,
      #2b2723 0%, #100e0c 22%, #1c1917 50%, #0a0908 78%, #201d19 100%);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.9),
    inset 0 1px 0 rgba(255, 236, 206, 0.22),
    inset 0 -1px 0 rgba(0, 0, 0, 0.8),
    0 22px 38px -14px rgba(0, 0, 0, 0.95),
    0 3px 9px rgba(0, 0, 0, 0.6);
}
/* passe-partout — kartonowy margines wokół zdjęcia, w kości słoniowej */
/* PASSE-PARTOUT — szerokie, w kości słoniowej. Przy czarnej listwie to
   ono robi całą elegancję: jasny margines odsuwa zdjęcie od ramy i jako
   jedyny jasny element wokół kadru wyłapuje światło kinkietu.
   Szersze niż listwa mniej więcej dwukrotnie — tak wieszają w galeriach. */
.rama-in {
  position: relative;
  display: block;
  padding: clamp(0.5rem, 1.15vw, 1.3rem);
  background: linear-gradient(180deg, #f3ede1 0%, #e4dccc 100%);
  box-shadow:
    inset 0 0 0 1px rgba(30, 26, 20, 0.28),
    inset 0 3px 6px -4px rgba(30, 26, 20, 0.4);
}
.obraz {
  display: block;
  width: 100%;
  /* Proporcję pliku wpisuje `galeria.js` (--ar). 4/5 to wartość zapasowa
     na czas przed pomiarem — dzięki niej wysokość ramy jest znana od
     pierwszej klatki i ściana nie podskakuje po wczytaniu okładek. */
  aspect-ratio: var(--ar, 4 / 5);
  height: auto;
  /* ⚠️ DRUGI BEZPIECZNIK, niezależny od zaciętej proporcji. Sama proporcja
     nie wystarcza: akt z dużą wagą (`--w: 1.4`) jest szerszy od sąsiadów,
     więc przy tej samej proporcji wychodzi też WYŻSZY. Policzone przy
     1366 × 768: rama o wadze 1,4 i proporcji pionowej ma 414 px, a cała
     wystawa ma nad przyciskiem tylko 37 px zapasu. Sufit ucina to do
     wysokości, którą kadr uniesie, a `object-fit: cover` przycina zdjęcie
     zamiast je ściskać. */
  max-height: min(42vh, 26rem);
  object-fit: cover;
  background: #0c0c0c;
}
/* refleks szkła — ukośna smuga na całej ramie ze zdjęciem */
.rama-in::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(118deg,
    rgba(255, 255, 255, 0.13) 0%,
    rgba(255, 255, 255, 0.03) 26%,
    rgba(255, 255, 255, 0) 44%,
    rgba(255, 255, 255, 0) 68%,
    rgba(255, 255, 255, 0.055) 88%);
}

/* ŚWIATŁO PADAJĄCE NA OBRAZ — ciepła poświata u góry i naturalny
   spadek jasności ku dołowi. Kinkiet wisi NAD obrazem, więc dolna
   krawędź musi być ciemniejsza; bez tego zdjęcie wygląda jak
   podświetlone od tyłu, a nie oświetlone lampą. */
.rama::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: var(--zap, 1);
  background: linear-gradient(to bottom,
    rgba(var(--zar), 0.24) 0%,
    rgba(var(--swiatlo), 0.11) 22%,
    rgba(var(--swiatlo), 0.03) 46%,
    rgba(0, 0, 0, 0) 66%,
    rgba(0, 0, 0, 0.12) 88%,
    rgba(0, 0, 0, 0.2) 100%);
  mix-blend-mode: soft-light;
}

/* ZASŁONA NOCY — to ona sprawia, że przed zapaleniem lamp obraz jest
   ciemny. Kryje CAŁĄ ramę (listwę, passe-partout i zdjęcie), więc
   rozjaśnia się wszystko naraz, jak w prawdziwej sali.
   ⚠️ Krycie liczy się z `--zap`: 0 = noc (1), 1 = zapalone (0,06).
   Resztka 0,06 zostaje celowo — obraz w sali nigdy nie jest tak jasny
   jak na białym ekranie. */
.rama::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: #070502;
  opacity: calc((1 - var(--zap, 1) * 0.94) * var(--noc, 1));
}

/* pole dotyku/kliknięcia „Wejdź w pełną galerię" */
.wejdz {
  position: absolute;
  left: 50%;
  bottom: clamp(0.6rem, 1.4vw, 1.1rem);
  translate: -50% 6px;
  z-index: 6;
  padding: 0.5rem 1.05rem;
  border: 1px solid rgba(var(--swiatlo), 0.45);
  border-radius: 999px;
  background: rgba(9, 9, 9, 0.78);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-family: var(--nav);
  font-size: 0.56rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(var(--swiatlo), 0.94);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.35s var(--ease), translate 0.35s var(--ease);
}

/* ------------------------------------------------------------
   PODPIS POD OBRAZEM — muzealna karteczka
   ------------------------------------------------------------ */
.akt-podpis {
  display: block;
  text-align: center;
  margin-top: clamp(0.7rem, 1.4vh, 1.15rem);
  opacity: 1;
}
.akt-linia {
  display: block;
  font-family: var(--serif);
  font-size: clamp(0.82rem, 1.02vw, 1.06rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  line-height: 1.35;
}
.akt-linia i {
  font-style: normal;
  color: var(--gold);
  margin: 0 0.4em;
  opacity: 0.75;
}
.akt-nr { font-weight: 400; color: var(--gold); }
.akt-tytul { font-weight: 400; }
.akt-pod {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(0.78rem, 0.95vw, 0.98rem);
  letter-spacing: 0.02em;
  color: var(--ink-dim);
}
.akt-cta {
  display: block;
  margin-top: 0.5rem;
  font-family: var(--nav);
  font-size: 0.5rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  /* ⚠️ KOLOR WPROST, nie przygaszenie alfą. Ten napis stał wcześniej na
     `rgba(…, 0.34)` — czytelne na czarnym tynku, ale ściana z kamienia
     jest pod lampami DUŻO jaśniejsza (zmierzone: 67–78 zamiast ~23),
     więc przygaszony tekst zlewał się z nią. Alfa przy tekście zawsze
     zbija kontrast razem z tłem (NOWY-WATEK §7.14). */
  color: #a79f92;
  transition: color 0.4s var(--ease);
}

/* ------------------------------------------------------------
   NAJAZD — obraz wychodzi bliżej światła
   ------------------------------------------------------------
   ⚠️ `translate` i `scale` jako OSOBNE właściwości, nie `transform`:
   przy takim zapisie składają się zamiast nadpisywać (NOWY-WATEK §7.17).
   ⚠️ Cała rama rośnie o 2%, więc sąsiedzi ani drgną — `offsetWidth`
   pozostaje bez zmian, rusza się tylko malowanie.
   ------------------------------------------------------------ */
.akt-a .rama {
  transition:
    translate 0.55s var(--ease),
    scale 0.55s var(--ease),
    box-shadow 0.55s var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .akt-a:hover .rama,
  .akt-a:focus-visible .rama {
    translate: 0 -7px;
    scale: 1.02;
    box-shadow:
      inset 0 0 0 1px rgba(0, 0, 0, 0.4),
      0 30px 46px -14px rgba(0, 0, 0, 0.95),
      0 0 46px rgba(var(--swiatlo), 0.14);
  }
  .akt-a:hover .wejdz,
  .akt-a:focus-visible .wejdz { opacity: 1; translate: -50% 0; }
  .akt-a:hover .akt-cta,
  .akt-a:focus-visible .akt-cta { color: var(--gold); }
  /* Światło „mocniej dosięga" obrazu, który wysunął się z rzędu.
     ⚠️ Nie przez `opacity` — krycie snopu i zasłony nocy liczy się teraz
     z `--zap` (patrz niżej), a nadpisanie go zerwałoby tę więź w trakcie
     zapalania. Snop rozjaśniamy `brightness`, a zasłonę zdejmujemy
     mnożnikiem `--noc`, który jest osobną, animowalną liczbą. */
  .akt-a:hover .snop,
  .akt-a:focus-visible .snop {
    filter: blur(var(--snop-miekkosc)) brightness(1.32);
  }
  .akt-a:hover,
  .akt-a:focus-visible { --noc: 0.3; }
}
.akt-a { transition: --noc 0.55s var(--ease); }
.akt-a .snop { transition: filter 0.55s var(--ease); }

/* ============================================================
   ZAPALANIE ŚWIATEŁ — animacja wejścia na stronę
   ============================================================
   Świetlówka nie zapala się płynnie: startuje przez kilka nierównych
   mrugnięć, gaśnie, łapie i dopiero wtedy trzyma światło. Dlatego klatki
   są nierówne i ostre (`steps(1, end)`), a nie rozłożone po równo.

   ⚠️ JEDNA LICZBA STERUJE CAŁĄ LAMPĄ. `--zap` idzie od 0 (ciemno) do 1
   (zapalone) i czytają ją WSZYSTKIE warstwy światła: żarnik, snop
   i poświata na obrazie biorą ją wprost jako krycie, a zasłona nocy
   liczy z niej odwrotność. Wcześniej stały tu DWA lustrzane zestawy
   klatek, które trzeba było poprawiać parami — i to jest dokładnie ten
   rodzaj duplikatu, który rozjeżdża się pierwszy (NOWY-WATEK §7.18).

   ⚠️ `@property` jest tu konieczne, żeby `--zap` w ogóle dało się
   animować jako LICZBĘ. Gdyby przeglądarka go nie znała, wartość i tak
   przeskakiwałaby skokowo między klatkami — a że migotanie jest z
   założenia skokowe (`steps(1, end)`), efekt degraduje się do niemal
   identycznego. Nic nie znika.

   ⚠️ TRZY CHARAKTERY ZAPŁONU, nie jeden. Prawdziwe świetlówki łapią
   światło każda inaczej: jedna długo się opiera, druga strzela od razu,
   trzecia mrugnie i zamilknie na chwilę. `galeria.js` rozdaje te trzy
   warianty losowo — razem z losową kolejnością lamp i rozrzutem czasów
   sprawia to, że dwa wejścia na stronę nigdy nie wyglądają tak samo.
   ============================================================ */
@property --zap {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}
/* mnożnik zasłony nocy — 1 = normalnie, mniej = obraz doświetlony
   (używa go najazd; osobna liczba, żeby nie deptać `--zap`) */
@property --noc {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

/* wariant A — długi opór: kilka prób, zanim tuba złapie */
@keyframes zapalA {
  0%   { --zap: 0; }
  5%   { --zap: 0.85; }
  8%   { --zap: 0.04; }
  14%  { --zap: 0.7; }
  18%  { --zap: 0.02; }
  24%  { --zap: 0.92; }
  30%  { --zap: 0.18; }
  36%  { --zap: 1; }
  43%  { --zap: 0.5; }
  51%  { --zap: 0.95; }
  60%  { --zap: 1; }
  100% { --zap: 1; }
}
/* wariant B — szybkie złapanie: dwa mrugnięcia i światło stoi */
@keyframes zapalB {
  0%   { --zap: 0; }
  6%   { --zap: 0.95; }
  10%  { --zap: 0.08; }
  19%  { --zap: 1; }
  25%  { --zap: 0.35; }
  33%  { --zap: 0.97; }
  40%  { --zap: 1; }
  100% { --zap: 1; }
}
/* wariant C — mrugnięcie, cisza, dopiero potem światło */
@keyframes zapalC {
  0%   { --zap: 0; }
  4%   { --zap: 0.72; }
  7%   { --zap: 0.02; }
  27%  { --zap: 0.02; }
  32%  { --zap: 0.9; }
  36%  { --zap: 0.2; }
  44%  { --zap: 0.98; }
  52%  { --zap: 1; }
  100% { --zap: 1; }
}

@keyframes wejdzMiekko {
  from { opacity: 0; translate: 0 10px; }
  to   { opacity: 1; translate: 0 0; }
}
@keyframes salaJasniej {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* --- STAN „PRZED": sala ciemna, nic nie świeci ---------------------
   Atrybut wpisuje skrypt z `<head>`, więc ten stan obowiązuje od
   pierwszej klatki. Bez JavaScriptu atrybutu nie ma i cała sekcja
   poniżej nie działa — strona jest normalnie oświetlona. */
html[data-zapal] .akt { --zap: 0; }
html[data-zapal] .akt-podpis,
html[data-zapal] .plakietka,
html[data-zapal] .sala-cta,
html[data-zapal] .site-head,
html[data-zapal] .site-foot { opacity: 0; }
html[data-zapal] .sala-tlo { opacity: 0; }

/* --- STAN „ŚWIECI": lampy odpalają w losowej kolejności ------------
   Klasę wariantu i opóźnienie `--d` rozdaje `galeria.js`; `--zapal-czas`
   też potrafi nadpisać przy pojedynczej lampie, żeby żadne dwie nie
   migotały w tym samym tempie. */
html[data-zapal="swieci"] .akt {
  animation: zapalA var(--zapal-czas) var(--d, 0s) steps(1, end) both;
}
html[data-zapal="swieci"] .akt.zapal-b { animation-name: zapalB; }
html[data-zapal="swieci"] .akt.zapal-c { animation-name: zapalC; }

/* podpis zapala się chwilę po swoim obrazie — najpierw zdjęcie, potem słowo */
html[data-zapal="swieci"] .akt-podpis {
  animation: wejdzMiekko 0.8s calc(var(--d, 0s) + var(--zapal-czas) * 0.66) var(--ease) both;
}
/* ściana rozjaśnia się razem z pierwszą lampą, ale spokojnie i długo —
   światło rozproszone nie pojawia się skokowo, tylko nasyca pomieszczenie */
html[data-zapal="swieci"] .sala-tlo {
  animation: salaJasniej 2.8s 0.2s ease-out both;
}
/* tabliczka, przycisk, nagłówek i stopka — po całej sekwencji.
   `--koniec` liczy `galeria.js` z NAJPÓŹNIEJ gasnącej lampy (przy losowej
   kolejności to nie musi być ostatni obraz w rzędzie). */
html[data-zapal="swieci"] .plakietka {
  animation: wejdzMiekko 1.1s calc(var(--koniec, 2.4s) - 0.35s) var(--ease) both;
}
html[data-zapal="swieci"] .sala-cta {
  animation: wejdzMiekko 1.1s calc(var(--koniec, 2.4s) - 0.1s) var(--ease) both;
}
html[data-zapal="swieci"] .site-head,
html[data-zapal="swieci"] .site-foot {
  animation: salaJasniej 1.2s calc(var(--koniec, 2.4s) - 0.2s) ease-out both;
}

/* ⚠️ Po skończonej sekwencji skrypt zdejmuje atrybut (`data-zapal`
   znika). Wtedy wszystkie reguły wyżej przestają obowiązywać i stany
   wracają do zwykłych wartości z arkusza (`--zap` = 1) — dzięki temu
   najazd na obraz nie walczy z dogasającą animacją. */

/* ============================================================
   PRZYCISK POD ŚCIANĄ
   ============================================================ */
.sala-cta {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: clamp(0.4rem, 1.6vh, 1.4rem);
  padding: 0.85rem 1.9rem;
  border: 1px solid rgba(200, 176, 138, 0.55);
  border-radius: 999px;
  background: rgba(11, 11, 11, 0.5);
  color: var(--gold);
  text-decoration: none;
  font-family: var(--nav);
  font-size: 0.75rem;   /* 2026-09-05: bylo 0.58rem = 9 px; 12 px to prog czytelnosci */
  letter-spacing: 0.3em;
  text-transform: uppercase;
  transition: border-color 0.4s var(--ease), color 0.4s var(--ease),
              background 0.4s var(--ease), box-shadow 0.4s var(--ease);
}
.sala-cta:hover,
.sala-cta:focus-visible {
  border-color: var(--gold);
  background: rgba(200, 176, 138, 0.1);
  color: #f0e3cb;
  box-shadow: 0 0 28px rgba(200, 176, 138, 0.18);
}
.sala-cta span:last-child { transition: translate 0.4s var(--ease); }
.sala-cta:hover span:last-child { translate: 4px 0; }


/* ============================================================
   WYŁĄCZNIK ŚWIATŁA
   ============================================================
   Mały kontakt na ścianie sali: gasi kinkiety i zostawia widza
   w ciemnym pomieszczeniu, a ponowne kliknięcie odpala CAŁY POKAZ
   ZAPALANIA od nowa — z nową losową kolejnością lamp. To jedyny sposób,
   żeby obejrzeć wejście na stronę drugi raz bez przeładowania.

   ⚠️ PRZYCISK BUDUJE `galeria.js`, nie ma go w HTML pary. Gdyby siedział
   w pliku pary, każdy nowy reportaż wymagałby dopisania go ręcznie —
   a zasada jest taka, że w folderze pary leży wyłącznie treść.

   ⚠️ OSOBNY ATRYBUT `data-swiatlo`, nie `data-zapal`. Stan „zgaszone"
   nie może korzystać z reguł ładowania, bo tamte chowają też nagłówek,
   stopkę i tabliczkę — a wtedy zniknąłby sam przycisk, którym trzeba
   zapalić z powrotem. Zgaszona sala zostaje więc obsługiwalna: gasną
   obrazy i ściana, interfejs zostaje.
   ============================================================ */
/* ============================================================
   POWRÓT DO OFERTY — lustro wyłącznika (2026-09-03)
   ============================================================
   Ten sam kształt, ta sama waga wizualna i ten sam dystans od krawędzi,
   tylko przeciwny róg. Dwa drobne sterowniki sali stoją dzięki temu
   symetrycznie i żaden nie wygląda na doklejony.
   ⚠️ `z-index: 4` — nad ścianą i nad snopami światła, tak samo jak
   wyłącznik. Niżej ginąłby pod poświatą lamp.
   ⚠️ Sala ma `overflow: hidden`, więc nic tu nie wystaje poza kadr.
   ============================================================ */
.sala-wroc {
  position: absolute;
  left: clamp(1rem, 3vw, 2.8rem);
  top: calc(var(--glowa) + clamp(0.6rem, 2vh, 1.4rem));
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.95rem 0.45rem 0.65rem;
  border: 1px solid rgba(194, 160, 103, 0.26);
  border-radius: 999px;
  background: rgba(10, 10, 10, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: rgba(216, 205, 184, 0.78);
  font-size: 0.64rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  text-decoration: none;
  transition: border-color 0.4s var(--ease), color 0.4s var(--ease),
              background 0.4s var(--ease);
}
.sala-wroc svg {
  width: 13px; height: 13px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: translate 0.4s var(--ease);
}
.sala-wroc:hover,
.sala-wroc:focus-visible {
  border-color: rgba(194, 160, 103, 0.7);
  color: #d9cdb6;
  background: rgba(16, 14, 11, 0.72);
}
.sala-wroc:hover svg { translate: -3px 0; }
/* ⚠️ Na wąskim telefonie zostaje sama strzałka — napis „OFERTA" z rozstrzeloną
   spacją zabierał tam szerokość potrzebną tabliczce z imionami pary. */
@media (max-width: 420px) {
  .sala-wroc { padding: 0.45rem 0.55rem; }
  .sala-wroc span { display: none; }
}

.wylacznik {
  position: absolute;
  right: clamp(1rem, 3vw, 2.8rem);
  bottom: clamp(1rem, 3.5vh, 2.4rem);
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.45rem 1rem 0.45rem 0.5rem;
  border: 1px solid rgba(194, 160, 103, 0.26);
  border-radius: 999px;
  background: rgba(10, 10, 10, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--ink-dim);
  font-family: var(--nav);
  font-size: 0.75rem;   /* 2026-09-05: było 0.52rem = 8 px */
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: border-color 0.4s var(--ease), color 0.4s var(--ease),
              background 0.4s var(--ease);
}
.wylacznik:hover,
.wylacznik:focus-visible {
  border-color: rgba(194, 160, 103, 0.7);
  color: #d9cdb6;
  background: rgba(16, 14, 10, 0.7);
}

/* lampka stanu — świeci, gdy w sali pali się światło */
.wylacznik-plyta {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 38%,
    rgba(var(--zar), 0.95) 0%,
    rgba(var(--swiatlo), 0.55) 46%,
    rgba(40, 32, 20, 0.9) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(194, 160, 103, 0.55),
    0 0 10px rgba(var(--swiatlo), 0.45);
  transition: background 0.6s var(--ease), box-shadow 0.6s var(--ease);
}

/* --- SALA ZGASZONA ------------------------------------------------- */
html[data-swiatlo="off"] .wylacznik-plyta {
  background: radial-gradient(circle at 50% 38%, #3b3529 0%, #14120e 72%);
  box-shadow: inset 0 0 0 1px rgba(194, 160, 103, 0.26);
}
html[data-swiatlo="off"] .akt { --zap: 0; }
html[data-swiatlo="off"] .sala-tlo { opacity: 0.16; }
html[data-swiatlo="off"] .akt-podpis { opacity: 0.14; }
/* ⚠️ TABLICZKI Z IMIONAMI PARY NIE RUSZAMY. Gaszenie światła gasi salę,
   a nie podpis wystawy — imiona i data mają stać nieruchomo, bo to one
   mówią, czyj to reportaż. Wcześniej przygasały do 0,3 razem z resztą
   i przy każdym pstryknięciu skakały. */

/* --- PO PIERWSZYM WEJŚCIU ------------------------------------------
   Sekwencja zapalania jest animacją WEJŚCIA NA STRONĘ i ma zagrać raz.
   Kolejne zapalenia (wyłącznikiem) mają dotyczyć wyłącznie światła —
   tabliczka, przycisk, nagłówek i stopka zostają tam, gdzie były.
   Klasę dokłada `galeria.js` po pierwszej sekwencji. */
html.bez-wejscia .plakietka,
html.bez-wejscia .sala-cta,
html.bez-wejscia .site-head,
html.bez-wejscia .site-foot {
  animation: none !important;
  opacity: 1 !important;
}

/* ⚠️ `--zap` jest zarejestrowane przez `@property`, więc DA SIĘ je
   płynnie przejść — bez tego gaszenie byłoby skokiem, a nie ściemnianiem.
   Przejście siedzi na `.akt`, bo tam zmienia się wartość; potomkowie
   dziedziczą ją już animowaną. */
.akt { transition: --zap 0.9s var(--ease); }
.sala-tlo,
.akt-podpis,
.plakietka { transition: opacity 0.9s var(--ease); }

/* ============================================================
   WIDOK AKTU — pełna galeria jednego aktu
   ============================================================
   Wjeżdża NAD wszystkim (z-index 210 > nagłówek 120), bo to osobne
   pomieszczenie, a nie kolejna sekcja strony. Przewija się SAM
   (`overflow-y: auto`), więc wystawa pod spodem zostaje tam, gdzie była
   — po zamknięciu nie trzeba nic przywracać.
   ============================================================ */
.wid {
  position: fixed;
  inset: 0;
  z-index: 210;
  overflow-y: auto;
  overscroll-behavior: contain;
  background:
    radial-gradient(120% 60% at 50% 0%,
      rgba(var(--swiatlo), 0.06), rgba(0, 0, 0, 0) 60%),
    linear-gradient(to bottom, #0e0e0e, #080808 60%, #040404);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s var(--ease), visibility 0.5s;
  /* ⚠️ RUCH W BOK NALEŻY DO NAS, W PIÓN — DO PRZEGLĄDARKI (2026-09-02).
     Odkąd kolaż jedzie za palcem, przeglądarka nie może zabierać ruchu
     poziomego na własne gesty (cofanie karty, przewijanie w bok): przerywa
     go wtedy `pointercancel` w połowie drogi. `pan-y` oddaje jej dokładnie
     to, co tu naprawdę robi — pionowe przewijanie kolażu na kilka ekranów
     w dół; `pinch-zoom` zostawia szczypanie, bo powiększenie strony to
     ustawienie dostępności, a nie ozdobnik. */
  touch-action: pan-y pinch-zoom;
}
.wid.otwarty { opacity: 1; visibility: visible; }
.wid[hidden] { display: none; }
body.wid-open { overflow: hidden; }

/* ⚠️ BELKA JEST SŁUPKIEM, NIE RZĘDEM (2026-08-28). Górny wiersz („Akt II ·
   110 kadrów · Wróć do wystawy"), pod nim nazwa aktu. Wcześniej stał tu
   `flex` z `space-between`: tytuł po lewej, licznik i przycisk po prawej —
   układ, który na wąskim ekranie ZAWIJAŁ i wyrzucał licznik pod nazwę.
   Słupek daje ten sam porządek czytania na każdej szerokości. */
.wid-belka {
  position: sticky;
  top: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: clamp(0.5rem, 1.4vh, 0.9rem);
  padding: clamp(1.2rem, 3vh, 2.2rem) clamp(1.2rem, 4vw, 3.4rem) clamp(0.9rem, 2vh, 1.4rem);
  background: linear-gradient(to bottom, rgba(8, 8, 8, 0.94), rgba(8, 8, 8, 0.72) 70%, rgba(8, 8, 8, 0));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
/* Górny wiersz. ⚠️ Przycisk odpycha `margin-left: auto`, a NIE
   `space-between` — inaczej przy dwóch elementach (gdyby licznik zniknął)
   „Akt II" i przycisk rozjechałyby się na przeciwne krańce ekranu. */
.wid-gora {
  display: flex;
  align-items: center;
  gap: clamp(0.7rem, 2vw, 1.4rem);
  flex-wrap: wrap;
}
.wid-gora .wid-zamknij { margin-left: auto; }
.wid-tyt { min-width: 0; }
.wid-nr {
  font-family: var(--nav);
  font-size: 0.6rem;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--gold);
}
/* Kropka rozdzielająca „Akt II" od licznika — ten sam separator co wszędzie
   na stronie (NOWY-WATEK §7.43). Pseudoelement, żeby nie było go w drzewie
   dostępności: czytnik ekranu ma przeczytać „Akt II, 110 kadrów", a nie
   „Akt II kropka 110 kadrów". */
.wid-gora .wid-ile::before {
  content: "·";
  margin-right: clamp(0.7rem, 2vw, 1.4rem);
  color: var(--gold);
  opacity: 0.75;
}
.wid-h {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 3.2vw, 2.5rem);
  line-height: 1.1;
}
.wid-pod {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-dim);
  font-size: clamp(0.85rem, 1.1vw, 1.02rem);
  margin-top: 0.25rem;
}
/* ⚠️ `.wid-prawa` USUNIĘTE 2026-08-28 — licznik i przycisk przeniosły się
   do górnego wiersza belki (`.wid-gora`), więc prawa kolumna nie ma już
   czego trzymać. */
.wid-ile {
  font-family: var(--nav);
  font-size: 0.58rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-dim);
  white-space: nowrap;
}
/* ⚠️ MNIEJSZY OD 2026-08-28 — razem ze skróceniem napisu do „Powrót".
   Przy trzech słowach przycisk był najszerszym elementem belki i przeciągał
   na siebie uwagę należną nazwie aktu. Wyjście z widoku ma być łatwe
   do trafienia, nie najgłośniejsze. Wyściółka zeszła z 0,7/1,4 rem
   na 0,5/1 rem, a odstęp od krzyżyka z 0,6 na 0,45 rem. */
.wid-zamknij {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(11, 11, 11, 0.6);
  color: var(--ink);
  font-family: var(--nav);
  font-size: 0.56rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  transition: border-color 0.4s var(--ease), color 0.4s var(--ease);
}
.wid-zamknij:hover, .wid-zamknij:focus-visible {
  border-color: var(--gold);
  color: var(--gold);
}

/* ------------------------------------------------------------
   KOLAŻ KADRÓW
   ------------------------------------------------------------
   ⚠️ KOLUMNY CSS, nie siatka. W siatce rząd jest tak wysoki jak jego
   najwyższy kafelek, więc jedno pionowe zdjęcie rozpychałoby cały rząd
   (NOWY-WATEK §7.6). Kolumny pakują kadry ciasno, każdy w swojej
   proporcji. Cena: kolejność czytania idzie kolumnami — przy zdjęciach
   nie ma to znaczenia, a podgląd i tak numeruje je po kolei.
   ------------------------------------------------------------ */
.wid-tresc {
  padding: 0 clamp(1.2rem, 4vw, 3.4rem) clamp(3rem, 8vh, 6rem);
  column-width: clamp(14rem, 21vw, 21rem);
  column-gap: clamp(0.6rem, 1.2vw, 1.1rem);
}

/* ---- KOLAŻ JEDZIE ZA PALCEM PRZY ZMIANIE AKTU (2026-09-02) ----------
   Przewracanie aktów gestem działa od 2026-08-28, ale było PROGOWE: palec
   sunął po nieruchomym ekranie i po 56 px akt przeskakiwał. Nic nie mówiło,
   że gest jest zauważany ani ile jeszcze brakuje — a to jedyny sygnał, po
   którym poznaje się, że w bok w ogóle da się przesuwać. Dziś kolaż idzie
   dokładnie za palcem i gaśnie w miarę odsuwania: po puszczeniu albo
   wyjeżdża z ekranu (kolejny akt), albo wraca na miejsce. Ten sam
   mechanizm, te same czasy i ten sam próg co w obu podglądach kadru.

   ⚠️ RUCH IDZIE PRZEZ `translate`, NIE `transform` — osobna właściwość
   nie wejdzie w drogę niczemu, co ktoś kiedyś dołoży (pułapka §7.17).
   ⚠️ JEDZIE SAM KOLAŻ, NIE CAŁY WIDOK. Belka aktu jest `sticky` w `.wid`
   i ma zostać na miejscu: to ona mówi, w którym akcie jesteśmy, i z niej
   się wychodzi. Odjeżdża treść, nie rama.
   ⚠️ PRZEJŚCIE TYLKO NA CZAS DOJECHANIA — założone na stałe dokładałoby
   opóźnienie między palcem a obrazem, a to czyta się jak zacinanie.
   Czasy (190/240 ms w `galeria.js`) muszą się mieścić w 0,24 s. */
.wid-tresc.ciagnie { transition: none; }
.wid-tresc.dojezdza {
  transition: translate 0.24s var(--ease), opacity 0.24s var(--ease);
}
/* KADR W KOLAŻU — mały oprawiony obraz, nie goła miniatura.
   Każde zdjęcie dostaje jasne passe-partout i cienką czarną listwę,
   dokładnie jak obrazy na ścianie. Dzięki temu pełna galeria aktu jest
   dalszym ciągiem wystawy, a nie zwykłą siatką zdjęć.

   ⚠️ PROPORCJA SIEDZI NA ZDJĘCIU, NIE NA KAFELKU. Wcześniej `aspect-ratio`
   było na `.kadr`, ale po dołożeniu passe-partout kafelek ma jeszcze
   marginesy — a przy `box-sizing: border-box` proporcja liczy się od
   pudełka ZEWNĘTRZNEGO. Zdjęcie w środku dostawałoby wtedy inny kształt
   niż zmierzony z pliku i `object-fit: cover` przycinałby je bez powodu.
   `--ar` skrypt wpisuje na `.kadr`, a dziedziczy je `img` — własne
   właściwości CSS przechodzą w dół drzewa.

   ⚠️ `overflow: hidden` zostaje na ZDJĘCIU (patrz `.kadr img`), a nie na
   kafelku: powiększenie przy najeździe ma być przycięte do kadru, ale
   cień rzucany przez cały obrazek musi mieć gdzie paść. */
.kadr {
  display: block;
  width: 100%;
  border: 0;
  padding: clamp(0.4rem, 0.85vw, 0.8rem);
  background: linear-gradient(180deg, #f3ede1 0%, #e4dccc 100%);
  margin: 0 0 clamp(0.8rem, 1.5vw, 1.4rem);
  break-inside: avoid;
  position: relative;
  border-radius: 1px;
  box-shadow:
    /* passe-partout lekko zagłębione */
    inset 0 2px 5px -3px rgba(30, 26, 20, 0.45),
    /* cienka czarna listwa dookoła */
    0 0 0 1px rgba(0, 0, 0, 0.92),
    /* obraz odstaje od ściany galerii */
    0 12px 24px -10px rgba(0, 0, 0, 0.85);
  opacity: 0;
  transition: opacity 0.7s var(--ease), translate 0.45s var(--ease),
              box-shadow 0.45s var(--ease);
}
.kadr.jest { opacity: 1; }
.kadr img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--ar, 3 / 2);
  object-fit: cover;
  background: #0c0c0c;
  transition: scale 0.7s var(--ease), filter 0.5s var(--ease);
}
/* przycięcie powiększenia — na opakowaniu zdjęcia, nie na całym kafelku */
.kadr { overflow: visible; }
.kadr img { position: relative; z-index: 1; }
/* włos oddzielający zdjęcie od passe-partout */
.kadr::after {
  content: "";
  position: absolute;
  inset: clamp(0.4rem, 0.85vw, 0.8rem);
  z-index: 2;
  box-shadow: inset 0 0 0 1px rgba(30, 26, 20, 0.35);
  transition: box-shadow 0.4s var(--ease);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .kadr:hover, .kadr:focus-visible {
    translate: 0 -4px;
    box-shadow:
      inset 0 2px 5px -3px rgba(30, 26, 20, 0.45),
      0 0 0 1px rgba(0, 0, 0, 0.92),
      0 20px 34px -12px rgba(0, 0, 0, 0.9),
      0 0 34px rgba(var(--swiatlo), 0.12);
  }
  .kadr:hover img, .kadr:focus-visible img { filter: brightness(1.08); }
  .kadr:hover::after, .kadr:focus-visible::after {
    box-shadow: inset 0 0 0 1px rgba(200, 176, 138, 0.55);
  }
}

/* komunikat, gdy w folderze aktu nie ma jeszcze zdjęć */
.wid-pusto {
  column-span: all;
  max-width: 34rem;
  margin: clamp(2rem, 8vh, 5rem) auto;
  text-align: center;
  color: var(--ink-dim);
  line-height: 1.8;
}
.wid-pusto code {
  font-family: var(--nav);
  font-size: 0.8em;
  letter-spacing: 0.08em;
  color: var(--gold);
}

/* ------------------------------------------------------------
   STOPKA WIDOKU — skok do sąsiedniego aktu
   ------------------------------------------------------------ */
.wid-stopka {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0 clamp(1.2rem, 4vw, 3.4rem) clamp(2.4rem, 6vh, 4rem);
}
.wid-skok {
  border: 0;
  background: none;
  color: var(--ink-dim);
  font-family: var(--nav);
  font-size: 0.58rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  padding: 0.6rem 0;
  transition: color 0.4s var(--ease);
}
.wid-skok:hover:not([disabled]), .wid-skok:focus-visible:not([disabled]) { color: var(--gold); }
.wid-skok[disabled] { opacity: 0.25; }

/* ============================================================
   PODGLĄD KADRU
   ============================================================
   Własny, a NIE `#lightbox` ze wspólnego `script.js`: tamten obsługuje
   galerię strony głównej (`galleryData`) i podpięcie się pod jego
   strzałki dawałoby dwa skoki na jedno kliknięcie. Wygląd jest ten sam,
   kod osobny.
   ============================================================ */
.pg {
  position: fixed;
  inset: 0;
  z-index: 252;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 5, 5, 0.96);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s var(--ease), visibility 0.4s;
  /* ⚠️ GESTY NALEŻĄ DO PODGLĄDU, NIE DO PRZEGLĄDARKI — i od 2026-09-01
     dotyczy to CAŁEJ warstwy, nie samego zdjęcia. Skrypt nasłuchuje teraz
     na `.pg` (przesuwanie zaczyna się często przy krawędzi ekranu, gdzie
     zdjęcia już nie ma), więc i przeglądarce trzeba odebrać gest na całej
     warstwie. Inaczej ruch zaczęty obok kadru idzie na przewijanie strony
     albo cofanie karty, a podgląd dostaje `pointercancel` w połowie. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.pg.otwarty { opacity: 1; visibility: visible; }
.pg[hidden] { display: none; }
.pg-obraz {
  max-width: min(92vw, 1500px);
  max-height: 84vh;
  object-fit: contain;
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.8);
  /* ⚠️ `touch-action: none` — GESTY OBSŁUGUJEMY SAMI (2026-08-28).
     Bez tego przeglądarka zabiera szczypanie i przeciąganie dla siebie:
     powiększa CAŁE okno, a `.pg` jest `position: fixed`, więc warstwa
     zostaje przyklejona do ekranu i do brzegu zdjęcia nie da się dojechać.
     Odbierając gest przeglądarce, musimy dać wszystko z powrotem — stąd
     szczypanie, przeciąganie i dwustuk w `galeria.js`. */
  touch-action: none;
  cursor: zoom-in;
}
/* Przy przybliżeniu zdjęcie przestaje przenikać (animacja `pgSwap` rusza
   `scale`, a przybliżenie `transform` — składają się i wychodzi z tego
   podskok) i pokazuje, że da się je złapać. */
.pg.blisko .pg-obraz { cursor: grab; animation: none; }
.pg.blisko .pg-obraz:active { cursor: grabbing; }
/* Strzałki i licznik nie mają czego robić nad przybliżonym kadrem —
   zabierają miejsce dokładnie tam, gdzie Klient właśnie ogląda szczegół. */
.pg.blisko .pg-strz, .pg.blisko .pg-znak { opacity: 0; pointer-events: none; }
@keyframes pgSwap {
  from { opacity: 0; scale: 0.985; }
  to   { opacity: 1; scale: 1; }
}
.pg-obraz.swap { animation: pgSwap 0.4s var(--ease); }

/* 🔴 ZASŁONA NA CZAS PODMIANY KADRU (2026-09-02)
   ------------------------------------------------------------
   `pgImg.src = …` NIE wymienia pikseli natychmiast — element pokazuje stary
   obraz, dopóki nowy nie zostanie zdekodowany. Przez tę szparę (jedna klatka
   przy pliku z pamięci podręcznej, kilkanaście przy świeżym) było widać
   POPRZEDNI kadr: po przewróceniu palcem zjeżdżał on z ekranu, po czym
   WRACAŁ na środek w pełnym kryciu i dopiero potem podmieniał się na nowy.
   Dokładnie to zgłosił Krzysztof: „na krótki moment pojawia się poprzednie
   zdjęcie".
   Klasę zakłada `pokazKadr` na początku KAŻDEJ podmiany (także przy
   strzałkach i przy otwieraniu podglądu — element trzyma wtedy kadr
   z poprzedniego otwarcia), a zdejmuje ją `zapal()` dopiero wtedy, gdy
   piksele są już nowe. Zaraz po zdjęciu rusza przenikanie `pgSwap`.
   ⚠️ Klasa MUSI zejść przed `pgSwap` — animacja bije regułę klasy przez czas
   swojego trwania, ale po jej końcu `czeka` wróciłoby na wierzch i kadr
   zgasłby na końcu przenikania.
   ⚠️ Bez `transition`: zasłona ma paść natychmiast, a nie płynnie. */
.pg-obraz.czeka { opacity: 0; }
/* ⚠️ PRZEJŚCIE TYLKO NA CZAS DOJECHANIA. Kadr ciągnięty palcem ma iść
   DOKŁADNIE za nim — przejście założone na stałe dokładałoby opóźnienie
   między palcem a zdjęciem, a to czyta się jak zacinanie. Klasę zakłada
   i zdejmuje `dojedz()` w `galeria.js`; czasy (190/240 ms) muszą się
   mieścić w tej wartości. */
.pg-obraz.dojezdza {
  transition: transform 0.24s var(--ease), opacity 0.24s var(--ease);
}

.pg-znak {
  position: absolute;
  left: 50%;
  bottom: clamp(1rem, 4vh, 2.4rem);
  translate: -50% 0;
  font-family: var(--nav);
  font-size: 0.58rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.pg-znak b { color: var(--ink); font-weight: 400; }

.pg-x {
  position: absolute;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: rgba(11, 11, 11, 0.55);
  color: var(--ink);
  transition: border-color 0.35s var(--ease), color 0.35s var(--ease),
              background 0.35s var(--ease), rotate 0.45s var(--ease);
  top: clamp(1rem, 3vh, 2rem);
  right: clamp(1rem, 3vw, 2.2rem);
  width: 46px; height: 46px;
  font-size: 1rem;
  /* ⚠️ NAD STREFAMI STRZAŁEK. Strefy stoją w kodzie PO krzyżyku, więc bez
     tego malowałyby się nad nim i łapały kliknięcia w prawym górnym rogu —
     czyli dokładnie tam, gdzie się zamyka podgląd.
     ⚠️ 3, NIE 2 (2026-09-02) — odkąd pasy strzałek mają własne `z-index: 2`
     (patrz niżej), krzyżyk musi stać o stopień wyżej. Tak samo jak
     `.lb-close` na stronie głównej. */
  z-index: 3;
}
.pg-x:hover, .pg-x:focus-visible { border-color: var(--gold); color: var(--gold); rotate: 90deg; }

/* ---- STRZAŁKI TO CAŁA KRAWĘDŹ EKRANU (2026-09-01) ------------------
   Były kółkami 54 px przy brzegu: na telefonie trzeba było w nie CELOWAĆ,
   a kciuk trzymany przy krawędzi lądował obok. Teraz przycisk jest pasem
   na całą wysokość warstwy — wystarczy dotknąć prawej albo lewej krawędzi
   ekranu, w dowolnym miejscu w pionie. Sam znak zostaje, bez obwódki:
   ma mówić „tu jest następny kadr", a nie rysować drugą ramkę na zdjęciu.

   ⚠️ PAS LEŻY NA ZDJĘCIU i to jest w porządku — gesty nasłuchują na całej
   warstwie `.pg`, więc przesuwanie zaczęte na pasie działa tak samo jak
   wszędzie indziej. Stuknięcie bez ruchu zmienia kadr, ruch przewraca.
   ⚠️ Przy przybliżeniu pasy znikają (`.pg.blisko` niżej) — wtedy każdy
   piksel ekranu należy do oglądania szczegółu.
   ⚠️ Znak ma własny cień, bo leży na zdjęciu: na jasnym kadrze biały
   szewron bez cienia jest niewidoczny. */
.pg-strz {
  position: absolute;
  top: 0; bottom: 0;
  width: clamp(72px, 22vw, 150px);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--ink);
  transition: color 0.3s var(--ease), background 0.3s var(--ease),
              opacity 0.3s var(--ease);
  /* 🔴 PASY MUSZĄ LEŻEĆ NAD ZDJĘCIEM (2026-09-02) — i tego tu przez trzy dni
     nie było. `.lb-arrow` na stronie głównej ma `z-index: 2` od początku,
     `.pg-strz` nie miało żadnego; a `<img class="pg-obraz">` stoi w kodzie
     PO strzałkach, więc bez własnej warstwy szewrony malowały się POD
     zdjęciem.
     Zgłoszenie brzmiało „dziwne czarne strzałki przy przesuwaniu zdjęć
     na telefonie" i to jest dokładnie ten mechanizm: kadr ciągnięty palcem
     gaśnie (krycie spada do 0,3) i jedzie w bok, więc schowany pod nim
     szewron zaczyna PRZEŚWITYWAĆ — a razem z nim jego czarny cień
     (`drop-shadow` przy `.pg-chev path`, który na czarnym tle jest
     niewidoczny, ale na jasnym kadrze robi kleks). Strzałka wychodziła
     zza zdjęcia jako ciemna plama i chowała się z powrotem.
     Z własną warstwą szewron leży NA kadrze przez cały gest — biały,
     z cieniem, dokładnie tak jak w podglądzie na stronie głównej. */
  z-index: 2;
}
.pg-chev { width: clamp(20px, 5.5vw, 28px); height: auto; overflow: visible; }
.pg-chev path {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* kreska leży na zdjęciu — bez cienia ginie na jasnym kadrze */
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.95));
}
.pg-prev { left: 0; }
.pg-next { right: 0; }
/* Delikatna winieta pod kursorem — na komputerze pokazuje, dokąd sięga pas.
   Na dotyku nikt jej nie zobaczy i nie musi: tam pas jest po prostu duży. */
.pg-strz:hover, .pg-strz:focus-visible { color: var(--gold); }
.pg-prev:hover, .pg-prev:focus-visible {
  background: linear-gradient(to right, rgba(0, 0, 0, 0.45), transparent);
}
.pg-next:hover, .pg-next:focus-visible {
  background: linear-gradient(to left, rgba(0, 0, 0, 0.45), transparent);
}

/* ============================================================
   NISKIE OKNO — wystawa ma się zmieścić w jednym kadrze
   ============================================================
   Przy niskim oknie znika najpierw to, co najmniej ważne: tabliczka
   traci drugi wiersz, a podpisy — zdanie „kliknij, aby wejść".
   ============================================================ */
@media (max-height: 760px) {
  .akt-cta { display: none; }
  .plakietka-meta { font-size: 0.6rem; }
  .sala-in { gap: clamp(0.8rem, 2vh, 1.6rem); }
}
@media (max-height: 620px) {
  .plakietka h1 { font-size: clamp(1.5rem, 3.2vw, 2.2rem); }
  .akt-pod { display: none; }
}

/* ============================================================
   TELEFON — ściana staje się pionem
   ============================================================
   ⚠️ Cztery obrazy obok siebie na 390 px to cztery znaczki po 80 px.
   Dlatego poniżej 860 px ściana układa się w KOLUMNĘ, obrazy dostają
   pełną szerokość, a sala przestaje być „jednym ekranem" — przewija się
   i to jest w porządku, bo nic się nie nakłada.
   ============================================================ */
@media (max-width: 860px) {
  .sala {
    min-height: 0;
    padding: calc(var(--glowa) + 1.6rem) 1.2rem 3rem;
  }
  /* ŚCIANA NA TELEFONIE = SIATKA, nie kolumna.
     ⚠️ Jeden obraz na rząd zajmował 524 px wysokości — na ekranie 390 × 844
     mieściło się **półtora aktu** i cała wystawa robiła się listą do
     przewijania. Dwie kolumny skracają ten skok o połowę, a na szerszych
     ekranach (od 561 px) wchodzą trzy — więcej się nie mieści, bo obraz
     zszedłby poniżej ~110 px i przestałby cokolwiek pokazywać.
     ⚠️ W siatce rząd jest tak wysoki jak jego najwyższy kafelek (PROJEKT
     §7.6) — i TU JEST TO POŻĄDANE: obrazy o różnych proporcjach wiszą
     wtedy od wspólnej górnej linii, jak na prawdziwej ścianie. Stąd
     `align-items: start`.
     ⚠️ Wagi `--w` z HTML są tu NIEUŻYWANE: w siatce wszystkie kafelki
     mają równą szerokość, inaczej kolumny by się rozjechały. */
  .sciana {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    justify-items: center;
    /* Resztka szerokości (gdyby kolumny nie zjadły jej co do piksela) idzie
       na obie strony po równo, a nie w całości na prawą. */
    justify-content: center;
    /* ⚠️ ODSTĘP MIĘDZY WIERSZAMI JUŻ NIE MUSI MIEŚCIĆ LAMPY — od 2026-08-28
       robi to `padding-top` samego aktu (niżej). Tu został czysty oddech. */
    --kol-odstep: clamp(0.7rem, 2.8vw, 1.5rem);
    gap: clamp(2.2rem, 7vw, 3rem) var(--kol-odstep);
  }
  /* ⚠️ OSAMOTNIONY AKT STAJE NA ŚRODKU, NIE PRZY LEWEJ KRAWĘDZI.
     Przy nieparzystej liczbie aktów (3 albo 5) ostatni zostaje sam
     w dolnym wierszu dwukolumnowej siatki — i domyślnie ląduje w lewej
     kolumnie, z pustką po prawej. Na ścianie galerii czyta się to jak
     brakujący obraz, a nie jak koniec wystawy. Rozciągamy go więc na oba
     tory i wyśrodkowujemy, zostawiając mu szerokość JEDNEJ kolumny —
     inaczej ostatni obraz byłby dwa razy większy od pozostałych.
     ⚠️ `--kol-odstep` jest wspólny z `gap` wyżej: szerokość kolumny to
     połowa ściany MINUS połowa odstępu, więc obie liczby muszą pochodzić
     z jednego miejsca. */
  .sciana .akt.sam {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc((100% - var(--kol-odstep)) / 2);
  }
  /* ⚠️ Odstępy z reguł `[data-ile]` (sekcja komputerowa) mają WYŻSZĄ
     swoistość niż samo `.sciana`, więc mobilny odstęp trzeba powtórzyć
     z takim samym selektorem — inaczej wygrywa tamten i wiersze zostają
     ciasne (zmierzone: 32 px zamiast 44). */
  .sciana[data-ile] {
    --kol-odstep: clamp(0.7rem, 2.8vw, 1.5rem);
    gap: clamp(2.2rem, 7vw, 3rem) var(--kol-odstep);
    justify-content: center;
  }
  .akt {
    width: 100%;
    margin-top: 0;
    /* ⚠️ LAMPA MIESZKA WEWNĄTRZ AKTU, NIE NAD NIM — i to jest poprawka
       strukturalna, nie kosmetyczna. Kinkiet jest `position: absolute`
       i wisiał 34 px POWYŻEJ pudełka aktu; pudełko o tym nie wiedziało
       (NOWY-WATEK §7.36), więc te 34 px spadały na podpis z wiersza wyżej.
       Ratował to powiększony odstęp wierszy — ale odstęp jest stałą, a
       wysokość lampy rośnie z szerokością ekranu (`2.7vw`), więc przy
       każdym powiększeniu lampa znów pełzła w górę, na cudzy tekst.
       Wyściółka policzona TYM SAMYM wyrażeniem rośnie razem z nią:
       lampa nie ma już jak wyjść poza swój akt. */
    padding-top: calc(0.5rem + clamp(1.65rem, 2.7vw, 2.35rem) + 0.6rem);
  }

  /* ⚠️ NA TELEFONIE WSZYSTKIE RAMY MAJĄ TĘ SAMĄ PROPORCJĘ.
     W siatce kafelki stoją obok siebie, więc różne kształty okładek dawały
     kafelki różnej wysokości — a podpisy lądowały na różnych poziomach
     i rząd wyglądał na rozjechany (zmierzone: 269 px obok 193 px przy
     dwóch aktach). Na komputerze proporcja z pliku zostaje, bo tam obrazy
     wiszą swobodnie i różnica kształtów jest zaletą.
     Zdjęcie jest PRZYCINANE do wspólnego kadru, nie ściskane. */
  .obraz { aspect-ratio: 4 / 5; }

  .akt-cta { display: block; }

  /* Wylacznik przestaje wisiec w rogu sali: przy scianie ulozonej
     w kolumne "dol sali" jest dwa tysiace pikseli nizej, wiec przycisk
     bylby poza zasiegiem wzroku i nachodzil na przycisk kalkulatora.
     Na telefonie staje sie zwyklym elementem w przeplywie, pod nim. */
  .wylacznik {
    position: static;
    margin-top: clamp(1rem, 3vw, 1.6rem);
    align-self: center;
  }
  .wejdz { display: none; }        /* na dotyku nie ma najazdu, a więc i podpowiedzi */
  /* ⚠️ SNOP MUSI ZOSTAĆ NAD SWOIM OBRAZEM. Przy 220% szerokości światło
     z jednego kafelka wchodziło trybem `screen` na PODPIS SĄSIADA i tekst
     robił się wyprany. W siatce sąsiad jest tuż obok, a nie pół ekranu dalej.
     ⚠️ Rozmycia są tu też DUŻO MNIEJSZE — nie dla wyglądu, tylko dla mocy:
     rozmycie liczy się po powierzchni, a cztery snopy z promieniem 24–46 px
     to na telefonie realny koszt każdej klatki. */
  .snop {
    width: 100%;
    height: calc(100% + 1.2rem);
    filter: blur(10px);
  }
  .snop::before { width: 92%; height: 88%; filter: blur(16px); }
  .snop::after { filter: blur(12px); }
  .lampa::before { filter: blur(6px); }
  .lampa::after { filter: blur(5px); }

  /* `backdrop-filter` na telefonie kosztuje osobną warstwę i przemalowanie
     przy każdym przewinięciu — a pod tymi elementami i tak jest ciemne tło. */
  .wylacznik { backdrop-filter: none; -webkit-backdrop-filter: none; background: rgba(10, 10, 10, 0.78); }
  .wid-belka { backdrop-filter: none; -webkit-backdrop-filter: none; }

  /* ⚠️ Belka jest słupkiem także tutaj — `align-items` z dawnego układu
     rzędowego nie ma już czego wyrównywać. Na wąskim ekranie przycisk
     „Wróć do wystawy" zostaje w górnym wierszu razem z licznikiem;
     jeśli się nie mieści, `flex-wrap` zsuwa go NIŻEJ, ale wciąż NAD nazwą. */
  .wid-gora { gap: 0.6rem 0.9rem; }
  .wid-gora .wid-zamknij { padding: 0.55rem 1rem; }
  /* ⚠️ 12,5rem, nie 10,5. Przy 10,5 tablet (768 px) dostawał CZTERY
     kolumny po 169 px, a po odjęciu passe-partout zostawało 156 px
     zdjęcia — za mało jak na ten ekran. 12,5rem wymusza trzy szersze. */
  .wid-tresc { column-width: 12.5rem; }
  /* ⚠️ NA TELEFONIE PAS JEST WĘŻSZY, ALE NADAL PASEM (2026-09-01). Stały tu
     kółka 44 px przy krawędzi; dziś strzałka to strefa na całą wysokość,
     tylko zwężona do 20% szerokości — po dwóch stronach zostawia w środku
     ponad połowę ekranu na przesuwanie i na oglądanie samego zdjęcia.
     Znak schodzi o pół kroku, bo przy 375 px pas ma ~75 px szerokości. */
  .pg-strz { width: 20vw; }
}
/* Od 561 px w rzędzie mieszczą się TRZY obrazy — to górna granica:
   przy czterech kafelek schodzi poniżej ~110 px i zdjęcie przestaje
   cokolwiek pokazywać. */
@media (min-width: 561px) and (max-width: 860px) {
  .sciana { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* ⚠️ ŚRODKOWANIE OSAMOTNIONEGO AKTU DOTYCZY WYŁĄCZNIE DWÓCH KOLUMN.
     Klasę `sam` skrypt nadaje przy nieparzystej liczbie obrazów — a to
     rachunek dla siatki 2-kolumnowej. Przy trzech kolumnach „nieparzysty"
     nie znaczy „samotny" (5 obrazów to pełny wiersz i dwa w drugim),
     a połowa szerokości nie jest szerokością kolumny. Tu więc kasujemy. */
  .sciana .akt.sam { grid-column: auto; width: 100%; }
}

@media (max-width: 480px) {
  /* W siatce szerokość liczy kolumna, więc obrazu już nie ustawiamy.
     Zostaje tylko odchudzenie podpisu — przy kafelku ok. 165 px
     rozstrzelone wersaliki „kliknij, aby wejść” łamały się na trzy
     linijki i zjadały więcej miejsca niż samo zdjęcie. */
  .akt-cta { display: none; }
  .wid-tresc { column-width: 9rem; }
}

/* ============================================================
   OGRANICZONY RUCH
   ============================================================
   Migotanie świetlówki jest dokładnie tym, czego ta ustawienie ma
   oszczędzić. Światło po prostu JEST — sala od razu oświetlona,
   bez jednej klatki animacji.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  /* Jedna linijka gasi całe migotanie: `--zap` na 1 to lampa zapalona,
     snop rozłożony i zasłona nocy zdjęta z obrazu. */
  html[data-zapal] .akt { --zap: 1; animation: none !important; }
  html[data-zapal] .akt-podpis,
  html[data-zapal] .plakietka,
  html[data-zapal] .sala-cta,
  html[data-zapal] .sala-tlo,
  html[data-zapal] .site-head,
  html[data-zapal] .site-foot { opacity: 1; animation: none !important; }
  .akt-a { transition: none; }
  .kadr { opacity: 1; }
}
