/* =============================================================================
   foreto-kit — fragmenty przeniesione do motywu.

   Reguły są kopiowane ze źródła BEZ ZMIAN, razem z komentarzami, żeby dało się
   je porównać z oryginałem linia po linii. Zmienne --foreto-* rozwiązuje
   kit-bridge.css, który mapuje je na theme.json.

   Źródło: projekt Claude Design „Baza komponentów do stworzenia",
   foreto-kit/kit.css (3717 linii). Kolejne sekcje dokładamy przy budowie
   kolejnych bloków — plik rośnie, nigdy się nie przepisuje.

   Przeniesione zakresy źródła, w kolejności występowania w TYM pliku.
   Zakresy z wcześniejszych podejść są PRZEPISANE Z KOMENTARZY SEKCJI i nie
   były w tej turze sprawdzane ze źródłem; zakresy oznaczone [v] sprawdzono
   linia po linii przy tym dopisaniu.
     13–195    korzeń i reset, typografia, Container, Section, tony data-tone
     196–244   Stack, Grid (bez progu 640 — dołożony niżej z 244–283)
     1938–1969 NavBar (bez podmenu — nie przeniesione)
     484–528   IconButton
     1698–1723 KeyValue
     2245–2320 NavDrawer (bez podmenu — menu jest płaskie)
     2072–2091 SkipLink
     337–341, 366–447, 572–574  SectionTitle, Button, Link
     449–462   Button: warianty pozostałe
     533–546   ButtonGroup — UWAGA: kopia w motywie ROZCHODZI SIĘ ze źródłem
               (--vertical ma tu align-items: stretch zamiast flex-start
               i nowrap, --stretch ma flex: 1 1 auto zamiast 1 1 0, brakuje
               reguły --vertical > .foreto-btn--full). Do zestrojenia.
     244–283   Grid: próg 640, pozostałe sterowanie i presety
     288–297   Card
     3165–3206 Testimonial
     3208–3225 CheckList
     3387–3409 LogoTile
     3411–3455 StepCard
     [v] 300–324    Media (kadr dwuwarstwowy) + warianty ramki
     [v] 1725–1748  Stat
     [v] 1825–1831  przełamanie ≤480 dla Stat i KeyValue (wyjęte z bloku
                    PRZEŁAMANIA WARSTWY DATA 1792–1831; reszta bloku dotyczy
                    tabeli, paginacji i DataListy — nie przeniesiona)
     [v] 2750–2834  Disclosure / Accordion
     [v] 2843–2845  Avatar
     [v] 2847–3163  Timeline: szyna, kropki, pozycje parzyste i nieparzyste,
                    daty, przełącznik „pokaż wszystkie", warianty responsywne
                    (1181, 760) i kontenerowe (.foreto-timeline-box) — CAŁY
                    komponent
     [v] 3227–3243  ScrollMarker
     [v] 3305–3333  TraitBar (bez wariantów kontenerowych z 3456–3497: siedzą
                    w blokach wspólnych dla kafli grupy 9, których tu nie ma)
   ========================================================================== */

/* --- Korzeń i reset -------------------------------------------------------
   Odpowiednik base/reset.css i base/a11y.css systemu, zawężony do .foreto-root,
   żeby kit nie narzucał niczego stronie, w którą go wpinasz. Bez tej warstwy
   „niezależny zestaw" jest niezależny tylko na papierze: linki wracają do
   przeglądarkowego niebieskiego, listy dostają punktory, a nagłówki marginesy. */
.foreto-root, .foreto-root *, .foreto-root *::before, .foreto-root *::after { box-sizing: border-box; }
.foreto-root {
  font-family: var(--foreto-font);
  font-size: var(--foreto-font-size-body);
  font-weight: 400;
  line-height: var(--foreto-line-body);
  letter-spacing: 0;
  color: var(--foreto-ink);
  background: var(--foreto-page);
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}
/* Selektory elementów są w :where(), żeby reset miał specyficzność JEDNEJ klasy
   (.foreto-root) i nigdy nie przebijał klas komponentów. Bez tego
   .foreto-root button (klasa + typ) wygrywa z .foreto-btn--solid (klasa)
   i przycisk traci tło oraz dopełnienie — sprawdzone pomiarem, nie założeniem. */
.foreto-root :where(h1, h2, h3, h4, h5, h6, p, figure, blockquote) { margin: 0; }
.foreto-root :where(ul, ol) { margin: 0; padding: 0; list-style: none; }
.foreto-root :where(img, svg, video) { display: block; max-width: 100%; }
.foreto-root :where(button) { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
/* Zaznaczenie tekstu jest częścią palety, nie ustawieniem przeglądarki:
   czerwień z bielą w treści, czerń z bielą na czerwonym paśmie.             */
.foreto-root ::selection {
  background: var(--foreto-selection-bg, var(--foreto-red));
  color: var(--foreto-selection-fg, #FFFFFF);
}
.foreto-root :where(a) { color: inherit; text-decoration: none; }
.foreto-root :where(a:hover, a:focus-visible) { color: var(--foreto-text-accent); }
/* Fokus: 2 px w kolorze akcentu z odstępem 2 px. Kolor bierze się WYŁĄCZNIE
   z --foreto-focus, żeby kontekst pasma (data-tone, Section, Card, Toolbar)
   przestawiał go w jednym miejscu — na czerwieni #F00000 na #E00000 to
   kontrast 1,06:1, czyli pierścienia nie ma.                                 */
.foreto-root :focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 2px; }
.foreto-visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* --- Typografia: siedem stopni, wersaliki jako stan CSS -------------------- */
.foreto-h1 { font-size: var(--foreto-font-size-h1); line-height: var(--foreto-line-h1); font-weight: 700; }
.foreto-h2 { font-size: var(--foreto-font-size-h2); line-height: var(--foreto-line-h2); font-weight: 700; }
.foreto-h3 { font-size: var(--foreto-font-size-h3); line-height: var(--foreto-line-h3); font-weight: 700; }
.foreto-h4 { font-size: var(--foreto-font-size-h4); line-height: var(--foreto-line-h4); font-weight: 700; }
.foreto-eyebrow { font-size: var(--foreto-font-size-body); line-height: var(--foreto-space-4); font-weight: 700; text-transform: uppercase; }
.foreto-body { font-size: var(--foreto-font-size-body); line-height: var(--foreto-line-body); }
.foreto-body--card { font-size: var(--foreto-font-size-card); }
.foreto-accent-text { color: var(--foreto-text-accent); }
.foreto-muted { color: var(--foreto-text-muted); }

/* --- Container: 1134 na kanwie 1920 (marginesy 393). Kolumna czytelnicza 940
       jest ZWĘŻENIEM wewnątrz kontenera, nie osobnym kontenerem.              */
.foreto-container {
  width: 100%;
  /* 1134 + 2 × 24: rynna leży POZA kolumną treści, więc treść ma zmierzone 1134,
     nie 1086. Ta sama arytmetyka co .wrap w base/reset.css systemu.           */
  max-width: calc(var(--foreto-wrap-max) + 2 * var(--foreto-wrap-gutter));
  margin-inline: auto;
  padding-inline: var(--foreto-wrap-gutter);
}
.foreto-container--prose { max-width: calc(var(--foreto-prose-max) + 2 * var(--foreto-wrap-gutter)); }
.foreto-container--full  { max-width: none; }

/* --- Section: rytm 128 domyślnie, 80 w wariancie zwężonym. Maksymalnie dwa
       tła na stronę: białe i szare pasmo.                                     */
.foreto-section { padding-block: var(--foreto-space-12); }
.foreto-section--tight  { padding-block: var(--foreto-space-9); }
.foreto-section--band   { background: var(--foreto-surface-band); }
.foreto-section--accent { background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent); }

/* ====================================================================
   TONY POWIERZCHNI — data-tone
   CSS nie potrafi zapytać „jakie tło mam pod sobą", więc kontekst musi być
   ZADEKLAROWANY. Zamiast klasy na każdym dziecku: jeden atrybut na elemencie,
   który maluje tło.                                                        */
.foreto-root[data-tone],
.foreto-root [data-tone] {
  background: var(--foreto-tone-bg, var(--foreto-page));
  color: var(--foreto-tone-fg, var(--foreto-ink));
}
.foreto-root[data-tone="page"],
.foreto-root [data-tone="page"] { --foreto-tone-bg: var(--foreto-page); --foreto-tone-fg: var(--foreto-ink); }
.foreto-root[data-tone="band"],
.foreto-root [data-tone="band"] { --foreto-tone-bg: var(--foreto-surface-band); --foreto-tone-fg: var(--foreto-ink); }
/* Czerń: hover linku zostaje czerwony (na czerni działa), ale tekst
   drugorzędny i kreski muszą zejść z #6E6E6E i #DFDFDF.                     */
.foreto-root[data-tone="ink"],
.foreto-root [data-tone="ink"] {
  --foreto-tone-bg: var(--foreto-ink);
  --foreto-tone-fg: var(--foreto-page);
  --foreto-text-muted: rgb(255 255 255 / .7);
  --foreto-border-hairline: rgb(255 255 255 / .24);
}
.foreto-root[data-tone="accent"],
.foreto-root [data-tone="accent"] {
  --foreto-tone-bg: var(--foreto-surface-accent);
  --foreto-tone-fg: var(--foreto-text-on-accent);
}

/* KONTEKST CZERWIENI. Hover linku to normalnie przejście na czerwień — na
   czerwonym tle znaczy zniknięcie. Wszystko, co maluje czerwień, przestawia
   więc jeden token, a nie każdy komponent osobno: linki idą na czerń.       */
/* .foreto-card--accent dopisana przy przenoszeniu grupy forms: czerwona KARTA
   maluje tę samą czerwień co pasmo, ale nie jest sekcją i nie nosi data-tone,
   więc bez niej tekst wyciszony zostawał na #6E6E6E — zmierzone 1,01:1 na
   czerwieni, czyli napis nie do odczytania. Kit ma ją w tej liście od wersji
   z kartą kontaktu.                                                        */
.foreto-card--accent,
.foreto-section--accent,
.foreto-root[data-tone="accent"],
.foreto-root [data-tone="accent"],
.foreto-on-accent {
  --foreto-link-hover: var(--foreto-ink);
  --foreto-border-hairline: rgb(255 255 255 / .4);
  --foreto-selection-bg: var(--foreto-ink);
  /* Tekst drugorzędny na paśmie jest PEŁNĄ bielą (5,04:1), a różnicę wobec
     tekstu zwykłego niesie stopień pisma i grubość — nie przezroczystość.   */
  --foreto-text-muted: #FFFFFF;
  --foreto-tab-on: #FFFFFF;
  --foreto-tab-rule: #FFFFFF;
  --foreto-tab-off: var(--foreto-ink);
  --foreto-focus: #FFFFFF;
  --foreto-border-control: rgb(255 255 255 / .7);
  --foreto-border-strong: rgb(255 255 255 / .8);
}
.foreto-section--page { background: var(--foreto-page); }
.foreto-section--transparent { background: transparent; }
@media (max-width: 1181px) { .foreto-section { padding-block: var(--foreto-space-9); } }

/* --- Stack: odstęp podawany krokiem skali (1–12), nie dowolną liczbą.        */
.foreto-stack { display: flex; flex-direction: column; gap: var(--foreto-stack-gap, var(--foreto-space-4)); }
.foreto-stack--row { flex-direction: row; align-items: center; }
.foreto-stack--wrap { flex-wrap: wrap; }

/* --- Grid ---------------------------------------------------------------
   Drabinka responsywna: system ma trzy przełamania — 1181, 760 i 640. Liczba
   kolumn na każdym z nich jest OSOBNĄ zmienną z łańcuchem zapasowym: krok
   nieustawiony dziedziczy z szerszego.
   UWAGA: próg 640 NIE jest jeszcze przeniesiony ze źródła.                  */
.foreto-grid {
  --foreto-grid-at: var(--foreto-grid-cols, 3);
  display: grid;
  row-gap: var(--foreto-grid-row-gap, var(--foreto-grid-gap, 30px));
  column-gap: var(--foreto-grid-col-gap, var(--foreto-grid-gap, 30px));
  /* Furtka: --foreto-grid-template przyjmuje gotową wartość grid-template-columns
     i bije drabinkę kolumn. Układ, którego skala nie przewiduje, nie wymaga
     nowej klasy.                                                             */
  grid-template-columns: var(--foreto-grid-template, repeat(var(--foreto-grid-at), minmax(0, 1fr)));
  align-items: start;
}
@media (max-width: 1181px) {
  .foreto-grid {
    --foreto-grid-at: var(--foreto-grid-cols-1181, var(--foreto-grid-cols, 3));
    grid-template-columns: var(--foreto-grid-template-1181, var(--foreto-grid-template, repeat(var(--foreto-grid-at), minmax(0, 1fr))));
    row-gap: var(--foreto-grid-row-gap-1181, var(--foreto-grid-gap-1181, var(--foreto-grid-row-gap, var(--foreto-grid-gap, 30px))));
    column-gap: var(--foreto-grid-col-gap-1181, var(--foreto-grid-gap-1181, var(--foreto-grid-col-gap, var(--foreto-grid-gap, 30px))));
  }
}
@media (max-width: 760px) {
  .foreto-grid {
    --foreto-grid-at: var(--foreto-grid-cols-760, var(--foreto-grid-cols-1181, var(--foreto-grid-cols, 3)));
    grid-template-columns: var(--foreto-grid-template-760, var(--foreto-grid-template-1181, var(--foreto-grid-template, repeat(var(--foreto-grid-at), minmax(0, 1fr)))));
    row-gap: var(--foreto-grid-row-gap-760, var(--foreto-grid-gap-760, var(--foreto-grid-row-gap-1181, var(--foreto-grid-gap-1181, var(--foreto-grid-row-gap, var(--foreto-grid-gap, 30px))))));
    column-gap: var(--foreto-grid-col-gap-760, var(--foreto-grid-gap-760, var(--foreto-grid-col-gap-1181, var(--foreto-grid-gap-1181, var(--foreto-grid-col-gap, var(--foreto-grid-gap, 30px))))));
  }
}

/* --- NavBar — 1:1 z nagłówka serwisu -------------------------------------
   Pozycje 14 px bold wersaliki, odstęp 32. Pozycja bieżąca dostaje czerwoną
   ramkę 1 px rysowaną PSEUDOELEMENTEM — ramka na samym elemencie zmieniłaby
   jego wymiar i odstępy 32 px przestałyby się zgadzać. Kolor + ramka to dwa
   nośniki stanu, bo dla osoby nierozróżniającej czerwieni kolor nie istnieje.
   Podmenu przeniesione razem z resztą — patrz blok pod pozycjami paska.     */
.foreto-navbar { display: flex; align-items: center; gap: var(--foreto-space-5); }
.foreto-navbar__list {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--foreto-space-5);
  margin: 0; padding: 0; list-style: none;
}
.foreto-navbar__link {
  position: relative; display: inline-flex; align-items: center;
  min-height: 24px; padding: var(--foreto-space-1) var(--foreto-space-2);
  color: inherit; text-decoration: none;
  font-size: 14px; line-height: 24px; font-weight: 700; text-transform: uppercase;
  transition: color var(--foreto-transition-base, 200ms ease-out);
}
.foreto-navbar__link:hover { color: var(--foreto-text-accent); }
.foreto-navbar__link[aria-current] { color: var(--foreto-text-accent); }
.foreto-navbar__link[aria-current]::after {
  content: ''; position: absolute; inset: calc(-1 * var(--foreto-space-1)) 0;
  border: var(--foreto-border-width) solid currentColor; pointer-events: none;
}
.foreto-navbar--stacked .foreto-navbar__list { flex-direction: column; align-items: stretch; gap: var(--foreto-space-2); }

/* --- NavBar: podmenu ------------------------------------------------------
   JEDNO ŹRÓDŁO STANU: `aria-expanded` na przycisku. Pierwsza wersja otwierała
   panel na trzy sposoby naraz — `:hover` na pozycji, `:focus-within` i klik
   strzałki — i te trzy mechanizmy sobie przeczyły: zamknięcie kliknięciem
   wracało natychmiast, bo kursor dalej stał nad pozycją, a tabulator otwierał
   panel, którego nikt nie prosił. Trzeba było znacznika gaszącego hover, czyli
   obejścia na własny konflikt.

   Wzorzec APG „disclosure navigation" ma dokładnie jeden przełącznik i to on
   niesie stan — dla arkusza i dla czytnika ekranu ten sam atrybut.

   BEZ SKRYPTU STRZAŁKI NIE MA WCALE. Przycisk, który nic nie robi, jest gorszy
   niż jego brak: obiecuje działanie i zabiera przystanek tabulatora. Skrypt
   stawia znacznik na <html> i dopiero wtedy strzałka się pokazuje — a pozycja
   nadrzędna prowadzi wtedy, jak i zawsze, do strony działu, gdzie te podstrony
   i tak są wypisane.

   POWIERZCHNIA JAK KAŻDA NAKŁADKA W SYSTEMIE: pełne pasmo #F6F6F6 plus ramka
   1 px w #737373. Nie cień — system ma dokładnie jeden i to jest jego cecha.
   Ramka niesie znaczenie („to osobna płaszczyzna"), więc obowiązuje ją próg
   3:1 z 1.4.11, stąd --border-control, nie hairline.                        */
.foreto-navbar__item { position: relative; display: flex; align-items: center; gap: var(--foreto-space-1); }

/* UKRYTA WIDOCZNOŚĆ, NIE `display: none` — i to jest różnica mierzalna.
   Skrypt stoi w stopce, więc znacznik na <html> pojawia się po pierwszym
   rysowaniu. Przy `display: none` strzałka wskakiwałaby wtedy do układu
   i przesuwała cały pasek o 24 px plus odstęp, na oczach czytelnika.
   `visibility: hidden` rezerwuje miejsce od razu, a element i tak wypada
   z kolejności tabulacji i z drzewa dostępności — czyli bez skryptu nie ma
   przycisku, który nic nie robi.                                           */
.foreto-navbar__sub-toggle {
  display: grid;
  place-items: center;
  width: 24px; height: 24px;
  color: inherit;
  visibility: hidden;
  transition: color var(--foreto-transition-base, 200ms ease-out);
}
[data-foreto-nav-js] .foreto-navbar__sub-toggle { visibility: visible; }
.foreto-navbar__sub-toggle:hover { color: var(--foreto-text-accent); }

.foreto-navbar__chev {
  width: 12px; height: 12px;
  background-color: currentColor;
  -webkit-mask: url('../icons/chevron-down.svg') no-repeat center / contain;
          mask: url('../icons/chevron-down.svg') no-repeat center / contain;
  transition: transform var(--foreto-transition-base, 200ms ease-out);
}
/* RUCH GROTA W DWÓCH STANACH, i każdy mówi co innego.
   Wskazanie — grot schodzi o 2 px, czyli zapowiada kierunek, w którym coś się
   otworzy. Rozwinięcie — obrót o 180°, czyli grot wskazuje drogę powrotną.
   Stan rozwinięty stoi NIŻEJ, żeby wygrywał kolejnością, a wariant „rozwinięte
   i wskazane" składa oba przekształcenia, bo inaczej najechanie na otwarty
   panel prostowałoby grot z powrotem w dół. */
.foreto-navbar__sub-toggle:hover .foreto-navbar__chev { transform: translateY(2px); }
.foreto-navbar__sub-toggle[aria-expanded="true"] .foreto-navbar__chev { transform: rotate(180deg); }
.foreto-navbar__sub-toggle[aria-expanded="true"]:hover .foreto-navbar__chev { transform: rotate(180deg) translateY(2px); }

/* min-width 100%, nie width: panel ma być co najmniej tak szeroki jak pozycja,
   ale dłuższa nazwa podstrony może go poszerzyć. Sztywna szerokość ucinałaby
   pozycje przychodzące z menu.

   Dopełnienie pionowe ZERO: pozycje mają własne 8 px góra-dół i wysokość celu
   24, więc pas panelu nad pierwszą i pod ostatnią pozycją dokładał pustkę,
   której nic nie oddziela od tych pozycji.                                  */
.foreto-navbar__sub {
  position: absolute; top: calc(100% + var(--foreto-space-2)); left: 0; z-index: 100;
  min-width: 100%;
  margin: 0; padding: 0;
  list-style: none; white-space: nowrap;
  background: var(--foreto-surface-band);
  border: var(--foreto-border-width) solid var(--foreto-border-control);
  /* visibility, nie display: display: none nie da się animować, a bez przejścia
     panel miga. Przy prefers-reduced-motion globalna reguła zdejmuje czas
     przejścia i zostaje samo pokazanie — poprawnie.                         */
  opacity: 0; visibility: hidden;
  transition: opacity var(--foreto-transition-fast, 120ms ease-out),
              visibility var(--foreto-transition-fast, 120ms ease-out);
}

.foreto-navbar__sub-toggle[aria-expanded="true"] ~ .foreto-navbar__sub {
  opacity: 1; visibility: visible;
}

/* KŁADKA NAD SZCZELINĄ. Panel stoi 8 px pod paskiem i ta przerwa nie należy
   ani do pozycji, ani do panelu — kursor jadący w dół z grota wychodził na
   moment poza obie i wyzwalał `mouseleave`, czyli panel zamykał się w połowie
   drogi do niego. Pseudoelement wypełnia szczelinę, nic nie rysując; istnieje
   tylko razem z panelem, więc przy zamkniętym menu nie łapie niczego.       */
.foreto-navbar__sub::before {
  content: ''; position: absolute;
  top: calc(-1 * var(--foreto-space-2) - var(--foreto-border-width));
  left: 0; right: 0; height: var(--foreto-space-2);
}

.foreto-navbar__sub-link {
  display: block;
  min-height: var(--foreto-hit);
  padding: var(--foreto-space-2) var(--foreto-space-4);
  color: inherit; text-decoration: none;
  font-size: 14px; line-height: var(--foreto-space-4); font-weight: 700; text-transform: uppercase;
  transition: background-color var(--foreto-transition-fast, 120ms ease-out),
              color var(--foreto-transition-fast, 120ms ease-out);
}

/* Pozycja bieżąca: sama czerwień tekstu. */
.foreto-navbar__sub-link[aria-current] { color: var(--foreto-text-accent); }

/* WSKAZANIE I FOKUS STOJĄ NA KOŃCU i obejmują też pozycję bieżącą. Reguła
   `[aria-current]` waży tyle samo co `:hover`, więc bez tego czerwień tekstu
   wygrywała kolejnością na czerwonym tle — pozycja bieżąca znikała pod
   kursorem. Kontrast biel/#E00000 to 5,04:1.                                */
.foreto-navbar__sub-link:hover,
.foreto-navbar__sub-link:focus-visible,
.foreto-navbar__sub-link[aria-current]:hover,
.foreto-navbar__sub-link[aria-current]:focus-visible {
  background: var(--foreto-surface-accent);
  color: var(--foreto-text-on-accent);
}

.foreto-drawer__sublist {
  display: grid; gap: var(--foreto-space-2);
  margin: var(--foreto-space-2) 0 0 var(--foreto-space-4);
  padding: 0; list-style: none;
}
.foreto-drawer__link--child {
  font-size: var(--foreto-font-size-body); line-height: var(--foreto-space-5);
  color: var(--foreto-text-muted);
}
.foreto-drawer__link--child:hover { color: var(--foreto-text-accent); }

/* IconButton — DOŁOŻONY. Pole = stopień ikony + 2 × 15 dopełnienia, czyli
   ikona 24 daje 54 (wysokość przycisku z pomiaru), 36 daje 66, 12 daje 42.
   Wymiar idzie ze zmiennej, więc rząd przycisków o tym samym stopniu ikony
   jest równy bez podawania szerokości. Źródło: 484–528.                     */
.foreto-icon-btn {
  --foreto-icon-btn-glyph: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  width: calc(var(--foreto-icon-btn-glyph) + 2 * var(--foreto-btn-pad-y));
  height: calc(var(--foreto-icon-btn-glyph) + 2 * var(--foreto-btn-pad-y));
  padding: 0; border: 0; border-radius: var(--foreto-radius, 0);
  color: inherit; background: none; cursor: pointer;
  transition: background-color var(--foreto-transition-base), color var(--foreto-transition-base);
}
.foreto-icon-btn > .foreto-icon,
.foreto-icon-btn > svg {
  width: var(--foreto-icon-btn-glyph); height: var(--foreto-icon-btn-glyph);
  transition: transform var(--foreto-transition-base);
}
/* Skaluje się GLIF, nie pole: przycisk zostaje w rytmie rzędu, a reakcja jest
   widoczna.                                                                 */
.foreto-icon-btn:hover > .foreto-icon,
.foreto-icon-btn:focus-visible > .foreto-icon,
.foreto-icon-btn:hover > svg,
.foreto-icon-btn:focus-visible > svg { transform: scale(var(--foreto-icon-hover-scale)); }
.foreto-icon-btn:active > .foreto-icon,
.foreto-icon-btn:active > svg { transform: scale(1); }
.foreto-icon-btn--dense { --foreto-btn-pad-y: var(--foreto-btn-pad-y-sm); }
.foreto-icon-btn--12 { --foreto-icon-btn-glyph: 12px; }
.foreto-icon-btn--24 { --foreto-icon-btn-glyph: 24px; }
.foreto-icon-btn--36 { --foreto-icon-btn-glyph: 36px; }
.foreto-icon-btn--48 { --foreto-icon-btn-glyph: 48px; }
.foreto-icon-btn--solid { background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent); box-shadow: var(--foreto-shadow-card); }
.foreto-icon-btn--solid:hover, .foreto-icon-btn--solid:focus-visible { background: var(--foreto-accent-hover); }
.foreto-icon-btn--dark { background: var(--foreto-ink); color: var(--foreto-page); }
.foreto-icon-btn--dark:hover, .foreto-icon-btn--dark:focus-visible { background: var(--foreto-ink-hover); }
.foreto-icon-btn--band { background: var(--foreto-surface-band); color: var(--foreto-ink); }
.foreto-icon-btn--band:hover, .foreto-icon-btn--band:focus-visible { background: var(--foreto-border-hairline); }

/* KeyValue — <dl>, nie tabela dwukolumnowa: para ma znaczenie, które <dl>
   niesie bez ról ARIA. Źródło: 1698–1723.                                   */
