/* ══════════════════════════════════════════════════════════════════════
   SECTIONS - style przypisane do konkretnych sekcji strony.
   Kolejnosc zgodna z kolejnoscia na stronie glownej.
   ══════════════════════════════════════════════════════════════════════ */


/* ══ HERO ══════════════════════════════════════════════════════════════
   Dwa rozne uklady, nie jeden skalowany:

   waski  - render pionowy u gory, tresc wysrodkowana pod nim. Calosc
            mieści sie w jednym ekranie: zdjecie dostaje wiersz 1fr
            z min-height 0, wiec to ono oddaje wysokosc, gdy okno jest
            niskie, a nie tekst.
   szeroki - render poziomy jako tlo pelnoekranowe, tresc lezy na nim
            po lewej.

   Sekcja wchodzi pod naglowek, zeby kadr zaczynal sie przy gornej
   krawedzi okna, a nie pod przyklejonym paskiem. */

.hero {
  position: relative;
  min-height: 100svh;
  margin-top: calc(var(--h-header) * -1);
  /* Gora: wysokosc naglowka plus zapas. Kaseta w renderze zaczyna sie
     tuz pod gorna krawedzia kadru, wiec bez tego marginesu przyklejony
     pasek naglowka wchodzi na jej szczyt.
     Boki bez paddingu - kadr ma dochodzic do krawedzi okna, margines
     boczny bierze na siebie sama tresc. */
  padding: calc(var(--h-header) + var(--s-4)) 0 clamp(var(--s-8), 5vh, var(--s-16));

  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);   /* zdjecie / tresc */
  gap: clamp(var(--s-4), 2vh, var(--s-8));
}

/* ── kadr (render) ──
   Wiersz pierwszy siatki: 60% wysokosci okna na zdjecie, reszta na tresc.

   Ograniczenie do 100vw jest zabezpieczeniem kadru. Render jest kwadratem
   1:1 z panelem posrodku - dopoki pudelko nie jest szersze niz wysokie,
   cover przycina wylacznie boki (puste sciany hali) i cala kaseta zostaje
   w kadrze. Przy niskim, szerokim oknie 60svh byloby szersze niz wysokie
   i zaczeloby scinac panel u gory i u dolu. */
.hero__foto {
  grid-area: 1 / 1;
  height: min(60svh, 100vw);
  z-index: 0;
  min-height: 0;
  overflow: hidden;
}

/* picture nie ma byc pudlem posrednim - liczy sie tylko img w polu siatki */
.hero__foto picture { display: contents; }

.hero__foto img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* ── zaslona ──
   Lezy w tym samym polu siatki co zdjecie. Na waskim ekranie ma jedno
   zadanie: wygasic dol kadru w tlo strony, zeby zdjecie nie konczylo sie
   twarda linia nad tekstem. */
.hero__zaslona {
  grid-area: 1 / 1;
  z-index: 1;
  pointer-events: none;
  /* Przejscie idzie wysoko - obejmuje cala jasna podloge w dolnej czesci
     renderu. Krotszy gradient konczyl sie w polowie rozswietlonego pola
     i granica zdjecia dawala sie wyczuc jako pas. */
  background: linear-gradient(
    to top,
    var(--c-bg) 0%,
    color-mix(in oklab, var(--c-bg) 76%, transparent) 12%,
    color-mix(in oklab, var(--c-bg) 36%, transparent) 28%,
    transparent 52%);
}

/* ── tresc ──
   Drugi wiersz bierze cala reszte ekranu, a tresc stoi w jego pionowym
   srodku - miedzy zdjeciem a dolna krawedzia okna. */
.hero__tresc {
  grid-area: 2 / 1;
  z-index: 2;
  display: grid;
  align-content: center;
  justify-items: center;
  text-align: center;
  gap: clamp(var(--s-4), 2vh, var(--s-6));
  padding-inline: var(--pad-x);
  max-width: var(--w-hero);
  margin-inline: auto;
}

/* Naglowek marki. Wersaliki plus zaciesnienie - Afacad w duzym stopniu
   ma szerokie swiatlo miedzyliterowe i bez tego rozjezdza sie w pasek.
   Na waskim ekranie lamie sie naturalnie na dwie-trzy linie; szerokosc
   liczona w ch, wiec zawsze zostaje w kolumnie tekstu. */
.hero__title {
  font-size: var(--t-display-xl);
  font-weight: var(--w-bold);
  line-height: var(--lh-tight);
  letter-spacing: -0.015em;
  text-transform: uppercase;
  max-width: 9ch;
}

/* ── naglowek dluzszy niz dwa slowa ──
   9ch to miara dobrana pod "DAJ SIĘ ZOBACZYĆ" i "KIM JESTEŚMY". Piec slow
   oferty ("Wybierz ekran do swojego wydarzenia") w tej samej mierze rozpada
   sie na cztery wiersze po jednym slowie, a najdluzsze z nich - WYDARZENIA -
   jest SZERSZE niz 9ch, wiec wychodzi poza pudelko w prawo. Wiersze centruja
   sie wtedy wzgledem roznych szerokosci i naglowek czyta sie jak tekst
   justowany spacjami (zgloszone 2026-08-11).

   Naprawa idzie dwoma krokami naraz, bo sama szersza miara zostawilaby
   naglowek na 48 px i tak dlugi, ze zjadlby ekran: stopien nizej w skali
   plus miara, w ktorej mieszcza sie DWA slowa. `text-wrap: balance`
   (base.css) rozklada z tego trzy rowne wiersze.

   Wylacznie ponizej 940 px - od tego progu hero przechodzi na uklad szeroki,
   gdzie obowiazuje wspolne `max-width: 20ch` i pelny stopien display-xl. */
@media (max-width: 939px) {
  .hero__title--dlugi {
    font-size: var(--t-display-lg);
    max-width: 14ch;
  }
}

.hero__opis {
  max-width: 36ch;
  color: var(--c-text-2);
  font-size: var(--t-body-lg);
  line-height: 1.45;
}

.hero__akcje { justify-content: center; }

/* Na najwezszych ekranach przyciski schodza w kolumne na pelna szerokosc.
   Prog jest nisko, bo "Kontakt" i "Oferta" to krotkie etykiety i obok
   siebie mieszcza sie bez sciskania jeszcze na typowym telefonie. */
@media (max-width: 359px) {
  .hero__akcje { width: 100%; flex-direction: column; }
  .hero__akcje .btn { width: 100%; }
}

/* ── uklad szeroki hero ──
   Powyzej 940 px zdjecie przestaje byc wierszem siatki i wraca na tlo
   calej sekcji, a tresc siada po lewej stronie kadru - dokladnie tam,
   gdzie render zostawil puste, nieoswietlone pole.
   Kadr i zaslona ida na pelna szerokosc okna (100vw), niezaleznie od
   kontenera sekcji. */
@media (min-width: 940px) {

  .hero {
    max-width: var(--w-wide);
    margin-inline: auto;
    padding: calc(var(--h-header) + clamp(var(--s-8), 6vh, var(--s-20))) var(--pad-x) clamp(var(--s-12), 8vh, var(--s-24));
    grid-template-rows: auto;
    align-content: center;
  }

  /* grid-area: auto jest tu konieczne. Element bezwzglednie pozycjonowany
     z przypisanym polem siatki liczy inset wzgledem tego pola, a nie
     wzgledem sekcji - kadr wchodzilby wtedy w padding hero zamiast
     wypelnic caly pierwszy ekran. */
  .hero__foto,
  .hero__zaslona {
    grid-area: auto;
    position: absolute;
    inset: 0;
    left: 50%;
    width: 100vw;
    height: auto;
    transform: translateX(-50%);
  }

  .hero__tresc {
    grid-area: auto;
    justify-items: start;
    text-align: left;
    padding-inline: 0;
    margin-inline: 0;
    gap: var(--s-6);
    /* 52% - tyle potrzebuje "DAJ SIĘ ZOBACZYĆ" w jednej linii przy pelnym
       stopniu display-xl. Akapit i tak trzyma sie wlasnych 36ch. */
    max-width: 52%;
  }

  .hero__title { max-width: 20ch; }
  .hero__akcje { justify-content: flex-start; }

  .hero__zaslona {
    background:
      linear-gradient(
        to top,
        var(--c-bg) 0%,
        color-mix(in oklab, var(--c-bg) 45%, transparent) 5%,
        transparent 14%),
      linear-gradient(
        to right,
        color-mix(in oklab, var(--c-bg) 82%, transparent) 0%,
        color-mix(in oklab, var(--c-bg) 46%, transparent) 30%,
        transparent 58%);
  }
}


/* ══ PARAMETRY MODULU (bento) ══════════════════════════════════════════
   Karta niesie jedna liczbe. Liczba jest tu trescia glowna, wiec dostaje
   krój marki i stopien naglowka - etykieta i opis sa przy niej cicho.
   Rozdzielczosc dostaje duzy kafel: to parametr, ktory sprzedaje. */

/* Tresc kafla stoi w jego srodku - w poziomie i w pionie. Przy roznej
   wysokosci kafli (dlugi blok obok dwoch mniejszych) wysrodkowanie
   pionowe trzyma liczby na wspolnej osi optycznej. */
