/*
 * Motyw bazowy — struktura i mapowanie tokenów. Zero wartości marki.
 *
 * Cztery części, w tej kolejności:
 *   1. mapowanie --wp--* → --s-*  (jedyne miejsce poza :where() obok tonów)
 *   2. tony                       (muszą wygrywać z mapowaniem, więc też bez :where())
 *   3. struktura                  (wszystko w :where(), czyli specyficzność 0)
 *   4. punkty załamania           (jedyne w całym repozytorium)
 *
 * Poza mapowaniem i tonami każdy selektor siedzi w :where(), czyli ma
 * specyficzność zero. To jedyny powód, dla którego dowolna reguła w
 * sites/<klient>/skin.css wygrywa z bazą samą kolejnością w kaskadzie, bez
 * !important — a nie kwestia gustu.
 */

/* ---------------------------------------------------------------------------
 * 1. Mapowanie: warstwa źródeł (--wp--*) → warstwa semantyczna (--s-*).
 *
 * Jeden wiersz na token ze słownika docs/tokens.md §2 — dziewiętnaście wierszy
 * tokenów koloru, powierzchni, kształtu i kroju, do tego dwadzieścia pięć
 * wierszy rozszerzenia słownika: tokeny ról typografii (dziewiętnaście) i
 * długości (dwa) z Zadania 4 oraz kolor eyebrow, rozmiar przycisku (dwa) i
 * cień pigułki z Zadania 5 — ani jednego więcej. Token spoza słownika nie istnieje; token ze słownika
 * bez wiersza tutaj unieważnia po cichu każdą deklarację, w której go użyto.
 *
 * Wartości zapasowe istnieją po to, żeby BRAK tokenu było widać, a nie po to,
 * żeby go zastąpić. Stąd dwie reguły ich doboru:
 *   – gdzie „nic” samo w sobie jest widoczne (powierzchnia, linia, cień,
 *     promień, odstęp, gradient) — wartość zdegenerowana: transparent, none, 0;
 *   – wszędzie indziej wartość, która KRZYCZY: magenta #f0f na powierzchniach,
 *     cyjan #0ff na tekście głównym, żółty #ff0 na tekście pobocznym, i krój,
 *     którego nikt nie zaprojektował.
 *
 * Trzy różne krzyki, a nie jeden, bo tekst musi być widoczny na powierzchni,
 * która też spadła na wartość zapasową, a tekst poboczny musi być odróżnialny
 * od głównego. Magenta na magencie i cyjan na cyjanie są niewidoczne — czyli
 * są dokładnie tym, przed czym ta reguła ma bronić.
 *
 * Tło, tekst i kroje NIE są od tej reguły wyjątkiem. Stało tu wcześniej #fff,
 * #000 i system-ui — wartości neutralne, czyli takie, które brak wypełniają po
 * cichu zamiast go pokazać. Powód, dla którego to jest awaria, a nie kwestia
 * gustu: tablice presetów są przy dziedziczeniu ZASTĘPOWANE w całości
 * (docs/tokens.md §5), więc slug palety pominięty przez stronę jest slugiem
 * NIEOBECNYM — a przy liściu nieobecnym wartość zapasowa się URUCHAMIA
 * (zmierzone, tamże; sonda tooling/container/check-empty-leaf.php). Strona,
 * która poda bg i accent, a zgubi ink, wyrenderowałaby czarny tekst na białym
 * tle: wygląda jak decyzja projektowa, przechodzi kontrast w axe, przechodzi
 * bramkę koloru i idzie do klienta z wykasowanym kolorem tekstu marki. To samo
 * z fontFamilies bez slugu head — wszystkie nagłówki w system-ui, schludnie
 * i bezgłośnie.
 * ------------------------------------------------------------------------- */

