/* =========================================================
   TIVOLI — sections.css
   Front-page sections: hero, featured, about, menu preview,
   promotions, locations, final CTA.
   ========================================================= */

/* ---------- HERO ---------- */
.hero {
	position: relative;
	overflow: hidden;
	padding-block: var(--space-2xl) var(--space-2xl);
	background:
		radial-gradient(1200px 600px at 85% -10%, rgba(199, 154, 68, 0.16), transparent 60%),
		linear-gradient(180deg, var(--color-cream-soft) 0%, var(--color-cream) 100%);
}

.hero__inner {
	display: grid;
	grid-template-columns: 1.05fr 0.95fr;
	align-items: center;
	gap: var(--space-xl);
	min-height: min(78vh, 780px);
}

@media (max-width: 1024px) {
	.hero__inner {
		grid-template-columns: 1fr;
		min-height: auto;
		gap: var(--space-2xl);
	}
}

.hero__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-md);
	max-width: 620px;
}

.hero__title {
	font-size: var(--fs-h1);
}

.hero__title em {
	font-style: italic;
	color: var(--color-green-700);
}

.hero__text {
	font-size: var(--fs-lead);
	color: var(--color-text-muted);
	max-width: 46ch;
}

.hero__stats {
	display: flex;
	gap: var(--space-lg);
	margin-top: var(--space-sm);
	padding-top: var(--space-md);
	border-top: 1px solid var(--color-cream-line);
	width: 100%;
}

.hero__stat strong {
	display: block;
	font-family: var(--font-display);
	font-size: 1.6rem;
	color: var(--color-green-800);
}

.hero__stat span {
	font-size: 0.8rem;
	color: var(--color-text-muted);
}

/* ---------- Hero visual / composition ---------- */
.hero__visual {
	position: relative;
	height: 100%;
	min-height: 460px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.hero__plate {
	position: relative;
	width: min(560px, 90%);
	aspect-ratio: 1 / 1;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
}

.hero__plate::before {
	content: '';
	position: absolute;
	/* ETAP 18E (§6) — "za pizzą trochę bardziej miękkie światło": inset
	   poszerzony i filter blur znacznie zwiększony (4px→22px), żeby ten
	   sam, niezmieniony w kolorze conic-gradient rozlewał się jako
	   miękka, rozmyta poświata zamiast wyraźnego pierścienia z ostrą
	   krawędzią — mocniejszy blur realnie OBNIŻA lokalny kontrast (nie
	   podnosi go), więc spełnia "nie zwiększaj kontrastu". Opacity lekko
	   obniżone, żeby szerszy/bardziej rozmyty obszar nie zrobił się
	   wizualnie bardziej dominujący niż poprzednio. */
	inset: -12%;
	border-radius: 50%;
	background: conic-gradient(from 180deg, var(--color-green-600), var(--color-gold) 35%, var(--color-green-700) 70%, var(--color-green-600));
	opacity: 0.15;
	filter: blur(22px);
}

.hero__plate-img {
	position: relative;
	/* ETAP 18E (§4) — "powiększ pizzę delikatnie, około 5%": 84%→88.2%
	   (dokładnie ×1.05) względem NIEZMIENIONEGO .hero__plate — sama
	   pizza rośnie, reszta geometrii (kolumna/kontener/promień orbity)
	   liczona względem .hero__plate zostaje nietknięta.
	   ETAP 18F (§2) — "powiększ jeszcze o około 5%, nie zmieniaj kadru/
	   zdjęcia": kolejne ×1.05 na TEJ SAMEJ, niezmienionej podstawie
	   (88.2%→92.61%) — łącznie ok. +10,25% względem stanu z ETAPU 18D,
	   ten sam plik zdjęcia (rucola1.png), ten sam object-fit:cover +
	   object-position:center (kadr/przycięcie nietknięte). */
	width: 92.61%;
	aspect-ratio: 1 / 1;
	border-radius: 50%;
	overflow: hidden;
	/* ETAP 18D (§4) — zielony okrąg (.hero__plate::before, konicowy
	   gradient poniżej) nietknięty; dodana wyłącznie druga, bardzo
	   subtelna warstwa cienia pod samą pizzą (miękka, przesunięta w dół
	   plama), obok już istniejącego --shadow-lg wokół krawędzi zdjęcia. */
	box-shadow: var(--shadow-lg), 0 34px 44px -24px rgba(18, 35, 26, 0.28);
	background: var(--color-green-900);
	/* ETAP 26 — usunięty ciągły obrót (dawne tivoli-plate-spin, ETAP 18D
	   §3/18E §8): na wyraźną prośbę użytkownika hero nie ma już żadnej
	   niekończącej się animacji na samym zdjęciu. Zastąpione mysz-
	   -parallax (assets/js/hero-parallax.js, hook: [data-hero-parallax-
	   target] ustawiony w hero.php) — subtelny transform: translate3d(),
	   wyłącznie w reakcji na realny ruch myszką, zerowy ruch w spoczynku.
	   Skrolowy parallax na .hero__plate (data-parallax wyżej) zostaje
	   bez zmian — inny element, inna właściwość, brak konfliktu.
	   Transition (nie JS-owy lerp) odpowiada za płynność — JS tylko
	   ustawia docelową wartość transform przy każdym mousemove
	   (ograniczonym do jednej aktualizacji na klatkę przez rAF),
	   przeglądarka sama animuje dojście do niej. */
	transition: transform 0.5s var(--ease-out);
}

.hero__plate-img img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* ETAP 18D (§2) — jawne, "idealne" wycentrowanie zdjęcia Rucola. */
	object-position: center;
}