.param {
  gap: var(--s-2);
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* dlugi kafel - liczba rosnie razem z powierzchnia; to parametr, ktory
   sprzedaje, wiec dostaje stopien wypowiedzi, nie danych technicznych */
.param.is-duzy .param__value { font-size: var(--t-display-lg); }
.param.is-duzy .param__unit  { font-size: var(--t-body-lg); }
.param.is-duzy .param__desc  { padding-top: var(--s-6); }

/* --c-text-2, nie --c-text-muted (2026-08-11). Kafel parametrow przeszedl tego
   dnia na `card--szklo`, a szklo jest JASNIEJSZE od zwyklej karty i dodatkowo
   niesie gradient swiatla - najciemniejszy stopien tekstu spadl przez to
   z 4,78 na 3,84 (zmierzone pod glifami, prog 4,5). To ten sam ruch, ktory
   zrobiono juz w panelu kontaktowym i przy danych konfiguratora: gdzie tlo
   jasnieje, drobny tekst schodzi o stopien wyzej w hierarchii.
   Etykieta zostaje cichsza od liczby mimo rownej jasnosci z opisem - niesie
   ja stopien (--t-xs), wersaliki i rozstrzelenie, nie sam kolor. */
.param__label {
  color: var(--c-text-2);
  font-size: var(--t-xs);
  font-weight: var(--w-medium);
  letter-spacing: var(--ls-label);
  line-height: 1;
  text-transform: uppercase;
}

.param__value {
  font-family: var(--f-display);
  font-size: var(--t-h2);
  font-weight: var(--w-semi);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
}

/* znak mnozenia oddziela wymiary, ale nie jest liczba - schodzi w cien
   i dostaje wlasne swiatlo, zeby cyfry nie sklejaly sie w jeden ciag */
.param__x {
  margin-inline: 0.12em;
  color: var(--c-text-muted);
  font-weight: var(--w-regular);
}

/* jednostka nalezy do liczby, wiec zostaje w tej samej linii, tylko
   mniejsza i ciemniejsza - to podpis, nie wartosc */
.param__unit {
  margin-left: 0.28em;
  color: var(--c-text-2);
  font-family: var(--f-body);
  font-size: var(--t-sm);
  font-weight: var(--w-regular);
  letter-spacing: 0;
}

/* opis siedzi tuz pod liczba - bez dosuwania do dolu, bo cala tresc
   kafla jest wysrodkowana */
.param__desc {
  padding-top: var(--s-4);
  color: var(--c-text-2);
  font-size: var(--t-sm);
  line-height: 1.5;
}

/* Kafle sa statyczne - nie reaguja na kursor. Nie ma czego kliknac, wiec
   zmiana pod kursorem obiecywalaby akcje, ktorej nie ma. */


/* ══ HASLO ═════════════════════════════════════════════════════════════
   ODWROCONE HERO (2026-08-10, decyzja klienta). Do tej daty byla to sekcja
   samego tekstu z trzema atrapami kaset wjezdzajacymi od dolu; kasety byly
   miejscem tymczasowym, czekajacym wlasnie na ten material. Teraz sekcja
   niesie scene z filmem i powtarza konstrukcje hero ODBITA W OBU OSIACH:

     szeroki  - film jako tlo pelnoekranowe, kaseta po LEWEJ stronie kadru
                (hero ma ja po prawej), tresc lezy po PRAWEJ
     waski    - tresc u gory, film pod nia (hero odwrotnie: zdjecie u gory)

   Odbicie jest cala trescia tej decyzji. Ten sam uklad w te sama strone
   dwa razy na jednej stronie czytalby sie jak powtorzony kadr; odbity
   czyta sie jak druga strona tej samej sceny.

   Zdanie jest wizualnie naglowkiem, ale znaczeniowo wypowiedzia, wiec
   niesie je <p> w kroju marki (h2 zajmie sekcja wideo pod spodem).

   Kolejnosc pol siatki (tresc = wiersz 1, kadr = wiersz 2) rzadzi tylko
   ukladem waskim; powyzej 940 px kadr wychodzi z siatki na tlo calej
   sekcji, dokladnie tak jak w hero. */

.haslo {
  position: relative;
  /* bez `overflow: hidden` - powyzej 940 px kadr wychodzi poza sekcje na
     pelne 100vw i obciecie zjadloby mu boki. Kadr tnie sie sam
     (patrz .haslo__kadr), a poziom strony pilnuje `overflow-x: clip`
     na <body> (base.css). */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto;   /* tresc / kadr */
  align-content: stretch;   /* nadpisuje `center` z .section--full */
  gap: clamp(var(--s-4), 2vh, var(--s-8));
  /* Dol na zero: film konczy sekcje i dochodzi do jej krawedzi, tak jak
     kadr hero dochodzi do gornej krawedzi okna.
     Gora ciasniej niz --s-section: sekcja ma trzymac sie w jednym ekranie
     razem z kadrem (60svh), wiec pion, ktory zostaje na tekst, jest tu
     towarem deficytowym. Odstep od sekcji wyzej i tak niesie jej wlasny
     padding dolny. */
  padding-block: clamp(var(--s-12), 8vh, var(--s-20)) 0;
}

/* ── kadr (scena z filmem) ──
   KADR JEST KWADRATEM, ZAWSZE (2026-08-10, druga tura: "nie moze przycinac
   dolu filmu - lepiej, zeby film przechodzil w pelne 1:1"). Materiał waski
   jest renderowany 1:1, wiec kwadratowa ramka pokazuje go w calosci - nic
   sie nie przycina, na zadnej szerokosci okna.

   PELNA SZEROKOSC, BEZ SUFITU (2026-08-13, decyzja klienta: "kwadratowy
   film moze zajmowac pelna szerokosc viewportu"). Historia tej wartosci:
   najpierw `height: min(60svh, 100vw)` na pelnej szerokosci kolumny - na
   telefonie w pionie wygrywalo 100vw i wychodzil kwadrat, ale przy oknie
   NISKIM I SZEROKIM (np. 800x700 px, czyli przegladarka zwezona ponizej
   progu 940 px) wygrywalo 60svh, pudelko robilo sie szersze niz wyzsze
   i `cover` scinal filmowi gore i dol. Poprawka przeniosla te liczbe
   z wysokosci na BOK kwadratu (`width` + `aspect-ratio`), co usunelo
   przycinanie, ale przy niskim oknie zostawialo maly kwadrat posrodku
   kolumny.

   Teraz bok kwadratu to po prostu szerokosc sekcji. Na telefonie nic sie
   nie zmienia (tam 100vw i tak wygrywalo), a przy niskim i szerokim oknie
   kadr rosnie do krawedzi - film jest wyzszy niz okno i trzeba go przewinac,
   i to jest przyjete swiadomie: lepiej duzy kadr do przewiniecia niz
   znaczek posrodku pustego pasa.

   `100%`, nie `100vw`: sekcja w ukladzie waskim nie ma paddingu bocznego,
   wiec jej szerokosc to juz cale okno BEZ paska przewijania. `100vw` liczy
   pasek razem z trescia i wygenerowalby przewijanie w poziomie - ta sama
   pulapka, ktora sekcja FILM omija odwrotnym paddingiem.

   NIE DOTYCZY tego drugiego kwadratu na desktopie (sekcja FILM, `.film__kadr`):
   tam kadr jest polowa ukladu dwukolumnowego i zostaje w kolumnie.

   OKLADKA WCHODZI TU, NIE ATRYBUTEM `poster`. Dwa powody: przy dwoch
   elementach <video> (szeroki/waski) atrybut sciagnalby obie okladki,
   takze te schowana, a tlo w CSS pobiera tylko ta z aktywnego zapytania
   medialnego. Do tego <video> przed pierwsza klatka jest PRZEZROCZYSTE,
   wiec okladka pod spodem po prostu przez nie widac - nie trzeba jej
   niczym gasic w chwili startu.

   `background-color` pod spodem zostaje na czas, zanim okladka sie pobierze
   - inaczej przez przezroczyste <video> widac by bylo goly kolor <body>. */
.haslo__kadr {
  grid-area: 2 / 1;
  z-index: 0;
  width: 100%;
  aspect-ratio: 1 / 1;
  min-height: 0;
  overflow: hidden;
  background-color: var(--c-bg);
  background-image: url('../assets/haslo-mobile-okladka.webp');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.haslo__wideo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Przelacznik formatow. Prog 940 px ten sam co reszta ukladu - powyzej
   niego wchodzi scena pozioma, ponizej pionowa. Schowany <video> ma
   `preload="none"` i nie ma pudelka, wiec nie pobiera ani bajta
   (patrz komentarz w js/wideo.js). */
.haslo__wideo--szeroki { display: none; }

/* ── zaslona: TYLKO DOL ──
   Hero ma zaslone o dwoch zadaniach - przyciemnia strone kadru pod tekstem
   i wygasza krawedz. Tutaj zostaje wylacznie to drugie (wymog klienta
   2026-08-10): scena jest wyrezyserowana tak, ze tekst ma swoje ciemne pole
   w samym kadrze, wiec przyciemnianie czegokolwiek pod nim tylko zabrudzilo
   by material. Nie dokladac tu warstwy bocznej ani gornej.

   Dol zostaje, bo to nie jest przyciemnienie tresci, tylko przejscie: film
   konczy sie na krawedzi sekcji i bez tego wchodzilby w nastepna twarda
   linia. Ta sama krzywa co dolna warstwa zaslony hero.

   Jedna regula dla obu ukladow - procenty licza sie od wysokosci wlasnego
   pudelka, wiec na waskim (kadr ok. 390 px) wychodzi ok. 60 px przejscia,
   a na szerokim (pelna sekcja) ok. 130 px. */
.haslo__zaslona {
  grid-area: 2 / 1;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to top,
    var(--c-bg) 0%,
    color-mix(in oklab, var(--c-bg) 45%, transparent) 5%,
    transparent 16%);
}

/* ── tresc ──
   Pierwszy wiersz bierze cala reszte ekranu, a tekst stoi w jego pionowym
   srodku - miedzy gorna krawedzia sekcji a kadrem. */
.haslo__tresc {
  grid-area: 1 / 1;
  z-index: 2;
  display: grid;
  align-content: center;
  justify-items: center;
  text-align: center;
  gap: clamp(var(--s-6), 3vh, var(--s-10));
  padding-inline: var(--pad-x);
  max-width: var(--w-hero);
  margin-inline: auto;
}

.haslo__zdanie {
  font-family: var(--f-display);
  font-size: var(--t-display-lg);
  font-weight: var(--w-semi);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  /* w ch, wiec lamie sie w tym samym miejscu niezaleznie od stopnia.
     Dwa zdania o zblizonej dlugosci (22 i 19 znakow) - balance przy tej
     szerokosci sam znajduje zlamanie na granicy zdan. */
  max-width: 18ch;
  text-wrap: balance;
}

/* kropka konczaca zdanie w czerwieni marki - ten sam znacznik co przy
   pozycji w menu mobilnym. Jedyny akcent koloru w calej sekcji. */
.haslo__kropka { color: var(--c-brand); }

.haslo__rozwiniecie {
  max-width: 46ch;
  color: var(--c-text-2);
  font-size: var(--t-body-lg);
  line-height: 1.55;
}

/* USUNIETE 2026-08-10: `.haslo__ekrany` / `.haslo__ekran` - trzy atrapy
   kaset wjezdzajace w kadr od dolu. Byly jawnie tymczasowe ("docelowo
   w to miejsce wchodzi render z filmem"), a ten material wlasnie wszedl
   jako scena w tle calej sekcji. Zostawianie ich obok filmu dalo by dwa
   razy ten sam przedmiot w jednym kadrze. */

/* ── uklad szeroki ──
   Powyzej 940 px kadr przestaje byc wierszem siatki i wraca na tlo calej
   sekcji, a tresc siada po PRAWEJ stronie - tam, gdzie scena zostawila
   puste, nieoswietlone pole (kaseta stoi w lewej czesci renderu). Hero
   robi dokladnie to samo w druga strone.

   Prog 940 px ten sam co w hero, filmie i konfiguratorze - jedna granica
   ukladu w calym serwisie, nie cztery rozne. */
@media (min-width: 940px) {

  .haslo {
    max-width: var(--w-wide);
    margin-inline: auto;
    padding-inline: var(--pad-x);
    padding-block: clamp(var(--s-12), 8vh, var(--s-24));
    grid-template-rows: auto;
    align-content: center;
  }

  /* grid-area: auto jest tu konieczne. Element bezwzglednie pozycjonowany
     z przypisanym polem siatki liczy inset wzgledem tego pola, a nie
     wzgledem sekcji - kadr wchodzilby wtedy w padding sekcji zamiast
     wypelnic caly ekran. Ta sama pulapka co w hero. */
  .haslo__kadr,
  .haslo__zaslona {
    grid-area: auto;
    position: absolute;
    inset: 0;
    left: 50%;
    width: 100vw;
    height: auto;
    transform: translateX(-50%);
    /* kwadrat obowiazuje WYLACZNIE w ukladzie waskim - tutaj kadr jest
       tlem calej sekcji i wysokosc dyktuja `inset`, nie proporcja.
       Zerowane jawnie, zeby nie zalezec od tego, jak przegladarka
       rozstrzyga konflikt `aspect-ratio` z para top/bottom. */
    aspect-ratio: auto;
  }

  .haslo__kadr { background-image: url('../assets/haslo-desktop-okladka.webp'); }

  .haslo__wideo--szeroki { display: block; }
  .haslo__wideo--waski   { display: none; }

  /* Zaslona bez zmian wzgledem ukladu waskiego - samo przejscie u dolu.
     Zadnej warstwy bocznej: patrz uzasadnienie przy .haslo__zaslona. */

  /* BLOK po prawej, TEKST do lewej (wymog klienta 2026-08-10). Odbita jest
     pozycja kolumny, nie kierunek pisma: hero stoi przy lewej krawedzi,
     ta sekcja przy prawej, ale oba akapity zaczynaja sie na tej samej,
     lewej osi. Wyrownanie do prawej dawalo poszarpana krawedz wlasnie
     tam, gdzie zaczyna sie czytanie.
     52% tak samo jak w hero: tyle zostaje wolnego pola obok kasety. */
  .haslo__tresc {
    grid-area: auto;
    justify-self: end;
    justify-items: start;
    text-align: left;
    padding-inline: 0;
    margin-inline: 0;
    max-width: 52%;
  }
}


/* ══ FILM ══════════════════════════════════════════════════════════════
   KADR JEST KWADRATEM (2026-08-13). Nowy material `konfiguracja.*` ma
   1080x1080 zamiast dotychczasowych 1920x1080, wiec rama idzie za plikiem,
   a nie odwrotnie - przy 16:9 kwadratowy film zostalby przyciety z gory
   i z dolu przez `object-fit: cover`, czyli dokladnie tam, gdzie stoi
   kaseta. Proporcja jest zadeklarowana na sztywno, wiec uklad strony nie
   skacze przy starcie playbacku, niezaleznie od tego, czy plik zdazyl sie
   zaladowac.
   Film trzyma sie marginesow strony (decyzja klienta 2026-08-07, korekta
   wczesniejszego ujecia pelnoekranowego) - element stoi w `.wrap` w HTML
   (patrz index.html), wiec szerokosc i marginesy boczne niesie kontener,
   a nie osobna regula tutaj. Zadnego 100vw ani margin-inline: film ma byc
   w tej samej osi co naglowek nad nim, na kazdej szerokosci ekranu. */

.film__kadr {
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--c-surface);
  /* --r-xl to promien zarezerwowany dla mediow; kadr nie dotyka juz
     krawedzi okna, wiec ma wlasny, widoczny naroznik */
  border-radius: var(--r-xl);
}

/* ── uklad sekcji: tekst po lewej, kadr po prawej ──
   POLOWA NA POLOWE (2026-08-13, decyzja klienta - wczesniej 0.8 : 1.2 na
   korzysc kadru). Material jest teraz kwadratowy, wiec przy szerszej
   kolumnie kadru film rosl w PIONIE ponad wysokosc tekstu obok i sekcja
   robila sie nienaturalnie wysoka; w polowie kontenera kwadrat ma mniej
   wiecej wysokosc kolumny tekstu i oba pola czytaja sie jak para.

   Prog 940 px ten sam co w hero i konfiguratorze - jedna granica ukladu
   w calym serwisie, nie trzy rozne. Ponizej niej kolumny wracaja jedna
   pod druga, tekst nad kadrem. */

.film__uklad {
  display: grid;
  gap: clamp(var(--s-8), 5vw, var(--s-12));
}

/* odstep miedzy naglowkiem a kadrem niesie `gap` siatki, nie wlasny
   margines naglowka - inaczej na waskim ekranie (gdzie kolumny stoja
   jedna pod druga) odstep liczylby sie dwa razy */
.film__uklad .head { margin-bottom: 0; }

@media (min-width: 940px) {
  .film__uklad {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    column-gap: clamp(var(--s-10), 6vw, var(--s-20));
  }
}

/* ── TELEFON I WASKIE OKNO: KADR OD KRAWEDZI DO KRAWEDZI (2026-08-11) ──
   Ponizej 940 px kolumny stoja jedna pod druga, wiec film nie jest juz
   sasiadem tekstu, tylko osobnym pasem pod nim - a pas 312 px szerokosci
   z marginesem po obu stronach pokazywal material wielkosci znaczka. Kadr
   wychodzi wiec poza kontener na pelna szerokosc okna. Powyzej progu
   ZOSTAJE W KOLUMNIE: tam jest polowa ukladu dwukolumnowego i wyjscie poza
   `.wrap` rozwalilo by cala sekcje.

   Wyjscie liczone ODWROTNYM PADDINGIEM RODZICA, nie przez `100vw`. Roznica
   jest istotna, bo prog 940 px lapie takze zwezone okno na desktopie, gdzie
   pasek przewijania zabiera kilkanascie pikseli: `100vw` liczy sie razem
   z nim i film wystawalby poza ekran, generujac przewijanie w poziomie.
   `--pad-x` to dokladnie ten margines, ktory `.wrap` dodaje - odjety wraca
   do krawedzi okna i ani piksela dalej. Dziala, dopoki `.wrap` nie jest
   ograniczony wlasnym `max-width`, czyli ponizej --w-page - a caly ten blok
   i tak konczy sie na 939 px.

   Promien znika razem z marginesem: zaokraglony naroznik dotykajacy krawedzi
   ekranu czyta sie jak niedomalowany, nie jak detal. */
@media (max-width: 939px) {
  .film__kadr {
    margin-inline: calc(var(--pad-x) * -1);
    border-radius: 0;
  }
}

/* Plik wypelnia rame w calosci. Od 2026-08-13 rama i material maja te sama
   proporcje (1:1), wiec `cover` nie ma czego przycinac - zostaje na miejscu
   jako zabezpieczenie na wypadek podmiany pliku na inny format. */
.film__wideo {
  width: 100%;
  height: 100%;
  object-fit: cover;
}


/* ══ FILM 2 (index.html) ═══════════════════════════════════════════════
   Ten sam wzorzec co FILM wyzej - siatka 50/50 od 940px, kolumna
   ponizej - ale LUSTRZANY: kadr wchodzi jako pierwsza kolumna (`order:
   -1` na `.film2__kadr`, tylko wewnatrz media query, bo `order` poza
   grid/flex nie ma znaczenia). Znacznik trzyma tekst PRZED kadrem w DOM,
   wiec ponizej 940px (kolumny jedna pod druga) kolejnosc naturalnie
   wraca do "tekst nad filmem" - identycznie jak w FILM - bez osobnej
   reguly na waskim ekranie.

   Kadr w proporcji 4:3 (nie 1:1 jak w FILM) - swiadoma roznica z briefu,
   nie pomylka. */
.film2__uklad {
  display: grid;
  gap: clamp(var(--s-8), 5vw, var(--s-12));
}

.film2__uklad .head { margin-bottom: 0; }

@media (min-width: 940px) {
  .film2__uklad {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    column-gap: clamp(var(--s-10), 6vw, var(--s-20));
  }

  .film2__kadr { order: -1; }
}

