/* =====================================================================
   clients/fbiol/website/multipage/assets/css/site.css — CSS instancji FBiOL (FWW-01: szkielet).
   Kaskada (core/web-app/docs/sections-library.md §Kaskada):
     /_tokens.css (--fb-*) → [mostek niżej] → /assets/engine/sections.css (biblioteka) → TEN plik.
   head.twig silnika linkuje ten plik automatycznie, gdy istnieje (WAT-02).
   ZASADY: zero hex / rgb() / hsl() (WA04) — kolory WYŁĄCZNIE var(--fb-*) / var(--c-*);
   selektory biblioteki nadpisuj bez !important (ta sama specyficzność, późniejszy plik wygrywa).
   ===================================================================== */

/* 1. Mostek tokenów biblioteki → --fb-* (musi być PIERWSZĄ regułą: @import poprzedza wszystko). */
@import url("compat-www-fb.css");

/* 2. Baza dokumentu — pismo i barwy z tokenów marki (Montserrat 400/700 przez @font-face w _tokens.css). */
html {
  font-family: var(--fb-font-body);
  font-size: 100%;                        /* 16px = 12pt = --fb-fs-md (patrz mostek --fs-body) */
  line-height: var(--fb-line-body);
  color: var(--fb-color-text);
  background: var(--fb-color-bg);
  -webkit-text-size-adjust: 100%;
}
body { margin: 0; }
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: var(--fb-fw-bold);
  line-height: var(--fb-line-tight);
  color: var(--c-heading);            /* przez mostek: K1 = granat, w .section-hero--dark = biel (wariant podmienia token, nie regułę) */
}
a { color: var(--c-heading); }
/* Linki WEWNĄTRZ sekcji biblioteki: sections.css:91 `.section a { color: var(--c-accent) }` (0,1,1) wygrywa z `a {}` wyżej,
   a --fb-akcent na bieli = 2,84:1 / na bg-soft 2,61:1 (macierz: akcent = PLAMA, nie pismo). Ta sama specyficzność (0,1,1),
   późniejszy plik wygrywa → granat 14,82 (K1) / biel 14,82 (K2). Akcent zostaje dla CTA (tło), nie dla pisma linków. */
.section a { color: var(--c-heading); }
.section-hero--dark a { color: var(--c-text); }
/* Focus: norma instancji = 2 px (jak biblioteka, sections.css:99) — bez rozjazdu 2/3 px między sekcjami a resztą strony. */
:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }
img, svg { max-width: 100%; height: auto; }

/* 3. Hero K2 (strona główna, W-10) — szkielet stylistyczny wariantu .section-hero--dark biblioteki
   (WAS-05 dostarczy sam typ hero; tu: tło, dekory D1/D2, kontener). Tokeny wariantu → mostek.
   Kanon: clients/fbiol/brand/key-visual/README.md §1 (fala, rola „krawędź”), §2 (D2), §4 (wiersz „strona wydarzenia”).
   Reguła „jedna fala, jeden okres, jedna rola”: w hero fala stoi w roli KRAWĘDZI (nie D1-makro) — nigdzie indziej w hero
   nie wolno dołożyć pasma ani D4. */
.section-hero--dark {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--c-bg);                /* granat (mostek: --fb-color-primary) */
  color: var(--c-text);                   /* biel — para 14,82 T; cyan/akcent = plama, nie pismo */
  padding-block: var(--space-2xl) calc(var(--space-2xl) + var(--kv-fala-h));
  /* Parametry fali-krawędzi i D2 — jedno miejsce do strojenia w FWW-04 */
  --kv-fala-h: clamp(28px, 18px + 3.4vw, 80px);   /* wysokość okna fali: 67px @1440 wg fala.js (wychył 60 + grubość 7);
                                                     R2 (∝ √W) przybliżone liniowo: 320→29 (R2: 32) · 768→44 (49) · 1440→67 (67) */
  --kv-d2-krycie: 0.30;                           /* D2 na K2 = 30 % (K1 = 12 %) */
  --kv-d2-obraz: url("/assets/img/logo/fbiol-sygnet-rewers.svg"); /* plik dostarcza FWW-03 (assets/img/logo/); wariant rewers = na granat */
}
/* Kolumna tekstu w lewej połowie (D2 zajmuje prawą — żaden napis nie leży na dekorze). */
.section-hero--dark .section__inner { position: relative; z-index: 2; }
.section-hero--dark .section__inner > * { max-width: 50%; }
@media (max-width: 767px) {
  .section-hero--dark .section__inner > * { max-width: none; }   /* na wąskim ekranie D2 jest za krawędzią — tekst na całą szerokość */
}

/* FALA W ROLI „KRAWĘDŹ” (kanon §1 role fali + §4 wiersz „strona wydarzenia”: „hero: fala jako krawędź”): jedna linia cyanu
   (na granacie 6,01 — plama) przy dolnej krawędzi hero, PEŁNY okres mid→mid (dolina 0,25 W, grzbiet 0,74 W), baza AMP/GRUB_GLOWNA.
   To NIE jest D1 „fala makro” z §2 (nośnik odniesienia 3200 px, kadr = wycinek 0,75 okresu wycentrowany na grzbiecie) —
   D1 zastępuje falę kadru w skali makro; tu stoi zwykła fala hero w roli krawędzi. Jedna fala, jeden okres, jedna rola.
   Kształt = maska SVG wygenerowana z fala.js (JEDYNE źródło liczb) przez tools/gen-fala-path.mjs 1440
   (96 odcinków na okres); kolor NIE siedzi w SVG — daje go background.
   preserveAspectRatio='none' rozciąga okres na szerokość hero; wysokość okna = --kv-fala-h (grubość ∝ wysokość, jak R2). */
.section-hero--dark::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: var(--space-lg);
  height: var(--kv-fala-h);
  z-index: 1;
  pointer-events: none;
  background-color: var(--fb-cyan);
  -webkit-mask: var(--kv-fala-maska) center / 100% 100% no-repeat;
          mask: var(--kv-fala-maska) center / 100% 100% no-repeat;
}
/* Maska fali-krawędzi na :root (od PW-148: czyta ją też nagłówek stron prawnych §9r — jedno źródło kształtu). */
:root {
  --kv-fala-maska: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 67' preserveAspectRatio='none'%3E%3Cpath d='M0.00,33.50 L15.00,35.46 L30.00,37.42 L45.00,39.35 L60.00,41.26 L75.00,43.14 L90.00,44.98 L105.00,46.77 L120.00,48.50 L135.00,50.17 L150.00,51.76 L165.00,53.28 L180.00,54.71 L195.00,56.06 L210.00,57.30 L225.00,58.44 L240.00,59.48 L255.00,60.41 L270.00,61.22 L285.00,61.91 L300.00,62.48 L315.00,62.92 L330.00,63.24 L345.00,63.44 L360.00,63.50 L375.00,63.44 L390.00,63.24 L405.00,62.92 L420.00,62.48 L435.00,61.91 L450.00,61.22 L465.00,60.41 L480.00,59.48 L495.00,58.44 L510.00,57.30 L525.00,56.06 L540.00,54.71 L555.00,53.28 L570.00,51.76 L585.00,50.17 L600.00,48.50 L615.00,46.77 L630.00,44.98 L645.00,43.14 L660.00,41.26 L675.00,39.35 L690.00,37.42 L705.00,35.46 L720.00,33.50 L735.00,31.54 L750.00,29.58 L765.00,27.65 L780.00,25.74 L795.00,23.86 L810.00,22.02 L825.00,20.23 L840.00,18.50 L855.00,16.83 L870.00,15.24 L885.00,13.72 L900.00,12.29 L915.00,10.94 L930.00,9.70 L945.00,8.56 L960.00,7.52 L975.00,6.59 L990.00,5.78 L1005.00,5.09 L1020.00,4.52 L1035.00,4.08 L1050.00,3.76 L1065.00,3.56 L1080.00,3.50 L1095.00,3.56 L1110.00,3.76 L1125.00,4.08 L1140.00,4.52 L1155.00,5.09 L1170.00,5.78 L1185.00,6.59 L1200.00,7.52 L1215.00,8.56 L1230.00,9.70 L1245.00,10.94 L1260.00,12.29 L1275.00,13.72 L1290.00,15.24 L1305.00,16.83 L1320.00,18.50 L1335.00,20.23 L1350.00,22.02 L1365.00,23.86 L1380.00,25.74 L1395.00,27.65 L1410.00,29.58 L1425.00,31.54 L1440.00,33.50' fill='none' stroke='black' stroke-width='7' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* D2 — sygnet jako POLE (świetlna masa po prawej). Parametry kanwy dekorów (nośnik KWADRATOWY 1080) przeliczone
   na % WYSOKOŚCI hero, żeby dekor nie zależał od proporcji nośnika (hero www jest szeroki i niski):
   sygnet 1500 px @ top 200 → wys. 139 %, przesunięcie 18,5 % w dół; pole right:-620 → sygnet wysunięty w prawo
   o 620/1500 = 41,3 % WŁASNEJ szerokości (translate liczy % od własnego pudełka — dlatego nie `right: -x%`, który
   liczy od szerokości hero). Strefa fal sygnetu (59–92 % jego wysokości) zaczyna się na 0,185 + 0,59×1,39 = 1,005 wys. hero
   — pod krawędzią, więc fala w hero jest jedna (D1).
   Maska kanwy (pole 1180×1080: od lewej transparent 0 % → 25 % krycia na 40 % → pełne od 78 %; od dołu pełne do 56 %)
   przeliczona na pudełko sygnetu (szer. ≈ 1485 px, lewa krawędź 305 px na lewo od pola):
   w poziomie 20,5 % / 52 % / 82,5 % · w pionie pełne do (0,56−0,185)/1,39 = 27 %, zero na (1−0,185)/1,39 = 58,6 %. */
.section-hero--dark::after {
  content: "";
  position: absolute;
  top: 18.5%;
  right: 0;
  height: 139%;
  aspect-ratio: 333.8182 / 337.09095;   /* viewBox sygnetu (brand/logo/fbiol-sygnet-*.svg) */
  translate: 41.3% 0;
  z-index: 0;
  pointer-events: none;
  opacity: var(--kv-d2-krycie);
  background: var(--kv-d2-obraz) no-repeat 0 0 / 100% 100%;
  -webkit-mask-image: linear-gradient(to right, transparent 20.5%, color-mix(in srgb, black 25%, transparent) 52%, black 82.5%),
                      linear-gradient(to bottom, black 27%, transparent 58.6%);
          mask-image: linear-gradient(to right, transparent 20.5%, color-mix(in srgb, black 25%, transparent) 52%, black 82.5%),
                      linear-gradient(to bottom, black 27%, transparent 58.6%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

/* 4. (Sticky CTA rejestracji #rejestracja-sticky — FWW-06 — ZDJĘTE w sesji forma/struktura 2026-09-23, B4: stale widoczne CTA
   daje przyklejony nagłówek z przyciskiem „Zarejestruj się” (override templates/partials/nav.twig, B3). Historia: git log.) */

/* 5. Prelegenci panelu w widoku pełnym (`/2026/program`, FWW-06): hook FWW-05 składa `name` = „osoba — afiliacja”, a afiliacje
   same zawierają przecinki („…Pola Walki, WIM-PIB, dr Tadeusz Masiowski — …”), więc separator „, ” biblioteki między osobami
   jest nieodróżnialny od przecinka w afiliacji (zmierzone na zrzucie 320/1440). Każda osoba w osobnej linii.
   K-27 (PW-221, L2 N-1195/N-1197 b): markup przeszedł z `<p>` + separator na `<ul role="list">`/`<li>`
   (override templates/sections/_schedule-row.twig) — separator zniknął z HTML, `.sch__who-sep` nie istnieje już
   w DOM (reguła niżej USUNIĘTA, była martwa od tej zmiany). `role="list"` w HTML (nie CSS) — Safari/VoiceOver
   z `list-style: none` potrafi zgubić semantykę listy bez tego atrybutu (dlatego atrybut zostaje NAWET z widoczną
   listą punktowaną — nie jest to wyłącznie obejście `list-style: none`).
   K-27 POPRAWKA (N-1214, L2, po zrzucie z `list-style: none`): właściciel chciał WIDOCZNĄ listę wypunktowaną —
   `list-style: none` był defektem tej brawy, nie zamierzoną formą. Punktor `disc`, `list-style-position: outside`
   (punktor w wcięciu PRZED tekstem, nie nachodzi na tekst), kolor markera = `--c-heading` (ten sam token co
   `.sch__title` — wizualne dowiązanie punktora do tytułu panelu, kontrast 14,82:1 ≫ 3:1 non-text). Wcięcie
   celowo MAŁE (`1.1em`, nie domyślne UA `40px`/`2.5em`) — tekst panelisty ma leżeć BLISKO lewej krawędzi tytułu
   panelu (pomiar: tools/tests/test_program_lista_punktowana_k27.py), a przy 320 px 1,1em (~17,6 px przy 16 px
   body) nie zabiera zauważalnie szerokości kolumny treści.
   🔴 GOTCHA: `display: flex`/`grid` na `<ul>` BLOKUJE marker `<li>` — spec CSS Display „blockifikuje” outer
   display `list-item` do `block` dla elementów flex/grid (i floated/abs-pos), więc `<li>` przestaje generować
   `::marker` NIEZALEŻNIE od `list-style`. Poprzednia wersja (`display:flex; flex-direction:column; gap:…` niżej)
   dawała `list-style: none` efekt PODWÓJNIE — nawet zmiana na `disc` bez usunięcia flexa nic by nie pokazała.
   Odstęp między panelistami — teraz `margin-block-start` na `.sch__who-item + .sch__who-item` (li są block/list-item
   z natury, stack się pionowo bez flexa).
   Widok compact (hub `/`) nie renderuje `.sch__who` — regresja `/` = 0. */
.section-schedule:not(.section-schedule--compact) .sch__who .sch__person { display: block; }
.section-schedule:not(.section-schedule--compact) .sch__who {
  list-style: disc;
  list-style-position: outside;
  margin: 0;
  padding-inline-start: 1.1em;
}
.section-schedule:not(.section-schedule--compact) .sch__who-item::marker {
  color: var(--c-heading);
}
.section-schedule:not(.section-schedule--compact) .sch__who-item + .sch__who-item {
  margin-block-start: var(--space-xs, 0.25rem);
}
/* PW-164 (rewizja finalna 2026-09-25 §3 „program” pkt 1–2): w pełnym programie pogrubione TYLKO imię i nazwisko —
   hook instancji (lib-instancji/agenda.php, fbiol_osoba_html) opakowuje `osoba` w .sch__person-name, a „ — funkcja”
   zostaje zwykłym krojem (silnik daje całemu .sch__who 600). Dotyczy prelegentów i moderacji.
   Etykieta „Moderacja:” — większy stopień (--fs-body 16 px zamiast --fs-sm 13,3 px) i ciemniejszy kolor: --fb-granat
   14,82:1 na bieli (było --c-text-muted = --fb-muted 4,63:1). */
.section-schedule:not(.section-schedule--compact) .sch__who { font-weight: var(--fb-fw-regular); }
.section-schedule:not(.section-schedule--compact) .sch__person-name { font-weight: var(--fb-fw-bold); }
.section-schedule:not(.section-schedule--compact) .sch__mod-label {
  color: var(--fb-granat);
  font-size: var(--fs-body);
}
/* K-27 (PW-221, L2 N-1195/N-1197 c/d): afiliacja („ — opis”) prelegenta I moderatora — JEDEN token koloru
   (var(--c-text-muted); na `.section--surface`/bg-soft mostek D-CB-17 podmienia go automatycznie na --fb-text
   8,35:1, więc reguła jest bezpieczna niezależnie od tła sekcji). Wcześniej afiliacja moderatora miała
   `.sch__mod-role` (ta sama zmienna, ALE osobny element POZA linkiem — patrz zmiana d w hooku/szablonie);
   afiliacja prelegenta nie miała żadnego koloru (dziedziczyła kolor linku). Teraz obie idą przez
   `.sch__person-role` (hook: fbiol_osoba_html()) — jeden token, jeden wygląd. */
.section-schedule:not(.section-schedule--compact) .sch__person-role {
  color: var(--c-text-muted);
}
/* K-27 (PW-221, L2 N-1195/N-1197 e): odstęp MIĘDZY blokami wiersza panelu (tytuł / lista panelistów / linia
   moderatora / opis) ≈ 1 linia tekstu — było `gap: var(--space-xs)` (0,25rem = 3,8 mm — ciasne, biblioteka
   zakładała gęstszy układ). Liczone z tokenów (nie hardcode): --fs-body (12pt/16px) × --fb-line-body (1,45) —
   ta sama para, którą liczy `line-height` akapitów `.sch__who/.sch__mod/.sch__desc` (sections.css `line-height: 1.5`
   to swoisty fallback biblioteki; instancja FBiOL nie nadpisuje line-height, więc realna wysokość linii tekstu
   body = fs-body × fb-line-body). Gap między POSZCZEGÓLNYMI panelistami — patrz `.sch__who-item + .sch__who-item`
   wyżej (§5, K-27 POPRAWKA N-1214: margin, NIE flex `gap` — flex na `<ul>` gasiłby punktory `<li>`). */
.section-schedule:not(.section-schedule--compact) .sch__main {
  gap: calc(var(--fs-body) * var(--fb-line-body));
}

/* 6. Baner zgody cookies (FWW-12; templates/partials/consent-banner.twig, assets/js/consent.js). Renderowany przez
   layout.twig silnika TYLKO gdy GA4_ID/GTM_ID niepuste. Pasek przypięty do dołu okna (z-index 30 > sticky CTA 20);
   gdy otwarty, consent.js ustawia `html.consent-open` + `--consent-h` (wysokość banera) — sticky CTA `/2026/program`
   podnosi się o tę wysokość, a body dostaje dolny odstęp, więc baner NIE zasłania ani rejestracji, ani stopki.
   Kolory WYŁĄCZNIE var(--c-*) (WA04). `[hidden]` = display:none niezależnie od klas (HTML default, wzmocnione). */
.consent-banner {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 30;
  padding: var(--space-sm) var(--space-gutter);
  background: var(--c-surface);
  color: var(--c-text);
  border-block-start: 2px solid var(--c-border);
  box-shadow: 0 -4px 24px color-mix(in srgb, var(--c-heading) 18%, transparent);
  /* PW-209 N-1167 (defekt z toru rejestracji, WCAG 1.4.10): warstwa 2 na telefonie jest wyższa niż okno — bez tego
     tytuł i wstęp były ucięte i nieosiągalne. Ta sama reguła co w rejestracji (port 1:1). Bramka: check-baner-mobile.py. */
  box-sizing: border-box;   /* max-height liczy padding i obramowanie (content-box dawał 13 px poza oknem) */
  max-height: 100vh;
  max-height: 100dvh;
  overflow-y: auto;
}
.consent-banner[hidden] { display: none; }
.consent-banner__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  max-width: var(--content-width);
  margin-inline: auto;
}
.consent-banner__copy { flex: 1 1 32rem; min-width: 0; }
.consent-banner__title {
  margin: 0 0 var(--space-xs);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 700;
  color: var(--c-heading);
}
.consent-banner__text { margin: 0; font-size: var(--fs-sm); line-height: 1.5; }
.consent-banner__text a { color: var(--c-link); text-decoration: underline; }
.consent-banner__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  flex: 0 0 auto;
}
/* PW-209: `[hidden]` sam nie wystarczy — `display:flex` wyżej ma tę samą specyficzność co `[hidden]{display:none}`
   z arkusza UA i wygrywa jako regułą autora (ten sam gotcha, który `.consent-banner[hidden]` już rozwiązywał
   dla całego banera; `#consent-actions-1` potrzebuje WŁASNEJ reguły, bo consent.js chowa go OSOBNO przy
   otwarciu warstwy 2 — złapane na zrzucie 03-warstwa2-1440.png: oba rzędy przycisków widoczne naraz). */
.consent-banner__actions[hidden] { display: none; }
/* Przyciski poza `.section` — biblioteka styluje tylko `.section .btn`, więc baner powtarza kształt tokenami
   (ta sama para akcent/pismo co CTA sekcji: --c-accent / --c-on-accent, D-CB-17). */
.consent-banner .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--c-heading);
  border-radius: 0.25rem;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.25;
  color: var(--c-heading);
  background: transparent;
  cursor: pointer;
}
/* PW-209 (P18/K-23): RÓWNA RANGA — `.btn--eq` jest UNIKA klasa dla akceptacji/odmowy (warstwa 1 i 2) i
   „Zapisz ustawienia” (warstwa 2): sam kolor tła/obramowania/wysokości/kroju co reszta `.consent-banner .btn`,
   bez wariantu `--primary` (korpus §5: „Odmowa … tak samo łatwa jak … akceptacja” — plan D3, ustalenie 5:
   ANI PL:ON, ani poprzednia wersja FBiOL tego nie spełniały, bo miały --primary vs ghost). `.btn--ghost` zostaje
   dla „Ustawienia cookies” (warstwa 1) — to nawigacja do panelu, nie decyzja o zgodzie, korpus nie wymaga
   dla niej równej rangi z akceptacją/odmową. */