/* ETAP 18D (§9) — cienki tor orbity za pizzą, sama ścieżka po której
   "poruszają się" dymki (wizualnie — same dymki liczą pozycję niezależnie,
   patrz .hero__orbit niżej). Statyczny, nigdy się nie animuje.
   ETAP 18E (§5) — "obecny pierścień zbyt płaski, dodaj bardzo delikatny
   gradient, nie zmieniaj kolorystyki": płaski `border: 1px solid` nie da
   się pogodzić z gradientem na okręgu (border-image nie działa poprawnie
   z border-radius w żadnej przeglądarce) — zastąpiony cienkim "pierścieniem"
   zbudowanym z radial-gradient (przezroczysty środek → wąski pas koloru →
   przezroczystość na zewnątrz), z DELIKATNYM przejściem tonalnym w obrębie
   tego samego pasa (--color-green-700 wewnątrz → --color-green-600 na
   zewnątrz) — dokładnie te same dwa, już istniejące w palecie odcienie
   zieleni, żadnego nowego koloru. Ta sama grubość (~1.5px) i opacity co
   poprzednio (0.2→0.22, praktycznie bez zmiany wagi wizualnej). */
.hero__orbit-track {
	position: absolute;
	inset: -9%;
	border-radius: 50%;
	/* KRYTYCZNA POPRAWKA (adwersarialny review): bez jawnego <size>
	   `radial-gradient(circle, ...)` domyślnie liczy 100% jako
	   farthest-corner (środek→róg kwadratowego pudełka), a NIE
	   closest-side (środek→krawędź — dokładnie tam, gdzie border-radius:
	   50% wycina widoczny okrąg). Bez `closest-side` pas koloru lądował
	   na promieniu MNIEJSZYM niż promień samego zdjęcia pizzy — cały,
	   poprawny kolorystycznie gradient renderował się, ale był w 100%
	   niewidoczny, całkowicie zasłonięty przez nieprzezroczystą pizzę na
	   KAŻDEJ szerokości ekranu. `circle closest-side` sprawia, że 50% w
	   stopach poniżej odpowiada dokładnie widocznej krawędzi okręgu —
	   tam, gdzie wcześniej (ETAP 18D) był `border: 1px solid`. */
	background: radial-gradient(
		circle closest-side,
		transparent calc(50% - 1.5px),
		var(--color-green-700) calc(50% - 1.5px),
		var(--color-green-600) 50%,
		transparent calc(50% + 1.5px)
	);
	opacity: 0.22;
	pointer-events: none;
}