.film2__kadr {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--c-surface);
  border-radius: var(--r-xl);
}

/* Telefon i waskie okno: kadr od krawedzi do krawedzi - identyczna
   logika co `.film__kadr` (patrz jego komentarz po pelne uzasadnienie
   odwrotnego paddingu zamiast 100vw). */
@media (max-width: 939px) {
  .film2__kadr {
    margin-inline: calc(var(--pad-x) * -1);
    border-radius: 0;
  }
}

.film2__wideo {
  width: 100%;
  height: 100%;
  object-fit: cover;
}


/* ══ WSTĘP (oferta.html) ══════════════════════════════════════════════
   Ten sam uklad co FILM na index.html - tekst po lewej, kwadratowy kadr
   po prawej, 50/50 od 940px, kolumna ponizej (patrz `.film__uklad`/`.film__kadr`
   wyzej po pelne uzasadnienie proporcji i progu). Blok BEM WLASNY (`wstep`),
   nie reuzyty `film` - ten sam wzorzec ukladu ("kadr" jako nazwa ramki
   w obu), ale inna tresc: `.film__wideo` niesie wideo (nazwa elementu,
   komentarz o playbacku przy `.film__wideo` wyzej), `.wstep__obraz` niesie
   nieruchomy obraz. Ten sam podzial, co przy `.wzestawie__poz` obok
   `.proces__krok` - osobny blok na sekcje, wspolny na wzorzec
   (2026-08-14: `.wideo`/`.wideo__plik` przemianowane na `.film__kadr`/
   `.film__wideo` wlasnie po to, zeby ten wzorzec bylo widac w nazwach,
   nie tylko w komentarzu). */
.wstep__uklad {
  display: grid;
  gap: clamp(var(--s-8), 5vw, var(--s-12));
}

.wstep__uklad .head { margin-bottom: 0; }

@media (min-width: 940px) {
  .wstep__uklad {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    column-gap: clamp(var(--s-10), 6vw, var(--s-20));
  }
}

.wstep__kadr {
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--c-surface);
  border-radius: var(--r-xl);
}

/* Telefon i waskie okno: kadr od krawedzi do krawedzi - identyczna logika
   co `.film__kadr` (patrz komentarz tam po pelne uzasadnienie odwrotnego
   paddingu zamiast 100vw). */
@media (max-width: 939px) {
  .wstep__kadr {
    margin-inline: calc(var(--pad-x) * -1);
    border-radius: 0;
  }
}

.wstep__obraz {
  width: 100%;
  height: 100%;
  object-fit: cover;
}


/* ══ KONFIGURACJE ══════════════════════════════════════════════════════
   Cztery ustawienia tego samego modulu. Cztery jednakowe karty byly by
   tu falszem - warianty roznia sie tylko liczba i rozstawem kaset, wiec
   niosa je wiersze rozdzielone wloskiem, a rozroznia rysunek.

   Uklad jest niesymetryczny celowo: waska kolumna wprowadzenia stoi
   w miejscu (sticky), szeroka kolumna wariantow przesuwa sie obok niej. */

/* ── siatka wariantow ──
   2x2 na szerokim ekranie, kolumna na waskim (zmiana 2026-08-07 -
   wczesniej cztery pelnoszerokie wiersze kadr|opis). Kafle ida na cala
   szerokosc kontenera, wiec przy 1440px jeden ma ok. 640px - dosc, zeby
   render czytal sie bez wysilku. Wszystkie cztery pola maja identyczny
   wymiar, a rendery pochodza z jednego, nieruchomego ujecia, wiec realna
   proporcja miedzy wariantami zostaje zachowana. */
.konfig__lista {
  display: grid;
  gap: clamp(var(--s-4), 2vw, var(--s-6));
}

@media (min-width: 800px) {
  .konfig__lista { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── kafel jako link (2026-08-14) ──
   `<a>` opakowuje `<figure>` wewnatrz `<li>` - `display: block` gwarantuje,
   ze dodatkowy element w drzewie nie zmienia geometrii (nadruk jest
   pozycjonowany wzgledem `.konfig__kadr`, nie wzgledem linku). Kolor
   i podkreslenie dziedzicza z tekstu wewnatrz kafla, `:focus-visible`
   dochodzi, bo kafel byl dotad calkowicie poza tabulatorem. */
.konfig__kafel-link {
  display: block;
  color: inherit;
  text-decoration: none;
  border-radius: var(--r-lg);
}

.konfig__kafel-link:focus-visible {
  outline: 2px solid var(--c-brand);
  outline-offset: 2px;
}

/* ── kadr z nadrukiem ──
   Pole ma proporcje realnych renderow - 4:3, 1440x1080 (dostarczone
   2026-08-06). Obraz wypelnia je dokladnie, bez pasow i bez przyciecia,
   a caly opis lezy na nim: liczby u gory, nazwa i zdanie w lewym dolnym
   rogu. Bez przyciemnienia pod tekstem - rendery sa niemal czarne, wiec
   bialy tekst czyta sie na nich wprost (decyzja 2026-08-07). */
.konfig__kadr {
  position: relative;
  margin: 0;                    /* figure ma wlasny margines od przegladarki */
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--c-surface);

  /* odstep nadruku od krawedzi kadru - jedno miejsce dla obu warstw */
  --nadruk-pad: clamp(var(--s-4), 2.4vw, var(--s-8));
}

.konfig__kadr img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform var(--d-normal) var(--ease-out);
}

/* ── lekkie powiekszenie kadru pod kursorem (2026-08-10, prosba klienta) ──
   Rusza sie WYLACZNIE obraz - nadruk (`.konfig__dane`, `.konfig__opis`)
   jest pozycjonowany wzgledem `.konfig__kadr`, wiec zostaje nieruchomy
   i tekst nie plywa razem z renderem. Przyciecie bierze na siebie
   `overflow: hidden`, ktore kadr ma juz dla samego promienia.

   1.04, nie wiecej: rendery pochodza z jednego, nieruchomego ujecia
   i pokazuja realna proporcje miedzy wariantami - mocniejszy zoom
   zaczalby ta proporcje falszowac. Czas --d-normal (300 ms), nie --d-fast:
   to ruch obrazu, nie odpowiedz kontrolki.

   Cala regula jest w @media (hover: hover) - na dotyku stan przykleja sie
   po tapnieciu i kadr zostaje powiekszony bez powodu. */
@media (hover: hover) {
  .konfig__kafel:hover .konfig__kadr img { transform: scale(1.04); }
}

/* ── CIEN POD NADRUKIEM, TYLKO NA WASKIM EKRANIE (2026-08-11) ─────────
   Decyzja z 2026-08-07 brzmiala "bez przyciemnienia - rendery sa niemal
   czarne, wiec bialy tekst czyta sie na nich wprost; najpierw zobaczyc bez,
   potem ocenic". Ocena przyszla z pomiaru kontrastu liczonego POD LITERAMI:
   na desktopie nadruk jest w porzadku, ale ponizej 800 px kafle ida w jednej
   kolumnie, wiec render jest duzo wiekszy i opis wypada nad rozswietlona
   podloga kadru, a nie nad czernia. Zmierzone 3,12 przy progu 4,5.

   Gradient siega tylko dolnej czesci kadru - dokladnie tam, gdzie lezy nazwa
   i opis. Gorne dwie trzecie renderu (sama kaseta) zostaja nietkniete, wiec
   obraz nadal jest obrazem, a nie przygaszonym tlem. Liczby wymiarowe u gory
   kadru nie potrzebuja niczego: leza na czerni sufitu hali.

   Pseudoelement na `.konfig__kadr`, nie tlo na `.konfig__opis`: cien ma byc
   ciagly przez cala szerokosc kadru, a nie prostokatem pod samym akapitem.

   ── ::before, NIGDY ::after (poprawka 2026-08-11, zgloszenie klienta:
      "zdjecia przyciemnione bardzo mocno, RAZEM Z CZCIONKA - jakby gradient
      byl na pierwszym planie") ──
   Pierwsza wersja uzywala `::after` z komentarzem "z-index nie jest
   potrzebny, nadruk stoi w drzewie po tym pseudoelemencie". To bylo
   odwrotnie. `::after` jest wstawiany jako OSTATNIE dziecko elementu, czyli
   PO `.konfig__dane` i `.konfig__opis` - a wsrod elementow pozycjonowanych
   bez z-indexu o kolejnosci malowania decyduje wlasnie kolejnosc w drzewie.
   Welon lezal wiec na literach, nie pod nimi: tekst dostawal to samo 72%
   czerni co obraz i gasl razem z nim. Pomiar kontrastu tego nie wychwycil,
   bo liczyl jasnosc glifow i tla PO zlozeniu warstw - obie przyciemnione
   w tym samym stopniu daja poprawny iloraz przy zle wygladajacym kadrze.

   `::before` jest pierwszym dzieckiem, wiec maluje sie przed nadrukiem
   (nad obrazem, bo jest pozycjonowany, a obraz nie) - i cala zaleznosc
   wynika z konstrukcji, a nie z z-indexu, ktory ktos moze pozniej nadpisac.

   Krycie zeszlo przy okazji z 72/45% na plaskie 32-34%: skoro welon przestal
   gasic litery, do progu wystarcza go o polowe mniej, a decyzja klienta
   z 2026-08-07 ("bez przyciemnienia - rendery sa niemal czarne") kaze brac
   minimum, nie zapas.

   ZMIERZONE pod glifami (metoda dwoch zrzutow, maska z roznicy, okno 360 px,
   wszystkie cztery kafle, kadry wymuszone jako `eager` - inaczej pomiar leci
   na pustym `--c-surface` zamiast na renderze):
     Ekran zlozony 11,62 | Pojedynczy ekran 7,95 | Grupa osobno 4,61
     | Grupa polaczona 10,58   - najgorszy PIKSEL w sekcji 4,61 przy progu 4,5,
   zero pikseli ponizej progu. Bez welonu najgorszy piksel to 3,01. Marginesu
   jest tu malo, wiec przy podmianie renderow pomiar powtorzyc. */