.foreto-kv { margin: 0; }
.foreto-kv__row {
  display: flex; flex-wrap: wrap; gap: var(--foreto-space-2) var(--foreto-space-4);
  padding-block: var(--foreto-space-3);
  border-bottom: 1px solid var(--foreto-border-hairline);
}
.foreto-kv__row:last-child { border-bottom: 0; }
.foreto-kv dt {
  flex: 0 0 auto; min-width: var(--foreto-kv-key, 18ch);
  font-size: var(--foreto-font-size-card); line-height: var(--foreto-line-body);
  color: var(--foreto-text-muted);
}
.foreto-kv dd {
  flex: 1 1 24ch; margin: 0; text-wrap: pretty;
  font-size: var(--foreto-font-size-body); line-height: var(--foreto-line-body);
}
.foreto-kv--stacked .foreto-kv__row { flex-direction: column; gap: var(--foreto-space-1); border-bottom: 0; padding-block: 0; }
.foreto-kv--stacked dt, .foreto-kv--stacked dd { flex: 0 0 auto; }
.foreto-kv--stacked { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--foreto-space-5); }
.foreto-kv--stacked dt { min-width: 0; font-size: 12px; line-height: 16px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }

/* --- NavDrawer — 1:1 z kit/nav-drawer.css. Źródło: 2245–2320.
   Szuflada z PRAWEJ, pełna wysokość, pozycje 32 px. Otwarcie na UKRYTYM
   CHECKBOKSIE, zero JS w samym rysunku. Czego CSS tu nie zrobi i co dokłada
   js/drawer.js: blokada przewijania strony i pułapka fokusu + Esc.
   Podmenu NIE przeniesione — menu jest płaskie.                            */
.foreto-drawer { --foreto-drawer-w: min(420px, 100vw); --foreto-drawer-item: 32px; }
.foreto-drawer__toggle { position: fixed; top: 0; left: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.foreto-drawer__button {
  position: relative; z-index: 10;
  display: flex; flex-direction: column; justify-content: space-between; flex: 0 0 auto;
  width: 24px; height: 24px; padding: 3px 0; cursor: pointer; color: currentColor;
}
.foreto-drawer__button i { display: block; width: 100%; height: 2px; background: currentColor; transition: transform var(--foreto-transition-base, 200ms ease-out), opacity var(--foreto-transition-base, 200ms ease-out); }
.foreto-drawer__toggle:focus-visible + .foreto-drawer__button { outline: 2px solid var(--foreto-text-accent); outline-offset: 2px; }
.foreto-drawer__toggle:checked + .foreto-drawer__button i:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.foreto-drawer__toggle:checked + .foreto-drawer__button i:nth-child(2) { opacity: 0; }
.foreto-drawer__toggle:checked + .foreto-drawer__button i:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }
.foreto-drawer__scrim {
  position: absolute; inset: 0; z-index: 8;
  background: var(--foreto-scrim, rgb(0 0 0 / .6)); opacity: 0; visibility: hidden; cursor: pointer;
  transition: opacity var(--foreto-transition-base, 200ms ease-out), visibility var(--foreto-transition-base, 200ms ease-out);
}
.foreto-drawer__toggle:checked ~ .foreto-drawer__scrim { opacity: 1; visibility: visible; }
.foreto-drawer__panel {
  position: absolute; top: 0; right: 0; bottom: 0; z-index: 9;
  display: flex; flex-direction: column;
  width: var(--foreto-drawer-w); padding: var(--foreto-space-5);
  overflow-y: auto;
  background: var(--foreto-page); color: var(--foreto-ink);
  border-left: var(--foreto-border-width) solid var(--foreto-border-hairline);
  transform: translateX(100%); visibility: hidden;
  transition: transform var(--foreto-transition-base, 200ms ease-out), visibility var(--foreto-transition-base, 200ms ease-out);
  overflow-x: clip;
}
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel { transform: translateX(0); visibility: visible; }
.foreto-drawer--fixed .foreto-drawer__scrim, .foreto-drawer--fixed .foreto-drawer__panel { position: fixed; }
.foreto-drawer__list { display: grid; gap: var(--foreto-space-3); margin: var(--foreto-space-8) 0 0; padding: 0; list-style: none; overflow-x: clip; }
.foreto-drawer__link {
  display: block; color: inherit; text-decoration: none;
  font-size: var(--foreto-drawer-item); line-height: 39px; font-weight: 700; text-transform: uppercase;
  transition: color var(--foreto-transition-base, 200ms ease-out);
}
.foreto-drawer__link:hover { color: var(--foreto-text-accent); }
.foreto-drawer__link[aria-current] { color: var(--foreto-text-accent); text-decoration: underline; text-underline-offset: 6px; text-decoration-thickness: 2px; }
.foreto-drawer__list > li,
.foreto-drawer__foot {
  opacity: 0;
  transform: translateX(var(--foreto-space-4));
  transition: opacity var(--foreto-transition-fast, 120ms ease-out),
              transform var(--foreto-transition-fast, 120ms ease-out);
}
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__list > li,
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__foot {
  opacity: 1;
  transform: translateX(0);
  transition-duration: 200ms;
  transition-delay: 200ms;
}
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__list > li:nth-child(2) { transition-delay: 232ms; }
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__list > li:nth-child(3) { transition-delay: 264ms; }
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__list > li:nth-child(4) { transition-delay: 296ms; }
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__list > li:nth-child(5) { transition-delay: 328ms; }
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__list > li:nth-child(n+6) { transition-delay: 360ms; }
.foreto-drawer__toggle:checked ~ .foreto-drawer__panel .foreto-drawer__foot { transition-delay: 392ms; }

/* --- SkipLink — DOŁOŻONY. Źródło: 2072–2091.
   Pierwszy element w kolejności tabulacji, widoczny WYŁĄCZNIE na fokusie.
   Nie ukrywany przez display: none ani visibility — oba zdejmują element
   z kolejności tabulacji, czyli likwidują cały sens skrótu.                */
.foreto-skip {
  position: absolute; left: var(--foreto-space-3); top: var(--foreto-space-3);
  z-index: 100;
  padding: var(--foreto-space-2) var(--foreto-space-4);
  background: var(--foreto-ink); color: var(--foreto-page);
  font-size: 14px; line-height: 24px; font-weight: 700; text-transform: uppercase;
  text-decoration: none;
  transform: translateY(calc(-100% - var(--foreto-space-3) * 2));
  transition: transform var(--foreto-transition-base, 200ms ease-out);
}
.foreto-skip:focus-visible { transform: translateY(0); color: var(--foreto-page); }
.foreto-skip--contained { position: absolute; }

/* --- SectionTitle, Button, Link. Źródło: 337–341, 366–447, 572–574.
   JEDNA zmiana wobec źródła: ścieżki masek. Kit trzyma glify w ../assets/icons/
   licząc od swojego katalogu; w motywie arkusz leży w assets/css/, więc ta sama
   ścieżka wskazywałaby assets/assets/icons. Zostaje ../icons/.              */
.foreto-section-title { text-align: center; text-transform: uppercase; text-wrap: balance; }
.foreto-section-title--start { text-align: left; }
.foreto-section-title__accent { display: block; color: var(--foreto-text-accent); }
/* --inline — odstępstwo od źródła. Makieta łamie drugą linię ZAWSZE
   (display: block), i to jest cały motyw firmowy: „YOUR VISION / OUR CODE".
   Ten modyfikator puszcza akcent w jeden wiersz — dla krótkich tytułów
   sekcyjnych („Dane rejestrowe", „Osoby kontaktowe"), gdzie dwa wiersze robią
   z dwuwyrazowej frazy nagłówek wysoki na 104 px.
   Odstęp niesie spacja w treści, nie margines: przy display: inline margines
   poziomy na łamaniu wiersza znika, a spacja zostaje.                        */
.foreto-section-title--inline .foreto-section-title__accent { display: inline; }

.foreto-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--foreto-space-3);
  font-family: var(--foreto-font);
  font-weight: 700;
  font-size: var(--foreto-font-size-body);
  line-height: var(--foreto-line-body);
  text-transform: uppercase;
  text-decoration: none;
  border: 0; border-radius: var(--foreto-radius, 0);
  cursor: pointer;
  transition: background-color var(--foreto-transition-base), color var(--foreto-transition-base);
}
.foreto-btn:focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 3px; }
/* Grot: 20 px to POMIAR z makiety (glif 12 × 20), nie stopień skali ikon.
   Odjeżdża o 4 px; przy 12 px szerokości większy ruch wygląda jak usterka.  */
.foreto-btn__arrow {
  flex: 0 0 auto; width: 20px; height: 20px;
  background-color: currentColor;
  -webkit-mask: url('../icons/arrow-right.svg') no-repeat center / 100% 100%;
          mask: url('../icons/arrow-right.svg') no-repeat center / 100% 100%;
  transition: transform var(--foreto-transition-base);
}
.foreto-btn:hover .foreto-btn__arrow,
.foreto-btn:focus-visible .foreto-btn__arrow,
.foreto-link:hover .foreto-btn__arrow,
.foreto-link:focus-visible .foreto-btn__arrow { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) {
  .foreto-btn__arrow { transition: none; }
  .foreto-btn:hover .foreto-btn__arrow,
  .foreto-btn:focus-visible .foreto-btn__arrow,
  .foreto-link:hover .foreto-btn__arrow,
  .foreto-link:focus-visible .foreto-btn__arrow { transform: none; }
}
.foreto-btn--sm {
  --foreto-btn-pad-y: var(--foreto-btn-pad-y-sm);
  --foreto-btn-pad-x: var(--foreto-btn-pad-x-sm);
  --foreto-btn-pad-x-narrow: var(--foreto-btn-pad-x-sm-narrow);
  font-size: var(--foreto-font-size-card);
}
.foreto-btn--sm .foreto-btn__arrow {
  width: 13px; height: 13px;
  -webkit-mask-image: url('../icons/arrow-right-sm.svg');
          mask-image: url('../icons/arrow-right-sm.svg');
}
.foreto-btn--solid { padding: var(--foreto-btn-pad-y) var(--foreto-btn-pad-x); background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent); box-shadow: var(--foreto-shadow-card); }
.foreto-btn--solid:hover, .foreto-btn--solid:focus-visible { background: var(--foreto-accent-hover); }
.foreto-btn--dark  { padding: var(--foreto-btn-pad-y) var(--foreto-btn-pad-x); background: var(--foreto-ink); color: var(--foreto-page); }
.foreto-btn--dark:hover, .foreto-btn--dark:focus-visible { background: var(--foreto-ink-hover); }
.foreto-btn--light { padding: var(--foreto-btn-pad-y) var(--foreto-btn-pad-x); background: #FFFFFF; color: #E00000; }
.foreto-btn--light:hover, .foreto-btn--light:focus-visible { background: #F6F6F6; }
.foreto-btn--narrow { padding-inline: var(--foreto-btn-pad-x-narrow); }

/* Link — przycisk-link z sekcji: wersaliki, grot, bez tła. */
.foreto-link { display: inline-flex; align-items: center; gap: var(--foreto-space-3); font-weight: 700; text-transform: uppercase; text-decoration: none; color: var(--foreto-ink); }
.foreto-link:hover, .foreto-link:focus-visible { color: var(--foreto-link-hover, var(--foreto-text-accent)); }

/* --- Button: warianty pozostałe. Źródło: 449–462. */
.foreto-btn--full { width: 100%; justify-content: flex-start; }
.foreto-btn--link { padding: 0; background: none; color: var(--foreto-ink); }
.foreto-btn--link:hover, .foreto-btn--link:focus-visible { color: var(--foreto-link-hover, var(--foreto-text-accent)); }
.foreto-btn[disabled], .foreto-btn[aria-disabled="true"] {
  background: var(--foreto-border-hairline); color: var(--foreto-text-muted);
  box-shadow: none; cursor: not-allowed; pointer-events: none;
}
.foreto-btn[disabled] .foreto-btn__arrow, .foreto-btn[aria-disabled="true"] .foreto-btn__arrow { opacity: .5; }

/* ButtonGroup — DOŁOŻONY. Odstęp 16 (--space-3): akcje czytają się jako kilka
   osobnych, nie jedna kontrolka. Pionowy wariant dla wąskiej kolumny.
   Źródło: 535–547. Wcześniej stała tu moja wersja pisana z dokumentacji
   komponentu, nie z arkusza — rozchodziła się w trzech miejscach.           */
.foreto-btn-group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--foreto-space-3); }
/* Pionowy NIE rozciąga przycisków — zachowują własną szerokość. Rozciągnięcie
   jest decyzją PRZYCISKU (Button fullWidth), nie grupy: w jednej grupie jeden
   przycisk może brać pełną szerokość, a drugi nie.                           */
.foreto-btn-group--vertical { flex-direction: column; align-items: flex-start; flex-wrap: nowrap; }
.foreto-btn-group--vertical > .foreto-btn--full { align-self: stretch; }
/* stretch to decyzja GRUPY o wszystkich dzieciach — inna sprawa niż fullWidth
   na jednym przycisku. W pionie: każdy przycisk bierze szerokość kolumny.
   W poziomie: przyciski dzielą wiersz po równo (flex: 1 1 0), więc nie ma
   mowy o rozciąganiu w pionie, które align-items: stretch by zrobiło.       */
.foreto-btn-group--vertical.foreto-btn-group--stretch { align-items: stretch; }
.foreto-btn-group--stretch:not(.foreto-btn-group--vertical) > .foreto-btn { flex: 1 1 0; }

/* --- Grid: pozostałe sterowanie i presety. Źródło: 244–283. --------------- */
@media (max-width: 640px) {
  .foreto-grid {
    --foreto-grid-at: var(--foreto-grid-cols-640, var(--foreto-grid-cols-760, var(--foreto-grid-cols-1181, var(--foreto-grid-cols, 3))));
    grid-template-columns: var(--foreto-grid-template-640, var(--foreto-grid-template-760, var(--foreto-grid-template-1181, var(--foreto-grid-template, repeat(var(--foreto-grid-at), minmax(0, 1fr))))));
  }
}
.foreto-grid {
  grid-auto-flow: var(--foreto-grid-flow, row);
  grid-auto-rows: var(--foreto-grid-rows, auto);
  align-items: var(--foreto-grid-align, start);
  justify-items: var(--foreto-grid-justify, stretch);
}
/* Preset to zmierzona para kolumn i odstępu WRAZ z drabinką. */
.foreto-grid--m3 {
  --foreto-grid-cols: 3; --foreto-grid-gap: 30px;
  --foreto-grid-cols-1181: 2; --foreto-grid-cols-760: 1;
}
/* Dwie kolumny szerokich kart (segmenty: cztery karty jako 2 × 2, nie 3 + 1;
   marki Grupy: siedem kart w dwóch kolumnach zamiast 3 + 3 + 1). */
.foreto-grid--m2 {
  --foreto-grid-cols: 2; --foreto-grid-gap: 30px;
  --foreto-grid-cols-760: 1;
}
.foreto-grid--m5 {
  --foreto-grid-cols: 5; --foreto-grid-gap: 0px;
  --foreto-grid-cols-1181: 3; --foreto-grid-cols-760: 2; --foreto-grid-cols-640: 1;
}
.foreto-grid--split { grid-template-columns: minmax(0, 486fr) minmax(0, 528fr); gap: 72px; padding-right: 48px; }
@media (max-width: 1181px) {
  .foreto-grid--split { grid-template-columns: minmax(0, 1fr); padding-right: 0; gap: var(--foreto-space-6); }
}

/* --- Card: dwa rodzaje pojemnika i nic poza nimi — z cieniem i bez, plus
       wariant czerwony niosący biały tekst. Źródło: 288–297.                  */
.foreto-card { background: var(--foreto-surface-card); border-radius: var(--foreto-radius, 0); }
.foreto-card--shadow { box-shadow: var(--foreto-shadow-card); }
[data-theme="dark"] .foreto-card--shadow { box-shadow: none; border: 1px solid var(--foreto-border-elevation); }
[data-theme="dark"] .foreto-card--shadow.foreto-card--accent { border-color: transparent; }
.foreto-card--accent { background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent); }
.foreto-card--pad    { padding: var(--foreto-space-6) var(--foreto-space-7); }

/* --- Testimonial — cytat z podpisem. Źródło: 3165–3206.
   PODPIS STOI PRZED cytatem: najpierw kto mówi, potem co powiedział.         */
.foreto-quote { --foreto-quote-indent: 97px; --foreto-quote-avatar: 100px; margin: 0; }
.foreto-quote__mark {
  display: block; width: 123px; height: 112px;
  margin-bottom: var(--foreto-space-7);
  background-color: var(--foreto-surface-accent);
  -webkit-mask: url('../icons/quote.svg') no-repeat center / contain;
          mask: url('../icons/quote.svg') no-repeat center / contain;
}
.foreto-quote__figure {
  display: grid;
  grid-template-columns: var(--foreto-quote-avatar) minmax(0, 1fr);
  column-gap: var(--foreto-space-8);
  margin: 0 0 0 var(--foreto-quote-indent);
}
.foreto-quote__avatar { grid-row: 1 / span 2; align-self: start; }
.foreto-quote__who { display: flex; flex-direction: column; }
.foreto-quote__name { font-weight: 700; line-height: var(--foreto-space-4); }
.foreto-quote__role { color: var(--foreto-text-muted); line-height: var(--foreto-space-4); }
.foreto-quote__text { max-width: 746px; margin: var(--foreto-space-3) 0 0; line-height: var(--foreto-space-4); }
@media (max-width: 1181px) {
  .foreto-quote { --foreto-quote-indent: 0px; }
  .foreto-quote__figure { column-gap: var(--foreto-space-5); }
}
@media (max-width: 640px) {
  .foreto-quote__mark { width: 66px; height: 60px; margin-bottom: var(--foreto-space-5); }
  .foreto-quote__figure { grid-template-columns: minmax(0, 1fr); row-gap: var(--foreto-space-4); }
  .foreto-quote__avatar { grid-row: auto; }
}

/* --- CheckList — wyliczenie z ptaszkiem. Źródło: 3208–3225. --------------- */
.foreto-checklist { --foreto-check-line: var(--foreto-space-4); margin: 0; padding: 0; list-style: none; }
.foreto-checklist li {
  display: grid; grid-template-columns: 16px minmax(0, 1fr); gap: 12px;
  line-height: var(--foreto-check-line);
}
.foreto-checklist li + li { margin-top: var(--foreto-space-4); }
.foreto-checklist li::before {
  content: ''; width: 16px; height: 16px;
  margin-top: calc((var(--foreto-check-line) - 16px) / 2);
  background-color: var(--foreto-text-accent);
  -webkit-mask: url('../icons/check-square.svg') no-repeat center / contain;
          mask: url('../icons/check-square.svg') no-repeat center / contain;
}

/* --- IconTile — kafel z ikoną (wartości). Przeniesione z kit/tiles.css. ----
   Kafel jest KWADRATOWY i wszystkie w rzędzie są równe, więc wysokość bierze
   się z aspect-ratio, a nie z treści. Treść pozycjonują odstępy od GÓRY
   (ikona +102, nazwa +206) — takie są zmierzone pozycje w makiecie, a nie
   efekt środkowania w pionie.                                               */
.foreto-icon-tile {
  --foreto-tile-icon: 80px;
  --foreto-tile-icon-top: 102px;
  --foreto-tile-name-top: 206px;
  aspect-ratio: 1;
  padding-inline: var(--foreto-space-5);
  text-align: center;
}
/* Pliki ikon są znormalizowane do kwadratu 80 × 80 z glifem wpisanym
   i wyśrodkowanym, więc wszystkie mają tę samą wagę optyczną. Bez tego pięć
   ikon o pięciu różnych wymiarach dawało rząd, w którym jedna była optycznie
   o jedną trzecią większa od sąsiadów.                                      */
.foreto-icon-tile__icon {
  display: block;
  width: var(--foreto-tile-icon); height: var(--foreto-tile-icon);
  margin-inline: auto;
  margin-top: var(--foreto-tile-icon-top);
}
/* Szerokość ŁAMANIA tekstu, nie szerokość kafla: w makiecie każdy tytuł ma
   dokładnie dwa wiersze. 262 = 358 − 2 × 48, tuż powyżej najszerszego (261). */
.foreto-icon-tile__name {
  max-width: 262px;
  margin-top: calc(var(--foreto-tile-name-top) - var(--foreto-tile-icon-top) - var(--foreto-tile-icon));
  margin-inline: auto;
  font-size: var(--foreto-font-size-body);
  font-weight: 700;
  text-transform: uppercase;
}
/* ODSTĘPSTWO OD KITU, świadome. Kit stawia nagłówek na zmierzonym odstępie
   136 px od góry — ta liczba trafia w środek TYLKO przy kaflu 358 px, bo
   z niej wynikała. Przy innej szerokości kolumny (siatka jest auto-fit, więc
   szerokość zmienia się płynnie) nagłówek siedzi za wysoko albo za nisko,
   a przy dwóch wierszach wychodzi poza kafel.

   Kafel z tytułem ma JEDNO dziecko, więc nie ma tu czego wyrównywać do
   sąsiadów: środkowanie daje ten sam wynik przy 358 i trzyma się każdej innej
   szerokości. Kafle z ikoną zostają przy pomiarach, bo tam dwa elementy mają
   ustalone pozycje względem góry i muszą się zgadzać między kaflami.        */
.foreto-icon-tile--title {
  display: grid; place-items: center;
  background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent);
}
.foreto-icon-tile__heading {
  margin: 0;
  font-size: var(--foreto-font-size-h3); line-height: var(--foreto-line-h3);
  font-weight: 700; text-transform: uppercase; text-wrap: balance;
}
@media (max-width: 1181px) {
  .foreto-icon-tile {
    --foreto-tile-icon-top: var(--foreto-space-7);
    --foreto-tile-name-top: calc(var(--foreto-tile-icon-top) + var(--foreto-tile-icon) + var(--foreto-space-5));
  }
}
/* Kafle przestają być kwadratami dopiero wtedy, gdy kwadrat zaczyna ucinać
   tekst — czyli w jednej kolumnie. Przy dwóch zostają.                      */
@media (max-width: 640px) {
  .foreto-icon-tile { aspect-ratio: auto; padding-block: var(--foreto-space-6); }
  .foreto-icon-tile__icon { margin-top: 0; }
}

/* --- TechTile — kafel technologii. Przeniesione z kit/tiles.css. ----------
   Dwa pasy: pierwszy na ikonę (od --tech-icon-top do --tech-name-top), drugi
   na nazwę. Sztywne wiersze zamiast wyśrodkowania w pionie — inaczej ikona
   63 px siedziałaby wyżej niż 80 px i podpisy by się rozjechały.

   Rozmiar maski NIE wynika z pliku, więc każda ikona dostaje zmierzoną
   wysokość i proporcję w swojej klasie. Wysokość ogranicza pas przez min(),
   bo poniżej 640 px pas schodzi do 64 px i podpis wchodziłby na ikonę.

   Barwa jest jednolita w spoczynku i wchodzi w kolor marki na wskazanie —
   maska niesie jeden kolor, więc detal w tarczy HTML5 i CSS3 i tak nie
   wyszedłby.                                                                */
.foreto-tech-tile {
  --foreto-tech-h: 197px; --foreto-tech-icon-top: 38px; --foreto-tech-name-top: 142px;
  display: grid;
  grid-template-rows: calc(var(--foreto-tech-name-top) - var(--foreto-tech-icon-top)) auto;
  justify-items: center; align-items: center;
  min-height: var(--foreto-tech-h);
  padding: var(--foreto-tech-icon-top) var(--foreto-space-4);
}
.foreto-tech-tile__icon {
  display: block; height: min(80px, 100%); aspect-ratio: 1;
  background-color: var(--foreto-border-control);
  /* Maska ZAPASOWA dla logotypu spoza dziesiątki zmierzonej niżej: adres
     podaje render w atrybucie stylu, a klasa modyfikująca — gdy istnieje —
     nadpisuje ją razem z wymiarem. Bez tej linii nieznany plik dawał kafel
     z podpisem i pustym polem nad nim.                                      */
  -webkit-mask-image: var(--foreto-tech-ikona, none);
          mask-image: var(--foreto-tech-ikona, none);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;  mask-position: center;
  -webkit-mask-size: contain;     mask-size: contain;
  transition: background-color var(--foreto-transition-base, 200ms ease-out);
}
.foreto-tech-tile__name { font-size: var(--foreto-font-size-body); font-weight: 700; text-transform: uppercase; text-align: center; }

/* Zmierzone wysokości i proporcje dziesięciu znanych logotypów. Termin
   taksonomii z ikoną spoza tej listy dostaje kwadrat 80 × 80 z reguły wyżej
   i maskę ze zmiennej --foreto-tech-ikona, którą podaje render.             */