/* ---------- ETAP 18D — orbitujące dymki ---------- */
.hero__orbit {
	/* Promień w formie clamp() (nie stały px) — poprawka po adwersarialnym
	   review: sztywne 300px na desktopie geometrycznie nachodziło na kolumnę
	   .hero__content na wielu szerokościach (kolumna .hero__visual ma tu
	   realnie ok. 209–276px promienia, licząc od środka płytki, w zależności
	   od szerokości viewportu — wyliczone z --container-max/--container-pad/
	   proporcji kolumn grid 1.05fr/.95fr/gap w base.css i sections.css), a
	   promień płytki/zdjęcia pizzy (po jej capie 560px) to ok. 208px — więc
	   jeden sztywny promień nie mógł jednocześnie mieścić się w kolumnie I
	   omijać zdjęcia.

	   ETAP 18E — przeliczone ponownie, DWUKROTNIE: pizza +5% (§4) i dymek
	   +15% (§1) zawęziły margines, a pierwsza wersja poprawki
	   (clamp(172px,17vw,234px)) okazała się mieć BŁĄD w arytmetyce
	   weryfikującej — budżet kolumny liczony był jako PEŁNA szerokość
	   .hero__visual zamiast jej POŁOWY (odległość od wyśrodkowanej
	   płytki do własnej krawędzi kolumny), co sztucznie zawyżało margines
	   ~2×. Błąd znaleziony i naprawiony adwersarialnym review (niezależne
	   przeliczenie + pomiar realnej szerokości fontu Inter 600 przez
	   opentype.js) — z poprawną arytmetyką dymek na kącie 180° realnie
	   nachodził na kolumnę .hero__content na całym zakresie 1025–1920px.
	   Naprawione dwuwarstwowo: (1) poprawiony promień clamp(150px,14vw,
	   205px), przeliczony z POPRAWNYM wzorem budżetu (połowa szerokości
	   kolumny .hero__visual + gap) i ostrożnym współczynnikiem szerokości
	   znaku (0.58em/znak, powyżej zmierzonej wartości ~0.544 dla Inter
	   600); (2) kolejność haseł w hero.php przeprojektowana tak, by
	   NAJDŁUŻSZY tekst nie trafiał na najgorszy kąt (0°/180°, pełny
	   poziomy zasięg) — patrz komentarz przy $tivoli_hero_bubbles. Oba
	   razem dają realny, dodatni margines (rząd 24–56px) na całym
	   zakresie 1025–1920px, zweryfikowane skryptowo, bez żywego renderu —
	   patrz raport po pełne liczby.

	   ETAP 18F (§1) — "powiększ promień orbity o około 30px, odsuń
	   wszystkie dymki od pizzy". PIERWSZA wersja tej poprawki
	   (clamp(174px,calc(20vw - 37px),235px)) była oparta WYŁĄCZNIE na
	   budżecie w stronę .hero__content (lewo) — dokładnie tej samej
	   metodzie, której używały WSZYSTKIE poprzednie etapy (18D/18E).
	   Adwersarialny review (z żywym renderem w przeglądarce, nie tylko
	   arytmetyką) znalazł KRYTYCZNY, nigdy wcześniej nie audytowany
	   błąd: na desktopie (>1024px, układ DWUKOLUMNOWY) płytka jest
	   wyśrodkowana WEWNĄTRZ kolumny .hero__visual, a nie względem
	   viewportu — więc odległość od środka płytki do PRAWEJ, zewnętrznej
	   krawędzi viewportu (przez którą wychodzi dymek na kącie 0°) jest
	   INNA (i w praktyce MNIEJSZA w zakresie ok. 1025-1180px, bo
	   container-pad tam jest mniejszy niż gap 64px) niż odległość do
	   lewej krawędzi kolumny tekstowej — dwa OSOBNE budżety, nigdy
	   wcześniej policzone niezależnie. Skutek: dymek "Autorskie sosy"
	   (kąt 0°, najkrótszy tekst, celowo tam umieszczony w 18E) realnie
	   wychodził poza prawą krawędź viewportu w tym zakresie szerokości.
	   Naprawione przeliczeniem OBU budżetów niezależnie (lewego i
	   prawego, każdego z WŁASNYM najgorszym widocznym tekstem — 16 znaków
	   "Własne receptury" po lewej/180°, 14 znaków "Autorskie sosy" po
	   prawej/0°) i wzięciem minimum z obu na każdej szerokości —
	   clamp(153px,calc(26vw - 113px),234px). Wzrost względem 18E jest
	   teraz NIEJEDNOLITY (co jest matematycznie uczciwe, nie arbitralne):
	   zaledwie +3,5px dokładnie przy 1025px (najciaśniejszy punkt całego
	   systemu, oba budżety niemal się stykają), rosnąco do +45,4px przy
	   1320px, osiadając na ok. +29–30px na najpowszechniejszych
	   szerokościach desktop (1440–1920px) — "o około 30px" z briefu
	   (furtka) zastosowane świadomie tam, gdzie prawa krawędź faktycznie
	   na to nie pozwala. Zweryfikowane niezależnie DWOMA metodami
	   (arytmetyka + żywy render, z korektą na artefakt tego środowiska
	   testowego, gdzie zawieszona karta nigdy nie kończy wejściowej
	   animacji GSAP i tymczasowo pomniejsza całą orbitę o transform:
	   scale(0.92) — poprawka do pomiaru, nie do kodu, patrz raport). */
	--orbit-radius: clamp(153px, calc(26vw - 113px), 234px);
	position: absolute;
	inset: 0;
	pointer-events: none;
	/* Cały pierścień kręci się jednym kawałkiem — każdy dymek jest jego
	   dzieckiem, więc "dziedziczy" ten obrót do przemieszczania pozycji;
	   wyprostowanie treści dzieje się niżej, w .hero__orbit-counter.
	   animation-fill-mode:both — patrz komentarz przy .hero__bubble niżej.
	   ETAP 18E (§8) — czas wydłużony 35s→50s (żądany zakres 45–55s).
	   MUSI pozostać identyczny z .hero__orbit-counter poniżej — inaczej
	   kontr-obrót przestaje dokładnie znosić obrót tego elementu i tekst
	   dymków zacznie się z czasem przekrzywiać (naruszenie brief §8). */
	animation: tivoli-orbit-spin 50s linear infinite;
	animation-fill-mode: both;
}

.hero__orbit-item {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 0;
	height: 0;
	/* Stałe rozmieszczenie na okręgu pod kątem --angle (0/60/120°...),
	   ustawianym inline per-dymek w hero.php. */
	transform: rotate(var(--angle)) translateX(var(--orbit-radius));
}

.hero__orbit-counter {
	/* Animowany kontr-obrót TEJ SAMEJ długości i prędkości co .hero__orbit
	   (50s/linear/infinite, ETAP 18E — patrz komentarz wyżej dlaczego to
	   MUSI zostać zsynchronizowane), startujący od -1×--angle — matematycznie
	   znosi zarówno statyczny kąt rozmieszczenia, jak i narastający w
	   czasie obrót rodzica, więc treść wewnątrz NIGDY nie jest przekrzywiona
	   (brief §8), mimo że sama pozycja realnie krąży wokół pizzy (§7).
	   animation-fill-mode:both — patrz komentarz przy .hero__bubble niżej. */
	animation: tivoli-orbit-counter 50s linear infinite;
	animation-fill-mode: both;
}

