/* =========================================================================
   Perełki — aplikacja. Warstwa komponentów na tokenach Puenta.
   Reguły z design systemu: azure to nawigacja, róż tylko puenta i akcent,
   najwyżej dwa tła na ekran, layout na gap, dwa poziomy cienia, bez emoji.
   ========================================================================= */

/* Szerokość kolumny aplikacji. `none` rozpuszcza ją na całą szerokość okna —
   telefonowi to nic nie zmienia, bo tam kolumna i tak zajmowała wszystko.
   Wpisanie tu z powrotem `460px` przywraca układ telefonu na biurku: z tej
   zmiennej czytają .app, .aura, .tabbar i .sheet, więc wszystkie cztery jadą
   razem i nie ma jak ich rozjechać. */
:root { --kolumna: none; }

/* Poza kolumną aplikacji nic już nie ma — ciemne tło odcina ją na szerokim
   ekranie. Widoczne tylko przy zawężonej kolumnie; przy --kolumna: none
   .app zakrywa całe okno. */
body { background: #333; }

/* Atrybut hidden musi wygrywać z display z klas (.sheet ma display:flex). */
[hidden] { display: none !important; }

/* Bez powiększania gestem. pan-x pan-y zostawia przewijanie w obu osiach
   (poziome ma karuzela w trybie ograniczonego ruchu), a zdejmuje szczypnięcie
   i powiększenie dwuklikiem. Element formularza dalej można zbliżyć — o tym
   decyduje viewport, nie touch-action. */
html { touch-action: pan-x pan-y; }

/* Ekran Tematów nie ma czego przewijać: karuzela jest dopasowana do wolnej
   wysokości, więc przewijanie mogłoby tylko szarpnąć stroną o parę pikseli
   zaokrągleń. Klasę wstawia app.js po dopasowaniu torów — bez JS tory nie są
   przycięte, wystają poza ekran i wtedy przewijanie jest potrzebne. */
.bez-przewijania, .bez-przewijania body { overflow: hidden; }

.app {
	max-width: var(--kolumna);
	margin: 0 auto;
	min-height: 100vh;
	background: var(--bg-screen);
	padding-bottom: 108px;   /* miejsce na pasek zakładek */
}

.ic { flex: none; }

/* --- nagłówki ekranów ----------------------------------------------------- */
/* Nagłówek zostaje na górze przy przewijaniu. Sticky, nie fixed: element
   zostaje w przepływie, więc nie trzeba odsuwać treści na jego wysokość ani
   powtarzać za nim szerokości kolumny (--kolumna). Tło musi być kryjące, bo treść
   przejeżdża pod spodem. z-index niżej niż scrim (8) i arkusze (9), żeby
   otwarty arkusz przykrywał nagłówek, a nie odwrotnie. */
.head {
	position: sticky; top: 0; z-index: 6;
	display: flex;
	align-items: center;
	gap: var(--space-3);
	padding: 14px var(--screen-gutter) 10px;
	background: var(--bg-screen);
}

/* Kreska odcinająca nagłówek od treści pokazuje się dopiero po przewinięciu —
   w spoczynku ekran zostaje taki, jak go zaprojektowano. To box-shadow, a nie
   border, bo nie może zmieniać wysokości nagłówka: pod nią klei się pasek
   postępu, którego pozycję liczymy właśnie z tej wysokości. */
.head.is-scrolled:not(.head--push) { box-shadow: 0 1px 0 var(--line); }

.head--push { background: var(--surface); border-bottom: 1px solid var(--line); }

/* Logo dokładnie na środku ekranu, nie na środku miejsca, które zostało po
   strzałce powrotu — stąd trzy kolumny zamiast flexa: skrajne równe sobie,
   środkowa z treści. Trzecia zostaje pusta i to jest jej zadanie. */
.head--logo { display: grid; grid-template-columns: 1fr auto 1fr; justify-items: start; }
.head--logo .lockup { justify-self: center; }
.head__right { margin-left: auto; display: flex; align-items: center; gap: var(--space-3); }

.lockup { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--ink); }
.lockup:hover { text-decoration: none; color: var(--ink); }

/* Logo jest jednym plikiem (perła + napis), więc nie ma tu czego składać —
   wysokość ustawia całość, szerokość idzie za proporcją 1167:246. */
.lockup__logo { height: 35px; width: auto; display: block; }

.tytul { font-family: var(--font-display); font-size: 26px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.15; margin: 0; color: var(--ink); }

.streak-pill {
	display: inline-flex; align-items: center; gap: 5px;
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500;
	color: var(--rose); white-space: nowrap;
}


.avatar {
	width: 30px; height: 30px; border-radius: 50%; flex: none;
	background: var(--mist-100); border: 1px solid var(--line); display: block;
}
/* Buźki z assets/avatars są rysowane na całym kole — obrazek ma je wypełnić. */
img.avatar { object-fit: cover; }
.avatar--sm { width: 26px; height: 26px; }
/* W pasku zakładek awatar staje w rzędzie z ikonami — musi mieć ich wzrost.
   Bez obwódki: pasek i tak jest oddzielony kreską, drugie kółko tylko zaśmieca.
   Okrąglenie idzie przez mask, nie border-radius: przy takim przeskalowaniu
   w dół (70px SVG na ~20px) border-radius na <img> dawał widoczną jasną
   obwódkę na granatowym tle paska — artefakt antyaliasingu przy przycinaniu
   przeglądarki, nie coś rysowanego w kodzie. Maska tego nie robi. */
.avatar--tab {
	width: 22px; height: 22px; border: none; border-radius: 0;
	-webkit-mask-image: radial-gradient(circle closest-side, #000 99%, transparent 100%);
	mask-image: radial-gradient(circle closest-side, #000 99%, transparent 100%);
}
.avatar--lg { width: 40px; height: 40px; }
/* Na profilu buźka jest jedynym zdjęciem na ekranie i ma być pierwszą rzeczą,
   którą widać — stąd 84px, rozmiar tylko tego jednego awatara, bo --xl nie
   występuje poza ekranem Profilu. Kreska obwódki zeszła na rzecz różowej
   poświaty z .profil__glowa: na białej karcie sam włos ginie. */
.avatar--xl { width: 84px; height: 84px; border: none; }
/* Przy polu komentarza awatar stoi obok inputa — bierze jego wzrost, żeby rząd
   miał jedną linię. Bez obwódki: pole ma już własną, druga kreska obok szumi. */
.avatar--composer { width: 44px; height: 44px; border: none; }

.ikonka {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: var(--touch-min-text); min-height: var(--touch-min-text);
	/* Zerowanie paddingu: <button> ma go domyślnie, <a> nie — bez tego te same
	   ikony miały różną szerokość i rozjeżdżały się względem gutteru. */
	padding: 0; border: none; background: transparent; color: var(--azure); cursor: pointer;
	border-radius: var(--r-sm); transition: color 140ms ease;
}
.ikonka:hover { color: var(--azure-700); }

/* Ikona w delikatnym kółku — pole dotyku zostaje na linku, kółko jest mniejsze.
   Kółko jest o 10 px węższe od pola dotyku, więc bez tego wcięcia stałoby 25 px
   od krawędzi, kiedy tekst stoi na 20 px. Margines zdejmuje te 5 px różnicy:
   widoczna krawędź kółka trafia w tę samą oś co treść, a pole dotyku wchodzi
   w gutter (i tak jest przezroczyste). */
.ikonka--kolko:last-child { margin-right: -5px; }

/* To samo od lewej dla strzałki powrotu — jedyna ikona, która na tych
   ekranach stoi przed treścią. Warunek na dziecko .head, a nie zwykłe
   :first-child: ikona bywa w .head__right jedynym dzieckiem (plus na Dziś
   bez pigułki serii) i dostałaby oba wcięcia naraz. */
.head > .ikonka--kolko:first-child { margin-left: -5px; }

.ikonka--kolko .kolko {
	display: inline-flex; align-items: center; justify-content: center;
	width: 34px; height: 34px; border-radius: 50%;
	background: var(--mist-100); transition: background-color 140ms ease;
}
.ikonka--kolko:hover .kolko { background: var(--azure-soft); }

/* Kropka stanu na ikonie w kółku — mały znacznik "coś jest włączone" (filtry
   losowania). Siedzi na kółku, nie na polu dotyku, żeby nie odjechała w gutter;
   obwódka w kolorze tła wycina ją z kreski ikony, gdy się nałożą. */
.ikonka--kropka .kolko { position: relative; }
.ikonka--kropka .kolko::after {
	content: ''; position: absolute; top: 1px; right: 1px;
	width: 8px; height: 8px; border-radius: 50%;
	background: var(--rose); border: 2px solid var(--bg-screen);
}

/* --- pasek postępu czytania ----------------------------------------------- */
/* Klei się dokładnie pod nagłówkiem, więc razem z nim tworzy jeden pasek
   sterowania. Wysokość nagłówka wstawia JS (--head-h), bo różni się między
   ekranami; bez niego pasek przykleja się do zera i chowa pod nagłówkiem —
   niewidoczny, ale nic nie psuje. Stąd z-index niższy niż nagłówka. */
.progress { position: sticky; top: var(--head-h, 0px); z-index: 5; height: 3px; background: var(--mist-100); }
.progress__fill {
	display: block; height: 100%; width: 0; background: var(--azure);
	transition: width 120ms ease, background 160ms ease;
}
.progress__fill.is-done { background: var(--rose); }

/* --- sekcje --------------------------------------------------------------- */
.sekcja { padding: 18px var(--screen-gutter) 22px; }
.sekcja--alt { background: var(--bg-alt); }
.sekcja__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-2); }
.sekcja__meta { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; color: var(--ink-40); margin: 0; }

/* Nagłówek sekcji na ekranie Dziś — czytany jak tytuł, nie jak etykieta,
   więc krój display zamiast drobnego druku i bez wersalików. */
.sekcja__tytul {
	margin: 0; font-family: var(--font-display); font-size: 19px; font-weight: 700;
	letter-spacing: -0.01em; color: var(--ink);
}

.eyebrow {
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500;
	text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-40); margin: 0;
}
.eyebrow--azure { color: var(--azure); }
.eyebrow--rose { color: var(--rose-700); }

.id-zart { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; color: var(--ink-40); }
/* Numer żartu nad treścią — jedyna rzecz w tym rzędzie, którą się podaje dalej. */
.id-zart--azure { color: var(--azure); }

/* --- żart jako wiersz (ekran Dziś) ---------------------------------------- */
.wiersze { display: flex; flex-direction: column; }

.wiersz { display: flex; flex-direction: column; gap: 14px; padding: 18px 0; }
/* Kreska między żartami rysowana tokenem --line, nie tłem --mist-100:
   sekcja "Najnowsze" ma tło mist-100 i kreska w tym samym kolorze ginęła. */
.wiersz + .wiersz { border-top: 1px solid var(--line); }