.consent-banner .btn--eq {
  background: var(--c-heading);
  border-color: var(--c-heading);
  color: var(--c-surface);
}
.consent-banner .btn--ghost { background: transparent; }
.consent-banner .btn:hover,
.consent-banner .btn:focus-visible { filter: brightness(0.92); }
.consent-banner .btn:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }
html.consent-open body { padding-block-end: var(--consent-h, 0px); }
.consent-banner:focus { outline: none; }   /* fokus programowy na kontenerze (tabindex=-1) — ring tylko na przyciskach/linku */
.consent-banner__links { margin: var(--space-xs) 0 0; font-size: var(--fs-sm); }
.consent-banner__links a { color: var(--c-link); text-decoration: underline; }

/* Warstwa 2 — panel „Ustawienia cookies” (PW-209). `#consent-actions-1` / `#consent-settings`: dokładnie
   jeden widoczny naraz (consent.js przełącza [hidden]). Bez position:absolute — panel wypycha wysokość
   banera, consent.js mierzy `offsetHeight` PO przełączeniu i ustawia `--consent-h` na nowo (setOffset()). */
.consent-settings[hidden] { display: none; }
.consent-settings {
  max-width: var(--content-width);
  margin-inline: auto;
  padding-block-start: var(--space-sm);
  border-block-start: 1px solid var(--c-border);
}
.consent-settings__title {
  margin: 0 0 var(--space-2xs, 0.25rem);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 700;
  color: var(--c-heading);
}
.consent-settings__intro { margin: 0 0 var(--space-sm); font-size: var(--fs-sm); }
.consent-cat {
  padding-block: var(--space-xs);
  border-block-start: 1px solid var(--c-border);
}
.consent-cat:first-of-type { border-block-start: none; }
.consent-cat__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}
.consent-cat__name { font-weight: 700; color: var(--c-heading); }
.consent-cat__desc { margin: var(--space-2xs, 0.25rem) 0 0; font-size: var(--fs-sm); line-height: 1.5; }
/* `--c-text-muted` (nie `--c-text`) na `--c-surface` FAILuje axe `color-contrast` w tym banerze (zmierzone
   PW-209/check-a11y-warstwa2.py — 4,63:1 z komentarza wyżej w tym pliku liczone było na innym tle). Pełny
   `--c-text`, tak jak `.consent-banner__text` (dziedziczy z `.consent-banner`), 0 naruszeń. */
.consent-cat__status { margin: var(--space-2xs, 0.25rem) 0 0; font-size: var(--fs-sm); color: var(--c-text); }
/* PW-211 (P19/K-24): status NA ŻYWO — `Włączone`/`Wyłączone`, oba w DOM (bramka tekstów je widzi), CSS pokazuje
   jedno wg `:checked` (reguła `:has()` niżej). `aria-hidden="true"` jest na CAŁYM `<p>` w markupie (Analityczne/
   Marketingowe) — dekoracyjne dublowanie: `input[role="switch"]` już niesie stan `checked`/`unchecked` do
   czytnika (zmierzone Playwright `aria_snapshot()` — mapowanie natywne, bez ręcznego `aria-checked`). Niezbędne
   (bez przełącznika) BEZ `aria-hidden` — jedyne źródło stanu dla czytnika. */
.consent-cat__status-word { display: inline; }
.consent-cat__status-word .w-on { display: none; }
.consent-cat__status-word .w-off { display: inline; }
/* `:has()` (Chromium/Firefox/Safari aktualne — QA tego projektu renderuje wyłącznie na aktualnym Chromium,
   `tools/visual/`/Playwright): status-word jest w INNYM poddrzewie niż input (`.consent-cat__row` vs
   `.consent-cat__status`, wspólny rodzic dopiero `.consent-cat`) — zwykły `~`/`+` tego nie dosięga. */
.consent-cat:has(.consent-toggle-input:checked) .consent-cat__status-word .w-on { display: inline; }
.consent-cat:has(.consent-toggle-input:checked) .consent-cat__status-word .w-off { display: none; }
/* Przełącznik: input nativny, appearance:none + pseudo-element gałki — semantyka checkbox zostaje (rola,
   aria-labelledby/-describedby na markupie), wygląd toggle-switcha tokenami (WA04, zero hex). */
.consent-toggle-input {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 2.5rem;
  height: 1.375rem;
  flex: 0 0 auto;
  border: 1px solid var(--c-heading);
  border-radius: 1rem;
  background: var(--c-surface);
  cursor: pointer;
}
.consent-toggle-input::before {
  content: "";
  position: absolute;
  top: 1px;
  left: 1px;
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: var(--c-heading);
  transition: transform 0.15s ease;
}
/* N12 (PW-211, L2 N-1183, 1.4.11): border-color:var(--c-accent) (= tło, bez widocznego obrysu) dawał
   obramowanie/tło switcha ON wobec tła banera (--c-surface) 2,61:1 — POD progiem 3:1 (zmierzone
   check-consent.py, akcent #FF6B35 na bg-soft #F2F6F9). Obrys `--c-heading` (granat) daje 13,64:1 na tym
   samym tle — wypełnienie ON zostaje pomarańczowe (marka), tylko obrys dostaje kontrastowy token. */
.consent-toggle-input:checked { background: var(--c-accent); border-color: var(--c-heading); }
.consent-toggle-input:checked::before { transform: translateX(1.125rem); background: var(--c-on-accent); }
.consent-toggle-input:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }
/* PW-211: przycisk „więcej" (opis kategorii zwinięty domyślnie) — wygląd linku tekstowego, nie `.btn`
   (nie jest decyzją o zgodzie, jak `.btn--ghost` „Ustawienia cookies" nie jest równą rangą z akceptacją). */
.consent-cat__more {
  display: inline-flex;
  align-items: center;
  margin: var(--space-2xs, 0.25rem) 0 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--c-link);
  text-decoration: underline;
  font: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
}
.consent-cat__more:hover,
.consent-cat__more:focus-visible { filter: brightness(0.85); }
.consent-cat__more:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }
.consent-settings__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-block-start: var(--space-sm);
}
@media (max-width: 767px) {
  .consent-banner { padding-block: var(--space-xs) var(--space-sm); }
  .consent-banner__title { font-size: var(--fs-body); }
  .consent-banner__text { line-height: 1.4; }
  .consent-banner__actions { width: 100%; }
  .consent-banner__actions .btn { flex: 1 1 0; justify-content: center; }
  .consent-settings__actions { width: 100%; }
  .consent-settings__actions .btn { flex: 1 1 0; justify-content: center; }
  .consent-cat__row { flex-wrap: wrap; }
  .consent-banner .btn { min-height: 44px; }   /* PW-209 N-1167: cel dotykowy ≥ 44 px (było ~37 px @390) */
  .consent-cat__more { min-height: 44px; padding-block: var(--space-2xs, 0.25rem); }   /* PW-211 M6: cel dotykowy ≥ 44 px (WCAG 2.5.8) */
}