.foreto-tech-tile__icon--html5      { height: min(79px, 100%); aspect-ratio: 67/79;  -webkit-mask-image: url('../icons/tech/html5.svg');      mask-image: url('../icons/tech/html5.svg'); }
.foreto-tech-tile__icon--css3       { height: min(80px, 100%); aspect-ratio: 69/80;  -webkit-mask-image: url('../icons/tech/css3.svg');       mask-image: url('../icons/tech/css3.svg'); }
.foreto-tech-tile__icon--javascript { height: min(79px, 100%); aspect-ratio: 80/79;  -webkit-mask-image: url('../icons/tech/javascript.svg'); mask-image: url('../icons/tech/javascript.svg'); }
.foreto-tech-tile__icon--php        { height: min(63px, 100%); aspect-ratio: 116/63; -webkit-mask-image: url('../icons/tech/php.svg');        mask-image: url('../icons/tech/php.svg'); }
.foreto-tech-tile__icon--laravel    { height: min(80px, 100%); aspect-ratio: 78/80;  -webkit-mask-image: url('../icons/tech/laravel.svg');    mask-image: url('../icons/tech/laravel.svg'); }
.foreto-tech-tile:hover .foreto-tech-tile__icon--html5      { background-color: #E34F26; }
.foreto-tech-tile:hover .foreto-tech-tile__icon--css3       { background-color: #1572B6; }
.foreto-tech-tile:hover .foreto-tech-tile__icon--javascript { background-color: #E9B902; }
.foreto-tech-tile:hover .foreto-tech-tile__icon--php        { background-color: #6C78AF; }
.foreto-tech-tile:hover .foreto-tech-tile__icon--laravel    { background-color: #FF2D20; }
@media (max-width: 1181px) { .foreto-tech-tile { --foreto-tech-h: 160px; --foreto-tech-icon-top: var(--foreto-space-5); --foreto-tech-name-top: 104px; } }
@media (max-width: 640px)  { .foreto-tech-tile { --foreto-tech-h: 132px; --foreto-tech-name-top: 88px; } }

/* --- LogoTile — kafel logotypu klienta. Źródło: 3387–3409. ---------------- */
.foreto-logo-tile {
  --foreto-logo-h: 96px;
  display: grid; place-items: center;
  height: var(--foreto-logo-h);
  padding-inline: var(--foreto-space-4);
  background: var(--foreto-surface-card);
  border: 2px solid var(--foreto-surface-band);
}
.foreto-logo-tile img {
  max-width: 100%; height: auto;
  filter: grayscale(1); opacity: .75;
  transition: filter var(--foreto-transition-base, 200ms ease-out), opacity var(--foreto-transition-base, 200ms ease-out);
}
.foreto-logo-tile:hover img { filter: none; opacity: 1; }
.foreto-logo-tile__name { color: var(--foreto-ink); font-size: var(--foreto-font-size-card); font-weight: 700; text-transform: uppercase; text-align: center; }
@media (max-width: 640px) { .foreto-logo-tile { --foreto-logo-h: 72px; } }

/* --- StepCard — karta etapu z numerem. Źródło: 3411–3455. ----------------- */
.foreto-step-card {
  --foreto-step-h: 432px; --foreto-step-pad-num: 32px;
  --foreto-step-name-top: 111px; --foreto-step-desc-top: 198px; --foreto-step-pad-x: 56px;
  display: grid;
  grid-template-rows:
    calc(var(--foreto-step-name-top) - var(--foreto-step-pad-num))
    calc(var(--foreto-step-desc-top) - var(--foreto-step-name-top))
    1fr;
  justify-items: center;
  min-height: var(--foreto-step-h);
  padding: var(--foreto-step-pad-num) var(--foreto-step-pad-x) var(--foreto-space-6);
}
.foreto-step-card__num { font-size: var(--foreto-font-size-h1); line-height: var(--foreto-line-h1); font-weight: 700; color: var(--foreto-text-accent); }
.foreto-step-card__name { margin: 0; font-size: var(--foreto-font-size-body); line-height: var(--foreto-space-4); font-weight: 700; }
.foreto-step-card__desc { align-self: start; width: 100%; margin: 0; line-height: var(--foreto-space-4); }
@media (max-width: 1181px) {
  .foreto-step-card {
    --foreto-step-h: 0px; --foreto-step-pad-x: var(--foreto-space-5);
    grid-template-rows: none; gap: var(--foreto-space-2);
    padding: var(--foreto-space-6) var(--foreto-step-pad-x);
  }
}

/* --- Media: kadr dwuwarstwowy. Zamiast wybierać między cover (przycina)
       i contain (puste pasy), to samo zdjęcie leży dwa razy: w tle rozmyta
       kopia dająca kolor, na wierzchu oryginał w całości.                     */
.foreto-media { --foreto-media-blur: 48px; --foreto-media-scale: 1.8; --foreto-media-sat: 1.7; --foreto-media-pad: 0px;
  position: relative; overflow: hidden; isolation: isolate; background: var(--foreto-surface-band); }
.foreto-media__bg, .foreto-media__img { display: block; position: absolute; }
.foreto-media__bg { inset: 0; width: 100%; height: 100%; object-fit: cover;
  filter: blur(var(--foreto-media-blur)) saturate(var(--foreto-media-sat)); transform: scale(var(--foreto-media-scale)); }
.foreto-media__img { top: var(--foreto-media-pad); left: var(--foreto-media-pad);
  width: calc(100% - 2 * var(--foreto-media-pad)); height: calc(100% - 2 * var(--foreto-media-pad)); object-fit: contain; }
@media (max-width: 1181px) { .foreto-media { --foreto-media-pad: 16px; } }

/* Ramka kadru — wariant DOŁOŻONY (samo Media jest przeniesione 1:1).
   Powód: kadr w dużej części czarny zlewa się z ciemnym tłem i pole przestaje
   być widoczne jako pole. Grubość 1 px, bo tyle ma jedyna szerokość ramki
   w systemie; promień 0 jak wszędzie.

   Kolory NIE odwracają się w ciemnym motywie i to jest celowe: ramka ma
   kontrastować ze ZDJĘCIEM, nie ze stroną. Dlatego paper zostaje biały,
   a ink czarny niezależnie od motywu.                                        */
.foreto-media--framed { border: var(--foreto-border-width, 1px) solid var(--foreto-media-frame, var(--foreto-border-hairline)); }
.foreto-media--frame-hairline { --foreto-media-frame: #DFDFDF; }
.foreto-media--frame-paper    { --foreto-media-frame: #FFFFFF; }
.foreto-media--frame-ink      { --foreto-media-frame: #000000; }
.foreto-media--frame-accent   { --foreto-media-frame: var(--foreto-red); }

/* --- Stat -----------------------------------------------------------------
   Liczba jest treścią, więc bierze stopień nagłówka (h2 48/56 na desktopie),
   a etykieta zostaje etykietą 12/16. Odwrotna hierarchia — duża etykieta nad
   małą liczbą — jest najczęstszym błędem w kafelkach z danymi.
   Zmiana (delta) ma znak I kolor: „+12%" na zielono, ale „+" zostaje, bo dla
   osoby nierozróżniającej kolorów sam odcień nie istnieje.                   */
.foreto-stat { display: flex; flex-direction: column; gap: var(--foreto-space-2); }
.foreto-stat__label { margin: 0; font-size: 12px; line-height: 16px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--foreto-text-muted); }
.foreto-stat__value { margin: 0; font-size: var(--foreto-font-size-h2); line-height: var(--foreto-line-h2); font-weight: 700; font-variant-numeric: tabular-nums; }
.foreto-stat__unit { font-size: var(--foreto-font-size-h4); line-height: 1; font-weight: 700; color: var(--foreto-text-muted); }
.foreto-stat__delta { display: inline-flex; align-items: center; gap: var(--foreto-space-1); margin: 0; font-size: var(--foreto-font-size-card); line-height: var(--foreto-msg-line); font-weight: 700; font-variant-numeric: tabular-nums; }
.foreto-stat__delta--up   { color: var(--foreto-text-success); }
.foreto-stat__delta--down { color: var(--foreto-text-accent); }
/* Na czerwonym paśmie ani zieleń (#007A33 → 1,09:1), ani czerwień nie istnieją.
   Zmiana i tak niesie ZNAK (+/−) oraz glif, więc kolor ustępuje kolorowi
   pasma — to jedyny nośnik, którego nie da się tam odtworzyć.                */
.foreto-root[data-tone="accent"] .foreto-stat__delta--up,
.foreto-root [data-tone="accent"] .foreto-stat__delta--up,
.foreto-on-accent .foreto-stat__delta--up,
.foreto-root[data-tone="accent"] .foreto-stat__delta--down,
.foreto-root [data-tone="accent"] .foreto-stat__delta--down,
.foreto-on-accent .foreto-stat__delta--down { color: inherit; }
.foreto-stat__note { margin: 0; font-size: var(--foreto-font-size-card); line-height: var(--foreto-msg-line); color: var(--foreto-text-muted); }
.foreto-stat--sm .foreto-stat__value { font-size: var(--foreto-font-size-h4); line-height: var(--foreto-line-h4); }

@media (max-width: 480px) {
  /* Ta sama pułapka co w stacked: obrót osi zamienia flex-basis dd na wysokość. */
  .foreto-kv__row { flex-direction: column; gap: var(--foreto-space-1); }
  .foreto-kv dt, .foreto-kv dd { flex: 0 0 auto; min-width: 0; }
  .foreto-stat__value { font-size: var(--foreto-font-size-h4); line-height: var(--foreto-line-h4); }
  .foreto-stat__unit  { font-size: var(--foreto-font-size-body); }
}

/* --- Accordion — PRZENIESIONY 1:1 z kit/disclosure.css -------------------
   Odwzorowanie sekcji „z kim pracujemy", geometria co do piksela: nagłówek
   czerwony 112 px z cieniem, pismo 32 px w wersalikach.
   Jedna proporcja trzyma warianty razem: WYSOKOŚĆ NAGŁÓWKA = 3,5 × STOPIEŃ
   PISMA (112 = 3,5 × 32; 56 = 3,5 × 16). Oba wymiary już istnieją w systemie —
   112 to krok skali, 56 to wysokość pola formularza.
   Chevron ma jednostkę PIKSELA, nie stopień skali: 8 px w wariancie
   podstawowym (5 × 8 szerokości, 3 × 8 wysokości) i 4 px w zwartym. Nie jest
   w bibliotece ikon, bo ma 40 × 24 i nie da się go wpisać w kwadrat bez zmiany
   rysunku.                                                                   */
.foreto-accordion {
  --foreto-disc-head-h: 112px;   /* 3,5 × 32 */
  --foreto-disc-pad-x: 65px;
  --foreto-disc-font: var(--foreto-font-size-h3);
  --foreto-disc-line: var(--foreto-line-h3);
  --foreto-disc-gap: var(--foreto-space-3);
  --foreto-disc-tail: var(--foreto-space-7);
  --foreto-disc-panel-gap: 3px;
  --foreto-disc-panel-pad: 46px 47px;
  --foreto-disc-unit: 8px;
  display: flex; flex-direction: column; gap: var(--foreto-disc-gap);
  margin: 0; padding: 0; list-style: none;
}
/* Wariant zwarty — ta sama proporcja, połowa wartości. Zapisany jako atrybut
   danych, nie klasa: zmienia wyłącznie wartości zmiennych.                  */
.foreto-accordion[data-size="sm"] {
  --foreto-disc-head-h: 56px;    /* 3,5 × 16 */
  --foreto-disc-pad-x: var(--foreto-space-5);
  --foreto-disc-font: var(--foreto-font-size-body);
  --foreto-disc-line: var(--foreto-space-4);
  --foreto-disc-gap: var(--foreto-space-2);
  --foreto-disc-tail: var(--foreto-space-4);
  --foreto-disc-panel-pad: var(--foreto-space-4);
  --foreto-disc-unit: 4px;
}
/* Rozwinięta pozycja ma pod panelem więcej powietrza niż zwykły odstęp między
   pozycjami — tak jest w makiecie (48 wobec 16).                            */
.foreto-disclosure[open] { margin-bottom: calc(var(--foreto-disc-tail) - var(--foreto-disc-gap)); }
.foreto-disclosure[open]:last-child { margin-bottom: 0; }
.foreto-disclosure__summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--foreto-space-4);
  min-height: var(--foreto-disc-head-h);
  padding-inline: var(--foreto-disc-pad-x);
  background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent);
  box-shadow: var(--foreto-shadow-card);
  font-size: var(--foreto-disc-font); line-height: var(--foreto-disc-line);
  font-weight: 700; text-transform: uppercase;
  cursor: pointer; list-style: none; user-select: none;
}
.foreto-disclosure__summary::-webkit-details-marker { display: none; }
/* Hover zmienia JASNOŚĆ, nie odcień — nagłówek stoi na czerwieni marki, więc
   zmiana odcienia zlałaby go z tłem sekcji. Token --foreto-accent-hover, NIE
   --foreto-ink: ten drugi odwraca się z motywem i w ciemnym dawał biel, czyli
   nagłówek bielał razem z białym tekstem.                                   */
.foreto-disclosure__summary:hover { background: var(--foreto-accent-hover); }
/* Pierścień BIAŁY, bo tło jest czerwone — akcentowy zniknąłby w nim. Token
   nieodwracalny, nie --foreto-page: ten w ciemnym motywie dawał czerń na
   czerwieni, czyli kod przeczył temu zdaniu.                                 */
.foreto-disclosure__summary:focus-visible { outline: 2px solid var(--foreto-focus-on-accent); outline-offset: -6px; }
.foreto-disclosure__marker {
  display: flex; align-items: flex-start; flex: 0 0 auto;
  width: calc(5 * var(--foreto-disc-unit)); height: calc(3 * var(--foreto-disc-unit));
  transition: transform var(--foreto-transition-base, 200ms ease-out);
}
.foreto-disclosure__marker i { width: var(--foreto-disc-unit); height: var(--foreto-disc-unit); background: currentColor; }
.foreto-disclosure__marker i:nth-child(2), .foreto-disclosure__marker i:nth-child(4) { margin-top: var(--foreto-disc-unit); }
.foreto-disclosure__marker i:nth-child(3) { margin-top: calc(2 * var(--foreto-disc-unit)); }
.foreto-disclosure[open] .foreto-disclosure__marker { transform: rotate(180deg); }
.foreto-disclosure__panel {
  display: flex; flex-direction: column; gap: var(--foreto-space-4);
  margin-top: var(--foreto-disc-panel-gap);
  padding: var(--foreto-disc-panel-pad);
  line-height: var(--foreto-space-4);
}
/* Poniżej 1181 zmierzony był tylko desktop, więc niżej to decyzje: dopełnienie
   65 px przy węższej kolumnie zostawiałoby na tekst mniej niż na marginesy.  */
@media (max-width: 1181px) {
  .foreto-accordion { --foreto-disc-pad-x: var(--foreto-space-5); --foreto-disc-panel-pad: var(--foreto-space-5); }
}
@media (max-width: 760px) {
  /* Stopień 32 px w kolumnie 327 px łamie się po jednym słowie. Nagłówek
     schodzi na 24, wysokość za nim — proporcja 3,5 zostaje.                 */
  .foreto-accordion { --foreto-disc-font: var(--foreto-font-size-h4); --foreto-disc-line: var(--foreto-line-h4); --foreto-disc-head-h: 84px; }
}

/* --- Avatar --------------------------------------------------------------
   JEDYNY wyjątek od promienia 0 w całym systemie: okrągłe zdjęcia osób.     */
.foreto-avatar { width: 100px; height: 100px; border-radius: 0; object-fit: cover; }
/* --mono — monochromatyczne w spoczynku, kolor na hover. NIE nowy wzorzec:
   dokładnie ten sam zabieg co przy kaflu logotypu, który gasi znaki klientów
   do szarości i zwraca im barwę po najechaniu.
   Różnica jedna, policzona: tam dochodzi opacity .75, a tu go NIE MA —
   przygaszona twarz czyta się jako stan wyłączony, a osoba kontaktowa nie jest
   wyłączona. Monochromia sama wystarcza.
   Wyzwalaczem jest RODZIC, nie samo zdjęcie: obrazek nie jest klikalny, więc
   hover na nim byłby obietnicą bez pokrycia — kolor wraca, gdy kursor jest
   gdziekolwiek na wpisie, i przy fokusie łącza z klawiatury.                 */
.foreto-avatar--mono {
  filter: grayscale(1);
  transition: filter var(--foreto-transition-base, 200ms ease-out);
}
.foreto-avatar--mono:hover,
:hover > .foreto-avatar--mono,
:focus-within > .foreto-avatar--mono { filter: none; }
@media (prefers-reduced-motion: reduce) {
  .foreto-avatar--mono { transition: none; }
}

/* --- Timeline — sygnaturowy komponent systemu -----------------------------
   Raster poziomy w ramce 1134: kolumna 405 │ 82 │ SZYNA 160 │ 82 │ 405.
   Naprzemienność czytana ze WSPÓŁRZĘDNYCH, nie z nazw warstw: nieparzyste po
   prawej, parzyste po lewej, punkt idzie za treścią.
   Skok 175 px to RASTER DAT, nie suma wysokości treści — treść z CMS-a bywa
   wyższa, dlatego min-height, a nie height.
   Szyna ma --surface-accent (#E00000), nie #F00000: NIESIE MAŁY TEKST (biały
   „kwiecień" 16 bold), a na #F00000 to 4,46:1, poniżej progu. Rdzeń punktu
   zostaje na czystej czerwieni — to grafika bez tekstu, a biała obwódka 7 px
   trzyma obie czerwienie z dala od siebie.
   WSZYSTKIE wartości zależne od przełamania siedzą w zmiennych; właściwości
   (grid-column, left, width) są ustawiane DOKŁADNIE RAZ, poza media query —
   media query nie podnosi specyficzności, więc nadpisywanie w niej właściwości
   psuje się w sposób, którego nie widać na pierwszy rzut oka.               */
.foreto-timeline {
  --foreto-tl-rail: 160px;      /* zmierzone */
  --foreto-tl-gap: 82px;        /* 729 − 647 */
  --foreto-tl-pitch: 175px;     /* raster dat: 4644 − 4469 */
  --foreto-tl-dot: 34px;
  --foreto-tl-dot-core: 20px;   /* biała obwódka (34 − 20) / 2 = 7 */
  --foreto-tl-dot-top: calc((var(--foreto-tl-year-line, var(--foreto-line-h2)) - var(--foreto-tl-dot)) / 2);
  --foreto-tl-head-top: 13px;
  /* Grot przełącznika „pokaż wszystkie": 30 px to wymiar GLIFU z biblioteki
     (30 × 27, podwójny chevron), a nie stopień skali ikon — ten sam plik i ten
     sam wymiar co ScrollMarker, bo to ten sam komunikat: „dalej jest więcej".
     Biel, bo grot stoi NA szynie; wariant ≤ 760 zmienia oba (szyna jest tam
     kreską 8 px i biały glif nie miałby na czym stanąć).                    */
  --foreto-tl-more: 30px;
  --foreto-tl-more-ink: #FFFFFF;
  --foreto-tl-cap: 52px;        /* szyna nad pierwszą datą */
  --foreto-tl-foot: var(--foreto-space-4);
  --foreto-tl-item-pad: var(--foreto-space-4);
  --foreto-tl-cols: minmax(0, 1fr) var(--foreto-tl-rail) minmax(0, 1fr);
  --foreto-tl-rail-x: calc(50% - var(--foreto-tl-rail) / 2);
  --foreto-tl-date-col: 2;
  /* Kolumna SZYNY — osobno od kolumny daty, bo poniżej 760 px data przechodzi
     nad tekst (kolumna 2), a szyna zostaje w pierwszej. Grot przełącznika
     trzyma się szyny, nie daty.                                            */
  --foreto-tl-rail-col: 2;
  --foreto-tl-date-pad: 0px;
  --foreto-tl-content-row: 1;
  --foreto-tl-col-odd: 3;
  --foreto-tl-col-even: 1;
  --foreto-tl-dot-x-odd: calc(50% + var(--foreto-tl-rail) / 2 - var(--foreto-tl-dot) / 2);
  --foreto-tl-dot-x-even: calc(50% - var(--foreto-tl-rail) / 2 - var(--foreto-tl-dot) / 2);
  position: relative;
  /* Wnętrze SZYNY, nie odstęp sekcji: czerwień zaczyna się 52 px nad pierwszą
     datą i kończy 24 px pod ostatnią. Odstęp od sąsiadów należy do sekcji.  */
  padding-block: var(--foreto-tl-cap) var(--foreto-tl-foot);
}
/* Szyna jest prawdziwym elementem, nie pseudoelementem — wbrew odruchowi, bo
   to dekoracja. Powód jest mierzalny: skaner dostępności przerywa liczenie
   kontrastu, gdy nad tekstem leży ::before z tłem („incomplete" zamiast
   wyniku). Ze zwykłym elementem stos tła daje się policzyć automatycznie.
   Stoi PIERWSZY w drzewie, więc maluje się pod resztą bez z-index.          */
.foreto-timeline__rail {
  position: absolute; top: 0; bottom: 0;
  left: var(--foreto-tl-rail-x); width: var(--foreto-tl-rail);
  background: var(--foreto-surface-accent);
}
.foreto-timeline__list { position: relative; margin: 0; padding: 0; list-style: none; }
/* Siatka jest na KAŻDEJ pozycji, nie na liście: wysokość wiersza wynika
   z treści tej jednej pozycji i nie zależy od liczby pozostałych.           */
.foreto-timeline__item {
  position: relative; display: grid;
  grid-template-columns: var(--foreto-tl-cols);
  column-gap: var(--foreto-tl-gap);
  align-items: start; min-height: var(--foreto-tl-pitch);
}
.foreto-timeline__item:nth-child(odd) .foreto-timeline__content { grid-column: var(--foreto-tl-col-odd); }
.foreto-timeline__item:nth-child(even) .foreto-timeline__content { grid-column: var(--foreto-tl-col-even); }
.foreto-timeline__item:nth-child(odd) .foreto-timeline__dot { left: var(--foreto-tl-dot-x-odd); }
.foreto-timeline__item:nth-child(even) .foreto-timeline__dot { left: var(--foreto-tl-dot-x-even); }
/* Ostatnia pozycja nie potrzebuje pustego slotu pod sobą.                    */
.foreto-timeline__item:last-child { min-height: 0; }
.foreto-timeline__date {
  grid-column: var(--foreto-tl-date-col); grid-row: 1;
  padding-inline: var(--foreto-tl-date-pad);
  text-align: center; color: #FFFFFF;
  /* Ten sam kolor co szyna, więc niewidoczne — ale dzięki temu biały tekst ma
     nieprzezroczyste tło na SWOIM rodzicu i kontrast daje się wyliczyć.     */
  background: var(--foreto-surface-accent);
}
/* width: fit-content + margin auto zamiast display: block. Efekt wizualny
   identyczny, ale pole tekstowe przestaje sięgać pod biały punkt leżący
   okrakiem na krawędzi szyny — inaczej skaner zgłasza „partially obscured". */
.foreto-timeline__year, .foreto-timeline__month { display: block; width: fit-content; margin-inline: auto; }
/* Numer etapu w Doto, tak jak numerały Reveal i liczby w paśmie faktów —
   ten sam krój niosą w systemie wszystkie WARTOŚCI LICZBOWE. Waga 900, bo
   przy tym stopniu cieńsza siatka pikseli rozpada się na tle szyny.        */
.foreto-timeline__year {
  margin: 0;
  font-family: var(--foreto-font-display), 'Poppins', sans-serif;
  font-variation-settings: 'ROND' 0, 'wght' 900;
  font-weight: 900;
  font-size: var(--foreto-tl-year-font, var(--foreto-font-size-h2));
  line-height: var(--foreto-tl-year-line, var(--foreto-line-h2));
}
.foreto-timeline__month {
  margin-top: var(--foreto-tl-month-gap, var(--foreto-space-1));
  line-height: var(--foreto-space-4);
  font-size: var(--foreto-font-size-body); font-weight: 700;
}
/* Punkt leży NA krawędzi szyny, czyli w poprzek granicy dwóch komórek siatki
   — stąd pozycjonowanie absolutne.                                          */
.foreto-timeline__dot {
  position: absolute; top: var(--foreto-tl-dot-top);
  display: grid; place-items: center;
  width: var(--foreto-tl-dot); height: var(--foreto-tl-dot);
  background: #FFFFFF;
}
.foreto-timeline__dot::before {
  content: ''; width: var(--foreto-tl-dot-core); height: var(--foreto-tl-dot-core);
  background: var(--foreto-red);
}
.foreto-timeline__content {
  grid-row: var(--foreto-tl-content-row);
  padding-top: var(--foreto-tl-head-top); padding-bottom: var(--foreto-tl-item-pad);
}
/* overflow-wrap: gwarancja przeciw poziomemu przewijaniu przy długim słowie
   z CMS-a — nagłówek 32 px w kolumnie 264 px mieści słowa do ~13 znaków.    */
.foreto-timeline__heading {
  margin: 0; font-size: var(--foreto-font-size-h3); line-height: var(--foreto-line-h3);
  font-weight: 700; text-transform: uppercase; overflow-wrap: anywhere;
}
/* Opisy MAŁYMI LITERAMI — zamierzony zabieg typograficzny ze źródła, nie
   artefakt wypełniacza. Zapis w treści zostaje normalny, małe litery robi CSS:
   zdanie bez wielkiej litery traci sygnał początku, a redaktor w CMS-ie nie
   musi pamiętać o konwencji.                                                */
.foreto-timeline__text { margin-top: var(--foreto-space-1); line-height: var(--foreto-space-4); text-transform: lowercase; }

/* --- Timeline: stan „pokaż wszystkie" -------------------------------------
   Oś czasu firmy rośnie z każdym rokiem: dwanaście pozycji to 2100 px sekcji
   i nikt nie dochodzi do stopki. Próg podaje właściciel (props initial), a nie
   komponent — bo to decyzja o treści, nie o wyglądzie.
   Pozycje poza progiem dostają ATRYBUT hidden, nie klasę: to jedyny mechanizm
   ukrycia, który wypada razem z układu I z drzewa dostępności, więc czytnik nie
   czyta pozycji, której nie widać. Reguła poniżej jest potrzebna, bo
   .foreto-timeline__item deklaruje display: grid, a klasa bije display: none
   z arkusza przeglądarki — bez niej hidden nic nie robi.                     */
.foreto-timeline__item[hidden] { display: none; }

/* Wejście pozycji przy przewijaniu. Ten sam język co pasmo faktów: wyłonienie
   z lekkim podjazdem, bez przesuwania w bok — pozycje stoją na przemian po
   dwóch stronach szyny tylko w szerokim układzie, a w wąskim wszystkie po
   jednej, więc wjazd „ze swojej strony" wyglądałby tam jak usterka.

   Oś to `view()` NA POZYCJI, nie zegar dokumentu. Oś czasu odpalałaby się przy
   wczytaniu strony, a komponent stoi nisko — animacja skończyłaby się, zanim
   ktokolwiek do niej doscrolluje, czyli byłaby pracą wykonaną w ciemno.

   Całość siedzi w `@supports`, bo `both` zostawia stan początkowy: przeglądarka
   bez osi przewijania trzymałaby pozycje na `opacity: 0` w nieskończoność.
   Brak animacji jest tu poprawnym wynikiem, niewidoczna treść nie jest.     */
@keyframes foreto-timeline-in {
  from { opacity: 0; transform: translateY(var(--foreto-space-5)); }
  to   { opacity: 1; transform: none; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .foreto-timeline__item {
      animation: foreto-timeline-in linear both;
      animation-timeline: view();
      /* Zakres w fazie „cover", NIE „entry" — i to jest cała różnica między
         animacją, którą widać, a taką, której nie ma.

         Faza `entry` dla elementu NIŻSZEGO OD OKNA trwa dokładnie tyle, ile
         jego wysokość: pozycja ma 175 px, okno bywa 900-1600. Całe wyłanianie
         mieściło się więc w pasie 90 px tuż przy dolnej krawędzi ekranu i
         kończyło, zanim pozycja dojechała tam, gdzie ktokolwiek patrzy.
         Zmierzone: krycie 0 aż do chwili, gdy pozycja dotyka dolnej krawędzi,
         0,75 sto pikseli dalej, 1 po dwustu.

         `cover` liczy całą drogę przez ekran, więc 5-20% to odcinek, na którym
         pozycja wjeżdża od dołu i zatrzymuje się mniej więcej ćwierć ekranu
         wyżej — czyli dokładnie tam, gdzie wzrok już jest.                  */
      animation-range: cover 5% cover 20%;
    }
  }
}
/* Ostatnia WIDOCZNA pozycja to ta, za którą stoi ukryta. Bez tego zostaje pod
   nią pusty raster 175 px i przełącznik odjeżdża od treści o pół ekranu.     */
.foreto-timeline__item:has(+ .foreto-timeline__item[hidden]) { min-height: 0; }

/* Przełącznik czyta TĘ SAMĄ siatkę co pozycja, więc grot ląduje na szynie bez
   ani jednej własnej liczby: kolumna daty jest w każdym wariancie kolumną
   szyny, a etykieta idzie tam, gdzie treść pozycji nieparzystej.
   Cały wiersz jest jednym przyciskiem: cel kliknięcia to grot ORAZ etykieta,
   nie sam glif 30 px.                                                       */
.foreto-timeline__more {
  display: grid; align-items: center;
  grid-template-columns: var(--foreto-tl-cols);
  column-gap: var(--foreto-tl-gap);
  width: 100%; padding: 0; border: 0; background: none;
  color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.foreto-timeline__more-glyph {
  grid-column: var(--foreto-tl-rail-col); grid-row: 1; justify-self: center;
  width: var(--foreto-tl-more); aspect-ratio: 30 / 27;
  background-color: var(--foreto-tl-more-ink);
  -webkit-mask: url('../icons/double-chevron-down.svg') no-repeat center / contain;
          mask: url('../icons/double-chevron-down.svg') no-repeat center / contain;
  transition: transform var(--foreto-transition-fast);
}
.foreto-timeline__more-label {
  grid-column: var(--foreto-tl-col-odd); grid-row: 1;
  font-size: var(--foreto-font-size-card); line-height: var(--foreto-space-4);
  font-weight: 700; text-transform: uppercase;
}
/* Obrót niesie STAN, więc zostaje także przy prefers-reduced-motion; ruch o
   4 px to ta sama reakcja co grot w przycisku, tylko w osi pionowej — bo tu
   „dalej" znaczy w dół. Kolor się nie zmienia: etykieta stoi raz na bieli, raz
   na paśmie, a przycisk i tak niesie reakcję ruchem i podkreśleniem.        */
.foreto-timeline__more:hover .foreto-timeline__more-label,
.foreto-timeline__more:focus-visible .foreto-timeline__more-label { text-decoration: underline; text-underline-offset: 3px; }
.foreto-timeline__more:hover .foreto-timeline__more-glyph,
.foreto-timeline__more:focus-visible .foreto-timeline__more-glyph { transform: translateY(4px); }
.foreto-timeline__more[aria-expanded="true"] .foreto-timeline__more-glyph { transform: rotate(180deg); }
.foreto-timeline__more[aria-expanded="true"]:hover .foreto-timeline__more-glyph,
.foreto-timeline__more[aria-expanded="true"]:focus-visible .foreto-timeline__more-glyph { transform: rotate(180deg) translateY(4px); }
@media (prefers-reduced-motion: reduce) {
  .foreto-timeline__more-glyph { transition: none; }
  .foreto-timeline__more:hover .foreto-timeline__more-glyph,
  .foreto-timeline__more:focus-visible .foreto-timeline__more-glyph { transform: none; }
  .foreto-timeline__more[aria-expanded="true"]:hover .foreto-timeline__more-glyph,
  .foreto-timeline__more[aria-expanded="true"]:focus-visible .foreto-timeline__more-glyph { transform: rotate(180deg); }
}

/* ≤ 1181: jedna kolumna, szyna przy lewej krawędzi. Zygzak wymaga 2 × 405 px
   na treść; niżej kolumny schodzą pod 300 px, gdzie nagłówek 32 px łamie się
   po jednym słowie. Naprzemienność znika przez ZRÓWNANIE zmiennych — bez ani
   jednej dodatkowej reguły. Szyna 128 = 96 (najszersza data) + 2 × 16.      */
@media (max-width: 1181px) {
  .foreto-timeline {
    --foreto-tl-rail: 128px; --foreto-tl-gap: var(--foreto-space-5);
    --foreto-tl-pitch: 0px; --foreto-tl-item-pad: var(--foreto-space-7);
    --foreto-tl-cols: var(--foreto-tl-rail) minmax(0, 1fr);
    --foreto-tl-rail-x: 0px;
    --foreto-tl-date-col: 1; --foreto-tl-rail-col: 1;
    /* Punkt siedzi okrakiem na prawej krawędzi szyny i zjada 17 px jej
       szerokości — bez tego „2014" kończy się tam, gdzie zaczyna punkt.    */
    --foreto-tl-date-pad: calc(var(--foreto-tl-dot) / 2);
    --foreto-tl-year-font: var(--foreto-font-size-h3);
    --foreto-tl-year-line: var(--foreto-line-h3);
    --foreto-tl-col-odd: 2; --foreto-tl-col-even: 2;
    --foreto-tl-dot-x-odd: calc(var(--foreto-tl-rail) - var(--foreto-tl-dot) / 2);
    --foreto-tl-dot-x-even: calc(var(--foreto-tl-rail) - var(--foreto-tl-dot) / 2);
  }
}
/* ≤ 760: szyna jako kreska, data nad tekstem. Przy 375 px kontener ma 327;
   szyna 128 zostawiłaby 167 px na tekst — za mało dla nagłówka 32 px.       */
@media (max-width: 760px) {
  .foreto-timeline {
    --foreto-tl-rail: 8px; --foreto-tl-gap: var(--foreto-space-4);
    /* Szyna jest tu kreską 8 px, więc biały grot nie miałby na czym stanąć:
       kolor schodzi na czerwień tekstową, a wymiar na 24 — tyle ma pierwsza
       kolumna (szerokość punktu), a 30 px wystawałoby poza komponent.       */
    --foreto-tl-more: 24px; --foreto-tl-more-ink: var(--foreto-text-accent);
    --foreto-tl-dot: 24px; --foreto-tl-dot-core: 12px; --foreto-tl-dot-top: 12px;
    --foreto-tl-cap: var(--foreto-space-5); --foreto-tl-item-pad: var(--foreto-space-6);
    --foreto-tl-head-top: 0px;
    /* Pierwsza kolumna ma szerokość PUNKTU, nie szyny — inaczej punkt 24 px
       wystawałby 8 px poza komponent i dokładał poziomy pasek.             */
    --foreto-tl-cols: var(--foreto-tl-dot) minmax(0, 1fr);
    --foreto-tl-rail-x: calc(var(--foreto-tl-dot) / 2 - var(--foreto-tl-rail) / 2);
    --foreto-tl-date-col: 2; --foreto-tl-rail-col: 1; --foreto-tl-date-pad: 0px; --foreto-tl-content-row: 2;
    --foreto-tl-col-odd: 2; --foreto-tl-col-even: 2;
    --foreto-tl-dot-x-odd: 0px; --foreto-tl-dot-x-even: 0px;
    --foreto-tl-year-font: var(--foreto-font-size-h3);
    --foreto-tl-year-line: var(--foreto-line-h3);
  }
  .foreto-timeline__item { row-gap: var(--foreto-space-2); }
  /* Data poziomo, bo nie ma już czerwonego pasa na dwa wiersze.            */
  .foreto-timeline__date {
    display: flex; align-items: baseline; gap: var(--foreto-space-2);
    text-align: left; color: var(--foreto-ink); background: none;
  }
  .foreto-timeline__year, .foreto-timeline__month { margin-inline: 0; }
  .foreto-timeline__year { color: var(--foreto-text-accent); }
  .foreto-timeline__month { margin-top: 0; }
}

/* Właściciel może włączyć pomiar kontenerem jedną klasą — wtedy komponent
   dopasowuje się do kolumny, w której stoi, a nie do szerokości okna.        */
.foreto-timeline-box { container-type: inline-size; }

@container (max-width: 1133px) {
  .foreto-timeline {
    --foreto-tl-rail: 128px; --foreto-tl-gap: var(--foreto-space-5);
    --foreto-tl-pitch: 0px; --foreto-tl-item-pad: var(--foreto-space-7);
    --foreto-tl-cols: var(--foreto-tl-rail) minmax(0, 1fr);
    --foreto-tl-rail-x: 0px;
    --foreto-tl-date-col: 1; --foreto-tl-rail-col: 1;
    --foreto-tl-date-pad: calc(var(--foreto-tl-dot) / 2);
    --foreto-tl-year-font: var(--foreto-font-size-h3);
    --foreto-tl-year-line: var(--foreto-line-h3);
    --foreto-tl-col-odd: 2; --foreto-tl-col-even: 2;
    --foreto-tl-dot-x-odd: calc(var(--foreto-tl-rail) - var(--foreto-tl-dot) / 2);
    --foreto-tl-dot-x-even: calc(var(--foreto-tl-rail) - var(--foreto-tl-dot) / 2);
  }
}
/* Powyżej 1181 px kontener PRZYWRACA zygzak — inaczej wąskie okno raz na
   zawsze zabrałoby wariant szeroki, nawet w szerokiej kolumnie.              */
@container (min-width: 1134px) {
  .foreto-timeline {
    --foreto-tl-rail: 160px; --foreto-tl-gap: 82px; --foreto-tl-pitch: 175px;
    --foreto-tl-more: 30px; --foreto-tl-more-ink: #FFFFFF; --foreto-tl-rail-col: 2;
    --foreto-tl-dot: 34px; --foreto-tl-dot-core: 20px;
    --foreto-tl-dot-top: calc((var(--foreto-line-h2) - 34px) / 2);
    --foreto-tl-head-top: 13px;
  /* Grot przełącznika „pokaż wszystkie": 30 px to wymiar GLIFU z biblioteki
     (30 × 27, podwójny chevron), a nie stopień skali ikon — ten sam plik i ten
     sam wymiar co ScrollMarker, bo to ten sam komunikat: „dalej jest więcej".
     Biel, bo grot stoi NA szynie; wariant ≤ 760 zmienia oba (szyna jest tam
     kreską 8 px i biały glif nie miałby na czym stanąć).                    */
  --foreto-tl-more: 30px;
  --foreto-tl-more-ink: #FFFFFF; --foreto-tl-cap: 52px;
    --foreto-tl-item-pad: var(--foreto-space-4);
    --foreto-tl-cols: minmax(0, 1fr) var(--foreto-tl-rail) minmax(0, 1fr);
    --foreto-tl-rail-x: calc(50% - var(--foreto-tl-rail) / 2);
    --foreto-tl-date-col: 2; --foreto-tl-date-pad: 0px; --foreto-tl-content-row: 1;
    --foreto-tl-col-odd: 3; --foreto-tl-col-even: 1;
    --foreto-tl-dot-x-odd: calc(50% + var(--foreto-tl-rail) / 2 - var(--foreto-tl-dot) / 2);
    --foreto-tl-dot-x-even: calc(50% - var(--foreto-tl-rail) / 2 - var(--foreto-tl-dot) / 2);
    --foreto-tl-year-font: var(--foreto-font-size-h2);
    --foreto-tl-year-line: var(--foreto-line-h2);
  }
  .foreto-timeline__date {
    display: block; text-align: center;
    color: #FFFFFF; background: var(--foreto-surface-accent);
  }
  .foreto-timeline__year, .foreto-timeline__month { margin-inline: auto; }
  .foreto-timeline__year { color: inherit; }
  .foreto-timeline__month { margin-top: var(--foreto-tl-month-gap, var(--foreto-space-1)); }
}
@container (max-width: 760px) {
  .foreto-timeline {
    --foreto-tl-rail: 8px; --foreto-tl-gap: var(--foreto-space-4);
    /* Szyna jest tu kreską 8 px, więc biały grot nie miałby na czym stanąć:
       kolor schodzi na czerwień tekstową, a wymiar na 24 — tyle ma pierwsza
       kolumna (szerokość punktu), a 30 px wystawałoby poza komponent.       */
    --foreto-tl-more: 24px; --foreto-tl-more-ink: var(--foreto-text-accent);
    --foreto-tl-dot: 24px; --foreto-tl-dot-core: 12px; --foreto-tl-dot-top: 12px;
    --foreto-tl-cap: var(--foreto-space-5); --foreto-tl-item-pad: var(--foreto-space-6);
    --foreto-tl-head-top: 0px;
    --foreto-tl-cols: var(--foreto-tl-dot) minmax(0, 1fr);
    --foreto-tl-rail-x: calc(var(--foreto-tl-dot) / 2 - var(--foreto-tl-rail) / 2);
    --foreto-tl-date-col: 2; --foreto-tl-rail-col: 1; --foreto-tl-date-pad: 0px; --foreto-tl-content-row: 2;
    --foreto-tl-col-odd: 2; --foreto-tl-col-even: 2;
    --foreto-tl-dot-x-odd: 0px; --foreto-tl-dot-x-even: 0px;
    --foreto-tl-year-font: var(--foreto-font-size-h3);
    --foreto-tl-year-line: var(--foreto-line-h3);
  }
  .foreto-timeline__item { row-gap: var(--foreto-space-2); }
  .foreto-timeline__date {
    display: flex; align-items: baseline; gap: var(--foreto-space-2);
    text-align: left; color: var(--foreto-ink); background: none;
  }
  .foreto-timeline__year, .foreto-timeline__month { margin-inline: 0; }
  .foreto-timeline__year { color: var(--foreto-text-accent); }
  .foreto-timeline__month { margin-top: 0; }
}

/* --- ScrollMarker — znacznik „przewiń dalej" -----------------------------
   Odstęp czytany ze zmiennej Z WARTOŚCIĄ DOMYŚLNĄ w var(), a nie zadeklarowany
   na elemencie: deklaracja wygrywałaby z dziedziczeniem, więc strona nie
   mogłaby podać własnej wartości przez style. Nazwane modyfikatory straciły
   sens przy trzeciej podstronie z innym odstępem — nazwa nic nie znaczy,
   wartość znaczy.                                                           */
.foreto-scroll-mark { padding-top: var(--foreto-mark-gap, 23px); color: var(--foreto-red); }
/* Podwójny chevron 30×27 — wymiar GLIFU, nie ramki, w której go narysowano.  */
.foreto-scroll-mark__glyph {
  display: block; width: 30px; height: 27px;
  background-color: currentColor;
  -webkit-mask: url('../icons/double-chevron-down.svg') no-repeat center / contain;
          mask: url('../icons/double-chevron-down.svg') no-repeat center / contain;
}
/* Obrazki mają display: block, więc text-align rodzica ich nie wyśrodkuje.   */
.foreto-scroll-mark > * { margin-inline: auto; }
@media (max-width: 1181px) { .foreto-scroll-mark { padding-top: var(--foreto-mark-gap, var(--foreto-space-6)); } }

/* --- TraitBar — czerwony pasek z jedną cechą -----------------------------
   Pasek 100 px, ikona 49 jako maska w kolorze tekstu (biała na czerwonym).
   Odstęp liczony ze zmierzonych pozycji: 95 − 26 − 49 = 20.                  */
.foreto-trait-bar {
  --foreto-trait-h: 100px; --foreto-trait-icon: 49px; --foreto-trait-pad: 26px;
  display: flex; align-items: center;
  gap: calc(95px - var(--foreto-trait-pad) - var(--foreto-trait-icon));
  min-height: var(--foreto-trait-h);
  padding-inline: var(--foreto-trait-pad);
  background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent);
  font-size: var(--foreto-font-size-h3); line-height: var(--foreto-line-h3); font-weight: 700;
}
.foreto-trait-bar__icon {
  flex: 0 0 auto;
  width: var(--foreto-trait-icon); height: var(--foreto-trait-icon);
  background-color: currentColor;
  -webkit-mask: url('../icons/values/radio-on.svg') no-repeat center / contain;
          mask: url('../icons/values/radio-on.svg') no-repeat center / contain;
}
/* Jedna kolumna już przy 760, nie dopiero na telefonie: przy dwóch kolumnach na
   768 px na pasek zostaje 337 px, z czego na tekst 216 — a „odpowiedzialność"
   w 32 px bold ma około 290. Element flex nie zejdzie poniżej najdłuższego
   słowa, więc siatka rozpychała stronę.                                      */
@media (max-width: 760px) {
  .foreto-trait-bar {
    --foreto-trait-icon: 32px; --foreto-trait-pad: var(--foreto-space-4); --foreto-trait-h: 72px;
    font-size: var(--foreto-font-size-body); line-height: var(--foreto-space-4);
  }
}

/* --- Icons — PRZENIESIONY 1:1 z kit/icons.css -----------------------------
   Ikony są MASKAMI, nie obrazkami: kolor bierze się z currentColor, więc jeden
   plik obsługuje biel na czerwonym i czerń na białym.

   Skala 12/24/36/48, nie „standardowa" 16/24/32. Powód jest w rysunku: glify
   stanów formularza mają siatkę o kroku 2 na 24, czyli 12 komórek, a komórka
   wypada na całym pikselu tylko przy wielokrotności 12. Przy 16 ma 1,33 px
   i cienka kreska rozmywa się na pół piksela.

   contain, nie 100% 100%: glify mają różne proporcje (12×20, 18×16, 30×17),
   a rozciągnięcie do kwadratu by je zniekształciło.                        */
.foreto-icon {
  display: block; flex: 0 0 auto;
  width: 24px; height: 24px;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;  mask-position: center;
  -webkit-mask-size: contain;     mask-size: contain;
}
.foreto-icon--12 { width: 12px; height: 12px; }
.foreto-icon--24 { width: 24px; height: 24px; }
.foreto-icon--36 { width: 36px; height: 36px; }
.foreto-icon--48 { width: 48px; height: 48px; }

.foreto-icon--arrow-right    { -webkit-mask-image: url('../icons/arrow-right.svg');         mask-image: url('../icons/arrow-right.svg'); }
.foreto-icon--arrow-right-sm { -webkit-mask-image: url('../icons/arrow-right-sm.svg');      mask-image: url('../icons/arrow-right-sm.svg'); }
.foreto-icon--arrow-long     { -webkit-mask-image: url('../icons/arrow-long-right.svg');    mask-image: url('../icons/arrow-long-right.svg'); }
.foreto-icon--check          { -webkit-mask-image: url('../icons/check-square.svg');        mask-image: url('../icons/check-square.svg'); }
.foreto-icon--chevron-sm     { -webkit-mask-image: url('../icons/chevron-sm.svg');          mask-image: url('../icons/chevron-sm.svg'); }
.foreto-icon--chevron-down   { -webkit-mask-image: url('../icons/chevron-down.svg');        mask-image: url('../icons/chevron-down.svg'); }
.foreto-icon--chevron-back   { -webkit-mask-image: url('../icons/chevron-down.svg');        mask-image: url('../icons/chevron-down.svg'); transform: rotate(90deg); }
.foreto-icon--double-chevron { -webkit-mask-image: url('../icons/double-chevron-down.svg'); mask-image: url('../icons/double-chevron-down.svg'); }
.foreto-icon--quote          { -webkit-mask-image: url('../icons/quote.svg');               mask-image: url('../icons/quote.svg'); }

/* --- Breadcrumbs — PRZENIESIONY 1:1 z kit/navigation.css ------------------
   Separator to strzałka DŁUGA, inny kształt niż grot z przycisku: grot czyta
   się jak „>", a strzałka jak „→", czyli jak przejście o stopień dalej.

   Bieżąca strona czerwona przez --foreto-text-accent, NIE --foreto-red: to
   tekst 16 px, a surowa czerwień marki nie przechodzi AA (4,46:1).

   Wyśrodkowanie jest tu domyślne, bo komponent wyjęto z nagłówka podstrony,
   gdzie ścieżka stoi pod tytułem na środku.                                 */
.foreto-crumbs__list {
  display: flex; align-items: center; justify-content: center;
  gap: var(--foreto-space-3);
  font-size: var(--foreto-font-size-body);
  font-weight: 700;
}
.foreto-crumbs a { transition: color var(--foreto-transition-base); }
.foreto-crumbs [aria-current="page"] { color: var(--foreto-text-accent); }

/* --- FacetFilter — PRZENIESIONY 1:1 z kit/data.css ------------------------
   Panel zawężeń o DWÓCH osiach i więcej (branża × usługa), czyli to, czego
   FilterBar nie robi: tamten jest jednym paskiem nad tabelą i niesie jedną
   grupę. Rozdzielenie jest konieczne, bo dopiero przy dwóch osiach pojawiają
   się trzy problemy, których w jednej grupie nie ma: która pozycja należy do
   której grupy, ile jest wyników za każdą pozycją i co zrobić z kombinacjami
   pustymi.

   CZEGO TU NIE MA I DLACZEGO. Nie ma pozycji „wszystkie" — zdjęcie zawężenia
   to kliknięcie w tę samą pozycję drugi raz, a osobna pozycja „wszystkie"
   byłaby czwartym stanem do rozpoznania. Nie ma zerowania per grupa: przy
   dwóch grupach i jednym przycisku „Wyczyść wszystko" nie ma czego oszczędzać,
   a trzy przyciski zerujące na jednym panelu czyta się dłużej niż same filtry.

   PODZIAŁ NA KOLUMNY. Etykieta grupy stoi w kolumnie 128 (--space-12), pozycje
   w resztę szerokości. Etykieta NAD grupą zostawia oko bez punktu zaczepienia:
   dwa rzędy prostokątów o różnych szerokościach czytają się jak jedna plama.
   Kolumna daje panelowi pionową krawędź i to ona grupuje, nie odstęp.        */
.foreto-facets {
  display: flex; flex-direction: column;
  /* Stopień skali ikon, i ten, który mieści się w wierszu 16 px etykiety. */
  --foreto-facets-tick: 12px;
}
.foreto-facets__group {
  display: grid;
  grid-template-columns: var(--foreto-space-12) minmax(0, 1fr);
  column-gap: var(--foreto-space-4);
  align-items: start;
  margin: 0; padding: var(--foreto-space-4) 0; border: 0;
}
.foreto-facets__group + .foreto-facets__group { border-top: 1px solid var(--foreto-border-hairline); }
.foreto-facets__legend {
  margin: 0; padding: 0;
  /* Wyśrodkowanie względem PIERWSZEGO rzędu pozycji, policzone z wysokości
     pozycji, nie wpisane liczbą: (40 − 16) / 2. Gdy pozycje zmienią wysokość,
     etykieta pojedzie za nimi.                                              */
  padding-top: calc((var(--foreto-field-h-dense) - 16px) / 2);
  font-size: 12px; line-height: 16px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--foreto-ink);
}
.foreto-facets__opts { display: flex; flex-wrap: wrap; gap: var(--foreto-space-2); }

/* POZYCJA. Wysokość 40 = --field-h-dense, ta sama co pole zwarte i przycisk
   size="sm" — panel filtrów stoi obok formularzy i sortowań, więc ma ich rytm.
   Ramka jest w --border-control (#737373), nie hairline: to GRANICA KONTROLKI,
   czyli próg 3:1 z WCAG 1.4.11, ta sama decyzja co przy polach formularza.
   Pismo 12/16 wersalikami z .04em — jedna klasa typograficzna z etykietami
   grup, więc cały panel mówi jednym głosem.                                 */
.foreto-facets__opt {
  /* Odstęp 0, bo światło wokół ptaszka i licznika jest podawane osobno: slot
     ptaszka ROŚNIE przy wyborze, więc nie może brać odstępu z gap — inaczej
     w spoczynku zostawałby po lewej 4 px pustego bez powodu.                 */
  display: inline-flex; align-items: center; gap: 0;
  min-height: var(--foreto-field-h-dense);
  padding-inline: var(--foreto-space-3);
  border: 1px solid var(--foreto-border-control); border-radius: 0;
  background: transparent; color: var(--foreto-ink);
  font-size: 12px; line-height: 16px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  text-align: left; cursor: pointer;
  transition: background-color var(--foreto-transition-base),
              border-color var(--foreto-transition-base),
              color var(--foreto-transition-base);
}
/* Ptaszek NIE jest jedynym nośnikiem wyboru — jest drugim nośnikiem obok
   czerwieni: kolor nigdy sam.

   SLOT ROŚNIE, nie jest rezerwowany. Rezerwa 12 + 4 px stała w spoczynku pusta
   i dawała po lewej 32 px światła przy 16 po prawej — pozycja wyglądała na
   przekrzywioną, mimo że dopełnienie było symetryczne. W spoczynku slot ma
   szerokość 0 i światło jest po obu stronach równe, a przy wyborze rozsuwa się
   do 12 + 4 razem z pojawieniem się ptaszka.

   Glif to check.svg (goły ptaszek), NIE check-square.svg. To drugie jest polem
   wyboru — ramką z ptaszkiem w środku — i w pozycji już zaznaczonej rysowało
   drugi checkbox wielkości 12 px, w którym sam ptaszek miał 5 px.

   MASKA JEST WIĘKSZA OD SLOTU i to jest cały rachunek: ptaszek zajmuje w pliku
   10 × 8 jednostek pola 24 × 24, więc przy masce równej slotowi kreska ma
   12 × 10/24 = 5 px i wygląda na usterkę. Maska 12 × 24/10 = 28,8 px daje
   kreskę dokładnie 12 px szerokości.                                        */
.foreto-facets__opt::before {
  content: ''; flex: 0 0 auto;
  width: 0; height: var(--foreto-facets-tick);
  margin-inline-end: 0;
  background-color: currentColor; opacity: 0;
  transition: width var(--foreto-transition-base),
              margin-inline-end var(--foreto-transition-base),
              opacity var(--foreto-transition-base);
  -webkit-mask-image: url('../icons/check.svg'); mask-image: url('../icons/check.svg');
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-size: calc(var(--foreto-facets-tick) * 24 / 10);
          mask-size: calc(var(--foreto-facets-tick) * 24 / 10);
}
.foreto-facets__opt[aria-pressed="true"]::before {
  width: var(--foreto-facets-tick);
  margin-inline-end: var(--foreto-space-1);
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .foreto-facets__opt::before { transition: none; }
}
.foreto-facets__opt:hover { background: var(--foreto-surface-band); border-color: var(--foreto-ink); }
.foreto-facets__opt:focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 2px; }
.foreto-facets__opt[aria-pressed="true"] {
  background: var(--foreto-surface-accent); border-color: var(--foreto-surface-accent);
  color: var(--foreto-text-on-accent);
}
.foreto-facets__opt[aria-pressed="true"]:hover {
  background: var(--foreto-accent-hover); border-color: var(--foreto-accent-hover);
}
/* KOMBINACJA BEZ TREŚCI. Pozycja zostaje widoczna i wyłączona, a nie zniknięta:
   znikające pozycje przebudowują rząd po każdym kliknięciu i nie da się
   zauważyć, że gdzieś indziej coś jest. Licznik pokazuje wtedy 0 i to on
   niesie stan bez koloru. Ramka schodzi na hairline, bo próg 1.4.11 nie
   dotyczy kontrolek wyłączonych.

   POPRAWKA MOTYWU: kit pisze samo :disabled, a jego własny szablon PHP rysuje
   pozycję wyłączoną jako <span aria-disabled="true"> — a w span :disabled nie
   trafia nigdy, bo to pseudoklasa kontrolek formularza. Stąd bliźniaczy
   selektor atrybutowy; :disabled zostaje dla wariantu na przyciskach.       */
.foreto-facets__opt:disabled,
.foreto-facets__opt[aria-disabled="true"] {
  border-color: var(--foreto-border-hairline); color: var(--foreto-text-muted);
  cursor: not-allowed;
}
.foreto-facets__opt:disabled:hover,
.foreto-facets__opt[aria-disabled="true"]:hover { background: transparent; border-color: var(--foreto-border-hairline); }
/* Licznik stoi po PRAWEJ, ptaszek po lewej, więc pozycja czyta się od stanu do
   liczby. Światło 8 (--space-2), nie 4: przy 4 licznik czytał się jako drugie
   słowo etykiety, a nie jako liczba. Nie ma go w gap, bo gap jest zerowy —
   patrz slot ptaszka. Waga 400 i tabular-nums, żeby rząd nie drgał przy
   przeliczeniu.                                                             */
.foreto-facets__n {
  margin-inline-start: var(--foreto-space-2);
  font-weight: 400; letter-spacing: 0;
  font-variant-numeric: tabular-nums; color: var(--foreto-text-muted);
}
.foreto-facets__opt[aria-pressed="true"] .foreto-facets__n,
.foreto-facets__opt:disabled .foreto-facets__n,
.foreto-facets__opt[aria-disabled="true"] .foreto-facets__n { color: inherit; }

/* STOPKA. Liczba wyników jest w panelu, nie pod listą, bo zmienia się razem
   z zawężeniami. Ma role="status", bo bez zapowiedzi kliknięcie w filtr jest
   dla czytnika bezgłośne.                                                   */
.foreto-facets__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--foreto-space-2) var(--foreto-space-4);
  padding-top: var(--foreto-space-4);
  border-top: 1px solid var(--foreto-border-hairline);
}
.foreto-facets__result {
  margin: 0; font-size: var(--foreto-font-size-card); line-height: var(--foreto-msg-line);
  color: var(--foreto-text-muted); font-variant-numeric: tabular-nums;
}
/* „Wyczyść" stopniem etykiety (12/16 + .04em), nie stopniem przycisku: przy
   16 px wersalikami czytało się jak nagłówek sekcji, a nie jak drobna akcja.
   Podkreślenie niesie „to działa" — bez niego drobny tekst wygląda na opis.  */