.hero__bubble {
	display: inline-flex;
	align-items: center;
	/* ETAP 18E (§1) — "cały bubble o około 15% większy": gap/padding/
	   font-size wszystkie skalowane ×1.15 względem wartości z ETAPU 18D
	   (0.45em→0.52em, 0.55/0.95em→0.63/1.09em, 0.82rem→0.94rem). */
	gap: 0.52em;
	padding: 0.63em 1.09em;
	border-radius: var(--radius-full);
	background: var(--color-white);
	/* ETAP 18E (§7) — cień zmniejszony (był --shadow-md, 12px offset/32px
	   blur/0.12 opacity — "trochę zbyt AI"), zastąpiony ciaśniejszym,
	   bardziej eleganckim cieniem + cienką jasną ramką dla delikatnej
	   definicji krawędzi na jasnym tle. border-radius zostaje przy
	   --radius-full (999px) — to już maksymalne zaokrąglenie dla kapsuły
	   tej wysokości, nie ma tu wizualnie dokąd "jeszcze bardziej zaokrąglić". */
	box-shadow: 0 6px 16px rgba(18, 35, 26, 0.10);
	border: 1px solid var(--color-cream-line);
	font-size: 0.94rem;
	font-weight: 600;
	color: var(--color-ink);
	white-space: nowrap;
	/* ETAP 18D (§10) — bardzo delikatne unoszenie góra-dół, niezależne od
	   obrotu/kontr-obrotu powyżej (osobny element = zero konfliktu
	   właściwości transform), z różnym opóźnieniem per dymek. Stałe
	   wyśrodkowanie względem punktu zakotwiczenia (rodzic ma
	   width:0/height:0) jest częścią samych klatek kluczowych poniżej,
	   bo animowany `transform` całkowicie zastępuje statyczną wartość.

	   animation-fill-mode:both — POPRAWKA znaleziona adwersarialnym
	   review po pierwszej wersji tego etapu, dotyczy WSZYSTKICH 4 nowych
	   animacji (tivoli-plate-spin/-orbit-spin/-orbit-counter/-bubble-float).
	   Domyślne animation-fill-mode to "none": (a) PRZED startem animacji
	   (w oknie animation-delay, dotyczy tylko tego elementu) transform
	   wraca do wartości spoza animacji — tu: brak (bo transform istnieje
	   WYŁĄCZNIE w @keyframes) — bez "backwards" dymek renderowałby się
	   nie wyśrodkowany i SKAKAŁ do właściwej pozycji dopiero po upływie
	   opóźnienia (naruszenie §7 "nigdy nie skaczą"); (b) PO zakończeniu
	   animacji ten sam mechanizm ujawnia się przy prefers-reduced-motion
	   (który wymusza animation-iteration-count:1, patrz animations.css) —
	   bez "forwards" transform wraca do braku zamiast do klatki końcowej,
	   co dla tivoli-orbit-counter cofa skompensowanie kąta --angle
	   (przekrzywiony tekst, naruszenie §8) i dla tego elementu usuwa
	   wypieczone centrowanie -50%/-50% (naruszenie §12). "both" pokrywa
	   oba przypadki naraz. Dla tivoli-plate-spin/-orbit-spin brak
	   fill-mode nie był widoczny (obrót 0deg≡360deg≡brak obrotu), ale
	   dodany też tam dla spójności/defense-in-depth.

	   ETAP 18E (§8) — "każdy bubble ma delikatnie oddychać (scale
	   1→1.03→1) bez przesuwania": DRUGA, w pełni niezależna animacja
	   (tivoli-bubble-breathe, animations.css) na oddzielnej, samodzielnej
	   właściwości CSS `scale` (NIE `transform: scale(...)`) — `scale` to
	   osobna właściwość transformująca, którą przeglądarka składa razem
	   z `transform` (translate × rotate × scale × transform, ten sam
	   punkt transform-origin), więc nie koliduje z istniejącym
	   `transform: translate3d(...)` w tivoli-bubble-float ani z niczym
	   powyżej w łańcuchu orbitu/kontr-obrotu — "float" i "breathe" są
	   całkowicie osobnymi animacjami na osobnych właściwościach, zero
	   ryzyka dla już naprawionego (patrz wyżej) wycentrowania/kontr-obrotu. */
	animation: tivoli-bubble-float 4s ease-in-out infinite, tivoli-bubble-breathe 5s ease-in-out infinite;
	animation-delay: var(--float-delay, 0s);
	animation-fill-mode: both;
}

.hero__bubble-icon {
	/* ETAP 18E (§2) — emoji zastąpione jednokolorowymi ikonami SVG
	   (hero.php), kolorowane tu przez currentColor. Rozmiar w em, żeby
	   skalował się razem z font-size dymka na każdym breakpoincie, tak
	   jak wcześniej emoji (font-size:1.05em) — bez osobnych override'ów
	   per-breakpoint. */
	display: inline-flex;
	width: 1.15em;
	height: 1.15em;
	color: var(--color-green-700);
	flex-shrink: 0;
	line-height: 1;
}

.hero__bubble-icon svg {
	width: 100%;
	height: 100%;
	display: block;
}

.hero__scroll-cue {
	position: absolute;
	bottom: var(--space-lg);
	left: 50%;
	display: none;
	align-items: center;
	flex-direction: column;
	gap: 0.5em;
	font-size: 0.7rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-text-muted);
}

@media (min-width: 1025px) {
	.hero__scroll-cue {
		display: flex;
	}
}

.hero__scroll-cue::after {
	content: '';
	width: 1px;
	height: 34px;
	background: linear-gradient(var(--color-text-muted), transparent);
}

/* ETAP 18D (§13) — "Tablet: ta sama orbita tylko mniejsza" — sam
   promień (i lekko rozmiar dymków) w dół, wciąż 6 dymków, ten sam
   punkt załamania co reszta hero (kolumna zwija się tu również).
   Promień POPRAWIONY po adwersarialnym review: sztywne 230px było
   MNIEJSZE niż promień samej pizzy po jej capie 560px (~235px) na tym
   breakpoincie (hero już jednokolumnowy tutaj, więc pizza może
   osiągnąć swój pełny cap na większości szerokości tabletowych) —
   dymki lądowały na/wewnątrz zdjęcia zamiast wokół niego. clamp()
   przeliczony tak, by zostawić margines nad promieniem zdjęcia na
   typowych szerokościach tabletowych (768–1024px) — arytmetyka CSS,
   bez żywego renderu, patrz raport.
   ETAP 18E — przeliczone ponownie (ten sam poprawiony wzór budżetu i
   ostrożniejszy współczynnik znaku co przy bazowym .hero__orbit wyżej —
   patrz ten komentarz po pełne wyjaśnienie znalezionego błędu
   arytmetyki). clamp(190px,24vw,245px): margines pozostaje bardzo
   komfortowy (rząd 35–160px) na całym zakresie 726–1024px, zweryfikowane
   skryptowo. Rozmiar dymka podniesiony o pełne ~15% — margines starcza
   z zapasem nawet po tym wzroście.

   ETAP 18F (§1) — promień +30px tam gdzie budżet pozwala (jest tu dużo
   miejsca, patrz margines wyżej), clamp(220px,26vw,275px) — wzrost
   +18 do +30px w całym zakresie, zweryfikowane skryptowo, ORAZ przy
   okazji clamp dobrany tak, by przejście na granicy 725/726px było
   niemal niezauważalne (~9,8px skoku, dużo mniej niż poprzednie ~32,5px
   — poprawka mikro-niespójności z §6/adwersarialnego review 18E). */