@media (max-width: 799px) {
  .konfig__kadr::before {
    content: '';
    position: absolute;
    inset: auto 0 0 0;
    height: 70%;
    pointer-events: none;
    /* Ksztalt: PLASKI WELON POD CALYM NADRUKIEM, wygaszony dopiero w gornej
       cwiartce - nie gradient rosnacy ku dolowi. Wersja gradientowa (35% przy
       dolnej krawedzi, 18% w polowie) miala krycie najwieksze tam, gdzie
       render i tak jest czarny, a najmniejsze przy PIERWSZEJ linii opisu, czyli
       dokladnie na wysokosci, na ktorej w kadrze "Grupa osobno" przechodzi
       rozswietlona krawedz srodkowego ekranu. Pomiar pod glifami: 11 pikseli
       na 3969 schodzilo do 3,01 przy progu 4,5 - malo, ale to realne litery,
       nie szum.
       Plaskie 32% w calym pasie tekstu kosztuje przy dolnej krawedzi MNIEJ
       niz poprzednie 35%, a zdejmuje hotspot. Gorne 30% kadru - sama kaseta -
       zostaje nietkniete. */
    background: linear-gradient(
      to top,
      color-mix(in oklab, #000 34%, transparent) 0%,
      color-mix(in oklab, #000 32%, transparent) 55%,
      color-mix(in oklab, #000 16%, transparent) 78%,
      transparent 100%);
  }
}

/* nazwa + opis: lewy dolny rog */
.konfig__opis {
  position: absolute;
  left: var(--nadruk-pad);
  bottom: var(--nadruk-pad);
  right: var(--nadruk-pad);      /* granica lamania, nie szerokosc bloku */
  max-width: 34ch;
}

.konfig__nazwa {
  font-size: var(--t-h3);
  font-weight: var(--w-semi);
}

.konfig__opis p {
  margin-top: var(--s-2);
  color: var(--c-text-2);
  font-size: var(--t-sm);
  line-height: 1.5;
}

/* ── stopka sekcji ──
   Pytanie po lewej, przycisk po prawej. Stoi pod lista, bo dopiero po
   czterech ustawieniach klient wie, czego nie wie.

   BEZ LINII NAD SOBA (2026-08-10, decyzja klienta): kreska zamykala liste
   wariantow jak stopke tabeli, a to nie jest podsumowanie tylko osobne
   wezwanie. Rozdziela je teraz sam odstep (padding-top zostaje). */
.konfig__stopka {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-6);
  padding-top: clamp(var(--s-8), 4vw, var(--s-12));
  color: var(--c-text-2);
  font-size: var(--t-body-lg);
}

/* dane wymiarowe - liczby techniczne, wiec tabularne i ciche.
   Stoja u gory kadru, na tej samej osi co opis pod nimi: caly nadruk
   ma jedna linie lewej krawedzi, zamiast dwoch niezaleznych rogow. */
.konfig__dane {
  position: absolute;
  top: var(--nadruk-pad);
  left: var(--nadruk-pad);
  right: var(--nadruk-pad);
  color: var(--c-text-muted);
  font-size: var(--t-xs);
  letter-spacing: 0.04em;
}


/* ══ KONFIGURATOR 3D (oferta.html) ═══════════════════════════════════
   Model przypiety w kadrze (position: sticky), tresc czterech wariantow
   zmienia sie pod nim w rytmie przewijania - patrz js/konfig3d.js.
   Statyczna lista `#warianty-lista` (.sr-only) jest jedynym miejscem
   prawdy dla tresci i pelnoprawnym fallbackiem bez JS/WebGL.

   Bez karty/ramki wokol SCENY (decyzja 2026-08-06: "nie musimy miec
   kontenera - karta i kolka moga byc w przestrzeni bez ograniczenia
   wizualnego") - model stoi wprost na tle strony, nie w pudelku.
   UWAGA: dotyczy juz tylko sceny. Panel opisowy pod nia dostal wlasna
   powierzchnie (kafel `--c-szklo`) 2026-08-10 - patrz .konfig3d__panel.

   Caly kadr miesci sie w jednym viewporcie (decyzja 2026-08-06): naglowek
   sekcji, scena i okno opisowe to trzy wiersze jednej siatki o wysokosci
   ekranu pomniejszonej o pasek naglowka. Scena to jedyny wiersz elastyczny
   (1fr) - to ona oddaje miejsce tekstowi, nie odwrotnie. Model dopasowuje
   sie do tego, co zostanie: kamera cofa sie, gdy kadr jest za waski
   (patrz `ustawKamere` w js/konfig3d.js).

   Kolejnosc krokow (2026-08-06, desktop ≥940px, feedback klienta):
   naglowek/panel dostaja lekki ujemny margines w strone sceny, zeby
   wchodzily w jej pusta przestrzen zamiast stac w trzech zamknietych
   pasach - ten sam kierunek "bez wrappera" co reszta sekcji. */

.konfig3d__uklad {
  position: relative;
  /* 400vh na trzy przejscia miedzy czterema stanami + 70vh zapasu na koncu
     (feedback klienta 2026-08-06), zeby po dojechaniu do "grupa polaczona"
     kadr chwile postal, zanim strona wjedzie w kolejna sekcje - bez zapasu
     pin zwalnial sie natychmiast po ostatnim kroku. Wartosc bufora musi byc
     zgodna z BUFOR_VH w js/konfig3d.js (jedno zrodlo liczby w dwoch
     miejscach, bo JS liczy geometrie z realnych wymiarow, nie z CSS). */
  height: calc(400vh + 70vh);
  margin-bottom: clamp(var(--s-16), 8vw, var(--s-24));
}

.konfig3d__pin {
  position: sticky;
  top: var(--h-header);
  z-index: var(--z-body);
  height: calc(100svh - var(--h-header));
  display: grid;
}

/* trzy wiersze: naglowek / scena / okno opisowe.
   min-height:0 na scenie jest KONIECZNE - bez tego wiersz 1fr nie zejdzie
   ponizej rozmiaru canvasa i siatka wypchnie panel poza ekran. */
.konfig3d__kadr {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(var(--s-3), 2vh, var(--s-6));
  height: 100%;
  padding-block: clamp(var(--s-4), 3vh, var(--s-10));
}

/* naglowek w kadrze jest cichszy niz w sekcjach przewijanych - konkuruje
   o wysokosc z modelem, wiec traci dolny margines i schodzi o stopien.
   Ujemny margines (2026-08-06, desktop) ciagnie go w puste gorne pole
   sceny (tam gdzie LUZ w kamerze i tak zostawia powietrze wokol modelu)
   zamiast trzymac go w calkowicie osobnym pasie. */
.konfig3d__head { margin-bottom: 0; }
.konfig3d__head .head__title { font-size: var(--t-h2); }
.konfig3d__head .head__sub   { margin-top: var(--s-2); font-size: var(--t-body); }

/* ujemny margines nachodzenia obowiazuje juz TYLKO na waskim ekranie -
   na desktopie naglowek nie stoi nad scena, tylko obok niej (patrz blok
   dwukolumnowy na koncu tej sekcji) */

/* position:relative - hostuje kropki jako overlay (patrz .konfig3d__kropki)
   i maske na krawedziach. Maska zmiekcza boki canvasa, zeby boczne kasety
   (patrz X_POZA_KADREM w konfig3d.js) nigdy nie wygladaly jak twardo
   obciete prostokatem - dziala niezaleznie od etapu ich wjazdu w kadr.
   Procent trzymany nisko (6%), bo najszerszy stan ("grupa polaczona") ma
   najmniej luzu wokol siebie (LUZ w konfig3d.js) - zbyt szeroka maska
   zaczelaby wizualnie ucinac sam model, nie tylko jego otoczenie. */
.konfig3d__scena {
  position: relative;
  min-height: 0;
  width: 100%;
  height: 100%;
}
@media (min-width: 940px) {
  .konfig3d__scena {
    -webkit-mask-image: linear-gradient(to right, transparent 0%, black 6%, black 94%, transparent 100%);
            mask-image: linear-gradient(to right, transparent 0%, black 6%, black 94%, transparent 100%);
  }
}
.konfig3d__scena canvas { display: block; }

/* ── kropki - element sceny, nie panelu opisowego ──
   Overlay wysrodkowany u dolu kontenera modelu (2026-08-06, feedback
   klienta: z rogu na srodek) - nawigacja po obiekcie stoi na obiekcie,
   panel pod modelem zostaje czysto opisowy (tytul + opis + dane), bez
   trzeciej kolumny na kontrolki. Srodek zamiast bocznego paska w pionie:
   maska na krawedziach sceny (`.konfig3d__scena`) wygasza tresc przy
   bokach, wiec stala kontrolka tam bylaby czesciowo przezroczysta. */
.konfig3d__kropki {
  position: absolute;
  left: 50%;
  /* nizej niz przy okraglych kropkach: znak ma teraz 10 px wlasnego
     paddingu u dolu, wiec sama kreska i tak lezy wyzej niz krawedz boxa */
  bottom: var(--s-3);
  transform: translateX(-50%);
  z-index: 1;
  display: flex;
  gap: var(--s-3);
}

/* Znacznik jest KRESKA, nie kolkiem (2026-08-07). Marka jest ostra
   (promienie 2-4 px, patrz tokens.css) i sklada sie z modulow - okragla
   kropka byla jedynym pelnym owalem w calym serwisie i czytala sie jak
   kontrolka z obcego zestawu. Ten sam jezyk co pasek postepu formularza
   kontaktowego (.stepper), ktory tez jest przeciety na kasety.

   Padding + background-clip: content-box - znak ma 3 px wysokosci, ale
   obszar dotyku 23 px. Bez tego kreska bylaby celem nie do trafienia
   palcem. Stan aktywny rosnie transformem, nie szerokoscia: `width` jest
   wlasnoscia ukladu i jej animowanie kosztuje przeliczenie ramki. */
/* padding-block: 11px, nie 10px (2026-08-11) - obszar dotyku mial przez to
   44 x 23 px, czyli jednego piksela zabraklo do progu WCAG 2.2 (2.5.8, AA,
   minimum 24 px). Sam znak ma nadal 3 px wysokosci, wiec wyglad sie nie zmienia.

   Kolor stanu nieaktywnego z --c-border-kontrolka, nie z --c-border-hover:
   kropka JEST kontrolka, wiec obowiazuje ja prog 3:1 (WCAG 1.4.11), a stary
   token dawal 2,40 na kanwie. Nowy daje 3,13. */
.konfig3d__kropka {
  appearance: none;   /* bez tego niektore przegladarki rysuja wlasny, owalny ksztalt przycisku */
  box-sizing: content-box;
  width: 32px;
  height: 3px;
  padding-block: 11px;
  background-clip: content-box;
  border-radius: 1px;
  background-color: var(--c-border-kontrolka);
  transition: background-color var(--d-fast) var(--ease-out), transform var(--d-fast) var(--ease-out);
}
/* Stan aktywny w kolorze podstrony, nie marki (2026-08-08, decyzja klienta).
   Konfigurator zyje wylacznie na oferta.html, a ta podstrona ma niebieski
   neon w kadrze hero i niebieska wstege - kropki sa najnizszym pietrem tej
   samej warstwy "atmosfery", wiec ida za nia.

   To JEDYNE ustepstwo od zasady "czerwien = akcja". Czerwien zostaje na
   przyciskach i na finalnym CTA na wszystkich stronach - kropka nie jest
   wezwaniem do dzialania, tylko znacznikiem pozycji w czterech stanach
   modelu. Nie rozszerzac tego na .btn ani na sekcje .cta. */
.konfig3d__kropka.is-aktywna { background-color: var(--neon-oferta); transform: scaleX(1.25); }

/* ── okno opisowe ──
   WRACA JAKO KAFEL (2026-08-10, decyzja klienta) - biel 10% i obrys, ten
   sam wariant co kafle w pozostalych sekcjach tej rundy (`--c-szklo`,
   tokens.css). Odwraca to decyzje z 2026-08-06 ("karta/blur/ramka zdjete,
   tekst stoi wprost na tle strony"), i to swiadomie: wtedy chodzilo o to,
   zeby panel nie byl pudelkiem obok sceny bez pudelka; teraz powierzchnia
   ma robic co innego - odciac opis wariantu od modelu i tla, zeby bylo
   widac, ze to on zmienia sie przy przewijaniu.

   Roznica wobec starej wersji: to nie jest karta z rozmyciem tla
   (backdrop-filter), tylko przezroczysta biel - kolor sceny pod spodem
   nadal przez nia przechodzi.

   FAKTURA SZKLA (2026-08-10, druga tura) - panel dostaje kierunek swiatla
   i refleks na krawedzi z tych samych tokenow, co `.card--szklo`, ALE BEZ
   `--blur-szklo`. Pod tym jednym kaflem stoi zywy canvas WebGL: rozmycie
   tla kazalo by przegladarce przeliczac je przy KAZDEJ klatce modelu,
   a to jedyna sekcja w serwisie, ktora ma cos animowanego pod spodem.
   Reszta kafli stoi nad statycznym tlem, wiec tam blur jest darmowy.

   UWAGA NA TEN KOMENTARZ (blad znaleziony 2026-08-10): w polowie tekstu
   stal znacznik zamykajacy komentarz, a akapit konczyl sie drugim takim
   samym. Dla przegladarki komentarz konczyl sie w tym pierwszym miejscu,
   wiec reszta akapitu byla juz trescia arkusza - bledna, a odzyskiwanie
   po bledzie zjadalo razem z nia CALA regule ponizej. Objaw byl mylacy:
   kafel szkla po prostu nie istnial (zero tla, zero obrysu, zero
   paddingu), chociaz regula stala w pliku i wygladala poprawnie.
   Jeden komentarz = jedna para znacznikow, i nigdy nie wpisywac
   znacznika zamykajacego w srodku zdania.

   Tytul wysrodkowany, opis pod nim - jedna wysrodkowana kolumna, kropki
   juz nie sa kolumna gridu (patrz wyzej, przeniesione do sceny). Wysokosc
   jest zarezerwowana (min-height na .konfig3d__tresc), zeby zmiana wariantu
   nie przesuwala wiersza sceny - inaczej canvas przeliczalby sie przy
   kazdym kroku.

   Padding celowo mniejszy niz --pad-card: cala sekcja miesci sie w jednym
   viewporcie, wiec kazdy piksel wnetrza panelu zabiera wysokosc modelowi. */
.konfig3d__panel {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--s-2);
  max-width: var(--w-narrow);
  margin-inline: auto;
  padding: var(--s-4) var(--s-6);
  background-color: var(--c-szklo);
  background-image: var(--grad-szklo);
  border: 1px solid var(--c-szklo-krawedz);
  border-radius: var(--r-lg);
  box-shadow: var(--cien-szklo);
}

/* jw. - nachodzenie panelu na scene dotyczy tylko ukladu jednokolumnowego */

/* Numer kroku ("1 / 4") - usuniety wizualnie 2026-08-06 (feedback klienta:
   zostaja same kropki), zostaje w DOM/aktualizowany przez js/konfig3d.js
   (po #konfig3d-num) wylacznie dla czytnikow ekranu. Ukrycie niesie klasa
   `.sr-only` z base.css wprost w znaczniku (2026-08-14 - dawniej wlasna
   regula `.konfig3d__krok` tu, bajtowo identyczna z `.sr-only`; klasa
   `.konfig3d__krok` skasowana z HTML razem z nia, bo nie mial jej kto
   uzywac - ani CSS, ani JS, ktory trzyma sie samego #konfig3d-num). */

.konfig3d__nazwa {
  font-size: var(--t-h3);
  font-weight: var(--w-semi);
  line-height: var(--lh-head);
}

/* Rezerwa wysokosci = najdluzszy z czterech opisow plus wiersz danych.
   Bez rezerwy zmiana wariantu zmienialaby wysokosc panelu, a wiec
   i wysokosc canvasa - model skakalby przy kazdym kroku.

   Rezerwa siedzi na WRAPPERZE, nie na akapicie (poprawka 2026-08-07).
   Wczesniej `min-height` stalo na `.konfig3d__opis` i przy krotkich
   opisach ("Niska, dwustronna forma...", jedna linia) puste wiersze
   rezerwy wpadaly MIEDZY opis a dane techniczne - linia z wymiarami
   odrywala sie od swojego opisu i zawisala samotnie w polowie kolumny.
   Na wrapperze ta sama rezerwa dziala tak samo dla canvasa, ale wolne
   miejsce zostaje POD para "opis + dane", czyli tam, gdzie ma byc.

   Wartosc: liczba linii opisu razy interlinia, plus odstep i wiersz
   danych. Baza to ekran waski (tekst lamie sie na najwiecej wierszy). */
.konfig3d__tresc {
  display: grid;
  align-content: start;
  min-height: calc(1.55em * 5 + var(--s-2) + 1.55em);
}
@media (min-width: 720px) {
  .konfig3d__tresc { min-height: calc(1.55em * 4 + var(--s-2) + 1.55em); }
}

.konfig3d__opis {
  color: var(--c-text-2);
  line-height: 1.55;
}

/* --c-text-2, nie --c-text-muted (2026-08-11). To najmniejszy stopien w calym
   serwisie (13 px) na PRZEZROCZYSTYM kaflu, pod ktorym przesuwa sie model -
   tlo nie jest stale, wiec kontrast zmienial sie w trakcie przewijania:
   zmierzone 4,23 w spoczynku i 3,77 nad jasniejszym miejscem sceny, przy
   progu 4,5. Po zmianie 7,16. Ta sama zasada, ktora obowiazuje juz w panelu
   kontaktowym: gdzie tlo jest jasne albo ruchome, drobny tekst schodzi
   o stopien wyzej w hierarchii zamiast podnosic globalny token. */
.konfig3d__dane {
  margin-top: var(--s-2);
  color: var(--c-text-2);
  font-size: var(--t-xs);
  letter-spacing: 0.04em;
}

/* ══ KONFIGURATOR 3D - UKLAD DWUKOLUMNOWY (desktop) ════════════════════
   Model po prawej, opisy po lewej (decyzja klienta 2026-08-07). Wczesniej
   wszystko stalo w jednej wysrodkowanej kolumnie: naglowek, model, opis -
   trzy pasy jeden pod drugim, kazdy wysrodkowany. Uklad boczny robi z tego
   normalna relacje "obiekt i jego opis": oko czyta z lewej, potwierdza
   modelem po prawej, i nie musi skakac w pionie przy kazdym kroku.

   Model dostaje szersza kolumne (0.85 : 1.15) - to on jest tresc, tekst
   go opisuje. Kropki postepu zostaja tam, gdzie byly: przy dolnej
   krawedzi sceny, czyli POD modelem (overlay w .konfig3d__scena) - to
   nawigacja po obiekcie, wiec nalezy do obiektu, nie do kolumny tekstu.

   Siatka: dwa wiersze (naglowek / opis wariantu) w lewej kolumnie, scena
   rozpieta na obu w prawej. Opis stoi przy gornej krawedzi swojego
   wiersza (align-self: start), a wiersz jest elastyczny (1fr) - dzieki
   temu dluzszy opis rosnie w dol w pustej przestrzeni i NIE zmienia
   wysokosci kadru, wiec canvas nie przelicza sie przy zmianie wariantu.
   `head--center` z HTML obowiazuje od tej chwili tylko na waskim ekranie. */

@media (min-width: 940px) {

  .konfig3d__kadr {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    grid-template-rows: auto minmax(0, 1fr);
    align-content: center;
    column-gap: clamp(var(--s-10), 5vw, var(--s-20));
  }

  /* Kreska pod naglowkiem (2026-08-07) - jedyny znak w calej sekcji.
     Bez niej lewa kolumna byla trojka blokow tekstu wiszaca w pustym
     polu: oko nie mialo sie o co zaczepic i cala kompozycja czytala sie
     jako niedokonczona. Kreska pozioma na pelna szerokosc kolumny, a nie
     akcent przy krawedzi - to podzial tresci, nie ozdoba. */
  /* NAGLOWEK ZACZYNA SIE NIZEJ NIZ KADR (2026-08-10, decyzja klienta:
     "h2 i opis trochę nizej - tresc po lewej dopasowana do wysokosci
     wrappera modelu"). Lewa kolumna szla wczesniej od gornej do dolnej
     krawedzi przypietego kadru, czyli przez cala wysokosc ekranu, podczas
     gdy model - przez `LUZ` w kamerze (js/konfig3d.js) - zajmuje w swojej
     kolumnie wyraznie mniej. Naglowek startowal wiec kilkadziesiat pikseli
     nad gornym punktem modelu, a cala kompozycja czytala sie jako dwie
     kolumny o roznej wysokosci.

     Odsuniecie liczone w `vh`, nie w pikselach: kadr ma wysokosc ekranu,
     wiec puste pole wokol modelu tez rosnie razem z nim. Clamp trzyma
     wartosc w granicach, w ktorych naglowek nie zjedzie na model przy
     niskim oknie.

     Wysokosc kadru sie NIE zmienia - naglowek stoi w wierszu `auto`,
     a scena jest rozpieta na obu wierszach, wiec canvas nie przelicza
     sie przez to ani razu. Dolna krawedz lewej kolumny zostaje tam, gdzie
     byla: panel jest dosuniety do dolu (align-self: end) i lezy w jednej
     linii ze znacznikami krokow pod modelem. */
  .konfig3d__head {
    grid-column: 1;
    grid-row: 1;
    justify-items: start;
    text-align: left;
    margin-top: clamp(var(--s-6), 7vh, var(--s-16));
    margin-bottom: var(--s-6);
    padding-bottom: var(--s-6);
    border-bottom: 1px solid var(--c-border);
  }
  /* 22ch, nie 14ch (2026-08-10): "Wybierz swoj wariant" ma 20 znakow i przy
     ciasniejszym limicie lamalo sie na dwa wersy, mimo ze lewa kolumna ma
     na nie miejsce. Limit zostaje (kolumna jest waska, wiec przy dluzszym
     copy ma byc gdzie zwinac), tylko lezy powyzej realnej dlugosci tytulu. */
  .konfig3d__head .head__title { max-width: 22ch; }
  .konfig3d__head .head__sub   { margin-inline: 0; }

  /* Opis wariantu siada przy DOLNEJ krawedzi kadru (2026-08-07), nie przy
     gornej. Wczesniej naglowek i opis staly jeden pod drugim u gory,
     a dolna trzecia lewej kolumny zostawala pusta - kompozycja opadala.
     Teraz kadr ma dwa punkty zaczepienia po przekatnej (naglowek u gory,
     opis u dolu), a puste pole miedzy nimi jest zamierzone.
     Przy okazji opis lezy na tej samej wysokosci co znaczniki krokow pod
     modelem - dwie rzeczy, ktore zmieniaja sie razem, stoja w jednej linii.
     Zmiana wariantu nie rusza kadru, bo wysokosc panelu jest zarezerwowana
     (patrz .konfig3d__tresc wyzej). */
  .konfig3d__panel {
    grid-column: 1;
    grid-row: 2;
    align-self: end;
    justify-items: start;
    text-align: left;
    max-width: none;
    margin-inline: 0;
    /* rowne wnetrze ze wszystkich stron - od 2026-08-10 panel jest kaflem
       z wlasna powierzchnia, wiec `padding: 0` z poprzedniej wersji
       (tekst wprost na tle) sklejalby tekst z jego wlasnym obrysem */
    padding: var(--s-6);
  }

  .konfig3d__scena {
    grid-column: 2;
    grid-row: 1 / -1;
  }
}

/* ── waski ekran ──
   Kompozycja desktopowa (nachodzenie, maska na scenie) NIE wchodzi tutaj
   (decyzja 2026-08-06) - mobile ma juz zweryfikowany uklad, dostaje tylko
   techniczne dostosowania. Panel jest juz wysrodkowana kolumna w bazowym
   gridzie (patrz wyzej), wiec tu tylko ciasniejszy padding. Podtytul
   sekcji znika - to jedyna tresc, ktora sie powtarza (model i tak
   pokazuje, ze cos sie zmienia), a jest warta dwoch linii wysokosci
   modelu. Zostaje w DOM dla czytnikow ekranu. */
@media (max-width: 719px) {

  .konfig3d__head .head__sub {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .konfig3d__panel { padding: var(--s-4); }

  /* ── ZNACZNIKI W PION, PRZY BOKU MODELU (2026-08-11, decyzja klienta) ──
     Ta sama kontrolka co na desktopie, obrocona o cwiartke i przesunieta
     z dolu na prawa krawedz sceny. Powod jest jeden: na telefonie ta sekcja
     jest przypieta i steruje nia PRZEWIJANIE, a poziomy rzad kresek u dolu
     mowi dokladnie odwrotnie - kazdy poziomy pasek postepu czyta sie jako
     "przesun w bok". Pionowy tor mowi "jedz dalej w dol", czyli to, co
     uzytkownik ma tu naprawde zrobic.

     TO NIE JEST ODWROCENIE DECYZJI Z 2026-08-06 (kropki z rogu na srodek).
     Tamta odrzucila boczny pasek, bo maska na krawedziach sceny wygaszalaby
     stala kontrolke do polprzezroczystosci - ale maska stoi w bloku
     `min-width: 940px` (patrz .konfig3d__scena), wiec ponizej tego progu
     nie ma jej wcale i zarzut nie obowiazuje. Nie przenosic tego ukladu
     w gore progu bez zdjecia maski.

     Obszar dotyku zostaje nad progiem WCAG 2.2 (2.5.8, min. 24 px): padding
     przenosi sie z osi pionowej na pozioma, wiec 32 x 25 px zamiast
     25 x 32 px. Sam znak nadal ma 3 px grubosci. */
  .konfig3d__kropki {
    left: auto;
    right: 0;
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    flex-direction: column;
    gap: var(--s-2);
  }

  .konfig3d__kropka {
    width: 3px;
    height: 32px;
    padding-block: 0;
    padding-inline: 11px;
  }

  /* stan aktywny rosnie w osi DLUGOSCI kreski, tak samo jak na desktopie -
     tam byla to os X, tutaj kreska stoi w pionie, wiec Y */
  .konfig3d__kropka.is-aktywna { transform: scaleY(1.25); }

  /* Mniejszy stopien = ten sam tekst lamie sie na wiecej wierszy, wiec
     rezerwa (na wrapperze, patrz wyzej) tez musi urosnac. Liczona wprost
     ze stopni, nie w `em` - `em` na wrapperze to nadal 1rem, wiec rezerwa
     w `em` bralaby tu o kilkanascie px za duzo, a na mobile kazdy taki
     zapas zabiera wysokosc modelowi. */
  .konfig3d__opis  { font-size: var(--t-sm); }
  .konfig3d__tresc { min-height: calc(1.55 * var(--t-sm) * 6 + var(--s-2) + 1.55 * var(--t-xs)); }
}


/* ══ PROCES (oferta.html) ══════════════════════════════════════════════
   Cztery kroki obok siebie, bez numerow - klient wprost odrzucil typowy
   numerowany timeline z krokami 1-2-3-4. Uklad reuzywa .siatka-4
   (layout.css, 4 -> 2 -> 1 przez @media). Kazdy krok stoi samodzielnie,
   bez klasy .card - caly jego uklad jest tutaj. */

.proces__lista { list-style: none; }

/* ── kreska po lewej ──
   Krok dostaje pionowa linie zamiast numeru (decyzja klienta 2026-08-07).
   To ten sam gest, co kreska przecinajaca sygnet: znak porzadku, nie ozdoba.
   Cztery kroki = cztery rowne kreski, wiec czytaja sie jako jedna lista,
   a nie jako cztery akcenty konkurujace o uwage.

   KRESKA JEST NIEBIESKA, NIE CZERWONA (2026-08-10, decyzja klienta:
   "usunac albo zmienic na niebieski"). Kolor to --neon-oferta, czyli neon
   TEJ podstrony - ten sam, ktory niesie wstega w tle i aktywny znacznik
   kroku w konfiguratorze. Czerwien w calym serwisie znaczy "to jest akcja
   handlowa" (przyciski, sekcja CTA), a krok procesu niczego nie wzywa -
   jest opisem. Drugie ustepstwo od zasady "czerwien = akcja" i z tego
   samego powodu co pierwsze (patrz .konfig3d__kropka.is-aktywna).
   NIE rozszerzac tego na .btn ani na .cta.

   ZNAK O STALEJ WYSOKOSCI, NIE `border-left` (2026-08-08). Obramowanie
   idzie za wysokoscia pola, wiec kreska mierzyla dlugosc tekstu: "Realizacja"
   ma trzy linie opisu, "Zapytanie" jedna. W jednym wierszu flex wyrownuje to
   sam (align-items: stretch domyslnie w .siatka-4), ale w ukladzie
   jednokolumnowym (<640px) i miedzy wierszami siatki 2x2 (640-1039px) kazdy
   krok jest wlasnym wierszem i rownac nie ma czego. Pseudoelement o wysokosci
   nazwy kroku daje cztery IDENTYCZNE znaki na kazdym progu - kreska przestaje
   byc miarka, a zostaje znakiem.

   KROK JEST TERAZ KAFLEM (2026-08-10, decyzja klienta): `.card--szklo`
   w znaczniku, czyli biel 10% i obrys. Wczesniej stal wprost na tle strony,
   a jedynym znakiem byla czerwona kreska. Powierzchnia daje krokom wspolna
   ramke i - w odroznieniu od samej kreski - widac je takze wtedy, gdy oko
   przelatuje sekcje bez czytania.

   Kreska ZOSTAJE i nadal jest pseudoelementem o stalej wysokosci, nie
   `border-left`: wewnatrz kafla obramowanie tym bardziej szloby za jego
   wysokoscia, a rowne rzedy flexa (align-items: stretch) sprawilyby, ze
   kreska mierzylaby najdluzszy tekst w wierszu.

   Kreska stoi PRZY LEWEJ KRAWEDZI kafla (left: 0), a w pionie na wysokosci
   pierwszego wiersza nazwy - stad `top: var(--pad-card)`, ten sam token,
   ktorego uzywa `.card` na swoje wnetrze (tokens.css). Bez tego kreska
   zaczynalaby sie w samym rogu, kilkadziesiat pikseli nad tekstem, do
   ktorego nalezy. Wlasny `padding-left` odpadl - odstep od kreski daje
   teraz padding kafla. */
.proces__krok {
  position: relative;             /* uklad odniesienia dla kreski */
}

.proces__krok::before {
  content: '';
  position: absolute;
  left: 0;
  top: var(--pad-card);
  width: 2px;
  /* dokladnie jeden wiersz nazwy kroku - liczone ze stopnia i interlinii
     naglowka, nie w `em` na kroku (tam `em` to nadal 1rem tekstu ciaglego) */
  height: calc(var(--t-h3) * var(--lh-head));
  background: var(--neon-oferta);
}

.proces__nazwa { font-weight: var(--w-medium); }

.proces__opis {
  margin-top: var(--s-2);
  color: var(--c-text-2);
}


/* ══ W ZESTAWIE (oferta.html) ═════════════════════════════════════════
   Szesc pozycji w .siatka-2 (layout.css), bez `.card--szklo` - sasiedni
   "Proces" to juz szklane kafle, wiec kolejne szesc obok czytaloby sie
   jak ten sam komponent powtorzony. Zamiast tego ten sam gest znaku, co
   kreska procesu wyzej: pionowa kreska w --neon-oferta (niebieska - w tym
   serwisie czerwien znaczy wylacznie akcje handlowa, a to jest opis, nie
   wezwanie). Struktura reguly 1:1 z `.proces__krok::before`. */
.wzestawie__poz { position: relative; }

/* top: 0, NIE var(--pad-card) (poprawka 2026-08-14, zgloszenie klienta:
   "kreski sa trochę ponizej tekstu"). `.proces__krok::before` uzywa
   --pad-card, bo krok TAM jest kaflem `.card--szklo` z realnym paddingiem -
   offset kreski trafia w padding, ktory i tak przesuwa tekst. `.wzestawie__poz`
   to zwykle `<li>` bez paddingu (`.wzestawie__nazwa` to `<p>`, margin: 0
   po globalnym resecie w base.css), wiec tekst zaczyna sie dokladnie na
   gornej krawedzi `<li>` - kreska musi zaczynac sie tam samo.

   WYSOKOSC: `top: 0; bottom: 0`, NIE stala wysokosc jednej linii jak
   w `.proces__krok::before` (druga poprawka klienta 2026-08-14: "kreska
   moze byc dluzsza - na wysokosc bloku tekstowego"). Tu blok niesie nazwe
   I opis pod nia, wiec kreska ma isc przez cale `<li>`, nie tylko przez
   naglowek. Dziala, bo `.wzestawie__poz` jest elementem flex w `.siatka-2`
   (layout.css) - domyslne `align-items: stretch` daje mu realna, nie-auto
   wysokosc rowna najwyzszej pozycji w tym samym wierszu, wiec `top`+`bottom`
   maja wzgledem czego sie policzyc. */
.wzestawie__poz::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--neon-oferta);
}