.foreto-facets__clear {
  align-self: center; margin-inline-start: auto;
  font-size: 12px; line-height: 16px; letter-spacing: .04em;
  text-decoration: underline; text-underline-offset: 3px;
}
.foreto-facets__clear:hover, .foreto-facets__clear:focus-visible { color: var(--foreto-text-accent); }

/* Etykieta nad pozycjami dopiero tam, gdzie kolumna 128 + odstęp 24 zabiera
   ponad połowę szerokości. Przy 312 px zostawiałaby na pozycje 160.         */
@media (max-width: 760px) {
  .foreto-facets__group { grid-template-columns: minmax(0, 1fr); row-gap: var(--foreto-space-2); }
  .foreto-facets__legend { padding-top: 0; }
  .foreto-facets__clear { margin-inline-start: 0; }
}
/* Ten sam próg mierzony NA KOLUMNIE: panel stoi w treści strony, w szufladzie
   i w kolumnie bocznej, a @media o szerokości kolumny nic nie wie. Pomiar
   włącza .foreto-facets-box na przodku.                                     */
.foreto-facets-box { container-type: inline-size; }
@container (min-width: 761px) {
  .foreto-facets__group {
    grid-template-columns: var(--foreto-space-12) minmax(0, 1fr);
    row-gap: 0;
  }
  .foreto-facets__legend { padding-top: calc((var(--foreto-field-h-dense) - 16px) / 2); }
  .foreto-facets__clear { margin-inline-start: auto; }
}
@container (max-width: 760px) {
  .foreto-facets__group { grid-template-columns: minmax(0, 1fr); row-gap: var(--foreto-space-2); }
  .foreto-facets__legend { padding-top: 0; }
  .foreto-facets__clear { margin-inline-start: 0; }
}