@media (max-width: 1024px) {
	.hero__orbit {
		--orbit-radius: clamp(220px, 26vw, 275px);
	}

	.hero__bubble {
		font-size: 0.9rem;
		padding: 0.58em 0.98em;
	}
}

/* ETAP 18E — DODATKOWY, WĄSKI breakpoint wewnątrz zakresu tabletowego,
   znaleziony podczas przeliczania geometrii pod większe rozmiary z tego
   etapu (skryptowa weryfikacja, nie ręczna arytmetyka — patrz raport).
   Powyższy clamp(250px,28vw,278px) ma dolną granicę (250px) bezpieczną
   dopiero od ok. 726px w górę — poniżej tego punktu (typowy zakres
   561–725px: małe tablety w pionie, telefony w poziomie) budżet do
   krawędzi viewportu (brak sąsiedniej kolumny na tym breakpoincie —
   hero jest już jednokolumnowy) jest węższy niż 250px + połowa
   szerokości (teraz większego, +15%) dymka — bez tej poprawki dymek
   fizycznie wychodziłby poza ekran w tym wąskim zakresie. Ten blok,
   będąc PO powyższym w pliku, poprawnie go nadpisuje dla ≤725px (ta sama
   reguła ".hero__orbit", więc o kolejności w kaskadzie decyduje wyłącznie
   kolejność w pliku). Kosztem jest orbita bliżej/lekko nachodząca na
   zdjęcie pizzy w tym wąskim zakresie zamiast czysto go omijać — dokładnie
   ten sam, świadomy priorytet "nigdy poza viewport" > "nigdy na zdjęciu",
   już ustalony dla telefonu w ETAPIE 18D/18E niżej.
   Wartości PRZELICZONE PONOWNIE po adwersarialnym review (ten sam
   poprawiony wzór budżetu co w bazowym .hero__orbit) — clamp(140px,
   25vw,190px), margines rzędu 48–89px na całym zakresie 561–725px,
   ORAZ dodatkowo dobrany tak, żeby przejście na granicy 725/726px było
   znacznie łagodniejsze niż w pierwszej wersji tej poprawki (skok
   ~8.7px zamiast poprzednich ~32.5px) — druga, osobna nieciągłość
   znaleziona przez ten sam review.

   ETAP 18F (§1) — promień +30px: clamp(170px,29vw,220px), wzrost +24 do
   +29,8px w całym zakresie 561–725px, zweryfikowane skryptowo. Dobrany
   też pod kątem przejścia na granicy 725/726px (patrz komentarz przy
   sąsiednim breakpoincie ≤1024px) — skok zredukowany do ~9,8px. */
@media (max-width: 725px) {
	.hero__orbit {
		--orbit-radius: clamp(170px, 29vw, 220px);
	}
}

/* ETAP 18D (§13) — "Telefon: 4 dymki", nigdy poza ekran. Promień
   dalej w dół, 2 z 6 dymków ukryte (3. i 6.), pozostałe 4 przełożone
   na równe 90° rozstawienie (0/90/180/270) zamiast oryginalnych
   0/60/180/240 — nadal telefon współdzieli punkt załamania 560px z
   resztą hero (patrz .hero__stats niżej, bez zmian).
   Promień i rozmiar dymka POPRAWIONE po adwersarialnym review: przy
   oryginalnym 155px + ówczesnym rozmiarze dymka, dymki poziome
   (--angle 0°/180°) matematycznie wychodziły poza krawędź viewportu
   na KAŻDEJ typowej szerokości telefonu (360–414px) — .hero nie ma
   poziomego paddingu, więc jego overflow:hidden tnie dokładnie na
   krawędzi ekranu, a budżet od środka płytki do tej krawędzi to
   viewport/2. 100px + zmniejszony dymek niżej zostawiają realny,
   policzony margines na 360–560px (oparte na arytmetyce CSS, bez
   żywego renderu — patrz raport; skrajnie małe, dziś rzadkie
   szerokości <340px mogą mieć mniejszy margines).

   ETAP 18E — PIERWSZA wersja tej poprawki (promień 100px→85px + tylko
   ~7% wzrostu dymka, nie pełne 15%) była jawnie odnotowanym kompromisem
   wymuszonym błędną wtedy oceną budżetu. Adwersarialny review znalazł
   błąd w arytmetyce weryfikującej (patrz obszerny komentarz przy
   bazowym .hero__orbit wyżej) — po jego naprawie ORAZ po przeprojektowaniu
   kolejności haseł (najdłuższy tekst "Wypiekane na miejscu" przeniesiony
   z kąta 0° na kąt 60°/90° — patrz komentarz przy $tivoli_hero_bubbles w
   hero.php — kąty 0°/180°, jedyne które NIE zmieniają się na telefonie,
   dostały dwa najkrótsze teksty) margines wychodzi dodatni z zapasem na
   całym zakresie 320–560px NAWET przy pełnym +15% z §1 — kompromis z
   pierwszej wersji już niepotrzebny, dymek na telefonie przywrócony do
   pełnego wzrostu (patrz .hero__bubble niżej), promień clamp(75px,
   22vw,130px).

   ETAP 18F (§1) — promień +30px: telefon to najciaśniejsza strefa w
   całym systemie (patrz historia wyżej), więc pełne +30px nie mieści
   się bezpiecznie na całym zakresie — clamp(91px,27vw,155px)
   przeliczony skryptowo daje wzrost +16 do +28px (nigdy mniej), z
   marginesem dodatnim na każdej sprawdzonej szerokości 320–560px —
   "o około 30px" w brifie (furtka) zastosowane świadomie, priorytet
   dla twardego wymogu "nigdy poza viewport" ustalonego jeszcze w 18D. */