.wzestawie__nazwa {
  padding-left: var(--pad-card);
  font-weight: var(--w-medium);
}

.wzestawie__opis {
  margin-top: var(--s-2);
  padding-left: var(--pad-card);
  color: var(--c-text-2);
}


/* ══ ZASTOSOWANIA ══════════════════════════════════════════════════════
   PRZEBUDOWANE 2026-08-11 (decyzja klienta: "ilosc i rozklad nie jest
   idealny, sekcja wymaga redesignu, musi pasowac do reszty strony").

   CO BYLO ZLE - trzy rzeczy naraz, z pomiaru na 390 i 1400 px:

     1. 23 hasla, w tym pary bliskoznaczne (Kongresy/Konferencje,
        Wystawy/Ekspozycje/Wernisaze/Showroomy/Stoiska, Gale/Bankiety/
        Jubileusze). Czytalo sie jak nasycanie slowami kluczowymi, a nie
        jak oferta - czyli odwrotnie, niz chce marka premium.
     2. Cztery akcenty `.is-mocne` byly ciemnym brazem na ciemnej szarosci
        i nie niosly zadnej informacji: czytelnik nie mial jak odgadnac,
        dlaczego akurat te cztery. Hierarchia bez znaczenia to szum.
     3. "Twoje wydarzenie" bylo srodkiem LISTY, ale nie srodkiem UKLADU -
        jedna z 24 identycznych pigulek.

   CO JEST TERAZ: osiem kategorii jako sam tekst, rozstawionych wokol
   jednej czerwonej plyty. Zero pudelek dookola. Pigulki odpadly w calosci
   razem z pelnym zaokragleniem - to bylo jedyne miejsce w serwisie
   z owalem, a klient prosil, zeby sekcja pasowala do reszty strony.

   SRODEK JEST SRODKIEM DOSLOWNIE, i to bez ani jednej reguly pozycjonujacej.
   Dziewiec pozycji, plyta na piatej: przy trzech kolumnach naturalny przeplyw
   siatki sadza ja w polu 2/2, czyli w geometrycznym srodku 3 x 3; przy dwoch
   kolumnach bierze caly wiersz i zostaja cztery kategorie nad nia i cztery
   pod. KOLEJNOSC W ZNACZNIKU JEST UKLADEM - nie ma `order`, nie ma
   `grid-area`, nie ma zamrozonych indeksow.

   To ostatnie jest calym sensem przebudowy od strony utrzymania. Poprzednia
   wersja miala rozklad wyszukany recznie tak, zeby JEDNA lista obsluzyla dwa
   rozne uklady (siatka 3 x N ponizej 640 px, chmura flex powyzej), a kazde
   dodane haslo przesuwalo wszystko w obu naraz. Tutaj dodanie kategorii to
   zmiana liczby kolumn albo wierszy, nie ponowne szukanie kolejnosci.

   PRZY ZMIANIE LICZBY KATEGORII: musi ich zostac OSIEM. Dziewiec pozycji to
   3 x 3 i 2 + 2 + plyta + 2 + 2. Siedem albo dziewiec kategorii rozsypuje
   symetrie w obu ukladach i plyta przestaje byc srodkiem. */