/* --- AspectRatio — PRZENIESIONY 1:1 z kit/layout.css ----------------------
   Proporcja pola podawana zmienną, żeby jeden element obsługiwał kadr 640/594
   z galerii i 16/9 z wideo.                                                 */
.foreto-ratio { aspect-ratio: var(--foreto-ratio, 16 / 9); }

/* --- CaseList + CaseCard — PRZENIESIONE 1:1 z kit/content.css -------------
   Karta listingu realizacji. W serwisie tej pozycji nie ma jako komponentu:
   listing case studies jest tam składany ad hoc i widać to po skutkach —
   nadtytuł powtarza tytuł słowo w słowo, dwie sąsiednie karty mają dwa różne
   języki akcji (czerwony link z grotem i czarny przycisk), prawa kolumna kończy
   się w połowie wysokości, a wysokość karty bierze się z wysokości makiety
   w środku obrazka, nie z siatki — więc dwie karty pod sobą mają różną.

   DWIE SIATKI, OBIE ZE ŹRÓDŁA:
   • wariant szeroki  486 + 72 + 528 + 48 = 1134
   • wariant kaflowy  3 × 358 + 2 × 30   = 1134
   Kolumny szerokiego są podane JAKO fr (486fr / 528fr), nie w pikselach:
   1134 − 72 odstępu − 48 dopełnienia = 1014 = 486 + 528, więc przy pełnym
   kontenerze rozwiązują się dokładnie w wymiar ze źródła, a w węższym niosą tę
   samą proporcję bez ani jednego przełamania.                                */
/* 16/9 W OBU WARIANTACH, bo miniatury są dziś GENEROWANE, nie wgrywane ręcznie:
   skrypt makiet oddaje 16/9 i przy kadrze 3/2 obraz z `object-fit: contain`
   dostawał 58 px szarych pasów u góry i u dołu. Kadr dopasowany do pliku, a nie
   plik przycinany do kadru — przycięcie zjadałoby makietę telefonu ze skraju.  */
.foreto-cases {
  --foreto-case-ratio: 16 / 9;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--foreto-space-9) 30px;
}
/* Szeroki listing to jedna kolumna kart pełnej szerokości. Odstęp 80
   (--space-9), nie 30: karty są tu wysokie i przy 30 zlewały się w pas.      */
.foreto-cases--wide { grid-template-columns: minmax(0, 1fr); gap: var(--foreto-space-9); }

/* Sufit dwóch kolumn, ustawiany atrybutem bloku. Dwie klasy, więc bije bazowe
   `.foreto-cases` (jedna) — i STOI PRZED progami, bo z regułami wąskimi ma
   wagę równą i rozstrzyga wtedy kolejność w źródle. Dzięki temu przy telefonie
   nadal schodzi do jednej kolumny, zamiast trzymać dwie na siłę.
   Szersza karta mieści też oba napisy akcji w jednym wierszu: „Zobacz
   realizację" i „Otwórz serwis" potrzebują 358 px, a w układzie trzech kolumn
   karta dawała 310.                                                          */
.foreto-collection--kolumny-2 .foreto-cases { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Cień jest STAŁY, nie pojawia się na hover. Karta z cieniem to jeden z dwóch
   rodzajów pojemnika, jakie system ma — i kafel listingu jest właśnie takim
   pojemnikiem od pierwszej klatki, a nie dopiero pod kursorem. Cień
   przychodzący na hover niósł dodatkowo fałsz: sugerował, że dopiero wtedy
   karta staje się elementem, a nie że jest nim cały czas.
   Wyniesienia zostaje więc jeden poziom, tak jak w całym systemie, a reakcję na
   kursor niosą rzeczy, które nic o warstwach nie mówią: tytuł, grot, kreska
   i kadr.                                                                    */
.foreto-case {
  position: relative;
  display: grid; gap: var(--foreto-space-4);
  background: var(--foreto-surface-card);
  box-shadow: var(--foreto-shadow-card);
}
/* W ciemnym motywie cień rgb(0 0 0 / .08) na czerni jest niewidzialny, więc
   granicę niesie ramka — ta sama decyzja co w .foreto-card--shadow.          */
[data-theme="dark"] .foreto-case { box-shadow: none; outline: 1px solid var(--foreto-border-elevation); }
/* Pierścień fokusu obejmuje CAŁĄ KARTĘ, nie sam tytuł: łącze jest tytułem,
   więc jego pudełko ma wysokość dwóch wierszy tekstu, a klikalny jest cały
   obszar karty — obwódka wokół samego tytułu kłamałaby o celu.               */
.foreto-case:has(.foreto-case__link:focus-visible) {
  outline: 2px solid var(--foreto-focus); outline-offset: 3px;
}
.foreto-case__media { grid-area: media; --foreto-ratio: var(--foreto-case-ratio); }
/* Powiększenie 1,03 — skaluje się TREŚĆ kadru, nie kadr: .foreto-media ma
   overflow: hidden i proporcję z --case-ratio, więc pole zostaje co do piksela
   na swoim miejscu i w układzie nie rusza się nic. Przy szerokości 486 to 15 px
   przyrostu, czyli mniej niż dopełnienie kadru — makieta nie wyjeżdża poza
   obszar, w którym była widoczna. Rozmyte tło zostaje bez zmian: jest już
   skalowane 1,8 i drugie powiększenie byłoby na nim niewidoczne.             */
.foreto-case__media .foreto-media__img { transition: transform var(--foreto-transition-base); }
/* Dopełnienie treści niesie BODY, nie karta: obrazek ma dochodzić do krawędzi
   kafla, a tekst nie. Bez tego cień przylegał wprost do liter i kafel wyglądał
   na przycięty w druku. Góry nie dopełniam — odstęp 24 od obrazka daje już
   siatka karty, więc wszystkie cztery światła wokół tekstu mają 24.
   W wariancie szerokim tego dopełnienia nie ma: tam treść jest osobną kolumną,
   odsuniętą o 72, a prawe światło 48 niesie sama karta.                      */
.foreto-case__body {
  grid-area: body;
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--foreto-space-3);
  padding: 0 var(--foreto-space-4) var(--foreto-space-4);
}

/* Kreska akcentowa nad nadtytułem — ten sam język co .foreto-divider--accent
   (czerwień, 2 px). Rośnie o jeden krok skali, 24 → 32, i to jest cały jej
   ruch: karta niesie wtedy cztery zgodne reakcje (kreska, tytuł, grot, cień),
   a nie cztery różne.                                                        */
.foreto-case__rule {
  width: var(--foreto-space-4); height: 2px; border: 0; margin: 0;
  background: var(--foreto-red);
  transition: width var(--foreto-transition-base);
}
/* KIEDY KARTA REAGUJE. Nie na „:hover gdziekolwiek": gdy kursor stoi na łączu
   „Podgląd na żywo", kliknięcie prowadzi TAM, nie do case study — a karta
   reagująca w całości obiecywałaby to drugie. Dlatego każda reakcja ma
   wyłączenie :not(:has(.foreto-case__live:hover)); samo łącze niesie wtedy
   własną reakcję i czerwienieje.
   Drugi selektor dokłada te same reakcje przy fokusie tytułu z klawiatury —
   bez niego osoba na klawiaturze widziała tylko pierścień, a osoba z myszą
   cztery zmiany.                                                            */
.foreto-case:hover:not(:has(.foreto-case__live:hover)) .foreto-case__rule,
.foreto-case:has(.foreto-case__link:focus-visible) .foreto-case__rule { width: var(--foreto-space-5); }
/* Nadtytuł stoi w KAŻDEJ karcie, także pustej — inaczej karta z klientem
   spycha tytuł o 48 px w dół i w rzędzie trzech kart tytuły są na trzech
   wysokościach. Pusty akapit nic nie ogłasza czytnikowi, a `min-height` trzyma
   wiersz, którego w nim nie ma.                                              */
.foreto-case__client {
  margin: 0; font-size: 12px; line-height: 16px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--foreto-text-muted);
}
/* DWA WIERSZE I TYLE SAMO MIEJSCA ZAREZERWOWANE — ta sama zasada co przy
   zajawce, tylko policzona dla tytułu. Bez tego kafel z jednowierszowym tytułem
   był o 32 px niższy od sąsiada i rząd kart kończył się na trzech różnych
   wysokościach. min-height, nie height: dłuższy tytuł ma się gdzie zmieścić
   w wariancie, który kiedyś rozluźni obcięcie.                               */
.foreto-case__title {
  margin: 0;
  font-size: var(--foreto-font-size-h4); line-height: var(--foreto-line-h4);
  font-weight: 700; text-wrap: pretty;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
  min-height: calc(2 * var(--foreto-line-h4));
}
/* Łączem jest TYTUŁ, nie napis „case study": dziewięć kart w listingu dawałoby
   dziewięć identycznych łączy, a tytuł jest za każdym razem inny — to ta sama
   poprawka, którą w źródle robił ukryty dopisek z nazwą klienta, tylko bez
   ukrytego tekstu. ::after rozciąga cel na całą kartę.                       */
.foreto-case__link {
  color: inherit; text-decoration: none;
  transition: color var(--foreto-transition-base);
}
.foreto-case__link::after { content: ''; position: absolute; inset: 0; }
.foreto-case:hover:not(:has(.foreto-case__live:hover)) .foreto-case__link,
.foreto-case:has(.foreto-case__link:focus-visible) .foreto-case__link { color: var(--foreto-text-accent); }
/* Znany koszt nakładki: tekstu karty nie da się zaznaczyć myszą. Cena za cel
   kliknięcia wielkości karty zamiast wielkości dwóch słów.                   */
.foreto-case__excerpt {
  margin: 0; font-size: var(--foreto-font-size-body); line-height: var(--foreto-line-body);
  color: var(--foreto-text-muted); text-wrap: pretty;
  /* Trzy wiersze i tyle samo miejsca zarezerwowane: treść idzie z CMS-a, więc
     jedna karta ma dwa wiersze, a druga pięć — bez rezerwy rząd kafli miałby
     akcje na trzech różnych wysokościach. min-height, nie height. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
  min-height: calc(3 * var(--foreto-line-body));
}
/* Metryka realizacji: to są ETYKIETY, nie chipy. Chip wygląda klikalnie i musi
   być klikalny — te wartości tylko opisują. Kropka rozdzielająca jest
   pseudoelementem NASTĘPNIKA, więc przy zmianie kolejności ani usunięciu
   pozycji nie zostaje sierotą.                                               */
/* Kotwica DOŁU siedzi na meta, nie na akcjach. Wcześniej wolne miejsce karty
   zbierało się między wierszem taksonomii a akcjami — czyli w środku pary,
   która czyta się razem: „czym to jest" i „gdzie to obejrzeć". Teraz nadmiar
   ląduje nad meta, a ono z akcjami stoi zwartą stopką karty.                  */
/* KOLUMNA, nie wiersz: jedna taksonomia zajmuje jeden wiersz, bo branża i
   usługi odpowiadają na dwa różne pytania. Poprzednia wersja sypała je jednym
   ciągiem i ta sama kropka łączyła terminy z jednej osi oraz osie między sobą
   — czytało się to jak jedna lista pięciu równorzędnych etykiet.             */
.foreto-case__meta {
  display: flex; flex-direction: column; gap: var(--foreto-space-1) 0;
  margin: 0; margin-top: auto; padding: 0; list-style: none;
  font-size: 12px; line-height: 16px; color: var(--foreto-text-muted);
}
/* Wiersz osi zawija się sam, gdy terminów jest dużo — stąd flex-wrap tutaj,
   a nie na całej liście. */
.foreto-case__meta > li { display: flex; flex-wrap: wrap; }
.foreto-case__meta > li > span + span::before { content: '·'; padding-inline: var(--foreto-space-2); }

/* Kropka rozdzielająca stoi PRZED pozycją, więc po zawinięciu wiersza ląduje
   na jego początku i udaje wypunktowanie — a lista taksonomii wypunktowaniem
   nie jest. W wąskiej karcie zawija się zawsze, bo trzy nazwy taksonomii
   nie mieszczą się w jednym wierszu.

   Pomiar musi iść PO KARCIE, nie po siatce: ta sama siatka daje kartę 552 px
   przy dwóch kolumnach i 358 px przy trzech, więc szerokość siatki nic o karcie
   nie mówi. Kontenerem jest tylko kafel — wariant szeroki zostaje poza tym
   celowo, bo blok progowy przy 760 celuje w `.foreto-case--wide .foreto-case__body`
   i po uczynieniu tamtej karty kontenerem mierzyłby ją zamiast siatki.        */
.foreto-case:not(.foreto-case--wide) { container-type: inline-size; }

@container (max-width: 440px) {
  /* Jedna nazwa na wiersz i bez kropek: skoro i tak łamie się na kilka
     wierszy, to niech to będzie lista pionowa, a nie rząd z sierotami. */
  .foreto-case__meta { flex-direction: column; gap: var(--foreto-space-1) 0; }
  .foreto-case__meta > li + li::before { content: none; }
}
/* Akcje przejmują kotwicę tylko wtedy, gdy meta NIE MA — inaczej dwa marginesy
   `auto` w jednej kolumnie dzieliłyby wolne miejsce po połowie i para znów
   rozjechałaby się w pionie.                                                  */
.foreto-case__actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--foreto-space-2) var(--foreto-space-5);
  margin-top: auto;
}
.foreto-case__meta ~ .foreto-case__actions { margin-top: 0; }
/* Grot idzie za tytułem, bo tytuł jest łączem. Odjeżdża o 4 px — reguła
   systemu, nie decyzja tej karty.                                           */
.foreto-case__go {
  display: inline-flex; align-items: center; gap: var(--foreto-space-2);
  font-size: var(--foreto-font-size-card); line-height: 24px;
  font-weight: 700; text-transform: uppercase; white-space: nowrap;
  color: var(--foreto-text-accent);
}
/* TEST GLIFU — grot o kroku 2, nie 4,8. Reszta systemu zostaje na
   arrow-right.svg; ta karta jest jedynym miejscem, gdzie sprawdzamy gęstszą
   siatkę, żeby dało się je porównać obok siebie.

   maska = slot, bez calc. Blok ma wtedy dokładnie slot × 3/24, więc jest ostry,
   gdy slot jest wielokrotnością 8. Każdy calc powiększający maskę ponad slot
   psuł właśnie to.

   PLIK JEST ZNORMALIZOWANY, nie skalowany maską: kształt narysowano schodek
   w schodek blokami po 3 jednostki, tusz wypełnia 12 × 21 z pola 24 × 24.
   KROK RÓWNA SIĘ BLOKOWI, inaczej schodek przestaje być schodkiem: przy bloku 3
   i kroku 2 sąsiednie kwadraty nachodzą na siebie o jedną jednostkę i diagonala
   łamie się z 45° na coś płaskiego. Bloki stoją więc co 3 w obu osiach.
   Pole grotu w przycisku zostaje 20, bo tam ostry jest wyłącznie glif o kroku
   4,8 — i to jest cała przeszkoda dla podmiany w całym systemie.             */
.foreto-case__arrow {
  flex: 0 0 auto; width: 24px; height: 24px; background-color: currentColor;
  -webkit-mask-image: url('../icons/arrow-right-step2.svg'); mask-image: url('../icons/arrow-right-step2.svg');
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-size: 24px 24px;    mask-size: 24px 24px;
  transition: transform var(--foreto-transition-base);
}
.foreto-case:hover:not(:has(.foreto-case__live:hover)) .foreto-case__arrow,
.foreto-case:has(.foreto-case__link:focus-visible) .foreto-case__arrow { transform: translateX(4px); }
.foreto-case:hover:not(:has(.foreto-case__live:hover)) .foreto-media__img,
.foreto-case:has(.foreto-case__link:focus-visible) .foreto-media__img { transform: scale(1.03); }
/* Drugie łącze musi leżeć NAD nakładką tytułu, inaczej nakładka je przechwyci
   i „podgląd na żywo" prowadziłby do case study.                            */
.foreto-case__live {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: var(--foreto-space-2);
  font-size: var(--foreto-font-size-card); line-height: 24px;
  font-weight: 700; text-transform: uppercase; white-space: nowrap;
  color: var(--foreto-ink); text-decoration: none;
  transition: color var(--foreto-transition-base);
}
.foreto-case__live:hover, .foreto-case__live:focus-visible { color: var(--foreto-text-accent); }
.foreto-case__live:focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 2px; }
/* Glif łącza zewnętrznego: ramka otwarta w prawym górnym narożniku i ukośna
   strzałka wychodząca poza nią — kierunek na ukos w górę odróżnia „na zewnątrz"
   od grotu „dalej w tym serwisie". Tusz zajmuje 18 × 18 jednostek pola 24 × 24
   (marginesy po 3), siatka ma krok 2, maska = slot.                          */
.foreto-case__live::after {
  content: ''; flex: 0 0 auto;
  width: 24px; height: 24px; background-color: currentColor;
  -webkit-mask-image: url('../icons/external-link.svg'); mask-image: url('../icons/external-link.svg');
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-size: 24px 24px;    mask-size: 24px 24px;
}
@media (prefers-reduced-motion: reduce) {
  .foreto-case__rule, .foreto-case__arrow, .foreto-case__link,
  .foreto-case__live, .foreto-case__media .foreto-media__img { transition: none; }
  .foreto-case:hover .foreto-case__arrow,
  .foreto-case:has(.foreto-case__link:focus-visible) .foreto-case__arrow { transform: none; }
  .foreto-case:hover .foreto-case__rule,
  .foreto-case:has(.foreto-case__link:focus-visible) .foreto-case__rule { width: var(--foreto-space-4); }
  /* Zoom jest ruchem dekoracyjnym i wypada w całości — stan niesie kolor
     tytułu, grot i kreska, więc nic nie ginie.                               */
  .foreto-case:hover .foreto-media__img,
  .foreto-case:has(.foreto-case__link:focus-visible) .foreto-media__img { transform: none; }
}

/* Kaflowy: obrazek nad treścią, jedna kolumna.

   `1fr` na drugim wierszu wyrównuje treść DO GÓRY. Bez niego wiersz treści miał
   wysokość swojej zawartości, a nadmiar wysokości karty (wszystkie w rzędzie są
   równe najwyższej) lądował NAD nim — czyli treść była dociskana do spodu i jej
   góra pływała: zmierzone 267, 303 i 263 px od góry karty w jednym rzędzie.
   Teraz kreska startuje w tym samym miejscu w całym rzędzie, a to, co niżej,
   układa się wedle własnej długości — miejsca na nieobecny nadtytuł nie
   rezerwujemy, bo pusty wiersz w karcie bez klienta jest gorszy niż przesunięcie. */
.foreto-case:not(.foreto-case--wide) {
  grid-template-columns: minmax(0, 1fr); grid-template-areas: 'media' 'body';
  grid-template-rows: auto 1fr;
}
.foreto-case--wide { grid-template-columns: minmax(0, 1fr); grid-template-areas: 'media' 'body'; }
/* Szeroki: obrazek i treść obok siebie.

   KADR 16/9, NIE 3/2 — i to nie jest zmiana upodobania, tylko dopasowanie do
   materiału. Miniatury realizacji generujemy dziś sami, w 1920 × 1080; przy
   proporcji 3/2 `object-fit: cover` skalował je do wypełnienia wysokości
   i ucinał boki. Zmierzone: obraz renderował się na 875 px w kadrze 486 px,
   czyli 44% szerokości szło pod nóż — a to na niej stoi cała treść zrzutu,
   bo urządzenia w mockupie leżą obok siebie.

   Motyw znał już tę proporcję: bloki progowe niżej przełączają kadr na 16/9
   poniżej 760 px i w wąskiej kolumnie. Tu zmiana dotyczy szerokiego wariantu
   na pulpicie, czyli ostatniego miejsca, gdzie zostawało 3/2.

   Kolumny 576/438 zamiast 486/528: 576 × 9/16 = 324, czyli DOKŁADNIE ta sama
   wysokość karty co przy 486 × 2/3. Wiersz nie drgnął, przesunął się tylko
   podział — obrazek zyskał 90 px, treść tyle samo oddała. Suma trzyma siatkę
   ze źródła: 576 + 72 + 438 + 48 = 1134.                                    */
.foreto-case--wide {
  --foreto-case-ratio: 16 / 9;
  grid-template-columns: minmax(0, 576fr) minmax(0, 438fr);
  grid-template-areas: 'media body';
  column-gap: 72px; padding-inline-end: 48px;
  align-items: center;
}
/* Przy dwóch kolumnach treść jest wyśrodkowana w pionie, więc margin-top: auto
   w akcjach nie ma czego rozpychać — i nie powinien, bo rozciągałby blok. */
.foreto-case--wide .foreto-case__actions { margin-top: 0; }
.foreto-case--wide .foreto-case__body { padding: 0; }
/* DWA WIERSZE NA TYTUŁ I NA ZAJAWKĘ. Kolumna treści zwęziła się z 528 do 438,
   bo kadr wziął 16/9 pod generowane miniatury — przy czterech wierszach zajawki
   tekst sięgał dolnej krawędzi obrazka i karta przestawała oddychać. Dwa wiersze
   to tyle, ile zdąży przeczytać ktoś przeglądający listę; reszta jest na stronie
   realizacji, dokąd prowadzi cała karta.

   Tytuł dostaje przycięcie po raz pierwszy: dotąd rósł swobodnie i przy dłuższej
   nazwie („Narodowe Centrum Edukacji Żywieniowej (NCEŻ)") brał trzy wiersze,
   spychając zajawkę i akcje poniżej obrazka.

   PRZYCIĘCIE TO ZA MAŁO NA RÓWNE KARTY. Samo `line-clamp` ogranicza od góry,
   ale karta z jednowierszowym tytułem nadal byłaby niższa od tej z dwoma —
   a wiersz kart ma stać równo. Dlatego oba pola dostają `min-height` na tę
   samą liczbę wierszy: miejsce jest zarezerwowane niezależnie od tego, co
   przyszło z CMS-a. To ta sama zasada, którą kit stosuje w wąskiej karcie
   (`min-height: calc(3 * line)` przy zajawce), tylko policzona dla dwóch. */
.foreto-case--wide .foreto-case__excerpt {
  -webkit-line-clamp: 2;
  min-height: calc(2 * var(--foreto-line-body));
}

/* Poniżej 1181 dopełnienie 48 i odstęp 72 zjadają trzecią część wiersza. */
@media (max-width: 1181px) {
  /* :not() jest konieczne, nie kosmetyczne: .foreto-cases--wide ma tę samą
     specyficzność co .foreto-cases (jedna klasa), a ten blok stoi niżej, więc
     bez wyłączenia zabierał wariantowi szerokiemu jego jedną kolumnę.        */
  .foreto-cases:not(.foreto-cases--wide) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foreto-case--wide { column-gap: var(--foreto-space-5); padding-inline-end: 0; }
}
/* Poniżej 760 obrazek wraca nad treść także w szerokim: kolumna 486fr przy
   312 px daje 150, czyli makieta w środku przestaje być czytelna. Kadr
   spłaszcza się wtedy do 16/9 — pionowo karta i tak rośnie o treść.          */