@media (max-width: 560px) {
	.hero__orbit {
		--orbit-radius: clamp(91px, 27vw, 155px);
	}

	.hero__orbit-item:nth-child(3),
	.hero__orbit-item:nth-child(6) {
		display: none;
	}

	/* !important konieczny: --angle jest ustawiany inline (style="--angle:...")
	   per-dymek w hero.php, a inline style zawsze wygrywa z regułą w
	   zewnętrznym arkuszu niezależnie od specyficzności selektora — bez
	   !important ta reguła by się nigdy nie zastosowała, a --angle jest
	   dziedziczone też przez kontr-rotację (tivoli-orbit-counter), więc
	   niespójność dałaby WIDOCZNIE PRZEKRZYWIONY tekst (naruszenie §8). */
	.hero__orbit-item:nth-child(2) {
		--angle: 90deg !important;
	}

	.hero__orbit-item:nth-child(5) {
		--angle: 270deg !important;
	}

	.hero__bubble {
		font-size: 0.71rem;
		padding: 0.4em 0.63em;
		gap: 0.32em;
	}

	.hero__stats {
		flex-wrap: wrap;
		row-gap: var(--space-sm);
	}
}

/* ---------- BENEFITS TICKER (ETAP 26) ---------- */
.benefits-ticker {
	overflow: hidden;
	background: var(--color-cream-deep);
	border-block: 1px solid var(--color-cream-line);
	padding-block: var(--space-sm);
}

.benefits-ticker__track {
	display: flex;
	width: max-content;
	animation: tivoli-ticker-scroll 42s linear infinite;
}

.benefits-ticker__group {
	display: flex;
	align-items: center;
	flex-shrink: 0;
}

.benefits-ticker__item {
	display: inline-flex;
	align-items: center;
	gap: 0.55em;
	padding-inline: var(--space-md);
	font-size: 0.9rem;
	font-weight: 600;
	color: var(--color-green-800);
	white-space: nowrap;
}

.benefits-ticker__item svg {
	width: 1.15em;
	height: 1.15em;
	color: var(--color-gold-deep);
	flex-shrink: 0;
}

.benefits-ticker__sep {
	color: var(--color-gold);
	opacity: 0.6;
}

@media (max-width: 560px) {
	.benefits-ticker__item {
		font-size: 0.82rem;
		padding-inline: var(--space-sm);
	}
}

/* ---------- PROMO TEASER (between hero and next section) ---------- */
.promo-teaser {
	display: flex;
	justify-content: center;
	padding-block: var(--space-lg);
	background: var(--color-cream);
}

.promo-teaser__inner {
	display: flex;
	justify-content: center;
}

.promo-teaser__pill {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2xs);
	padding: 0.55em 1.1em 0.55em 0.55em;
	border-radius: var(--radius-full);
	background: var(--color-cream-soft);
	border: 1px solid var(--color-cream-line);
	box-shadow: var(--shadow-sm);
	color: var(--color-green-800);
	font-size: 0.88rem;
	font-weight: 600;
	transition: box-shadow var(--dur-base) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

.promo-teaser__pill:hover {
	box-shadow: var(--shadow-md);
	transform: translateY(-1px);
}

.promo-teaser__pill:focus-visible {
	outline: none;
	box-shadow: 0 0 0 3px var(--color-cream), 0 0 0 6px var(--color-gold-deep);
}

.promo-teaser__icon {
	width: 2.1rem;
	height: 2.1rem;
	background: var(--color-gold-soft);
	color: var(--color-gold-deep);
}

.promo-teaser__icon svg {
	width: 1.1rem;
	height: 1.1rem;
}

.promo-teaser__arrow {
	width: 1em;
	height: 1em;
	opacity: 0.6;
	transition: transform var(--dur-fast) var(--ease-out);
}

.promo-teaser__pill:hover .promo-teaser__arrow {
	transform: translateX(3px);
}

/* ---------- FEATURED PIZZAS ---------- */
.featured .grid--3 {
	align-items: stretch;
}

/* Controlled 2-line heading on desktop ("Nasze najczęściej" /
   "zamawiane pizze") instead of leaving the browser to wrap it
   wherever it happens to run out of room. Left to wrap naturally on
   tablet/mobile, where the narrower column makes that unnecessary. */
.featured__title-break {
	display: none;
}

@media (min-width: 1024px) {
	.featured__title-break {
		display: block;
	}
}

/* ---------- ABOUT / STORYTELLING ---------- */
.about__inner {
	display: grid;
	grid-template-columns: 0.95fr 1.05fr;
	gap: var(--space-2xl);
	align-items: center;
}

@media (max-width: 1024px) {
	.about__inner {
		grid-template-columns: 1fr;
	}
}

.about__media {
	position: relative;
}

.about__media-frame {
	position: relative;
	border-radius: var(--radius-lg);
	overflow: hidden;
	aspect-ratio: 4 / 5;
	box-shadow: var(--shadow-lg);
}