.wiersz__eyebrow { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.wiersz__meta { margin-left: auto; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; color: var(--ink-60); }

.wiersz__tresc, .karta__tresc { display: block; color: inherit; }
.wiersz__tresc:hover, .karta__tresc:hover { text-decoration: none; color: inherit; }

.setup {
	margin: 0; font-family: var(--font-body); font-size: 18px; line-height: 1.5;
	color: var(--ink); white-space: pre-line; text-wrap: pretty; overflow-wrap: anywhere;
}
.setup--duzy { font-size: 18px; }

/* Żart na liście przycięty do czterech linii (patrz SKROT_LINII w helpers.php)
   i rozwijany przez <details> — stąd :has(), a nie klasa dokładana JS-em:
   ranking ma działać bez skryptu. */
.wiersz--skrot:not(:has(.wiecej[open])) .setup {
	display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}

.wiecej { margin-top: -6px; }
/* Link gaszony przez app.js, gdy przycięcie i tak nic nie zasłania. */
.wiersz.is-caly .wiecej { display: none; }
/* Klik w treść rozwija tak samo jak link, ale tylko ze skryptem — stąd .js. */
.js .wiersz--skrot:not(.is-caly) .wiersz__tresc { cursor: pointer; }
.wiecej__link {
	display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
	/* Ten sam stopień, co treść żartu (.setup) — link czyta się jak jej dalszy ciąg. */
	font-family: var(--font-body); font-size: 18px; line-height: 1.5; font-weight: 500; color: var(--azure);
	list-style: none;  /* strzałka jest nasza, nie systemowa */
}
.wiecej__link::-webkit-details-marker { display: none; }
.wiecej__link:hover { text-decoration: underline; }
.wiecej__zwin, .wiecej[open] .wiecej__pokaz { display: none; }
.wiecej[open] .wiecej__zwin { display: inline; }
.wiecej[open] .ic { transform: rotate(180deg); }

.punchline {
	margin: 10px 0 0; font-family: var(--font-display); font-size: 20px; font-weight: 600;
	line-height: 1.25; color: var(--rose-700); white-space: pre-line; text-wrap: pretty; overflow-wrap: anywhere;
}
.punchline--duza { margin-top: 16px; }

/* --- tagi pod żartem ------------------------------------------------------ */
/* Rząd tagów stoi pod treścią, nad wierszem akcji — wszędzie tak samo (Dziś,
   ranking, karty, Losuj, ekran żartu, arkusz karuzeli), stąd jedna reguła
   zamiast wariantów per ekran.
   Nie pigułki, a wyszarzony drobny druk — tag jest podpisem żartu, nie akcją do
   klikania, więc ma ważyć tyle, co data w nagłówku wiersza. Nie klika się:
   w HTML-u to <span>, żaden stan hoveru nie obiecuje przejścia. */
.tagi-rzad { display: flex; gap: var(--space-3); flex-wrap: wrap; }
.tag-meta { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; color: var(--ink-60); }

/* --- wiersz akcji --------------------------------------------------------- */
.akcje { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.chip {
	display: inline-flex; align-items: center; gap: 6px;
	border-radius: var(--r-pill); cursor: pointer;
	font-family: var(--font-body); font-size: 13px; font-weight: 500;
	transition: background-color 140ms ease, color 140ms ease, border-color 140ms ease;
}

.chip--komentarz {
	height: 32px; padding: 0 12px;
	background: transparent; border: 1px solid transparent; color: var(--ink-60);
}
.chip--komentarz:hover { background: var(--mist-50); }

.chip__count { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; }

/* --- ocena kciukiem ------------------------------------------------------- */
/* Róż zostaje przy puencie i akcentach, więc głos w górę bierze jadeit, a głos
   w dół terakotę — dwa kolory stanu, ciepły kontra zimny, więc para czyta się
   od razu jako zgoda i niezgoda. Dwa kciuki i liczba trzymają się razem jako
   jedna grupa, żeby nie czytały się jak trzy osobne akcje. */
.ocena { display: inline-flex; align-items: center; gap: 2px; }

/* Kciuki są od siebie odsunięte: to przeciwne decyzje i chybione stuknięcie
   kosztuje tu głos, a pola dotyku (32 px) i tak stykały się bokami. Odstęp
   idzie marginesem, nie gapem grupy, żeby nie odsuwał przy okazji liczby. */
.ocena__glos--dol { margin-left: 8px; }

.ocena__glos {
	display: inline-flex; align-items: center; justify-content: center;
	position: relative; width: 32px; height: 32px; padding: 0; flex: none;
	border: 1px solid transparent; border-radius: var(--r-pill);
	background: transparent; color: var(--icon-idle); cursor: pointer;
	transition: background-color 140ms ease, color 140ms ease;
}
/* Podświetlenie kciuka wygrywa z najechaniem — kolor głosu widać od razu po
   kliknięciu, bez zjeżdżania kursorem. */
.ocena__glos:hover:not(:disabled):not(.is-on) { background: var(--mist-50); color: var(--ink-60); }
/* Wybrany kciuk jest wypełniony. Tło pigułki samo stanu nie udźwignie:
   jade-soft i mist-100 są nie do odróżnienia od tła sekcji--alt (1,01:1 i
   1,00:1), więc tam pigułka znikała i zostawał wyłącznie kolor ikony. Kontur
   kontra plama to różnica kształtu, nie barwy, więc widać ją też wtedy, gdy
   koloru nie widać. Mankiet zostaje przy tym wycięty: zalana na wylot plama
   gubi dłoń i kciuk przestaje być kciukiem (dlatego ten jeden podpath ma w
   icons.php własną klasę). Kreska idzie kolorem pigułki, więc czyta się jak
   dziura — stąd tło w osobnym --pigula, żeby jedno i drugie brało go z jednego
   miejsca i nie rozjechało się przy zmianie odcienia. */
.ocena__glos--gora.is-on { --pigula: var(--jade-soft); color: var(--jade); }
.ocena__glos--dol.is-on { --pigula: var(--terra-soft); color: var(--terra); }
.ocena__glos.is-on { background: var(--pigula); }
.ocena__glos.is-on .ic { fill: currentColor; }
.ocena__glos.is-on .ic__mankiet { stroke: var(--pigula); }

/* Nowy głos w górę kwitowany garścią kropek: dwanaście sztuk po 5 px, wachlarz
   w górę na 46 px, 700 ms. Kropki są poza przepływem, więc mogą wyjść poza
   przycisk, nic nie przesuwając; animuje się sam kciuk, nie przycisk, żeby jego
   32 px zostało nietknięte i nic w rzędzie nie drgnęło. */
.ocena__glos.is-pyk .ic { animation: pyk 460ms cubic-bezier(.2,.9,.25,1); }

.konfetti { position: absolute; inset: 0; pointer-events: none; }
.konfetti i {
	position: absolute; top: 50%; left: 50%;
	width: 5px; height: 5px; border-radius: 1px;
	background: var(--jade);
	animation: konfetti 700ms cubic-bezier(.15,.75,.3,1) both;
}
/* Co trzecia w różu — kolor akcentu serwisu. */
.konfetti i:nth-child(3n) { background: var(--rose); }

@keyframes pyk { 0% { transform: scale(1); } 35% { transform: scale(1.34); } 100% { transform: scale(1); } }
@keyframes konfetti {
	0%   { opacity: 0; transform: translate(-50%, -50%) scale(.4) rotate(0deg); }
	15%  { opacity: 1; transform: translate(-50%, -50%) scale(1.1) rotate(0deg); }
	100% {
		opacity: 0;
		transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(.5) rotate(var(--obrot));
	}
}

/* Kciuki gasną tylko wtedy, gdy głos zajęła sieć — mając własny głos, można go
   przestawić, więc oba zostają czynne, a informacją jest kolor wybranego. */
.ocena__glos:disabled { cursor: default; opacity: 0.45; }

.ocena__wynik {
	margin-left: 4px; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 13px;
	font-weight: 500; color: var(--ink-60); white-space: nowrap;
}
/* Etykieta i liczba mają teraz ten sam kolor — obie muszą go mieć czytelnym —
   więc liczbę odróżnia waga. */
.ocena__liczba { color: var(--ink); font-weight: 600; }

.zakladka {
	margin-left: auto; border: none; background: transparent; cursor: pointer;
	color: var(--icon-idle); display: inline-flex; align-items: center; padding: 6px;
	transition: color 140ms ease;
}
.zakladka.is-on { color: var(--rose-700); }
.zakladka.is-on .ic { fill: currentColor; }

/* Udostępnianie stoi w parze z zakładką przy prawej krawędzi — obie akcje
   dotyczą żartu jako całości, inaczej niż kciuki i komentarze. Odstęp
   rozpychający przejmuje więc ta ikona, a zakładka staje tuż obok niej;
   tam, gdzie udostępniania nie ma, zakładka odpycha się sama, jak dotąd. */
.udostepnij {
	margin-left: auto; border: none; background: transparent; cursor: pointer;
	color: var(--icon-idle); display: inline-flex; align-items: center; padding: 6px;
	transition: color 140ms ease;
}
.akcje .udostepnij + .zakladka,
.karta__gora .udostepnij + .zakladka { margin-left: 0; }

/* Na karcie ikony siedzą w nagłówku, gdzie jest ciaśniej niż w wierszu akcji. */
.udostepnij--karta { padding: 4px; }

/* Ołówek i kosz przy dowcipie — tylko admin, patrz Auth::isAdmin() w
   _akcje.php i _zart_karta.php. Ten sam wygląd co udostępnianie/zakładka
   (przezroczyste tło, kolor bezczynnej ikony), kolor na hover mówi, co się
   stanie: azure to zwykła nawigacja (edycja), terra to coś nieodwracalnego
   (kasowanie) — ten sam podział barw co reszta panelu admina. */
.admin-ikonka {
	border: none; background: transparent; cursor: pointer;
	color: var(--icon-idle); display: inline-flex; align-items: center; padding: 6px;
	transition: color 140ms ease;
}
.admin-ikonka--edytuj:hover { color: var(--azure); }
.admin-ikonka--usun:hover { color: var(--terra); }
.admin-usun-forma { display: inline-flex; }

/* --- karta serii ---------------------------------------------------------- */
.seria-card {
	position: relative; overflow: hidden;
	/* Róż po lewej, biel po prawej. Powód jest techniczny, nie dekoracyjny:
	   film wtapia się w tło przez multiply, a multiply na różu przygniata mu
	   półcienie na brudno — na bieli daje dokładnie to, co widać w pliku.
	   Biel musi być pełna, zanim zacznie się film: ten stoi od ~76% szerokości
	   karty na telefonie i ~84% na biurku, więc przejście domyka się na 62%.
	   Karta przestaje być różową plamą, więc kształt trzyma teraz obwódka —
	   bez niej biała prawa połowa rozpływałaby się w tle ekranu (mist-50). */
	background: linear-gradient(90deg, var(--rose-soft) 0%, var(--surface) 62%);
	border: 1px solid var(--rose-100);
	/* Pion ciaśniej niż w innych kartach: wysokość robi tu film, nie tekst,
	   a pełne 18 px u góry i u dołu rozciągało pasek nagrody bardziej, niż
	   jest w nim treści. Boki zostają na wspólnym tokenie. */
	border-radius: var(--r-card); padding: 10px var(--card-padding);
	display: flex; align-items: center; gap: var(--space-3);
	animation: podnies 460ms cubic-bezier(.2,.9,.25,1) both;
}
/* Kolumna z napisami była wcześniej samą kartą — teraz stoi w niej obok filmu,
   więc gap 6px i kierunek schodzą piętro niżej, a karta układa wiersz. */
.seria-card__tresc { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }

/* Film bez media query: 20% kolumny to ~63 px na telefonie, a na biurku
   (kolumna 700 px) biłoby w 133 px — tam clamp ścina go na 92 px. Procent
   i sufit zeszły o 30% względem pierwszego podejścia: ozdoba ma stać obok
   zdania, a nie zamiast niego. Dołu pilnuje 56 px — na 320 px ekranie sam
   procent zrobiłby z filmu plamkę.
   multiply zdejmuje filmowi własne tło; stoi on na białym końcu gradientu,
   więc zlewa się z kartą bez obwódki wokół kadru. */
.seria-card__film {
	flex: none; display: block;
	width: clamp(56px, 20%, 92px); height: auto; aspect-ratio: 1;
	mix-blend-mode: multiply;
	pointer-events: none;
}
.seria-card__tekst { margin: 0; font-family: var(--font-display); font-size: 20px; font-weight: 600; color: var(--rose-700); }
.seria-card__kreska {
	height: 3px; background: var(--rose); border-radius: 2px; margin-top: 4px;
	transform: scaleX(0); transform-origin: left;
	animation: kreska 620ms ease 420ms both;
}
.seria-card::after {
	content: ""; position: absolute; inset: 0;
	background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.65) 50%, transparent 70%);
	transform: translateX(-100%);
	animation: przetarcie 900ms ease 320ms both;
}
@keyframes podnies { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes kreska { to { transform: scaleX(1); } }
@keyframes przetarcie { to { transform: translateX(100%); } }


/* --- paginacja Najnowszych -------------------------------------------------- */
/* Trzy piętra pod listą, od najczęstszego ruchu do najrzadszego: kroki, numery,
   skok wprost. Odstęp nad całością większy niż między żartami, żeby czytało się
   jako koniec listy, a nie jej kolejny wiersz — tak samo jak .dalej w rankingu. */
.pager { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); margin-top: 26px; }

.pager__kroki { display: flex; justify-content: center; gap: var(--space-2); flex-wrap: wrap; }
/* Niższe i węższe od pełnego .btn: stoją dwa obok siebie, więc mieszczą się
   w jednym rzędzie do samego 360 px. Poniżej zawijają się jeden pod drugi —
   stąd flex-wrap wyżej, zamiast ściskania napisów. */
.pager__krok { min-height: 44px; padding: 0 16px; font-size: 14px; }

/* Numery wyraźnie odsunięte od kroków: „poprzednia" i „następna" prowadzą
   o jeden krok, numery skaczą gdzie indziej — 48 px przerwy (12 z gap plus
   36 stąd) czyta się jako dwie osobne rzeczy, a nie jeden trzypiętrowy blok.
   Do pola „skocz do strony" zostaje bliżej, bo ono jest przedłużeniem
   numerów: tym samym ruchem, tylko dalej niż widać na pasku. */
.pager__numery {
	display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap;
	margin-top: 36px;
}
.pager__grupa { display: inline-flex; align-items: center; gap: 6px; }
.pager__nr {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 36px; height: 36px; padding: 0 8px;
	border: 1px solid var(--line); border-radius: var(--r-input); background: var(--surface);
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 500;
	color: var(--ink-60);
}
.pager__nr:hover { text-decoration: none; border-color: var(--ink-40); }
/* Bieżąca strona nie jest linkiem — wyróżniona tak samo jak włączony chip
   filtra, żeby „tu jesteś" wyglądało w serwisie zawsze tak samo. */
.pager__nr.is-on { background: var(--ink); border-color: var(--ink); color: #fff; }
.pager__przerwa { color: var(--ink-40); font-family: var(--font-meta); font-size: 14px; padding: 0 2px; }

.pager__skok { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; justify-content: center; }
.pager__skok-etykieta {
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500;
	text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-40);
}
/* Szerokość na cztery cyfry — tyle wystarczy na całą bazę, a szersze pole
   sugerowałoby, że wpisuje się tu coś więcej niż numer. */
.pager__skok-pole { width: 84px; height: 40px; text-align: center; font-variant-numeric: tabular-nums; }
.pager__skok-ok { min-height: 40px; padding: 0 14px; font-size: 14px; }


/* --- karty ---------------------------------------------------------------- */
.karty { display: flex; flex-direction: column; gap: var(--feed-gap); }

.karta {
	background: var(--surface); border-radius: var(--r-card); box-shadow: var(--elev-1);
	padding: var(--card-padding); display: flex; flex-direction: column; gap: var(--space-3);
}
.karta__gora { display: flex; align-items: center; }
.karta__gora .zakladka { margin-left: auto; }
.karta__tresc.is-clamped { max-height: 79px; overflow: hidden; }
.dalej { font-family: var(--font-display); font-size: 14px; font-weight: 600; color: var(--azure); }