/* 7. CHROME STRONY — nagłówek, nawigacja desktop/mobile, stopka (FWW-17). Markup = silnik, bez zmian:
   core/web-app/templates/partials/nav.twig (header#siteHeader › .wrap.header-inner › a.wm + nav.nav › ul.nav-links + button#hamburger)
   i partials/footer.twig (footer.site-footer › .wrap › .footer-top › a.wm + nav.footer-links; p.disclaimer; .footer-base).
   Selektory .wrap zawsze przez rodzica (.site-header / .site-footer) — silnik nie ma reguły .wrap, nie zakładamy globalnej.
   Kolory WYŁĄCZNIE var(--c-*) / var(--fb-*) (zero hex, WA04); pary kontrastu wg docs/design-system/macierz-kontrastu.md:
   nagłówek K1 = granat/ink na bieli 14,82 T; stopka = biel na granacie 14,82 T; focus: granat na bieli 14,82 / cyan na granacie 6,01 (≥ 3:1).
   PUNKT PRZEŁAMANIA 768 px: 5 pozycji nav.yaml (Program · Partnerzy · Informacje · O Forum · Kontakt) + znak mieszczą się
   w jednym wierszu od 768 (zmierzone: FWW-17 §Wynik); ten sam próg co reszta instancji (max-width: 767px). Wysokość
   nagłówka = --chrome-header-h (64 px < 768, 72 px ≥ 768) — decyzja instancji (brand nie ma tokenów układu), odczyt dla FWW-23. */
:root {
  --chrome-header-h: 64px;
  --chrome-logo-h: 44px;                 /* znak F2 (sygnet + akronim, jeden wiersz) — viewBox 592,2 × 268,8 → szer. ≈ 97 px */
  --chrome-tap: 44px;                    /* minimalny cel dotyku (WCAG 2.5.8 AA 24 px; przyjmujemy 44 px jak 2.5.5) */
  scroll-padding-top: calc(var(--chrome-header-h) + var(--space-sm));   /* kotwice i fokus nie chowają się pod przyklejonym nagłówkiem (WCAG 2.4.11) */
}
@media (min-width: 768px) {
  :root { --chrome-header-h: 72px; --chrome-logo-h: 48px; }
}

/* 7a. Nagłówek — biel K1, przyklejony u góry (panel menu mobilnego liczy top od jego wysokości). z-index 25: nad sticky CTA (20),
   pod banerem zgód (30) — baner jest nadrzędny wobec każdego stanu menu. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 25;
  background: var(--c-bg);
  color: var(--c-heading);
  border-block-end: 1px solid var(--c-border);
}
.site-header .wrap {
  box-sizing: border-box;
  max-width: var(--content-width);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: var(--chrome-header-h);
}
.site-header .wm {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  min-height: var(--chrome-tap);
  color: var(--c-heading);
  font-family: var(--font-display);
  font-weight: var(--fb-fw-bold);
  font-size: var(--fs-h3);
  text-decoration: none;
}
.site-header .wm img {
  display: block;
  width: auto;
  height: var(--chrome-logo-h);
  max-width: none;                       /* reguła bazowa img{max-width:100%} ścisnęłaby znak w wąskim flex-itemie */
}
.site-header .nav {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}
.nav-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}
.nav-links a {
  display: inline-flex;
  align-items: center;
  min-height: var(--chrome-tap);
  color: var(--c-heading);
  font-size: var(--fs-body);
  font-weight: var(--fb-fw-bold);
  text-decoration: none;
  text-underline-offset: 0.35em;
  text-decoration-thickness: 2px;
}
.nav-links a:hover { text-decoration-line: underline; }
/* Stan aktywny (aria-current="page" ustawia app.js): podkreślenie 3 px granatem — wskaźnik stanu nie może być kolorem
   akcentu/cyanu (na bieli 2,84 / 2,46 < 3:1, WCAG 1.4.11), więc różni się grubością, nie barwą. */
.nav-links a[aria-current="page"] {
  text-decoration-line: underline;
  text-decoration-thickness: 3px;
}
.nav-dropdown { display: none; }         /* nav.yaml nie ma pozycji z children; podmenu = osobny projekt, gdy powstanie */

/* Burger — domyślnie ukryty: bez JS (brak html.js) i na desktopie nawigacja jest listą. */
.hamburger {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: var(--chrome-tap);
  height: var(--chrome-tap);
  margin-inline-end: calc(var(--space-sm) * -1);   /* optyczne wyrównanie kresek z krawędzią kontenera */
  padding: 0;
  border: 0;
  border-radius: var(--fb-radius);
  background: transparent;
  color: var(--c-heading);
  cursor: pointer;
}
.hamburger span {
  display: block;
  width: 24px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;             /* granat na bieli 14,82 — ikona jest jedynym nośnikiem, obrys przycisku zbędny */
}
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
@media (prefers-reduced-motion: no-preference) {
  .hamburger span { transition: transform 0.15s ease, opacity 0.15s ease; }
}

/* 7b. < 768 px. Bez JS: znak w pierwszym wierszu, linki zawijanym rzędem pod nim (dostępne bez skryptu). */
/* Próg menu 768 → 1024 (sesja 2026-09-23, B3/B5): przy CTA w nagłówku 5 linków zawijało się w 2 wiersze od 768 do ~1000 px (zmierzone).
   Próg 1024 → 1120 (PW-154, 2026-09-24): 8. pozycja „Galeria” — lista linków w JEDNYM wierszu dopiero od 1108 px (zmierzone
   Playwrightem na `/`; bez „Galerii” od 1030 px, czyli przy 1024 menu zawijało się już wcześniej). 1120 = 1108 + zapas 12 px.
   Ten sam próg w app.js (matchMedia) i w §9f. */
@media (max-width: 1119px) {
  .header-inner { flex-wrap: wrap; gap: 0 var(--space-md); }
  html:not(.js) .site-header .nav { flex-basis: 100%; }
  html:not(.js) .nav-links { gap: 0 var(--space-md); padding-block-end: var(--space-xs); }

  /* Z JS: burger + panel na całą wysokość okna pod nagłówkiem. Zamknięty panel = display:none (linki poza kolejnością Tab). */
  .js .hamburger { display: inline-flex; }
  .js .nav-links { display: none; }
  .js .site-header.is-open .nav-links {
    position: fixed;
    inset: var(--chrome-header-h) 0 0 0;
    z-index: 25;
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
    padding: var(--space-sm) var(--space-gutter) var(--space-xl);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--c-bg);
    border-block-start: 1px solid var(--c-border);
  }
  .js .site-header.is-open .nav-links li { border-block-end: 1px solid var(--c-border); }
  .js .site-header.is-open .nav-links a {
    display: flex;
    min-height: 56px;
    font-size: var(--fs-lead);
  }
  html.nav-open,
  html.nav-open body { overflow: hidden; }   /* blokada przewijania tła przy otwartym menu */
}
@media (min-width: 1120px) {
  .hamburger { display: none; }
}

/* 7c. Stopka — granat z pismem białym (jedyna para tekstowa na granacie: 14,82 T). Wariant podmienia tokeny, nie reguły —
   ten sam wzorzec co K2 (compat-www-fb.css .section-hero--dark): :focus-visible z §2 czyta --c-focus → cyan 6,01 na granacie. */
.site-footer {
  --c-bg:      var(--fb-color-primary);
  --c-text:    var(--fb-biel);
  --c-heading: var(--fb-biel);
  --c-link:    var(--fb-biel);
  --c-focus:   var(--fb-cyan);
  --c-border:  var(--fb-granat-linia);   /* linia podziału na granacie — plama 1,71 (decyzja 2026-09-18), dekor, nie pismo */
  background: var(--c-bg);
  color: var(--c-text);
  padding-block: var(--space-xl) var(--space-lg);
  font-size: var(--fs-sm);
}
.site-footer .wrap {
  box-sizing: border-box;
  max-width: var(--content-width);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
}
.footer-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md) var(--space-lg);
  padding-block-end: var(--space-lg);
  border-block-end: 1px solid var(--c-border);
}
.site-footer .wm {
  color: var(--c-heading);
  font-family: var(--font-display);
  font-weight: var(--fb-fw-bold);
  font-size: var(--fs-h2);
  letter-spacing: var(--fb-tracking-caps);
  text-decoration: none;
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-lg);
}
.footer-links a,
.footer-cookie-btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--chrome-tap);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--c-link);
  font: inherit;
  font-size: var(--fs-body);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.3em;
  cursor: pointer;
}
.footer-links a:hover,
.footer-cookie-btn:hover { text-decoration-thickness: 2px; }
.site-footer .disclaimer {
  max-width: var(--prose-width);
  margin: var(--space-md) 0 0;
  color: var(--c-text);                  /* brak pary „przyciszona biel na granacie” w macierzy → pełna biel */
  line-height: var(--fb-line-body);
}
.footer-base {
  margin-block-start: var(--space-md);
  color: var(--c-text);
}
@media (max-width: 767px) {
  .footer-top { flex-direction: column; align-items: flex-start; }
  .footer-links { flex-direction: column; align-items: flex-start; gap: 0; }
}

/* 8. Strony prawne (FWW-18) — `templates/legal.twig` silnika: `<section class="section"><div class="wrap" style="max-width:…">`
   + `<article class="prose legal-prose">`. Silnik nie ma reguły `.wrap` (patrz 7.), więc bez tego bloku treść stała na x = 0.
   Selektor wąski: `.legal-prose` występuje WYŁĄCZNIE w legal.twig (grep core/web-app/templates + templates instancji) — inne
   strony nie mają dziecka `.legal-prose`, więc blok ich nie dotyka. Szerokość zostaje z inline `max-width` szablonu (--prose-width). */
.section > .wrap:has(> .legal-prose) {
  box-sizing: border-box;
  margin-inline: auto;
  padding-inline: var(--space-gutter);
}
.section > .wrap:has(> .legal-prose) > h1 {
  margin-block: 0 var(--space-xs);
  font-size: var(--fs-h1);
}
/* PW-170: odstęp belka(h1)→data — szablon `legal.twig` nie ustawia marginesu na `.legal-updated`
   (tylko kolor+font-size inline), więc liczyła się domyślna 1em akapitu UA. Zmierzone Playwright
   (getBoundingClientRect, 1440px, /regulamin): PRZED ~18,9px, PO ~30,2px. Jawny margines instancji:
   większy odstęp od h1 i od artykułu poniżej. */
.legal-updated { margin-block: var(--space-lg) var(--space-md); }
.legal-prose { overflow-wrap: break-word; }
.legal-prose h2 {
  margin-block: var(--space-lg) var(--space-sm);
  font-size: var(--fs-h2);
}
.legal-prose h3 {
  margin-block: var(--space-md) var(--space-xs);
  font-size: var(--fs-h3);
}
.legal-prose ol,
.legal-prose ul { padding-inline-start: var(--space-lg); }
.legal-prose li + li { margin-block-start: var(--space-xs); }

/* 9. FORMA — sesja interaktywna 2026-09-23 (clients/fbiol/docs/sesje/2026-09-23-fbiol-www-forma-struktura.md, domena A).
   Zero hex; markup silnika bez zmian. */

/* 9a. Hero `/` (K2) — KARTA FAKTÓW po prawej (A1). Dotąd `.section__inner > * { max-width: 50% }` × siatka silnika
   `.hero__inner` 1,15fr/0,85fr (sections.css:194) dawały kolumnę ok. 300 px. Teraz: jedna kolumna `.hero__inner`,
   w `.hero__copy` siatka tekst 1,5fr | karta 1fr; `.hero__stats` (dzień/godzina/sala) = karta na tle D2 (krycie 30 % bez zmian). */
.section-hero--dark .hero__stats {
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-lg);
  border: 1px solid var(--c-border);
  background: color-mix(in srgb, var(--c-bg) 88%, transparent);   /* granat, lekko przezierny na D2 — pismo biel 14,82 niezależnie od dekoru */
}
@media (min-width: 768px) {
  .section-hero--dark .section__inner > * { max-width: none; }
  .section-hero--dark .hero__inner { grid-template-columns: minmax(0, 1fr); }
  .section-hero--dark .hero__copy {
    max-width: none;
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    column-gap: var(--space-2xl);
    align-items: center;
  }
  .section-hero--dark .hero__copy > * { grid-column: 1; }
  .section-hero--dark .hero__badge { justify-self: start; }
  .section-hero--dark .hero__stats { grid-column: 2; grid-row: 1 / span 5; margin: 0; }
}

/* 9b. Hero podstron (K1, A2) — kanon brand/key-visual/README.md:95 „reszta strony (w tym hero podstron) K1 … D2 ściszone,
   fala jako pasmo przy dolnej krawędzi”. Tło bg-soft; PASMO = wypełnione pole granatu pod falą (::after) z krawędzią cyanu
   (::before = ta sama maska przesunięta w górę o grubość krawędzi — wzór „pasmo + obrys na ścieżce krawędzi”, kanon §1);
   pasmo zostaje PUSTE. Maska z fala.js: `node tools/gen-fala-path.mjs 1440 --pasmo`. D2 = sygnet kolor 12 % na pseudo
   `.hero__inner::before` (pseudo sekcji zajęte przez pasmo). Lead na bg-soft: --fb-muted = 4,26 → --fb-text (8,35). */