.about__media-frame img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.about__media-tag {
	position: absolute;
	bottom: var(--space-md);
	left: var(--space-md);
	right: var(--space-md);
	padding: var(--space-sm) var(--space-md);
	border-radius: var(--radius-md);
	background: rgba(18, 35, 26, 0.55);
	backdrop-filter: blur(10px);
	color: var(--color-cream);
	display: flex;
	align-items: center;
	gap: var(--space-sm);
	font-size: 0.85rem;
}

.about__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-md);
}

.about__content p {
	color: var(--color-text-muted);
}

.about__facts {
	display: grid;
	grid-template-columns: repeat(3, auto);
	gap: var(--space-lg);
	margin-top: var(--space-sm);
}

@media (max-width: 560px) {
	.about__facts {
		grid-template-columns: 1fr 1fr;
	}
}

.about__fact strong {
	display: block;
	font-family: var(--font-display);
	font-size: 1.7rem;
	color: var(--color-green-700);
}

.about__fact span {
	font-size: 0.78rem;
	color: var(--color-text-muted);
}

/* ---------- PIZZA 60 CM (ETAP 26) ---------- */
.pizza-60cm__inner {
	display: grid;
	grid-template-columns: 1.1fr 0.9fr;
	gap: var(--space-2xl);
	align-items: center;
}

@media (max-width: 1024px) {
	.pizza-60cm__inner {
		grid-template-columns: 1fr;
		text-align: center;
	}
}

.pizza-60cm__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-sm);
}

@media (max-width: 1024px) {
	.pizza-60cm__content {
		align-items: center;
	}
}

.pizza-60cm__title {
	margin: 0;
}

.pizza-60cm__price {
	font-family: var(--font-display);
	font-size: 1.15rem;
	font-weight: 600;
	color: var(--color-gold-soft);
	margin-top: var(--space-3xs);
}

.pizza-60cm__visual {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 260px;
}

.pizza-60cm__ring {
	position: relative;
	width: min(300px, 70vw);
	aspect-ratio: 1 / 1;
	border-radius: 50%;
	border: 2px solid var(--color-gold);
	background: radial-gradient(circle, rgba(199, 154, 68, 0.1), transparent 70%);
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Smaller, dashed inner ring — a wordless "this is how much bigger"
   scale cue, deliberately unlabeled so it doesn't compete with the
   Ø 60 cm text at the center. */
.pizza-60cm__ring::before {
	content: '';
	position: absolute;
	inset: 22%;
	border-radius: 50%;
	border: 1.5px dashed rgba(231, 206, 148, 0.45);
}

.pizza-60cm__diameter {
	font-family: var(--font-display);
	font-size: 1.5rem;
	font-weight: 600;
	color: var(--color-gold-soft);
	letter-spacing: 0.02em;
}

/* ---------- MOBILE STICKY CTA (ETAP 26, front page only) ---------- */
.mobile-sticky-cta {
	display: none;
}

@media (max-width: 900px) {
	.mobile-sticky-cta {
		display: block;
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 150;
		padding: var(--space-sm) var(--space-md);
		padding-bottom: calc(var(--space-sm) + env(safe-area-inset-bottom, 0px));
		background: rgba(247, 242, 228, 0.92);
		backdrop-filter: blur(10px);
		-webkit-backdrop-filter: blur(10px);
		border-top: 1px solid var(--color-cream-line);
		box-shadow: 0 -8px 24px rgba(18, 35, 26, 0.12);
		transform: translateY(120%);
		/* Fix (adversarial review): translateY alone left the "Zamów
		   online" link focusable/screen-reader-exposed while sitting
		   off-screen, before the user has scrolled past the hero —
		   position:fixed means page scroll can never bring it into a
		   focus indicator's reported position, so a keyboard/AT user
		   could land on an invisible control. visibility:hidden pulls it
		   out of the tab order and accessibility tree.
		   Deliberately INSTANT both ways (visibility left out of
		   `transition`, no delay) rather than delayed-on-the-way-out —
		   tested directly and `transition: visibility 0s linear <delay>`
		   does not reliably re-hide in every engine (a zero-DURATION
		   transition with a non-zero delay is a known, flaky pattern;
		   confirmed here in isolation, independent of this element).
		   Trade-off accepted: the link exits the tab order slightly
		   before its ~0.4s slide-down finishes, which is harmless (it's
		   already leaving the viewport) and far safer than risking it
		   getting stuck accessible after scrolling back up. */
		visibility: hidden;
		transition: transform var(--dur-base) var(--ease-out);
	}

	.mobile-sticky-cta.is-visible {
		transform: translateY(0);
		visibility: visible;
	}

	.mobile-sticky-cta .btn {
		width: 100%;
	}

	/* NOT redundant (comment corrected after adversarial review found the
	   original wrong): .mobile-sticky-cta is a DOM sibling of
	   .site-header, not a descendant (it's printed after </main>, see
	   front-page.php) — and .site-header (position:sticky + z-index)
	   establishes its own stacking context, so .site-nav's z-index:200 /
	   .nav-toggle's z-index:210 are only compared to each other INSIDE
	   that context, capped at .site-header's own z-index:100 at the
	   document root. Without this rule, this element's z-index:150 would
	   actually render ON TOP of the open drawer, not behind it. Known,
	   accepted trade-off: because `display` can't transition, closing the
	   drawer (which fades over var(--dur-base) itself) can very briefly
	   let this snap back to visible before the drawer has fully faded —
	   a narrow, cosmetic-only edge case (scrolled past hero + open/close
	   the drawer quickly), not fixed here to avoid adding JS-side timing
	   coordination for a purely cosmetic ~0.4s overlap. */
	body.nav-open .mobile-sticky-cta {
		display: none;
	}
}

/* ---------- MENU PREVIEW ---------- */
.menu-preview {
	position: relative;
	overflow: hidden;
}

.menu-preview__grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: var(--space-sm);
}