.zast__krag {
  display: grid;
  /* Telefon: dwie kolumny. Trzy nie wchodza - "Konferencje" w kroju
     naglowkowym potrzebuje ok. 108 px, a trzecia czesc kolumny tekstu przy
     320 px okna to 85 px. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* Odstep w pionie niesie cala luznosc tej sekcji, odkad nie ma pudelek -
     stad wyraznie wiekszy niz w poziomie i rosnacy z ekranem. */
  gap: clamp(var(--s-8), 5vw, var(--s-16)) var(--s-4);
  /* `center`, nie domyslne `stretch`: srodkowy wiersz jest wysoki na plyte,
     a kategorie po jej bokach maja dwa wiersze tekstu. Przy `stretch`
     wisialyby przy gornej krawedzi tego wiersza z duza pustka pod spodem -
     czytalo sie jako zle wyrownany rzad, nie jako kompozycja wokol srodka. */
  align-items: center;
  max-width: 860px;
  margin-inline: auto;
  text-align: center;
}

/* ── kategoria ──
   Kroj naglowkowy, nie tekstowy: to sa wypowiedzi sekcji, a nie etykiety
   interfejsu. Stopien plynny miedzy 20 a 32 px - dolna granica jest
   policzona, nie okragla: przy 320 px okna kolumna ma 128 px, a
   "Konferencje" w Afacad zajmuje przy 20 px okolo 108 px. */
.zast__poz {
  font-family: var(--f-display);
  font-size: clamp(1.25rem, 5.5vw, 2rem);
  font-weight: var(--w-semi);
  line-height: var(--lh-head);
  letter-spacing: var(--ls-display);
  color: var(--c-text);
  /* Haslo to TEKST, nie kontrolka, ale kursor edycji tekstu byl juz raz
     zglaszany w tej sekcji (2026-08-10) - `default` zostaje. */
  cursor: default;
}

/* Drugi czlon kategorii ("i kongresy") - wlasny wiersz, kroj tekstowy,
   stopien nizej i ciszej. To on robi z osmiu golych slow osiem kategorii:
   bez niego sekcja czytalaby sie jako niedokonczona lista, z nim kazda
   pozycja ma pierwszy i drugi rzut oka. Ten sam uklad, co `.head__title`
   plus `.head__sub` w naglowkach sekcji - tylko o dwa stopnie mniejszy.

   Selektor na klasie, nie na `span` - pierwszy człon też jest w <span>
   (potrzebne do ea()), więc gołe `.zast__poz span` łapałoby oba naraz. */
.zast__dodatkowe {
  display: block;
  margin-top: var(--s-1);
  color: var(--c-text-muted);
  font-family: var(--f-body);
  font-size: var(--t-sm);
  font-weight: var(--w-regular);
  letter-spacing: normal;
}

/* ── srodek: "Twoje wydarzenie" ──
   Jedyna nasycona powierzchnia w sekcji i jedyna, ktora w ogole jest
   powierzchnia. Cala hierarchia siedzi w tym jednym kontrascie, wiec
   `.is-mocne` nie jest juz do niczego potrzebne i zostalo usuniete.

   BIEL, NIE --c-text: --c-text to zlamana biel z lekkim cieplem, ktora na
   pelnej czerwieni marki daje 4,32 przy progu 4,5. Czysta biel daje 4,70.
   Ta sama poprawka, co na bloku CTA (patrz --c-brand w tokens.css).

   BEZ POSWIATY (--glow-brand), choc plyta jest tu ekranem. Sekcja lezy
   wewnatrz `.tor`, czyli czerwona wstega neonu przechodzi wprost za nia -
   czerwony blask na czerwonej wstedze zlewa sie w plame zamiast swiecic.
   Neon w tym kadrze niesie wstega, plyta niesie kolor.

   Wysokosc bierze sie z paddingu, nie z `aspect-ratio`: proporcja kasety
   zmienila sie 2026-08-11 (0,96 -> 0,64 m szerokosci), a --ratio-modul
   opisuje jeszcze stara bryle - przypiecie sie do niego cytowaloby
   nieaktualny wymiar produktu.

   LINK DO KONTAKTU (2026-08-14, decyzja klienta). `.zast__srodek` (`<li>`)
   zostaje CZYSTA KOMORKA SIATKI - trzyma wylacznie `grid-column`, bo to on
   robi srodek ukladu (patrz komentarz nad `.zast__krag`). Caly wyglad plyty
   - tlo, padding, krój, promien - schodzi na `.zast__srodek-link` (`<a>`),
   zeby hover/fokus/klik obejmowaly caly prostokat, nie tylko tekst.

   Rozmiar i krój ZOSTAJA - to swiadomie ta sama plyta, nie mniejszy
   przycisk. Poprzedni argument "wysokosc taka, zeby nie dalo sie pomylic
   z kontrolka" przestal obowiazywac, bo plyta STAJE SIE kontrolka - ale
   kompozycja sekcji (piata z dziewieciu, srodek 3x3) nie moze przez to
   drgnac. Sygnalem "to link" sa teraz hover (przyciemnienie do
   --c-brand-hover) i `:focus-visible` - bez strzalki `.btn__arrow`
   (usunieta 2026-08-14, decyzja klienta: tekst solo w jednej linii). */
.zast__srodek {
  grid-column: 1 / -1;
}

.zast__srodek-link {
  display: grid;
  align-items: center;
  justify-content: center;
  width: 100%;
  /* Wysokosc jest tu informacja, nie ozdoba: na telefonie plyta idzie przez
     cala szerokosc, a czerwony pasek na 48 px to w tym serwisie przycisk.
     Przy 40 px wypelnienia w pionie plyta ma ok. 110 px - dosc, zeby nie
     dalo sie jej pomylic z drobna kontrolka, mimo ze teraz nia jest.
     (`height: 100%` z tego miejsca skasowane 2026-08-14 - bylo martwe:
     `.zast__krag` ma `align-items: center`, wiec `.zast__srodek` nigdy
     nie ma wlasnej, definitywnej wysokosci do przejecia w procentach;
     wysokosc plyty i tak zawsze pochodzila z paddingu.) */
  padding: clamp(var(--s-10), 6vw, var(--s-12)) var(--s-6);
  background: var(--c-brand);
  border-radius: var(--r-md);
  color: #fff;
  font-family: var(--f-display);
  font-size: clamp(1.5rem, 6vw, 2.5rem);
  font-weight: var(--w-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--d-fast) var(--ease-out);
}

@media (hover: hover) {
  .zast__srodek-link:hover { background: var(--c-brand-hover); }
}

.zast__srodek-link:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -4px;
}

/* ── od 760 px: trzy kolumny, czyli pelne 3 x 3 ──
   Plyta wraca do jednego pola siatki (`grid-column: auto`) i trafia w srodek
   sama z siebie - piata pozycja z dziewieciu w trzech kolumnach to wiersz 2,
   kolumna 2. `align-self` zostaje domyslny (`stretch`), wiec plyta wypelnia
   wysokosc swojego wiersza i jest najwyzszym elementem srodkowego rzedu. */
@media (min-width: 760px) {

  /* Kolumna srodkowa szersza od bocznych (1,2fr wobec 1fr). Przy rownych
     trzecich plyta wychodzila niemal kwadratem i czytala sie jak przypadkowe
     czerwone pudlo; szersze pole daje jej proporcje bliska ekranowi, czyli
     temu, czym jest. Boczne kolumny traca po ok. 20 px i nadal mieszcza
     najdluzsza kategorie ("Konferencje", ok. 175 px przy 32 px stopnia). */
  .zast__krag {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, 1fr);
    gap: clamp(var(--s-12), 4vw, var(--s-20)) var(--s-8);
  }

  .zast__srodek {
    grid-column: auto;
  }

  /* PLYTA DOPASOWANA DO TEKSTU, NIE ROZCIAGNIETA NA KOLUMNE (2026-08-14,
     zgloszenie: "przycisk krotszy niz tekst"). Przyczyna: `width: 100%`
     z bazowej reguly rozciagal plyte na cala szerokosc kolumny srodkowej
     (`minmax(0, 1.2fr)`), a ta kolumna moze scisnac sie ponizej naturalnej
     szerokosci napisu przy stopniu z gornej granicy clamp (2,5rem, osiaganym
     juz od ~667px szerokosci okna, czyli tuz nad progiem 760px, gdzie
     kolumna srodkowa jest jeszcze waska) - `white-space: nowrap` z bazowej
     reguly nie mial wtedy jak sie zmiescic i tekst wychodzil poza czerwone
     tlo.

     Rozwiazanie: szerokosc plyty przejmuje tresc (`fit-content`), wysrodkowana
     w kolumnie (`margin-inline: auto`), a `max-width: 100%` gwarantuje, ze
     plyta nigdy nie przekroczy kolumny - nie generuje wiec przewijania
     w poziomie w zadnym scenariuszu. Dopiero gdy nawet peleny szerokosci
     kolumny nie starczy, tekst przechodzi na dwa wiersze (`white-space: normal`
     + `text-wrap: balance`, ten sam mechanizm co w naglowkach sekcji) zamiast
     wystawac poza plyte - to samoczynny margines bezpieczenstwa, nie kolejna
     magiczna liczba do przestrajania przy kazdej zmianie tresci przycisku. */
  .zast__srodek-link {
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
    padding: var(--s-12) var(--s-8);
    white-space: normal;
    text-wrap: balance;
  }
}


/* ══ O FIRMIE ══════════════════════════════════════════════════════════
   UKLAD BENTO + KAFLE (2026-08-10, decyzja klienta). Wczesniej byly to dwie
   kolumny golego tekstu: po lewej tytul ze zdaniem-wyroznikiem, po prawej
   dwa akapity prozy. Teraz tresc siedzi w kaflach `.card--szklo` (biel 10%,
   patrz components.css), ulozonych ta sama siatka `.bento` co parametry
   modulu: jeden dlugi blok na calej szerokosci, pod nim dwa mniejsze obok
   siebie.

   Zdanie o doswiadczeniu dostaje ten dlugi kafel, bo niesie TEZE sekcji:
   21 lat nalezy do If Group i do calej branzy eventowej, nie do ekranow LED.
   Liczba w malym kafelku obok akapitow sugerowalaby staz samych ekranow.

   PRZY OKAZJI ZAMKNIETY punkt z poprzedniej rundy: h2 tej sekcji nie
   miescil sie w jednej linii, bo stal w kolumnie 1fr z pary 1/1.35 (ok.
   520 px przy oknie 1440). Teraz jest zwyklym `.head__title` nad siatka,
   na pelnej szerokosci kontenera - miejsca ma az nadto. Klasa
   `.firma__tytul` i jej limit zniknely razem z dwukolumnowym ukladem. */

/* siatke niesie `.bento` (layout.css) - tutaj tylko odstep miedzy kaflami
   wiekszy niz w parametrach: to bloki tekstu, nie liczby, wiec potrzebuja
   wiecej powietrza miedzy soba, zeby nie czytaly sie jako jedna plyta */
.firma__uklad { gap: var(--s-6); }

/* proza w kaflu - stopien i kolor tekstu ciaglego, bez wlasnego max-width:
   szerokosc miary ustala juz sam kafel (polowa siatki bento) */
.firma__kafel p { color: var(--c-text-2); }

/* Teza sekcji - kroj marki i stopien naglowka, wysrodkowana w swoim dlugim
   kaflu. To samo rozwiazanie co `.param.is-duzy` w parametrach modulu:
   pojedyncza, krotka wypowiedz na calej szerokosci czyta sie jako wypowiedz
   dopiero wtedy, gdy stoi w srodku pola, a nie dosunieta do lewej krawedzi
   z pustka obok. */
.firma__wyroznik {
  color: var(--c-text);
  font-family: var(--f-display);
  font-size: var(--t-h3);
  font-weight: var(--w-medium);
  line-height: var(--lh-head);
  letter-spacing: var(--ls-display);
  text-wrap: balance;
}

.firma__kafel.is-duzy {
  align-items: center;
  text-align: center;
}


/* ══ FAQ ═══════════════════════════════════════════════════════════════
   Naglowek po lewej zostaje w kadrze, pytania przewijaja sie obok.
   Sama harmonijka to details/summary - style w components.css. */