.section-hero:not(.section-hero--dark) {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--fb-bg-soft);
  padding-block: var(--space-xl) calc(var(--space-xl) + var(--kv-pasmo-h));
  --c-text-muted: var(--fb-text);
  --kv-fala-h: clamp(28px, 18px + 3.4vw, 67px);
  --kv-pasmo-h: calc(var(--kv-fala-h) + var(--space-md));
  --kv-krawedz: clamp(3px, 1px + 0.42vw, 7px);
  --kv-pasmo-maska: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 67' preserveAspectRatio='none'%3E%3Cpath d='M0.00,33.50 L15.00,35.46 L30.00,37.42 L45.00,39.35 L60.00,41.26 L75.00,43.14 L90.00,44.98 L105.00,46.77 L120.00,48.50 L135.00,50.17 L150.00,51.76 L165.00,53.28 L180.00,54.71 L195.00,56.06 L210.00,57.30 L225.00,58.44 L240.00,59.48 L255.00,60.41 L270.00,61.22 L285.00,61.91 L300.00,62.48 L315.00,62.92 L330.00,63.24 L345.00,63.44 L360.00,63.50 L375.00,63.44 L390.00,63.24 L405.00,62.92 L420.00,62.48 L435.00,61.91 L450.00,61.22 L465.00,60.41 L480.00,59.48 L495.00,58.44 L510.00,57.30 L525.00,56.06 L540.00,54.71 L555.00,53.28 L570.00,51.76 L585.00,50.17 L600.00,48.50 L615.00,46.77 L630.00,44.98 L645.00,43.14 L660.00,41.26 L675.00,39.35 L690.00,37.42 L705.00,35.46 L720.00,33.50 L735.00,31.54 L750.00,29.58 L765.00,27.65 L780.00,25.74 L795.00,23.86 L810.00,22.02 L825.00,20.23 L840.00,18.50 L855.00,16.83 L870.00,15.24 L885.00,13.72 L900.00,12.29 L915.00,10.94 L930.00,9.70 L945.00,8.56 L960.00,7.52 L975.00,6.59 L990.00,5.78 L1005.00,5.09 L1020.00,4.52 L1035.00,4.08 L1050.00,3.76 L1065.00,3.56 L1080.00,3.50 L1095.00,3.56 L1110.00,3.76 L1125.00,4.08 L1140.00,4.52 L1155.00,5.09 L1170.00,5.78 L1185.00,6.59 L1200.00,7.52 L1215.00,8.56 L1230.00,9.70 L1245.00,10.94 L1260.00,12.29 L1275.00,13.72 L1290.00,15.24 L1305.00,16.83 L1320.00,18.50 L1335.00,20.23 L1350.00,22.02 L1365.00,23.86 L1380.00,25.74 L1395.00,27.65 L1410.00,29.58 L1425.00,31.54 L1440.00,33.50 L1440,67 L0,67 Z' fill='black'/%3E%3C/svg%3E");
}
.section-hero:not(.section-hero--dark)::before,
.section-hero:not(.section-hero--dark)::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: var(--kv-pasmo-h);
  pointer-events: none;
  -webkit-mask: var(--kv-pasmo-maska) top / 100% var(--kv-fala-h) no-repeat,
                linear-gradient(black, black) bottom / 100% calc(100% - var(--kv-fala-h) + 1px) no-repeat;
          mask: var(--kv-pasmo-maska) top / 100% var(--kv-fala-h) no-repeat,
                linear-gradient(black, black) bottom / 100% calc(100% - var(--kv-fala-h) + 1px) no-repeat;
}
.section-hero:not(.section-hero--dark)::before { z-index: 1; background: var(--fb-cyan); translate: 0 calc(-1 * var(--kv-krawedz)); }
.section-hero:not(.section-hero--dark)::after  { z-index: 1; background: var(--fb-granat); }
.section-hero:not(.section-hero--dark) .hero__copy { position: relative; z-index: 2; }
.section-hero:not(.section-hero--dark) .hero__inner::before {
  content: "";
  position: absolute;
  top: 12%;
  right: 0;
  height: 139%;
  aspect-ratio: 333.8182 / 337.09095;
  translate: 41.3% 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.12;                                   /* D2 w K1 = 12 % (kanon §2) */
  background: url("/assets/img/logo/fbiol-sygnet-kolor.svg") no-repeat 0 0 / 100% 100%;
  -webkit-mask-image: linear-gradient(to right, transparent 20.5%, black 82.5%);
          mask-image: linear-gradient(to right, transparent 20.5%, black 82.5%);
}

/* 9c. Rytm (A3) — co druga sekcja TREŚCI na bg-soft (hero i CTA poza rachubą), linie idea zbędne. Pola komponentów w sekcji
   szarej dostają biel (--c-surface), mikrotekst --fb-text (para --fb-muted/bg-soft = 4,26 < 4,5). */
.section-idea { border-block: 0; }
main > .section:nth-child(even of .section:not(.section-hero):not(.section-cta)) {
  background: var(--fb-bg-soft);
  --c-surface: var(--fb-biel);
  --c-text-muted: var(--fb-text);
}

/* 9d. Idea z ilustracją (A3) — układ silnika `.section-idea--foto` (tekst 1,1fr | zdjęcie 1fr); do dostawy zdjęć
   zaślepka assets/img/zaslepka/zdjecie-4x3.svg (tools/gen-zaslepka.py). Zdjęcie przyklejone do góry kolumny tekstu. */
.section-idea--foto .idea__foto img { aspect-ratio: 4 / 3; object-fit: cover; }
@media (min-width: 768px) {
  .section-idea--foto .idea__foto { position: sticky; top: calc(var(--chrome-header-h, 72px) + var(--space-md)); }
}

/* 9e. Stopka (A4) — override templates/partials/footer.twig: pas belki na bieli NAD stopką + stopka 3-kolumnowa na granacie.
   Belka: logotypy PNG kolorowe (dług FDS-23, 165 ppi) → wysokość 56/72 px × skala optyczna (PW-161), na bieli; etykiety grup = --fb-text (nie muted). */
.footer-belka {
  background: var(--fb-biel);
  border-block-start: 1px solid var(--c-border);
  padding-block: var(--space-lg);
}
.footer-belka .wrap {
  box-sizing: border-box;
  max-width: var(--content-width);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
  display: grid;
  gap: var(--space-md);
}
/* PW-182 (b): odstęp etykieta→pierwszy rząd znaków w stopce był --space-xs (≈3,8 px, zmierzone Playwright:
   3,77–7,36 px zależnie od zawijania etykiety) — dużo mniej niż ten sam odstęp w `partner-grid`
   (.plg__group-label margin-bottom = --space-md ≈ 18,9 px, silnik/sections.css). Ujednolicone do --space-md,
   tego samego rytmu co siatka partnerów. */
.fbk__label {
  margin: 0 0 var(--space-md);
  color: var(--fb-text);
  font-size: var(--fs-sm);
  font-weight: var(--fb-fw-bold);
  letter-spacing: var(--fb-tracking-caps);
  text-transform: uppercase;
}
.fbk__logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md) var(--space-md);   /* PW-161 (W-K2): było sm × lg; mobile — pion i poziom po ~19 px */
  margin: 0;
  padding: 0;
  list-style: none;
}
/* PW-161 (W-K2, rewizja finalna 2026-09-25): znaki 48 → 56 px (mobile) / 72 px (≥ 768 px) × `--skala` — wyrównanie
   OPTYCZNE: znaki z mikrotekstem (orzeł/herb/godło z drobnym podpisem) dostają indywidualnie większą skalę, reszta 1.
   Zaczep = `data-logo` (numer pliku belki, footer.twig); pliki znaków bez zmian. PW-167: ta SAMA tabela skali obsługuje
   siatkę `/2026/partnerzy` (`#partnerzy`) — zaczepem jest tam plik znaku w `src` (markup silnika `partner-grid` nie ma
   `data-logo`; hook daje tylko name/logo/caption), zużycie `--skala` w §9x. `max-width` rośnie razem ze skalą,
   ale na mobile ogranicza go szerokość rzędu (kafel nie może wyjść poza okno — brak scrolla przy 320/390). */
.fbk__logo { --fbk-h: 56px; --fbk-max: 200px; }
.fbk__logo[data-logo="01"],
#partnerzy .plg__tile:has(img[src*="/belka/01-"]) { --skala: 1.25; } /* Prezydent Olsztyna — podpis nad herbem (W-K1) */
.fbk__logo[data-logo="03"],
#partnerzy .plg__tile:has(img[src*="/belka/03-"]) { --skala: 1.2; }  /* RCB — „Regionalne Centrum Bezpieczeństwa” */
.fbk__logo[data-logo="06"],
#partnerzy .plg__tile:has(img[src*="/belka/06-"]) { --skala: 1.1; }  /* MSWiA — szeroki, tekst już średni */
.fbk__logo[data-logo="07"],
#partnerzy .plg__tile:has(img[src*="/belka/07-"]) { --skala: 1.2; }  /* MON — podpis ministra pod linią */
.fbk__logo[data-logo="10"],
#partnerzy .plg__tile:has(img[src*="/belka/10-"]) { --skala: 1.35; } /* Wojewoda */
.fbk__logo[data-logo="11"],
#partnerzy .plg__tile:has(img[src*="/belka/11-"]) { --skala: 1.35; } /* Rektor UWM */
.fbk__logo[data-logo="12"],
#partnerzy .plg__tile:has(img[src*="/belka/12-"]) { --skala: 1.3; }  /* Marszałek */
.fbk__logo[data-logo="13"],
#partnerzy .plg__tile:has(img[src*="/belka/13-"]) { --skala: 1.2; }  /* AMW */
.fbk__logo[data-logo="14"],
#partnerzy .plg__tile:has(img[src*="/belka/14-"]) { --skala: 1.2; }  /* PSP */
.fbk__logo[data-logo="16"],
#partnerzy .plg__tile:has(img[src*="/belka/16-"]) { --skala: 1.25; } /* ZGWM */
.fbk__logo[data-logo="18"],
#partnerzy .plg__tile:has(img[src*="/belka/18-"]) { --skala: 0.8; }  /* Edugika — szeroki znak, optycznie większy (słowo właściciela 2026-10-02) */
.fbk__logo img {
  display: block;
  height: calc(var(--fbk-h) * var(--skala, 1));
  width: auto;
  max-width: min(calc(var(--fbk-max) * var(--skala, 1)), calc(100vw - 2 * var(--space-gutter) - 2 * var(--space-sm) - 2px));
  object-fit: contain;
}
@media (min-width: 768px) {
  .fbk__logos { gap: var(--space-lg) var(--space-xl); }   /* PW-161: pion ~30 px, poziom ~61 px */
  .fbk__logo { --fbk-h: 72px; --fbk-max: 240px; }
}
.footer-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
  padding-block-end: var(--space-lg);
  border-block-end: 1px solid var(--c-border);
}
@media (min-width: 768px) {
  .footer-cols { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1.2fr); gap: var(--space-xl); }
}
.site-footer .footer-col--marka .wm { display: inline-block; }
.site-footer .footer-col--marka .wm img { display: block; height: 60px; width: auto; }
.footer-name { margin: var(--space-md) 0 var(--space-xs); font-weight: var(--fb-fw-bold); font-size: var(--fs-body); }
.footer-when { margin: 0; }
/* FWW-30: hasło przekazu w stopce (strategia.md §Hasła, PW-108) — biel stopki (`--c-text` = `--fb-biel`, para `tekst` 14,82:1 w macierz-kontrastu.md), kursywa zamiast dekoru. Cyan NIE: para cyan/granat ma w macierzy rolę `plama` (INFO), nie pismo — recenzja spec FWW-30. */
.footer-haslo { margin: var(--space-sm) 0 0; font-style: italic; color: var(--c-text); max-width: 32ch; }
.footer-col__title {
  margin: 0 0 var(--space-sm);
  font-weight: var(--fb-fw-bold);
  letter-spacing: var(--fb-tracking-caps);
  text-transform: uppercase;
}
.footer-col ul { margin: 0; padding: 0; list-style: none; }
.footer-col li a {
  display: inline-flex;
  align-items: center;
  min-height: var(--chrome-tap);
  color: var(--c-link);
  font-size: var(--fs-body);
  text-underline-offset: 0.3em;
}
.footer-col--kontakt .footer-links { flex-direction: column; align-items: flex-start; gap: 0; margin-block-start: var(--space-sm); }
/* FWW-39: profile Forum — linki tekstowe z nazwą platformy (stopka: kolumna Kontakt; /kontakt: sekcja `social`). Bez ikon (L2 N-911). */
.footer-social { display: flex; flex-wrap: wrap; gap: var(--space-xs) var(--space-md); }   /* margin/padding/list-style = `.footer-col ul` */
.section-social .section__inner { text-align: center; }   /* nagłówek w osi listy — QA FWW-39: bez tego h2 `start`, lista `center` */
.social-list { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-sm) var(--space-md); margin: var(--space-md) 0 0; padding: 0; list-style: none; }
.social-list a { display: inline-flex; align-items: center; min-height: var(--chrome-tap); }   /* cel dotyku 44 px — lustro `.footer-col li a` */
.section-idea--foto .idea__foto img { border: 1px solid var(--c-border); }   /* zaślepka bg-soft na szarej sekcji nie znika */

/* 9f. CTA rejestracji w nagłówku (B3, override templates/partials/nav.twig) — akcent z granatowym pismem 5,23 T (D-CB-17).
   < 480 px etykieta krótka „Rejestracja” obok burgera; CTA poza panelem menu (stale widoczne, B4). */
.site-header .nav-cta {                 /* .btn biblioteki działa tylko w `.section` (sections.css:129) — styl przycisku tutaj */
  flex: 0 0 auto;
  min-height: var(--chrome-tap);
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  padding: 0 var(--space-md);
  border: 1px solid transparent;
  border-radius: 0.25rem;
  background: var(--c-accent);
  color: var(--c-on-accent);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fb-fw-bold);
  line-height: 1.25;
  white-space: nowrap;
  text-decoration: none;
}
.site-header .nav-cta:hover { text-decoration: underline; text-underline-offset: 0.3em; }
.site-header .nav-cta .nav-cta__krotka { display: none; }
@media (max-width: 479px) {
  /* PW-149 (WCAG 2.5.3): pełna display:none (poza drzewem a11y), krótka bez aria-hidden → nazwa dostępna = widoczne „Rejestracja” */
  .site-header .nav-cta .nav-cta__pelna { display: none; }
  .site-header .nav-cta .nav-cta__krotka { display: inline; }
  .site-header .nav-cta { padding-inline: var(--space-sm); }
}
/* 9f-bis. Link „Przejdź do treści” (PW-149 / WAS-19, WCAG 2.4.1) — baza w engine/sections.css (poza ekranem do fokusu);
   tu tylko forma marki: przycisk akcentu jak CTA nagłówka, ring fokusu --c-focus z §2. */