@media (max-width: 760px) {
  /* `:not()` NIE jest tu ozdobą. Reguła dwukolumnowa wyżej celuje
     w `.foreto-cases:not(.foreto-cases--wide)`, czyli ma wagę dwóch klas,
     a `.foreto-cases` samo w sobie jedną. Waga bije kolejność w źródle,
     więc bez wyrównania kształtu dwie kolumny przeżywały do 375 px —
     karty po 140 px na telefonie. Ta sama pułapka, przed którą ostrzega
     komentarz przy regule 1181, tylko niezastosowana po tej stronie. */
  .foreto-cases:not(.foreto-cases--wide), .foreto-cases--wide { grid-template-columns: minmax(0, 1fr); gap: var(--foreto-space-7); }
  .foreto-case--wide {
    grid-template-columns: minmax(0, 1fr); grid-template-areas: 'media' 'body';
    column-gap: 0; row-gap: var(--foreto-space-4); align-items: start;
  }
  /* Szeroki staje się tu kaflem, więc odzyskuje dopełnienie treści — inaczej
     cień znów przylegałby do liter.                                          */
  .foreto-case--wide .foreto-case__body { padding: 0 var(--foreto-space-4) var(--foreto-space-4); }
  .foreto-case { --foreto-case-ratio: 16 / 9; }
  .foreto-case--wide .foreto-case__excerpt { -webkit-line-clamp: 3; }
}
/* Ten sam próg mierzony NA KOLUMNIE — listing stoi w treści strony i w węższej
   kolumnie case study, a @media o szerokości kolumny nic nie wie.            */
.foreto-cases-box { container-type: inline-size; }
/* 1133, nie 1181: kontenerem jest KOLUMNA, a ta ma najwyżej --wrap-max (1134),
   więc próg 1181 był zawsze prawdziwy i obie siatki ze źródła nie renderowały
   się nigdy — karta podawała jako pochodzenie liczby, których nie da się
   zobaczyć. Progi @media zostają na 1181/760, bo tam mierzone jest OKNO.     */
@container (max-width: 1133px) {
  .foreto-cases:not(.foreto-cases--wide) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foreto-case--wide { column-gap: var(--foreto-space-5); padding-inline-end: 0; }
}
@container (max-width: 760px) {
  /* `:not()` NIE jest tu ozdobą. Reguła dwukolumnowa wyżej celuje
     w `.foreto-cases:not(.foreto-cases--wide)`, czyli ma wagę dwóch klas,
     a `.foreto-cases` samo w sobie jedną. Waga bije kolejność w źródle,
     więc bez wyrównania kształtu dwie kolumny przeżywały do 375 px —
     karty po 140 px na telefonie. Ta sama pułapka, przed którą ostrzega
     komentarz przy regule 1181, tylko niezastosowana po tej stronie. */
  .foreto-cases:not(.foreto-cases--wide), .foreto-cases--wide { grid-template-columns: minmax(0, 1fr); gap: var(--foreto-space-7); }
  .foreto-case--wide {
    grid-template-columns: minmax(0, 1fr); grid-template-areas: 'media' 'body';
    column-gap: 0; row-gap: var(--foreto-space-4); align-items: start;
  }
  .foreto-case--wide .foreto-case__body { padding: 0 var(--foreto-space-4) var(--foreto-space-4); }
  .foreto-case { --foreto-case-ratio: 16 / 9; }
  .foreto-case--wide .foreto-case__excerpt { -webkit-line-clamp: 3; }
}

/* --- GRUPA 3: FORMS — PRZENIESIONA 1:1 z kit/forms.css --------------------
   Trzy zasady, które są tu decyzją projektową, nie stylem:
     1. Stan niesie ATRYBUT (aria-invalid, aria-busy, data-state), nie klasa —
        atrybut i tak musi być dla czytnika, więc niech od razu niesie wygląd.
     2. Miejsce na komunikat jest ZAREZERWOWANE (min-height), więc formularz
        nie skacze przy pojawieniu się błędu.
     3. Kolor nigdy nie jest jedynym nośnikiem stanu: przy błędzie jest tekst
        pod polem.
   ========================================================================== */

/* Legenda pól wymaganych stoi NAD formularzem — czytana po polach jest
   bezużyteczna. Gwiazdka bez legendy nic nie znaczy.                        */
.foreto-form__legend {
  margin: 0 0 var(--foreto-space-5); text-wrap: pretty;
  font-size: var(--foreto-font-size-card);
  line-height: var(--foreto-msg-line);
  color: var(--foreto-text-muted);
}
.foreto-form__req { color: var(--foreto-text-accent); }

/* position: relative jest KONIECZNE: ukryty input[type=file] ma
   .foreto-visually-hidden (position: absolute bez offsetów). Bez
   pozycjonowanego rodzica jego blokiem zawierającym staje się korzeń
   dokumentu i pole ląduje 2871 px od lewej — strona dostaje poziomy pasek.  */
.foreto-field {
  position: relative;
  display: grid;
  gap: var(--foreto-label-gap);
  margin-bottom: var(--foreto-field-gap);
}
.foreto-label { font-weight: 400; }

/* POPRAWKA MOTYWU, zmierzona. Element siatki ma domyślnie min-width: auto,
   więc kolumna pola rośnie do max-content swojej treści. Przy załączniku tą
   treścią jest nazwa pliku z white-space: nowrap: na ekranie 375 px pole
   rozpychało stronę do 486 i pojawiał się poziomy pasek przewijania. Kit ma
   min-width: 0 na samej nazwie pliku, ale to za nisko — skracać musi się
   kolumna, nie dopiero jej zawartość.                                       */
.foreto-field > * { min-width: 0; }

/* DENSE — jedna klasa na POLU, nie osobny wariant każdej kontrolki. Nadpisuje
   TOKENY, więc input, textarea, select i załącznik schodzą do 40 px tym samym
   wzorem, a każda reguła stanu działa dalej bez zmian.                       */
.foreto-field--dense,
.foreto-input--dense,
.foreto-textarea--dense,
.foreto-select--dense,
.foreto-file--dense {
  --foreto-field-h:     var(--foreto-field-h-dense);
  --foreto-field-pad-y: var(--foreto-field-pad-y-dense);
  --foreto-field-pad-x: var(--foreto-field-pad-x-dense);
  --foreto-textarea-h:  var(--foreto-textarea-h-dense);
}
.foreto-field--dense { --foreto-field-gap: var(--foreto-field-gap-dense); }

/* JEDNA reguła na wszystkie typy pól — inaczej każdy dostałby własną
   i pierwsza zmiana wyglądu rozjechałaby je między sobą.                    */
.foreto-input,
.foreto-textarea,
.foreto-select {
  width: 100%;
  min-height: var(--foreto-field-h);
  padding: var(--foreto-field-pad-y) var(--foreto-field-pad-x);
  border: var(--foreto-border-width) solid var(--foreto-border-control);
  border-radius: var(--foreto-radius, 0);
  background: var(--foreto-page);
  font: inherit;
  /* Interlinia przypięta do 24, nie „normal": dla Poppinsa 16 px „normal" daje
     23,x px i wysokość pola wychodzi 57 albo 58 zależnie od zaokrąglenia —
     obok załącznika, który ma 24, jeden piksel widać jako nierówny rząd.     */
  line-height: var(--foreto-space-4);
  color: inherit;
  transition: border-color var(--foreto-transition-fast);
}
.foreto-textarea { min-height: var(--foreto-textarea-h); resize: vertical; }

/* POPRAWKA MOTYWU, zmierzona. Kit daje wszystkim polom dopełnienie pionowe
   z tokenu (16), a obok deklaruje min-height 56 jako wysokość z makiety. Dla
   pola JEDNOWIERSZOWEGO te dwie liczby się wykluczają: 24 interlinii + 2 × 16
   dopełnienia + 2 ramki = 58, więc min-height nigdy nie dochodzi do głosu
   i rząd pól jest o 2 px wyższy, niż projekt mówi. Dopełnienie pionowe schodzi
   więc do zera, a wysokość niesie min-height — pole jednowierszowe centruje
   tekst samo. Obszar opisu zostaje z dopełnieniem, bo jest wielowierszowy
   i jego wysokość liczy się inaczej (własne min-height 177).                */
.foreto-input,
.foreto-select { padding-block: 0; }

/* Podpowiedź: #6E6E6E, nie przygaszony czarny. Bez opacity — łamie próg
   kontrastu.                                                                */
.foreto-input::placeholder,
.foreto-textarea::placeholder { color: var(--foreto-text-muted); opacity: 1; }

/* HOVER — ramka czernieje. Ramka jest GRAFIKĄ, więc próg to 3:1.            */
.foreto-input:hover,
.foreto-textarea:hover,
.foreto-select:hover { border-color: var(--foreto-ink); }

/* WYPEŁNIONE — ramka czernieje, pole wygląda domknięte. Dwa selektory, bo dwa
   mechanizmy: :not(:placeholder-shown) działa bez JS, ale wymaga atrybutu
   placeholder (choćby placeholder=" "); [data-filled] ustawia skrypt.        */
.foreto-input:not(:placeholder-shown),
.foreto-textarea:not(:placeholder-shown),
.foreto-field[data-filled] .foreto-input,
.foreto-field[data-filled] .foreto-textarea { border-color: var(--foreto-ink); }

/* FOKUS — osobno od hovera: ramka czernieje, a pierścień jest globalny (2 px
   z odstępem 2 px). Bez odstępu pierścień i ramka zlałyby się w jedną kreskę. */
.foreto-input:focus-visible,
.foreto-textarea:focus-visible,
.foreto-select:focus-visible { border-color: var(--foreto-ink); }

/* BŁĄD — sterowany aria-invalid. Ramka w #E00000, ta sama czerwień co
   komunikat obok, żeby błąd czytał się jako jeden element.                   */
.foreto-input[aria-invalid="true"],
.foreto-textarea[aria-invalid="true"],
.foreto-select[aria-invalid="true"] { border-color: var(--foreto-text-accent); }
/* Wariant bez JS: :user-invalid zapala się po opuszczeniu pola i gaśnie przy
   pierwszym poprawnym znaku. Obok aria-invalid, nie zamiast.                 */
.foreto-input:user-invalid,
.foreto-textarea:user-invalid { border-color: var(--foreto-text-accent); }

/* ZABLOKOWANE — szare tło i tekst #6E6E6E (5,10:1), bez opacity. Ramka
   schodzi na hairline: pole ma wyglądać nieaktywnie, nie błędnie.            */
.foreto-input:disabled,
.foreto-textarea:disabled,
.foreto-select:disabled {
  background: var(--foreto-surface-band);
  border-color: var(--foreto-border-hairline);
  color: var(--foreto-text-muted);
  cursor: not-allowed;
}

/* Select rysuje własny chevron maską — natywna strzałka jest inna w każdej
   przeglądarce. Miejsce na nią robi dopełnienie z prawej.                     */
.foreto-select {
  appearance: none; -webkit-appearance: none;
  padding-right: calc(var(--foreto-field-pad-x) * 2 + 12px);
  background-image: none;
}
.foreto-select-wrap { position: relative; display: block; }
.foreto-select-wrap::after {
  content: ""; position: absolute; pointer-events: none;
  right: var(--foreto-field-pad-x); top: 50%; transform: translateY(-50%);
  width: 12px; height: 12px;
  background-color: currentColor;
  /* chevron-down: glif rozwijanej listy musi wskazywać w DÓŁ. chevron-sm jest
     glifem nawigacyjnym (w prawo) i nie ma nic wspólnego z rozwijaniem.       */
  -webkit-mask: url('../icons/chevron-down.svg') no-repeat center / contain;
          mask: url('../icons/chevron-down.svg') no-repeat center / contain;
}
/* NATYWNY SELECT NIE ANIMUJE CHEVRONU — i to jest decyzja, nie brak.
   CSS nie zna stanu „lista otwarta": przeglądarka nie wystawia go w żadnym
   selektorze. Fokus wyglądał na przybliżenie, ale kłamie w obie strony:
   zamknięcie listy kliknięciem w pole ZOSTAWIA fokus, więc strzałka zostawała
   obrócona przy zamkniętej liście, a Tab obracał ją bez otwierania czegokolwiek. */

/* KOMUNIKAT — miejsce zarezerwowane przez min-height, więc pojawienie się
   błędu nie przesuwa niczego pod nim. Komunikat siedzi PRZY polu, bo
   aria-describedby wiąże go z polem.                                         */
.foreto-msg {
  display: flex;
  align-items: flex-start;
  gap: var(--foreto-space-2);
  min-height: var(--foreto-msg-line);
  margin: calc(var(--foreto-msg-gap) - var(--foreto-label-gap)) 0 0;
  font-size: var(--foreto-font-size-card);
  line-height: var(--foreto-msg-line);
  color: var(--foreto-text-accent);
}
.foreto-msg--hint { color: var(--foreto-text-muted); }

/* ZGODA — kontrolka RYSOWANA NA POLU, nie obok niego: appearance: none zdejmuje
   wygląd przeglądarki, a glif wchodzi maską na tym samym elemencie. Gdyby glif
   siedział w osobnym <span>, a pole było schowane, pierścień fokusu trafiałby
   w niewidoczny element 1×1 px. Jeden element = fokus, klawiatura i zapowiedź
   czytnika działają bez dodatkowej reguły.
   Kwadrat ma 24, nie 16: siatka glifu ma krok 2 na 24, więc przy 16 komórka
   ma 1,33 px i ramka się rozmywa; przy 24 cel kliknięcia §2.5.8 spełnia sama
   kontrolka, a nie dopiero etykieta obok.                                    */
.foreto-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--foreto-space-4);
  margin: var(--foreto-space-6) 0 0;
  font-size: var(--foreto-font-size-card);
  line-height: var(--foreto-space-4);
}
.foreto-checkbox {
  flex: 0 0 auto;
  appearance: none; -webkit-appearance: none;
  width: var(--foreto-hit); height: var(--foreto-hit);
  margin: 0;
  cursor: pointer;
  /* #737373, nie #DFDFDF: kontrolka jest grafiką niosącą znaczenie, więc
     obowiązuje próg 3:1 — #DFDFDF na bieli daje 1,35:1.                      */
  background-color: var(--foreto-border-control);
  -webkit-mask: url('../icons/checkbox.svg') no-repeat center / contain;
          mask: url('../icons/checkbox.svg') no-repeat center / contain;
  transition: background-color var(--foreto-transition-fast);
}
.foreto-checkbox:hover { background-color: var(--foreto-ink); }
.foreto-checkbox:checked {
  background-color: var(--foreto-surface-accent);
  -webkit-mask-image: url('../icons/checkbox-on.svg');
          mask-image: url('../icons/checkbox-on.svg');
}
.foreto-checkbox:disabled { background-color: var(--foreto-border-hairline); cursor: not-allowed; }
.foreto-checkbox[aria-invalid="true"] { background-color: var(--foreto-text-accent); }
.foreto-consent label { cursor: pointer; text-wrap: pretty; }
.foreto-consent .foreto-msg { margin-top: var(--foreto-space-2); }

/* ZAŁĄCZNIK — natywny input[type=file] ma wygląd narzucony przez przeglądarkę,
   więc jest ukryty, a klikalna jest etykieta. Input ZOSTAJE w przepływie
   i przyjmuje fokus, dlatego pierścień pokazujemy na etykiecie.
   Stan „plik wybrany" WYMAGA JS — nazwy pliku nie da się odczytać w CSS.      */
.foreto-file {
  display: flex;
  align-items: center;
  gap: var(--foreto-space-4);
  min-height: var(--foreto-field-h);
  padding: var(--foreto-field-pad-y) var(--foreto-field-pad-x);
  border: var(--foreto-border-width) solid var(--foreto-border-control);
  /* Interlinia przypięta do 24 — to cały mechanizm równych wysokości. Goły
     tekst „Załącz plik" w kontenerze flex staje się anonimowym elementem,
     a jego wiersz przy „normal" nie ma przewidywalnej wysokości: pole rosło
     o 29% ponad pole tekstowe.                                               */
  line-height: var(--foreto-space-4);
  cursor: pointer;
  transition: border-color var(--foreto-transition-fast);
}
/* Ta sama poprawka co przy polach jednowierszowych — patrz wyżej. Musi stać
   PO skrócie padding z .foreto-file, bo skrót ustawia obie osie naraz.       */
.foreto-file { padding-block: 0; }
.foreto-file:hover { border-color: var(--foreto-ink); }
.foreto-field--file:has(input:focus-visible) .foreto-file {
  border-color: var(--foreto-ink);
  outline: 2px solid var(--foreto-focus);
  outline-offset: 2px;
}
.foreto-file[data-state="selected"] { border-color: var(--foreto-ink); }
.foreto-file .foreto-icon--attachment { align-self: center; }
.foreto-file__name {
  min-width: 0; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  line-height: var(--foreto-space-4);
}
.foreto-file__size {
  flex: 0 0 auto; color: var(--foreto-text-muted);
  font-size: var(--foreto-font-size-card); line-height: var(--foreto-space-4);
  white-space: nowrap;
}
/* Cel kliknięcia 24×24 dotyczy też tego przycisku. Glif krzyżyka ma dokładnie
   24, więc wypełnia cel co do piksela — bez dopełnienia.                     */
.foreto-file__remove {
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: var(--foreto-hit); height: var(--foreto-hit);
  padding: 0; border: 0; background: none;
  color: inherit; cursor: pointer;
  transition: color var(--foreto-transition-fast);
}
.foreto-file__remove:hover,
.foreto-file__remove:focus-visible { color: var(--foreto-text-accent); }
.foreto-file[aria-invalid="true"],
.foreto-field--file[data-invalid] .foreto-file { border-color: var(--foreto-text-accent); }

/* PRZYCISK WYSYŁKI NIE JEST PRZENIESIONY. Kit rozciąga go na całą szerokość
   formularza (552 × 54 z makiety), ale poprawiony widok kontaktu pokazuje
   przycisk naturalnej szerokości z notą pod spodem — i to on jest tu
   obowiązujący. Stan wysyłki (aria-busy) rysuje contact-form.css.           */

/* 480: wiersz WYBRANEGO pliku zawija się — nazwa na pełną szerokość, rozmiar
   i przycisk pod nią. Zakres zawężony do [data-state="selected"]: bez tego
   reguła łapała stan pusty, w którym nie ma czego zawijać, i pole rosło z 57
   do 75 px. Stan pusty i z plikiem to ten sam element, więc ta sama wysokość. */
@media (max-width: 480px) {
  .foreto-file[data-state="selected"] { flex-wrap: wrap; }
  .foreto-file[data-state="selected"] .foreto-file__name { flex-basis: 100%; }
}

.foreto-icon--attachment  { -webkit-mask-image: url('../icons/attachment.svg');  mask-image: url('../icons/attachment.svg'); }
.foreto-icon--checkbox    { -webkit-mask-image: url('../icons/checkbox.svg');    mask-image: url('../icons/checkbox.svg'); }
.foreto-icon--checkbox-on { -webkit-mask-image: url('../icons/checkbox-on.svg'); mask-image: url('../icons/checkbox-on.svg'); }

/* --- PersonLink — PRZENIESIONY 1:1 z kit/content.css ----------------------
   Łącze do profilu osoby, wkomponowane w wiersz kontaktów wizytówki. Nie jest
   to kwadratowa ikona obok: rząd kontaktów niesie już dwa wiersze z czerwonym
   prefiksem (tel., mail.), więc trzeci kanał wchodzi TĄ SAMĄ formą — glif 20 px
   w miejscu prefiksu, etykieta w miejscu wartości.
   Glif ma 20, nie 36: wiersz kontaktu ma interlinię 24, a 36 rozpychałoby go
   o połowę. To jedyny wymiar poza skalą ikon w tym komponencie i wynika
   z wiersza, nie z gustu — dokładnie jak pole grotu 20 w przycisku.          */
.foreto-person-in {
  display: inline-flex; align-items: center; gap: var(--foreto-space-2);
  font-size: var(--foreto-font-size-body); line-height: var(--foreto-line-body);
  color: inherit; text-decoration: none;
  transition: color var(--foreto-transition-base, 200ms ease-out);
}
.foreto-person-in::before {
  content: ''; flex: 0 0 auto; width: 20px; height: 20px;
  background-color: var(--foreto-text-accent);
  -webkit-mask: url('../icons/social/linkedin-outline.svg') no-repeat center / contain;
          mask: url('../icons/social/linkedin-outline.svg') no-repeat center / contain;
  transition: background-color var(--foreto-transition-base, 200ms ease-out);
}
/* Na czerwonym paśmie wraca wariant wypełniony: kontur 2 px na czerwieni
   rozpada się na kreski i logo przestaje być rozpoznawalne. Prefiks nie może
   tam też być czerwony — glif idzie na biel, tak jak tel./mail.             */
[data-tone="accent"] .foreto-person-in::before,
.foreto-card--accent .foreto-person-in::before {
  -webkit-mask-image: url('../icons/social/linkedin.svg');
          mask-image: url('../icons/social/linkedin.svg');
  background-color: currentColor;
}
.foreto-person-in:hover, .foreto-person-in:focus-visible { color: var(--foreto-text-accent); }
.foreto-person-in:focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .foreto-person-in, .foreto-person-in::before { transition: none; }
}

/* --- Znak słowny -----------------------------------------------------------
   WYMIAROWANIE W JEDNYM MIEJSCU, komponent podaje samą wysokość przez
   `--foreto-znak-h`. Proporcja stała trzy razy — w viewBoxie pliku i w dwóch
   arkuszach — a to jest liczba, która przy każdej poprawce kadru musi zmienić
   się wszędzie naraz. Raz już się z tego powodu rozjechała.

   SZEROKOŚĆ LICZONA Z WYSOKOŚCI, nie odwrotnie. Przy `height` i `width: auto`
   obie osie ustawiają się niezależnie: `auto` bierze wymiar własny, `max-width`
   przycina go do rodzica, a wysokość zostaje przy swoim — rysunek wychodzi
   rozciągnięty. Zmierzone w stopce: 418 na 48 przy proporcji rysunku 4,89.
   Dołożone `aspect-ratio` tego nie ratowało, bo przegrywa z dwoma podanymi
   wymiarami. Tutaj podany jest jeden.

   BEZ `min(…, 100%)`, choć odruch podpowiada inaczej — i to jest tu druga
   pułapka, w którą wpadłem dwa razy. Procent liczy się względem rodzica,
   a jego szerokość przy ustalaniu szerokości linku jest jeszcze nieznana:
   przeglądarka bierze wtedy domyślny rozmiar zastępczy SVG, czyli 300 × 150,
   i link robi się na 300 px zamiast na szerokość rysunku. W pasku nagłówka
   wypychało to menu poza ekran razem z poziomym paskiem przewijania. Sam
   `calc` jest wymiarem określonym i tego nie robi. Wąskie kolumny domyka więc
   dobór wysokości przez komponent, nie klamra na szerokości.

   Liczby to wymiary viewBoxa Z LUZEM jednej jednostki na stronę. Kadr ciasny
   co do piksela wyglądał na ucięty u dołu: tusz podpisu leżał dokładnie na
   granicy pudełka, więc przy skalowaniu w dół ostatni rząd pikseli znikał
   w zaokrągleniu.                                                            */
.foreto-logo {
  display: block;
  width: calc(var(--foreto-znak-h, 48px) * 703.14 / 143.72);
  height: auto;
  aspect-ratio: 703.14 / 143.72;
}

/* --- Enter -----------------------------------------------------------------
   Wejście treści na ZAŁADOWANIE: nagłówek podstrony, jego lead i okruszki.

   Czas 700 ms, nie 200. Stało tu `--foreto-transition-base`, czyli token
   PRZEJŚCIA — czasu, w jakim przycisk odpowiada na kursor. Przejście ma być
   natychmiastowe, bo jest reakcją na czyjś ruch; wejście ma być widoczne, bo
   nikt go nie wywołał i trzeba dać oku szansę je zauważyć. Przy 200 ms nagłówek
   strony pojawiał się, zanim wzrok na nim spoczął.

   Komentarz stał tu wcześniej inaczej: że 200 ms i 8 px są wspólne z teksturą
   pikseli, więc strona wstaje jednym ruchem. To nieprawda — `foreto-spark` to
   ośmiosekundowe pulsowanie samą przezroczystością, bez przesunięcia. Nie było
   z czym być spójnym, a uzasadnienie brzmiało wiarygodnie i trzymało zły czas.

   Dystans zostaje 8 px: skarga dotyczyła tempa, nie drogi, a większy skok na
   nagłówku pierwszego poziomu czytałby się jak przeskok układu.

   POTEM 700 ms okazało się z drugiej strony za dużo — wejście było widoczne aż
   nadto i całą stronę czytało się przez nie. 500 ms to punkt między dwiema
   skargami, a nie powrót do 200: tamta wartość jest tokenem PRZEJŚCIA i wciąż
   znaczy co innego. Ten czas obowiązuje też `motion.css` — obie warstwy
   wejścia mają jedno tempo, inaczej nagłówek podstrony i treść pod nim wchodzą
   w innym rytmie.                                                            */