.faq__uklad {
  display: grid;
  gap: clamp(var(--s-8), 4vw, var(--s-12));
}

@media (min-width: 940px) {
  .faq__uklad {
    grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr);
    gap: var(--s-16);
    align-items: start;
  }
  /* przyklejona jest cala glowa kolumny, nie sam naglowek - zdanie
     i odnosnik jada razem z nim (patrz .faq__glowa nizej) */
  .faq__glowa {
    position: sticky;
    top: calc(var(--h-header) + var(--s-10));
  }
}

/* Glowa lewej kolumny (2026-08-08). Sam przyklejony naglowek zostawial
   ~700 px pustki obok listy pytan. Zdanie + odnosnik daja kolumnie druga
   warstwe i - co wazniejsze - stawiaja droge do kontaktu dokladnie tam,
   gdzie rodzi sie pytanie, ktorego na liscie nie ma.
   Odnosnik to `.lnk` (components.css), czyli TRZECI poziom hierarchii:
   naga strzalka bez kwadracika, swiadomie cichszy niz przycisk - lewa
   kolumna ma pomagac, nie konkurowac z zamknieciem strony. */
.faq__glowa {
  display: grid;
  justify-items: start;
  gap: var(--s-4);
}

.faq__pomoc { color: var(--c-text-2); }

/* pierwsza kreska listy - pozycje maja tylko dolna, wiec gorna nalezy
   do calosci */
.faq__lista { border-top: 1px solid var(--c-border); }


/* ══ KONTAKT (podstrona) ═══════════════════════════════════════════════
   Jeden szary panel "strefy bezpiecznej" (`--c-surface-safe`), nie dwie
   osobne ciemne karty - formularz i dane kontaktowe dzielily wczesniej
   ta sama ciemna plyta co reszta strony i konkurowaly ze soba o uwage
   jako dwa osobne obiekty (decyzja klienta 2026-08-07: sekcja ma czuc
   sie prosto, informujaco, bezpiecznie). Formularz jako glowna kolumna,
   dane kontaktowe jako waska kolumna obok, rozdzielone kreska, nie
   przerwa. Na mobile formularz stoi pierwszy, bo to on jest akcja strony. */

/* ── kadr w tle calej sekcji (2026-08-10, material od klienta) ──
   `assets/kontakt-tlo.webp` - ten sam jezyk neonowych ksztaltow co kadry
   hero na pozostalych podstronach, i jedyny obraz na tej stronie: kontakt
   nie ma wlasnego hero, wiec bez niego bylby jedyna calkowicie plaska
   strona serwisu.

   Obraz idzie jako TLO PSEUDOELEMENTU, nie jako `background` sekcji -
   dzieki temu przyciemnienie i wygaszenie krawedzi leza w tej samej
   warstwie co on, a tresc sekcji zostaje nietknieta (`z-index: -1`
   w izolowanym kontekscie, ten sam wzorzec co `.siatka` i `.tor`
   w layout.css).

   TRZY WARSTWY PRZYCIEMNIENIA, kazda z innego powodu:
   1. plaski welon (--c-bg 78%) - render jest jaskrawy (czerwone neony na
      pelnej mocy) i bez niego bialy naglowek na nim po prostu nie czyta
      sie z odleglosci ekranu;
   2. wygaszenie u gory - sekcja zaczyna sie tuz pod przyklejonym
      naglowkiem, wiec bez tego obraz wjezdzalby pod pasek twarda linia;
   3. wygaszenie u dolu - sekcja styka sie ze stopka, ktora ma tlo strony;
      bez przejscia byloby widac szew miedzy nimi.
   Kolejnosc w `background-image` jest odwrotna do kolejnosci malowania:
   pierwsza pozycja lezy najwyzej, obraz na koncu jest najnizej.

   Panel formularza (.kontakt__uklad nizej) ma wlasne, NIEPRZEZROCZYSTE
   tlo "strefy bezpiecznej" i to sie nie zmienia: pola do wypelnienia nie
   moga stac na zdjeciu. Obraz otacza panel, nie wchodzi pod niego.

   Bez `background-attachment: fixed` - na iOS jest niewspierane, a na
   Androidzie daje szarpanie przy przewijaniu. */
.kontakt {
  position: relative;
  isolation: isolate;
}

.kontakt::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(to bottom, var(--c-bg) 0%, transparent 20%),
    linear-gradient(to top,    var(--c-bg) 0%, transparent 24%),
    linear-gradient(color-mix(in oklab, var(--c-bg) 78%, transparent) 0 100%),
    url('../assets/kontakt-tlo.webp');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* ── KONTRAST DROBNEGO TEKSTU W PANELU ──────────────────────────────
   Panel "strefy bezpiecznej" jest najjasniejsza powierzchnia w serwisie
   (--c-surface-safe), wiec najciemniejszy stopien tekstu przestaje sie na
   nim bronic: --c-text-muted ma tu 3,48 przy progu WCAG 4,5, i to pod
   tekstem --t-xs, czyli najmniejszym jaki jest.

   Rozwiazanie NIE przez kolejne podnoszenie tokenu (musialby dojsc do
   0.710, czyli zrownac sie z --c-text-2 i skasowac trzeci stopien w calym
   serwisie), tylko przez zejscie o stopien wyzej w hierarchii dokladnie
   tam, gdzie tlo jest jasne: w panelu drobny tekst nosi --c-text-2 (5,88).
   Jedna regula zamiast szescia poprawek w komponentach - i jedno miejsce
   do cofniecia, gdyby panel kiedys sciemnial. */
.kontakt__uklad .stepper__krok,
.kontakt__uklad .kform__hint,
.kontakt__uklad .kform__ufnosc,
.kontakt__uklad .zgoda__label,
.kontakt__uklad .kinfo__note {
  color: var(--c-text-2);
}

/* ── GRANICE KONTROLEK W PANELU (2026-08-11) ─────────────────────────
   Ta sama zasada, co regula wyzej, tylko dla krawedzi zamiast tekstu:
   wartosc liczona OD PANELU, nie brana z globalnego tokenu.

   Co bylo zle: obrys pola to --c-border (#3C3636), a panel to
   --c-surface-safe (#3A3737) - czyli praktycznie ten sam kolor, kontrast
   1,00. Pole bylo widoczne WYLACZNIE dzieki temu, ze jego wypelnienie jest
   ciemniejsze od panelu, a to daje 1,37 przy progu 3:1 (WCAG 1.4.11 dla
   granic kontrolek). Na slabym monitorze albo w sloncu pole przestawalo
   byc polem.

   38% bieli to nie okragla liczba tylko minimum: daje #858383, czyli 3,13.
   36% konczy sie na 2,96 i progu nie przechodzi. Swiadomy koszt: pola maja
   teraz czytelna, jasnoszara ramke zamiast dawnego zatopienia w panelu.

   Ta sama wartosc na obrysie przycisku zablokowanego (bylo 1,51) i jasniejszy
   napis na nim - 60% bieli daje 5,39. Przycisk wylaczony jest formalnie poza
   wymogiem 1.4.3, ale to jedyna droga naprzod w tym formularzu i przez
   wiekszosc czasu byl najslabiej widocznym elementem panelu. */