.skip-link {
  background: var(--c-accent);
  color: var(--c-on-accent);
  font-weight: var(--fb-fw-bold);
  min-height: var(--chrome-tap);
  display: inline-flex;
  align-items: center;
  text-decoration: underline;
}
.skip-link:focus { outline: 2px solid var(--c-focus); outline-offset: -4px; }   /* N-1016: ujemny — link stoi w 0,0, dodatni obrys wychodził poza okno */
@media (max-width: 1119px) {   /* próg menu §7b (PW-154: 1024 → 1120) */
  .js .site-header .nav { margin-inline-start: auto; }
}

/* 9g. Telefon (C1, sesja 2026-09-23): < 768 px liczby 2×2 zamiast kolumny; zdjęcia idea 16:9 zamiast 4:3 (ok. 1 ekran krócej na `/`). */
@media (max-width: 767px) {
  .section-factbar .factbar__stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .section-idea--foto .idea__foto img { aspect-ratio: 16 / 9; }
}

/* 9h. Komitet — person-grid bez zdjęć (FWW-41, recenzja jakości 2026-09-24). Tylko `.section-person-grid`, bez zmian w silniku.
   (1) Rola = afiliacja ze źródła 1:1 — wersaliki silnika (.pg__role uppercase) gubią wielkość liter skrótów (ASzWoj, UwB, UwS).
   (2) Kafel z inicjałami (gałąź _portrait.twig bez photo/photo_fallback) = wariant kompaktowy: znak 4.5rem obok nazwiska
       zamiast kadru 4:5 na pełną szerokość (22 kafle przy 320 px ≈ 12,6 tys. px strony). Gałęzie photo/fallback nietknięte
       (`:has(> .portrait__initials)`). `.pg__link` bywa <div> (kafel bez href) albo <a> — selektor obejmuje oba. */
.section-person-grid .pg__role { text-transform: none; letter-spacing: 0; }
.section-person-grid .pg__link:has(> .portrait > .portrait__initials) { flex-direction: row; align-items: center; }
.section-person-grid .portrait:has(> .portrait__initials) { flex: 0 0 4.5rem; aspect-ratio: 1; margin-inline-start: var(--space-md, 1rem); }
.section-person-grid .portrait__initials { font-size: var(--fs-h3, 1.25rem); }

/* 9i. Hero ze zdjęciem w tle — PW-136 (`/`, G11) i PW-137 (`/o-forum`, G14), rewizja właściciela 2026-09-24.
   Pole silnika `media{src, alt}` renderuje `<figure class="hero__media">` jako drugą kolumnę `.hero__inner`
   (klasa sekcji `.section-hero--media`); tu robimy z niego PEŁNE TŁO sekcji. Kolejność warstw w kontekście
   `isolation: isolate` sekcji (§3): zdjęcie + nakładka granatu (z-index −1) → D2 sygnet 30 % (::after, 0) →
   D1 fala-krawędź (::before, 1) → tekst (`.hero__copy`, 2). `.section__inner` traci pozycjonowanie (§3 daje mu
   z-index 2 = własny kontekst), żeby figura liczyła `inset: 0` od SEKCJI, a nie od kontenera treści.
   Nakładka = granat marki (kontrast pisma bieli mierzy tools/check-kontrast-hero.py na najgorszym pikselu,
   NIE na średniej): mocniejsza po stronie kolumny tekstu, lżejsza po prawej (tam D2 i karta faktów z własnym tłem). */
.section-hero--dark.section-hero--media .section__inner { position: static; z-index: auto; }
.section-hero--dark.section-hero--media .hero__copy { position: relative; z-index: 2; }
.section-hero--dark.section-hero--media .hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  margin: 0;
  max-width: none;
  pointer-events: none;
}
.section-hero--dark.section-hero--media .hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 35%;
}
.section-hero--dark.section-hero--media .hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: color-mix(in srgb, var(--fb-granat) 84%, transparent);
}
@media (min-width: 768px) {
  .section-hero--dark.section-hero--media .hero__media::after {
    background: linear-gradient(to right,
      color-mix(in srgb, var(--fb-granat) 88%, transparent) 0%,
      color-mix(in srgb, var(--fb-granat) 82%, transparent) 55%,
      color-mix(in srgb, var(--fb-granat) 66%, transparent) 100%);
  }
}
/* 9i-bis. Hero `/o-forum` (K-28, PW-222, L2 N-1198/N-1199) — eyebrow i lead na pełną szerokość kontenera hero
   (1120 px), nie tylko lewa kolumna 1,5fr. §9a rozbija `.hero__copy` na siatkę `1,5fr | 1fr` z myślą o karcie
   faktów `.hero__stats` (hero `/`); `/o-forum` nie ma stats, więc druga kolumna stoi pusta i tekst wygląda na
   zawężony do ok. połowy. Zaczep JAWNY (nie `:has()`) — `id="o-forum-hero"` z pola `anchor` sekcji hero
   (`content/o-forum.md`, kontrakt `hero.twig` l.3/silnik `docs/sections-schema.yaml` — pole `anchor` już tam
   jest, zero zmian silnika); id unikalny w serwisie (sprawdzone). Selektor DOTYKA WYŁĄCZNIE tej strony —
   `/` (ma stats) i heroy ciemne niskie (`:not(.section-hero--media)`, np. `/2026/informacje`) mają inny/brak
   `id` i zostają bajtowo bez zmiany (dowód w raporcie zadania). */
@media (min-width: 768px) {
  #o-forum-hero .hero__copy {
    display: block;
    max-width: none;
  }
}
/* Bez podpisu autora w hero: szablon hero silnika nie ma pola `media.caption` (dług silnika — status.md);
   autor G11 podpisany na /o-forum#cel (foto_caption). */
/* CTA „Pobierz program (PDF)” (style: secondary) na K2 ze zdjęciem: biała obwódka 2 px, pismo biel (PW-136). */
.section-hero--dark .btn--secondary {
  border: 2px solid var(--fb-biel);
  padding: calc(var(--space-sm) - 1px) calc(var(--space-md) - 1px);
  background: transparent;
  color: var(--fb-biel);
}

/* 9j. Liczby `/` (#liczby) — PW-136: 3 kafle bez nagłówka; lekkie dekory z kanonu KV: krawędź cyanu u góry kafla
   (cyan = plama, nie pismo) + D2 sygnet kolor 12 % (wartość K1, kanon §2) wygaszany od lewej w prawym dolnym rogu. */
#liczby .factbar__stat {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-block-start: 4px solid var(--fb-cyan);
}
#liczby .factbar__stat::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 7.5rem;
  aspect-ratio: 333.8182 / 337.09095;   /* viewBox sygnetu, jak §3/§9b */
  translate: 30% 30%;
  z-index: -1;
  pointer-events: none;
  opacity: 0.12;
  background: url("/assets/img/logo/fbiol-sygnet-kolor.svg") no-repeat 0 0 / 100% 100%;
  -webkit-mask-image: linear-gradient(to right, transparent 10%, black 70%);
          mask-image: linear-gradient(to right, transparent 10%, black 70%);
}
#liczby .factbar__value { color: var(--fb-granat); }
/* < 768 px siatka 2×2 (§9g) przy 3 kaflach zostawiała pustą komórkę — ostatni nieparzysty kafel na całą szerokość. */
@media (max-width: 767px) {
  #liczby .factbar__stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* 9k. Lokalizacja `/` (#praktycznie) i „Adres” `/2026/informacje` (#miejsce) — PW-136, uogólnione w PW-143 (jeden
   zestaw reguł dla obu sekcji `location`, selektor `:is(#praktycznie, #miejsce)`; wiersze Dojazd/Parking 1:1 między plikami): stopnie (nazwa miejsca większa, adres pismem tekstu), ikony z
   assets/img/ikony/ jako MASKA koloru granatu (bez hex; kształt z pliku, barwa z tokenu), pogrubienia z markdownu
   w wierszach. Kolejność wierszy = content/2026/index.md: 1 Dojazd (strzałka), 2 Parking (budynek „sala”).
   PW-181 (rewizja właściciela 2026-09-25, pakiet 2): pinezka „lokalizacja.svg” PRZENIESIONA z `.loc__name` na
   `.loc__addr` (§9k-bis niżej) — sugerowała, że klikalna jest nazwa miejsca, a klikalny jest adres. */