@keyframes foreto-pixel-in {
  from { opacity: 0; transform: translateY(var(--foreto-space-2)); }
  to   { opacity: 1; transform: none; }
}
.foreto-enter {
  animation: foreto-pixel-in 500ms ease-out both;
  animation-delay: var(--d, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .foreto-enter { animation: none; }
}

/* Numerały Reveal, liczby pasma faktów i numery etapów jadą KROJEM
   NAGŁÓWKOWYM (Outfit), a nie osobnym krojem pikselowym. Doto z Foreto
   nie jest przenoszona: to znak tamtej marki, a nie element systemu
   FormImpress — patrz README.md, sekcja „Decyzje".                         */


/* --- Reveal — PRZENIESIONY 1:1 z kit/content.css --------------------------
   Poziomy akordeon: rząd wielkich numerów, z których jeden jest rozwinięty
   i pokazuje tytuł z opisem. Kliknięcie w inny numer przesuwa rozwinięcie.

   Po co, skoro jest oś czasu: tamta POKAZUJE cały ciąg naraz i nadaje się, gdy
   każdy punkt to jedno zdanie. Reveal służy sytuacji odwrotnej — punkty mają
   akapit opisu, więc pokazanie czterech naraz dałoby ścianę tekstu. Tu naraz
   czytasz jeden, a pozostałe zostają widoczne jako numery, czyli nie tracisz
   orientacji w ciągu (to przewaga nad zakładkami, gdzie niewybrane pozycje są
   tylko etykietami).

   SZEROKOŚĆ ANIMUJE flex-grow, nie grid-template-columns: przejście auto 0fr →
   auto 1fr nie działa, bo przeglądarka nie interpoluje ścieżek z auto i fr.
   flex-grow to liczba, więc interpoluje się zawsze.

   Kreska nad numerem jest STANEM: hairline przy zwiniętych, czerwień 2 px przy
   rozwiniętym — kolor nie jest jedynym nośnikiem, bo rozwinięty ma też grubszą
   kreskę, ciemniejszy numer i widoczny opis.                                 */
@keyframes foreto-reveal-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.foreto-reveal {
  --foreto-reveal-num: 256px;
  /* Szerokość WIERSZA TEKSTU w rozwiniętym panelu. Podana wprost, bo od tego
     zależy, czy tekst się przelewa w trakcie animacji — patrz __inner.        */
  --foreto-reveal-text: 30ch;
  /* Numer zwinięty: wartość POLICZONA, nie wzięta z hairline. #DFDFDF na paśmie
     #F6F6F6 daje 1,2:1, a numer jest widoczną etykietą przycisku, więc
     obowiązuje próg 3:1 dla tekstu dużego. #9A9A9A daje 3,07:1 i jest
     najjaśniejszą szarością, która ten próg przechodzi. Przygaszenie przez
     opacity byłoby zabronione: system nie pozwala na to na tekście.          */
  --foreto-reveal-off: #9A9A9A;
  display: flex; align-items: stretch; gap: var(--foreto-space-7);
  margin: 0; padding: 0; list-style: none;
}
/* PODSTAWA ZWINIĘTEGO JEST PODANA WPROST, nie liczona z treści: flex-basis auto
   brał rozmiar wewnętrzny pozycji razem z całym akapitem opisu, mimo że opis ma
   flex-basis 0 i overflow: hidden — żaden przystanek nie był wtedy zwinięty.
   1,1 × stopień numeru to szerokość dwóch cyfr w piśmie tabelarycznym.       */
.foreto-reveal__item {
  position: relative;
  display: flex; align-items: flex-start;
  padding-top: var(--foreto-space-4);
  min-width: 0; flex: 0 1 calc(var(--foreto-reveal-num) * 0.8);
  border-top: 2px solid var(--foreto-border-hairline);
  transition: flex-grow var(--foreto-transition-base, 200ms ease-out),
              border-color var(--foreto-transition-base, 200ms ease-out);
}
.foreto-reveal__item[data-open="true"] { flex: 1 1 auto; }
/* KRESKA NAD NUMEREM JEST PASKIEM POSTĘPU: ścieżka to hairline na całą
   szerokość pozycji, wypełnienie to czerwony pasek skalowany w poziomie —
   scaleX, nie width, bo transform nie wymusza przeliczenia układu przy każdej
   klatce. Gdy pętla jest wyłączona, pasek stoi wypełniony jako znacznik stanu. */
.foreto-reveal__item::before {
  content: ''; position: absolute; top: -2px; left: 0;
  width: 100%; height: 2px; background: var(--foreto-red);
  transform: scaleX(0); transform-origin: left;
}
.foreto-reveal__item[data-open="true"]::before { transform: scaleX(1); }
.foreto-reveal[data-auto="true"] .foreto-reveal__item[data-open="true"]::before {
  animation: foreto-reveal-progress var(--foreto-reveal-dwell, 6000ms) linear both;
}
/* Postój ZATRZYMUJE odliczanie przy najechaniu i przy fokusie w środku —
   czytanie nie może się kończyć samo. :focus-within, nie tylko :hover, bo na
   klawiaturze nie ma kursora.                                                */
.foreto-reveal[data-auto="true"]:hover .foreto-reveal__item[data-open="true"]::before,
.foreto-reveal[data-auto="true"]:focus-within .foreto-reveal__item[data-open="true"]::before {
  animation-play-state: paused;
}
/* Cały numer jest przyciskiem — cel kliknięcia wielkości glifu, nie napisu. */
.foreto-reveal__btn {
  display: block; flex: 0 0 auto; padding: 0 0 var(--foreto-space-6);
  border: 0; background: none; cursor: pointer;
  font: inherit; color: var(--foreto-reveal-off);
  transition: color var(--foreto-transition-base, 200ms ease-out);
}
/* Trzy stopnie: zwinięty jasny, wskazany czarny, otwarty czerwony.          */
.foreto-reveal__btn:hover { color: var(--foreto-ink); }
.foreto-reveal__btn:focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 2px; }
.foreto-reveal__item[data-open="true"] .foreto-reveal__btn { color: var(--foreto-text-accent); cursor: default; }
/* CYFRA W KROJU DOTO, oś ROND ustawiona na 0. Doto jest krojem pikselowym:
   każdy znak składa się z osobnych bloków, a oś ROND (0–100) decyduje o ich
   kształcie — 0 znaczy KWADRAT, 100 kółko. Ustawiamy 0, bo cała ikonografia
   systemu jest z kwadratów i kółko byłoby nowym kształtem w palecie.

   ODSTĘPSTWO OD PRZEWODNIKA, świadome i zakresowane: system ma jedną rodzinę
   (Poppins, dwie odmiany). Doto wchodzi WYŁĄCZNIE na numerały w Reveal —
   cztery znaki, zero tekstu czytanego.

   font-variation-settings, nie font-weight w skrócie: ROND to oś własna kroju
   i skrót jej nie zna. Szerokość pola zostaje podana, bo Doto nie jest
   monospace i jedynka jest węższa od czwórki.                               */
.foreto-reveal__n {
  display: block;
  width: calc(var(--foreto-reveal-num) * 0.8);
  text-align: center;
  font-family: var(--foreto-font-display), 'Poppins', sans-serif;
  font-size: var(--foreto-reveal-num); line-height: 0.8;
  font-variation-settings: 'ROND' 0, 'wght' 900;
}
.foreto-reveal__body {
  flex: 1 1 0; min-width: 0; overflow: hidden;
  padding: 0 0 var(--foreto-space-6);
}
/* TEKST MA STAŁĄ SZEROKOŚĆ, a animuje się tylko okno, które go obcina.
   Bez tego akapit dostawał szerokość rosnącej pozycji i przez całe 200 ms
   przelewał wiersze — cztery razy w trakcie jednego kliknięcia.
   Wjazd: 16 px z lewej i wygaszenie, z opóźnieniem 120 ms — panel najpierw robi
   miejsce, potem wchodzi treść. Zamknięcie bez opóźnienia, bo treść ma zniknąć
   przed zwężeniem, nie po.                                                   */
.foreto-reveal__inner {
  width: var(--foreto-reveal-text);
  padding-inline-start: var(--foreto-space-5);
  opacity: 0; transform: translateX(var(--foreto-space-3));
  transition: opacity var(--foreto-transition-base, 200ms ease-out),
              transform var(--foreto-transition-base, 200ms ease-out);
}
.foreto-reveal__item[data-open="true"] .foreto-reveal__inner {
  opacity: 1; transform: none;
  transition-delay: 120ms;
}
.foreto-reveal__title {
  margin: 0 0 var(--foreto-space-2);
  font-size: var(--foreto-font-size-h4); line-height: var(--foreto-line-h4);
  font-weight: 700; text-wrap: balance;
}
.foreto-reveal__text {
  margin: 0; font-size: var(--foreto-font-size-body); line-height: var(--foreto-line-body);
  color: var(--foreto-text-muted); text-wrap: balance;
}
@media (prefers-reduced-motion: reduce) {
  .foreto-reveal__item, .foreto-reveal__btn, .foreto-reveal__inner { transition: none; }
  .foreto-reveal__item::before { animation: none; }
  /* Bez ruchu treść musi być widoczna od razu — wygaszenie niosło stan tylko
     razem z wjazdem.                                                         */
  .foreto-reveal__item[data-open="true"] .foreto-reveal__inner { opacity: 1; transform: none; }
}

/* Poniżej pełnej kolumny akordeon poziomy nie ma sensu: cztery numery plus
   akapit nie zmieszczą się w jednym rzędzie. Rozwija się w pionową listę
   z WSZYSTKIMI opisami widocznymi — zwijanie w pionie zabierałoby treść bez
   powodu, bo miejsca w dół nie brakuje.                                      */
.foreto-reveal-box { container-type: inline-size; }
@container (max-width: 1133px) {
  .foreto-reveal { flex-direction: column; gap: var(--foreto-space-6); }
  .foreto-reveal__item,
  .foreto-reveal__item[data-open="true"],
  .foreto-reveal__item[data-open="false"] { flex: 1 1 auto; }
  /* W pionie wszystko jest otwarte, więc pasek nie odlicza — stoi wypełniony
     jako kreska sekcji, a treść stoi na miejscu bez ruchu.                    */
  .foreto-reveal__item::before,
  .foreto-reveal[data-auto="true"] .foreto-reveal__item[data-open="true"]::before {
    transform: scaleX(1); animation: none;
  }
  .foreto-reveal__inner,
  .foreto-reveal__item[data-open="false"] .foreto-reveal__inner {
    width: auto; opacity: 1; transform: none; transition: none;
  }
  .foreto-reveal__btn { pointer-events: none; color: var(--foreto-ink); padding-bottom: var(--foreto-space-4); }
  .foreto-reveal__body { padding-bottom: var(--foreto-space-4); }
  .foreto-reveal__inner { min-width: 0; }
  .foreto-reveal { --foreto-reveal-num: 128px; }
}

/* --- Prose — PRZENIESIONY 1:1 z kit/layout.css ---------------------------
   Kolumna czytelnicza 940. Interlinia akapitu przypięta do 24, bo przy normal
   Poppins 16 px daje 23,x i rytm się rozjeżdża.                             */
.foreto-prose { max-width: var(--foreto-prose-max); margin-inline: auto;
  padding-top: var(--foreto-prose-pad-top, 0px); padding-bottom: var(--foreto-prose-pad-bottom, 0px); }
.foreto-prose p { line-height: var(--foreto-space-4); }
.foreto-prose p + p { margin-top: var(--foreto-space-4); }
.foreto-prose h3 { margin-top: var(--foreto-space-6); margin-bottom: var(--foreto-space-3);
  font-size: var(--foreto-font-size-h3); line-height: var(--foreto-line-h3); font-weight: 700; text-transform: uppercase; }
.foreto-prose h3:first-child { margin-top: 0; }
.foreto-prose a { text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 1181px) { .foreto-prose { padding-top: var(--foreto-space-8); padding-bottom: var(--foreto-space-8); } }

/* --- Eat — PRZENIESIONE 1:1 z kit/blog.css, motyw „wirus zjada piksele" ----
   Siatka wielkich pikseli, które ZNIKAJĄ ze zdjęcia po najechaniu: kwadraty
   pojawiają się jeden po drugim od losowego narożnika i wygryzają kadr.
   To nie ozdoba wymyślona pod tę kartę — to ten sam motyw, na którym stoi cała
   ikonografia systemu (rysunek z kwadratów o jednym kroku), tylko użyty jako
   ruch, nie jako glif.

   DLACZEGO KWADRATY W DOM, A NIE MASKA. Rozważone i odrzucone: gradient
   szachownicowy z animowanym clip-path daje jedną falę, ale przy krawędzi
   diagonalnej PRZECINA kwadraty na trójkąty — a motyw systemu to kwadrat.
   Piksel musi pojawiać się cały albo wcale, więc każdy jest osobnym elementem
   z własnym opóźnieniem.

   OPÓŹNIENIE JEST POLICZONE Z POŁOŻENIA, nie wpisane: odległość w metryce
   miejskiej od narożnika źródła. Piksele na jednej przekątnej wchodzą razem
   i front posuwa się ukosem.

   Rozstaw pikseli jest RZADSZY W MIARĘ ODDALANIA od źródła — to nie estetyka,
   to jedyny sposób, żeby rzecz czytała się jako rozprzestrzenianie, a nie jak
   opadająca kurtyna: przy pełnym pokryciu front nie ma frontu.

   Kolor: rdzeń w czerwieni marki, front w czerni („wypalone" piksele), biel
   rozsiana po całości. Zdjęcia w kartach są dekoracyjne (alt=""), więc
   zasłonięcie części kadru nie odbiera żadnej informacji — a cała siatka jest
   aria-hidden.                                                               */
.foreto-eat {
  --foreto-eat-cols: 8;
  --foreto-eat-rows: 5;
  --foreto-eat-step: 45ms;
  position: absolute; inset: 0; z-index: 1;
  display: grid;
  grid-template-columns: repeat(var(--foreto-eat-cols), 1fr);
  grid-template-rows: repeat(var(--foreto-eat-rows), 1fr);
  pointer-events: none;
  /* BEZ PERSPEKTYWY. Wcześniej kafelki wstawały obrotem 3D wokół dolnej
     krawędzi — decyzja zdjęta: rozwijanie na wysokość dokładało pikselowi ruch
     w trzeciej osi, którego nikt nie potrzebował, a kwadrat przez chwilę nie
     był kwadratem. Piksel albo jest, albo go nie ma; przejściem jest samo
     krycie.                                                                  */
}
/* PRZESUNIĘCIE JEST W PROCENTACH WŁASNEGO PUDEŁKA, a pudełko równa się komórce
   siatki — więc 100% to dokładnie jeden krok w siatce i chód nigdy nie wypada
   między komórki. Żadna liczba pikseli nie jest tu potrzebna.                */
.foreto-eat__px {
  grid-column: calc(var(--x) + 1); grid-row: calc(var(--y) + 1);
  background: var(--foreto-red);
  translate: calc(var(--dx, 0) * 100%) calc(var(--dy, 0) * 100%);
  /* Krycie jest JEDYNYM nośnikiem wejścia — po zdjęciu obrotu piksel pojawia
     się i znika samym kryciem, a docelowa wartość idzie z --a (per piksel).
     Krycie na GRAFICE jest dozwolone; zakaz systemu dotyczy przygaszania
     TEKSTU, bo tam łamie próg kontrastu.                                     */
  opacity: 0;
  /* Opóźnienie TYLKO na wejściu (krycie), nie na chodzie: wspólne
     transition-delay opóźniałoby każdy krok chodu o nawet pół sekundy i piksel
     ruszałby dopiero po tym, jak dostał już następne polecenie.              */
  transition:
    opacity var(--foreto-duration-base) ease-out var(--foreto-eat-delay),
    translate var(--foreto-duration-base) ease-out 0ms;
  --foreto-eat-delay: calc(var(--o, 0) * var(--foreto-eat-step));
}
.foreto-eat__px--dead  { background: var(--foreto-ink); }
.foreto-eat__px--paper { background: var(--foreto-surface-card); }
.foreto-eat-host:hover .foreto-eat__px,
.foreto-eat-host:has(.foreto-post__link:focus-visible) .foreto-eat__px { opacity: var(--a, 1); }
.foreto-eat-host:not(:hover):not(:has(.foreto-post__link:focus-visible)) .foreto-eat__px {
  --foreto-eat-delay: 0ms;
}

/* --- PostCard — PRZENIESIONE 1:1 z kit/blog.css ----------------------------
   Kafel wpisu bloga. Odziedziczona geometria z CaseCard (kolumna 358 z siatki
   3 × 358 + 2 × 30, kadr 16/9, dopełnienie treści 24) — różni się treścią:
   kicker to KATEGORIA, nie klient, i dochodzi metryka daty i czasu czytania,
   a nie ma drugiego łącza „podgląd na żywo". Dlatego osobny komponent: karta
   z jednym celem kliknięcia nie potrzebuje ani jednego z wyłączeń
   :not(:has(...)), na których stoi CaseCard.                                 */
.foreto-post {
  --foreto-post-ratio: 16 / 9;
  position: relative; display: flex; flex-direction: column;
  min-width: 0; height: 100%;
  background: var(--foreto-surface-card);
  box-shadow: var(--foreto-shadow-card);
  border: var(--foreto-border-width) solid var(--foreto-border-elevation);
  transition: transform var(--foreto-transition-base);
}
/* Pierścień na CAŁEJ karcie, bo klikalna jest cała karta — obwódka wokół
   samego tytułu kłamałaby o celu. Ta sama zasada co w CaseCard.              */
.foreto-post:has(.foreto-post__link:focus-visible) {
  outline: 2px solid var(--foreto-focus); outline-offset: 3px;
}
/* Karta podnosi się o 4 px — ten sam dystans, co odjazd grotu w całym
   systemie, więc reakcje są jednym ruchem, a nie dwoma różnymi.              */
.foreto-post:hover,
.foreto-post:has(.foreto-post__link:focus-visible) { transform: translateY(-4px); }
.foreto-post__media { --foreto-ratio: var(--foreto-post-ratio); }
.foreto-post__body {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--foreto-space-3); flex: 1 1 auto;
  padding: var(--foreto-space-4);
}
.foreto-post__kicker {
  margin: 0; font-size: 12px; line-height: 16px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--foreto-text-accent);
}
.foreto-post__title {
  margin: 0;
  font-size: var(--foreto-font-size-h4); line-height: var(--foreto-line-h4);
  font-weight: 700; text-wrap: balance;
  /* Dwa wiersze zarezerwowane: tytuły wpisów mają nieprzewidywalną długość,
     a bez rezerwy kafle w rzędzie różnią się o 32 px.                        */
  min-height: calc(2 * var(--foreto-line-h4));
}
.foreto-post__link { color: inherit; text-decoration: none; transition: color var(--foreto-transition-base); }
/* Nakładka rozciąga cel kliknięcia na całą kartę. Znany koszt: tekstu karty
   nie da się zaznaczyć myszą — cena za cel wielkości kafla.                  */
.foreto-post__link::after { content: ''; position: absolute; inset: 0; z-index: 2; }
.foreto-post:hover .foreto-post__link,
.foreto-post:has(.foreto-post__link:focus-visible) .foreto-post__link { color: var(--foreto-text-accent); }
.foreto-post__excerpt {
  margin: 0; font-size: var(--foreto-font-size-body); line-height: var(--foreto-line-body);
  color: var(--foreto-text-muted); text-wrap: pretty;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
  min-height: calc(3 * var(--foreto-line-body));
}
/* Metryka na dole karty niezależnie od długości tytułu i zajawki.            */
.foreto-post__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--foreto-space-1) 0;
  margin: auto 0 0; padding: 0; list-style: none;
  font-size: 12px; line-height: 16px; color: var(--foreto-text-muted);
}
/* Separator niesie NASTĘPNIK, więc przy usunięciu pozycji nie zostaje sierotą. */
.foreto-post__meta > li + li::before { content: '·'; padding-inline: var(--foreto-space-2); }
@media (prefers-reduced-motion: reduce) {
  .foreto-post, .foreto-post__link, .foreto-eat__px { transition: none; }
  /* Chód jest ruchem w całości dekoracyjnym i nie startuje wcale — logika
     karty nie uruchamia pętli przy tej preferencji.                          */
  .foreto-post:hover, .foreto-post:has(.foreto-post__link:focus-visible) { transform: none; }
  /* Piksele nie pojawiają się WCALE — to ruch w całości dekoracyjny, a stan
     karty niesie kolor tytułu i podniesienie, więc nic nie ginie.            */
  .foreto-eat { display: none; }
}

/* Wariant szeroki — wpis wyróżniony. Kolumny z siatki źródłowej: 528 kadru,
   rynna 72, resztę bierze treść. Kadr jest wtedy szerszy niż 16/9, bo pole
   ma pełną wysokość kolumny treści.                                          */
.foreto-post--wide {
  display: grid; grid-template-columns: 528px minmax(0, 1fr);
  gap: 0 72px; align-items: center;
  padding: var(--foreto-space-5);
}
.foreto-post--wide .foreto-post__media { height: 100%; --foreto-ratio: auto; min-height: 297px; }
.foreto-post--wide .foreto-post__body { padding: 0; }
.foreto-post--wide .foreto-post__title {
  font-size: var(--foreto-font-size-h3); line-height: 40px; min-height: 0;
}
.foreto-post--wide .foreto-post__excerpt { -webkit-line-clamp: 4; min-height: 0; }
.foreto-post--wide .foreto-post__meta { margin-top: var(--foreto-space-2); }
/* Poniżej pełnej szerokości kolumna 528 plus rynna 72 nie mieszczą się razem
   z czytelną kolumną treści — wyróżniony staje się zwykłym kaflem.           */
@container (max-width: 1133px) {
  .foreto-post--wide { grid-template-columns: minmax(0, 1fr); gap: 0; padding: 0; }
  .foreto-post--wide .foreto-post__media { min-height: 0; --foreto-ratio: 16 / 9; height: auto; }
  .foreto-post--wide .foreto-post__body { padding: var(--foreto-space-4); }
  .foreto-post--wide .foreto-post__title { font-size: var(--foreto-font-size-h4); line-height: var(--foreto-line-h4); }
  .foreto-post--wide .foreto-post__excerpt { -webkit-line-clamp: 3; }
}
/* Siatka wpisów: 3 × 358 + 2 × 30 = 1134, prawdziwa siatka serwisu.
   Progi mierzone NA KOLUMNIE, nie na oknie — listing stoi w treści strony.   */
.foreto-posts-box { container-type: inline-size; }
.foreto-posts {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--foreto-space-6) 30px;
  margin: 0; padding: 0; list-style: none;
}
@container (max-width: 1133px) { .foreto-posts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container (max-width: 760px)  { .foreto-posts { grid-template-columns: minmax(0, 1fr); } }

/* --- Chip — PRZENIESIONY 1:1 z kit/data.css -------------------------------
   Interaktywne rodzeństwo plakietki: ta sama wysokość 24 px, ale chip DA SIĘ
   zdjąć albo przełączyć. Rozdzielenie jest celowe — element, który wygląda
   klikalnie, musi być klikalny.                                              */
.foreto-chip {
  display: inline-flex; align-items: center; gap: var(--foreto-space-1);
  height: var(--foreto-hit); padding-inline: var(--foreto-space-2);
  border: 0; border-radius: 0;
  background: var(--foreto-surface-band); color: var(--foreto-ink);
  font-size: 12px; line-height: 16px; font-weight: 700;
  white-space: nowrap;
  transition: background-color var(--foreto-transition-base), color var(--foreto-transition-base);
}
.foreto-chip__label { overflow: hidden; text-overflow: ellipsis; }
/* Chip przełączalny to PRZYCISK z aria-pressed — nie checkbox: nie wysyła
   wartości w formularzu, tylko zawęża widok.                                 */
.foreto-chip--toggle { cursor: pointer; }
.foreto-chip--toggle:hover { background: var(--foreto-border-hairline); }
.foreto-chip--toggle:focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 2px; }
.foreto-chip.foreto-chip--toggle[aria-pressed="true"] { background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent); }
.foreto-chip.foreto-chip--toggle[aria-pressed="true"]:hover { background: var(--foreto-accent-hover); }

/* --- FilterBar — PRZENIESIONY 1:1 z kit/data.css --------------------------
   Pasek zawężeń: chipy plus podsumowanie. Liczba wyników jest w tym samym
   pasku, bo zmienia się razem z chipami — trzymanie jej pod listą zmuszałoby
   oko do skakania.                                                           */
.foreto-filterbar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--foreto-space-2) var(--foreto-space-3);
  padding-block: var(--foreto-space-3);
  border-bottom: 1px solid var(--foreto-border-hairline);
}
.foreto-filterbar__label { margin: 0; font-size: 12px; line-height: 16px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--foreto-text-muted); }
.foreto-filterbar__group { display: flex; flex-wrap: wrap; gap: var(--foreto-space-2); }
.foreto-filterbar__count { margin: 0; margin-inline-start: auto; font-size: var(--foreto-font-size-card); line-height: var(--foreto-msg-line); color: var(--foreto-text-muted); font-variant-numeric: tabular-nums; }
@media (max-width: 760px) {
  .foreto-filterbar__count { margin-inline-start: 0; flex-basis: 100%; }
}

/* --- Pagination — PRZENIESIONA 1:1 z kit/data.css -------------------------
   GEOMETRIA ZE ŹRÓDŁA: pole 36 × 36, odstęp 16, tło pasma pod każdym numerem.
   Pole jest kwadratem, więc numer, wielokropek i strzałka mają jeden wymiar.
   Bieżąca strona to <span aria-current="page">, NIE odsyłacz: nie ma dokąd
   prowadzić, a odsyłacz, który nic nie robi, jest kłamstwem.                 */
.foreto-pagination { --foreto-page-size: 36px; display: flex; flex-wrap: wrap; align-items: center; gap: var(--foreto-space-3); }
.foreto-pagination__list { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--foreto-space-3); margin: 0; padding: 0; list-style: none; }
.foreto-page-btn {
  display: grid; place-items: center;
  width: var(--foreto-page-size, 36px); height: var(--foreto-page-size, 36px);
  border: 0; border-radius: 0; background: var(--foreto-surface-band); color: var(--foreto-ink);
  font: 700 var(--foreto-font-size-card) / var(--foreto-space-4) inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background-color var(--foreto-transition-base), color var(--foreto-transition-base);
}
.foreto-page-btn:hover { background: var(--foreto-border-hairline); }
.foreto-page-btn:focus-visible { outline: 2px solid var(--foreto-focus); outline-offset: 2px; }
.foreto-page-btn[aria-current="page"], .foreto-page-btn[data-current="true"] { background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent); cursor: default; }
.foreto-page-btn[aria-current="page"]:hover, .foreto-page-btn[data-current="true"]:hover { background: var(--foreto-surface-accent); }
.foreto-page-btn[disabled], .foreto-page-btn[aria-disabled="true"] { color: var(--foreto-border-hairline); cursor: not-allowed; pointer-events: none; }
.foreto-pagination__gap { display: grid; place-items: center; width: var(--foreto-page-size, 36px); height: var(--foreto-page-size, 36px); background: none; color: var(--foreto-text-muted); }
@media (max-width: 640px) {
  .foreto-pagination { gap: var(--foreto-space-2); }
  .foreto-pagination__list { gap: var(--foreto-space-2); }
  .foreto-pagination__gap { display: none; }
}

/* --- Pagination: glif kierunku — dopełnienie przeniesionego wycinka -------
   Strzałki to ten sam plik chevron-sm, obrócony o 180° — i nic więcej. Glif
   pokazuje W PRAWO, więc „następna" nie potrzebuje obrotu, a „poprzednia" to
   lustro. Obroty o 90° dawały chevron pokazujący w dół i w górę, czyli
   strzałki „bokiem".                                                        */
.foreto-page-btn__glyph {
  width: 12px; height: 12px; background-color: currentColor;
  -webkit-mask: url('../icons/chevron-sm.svg') no-repeat center / contain;
          mask: url('../icons/chevron-sm.svg') no-repeat center / contain;
}
/* --next nie ma własnej reguły i to jest celowe: glif pokazuje natywnie
   w prawo, więc obracamy tylko „poprzednią".                                */
.foreto-page-btn--prev .foreto-page-btn__glyph { transform: rotate(180deg); }
.foreto-pagination__status {
  margin: 0; margin-inline-start: auto;
  font-size: var(--foreto-font-size-card); line-height: var(--foreto-msg-line);
  color: var(--foreto-text-muted); font-variant-numeric: tabular-nums;
}
@media (max-width: 760px) {
  .foreto-pagination__status { margin-inline-start: 0; flex-basis: 100%; }
}

