/* System wizualny sklepu /gift.
   Referencja: **print.gg** - sklep macierzysty (polecenie Arta 9.08 08:33,
   wytyczne w `SPEC_WYGLAD_SCALENIE.md`). Wczesniej apple.com; zmiana bierze
   sie stad, ze od scalenia koszyka klient przechodzi z `/gift` na `/koszyk/`
   sklepu w polowie sciezki zakupowej i widzial dwie rozne witryny.

   Zasady, ktore trzymaja ten plik razem:
   - Kontrast KAZDEGO koloru tekstu zmierzony wobec OBU tel (bialego i szarego
     `--tlo-2`), nie dobrany na oko. Akcent TEKSTOWY (`--akcent`) i kolor MARKI
     (`--marka`) to dwie rozne role, nie jedna wartosc w dwoch miejscach.
   - Zero animacji odslaniajacych przy przewijaniu. Ukrywanie tresci do czasu
     zadzialania przejscia konczy sie pusta strona wszedzie tam, gdzie
     przejscia nie startuja: w tle karty, w czytniku, w rendererze zrzutow.
*/

:root {
  /* Kolor. Wartosci zmierzone na print.gg 2026-08-09 odczytem
     `getComputedStyle` ze wszystkich elementow strony. */
  --ink: #000000;          /* 21,00 na bialym, 19,09 na --tlo-2. Sklep uzywa
                              czystej czerni - 755 elementow */
  --ink-2: #666666;        /* 5,74 / 5,22. Sklep: 249 elementow */
  --tlo: #ffffff;
  --tlo-2: #f4f4f4;        /* tlo sekcji i siatki */
  --linia: #dddddd;
  --linia-slaba: #ebebeb;
  --akcent: #0068cf;       /* TEKST i linki: 5,41 / 4,92 */

  /* ⛔ ZOLC MARKI JEST WYLACZNIE TLEM. Jako kolor tekstu daje na bieli
     1,62:1, czyli jest nieczytelna. Na tle z czarnym napisem - 12,95:1.
     Dlatego `--marka` i `--marka-tekst` wystepuja ZAWSZE w parze.
     ⚠️ Swiadome odstepstwo: sam przycisk odcina sie od bialego tla tylko
     w 1,62:1, ponizej progu 3:1, ktorego WCAG 1.4.11 chce dla granicy
     elementu. Rozpoznaje go czarny napis. print.gg ma dokladnie tak samo
     i wiernosc marce wygrywa tu z norma - ale to jest DECYZJA, nie
     przeoczenie. */
  --marka: #fcc300;
  --marka-hover: #e8b400;  /* czern na niej 10,96 */
  --marka-tekst: #000000;

  /* AKCENT WEZWAN - decyzja Arta 2026-08-12 19:46, po obejrzeniu makiety:
     „zmien akcenty z zoltych na niebieskie jak na screen (wzialem z baneru
     na glownej)". Wartosci ZMIERZONE na zywym `a.link--fetch` z suwaka banera
     print.gg (8 sztuk, wszystkie identyczne), nie odczytane ze zrzutu.

     ⛔ ZOLC ZOSTAJE, i to nie jest niedokonczona podmiana. print.gg dzieli
     te dwie role: zolte jest KONTO i KOSZYK (przycisk „Zaloguj sie", licznik
     w naglowku), niebieskie sa WEZWANIA w tresci (baner). Nasz naglowek to
     rama sklepu, wiec zolc widac na `/gift` dalej - i ma tak byc.

     ⛔ `background-size: 200%` to CALY MECHANIZM, nie ozdobnik. Gradient ma
     trzy stopy (ciemny, jasny w 51 %, ciemny), a przycisk pokazuje POLOWE
     tego pasa; najechanie przesuwa tlo i daje przeplyw w 0,8 s. Przy
     domyslnym `100%` pas scisnalby sie do szerokosci przycisku i widac by
     bylo oba ciemne konce naraz - zupelnie inny ksztalt plamy.

     ⛔ CIENIA NIE MA. Na zrzucie od Arta wyglada, jakby byl, ale zmierzone
     `box-shadow: none` na wszystkich osmiu przyciskach - cien bierze sie
     z ciemnego tla banera pod przyciskiem.

     ⚠️ ODSTEPSTWO SWIADOME, jak wyzej przy zolci: bialy tekst ma na ciemnym
     koncu gradientu kontrast 5,97:1, ale na jasnym (`#009ee0`) tylko 3,04:1
     przy normie 4,5:1 z WCAG 1.4.3; napis lezy w polowie pasa, czyli realnie
     okolo 4,2:1. print.gg ma dokladnie tak samo i wiernosc marce wygrywa -
     ale to DECYZJA, nie przeoczenie. Podniesienie bez zmiany wygladu:
     przesunac jasny stop z 51 % blizej krawedzi. */
  --akcent-grad: linear-gradient(90deg, #0062c5, #009ee0 51%, #0062c5);
  --akcent-plama: #0062c5;   /* jednolity, do malych plam - 5,97:1 z biala */
  --akcent-tekst: #ffffff;

  --alarm: #b3261e;        /* 6,54 / 5,94 */
  --alarm-tlo: #fdf1f0;
  --ok: #1d7d3f;           /* 5,18 / 4,71 */
  --ok-tlo: #eef7f0;

  /* Wymiary */
  --kontener: 1120px;
  --waski: 700px;
  --r-kafel: 12px;         /* sklep jest mniej zaokraglony niz apple.com */
  --r-pole: 8px;
  --r-pigulka: 50px;       /* wartosc print.gg; efekt ten sam co 980px */
  --nag-wys: 90px;         /* wysokosc naglowka print.gg (zmierzona) */

  /* Ruch. Krzywa wyhamowujaca, bez odbicia. */
  --ruch: 400ms cubic-bezier(0.32, 0.72, 0, 1);
  --ruch-szybko: 200ms cubic-bezier(0.32, 0.72, 0, 1);

  /* Warstwy - skala nazwana, zeby nie konczyc na 9999 */
  --z-tresc: 1;
  /* ⛔ 5, NIE 100 - i to jest wartosc WYLICZONA, nie dobrana. Rozwijane menu
     „Produkty" ma wprawdzie `z-index: 999999`, ale jego KONTEKST NAKLADANIA
     tworzy `header.banner` sklepu z `z-index: 10` - wiec cale menu konkuruje
     ze swiatem jako warstwa 10, nie 999999. Nasz pasek kategorii na 100
     malowal sie NAD rozwinietym menu i przecinal je w poprzek (zgloszenie
     Arta 12.08 ze zrzutem).
     Warunek jest obustronny: mniej niz 10 (pod menu sklepu) i wiecej niz
     zawartosc siatki (nad kaflami przy przewijaniu). 5 spelnia oba. */
  --z-lepki: 5;
  --z-naglowek: 200;
  --z-modal: 300;
}

/* Epilogue - font sklepu macierzystego print.gg (1618 elementow jego strony
   uzywa tego kroju). HOSTOWANY U NAS, nie z Google Fonts: `/gift` ma decyzje
   „biblioteki nigdy z CDN" po incydencie skimmera kart, ta sama droga co
   pdf.js i konva. Zrodlo, licencja (SIL OFL 1.1) i md5 w `fonty/WERSJA.txt`.

   ⛔ Font ZMIENNY - jeden plik obsluguje cala os wagi 100-900, stad
   `font-weight: 100 900` zamiast czterech blokow po jednej wadze. Cztery
   osobne bloki pobralyby ten sam plik cztery razy.
   ⛔ `unicode-range` jest tu po to, zeby przegladarka pobrala TYLKO potrzebny
   podzbior. Bez niego kazdy klient sciaga oba pliki (67 kB) zamiast jednego.
   Polskie znaki (ą, ć, ę, ł, ń, ó, ś, ź, ż) leza w `latin-ext`. */
@font-face {
  font-family: Epilogue;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonty/epilogue-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Epilogue;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonty/epilogue-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

*, *::before, *::after { box-sizing: border-box; }

/* MINA CSS: atrybut `hidden` to tylko domyslne `display: none` przegladarki -
   KAZDA wlasna regula `display` go nadpisuje. `.formularz { display: grid }`
   pokazywalo formularz kontaktu mimo `hidden`, a `element.hidden` w JS nadal
   zwracalo `true`. Odczyt DOM potwierdzalby ukrycie; zlapal to dopiero ZRZUT. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--tlo);
  color: var(--ink);
  /* Zapasowe kroje zostaja: `font-display: swap` pokazuje tekst nimi, zanim
     dojdzie Epilogue, a przy niepowodzeniu pobrania strona dalej sie czyta. */
  font: 400 17px/1.47 Epilogue, -apple-system, BlinkMacSystemFont, "Segoe UI",
        Roboto, "Helvetica Neue", Arial, sans-serif;
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: var(--akcent); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; }

/* Widoczny focus dla klawiatury - nie kasujemy obrysu, tylko go porzadkujemy. */
:focus-visible { outline: 2px solid var(--akcent); outline-offset: 3px; border-radius: 4px; }

/* Etykieta dla czytnika ekranu. Nie `display: none` ani `visibility: hidden` -
   tamte usuwaja element takze z drzewa dostepnosci, czyli kasuja to, po co
   etykieta istnieje. */
.wizualnie-ukryte {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.kontener { max-width: var(--kontener); margin: 0 auto; padding: 0 22px; }

/* ---------------------------------------------------------------- naglowek */
/* Przezroczystosc z rozmyciem jest tu odwzorowaniem referencji, a nie ozdoba:
   pasek ma zostac czytelny nad przewijana siatka zdjec. Stosowana WYLACZNIE
   tutaj; kafle i panele sa kryjace. */
.naglowek {
  position: sticky; top: 0; z-index: var(--z-naglowek);
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--linia-slaba);
}
@supports not (backdrop-filter: blur(1px)) {
  .naglowek { background: #fff; }
}
.naglowek-tresc {
  max-width: var(--kontener); margin: 0 auto; padding: 0 22px;
  min-height: var(--nag-wys);
  display: flex; align-items: center; gap: 18px;
}
/* Logo sklepu macierzystego. Zolta kropka to jedyny akcent marki w naglowku
   POZA przyciskiem - zgodnie z zasada, ze zolc jest tlem, nie tekstem:
   kropka jest KSZTALTEM w kolorze marki, nie napisem. */
.logo {
  font-size: 21px; font-weight: 700; letter-spacing: -0.01em;
  color: var(--ink); text-decoration: none; white-space: nowrap;
  display: inline-flex; align-items: baseline; gap: 1px;
}
.logo:hover { text-decoration: none; }
.logo-kropka { color: var(--marka); font-size: 26px; line-height: 0; }

/* Menu sklepu. `.teraz` oznacza sekcje, w ktorej klient JEST - pogrubieniem,
   nie kolorem, bo kolor marki na tekscie ma 1,62:1. */
.menu-sklepu { display: flex; align-items: center; gap: 22px; margin-left: 8px; }
.menu-sklepu a { font-size: 15px; color: var(--ink); white-space: nowrap; }
.menu-sklepu a:hover { color: var(--akcent); text-decoration: none; }
.menu-sklepu .teraz { font-weight: 600; }

.naglowek-akcje { margin-left: auto; display: flex; align-items: center; gap: 14px; }

/* „Zaloguj sie" - pigulka w kolorze marki, jak w sklepie. Czarny napis,
   bo biel na zolci ma 1,62:1. */
.konto {
  padding: 9px 20px; font-size: 14px; font-weight: 500;
  color: var(--marka-tekst); background: var(--marka);
  border-radius: var(--r-pigulka); white-space: nowrap;
  transition: background var(--ruch-szybko);
}
.konto:hover { background: var(--marka-hover); color: var(--marka-tekst);
  text-decoration: none; }

/* Koszyk z licznikiem. Licznik jest UKRYTY przy pustym koszyku - „0" na
   plakietce to szum, a nie informacja. */
.koszyk { position: relative; display: inline-flex; color: var(--ink);
  padding: 4px; }
.koszyk:hover { color: var(--akcent); text-decoration: none; }
.koszyk .licznik {
  position: absolute; top: -3px; right: -5px; min-width: 17px; height: 17px;
  padding: 0 4px; border-radius: var(--r-pigulka);
  background: var(--marka); color: var(--marka-tekst);
  font-size: 11px; font-weight: 600; line-height: 17px; text-align: center;
}

/* Szukajka w ksztalcie pola Apple: pigulka na szarym, bez obwodki. */
.szukajka { position: relative; flex: 1; max-width: 420px; }
.szukajka input {
  width: 100%; padding: 8px 14px 8px 36px;
  font-size: 15px; font-family: inherit; color: var(--ink);
  background: var(--tlo-2); border: 1px solid transparent;
  border-radius: var(--r-pigulka);
  transition: background var(--ruch-szybko), border-color var(--ruch-szybko);
}
.szukajka input::placeholder { color: #6e6e73; }   /* 4,66 na --tlo-2 */
.szukajka input:focus { background: #fff; border-color: var(--linia); outline: none; }
.szukajka input:focus-visible { outline: 2px solid var(--akcent); outline-offset: 2px; }
.szukajka::before {
  content: ""; position: absolute; left: 13px; top: 50%; width: 15px; height: 15px;
  transform: translateY(-50%); pointer-events: none; opacity: .5;
  background: currentColor;
  -webkit-mask: no-repeat center/contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E");
  mask: no-repeat center/contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E");
}

/* -------------------------------------------------------------------- hero */
.hero { text-align: center; padding: 56px 22px 40px; }
.hero h1 {
  margin: 0 0 10px;
  /* ⛔ px, NIE rem - patrz „GRANICA RAMY" na koncu pliku. Rama sklepu ustawia
     `html { font-size: 0.833334vw }`, wiec kazdy `rem` w naszej tresci kurczy
     sie razem z oknem. Wartosci sa doslownym przeliczeniem z korzenia 16 px. */
  font-size: clamp(34px, 5.2vw, 54px);       /* 34-54 px, sufit daleko od krzyku */
  line-height: 1.06;
  font-weight: 600;
  letter-spacing: -0.028em;                   /* powyzej podlogi -0.04em */
  text-wrap: balance;
}
.hero p {
  margin: 0 auto; max-width: 46ch;   /* wezsze lamalo zdanie w pol slowa */
  font-size: clamp(17px, 2.2vw, 22px);   /* px, nie rem - patrz „GRANICA RAMY" */
  color: var(--ink-2); line-height: 1.35; text-wrap: balance;
}
.hero .linki { margin-top: 18px; display: flex; gap: 26px; justify-content: center;
  flex-wrap: wrap; font-size: 17px; }
.strzalka::after { content: " \203A"; }

/* -------------------------------------------------------------- kategorie */
.kategorie-pas { position: sticky; top: var(--nag-wys); z-index: var(--z-lepki);
  background: var(--tlo); border-bottom: 1px solid var(--linia-slaba); }
/* Zanikanie przy prawej krawedzi mowi, ze pasek jedzie dalej. Bez tego
   ucieta w polowie nazwa kategorii wyglada jak blad ukladu. */
.kategorie-pas::after {
  content: ""; position: absolute; top: 0; right: 0; bottom: 1px; width: 46px;
  pointer-events: none;
  background: linear-gradient(to right, rgba(255,255,255,0), #fff 78%);
}
.kategorie {
  display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none;
  max-width: var(--kontener); margin: 0 auto; padding: 11px 22px;
  scroll-snap-type: x proximity;
}
.kategorie::-webkit-scrollbar { display: none; }
.kategorie button, .kategorie a {
  /* Pigulki sa teraz ODNOSNIKAMI (patrz katalog.js) - te same reguly musza
     obowiazywac oba znaczniki, inaczej pasek rozjezdza sie po jednej zmianie. */
  display: inline-block; text-decoration: none;
  white-space: nowrap; scroll-snap-align: start;
  padding: 7px 15px; font-size: 14px; font-family: inherit; color: var(--ink);
  background: var(--tlo-2); border: 0; border-radius: var(--r-pigulka);
  cursor: pointer; transition: background var(--ruch-szybko), color var(--ruch-szybko);
}
.kategorie button:hover, .kategorie a:hover { background: #ebebef; }
/* Wybrana grupa w kolorze akcentu, nie czernia. Czern czytala sie jak
   „wylaczone" obok niebieskich wezwan; ta sama para tlo+tekst co przycisk.
   Gradient, bo pigulka jest szeroka i pas ma sie na czym rozlozyc. */
.kategorie button[aria-pressed="true"], .kategorie a[aria-pressed="true"] {
  background-image: var(--akcent-grad); background-size: 200%;
  background-position: 0% 0%; color: var(--akcent-tekst);
  transition: background-position .8s;
}
.kategorie button[aria-pressed="true"]:hover,
.kategorie a[aria-pressed="true"]:hover { background-position: 100% 0%; }

/* Drugi poziom - kategorie WYBRANEJ grupy. Lzejszy wizualnie od paska grup,
   zeby bylo widac, ktory jest nadrzedny: mniejsze pigulki na szarym tle. */
.kategorie-pas--drugi { background: var(--tlo-2); border-bottom-color: var(--linia); }
.kategorie-pas--drugi .kategorie { padding-block: 8px; }
.kategorie-pas--drugi .kategorie button, .kategorie-pas--drugi .kategorie a { font-size: 13px; padding: 6px 13px;
  background: var(--tlo); }
.kategorie-pas--drugi .kategorie button:hover, .kategorie-pas--drugi .kategorie a:hover { background: #ebebeb; }
/* ⛔ Poziomy 2 i 3 dostaja JEDNOLITY akcent, nie gradient - te pigulki sa
   o polowe mniejsze i pas z jasnym stopem w srodku zrobilby z nich plamke
   pod napisem. Hierarchia idzie przez rozmiar i tlo paska, nie przez inny
   kolor zaznaczenia: trzy rozne kolory „wybranego" na jednej stronie czytaja
   sie jak trzy niezalezne mechanizmy. */
.kategorie-pas--drugi .kategorie button[aria-pressed="true"], .kategorie-pas--drugi .kategorie a[aria-pressed="true"] {
  background: var(--akcent-plama); color: var(--akcent-tekst); }
/* ⛔ Gradient krawedzi dziedziczy sie z `.kategorie-pas`, ale tam jest bialy.
   Na szarym tle drugiego paska bialy gradient rysuje jasna plame. */
.kategorie-pas--drugi::after {
  background: linear-gradient(to right, rgba(244,244,244,0), var(--tlo-2) 78%);
}

.kategorie-pas--trzeci {
  background: var(--tlo); border-bottom-color: var(--linia-slaba);
}
/* Trzeci poziom jest jeszcze lzejszy od drugiego: obrys zamiast wypelnienia.
   Trzy rzedy pelnych pigulek jeden pod drugim czytaja sie jak jeden blok
   i przestaje byc widac, ktory poziom jest ktory. */
.kategorie-pas--trzeci .kategorie { padding-block: 7px; }
.kategorie-pas--trzeci .kategorie button, .kategorie-pas--trzeci .kategorie a {
  font-size: 12px; padding: 5px 12px; background: transparent;
  border: 1px solid var(--linia); color: var(--ink-2, var(--ink)); }
.kategorie-pas--trzeci .kategorie button:hover, .kategorie-pas--trzeci .kategorie a:hover { background: var(--tlo-2); }
.kategorie-pas--trzeci .kategorie button[aria-pressed="true"], .kategorie-pas--trzeci .kategorie a[aria-pressed="true"] {
  background: var(--akcent-plama); border-color: var(--akcent-plama);
  color: var(--akcent-tekst); }
/* Drugi pasek nie moze byc lepki razem z pierwszym - dwa przyklejone paski
   plus naglowek zabieraja pol ekranu.
   ⛔ Trzeci tak samo, i to nie jest kopia reguly przez nieuwage: pasek
   przyklejony POJEDYNCZO, gdy nadrzedny odjezdza z trescia, czyta sie gorzej
   niz brak przyklejania - zmierzone, trzeci stawal na 188 px nad pustym
   miejscem po drugim. */
.kategorie-pas--drugi,
.kategorie-pas--trzeci { position: relative; top: auto; }

/* -------------------------------------------------------------- okruszki */
/* Sciezka nawigacji. Na liscie stoi nad hero, na karcie produktu w kolumnie
   opisu - stad dwa polozenia i jeden wyglad. */
/* ⛔ `width: 100%` z tego samego powodu co przy `main` - TRZECIE wystapienie
   tej samej miny w tym pliku. Na liscie `.okruszki` jest BEZPOSREDNIM dzieckiem
   `body`, a sklep daje `body { display:flex; flex-direction:column }`; kazdy
   nasz element z `margin: 0 auto` staje sie wtedy elementem flexboxa
   o szerokosci dopasowanej do TRESCI i laduje na srodku. Objaw byl subtelny:
   sciezka wygladala na „wysrodkowana z projektu", a na karcie produktu (gdzie
   nie jest dzieckiem `body`) stala przy lewej - ten sam element w dwoch
   miejscach wygladal inaczej.

   ⛔ REGULA OGOLNA: kazdy NASZ element bedacy bezposrednim dzieckiem `body`
   i uzywajacy `margin: 0 auto` musi miec `width: 100%`. */
.okruszki { max-width: var(--kontener); width: 100%; margin: 0 auto;
  padding: 14px 22px 0; }
.produkt .okruszki { max-width: none; margin: 0; padding: 0 0 10px; }
.okruszki ol {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0 6px;
  font-size: 13px; color: var(--ink-2);
}
.okruszki li { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* ⛔ Separator z `::before`, nie znak w tresci - inaczej czytnik ekranu
   odczytuje go przy kazdym ogniwie. `aria-hidden` przez `content` jest
   domyslne: tresc generowana nie trafia do drzewa dostepnosci w wiekszosci
   czytnikow, a te, ktore ja czytaja, i tak pomijaja znaki interpunkcyjne. */
.okruszki li + li::before { content: "\203A"; color: var(--linia); }
.okruszki a { color: var(--ink-2); }
.okruszki a:hover { color: var(--akcent); text-decoration: underline; }
/* Ostatnie ogniwo: to, na czym klient stoi. Ciemniejsze, bez odnosnika.
   Skracane wielokropkiem, bo nazwy podkategorii bywaja dlugie i na waskim
   ekranie lamalyby sciezke na trzy wiersze. */
.okruszki [aria-current="page"] {
  color: var(--ink); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; max-width: 22ch;
}

/* ------------------------------------------------------------------ siatka */
.tlo-siatki { background: var(--tlo-2); padding: 30px 0 64px; }

/* Pasek nad siatka: filtr katalogu + licznik wynikow.
   ⛔ To NIE jest kopia pola z naglowka. Tamto szuka w SKLEPIE, to filtruje
   TEN katalog - dwa rozne zakresy, dlatego dwa osobne pola z jawnymi
   etykietami (zgloszenie Arta 12.08, uzasadnienie w `katalog.js`). */
.pasek-filtra {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin: 0 0 18px;
}
.filtr { flex: 0 1 320px; min-width: 0; position: relative; display: block; }

/* Sortowanie katalogu (Standard / Cena rosnaco / Cena malejaco).
   ⛔ WYSOKOSCI, FONTU, TLA I OBRAMOWANIA TU NIE MA - I TO JEST CELOWE.
   Ustawia je wspolna regula pol formularza (`body.gift main select:...`),
   ktora ma WYZSZA specyficznosc niz `.sortowanie select`. Pierwsza wersja
   probowala je nadpisac i przegrala po cichu: pomiar `getComputedStyle` dal
   font 17 px zamiast zapisanych 14 i wysokosc 43 px obok pola szukania
   majacego 49. Podporzadkowanie sie wspolnej regule daje zgodnosc z polem
   obok Z KONSTRUKCJI - i przezyje zmiane tamtej reguly, ktorej wlasne
   wartosci by nie przezyly.
   Zostaje tu wylacznie to, co dotyczy SAMEGO rozwijania. */
/* ⛔ `align-self: stretch` + `height: 100%` zamiast wpisanej wysokosci.
   Zmierzone: pole szukania ma 49 px, a lista wyboru 43 px przy IDENTYCZNYM
   wypelnieniu i foncie - bo przegladarka wymusza na `select` interlinie
   `normal` i nie dziedziczy on `line-height` po reszcie formularza. Wpisanie
   „49px" albo „24.99px" zalatwiloby dzisiaj i rozjechalo sie przy pierwszej
   zmianie fontu pola obok. Dorownanie do wiersza jest samoutrzymujace sie. */
.sortowanie { flex: 0 0 auto; display: block; align-self: stretch; }
.sortowanie select {
  height: 100%;
  /* ⛔ `!important` dokladnie tak, jak przy `.filtr input` kilka linii wyzej
     i z tego samego powodu: wspolna regula zadaje `padding: 11px 13px`,
     a pod strzalka musi byc miejsce - bez tego dluga etykieta („Cena
     (malejaco)") wchodzi pod ikone. To jedyna wlasnosc, ktora tu odbieramy
     wspolnej regule, i tylko na jednej krawedzi. */
  padding-right: 38px !important;
  min-width: 172px;
  /* Wlasna strzalka zamiast systemowej: `appearance: none` zdejmuje takze
     natywna, a bez podmiany pole wygladaloby jak zwykly prostokat i nie
     dawaloby znac, ze jest rozwijane. Strzalka jako `data:` w tle - zero
     dodatkowych pobran i zero pliku do utrzymania. */
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23666' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
  cursor: pointer;
}
.sortowanie select:hover { border-color: var(--ink-2); }
/* Miejsce na lupe po prawej - inaczej dluga fraza wchodzi pod ikone. */
.filtr input { width: 100%; padding-right: 42px !important; }
.filtr-lupa {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  color: var(--ink-2);
  /* ⛔ `fill` USTAWIONY W CSS, mimo ze znacznik ma juz `fill="none"`.
     `home.css` sklepu ma `svg { fill: inherit }`, a KAZDA regula CSS bije
     atrybut prezentacyjny - niezaleznie od specyficznosci, bo atrybut liczy
     sie jak waga zerowa. Skutek byl widoczny: lupa wypelniala sie czernia
     i zamiast konturu wychodzila plama (zrzut z produkcji 12.08).
     ⚠️ To dotyczy KAZDEGO naszego SVG w tresci - `fill`, `stroke` i `stroke-width`
     podawaj w CSS, nie w atrybutach, dopoki zyjemy pod arkuszami sklepu. */
  fill: none;
  stroke: currentColor;
  /* ⛔ Klikniecie w ikone ma ustawic kursor w polu, a nie zatrzymac sie na
     obrazku. Bez tego trafienie w prawy skraj pola nie robi nic i czyta sie
     jak martwy przycisk. */
  pointer-events: none;
}
/* ⛔ Krzyzyk czyszczenia w polu `type=search` stoi dokladnie tam, gdzie nasza
   lupa - dwie ikony jedna na drugiej. Chowamy natywny; czyszczenie zostaje
   pod Escape i pod zaznaczeniem, a filtr i tak reaguje na kazda zmiane. */
.filtr input::-webkit-search-cancel-button { display: none; }
.podsumowanie { color: var(--ink-2); font-size: 14px; margin: 0; }
.siatka { display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); }

/* Kafel produktu. Siatka kafli jest tu wlasciwa afordancja - kazdy kafel to
   osobny towar, ktory klient porownuje wzrokiem; to nie jest dekoracyjny
   raster "ikona + naglowek". */
/* ⛔ WARTOSCI PRZENIESIONE ZE SKLEPU, zmierzone 12.08 na `print.gg/product-category/...`:
   kafel 211,5 x 341,5 px, tlo `#f4f4f4`, promien **6 px**, BEZ cienia, tytul
   11,25 px / waga 400. Wczesniej mielismy promien 12 px, tytul 16 px / waga 600
   i cien przy najechaniu - czyli jezyk apple.com z 8.08.

   ℹ️ INWERSJA TLA JEST ZAMIERZONA I ZOSTAJE: sklep ma szare kafle na bialym
   tle, my biale na szarym (`.tlo-siatki`). Ta sama para kolorow uzyta
   odwrotnie - odwrocenie jej tutaj daloby szary kafel na szarym tle, czyli
   produkty bez zadnej krawedzi.

   ⛔ Najechanie NIE podnosi kafla i nie rzuca cienia - sklep tego nie robi
   nigdzie. Zostaje samo przyblizenie zdjecia, ktore mowi „to jest klikalne"
   bez ruszania siatki. */
.karta {
  display: flex; flex-direction: column;
  background: var(--tlo); border-radius: 6px;
  padding: 16px 16px 18px; color: inherit; text-decoration: none;
  transition: background var(--ruch-szybko);
}
.karta:hover { text-decoration: none; background: #fafafa; }
.karta:hover .foto img { transform: scale(1.045); }
.karta h2 {
  font-size: 13px; font-weight: 500; letter-spacing: -0.005em;
  margin: 0 0 3px; line-height: 1.3; text-wrap: balance;
}
/* Regula BAZOWA, nie kontekstowa: `.kat` rysuje tez koszyk (`koszyk.js`),
   a zawezenie jej do `.karta`/`.opis` zostawialoby tam goly tekst.
   ⛔ Dlatego ZMNIEJSZENIE do 11 px jest zapisane OSOBNO, pod `.karta .kat` -
   inaczej ta sama zmiana skurczylaby opis pozycji w koszyku, gdzie nikt
   o nia nie prosil. */
.kat { color: var(--ink-2); font-size: 13px; }
.karta .kat { font-size: 11px; }
/* ⚠️ Cena zostaje CZARNA i w 14 px, choc sklep pisze ja `#aaaaaa` w 11,25 px.
   To jedyne swiadome odstepstwo od wzorca w kaflu: `#aaaaaa` na szarym tle
   daje kontrast ok. 2,3:1 przy normie 4,5:1. Na sklepie z kilkunastoma
   produktami to uchodzi; tutaj katalog ma 2540 pozycji i cena jest kluczem,
   po ktorym klient wybiera. Wariant wierny sklepowi byl pokazany Artowi obok
   tego w makiecie (`makieta/katalog.html`) - wybrany zostal ten. */
.karta .cena { margin-top: auto; padding-top: 10px; font-size: 14px; font-weight: 600; }
.karta .cena .od { font-weight: 400; color: var(--ink-2); }
/* ⛔ KOLIZJA NAZW, nie kosmetyka: `.niedostepny` jest jednoczesnie komunikatem
   alarmowym na karcie produktu (czerwony tekst na rozowym tle). Kafel katalogu
   dostawal ten sam styl, wiec towar chwilowo wyprzedany czytal sie jak awaria
   sklepu - czerwona nazwa i rozowe tlo w siatce. Kafel wycisza sie samym
   przygaszeniem zdjecia; komunikat alarmowy zostaje przy `p.niedostepny`. */
.karta.niedostepny { background: var(--tlo); color: var(--ink); }
.karta.niedostepny .foto { opacity: .5; }
.karta.niedostepny h2 { color: var(--ink-2); }
.karta .brak { color: var(--ink-2); font-size: 14px; font-weight: 400; }

/* Ramka zdjecia trzyma proporcje ZAWSZE, takze gdy zdjecia nie ma - inaczej
   kafelki bez zdjecia bylyby nizsze i siatka robilaby sie poszarpana. */
.foto { aspect-ratio: 1; margin-bottom: 14px; border-radius: 10px;
  background: #fff; display: grid; place-items: center; overflow: hidden; }
/* `contain`, nie `cover`: dostawca fotografuje produkt wpisany w kwadrat na
   bialym tle, wiec `cover` obcinalby dlugopisy i parasole przy krawedziach. */
.foto img { width: 100%; height: 100%; object-fit: contain; display: block;
  transition: transform var(--ruch); }
.foto.pusta { background: var(--tlo-2); }
.foto .znak { color: var(--ink-2); font-size: 13px; }

/* Probki dostepnych kolorow pod kaflem.
   Rysuje je `katalog.js` tylko przy wiecej niz jednym kolorze - 63 % katalogu
   ma jeden i pasek z pojedyncza kropka bylby szumem. */
.kolory { display: flex; align-items: center; gap: 5px; margin-top: 8px;
  flex-wrap: wrap; }
.probka { width: 22px; height: 22px; border-radius: 50%; overflow: hidden;
  background: #fff; box-shadow: inset 0 0 0 1px var(--linia);
  display: grid; place-items: center; flex: 0 0 auto; }
/* ⛔ ZOOM 270 %, nie `cover`. Dostawca fotografuje produkt WPISANY w kwadrat
   na bialym tle, a zdjecie jest kwadratowe - wiec `cover` w kolku 22 px
   pokazuje cale zdjecie, czyli glownie biel. Zoom przycina do srodka kadru,
   gdzie jest towar. Zmierzone na 14 produktach: caly kadr daje probke prawie
   biala w 2 przypadkach, srodek 50 % w ZERO, a srodek 35 % oddaje odcien
   najwierniej (towar „Czarny" wychodzi #343432 zamiast #e6e6e7). */
.probka img { width: 270%; height: 270%; object-fit: contain; display: block; }
.probka.pusta { background: var(--tlo-2); }
/* ⛔ POWIEKSZENIE PROBKI PRZY NAJECHANIU (Art 13.08). Probka ma 22 px, a przy
   tej wielkosci „Granatowy" i „French Navy" wygladaja identycznie. Powiekszenie
   pozwala je rozroznic bez wchodzenia w karte produktu.
   ⛔ Rosnie SAMA PROBKA, nie kafel - `transform` na kafelku przesunelby cala
   siatke katalogu. `position: relative` + `z-index` wyciagaja ja nad sasiadow.
   ⛔ `overflow: visible` na czas najechania, bo probka normalnie przycina
   zdjecie do kola (`overflow: hidden`), a powiekszenie musi miec dokad urosnac. */
.probka { position: relative; transition: transform var(--ruch-szybko),
                                          box-shadow var(--ruch-szybko); }
.karta .kolory .probka:hover {
  transform: scale(2.4); z-index: 4;
  box-shadow: 0 3px 12px rgba(0, 0, 0, .18);
}
/* Wyprzedany kafel nie powieksza probek - jego kolory sa i tak przygaszone,
   a ruch sugerowalby, ze da sie w nie kliknac. */
.karta.niedostepny .kolory .probka:hover { transform: none; box-shadow: none; }

/* Dymek nad probka w katalogu: nazwa koloru, a przy malym zapasie takze ile
   zostalo. ⛔ Wlasny, nie natywny `title` - tamten pokazuje sie po sekundzie
   i nie lamie linii. */
.probka.ma-dymek::after {
  content: attr(data-dymek);
  position: absolute; bottom: calc(100% + 8px); left: 50%;
  transform: translateX(-50%) scale(.42) translateY(4px);
  transform-origin: bottom center;
  opacity: 0; pointer-events: none;
  transition: opacity var(--ruch-szybko), transform var(--ruch-szybko);
  background: var(--ink); color: #fff; font-size: 26px; line-height: 1.3;
  padding: 14px 20px; border-radius: 14px; width: max-content; max-width: 420px;
  text-align: center; z-index: 6; white-space: normal;
}
/* ⛔ Rozmiary w dymku sa CELOWO duze i zdjete `scale(.42)`. Probka rosnie
   przy najechaniu 2,4 raza razem z CALA zawartoscia, wiec dymek pisany
   normalnym rozmiarem wychodzilby po powiekszeniu gigantyczny. Skala odwrotna
   sprowadza go do czytelnych ~11 px na ekranie. */
.karta .kolory .probka:hover::after {
  opacity: 1; transform: translateX(-50%) scale(.42) translateY(0);
}
/* Kafel wyprzedany nie powieksza probek, wiec jego dymek nie jest skalowany
   w druga strone - inaczej bylby nieczytelnie maly. */
.karta.niedostepny .kolory .probka.ma-dymek::after {
  font-size: 12px; padding: 6px 9px; border-radius: 6px; max-width: 180px;
  transform: translateX(-50%) translateY(4px);
}
.karta.niedostepny .kolory .probka:hover::after {
  opacity: 1; transform: translateX(-50%) translateY(0);
}
/* ⛔ `.reszta`, NIE `.wiecej` - ta druga nazwa nalezy do przycisku paginacji
   pod siatka i nadawala dopiskowi „+8" pigulke 64x41 px w osobnym wierszu. */
.kolory .reszta { font-size: 12px; color: var(--ink-2); margin-left: 2px; }
/* Kafel wyszarza probki razem ze zdjeciem - towar wyprzedany nie ma
   reklamowac palety kolorow mocniej niz samego siebie. */
.karta.niedostepny .kolory { opacity: .5; }

.wiecej { display: block; margin: 34px auto 0; padding: 11px 24px;
  font-size: 15px; font-family: inherit; color: var(--ink);
  background: var(--tlo); border: 1px solid var(--linia);
  border-radius: var(--r-pigulka); cursor: pointer;
  transition: background var(--ruch-szybko); }
.wiecej:hover { background: #ebebef; }

/* ---------------------------------------------------------- karta produktu */
.produkt { max-width: var(--kontener); margin: 0 auto;
  display: grid; gap: 40px; grid-template-columns: minmax(0, 1fr) 372px;
  padding: 34px 22px 72px; align-items: start; }
.produkt > * { min-width: 0; }

/* px, nie rem - patrz „GRANICA RAMY" na koncu pliku */
.opis h1 { font-size: clamp(27px, 3.4vw, 40px); font-weight: 600;
  letter-spacing: -0.025em; line-height: 1.1; margin: 0 0 6px; text-wrap: balance; }
.opis .kat { font-size: 15px; margin: 0 0 22px; }
.opis .tresc { max-width: 62ch; color: var(--ink-2); text-wrap: pretty;
  overflow-wrap: anywhere;
  /* Margines jawny - sklep narzuca akapitom wlasny, liczony z plynnego rem. */
  margin: 0 0 17px; }

/* Sufit szerokosci: bez niego zdjecie rozciaga sie na cala kolumne i na
   szerokim ekranie torba na zakupy ma 700 px, czyli wiecej niz cokolwiek
   innego na stronie. */
.foto-produkt { aspect-ratio: 1; width: 100%; max-width: 560px;
  margin: 0 0 28px; background: var(--tlo-2); border-radius: var(--r-kafel);
  display: grid; place-items: center; overflow: hidden; }
.foto-produkt img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* ------------------------------------------------------------- konfigurator */
.kalkulator { background: var(--tlo-2); border-radius: var(--r-kafel);
  padding: 22px; display: grid; gap: 16px; position: sticky; top: calc(var(--nag-wys) + 18px); }
.kalkulator label { display: grid; gap: 6px; font-size: 13px; color: var(--ink-2); }
.kalkulator select, .kalkulator input {
  padding: 11px 13px; font-size: 16px; font-family: inherit; color: var(--ink);
  background: var(--tlo); border: 1px solid var(--linia); border-radius: var(--r-pole);
  width: 100%; max-width: 100%; min-width: 0;
  transition: border-color var(--ruch-szybko);
}
.kalkulator select:hover, .kalkulator input:hover { border-color: #b8b8bf; }
.kalkulator fieldset > * { min-width: 0; }
.kalkulator input:disabled { background: #ebebef; color: var(--ink-2); }
.kalkulator fieldset { border: 1px solid var(--linia); border-radius: var(--r-pole);
  padding: 15px; display: grid; gap: 13px; margin: 0; background: var(--tlo); }
.kalkulator legend { font-size: 12px; color: var(--ink-2); padding: 0 5px; }

.wynik { border-top: 1px solid var(--linia); padding-top: 16px; }
.cena-glowna { font-size: 30px; font-weight: 600; letter-spacing: -0.025em; }
.cena-glowna small { font-size: 13px; font-weight: 400; color: var(--ink-2);
  letter-spacing: 0; }
.cena-szt { color: var(--ink-2); font-size: 14px; margin-top: 2px; }
.rozbicie { width: 100%; margin-top: 14px; font-size: 13px; color: var(--ink-2);
  border-collapse: collapse; }
.rozbicie td { padding: 3px 0; }
.rozbicie td:last-child { text-align: right; color: var(--ink); }
.blad { color: var(--alarm); font-size: 14px; margin: 8px 0 0; }
/* Selektor ZAWEZONY do akapitu. Bez tego lapie tez `.karta.niedostepny`
   w siatce katalogu - patrz komentarz przy kaflu wyzej. */
p.niedostepny { margin: 0; padding: 13px 15px; border-radius: var(--r-pole);
  background: var(--alarm-tlo); color: var(--alarm); font-size: 14px; }

/* --------------------------------------------------------- wybor koloru */
/* Kafelki ze zdjeciem produktu w danym kolorze (wzor: giffits.de).
   ⛔ Kolumny z `auto-fill`, nie liczba stala: 93,2 % produktow ma do 8 kolorow,
   ale jeden ma 53 - stala liczba kolumn dawalaby albo dziure przy dwoch
   wariantach, albo kafelek 8 px przy pieciudziesieciu. */
.kolory-kafle {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
  gap: 6px; margin-bottom: 8px;
}
.kolor-kafel {
  position: relative; aspect-ratio: 1; padding: 2px; background: var(--tlo);
  border: 1px solid var(--linia); border-radius: 6px; cursor: pointer;
  display: grid; place-items: center; overflow: hidden;
  transition: border-color var(--ruch-szybko), box-shadow var(--ruch-szybko);
}
.kolor-kafel:hover { border-color: var(--ink-2); }
/* ⛔ Zaznaczenie NIE samym kolorem obwodki - dokladany jest jej grubszy obrys
   wewnetrzny, zeby stan „wybrany" byl widoczny takze przy monochromatycznym
   widzeniu i na wydruku. */
.kolor-kafel[aria-checked="true"] {
  border-color: var(--akcent-plama);
  box-shadow: inset 0 0 0 2px var(--akcent-plama);
}
.kolor-kafel img { width: 100%; height: 100%; object-fit: contain;
  mix-blend-mode: multiply; }
/* Wariant bez zdjecia dostaje sam podpis - lepszy niz pusty kwadrat. */
.kolor-kafel .znak { font-size: 10px; color: var(--ink-2); padding: 2px;
  text-align: center; line-height: 1.15; overflow: hidden; }
/* Wyprzedany wariant zostaje WIDOCZNY, tylko przygaszony: klient ma wiedziec,
   ze taki kolor istnieje, i moc o niego zapytac. */
.kolor-kafel:disabled { cursor: not-allowed; opacity: .38; }
.kolor-kafel:disabled:hover { border-color: var(--linia); }
.kolor-wybrany { margin: 0 0 12px; font-size: 13px; color: var(--ink-2); }
.kolor-wybrany b { color: var(--ink); font-weight: 500; }

/* ⛔ POWIEKSZENIE PRZY NAJECHANIU. Kafelek ma 46-50 px, a zdjecie dostawcy
   1000 px - przy tej skali roznica miedzy „Granatowy" a „French Navy" znika.
   Powiekszenie pokazuje je bez wchodzenia w karte produktu.
   ⛔ `transform` na WNETRZU, nie na kafelku: skalowanie samego przycisku
   przesunieteloby sasiadow w siatce. Tu rosnie tylko obraz, a `overflow`
   zdejmujemy na czas najechania, zeby mial dokad urosnac. */
.kolor-kafel img { transition: transform var(--ruch-szybko); }
.kolor-kafel:hover:not(:disabled), .kolor-kafel:focus-visible { overflow: visible; z-index: 3; }
.kolor-kafel:hover:not(:disabled) img,
.kolor-kafel:focus-visible img { transform: scale(2.6); }
/* Tlo pod powiekszonym zdjeciem - bez niego obraz nachodzi na sasiednie
   kafelki i zlewa sie z nimi w plame. */
.kolor-kafel:hover:not(:disabled)::before,
.kolor-kafel:focus-visible::before {
  content: ""; position: absolute; inset: -30%; z-index: -1;
  background: var(--tlo); border: 1px solid var(--linia); border-radius: 8px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, .12);
}
/* ⛔ Siatka musi pozwolic dziecku wyjsc poza swoje granice - inaczej
   `overflow: visible` na kafelku niczego nie da. */
.kolory-kafle { overflow: visible; }

/* Dymek z dostepnoscia. Pokazuje sie TYLKO tam, gdzie jest co powiedziec
   (`data-dymek` ustawia skrypt), wiec kolor dostepny w normalnej ilosci
   nie dostaje zadnej chmurki. */
.kolor-kafel.ma-dymek::after {
  content: attr(data-dymek);
  position: absolute; bottom: calc(100% + 10px); left: 50%;
  transform: translateX(-50%) translateY(4px);
  opacity: 0; pointer-events: none; transition: opacity var(--ruch-szybko),
                                                transform var(--ruch-szybko);
  background: var(--ink); color: #fff; font-size: 12px; line-height: 1.35;
  padding: 7px 10px; border-radius: 6px; white-space: normal; width: max-content;
  max-width: 190px; text-align: center; z-index: 5;
}
.kolor-kafel.ma-dymek:hover::after,
.kolor-kafel.ma-dymek:focus-visible::after {
  opacity: 1; transform: translateX(-50%) translateY(0);
}
/* ⛔ Skrajne kafelki: dymek wysrodkowany na pierwszym w rzedzie wychodzilby
   poza lewa krawedz panelu. Przy pierwszym i ostatnim w wierszu przyklejamy
   go do krawedzi kafelka zamiast centrowac. */
.kolor-kafel.ma-dymek:nth-child(6n+1)::after { left: 0; transform: none; }
.kolor-kafel.ma-dymek:nth-child(6n+1):hover::after,
.kolor-kafel.ma-dymek:nth-child(6n+1):focus-visible::after { transform: none; }

/* ------------------------------------------------------- miejsca nadruku */
/* Lista miejsc, w ktorych klient chce nadruk. Pokazuje sie dopiero przy DRUGIM
   miejscu - przy jednym bylaby pojedyncza zakladka nad polami, ktore i tak jej
   dotycza, czyli szumem. */
.miejsca { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.miejsce {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 12px; font-size: 13px; font-family: inherit; color: var(--ink);
  background: var(--tlo-2); border: 0; border-radius: var(--r-pigulka);
  cursor: pointer; transition: background var(--ruch-szybko);
}
.miejsce:hover { background: #ebebef; }
.miejsce[aria-selected="true"] {
  background: var(--akcent-plama); color: var(--akcent-tekst);
}
/* Kropka mowi, ze to miejsce ma juz wgrana grafike. ⛔ Znak, nie sam kolor:
   przy jednym i drugim stanie pigulki roznica koloru kropki bywa niewidoczna
   dla czesci klientow, a to jedyna informacja o tym, czy nadruk jest gotowy. */
.miejsce .ma-logo::before { content: "●"; font-size: 9px; line-height: 1; }
.miejsce .bez-logo::before { content: "○"; font-size: 9px; line-height: 1; }
/* Krzyzyk usuwania. ⛔ `<span>`, nie zagniezdzony `<button>` - przycisk
   w przycisku jest niepoprawny i przegladarki rozjezdzaja sie w tym, ktore
   klikniecie zglaszaja. Usuwanie obsluguje skrypt po `data-usun`. */
.miejsce .usun-miejsce {
  font-size: 15px; line-height: 1; opacity: .65; padding: 0 2px;
}
.miejsce .usun-miejsce:hover { opacity: 1; }

.dodaj-miejsce {
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: 4px; padding: 9px 16px; font-size: 14px; font-family: inherit;
  color: var(--ink); background: transparent;
  border: 1px dashed var(--linia); border-radius: var(--r-pigulka);
  cursor: pointer; transition: background var(--ruch-szybko);
}
.dodaj-miejsce:hover { background: var(--tlo-2); }
.dodaj-miejsce:disabled { opacity: .5; cursor: not-allowed; }

/* ----------------------------------------------------------------- kreator */
.kreator { margin-top: 34px; padding-top: 30px; border-top: 1px solid var(--linia-slaba); }
.kreator h2 { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 4px;
  line-height: 1.2;  /* patrz `.waski h1` - rama wstawia `normal` */
}
.kreator-wstep { color: var(--ink-2); font-size: 15px; margin: 0 0 18px; max-width: 58ch; }
.kreator-pasek { display: flex; gap: 10px; align-items: center; margin-bottom: 16px;
  flex-wrap: wrap; }
.podglad { display: block; width: 100%; max-width: 520px; height: auto;
  background: var(--tlo-2); border-radius: var(--r-kafel);
  cursor: grab; touch-action: none; }
/* `touch-action: none` jest konieczne, nie kosmetyczne: bez tego przeciagniecie
   palcem po plotnie przewija strone zamiast przesuwac logo. */
.podglad.ciagnie { cursor: grabbing; }

.narzedzia-logo { display: flex; align-items: end; gap: 14px; flex-wrap: wrap;
  max-width: 520px; margin-top: 14px; }
.narzedzia-logo .suwak { flex: 1; min-width: 200px; display: grid; gap: 6px;
  font-size: 13px; color: var(--ink-2); }
.narzedzia-logo input[type="range"] { width: 100%; accent-color: var(--ink); }
.narzedzia-logo .przelacznik { display: flex; align-items: center; gap: 7px;
  font-size: 13px; color: var(--ink-2); white-space: nowrap; cursor: pointer; }
.narzedzia-logo .przelacznik input { accent-color: var(--ink);
  width: 17px; height: 17px; }
.podglad-info { font-size: 13px; color: var(--ink-2); margin: 12px 0 0; max-width: 520px; }
.podglad-info.uwaga { color: var(--alarm); }

/* --------------------------------------------------------------- przyciski */
/* Odwzorowanie przycisku z banera print.gg - wartosci i uzasadnienie stoja
   przy tokenach `--akcent-*` na gorze pliku.

   ⛔ Kolor tekstu ZAWSZE w parze z tlem (`--akcent-tekst` przy `--akcent-grad`).
   Ta sama zasada, ktora obowiazywala przy zolci: podmiana samego tla bez
   koloru napisu daje nieczytelna pare.

   ⛔ Animujemy WYLACZNIE `background-position`. Skrot `background` objalby
   `background-image`, a gradientow przegladarka nie interpoluje - przejscie
   nie ruszyloby wcale i przycisk przeskakiwalby skokowo. */
.przycisk, .do-koszyka, .wgraj {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 22px; font-size: 15px; font-family: inherit; font-weight: 500;
  color: var(--akcent-tekst); background-image: var(--akcent-grad);
  background-size: 200%; background-position: 0% 0%; border: 0;
  border-radius: var(--r-pigulka); cursor: pointer; text-decoration: none;
  transition: background-position .8s, transform var(--ruch-szybko);
}
.przycisk:hover, .do-koszyka:hover, .wgraj:hover {
  background-position: 100% 0%; color: var(--akcent-tekst);
  text-decoration: none; }
.przycisk:active, .do-koszyka:active { transform: scale(.985); }
.do-koszyka { width: 100%; padding: 13px 22px; font-size: 16px; }
/* Stan wylaczony to PLASKA szarosc - musi zgasic takze gradient, wiec
   `background-image: none` jest tu konieczne, nie ozdobne: sam
   `background-color` zostalby pod obrazem tla i przycisk dalej bylby
   niebieski, tylko bez mozliwosci klikniecia. */
.do-koszyka:disabled {
  background-image: none; background-color: #e4e4e4;
  color: #767676; cursor: not-allowed;
}
.do-koszyka:disabled:hover { background-image: none; background-color: #e4e4e4; }

.usun {
  padding: 11px 20px; font-size: 15px; font-family: inherit;
  color: var(--ink); background: transparent;
  border: 1px solid var(--linia); border-radius: var(--r-pigulka); cursor: pointer;
  transition: background var(--ruch-szybko);
}
.usun:hover { background: #ebebef; }
.do-koszyka + .usun, .usun.pod-glownym { width: 100%; margin-top: 10px; }

/* ----------------------------------------------------------------- koszyk */
.waski { max-width: var(--waski); margin: 0 auto; padding: 40px 22px 80px; }
/* px, nie rem - patrz „GRANICA RAMY" na koncu pliku */
.waski h1 { font-size: clamp(29px, 4vw, 42px); font-weight: 600;
  /* ⛔ `line-height` JAWNIE: bez niej rama wstawia `normal` i naglowek traci
     19 px wysokosci wiersza (zmierzone 12.08: 43 zamiast 62). Dziedziczenie
     z `main` nie wystarcza - deklaracja wprost na elemencie zawsze je bije. */
  line-height: 1.1;
  letter-spacing: -0.025em; margin: 0 0 26px; }
.waski h2 { font-size: 19px; font-weight: 600; letter-spacing: -0.02em;
  margin: 6px 0 4px; }
.pozycja { display: grid; grid-template-columns: 1fr auto auto; gap: 16px;
  align-items: center; padding: 18px; background: var(--tlo-2);
  border-radius: var(--r-kafel); margin-bottom: 12px; }
.pozycja-kwota { font-weight: 600; white-space: nowrap; }
.sumy { width: 100%; margin: 24px 0; border-collapse: collapse; font-size: 16px; }
.sumy td { padding: 7px 0; color: var(--ink-2); }
.sumy td:last-child { text-align: right; color: var(--ink); }
.sumy .brutto td { font-size: 22px; font-weight: 600; color: var(--ink);
  letter-spacing: -0.02em; border-top: 1px solid var(--linia); padding-top: 14px; }

.formularz { display: grid; gap: 14px; margin-top: 22px; padding-top: 22px;
  border-top: 1px solid var(--linia); }
.formularz label { display: grid; gap: 6px; font-size: 13px; color: var(--ink-2); }
.formularz input, .formularz textarea {
  padding: 11px 13px; font-size: 16px; font-family: inherit; color: var(--ink);
  background: var(--tlo); border: 1px solid var(--linia); border-radius: var(--r-pole);
  width: 100%; resize: vertical;
}
.opcjonalne { color: var(--ink-2); font-weight: 400; }
.potwierdzenie { margin: 16px 0 0; padding: 14px 16px; border-radius: var(--r-pole);
  background: var(--ok-tlo); color: var(--ok); font-size: 15px; }
.pod-przyciskiem { margin: 12px 0 0; font-size: 12px; color: var(--ink-2);
  text-align: center; }

/* ================================================================= LANDING */
/* Strona glowna `/gift` jako strona SPRZEDAZOWA, nie siatka produktow.
   Decyzja Arta 2026-08-12 19:10, wzor ukladu: giffits.de.

   ⛔ POWOD, nie moda: giffits.de na wejsciu ma siedem sekcji i ZERO kafli
   produktowych - zmierzone 12.08. Klient B2B szukajacy gadzetu z nadrukiem
   zwykle nie wie, czego chce; 48 losowych kafli mu tego nie powie, a lista
   grup i cennik owszem.

   ⛔ Katalog NIE ZNIKA - przenosi sie na `/gift/katalog`. Zaden istniejacy
   adres nie ginie: `/gift/grupa/...`, `/gift/kategoria/...` i `/gift/produkt/...`
   zostaja nietkniete, a mapa witryny (2778 adresow) dostaje jeden adres wiecej.

   ⛔ Wszystkie rozmiary w px - patrz „GRANICA RAMY" na koncu pliku. */

.sekcja { padding: 64px 0; }
.sekcja--szara { background: var(--tlo-2); }

.tytul-sekcji {
  font-size: 26px; font-weight: 500; line-height: 1.2; color: var(--ink);
  margin: 0 0 8px; letter-spacing: -0.01em;
}
.lead { font-size: 15px; color: var(--ink-2); margin: 0 0 32px; max-width: 640px;
  line-height: 1.5; }
.naglowek-rzad {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 20px; margin-bottom: 28px; flex-wrap: wrap;
}
.naglowek-rzad .tytul-sekcji { margin-bottom: 0; }
/* ⛔ `.wiecej-link`, NIE `.wiecej` - ta druga nazwa nalezy do przycisku
   paginacji pod siatka katalogu i zrobilaby z odnosnika pigulke. */
.wiecej-link { font-size: 14px; color: var(--akcent); white-space: nowrap; }

/* ⛔ Wejscie jest BIALE, a pas grup pod nim szary. Gdyby oba byly szare,
   strona zaczynalaby sie jedna plama bez hierarchii. */
.wejscie { background: var(--tlo); padding: 56px 0 48px; }
.wejscie-siatka {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: 48px; align-items: center;
}
.wejscie h1 {
  font-size: clamp(30px, 3.4vw, 44px); font-weight: 500; line-height: 1.1;
  letter-spacing: -0.02em; color: var(--ink); margin: 0 0 16px;
  text-wrap: balance;
}
.wejscie-lead {
  font-size: clamp(15px, 1.3vw, 17px); font-weight: 400; color: var(--ink-2);
  line-height: 1.5; margin: 0 0 28px; max-width: 520px;
}
.cta-rzad { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
/* Drugie wezwanie bez wypelnienia - dwa pelne przyciski obok siebie znosza
   sobie nawzajem pierwszenstwo. */
.przycisk--obrys {
  background-image: none; background-color: transparent; color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--linia);
  transition: background-color var(--ruch-szybko);
}
.przycisk--obrys:hover { background-color: #ebebeb; color: var(--ink); }

.zaufanie { display: flex; gap: 22px; flex-wrap: wrap; margin-top: 28px;
  font-size: 13px; color: var(--ink-2); }
.zaufanie span { display: inline-flex; align-items: center; gap: 8px; }
/* ⛔ `fill`/`stroke` w CSS, nie w atrybutach - `home.css` sklepu ma
   `svg { fill: inherit }` i przemalowalby te ikony na czarne plamy. */
.zaufanie svg { width: 16px; height: 16px; flex: none;
  fill: none; stroke: var(--ink); stroke-width: 2; stroke-linecap: round; }

/* Kolaz zdjec przy wejsciu. Zdjecia dostawcy sa na BIALYM tle, wiec plyta pod
   nimi musi byc szara - inaczej produkt nie ma krawedzi i wisi w prozni. */
.wejscie-kolaz { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.wejscie-kolaz figure {
  margin: 0; background: var(--tlo-2); border-radius: var(--r-kafel);
  aspect-ratio: 1 / 1; display: grid; place-items: center; overflow: hidden;
}
.wejscie-kolaz figure:first-child { grid-row: span 2; aspect-ratio: auto; }
.wejscie-kolaz img { width: 82%; height: auto; object-fit: contain;
  mix-blend-mode: multiply; }

/* Kafle grup: mozaika „Top kategorie" jak na print.gg (polecenie Arta 14.08,
   zrzut) - biale karty na szarej sekcji, naglowek W karcie nad zdjeciem,
   dwa kafle duze (2x2) i dwa szerokie (2x1) nadaja rytm.
   ⛔ LICZBA KOLUMN JAWNA, nie `auto-fill`: grup jest DWANASCIE (11 plus
   „wszystkie") i to zbior zamkniety - wzor mozaiki jest dobrany na 12 kafli
   w 4 kolumnach (2x duzy=4 pola, 2x szeroki=2 pola, 8x zwykly = 20 pol,
   czyli rowne 5 rzedow). Przy `auto-fill` liczba kolumn plywalaby z oknem
   i wzor by sie rozjezdzal.
   ⛔ Wzor idzie po POZYCJI (`nth-child`), nie po tresci - serwer sortuje
   grupy od najliczniejszej, wiec duzy kafel dostaje najszerszy asortyment
   sam z siebie. Przy mniejszej liczbie kafli (testy, ubytek grupy w feedzie)
   `grid-auto-flow: dense` domyka dziury. */
.grupy-kafle { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: 210px; grid-auto-flow: dense; gap: 14px; }
.grupa-kafel {
  display: flex; flex-direction: column; gap: 4px; padding: 22px 22px 16px;
  background: var(--tlo); border-radius: var(--r-kafel); color: var(--ink);
  text-decoration: none; overflow: hidden;
  transition: box-shadow var(--ruch-szybko);
}
.grupa-kafel:hover { text-decoration: none; color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--linia); }
.grupa-kafel:nth-child(1),
.grupa-kafel:nth-child(7) { grid-row: span 2; grid-column: span 2; }
.grupa-kafel:nth-child(4),
.grupa-kafel:nth-child(8) { grid-column: span 2; }
.grupa-naglowek { display: flex; flex-direction: column; gap: 2px; flex: none; }
/* ⛔ `min-height: 0` i `height: auto` NIE SA kosmetyka. Bez nich kafel zalezy
   od resetu obrazkow z arkusza sklepu (`img { height: auto }`): zdjecia
   dostawcy maja atrybuty 700x700, wiec bez resetu rozpychaly kafel z 263 px
   do 787 px, a cala sekcje z 1050 do 2620. Zmierzone bramka granicy - to
   dokladnie ten rodzaj zaleznosci, ktory ma ona wylapywac: strona wygladala
   poprawnie, ale wylacznie dzieki cudzemu arkuszowi.
   Wysokosc karty daje teraz wiersz siatki (`grid-auto-rows`), nie
   `aspect-ratio` - zdjecie wypelnia reszte karty pod naglowkiem. */
/* KOMPOZYCJA KATEGORII: do trzech prawdziwych zdjec produktow zamiast
   jednego. Zero nowej grafiki do hostowania, zero tekstu w obrazie - ma byc
   zrozumiala tak samo dla klienta niemieckiego.

   ⛔ Obrazki sa POZYCJONOWANE ABSOLUTNIE, nie ukladane siatka. W ukladzie
   przeplywowym `max-height: 100%` nie ma sie od czego policzyc (wysokosc
   wiersza zalezy od tresci, czyli od samego obrazka - cykl) i przegladarka
   ignoruje ograniczenie: zdjecia 700x700 wystawaly z kafli i ucinala je
   krawedz karty (zmierzone zrzutem na 900 px, nie na desktopie - tam duzy
   kafel przypadkiem je miescil).

   ⛔ ZDJECIA NIE MOGA NA SIEBIE NACHODZIC. Maja `mix-blend-mode: multiply`
   (dzieki temu biale tlo znika), a multiply dwoch NAKLADAJACYCH SIE
   przedmiotow mnozy kolory i daje brudna plame. Bialy obszar mnozony przez
   bialy zostaje bialy, wiec ramki moga sie stykac - nie moga stykac sie
   TRESCI. Stad zapas: pola kompozycji maja miedzy soba 2-16 % luzu, a `--z`
   ma wlasny sufit (patrz `kadr.zoom`). */
.grupa-obraz { flex: 1; position: relative; min-height: 0; overflow: hidden;
  container-type: size; container-name: kompozycja; }
.grupa-produkt { position: absolute; }
.grupa-produkt img { display: block; width: 100%; height: 100%;
  object-fit: contain; mix-blend-mode: multiply;
  /* `--z` liczy SERWER z kadru zdjecia (`gadzety/sklep/kadr.py`), bo tylko on
     widzi piksele - przegladarka nie moze zmierzyc obcego obrazka z CDN bez
     naruszenia zasad pochodzenia. Brak pomiaru = 1, czyli zachowanie
     sprzed kompozycji. */
  transform: scale(var(--z, 1));
  transition: transform var(--ruch-szybko); }
/* Ten sam gest co dotad, ale DOMNOZONY do wlasnego `--z` zdjecia. Zapis
   `scale(1.04)` skasowalby korekte wielkosci i drobny towar skakalby przy
   najechaniu do pierwotnej, za malej skali. */
.grupa-kafel:hover .grupa-produkt img { transform: scale(calc(var(--z, 1) * 1.04)); }
@media (prefers-reduced-motion: reduce) {
  .grupa-produkt img { transition: none; }
  .grupa-kafel:hover .grupa-produkt img { transform: scale(var(--z, 1)); }
}

/* Uklad A „rozrzut" - dla ramek blizszych kwadratowi. Glowny produkt w lewym
   dolnym rogu, dwa mniejsze po przekatnej. */
.grupa-produkt--1 { left: 0; bottom: 0; width: 56%; height: 72%; }
.grupa-produkt--2 { right: 0; top: 0; width: 42%; height: 48%; }
.grupa-produkt--3 { right: 4%; bottom: 6%; width: 26%; height: 30%; }

/* Uklad B „polka" - trzy produkty w rzedzie, malejace w prawo, oparte o dol.
   ⛔ Przelacza je PROPORCJA WLASNEGO PUDELKA, nie `nth-child` ani szerokosc
   okna. Ten sam kafel bywa kwadratem i wstega zaleznie od liczby kolumn
   (zmierzone: od 1,4:1 do 7,7:1), wiec klucz po numerze kafla rozjechalby sie
   przy kazdej zmianie siatki - a to dokladnie ta klasa zaleznosci od cudzego
   ukladu, ktora juz raz zepsula ten plik.
   ⛔ Bez `@supports`: przegladarka bez zapytan o kontener po prostu pomija te
   regule i zostaje przy ukladzie A. Degradacja, nie awaria. */
@container kompozycja (min-aspect-ratio: 2/1) {
  .grupa-produkt--1 { left: 3%; right: auto; top: auto; bottom: 0;
    width: 30%; height: 94%; }
  .grupa-produkt--2 { left: 37%; right: auto; top: auto; bottom: 0;
    width: 25%; height: 72%; }
  .grupa-produkt--3 { left: 68%; right: auto; top: auto; bottom: 0;
    width: 21%; height: 54%; }
}

/* Grupa, ktora nie ma trzech roznych podkategorii ze zdjeciem (maly katalog,
   testy, „Targi i eventy" po ubytku w feedzie) - wracamy do DOKLADNIE starego
   wygladu, zeby pojedyncze zdjecie w rogu nie wygladalo na pomylke ukladu. */
.grupa-produkt:only-child { left: 0; right: 0; top: 0; bottom: 0;
  width: auto; height: auto; }
.grupa-produkt:only-child img { position: absolute; inset: 0; margin: auto;
  max-width: 76%; max-height: 94%; width: auto; height: auto; }
.grupa-nazwa { font-size: 17px; font-weight: 600; line-height: 1.3;
  letter-spacing: -0.01em; }
.grupa-ile { font-size: 13px; color: var(--ink-2); }

.kroki { display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px;
  counter-reset: krok; }
.krok { counter-increment: krok; }
/* ⛔ Kolko dostaje JEDNOLITY akcent, nie gradient: na plamie 34 px jasny stop
   w polowie zrobilby jasna kropke dokladnie pod cyfra. */
.krok::before {
  content: counter(krok); display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--akcent-plama); color: var(--akcent-tekst);
  font-size: 15px; font-weight: 600; margin-bottom: 14px;
}
.krok h3 { font-size: 16px; font-weight: 500; line-height: 1.35;
  margin: 0 0 6px; color: var(--ink); }
.krok p { font-size: 14px; color: var(--ink-2); line-height: 1.5; margin: 0; }

/* Pas polecanych. ⛔ Zbior ZAMKNIETY (osiem sztuk), wiec kolumny jawne
   i dobrane na dzielnik - cztery albo dwie, nigdy piec. Katalog ma
   `auto-fill` i to jest tam poprawne, bo produktow sa tysiace. */
.siatka--polecane { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.techniki { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.technika { background: var(--tlo); border-radius: 6px; padding: 22px; }
.technika h3 { font-size: 16px; font-weight: 500; line-height: 1.35;
  margin: 0 0 6px; }
.technika p { font-size: 14px; color: var(--ink-2); line-height: 1.5; margin: 0 0 10px; }
.technika .meta { font-size: 12px; color: var(--ink-2); }

.liczby { display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px; }
.liczba strong { display: block; font-size: 34px; font-weight: 500; line-height: 1;
  letter-spacing: -0.02em; }
.liczba span { display: block; font-size: 14px; color: var(--ink-2); margin-top: 8px; }

/* ⛔ Pas na cala szerokosc jest SZARY, nie w kolorze akcentu. Niebieski jako
   duza plama zabralby pierwszenstwo przyciskowi, ktory na nim stoi - a to
   przycisk ma byc tu jedyna rzecza do klikniecia. */
.pas-kontakt { background: var(--tlo-2); }
.pas-kontakt-siatka { display: flex; align-items: center;
  justify-content: space-between; gap: 32px; flex-wrap: wrap; }
.pas-kontakt h2 { font-size: 24px; font-weight: 500; line-height: 1.25;
  margin: 0 0 6px; }
.pas-kontakt p { font-size: 15px; color: var(--ink-2); margin: 0; }
.tel { font-size: 20px; font-weight: 500; color: var(--ink); text-decoration: none;
  white-space: nowrap; }
.tel:hover { color: var(--ink); text-decoration: none; }
.tel small { display: block; font-size: 12px; font-weight: 400; color: var(--ink-2);
  margin-top: 4px; }

/* ------------------------------------------------------------ responsywnosc */
/* Progi wziete z referencji: 734 px (telefon -> tablet), 1068 px (-> desktop). */
@media (max-width: 1068px) {
  .produkt { grid-template-columns: minmax(0, 1fr) 330px; gap: 30px; }
  /* ⛔ Menu sklepu schodzi do WLASNEGO wiersza. Szesc pozycji obok logo,
     szukajki i dwoch akcji nie miesci sie ponizej ~1050 px - przy 375 px
     cala strona przewijala sie w poziomie, a „Zaloguj sie" konczylo sie
     na „Zalo". Przewijanie poziome samego menu jest tanszym rozwiazaniem
     niz chowanie go pod przycisk: `/gift` ma byc widoczna sekcja sklepu,
     a menu schowane w hamburgerze przestaje o tym mowic. */
  .naglowek-tresc { flex-wrap: wrap; row-gap: 4px; padding-block: 10px; }
  .menu-sklepu {
    order: 10; width: 100%; margin-left: 0; gap: 18px;
    overflow-x: auto; overscroll-behavior-x: contain;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .menu-sklepu::-webkit-scrollbar { display: none; }
}
@media (max-width: 900px) {
  /* Landing schodzi do jednej kolumny. ⛔ Liczby kolumn dobrane tak, zeby
     zbiory zamkniete dalej dzielily sie bez reszty: 12 grup / 3 = 4 rzedy,
     8 polecanych / 2 = 4 rzedy. */
  .wejscie-siatka { grid-template-columns: 1fr; gap: 32px; }
  .wejscie-kolaz { max-width: 420px; }
  /* ⛔ DWIE kolumny, nie trzy: kafle duze i szerokie rozpinaja sie na 2 pola,
     wiec na trzech kolumnach wzor mozaiki zostawialby dziury w co drugim
     rzedzie. Na dwoch „span 2" znaczy po prostu „caly rzad". */
  .grupy-kafle { grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: 190px; }
  .siatka--polecane { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kroki { grid-template-columns: repeat(2, 1fr); }
  .techniki { grid-template-columns: repeat(2, 1fr); }
  .liczby { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 860px) {
  .produkt { grid-template-columns: 1fr; gap: 26px; padding-top: 26px; }
  /* Na waskim ekranie panel zakupu przestaje byc lepki - inaczej zabiera
     pol ekranu i klient nie moze doczytac opisu. */
  .kalkulator { position: static; }
  .foto-produkt { max-width: 520px; margin-inline: auto; }
}
@media (max-width: 734px) {
  body { font-size: 16px; }
  .hero { padding: 38px 22px 28px; }
  .naglowek-tresc { gap: 12px; padding: 0 16px; }
  .kontener, .kategorie { padding-inline: 16px; }
  .produkt, .waski { padding-inline: 16px; }
  .siatka { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 12px; }
  /* ⛔ Promien 6 px tak samo jak na desktopie. Do 12.08 stalo tu 14 px, czyli
     kafel zmienial ksztalt razem z szerokoscia okna - wartosc zostala z czasow,
     gdy `--r-kafel` bylo 12 px i telefon mial byc „troche bardziej oble". */
  .karta { padding: 12px 12px 14px; border-radius: 6px; }
  .karta h2 { font-size: 13px; }
  /* Landing na telefonie: 12 grup / 2 = 6 rzedow, 8 polecanych / 2 = 4 rzedy. */
  .sekcja { padding: 40px 0; }
  .wejscie { padding: 32px 0 36px; }
  .tytul-sekcji { font-size: 21px; }
  .grupy-kafle { grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: 150px; gap: 10px; }
  .grupa-kafel { padding: 14px 14px 12px; }
  .grupa-nazwa { font-size: 15px; }
  .siatka--polecane { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kroki { grid-template-columns: 1fr; gap: 18px; }
  .techniki { grid-template-columns: 1fr; }
  .pas-kontakt-siatka { flex-direction: column; align-items: flex-start; }
  /* Kciuk trafia w cel na calej szerokosci, a nie w pigulke stojaca przy
     lewej krawedzi. */
  .cta-rzad { flex-direction: column; align-items: stretch; width: 100%; }
  .cta-rzad .przycisk { width: 100%; }
  .logo { font-size: 18px; }
  .logo-kropka { font-size: 22px; }
  .menu-sklepu a { font-size: 14px; }
  /* „Zaloguj sie" zwezone, ale NIE zamienione na sama ikone - to jedyne
     wejscie do konta, a ikona ludzika bez podpisu bywa czytana jako profil
     sprzedawcy. */
  .konto { padding: 8px 15px; font-size: 13px; }
}
@media (max-width: 560px) {
  .pozycja { grid-template-columns: 1fr auto; }
  .pozycja .usun { grid-column: 2; }
}
/* Szukajka schodzi pod pasek marki, zamiast sciskac sie do nieczytelnosci. */
@media (max-width: 620px) {
  .naglowek-tresc { flex-wrap: wrap; padding-block: 8px; row-gap: 8px; }
  .szukajka { order: 3; flex-basis: 100%; max-width: none; }
  .naglowek-akcje { margin-left: auto; }
  /* ⛔ Naglowek przestaje byc LEPKI. Na telefonie ma juz trzy rzedy (logo
     z akcjami, szukajka, menu) i przyklejony zabieralby jedna trzecia
     ekranu na kazdym przewinieciu. */
  .naglowek { position: static; }
  /* Pasek przestaje byc lepki (naglowek z szukajka zjada juz dwa rzedy),
     ale MUSI zostac kontekstem pozycjonowania - inaczej gradient krawedzi
     (`::after`) ucieka do innego przodka i laduje w poprzek strony.
     Zlapane pomiarem `position: static` + `pseudo_ma_kontekst: false`. */
  .kategorie-pas { top: auto; position: relative; }
}

/* ====================================================================== */
/* GRANICA RAMY - styk naszej tresci z naglowkiem i stopka sklepu print.gg */
/* ====================================================================== */
/*
  Od 11.08 (zadanie A2, wariant 1) `/gift` nie rysuje wlasnego naglowka, tylko
  wstawia PRAWDZIWY fragment sklepu, pobrany przez `narzedzia/pobierz_rame.py`.
  Nasza tresc i rama sklepu dziela od tej pory JEDEN dokument i JEDNA kaskade.

  ⛔ ARKUSZE SKLEPU LADUJA SIE PO NASZYM - i tak ma zostac. Kolejnosc odwrotna
     zmierzono zrzutem: naglowek gubi wtedy pole szukania i cale menu.
     Wniosek: o rame walczymy SPECYFICZNOSCIA, nie kolejnoscia.

  ⛔ KAZDA nasza regula na goly ZNACZNIK (0,0,1) przegrywa z regula sklepu
     o tej samej postaci, bo sklep jest pozniej. Regula z KLASA (0,1,0) wygrywa
     zawsze, niezaleznie od kolejnosci. Dlatego wszystko ponizej ma klase.

  ⛔ SKLEP MA TYPOGRAFIE PLYNNA: `html { font-size: 0.833334vw }`. Przy oknie
     1440 px korzen to ~12 px zamiast 16. W naszej tresci NIE MOZE byc ani
     jednego `rem` - byly cztery (naglowki `clamp`), zamienione na `px`.

  Kryterium odbioru tej sekcji to RELACJA, nie zmiana: nasza tresc ma sie
  mierzyc TAK SAMO z rama i bez ramy. Sprawdza to `tests/test_rama.py`.
*/

/* ⛔ NAGLOWEK SKLEPU JEST `position: fixed` - NIE ZAJMUJE MIEJSCA W PRZEPLYWIE.
   Bez rekompensaty nasza pierwsza sekcja lezy na `y=0`, czyli POD naglowkiem.
   Zmierzone `elementFromPoint` w srodku hero: trafia w odnosnik menu, nie w hero.

   ⛔ Sklep kompensuje to u siebie regula `main.main { padding-top: 129px }`,
      ktora siedzi w STYLU WPISANYM W STRONE, a nie w zadnym z arkuszy, ktore
      pobieramy. Swiadomie NIE bierzemy stylow inline: na 43 reguly tylko 5
      dotyczy ramy, reszta to WooCommerce, emoji i banner zgody. Dlatego
      rekompensate liczymy sami.

   ⛔ PROG 1200 px NIE JEST WYBRANY - jest PRZEPISANY z reguly sklepu
      `@media (min-width: 1200px) { header.banner { top: 40px } }`. Powyzej
      progu nad naglowkiem stoi pasek jezyka (40 px), ponizej znika. Wlasny,
      inny prog rozjechalby sie z paskiem przy pierwszej zmianie szerokosci.

   Zmierzone: wysokosc naglowka 120,9 px jest STALA w obu pasmach; dol chrome
   to 160,9 px powyzej progu i 120,9 px ponizej.

   ⛔ NAGLOWEK MUSI DOSTAC JAWNE `top`, inaczej rekompensata przesuwa JEGO SAMEGO.
      Ponizej 1200 px sklep daje mu `position: fixed` BEZ `top`, a element ustalony
      bez `top` stoi na swojej pozycji STATYCZNEJ - czyli tam, dokad zepchnal go
      nasz `padding-top` na `body`. Zmierzone: dol chrome skakal ze 120,9 na 241,9,
      dokladnie o wartosc rekompensaty. Powyzej progu sklep ustawia `top: 40px`
      sam i tej reguly nie potrzebuje - dlatego dajemy ja tylko ponizej progu. */
@media (max-width: 1199.98px) { body.gift header.banner { top: 0; } }
body.gift { padding-top: 121px; }
@media (min-width: 1200px) { body.gift { padding-top: 161px; } }

/* ⛔ `home.css` sklepu ma `main { display:flex; flex-direction:column }`.
   We flexboxie AUTO-MARGINES W POPRZEK OSI WYLACZA ROZCIAGANIE, wiec nasze
   `.kontener { margin: 0 auto }` przestawalo byc szerokie na caly `main`
   i kurczylo sie do tresci: 3x232 + 2x18 + 44 = 776 px zamiast 1120 px.
   Siatka schodzila przez to z czterech kolumn na trzy. */
/* ⛔ `:where(.gift)` ZERUJE WAGE KLASY - i to jest cala istota tego selektora,
   nie ozdobnik. Potrzebna jest waga POMIEDZY: wieksza niz `main` sklepu
   (0,0,1), zeby zdjac jego `display:flex`, ale MNIEJSZA niz nasze wlasne
   korzenie z klasa (`.produkt { display: grid }` = (0,1,0)), zeby ich nie
   przykryc. `:where()` daje dokladnie to: (0,0,2).

   ⛔ Pierwsza wersja miala `body.gift main` = (0,1,2) i POLOZYLA UKLAD KARTY
   PRODUKTU - dwie kolumny (opis + kalkulator) zlozyly sie w jedna, kalkulator
   zjechal 1100 px w dol. Bylo to na produkcji ~10 minut.
   ⚠️ Test relacyjny tego NIE ZLAPAL i nie mogl: porownuje nasza tresc Z RAMA
   i BEZ RAMY, a ta wada psula obie strony jednakowo. Relacja dowodzi, ze rama
   nas nie przemalowuje - NIE dowodzi, ze nasz wlasny uklad jest poprawny.
   Stad osobna asercja ukladu w `sprawdz_granice.py`. */
body:where(.gift) main {
  display: block;
  /* ⛔ `main` jest DZIECKIEM `body`, a sklep daje `body { display:flex;
     flex-direction:column }`. Gdy `main` niesie tez `.waski` z `margin: 0 auto`
     (koszyk, strona podziekowania), auto-margines w poprzek osi WYLACZA
     rozciaganie i element kurczy sie do tresci. Zmierzone na produkcji 12.08:
     koszyk mial **187 px zamiast 700**. `width: 100%` przywraca pelna szerokosc
     linii; `max-width` nadal przycina, a auto-marginesy nadal centruja.
     ⚠️ Wada byla UTAJONA: przy pustym koszyku 187 px wyglada poprawnie, bo
     tresci jest mniej niz szerokosci. Widac ja dopiero z pozycjami. */
  width: 100%;
  /* ⛔ TLO JAWNE, nie odziedziczone po sklepie. Do 12.08 bialy `main` bral
     sie z `home.css`, a nasz byl przezroczysty - zmierzone bramka granicy.
     Gdyby sklep kiedys zdjal to tlo, przez nasza strone przebilby sie `body`,
     ktory na print.gg jest CZARNY. Strony z wlasnym tlem (`.tlo-siatki`
     w katalogu) nadal wygrywaja: klasa (0,1,0) bije ten selektor (0,0,2). */
  background: var(--tlo);
}

/* Sklep ustawia `body { font-size }` wzgledem plynnego korzenia, a to
   dziedziczy sie w glab naszej tresci. Przywracamy nasza typografie na
   KORZENIU TRESCI.

   ⛔ REGULA IDZIE PO `main`, NIE PO LISCIE KLAS - i to jest poprawka bledu
      z 11.08. Poprzednia wersja wymieniala `.tlo-siatki, .waski, .hero,
      .kategorie-pas`, czyli korzenie stron, ktore akurat wtedy sprawdzalem.
      Karta produktu ma `main.produkt` i wypadla z listy: cala karta
      dziedziczyla plynna typografie sklepu i **opis produktu renderowal sie
      w 10 px zamiast 17**. Lista klas wymaga pamietania o niej przy KAZDEJ
      nowej stronie; `main` obowiazuje sam z siebie.

   ⛔ Nie dajemy tego na `body`, bo `body` obejmuje takze rame - przemalowalibysmy
      naglowek i stopke sklepu, ktore maja wygladac jak sklep.

   `.hero` i `.kategorie-pas` leza POZA `main` (sa rodzenstwem), wiec musza byc
   wymienione osobno - to jedyne dwa takie miejsca i pilnuje ich test. */
body.gift main,
body.gift .hero,
body.gift .kategorie-pas {
  font: 400 17px/1.47 Epilogue, -apple-system, BlinkMacSystemFont, "Segoe UI",
        Roboto, "Helvetica Neue", Arial, sans-serif;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* ⛔ POLA FORMULARZY - ARYTMETYKA SPECYFICZNOSCI, NIE INTUICJA.
   `home.css` sklepu ma:

       button, input:not([type="checkbox"]):not(.builderius-setting…)
           { border: medium; background: none; padding: 0 }

   ⛔ SELEKTOR SKLEPU MA SIEDEM KLAUZUL `:not()` - odczytany z przegladarki,
   nie ze zrzutu ekranu:

       input:not([type="checkbox"]):not(.builderius-setting-field)
            :not(.uni-cpo-settings-saved):not(.woocommerce-Input)
            :not([name="sacon_cart_order_custom_name"])
            :not(.shop_table .addresses input):not(.uni-cpo-settings-btn)

   Argumenty `:not()` licza sie do wagi, wiec to jest **(0,8,2)** - globalny
   reset kazdego pola w witrynie. Zeby go przebic zwyklym selektorem, trzeba
   by miec DZIEWIEC pozycji w kolumnie klas: selektor nie do utrzymania.
   Probowalem po kolei i oba przegraly: `.kalkulator input` (0,1,1) oraz
   `body.gift main input:not([type=hidden])` (0,2,3) - porownanie idzie
   kolumnami, wiec liczba klas rozstrzyga, zanim policzy sie znaczniki.

   Skutek na produkcji, zmierzony 12.08: pole „Ilosc" na karcie produktu mialo
   **17 px wysokosci zamiast 41**, bez ramki i bez tla - pole, od ktorego
   zalezy cena, wygladalo jak zwykly tekst i mialo obszar dotyku ponizej
   polowy zalecanych 44 px.

   ⛔ Stad `!important` - JEDYNE w tym pliku. To nie jest skrot zamiast
      poprawnego selektora; to koniec wyscigu, ktorego nie da sie wygrac.
   ⚠️ Deklaracje sklepu NIE sa `!important` (sprawdzone w `cssText`), wiec
      nasze wystarcza i niczego nie eskaluje.
   ⚠️ Dziala WYLACZNIE w naszej tresci (`body.gift main`). Pola ramy - w tym
      „znajdz produkt" w naglowku - zostaja sklepowe. */
body.gift main input:not([type="hidden"]),
body.gift main select:not([multiple]),
body.gift main textarea:not([readonly]) {
  border: 1px solid var(--linia) !important;
  background: var(--tlo) !important;
  border-radius: var(--r-pole) !important;
  padding: 11px 13px !important;
  appearance: auto;
  font: inherit;
  color: var(--ink);
}
/* Pola, ktore maja WLASNY wyglad, musza zostac wylaczone z reguly wyzej -
   inaczej suwak i przelacznik kreatora dostana ramke i tlo pola tekstowego.
   ⛔ TU TEZ `!important`, i to nie przez symetrie: regula wyzej jest
   `!important`, wiec zwykla deklaracja by z nia PRZEGRALA mimo wyzszej
   specyficznosci. `!important` przebija sie tylko `!important`em. */
body.gift main input[type="range"],
body.gift main input[type="checkbox"],
body.gift main input[type="radio"],
body.gift main input[type="file"] {
  border: 0 !important; background: none !important;
  padding: 0 !important; border-radius: 0 !important;
}

/* ⛔ Tlo korzenia WLASNE, nie odziedziczone po sklepie. `home.css` daje
   `main { background-color: #fff }`; bez wlasnej deklaracji koszyk wyglada
   tak samo tylko dlatego, ze `body` jest akurat biale. Jawne tlo sprawia,
   ze relacja „z rama == bez ramy" trzyma sie na wlasnych nogach.
   ⚠️ NIE dajemy tego na `body.gift main`, bo (0,1,2) przebilo by
   `.tlo-siatki` (0,1,0) i zabralo szare tlo stronie listy. */
body.gift main.waski,
body.gift main.produkt { background: var(--tlo); }

/* Akapity BEZ WLASNEJ KLASY. Sklep narzuca im margines liczony z plynnego rem
   (14,83 px zamiast naszych 17). `:not([class])` zostawia nasze wlasne akapity
   w spokoju - te maja klasy i wyzsza specyficznosc. */
body.gift main p:not([class]) { margin: 0 0 17px; }

/* `home.css` ma `a { color: inherit }`, a `uikit.css` `a { color:#1e87f0 }` -
   oba biją nasze `a { color: var(--akcent) }`. Kafle i przyciski maja wlasne
   klasy i sa bezpieczne; to jest dla GOLYCH odnosnikow w naszej tresci. */
.tlo-siatki a:not([class]),
.waski a:not([class]),
.hero a:not([class]) {
  color: var(--akcent);
}

/* ------------------------------------------------- dymek z powiekszeniem */
/* Polecenie Arta 2026-08-14: „najechanie na miniature powieksza ja jako dymek".
   Zawartosc i polozenie liczy `lupa.js`; tutaj jest wylacznie wyglad.

   ⛔ CALOSC W `@media (hover: hover)`. Na telefonie najechania nie ma - palec
   generuje `mouseover` tuz przed klikiem, wiec bez tego warunku chmurka
   zapalalaby sie dokladnie na kaflu, w ktory klient celuje, i zaslanialaby go.

   ⛔ `pointer-events: none` jest te w skrypcie (styl wbudowany), bo to warunek
   POPRAWNOSCI, nie wygladu: dymek lezy nad kaflem-odnosnikiem i bez tego
   przejalby klikniecie. Tutaj powtarzamy go swiadomie - gdyby ktos kiedys
   wyciagnal styl ze skryptu, regula ma zostac. */
@media (hover: hover) {
  /* ⛔ Wymiarow tu NIE MA. Rozmiar zalezy od kafla (`data-lupa-bok`) i od
     wielkosci okna, wiec liczy go skrypt i wpisuje jako styl wbudowany.
     Sztywne 300 px stalo tu do 14.08 15:30 - po dolozeniu wiekszego dymka dla
     kolorow byloby drugim, nieaktualnym zrodlem tej samej liczby. */
  .lupa {
    position: fixed; z-index: 60; pointer-events: none;
    padding: 10px;
    background: #fff; border: 1px solid var(--linia); border-radius: 12px;
    box-shadow: 0 10px 34px rgba(0, 0, 0, .16);
    opacity: 0; transition: opacity var(--ruch-szybko);
  }
  .lupa[hidden] { display: none; }
  .lupa.widoczna { opacity: 1; }
  /* `contain` - zdjecia dostawcy sa kwadratowe, ale nie wszystkie kadry
     wypelniaja pole; przyciecie ucinaloby produkt przy krawedzi. */
  .lupa img { width: 100%; height: 100%; object-fit: contain; display: block; }
}
/* Poza urzadzeniami z mysza chmurka nie istnieje - nawet gdyby skrypt ja
   zbudowal (np. na hybrydzie przelaczonej w tryb dotykowy). */
@media not all and (hover: hover) {
  .lupa { display: none !important; }
}

/* --------------------------------------------------------------- less ruch */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
  .karta:hover { transform: none; }
  .karta:hover .foto img { transform: none; }
}