.kontakt__uklad .input {
  border-color: color-mix(in srgb, #fff 38%, var(--c-surface-safe));
}

.kontakt__uklad .btn--safe[aria-disabled='true'] {
  color: color-mix(in srgb, #fff 60%, var(--c-surface-safe));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #fff 38%, var(--c-surface-safe));
}

.kontakt__uklad {
  display: grid;
  gap: var(--s-10);
  max-width: var(--w-hero);
  margin-inline: auto;
  padding: clamp(var(--s-6), 4vw, var(--s-10));
  background: var(--c-surface-safe);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}

@media (min-width: 760px) {
  .kontakt__uklad {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
    align-items: start;
  }
}

.kform { display: grid; gap: var(--s-6); }

/* widoczne tylko bez JS (patrz noscript w kontakt.html) */
.kontakt__col > noscript p { margin-bottom: var(--s-6); color: var(--c-text-2); }

/* zaufanie tuz przy akcji - odpowiedz na "co sie stanie z tym, co napisze"
   dokladnie w miejscu, gdzie to pytanie sie pojawia, nie w regulaminie. */
.kform__ufnosc {
  color: var(--c-text-muted);
  font-size: var(--t-xs);
}

/* kreska dzielaca zamiast osobnej karty - dane kontaktowe naleza do tego
   samego panelu, nie stoja obok jako drugi obiekt */
.kinfo {
  padding-top: var(--s-8);
  border-top: 1px solid var(--c-border);
}

@media (min-width: 760px) {
  .kinfo {
    position: sticky;
    top: calc(var(--h-header) + var(--s-10));
    padding-top: 0;
    padding-left: var(--s-10);
    border-top: 0;
    border-left: 1px solid var(--c-border);
  }
}

.kinfo__tytul { font-size: var(--t-h3); }

/* rytm kolumny: jeden odstep (s-6) miedzy kazdym blokiem, zamiast
   mieszanki s-4/s-4/s-6 ktora czytala sie jako nierowna (zgloszone
   2026-08-07) - tylko lista wewnatrz siebie ma cieśniejszy odstep (s-3),
   bo email i telefon naleza do jednej grupy, nie do osobnych blokow */
.kinfo__marka {
  margin-top: var(--s-6);
  font-weight: var(--w-medium);
}
/* USUNIETE 2026-08-10: `.kinfo__grupa` - dopisek "- czesc If Group" przy nazwie
   marki w panelu kontaktowym. Decyzja klienta: w formularzu stoi sama nazwa.
   Informacja o grupie zostaje w stopce, gdzie jest danymi o firmie, a nie
   trzecim wtretem w kolumnie z adresem. */

.kinfo__lista { margin-top: var(--s-6); display: grid; gap: var(--s-3); }
.kinfo__lista a {
  color: var(--c-text-2);
  transition: color var(--d-fast) var(--ease-out);
}
.kinfo__lista a:hover { color: var(--c-brand); }

.kinfo__note {
  margin-top: var(--s-6);
  padding-top: var(--s-6);
  border-top: 1px solid var(--c-border);
  color: var(--c-text-muted);
  font-size: var(--t-sm);
}


/* ══ ZAMKNIECIE / CTA ══════════════════════════════════════════════════
   Jedyne miejsce na calej stronie, gdzie czerwien marki wypelnia cala
   sekcje na pelna szerokosc okna, nie tylko akcent (decyzja klienta
   2026-08-07 - poprzednia, powsciagliwa wersja bez powierzchni i bez
   tla "nie przemawiala"). Odwrocenie palety samo w sobie jest sygnalem
   "to jest ten moment" - nie potrzeba do tego renderu ani ruchu.

   Uklad (makieta klienta, 2026-08-07): wezwanie wersalikami po lewej,
   po prawej siatka 2x2 - trzy obietnice w polach z obrysem i przycisk
   jako czwarte, rownoprawne pole. Wczesniej byla to lista punktow
   z kreska i pigulka przycisku pod nia.

   Sekcja koloruje sie sama, bez owijania w dodatkowy kontener - `.cta`
   jako `<section>` juz zajmuje pelna szerokosc okna (ten sam fakt, ktory
   wykorzystuje `.film__kadr` w sekcji filmu), wiec `background` tutaj
   wystarczy do efektu full-bleed.

   Przycisk jest "wyciety" z tla strony (ciemny na czerwieni) zamiast
   bialy-na-czerwieni - spojne z reszta serwisu, gdzie przyciski nigdy
   nie nosza koloru marki wprost na powierzchni wiekszej niz sam przycisk. */

.cta {
  background: var(--c-brand);
}

.cta__uklad {
  display: grid;
  gap: clamp(var(--s-8), 4vw, var(--s-12));
}

@media (min-width: 940px) {
  .cta__uklad {
    grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
    gap: var(--s-16);
    align-items: center;
  }
}

/* ── wezwanie ──
   Wersaliki tylko tutaj i tylko przez CSS (znacznik zostaje zdaniem, wiec
   czytnik ekranu nie literuje slow). To jedyne miejsce z odwrocona paleta,
   wiec jedyne, ktore udzwignie krzyk - reszta serwisu pisze naglowki
   zdaniowo. Odstepy miedzyliterowe wracaja do zera: ujemny tracking
   (--ls-display) dobierano dla malych liter, na kapitalikach sklejalby. */
/* BIEL, NIE --c-bg (2026-08-11). Cala tresc bloku byla ciemna na czerwieni
   marki - spojne z przyciskiem "wycietym z tla strony", ale punkty listy
   (16 px, grubosc 600, czyli formalnie NIE tekst duzy) mialy przy tym 3,98
   przy progu 4,5. Odwrocenie na biel daje 4,70 i jest jedynym wyjsciem, ktore
   nie rusza samego bloku: zadna czerwien marki nie unosi ciemnego tekstu do
   progu, bo musialaby byc wyraznie jasniejsza, niz jest.
   Naglowek idzie razem z punktami mimo ze jako tekst duzy przechodzil juz
   wczesniej (3,98 przy progu 3) - inaczej blok mialby ciemny tytul i biale
   pola obok siebie, czyli dwa rejestry w jednej kompozycji.
   PRZYCISK ZOSTAJE CIEMNY: jest osobnym obiektem, jedynym z wypelnieniem
   i promieniem w calej sekcji, i ma wlasne 17,23 w srodku. */
.cta__tytul {
  /* bez max-width - lamanie niesie `<br>` w znaczniku, zeby trzy wersy
     stały tak, jak w makiecie, niezaleznie od szerokosci kolumny */
  color: #fff;
  font-family: var(--f-display);
  font-size: var(--t-display-lg);
  font-weight: var(--w-semi);
  line-height: 1.02;
  letter-spacing: 0;
  text-transform: uppercase;
}

/* ── siatka 2x2 ──
   Trzy obietnice + akcja jako czwarte pole. Rzedy rowne (1fr), wiec
   przycisk ma dokladnie ten sam gabaryt co pole obok - to jego waga
   w ukladzie, nie kolor, robi z niego cel.

   Psychologia bez zmian wzgledem poprzedniej wersji punktowej (klient,
   2026-08-07): odpowiedz w 24 h = "co sie stanie po kliknieciu", warianty
   = konkret zamiast obietnicy, bez zobowiazan = zdjecie oporu. Zmienil sie
   nosnik: pola zamiast listy z kreska. */
.cta__siatka {
  display: grid;
  gap: var(--s-6);
}

/* Rowne rzedy i rezerwa wysokosci obowiazuja dopiero w ukladzie 2x2 - to tam
   pola musza zrownac sie z przyciskiem obok. W jednej kolumnie (<520px) nie
   ma z czym rownac, a `1fr` + min-height zostawialoby pod kazda jednolinijkowa
   pozycja pas martwego powietrza. */
@media (min-width: 520px) {
  .cta__siatka {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-4);
    grid-auto-rows: 1fr;
  }
  .cta__pole { min-height: clamp(96px, 11vw, 132px); }
}

/* lista zostaje lista w drzewie dostepnosci (role="list" w znaczniku
   przywraca semantyke, ktora `display: contents` potrafi zgubic),
   ale w ukladzie jej nie ma - `li` sa dziecmi siatki obok przycisku */
.cta__punkty { display: contents; }

/* Bez obrysu i bez promienia (2026-08-08). Pelna ramka + wersaliki czytaly
   sie jak przycisk, a klikalne jest wylacznie czwarte pole - ciemne, z
   wypelnieniem. Zostaje sam wlosek u gory: znak rozdzielajacy pozycje, nie
   obwodka wokol celu. W czerwonym bloku jest teraz DOKLADNIE JEDEN obiekt
   z wypelnieniem i promieniem, i to on jest do klikniecia.

   BLAD NAPRAWIONY PRZY OKAZJI: bylo `padding: var(--s-5) ...`, a --s-5 nie
   istnieje w skali odstepow (tokens.css). Niezdefiniowana zmienna uniewaznia
   CALA deklaracje na etapie wartosci obliczonej, wiec padding spadal do 0 -
   tekst dotykal obrysu. Dokladnie ten sam blad byl juz raz zlapany w
   .zast__chmura (gap: var(--s-5) liczyl sie jako 0). */
.cta__pole {
  display: grid;
  align-content: start;         /* tekst siedzi PRZY swojej kresce, nie plywa w srodku komorki */
  padding-top: var(--s-4);
  padding-right: var(--s-4);
  /* wlosek idzie za trescia na biel (2026-08-11, patrz .cta__tytul) - ciemna
     kreska pod bialym napisem czytalaby sie jak resztka poprzedniej wersji */
  border-top: 1px solid color-mix(in srgb, #fff 30%, transparent);
  color: #fff;
  font-size: var(--t-body);
  font-weight: var(--w-semi);
  line-height: 1.35;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-wrap: balance;
}

/* czwarte pole siatki - przycisk rozpiety na cala komorke, nie pigulka
   dosunieta do rogu. `.btn` niesie caly stan (hover, kwadracik ze
   strzalka), tutaj dochodzi tylko gabaryt i wersaliki pol obok. */
.cta__btn {
  min-height: 0;              /* gabaryt daje teraz komorka siatki */
  font-weight: var(--w-semi);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* przycisk "wyciety" z tla strony - jedyne miejsce, gdzie .btn--primary
   jest podmieniony na ciemny wariant, bo tutaj to on kontrastuje, nie
   czerwien (ktora juz jest tlem calej sekcji) */
.cta .btn--primary {
  background: var(--c-bg);
  color: var(--c-text);
}
.cta .btn--primary:hover  { background: var(--c-surface-up); }
.cta .btn--primary:active { background: var(--c-surface); }


/* ══ O NAS (o-nas.html) ════════════════════════════════════════════════
   Uklad w stylu wpisu blogowego: opis -> przerywnik (znak marki) -> opis
   dalej -> kafelki "wartosci" -> zamkniecie (.cta, wspoldzielone
   z index.html/oferta.html, bez zmian). Tytul strony niesie hero powyzej
   (.hero, ten sam komponent co index/oferta), wiec ta sekcja zaczyna sie
   od razu proza.

   Proza stoi w .wrap--text (var(--w-text), ta sama kolumna co proza sekcji
   o firmie na index.html). Kafelki wartosci wychodzily wczesniej do pelnego
   .wrap, bo potrzebowaly miejsca na 4 kolumny; przy ukladzie 2x2
   (2026-08-10) juz go nie potrzebuja, a w pelnej szerokosci robily sie dwa
   razy wieksze, niz mialy byc. Od drugiej tury tego samego dnia stoja
   posrodku: kolumna tekstu +15%, wylacznie od 1000 px (patrz
   .onas__wartosci nizej). */

/* za malo tresci na tej stronie na trzeci stopien wielkosci - caly tekst
   (lead i "opis dalej") w jednym rozmiarze, bez posredniego "podtytulu" */
.onas__lead,
.onas__dalej {
  color: var(--c-text-2);
  font-size: var(--t-body-lg);
}
/* lead otwiera sekcje - odstep bierze na siebie padding .section */
.onas__lead  { margin-top: 0; }
.onas__dalej { margin-top: var(--s-8); }

/* przerywnik - znak marki sam w swojej przestrzeni, bez ramki/karty, ten
   sam duch co .section-rule na zamknieciu, ale jako obraz, nie linia */
.onas__znak {
  display: block;
  width: clamp(160px, 20vw, 260px);
  height: auto;
  margin: clamp(var(--s-16), 8vw, var(--s-20)) auto;
}

/* Kafelki wartosci - siatka .siatka-2 (layout.css): 2x2 na desktopie,
   jedna kolumna ponizej 640 px (decyzja klienta 2026-08-10).

   Wczesniej stala tu .siatka-4, czyli ta sama siatka co kroki Procesu na
   oferta.html. Roznica jest merytoryczna, nie estetyczna: krok procesu to
   jedno zdanie przy kresce i czterech w rzedzie czyta sie jako os czasu,
   a wartosc to kafel (.card) z naglowkiem i opisem - przy czterech
   kolumnach kazdy z nich robil sie waskim slupkiem tekstu. 2x2 daje im
   szerokosc, przy ktorej opis miesci sie w dwoch wierszach.

   Wizualny wrapper zostaje i od 2026-08-10 jest to `.card--szklo` (biel
   10%), tak jak kafle na index.html i kroki Procesu na oferta.html - jeden
   material kafla w calym serwisie.

   UWAGA DO ZGLOSZENIA: klient wymienil "o nas: proces wspolpracy" wsrod
   miejsc na kafle biel 10%, ale sekcja o takiej nazwie na tej podstronie
   nie istnieje (Proces zyje wylacznie na oferta.html). Kafle wartosci to
   jedyna lista kafli na o-nas.html, wiec to one dostaly ten material -
   do potwierdzenia, czy o to chodzilo. */
.onas__wartosci {
  list-style: none;
  margin-top: clamp(var(--s-10), 5vw, var(--s-16));
}

/* WYJSCIE POZA KOLUMNE TEKSTU (decyzja klienta 2026-08-10, druga tura:
   "kafelki szersze o 15%, maja wychodzic poza szerokosc tekstu").

   Ujemny margines na obie strony, bez `width` - lista jest blokiem, wiec
   sama sie rozszerzy o tyle, ile jej oddamy. 7,5% na stronie daje razem
   +15% szerokosci, czyli dokladnie tyle, ile bylo w zgloszeniu.

   Prog 1000 px, nie od razu: ujemny margines je BOCZNY ODDECH STRONY
   (--pad-x). Ponizej 1000 px kolumna tekstu jest przy 680 px szersza, niz
   wynosi oddech, wiec 7,5% wyszloby poza krawedz ekranu. Od 1000 px
   --pad-x to juz co najmniej 50 px, a wyjscie ~43 px - kafle wychodza poza
   tekst, ale nadal stoja wewnatrz marginesu strony.

   Zamierzony efekt: proza ma jedna krawedz, kafle druga - wyraznie
   szersza, wiec czyta sie je jako inny rodzaj tresci, nie ciag dalszy
   akapitu. Reszta sekcji (lead, znak, "dalej") zostaje w .wrap--text. */
@media (min-width: 1000px) {
  .onas__wartosci { margin-inline: -7.5%; }
}

.onas__wartosc { gap: var(--s-2); }

/* `h2`, nie `h3` (2026-08-11): tytul strony niesie hero (h1), a sekcja prozy
   celowo nie ma wlasnego naglowka - przez to po h1 szly od razu h3 kafli
   i poziom drugi wypadal z drzewa dokumentu. Stopien pisma daje ta regula,
   wiec podniesienie poziomu w znaczniku nic nie zmienia wizualnie. */
.onas__wartosc h2 {
  font-size: var(--t-body-lg);
  font-weight: var(--w-medium);
  font-family: var(--f-display);
  line-height: var(--lh-head);
  letter-spacing: var(--ls-display);
}

.onas__wartosc p {
  color: var(--c-text-2);
  font-size: var(--t-sm);
}


/* ══ STRONA TEKSTOWA (POLITYKA PRYWATNOSCI) ════════════════════════════
   Jedyna podstrona bez kadru, bez wstegi neonu i bez kafli. To dokument,
   do ktorego zaglada sie po jedna konkretna odpowiedz, wiec cala robota
   projektowa idzie w SKANOWALNOSC, nie w wrazenie: numerowane naglowki
   z wyrazna przerwa nad soba, krotka miara (--w-text), akapity w kolorze
   drugorzednym, a najczesciej szukane dane - administrator - w jedynym
   wyroznionym bloku na stronie, tuz pod wstepem.

   Klasy sa CELOWO ogolne (.proza, .dane, .lista-punkty), nie
   `.polityka__*`: kazdy kolejny dokument tego typu (regulamin, informacja
   o cookies, jesli kiedys bedzie potrzebna) ma wygladac identycznie
   i korzystac z tych samych regul, zamiast dostawac wlasna kopie. */

/* data aktualizacji stoi NAD trescia i pod naglowkiem - to pierwsza rzecz,
   ktorej szuka sie w dokumencie prawnym ("czy to jest aktualne") */
.proza__data {
  margin-bottom: var(--s-8);
  padding-bottom: var(--s-6);
  border-bottom: 1px solid var(--c-border);
  color: var(--c-text-muted);
  font-size: var(--t-xs);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}

.proza {
  color: var(--c-text-2);
  font-size: var(--t-body);
}

.proza > * + * { margin-top: var(--s-4); }

/* Naglowek punktu dostaje DUZO wiecej powietrza nad soba niz pod soba
   (s-12 kontra s-4 z reguly wyzej). Ten sam zabieg, co odstepy sekcji na
   pozostalych podstronach: przerwa mowi "tu zaczyna sie nowa sprawa",
   a bliskosc naglowka i pierwszego akapitu mowi "to nalezy do siebie".
   Bez tego dwanascie punktow czyta sie jak jeden ciag tekstu. */
.proza h2 {
  margin-top: clamp(var(--s-10), 5vw, var(--s-12));
  color: var(--c-text);
  font-size: var(--t-h3);
  font-weight: var(--w-medium);
}

.proza strong { color: var(--c-text); }

/* Odnosniki w tresci prawnej sa podkreslone, inaczej niz w reszcie serwisu.
   W dokumencie bez grafiki i bez przyciskow sam kolor nie wystarczy do
   rozpoznania linku (WCAG 1.4.1 - kolor nie moze byc jedynym nosnikiem
   informacji), a podkreslenie z odsunieciem nie tnie polskich ogonkow. */
.proza a {
  color: var(--c-text);
  text-decoration: underline;
  text-decoration-color: var(--c-border-hover);
  text-underline-offset: 0.2em;
  transition: color var(--d-fast) var(--ease-out),
              text-decoration-color var(--d-fast) var(--ease-out);
}
.proza a:hover {
  color: var(--c-brand);
  text-decoration-color: var(--c-brand);
}

/* ── DANE ADMINISTRATORA ─────────────────────────────────────────────
   Jedyna powierzchnia na calej stronie. Etykieta i wartosc stoja w dwoch
   kolumnach, zeby dalo sie skanowac pionowo po etykietach; ponizej 520 px
   etykieta wskakuje nad wartosc, bo przy dwoch kolumnach na waskim ekranie
   NIP i adres lamalyby sie na trzy wersy. */
.dane {
  margin-top: var(--s-6);
  padding: var(--pad-card);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
}

.dane__nazwa {
  color: var(--c-text);
  font-family: var(--f-display);
  font-size: var(--t-h3);
  font-weight: var(--w-medium);
  line-height: var(--lh-head);
  letter-spacing: var(--ls-display);
}

.dane__lista {
  display: grid;
  gap: var(--s-3);
  margin-top: var(--s-6);
  font-size: var(--t-sm);
}

.dane__lista li {
  display: grid;
  gap: var(--s-1);
}

/* etykieta wersalikami - ten sam jezyk co naglowki kolumn w stopce */
.dane__lista span {
  color: var(--c-text-muted);
  font-size: var(--t-xs);
  font-weight: var(--w-medium);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}

/* 10rem, nie mniej: najdluzsza etykieta w tym bloku to "KAPITAL ZAKLADOWY",
   a wersaliki z rozstrzeleniem --ls-label rosna szybciej, niz sugeruje sama
   liczba znakow. Przy weszszej kolumnie ta jedna pozycja lamalaby sie na
   dwa wersy, a reszta stalaby w jednym - najbardziej widoczny rodzaj
   nierownosci w tabeli danych. */
@media (min-width: 520px) {
  .dane__lista li {
    grid-template-columns: 10rem minmax(0, 1fr);
    gap: var(--s-4);
    align-items: baseline;
  }
}

/* ── LISTA PUNKTOWANA ────────────────────────────────────────────────
   base.css zdejmuje znaczniki z kazdej listy w serwisie, wiec dokument
   prawny musi je odzyskac wlasnym punktem. Kwadrat, nie kolko - marka
   jest ostra, a to ten sam ksztalt, co kaseta w pasku postepu formularza.
   `::marker` odpada: nie da sie go zmniejszyc ani odsunac tak, zeby
   kwadrat stanal w linii z pierwszym wersem. */
.lista-punkty {
  display: grid;
  gap: var(--s-3);
  margin-top: var(--s-4);
}

.lista-punkty li {
  position: relative;
  padding-left: var(--s-6);
}

.lista-punkty li::before {
  content: '';
  position: absolute;
  left: 0;
  /* wysrodkowanie w PIERWSZYM wersie, nie w calym elemencie: punkt liczy
     sie od gornej krawedzi w gore o polowe wysokosci linii, wiec stoi
     rowno przy tekscie takze wtedy, gdy pozycja ma trzy wersy */
  top: calc(0.5em * var(--lh-body));
  transform: translateY(-50%);
  width: 6px;
  height: 6px;
  background: var(--c-brand);
}

/* powrot na dol dokumentu - kto doczytal do konca, jest na slepym koncu
   serwisu (polityka nie stoi w nawigacji glownej) */
.proza__powrot { margin-top: clamp(var(--s-12), 6vw, var(--s-16)); }