/* --- przyciski ------------------------------------------------------------ */
.btn {
	display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
	min-height: 50px; padding: 0 24px; border: none; cursor: pointer;
	font-family: var(--font-display); font-size: 15px; font-weight: 600; line-height: 1.2;
	border-radius: var(--r-btn); text-decoration: none;
	transition: background-color 140ms ease, color 140ms ease;
}
.btn--pelny { width: 100%; }
.btn--primary { background: var(--rose-600); color: #fff; }
.btn--primary:hover { background: var(--rose-700); color: #fff; text-decoration: none; }
.btn--outline { border: 1.5px solid var(--ink); border-radius: var(--r-btn-mirror); background: transparent; color: var(--ink); }
.btn--outline:hover { background: var(--ink); color: var(--mist-50); text-decoration: none; }

/* --- chipsy filtrów -------------------------------------------------------- */
/* Akapit wstępu nad listą tematu. Stoi między nagłówkiem a chipsami
   kolejności, więc odstęp trzyma tylko od góry — dolny daje mu .chipsy.
   Stopień i kolor jak tekst drugoplanowy: to zdanie o ekranie, nie treść,
   po którą się tu przyszło. */
.tag__wstep {
	margin: var(--space-2) 0 var(--space-4); max-width: 60ch;
	font-family: var(--font-body); font-size: 15px; line-height: 1.55;
	color: var(--ink-60); text-wrap: pretty;
}

.chipsy { display: flex; gap: var(--space-2); flex-wrap: wrap; margin: var(--space-3) 0; }
.chipsy--gutter { padding: 0 var(--screen-gutter); }

.chip--filtr {
	height: 34px; padding: 0 14px; background: var(--surface);
	border: 1px solid var(--line); color: var(--ink-60);
}
.chip--filtr.is-on { background: var(--ink); border-color: var(--ink); color: #fff; }
.chip--filtr:hover { text-decoration: none; }

/* Wybór rocznika — chip „Rok" po włączeniu staje się „Rok: 2026 ⌄" i sam jest
   rozwijaną listą: <select> leży na nim przezroczysty, na całej pigułce, więc
   dropdown rysuje system (na telefonie rodzimy picker), a wygląd zostaje nasz.
   Przycisk „Pokaż" obok żyje tylko bez JS-a — z JS-em app.js go chowa. */
.rok-pill { display: inline-flex; align-items: center; gap: var(--space-2); margin: 0; }
.rok-pill__chip { position: relative; gap: 4px; padding-right: 11px; }

.rok-pill__select {
	position: absolute; inset: 0; width: 100%; height: 100%;
	opacity: 0; cursor: pointer;
	/* Safari na iOS liczy własną minimalną wysokość pola — bez tego select
	   rozpycha pigułkę od środka. */
	appearance: none; border: 0; padding: 0; font-size: 16px;
}
.rok-pill__chip:has(.rok-pill__select:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; }

/* --- ekran żartu ----------------------------------------------------------- */
.zart { padding: 20px 22px 0; display: flex; flex-direction: column; gap: 16px; }
/* Tagi, wiersz akcji i „Następny żart" w jednym rytmie — wcześniej stały
   w gołym divie, więc pigułki kleiły się do akcji. */
.zart__stopka { display: flex; flex-direction: column; gap: 16px; }

/* --- Tematy · karuzela pigułek --------------------------------------------- */
/* Świadome odstępstwo od zasady dwóch tł na ekran: ten ekran ma być pastelową
   ścianą tematów, więc paleta jest szeroka. Zostaje lokalna — do tokenów
   Puenty nie wchodzi i poza Tematami nie obowiązuje. Każdy odcień to para:
   pastelowe tło i przyciemniony atrament z tej samej rodziny, żeby drobny
   napis na pigułce dalej dawał się przeczytać. */
.pastel-1  { --pt-bg: #FBE1EA; --pt-ink: #A8305A; }
.pastel-2  { --pt-bg: #DCEBF2; --pt-ink: #145670; }
.pastel-3  { --pt-bg: #DCF0E8; --pt-ink: #0F6B54; }
.pastel-4  { --pt-bg: #FBF0CE; --pt-ink: #7A5A0B; }
.pastel-5  { --pt-bg: #FDE4D3; --pt-ink: #9A4A18; }
.pastel-6  { --pt-bg: #EAE2F8; --pt-ink: #5B3E9E; }
.pastel-7  { --pt-bg: #D8F2EC; --pt-ink: #0E6B62; }
.pastel-8  { --pt-bg: #DCEAFB; --pt-ink: #1B4E96; }
.pastel-9  { --pt-bg: #FDDFDC; --pt-ink: #A63A2E; }
.pastel-10 { --pt-bg: #F1E7D8; --pt-ink: #75592F; }
.pastel-11 { --pt-bg: #E3EEDD; --pt-ink: #4A6A38; }
.pastel-12 { --pt-bg: #F9E3F1; --pt-ink: #8E3577; }
.pastel-13 { --pt-bg: #DDEFF3; --pt-ink: #14606E; }
.pastel-14 { --pt-bg: #ECE0EC; --pt-ink: #6E3A6B; }

/* Pas jest całą treścią ekranu i ma się w nim zmieścić bez przewijania: bierze
   dokładnie wolną wysokość — 68 px na nagłówek i 108 px na pasek zakładek
   (tyle samo, ile odsuwa .app). To wyliczenie jest przybliżeniem na pierwszą
   klatkę; dokładną wysokość i liczbę widocznych torów ustawia potem JS, bo
   nagłówek i pasek adresu przeglądarki mierzy się tylko na żywo. */
.karuzela-band {
	display: flex; flex-direction: column;
	height: calc(100vh - 176px);
	height: calc(100dvh - 176px);
	/* Bez własnego tła i kreski: pastelowe pigułki są tu jedynym kolorem, a
	   szara podkładka tylko przygaszałaby je i dzieliła ekran na dwa pasy. */
	padding: 18px 0;
	overflow: hidden;
}

.karuzela {
	flex: 1; min-height: 0;
	/* space-between: to, co zostaje po ostatnim całym torze, idzie w odstępy
	   między tory, a nie w pustkę pod nimi. gap jest wtedy minimum odstępu. */
	display: flex; flex-direction: column; justify-content: space-between;
	gap: 10px;
	/* Na telefonie pigułki urywają się na krawędzi ekranu — przy wąskim pasie
	   zanik zjadałby większość pigułek, a to i tak krawędź całego ekranu, nie
	   jakiegoś okna w środku czegoś większego. Rozpływanie ma sens dopiero na
	   biurku (@media min-width: 900px niżej w pliku), gdzie pas potrafi mieć
	   1400 px i wjeżdża/wyjeżdża z węższej kolumny ekranu. */
}

/* flex: none — tor ma trzymać wysokość swojej najwyższej pigułki. Domyślne
   flex-shrink ściskało tory, gdy było ich więcej niż wchodzi: zamiast wystawać
   (i dać się policzyć), zgniatały się i przycinały pigułki w połowie.

   Bez overflow: hidden — pigułka pod kursorem powiększa się i musi móc wyjść
   nad tor. Pas i tak nie ucieka za kolumnę: w poziomie przycina go maska
   .karuzeli, a w pionie overflow .karuzela-band (z 18 px paddingu w zapasie,
   czyli grubo więcej, niż zabiera powiększenie). */
.tor { flex: none; }

/* Oba ciągi w pasie są identyczne, więc po przesunięciu o -50% pas wraca
   dokładnie w to samo miejsce i pętla nie ma szwu. Każdy tor dostaje własne
   tempo i własną fazę (--czas, --faza z widoku) — inaczej wszystkie jechałyby
   równo jak jeden blok. */
.tor__pas {
	display: flex; width: max-content;
	animation: przesuw var(--czas, 40s) linear infinite;
	animation-delay: var(--faza, 0s);
}
@keyframes przesuw {
	from { transform: translate3d(0, 0, 0); }
	to   { transform: translate3d(-50%, 0, 0); }
}
.tor__ciag { display: flex; flex: none; gap: 9px; padding-right: 9px; }

/* Otwarty arkusz zatrzymuje karuzelę: temat, o którym się czyta, nie ma
   uciekać za krawędź ekranu. Na myszy wystarczy najechać na tor. */
.karuzela.is-pauza .tor__pas { animation-play-state: paused; }
@media (hover: hover) {
	.tor:hover .tor__pas { animation-play-state: paused; }
}

/* Pigułka jest dwuwierszowa: temat, a pod nim numer żartu i czas czytania.
   Wyrównanie w pionie idzie przez własny span na treści — align-items na samej
   pigułce liczyłby się względem dwóch wierszy naraz i przy stałej wysokości
   grupa siadałaby za wysoko. Tu pigułka centruje całą grupę, a grupa układa
   wiersze do lewej. Wysokość jest stała (nie z wierszy), żeby tor nie skakał
   od pigułki do pigułki. */
.pigula {
	flex: none; display: inline-flex; align-items: center;
	height: 56px; padding: 0 20px; border: none;
	cursor: pointer; background: var(--pt-bg); color: var(--pt-ink);
	transition: transform 140ms ease, box-shadow 140ms ease;
	/* Chybotliwy narożnik Puenty, nie pigułka — mimo nazwy. Wariant podstawowy
	   to --r-btn w skali tego pudełka (czynnik 0,65): token jest pisany pod
	   przycisk 50 px, a przy pełnych promieniach pionowe sumy sięgają samej
	   wysokości, więc na bokach nie zostaje prosty odcinek i z chybotania robi
	   się równy owal. Ścięte do ~2/3 wysokości zostawiają bok prosty i widać,
	   że każdy róg jest inny. Promienie w pikselach, nie w procentach: ten sam
	   narożnik na pigułce niskiej i wysokiej (--s … --l) daje im lekko różną
	   krzywiznę, tak jak różnią się między sobą warianty niżej. */
	border-radius: 17px 22px 18px 23px / 21px 17px 22px 18px;
}

/* Cztery obroty tego samego narożnika, po kolei wzdłuż toru: pigułki obok
   siebie nie są odbiciem jedna drugiej, a ściana tematów przestaje wyglądać
   jak jeden kształt powielony kilkadziesiąt razy. Warianty B i D idą
   z --r-btn-mirror, A i C z --r-btn — kształt jest cały czas ten sam rysunek
   Puenty, tylko postawiony na innym rogu.

   Cykl czwórkowy przy siedmiu pigułkach w torze (patrz $NA_TOR w tagi.php) nie
   trafia w tę samą fazę w kolejnym torze, więc sąsiadujące pionowo pigułki też
   się różnią. Liczenie jest bezpieczne dla pętli: oba ciągi w pasie mają te
   same dzieci w tej samej kolejności (kopie dokłada domknijTor w app.js do obu
   naraz), więc szew po przesunięciu o -50% dalej trafia kształt w kształt. */
.pigula:nth-child(4n+2) { border-radius: 10px 26px 12px 23px / 22px 12px 25px 13px; }
.pigula:nth-child(4n+3) { border-radius: 23px 17px 22px 18px / 18px 21px 17px 22px; }
.pigula:nth-child(4n+4) { border-radius: 23px 10px 26px 12px / 13px 22px 12px 25px; }
.pigula__tresc { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }

/* Rozmiar pigułki koduje długość żartu — dłuższy żart to większa pigułka. */
.pigula--l { height: 64px; padding: 0 24px; }
.pigula--s { height: 50px; padding: 0 18px; }
.pigula:active { transform: scale(0.96); }

/* Pod kursorem pigułka nie podskakuje, tylko się przybliża — dźwignięcie o 2 px
   ginęło w ruchu toru, a powiększenie widać od razu. z-index (i position, bez
   którego z-index nie działa) wyciąga ją nad sąsiadki i nad kolejne tory, więc
   nie chowa się pod nimi krawędzią. Tylko dla myszy: na dotyku nie ma stanu
   hover, a został-by przyklejony po stuknięciu.

   Wciśnięcie idzie od tej powiększonej skali, nie od zera — inaczej klik robił
   skok z 1.1 na 0.96. Sam dotyk zostaje przy zwykłym ściśnięciu wyżej. */
@media (hover: hover) {
	.pigula:hover {
		position: relative; z-index: 1;
		transform: scale(1.1); box-shadow: var(--elev-1);
	}
	.pigula:hover:active { transform: scale(1.04); }
}

.pigula__nazwa {
	font-family: var(--font-display); font-size: 17px; font-weight: 600;
	letter-spacing: -0.01em; line-height: 1.1; white-space: nowrap;
}
.pigula--l .pigula__nazwa { font-size: 20px; }
.pigula--s .pigula__nazwa { font-size: 15px; }

/* Ocena z łapką i czas czytania to ten sam atrament, tylko cichszy: pastelowe
   tło jest za jasne, żeby dać im osobny kolor bez utraty kontrastu. Łapka stoi
   w pionowym środku wiersza, nie na linii pisma — ikona nie ma linii pisma,
   a przy 13 px każdy piksel w dół widać. */
.pigula__meta {
	display: inline-flex; align-items: center; gap: 5px;
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500;
	line-height: 1.2; white-space: nowrap; opacity: 0.72;
}
.pigula--l .pigula__meta { font-size: 12px; }
.pigula__meta .ic { margin-top: -1px; }

.wybor {
	background: var(--surface); border-radius: var(--r-card); box-shadow: var(--elev-1);
	margin: 16px var(--screen-gutter) 0; padding: 14px 16px;
	display: flex; flex-direction: column; gap: var(--space-2); align-items: flex-start;
}
.wybor__nazwa { margin: 0; font-family: var(--font-display); font-size: 18px; font-weight: 600; color: var(--ink); }
.wybor__meta { margin: 0; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 500; color: var(--ink-40); }
.wybor .btn { min-height: 44px; }

.wzrosty { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-3); }
.wzrost { display: block; color: inherit; }
.wzrost:hover { text-decoration: none; color: inherit; }
.wzrost__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); margin-bottom: 5px; }
.wzrost__nazwa { font-family: var(--font-body); font-size: 15px; font-weight: 600; color: var(--ink); }
.wzrost__delta { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 500; color: var(--ink-40); }
.wzrost__tor { display: block; height: 6px; border-radius: var(--r-pill); background: var(--mist-100); overflow: hidden; }
.wzrost__fill {
	display: block; height: 100%; border-radius: var(--r-pill); background: var(--azure);
	transform-origin: left; animation: rosnij 520ms ease both;
}
.wzrost__fill.is-sel { background: var(--rose-600); }
@keyframes rosnij { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* --- Losuj ----------------------------------------------------------------- */
/* z-index: 0 na kolumnie zakłada kontekst nakładania: aura siedzi w nim na -1,
   czyli nad tłem kolumny, a pod całą treścią, i nie trzeba podnosić każdego
   kafla z osobna. */
.app--losuj { position: relative; z-index: 0; display: flex; flex-direction: column; }

/* Nagłówek kryjący jak na pozostałych ekranach, tylko kreska odcinająca jest
   tu od razu, nie dopiero po przewinięciu — aura idzie przez całą wysokość
   ekranu, więc tytuł musi mieć pod sobą własne tło i własną krawędź.
   Box-shadow, nie border: nie zmienia wysokości nagłówka, z której liczony
   jest --head-h. */
.app--losuj .head { box-shadow: 0 1px 0 var(--line); }

/* Sekcja bierze całą wysokość, jaka zostaje po nagłówku — na biurku po to,
   żeby żart miał się gdzie ustawić na środku (patrz blok na dole arkusza).
   Na telefonie tekst zaczyna się pod nagłówkiem i rośnie w dół: czyta się go
   kciukiem od góry, a przy dłuższym żarcie i tak trzeba przewijać — pierwszy
   wers wyrównany z tytułem daje jeden punkt startu dla każdego losowania,
   zamiast miejsca skaczącego wraz z długością tekstu. */
.losuj { padding: 26px 22px 40px; display: flex; flex-direction: column; gap: 14px; flex: 1; }
.losuj__zart { display: flex; flex-direction: column; gap: 18px; }

/* Im krótszy żart, tym większe litery — ta sama zasada co na biurku (stopień
   wybiera miara_zartu() w helpers.php), tylko przeliczona na kolumnę
   telefonu. Tam razem ze stopniem rosła szerokość kolumny, więc wiersz
   zostawał ten sam; tu szerokość jest dana przez ekran i nie ma jej skąd
   wziąć — większe litery kupuje się dodatkowymi wersami. Dlatego skok jest
   płytszy niż na biurku i kończy się na 'm': przy dłuższym żarcie zostaje
   stopień z list, bo każdy krok w górę spycha puentę pod krawędź ekranu.
   Miara w vw, żeby stopień jechał z szerokością telefonu, a clamp trzyma go
   między najwęższym a najszerszym ekranem, na jakim ma to jeszcze sens. */
.app--losuj-xl .setup { font-size: clamp(28px, 8.6vw, 40px); line-height: 1.28; }
.app--losuj-l  .setup { font-size: clamp(23px, 6.4vw, 30px); line-height: 1.34; }
.app--losuj-m  .setup { font-size: clamp(19px, 5.2vw, 23px); line-height: 1.44; }
.app--losuj-s  .setup { font-size: 18px; line-height: 1.5; }

/* Przycisk losowania stoi tylko na biurku (reguły na dole arkusza). Na
   telefonie to samo robi wyniesiona kostka w pasku zakładek — i robi to
   dokładnie w tym miejscu ekranu, w którym stanąłby przycisk. */
.losuj__rzut { display: none; }

/* --- aura losowania ---------------------------------------------------------- */
/* Trzy rozmyte owale pastelu, każdy w swoim tempie i swojej fazie. Kadr jest
   przycięty do kolumny (owale wychodzą poza nią i to jest zamierzone), a ruch
   jest na tyle wolny, że w spoczynku widać go kątem oka, nie wprost.
   Fixed, nie absolute: przy dłuższym żarcie tło ma stać, a przewijać się ma
   sam tekst. Kolor bierze --pt-bg z klasy .pastel-* (patrz Tematy). */
.aura {
	position: fixed; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
	width: 100%; max-width: var(--kolumna); z-index: -1;
	overflow: hidden; pointer-events: none;
}

.aura__owal {
	position: absolute; left: var(--x); top: var(--y);
	width: var(--r); height: calc(var(--r) * 0.74);
	border-radius: 50%;
	background: var(--pt-bg);
	opacity: 0.95;
	filter: blur(52px);
	will-change: transform;
	animation: aura-plyw var(--czas) ease-in-out var(--faza) infinite;
}

/* Owal odpływa do połowy pętli i wraca tą samą drogą — bez skoku na szwie,
   więc animacja nie potrzebuje ani klatki pośredniej, ani alternate. */
@keyframes aura-plyw {
	from { transform: translate3d(0, 0, 0) scale(1); }
	50%  { transform: translate3d(var(--dx), var(--dy), 0) scale(var(--skala)); }
	to   { transform: translate3d(0, 0, 0) scale(1); }
}

/* --- Ranking ---------------------------------------------------------------- */
.ranking { display: flex; flex-direction: column; gap: 22px; }
.rank { display: flex; gap: var(--space-2); }
/* min-width trzyma treść w jednej pionowej linii, gdy numery mają różną
   szerokość (1 kontra 10) — tyle, ile zajmuje dwucyfrowy numer, nic więcej. */
.rank__nr { font-family: var(--font-display); font-weight: 700; font-size: 22px; color: var(--ink-40); min-width: 26px; flex: none; line-height: 1.2; }
.rank__nr--top { font-size: 34px; color: var(--rose); }
/* „Pokaż kolejne 20" pod listą — na środku, w odstępie większym niż między
   pozycjami, żeby czytało się jako koniec listy, a nie jej kolejny wiersz. */
.dalej { display: flex; justify-content: center; margin-top: 26px; }
.dalej .btn.is-ladowanie { opacity: 0.6; pointer-events: none; }

/* Żart w rankingu to ten sam .wiersz, co na Dziś — tylko wchodzi w kolumnę
   obok numeru pozycji, a odstępy między pozycjami daje .ranking. */
.rank .wiersz { flex: 1; min-width: 0; padding: 0; }

.ludzie { display: flex; flex-direction: column; }
.osoba { display: flex; align-items: center; gap: var(--space-3); min-height: 56px; padding: var(--space-2) 0; }
.osoba + .osoba { border-top: 1px solid var(--mist-100); }
.osoba__body { min-width: 0; flex: 1; }
.osoba__nick { margin: 0; font-family: var(--font-body); font-size: 16px; font-weight: 600; color: var(--ink); }
.osoba__meta { margin: 2px 0 0; font-family: var(--font-body); font-size: 13px; font-weight: 400; color: var(--text-secondary); }
.osoba__ile { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; color: var(--ink-60); }

.zakladki-pod { display: flex; gap: var(--space-5); padding: 0 var(--screen-gutter); border-bottom: 1px solid var(--line); }
.zakladki-pod__item {
	font-family: var(--font-display); font-size: 15px; font-weight: 600; color: var(--ink-40);
	padding: 10px 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.zakladki-pod__item.is-on { color: var(--ink); border-bottom-color: var(--ink); }
.zakladki-pod__item:hover { text-decoration: none; }

/* --- Ranking: „Na temat" ---------------------------------------------------- */
/* Spis tematów zamiast trzeciej listy żartów: zakładka jest rozdrożem, a każdy
   temat ma własny adres z własną dwudziestką (patrz RANKING_TEMATY). */
.temat-wstep {
	margin: 0 0 var(--space-3); font-family: var(--font-body); font-size: 15px;
	line-height: 1.5; color: var(--ink-60);
}

/* Wiersze jak na liście autorów obok — kreska między pozycjami, nic więcej.
   Wysokość z min-height, nie z paddingu, żeby krótki podpis („pracy") miał
   pole dotyku takie samo jak długi. */
.tematy-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.tematy-lista li + li { border-top: 1px solid var(--mist-100); }
.tematy-lista__link {
	display: flex; align-items: center; gap: var(--space-2); min-height: 48px;
	font-family: var(--font-body); font-size: 16px; font-weight: 600; color: var(--ink);
}
.tematy-lista__link:hover { text-decoration: none; color: var(--azure); }
/* Strzałka odsunięta na prawą krawędź i zgaszona — mówi „to prowadzi dalej",
   a nie „kliknij tutaj"; klikalny jest cały wiersz. */
.tematy-lista__link svg { margin-left: auto; flex: none; color: var(--ink-40); }

/* Nagłówek ekranu tematycznego jest całym zdaniem, nie jednym słowem jak
   reszta tytułów — przy 26 px łamałby się na cztery linie i spychał pierwszą
   pozycję poza ekran. */
.tytul--temat { font-size: 21px; line-height: 1.25; margin-bottom: var(--space-4); }

/* --- Profil ------------------------------------------------------------------ */

/* Głowa profilu w karcie, tak jak seria i statki pod nią. Wcześniej ekran
   zaczynał się trzema rzeczami luzem — awatarem, mailem i wierszem „miasto ·
   wiek" doklejonym pod spodem ujemnym marginesem — i żadna nie miała ramy,
   przez co pierwszy kafelek niżej wyglądał na początek strony. */
.profil__karta { gap: var(--space-4); margin-bottom: var(--space-4); }
.profil__glowa { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }

/* Poświata zamiast obwódki — dwa pierścienie, biały i różowy, żeby zdjęcie
   odbiło się od białej karty i wzięło kolor serwisu. */
.profil__glowa .avatar { box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--rose-soft); }

/* min-width: 0 na obu — bez tego długi mail rozpycha flex i wypycha awatar
   poza kartę, zamiast się przyciąć. */
.profil__body { min-width: 0; }
.profil__nick {
	margin: 0; font-family: var(--font-display); font-size: 26px; font-weight: 700;
	letter-spacing: -0.02em; line-height: 1.15; color: var(--ink);
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Mail pod nickiem, małymi literami i ciszej: to podpis konta, a nie nagłówek. */
.profil__mail {
	margin: 4px 0 0; font-family: var(--font-meta); font-size: 13px; color: var(--ink-40);
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Fakty o koncie: miasto, wiek, staż. Wyglądem to pigułki, ale własną klasą,
   nie .chip — tamte są do klikania (mają kursor i stan is-on), a te niczego nie
   robią i nie mają udawać, że robią. */
.fakty { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.fakt {
	display: inline-flex; align-items: center; height: 28px; padding: 0 12px;
	border-radius: var(--r-pill); background: var(--mist-50); border: 1px solid var(--mist-100);
	font-family: var(--font-meta); font-variant-numeric: tabular-nums;
	font-size: 12px; font-weight: 500; color: var(--ink-60);
}

/* Kafelek serii jest dwukolumnowy: liczby po lewej, film po prawej. Kolumna
   z treścią ma min-width: 0, żeby przy wąskim ekranie to ona się ściskała,
   a nie wypychała filmu poza kartę. */
.karta--seria { flex-direction: row; align-items: center; gap: var(--space-3); }
.seria__tresc { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }

.seria__licz {
	margin: 0; display: flex; align-items: baseline; gap: 6px;
	font-family: var(--font-body); font-size: 14px; color: var(--ink-60);
}
.seria__licz span { font-family: var(--font-display); font-size: 34px; line-height: 1; font-weight: 700; color: var(--rose); }
/* Płomień siada na linii bazowej cyfry, nie na jej środku — inaczej wisi
   nad tekstem "dni" i rozjeżdża rząd. */
.seria__licz .ic { align-self: center; color: var(--rose); flex: none; }

.kalendarz { display: grid; grid-template-columns: repeat(14, 1fr); gap: 3px; }
.kalendarz__dzien { aspect-ratio: 1; border-radius: 3px; background: var(--mist-100); }
.kalendarz__dzien.is-on { background: var(--rose); }
/* Film w kafelku serii. mix-blend-mode: multiply wtapia białe tło nagrania
   w kartę — bez tego byłby to biały prostokąt na białym, z widoczną krawędzią. */
.seria__film {
	flex: none; display: block;
	width: 96px; height: auto; aspect-ratio: 1;
	mix-blend-mode: multiply;
	pointer-events: none;
}

.statki { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2); margin: var(--space-4) 0; }
.statka { background: var(--surface); border-radius: var(--r-card); box-shadow: var(--elev-1); padding: 14px; display: flex; flex-direction: column; gap: 2px; }
.statka__n { font-family: var(--font-display); font-size: 20px; font-weight: 700; color: var(--ink); }
.statka__e { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; color: var(--ink-40); }

.rzedy { background: var(--surface); border-radius: var(--r-card); box-shadow: var(--elev-1); overflow: hidden; margin-top: var(--space-2); }
/* W formularzu dodawania odstęp nad .rzedy już daje gap rodzica
   (.dodaj__scroll jest flex-column) — własny margines doklejał się do niego
   i "Twoje zgłoszenia" stało dalej od pierwszego wiersza niż gdziekolwiek
   indziej w serwisie. */
.dodaj__scroll .rzedy { margin-top: 0; }
.rzad { display: flex; align-items: center; min-height: 56px; padding: 0 16px; color: var(--ink); font-size: 15px; }
.rzad + .rzad { border-top: 1px solid var(--mist-100); }
.rzad:hover { background: var(--mist-50); text-decoration: none; color: var(--ink); }
.rzad__ch { margin-left: auto; color: var(--ink-40); }

/* --- stany puste --------------------------------------------------------------- */
.pusto { display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-start; }
.pusto--ekran { padding: var(--space-8) 0; }
.pusto__tytul { margin: 0; font-family: var(--font-display); font-size: 17px; font-weight: 600; color: var(--ink); }
.pusto__opis { margin: 0; font-family: var(--font-body); font-size: 14px; color: var(--ink-60); }

/* --- pasek zakładek ------------------------------------------------------------ */
.tabbar {
	position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
	width: 100%; max-width: var(--kolumna); z-index: 5;
	display: flex; justify-content: space-around; align-items: flex-end;
	background: var(--surface); border-top: 1px solid var(--line);
	padding: 8px 10px 22px;
}

/* Marka wchodzi do paska dopiero na biurku (patrz blok na dole arkusza).
   Na telefonie logo stoi w nagłówku ekranu Dziś i drugie, w pasku zakładek,
   nie miałoby gdzie się zmieścić ani po co. */
.tabbar__marka { display: none; }


.tabbar__item {
	display: flex; flex-direction: column; align-items: center; gap: 4px;
	min-width: 54px; min-height: var(--touch-min); padding: 4px 0;
	color: var(--ink-40); transition: color 140ms ease;
}
.tabbar__item:hover { text-decoration: none; }
.tabbar__item[aria-current="page"] { color: var(--azure); }
.tabbar__label { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 10px; font-weight: 500; letter-spacing: 0.04em; }

/* Dodawanie wchodzi do paska dopiero na biurku (patrz blok na dole arkusza) —
   tak samo jak marka. Na telefonie pasek ma pięć pozycji rozstawionych pod
   kciuk, szósta nie ma się gdzie zmieścić, a plus stoi w nagłówku ekranu.
   Reguła po .tabbar__item, nie przed: obie mają tę samą specyficzność, więc
   wcześniejsza przegrywałaby z display: flex zakładki. */
.tabbar__item--dodaj { display: none; }

/* Buźka w pasku jest zawsze pełna — przygaszona wyglądała jak wyłączona
   zakładka, a nie jak nieaktywna. Że Profil jest wybrany, mówi kolor podpisu,
   tak samo jak na pozostałych zakładkach (te dodatkowo zmieniają ikonę na
   wypełnioną, ale awatar wariantu solid nie ma). */

.tabbar__item--fab { color: var(--ink-40); }
.fab {
	position: relative;
	display: inline-flex; align-items: center; justify-content: center;
	width: 58px; height: 58px; border-radius: 50%;
	background: var(--rose-600); color: #fff;
	border: 5px solid var(--surface);
	margin-top: -34px; transition: background-color 140ms ease;
}
.tabbar__item--fab:hover .fab { background: var(--rose-700); }

/* Rzut kostkami. Gra się raz, przy wejściu na Losuj (klasę dokłada _tabbar.php
   tylko wtedy, gdy to jego zakładka jest bieżąca) — każde wejście na ten ekran
   to nowe losowanie, więc kostki mają je odegrać, a nie mrugać w kółko.

   Trzy warstwy ruchu, bo pojedynczy obrót wygląda jak kręcący się piktogram,
   nie jak rzut: krążek robi zamach i podskok, ikona koziołkuje, obie kostki
   drgają w przeciwfazie. Oczka wpadają dopiero na końcu, po kolei — w locie
   kostka nie ma jeszcze wyniku, a jak staje, to go pokazuje.

   Wszystko na CSS, bez JS: animacja rusza z pierwszą klatką strony i nie czeka
   na skrypt (a i tak nie ma tu czego obsługiwać po starcie). */
.fab--rzut { animation: rzut 760ms cubic-bezier(.22,.9,.3,1.1) both; }
.fab--rzut .kostki { animation: kostki-koziolek 760ms cubic-bezier(.3,.7,.3,1) both; }
.fab--rzut .kostki__a { animation: kostki-drgnij-a 480ms ease-in-out both; }
.fab--rzut .kostki__b { animation: kostki-drgnij-b 480ms ease-in-out both; }
.fab--rzut .kostki__oczko {
	animation: oczko 200ms ease-out both;
	animation-delay: calc(430ms + var(--kolej) * 70ms);
}

/* Fala. Razem z rzutem z pod krążka rozchodzą się dwa przezroczyste pierścienie —
   krążek dostaje puls, którego sam ruch kostek nie daje: rzut jest szarpany
   i krótki, a fala szeroka i spokojna, więc jedno nie zagłusza drugiego.

   Azure, nie róż: to element nawigacji, a róż zostaje na puentę i reakcje.
   Dwa pierścienie z przesunięciem, bo jeden czyta się jak rozdmuchane kółko,
   a nie jak woda — drugi wychodzi, gdy pierwszy jest już w połowie drogi.
   Cieniejąca krawędź i gaśnięcie do zera zamiast twardego końca: pierścień
   ma się rozejść, a nie zniknąć.

   z-index: -1 chowa je pod krążkiem i jego białą obwódką (ale nad tłem paska —
   .tabbar jest tu kontekstem układania), więc fala wypływa spod przycisku,
   a nie zza jego krawędzi. */
.fab--rzut::before,
.fab--rzut::after {
	content: ""; position: absolute; inset: -5px;   /* -5px = zewnętrzna krawędź obwódki */
	border-radius: 50%; border: 2px solid var(--azure);
	z-index: -1; pointer-events: none;
	animation: fala 1250ms cubic-bezier(.16,.72,.3,1) both;
}
.fab--rzut::after { animation-delay: 300ms; }

/* fill-box, nie domyślny kadr 24x24: kostka ma się obracać wokół własnego
   środka. Wokół środka ikony wyglądałoby to jak orbitowanie, nie jak drganie. */
.kostki__a, .kostki__b { transform-box: fill-box; transform-origin: center; }

@keyframes rzut {
	0%   { transform: scale(1)    translateY(0); }
	18%  { transform: scale(.93)  translateY(2px); }    /* zamach w dół */
	46%  { transform: scale(1.07) translateY(-4px); }   /* wyrzut */
	72%  { transform: scale(.98)  translateY(1px); }    /* lądowanie */
	100% { transform: scale(1)    translateY(0); }
}

/* Koziołek nie jest pełnym obrotem: kostki mają wrócić tą samą stroną do góry,
   a 360° przy 24 px zlewa się w rozmazane kółko. */
@keyframes kostki-koziolek {
	0%   { transform: rotate(0deg); }
	30%  { transform: rotate(-36deg); }
	62%  { transform: rotate(22deg); }
	84%  { transform: rotate(-7deg); }
	100% { transform: rotate(0deg); }
}

@keyframes kostki-drgnij-a {
	0%, 100% { transform: translate(0, 0) rotate(0deg); }
	25%      { transform: translate(-1.1px, 1px) rotate(-11deg); }
	60%      { transform: translate(.9px, -.8px) rotate(9deg); }
}

/* Przeciwfaza do kostki A — dwie kostki ruszające się zgodnie czytają się
   jako jedna bryła. Tylna ma mniejszy wychył, bo jej kształt jest przycięty
   przednią kostką i przy większym odjeżdża od jej krawędzi. */
@keyframes kostki-drgnij-b {
	0%, 100% { transform: translate(0, 0) rotate(0deg); }
	25%      { transform: translate(1px, -1px) rotate(9deg); }
	60%      { transform: translate(-.8px, .9px) rotate(-7deg); }
}

@keyframes oczko { from { opacity: 0; } to { opacity: 1; } }

@keyframes fala {
	0%   { transform: scale(.92); opacity: 0;   border-width: 2px; }
	12%  { opacity: .34; }
	100% { transform: scale(1.92); opacity: 0;  border-width: .5px; }
}

/* --- toast ---------------------------------------------------------------------- */
/* Krótkie potwierdzenie akcji, która nie zmienia nic na ekranie (skopiowany
   link). Siedzi nad paskiem zakładek, wyśrodkowany jak reszta serwisu, i nigdy nie
   rozlewa się szerzej niż 420 px — długi komunikat na biurku ma zostać
   kafelkiem, nie belką przez cały ekran. z-index nad arkuszami:
   udostępnić da się też z arkusza żartu, a komunikat schowany pod nim byłby
   po prostu brakiem odpowiedzi.

   Ciemny kafelek zamiast kartki — toast ma być czytelny jako warstwa nad
   dowolnym ekranem, także nad białym arkuszem i nad kolorową aurą Losuj.

   Domyślnie poza kadrem i bez zdarzeń: element zostaje w drzewie po zniknięciu
   (kolejny komunikat go używa ponownie), więc nie może łapać kliknięć w miejscu,
   gdzie nic nie widać. */
.toast {
	position: fixed; bottom: calc(90px + env(safe-area-inset-bottom, 0px));
	left: 50%; width: max-content; max-width: min(calc(100% - 40px), 420px); z-index: 10;
	padding: 11px 18px; border-radius: var(--r-pill);
	background: var(--bg-dark); color: var(--text-on-dark); box-shadow: var(--elev-2);
	font-family: var(--font-body); font-size: 14px; line-height: 1.3; text-align: center;
	pointer-events: none; opacity: 0;
	transform: translate(-50%, 10px);
	transition: opacity 180ms ease, transform 180ms ease;
}
.toast.is-on { opacity: 1; transform: translate(-50%, 0); }

@media (prefers-reduced-motion: reduce) {
	.toast { transition-duration: 1ms; transform: translate(-50%, 0); }
}

/* --- arkusz komentarzy ---------------------------------------------------------- */
.scrim { position: fixed; inset: 0; background: rgba(16,34,44,.38); z-index: 8; animation: pojaw 160ms ease both; }
@keyframes pojaw { from { opacity: 0; } to { opacity: 1; } }

.sheet {
	position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
	width: 100%; max-width: var(--kolumna); height: 82vh; z-index: 9;
	background: var(--surface); border-radius: 22px 22px 0 0; box-shadow: var(--elev-2);
	display: flex; flex-direction: column;
	animation: wjedz 200ms ease both;
}
@keyframes wjedz { from { transform: translate(-50%, 100%); } to { transform: translate(-50%, 0); } }

.sheet__handle {
	width: 38px; height: 4px; border-radius: 2px; background: var(--line);
	border: none; margin: 10px auto 6px; cursor: pointer; padding: 0; flex: none;
}
.sheet__title { margin: 0 0 6px; text-align: center; font-family: var(--font-display); font-size: 16px; font-weight: 600; color: var(--ink); }

.sheet__body { flex: 1; overflow-y: auto; padding: 8px var(--screen-gutter) 16px; display: flex; flex-direction: column; gap: 20px; }

.kom { display: flex; gap: var(--space-3); align-items: flex-start; }
/* Bez obwódki: buźki stoją w kolumnie jedna pod drugą i każde kółko rysowało
   drugą kreskę tuż przy krawędzi rysunku, przez co wątek wyglądał na kratkę
   z miniaturami. Tło z .avatar zostaje — trzyma kształt, zanim obrazek dojdzie. */
.kom .avatar { border: none; }
.kom--odp { margin-top: var(--space-3); }
.kom__body { flex: 1; min-width: 0; }
.kom__head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.kom__nick { font-family: var(--font-body); font-size: 13px; font-weight: 600; color: var(--ink); }
.kom__czas { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; color: var(--ink-60); }
.badge { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 10px; height: 18px; padding: 0 7px; border-radius: var(--r-pill); background: var(--rose-soft); color: var(--rose-700); display: inline-flex; align-items: center; }
.kom__tekst { margin: 3px 0 0; font-family: var(--font-body); font-size: 15px; line-height: 1.45; color: var(--ink); overflow-wrap: anywhere; }
.kom__odp { margin-top: 4px; background: none; border: none; padding: 0; cursor: pointer; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-60); transition: color 140ms ease; }
.kom__odp:hover { color: var(--ink); }
.kom__odpowiedzi { margin-left: 12px; padding-left: 12px; border-left: 1px solid var(--line); }
.kom--odp .kom__tekst { font-size: 14px; color: var(--ink-60); }

.kom__lajk { width: 26px; flex: none; background: none; border: none; cursor: pointer; color: var(--ink-60); display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 0; transition: color 140ms ease; }
/* Lajk komentarza to ten sam kciuk, co pod żartem — więc i ten sam jadeit.
   Zużyty klik gasi przycisk, ale go nie wyszarza mocno: to nie błąd, tylko
   koniec sprawy, a liczba obok musi zostać czytelna. */
.kom__lajk.is-on { color: var(--jade); }
.kom__lajk:disabled { cursor: default; opacity: 0.55; }
.kom__lajk-ile { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; }

/* --- moje komentarze (ekran spod Profilu) --------------------------------- */
/* Wiersz czyta się od góry: cicho żart, głośno moje zdanie. Stąd skrót żartu
   w rozmiarze drobnego druku i w --ink-60, a komentarz pod nim w kolorze
   tekstu — odwrotnie niż w arkuszu, gdzie to żart jest treścią. */
/* Dwie linie i koniec — przypomnienie „to było pod tym" ma zajmować mniej
   miejsca niż moje zdanie pod nim, a żarty bywają na pół ekranu. Skracamy
   dwa razy: skroc() w szablonie tnie tekst, a clamp domyka to, co i tak się
   nie mieści w kolumnie. */
.mojkom__zart {
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
	font-family: var(--font-body); font-size: 14px; line-height: 1.45; color: var(--ink-60);
	transition: color 140ms ease;
}
a.mojkom__zart:hover { color: var(--ink); text-decoration: none; }
.mojkom__zart--niema { display: block; margin: 0; font-style: italic; color: var(--ink-40); }
/* Bez white-space: pre-line — łamanie linii robi nl() w szablonie, tak samo
   jak w arkuszu komentarzy; razem z pre-line każdy enter wychodziłby podwójnie. */
.mojkom__tekst { margin: 0; font-family: var(--font-body); font-size: 16px; line-height: 1.5; color: var(--ink); overflow-wrap: anywhere; }
/* Odpowiedź wyjęta z wątku sama o sobie nie mówi, że jest odpowiedzią —
   w arkuszu widać to po wcięciu, tutaj musi to powiedzieć słowo. */
.mojkom__odp { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; color: var(--ink-40); }
.mojkom__lajki { display: flex; align-items: center; gap: 5px; margin: 0; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 500; color: var(--jade); }

.composer { background: var(--mist-50); border-top: 1px solid var(--mist-100); padding: 10px var(--screen-gutter) 16px; flex: none; }
.composer__quick { display: flex; gap: var(--space-2); overflow-x: auto; padding-bottom: 10px; }
.quick {
	flex: none; height: 32px; padding: 0 13px; border-radius: var(--r-pill);
	background: var(--surface); border: 1px solid var(--line); cursor: pointer;
	font-family: var(--font-display); font-size: 13px; font-weight: 500; color: var(--ink);
	transition: background-color 140ms ease, color 140ms ease;
}
.quick:hover { background: var(--ink); color: var(--mist-50); border-color: var(--ink); }
/* Pigułka z samym emoji: znak w 13 px byłby ledwo widoczny obok słów, a krój
   display i tak go nie rysuje — stąd własny rozmiar i jawne fonty emoji,
   żeby łapka wyglądała tak samo na telefonie i na biurku. */
.quick--emo {
	font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", var(--font-display);
	font-size: 17px; line-height: 1; padding: 0 11px;
}

.composer__row { display: flex; align-items: center; gap: var(--space-2); }

/* Wyślij leży na polu, przy jego prawej krawędzi: pole jest kontekstem
   pozycjonowania, a wcięcie tekstu po prawej robi kółku miejsce, żeby nie
   wjeżdżało na wpisywane zdanie. */
.composer__pole { position: relative; flex: 1; min-width: 0; }
.composer__input {
	width: 100%; height: 44px; padding: 0 44px 0 16px;
	border: 1px solid var(--line-input); border-radius: var(--r-pill);
	background: var(--surface); font-family: var(--font-body); font-size: 15px; color: var(--ink);
}
.composer__input::placeholder { color: var(--ink-40); }
.composer__input:focus { outline: 2px solid var(--azure); outline-offset: 1px; }

.composer__send {
	position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
	width: 32px; height: 32px; border-radius: 50%; border: none; cursor: pointer;
	display: inline-flex; align-items: center; justify-content: center;
	background: var(--mist-100); color: var(--ink-40);
	transition: background-color 140ms ease, color 140ms ease;
}
.composer__send:not([disabled]) { background: var(--rose-600); color: #fff; }

/* Stopka arkusza dla gościa: zamiast pola i kciuków jedno zdanie i wejście na
   logowanie. Ten sam pasek co composer, więc arkusz nie skacze przy zmianie. */
.composer--gosc { display: flex; flex-direction: column; gap: var(--space-3); padding-top: 16px; }
.composer__info { margin: 0; font-family: var(--font-body); font-size: 14px; color: var(--ink-60); text-align: center; }

/* --- overlay dodawania ------------------------------------------------------- */
/* Ta sama mechanika co arkusz komentarzy: wjeżdża z dołu, zamyka scrim, Escape
   albo X. Różni się nagłówkiem — tytuł do lewej, krzyżyk do prawej. */
.sheet--dodaj { height: 88vh; height: 88dvh; }

.sheet__head {
	display: flex; align-items: center; gap: var(--space-3);
	padding: 18px var(--screen-gutter) 10px;
	border-bottom: 1px solid var(--mist-100);
}
.sheet__head > div { flex: 1; min-width: 0; }
.sheet__title--lewo { margin: 0; text-align: left; font-size: 20px; }

/* Odstępy w środku trzyma gap kolumny — własne marginesy bloków by je podwoiły. */
.dodaj .formularz, .dodaj .banner, .dodaj .alert { margin: 0; }

/* Układ dodawania: pole i tagi przewijają się, przycisk stoi. Na pełnym ekranie
   /dodaj to zwykła kolumna — dopiero w arkuszu dostaje własny scroll i stopkę. */
.dodaj { display: flex; flex-direction: column; min-height: 0; }
.dodaj__scroll { display: flex; flex-direction: column; gap: var(--space-4); min-height: 0; }
.dodaj__akcje { padding-top: var(--space-4); }

/* Dodawanie stoi na szarawym tle — tym samym, co ekran /dodaj (.app bierze
   --bg-screen), więc arkusz i pełny ekran wyglądają jak jedno miejsce. Biel
   zostaje na to, co się wypełnia: pola, karta zgłoszeń, lista podpowiedzi.
   Dwa tła na ekran, jak w regułach Puenty — szare pod spodem, białe w polach. */
.sheet--dodaj { background: var(--bg-screen); }

/* Ciało arkusza oddaje padding do środka: scroll ma go u siebie, żeby pasek
   przewijania i pas tagów sięgały krawędzi. */
.sheet--dodaj .sheet__body { padding: 0; overflow: hidden; }
.sheet--dodaj .dodaj { flex: 1; min-height: 0; }
.sheet--dodaj .dodaj__scroll { flex: 1; overflow-y: auto; padding: 12px var(--screen-gutter) 6px; }

/* Stopka arkusza: przycisk zawsze w tym samym miejscu, z oddechem od dolnej
   krawędzi telefonu (pasek gestów potrafi podejść pod sam guzik). */
.sheet--dodaj .dodaj__akcje {
	flex: none;
	padding: 12px var(--screen-gutter) calc(20px + env(safe-area-inset-bottom, 0px));
	background: var(--bg-screen);
	border-top: 1px solid var(--mist-100);
}


/* --- slide-up z informacją ---------------------------------------------------- */
/* Wysokość z treści — komunikat na dwa zdania nie ma po co zajmować ekranu. */
.sheet--info { height: auto; }
.sheet--info .sheet__head { border-bottom: none; padding-bottom: 0; }
.sheet--info .sheet__title--lewo { font-size: 18px; }
.sheet__info {
	display: flex; flex-direction: column; gap: var(--space-4);
	padding: 4px var(--screen-gutter) 32px;
}

/* --- slide-up z podziękowaniem po wysłaniu żartu ------------------------------- */
.sheet--dziekujemy { z-index: 10; }
.sheet__info--dziekujemy { align-items: center; text-align: center; padding-top: 32px; }
.dziekujemy__film { width: 180px; height: 180px; border-radius: 50%; object-fit: cover; }
.dziekujemy__tekst { margin: 0; font-family: var(--font-display); font-size: 17px; font-weight: 600; color: var(--ink); }

/* --- slide-up z filtrami losowania -------------------------------------------- */
/* Ta sama mechanika co pozostałe arkusze, wysokość z treści (.sheet--info).
   Formularz jest ciałem arkusza, więc bierze jego padding i odstępy — dochodzi
   tylko oddech nad przyciskiem od dolnej krawędzi telefonu. */
.sheet--filtry .sheet__info { gap: var(--space-5); padding-bottom: calc(28px + env(safe-area-inset-bottom, 0px)); }

/* Chipy filtrów zawijają się do drugiego rzędu — cztery etykiety z nawiasem nie
   zmieszczą się w jednej linii i nie mają po co jechać poziomym pasem. */
.sheet--filtry .chipsy--filtry { margin: 0; }
.chip__dopisek { color: inherit; opacity: 0.65; }

/* --- arkusz żartu z karuzeli --------------------------------------------------- */
/* Ta sama mechanika co pozostałe arkusze; różnica jest w kolorze — arkusz
   przejmuje pastel pigułki, w którą się stuknęło (klasę .pastel-* podmienia JS),
   więc widać, skąd się przyszło. Kolory idą przez --pt-bg / --pt-ink. */
/* Pas koloru ma być paskiem, nie kaflem: wysokość bierze z krzyżyka (44 px pola
   dotyku) i wąskiego paddingu, a nie z hojnych 18/16 px, które odbierały żartowi
   pierwszy wiersz. Temat i krzyżyk stoją w osi pionowej pasa — przy wyrównaniu
   do góry nazwa tematu siedziała nad środkiem i pas wyglądał na cięższy u dołu.
   Najdłuższy temat dalej zawija czas do drugiej linii (patrz .sheet__ident) —
   wtedy pas rośnie o tę linię i zostaje wyśrodkowany. */
.sheet--dowcip .sheet__head {
	background: var(--pt-bg); border-radius: 22px 22px 0 0;
	padding: 10px var(--screen-gutter); align-items: center;
}
.sheet--dowcip .sheet__title--lewo { font-size: 22px; color: var(--pt-ink); }

/* Czas czytania stoi w jednej linii z nazwą tematu, po niej — to jej dopisek,
   nie osobne piętro nagłówka. Wyrównanie do linii pisma, bo stopnie pisma są
   różne, a flex-wrap zostaje dla najdłuższych tematów: wtedy czas schodzi do
   drugiej linii, zamiast ściskać nazwę.
   Mono bez wersalików (.eyebrow je narzuca, a "25S" czyta się gorzej niż
   "25s") i ten sam atrament co tytuł, tylko cichszy. Numer żartu jest już nad
   jego treścią, w tym samym wierszu co data i autor. */
.sheet__ident { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.sheet__meta {
	margin: 0; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 500;
	color: var(--pt-ink); opacity: 0.72;
}
.sheet--dowcip .ikonka { color: var(--pt-ink); }
.sheet--dowcip .ikonka--kolko .kolko { background: rgba(255, 255, 255, 0.55); }
.sheet--dowcip .ikonka--kolko:hover .kolko { background: rgba(255, 255, 255, 0.9); }

/* Żart bywa na trzy zdania i bywa na trzy tysiące znaków, więc arkusz rośnie
   z treści, ale nie wyżej niż ekran — dłuższy żart przewija się w środku. */
.sheet--dowcip { max-height: 86vh; max-height: 86dvh; }
.sheet--dowcip .sheet__info { overflow-y: auto; min-height: 0; padding-top: 18px; }

/* Struktura jak w wierszu na Dziś: podpis nad żartem, wiersz akcji pod nim —
   stąd te same klasy (.wiersz__eyebrow, .akcje) i żadnych własnych reguł. */
.dowcip { display: flex; flex-direction: column; gap: var(--space-4); }
.dowcip__stan { margin: 0; font-family: var(--font-body); font-size: 15px; color: var(--ink-60); }

/* Zamknięcie arkusza to inny rodzaj gestu niż lajk czy zakładka, więc przycisk
   odsuwa się od wiersza akcji bardziej, niż wynika to ze wspólnego gap-u. */
.dowcip .btn--pelny { margin-top: var(--space-3); }

/* --- dostępność ------------------------------------------------------------------ */
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
	.tor__pas, .seria-card, .seria-card::after, .seria-card__kreska, .wzrost__fill,
	.sheet, .scrim, .ocena__glos.is-pyk .ic, .aura__owal,
	.fab--rzut, .fab--rzut .kostki, .fab--rzut .kostki__a, .fab--rzut .kostki__b,
	.fab--rzut .kostki__oczko, .fab--rzut::before, .fab--rzut::after,
	/* Rzut co dziewięć sekund w pasku nawigacji (biurko) — animacja w pętli,
	   czyli dokładnie to, czego ten tryb nie chce. */
	.tabbar__item--fab .fab,
	/* Rzut kostek na przycisku losowania (biurko) — ten sam gest co w pasku. */
	.losuj__kostki, .losuj__rzut .kostki, .losuj__rzut .kostki__a,
	.losuj__rzut .kostki__b, .losuj__rzut .kostki__oczko { animation: none !important; }
	/* Stojący pierścień zostałby na ekranie obwódką, której nikt nie zamawiał. */
	.fab--rzut::before, .fab--rzut::after { display: none; }
	.konfetti { display: none; }
	/* Stojący pas pokazywałby tylko pierwsze pigułki — bez animacji tor
	   zostaje zwykłym paskiem do przesuwania palcem, a kopia ciągu (potrzebna
	   wyłącznie do domknięcia pętli) schodzi z ekranu. */
	.tor { overflow-x: auto; }
	.tor__ciag[aria-hidden="true"] { display: none; }
	/* Bez ruchu pigułki zaczynają się przy krawędzi, więc nie ma czego wygaszać —
	   przygaszona pierwsza pigułka wyglądałaby na wyłączoną. */
	.karuzela { -webkit-mask-image: none; mask-image: none; }
	.seria-card__kreska { transform: scaleX(1); }
	.wzrost__fill { transform: scaleX(1); }
}

/* --- formularze konta ------------------------------------------------------- */
.formularz { display: flex; flex-direction: column; gap: var(--space-4); margin: var(--space-4) 0; }
.pole { display: flex; flex-direction: column; gap: 6px; }
.pole__etykieta { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-40); }
.pole__input {
	height: 48px; padding: 0 14px; border: 1px solid var(--line-input);
	border-radius: var(--r-input); background: var(--surface);
	font-family: var(--font-body); font-size: 16px; color: var(--ink);
}
.pole__input:focus { outline: 2px solid var(--azure); outline-offset: 1px; }

/* Komunikat zamiast dymka przeglądarki (składa go app.js). Ton .alert, ale bez
   tła i bez ramki: uwaga dotyczy jednego pola, a nie całego ekranu, więc nie
   może ważyć tyle co plansza z błędem serwera. Kreska pola idzie w róż razem
   z nim — obwódka i zdanie pod spodem to jeden komunikat, nie dwa. */
.pole__blad {
	margin: 0; font-family: var(--font-body); font-size: 13px; line-height: 1.35;
	color: var(--rose-700); text-wrap: pretty;
}
.pole__input.is-blad { border-color: var(--rose-600); }
.pole__input.is-blad:focus { outline-color: var(--rose-600); }

.alert {
	margin: var(--space-3) 0 0; padding: 12px 14px; border-radius: var(--r-input);
	background: var(--rose-soft); color: var(--rose-700);
	font-family: var(--font-body); font-size: 14px;
}

.rzad--btn { width: 100%; background: none; border: none; cursor: pointer; font-family: inherit; text-align: left; }

/* --- wejście do konta -------------------------------------------------------- */
.karta--konto { gap: var(--space-3); margin-bottom: var(--space-4); }
.konto__tekst { margin: 0; font-family: var(--font-body); font-size: 15px; line-height: 1.5; color: var(--ink-60); }
.konto__akcje { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.konto__akcje .btn { min-height: 44px; }

.btn--text { background: transparent; color: var(--azure); padding: 0 8px; }
.btn--text:hover { color: var(--azure-700); text-decoration: underline; }

/* Pole na kod — cyfry duże i rozstrzelone, żeby wpisywało się bez pomyłki. */
.pole__input--kod {
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 28px; letter-spacing: 0.4em;
	text-align: center; height: 60px;
}

/* Podpowiedź tylko dla trybu lokalnego. */
.dev {
	margin: var(--space-3) 0 0; padding: 12px 14px; border-radius: var(--r-input);
	background: var(--azure-soft); color: var(--azure-700);
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 13px;
}

/* --- ekran dodawania --------------------------------------------------------- */
.pole__input--tekst {
	height: auto; min-height: 180px; padding: 14px;
	line-height: 1.5; resize: vertical; white-space: pre-wrap;
}

.pole__licznik {
	align-self: flex-end;
	font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; color: var(--ink-40);
}

/* Pole tagów. Jedno wolne pole plus pigułki tego, co już wpisane — listy
   wszystkich tematów nie ma, bo rosła z każdym własnym tagiem. Pigułki są
   ciemne (jak włączony chip filtra), żeby czytały się jako wybrane, a nie
   jako kolejna rzecz do stuknięcia. */
.tagbox { display: flex; flex-direction: column; gap: 6px; }
.tagbox__pigulki { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.tagbox__pigulki:empty { display: none; }

.tagbox__pigulka {
	display: inline-flex; align-items: center; gap: 4px; height: 34px;
	padding: 0 6px 0 14px; border-radius: var(--r-pill);
	background: var(--ink); color: #fff;
	font-family: var(--font-body); font-size: 13px; font-weight: 500;
}
.tagbox__x {
	display: inline-flex; align-items: center; justify-content: center;
	width: 22px; height: 22px; padding: 0; border: 0; border-radius: 50%;
	background: transparent; color: inherit; cursor: pointer;
	font-family: var(--font-body); font-size: 16px; line-height: 1;
}
.tagbox__x:hover { background: rgba(255, 255, 255, 0.18); }

.tagbox__wpis { position: relative; }
.tagbox__pole { width: 100%; height: 44px; }
.tagbox__pod { margin: 0; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-40); }

/* Lista podpowiedzi wisi nad polem — nad formularzem, nie w jego przepływie,
   żeby wchodząc i wychodząc nie przesuwała treści pod sobą. Pole tagów stoi
   nisko w arkuszu, tuż nad stopką — w dół lista nie miałaby się gdzie
   zmieścić i zasłaniałaby przycisk wysyłki, stąd otwiera się w górę. */
.tagbox__lista {
	position: absolute; z-index: 20; left: 0; right: 0; bottom: calc(100% + 4px);
	max-height: 232px; overflow-y: auto;
	margin: 0; padding: 6px; list-style: none;
	border: 1px solid var(--line); border-radius: var(--r-input);
	background: var(--surface); box-shadow: var(--elev-2);
}
.tagbox__opcja {
	display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
	padding: 9px 10px; border-radius: 8px; cursor: pointer;
	font-family: var(--font-body); font-size: 15px; color: var(--ink);
}
.tagbox__opcja:hover, .tagbox__opcja.is-akt { background: var(--mist-50); }
/* Ile żartów już siedzi w temacie — ta sama miara, po której lista jest
   posortowana, więc kolejność nie wygląda na przypadkową. */
.tagbox__ile { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-40); }

/* Chip jako etykieta radia — sam input chowamy. */
.chip--filtr input { position: absolute; opacity: 0; pointer-events: none; }
label.chip--filtr { cursor: pointer; user-select: none; }

/* Potwierdzenie — jedyny kolor stanu w systemie. */
.banner {
	display: flex; align-items: center; gap: var(--space-2);
	margin: var(--space-3) 0; padding: 12px 14px;
	border-radius: var(--r-input); background: var(--jade-soft); color: var(--jade);
	font-family: var(--font-body); font-size: 14px; font-weight: 500;
}
.banner__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--jade); flex: none; }

.rzad--zgloszenie { gap: var(--space-3); }
.zgl__tresc { flex: 1; min-width: 0; font-size: 14px; color: var(--ink-60); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.zgl__stan { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; flex: none; }
.zgl__stan--nowe { color: var(--ink-40); }
.zgl__stan--przyjete { color: var(--jade); }
.zgl__stan--usuniete { color: var(--rose-700); }

/* --- panel admina --------------------------------------------------------- */
/* Jedyny ekran w serwisie z prawdziwą tabelą — Użytkownicy mają zbyt wiele
   kolumn na karty jak gdzie indziej. white-space: nowrap i owijka z suwakiem,
   żeby na telefonie dało się ją przewinąć w bok zamiast łamać w nieskończoność. */
.admin-tabela-wrap { overflow-x: auto; margin-top: var(--space-3); }
.admin-tabela { width: 100%; border-collapse: collapse; font-size: 14px; white-space: nowrap; }
.admin-tabela th, .admin-tabela td { padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--mist-100); }
.admin-tabela th {
	font-family: var(--font-meta); font-size: 11px; font-weight: 500;
	text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-40);
}
.admin-tabela tbody tr:hover { background: var(--mist-50); }

.admin-szukaj { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin: var(--space-3) 0; }
.admin-szukaj__pole {
	flex: 1; min-width: 180px; height: 40px; padding: 0 14px;
	border: 1px solid var(--line-input); border-radius: var(--r-input);
	background: var(--surface); font-family: var(--font-body); font-size: 14px; color: var(--ink);
}
.admin-szukaj__czek { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink-60); white-space: nowrap; }

.admin-lista { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-3); }
.admin-wiersz { background: var(--surface); border-radius: var(--r-card); box-shadow: var(--elev-1); padding: 14px 16px; }
/* Kreska po lewej, nie tło na całej karcie: karta z podejrzanym komentarzem ma
   zostać czytelna jak każda inna, tylko oznaczona — nie zamieniać w baner. */
.admin-wiersz--flaga { box-shadow: inset 3px 0 0 var(--terra), var(--elev-1); }
.admin-wiersz__glowa { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin-bottom: 6px; }
.admin-wiersz__nick { font-family: var(--font-display); font-weight: 600; font-size: 14px; color: var(--ink); }
.admin-wiersz__meta { font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-40); }
.admin-wiersz__tresc { margin: 0 0 10px; font-size: 15px; line-height: 1.5; color: var(--ink); overflow-wrap: anywhere; white-space: pre-line; }
/* .tagi-rzad liczy zwykle na gap rodzica (.wiersz jest flex-column) — w karcie
   admina rodzic nim nie jest, więc bez własnego marginesu tagi klejiłyby się
   wprost do rzędu przycisków pod spodem. */
.admin-wiersz .tagi-rzad { margin: 0 0 10px; }

/* Terra, nie róż: ten sam ceglasty ton co głos w dół pod żartem — róż zostaje
   przy puencie i reakcjach, nie przy ostrzeżeniach moderacji. */
.admin-badge {
	display: inline-flex; align-items: center; height: 20px; padding: 0 8px;
	border-radius: var(--r-pill); background: var(--terra-soft); color: var(--terra);
	font-family: var(--font-meta); font-size: 10px; font-weight: 600;
	text-transform: uppercase; letter-spacing: 0.03em;
}
/* Głowa kolejki inkubatora — jade, nie terra: to nie ostrzeżenie, tylko dobra wiadomość. */
.admin-badge--jade { background: var(--jade-soft); color: var(--jade); }

.admin-akcje { display: flex; gap: var(--space-2); }
.admin-akcja {
	display: inline-flex; align-items: center; gap: 5px; height: 32px; padding: 0 12px;
	border-radius: var(--r-pill); border: 1px solid var(--line); background: var(--surface);
	font-family: var(--font-display); font-size: 13px; font-weight: 500; cursor: pointer;
	color: var(--ink); text-decoration: none;
	transition: background-color 140ms ease, color 140ms ease;
}
.admin-akcja:hover { text-decoration: none; }
/* Zatwierdź/odrzuć jako jade/terra — te same dwa kolory co kciuk w górę i w dół
   pod żartem, bo to dokładnie ta sama decyzja, tylko podjęta raz za wszystkich. */
.admin-akcja--zatwierdz { color: var(--jade); border-color: var(--jade-soft); }
.admin-akcja--zatwierdz:hover { background: var(--jade-soft); }
.admin-akcja--odrzuc, .admin-akcja--usun { color: var(--terra); border-color: var(--terra-soft); }
.admin-akcja--odrzuc:hover, .admin-akcja--usun:hover { background: var(--terra-soft); }
/* Edycja to zwykła nawigacja, nie decyzja — stąd azure, ten sam kolor co
   reszta linków w serwisie, a nie jade/terra zarezerwowane dla aprobaty/odrzutu. */
.admin-akcja--edytuj { color: var(--azure); border-color: var(--azure-soft); }
.admin-akcja--edytuj:hover { background: var(--azure-soft); }

/* --- callout ------------------------------------------------------------------ */
/* Wyjaśnia, czemu czegoś nie da się zrobić, i od razu daje drogę wyjścia. */
.callout {
	display: flex; gap: var(--space-3); align-items: flex-start;
	margin-bottom: var(--space-4); padding: 14px 16px;
	border-radius: var(--r-card); background: var(--azure-soft); color: var(--azure-700);
}
.callout__ikona { flex: none; display: inline-flex; margin-top: 1px; color: var(--azure); }
.callout__tytul { margin: 0; font-family: var(--font-display); font-size: 15px; font-weight: 600; color: var(--azure-700); }
.callout__opis { margin: 4px 0 0; font-family: var(--font-body); font-size: 14px; line-height: 1.45; color: var(--azure-700); }
.callout__link {
	display: inline-block; margin-top: var(--space-2);
	font-family: var(--font-display); font-size: 14px; font-weight: 600; color: var(--azure);
}
.callout__link:hover { color: var(--azure-700); }

/* Pusty stan wewnątrz sekcji nie może kleić się z następnym nagłówkiem. */
.sekcja .pusto { margin-bottom: var(--space-4); }
.licznik-boczny { margin-left: auto; font-family: var(--font-meta); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-40); }
.licznik-boczny + .rzad__ch { margin-left: var(--space-2); }

/* --- slide-up z ustawieniami gościa ------------------------------------------- */
/* Ta sama mechanika co filtry: wysokość z treści (.sheet--info), formularz jest
   ciałem arkusza, dochodzi tylko oddech nad dolną krawędzią telefonu. */
.sheet--ustawienia .sheet__info { padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px)); }

/* Buźki w siatce, nie w poziomym pasie: jest ich kilkanaście i wszystkie mają
   być widoczne naraz — przewijany pas kazałby szukać tej ładniejszej za
   krawędzią. auto-fit z minimum 56 px trzyma pięć w rzędzie na 430 px i schodzi
   do czterech na najwęższych ekranach, bez media query. */
.buzki {
	display: grid; grid-template-columns: repeat(auto-fit, minmax(56px, 1fr));
	gap: var(--space-3); margin-top: 4px;
}

/* Cały kafelek jest polem dotyku — radio siedzi w środku niewidoczne, bo
   zaznaczenie pokazuje obwódka buźki, a nie kropka obok niej. */
.buzka {
	position: relative; display: flex; align-items: center; justify-content: center;
	aspect-ratio: 1; border-radius: 50%; cursor: pointer;
	/* Przezroczysta obwódka od początku, żeby zaznaczenie nie przesuwało
	   sąsiadów o dwa piksele. */
	border: 2px solid transparent; transition: border-color 120ms ease;
}
.buzka input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.buzka img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; }
.buzka:hover { border-color: var(--line); }
/* Zaznaczenie prosto z pola — inaczej niż chipy filtrów, którym klasę .is-on
   przestawia JS: tam radio bywa przestawiane z kodu, tutaj zawsze klika w nie
   człowiek, więc :checked wystarczy i działa też bez JS-a. */
.buzka:has(input:checked) { border-color: var(--ink); }
.buzka:has(input:focus-visible) { outline: 2px solid var(--azure); outline-offset: 2px; }

/* Zdanie o tymczasowości i link do logowania stoją pod siatką, ciszej od niej. */
.sheet--ustawienia .konto__tekst { font-size: 14px; }
.callout__link--srodek { align-self: center; margin-top: 0; }

/* --- profil: dane konta ------------------------------------------------------ */

/* Mail na ekranie edycji stoi jako tekst, ale ma być widać, że to on jest
   kontem — stąd ciemniejszy niż reszta zdań w karcie. */
.konto__tekst--mocny { color: var(--ink); font-weight: 500; }

/* Potwierdzenie zapisu tam, gdzie normalnie ląduje błąd — jedno miejsce nad
   formularzem, raz różowe, raz zielone. Inaczej oko szuka odpowiedzi w dwóch
   miejscach naraz. */
.alert--ok { background: var(--jade-soft); color: var(--jade); }

/* --- onboarding -------------------------------------------------------------- */

/* Logo bez paska zakładek pod spodem, więc ekran dostaje własny oddech
   u dołu — inaczej ostatni przycisk klei się do krawędzi telefonu. */
.witaj { padding-bottom: calc(32px + env(safe-area-inset-bottom, 0px)); }

/* Powitalny film na pierwszym kroku. Plik nie ma kanału alfa — ma białe tło —
   więc przezroczystość robi mix-blend-mode: multiply: biel pomnożona przez
   kolor pod spodem daje dokładnie ten kolor, a ciemny rysunek zostaje sobą.
   Trzyma się to tylko dlatego, że pod spodem leży jednolity --bg-screen;
   na zdjęciu albo gradiencie sztuczka by się posypała, a na ciemnym tle
   zniknęłaby cała animacja. Gdyby przeglądarka nie znała mieszania, zostaje
   biały kwadrat na prawie białym tle — wada kosmetyczna, nie awaria.

   Kwadrat 960 × 960 opisany aspect-ratio, żeby miejsce było zajęte, zanim
   pierwsza klatka dojdzie — inaczej nagłówek podskakuje w trakcie czytania. */
.powitanie { display: flex; justify-content: center; padding-bottom: var(--space-2); }
.powitanie__film {
	display: block;
	width: min(200px, 52%);
	height: auto;
	aspect-ratio: 1;
	mix-blend-mode: multiply;
	pointer-events: none;
}

/* Przy ograniczonym ruchu film stoi (pauzę zakłada app.js) — tu zostaje sama
   pierwsza klatka, żeby zatrzymany obraz nie wyglądał na zepsuty. */
@media (prefers-reduced-motion: reduce) {
	.powitanie__film, .seria__film, .seria-card__film { opacity: .9; }
}

/* Kreski postępu: pełna szerokość podzielona na trzy, przebyte na ciemno.
   Napis obok jest tylko dla czytnika ekranu — widzącym mówi to samo układ. */
.kroki { display: flex; gap: 6px; padding: 0 var(--screen-gutter) var(--space-4); }
.kroki__kreska { flex: 1; height: 3px; border-radius: 2px; background: var(--line); transition: background-color 200ms ease; }
.kroki__kreska.is-on { background: var(--ink); }
.kroki__opis {
	position: absolute; width: 1px; height: 1px; overflow: hidden;
	clip-path: inset(50%); white-space: nowrap;
}

/* Zdanie pod polem — mówi, po co pytamy, zanim ktoś zdąży się zastanowić. */
.pole__podpowiedz { font-family: var(--font-meta); font-size: 12px; color: var(--ink-40); }

/* „Pomiń" pod głównym przyciskiem, nie obok: pominięcie ma być łatwe, ale nie
   ma wyglądać na równorzędny wybór. */
.witaj__akcje { display: flex; flex-direction: column; gap: var(--space-2); }

/* Wgrywanie pliku. Rodzime pole jest schowane wizualnie, ale zostaje w drzewie
   i łapie dotknięcie całej etykiety — stąd brak display:none, który zabrałby
   je też czytnikom ekranu i klawiaturze. */
.wgraj { display: flex; align-items: center; gap: var(--space-3); cursor: pointer; margin-top: 4px; }
.wgraj__input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.wgraj__przycisk {
	display: inline-flex; align-items: center; gap: 8px; flex: none;
	height: 40px; padding: 0 16px; border-radius: var(--r-input);
	border: 1px solid var(--line-input); background: var(--surface);
	font-family: var(--font-body); font-size: 14px; color: var(--ink);
}
.wgraj:hover .wgraj__przycisk { border-color: var(--ink); }
.wgraj:has(.wgraj__input:focus-visible) .wgraj__przycisk { outline: 2px solid var(--azure); outline-offset: 2px; }
.wgraj__nazwa {
	font-family: var(--font-meta); font-size: 12px; color: var(--ink-40);
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Wgrane zdjęcie stoi w tej samej siatce co gotowe buźki, ale ma obwódkę
   nawet niezaznaczone — inaczej ginie między szesnastoma rysunkami. */
.buzka--wlasna { border-color: var(--line); }

/* Przycisk wgrywania pod polem pliku, nie obok: obok napisu z nazwą robił się
   rząd, który na telefonie i tak się łamał. */
.wgraj__wyslij { margin-top: var(--space-3); }

/* Świeżo wgrane zdjęcie, jeszcze przed zapisaniem w koncie. Siatki buziek nie
   ma wtedy na ekranie, więc zdjęcie dostaje środek i rozmiar, przy którym
   widać, co właściwie wyszło z kadrowania — kafelek wielkości pozostałych
   buziek kazałby mrużyć oczy przy czymś, co za chwilę zostanie na stałe. */
.wgrane { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); margin: var(--space-3) 0 0; }
.wgrane__foto {
	display: block; width: 176px; height: 176px; max-width: 64vw; max-height: 64vw;
	border-radius: 50%; object-fit: cover; border: 2px solid var(--ink);
}
.wgrane__opis { font-family: var(--font-meta); font-size: 12px; color: var(--ink-40); text-align: center; }

/* „Wróć" pod „Zapisz", nie obok — tak samo jak „Pomiń" w onboardingu: wyjście
   ma być pod ręką, ale nie ma wyglądać na równorzędny wybór. */
.wgrane__akcje { display: flex; flex-direction: column; gap: var(--space-2); }

/* Płeć na trzech kafelkach, nie na pigułkach. Pigułki różnej szerokości łamały
   się do kolumny i wyglądały jak lista przypadkiem zrobiona z guzików. Trzy
   równe pola mieszczą się w jednym rzędzie i zostaje miejsce na ikonę, po
   której widać różnicę, zanim się przeczyta podpis.

   Radio siedzi niewidoczne pod kafelkiem — zaznaczenie niesie sam kafelek,
   jak przy buźkach, więc bez JS-a działa tak samo. */
.plcie { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2); margin-top: 4px; }
.plec { position: relative; display: flex; cursor: pointer; }
.plec input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }

.plec__kafel {
	display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
	width: 100%; min-height: 84px; padding: 12px 8px; text-align: center;
	border: 1px solid var(--line); border-radius: var(--r-input);
	background: var(--surface); color: var(--ink-60);
	user-select: none; transition: border-color 120ms ease, background-color 120ms ease, color 120ms ease;
}
.plec__ikona { display: flex; color: var(--ink-45); transition: color 120ms ease; }
.plec__nazwa { font-family: var(--font-body); font-size: 14px; line-height: 1.25; }

.plec:hover .plec__kafel { border-color: var(--ink-45); }

/* Wybrany kafelek zalany na ciemno — tak samo jak włączony filtr i zaznaczona
   buźka, żeby „to jest wybrane" wyglądało w serwisie zawsze jednakowo. */
.plec:has(input:checked) .plec__kafel {
	background: var(--ink); border-color: var(--ink); color: #fff;
}
.plec:has(input:checked) .plec__ikona { color: #fff; }
.plec:has(input:focus-visible) .plec__kafel { outline: 2px solid var(--azure); outline-offset: 2px; }



/* --- menu pod zębatką -------------------------------------------------------- */

/* <details> jako dropdown: rozwijanie i klawiatura są natywne, JS dokłada tylko
   zamykanie kliknięciem obok (patrz app.js). Pozycjonowanie względem kółka
   zębatki, dlatego .menu jest kotwicą. */
.menu { position: relative; }

/* Trójkącik, który <summary> rysuje sam, zdejmujemy w obu składniach — Safari
   rozumie ::-webkit-details-marker, reszta list-style. */
.menu > summary { list-style: none; cursor: pointer; }
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary::marker { content: ''; }

/* Otwarta zębatka zostaje wciśnięta, żeby było widać, skąd wyszło menu. */
.menu[open] > summary .kolko { background: var(--ink); color: #fff; }

.menu__lista {
	position: absolute; top: calc(100% + 8px); right: 0; z-index: 20;
	min-width: 208px; padding: 6px;
	background: var(--surface); border: 1px solid var(--line);
	border-radius: var(--r-input); box-shadow: 0 12px 28px rgb(16 34 44 / 0.14);
	display: flex; flex-direction: column; gap: 2px;
}

.menu__poz {
	display: flex; align-items: center; gap: 10px;
	padding: 11px 12px; border-radius: 10px;
	font-family: var(--font-body); font-size: 15px; color: var(--ink);
	text-align: left; white-space: nowrap;
}
.menu__poz:hover { background: var(--bg-alt); text-decoration: none; color: var(--ink); }
.menu__poz .ic { color: var(--icon-idle); flex: none; }

/* Wylogowanie jest przyciskiem w formularzu (POST), więc musi udawać pozostałe
   pozycje — i rozpychać się na całą szerokość, bo <button> tego nie robi sam. */
.menu__poz--btn { width: 100%; background: none; border: 0; cursor: pointer; font: inherit; }
.menu__lista form { display: flex; }


/* =========================================================================
   Biurko — od 900 px w górę
   -------------------------------------------------------------------------
   Wszystko, co odróżnia szeroki ekran od telefonu, stoi w tym jednym miejscu:
   niżej serwis zostaje tym, czym był — aplikacją na telefon. Granica na 900 px,
   bo tablet w pionie (768) obsługuje się kciukiem i pasek na dole jest tam
   nadal lepszy niż zakładki w nagłówku.

   Trzy rzeczy zmieniają się naraz:
   • treść dostaje miarę czytania zamiast całej szerokości okna,
   • pasek zakładek wyjeżdża z dołu na górę i staje się paskiem nawigacji,
   • arkusze wsuwają się od prawej zamiast od dołu.
   ========================================================================= */
@media (min-width: 900px) {

:root {
	/* Miara czytania. Żart stoi w 18 px krojem do czytania (.setup), więc
	   700 px to jakieś 60–65 znaków w wierszu — środek widełek, w których oko
	   trafia z końca linii na początek następnej bez szukania. Cała szerokość
	   okna dawała ponad 160 znaków i czytało się to jak tabelka. */
	--tresc: 700px;

	/* Wysokość paska nawigacji. Pod nią podkleja się nagłówek ekranu, więc
	   musi być liczbą, a nie „tym, co wyjdzie z treści". */
	--pasek: 64px;
}

/* --- kolumna treści ------------------------------------------------------- */
/* Miarę robi padding, nie max-width: dzięki temu tło zostaje na całą szerokość
   (sekcja „Najnowsze" ma własne, .sekcja--alt), a w środku i tak stoi kolumna.
   max() pilnuje, żeby przy oknie węższym od miary został zwykły gutter. */
.head,
.sekcja,
.zart,
.losuj,
.zakladki-pod,
.chipsy--gutter {
	padding-inline: max(var(--screen-gutter), calc((100% - var(--tresc)) / 2));
}

/* Karuzela Tematów i pasek postępu zostają na pełnej szerokości — pigułki mają
   jechać przez cały ekran, a kreska postępu jest kreską ekranu, nie kolumny. */

/* Rozpływanie krawędzi dopiero od biurka: na telefonie pas kończy się na
   krawędzi ekranu i nic tam nie gaśnie (patrz .karuzela wyżej w pliku).
   Przystanki w środku układają krzywą S: rampa zwalnia i przy samej krawędzi,
   i przy pełnym kryciu, więc nie widać, gdzie zanik się zaczyna, a gdzie
   kończy. Długość zaniku idzie z szerokości okna — stała wartość ginęłaby na
   biurku, gdzie pas potrafi mieć 1400 px. */
.karuzela {
	--zanik: clamp(90px, 30vw, 420px);
	--maska-zanik: linear-gradient(90deg,
		transparent,
		rgba(0, 0, 0, 0.12) calc(var(--zanik) * 0.25),
		rgba(0, 0, 0, 0.5)  calc(var(--zanik) * 0.5),
		rgba(0, 0, 0, 0.88) calc(var(--zanik) * 0.75),
		#000 var(--zanik),
		#000 calc(100% - var(--zanik)),
		rgba(0, 0, 0, 0.88) calc(100% - var(--zanik) * 0.75),
		rgba(0, 0, 0, 0.5)  calc(100% - var(--zanik) * 0.5),
		rgba(0, 0, 0, 0.12) calc(100% - var(--zanik) * 0.25),
		transparent);
	-webkit-mask-image: var(--maska-zanik);
	mask-image: var(--maska-zanik);
}

/* Admin — Użytkownicy wychodzi z kolumny czytania: to tabela z jedenastoma
   kolumnami, nie akapit, więc miara 700 px tylko ją ściska do przewijania
   w bok, choć ekran ma miejsce. Selektor podwójny (.sekcja.admin-pelny), żeby
   wygrać ze wspólną regułą wyżej bez !important. */
.head.admin-pelny,
.sekcja.admin-pelny {
	padding-inline: var(--screen-gutter);
}

/* --- pasek zakładek jako nawigacja ---------------------------------------- */
/* Ten sam <nav> co na telefonie, tylko położony na górze i rozłożony w wiersz:
   jedna lista zakładek w szablonie, żadnego drugiego menu do utrzymania.
   Sticky, nie fixed — pasek stoi w przepływie, więc nie trzeba odsuwać treści
   jego wysokością (patrz .tabbar + .app niżej). */
.tabbar {
	position: sticky; top: 0; bottom: auto; left: auto; transform: none;
	width: auto; max-width: none; height: var(--pasek); z-index: 7;
	justify-content: flex-start; align-items: center; gap: var(--space-1);
	padding: 0 28px;
	border-top: none; border-bottom: 1px solid var(--azure-700);
	background: var(--azure);
}

/* Pasek na biurku jest jedyną listwą na całą szerokość okna, więc bierze kolor
   marki zamiast bieli: ekran zaczyna się wtedy od czegoś, co widać, a kolumna
   treści pod spodem zostaje spokojna. Wszystko w środku idzie na biel — logo
   ma własny plik (patrz _tabbar.php), reszta stoi tutaj. */
.tabbar :focus-visible { outline-color: #fff; }

/* Marka wraca na swoje miejsce w lewym górnym rogu. Na ekranach, które miały
   logo we własnym nagłówku (Dziś, ekran żartu), znika ono niżej — dwa te same
   logo jedno pod drugim czytałyby się jak błąd składu. */
.tabbar__marka { display: inline-flex; align-items: center; margin-right: auto; }
/* Logo o 30% większe od pierwszej miary: 28 px ginęło w 64-pikselowym pasku,
   który na biurku jest jedyną listwą marki na całą szerokość okna. */
.tabbar__marka .lockup__logo { height: 36px; }
.head--logo .lockup { display: none; }

/* Na ekranie żartu po logo zostaje w nagłówku sama strzałka powrotu — a gdy
   i jej nie ma (żart otwarty z udostępnionego linku, patrz zart.php), nie
   zostaje nic i cała listwa schodzi z ekranu zamiast świecić pustą bielą.
   Warunek po .ikonka--kolko, bo miejsce po strzałce trzyma pusta .ikonka. */
.head--logo:not(:has(.ikonka--kolko)) { display: none; }

.tabbar__item {
	flex-direction: row; gap: var(--space-2);
	height: 40px; min-height: 0; min-width: 0; padding: 0 13px;
	/* Kształt zakładka ma zawsze, nie dopiero pod kursorem: --r-pill zostawiał
	   równy owal, który wychodził na wierzch przy zjeżdżaniu myszą (tło gaśnie
	   przez 140 ms, promień wracał natychmiast — przez te 140 ms po pasku
	   pełzała gasnąca pigułka) i rysował obrys focus-visible. Wartości
	   z --r-btn-mirror, przeskalowane o połowę tak samo jak zaznaczenie niżej:
	   pasek ma tylko chybotliwe narożniki Puenty, a lustrzany wariant odróżnia
	   zakładkę zwykłą od bieżącej kształtem, nie samym kolorem. */
	border-radius: 8px 20px 9px 18px / 17px 9px 19px 10px; color: #fff;
	transition: background-color 140ms ease, color 140ms ease;
}
/* Najechanie rozjaśnia tło samym białym welonem, a nie osobnym kolorem — pasek
   ma jedno tło i wszystko, co się na nim dzieje, jest jego rozjaśnieniem.
   Sam kształt się nie zmienia: welon wypełnia narożnik, który zakładka nosi
   i w spoczynku, więc pod kursorem nic nie przeskakuje. */
.tabbar__item:hover {
	background: rgba(255, 255, 255, 0.16); color: #fff;
}
/* Zakładka bieżąca odwraca się na biało: na błękicie sam jaśniejszy błękit
   ginął, a biała plama mówi „tu jesteś" z drugiego końca pokoju. Rogi bierze
   z --r-btn, czyli firmowego chybotliwego narożnika Puenty — żaden przycisk
   w tym systemie nie jest równym owalem, a --r-pill robił z zaznaczenia
   idealną pigułkę, jakiej nie ma nigdzie indziej w serwisie. */
.tabbar__item[aria-current="page"] {
	background: #fff; color: var(--azure-700);
	/* Wartości z tokenu, przeskalowane o połowę: --r-btn jest pisane pod
	   przycisk o wysokości 50 px, a zakładka ma 40 px — przy oryginalnych
	   promieniach pionowe sumy przekraczają wysokość, przeglądarka ściąga
	   wszystkie proporcjonalnie i z chybotania zostaje równa pigułka.
	   Te same proporcje narożników, tylko w skali tego pudełka. */
	border-radius: 13px 17px 14px 18px / 16px 13px 17px 14px;
}
.tabbar__label { font-family: var(--font-display); font-size: 14px; font-weight: 500; letter-spacing: 0; }

/* Ikony o 20% mniejsze niż na telefonie. Tam stoją nad podpisem i są głównym
   znakiem zakładki, tutaj stoją obok niego i mają go tylko zapowiadać — w tej
   samej mierze co na telefonie przykrywały 14-pikselowy napis obok.
   Rozmiar zdejmowany w CSS, a nie w atrybutach svg: te same ikony rysuje
   _tabbar.php dla obu szerokości (patrz icon() tamże).
   Buźka Profilu i kostki Losuj idą tą samą skalą, bo stoją w tym samym
   rzędzie — to podpisy trzymają wzrost, nie każdy znak z osobna. */
.tabbar__item .ic { width: 17.6px; height: 17.6px; }
.tabbar__item--fab .kostki { width: 19.2px; height: 19.2px; }
.avatar--tab { width: 19.2px; height: 19.2px; }

/* Losuj zostaje jedynym kolorowym punktem paska — na telefonie był wyniesionym
   krążkiem nad krawędzią, tutaj jest różową pigułką: to samo „to nie jest
   kolejna zakładka, to akcja", tylko środkami, które ma wiersz.
   Krążek pod spodem rozpuszcza się do samej ikony — zostaje z niego ruch. */
/* Koniec rzędu układa się inaczej niż w HTML-u: na telefonie Losuj stoi
   w środku paska, bo tam środek jest miejscem pod kciuk, a na biurku środek
   wiersza nie znaczy nic — obie akcje idą na koniec, tuż przed buźkę.
   Kolejnością flexa, a nie w szablonie: zakładki są jedną listą dla obu
   szerokości. Grupa 0 zostaje w kolejności z HTML-u (marka, Dziś, Tematy,
   Ranking), potem Losuj, potem Dodaj, a Profil — ostatni w pasku — zamyka. */
.tabbar__item--fab { order: 1; }
.tabbar__item--dodaj { order: 2; }
/* Passa ma order: 3 (reguła niżej) i wchodzi między akcje a buźkę. */
.tabbar__item:last-child { order: 4; }

/* Dodawanie wchodzi do paska (zaraz po Rankingu — patrz _tabbar.php) i znika
   z nagłówków ekranów: ten sam plus w dwóch miejscach naraz czytałby się jak
   dwie różne akcje. Zostaje w DOM-ie, więc wystarczy go schować — helper stoi
   w nagłówkach czterech ekranów i wycinanie go z każdego z osobna oznaczałoby
   warunek na szerokość okna w PHP, której PHP nie zna. */
.tabbar__item--dodaj { display: flex; margin-right: 20px; }
.head .ikonka--kolko[data-akcja="dodaj"] { display: none; }

/* Nagłówek Dziś schodzi z ekranu w całości: logo oddał paskowi, plusa też,
   a passa przeniosła się do paska (reguły niżej) — zostawała z niego biała
   listwa z jedną pigułką albo i pusta. Ten sam zabieg co na ekranie żartu
   (.head--logo wyżej). */
.head--dzis { display: none; }

/* Pasek postępu klei się pod nagłówek, ale --head-h mierzy sam nagłówek, nie
   jego odległość od góry okna — na biurku dochodzi do tego wysokość paska
   nawigacji, inaczej postęp podjeżdża pod pasek i chowa się za nim. Przy
   schowanym nagłówku Dziś --head-h wynosi 0 i kreska siada wprost pod paskiem. */
.progress { top: calc(var(--pasek) + var(--head-h, 0px)); }

/* Losuj wygląda jak każda inna pozycja paska: to samo tło, ten sam podpis, ta
   sama biała pigułka, gdy jest bieżącą zakładką. Wyróżnia je ruch, nie kolor —
   pasek ma już jeden mocny akcent (siebie), a różowa pigułka w środku rzędu
   ciągnęła oko mocniej niż zakładka, na której się stoi. Krążek spod telefonu
   rozpuszcza się do samej ikony. */
.tabbar__item--fab .fab {
	width: auto; height: auto; margin: 0; padding: 0;
	border: none; border-radius: 0; background: none; color: inherit;
}
/* Krążek zmienia kolor pod kursorem — ale krążka tu nie ma, a reguła telefonu
   (.tabbar__item--fab:hover .fab, wyżej w arkuszu) jest o klasę bardziej
   szczegółowa niż zerowanie tła obok, więc wygrywała z nim i malowała za
   kostkami różowy kwadrat: tło zostawało z telefonu, a rogi już nie. */
.tabbar__item--fab:hover .fab { background: none; }
/* Fala rozchodząca się spod krążka nie ma już spod czego wypływać — bez krążka
   zostałyby z niej dwa błękitne pierścienie wokół kostek. */
.fab--rzut::before, .fab--rzut::after { display: none; }

/* Kostki rzucają się same, co parę chwil — to jedyna zapowiedź tego, co ten
   link robi, a bez koloru musi ją nieść ruch. Rzut zajmuje ostatnią dziesiątą
   część pętli (9 s × 9% ≈ 800 ms, czyli tyle samo, co rzut przy wejściu na
   Losuj), a przez resztę ikona stoi — ciągły ruch w pasku nawigacji chodziłby
   po oczach przy każdym czytaniu. Pierwszy rzut wypada dopiero po ośmiu
   sekundach, bo burza klatek stoi na końcu pętli: strona nie otwiera się
   podrygującą ikoną.

   Na .fab, nie na .kostki: pod kursorem koziołkują same kostki (reguła niżej),
   a dwie animacje na jednym elemencie wykluczają się — ta z hovera zerowałaby
   nawyk i po zjechaniu myszą zaczynałby się on od nowa. Tak nakładają się na
   siebie: nawyk buja całą ikoną, hover obraca kostkami w środku.
   :not(.fab--rzut) zostawia ekran Losuj przy jego własnym rzucie na wejściu. */
.tabbar__item--fab .fab:not(.fab--rzut) {
	animation: kostki-nawyk 9s cubic-bezier(.3, .7, .3, 1) infinite;
}
@keyframes kostki-nawyk {
	0%, 91%, 100% { transform: translateY(0) rotate(0deg); }
	93%   { transform: translateY(2px)  rotate(7deg); }    /* zamach */
	95.5% { transform: translateY(-4px) rotate(-30deg); }  /* wyrzut */
	97.5% { transform: translateY(1px)  rotate(19deg); }   /* lądowanie */
	99%   { transform: translateY(0)    rotate(-6deg); }
}

/* Pod kursorem kostki koziołkują tymi samymi klatkami co przy wejściu na
   Losuj — jeden ruch, nie dwa. */
.tabbar__item--fab:hover .kostki { animation: kostki-koziolek 760ms cubic-bezier(.3,.7,.3,1) both; }

/* Nagłówek ekranu podkleja się pod pasek, a nie pod krawędź okna. */
.head { top: var(--pasek); }

/* Aura idzie przez całe okno, nie przez kolumnę czytania: na biurku tło ekranu
   jest tłem ekranu, a kadr urwany na 820 px czytał się jak niedomknięty pas za
   tekstem. --kolumna to i tak `none`, więc wystarczy zdjąć własne ograniczenie. */
.aura { max-width: none; }

/* Owale muszą urosnąć razem z kadrem. Ich --r (300–440 px) jest liczone pod
   kolumnę telefonu; rozstawione po szerokim oknie zostawiłyby między sobą
   puste pola zamiast jednego rozlanego tła. vw dokładane do promienia skaluje
   je z oknem, proporcja 0.74 zostaje ta sama co w regule bazowej, a rozmycie
   idzie w górę razem z nimi — inaczej większa plama ma ostrzejszą krawędź. */
.aura__owal {
	width: calc(var(--r) + 34vw);
	height: calc((var(--r) + 34vw) * 0.74);
	filter: blur(80px);
}

/* Losuj bierze cały ekran: jeden żart, nic pod nim i nic obok. Sekcja bierze
   wolną wysokość już w regule bazowej; biurko dokłada swój oddech i stawia
   żart na środku tej wysokości, bo ma pod nim jeszcze przycisk losowania —
   tekst dociągnięty do góry zostawiałby między nimi pustą połowę okna.
   Na telefonie przycisku nie ma (losuje kostka z paska) i tam żart zaczyna
   się pod nagłówkiem. */
.app--losuj .losuj { padding-block: 32px 40px; }
.losuj__zart { flex: 1; justify-content: center; }

/* Im krótszy żart, tym większe litery — ten sam skok co na telefonie, tylko
   o wiele głębszy, bo na biurku zostaje pod niego wolna wysokość (stopień
   wybiera miara_zartu() w helpers.php). Razem ze stopniem rośnie miara
   czytania: 50 px w kolumnie pisanej pod 18 px łamałoby się co pięć słów
   i z dwóch wersów robiło dziesięć. Proporcja zostaje ta sama: około 50–65
   znaków w wierszu. Telefon nie ma jak jej utrzymać — szerokość jest dana
   przez ekran — i dlatego tam stopnie są płytsze.
   --tresc, a nie szerokość sekcji: ta sama zmienna ustawia gutter nagłówka,
   więc tytuł „Losuj" zostaje w jednej osi z tekstem pod nim. */
.app--losuj-xl { --tresc: 1180px; }
.app--losuj-l  { --tresc: 1040px; }
.app--losuj-m  { --tresc: 900px; }
.app--losuj-s  { --tresc: 700px; }   /* tyle, co wszędzie: to stopień z list */

.app--losuj-xl .setup { font-size: 50px; line-height: 1.28; }
.app--losuj-l  .setup { font-size: 38px; line-height: 1.34; }
.app--losuj-m  .setup { font-size: 28px; line-height: 1.42; }
.app--losuj-s  .setup { font-size: 18px; line-height: 1.5; }

/* Przycisk losowania: szeroki na tyle, żeby był celem, a nie guzikiem, i na
   środku kolumny, bo losowanie jest tu jedyną akcją ekranu. Siada pod żartem
   sam z siebie — wysokość nad sobą zabiera .losuj__zart. */
.losuj__rzut {
	display: inline-flex; align-self: center; flex: none;
	min-height: 64px; padding: 0 40px; gap: 12px; font-size: 19px;
}
/* Kostki odgrywają rzut przy wejściu na ekran — tymi samymi klatkami co kostka
   w pasku zakładek (patrz .fab--rzut wyżej), bo to ten sam gest: strona
   przyszła z nowym losowaniem. Koziołek dostaje opakowanie, a nie samo svg:
   pod kursorem ma zagrać jeszcze raz, a ta sama animacja na tym samym
   elemencie nie rusza od nowa — musi być drugie pudełko.
   Rzutu (zamach i podskok) ani fali tu nie ma: obie są pisane pod krążek
   o średnicy 58 px, a przycisk na 300 px szeroki by się od nich rozdymał. */
.losuj__kostki { display: inline-flex; animation: kostki-koziolek 760ms cubic-bezier(.3, .7, .3, 1) both; }
.losuj__rzut .kostki__a { animation: kostki-drgnij-a 480ms ease-in-out both; }
.losuj__rzut .kostki__b { animation: kostki-drgnij-b 480ms ease-in-out both; }
.losuj__rzut .kostki__oczko {
	animation: oczko 200ms ease-out both;
	animation-delay: calc(430ms + var(--kolej) * 70ms);
}
.losuj__rzut:hover .kostki { animation: kostki-koziolek 760ms cubic-bezier(.3, .7, .3, 1) both; }

/* Pasek stoi w przepływie, więc zabiera swoją wysokość z okna — bez tego
   każdy ekran byłby o 64 px wyższy od widoku i dostawał suwak z niczego.
   Selektor po sąsiedzie, bo onboarding ($golo) idzie bez paska i tam pełne
   100vh jest prawidłowe. */
.tabbar + .app { min-height: calc(100vh - var(--pasek)); }

/* Rezerwa na pasek zakładek u dołu jest już niepotrzebna — zostaje oddech. */
.app { padding-bottom: var(--space-8); }

/* Toast siedział nad paskiem zakładek; paska u dołu nie ma, więc schodzi niżej. */
.toast { bottom: 28px; }

/* --- arkusze jako modal na środku ------------------------------------------ */
/* Wjazd z dołu jest gestem telefonu — kciuk sięga dolnej krawędzi. Na biurku
   nie ma ani kciuka, ani dolnej krawędzi, do której warto dosuwać, więc ten
   sam arkusz staje pośrodku ekranu jako spory modal: stała szerokość,
   wysokość z treści, do koła przyciemnione tło (.scrim).

   Wszystkie arkusze naraz — komentarze, dodawanie, żart z karuzeli, filtry,
   ustawienia, zapis — bo każdy z nich stoi na klasie .sheet i różni się
   wyłącznie zawartością. */
.sheet,
.sheet--info,
.sheet--dodaj,
.sheet--dowcip {
	top: 50%; bottom: auto; left: 50%; right: auto;
	transform: translate(-50%, -50%);
	width: min(600px, calc(100vw - 64px)); max-width: none;
	height: auto; max-height: min(720px, 86vh);
	border-radius: 22px; overflow: hidden;
	box-shadow: var(--elev-2);
	animation: wjedz-modal 200ms ease both;
}
@keyframes wjedz-modal {
	from { opacity: 0; transform: translate(-50%, -50%) scale(0.96); }
	to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* Uchwyt do zsuwania palcem zamienia się w krzyżyk: myszą nie ściąga się
   niczego w dół, a arkusz komentarzy jako jedyny nie ma własnego nagłówka
   z zamknięciem (pozostałe mają je w .sheet__head). Przycisk jest ten sam,
   z tym samym podpisem dla czytnika ekranu — zmienia się tylko rysunek. */
.sheet__handle {
	position: absolute; top: 15px; right: 16px; z-index: 1;
	width: 34px; height: 34px; margin: 0; border-radius: 50%;
	background: var(--mist-100); transition: background-color 140ms ease;
}
.sheet__handle::before, .sheet__handle::after {
	content: ""; position: absolute; top: 50%; left: 50%;
	width: 14px; height: 2px; border-radius: 1px; background: var(--ink);
}
.sheet__handle::before { transform: translate(-50%, -50%) rotate(45deg); }
.sheet__handle::after  { transform: translate(-50%, -50%) rotate(-45deg); }
.sheet__handle:hover { background: var(--azure-soft); }

/* Tytuł arkusza komentarzy (jedyny stojący luzem, poza .sheet__head) układa
   się jak nagłówki pozostałych arkuszy: do lewej, z kreską pod spodem
   i miejscem na krzyżyk po prawej. */
.sheet > .sheet__title {
	margin: 0; padding: 18px 60px 12px var(--screen-gutter);
	text-align: left; font-size: 20px;
	border-bottom: 1px solid var(--mist-100);
}

/* --- profil --------------------------------------------------------------- */
/* Kalendarz serii to czternaście dni, nie czternaście kolumn do wypełnienia:
   rozciągnięty na szerokość karty robił z dni kafle wielkości przycisku.
   Stały rozmiar kwadratu trzyma go paskiem, który ogarnia się jednym
   spojrzeniem — tak samo jak na telefonie, gdzie taki wychodził sam. */
.kalendarz { grid-template-columns: repeat(14, 30px); gap: 5px; }

}