:is(#praktycznie, #miejsce) .loc__name { font-size: var(--fs-h2); }
:is(#praktycznie, #miejsce) .loc__date,
:is(#praktycznie, #miejsce) .loc__row-label {
  position: relative;
  padding-inline-start: 2rem;
}
:is(#praktycznie, #miejsce) .loc__date::before,
:is(#praktycznie, #miejsce) .loc__row-label::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.1em;
  width: 1.4rem;
  height: 1.4rem;
  background: var(--fb-granat);
  -webkit-mask: var(--ikona) center / contain no-repeat;
          mask: var(--ikona) center / contain no-repeat;
}
:is(#praktycznie, #miejsce) .loc__date   { --ikona: url("/assets/img/ikony/kalendarz.svg"); }
:is(#praktycznie, #miejsce) .loc__row:nth-child(1) .loc__row-label { --ikona: url("/assets/img/ikony/strzalka-prawo.svg"); }
:is(#praktycznie, #miejsce) .loc__row:nth-child(2) .loc__row-label { --ikona: url("/assets/img/ikony/sala.svg"); }
:is(#praktycznie, #miejsce) .loc__row:nth-child(3) .loc__row-label { --ikona: url("/assets/img/ikony/osoba.svg"); }   /* 3 Dostępność (tylko #miejsce, PW-143): kanon nie ma piktogramu dostępności — „osoba” */
:is(#praktycznie, #miejsce) .loc__row-label { color: var(--fb-granat); font-size: var(--fs-body); }
:is(#praktycznie, #miejsce) .loc__row-label::before { top: 50%; translate: 0 -50%; width: 1.25rem; height: 1.25rem; }
:is(#praktycznie, #miejsce) .loc__row-text { font-size: var(--fs-body); color: var(--fb-text); }
:is(#praktycznie, #miejsce) .loc__row-text strong { color: var(--fb-granat); font-weight: var(--fb-fw-bold); }

/* 9k-bis. Adres z pinezką PRZED tekstem — WAS-26 (opt-in silnika `venue.address_note` + `venue.city_own_line`),
   PW-181 (rewizja właściciela 2026-09-25, pakiet 2, L2 N-1132). Trzy miejsca z blokiem adresu: `/` (#praktycznie),
   `/2026/informacje` (#miejsce), `/kontakt` (#adres — DOŁĄCZONY tu tylko dla pinezki/dopisku/miasta; reszta
   stopni/kolorów `/kontakt` z §9z zostaje bez zmian, PW-168). Ikona = CSS `::before` bez treści → niewidoczna dla
   czytnika ekranu, żadnego `aria-hidden` w markupie nie trzeba dopisywać (WCAG — element czysto dekoracyjny).
   `.loc__addr-note` = mikrotekst „(kliknij, aby nawigować)” POZA linkiem (decyzja: instrukcja obok linku, nie jego
   treść — nazwa dostępna linku zostaje adres + „(otwiera się w nowej karcie)”, WAS-23). `.loc__city` = „Sala
   Kongresowa” jako siostra `.loc__addr` w `.loc__venue` (flex-column, WAS-26) → osobna linia bez dodatkowego CSS
   pozycjonującego. */
:is(#praktycznie, #miejsce, #adres) .loc__addr {
  position: relative;
  padding-inline-start: 2rem;
}
:is(#praktycznie, #miejsce, #adres) .loc__addr::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.1em;
  width: 1.4rem;
  height: 1.4rem;
  background: var(--fb-granat);
  -webkit-mask: url("/assets/img/ikony/lokalizacja.svg") center / contain no-repeat;
          mask: url("/assets/img/ikony/lokalizacja.svg") center / contain no-repeat;
}
:is(#praktycznie, #miejsce, #adres) .loc__addr-note {
  margin-inline-start: 0.35em;
}
:is(#praktycznie, #miejsce, #adres) .loc__city {
  display: block;
  margin-block-start: var(--space-xs, 0.25rem);
}
/* Kolor/rozmiar wg stopni istniejących §9k/§9z: `/` i `/2026/informacje` — jak `.loc__addr` (fb-text, fs-body);
   `/kontakt` (#adres) bez override — zostaje na domyślnym mikrotekście silnika (muted, fs-sm, §9z „zero nowych
   kolorów”), jak dotąd „Sala Kongresowa” w tym samym `.loc__addr`. */
:is(#praktycznie, #miejsce) .loc__addr {
  color: var(--fb-text);
  font-size: var(--fs-body);
}
:is(#praktycznie, #miejsce) .loc__addr-note,
:is(#praktycznie, #miejsce) .loc__city {
  color: var(--fb-text);
  font-size: var(--fs-body);
}

/* 9l. Program `/2026/program` — osoba z profilem (PW-139, rewizja właściciela 2026-09-24 §3): zamiast podkreślenia
   IKONA na końcu linii sugerująca wejście w profil (assets/img/ikony/strzalka-prawo.svg jako MASKA, barwa = currentColor,
   czyli --c-link granat 14,82:1 na bieli / bg-soft — element graficzny ≥ 3:1, WCAG 1.4.1 spełnione przez ikonę,
   nie przez kolor). Dotyczy wyłącznie `a.sch__person` (prelegenci i moderacja w pełnym programie); osoba bez profilu
   (`span.sch__person`) zostaje bez ikony.
   K-27 (PW-221, L2 N-1195/N-1197 a, COFNIĘCIE PW-174 N5): stałe podkreślenie CAŁEGO tekstu linku (nazwisko + afiliacja)
   utrudniało czytanie na gęstym `/2026/program` — zmierzone L2 na zrzucie „przed”. Skoro ikona już samodzielnie spełnia
   WCAG 1.4.1 (element graficzny ≥ 3:1, nie kolor), stałe podkreślenie było wyróżnikiem NADMIAROWYM. Stan spoczynku:
   `text-decoration: none`; hover = sygnał na SAMEJ ikonie (przesunięcie), NIE podkreślenie całego tekstu; focus
   klawiatury = globalny `:focus-visible` (outline, site.css §2) — bez zmian. */
/* PW-164 (rewizja finalna §3 pkt 3): strzałka NIEROZDZIELNA z ostatnim słowem — przy `::after` na <a> przejście słowo→ikona
   (inline-block) było okazją łamania wiersza i na mobile ikona spadała sama (zmierzone 390 px: Kłos, Kiszkowiak, Szewczyk).
   Hook opakowuje ostatnie słowo linku w .sch__nobr (white-space: nowrap) i ikona siedzi na TYM spanie — słowo i ikona mają
   wspólnego rodzica bez łamania. Link bez .sch__nobr (np. dane spoza hooka) zachowuje ikonę na <a> jak w PW-139. */
.section-schedule a.sch__person {
  text-decoration: none;
}
.section-schedule a.sch__person .sch__nobr { white-space: nowrap; }
.section-schedule a.sch__person .sch__nobr::after,
.section-schedule a.sch__person:not(:has(.sch__nobr))::after {
  content: "";
  display: inline-block;
  width: 1em;
  height: 1em;
  margin-inline-start: 0.35em;
  vertical-align: -0.15em;
  background-color: currentColor;
  -webkit-mask: url("/assets/img/ikony/strzalka-prawo.svg") center / contain no-repeat;
          mask: url("/assets/img/ikony/strzalka-prawo.svg") center / contain no-repeat;
  transition: transform 0.15s ease;
}
.section-schedule a.sch__person:hover .sch__nobr::after,
.section-schedule a.sch__person:hover:not(:has(.sch__nobr))::after {
  transform: translateX(0.2em);
}

/* 9m. NISKI CIEMNY HERO podstron — rewizja właściciela 2026-09-24 („ciemny, wysokość zmniejszona do minimum”):
   /2026/prelegenci (PW-140), /2026/informacje (PW-143), /2026/partnerzy i /2026/komitet (PW-144), /kontakt (PW-145),
   /dla-mediow (PW-146). JEDEN wariant: `tone: dark` BEZ `media` (hero `/` i `/o-forum` mają zdjęcie → klasa
   `.section-hero--media` → ten blok ich nie dotyka; wzorzec tła, D2 i fali = §3, tu tylko wysokość).
   Pismo biel na granacie 14,82 (§3); fala-krawędź i D2 zostają, w skali niższego pudełka. Strony prawne (PW-148)
   nie mają sekcji hero (szablon legal silnika) — ich nagłówek dostaje ten sam wariant w §9r przez te same zmienne. */
.section-hero--dark:not(.section-hero--media),
.section:has(> .wrap > .legal-prose) {
  --kv-niski-pad: var(--space-lg);
  --kv-niski-fala-h: clamp(20px, 14px + 2vw, 44px);
  --kv-niski-fala-od-dolu: var(--space-sm);
}
.section-hero--dark:not(.section-hero--media) {
  --kv-fala-h: var(--kv-niski-fala-h);
  padding-block: var(--kv-niski-pad) calc(var(--kv-niski-pad) + var(--kv-fala-h));
}
.section-hero--dark:not(.section-hero--media)::before { bottom: var(--kv-niski-fala-od-dolu); }
.section-hero--dark:not(.section-hero--media) .hero__title { margin-block-end: 0; }
/* PW-164: `/2026/program` jako jedyny niski ciemny hero ma lead i przyciski — rytm pod tytułem wraca (bez tego lead
   klei się do h1). Pozostałe podstrony mają sam eyebrow + h1, więc :has() ich nie dotyka. */
.section-hero--dark:not(.section-hero--media) .hero__title:has(+ :is(.hero__lead, .hero__cta)) { margin-block-end: var(--space-sm); }

/* PW-164 (rewizja finalna §3 „rejestracja”): CTA rejestracji pod pełnym programem — obie sekcje białe, więc dolny padding
   programu + górny CTA (2 × --space-xl = 123 px) + stopka zastrzeżenia dawały ~150–175 px bieli nad panelem CTA. Standardowy
   rytm = JEDEN padding sekcji nad panelem CTA (jak na /, /o-forum, /2026/informacje, gdzie poprzednia sekcja ma tło bg-soft
   i biel nad panelem = 61 px @1440). Górny padding CTA zdjęty tylko w tym sąsiedztwie.
   PW-167 (dodatek PW-164): ta sama reguła dla siatki `/2026/partnerzy` (#partnerzy — sekcja biała, 123 → 61 px). Selektor
   uogólniony listą sąsiadów w :is(); /kontakt i /dla-mediow (też biały sąsiad, 123 px) świadomie POZA listą — nie objęte
   rewizją, zgłoszone w raporcie PW-167.
   PW-168 (L2 N-1058 — rozszerzenie decyzji §3 właściciela „odstęp nad CTA = standardowy rytm” na wszystkie strony, na
   podgląd właściciela): lista sąsiadów zastąpiona regułą OGÓLNĄ — każda BIAŁA sekcja treści przed (białym) CTA. „Biała” =
   nieparzysta w rachubie rytmu §9c (ta sama formuła `of .section:not(.section-hero):not(.section-cta)`; parzyste mają
   bg-soft i tam biel nad panelem to już jeden padding CTA). Obejmuje dotychczasowe #program i #partnerzy (bez zmiany
   wyniku) oraz /dla-mediow #kontakt-media (123 → 61 px). Założenie: sekcja treści nie ma własnego tła innego niż rytm
   §9c — typ z własnym ciemnym tłem przed CTA wymagałby wyjątku (dziś żaden: pomiar wszystkich tras z `cta`). */
main > .section:nth-child(odd of .section:not(.section-hero):not(.section-cta)) + .section-cta { padding-block-start: 0; }

/* 9n. Lista prelegentów — „Zobacz więcej” (PW-140, rewizja §4): pole silnika `card_label` (<span class="sg__card-cta">
   wewnątrz istniejącego <a class="sg__link"> — bez zagnieżdżonych linków). Silnik dosuwa je do dołu kafla
   (margin-block-start:auto w flex-column; kafle w rzędzie siatki mają równą wysokość). Instancja: delikatny przycisk
   z obrysem — obrys i pismo granat na bieli kafla 14,82:1 (≥ 3:1 / ≥ 4,5:1); hover/focus kafla = wypełnienie granatem,
   pismo biel 14,82:1. Obrys focus zostaje na <a> (silnik). */
.section-speaker-grid .sg__card-cta {
  align-self: flex-start;
  margin: auto var(--space-md) var(--space-md);
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--fb-granat);
  border-radius: 0.25rem;
  color: var(--fb-granat);
  line-height: 1.25;
}
.section-speaker-grid .sg__card--link:hover .sg__card-cta,
.section-speaker-grid .sg__card--link:focus-within .sg__card-cta {
  background: var(--fb-granat);
  color: var(--fb-biel);
}

/* 9o. Kafle logotypów na BIAŁYM tle — PW-144 (`/2026/partnerzy`, rewizja właściciela 2026-09-24 §7: „Lepiej będą wyglądać
   logotypy bez przezroczystości.”) i PW-148 (belka w stopce „tak samo jak na /2026/partnerzy”). JEDEN wspólny styl kafla
   dla obu miejsc (dotąd: #partnerzy = --c-surface bg-soft #f2f6f9 silnika, stopka = logo bez kafla na bieli pasa).
   Zawężenie: `#partnerzy` istnieje tylko na /2026/partnerzy; `/o-forum#organizatorzy` (partner-grid na bg-soft, kafle już
   białe przez §9c) NIE jest objęte — jego render zostaje bez zmian. Widoczność 20 znaków na bieli: 0 FAIL
   (tools/check-kontrast-logotypow.py, PW-144).
   PW-182 (rewizja właściciela 2026-09-25: „Gdy prezentacja logotypów jest na białym tle - ramki są niepotrzebne”):
   obrys ZDJĘTY na obu kaflach — zmierzone Playwright (computed background-color kafla I sekcji-rodzica, 1440/320):
   `#partnerzy .plg__tile` stoi na sekcji białej (`rgb(255,255,255)` = `rgb(255,255,255)`, biel-na-bieli) i
   `.footer-belka .fbk__logo` tak samo (`.footer-belka` ma własne białe tło, poza rytmem sekcji §9c) — obrys był
   czystym dekorem bez funkcji rozdzielającej. `/o-forum#organizatorzy` NIE dostaje tej zmiany: tam kafel biały stoi
   na sekcji bg-soft (`rgb(242,246,249)`), więc obrys nadal odróżnia kafel od tła — zostaje bez zmian (patrz wyżej). */
#partnerzy .plg__tile,
.footer-belka .fbk__logo {
  background: var(--fb-biel);
  border: none;
}
.footer-belka .fbk__logo { padding: var(--space-xs) var(--space-sm); }

/* 9p. Profile społecznościowe na /kontakt jako IKONY z nazwą — PW-145 (rewizja §9) + N-980 (instagram.svg z kanonu).
   Piktogram = MASKA z assets/img/ikony/<platforma>.svg (ścieżka z labels.pl.yaml `ikona`, przez `--ikona` na <span>),
   barwa = currentColor linku (--c-link granat 14,82:1 na bieli — element graficzny ≥ 3:1), 24 px (siatka kanonu 24 j).
   Nazwa platformy zostaje widocznym tekstem linku (brand/ikony/README.md §Znaki platform). Stopka bez ikon — bez zmian. */
.social-list--ikony a { gap: var(--space-xs); }
.social-ikona {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  background-color: currentColor;
  -webkit-mask: var(--ikona) center / contain no-repeat;
          mask: var(--ikona) center / contain no-repeat;
}

/* 9q. /dla-mediow #kontakt-media — PW-146 (rewizja §10): zamiast przycisku z adresem KARTA z ikoną koperty
   (assets/img/ikony/email.svg jako MASKA, barwa = currentColor) i klikalnym `mailto:` z widocznym adresem jako tekstem
   linku. Typ `cta` silnika bez zmian (nagłówek + podtytuł dosłownie); kartą jest panel `.cta__inner` (obrys + --c-surface),
   przycisk `.btn` w tej jednej sekcji traci formę przycisku i staje się linkiem tekstowym (granat --c-link na --c-surface
   bg-soft ≈ 13,9:1; ikona ≥ 3:1). Focus: obrys sekcji (bez zmian). */
#kontakt-media .cta__actions .btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: var(--chrome-tap);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--c-link);
  font-size: var(--fs-lead);
  font-weight: var(--fb-fw-bold);
  text-decoration: underline;
  text-underline-offset: 0.25em;
  overflow-wrap: anywhere;
}
#kontakt-media .cta__actions .btn::before {
  content: "";
  flex: 0 0 auto;
  width: 1.5em;
  height: 1.5em;
  background-color: currentColor;
  -webkit-mask: url("/assets/img/ikony/email.svg") center / contain no-repeat;
          mask: url("/assets/img/ikony/email.svg") center / contain no-repeat;
}
#kontakt-media .cta__actions .btn:hover { text-decoration-thickness: 2px; }

/* 9r. Strony prawne (/regulamin, /polityka-prywatnosci, /polityka-cookies) — PW-148 (rewizja właściciela 2026-09-24 §14):
   nagłówek strony jako CIEMNY NISKI HERO, ten sam wariant co §9m (zmienne --kv-niski-* deklarowane tam dla obu
   selektorów; fala = --kv-fala-maska z :root, §3). Szablon `legal` silnika NIE ma sekcji hero — jest <section class="section">
   <div class="wrap"><h1>…</h1><article class="legal-prose">…; bez override szablonu i bez core: pas granatu na całą
   szerokość okna = h1::before (100vw, wyśrodkowany na kolumnie treści, przycięty przez sekcję `overflow-x: clip` — clip
   nie tworzy kontenera przewijania ani poziomego przewinięcia strony), fala-krawędź = h1::after. Treść prawna bez zmian.
   ODSTĘPSTWO od §9m: bez D2 (sygnet 30 %) — h1 ma dwa pseudo-elementy i oba są zajęte (pas, fala); w niskim hero D2 i tak
   wystaje w ok. 1/4 poza prawą krawędź. Pismo biel na granacie 14,82 (§3). */
.section:has(> .wrap > .legal-prose) {
  isolation: isolate;
  overflow-x: clip;
  padding-block-start: 0;
}
.section > .wrap:has(> .legal-prose) > h1 {
  position: relative;
  margin-block: 0 var(--space-md);
  padding-block: var(--kv-niski-pad) calc(var(--kv-niski-pad) + var(--kv-niski-fala-h));
  color: var(--fb-biel);
}
.section > .wrap:has(> .legal-prose) > h1::before,
.section > .wrap:has(> .legal-prose) > h1::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 100vw;
  translate: -50% 0;
  pointer-events: none;
}
.section > .wrap:has(> .legal-prose) > h1::before { inset-block: 0; z-index: -1; background: var(--fb-granat); }
.section > .wrap:has(> .legal-prose) > h1::after {
  bottom: var(--kv-niski-fala-od-dolu);
  height: var(--kv-niski-fala-h);
  background-color: var(--fb-cyan);
  -webkit-mask: var(--kv-fala-maska) center / 100% 100% no-repeat;
          mask: var(--kv-fala-maska) center / 100% 100% no-repeat;
}

/* 9s. Galeria /galeria — PW-154 (L2 N-1002): typ INSTANCJI `galeria` (templates/sections/galeria.twig), sekcja per rok.
   PW-169 (rewizja finalna 2026-09-25 §11, L2 N-1034): siatka 2 kolumny od najwęższego ekranu (było 1 < 768) / 3 (≥ 1024),
   BEZ podpisów (równe rzędy) — podpis autora w lightboxie silnika (WAS-21, assets/engine/lightbox.css; sygnał powiększenia
   = znaczek lupy silnika na miniaturze). Komórki JEDNEJ proporcji 4:3 (object-fit: cover); kadry pionowe G03/G22/G23
   kadrowane OD GÓRY (decyzja właściciela; w lightboxie cały kadr). Podtytuł z datą ciemniej (--c-heading, granat 14,82:1
   zamiast --c-text-muted silnika), linia o autorach pod siatką = mikrotekst (--c-text-muted 4,63:1). Zero nowych kolorów. */
.galeria__lista {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-sm);
  margin: var(--space-lg) 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 768px)  { .galeria__lista { gap: var(--space-md); } }
@media (min-width: 1024px) { .galeria__lista { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.galeria__foto { margin: 0; }
.galeria__link { display: block; }
.galeria__foto img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--c-border);
}
.galeria__foto--pion img { object-position: 50% 0; }
.section-galeria .section__lead { color: var(--c-heading); }
.galeria__autorzy {
  margin-block: var(--space-md) 0;
  color: var(--c-text-muted);
  font-size: var(--fs-sm);
  line-height: 1.4;
}

/* 9t. Rewizja finalna 2026-09-25, pakiet 1 (PW-162, L2 N-1021; clients/fbiol/docs/sesje/2026-09-25-fbiol-www-rewizja-finalna.md §1).
   1-K4 (treść WSZYSTKICH hero do 1440 px, wyjście z siatki 1120 px kontenera) — COFNIĘTE PW-184 (L2 N-1099,
   2026-09-25): słowo właściciela „Szerokość kontenera powinna być analogiczna jak menu i content strony. Obecnie
   tekst w hero wyjeżdża z lewej za bardzo.” `.hero__inner` w hero.twig ma DWIE klasy (`section__inner hero__inner`)
   — bez override 1-K4 dziedziczy z powrotem `max-width: var(--content-width)` = 1120 px z biblioteki silnika
   (core/web-app/assets/sections.css .section__inner), czyli TĘ SAMĄ kolumnę co nagłówek (.site-header .wrap) i
   treść sekcji poniżej — zero nowej reguły, usunięcie 1-K4 wystarcza. Obejmuje `.section-hero .hero__inner`
   (hero `/` i `/o-forum` ze zdjęciem, niskie ciemne pasy tytułowe podstron — ten sam typ `hero`, np. tone: dark
   na /2026/informacje). Pomiar Playwright (raport PW-184): lewa krawędź tekstu hero = logo nagłówka = h2 pierwszej
   sekcji, 1440 i 1920, `/`, `/o-forum`, `/2026/informacje`.
   Z 1-K4 USUNIĘTA także reguła nagłówka stron prawnych (`.section > .wrap:has(> .legal-prose) > h1`,
   margin-inline+padding-inline do kolumny 1440 px) — istniała WYŁĄCZNIE po to, by h1 stron prawnych stał w linii
   z hero rozszerzonym do 1440 px; skoro hero wraca do 1120 px, ten override nie ma już celu. Cofnięcie wraca do
   stanu PRZED 1-K4: h1 stron prawnych w wąskiej kolumnie `.wrap` (--prose-width 760 px, §9r) — poza zakresem tej
   rewizji (właściciel mówił o hero, nie o stronach prawnych) i BEZ REGRESJI względem stanu przed PW-162.
   Pozostałe części PW-162 (dla-kogo 3×2/2×3/1, margines .cta__inner na mobile) NIETKNIĘTE — patrz niżej. */
/* dla-kogo (`/#dla-kogo`, value-cards 6 kart) — siatka 3×2 (≥ 1024) · 2×3 (768–1023) · 1 kolumna (< 768) zamiast auto-fit 4+2.
   Progi = istniejące w site.css (768, 1024). Treść bez zmian. */
#dla-kogo .vc__grid { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 768px)  { #dla-kogo .vc__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { #dla-kogo .vc__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* rejestracja (typ `cta`, każda podstrona) — panel `.cta__inner` (width 100 % z `.section__inner`) na mobile stał na krawędziach
   okna (pomiar 390: left/right 0) → 16 px marginesu bocznego; na szerokich ekranach wąski kontener i tak jest węższy. */
.section-cta .cta__inner { width: calc(100% - 2rem); }

/* 9u. Rewizja finalna 2026-09-25, pakiet 2 — /o-forum (PW-163, L2 N-1022 + dodatek N-1026; ślad
   clients/fbiol/docs/sesje/2026-09-25-fbiol-www-rewizja-finalna.md §2).
   (a) Siatka znaków `partner-grid` (/o-forum#organizatorzy i /2026/partnerzy — ten sam komponent silnika): rząd niepełny
       WYŚRODKOWANY zamiast auto-fill silnika, który zostawiał pustą prawą połowę 6 kolumn (grupa 3 i 2 znaków). Szerokość
       kafla = ta sama co w pełnym rzędzie (liczba kolumn --plg-kol: 2 · 3 od 560 · 4 od 768 · 6 od 1024 — jak auto-fill
       10rem silnika przy 390/768/1440), więc grupa 12 znaków wygląda jak dotąd, a ostatni rząd każdej grupy stoi pośrodku.
   (b) Grupy Organizator i Współorganizator — SZERSZE kafle (3 kolumny od 768) i wyższy slot znaku (N-1026: podpis
       „Prezydent Olsztyna / Robert Szewczyk” wpalony w znak belka/01 ma być czytelny: wersaliki = 33/480 wysokości pliku
       470×480, czyli slot 11rem = 176 px → 12,1 px przy 1440; 9,5rem = 152 px → 10,5 px przy 390 — pomiar Playwright w
       raporcie PW-163). Grupę rozpoznaje jej ZNAK (plik belki 01 = Organizator, 04 = Współorganizator — nazwy plików
       hubu, których pilnują testy belki), nie pozycja — kolejność kategorii w agenda.json jest danymi innego toru.
       Współorganizator: slot niższy (7rem / 5rem) — znaki poziome (CBOMB 2,8:1, CEI 1,8:1) przy 11rem byłyby masą
       większą niż znak Prezydenta.
   (c) /o-forum#organizatorzy-opisy (typ instancji `karty`): zdjęcie A07 nad siatką 4 kart; karty 2×2 od 768, 1 kolumna
       niżej (reguła `vc__grid` silnika auto-fit 16rem dawała 3+1 przy 1440).
   PW-161 (L2 N-1107/N-1108, dodatek 2026-09-25): selektory `src*=` niżej były `src$=` (kończy się na) — po dodaniu
   `?v=<sha256[:8]>` do `logo` (fbiol_agenda_belka(), cache-busting) URL już NIE kończy się na `.png`, więc `$=`
   przestałoby pasować (fałszywy powrót do slotu domyślnego, po cichu). `*=` (zawiera) pasuje niezależnie od
   ogona `?v=..` — w tym `:not([src*=…])` (linia niżej), gdzie `$=` po zmianie zaczęłoby PRZESTRZEC pasować
   znakowi Prezydenta i zaliczałoby go do „pozostałych” (zły max-height). Zmierzone Playwright: rozmiary (px)
   znaku Prezydenta i CBOMB w siatce identyczne przed/po (raport PW-161). */
.section-partner-grid .plg__grid {
  --plg-kol: 2;
  gap: var(--plg-gap, var(--space-sm));   /* PW-167: odstęp jako zmienna (domyślnie = silnik); §9v powiększa go na /2026/partnerzy */
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}
.section-partner-grid .plg__tile {
  box-sizing: border-box;   /* podstawa flex = pełna szerokość kafla z paddingiem i obrysem (inaczej rząd się przelewa) */
  flex: 0 0 calc((100% - (var(--plg-kol) - 1) * var(--plg-gap, var(--space-sm))) / var(--plg-kol));
}
@media (min-width: 560px)  { .section-partner-grid .plg__grid { --plg-kol: 3; } }
@media (min-width: 768px)  { .section-partner-grid .plg__grid { --plg-kol: 4; } }
@media (min-width: 1024px) { .section-partner-grid .plg__grid { --plg-kol: 6; } }

.section-partner-grid .plg__group:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]) { --plg-logo-h: 9.5rem; }
.section-partner-grid .plg__group:has(img[src*="/belka/04-cbomb.png"]) { --plg-logo-h: 5rem; }
.section-partner-grid .plg__group:is(:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]),
                                     :has(img[src*="/belka/04-cbomb.png"])) .plg__grid { --plg-kol: 2; }
.section-partner-grid .plg__group:is(:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]),
                                     :has(img[src*="/belka/04-cbomb.png"])) .plg__tile { padding: var(--space-sm); }
.section-partner-grid .plg__group:is(:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]),
                                     :has(img[src*="/belka/04-cbomb.png"])) .plg__logo { max-height: var(--plg-logo-h); }