/* --- Sparks — PRZENIESIONE 1:1 z kit/patterns.css --------------------------
   Delikatne kwadraty pojawiające się i gasnące w tle sekcji. Ten sam motyw co
   siatka zjadanych pikseli w karcie wpisu i ta sama jednostka 40 px, tylko
   zamiast reakcji na wskazanie jest to ruch WŁASNY pasma — pasmo CTA nie ma
   czego wskazywać, bo cały jego sens to jeden przycisk.

   RUCH JEST BEZ JS. Kwadraty pulsują animacją CSS z losowym czasem i UJEMNYM
   opóźnieniem — ujemnym, bo wtedy każdy zaczyna w innym punkcie swojego cyklu
   i nie ma momentu, w którym całe pasmo mruga zgodnie. Pętla animacji zamiast
   pętli w skrypcie: rzecz działa też wtedy, gdy komponent stoi w PHP.

   CZASY 5–11 s WYCHODZĄ POZA SKALĘ RUCHU SYSTEMU (120/200 ms) i to jest
   świadome: skala dotyczy reakcji na działanie użytkownika, gdzie liczy się
   natychmiastowość. To jest tło, a tło ma być na granicy dostrzegalności —
   przy 200 ms kwadraty stroboskopowałyby.

   KRYCIE 0,10–0,20: powyżej tego kwadraty konkurują z nagłówkiem, który stoi na
   tym samym paśmie. Barwa idzie z currentColor, więc na czerwieni są białe,
   a na bieli czarne — jeden komponent na oba tła.                            */
@keyframes foreto-spark {
  0%, 100% { opacity: 0; }
  50%      { opacity: var(--a, .14); }
}
.foreto-sparks {
  position: absolute; inset: 0; z-index: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, var(--foreto-px, 40px));
  grid-auto-rows: var(--foreto-px, 40px);
  pointer-events: none;
}
.foreto-spark {
  grid-column: calc(var(--x) + 1); grid-row: calc(var(--y) + 1);
  background: currentColor; opacity: 0;
  animation: foreto-spark var(--dur, 8s) ease-in-out var(--d, 0s) infinite;
}
@media (prefers-reduced-motion: reduce) {
  /* Bez ruchu kwadraty ZOSTAJĄ, tylko stoją: są kompozycją pasma, nie samą
     animacją, więc ich zniknięcie zabrałoby pasmu tło.                       */
  .foreto-spark { animation: none; opacity: var(--a, .14); }
}

/* --- CtaBand — PRZENIESIONE 1:1 z kit/patterns.css -------------------------
   Pasmo domykające stronę: nagłówek, jedno zdanie i jeden przycisk. Treść
   wyśrodkowana, bo pasmo nie ma nic obok — wyrównanie do lewej zostawiałoby
   po prawej pustkę wielkości połowy kontenera.                              */
/* container-type, bo próg dwóch kolumn mierzy się NA PAŚMIE, nie na oknie:
   pasmo stoi raz w pełnej szerokości strony, a raz w węższej kolumnie treści,
   i w tej drugiej @media nadal widziałby szerokie okno.                      */
/* `clip`, nie `hidden` — przycina tak samo, ale NIE tworzy kontenera
   przewijania, i na tym polega różnica. Oś `view()` mierzy położenie względem
   najbliższego kontenera przewijania, a `hidden` nim jest, mimo że nic się tu
   nie przewija. Wnętrze pasma stało w nim nieruchomo, więc postęp osi wynosił
   zero przez cały czas i treść zostawała niewidoczna — bez błędu, bez śladu
   w konsoli, po prostu pusty czerwony prostokąt.

   `clip` przycina bez tej konsekwencji: nie da się go przewinąć nawet
   programowo, czego to pasmo i tak nie robi. */
.foreto-cta { position: relative; overflow: clip; container-type: inline-size; }
.foreto-cta__inner {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center;
  gap: var(--foreto-space-4); text-align: center;
}
.foreto-cta__title {
  margin: 0;
  font-size: var(--foreto-font-size-h2); line-height: var(--foreto-line-h2);
  font-weight: 700; text-transform: uppercase; text-wrap: balance;
}
.foreto-cta__lead {
  margin: 0; max-width: 60ch;
  font-size: var(--foreto-font-size-body); line-height: var(--foreto-line-body);
  text-wrap: balance;
}
/* Przycisk odsunięty o krok więcej niż odstęp bloku tekstu: to osobna decyzja
   użytkownika, nie trzecia linijka akapitu.                                  */
.foreto-cta__action { margin-top: var(--foreto-space-4); margin-bottom: 0; }
@container (min-width: 1134px) {
  .foreto-cta__inner {
    flex-direction: row; align-items: center; justify-content: space-between;
    gap: var(--foreto-space-7) 72px; text-align: start;
  }
  /* Tytuł i lead stają się jedną kolumną, a przycisk drugą.                  */
  .foreto-cta__copy { display: flex; flex-direction: column; gap: var(--foreto-space-4); min-width: 0; }
  .foreto-cta__lead { max-width: 52ch; }
  /* Odstęp niesie już rynna kolumn, więc własny margines przycisku wypada —
     inaczej stałby o 24 px niżej niż środek bloku tekstu.                    */
  .foreto-cta__action { margin-top: 0; flex: 0 0 auto; }
}
@media (max-width: 640px) {
  .foreto-cta__title { font-size: var(--foreto-font-size-h3); line-height: 40px; }
}

/* --- Drift — PRZENIESIONY 1:1 z kit/patterns.css --------------------------
   Tło CAŁEJ strony: kwadraty 40 px dryfujące po siatce, pod treścią.

   z-index: -1 razem z isolation na przodku — i to jest cały mechanizm widoczności,
   o który chodzi. Pole leży POD treścią, ale NAD tłem strony: tam, gdzie sekcja
   nie maluje własnego koloru, kwadraty widać; tam, gdzie maluje (pasmo szare,
   czerwone, czarne), sekcja je zasłania sobą. Bez isolation ujemny z-index
   wypchnąłby pole pod tło <body> i nie byłoby go widać nigdzie.

   position: fixed, więc tło nie przewija się razem z treścią — dryf ma być
   ruchem samego pola, a nie efektem przewijania.                            */
.foreto-root:has(.foreto-drift) { isolation: isolate; }
.foreto-drift {
  position: fixed; inset: 0; z-index: -1;
  display: grid;
  grid-template-columns: repeat(var(--foreto-drift-cols, 24), var(--foreto-px, 40px));
  grid-template-rows: repeat(var(--foreto-drift-rows, 16), var(--foreto-px, 40px));
  place-content: center;
  pointer-events: none;
}
/* Komórka wyjściowa siedzi w grid-column/row i NIE zmienia się nigdy, a ruch
   niesie translate w procentach własnego pudełka. Gdyby zmieniała się komórka,
   przeglądarka przeliczałaby układ siatki przy każdym kroku każdego kwadratu —
   a translate to samo malowanie.                                            */
.foreto-drift__px {
  grid-column: calc(var(--x, 0) + 1);
  grid-row: calc(var(--y, 0) + 1);
  background: var(--foreto-drift-ink, var(--foreto-ink));
  opacity: var(--a, 0);
  translate: calc(var(--dx, 0) * 100%) calc(var(--dy, 0) * 100%);
  /* Krycie W PRZEJŚCIU, nie tylko przesunięcie. Skrypt ustawia `--a` skokowo —
     przy każdym takcie jedne kwadraty dostają wartość, inne zero — więc bez tej
     drugiej pozycji pojawiały się i znikały twardo, jednym rysunkiem. Tło ma
     się wyłaniać, a nie mrugać.

     Ten sam `--dur` co przesunięcie, celowo: kwadrat, który dryfuje wolniej,
     ma się wolniej pojawiać. Dwa niezależne czasy rozjechałyby rytm pola.

     `ease-in-out`, nie `linear` jak przy przesunięciu: ruch liniowy czyta się
     jako dryf, liniowe krycie jako miganie.                                  */
  transition:
    translate var(--dur, 1600ms) linear,
    opacity var(--dur, 1600ms) ease-in-out;
}
.foreto-drift__px--accent { background: var(--foreto-red); }
@media (prefers-reduced-motion: reduce) {
  /* Kwadraty ZOSTAJĄ, tylko przestają dryfować: są kompozycją tła, a nie samą
     animacją — ich zniknięcie zabrałoby stronie teksturę.

     Przejście krycia zostaje, znika samo przesunięcie. Prośba o mniej ruchu
     dotyczy RUCHU: kwadrat, który się wyłania w miejscu, niczego nie przesuwa,
     a wyłączenie tego akurat przejścia dałoby twarde mrugnięcie — czyli efekt
     bardziej niespokojny niż to, co miało zostać wyciszone.                  */
  .foreto-drift__px { transition: opacity var(--dur, 1600ms) ease-in-out; }
}

/* --- Badge — PRZENIESIONY 1:1 z kit/feedback.css --------------------------
   ZNACZNIK STANU, nie komunikat — dlatego wypełnienie tonem jest tu na miejscu,
   w przeciwieństwie do alertu. Wysokość 4 + 16 + 4 = 24 px, czyli dokładnie
   --foreto-hit: plakietka staje w rzędzie z ikoną 24 bez podpierania
   marginesem.                                                                */
.foreto-badge {
  display: inline-flex; align-items: center; gap: var(--foreto-space-1);
  padding: var(--foreto-space-1) var(--foreto-space-2);
  border-radius: var(--foreto-radius, 0);
  font-size: 10px; line-height: 16px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; white-space: nowrap;
  background: var(--foreto-surface-band); color: var(--foreto-ink);
}
/* Plakietka niesie WŁASNE tło, więc tokeny kontekstu muszą wrócić do wartości
   „na stronie" — inaczej postawiona na czerwonym paśmie odziedziczyłaby jego
   biel dla tekstu wyciszonego i kreski. Ta sama poprawka, którą kit stosuje do
   wszystkich komponentów z własnym tłem.                                     */
.foreto-badge {
  --foreto-text-muted: var(--foreto-text-muted-page);
  --foreto-border-hairline: var(--foreto-border-hairline-page);
}
.foreto-badge--accent { background: var(--foreto-surface-accent); color: var(--foreto-text-on-accent); }
.foreto-badge--ink { background: var(--foreto-ink); color: var(--foreto-page); }
.foreto-badge--outline { background: transparent; box-shadow: inset 0 0 0 1px var(--foreto-border-hairline); }
.foreto-badge .foreto-icon { width: 12px; height: 12px; }

/* --- Snake — PRZENIESIONE 1:1 z kit.css, tło pasma jako gra -----------------
   Wąż chodzący po siatce w tle pasma CTA. NIEINTERAKTYWNY z wyboru, nie
   z niedoróbki: klawiatura w tle sekcji przechwytywałaby strzałki, którymi
   użytkownik przewija stronę, a element dekoracyjny nie ma prawa zabierać
   sterowania. Cała rzecz jest aria-hidden i pointer-events: none.

   RUCH JEST SKOKOWY, bez przejścia między komórkami. To nie oszczędność —
   wąż w grze siatkowej przeskakuje o pole i właśnie po tym się go rozpoznaje;
   płynne przesunięcie zamieniłoby go w pełzającą wstęgę.

   Jednostka to te same 40 px co siatka Sparks i siatka zjadanych pikseli
   w karcie wpisu. Jeden piksel w całym systemie.

   Krycie segmentów maleje od głowy do ogona (--a nadawane przez logikę):
   bez tego wąż jest jednolitym pasem i nie widać, w którą stronę idzie.      */
.foreto-snake {
  position: absolute; inset: 0; z-index: 0;
  display: grid;
  grid-template-columns: repeat(var(--foreto-snake-cols, 24), var(--foreto-px, 40px));
  grid-template-rows: repeat(var(--foreto-snake-rows, 8), var(--foreto-px, 40px));
  align-content: center; justify-content: center;
  pointer-events: none;
}
/* ODSTĘPSTWO OD KITU, ZMIERZONE. Kit przestawia segmentom grid-column
   i grid-row, uzasadniając to tak: „na 200 komórkach jest tańsze niż warstwa
   kompozytora". Jako rachunek kosztu to prawda — ale grid-column i grid-row to
   UKŁAD, a przeglądarka każde ich przestawienie zapisuje jako przesunięcie
   układu. Przy takcie 150 ms daje to sześć przesunięć na sekundę bez końca:
   zmierzone CLS 0,2719 przy 88 przesunięciach w dwie i pół sekundy, każde
   ze źródłem .foreto-snake__seg.

   Skutek jest gorszy niż sam wynik pomiaru: kotwica przewijania przeglądarki
   wybiera węzeł, wokół którego trzyma pozycję, a dziesięć elementów ruszających
   się co 150 ms unieważnia jej wybór bez przerwy. Strona pod kursorem drga —
   i tak to było widać.

   Rozwiązanie jest W KICIE, tylko w innym komponencie: dryfujące tło strony
   trzyma STAŁĄ komórkę i rusza się translate, czyli malowaniem. Wąż robi teraz
   to samo. Komórka ma dokładnie 40 px, więc 100% na oś to jedno pole i rysunek
   nie zmienia się o piksel.                                                   */
.foreto-snake__seg,
.foreto-snake__food {
  grid-area: 1 / 1;
  translate: calc(var(--x, 0) * 100%) calc(var(--y, 0) * 100%);
  background: currentColor;
}
.foreto-snake__seg { opacity: var(--a, .18); }
/* Jabłko pulsuje, żeby odróżniało się od segmentów bez użycia drugiego koloru:
   czerwień na czerwonym paśmie nie istnieje, a biel jest już zajęta przez
   węża. Rytm 1,4 s — wolniej niż takt węża, więc dwa ruchy nie wpadają
   w jeden.                                                                   */
@keyframes foreto-snake-food {
  0%, 100% { opacity: .5; }
  50%      { opacity: .12; }
}
.foreto-snake__food {
  opacity: .5;
  animation: foreto-snake-food 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  /* Wąż ZOSTAJE, ale zamiera: logika nie uruchamia taktu, a jabłko przestaje
     pulsować. Kompozycja pasma zostaje, rusza się tylko nic.                 */
  .foreto-snake__food { animation: none; }
}


/* --- Count — DOŁOŻONE, liczba jako grafika sekcji --------------------------
   Liczba pasma „Foreto w liczbach". Jest tu GRAFIKĄ SEKCJI, nie wartością
   w tabeli — stąd krój Doto i stopień 80, a nie .foreto-stat, który służy
   liczbom w gęstych układach danych i ma deltę, trend oraz jednostkę.

   ROND 0, bo cała ikonografia systemu jest z kwadratów; kółko byłoby cytatem
   z innego języka. Zakres odstępstwa od jednej rodziny kroju — przy @font-face
   wyżej.

   MIEJSCE NA LICZBĘ JEST ZAREZERWOWANE WARTOŚCIĄ DOCELOWĄ. Doto nie jest
   monospace (jedynka jest węższa od czwórki), a licznik przechodzi po drodze
   przez inne cyfry — bez rezerwy etykieta pod nim drgałaby przez cały czas
   odliczania. Rozpórka to ta sama liczba, ukryta, w tej samej komórce siatki:
   szerokość bierze się z treści, więc żadnej liczby nie trzeba podawać.

   Rozpórka składa się z tych samych trzech części co liczba widoczna, bo
   prefiks jest w innym stopniu — jeden ciąg w Doto mierzyłby inną szerokość
   niż to, co widać.

   STOPIEŃ 80, NIE 88 — z rachunku, nie z gustu. Najdłuższa liczba pasma ma
   siedem znaków („20 000+") i przy stopniu 88 mierzy 370 px, a najwęższa
   kolumna układu wielokolumnowego to 358 (3 × 358 + 2 × 30 = 1134, siatka
   serwisu): liczba wychodziła poza kolumnę. Stosunek szerokości do stopnia
   w Doto wynosi 4,2, więc 358 / 4,2 = 85 to górna granica, a 80 zostawia 22 px
   zapasu na liczbę o jeden znak dłuższą. 80 nie jest przy tym nową liczbą
   w systemie: tym wymiarem Reveal skaluje numer w wariancie wąskim.          */
.foreto-count {
  --foreto-count-size: 80px;
  display: inline-grid; grid-template-areas: 'n';
  white-space: nowrap;
  font-family: var(--foreto-font-display), 'Poppins', sans-serif;
  font-size: var(--foreto-count-size); line-height: 1;
  font-weight: 900; font-variation-settings: 'ROND' 0, 'wght' 900;
  color: var(--foreto-text-accent);
}
.foreto-count > * { grid-area: n; }
.foreto-count__ghost { visibility: hidden; }
/* ZWĘŻENIE SPACJI OBEJMUJE WYŁĄCZNIE CIĄG CYFR. Na całej liczbie było błędem:
   word-spacing ściągało też spację po prefiksie, więc cyfry wjeżdżały na słowo
   „od" — a test przecięcia prostokątów tego nie łapał, bo pudełko prefiksu
   raportowało 13 px szerokości przy rysunku 35 px, czyli glify wychodziły poza
   własne pudełko. Ten sam błąd skracał rozpórkę o 36 px, więc etykieta mogła
   znów drgać w trakcie odliczania.
   Dlaczego zwężenie jest w ogóle potrzebne: Doto ma odstęp spacji szeroki jak
   cyfra, więc przy stopniu 80 „20 000" miało w środku ponad 40 px dziury
   i czytało się jak dwie liczby.
   Odstęp po prefiksie idzie MARGINESEM — margines nie podlega word-spacing,
   więc te dwie sprawy przestają się nawzajem psuć.                           */
.foreto-count__num { word-spacing: -0.45em; }
/* Prefiks w połowie stopnia liczby: „od" przy roku jest przyimkiem, nie częścią
   wartości, i przy tej samej wielkości co cyfry czytałoby się jak jednostka.  */
.foreto-count__fix--pre {
  font-size: calc(var(--foreto-count-size) * 0.5);
  margin-inline-end: 0.18em;
}
/* Prefiks i sufiks liczby w czerwieni — ten sam zabieg co czerwone „tel." i
   „mail." przy danych kontaktowych: akcent siedzi na drobiazgu, a nie na całej
   wartości, więc liczba zostaje najmocniejszym elementem, ale kolor marki jest
   obecny w każdej pozycji, nie tylko w jednej wybranej.                       */
.foreto-count__fix { color: var(--foreto-text-accent); }
/* Na czerwonym tle akcent nie istnieje: kolor wraca do dziedziczonego, tak samo
   jak kit robi to deltcie w .foreto-stat.                                     */
.foreto-root[data-tone="accent"] .foreto-count,
.foreto-root [data-tone="accent"] .foreto-count,
.foreto-section--accent .foreto-count,
.foreto-card--accent .foreto-count,
.foreto-root[data-tone="accent"] .foreto-count__fix,
.foreto-root [data-tone="accent"] .foreto-count__fix,
.foreto-section--accent .foreto-count__fix,
.foreto-card--accent .foreto-count__fix { color: inherit; }


/* --- Frost — DOŁOŻONE, wyciszenie dryfu pod nagłówkiem ---------------------
   Kwadraty mają być w pełni widoczne przy nagłówku strony i przygaszone tam,
   gdzie zaczyna się czytanie. Nie przez zmianę ich krycia — przez MLECZNĄ
   SZYBĘ pod sekcją treści: rozmycie odbiera kwadratowi krawędź, półprzezroczysta
   powierzchnia odbiera mu kontrast. Dalej widać, że coś tam jest, ale przestaje
   konkurować z literami.

   WARSTWA JEST PER SEKCJA I NA CAŁĄ JEJ WYSOKOŚĆ. Rozważana wersja z jedną
   ciągłą warstwą na całą stronę wymagałaby zmierzenia wysokości hero albo
   nagłówka podstrony, bo CSS nie umie zacząć „pod pierwszą sekcją" — a wynik
   byłby identyczny, bo sekcja bez własnego tła i tak jest jedynym miejscem,
   w którym cokolwiek prześwituje. Sekcja jest więc naturalną jednostką: zaczyna
   się dokładnie tam, gdzie kończy się nagłówek, i nic nie trzeba liczyć.

   BACKDROP-FILTER SIEDZI NA WARSTWIE POD TREŚCIĄ, NIE NA SEKCJI ANI NAD NIĄ.
   Filtr tła rozmywa WSZYSTKO, co namalowano pod danym elementem: warstwa nad
   sekcją rozmyłaby razem z kwadratami jej własny tekst. Na samej sekcji
   rozmywa wyłącznie to, co za nią — ale wtedy nie da się jej wygasić, bo maska
   na sekcji zgasiłaby także treść. Stąd ::before z ujemnym z-indeksem: leży
   pod tekstem, nad kwadratami, i maska gasi wyłącznie rozmycie.
   Warunkiem jest kolejność malowania — pole dryfu musi być wstawiane na
   POCZĄTEK <body> (patrz drift.js), bo przy równym ujemnym z-indeksie decyduje
   miejsce w drzewie.

   CO NIE DOSTAJE FROSTU, i dlaczego akurat to:
   — sekcja z nagłówkiem podstrony i hero strony głównej, bo to właśnie tam
     kwadraty mają być w pełnej ostrości;
   — sekcje z własnym tłem (data-tone), bo przez czerwień, czerń i pasmo i tak
     nic nie prześwituje, a filtr tła na nich to rozmycie liczone bez powodu.

   REGUŁA NIE UŻYWA RODZEŃSTWA i to jest wymuszone budową szablonów: na wpisie
   sekcje stoją obok nagłówka w <main>, ale na stronach z page-sekcje.html
   nagłówek jest w <main>, a sekcje treści w .entry-content — inny rodzic, więc
   „~" tam nie sięga. Zamiast szukać sekcji „po nagłówku" wyliczamy wprost, co
   frostu NIE dostaje.                                                        */
@supports (backdrop-filter: blur(1px)) {
  /* Sekcja musi być pozycjonowana, żeby warstwa miała się do czego przyciąć,
     ale NIE WOLNO jej tworzyć kontekstu warstw ani korzenia tła: element
     będący korzeniem tła nie ma czego rozmywać, bo pod nim jest wtedy tylko
     jego własne, przezroczyste tło. Stąd position: relative i ani słowa
     o isolation czy z-indeksie. */
  .foreto-root:has(.foreto-drift)
    .foreto-section:not([data-tone]):not(:has(.foreto-page-head)):not(.foreto-hero) {
    position: relative;
  }

  .foreto-root:has(.foreto-drift)
    .foreto-section:not([data-tone]):not(:has(.foreto-page-head)):not(.foreto-hero)::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    /* SEKCJA DOSTAJE SAMO LEKKIE ROZMYCIE, BEZ WELONU. To jest pas marginesu
       po bokach kontenera — nie ma tam czego czytać, więc kwadraty mają
       zostać widoczne, tylko bez ostrej krawędzi. Welon dokłada dopiero
       warstwa pod kontenerem treści niżej. */
    backdrop-filter: blur(var(--foreto-frost, 4px));
  }

  /* PRZEJŚCIE DOSTAJE TYLKO PIERWSZA FROSTOWANA SEKCJA W SWOIM RODZICU —
     rozpoznana po tym, że nie poprzedza jej żadna inna frostowana. Gdyby
     przejście dostawała każda, przy każdej granicy sekcji pojawiałby się pas
     bez rozmycia, czyli dokładnie ta krawędź, którą to ma usuwać. */
  .foreto-root:has(.foreto-drift)
    .foreto-section:not([data-tone]):not(:has(.foreto-page-head)):not(.foreto-hero):not(.foreto-section:not([data-tone]):not(:has(.foreto-page-head)):not(.foreto-hero) ~ *)::before {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--foreto-frost-fade, 220px));
            mask-image: linear-gradient(to bottom, transparent 0, #000 var(--foreto-frost-fade, 220px));
  }

  /* DRUGIE ROZMYCIE, WĘŻSZE — pod KONTENEREM TREŚCI. Rozmycia SIĘ SUMUJĄ, bo
     ta warstwa leży nad wynikiem poprzedniej i bierze go za swoje tło: 4 px
     sekcji plus 8 px kontenera daje pod treścią wyraźnie więcej niż obok niej.

     Punktem zaczepienia jest .foreto-section__inner, a nie .foreto-prose:
     kolumna prozy istnieje wyłącznie na wpisie, więc oparta na niej gradacja
     działała tylko tam i reszta serwisu jej nie miała. Kontener ma każda
     sekcja, niezależnie od typu treści.

     Pole kończy się na kontenerze i ani piksela dalej — margines sekcji ma
     zostać przy lekkim rozmyciu, z widocznym tłem. */
  .foreto-root:has(.foreto-drift)
    .foreto-section:not([data-tone]):not(:has(.foreto-page-head)):not(.foreto-hero) .foreto-section__inner {
    position: relative;
  }

  .foreto-root:has(.foreto-drift)
    .foreto-section:not([data-tone]):not(:has(.foreto-page-head)):not(.foreto-hero) .foreto-section__inner::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    backdrop-filter: blur(var(--foreto-frost-text, 8px));
    /* MLECZNA SZYBA TO ROZMYCIE PLUS PÓŁPRZEZROCZYSTA POWIERZCHNIA: rozmyty
       kwadrat zostaje tak samo ciemny, traci tylko kształt, więc kontrast
       odbiera mu dopiero warstwa barwy tła.
       Barwa z TOKENU STRONY, nie biel wpisana wprost — motyw ma tryb ciemny
       i biel rozjaśniłaby tam pas pod tekstem zamiast go przygasić.
       color-mix, nie opacity: opacity przygasiłaby cały element razem
       z wynikiem filtru, czyli osłabiła samo rozmycie. */
    background: color-mix(in srgb, var(--foreto-page) var(--foreto-frost-veil, 55%), transparent);
    /* WYGASZENIE NA CZTERECH KRAWĘDZIACH. Sprawdzone wcześniej: przy prostej,
       pionowej krawędzi oko łapie granicę nawet przy tak małej różnicy sił.
       Dwie maski złożone przez intersect, bo pojedynczy gradient wygasza jedną
       oś. Wygaszenie jest szerokie, bo przy małej różnicy sił krótkie
       przejście tylko przesuwa krawędź, zamiast ją usunąć. */
    -webkit-mask-image:
      linear-gradient(to right, transparent 0, #000 var(--foreto-frost-feather, 96px), #000 calc(100% - var(--foreto-frost-feather, 96px)), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 var(--foreto-frost-feather, 96px), #000 calc(100% - var(--foreto-frost-feather, 96px)), transparent 100%);
            mask-image:
      linear-gradient(to right, transparent 0, #000 var(--foreto-frost-feather, 96px), #000 calc(100% - var(--foreto-frost-feather, 96px)), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 var(--foreto-frost-feather, 96px), #000 calc(100% - var(--foreto-frost-feather, 96px)), transparent 100%);
    -webkit-mask-composite: source-in;
            mask-composite: intersect;
  }
}