:root {
	/* Kolor i tekst */
	--s-bg: var(--wp--preset--color--bg, #f0f);
	/* Cyjan, nie czerń: czerń na białym tle jest nierozróżnialna od decyzji
	   projektowej, a tekst musi zostać czytelny także wtedy, gdy tło też spadło
	   na wartość zapasową. */
	--s-ink: var(--wp--preset--color--ink, #0ff);
	--s-accent: var(--wp--preset--color--accent, #f0f);
	/* currentColor byłby tu dokładnie tą awarią, którą naprawiamy: tekst
	   poboczny w kolorze tekstu głównego, czyli hierarchia znikająca bez
	   jednego komunikatu (docs/tokens.md §3). Żółty, nie magenta: odkąd tło
	   spada na magentę, magenta byłaby tu niewidzialna. */
	--s-ink-muted: var(--wp--custom--ink--muted, #ff0);
	/* Cyjan, nie magenta: napis na przycisku musi zostać czytelny nawet wtedy,
	   gdy akcent też spadł na wartość zapasową. */
	--s-ink-on-accent: var(--wp--custom--ink--on-accent, #0ff);

	/* Powierzchnie i linie */
	--s-surface-card: var(--wp--custom--surface--card, transparent);
	--s-surface-alt: var(--wp--custom--surface--alt, transparent);
	/* Czarne tło wyglądałoby na decyzję i przeszłoby niezauważone. */
	--s-surface-inverse: var(--wp--custom--surface--inverse, #f0f);
	--s-line-soft: var(--wp--custom--line--soft, transparent);
	--s-line-strong: var(--wp--custom--line--strong, transparent);

	/* Kształt, cień i przestrzeń */
	--s-radius-card: var(--wp--custom--radius--card, 0);
	--s-radius-btn: var(--wp--custom--radius--btn, 0);
	--s-radius-pill: var(--wp--custom--radius--pill, 0);
	--s-shadow-card: var(--wp--custom--shadow--card, none);
	--s-space-section: var(--wp--custom--space--section, 0);
	--s-space-card: var(--wp--custom--space--card, 0);
	--s-grad-hero: var(--wp--custom--gradient--hero, none);

	/* Typografia — dokładnie dwie rodziny (docs/tokens.md §4).
	   Nazwa w cudzysłowie nie odpowiada żadnemu zainstalowanemu krojowi, więc
	   przeglądarka schodzi do generycznego `cursive` — pisma odręcznego, którego
	   nikt nie weźmie za projekt. system-ui stało tu wcześniej i było dokładnie
	   tą wartością zapasową, która brak wypełnia zamiast go pokazać: strona bez
	   slugu `head` wyglądała schludnie i nie zgłaszała niczego. */
	--s-font-head: var(--wp--preset--font-family--head, "BRAK KROJU head", cursive);
	--s-font-body: var(--wp--preset--font-family--body, "BRAK KROJU body", cursive);

	/* Typografia ról (docs/tokens.md §2 „Tokeny ról typografii", spec
	   2026-09-15 §3.1, pomiar 2026-09-15-slownik-pomiar.md §2.1). Siedem ról,
	   które przeszły próg 3/5 w tooling/skin-inventory.json; każda rola ma
	   TYLKO te pola, które ustawiają ≥3 z 5 skór (ten sam próg co dla roli —
	   decyzja autora po recenzji Zadania 4; tabela pole × liczność w
	   docs/tokens.md §2): h1 i eyebrow cztery pola, h2 bez interlinii (1/5),
	   h3 / btn / badge tylko rozmiar i grubość, lead tylko rozmiar i interlinia.

	   BEZ wartości zapasowej, inaczej niż wszystko wyżej: dla rozmiaru nie ma
	   odpowiednika #f0f — „prawie dobry" rozmiar wygląda jak decyzja. Brak
	   tokenu unieważnia deklarację konsumenta i element DZIEDZICZY z body
	   (font-size, font-weight, line-height, letter-spacing są dziedziczone).
	   Głośność daje lint: allTokensDefined (mapowanie tutaj) + noUnusedTokens
	   (konsument w bazie) w tooling/lint.mjs; typ wartości pilnuje
	   tooling/theme-json-validate.mjs.

	   NAZWY po prawej są ZMIERZONE na kontenerze (wp_get_global_stylesheet(),
	   2026-09-16), nie przepisane z theme.json: WordPress rozbija `h1` na
	   `h-1` (litera|cyfra), `lineHeight` na `line-height`, `letterSpacing` na
	   `letter-spacing` (docs/CONVENTIONS.md §9). `--wp--custom--type--h1--size`
	   nie istnieje i nigdy nie zgłosi, że go nie ma. */
	--s-type-h1-size: var(--wp--custom--type--h-1--size);
	--s-type-h1-weight: var(--wp--custom--type--h-1--weight);
	--s-type-h1-lh: var(--wp--custom--type--h-1--line-height);
	--s-type-h1-ls: var(--wp--custom--type--h-1--letter-spacing);

	--s-type-h2-size: var(--wp--custom--type--h-2--size);
	--s-type-h2-weight: var(--wp--custom--type--h-2--weight);
	--s-type-h2-ls: var(--wp--custom--type--h-2--letter-spacing);

	--s-type-h3-size: var(--wp--custom--type--h-3--size);
	--s-type-h3-weight: var(--wp--custom--type--h-3--weight);

	--s-type-lead-size: var(--wp--custom--type--lead--size);
	--s-type-lead-lh: var(--wp--custom--type--lead--line-height);

	--s-type-eyebrow-size: var(--wp--custom--type--eyebrow--size);
	--s-type-eyebrow-weight: var(--wp--custom--type--eyebrow--weight);
	--s-type-eyebrow-lh: var(--wp--custom--type--eyebrow--line-height);
	--s-type-eyebrow-ls: var(--wp--custom--type--eyebrow--letter-spacing);

	--s-type-btn-size: var(--wp--custom--type--btn--size);
	--s-type-btn-weight: var(--wp--custom--type--btn--weight);

	--s-type-badge-size: var(--wp--custom--type--badge--size);
	--s-type-badge-weight: var(--wp--custom--type--badge--weight);

	/* Długości tekstu (docs/tokens.md §2 „Tokeny długości", pomiar §2.2):
	   miara wiersza leadu i szerokość kontenera hero wyśrodkowanego. Też bez
	   wartości zapasowej — brak tokenu unieważnia `max-width`, czyli daje
	   `none`, dokładnie to, co dałaby wartość zapasowa `none`; strona, która
	   świadomie nie chce ograniczenia, wpisuje `none` w theme.json. */
	--s-measure-lead: var(--wp--custom--measure--lead);
	--s-hero-max-width: var(--wp--custom--hero--max-width);

	/* Zadanie 5 rozszerzenia słownika (pomiar §2.3 i §2.5; nazwy po prawej
	   ZMIERZONE na kontenerze 2026-09-16, `paddingInline` → `padding-inline`).

	   --s-ink-eyebrow: kolor, więc krzyczy jak każdy token koloru w tej
	   części — żółty #ff0 jak --s-ink-muted (tekst poboczny na powierzchni,
	   która też mogła spaść na magentę). „Bez wartości zapasowej" dotyczy
	   ROZMIARÓW (dla rozmiaru nie ma wartości, która krzyczy), nie kolorów
	   (decyzja kontrolera po recenzji Zadania 5). 5/5 skór ustawiało kolor
	   eyebrow, trzy z nich na `--s-ink-muted`. Token NIE należy do grupy
	   tonalnej: jedyna skóra, która zmienia kolor eyebrow w tonie odwróconym,
	   to agencja (hero, `#cfe3ff`) — 1/5, zostaje w skórze (pomiar §2.5, §6).

	   --s-btn-height / --s-btn-padding-inline: rozmiary, bez wartości
	   zapasowej jak --s-type-*. Brak tokenu unieważnia `height` (→ auto,
	   czyli min-height 44px z części 3b jak dotąd) i `padding-inline` (→ 0:
	   przycisk bez światła po bokach — strona bez tokenu to WIDZI).

	   --s-shadow-badge: cień — wartość zdegenerowana `none`, jak
	   --s-shadow-card wyżej; strona bez cienia wpisuje `none`. */
	--s-ink-eyebrow: var(--wp--custom--ink--eyebrow, #ff0);
	--s-btn-height: var(--wp--custom--btn--height);
	--s-btn-padding-inline: var(--wp--custom--btn--padding-inline);
	--s-shadow-badge: var(--wp--custom--shadow--badge, none);

	/* Metryki fontu (docs/tokens.md §2 „Tokeny metryk fontu”). Font, którego
	   ramka linii (ascent + descent) nie jest symetryczna wokół środka liter,
	   stawia litery niżej (albo wyżej) niż geometryczny środek ramki — a flex
	   i przeglądarka centrują ramkę. --s-text-sink: o ile optyczny środek
	   liter (połowa drogi między środkiem wielkich i małych) leży PONIŻEJ
	   środka ramki; ikony obok tekstu (.s-ico-txt) schodzą o tyle w dół.
	   Plus Jakarta Sans: ascent 1038, descent 222 → środek ramki 408 nad linią
	   bazową, optyczny 320 → 0.088em. Wartość zapasowa 0em to „bez poprawki”,
	   nie wartość marki. */
	--s-text-sink: var(--wp--custom--text--sink, 0em);

	/* Ikony w CSS (docs/CONVENTIONS.md §9, docs/tokens.md §2 „Ikony w CSS”):
	   maski z kształtów biblioteki base-theme/inc/icons.php — dla
	   pseudoelementu, który nie ma markupu (`::before` z `nth-child`, lista
	   wyboru). Kolor daje `background: currentColor` elementu z maską. Ten
	   sam kształt co w PHP — pilnuje tooling/icons.test.mjs. Nazwa po
	   myślniku = nazwa kształtu w bibliotece. */
	--s-icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
	--s-icon-strzalka-ukos: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17L17 7M8 7h9v9'/%3E%3C/svg%3E");
	--s-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");

	/* Chrome: warianty nagłówka `mega` i stopki `kolumny` (docs/tokens.md §2
	   „Tokeny chrome”). Kolory NIEPRZEZROCZYSTE — krycie zapisuje arkusz
	   wariantu przez color-mix(in srgb, var(--s-chrome-…) N%, transparent),
	   co daje piksel w piksel to samo co rgba(). Poza grupą tonalną — chrome
	   nie ma pola tone. Nazwy po prawej ZMIERZONE na kontenerze 2026-09-27
	   (`ctaHover` → `cta-hover`). */
	--s-chrome-surface: var(--wp--custom--chrome--surface, #f0f);
	--s-chrome-bar: var(--wp--custom--chrome--bar, #f0f);
	--s-chrome-panel: var(--wp--custom--chrome--panel, #f0f);
	--s-chrome-ink: var(--wp--custom--chrome--ink, #0ff);
	--s-chrome-accent: var(--wp--custom--chrome--accent, #ff0);
	--s-chrome-cta-hover: var(--wp--custom--chrome--cta-hover, #f0f);
	--s-chrome-icon: var(--wp--custom--chrome--icon, #ff0);
	--s-chrome-shadow: var(--wp--custom--chrome--shadow, #f0f);

	/* Blok agency/showcase (docs/tokens.md §2 „Tokeny bloku showcase”). Kolory
	   NIEPRZEZROCZYSTE — krycie zapisuje blocks/src/showcase/style.css przez
	   color-mix(in srgb, var(--s-showcase-…) N%, transparent), jak chrome. Poza
	   grupą tonalną: tekst i cieniowanie leżą na zdjęciu, nie na tle sekcji. */
	--s-showcase-link: var(--wp--custom--showcase--link, #ff0);
	--s-showcase-frame: var(--wp--custom--showcase--frame, #f0f);
	--s-showcase-shade: var(--wp--custom--showcase--shade, #f0f);
	--s-showcase-media-ink: var(--wp--custom--showcase--media-ink, #0ff);
	--s-showcase-chip-ink: var(--wp--custom--showcase--chip-ink, #ff0);
}

/* ---------------------------------------------------------------------------
 * 2. Tony — remapowanie tokenów semantycznych. Poza :where(), bo muszą wygrywać
 * z mapowaniem z części 1, które nadpisują.
 *
 * Grupa tonalna to zamknięta piątka (docs/tokens.md §3):
 *   --s-bg  --s-ink  --s-ink-muted  --s-surface-card  --s-line-soft
 * KAŻDA klasa przypisuje komplet — także wtedy, gdy powtarza ton domyślny.
 * Cztery przypisania zamiast pięciu kosztowały etap walidacyjny obramowanie
 * karty we wszystkich ciemnych sekcjach naraz, po cichu. Pilnuje tego kontrola
 * allTonesComplete w tooling/lint.mjs.
 *
 * Tony sięgają po warstwę źródeł (--wp--*), bo są częścią mapowania. Nie mogą
 * czytać tokenu, który same ustawiają (--s-ink: var(--s-ink)) — to cykl, a cykl
 * w CSS unieważnia obie strony.
 *
 * .is-tone-default nie jest ozdobą: bez niej ton domyślny jest brakiem klasy, a
 * brak klasy niczego nie resetuje — sekcja domyślna wewnątrz odwróconej
 * dziedziczyłaby cudzą piątkę i nie miała jak wrócić.
 * ------------------------------------------------------------------------- */

.is-tone-default {
	--s-bg: var(--wp--preset--color--bg, #f0f);
	--s-ink: var(--wp--preset--color--ink, #0ff);
	--s-ink-muted: var(--wp--custom--ink--muted, #ff0);
	--s-surface-card: var(--wp--custom--surface--card, transparent);
	--s-line-soft: var(--wp--custom--line--soft, transparent);
}

.is-tone-alt {
	--s-bg: var(--s-surface-alt);
	--s-ink: var(--wp--preset--color--ink, #0ff);
	--s-ink-muted: var(--wp--custom--ink--muted, #ff0);
	--s-surface-card: var(--wp--custom--surface--card, transparent);
	--s-line-soft: var(--wp--custom--line--soft, transparent);
}

.is-tone-inverse {
	--s-bg: var(--s-surface-inverse);
	/* Biel wyglądałaby na decyzję i przeszłaby niezauważona — tak samo jak
	   czerń w tonie domyślnym. */
	--s-ink: var(--wp--custom--ink--on-inverse, #0ff);
	--s-ink-muted: var(--wp--custom--ink--on-inverse-muted, #ff0);
	--s-surface-card: var(--wp--custom--surface--card-on-inverse, transparent);
	--s-line-soft: var(--wp--custom--line--soft-on-inverse, transparent);
}

/* ---------------------------------------------------------------------------
 * 3. Struktura. Wszystko w :where().
 * ------------------------------------------------------------------------- */

:where(*, *::before, *::after) {
	box-sizing: border-box;
}

:where(body) {
	margin: 0;
	background: var(--s-bg);
	color: var(--s-ink);
	font-family: var(--s-font-body);
}

:where(img, picture, video, svg) {
	max-width: 100%;
	height: auto;
	display: block;
}

:where(h1, h2, h3, h4, h5, h6) {
	font-family: var(--s-font-head);
	margin: 0;
}

/*
 * 3a. Role typograficzne — konsumenci tokenów --s-type-<rola>-* z części 1.
 * Rola to element, nie stopień skali: h1 to „nagłówek główny" (tytuł hero
 * JEST h1 — blocks/src/hero/render.php woła partial z 'h1'; klasa
 * `.s-hero__title` stała tu obok do Zadania 6 i zniknęła razem z klasami
 * per blok partiala), h3 to „tytuł karty". Wszystkie cztery
 * własności przez token roli — literał byłby wartością marki
 * (noBrandValues w tooling/lint.mjs). Bez tokenu w theme.json deklaracja
 * jest unieważniona i element dziedziczy z body; zmierzone w
 * tooling/base-css.test.mjs („bez wartości h1 dziedziczy z body").
 *
 * Grubość STOI tutaj celowo: do rozszerzenia słownika ten plik nie miał
 * prawa jej ustawić (docs/tokens.md §2, „nie ma tokenu grubości"). Nadal
 * nie ma tokenu SKALI grubości; jest pole `weight` tokenu ROLI — grubość
 * jest częścią roli, nie wagą samą w sobie (korekta w docs/tokens.md §2).
 */
:where(h1) {
	font-size: var(--s-type-h1-size);
	font-weight: var(--s-type-h1-weight);
	line-height: var(--s-type-h1-lh);
	letter-spacing: var(--s-type-h1-ls);
}

:where(h2) {
	font-size: var(--s-type-h2-size);
	font-weight: var(--s-type-h2-weight);
	letter-spacing: var(--s-type-h2-ls);
}

:where(h3, .s-card__title) {
	font-size: var(--s-type-h3-size);
	font-weight: var(--s-type-h3-weight);
}

/*
 * Lead i eyebrow po klasie partiala `.s-section__lead` / `.s-section__eyebrow`
 * (blocks/parts/section-header.php emituje ją obok klasy per blok
 * `.s-<blok>__lead`; do Zadania 5 stał tu sufiks `[class$="__lead"]`, bo
 * klasy wspólnej nie było). Lead strony 404 (base-theme/404.php) nosi tę
 * samą klasę — to ten sam element, tylko poza blokiem.
 *
 * Lead ma DWA pola (rozmiar, interlinia): żadna z pięciu skór nie ustawiała
 * leadowi grubości ani światła (skin-inventory, rola `lead`). Do tego miara
 * wiersza --s-measure-lead: cztery skóry ograniczały szerokość leadu hero
 * w `ch` (pomiar §2.2). W nagłówku dzielonym kolumna leadu jest węższa niż
 * miara i to ona ogranicza — token nie przeszkadza. Kolor poboczny stał do
 * Zadania 5 w pięciu arkuszach bloków (`.s-<blok>__lead { color }`) — jedna
 * reguła tutaj zastępuje pięć kopii.
 */
:where(.s-section__lead) {
	color: var(--s-ink-muted);
	font-size: var(--s-type-lead-size);
	line-height: var(--s-type-lead-lh);
	max-width: var(--s-measure-lead);
}

/*
 * Wersaliki są KONTRAKTEM bazy, nie polem tokenu (pomiar §2.1, uwaga o
 * `text-transform`): 5/5 skór ma eyebrow wersalikami — cztery przez
 * spike-base, agencja przez skórę. Token roli ma cztery pola ze spec §3.1;
 * piąte pole `transform` byłoby polem, którego żadna strona nie wypełnia
 * inaczej niż `uppercase`. Ta sama reguła dla `.s-catalog__meta` stoi w
 * blocks/src/catalog/style.css (klasa bloku, nie nagłówka sekcji).
 *
 * Kolor przez --s-ink-eyebrow (`eyebrow.color` 5/5 w inwentarzu, pomiar
 * §2.5): trzy skóry chciały `--s-ink-muted`, dwie akcentu — token daje
 * każdej własną odpowiedź. Do Zadania 5 pięć arkuszy bloków pisało tu
 * `color: var(--s-ink-muted)`.
 */
:where(.s-section__eyebrow) {
	color: var(--s-ink-eyebrow);
	font-size: var(--s-type-eyebrow-size);
	font-weight: var(--s-type-eyebrow-weight);
	line-height: var(--s-type-eyebrow-lh);
	letter-spacing: var(--s-type-eyebrow-ls);
	text-transform: uppercase;
}

:where(p, ul, ol) {
	margin: 0;
}

:where(ul, ol) {
	padding: 0;
	list-style: none;
}

:where(a) {
	color: inherit;
}

/*
 * Kontrolki formularza NIE dziedziczą fontu z rodzica domyślnie — przeglądarka
 * daje im własny, mniejszy font kontrolki systemowej (Chromium: 13.3333px,
 * niezależnie od font-size ciała strony). Bez tego resetu każdy prawdziwy
 * `<button>`/`<input>`/`<textarea>`/`<select>` w repozytorium (pierwszy taki:
 * Zadanie 12, forms/inc/render.php, przycisk „Wyślij") pada na bramce
 * rozmiaru tekstu (docs/CONVENTIONS.md §9, próg 14px) — nie dlatego, że jego
 * własny CSS ustawia coś złego, tylko dlatego, że nic nie ustawia fontu wcale.
 * Zmierzone tu, na tooling/gates.mjs: `mobile.textSize` na 390px zgłaszało
 * `button.s-btn… 13.3333px "Wyślij"`, mimo że forms/css/form.css nigdzie nie
 * definiuje font-size.
 */
:where(button, input, select, textarea) {
	/* Longhandy, NIE skrót `font:` — `noBrandValues` w tooling/lint.mjs
	   zabrania skrótu `font:` w base.css bezwarunkowo (jest wektorem
	   przemycenia font-weight, docs/tokens.md §Typografia), niezależnie od
	   tego, że tu niesie wyłącznie `inherit`. */
	font-family: inherit;
	font-size: inherit;
	line-height: inherit;
	color: inherit;
	/* `color: inherit` samo nie wystarczy: przeglądarka daje kontrolce
	   WŁASNE domyślne tło (np. jasnoszare "ButtonFace"), niezależne od tekstu.
	   Bez zresetowania tła tekst odziedziczony po --s-ink (jasny w tonie
	   ciemnym) ląduje na jasnym tle kontrolki — axe: color-contrast (serious),
	   zmierzone tu na tooling/gates.mjs (fixture bez skin.css, więc
	   .s-btn--primary nie ma WŁASNEGO tła i dopiero wtedy widać default UA).
	   transparent pokazuje powierzchnię, na której kontrolka faktycznie stoi,
	   spójną z kolorem tekstu odziedziczonym z tej samej powierzchni. */
	background-color: transparent;
}

/*
 * Szerokość treści czytamy z --wp--style--global--wide-size, bo wideSize i
 * contentSize są ustawione raz, w base-theme/theme.json, i nie są tokenem
 * strony (docs/tokens.md §5). To jedyne miejsce poza mapowaniem i tonami,
 * w którym base.css sięga po warstwę źródeł — i jedyne, na które słownik
 * pozwala wprost.
 *
 * 16px to margines boczny, a nie token: słownik jest zamknięty i nie ma w nim
 * ani odstępu bocznego, ani odstępu siatki. Liczba stoi tutaj, w jednym
 * miejscu, zamiast udawać token, którego nikt nie zadeklarował.
 *
 * CZTERY osobne deklaracje, nie jedno min(). Stało tu wcześniej
 * `width: min(100% - 32px, var(--wp--style--global--wide-size, 1200px))`
 * i miało dwie wady. Skóra chcąca innego marginesu bocznego musiała przepisać
 * całe wyrażenie — razem z odczytem warstwy źródeł — a duplikat rozjeżdżał się
 * z bazą po cichu przy pierwszej zmianie wideSize. Do tego wartość zapasowa
 * 1200px odpalała się dokładnie w tej awarii, którą miała uwidocznić, i
 * podstawiała poprawnie wyglądającą stronę. Bez niej brak zmiennej unieważnia
 * samo max-width (czyli kontener idzie na pełną szerokość — widać), a margines
 * i tak zostaje. Teraz margines i gap siatki są jednakowo tanie do nadpisania
 * jedną linią w skórze.
 */
:where(.s-container) {
	width: 100%;
	max-width: var(--wp--style--global--wide-size);
	padding-inline: 16px;
	margin-inline: auto;
}

/* Tło i kolor tekstu na sekcji, nie tylko na body — inaczej klasa tonu
   przemapowałaby tokeny, których nikt na tej sekcji nie czyta, i ton nie
   zmieniłby niczego widocznego (docs/tokens.md §2). */
:where(.s-section) {
	background: var(--s-bg);
	color: var(--s-ink);
	padding-block: var(--s-space-section);
}

/*
 * Jedyny konsument --s-grad-hero. docs/tokens.md §2 mówi „base.css: .s-hero
 * (background-image)" od Zadania 5 — ale do Zadania 15 żadna reguła w tym
 * repozytorium tego tokenu nie czytała (tokens-raport Zadania 14, §5: „nie ma
 * dziś w repozytorium żadnego konsumenta"). Strona wypełniała gradient w
 * theme.json, WordPress emitował zmienną, i nic jej nie renderowało — token
 * martwy bez jednego komunikatu. `background-image` longhandem, PO regule
 * .s-section wyżej, która skrótem `background` ustawia tło sekcji i kasuje
 * wszystkie warstwy obrazu: ta reguła dokłada gradient nad kolor tonu.
 * Wartość zapasowa `none` (część 1) sprawia, że strona bez gradientu niczego
 * nie traci.
 */
:where(.s-hero) {
	background-image: var(--s-grad-hero);
}

/*
 * Nagłówek sekcji — KONTRAKT bazy od Zadania 5 rozszerzenia słownika
 * (`section.header.layout` 3/5 w tooling/skin-inventory.json; pomiar
 * 2026-09-15-slownik-pomiar.md §2.4). Do tego zadania każdy z pięciu bloków
 * niósł własną kopię tej reguły (`.s-features__header { display: flex;
 * flex-direction: column; gap: 12px; margin-block-end: 32px }` i cztery
 * identyczne), a układ dzielony pisała skóra agencji na dwóch klasach bloków.
 * Partial blocks/parts/section-header.php emitował klasę `.s-section__header`
 * OBOK klasy per blok (`.s-features__header`) do Zadania 6, które zdjęło
 * klasy per blok — skóra agencji czyta od tego zadania wyłącznie klasy bazy
 * (`.s-section__header.is-split`, `.s-hero .s-section__title`).
 *
 * Trzy układy (pole `header_layout`): `stack` (kolumna, domyślny), `center`
 * (planweb i kanwa `.s-sectionhead { text-align: center }`, agencja w cenniku
 * — 3/5) i `split` (agencja w cechach i realizacjach — 1/5; oba zostają jako
 * wzorce standardowe, decyzja kontrolera po recenzji Zadania 5).
 *
 * Wartości strukturalne (odstępy) tak jak 16px marginesu .s-container i
 * 24px gap siatki — ŻADNA z pięciu skór ich nie ustawia, to struktura obu
 * baz: `gap: 12px` = odstęp eyebrow/tytuł/lead w spike-base
 * (`.s-sectionhead__eyebrow, __title { margin: 0 0 12px }`) i w pięciu
 * arkuszach bloków tego repozytorium; `margin-block-end: 32px` z czterech
 * arkuszy bloków tego repozytorium (spike-base miało 40px — zostaje 32,
 * bo na tej wartości zmierzono zrzuty „przed"); `column-gap: 24px` w
 * wariancie dzielonym = gap siatki bazy (`.s-grid`, ten sam w spike-base),
 * bo 32px, które stało tu po pierwszej fali, było wartością jednej skóry
 * (agencja). Szerokość kolumny leadu to `--s-measure-lead`: szerokość leadu
 * JEST szerokością jego kolumny (decyzja kontrolera); 340px agencji z
 * pierwszej fali było wartością jednej skóry i zostaje w jej skórze jako
 * odstępstwo (Zadanie 6). Bez tokenu `grid-template-columns` jest
 * unieważnione i nagłówek dzielony spada do jednej kolumny — brak tokenu
 * widać. Hero nadpisuje `margin-block-end` na 0 w swoim arkuszu: jego ciało
 * jest kolumną flex z własnym gap (blocks/src/hero/style.css).
 */
:where(.s-section__header) {
	display: flex;
	flex-direction: column;
	gap: 12px;
	margin-block-end: 32px;
}

/* Wariant wyśrodkowany: elementy i tekst do środka (nazwa klasy jak w hero,
   `.s-hero.is-centered`). */
:where(.s-section__header.is-centered) {
	align-items: center;
	text-align: center;
}

/* Wariant dzielony: tytuł z lewej, lead z prawej, oba do dolnej krawędzi
   (`align-items: end` — pudełka wiersza NIE są rozciągane; zmierzone w
   tooling/base-css.test.mjs, także na prostokątach: lead zaczyna się niżej
   niż tytuł); eyebrow nad obiema kolumnami (skóra agencji pisała to na
   `.s-catalog__eyebrow { grid-column: 1 / -1 }` — pomiar §2.4 liczy to do
   układu, nie do pigułki). Pod progiem 40rem wraca do jednej kolumny —
   reguła w części 4, jedynym miejscu z punktami załamania. */
:where(.s-section__header.is-split) {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, var(--s-measure-lead));
	column-gap: 24px;
	align-items: end;
	justify-items: start;
}

:where(.s-section__header.is-split .s-section__eyebrow) {
	grid-column: 1 / -1;
}

/* Wariant „obok” (spec florencja-tresci §6, D14): nagłówek w lewej kolumnie,
   PIERWSZY element po nim (siatka kart, logotypów) w prawej, w tym samym
   rzędzie, wyśrodkowane w pionie; każdy dalszy element (link „zobacz
   wszystkie” agency/products) pod spodem na pełną szerokość. Układ siedzi na
   KONTENERZE (`:has()`, precedens: blocks/src/showcase/style.css), więc działa
   w każdym bloku, który woła agency_section_header() w `.s-container` — bez
   zmiany render.php. Proporcja 1 : 3 i odstęp 24px to struktura jak `gap`
   siatki, nie token: kolumna nagłówka nie jest miarą leadu (lead dalej ma
   `max-width: var(--s-measure-lead)`), a `minmax(0, var(--s-measure-lead))`
   zwinęłoby kolumnę na stronie z `measure.lead: none` (docs/tokens.md §2,
   pułapka 2). `row-gap` = odstęp pod nagłówkiem sekcji. Pod 64rem — część 4. */
:where(.s-container:has(> .s-section__header.is-beside)) {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 3fr);
	column-gap: 24px;
	row-gap: 32px;
	align-items: center;
}

:where(.s-section__header.is-beside) {
	grid-column: 1;
	grid-row: 1;
}

/* Odstęp pod nagłówkiem znika tylko razem z siatką: przeglądarka bez `:has()`
   zostawia kontener blokiem i nagłówek bez odstępu przykleiłby się do treści. */
:where(.s-container:has(> .s-section__header.is-beside) > .s-section__header.is-beside) {
	margin-block-end: 0;
}

:where(.s-section__header.is-beside + *) {
	grid-column: 2;
	grid-row: 1;
}

:where(.s-section__header.is-beside ~ * ~ *) {
	grid-column: 1 / -1;
}

/* Spis treści (base-theme/inc/toc.php, spec treści bazy §3): wpis
   (kolumna boczna) i agency/tekst-prawny (`is-grid` — kafle spisu na całą
   szerokość arkusza). Struktura bazy; krój numerów i kolory pozycji w skórze. */
:where(.s-toc__title) {
	margin: 0 0 12px;
	color: var(--s-ink-eyebrow);
	font-size: var(--s-type-eyebrow-size);
	font-weight: var(--s-type-eyebrow-weight);
	letter-spacing: var(--s-type-eyebrow-ls);
}

:where(.s-toc__list) {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
}

:where(.s-toc.is-grid .s-toc__list) {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
	gap: 8px 24px;
}

:where(.s-toc__link) {
	display: flex;
	gap: 8px;
	color: inherit;
	text-decoration: none;
}

:where(.s-toc__num) {
	flex: none;
	color: var(--s-ink-muted);
}

:where(.visually-hidden) {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

:where(.s-grid) {
	display: grid;
	gap: 24px;
	grid-template-columns: repeat(var(--s-cols, 3), minmax(0, 1fr));
}

:where(.is-cols-2) { --s-cols: 2; }
:where(.is-cols-3) { --s-cols: 3; }
:where(.is-cols-4) { --s-cols: 4; }

/* Wysokość przyklejonego nagłówka (docs/tokens.md §2 „Tokeny strukturalne”):
   o tyle kotwice i spis treści schodzą pod pasek. Bez przyklejonego
   nagłówka 0 — wariant `kategorie` ustawia go w css/chrome/header-kategorie.css,
   skóra z innym paskiem nadpisuje tym samym selektorem. Osobna reguła :root
   (nie pierwszy blok) — token strukturalny, bez źródła w theme.json. */
:root {
	--s-header-offset: 0px;
}

/*
 * Zaczepy dla js/base.js. Obie funkcje są ODEJMUJĄCE: strona bez JavaScriptu
 * zostaje czytelna i użyteczna, a nie pusta.
 *
 * Przycisk menu bez JS-a nic nie robi, więc bez JS-a go nie ma. Samo menu
 * zostaje widoczne — to jest ta różnica między „odejmujące” a „dodające”.
 */
:where(html:not(.is-js)) :where([data-menu-toggle]) {
	display: none;
}

/*
 * Reveal ukrywa element dopiero wtedy, gdy jest kto go pokaże: klasę
 * is-reveal-ready dokłada base.js i tylko wtedy, gdy naprawdę uruchamia
 * obserwatora. Bez JS-a, bez IntersectionObserver albo przy włączonym
 * ograniczeniu ruchu klasy nie ma i [data-reveal] jest po prostu widoczne.
 */
:where(html.is-reveal-ready) :where([data-reveal]) {
	opacity: 0;
	transform: translateY(12px);
}

/* Przejście siedzi na stanie DOCELOWYM, nie na wyjściowym: skrypt stoi w
   stopce, więc ukrycie zdarza się już po pierwszym malowaniu i z przejściem na
   stanie wyjściowym element widocznie gasł przez 400 ms (zmierzone). Tak
   ukrycie jest natychmiastowe, a animuje się tylko pokazanie. */
:where(html.is-reveal-ready) :where([data-reveal].is-visible) {
	opacity: 1;
	transform: none;
	transition: opacity 400ms ease, transform 400ms ease;
}

/* ---------------------------------------------------------------------------
 * 3b. Nagłówek, stopka, karta, przycisk. Zadanie 6. Wszystko w :where(), zero
 * kolorów i rozmiarów marki — tylko tokeny semantyczne i wartości strukturalne
 * (odstępy, siatka), tak jak 16px marginesu .s-container wyżej.
 *
 * .s-btn niesie kształt (--s-radius-btn), minimalny cel dotyku, ROZMIAR z
 * tokenów `custom.btn.{height, paddingInline}` (Zadanie 5; `btn.size` 5/5
 * skór — każda nadawała przyciskowi własną parę) i typografię roli `btn`
 * (rozmiar 4/5 i grubość 5/5 skór — bez interlinii, której żadna nie
 * ustawiała, i bez światła, które ustawiała jedna: pole wchodzi przy ≥3/5,
 * jak rola).
 *
 * `min-height: 44px` ZOSTAJE obok `height: var(--s-btn-height)` — to dwie
 * różne rzeczy. 44px to cel dotyku (docs/CONVENTIONS.md, Dodatek „próg celu
 * dotyku"), struktura tak samo jak `min-height: 44px` na .s-badge, na
 * linkach menu i stopki, na polu formularza — pomiar §2.3 (`pill.size`)
 * rozstrzygnął dla pigułki, że baza „już to niesie" i token nie dodaje nic;
 * dla przycisku ta sama liczba jest tym samym minimum. `height` to rozmiar
 * marki: token 54px daje 54px, token 36px daje 44px (min-height wygrywa —
 * baza nie produkuje przycisku poniżej celu dotyku; zmierzone w
 * tooling/base-css.test.mjs), brak tokenu daje `auto`, czyli 44px jak dotąd.
 * Alternatywa `min-height: var(--s-btn-height)` bez literału oddałaby
 * minimum dotyku stronie bez tokenu (link .s-btn miałby ~20px) i łapałaby to
 * dopiero bramka mobilna — a każdy inny cel dotyku w tym pliku jest literałem.
 *
 * `padding-inline` bez literału: 20px, które tu stało, było wartością marki
 * (pięć skór, pięć różnych par) — bez tokenu przycisk nie ma światła po
 * bokach i strona to widzi.
 *
 * Kolor przycisku (.s-btn--primary / .s-btn--secondary) — od rozszerzenia
 * słownika (Zadanie 4) KONTRAKT bazy z istniejących tokenów. Stało tu
 * wcześniej: „baza go nie definiuje, inaczej motyw bazowy sam byłby marką".
 * Pomiar na pięciu skórach (tooling/skin-inventory.json, `btn.primary` 5/5,
 * `btn.secondary` 5/5; docs/superpowers/specs/2026-09-15-slownik-pomiar.md
 * §2.3) pokazał, że każda z pięciu pisała u siebie te same dwa tokeny —
 * czyli to nie była marka, tylko dwie linie kosztu stałego na stronę, a
 * `--s-ink-on-accent` i `--s-line-strong` nie miały w bazie żadnego
 * konsumenta (noUnusedTokens, Zadanie 3). Skóra, która chce inaczej
 * (agencja: biały przycisk w hero, szklany wtórny), nadpisuje kolejnością
 * — te reguły są w :where(), jak wszystko w tej części.
 * ------------------------------------------------------------------------- */

:where(.s-btn) {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 44px;
	height: var(--s-btn-height);
	padding-inline: var(--s-btn-padding-inline);
	border: none;
	border-radius: var(--s-radius-btn);
	font-family: var(--s-font-body);
	font-size: var(--s-type-btn-size);
	font-weight: var(--s-type-btn-weight);
	text-decoration: none;
	cursor: pointer;
}

:where(.s-btn--primary) {
	background: var(--s-accent);
	color: var(--s-ink-on-accent);
}

/* `background`, nie `background-color`: reguła stoi PO resecie kontrolek
   (`:where(button, …) { background-color: transparent }`) o tej samej
   specyficzności zero, więc wygrywa kolejnością — a skrót kasuje też każdą
   warstwę obrazu, której przycisk mieć nie ma. Obramowanie 1px: 4/5 skór
   pisało dokładnie `1px solid var(--s-line-strong)`. */
:where(.s-btn--secondary) {
	background: var(--s-surface-card);
	border: 1px solid var(--s-line-strong);
}

/*
 * Skip link jest .visually-hidden (jak każdy tekst tylko dla czytnika), ale
 * będąc jedynym elementem tej klasy, który może dostać ognisko klawiatury,
 * musi się na czas ogniska pokazać — inaczej użytkownik klawiatury dostaje
 * ognisko na czymś, czego nie widzi (WCAG 2.4.7). base.css z Zadania 5 nie
 * miał tego przypadku, bo .visually-hidden dotąd nie stało na niczym
 * klikalnym/fokusowalnym.
 */
:where(.visually-hidden:focus) {
	position: fixed;
	top: 8px;
	left: 8px;
	width: auto;
	height: auto;
	overflow: visible;
	clip-path: none;
	white-space: normal;
	padding: 8px 16px;
	background: var(--s-bg);
	color: var(--s-ink);
	border: 1px solid var(--s-line-soft);
	border-radius: var(--s-radius-btn);
	z-index: 100;
}

:where(.s-header) {
	background: var(--s-bg);
	color: var(--s-ink);
	border-bottom: 1px solid var(--s-line-soft);
}

:where(.s-header.is-sticky) {
	position: sticky;
	top: 0;
	z-index: 40;
}

:where(.s-header.is-transparent) {
	background: transparent;
	border-bottom-color: transparent;
}

:where(.s-header__inner) {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding-block: 12px;
}

:where(.s-header__brand) {
	display: flex;
	align-items: center;
}

:where(.s-header__name) {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	font-family: var(--s-font-head);
	text-decoration: none;
}

/* Burger widoczny wyłącznie pod najwęższym progiem (część 4) — tu tylko
   kształt i cel dotyku 44×44, domyślnie schowany. */
:where(.s-header__toggle) {
	display: none;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border: none;
	background: transparent;
	color: inherit;
	cursor: pointer;
}

:where(.s-header__burger) {
	position: relative;
	display: block;
	width: 20px;
	height: 2px;
	background: currentColor;
}

:where(.s-header__burger)::before,
:where(.s-header__burger)::after {
	content: "";
	position: absolute;
	left: 0;
	width: 100%;
	height: 2px;
	background: currentColor;
}

:where(.s-header__burger)::before {
	top: -6px;
}

:where(.s-header__burger)::after {
	top: 6px;
}

/*
 * Menu domyślnie WIDOCZNE na każdej szerokości — to jest ta odejmująca
 * zasada z base.js (część wprowadzenia w js/base.js): dopiero pod
 * najwęższym progiem i tylko wtedy, gdy html ma .is-js (czyli skrypt
 * naprawdę wystartował), część 4 chowa je za przyciskiem. Strona bez
 * JavaScriptu ma menu rozwinięte na każdej szerokości.
 */
:where(.s-header__nav) {
	display: block;
}

:where(.s-header__menu) {
	display: flex;
	align-items: center;
	gap: 24px;
}

:where(.s-header__menu a) {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	text-decoration: none;
}

:where(.s-footer) {
	background: var(--s-surface-alt);
	color: var(--s-ink);
	border-top: 1px solid var(--s-line-soft);
}

:where(.s-footer__inner) {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 32px;
	padding-block: var(--s-space-section);
}

:where(.s-footer__name) {
	font-family: var(--s-font-head);
}

:where(.s-footer__desc) {
	color: var(--s-ink-muted);
}

:where(.s-footer__menu) {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

:where(.s-footer__contact) {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

:where(.s-footer__menu a),
:where(.s-footer__contact a) {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	text-decoration: none;
}

:where(.s-footer__bottom) {
	border-top: 1px solid var(--s-line-soft);
	padding-block: 16px;
	color: var(--s-ink-muted);
}

:where(.s-card) {
	display: flex;
	flex-direction: column;
	gap: 12px;
	background: var(--s-surface-card);
	border: 1px solid var(--s-line-soft);
	border-radius: var(--s-radius-card);
	box-shadow: var(--s-shadow-card);
	padding: var(--s-space-card);
}

:where(.s-card__media) {
	display: block;
	border-radius: var(--s-radius-card);
	overflow: hidden;
}

:where(.s-card__title) {
	font-family: var(--s-font-head);
}

:where(.s-card__title a) {
	text-decoration: none;
}

:where(.s-card__excerpt) {
	color: var(--s-ink-muted);
}

/*
 * .s-card__price — cena wpisana jako tekst, nie jako liczba do obliczeń
 * (Zadanie 9, brief bloku catalog): wycena bywa treścią bez liczby w ogóle,
 * nie tylko kwotą. Nazwa bezprefiksowa jak resztę .s-card__*, bo dwa bloki
 * jej potrzebują niezależnie: pozycja katalogu (blocks/src/catalog) i pakiet
 * cennika (blocks/src/pricing) — obie karty to ten sam .s-card.
 *
 * Bez reguły tutaj celowo: cena NIE jest rolą słownika — `price` ma 1/5 w
 * tooling/skin-inventory.json (spike nie ma cennika), więc nie dostała tokenu
 * `custom.type.price` mimo że spec §3.1 ją wymieniała (pomiar
 * 2026-09-15-slownik-pomiar.md §3). Literał grubości jest wartością marki
 * (docs/tokens.md §2, „Typografia"), a ten plik deklaruje zero wartości
 * marki. Sama klasa w markupie jest wystarczającym zaczepem dla skóry —
 * strona, która chce pogrubić cenę, pisze `.s-card__price { font-weight: … }`
 * u siebie, dopóki druga pełna strona nie przeprowadzi tej roli przez próg.
 */

/*
 * .s-badge — wyróżnik w pigułce. Bezprefiksowa nazwa (bez `s-hero__`) to
 * sygnatura współdzielonego prymitywu (docs/CONVENTIONS.md §1), dokładnie jak
 * .s-card i .s-btn wyżej — dlatego mieszka tutaj, nie w
 * blocks/src/hero/style.css, choć dziś jedynym konsumentem jest hero.
 * docs/tokens.md §2 już zakładał, że .s-badge czyta base.css; przeniesienie
 * (poprawka po przeglądzie Zadania 8) czyni to zdanie prawdziwym.
 */
:where(.s-badge) {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding-inline: 16px;
	background: var(--s-surface-card);
	border: 1px solid var(--s-line-soft);
	border-radius: var(--s-radius-pill);
	/* Cień własny pigułki (`pill.shadow` 3/5, pomiar §2.3): planweb i
	   pewneumowy pisały `var(--s-shadow-card)`, agencja literał, kanwa i
	   axiom nie chcą cienia (obramowanie). Kontrakt „cień pigułki = cień
	   karty" zmieniłby dwie skóry; token daje każdej własną odpowiedź, w
	   tym `none`. Bez tokenu — brak cienia, jak dotąd (Zadanie 5). */
	box-shadow: var(--s-shadow-badge);
	/* Rola `badge` (pomiar §2.1, wiersz `pill`): rozmiar 5/5 i grubość 4/5
	   skór; interlinii ani światła żadna nie ustawiała — dwa pola, nie cztery.
	   Nazwa tokenu po klasie prymitywu (.s-badge), nie po roli `pill` z
	   tabeli inwentarza. */
	font-size: var(--s-type-badge-size);
	font-weight: var(--s-type-badge-weight);
}

/* ---------------------------------------------------------------------------
 * 3c. Formularz. PRZENIESIONE z forms/css/form.css w Zadaniu 13
 * (rozstrzygnięcie kontrolera 1 briefu Zadania 13; krok 3 briefu, który każe
 * DOPISAĆ te klasy tutaj, był w tej jednej rzeczy nieaktualny — dopisanie
 * zdublowałoby to, co Zadanie 12 już napisało w forms/css/form.css).
 *
 * Uzasadnienie przenosin, nie duplikacji: `.s-form__*` to nazwa BEZPREFIKSOWA
 * (jak `s-card`, `s-btn`, `s-badge` wyżej) — docs/CONVENTIONS.md §1 mówi, że
 * to jest sygnatura prymitywu WSPÓLNEGO, nie stylu jednego bloku ani jednej
 * wtyczki. Ta sama reguła została w tym repozytorium wyegzekwowana już raz, w
 * Zadaniu 8/9, na `.s-badge` — siedział w style.css bloku hero i został
 * przeniesiony tutaj z dokładnie tym samym uzasadnieniem. Formularz nie jest
 * blokiem Gutenberga (forms/inc/render.php: osadza się shortcode'em), ale to
 * nie czyni jego wyglądu WŁASNOŚCIĄ wtyczki — `.s-form`, pole, etykieta,
 * komunikat błędu wyglądałyby identycznie na dowolnym formularzu HTML tego
 * motywu, wtyczkowym czy nie.
 *
 * Co ZOSTAJE w forms/css/form.css: WYŁĄCZNIE reguła ukrywająca pole-pułapkę
 * (`.s-form__honeypot`) — to jest naprawdę specyficzne dla MECHANIZMU wtyczki:
 * pole-pułapka istnieje tylko dlatego, że forms/inc/render.php je renderuje
 * jako część zabezpieczenia przed botami, i blok bez wtyczki formularzy nie ma
 * tego pola, więc nie ma czego tam ukrywać. Reszta prymitywów wizualnych tej
 * własności nie ma.
 *
 * SKUTEK DLA KASKADY (zmierzony, nie wydedukowany — pełny pomiar w
 * .superpowers/sdd/task-13-report.md): przenosiny OBNIŻAJĄ pozycję tych reguł
 * z priorytetu 15 (forms/inc/render.php, agency_forms_enqueue_assets(),
 * MIĘDZY bazą a arkuszami bloków) na priorytet 10 (baza sama). Skóra strony
 * (priorytet 30) dalej wygrywa kolejnością — była i jest NIŻEJ w kaskadzie niż
 * baza. Blok warstwy `agency/*` (priorytet 20) teraz też może nadpisać te
 * reguły, czego wcześniej NIE mógł (forms.css ładował się PO blokach) — dla
 * `.s-form__*` żaden blok tego nie robi, więc to jest zmiana możliwości, nie
 * zaobserwowana regresja. Dwie bramki, które Zadanie 12 zamknęło na tym CSS-ie
 * (kontrast komunikatu błędu, rozmiar tekstu w <label>/komunikacie/zgodzie),
 * mierzą WYNIKOWY `getComputedStyle` na wyrenderowanej stronie, nie źródło
 * reguły — przenosiny same z siebie nie zmieniają żadnej WARTOŚCI (te same
 * tokeny, te same deklaracje), tylko to, KTO mógłby je przebić. Zmierzone
 * `npm run gates` PO przenosinach: obie bramki PASS (raport zadania).
 * ------------------------------------------------------------------------- */

:where(.s-form) {
	display: flex;
	flex-direction: column;
	gap: 20px;
	max-width: 40rem;
}

:where(.s-form__title) {
	font-family: var(--s-font-head);
	color: var(--s-ink);
}

:where(.s-form__field) {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

:where(.s-form__label) {
	color: var(--s-ink);
	font-family: var(--s-font-body);
}

:where(.s-form__field--consent .s-form__label) {
	display: flex;
	align-items: center;
	gap: 8px;
	/* Cel dotyku pola zgody to CAŁA etykieta, nie kwadracik 20×20 (W2,
	   recenzja Zadania 13, rozstrzygnięcie kontrolera). Zmierzone przed
	   poprawką: etykieta 38px na 390px i 25px na 1440px — poniżej progu
	   44px (docs/CONVENTIONS.md, Dodatek „próg celu dotyku"); nietrafienie
	   kciukiem jest ciche, a formularz bez zgody nie przechodzi walidacji.
	   Kwadracik zostaje 20×20 (niżej), rośnie obszar trafienia; `center`
	   zamiast `flex-start`, żeby kwadracik siedział w środku 44px, a nie
	   przy górnej krawędzi z pustką pod spodem. */
	min-height: 44px;
}

:where(.s-form__input) {
	font-family: var(--s-font-body);
	color: var(--s-ink);
	background-color: var(--s-surface-card);
	border: 1px solid var(--s-line-soft);
	border-radius: var(--s-radius-btn);
	padding: 10px 12px;
	/* Cel dotyku pola ≥ 44px (Zadanie 13, decyzja kontrolera 2 — próg NIE
	   obniżony, brief: bramka mobilna mierzy `a`/`button`, ale użytkownik
	   mierzy palcem także `input`). */
	min-height: 44px;
}

:where(textarea.s-form__input) {
	min-height: 132px;
	resize: vertical;
}

:where(.s-form__field--consent .s-form__input) {
	min-height: 0;
	width: 20px;
	height: 20px;
	flex: none;
}

/*
 * Brak tokenu koloru „błędu"/„ostrzeżenia" w słowniku (docs/tokens.md §2 go
 * nie ma i słownik jest zamknięty — nie wolno go tu dopisywać przy okazji).
 * `.s-form__error` i `.s-form__notice` czytają --s-accent: to jedyny token
 * koloru w słowniku przeznaczony do przyciągania uwagi, więc reużywamy jego
 * ROLI zamiast wymyślać nowy token dla jednego komponentu.
 */
:where(.s-form__error) {
	color: var(--s-accent);
}

:where(.s-form__error:empty) {
	display: none;
}

:where(.s-form__notice) {
	color: var(--s-ink);
	background-color: var(--s-surface-alt);
	border: 1px solid var(--s-line-soft);
	border-radius: var(--s-radius-card);
	padding: 12px 16px;
}

:where(.s-form__submit) {
	align-self: flex-start;
}

/*
 * Lista wyboru: natywna strzałka przeglądarki stoi tam, gdzie postawi ją
 * system (Chrome na macOS dociska ją do krawędzi, `padding` jej nie dotyczy),
 * więc jest wyłączona, a chevron z biblioteki ikon rysuje `::after` opakowania
 * `.s-form__select` (forms/inc/render.php) — `<select>` pseudoelementów nie
 * ma. Kolor: currentColor opakowania, skóra zmienia go jak każdy tekst.
 */
:where(.s-form__select) {
	position: relative;
	display: block;
}

:where(.s-form__select)::after {
	content: "";
	position: absolute;
	top: 50%;
	right: 14px;
	width: 16px;
	height: 16px;
	translate: 0 -50%;
	background: currentColor;
	mask: var(--s-icon-chevron) center / contain no-repeat;
	pointer-events: none;
}

:where(select.s-form__input) {
	appearance: none;
	width: 100%;
	padding-inline-end: 42px;
}

/* ---------------------------------------------------------------------------
 * 3d. Ikony (docs/CONVENTIONS.md §9). Ikona zawsze SVG z biblioteki
 * (agency_svg(), base-theme/inc/icons.php), nigdy znak fontu — dwie klasy
 * mówią, jak stoi obok tekstu:
 *
 * .s-ico-txt — ikona jest elementem flex obok tekstu (przycisk, link
 *   `inline-flex`). Flex centruje ramkę linii, a litery fontu leżą w niej
 *   o --s-text-sink niżej — ikona schodzi o tyle samo. `translate`, nie
 *   `transform`: skóra animuje strzałkę transformem przy najechaniu, a te
 *   dwie właściwości się składają, nie nadpisują.
 * .s-ico-inline — ikona płynie w tekście (link `inline`/`inline-block`).
 *   Środek na optycznym środku liter, tym samym co .s-ico-txt: połowa drogi
 *   między środkiem wielkich i małych, `(1cap + 1ex) / 4` nad linią bazową —
 *   jednostki fontu rodzica, więc bez tokenu i dla każdego fontu
 *   (`vertical-align: middle` przed nią to zapas dla przeglądarki bez `cap`:
 *   środek małych liter). Rozmiar 1.25em: rysunek strzałki ma wtedy szerokość
 *   znaku „→”, który zastępuje (0,75 em, zmierzone). Ujemne marginesy:
 *   pionowe — pudełko ikony w wierszu ma 0,55 em, więc ikona nigdy nie
 *   podnosi wysokości wiersza (bez nich karta z „Zobacz →” rosła o 1 px);
 *   poziome — ikona zajmuje w wierszu 0,93 em, dokładnie tyle co znak „→”,
 *   więc nic się nie przelewa (bez nich link „Odświeżenie Twojej strony →”
 *   był o 4,4 px szerszy i tekst obok zawinął się o wiersz więcej —
 *   zmierzone na zrzutach produkcji, projekt-graficzny-strony, 390 px). `vertical-align` długością liczy od dolnej
 *   krawędzi marginesu (0,35 em nad dolną krawędzią ikony), więc środek =
 *   L − 0.35em + 0.625em, czyli L = (1cap + 1ex) / 4 − 0.275em.
 * ------------------------------------------------------------------------- */

:where(.s-ico-txt) {
	flex: none;
	translate: 0 var(--s-text-sink);
}

:where(.s-ico-inline) {
	display: inline-block;
	width: 1.25em;
	height: 1.25em;
	margin: -0.35em -0.16em;
	vertical-align: middle;
	vertical-align: calc((1cap + 1ex) / 4 - 0.275em);
}

/* ---------------------------------------------------------------------------
 * 4. Punkty załamania — jedyne w całym repozytorium.
 *
 * Jedna drabina, dwa progi, w rem — nie w px — żeby powiększona domyślna
 * czcionka w przeglądarce przełączała układ razem z tekstem:
 *
 *   64rem (1024px): koniec układu szerokiego. Cztery i trzy kolumny kart nie
 *                   mieszczą się na tablecie w poziomie, więc schodzą do dwóch.
 *   40rem  (640px): najwęższy próg. Wszystko schodzi do jednej kolumny.
 *
 * Dlaczego akurat te dwa, a nie jeden: między czterema kolumnami a jedną jest
 * cały zakres tabletu, którego skok 4 → 1 nie obsługuje. Dlaczego nie trzy:
 * każdy próg to zakres szerokości, który ktoś musi obejrzeć, a bramki oglądają
 * dwie szerokości — 390px (wewnątrz progu 40rem) i 1440px (powyżej obu).
 * Trzeci próg byłby zakresem, którego nikt nie sprawdza.
 *
 * Progi są tutaj i tylko tutaj. sites/<klient>/skin.css podaje wartości dla
 * tych progów i nie wprowadza własnych — dwa niezależne progi w dwóch plikach
 * dają zakres, którego nikt nie zaprojektował (krok 0: 780px w bazie i 480px
 * w skórze).
 *
 * UWAGA na kolejność: te reguły mają tę samą specyficzność (zero) co klasy
 * .is-cols-*, więc wygrywają wyłącznie tym, że stoją niżej w pliku. Reguła
 * dopisana PO tym bloku i ustawiająca --s-cols cofnie zwijanie siatki i nie
 * zgłosi tego niczym. Nowe reguły idą przed część 4, nie za nią.
 * ------------------------------------------------------------------------- */

@media (max-width: 64rem) {
	:where(.s-grid) {
		--s-cols: 2;
	}

	/* Nagłówek „obok” (część 3) wraca nad treść: kontener z powrotem blokiem,
	   odstęp pod nagłówkiem jak w każdym innym układzie. */
	:where(.s-container:has(> .s-section__header.is-beside)) {
		display: block;
	}

	:where(.s-section__header.is-beside) {
		margin-block-end: 32px;
	}
}

@media (max-width: 40rem) {
	:where(.s-grid) {
		--s-cols: 1;
	}

	/* Stopka: trzy kolumny nie mieszczą się czytelnie na 390px, tak samo
	   jak siatka kart. */
	:where(.s-footer__inner) {
		grid-template-columns: 1fr;
	}

	/* Nagłówek sekcji dzielony (część 3, `.s-section__header.is-split`)
	   wraca do jednej kolumny: tytuł nad leadem, jak w wariancie domyślnym.
	   Skóra agencji pisała ten sam próg u siebie
	   (`.s-features__header, .s-catalog__header { grid-template-columns:
	   minmax(0, 1fr) }`) — pomiar §2.4 liczy go do wariantu, nie do skóry. */
	:where(.s-section__header.is-split) {
		grid-template-columns: minmax(0, 1fr);
	}

	/*
	 * Menu chowa się TYLKO gdy html.is-js — bez tej klasy (skrypt zablokowany
	 * albo nie zdążył wystartować) reguła nie dotyczy elementu i menu z
	 * części 3b zostaje widoczne. Przycisk-burger analogicznie: domyślnie
	 * (część 3b) ukryty na każdej szerokości, tu odsłaniany wyłącznie razem
	 * z .is-js, na jedynym progu, na którym ma sens.
	 *
	 * `:not([hidden])` — bo base.js chowa atrybutem `hidden` przycisk, którego
	 * `aria-controls` nie trafia w żadne menu, a każda reguła autora nadająca
	 * `display` pokonuje `display: none` z arkusza przeglądarki, także ta o
	 * specyficzności zero (M1 przeglądu końcowego; zmierzone w
	 * tooling/base-css.test.mjs).
	 */
	:where(html.is-js) :where(.s-header__toggle:not([hidden])) {
		display: inline-flex;
	}

	:where(html.is-js) :where(.s-header__nav) {
		display: none;
	}

	/* Reguła .is-open musi stać PO regule chowającej — obie mają
	   specyficzność zero, więc o wygranej decyduje wyłącznie kolejność. */
	:where(html.is-js) :where(.s-header__nav.is-open) {
		display: block;
		flex-basis: 100%;
	}

	:where(.s-header__menu) {
		flex-direction: column;
		align-items: stretch;
		gap: 0;
	}

	:where(.s-header__menu a) {
		width: 100%;
	}
}