/* masa optyczna w grupie Organizator: slot pełnej wysokości potrzebny jest znakowi Prezydenta (podpis nad herbem, sam herb
   ≈ 75 % wysokości pliku); Miasto Olsztyn i RCB w pełnym slocie przerastały go masą → 75 % slotu (≈ wysokość herbu). */
.section-partner-grid .plg__group:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"])
  .plg__logo:not([src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]) { max-height: calc(var(--plg-logo-h) * 0.75); }
@media (min-width: 768px) {
  .section-partner-grid .plg__group:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]) { --plg-logo-h: 11rem; }
  .section-partner-grid .plg__group:has(img[src*="/belka/04-cbomb.png"]) { --plg-logo-h: 7rem; }
  .section-partner-grid .plg__group:is(:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]),
                                       :has(img[src*="/belka/04-cbomb.png"])) .plg__grid { --plg-kol: 3; }
  .section-partner-grid .plg__group:is(:has(img[src*="/belka/01-prezydent-olsztyna-herb-olsztyna.png"]),
                                       :has(img[src*="/belka/04-cbomb.png"])) .plg__tile { padding: var(--space-md); }
}

.section-karty .karty__foto { margin: 0 0 var(--space-lg); }
.section-karty .karty__foto img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--c-border);
}
@media (min-width: 768px) { .section-karty .karty__foto img { aspect-ratio: 21 / 9; object-position: 50% 80%; } }   /* kadr na panelistów, nie na napis ekranu */
.section-karty .karty__grid { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 768px) { .section-karty .karty__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* 9v. Rewizja finalna 2026-09-25, pakiety 4–5 — lista prelegentów i profile (PW-165, L2 N-1027/N-1028/N-1029; ślad
   clients/fbiol/docs/sesje/2026-09-25-fbiol-www-rewizja-finalna.md §4–§5).
   (a) Lista /2026/prelegenci — funkcja pod nazwiskiem większa (~15 px; silnik: --fs-sm 13,3 px) i w kolorze tekstu
       podstawowego (--c-text 9,08:1 na bieli; silnik: --c-text-muted). Brand nie ma stopnia 15 px — wartość instancji.
       Równa wysokość kafli w rzędzie i „Zobacz więcej” przy dolnej krawędzi zapewnia już siatka + §9n (pomiar w PW-165).
   (b) Profil — etykieta panelu zwykłymi literami (silnik: wersaliki + rozstrzelenie); tekst w danych ma już właściwą
       wielkość liter („Panel II · Współpraca międzynarodowa — …”).
   (c) Profil ≥ 768 px (układ dwukolumnowy silnika) — portret PRZYKLEJONY przy przewijaniu długiego biogramu (znika pusta
       kolumna pod zdjęciem); `top` = wysokość przyklejonego nagłówka + odstęp, jak §9d (idea__foto).
   (d) Udostępnianie ikonami (WAS-20, opt-in labels.pl.yaml `profil_share_*`): piktogram kanonu jako MASKA w barwie linku
       (--c-link granat 14,82:1 — jak §9p /kontakt; <img> silnika z SVG `currentColor` rysowałby czernią), pole 44 px
       (--chrome-tap). Ścieżki masek = te same pliki co `icon` w labels.pl.yaml. Przycisk „Kopiuj link” z obrysem granatu
       (jak §9n), komunikat „Skopiowano link” obok przycisku. */
.section-speaker-grid .sg__role {
  color: var(--c-text);
  font-size: 0.9375rem;   /* 15 px przy 16 px bazy */
}

.profil__panel {
  letter-spacing: normal;
  text-transform: none;
}
@media (min-width: 768px) {
  .profil__media {
    position: sticky;
    top: calc(var(--chrome-header-h) + var(--space-md));
  }
}

.profil__share-icon {
  min-width: var(--chrome-tap);
  min-height: var(--chrome-tap);
  border-radius: 0.25rem;
  color: var(--c-link);
}
.profil__share-icon:hover { background: var(--c-surface); }
.profil__share-icon img { display: none; }
.profil__share-icon::before {
  content: "";
  width: 24px;
  height: 24px;
  background-color: currentColor;
  -webkit-mask: var(--ikona) center / contain no-repeat;
          mask: var(--ikona) center / contain no-repeat;
}
.profil__share-icon--email    { --ikona: url("/assets/img/ikony/email.svg"); }
.profil__share-icon--linkedin { --ikona: url("/assets/img/ikony/linkedin.svg"); }
.profil__share-icon--facebook { --ikona: url("/assets/img/ikony/facebook.svg"); }
.profil__share-icon--x        { --ikona: url("/assets/img/ikony/x.svg"); }
.section .profil__share-copy {
  border-color: var(--fb-granat);
  color: var(--fb-granat);
  font-size: var(--fs-sm);
}
.profil__share-status {
  color: var(--c-text);
  font-weight: 600;
}

/* 9w. Rewizja finalna 2026-09-25, pakiet 6 — /2026/informacje (PW-166, L2 N-1028; ślad
   clients/fbiol/docs/sesje/2026-09-25-fbiol-www-rewizja-finalna.md §6).
   (a) FAQ #faq w PEŁNYM kontenerze jak pozostałe sekcje (--content-width; silnik trzyma faq w wyśrodkowanej kolumnie
       prozy .section__inner--prose = --prose-width 760 px) — wspólna oś z resztą strony; odpowiedzi też na całą szerokość
       (silnik: .fq__a max-width --prose-width). Rekomendacja „kolumna prozy przy lewej krawędzi” odrzucona przez właściciela.
   (b) „Adres” #miejsce — siatka wierszy: Dojazd | Parking w jednym rzędzie, „Dostępność” (3. wiersz) na PEŁNĄ szerokość
       pod nimi (silnik: auto-fit minmax(14rem) → 3 kolumny w rzędzie przy 1080 px). Selektor wspólny §9k, ale 3. wiersz
       istnieje tylko w #miejsce — na `/` #praktycznie (Dojazd, Parking) siatka 2-kolumnowa bez zmian w wyglądzie.
       Pełna szerokość 3. wiersza obowiązuje w każdej szerokości (przy auto-fit silnika < 768 px nie zostaje dziura);
       na `/` przy 2 wierszach auto-fit i tak dawał 2 równe kolumny — obraz #praktycznie bez zmian (pomiar PW-166).
       Linki mailto:/tel: w wierszu = `.section a` (--c-link granat) + pogrubienie z markdownu (§9k `.loc__row-text strong`). */
#faq .section__inner--prose { max-width: var(--content-width); }
#faq .fq__a { max-width: none; }

:is(#praktycznie, #miejsce) .loc__row:nth-child(n + 3) { grid-column: 1 / -1; }
@media (min-width: 768px) {
  :is(#praktycznie, #miejsce) .loc__rows { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 9x. Rewizja finalna 2026-09-25, pakiet 7 — /2026/partnerzy (PW-167, L2 N-1030; ślad
   clients/fbiol/docs/sesje/2026-09-25-fbiol-www-rewizja-finalna.md §7 „partnerzy” pkt 1).
   Znaki w siatce WIĘKSZE, jak belka W-K2: slot 4rem (< 768) / 5.5rem (≥ 768) × `--skala` z tabeli §9e (ta sama tabela co
   stopka — jedno źródło skali optycznej; zaczep `src` pliku belki). Kafle SZERSZE: 3 kolumny od 768 (jak grupy
   Organizator/Współorganizator z §9u — jedna siatka strony), 2 poniżej; odstępy większe (--plg-gap: md / lg, między
   grupami xl). Grupy Organizator i Współorganizator: sloty z §9u zostają (wyższa specyficzność — `:where()` tutaj), ich
   liczba kolumn = ta sama (2 / 3). Zawężenie `#partnerzy` = tylko ta strona; /o-forum#organizatorzy bez zmian. */
#partnerzy .plg__grid { --plg-kol: 2; --plg-gap: var(--space-md); --plg-znak-h: 4rem; }
:where(#partnerzy) .plg__tile { padding: var(--space-sm); }
:where(#partnerzy) .plg__logo { max-height: calc(var(--plg-znak-h) * var(--skala, 1)); }
/* 2026-10-02 (słowo właściciela „Wprowadź poprawkę. Zostańmy przy 0,8”): znak SZEROKI (Edugika, ~6:1) jest ograniczony
   szerokością kafla, nie slotem — samo `max-height × --skala` go nie zmniejszało (zmierzone na produkcji: 303×50 przy 1 i przy 0,8).
   Skala < 1 zawęża więc także szerokość; `min(100%, …)` = znaki ze skalą ≥ 1 bez zmian (dalej 100% kafla). */
:where(#partnerzy) .plg__logo { max-width: min(100%, calc(100% * var(--skala, 1))); }
/* < 560 px (2 kolumny, kafel ~167 px przy 390) znaki POZIOME z mikrotekstem — MSWiA (06, ~5:1) i MON (07, ~3,8:1) — ogranicza
   szerokość, nie slot (zmierzone 29 / 39 px wysokości) → cały rząd dla każdego z nich. Oba stoją na początku grupy
   „Patronat honorowy” (kolejność agenda.json.belka), więc pozostałe 10 znaków dalej tworzy pełne pary — bez pustej komórki. */
@media (max-width: 559px) {
  #partnerzy .plg__tile:is(:has(img[src*="/belka/06-"]), :has(img[src*="/belka/07-"])) { flex-basis: 100%; }
}
@media (min-width: 768px) {
  #partnerzy .plg__grid { --plg-kol: 3; --plg-gap: var(--space-lg); --plg-znak-h: 5.5rem; }
  :where(#partnerzy) .plg__tile { padding: var(--space-md); }
  :where(#partnerzy) .plg__group + .plg__group { margin-block-start: var(--space-xl); }
}

/* 9y. Rewizja finalna 2026-09-25, pakiet 8 — /2026/komitet (PW-167, L2 N-1030; ślad §8 „komitet-naukowy” pkt 2 i
   „komitet-organizacyjny”). Oba składy jako LISTA 2-kolumnowa BEZ inicjałów zamiast kart 4 × ~250 px z kaflem inicjałów
   (§9h). Markup silnika `person-grid` bez zmian: `<ul role="list">` osób zostaje, portret z inicjałami (i tak
   aria-hidden) ukryty; kolumny = CSS `columns` (kolejność czytania w dół kolumny = kolejność DOM = kolejność źródła),
   1 kolumna < 768 px. Osoba = nazwisko z tytułami (1–2 linie) + afiliacja pod spodem, separator = --c-border (dekor).
   Zawężenie kotwicami sekcji — inne `person-grid` (dziś brak) bez zmian. */
:is(#komitet-naukowy, #komitet-organizacyjny) .pg__grid {
  display: block;
  columns: 1;
  column-gap: var(--space-xl);
  margin: 0;
  padding: 0;
}
@media (min-width: 768px) {
  :is(#komitet-naukowy, #komitet-organizacyjny) .pg__grid { columns: 2; }
}
:is(#komitet-naukowy, #komitet-organizacyjny) .pg__card {
  break-inside: avoid;
  border: 0;
  border-block-end: 1px solid var(--c-border);
  background: none;
}
:is(#komitet-naukowy, #komitet-organizacyjny) .portrait { display: none; }
:is(#komitet-naukowy, #komitet-organizacyjny) .pg__body { gap: 0.125rem; padding: var(--space-sm) 0; }
:is(#komitet-naukowy, #komitet-organizacyjny) .pg__name {
  font-family: var(--font-body, inherit);
  font-size: var(--fs-body);
  font-weight: var(--fb-fw-bold);
  line-height: 1.35;
}
:is(#komitet-naukowy, #komitet-organizacyjny) .pg__role { color: var(--fb-text); }

/* 9z. Rewizja finalna 2026-09-25, pakiet 9 — /kontakt (PW-168, L2 N-1031; ślad §9 „adres + napisz-do-nas + social”).
   Typ INSTANCJI `lokalizacja-kontakt` (templates/sections/lokalizacja-kontakt.twig): siatka mapa | info silnika, w prawej
   kolumnie pod adresem bloki `.lk__blok` (h3 + linki mailto/tel z ikoną-maską jak §9p/§9q, profile z ikonami §9p).
   Pole `note` silnika (`.loc__note`) niesie oba bloki — jego mikrotekst (fs-sm, muted) tu wraca do tekstu ciągłego.
   Linia daty i godzin: ciemniejsza (granat --c-heading zamiast --c-text), bez wersalików i rozstrzelenia — tylko w tej
   sekcji (`/`, `/2026/informacje` bez zmian). Zero nowych kolorów. */
.loc__note:has(> .lk__blok) {
  margin-block-start: var(--space-lg);
  color: var(--fb-text);
  font-size: var(--fs-body);
}
.lk__blok + .lk__blok {
  margin-block-start: var(--space-md);
  padding-block-start: var(--space-md);
  border-block-start: 1px solid var(--c-border);
}
.lk__title {
  margin: 0 0 var(--space-xs);
  font-size: var(--fs-h3);
}
.lk__kontakt {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-lg);
  margin: 0;
  padding: 0;
  list-style: none;
}
.lk__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--chrome-tap);
  font-weight: var(--fb-fw-bold);
}
/* PW-174 N6 (L2 N-1048): e-mail i telefon łamią się w dowolnym miejscu zamiast wypychać kolumnę — zero poziomego przewijania
   przy 320 px (lustro `#kontakt-media .btn` §9q i `.loc__addr-link` silnika). */
.lk__link .lk__tekst { min-width: 0; overflow-wrap: anywhere; }
.social-list.lk__social { justify-content: flex-start; margin: 0; }
.loc__info:has(.lk__blok) .loc__date {
  color: var(--c-heading);
  letter-spacing: normal;
  text-transform: none;
}

/* 9za. Rewizja finalna 2026-09-25, pakiet 10 — /dla-mediow #zaproszenie (PW-168, L2 N-1031; ślad §10 „zaproszenie” pkt 1–2).
   Kolumna tekstu ~720 px (45rem) zamiast połowy siatki `idea` silnika (~510 px) — bez pustej prawej połowy; lista
   paneli (Parsedown `<ul>` z elementu `copy[]`) w rytmie akapitów. */
#zaproszenie .idea__grid { grid-template-columns: minmax(0, 1fr); }
#zaproszenie .idea__copy { max-width: 45rem; }
#zaproszenie .idea__copy ul { margin-block: var(--space-sm) 0; padding-inline-start: 1.25em; }
#zaproszenie .idea__copy li + li { margin-block-start: var(--space-xs); }
#zaproszenie .idea__copy ul + p { margin-block-start: var(--space-md); }

/* 9zb. Strona 404 — PW-169 (rewizja finalna 2026-09-25 §12–13, L2 N-1035; klasy silnika WAS-22 `.not-found*`).
   Blok na siatce strony: ta sama oś co treść sekcji (kontener = `.section__inner` silnika: --content-width + gutter;
   było: wyśrodkowana kolumna 560 px bez marginesów, x≈83 zamiast osi treści), wyrównanie do lewej. „404” duże, granatem
   (--c-heading 14,82:1; było: eyebrow drobny i blady). Linki Program/Prelegenci (copy.not_found.links) w jednym rzędzie
   z przyciskiem, zawijane pod niego na wąskim ekranie. Zero nowych kolorów. */
.not-found__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--content-width);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
  text-align: start;
}
.not-found__code {
  display: block;
  margin: 0 0 var(--space-sm);
  color: var(--c-heading);
  font-size: clamp(4.5rem, 14vw, 8rem);
  font-weight: var(--fb-fw-bold);
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
}
.not-found__code::before,
.not-found__code::after { content: none; }
.not-found__body { max-width: 40rem; margin: var(--space-sm) 0 var(--space-lg); }
.not-found__links {
  display: inline-flex;
  flex-wrap: wrap;
  vertical-align: middle;
  gap: var(--space-xs) var(--space-md);
  margin: var(--space-sm) 0 0;
}
@media (min-width: 768px) { .not-found__links { margin: 0 0 0 var(--space-lg); } }
.not-found__links a { font-weight: var(--fb-fw-bold); }

/* PW-180 K-3 — pigułka programu na `/` (`.section-schedule--compact`), silnik: sections.css §schedule. */
/* K-3b: nagłówek dnia w dwóch liniach ("12 października 2026" / "poniedziałek, godz. …") — tylko w pigułce;
   `/2026/program` (bez klasy compact) zostaje w jednej linii. */
.section-schedule--compact .sch__day-label {
  display: block;
}
/* K-3c: więcej odstępu w pionie między pozycjami programu w pigułce — z var(--space-sm, 2mm) na
   var(--fb-space-3, 3mm) per strona (+50%, dobrane wzrokowo @1440/@320 — zrzuty PW-180). */
.section-schedule--compact .sch__row {
  padding-block: var(--fb-space-3, 0.75rem);
}

/* PW-182 K-6 b (rewizja właściciela 2026-09-25, L2 N-1096): więcej powietrza między nagłówkiem grupy partner-grid
   („Organizator”, „Współorganizator”, „Patronat honorowy”, „Patronat medialny”) a pierwszym rzędem logotypów —
   silnik: --space-md (18,9 px) → instancja: --space-lg (zmierzone niżej w kwicie). Tylko siatki logotypów FBiOL. */
.section-partner-grid .plg__group-label { margin-bottom: var(--space-lg); }

/* PW-248 (L2 N-1284) — baner zgody (#consentBanner, position:fixed, z-index 30) prześwituje przez tło
   lightboxa galerii przy 390 px: `.lightbox` (core/web-app/assets/lightbox.css) maluje swoje tło jako
   `color-mix(in srgb, var(--lightbox-bg) 94%, transparent)` — 94 % nieprzezroczyste, więc 6 % tego, co
   jest NIŻEJ w kolejności malowania (tu: baner zgody — z-index 30 < 9999 lightboxa, więc TEORETYCZNIE
   pod spodem, ale widoczne przez alfę), przebija jako „duch" kopii/przycisków banera pod strzałkami
   prev/next (elementFromPoint na 3 przyciskach lightboxa i tak wskazuje SAM przycisk — to defekt
   WIZUALNY, nie przechwycenie kliknięć). Poprawka WYŁĄCZNIE warstwy lightboxa instancji, selektorem
   `.lightbox` (core/web-app NIE dotknięty, diff = 0; `.consent-banner`/`#consentBanner`/consent.js/
   consent-banner.twig BEZ zmian, zgodnie z zakazem brief PW-248). `::before` z z-index:-1 jest ujemnym
   potomkiem stackingu `.lightbox` (position:fixed z-index:9999 z lightbox.css) — maluje się PO własnym
   (94-procentowym) tle `.lightbox`, więc w pełni je przykrywa nieprzezroczystym kolorem, ale w tej samej
   lokalnej kolejności co reszta `.lightbox` (obraz/przyciski/podpis) maluje się PÓŹNIEJ nad nim — więc
   nic z wyglądu treści lightboxa się nie zmienia, znika tylko „duch" tego, co jest NA ZEWNĄTRZ (baner,
   ewentualnie sticky CTA z z-index 20). */
.lightbox::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--lightbox-bg, var(--c-heading, #111));
}