@media (max-width: 900px) {
	.menu-preview__grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

.menu-preview__item {
	position: relative;
	border-radius: var(--radius-md);
	overflow: hidden;
	aspect-ratio: 3 / 4;
	box-shadow: var(--shadow-sm);
}

.menu-preview__item img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.7s var(--ease-out);
}

.menu-preview__item:hover img {
	transform: scale(1.1);
}

.menu-preview__scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, transparent 40%, rgba(18, 35, 26, 0.85) 100%);
}

.menu-preview__label {
	position: absolute;
	left: var(--space-sm);
	bottom: var(--space-sm);
	right: var(--space-sm);
	color: var(--color-cream);
	font-family: var(--font-display);
	font-size: 1.05rem;
}

.menu-preview__cta {
	margin-top: var(--space-xl);
	display: flex;
	align-items: center;
	justify-content: center;
}

/* ---------- PROMOTIONS ---------- */
.promotions__grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--space-lg);
}

@media (max-width: 900px) {
	.promotions__grid {
		grid-template-columns: 1fr;
	}
}

/* ETAP 17B (audyt 17A, ustalenie A1) — neutralny stan sekcji Promocje
   na stronie głównej, dopóki nie ma prawdziwej promocji (patrz
   template-parts/promotions.php). */
.promotions__empty {
	text-align: center;
	margin-top: var(--space-md);
}

/* ---------- LOCATIONS ---------- */
.locations__grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--space-lg);
}

@media (max-width: 900px) {
	.locations__grid {
		grid-template-columns: 1fr;
	}
}

/* ---------- FINAL CTA ---------- */
.final-cta {
	position: relative;
	overflow: hidden;
	background: var(--color-green-900);
	color: var(--color-cream);
	border-radius: var(--radius-xl);
	margin-inline: var(--container-pad);
	padding: var(--space-2xl) var(--space-xl);
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-md);
}

@media (max-width: 640px) {
	.final-cta {
		padding: var(--space-xl) var(--space-md);
		border-radius: var(--radius-lg);
	}
}

.final-cta::before {
	content: '';
	position: absolute;
	inset: 0;
	background: radial-gradient(700px 400px at 15% 20%, rgba(199, 154, 68, 0.22), transparent 60%),
		radial-gradient(700px 400px at 85% 80%, rgba(59, 110, 69, 0.35), transparent 60%);
	pointer-events: none;
}

.final-cta__title {
	position: relative;
	font-size: var(--fs-h2);
	color: var(--color-cream);
	max-width: 18ch;
}

.final-cta__text {
	position: relative;
	max-width: 50ch;
	color: rgba(247, 242, 228, 0.78);
	font-size: var(--fs-lead);
}

.final-cta .btn-row {
	position: relative;
	justify-content: center;
	margin-top: var(--space-sm);
}

/* ---------- Generic page template (page.php / index.php) ---------- */
.page-hero {
	padding-block: var(--space-xl) var(--space-lg);
	background: linear-gradient(180deg, var(--color-cream-soft) 0%, var(--color-cream) 100%);
	text-align: center;
}

.page-hero__title {
	font-size: var(--fs-h1);
}

.page-hero .breadcrumb {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2xs);
	font-size: var(--fs-small);
	color: var(--color-text-muted);
	margin-bottom: var(--space-sm);
}

.page-hero .breadcrumb a:hover {
	color: var(--color-green-700);
}

.entry-content {
	max-width: var(--container-narrow);
	margin-inline: auto;
	display: flex;
	flex-direction: column;
	gap: var(--space-md);
}

.entry-content h2 {
	font-size: var(--fs-h3);
	margin-top: var(--space-sm);
}

.entry-content h3 {
	font-size: var(--fs-h4);
}

.entry-content p {
	color: var(--color-text-muted);
}

.entry-content ul,
.entry-content ol {
	padding-left: 1.4em;
	color: var(--color-text-muted);
}

.entry-content ul {
	list-style: disc;
}

.entry-content ol {
	list-style: decimal;
}

.entry-content a {
	color: var(--color-green-700);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.entry-content img {
	border-radius: var(--radius-md);
}

.entry-content blockquote {
	border-left: 3px solid var(--color-gold);
	padding-left: var(--space-md);
	font-family: var(--font-display);
	font-style: italic;
	font-size: var(--fs-lead);
	color: var(--color-ink);
}

/* ---------- Blog / archive cards (index.php) ---------- */
.post-list {
	display: flex;
	flex-direction: column;
	gap: var(--space-lg);
	max-width: var(--container-narrow);
	margin-inline: auto;
}

.post-card {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	padding-bottom: var(--space-lg);
	border-bottom: 1px solid var(--color-cream-line);
}

.post-card__title a {
	font-family: var(--font-display);
	font-size: var(--fs-h3);
	color: var(--color-ink);
}

.post-card__title a:hover {
	color: var(--color-green-700);
}

.post-card__meta {
	font-size: var(--fs-small);
	color: var(--color-text-muted);
}

.post-card__excerpt {
	color: var(--color-text-muted);
}

/* ---------- 404 ---------- */
.error-404 {
	min-height: 60vh;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	padding-block: var(--space-2xl);
}

.error-404__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-md);
	max-width: 520px;
}

.error-404__code {
	font-family: var(--font-display);
	font-size: clamp(4rem, 3rem + 6vw, 9rem);
	line-height: 1;
	color: var(--color-green-700);
}
