/* ===================================================================

   GÓRNE NAROŻNIKI (dawny #top-bar, zlikwidowany 2026-07-26)

   Nie ma już paska na całą szerokość. Zamiast niego dwa NIEZALEŻNE,
   pływające klastry `position: fixed` w górnych rogach:
     • #naroznik-lewy  — #logo (+ strzałka #sidebar-toggle),
     • #naroznik-prawy — pigułki: #egzamin-timer | #total-score |
                         #egzamin-koniec-bar (dwie ostatnie wzajemnie
                         wykluczające się, patrz style/exam.css).
   Oba `z-index: 10` — tyle, ile miał pasek, czyli nad panelami PDF (9)
   i pod panelem bocznym (#sidebar, 12).

   Dlaczego „pigułki" z własnym tłem: bez paska te elementy przejeżdżają
   NAD treścią zadania przy scrollowaniu i bez tła zlałyby się z tekstem.
   To jedyny powód ramek i tła — nie dekoracja.

   Klastry nie mają wspólnego kontenera ani wspólnej szerokości, więc
   (inaczej niż pasek) nie mogą wypchnąć strony w poziomie: dawny
   #bar-container dawał 13px poziomego scrolla przy 360px.
   Jeśli edytujesz ten blok, popraw sekcję „Górne narożniki"
   w ARCHITECTURE_CSS.md.

   =================================================================== */
#naroznik-lewy,
#naroznik-prawy {
    position: fixed;
    top: 16px;
    z-index: 10;
    display: flex;
    align-items: center;
    gap: 8px;
}
#naroznik-lewy {
    left: 20px;
}
/* OTWARTY PANEL: lewy narożnik wychodzi NAD przyciemnienie (11) i nad sam panel (12).
   Bez tego welon leżał na strzałce: wyglądała na wyszarzoną (czyli nieczynną), nie
   łapała hoveru, a kliknięcie w nią trafiało w przyciemnienie. Panel i tak się wtedy
   zamykał — bo klik w welon jest jego drugim sposobem zamykania (app/bootstrap.js) —
   więc z zewnątrz wyglądało to na działający przycisk i długo nie było widać usterki.
   Narożnik kończy się nad górną krawędzią panelu (56px, na telefonie 48px), więc
   wyższy z-index niczego nie zasłania. */
body.sidebar-otwarty #naroznik-lewy {
    z-index: 13;
}
#naroznik-prawy {
    right: 20px;
    justify-content: flex-end;
}

/* Wspólny wygląd pigułki prawego klastra. Poszczególne stany widoczności
   (zegar/„zakończ" tylko w egzaminie) są w style/exam.css. */
#total-score,
#egzamin-timer,
#naroznik-prawy button {
    box-sizing: border-box;
    padding: 6px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pigulka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 13px;
    line-height: 1.3;
    white-space: nowrap;
    text-align: center;
    color: var(--text);
}
#naroznik-prawy button {
    cursor: pointer;
}
/* ZASADA HOVERU (decyzja Henricha 2026-08-09, spójność UI etap 2):
   • chrome strony (pigułki narożników, strzałka panelu, wiersze sidebara,
     przełącznik trybu, krzyżyki paneli) → PODŚWIETLONE TŁO (--bg-muted /
     --bg-hover), ramka bez zmian;
   • kontrolki w TREŚCI zadania (odpowiedzi ABCD/PF, samoocena, przyciski
     „sprawdź", stopka, pigułki formularza) → CIEMNIEJSZA RAMKA
     (--border-strong), bo ich tło i inset box-shadow niosą już stany
     poprawne/błędne/.selected i hover tłem by z nimi kolidował. */
#naroznik-prawy button:hover {
    background-color: var(--bg-muted);
}

/* Logo jest linkiem do strony głównej (index.html) — wygląda jak zwykły napis.
   Dostaje tło (bez ramki), żeby zostało czytelne nad przewijaną treścią.
   Stała szerokość 200px (nie „tyle, ile tekstu"): panele PDF przejeżdżają POD
   logo (z-index 9 < 10), więc jego prostokąt tła ma być przewidywalny, a nie
   zmieniać się z długością numeru wersji. Na telefonie zdejmowane w responsive.css. */
#logo {
    box-sizing: border-box;
    min-width: 200px;
    padding: 6px 10px;
    border-radius: var(--radius-pigulka);
    background-color: var(--bg);
    white-space: nowrap;
    text-align: left;
    color: var(--text);
    text-decoration: none;
}
#logo:hover {
    text-decoration: underline;
}
/* Numer wersji obok logo — celowo drobny i wyszarzony, to znacznik wdrożenia,
   nie element marki. */
#wersja {
    font-size: 11px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* Suma punktów bierze wygląd z reguły „pigułki" wyżej; tu tylko przygaszony
   kolor tekstu (to informacja, nie kontrolka). */
#total-score {
    color: var(--text-faint);
}
/* Procent zdobytych punktów obok sumy (tryb ćwiczeniowy). Zielony dopiero od
   progu zdawalności (30%); cursor: help podpowiada, że po najechaniu jest
   tooltip "zdałeś" / "nie zdałeś (jeszcze)". */
.total-percent {
    margin-left: 4px;
    cursor: help;
}
.total-percent.zdane {
    color: var(--correct);
    font-weight: bold;
}
/* ===================================================================

   PANEL BOCZNY (#sidebar) — następca okienka „⋯" (#bar-menu, usunięte 2026-07-27)

   Wysuwana NAKŁADKA po lewej: arkusz się nie rusza (żadnego transform ani
   zwężania kolumny zadań), animujemy wyłącznie `transform` panelu — `left`
   i `width` wymuszałyby layout na każdej klatce.

   Warstwy: karty/treść 1–4, .wskaznik-otwarte 8, panele PDF 9, narożniki 10,
   przyciemnienie 11, #sidebar 12, lewy narożnik przy OTWARTYM panelu 13,
   podsumowanie egzaminu 20, toast zgłoszeń 22.
   Panel MUSI być nad panelami PDF — dlatego 12, a nie 10.

   Zamknięty panel ma `visibility: hidden`, żeby Tab NIE wchodził w jego
   przyciski; samo translateX(-100%) ich nie wyklucza. Tranzycja `visibility`
   trzyma go widocznym przez czas wysuwania i gasi dopiero na końcu.
   Panel zaczyna się PONIŻEJ narożników (top: 56px), żeby logo i strzałka
   zostały widoczne i klikalne nad nim — tą samą strzałką się zamyka.

   Dwa typy pozycji (patrz komentarz w template.html): .sidebar-akcja (czasownik)
   i .sidebar-ustawienie (rzeczownik + wartość + kropki). Wiersz ustawienia jest
   GRIDEM `20px 1fr auto auto` — nie flexem z margin-left:auto — żeby wartości
   wyrównały się w pionie między wierszami.
   Jeśli edytujesz ten blok, popraw sekcję „Panel boczny" w ARCHITECTURE_CSS.md.

   =================================================================== */
#sidebar-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: 32px;
    height: 32px;
    padding: 0;
    /* Docisk do logo i o włos wyżej (Henrich 2026-07-27) — strzałka ma czytać się
       jako część lewego narożnika i równać się z górną krawędzią otwartego panelu.
       MUSI iść przez margin, nie transform: `body.sidebar-otwarty #sidebar-toggle`
       ustawia `transform: rotate(180deg)` i nadpisałoby każde `translate`,
       więc strzałka przeskakiwałaby w momencie otwarcia panelu.
       Na wąskich ekranach zerowane w style/responsive.css. */
    margin-left: -4px;
    margin-top: -2px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pigulka);
    background-color: var(--bg);
    color: var(--text-faint);
    cursor: pointer;
    transition: transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1), color 120ms ease;
}
/* Hover chrome = tło (patrz ZASADA HOVERU przy #naroznik-prawy button). */
#sidebar-toggle:hover {
    background-color: var(--bg-muted);
    color: var(--text);
}
/* Otwarty panel → strzałka obraca się o 180° (◀ ↔ ▶), tą samą krzywą
   i w tym samym czasie co wysuwanie, żeby szły w parze. */
body.sidebar-otwarty #sidebar-toggle {
    transform: rotate(180deg);
}

/* Blokada scrolla tła na czas otwartego panelu — zakładana WYŁĄCZNIE przez JS
   (app/bootstrap.js) i tylko poniżej progu nakładania, bo tylko wtedy panel
   zasłania arkusz. Dlaczego position: fixed, a nie overflow: hidden na <body>:
   na iOS Safari `overflow: hidden` nie zatrzymuje przewijania dokumentu.
   Cena tej metody — dokument „przeskakuje" na górę — jest spłacona przez JS:
   ustawia `top: -<pozycja>px` przy otwarciu i przywraca scroll przy zamknięciu.
   Sam panel (#sidebar, overflow-y: auto) scrolluje się dalej normalnie — jest
   position: fixed, więc ta reguła go nie dotyczy. */
body.blokada-scrolla {
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
    overflow: hidden;
}

#sidebar-przyciemnienie {
    position: fixed;
    inset: 0;
    z-index: 11;
    opacity: 0;
    background-color: rgba(0, 0, 0, 0.25);
    /* Domyślnie NIEaktywne: powyżej progu 1300px panel mieści się w marginesie
       i niczego nie zasłania, więc nie ma czego przygaszać ani zamykać klikiem.
       Reguła odsłaniająca to jest w style/responsive.css. */
    pointer-events: none;
    transition: opacity 180ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

#sidebar {
    position: fixed;
    top: 56px;
    bottom: 0;
    left: 0;
    z-index: 12;
    box-sizing: border-box;
    width: 260px;
    padding: 14px;
    overflow-y: auto;
    /* Bez border-right — pionową kreskę rysuje osobny #sidebar-linia (niżej):
       border jeździłby razem z zawartością panelu przy scrollu, nie dałby się
       skrócić od dołu ani odsunąć od krawędzi. */
    background-color: var(--bg);
    /* BEZ cienia (Henrich 2026-07-27: „wywal te cienie, nie współgrają z logiem
       ani z kreską"). Panel jest przypięty do krawędzi ekranu i ma własną
       pionową kreskę (#sidebar-linia) — cień dublował tę granicę i podnosił
       panel nad stronę, choć wizualnie jest jej częścią. Panele PDF i toast
       cień ZOSTAWIAJĄ: te pływają nad treścią i nie mają żadnej kreski. */
    text-align: left;
    /* visibility (nie tylko translateX) — inaczej Tab wchodziłby w przyciski
       zamkniętego panelu. Przełączana z ZEROWYM czasem, ale z opóźnieniem:
       przy ZAMYKANIU 180ms (panel zostaje widoczny na czas wsuwania), przy
       OTWIERANIU 0s w regule niżej — tak, żeby stała się `visible` w tej samej
       klatce, w której dochodzi klasa. Zwykłe `visibility 180ms` tego nie robi:
       w klatce zmiany klasy postęp tranzycji to 0, więc wartość jest jeszcze
       `hidden` i otworzSidebar() nie miałby czego zafokusować. */
    visibility: hidden;
    transform: translateX(-100%);
    transition: transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1), visibility 0s linear 180ms;
}
body.sidebar-otwarty #sidebar {
    visibility: visible;
    transform: translateX(0);
    transition: transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1), visibility 0s linear 0s;
}

/* Pionowa kreska po prawej stronie panelu — ODDZIELNY element, nie border
   panelu (Henrich 2026-07-27: ma być odsunięta w prawo i kończyć się nad dolną
   krawędzią ekranu). Dlaczego nie `border-right` i nie `#sidebar::after`:
     • border rysuje się na krawędzi panelu i przewija się z jego treścią
       (#sidebar ma overflow-y: auto), więc ani go nie odsuniesz, ani nie skrócisz;
     • pseudo-element z position: fixed też nie pomoże — #sidebar ma `transform`,
       czyli jest containing blockiem dla fixed, a `overflow` by go uciął.
   Kreska startuje 16px pod górną krawędzią panelu (56 + 16) i kończy 40px nad
   dołem ekranu; gaśnie razem z wsuwaniem panelu (ta sama krzywa i czas). */
#sidebar-linia {
    position: fixed;
    top: 72px;
    bottom: 40px;
    left: 276px; /* 260px panelu + 16px oddechu */
    /* 8, czyli POD panelami PDF (tablica wzorów i zasady oceniania mają 9), choć
       sam panel boczny ma 12. Poniżej 720px panel PDF rozciąga się na 94%
       ekranu, sięga więc w lewo aż tutaj, a kreska przecinała mu kartkę na
       wylot (Henrich, TODO). Kreska jest granicą panelu bocznego, a nie
       elementem pływającym nad stroną, więc ma chować się pod wszystkim,
       co nad stroną faktycznie pływa. */
    z-index: 8;
    width: 1px;
    background-color: var(--border-strong);
    opacity: 0;
    pointer-events: none;
    transition: opacity 180ms cubic-bezier(0.2, 0.7, 0.3, 1);
}
body.sidebar-otwarty #sidebar-linia {
    opacity: 1;
}

#sidebar-tytul {
    margin: 0 0 10px;
    padding: 0 6px;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--text);
}
#sidebar-tytul:empty {
    display: none;
}

.sidebar-kreska {
    height: 1px;
    margin: 8px 0;
    background-color: var(--separator);
}

/* Wspólny szkielet obu typów wiersza. */
.sidebar-akcja,
.sidebar-ustawienie {
    box-sizing: border-box;
    width: 100%;
    padding: 0 6px;
    border: 0;
    border-radius: var(--radius-pigulka);
    background-color: transparent;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: background-color 120ms ease;
}
.sidebar-akcja:hover:not(:disabled),
.sidebar-ustawienie:hover:not(:disabled) {
    background-color: var(--bg-muted);
}
/* Pozycja WYŁĄCZONA (2026-08-09 przeniesione z exam.css, bo powodów jest już
   dwa: trwający egzamin oraz „Sprawdź wszystkie odpowiedzi" przy poprawności
   „natychmiast" — patrz odswiezBlokadyMenu() w app/exam.js). Pozycja nie znika,
   tylko blednie: panel ma nie skakać. Warunkiem czytelności tego stanu jest
   dostatecznie mocny stan NORMALNY — dlatego etykiety ustawień jadą na
   --text-muted, nie na --text-faint-2 (patrz niżej). */
#sidebar button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.sidebar-akcja {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 40px;
    font-size: 13px;
    color: var(--text);
}

/* Potwierdzenie kliknięcia "Sprawdź wszystkie odpowiedzi" (issues/sprawdz-
   wszystkie-pola-i-komunikat.md). 260px nie mieści zdania obok etykiety, więc
   tylko ✓ przy prawej krawędzi wiersza — margin-left: auto działa bo
   .sidebar-akcja wyżej jest flex. Prawdziwy tekst dla czytnika ekranu idzie do
   aria-label (JS), nie do textContent — glif "✓" sam w sobie nic nie znaczy.
   Znika po ~2,5s (JS toggluje .widoczny); kolejny klik resetuje licznik.
   Kolor tylko przez tokeny: --correct / --text-faint (przygaszony = "nic nie
   było zaznaczone", żeby "sprawdzono" nie kłamało) — dark mode działa sam. */
.sprawdz-status-sidebar {
    margin-left: auto;
    flex-shrink: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--correct);
    opacity: 0;
    transition: opacity 400ms ease;
}
.sprawdz-status-sidebar.widoczny {
    opacity: 1;
}
.sprawdz-status-sidebar.przygaszony {
    color: var(--text-faint);
}

.sidebar-ustawienie {
    display: grid;
    grid-template-columns: 20px 1fr auto auto;
    align-items: center;
    /* Ciaśniej niż w akcjach (8px vs 10px): wiersz ustawienia ma cztery kolumny,
       a w 260px panelu na etykietę zostaje wtedy ~10px więcej — akurat tyle,
       żeby „Wskaźniki samooceny" i „Poprawność odpowiedzi" nie ucinały się. */
    gap: 8px;
    height: 40px;
    font-size: 13px;
    /* Etykieta ciemniejsza od wartości, ale wciąż od niej lżejsza — świadomie BEZ
       koloru akcentu: --accent-blue-strong znaczy już „Twój wybór w zadaniu"
       (ABCD, widżety), a zielony/czerwony to poprawność; kolor w menu
       konkurowałby ze znaczeniem z treści.
       2026-08-09: było --text-faint-2 (#909090 / #8c8c8c) i całe ustawienia
       wyglądały na wyłączone — po tej samej stronie skali co realnie wyszarzone
       „Punktacja" w egzaminie. Skoro `disabled` ma teraz znaczyć „wyłączone",
       stan normalny musi być od niego wyraźnie odróżnialny, stąd --text-muted. */
    color: var(--text-muted);
}
.sidebar-ustawienie .etykieta {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
/* Wartość o stopień mniejsza od etykiety, ale w pełnym --text — to kolor, nie
   waga, robi tu hierarchię.
   2026-08-09: było `font-weight: 600`. Lora jest szeryfowa i ma mocny kontrast
   grubości kresek; jej odmiana 600 przy 12px zlepiała szeryfy i laski w plamę
   („ciemny", „wszystko" wyglądały na rozmyte/rozlane, szczególnie jasny tekst
   na ciemnym tle). Zdiagnozowane porównaniem 600/400 w powiększeniu ×5 —
   przyczyną była waga, nie kontrast tła. Waga 400 przy tym samym rozmiarze jest
   ostra; hierarchię wobec etykiety utrzymuje --text vs --text-muted i kropki. */
.sidebar-ustawienie .wartosc {
    font-size: 12px;
    white-space: nowrap;
    color: var(--text);
    font-weight: 400;
}
/* Do 2026-08-10 hover pokazywał tu podgląd „auto → jasny" (.wartosc-podglad),
   chowając .wartosc — Henrich: odejmowało to intuicyjności, więc wartość jest
   teraz widoczna zawsze. Usunięty kod: issues/archiwum-hover-podglad.md. */

/* Kropki stanu (○○●) — ile stanów ma przełącznik i który jest aktywny.
   Generowane z data-stany przez ustawWartosc() (app/state.js). */
.sidebar-ustawienie .kropki {
    display: flex;
    align-items: center;
    gap: 3px;
}
/* Kropka nieaktywna: --border-strong, nie --border-muted (2026-08-09) — #ddd na
   bieli gubiło się przy 5px i cały przełącznik czytał się jako wyłączony. */
.sidebar-ustawienie .kropka {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background-color: var(--border-strong);
}
.sidebar-ustawienie .kropka-aktywna {
    background-color: var(--text);
}

/* Sub-opcje (Zegar/Wskaźniki pod egzaminem, „Sprawdź wszystkie" pod
   Poprawność): wcięte pod pozycją nadrzędną, mniejszy font, lżejsza ikona —
   wcięcie ma być widać także wtedy, gdy etykieta jest krótka. To NIE osobny
   typ kontrolki, tylko ten sam wzorzec — działa i na .sidebar-ustawienie
   (przełącznik z wartością), i na .sidebar-akcja (zwykły przycisk). */
.sidebar-sub {
    height: 34px;
    padding-left: 26px;
    font-size: 12px;
}
.sidebar-ustawienie.sidebar-sub .wartosc {
    font-size: 11px;
}
.sidebar-sub .sidebar-ikona {
    opacity: 0.7;
}

/* Ikony: kreskowe, dziedziczą kolor tekstu wiersza (akcja → --text,
   ustawienie → --text-faint-2 z reguły wyżej). Wspólne atrybuty trzymamy
   w CSS, nie w każdym <svg> osobno. */
.sidebar-ikona {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Użytkownik prosi o brak animacji → panel ma pojawiać się OD RAZU, a nie
   „szybciej". 0.01ms zamiast `none`, żeby zdarzenia transitionend (gdyby
   kiedyś doszły) nadal się odpalały. Zerujemy też transition-delay, inaczej
   zamknięty panel zostawałby `visible` przez 180ms po wsunięciu. */
@media (prefers-reduced-motion: reduce) {
    #sidebar,
    #sidebar-linia,
    #sidebar-toggle,
    #sidebar-przyciemnienie,
    .sidebar-akcja,
    .sidebar-ustawienie,
    #tryb-przelacznik button,
    body.sidebar-otwarty #sidebar,
    .sprawdz-status-sidebar,
    .sprawdz-status-stopka {
        transition-duration: 0.01ms;
        transition-delay: 0s;
    }
}

/* Floating formula-sheet panel — toggled to display:block by the JS.
   Panel to kontener (pozycja/rozmiar/ramka), a w środku <object> z PDF-em oraz
   krzyżyk zamykający. JS przełącza display panelu i podmienia sam <object>. */
#tablica-wzorow-panel,
#zasady-oceniania-panel {
    position: fixed;
    top: 100px;
    right: 30px;
    z-index: 9;
    display: none;
    width: 28%;
    height: 80vh;
    overflow: hidden;
    /* Obudowa jak w panelu bocznym (decyzja Henricha 2026-07-27): ta sama ramka,
       ten sam promień i ten sam --shadow-panel. Wcześniej panele miały mocniejszy
       własny cień (0 4px 8px / 0.2) i ostre narożniki, więc w ciemnym motywie
       odcinały się od reszty strony. Sama kartka PDF zostaje biała — filtrowanie
       treści PDF-a to osobna sprawa (issues/dark-mode-obrazki-wideo.md). */
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    box-shadow: var(--shadow-panel);
}
/* Zasady oceniania — ten sam panel, tylko przypięty do lewej krawędzi. */
#zasady-oceniania-panel {
    right: auto;
    left: 30px;
}
/* Panel jest "odblokowany": górny pasek to uchwyt przesuwania, narożnik
   prawy-dolny zmienia rozmiar (logika pointer-capture w matematykazen.html).
   padding-top robi miejsce na pasek, żeby nie zasłaniał PDF-a. */
.pdf-panel {
    box-sizing: border-box;
    padding-top: 40px;
}
/* Pasek-uchwyt: wysokość i typografia wiersza panelu bocznego (40px, 14px/600),
   tło --bg z cienką kreską zamiast szarej płachty --bg-muted — to ta płachta
   świeciła w ciemnym motywie. Wysokość czyta z DOM-u panels.js
   (`uchwyt.offsetHeight || 46`), więc zmiana tej liczby jest bezpieczna. */
.panel-uchwyt {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 40px;
    z-index: 1;
    padding-left: 14px;
    border-bottom: 1px solid var(--separator);
    background-color: var(--bg);
    font-size: 14px;
    font-weight: 600;
    line-height: 40px;
    text-align: left;
    color: var(--text-faint-3);
    cursor: move;
    touch-action: none;
    user-select: none;
}
.panel-rozmiar {
    position: absolute;
    right: 0;
    bottom: 0;
    width: 22px;
    height: 22px;
    z-index: 1;
    /* Prawy-dolny narożnik panelu jest teraz zaokrąglony — uchwyt rozmiaru
       powtarza ten promień, żeby nie wystawał kwadratem za obudowę. */
    border-bottom-right-radius: var(--radius-kontrolka);
    border: 1px solid var(--border-muted);
    background-color: var(--bg);
    font-size: 13px;
    line-height: 22px;
    text-align: center;
    color: var(--text-faint-3);
    cursor: nwse-resize;
    touch-action: none;
    user-select: none;
}
#tablica-wzorow,
#zasady-oceniania {
    display: block;
    width: 100%;
    height: 100%;
    border: none;
}
/* Krzyżyk zamykający w prawym górnym rogu paneli (tablica wzorów / zasady
   oceniania). Do 2026-07-27 był celowo duży (40px), pogrubiony i z własnym
   cieniem, żeby nie ginął na tle PDF-a — po przejściu paska-uchwytu na tło --bg
   siedzi na spokojnym tle, więc dociągamy go do wzorca `.zglos-blad-x`
   (32px, ramka 1px, bez cienia). Krzyżyk nadal jest NAD paskiem (z-index 2). */
#tablica-close,
#zasady-close {
    position: absolute;
    right: 4px;
    top: 4px;
    z-index: 2; /* nad paskiem-uchwytem panelu */
    width: 32px;
    height: 32px;
    padding: 0;
    border: var(--border-kontrolka) solid var(--border-close);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-size: 16px;
    line-height: 1;
    color: var(--text-muted-2);
    cursor: pointer;
}
#tablica-close:hover,
#zasady-close:hover {
    background-color: var(--bg-hover);
}

.exercise-container {
    position: relative;
    width: 650px;
    margin: auto;
    padding: 50px;

    background-color: var(--bg);
    font-size: 18px;
    text-align: left;
}

/* Separator między zadaniami — krótsza (75%) wyśrodkowana kreska zamiast
   border-bottom na całą szerokość (border-width nie przyjmuje procentów). */
.exercise-container::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 75%;
    border-bottom: 1px solid var(--separator);
}
/* Delikatne wcięcie treści zadania od lewej — bez niego tekst kleił się do
   lewej krawędzi karty i wyglądał na "przeciągnięty w lewo" względem
   wyśrodkowanych odpowiedzi / przycisków pod spodem. */
.question {
    padding-left: 16px;
}
/* Grafiki-rysunki w treści zadania jako blok wyśrodkowany — inaczej obrazek
   wstawiony w linii tekstu (np. zad10) lądował "w środku zdania" i przesunięty
   w prawo. Obrazki naprawdę inline (np. liczba w zad2) nadpisują to swoim
   stylem display:inline-block. */
.question img {
    display: block;
    /* Górny limit 380px: rysunki wycinane z PDF-u mają po ~700px i bez tego
       ciągnęły się przez całą kartę na komputerze (uwaga Henricha do zad. 12
       i 13; 450px z v52 nadal było dla niego za dużo). Pliki zostają w pełnej
       rozdzielczości, żeby były ostre na telefonie, gdzie ogranicza je dopiero
       100% szerokości karty. */
    max-width: min(100%, 380px);
    margin: 15px auto;
    /* Odwrócenie w dark mode — patrz --filtr-grafik-zadan w style/base.css. */
    filter: var(--filtr-grafik-zadan);
}
/* Dwa rysunki, które w arkuszu stoją obok siebie (diagramy klas w zad. 31
   maja). Jeden szeroki plik przy limicie 380px robił się nieczytelny, bo na
   wykres zostawało po ~190px. Rozdzielone na dwa obrazki w tym kontenerze
   stoją obok siebie na komputerze, a na telefonie zawijają się jeden pod
   drugi (style/responsive.css zdejmuje im tam limit szerokości). */
.rys-para {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin: 15px 0;
}
.question .rys-para img {
    max-width: min(100%, 310px);
    margin: 0;
}
.exercise-score {
    /*  Henrich: te cztery linijki przekleiłem z total-score aby było spójniej */
    box-sizing: border-box;
    padding: 6px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pigulka);

    position: absolute;
    top: 48px;
    /* -120px → -80px: badge podjechał ~40px bliżej karty zadania (Henrich,
       TODO.md), zamiast wisieć daleko za prawą krawędzią. */
    right: -80px;
    /* Zrównanie w pionie z pierwszą linią treści (numerem zadania). Bez tego
       badge siedział optycznie ~16px za wysoko względem "Zadanie N.". */
    margin-top: 16px;
    /* Henrich: no a tutaj zakomentowałem, żeby nie kolidowały ze sobą
    padding: 5px;
    border: 1px solid;
    */
    }
.low-exercise-score {
    border-color: var(--score-low-border);
    color: var(--score-low-text);
}
.mid-exercise-score {
    border-color: var(--score-mid-border);
    color: var(--score-mid-text);
}
.high-exercise-score {
    border-color: var(--score-high-border);
    color: var(--correct);
}

/* ===== PRZYCISKI ODPOWIEDZI =====
   SKALA KONTROLEK NA KARCIE ZADANIA (spójność UI etap 2, 2026-08-09) — trzy
   klasy rozmiaru, żeby cztery paddingi i rozmiary fontu nie rozjeżdżały się
   dalej po jednej kontrolce na raz:
     • DUŻA (odpowiedzi ABCD / multiselect): 18px — jak treść zadania,
       padding 10px 12px;
     • KONTROLKA (pola fillIn / „ostateczna odpowiedź", samoocena „N pkt"):
       17px, padding 8px 11px;
     • TEKSTOWA (textarea rozwiązania): 16px — proza, nie pojedyncza wartość,
       padding 10px 12px.
   Przyciski „Sprawdź" przy polach: 15px, padding 8px 18px (akcja drugorzędna,
   mniejsza od kontrolki, którą sprawdza). P/F zostaje 44px/16px — jego rozmiar
   niesie znaczenie (para jednoznakowych przycisków przy stwierdzeniu). */
.button-container {
    width: 90%;
    margin: 10px auto 0;
    text-align: center;
}
.button-container button {
    display: inline-block;
    /* min-width zamiast sztywnego width: krótkie odpowiedzi (liczby) trzymają
       równą siatkę, a dłuższy wzór KaTeX (przedziały, ułamki) poszerza swój
       przycisk zamiast łamać się na dwie linie ("A." nad wzorem). */
    min-width: 22%;
    white-space: nowrap;
    padding: 10px 12px;
    margin: 8px;
    /* Przyciski w wierszu wyrównują się do środka w pionie względem siebie,
       więc litera (A., B., ...) w niższym przycisku ląduje na środku obok
       wyższych sąsiadów zamiast przyklejać się do dołu. */
    vertical-align: middle;
    border: 1px solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: "STIX Two Math", "Lora", serif;
    font-size: 18px;
    text-decoration: none;
    color: var(--text);
    cursor: pointer;
}
/* Grafiki odpowiedzi (np. zad6) wyrównane do środka, aby litera A./B. obok
   nich była na środku w osi pionowej, a nie przy dolnej krawędzi obrazka. */
.button-container button img {
    vertical-align: middle;
}
/* Zaznaczone/ocenione odpowiedzi mają optycznie 2× grubszą ramkę — drugą
   "warstwę" dokłada wewnętrzny box-shadow w kolorze ramki, więc przycisk
   nie zmienia rozmiaru (prawdziwe pogrubienie border przesuwałoby layout). */
.button-container button.correct {
    border-color: var(--correct-soft);
    box-shadow: inset 0 0 0 1px var(--correct-soft);
}
.button-container button.incorrect {
    border-color: var(--incorrect-soft);
    box-shadow: inset 0 0 0 1px var(--incorrect-soft);
}
/* multiSelect: zaznaczenie przed skompletowaniem wymaganej liczby odpowiedzi.
   Ta sama klasa .selected służy też za NEUTRALNE zaznaczenie w trybie „sprawdź
   później" (natychmiastowa poprawność OFF) dla ABCD/PF — kliknięta, ale jeszcze
   nieoceniona odpowiedź ma niebieską ramkę zamiast zielonej/czerwonej. */
.button-container button.selected {
    border-color: var(--accent-blue);
    box-shadow: inset 0 0 0 1px var(--accent-blue);
}
/* Hover klikalnych odpowiedzi (ABCD, P/F, „N pkt") — informacja zwrotna przed
   kliknięciem, której wcześniej nie było. RAMKĄ, nie tłem (patrz ZASADA HOVERU
   przy #naroznik-prawy button): tło + inset box-shadow niosą już stany
   poprawne/błędne/.selected, więc :not() wyłącza hover na ocenionych/zaznaczonych.
   .answer-check-floating ma własny hover (tło) i niebieską ramkę — wykluczony. */
.button-container button:hover:not(.correct):not(.incorrect):not(.selected):not(.answer-check-floating),
.pf-row button:hover:not(.correct):not(.incorrect):not(.selected),
.self-score-container button:hover:not(.selected) {
    border-color: var(--border-strong);
}

/* Kontener odpowiedzi jest układem odniesienia dla pływającego przycisku
   „sprawdź" (position: absolute względem niego). */
.answers-container {
    position: relative;
}
/* Przycisk „sprawdź" pojedynczego zadania (tryb natychmiastowej poprawności OFF).
   Absolutnie po PRAWEJ stronie ramki odpowiedzi — nie zmienia wysokości zadania
   ani układu strony (nie wchodzi w flow). Trzyma się przy prawej krawędzi karty,
   na lewo od pływających kropek wskaźników (right: 70px), więc na nie nie nachodzi.
   Selektor .button-container .answer-check-floating celowo bije regułę
   .button-container button (szerokość/marginesy/font). Na wąskich ekranach (gdzie
   „na prawo od odpowiedzi" wyszłoby poza ekran) wraca do flow pod odpowiedziami. */
.button-container .answer-check-floating {
    position: absolute;
    /* +8px = margin górny przycisków odpowiedzi (.button-container button ma
       margin: 8px), żeby górna krawędź obu wyrównała się w jednej linii. */
    top: 8px;
    left: calc(100% + 12px);
    z-index: 4;
    min-width: 0;
    width: auto;
    margin: 0;
    padding: 6px 12px;
    white-space: nowrap;
    border: 1px solid var(--accent-blue);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 14px;
    color: var(--accent-blue);
    cursor: pointer;
}
.button-container .answer-check-floating:hover {
    background-color: var(--bg-muted);
}
@media (max-width: 900px) {
    /* Na wąskim ekranie brak miejsca po prawej — przycisk wraca do flow, pod
       odpowiedziami (wyśrodkowany). Tylko tu może zmienić wysokość zadania. */
    .button-container .answer-check-floating {
        position: static;
        display: inline-block;
        margin: 12px auto 0;
    }
}

/* ===== ZADANIA PRAWDA/FAŁSZ ===== */
/* Wiersz = treść stwierdzenia + przyciski P i F po prawej. */
.pf-container {
    text-align: left;
}
.pf-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 10px 0;
}
.pf-text {
    flex: 1;
}
.pf-row button {
    flex: 0 0 44px;
    width: 44px;
    /* Bez tego przycisk P/F rozciągał się na komputerze do 22% szerokości bloku
       odpowiedzi (jakieś 130px na kartę 650px): wspólny kontener .button-container
       ma `min-width: 22%`, a min-width bije zarówno `width`, jak i `flex-basis`.
       Litera „P" na takiej płycie wyglądała jak przycisk odpowiedzi ABCD
       (Henrich, TODO). Reguła telefonowa w responsive.css robi to samo od dawna. */
    min-width: 44px;
    padding: 8px 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: "STIX Two Math", "Lora", serif;
    font-size: 16px;
    color: var(--text);
    cursor: pointer;
}
.pf-row button.correct {
    border-color: var(--correct-soft);
    box-shadow: inset 0 0 0 1px var(--correct-soft);
}
.pf-row button.incorrect {
    border-color: var(--incorrect-soft);
    box-shadow: inset 0 0 0 1px var(--incorrect-soft);
}

/* ===== MULTISELECT ===== */
.multi-select-note {
    margin: 4px 0 2px;
    font-size: 14px;
    color: var(--text-faint-2);
}
/* Dłuższe wzory w odpowiedziach (np. zad 12.2) — szersze przyciski, 2-3 w rzędzie. */
.multi-select-container button {
    min-width: 30%;
}

/* ===== ZADANIA Z POLAMI DO UZUPEŁNIENIA (fillIn) ===== */
.fill-in-container {
    text-align: left;
}
.fill-in-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 10px 0;
}
.fill-in-label {
    flex: 1;
}
.fill-in-input {
    width: 130px;
    padding: 8px 11px; /* klasa KONTROLKA — patrz SKALA KONTROLEK wyżej */
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: "STIX Two Math", "Lora", serif;
    font-size: 17px;
    text-align: center;
}
.fill-in-input.correct {
    border-color: var(--correct);
    box-shadow: inset 0 0 0 1px var(--correct);
}
.fill-in-input.incorrect {
    border-color: var(--incorrect);
    box-shadow: inset 0 0 0 1px var(--incorrect);
}
/* Selektor celowo mocniejszy niż `.button-container button` (kontener odpowiedzi
   ma obie klasy), inaczej "Sprawdź" dostałby szerokość 22% i inline-block. */
.fill-in-container button.fill-in-check {
    display: block;
    width: auto;
    margin: 12px auto 0;
    /* 15px / 8px 18px — jak .final-answer-check (SKALA KONTROLEK wyżej). */
    padding: 8px 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 15px;
    color: var(--text);
    cursor: pointer;
}
.fill-in-container button.fill-in-check:hover {
    background-color: var(--bg-muted);
}

/* ===== ZADANIA OTWARTE: WŁASNA ODPOWIEDŹ / TOK ROZWIĄZANIA ===== */
/* Textarea, w której uczeń zapisuje rozwiązanie — przydatna zwłaszcza w trybie
   egzaminu (samoocena schowana), zapis w localStorage do porównania z kluczem
   po egzaminie. Osobny kontener od .self-score-container (tamten znika na czas
   egzaminu, textarea zostaje). */
.open-answer-container {
    margin-top: 15px;
    text-align: center;
}
.open-answer-label {
    margin-bottom: 6px;
    font-size: 14px;
    color: var(--text-faint-2);
}
.open-answer {
    box-sizing: border-box;
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 16px;
    line-height: 1.5;
    text-align: left;
    resize: vertical;
}
.open-answer:focus {
    border-color: var(--accent-blue);
    outline: none;
}

/* ===== SAMOOCENA ZADAŃ OTWARTYCH ===== */
.self-score-container {
    margin-top: 15px;
    text-align: center;
}
.self-score-label {
    margin-bottom: 6px;
    font-size: 14px;
    color: var(--text-faint-2);
}
.self-score-container button {
    display: inline-block;
    width: auto;
    min-width: 62px;
    padding: 8px 11px;
    margin: 4px;
    border: 1px solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: "STIX Two Math", "Lora", serif;
    font-size: 17px;
    color: var(--text);
    cursor: pointer;
}
.self-score-container button.selected {
    border-color: var(--accent-blue);
    background-color: var(--accent-blue-bg);
    box-shadow: inset 0 0 0 1px var(--accent-blue);
}

/* ===== ZADANIA OTWARTE: POLE „OSTATECZNA ODPOWIEDŹ" (auto-sprawdzane) ===== */
/* Osobny kontener POZA .self-score-container — dzięki temu (patrz exam.css)
   pole jest dostępne TAKŻE w trakcie egzaminu jako jedyny automatyczny sygnał
   poprawności zadań otwartych. Wygląd spójny z .fill-in-*. */
.final-answer-container {
    margin-top: 15px;
    text-align: center;
}
.final-answer-label {
    margin-bottom: 6px;
    font-size: 14px;
    color: var(--text-faint-2);
}
.final-answer-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
}
.final-answer-input {
    width: 160px;
    padding: 8px 11px; /* klasa KONTROLKA — patrz SKALA KONTROLEK wyżej */
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: "STIX Two Math", "Lora", serif;
    font-size: 17px;
    text-align: center;
}
.final-answer-input.correct {
    border-color: var(--correct);
    box-shadow: inset 0 0 0 1px var(--correct);
}
.final-answer-input.incorrect {
    border-color: var(--incorrect);
    box-shadow: inset 0 0 0 1px var(--incorrect);
}
/* Selektor mocniejszy niż `.button-container button` (kontener odpowiedzi ma obie
   klasy) — inaczej „Sprawdź" dostałby szerokość 22% jak przyciski odpowiedzi. */
.answers-container button.final-answer-check {
    display: inline-block;
    width: auto;
    margin: 0;
    /* 15px / 8px 18px — jak .fill-in-check (SKALA KONTROLEK wyżej); pion 8px
       zrównuje wysokość z polem obok (też 8px). */
    padding: 8px 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 15px;
    color: var(--text);
    cursor: pointer;
}
.answers-container button.final-answer-check:hover {
    background-color: var(--bg-muted);
}

/* ===== ZADANIA OTWARTE: BOKS „SPRAWDŹ OBLICZENIA" (kryteria + punkty) =====
   Zwijany <details> wewnątrz .self-score-container (a więc chowany w trybie
   egzaminu). To on przyznaje punkty za zadanie otwarte — przyciski „0/1/2 pkt"
   zostały tylko jako awaryjna ścieżka dla zadań bez kryteriów w danych.
   Wygląd celowo „mały panel boczny": ta sama ramka --border-strong, promień
   --radius-kontrolka i typografia nagłówka (14px/600) co #sidebar i panele PDF,
   tyle że w przepływie karty zadania — bez cienia, przeciągania i zmiany rozmiaru. */
.ocena-box {
    max-width: 520px;
    margin: 4px auto 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    text-align: left;
    overflow: hidden;
}
/* Nagłówek = jedyna widoczna rzecz w stanie zwiniętym: tytuł + strzałka.
   list-style/::-webkit-details-marker znikają, bo domyślny trójkącik ma inny
   kształt w każdej przeglądarce i nie da się go ustawić po prawej. */
.ocena-box-tytul {
    display: flex;
    align-items: center;
    padding: 10px 14px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text);
    cursor: pointer;
    list-style: none;
}
.ocena-box-tytul::-webkit-details-marker {
    display: none;
}
.ocena-box-tytul::after {
    content: "›";
    margin-left: auto;
    font-size: 18px;
    line-height: 1;
    color: var(--text-faint);
    transition: transform 150ms ease;
}
.ocena-box[open] > .ocena-box-tytul::after {
    transform: rotate(90deg);
}
.ocena-box-tytul:hover {
    background-color: var(--bg-muted);
}
.ocena-box-tresc {
    padding: 4px 14px 12px;
    border-top: 1px solid var(--separator);
}
.grading-criterion {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 10px 0;
    font-size: 15px;
    line-height: 1.4;
    cursor: pointer;
}
.grading-criterion input[type="checkbox"] {
    margin-top: 3px;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    cursor: pointer;
}
.grading-criterion-tekst {
    flex: 1;
}
/* Mały licznik punktów kryterium — młodszy brat badge'a .exercise-score:
   ta sama rola („ile to jest warte"), mniejszy rozmiar i wyszarzony, dopóki
   kryterium nie jest zaznaczone. */
.kryterium-punkty {
    flex-shrink: 0;
    align-self: center;
    min-width: 44px;
    padding: 1px 6px;
    border: 1px solid var(--score-low-border);
    border-radius: var(--radius-kontrolka);
    font-size: 12px;
    text-align: center;
    color: var(--score-low-text);
    white-space: nowrap;
}
.grading-criterion input[type="checkbox"]:checked ~ .kryterium-punkty {
    border-color: var(--score-high-border);
    color: var(--correct);
}
/* Kryterium warte 0 pkt (krok, za który klucz CKE sam z siebie nie daje punktu)
   zostaje szare także po zaznaczeniu — zielony licznik przy „0 pkt" sugerowałby
   zdobyty punkt. */
.grading-criterion input[type="checkbox"]:checked ~ .kryterium-punkty.kryterium-punkty-zero {
    border-color: var(--score-low-border);
    color: var(--score-low-text);
}

/* Ogólne zastrzeżenie prawne samooceny — RAZ na stronie (stopka arkusza), nie
   przy każdym zadaniu. */
.samoocena-disclaimer {
    max-width: 650px;
    margin: 0 auto 20px;
    padding: 0 20px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-faint);
    text-align: center;
}

/* Copyright + link do licencji, sam dół stopki arkusza — pod zastrzeżeniem
   samooceny. Ten sam wygląd (rozmiar/kolor/wyśrodkowanie) co .landing-footer
   w style/landing.css, żeby obie strony miały spójną stopkę. */
.stopka-copyright {
    margin: 0 auto 60px;
    padding: 0 20px;
    font-size: 13px;
    text-align: center;
    color: var(--text-faint);
}
.stopka-copyright a {
    color: var(--text-muted-2);
    text-decoration: underline;
}

/* ===== PRZYCISKI TEKSTOWE (podpowiedź / rozwiązanie / zgłoś błąd / wzory) =====
   Wiersz jest FLEXEM, a nie ciągiem inline-blocków po sztywne 30%: przycisków
   bywa od jednego do czterech (wzory znikają przy formulasPage: null,
   podpowiedź przy pustym hint, „zgłoś błąd" wyłącza przełącznik w panelu
   bocznym), więc szerokość musi wynikać z tego, ile ich zostało. */
.light-button-container {
    display: flex;
    justify-content: center;
    align-items: baseline;
    gap: 10px;
    width: 90%;
    margin: 20px auto 0;
}
/* Każdy przycisk bierze równą część wiersza (flex-basis 0, nie „auto" —
   inaczej dłuższa etykieta „Pokaż wzory" dostałaby więcej miejsca niż
   „Podpowiedź"). min-width: 0 pozwala tekstowi się zawinąć zamiast rozpychać
   wiersz na wąskim ekranie. */
.light-button-container > .light-button {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
}
.light-button {
    display: inline-block;
    width: 30%;
    padding: 0;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 16px;
    text-align: center;
    text-decoration: none;
    color: var(--text-muted-2);
    cursor: pointer;
}
.light-button:hover {
    text-decoration: underline;
}

.hint-container {
    display: none;
    margin-top: 20px;
    padding: 20px;
}
.solution-container {
    display: none;
    margin-top: 20px;
    padding: 20px;
    border: 1px solid var(--border-subtle);
}
.solution-text-container {
    padding: 30px 0 30px 30px;
    border-bottom: 1px solid var(--border-subtle); /* 2px→1px (etap 2): grubość nie niosła znaczenia */
}
.solution-text-container:empty {
    display: none;
}
/* Rozwiązanie zapisane linijka po linijce, w ślad za krokami filmu. Wzór na
   starcie idzie przez \[ \], czyli KaTeX centruje go sam; bez tej klasy kolejne
   linijki (zwykłe \( \) rozdzielone <br>) kleiły się do lewej i wzór wisiał nad
   nimi bez związku. Wyśrodkowanie zestawia je w jedną kolumnę pod wzorem. */
.rozwiazanie-kroki {
    text-align: center;
    /* Linijki rozdziela <br>, więc odstęp między nimi robi line-height. Ciaśniej
       niż tu i kolejne kroki rachunku zlewają się w blok; wzory mają własną
       wysokość (ułamki, pierwiastki), więc potrzebują więcej powietrza niż zwykły
       akapit. */
    line-height: 2.1;
}

/* Wysokie wzory (ułamki pisane \dfrac, pierwiastki) są wyższe niż line-height,
   więc kolejne linijki rozdzielone <br> wchodziły na siebie: mianownik jednej
   dotykał licznika następnej (zad. 6, 2026-08-21). Wzór jako inline-block sam
   rozpycha wiersz na swoją wysokość, więc działa to niezależnie od tego, jak
   wysoki jest zapis; line-height zostaje dla linijek tekstu. */
.rozwiazanie-kroki .katex {
    display: inline-block;
    margin: 0.35em 0;
}

/* KOMENTARZ MIĘDZY LINIJKAMI RACHUNKU (Henrich, 2026-08-27, zad. 8 z 2024-grudnia).
   Mówi, co dzieje się w przejściu z jednej linijki do następnej, i celowo jest
   BLEDSZY od samego rachunku: rachunek ma być pierwszy w kolejności czytania,
   komentarz drugi. Ten sam token co kolumna wzorów, więc ciemny motyw obsługuje
   się sam. Zapis w exercises.json:

       <div class="rozwiazanie-kroki">
         <div class="rozw-linia">\(rachunek\)</div>
         <div class="rozw-komentarz">co się zaraz stanie</div>
         …
       </div>

   Linijki idą wtedy w .rozw-linia zamiast być rozdzielane <br>: div sam łamie
   wiersz, a komentarz między nimi potrzebuje własnego, ciaśniejszego odstępu niż
   line-height 2.1 ustawiony dla wzorów. */
.rozwiazanie-kroki .rozw-komentarz {
    /* --text-faint, nie --text-muted-2: komentarz ma mieć MNIEJSZY kontrast niż
       rachunek (Henrich, 2026-08-27). Niżej niż ten token już nie schodzimy,
       bo #767676 na białym to jeszcze kontrast 4.6:1, czyli próg czytelności
       dla zwykłego tekstu; --text-faint-2 byłoby już poniżej. */
    color: var(--text-faint);
    line-height: 1.5;
    /* Powietrza z obu stron, więcej pod spodem niż nad: komentarz należy do
       linijki NAD nim (opisuje przejście, które się z niej zaczyna), a od
       następnej ma odstawać. Odstępy podniesione 2026-08-28 (Henrich: „między
       zdaniami a równaniami ma być przerwa, nie mają być ściśnięte") — przy
       0,55em/1,15em zdanie kleiło się do rachunku i całość czytała się jak
       jeden blok tekstu. */
    margin: 1.2em auto 1.8em;
    max-width: 34em;
}

/* PRZERWA MIĘDZY CZĘŚCIAMI ROZWIĄZANIA (Henrich, 2026-08-28, zad. 8): po
   policzeniu dziedziny ma być widać, że zaczyna się nowa część, a nie ciągnie
   ta sama kolumna rachunku. Klasa idzie na pierwszy element części, która się
   zaczyna (linijkę albo komentarz nad nią). */
.rozwiazanie-kroki .rozw-odstep { margin-top: 3em; }

/* Wzór wewnątrz komentarza ma być tak samo blady jak zdanie wokół niego. KaTeX
   maluje własny kolor na .katex, więc bez tego jeden wzór w środku komentarza
   wychodzi czarny i wygląda jak osobna linijka rachunku. */
.rozwiazanie-kroki .rozw-komentarz .katex {
    color: inherit;
    /* Bez zerowania marginesu wzór w środku zdania bierze 0,35em z reguły dla
       .rozwiazanie-kroki .katex (ta jest tam po to, żeby wysokie ułamki nie
       wchodziły na siebie między linijkami rachunku) i wiersz ze wzorem odjeżdża
       od sąsiednich. Akapit czyta się wtedy jak rozstrzelony. */
    margin: 0;
}

/* SPRAWDZENIE na końcu rozwiązania: osobna część, oddzielona SAMYM ODSTĘPEM
   (Henrich, 2026-08-27, poprawione 2026-08-29). Rachunek CKE kończy się wyżej,
   na wyniku; to, co niżej, jest dobrowolną kontrolą, więc ma być widać, że
   zaczyna się nowa rzecz, a nie ciągnie ten sam rachunek.

   Kreska stała tu do 2026-08-29 i została zdjęta na prośbę Henricha
   („wygląda to jakby było oddzielną częścią niż rozwiązanie zwykłe"): pozioma
   linia czyta się jak koniec karty, a sprawdzenie ma być dalszym ciągiem tego
   samego rozwiązania. Odstęp zostaje tej samej wielkości co przedtem, czyli
   dawne 2,4em marginesu plus 1,5em wypełnienia. */
.rozwiazanie-kroki .rozw-sprawdzenie {
    margin-top: 3.9em;
    max-width: 30em;
    margin-left: auto;
    margin-right: auto;
}

/* Zapis działania wykonywanego na obu stronach („/ · (x-1)", „/ - 6"). Stoi w tej
   samej linijce co rachunek, ale nie jest jego częścią: jest odsunięty i blady
   jak komentarz. W exercises.json idzie jako OSOBNY span obok wzoru:

       <div class="rozw-linia">\(2x + 6 = x\)<span class="rozw-dzialanie">\(\big/ - 6\)</span></div>

   Osobny span, a nie \qquad wewnątrz tego samego \( … \), bo na telefonie
   rachunek z doklejonym dopiskiem przekracza szerokość karty i KaTeX łamie go
   w przypadkowym miejscu, w środku ułamka. Rozdzielony zawija się jako całość:
   dopisek spada pod rachunek i dalej się czyta. */
.rozw-linia .rozw-dzialanie {
    margin-left: 2em;
    color: var(--text-faint);
    white-space: nowrap;
}
.rozw-linia .rozw-dzialanie .katex { color: inherit; }

/* ROZWIĄZANIE W DWÓCH KOLUMNACH: rachunek z lewej, użyty wzór z prawej, na tej
   samej wysokości co linijka, w której się go stosuje. Zapis w exercises.json:

       <div class="rozw-2kol">
         <div class="rozw-wiersz">
           <div class="rozw-obl">\(rachunek\)</div>
           <div class="rozw-wzor">\(wzór\)</div>
         </div>
         …
       </div>

   Wiersz bez wzoru zostawia pusty <div class="rozw-wzor"></div>. Pusta komórka
   jest potrzebna, żeby siatka nie przesunęła następnego rachunku do prawej
   kolumny. Kilka linijek rachunku pod jednym wzorem idzie w JEDNYM .rozw-obl,
   rozdzielone <br> (wtedy wzór stoi na środku całej grupy).

   Wyrównanie robi grid NA KONTENERZE, a nie flex na wierszu: wiersz jest tylko
   pojemnikiem logicznym i znika przez `display: contents`, więc wszystkie
   .rozw-obl i .rozw-wzor trafiają wprost do jednej siatki. Dzięki temu prawa
   kolumna ma jedną, wspólną krawędź dla całego rozwiązania. Przy flexie na
   każdym wierszu z osobna wzory rozjeżdżałyby się w poziomie.

   Szerokość kolumn: `auto 1fr` z lustrzanym wyrównaniem do środka między nimi.
   Kolumna wzorów bierze tyle, ile potrzebuje najszerszy wzór, a rachunek dostaje
   resztę. */
/* Zaznaczenie „popatrz TUTAJ" wewnątrz wzoru KaTeX-a, wołane z exercises.json
   jako \htmlClass{zielony}{...} (polecenie przepuszcza `trust` w renderMath).
   Token --accent-green, a NIE --correct: w COLORS.md zieleń poprawności znaczy
   „dobra odpowiedź", a to jest tylko wskazanie miejsca, do którego odnosi się
   wzór obok. Barwa idzie z tokenu, więc ciemny motyw obsługuje się sam.
   `color` musi wygrać z KaTeX-owym kolorem dziedziczonym po .katex. */
.katex .zielony,
.katex .zielony .mord,
.katex .zielony .mbin,
.katex .zielony .mrel { color: var(--accent-green); }

.rozw-2kol {
    display: grid;
    /* Obie kolumny biorą tyle, ile mają treści (było `1fr auto`). Przy 1fr lewa
       kolumna rozciągała się na całą kartę, więc rachunek wyrównany do prawej
       lądował na środku strony, a wzory dobijały do prawej krawędzi. */
    grid-template-columns: auto auto;
    column-gap: 2.5rem;
    row-gap: 1.1rem;
    /* Wzór ma stać na wysokości SWOJEGO rachunku, także gdy rachunek zajmuje
       dwie linijki, stąd wyśrodkowanie w pionie, nie wyrównanie do góry. */
    align-items: center;
    line-height: 2.1;   /* jak w .rozwiazanie-kroki: ułamki i pierwiastki potrzebują powietrza */
    text-align: left;
    /* Blok stoi PRZY LEWEJ krawędzi, a nie na środku karty (Henrich, 2026-08-21).
       Wyśrodkowany `margin: 0 auto` liczy środek dla pary kolumn RAZEM, więc im
       szersza kolumna wzorów, tym bardziej rachunek uciekał w prawo: przy zad. 3
       sam rachunek stał na środku karty, a wzory dobijały do prawej krawędzi.
       Przy lewej krawędzi rachunek zaczyna się w tym samym miejscu niezależnie
       od tego, jak szerokie są wzory. */
    margin: 0 auto 0 0;
    width: fit-content;
    max-width: 100%;
    /* Siatka mieści się na telefonie (zmierzone przy 485px: 364px treści na 423px
       miejsca), ale wzory KaTeX-a nie łamią się w środku, więc wyjątkowo szeroka
       para przewinie się TU, wewnątrz własnego bloku, zamiast rozpychać stronę. */
    overflow-x: auto;
}

.rozw-wiersz {
    /* Wiersz nie rysuje się sam, jego dzieci wchodzą wprost do siatki rodzica. */
    display: contents;
}

.rozw-2kol > .rozw-wiersz > .rozw-wzor {
    /* „Troszkę bledsze, ale tej samej wielkości": wzór jest przypomnieniem,
       więc ustępuje pierwszeństwa rachunkowi, ale nie robi się drobnym drukiem. */
    color: var(--text-muted-2);
}

/* Rachunek trzyma się prawej krawędzi swojej kolumny, wzór lewej krawędzi
   swojej: obie kolumny zbiegają się do wspólnej przerwy w środku, więc para
   „rachunek + wzór" czyta się jako jedna linijka, a nie dwa oddzielne słupki. */
.rozw-2kol > .rozw-wiersz > .rozw-obl { text-align: right; }
.rozw-2kol > .rozw-wiersz > .rozw-wzor { text-align: left; }

/* Blok wraca na środek karty. Domyślnie stoi przy lewej krawędzi (patrz wyżej),
   co pasuje do szerokich rozwiązań; wąskie, kilkuznakowe rachunki wyglądają przy
   niej na zgubione, więc dostają tę klasę w exercises.json (Henrich, 2026-08-21,
   na zad. 4). */
.rozw-2kol.rozw-srodek { margin: 0 auto; }

/* RÓWNE POŁÓWKI KARTY. Domyślne `auto auto` + fit-content stawia cały blok przy
   lewej krawędzi, więc przy wąskiej kolumnie wzorów obie kolumny zbijają się w
   lewy górny róg karty, a po prawej zostaje pusto (Henrich, 2026-08-30, zad. 2).
   Ta klasa daje każdej kolumnie połowę karty, dzięki czemu PRZERWA MIĘDZY NIMI
   wypada dokładnie na środku: rachunek dobija do środka z lewej, wzór od środka
   w prawo. Wyjątkowo szeroka linijka i tak rozepchnie swoją kolumnę, a blok
   przewinie się wtedy w sobie (`overflow-x` wyżej). */
.rozw-2kol.rozw-polowki {
    grid-template-columns: 1fr 1fr;
    width: 100%;
}

/* DWA TORY RACHUNKU OBOK SIEBIE, które na końcu schodzą się w jeden. Do zadań,
   w których liczy się dwie rzeczy niezależnie (zad. 7: osobno \(a\), osobno \(b\),
   dopiero na końcu ich iloczyn) — układ pokazuje to samo, co film: dwa etapy,
   a potem jedna linijka. Wiersz ma wtedy DWIE komórki .rozw-obl zamiast pary
   rachunek + wzór, a wiersze wspólne (układ na starcie, iloczyn na końcu,
   odpowiedź) dostają .rozw-pelny i rozpinają się na obie kolumny.
   Kolumny są równe i wyśrodkowane, bo żadna nie jest ważniejsza; przy domyślnym
   `auto auto` z wyrównaniem do środkowej przerwy dwa tory czytałyby się jak
   jeden długi zapis. */
.rozw-2kol.rozw-dwatory {
    grid-template-columns: 1fr 1fr;
    column-gap: 3rem;
}
.rozw-2kol.rozw-dwatory > .rozw-wiersz > .rozw-obl { text-align: center; }

/* Wiersz na całą szerokość, bez pary wzoru: zdanie wprowadzające albo końcowa
   odpowiedź. Wtedy .rozw-wiersz NIE dostaje drugiej komórki, sam .rozw-obl
   rozpina się na obie kolumny i wraca na środek. */
.rozw-2kol > .rozw-wiersz.rozw-pelny > .rozw-obl {
    grid-column: 1 / -1;
    text-align: center;
}

/* KOMENTARZ WEWNĄTRZ SIATKI (dodane 2026-08-29 dla zad. 7). W układzie dwutorowym
   komentarz nie może stać między dwoma blokami .rozw-2kol, bo każdy blok liczy
   szerokość kolumn osobno (`width: fit-content`) i tory rozjechałyby się w pionie.
   Idzie więc jako wiersz siatki, rozpięty na obie kolumny:

       <div class="rozw-wiersz rozw-pelny">
         <div class="rozw-komentarz">zdanie</div>
       </div>

   Marginesy pionowe zeruje row-gap siatki, który już daje odstęp; zostaje tylko
   lekkie odsunięcie, żeby zdanie nie kleiło się do rachunku nad nim. */
.rozw-2kol > .rozw-wiersz.rozw-pelny > .rozw-komentarz {
    grid-column: 1 / -1;
    text-align: center;
    margin: 0.5em auto 0.6em;
}


.solution-step-by-step-container {
    display: none;
    border-bottom: 1px solid var(--border-subtle); /* 2px→1px (etap 2): grubość nie niosła znaczenia */
    text-align: center;
}
/* Kadr 16:9 (zmiana 2026-08-11 — było sztywne 420×180, czyli 21:9).
   `width: 100%` zamiast stałej szerokości: film dostaje CAŁE miejsce, jakie ma
   kontener. Wcześniej przy 608px dostępnych zajmował 420 i 188px marnowało się
   bez powodu. Górna granica 640px pilnuje, żeby na bardzo szerokich ekranach
   nie zejść poniżej podwójnej gęstości pikseli (źródło ma 1280px szerokości). */
/* Proporcje kadru bierzemy Z PLIKU (--proporcje-filmu ustawia app/steps.js po
   wczytaniu metadanych), a 16/9 to tylko wartość na czas ładowania. Powód:
   zad. 2 jest już w nowym kadrze 16:9, ale zad. 1 i 3 wciąż mają stare 21:9 —
   wtłoczone na siłę w 16:9 zostawiały ~80 px martwego pasa nad i pod obrazem
   (w ciemnym motywie widocznego jako ciemne belki). Sztywna wartość na starcie
   rezerwuje miejsce, więc karta nie skacze, zanim film się wczyta. */
.solution-step-by-step-container video {
    display: block;
    width: 100%;
    max-width: 640px;
    height: auto;
    aspect-ratio: var(--proporcje-filmu, 16 / 9);
    margin: 0 auto;
    cursor: pointer;
    /* Odwrócenie w dark mode — patrz --filtr-grafik-zadan w style/base.css. */
    filter: var(--filtr-grafik-zadan);
}
/* Kontener filmu kroku. Nakładane na film ikonki pauzy/restartu zniknęły (2026-08-11, Henrich po testach v20): migały przy każdym zatrzymaniu, a stan odtwarzania pokazuje teraz przycisk w ROW 2. */
.step-video {
    display: block;
    line-height: 0;
}
.solution-step-by-step-container img {
    display: block;
    max-width: 320px;
    margin: auto;
    /* Odwrócenie w dark mode — patrz --filtr-grafik-zadan w style/base.css. */
    filter: var(--filtr-grafik-zadan);
}
.solution-step-by-step-container:empty {
    display: none;
}
/* Rezerwuje miejsce na kadr, ZANIM film się wczyta — inaczej blok ma zerową wysokość i karta podskakuje, gdy obraz wskoczy (Henrich po testach v20).
   Proporcje bierzemy z pliku (--proporcje-filmu ustawia app/steps.js po odczytaniu metadanych); do tego czasu obowiązuje 16/9, czyli kadr, w którym są nowe filmy 720p. Wartość siedzi na .steps-content, bo to jedyny element przeżywający podmianę kroku — .step-video powstaje dopiero z filmem. */
/* Kadr: kotwica (position: relative) dla wskaźnika ładowania. Ograniczenie
   szerokości i margines siedzą TUTAJ, a nie na .steps-content — inaczej
   margines przeciekłby poza kadr (position: relative nie tworzy BFC) i kropki
   ładowania lądowałyby 10px pod dolną krawędzią filmu. */
.steps-kadr {
    position: relative;
    max-width: 640px;   /* to samo ograniczenie co film — inaczej rezerwacja
                           i obraz liczylyby rozna wysokosc */
    margin: 10px auto;
}
.steps-content {
    aspect-ratio: var(--proporcje-filmu, 16 / 9);
    text-align: center;
}
/* Znak ładowania kroku: trzy pulsujące kropki w pigułce przy dolnej krawędzi
   kadru (zamiana 2026-08-14, Henrich po testach v32). Poprzednik — puls tła
   plus przygaszanie całego obrazu — migał przy każdej zmianie kierunku i przy
   spamowaniu ►, przez co strona wyglądała, jakby ładowała się bez końca.
   Kropki nie ruszają obrazu: leżą NAD nim, jak kręciołek w odtwarzaczu wideo.
   ZWŁOKA 500 ms zrobiona transition-delay (Henrich, TODO): przy szybkim łączu
   krok podmienia się wcześniej i nic nie zdąży mrugnąć. Zgaszenie jest
   natychmiastowe — opóźnienie ma tylko pojawienie się. */
.steps-ladowanie {
    position: absolute;
    left: 50%;
    bottom: 8px;
    transform: translateX(-50%);
    display: flex;
    gap: 5px;
    padding: 6px 9px;
    border-radius: var(--radius-pigulka);
    background-color: var(--bg);
    opacity: 0;
    pointer-events: none;
    transition: opacity 120ms ease;
}
.steps-kadr.laduje .steps-ladowanie {
    opacity: 1;
    transition: opacity 150ms ease 500ms;
}
.steps-ladowanie span {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: var(--text-faint);
    animation: kropki-ladowania 1.2s ease-in-out infinite;
}
.steps-ladowanie span:nth-child(2) {
    animation-delay: 0.16s;
}
.steps-ladowanie span:nth-child(3) {
    animation-delay: 0.32s;
}
@keyframes kropki-ladowania {
    0%, 70%, 100% { opacity: 0.3; }
    35%           { opacity: 1; }
}
/* Użytkownik prosi o brak animacji → pigułka pokazuje się, ale kropki stoją. */
@media (prefers-reduced-motion: reduce) {
    .steps-ladowanie span {
        animation: none;
        opacity: 0.6;
    }
}

/* ROW 1 — kropki stanu z paskiem postępu w odstępie (przebudowa 2026-08-11).
   Model: kropka = stan działania, odcinek między kropkami = krok. Pasek, który
   był belką pod filmem, wypełnia teraz odcinek bieżącego kroku. */
/* Szerokość: 80% z v21 okazało się za ciasne dla wersji przewijanej — mieściło
   za mało kropek naraz (Henrich po testach v21). Wracamy prawie do pełnej
   szerokości kadru, ale odzyskane miejsce idzie w KROPKI, nie w rozciągnięcie:
   niżej ścieśnione są odstępy kropek, odcinków i strzałek. */
.steps-dots-row {
    position: relative; /* kotwica dla schowanego .step-counter */
    display: flex;
    align-items: center;
    gap: 2px;
    max-width: 96%;
    margin: 12px auto 6px;
}
/* Okno przewijania: powyżej siedmiu kropek pasek jedzie w poziomie zamiast się
   ściskać. Pasek przewijania chowamy — od tego są strzałki po bokach.
   Margines 8 px z obu stron (Henrich, TODO): krańcowe kropki nie stykają się
   z krawędzią paska. Margines TU, a nie padding na .steps-dots — tamten ma
   min-width: 100% przy content-box, więc poziomy padding dokładałby się do
   szerokości i uruchamiał przewijanie przedwcześnie (patrz komentarz niżej). */
.steps-dots-okno {
    flex: 1;
    margin: 0 8px;
    overflow-x: auto;
    scrollbar-width: none;
    /* Sprężynowanie na iOS ma zostać w tym pasku i nie ciągnąć za sobą strony;
       przy okazji nie zostawia paska odbitego poza treścią. */
    overscroll-behavior-x: contain;
}
.steps-dots-okno::-webkit-scrollbar {
    display: none;
}
.steps-dots {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 100%;
    width: max-content;
    /* box-sizing: border-box jest tu konieczne. Przy content-box poziomy padding
       dokładał się do min-width: 100% i pasek zaczynał się przewijać wcześniej,
       niż powinien (stąd dawny zakaz paddingu w poziomie). Przy border-box
       padding mieści się w środku, a jest potrzebny: powiększone pola dotyku
       skrajnych kropek wchodzą właśnie w te 5 px i nie wystają poza treść. */
    box-sizing: border-box;
    padding: 8px 5px;
}
/* Pole dotyku 44px (Henrich: dzisiejsze strzałki są za małe na kciuk) uzyskane
   paddingiem — sama kropka zostaje mała. box-sizing: content-box, żeby padding
   nie zjadł średnicy. */
.step-dot {
    flex: none;
    position: relative; /* kotwica dla powiększonego pola dotyku (::after) */
    box-sizing: content-box;
    width: 10px;
    height: 10px;
    /* Poziomy padding 8→5 px (Henrich po testach v21: „byle wersja przewijana
       mieściła więcej kropek"). Pionowy zostaje — to on daje 44 px wysokości
       pola dotyku. */
    padding: 17px 5px;
    border: none;
    border-radius: 0;
    background-color: transparent;
    background-clip: content-box;
    cursor: pointer;
    /* Sama kropka rysowana tłem obszaru treści: pole dotyku jest większe niż to,
       co widać. */
    background-image: radial-gradient(circle, var(--border-muted) 0 50%, transparent 50%);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 10px 10px;
}
/* Pole dotyku szersze niż sama kropka (Henrich, iPhone SE: „ciężko trafić
   w kropki"). Rozszerzamy pseudoelementem, a nie paddingiem, bo padding
   zmieniłby układ i zmniejszył liczbę kropek mieszczących się w pasku.
   Po 5 px na stronę przy odcinkach szerokich na 10 px znaczy, że pola dotyku
   sąsiednich kropek dokładnie się stykają i nigdzie nie nachodzą: każde
   dotknięcie paska trafia w najbliższą kropkę, żadna nie kradnie cudzego. */
.step-dot::after {
    content: "";
    position: absolute;
    inset: 0 -5px;
}
.step-dot.odwiedzona {
    background-image: radial-gradient(circle, var(--text-faint) 0 50%, transparent 50%);
}
.step-dot.biezaca {
    background-image: radial-gradient(circle, var(--text) 0 50%, transparent 50%);
    background-size: 15px 15px;
}
.step-dot:hover {
    background-size: 15px 15px;
}
/* Odcinek między dwiema kropkami = jeden krok. Cienka kreska zawsze, wypełnienie
   tylko w kroku bieżącym (--postep ustawia rysujPostep w app/steps.js). */
.step-link {
    position: relative;
    /* Odcinek jest wyłącznie ozdobą, a jako element pozycjonowany leżałby NAD
       powiększonym polem dotyku sąsiedniej kropki i przechwytywał jej kliknięcia.
       Przepuszczamy je dalej. */
    pointer-events: none;
    flex: 1 1 14px;
    min-width: 10px;
    height: 2px;
    background-color: var(--border-muted);
    --postep: 0%;
}
/* Trzy stopnie kontrastu, od najsłabszego: odcinek pusty (--border-muted, tło
   .step-link) → odcinek już przerobiony (--text-faint) → odcinek bieżący
   (--text). Bieżący ma być NAJMOCNIEJSZY, żeby od razu było widać, gdzie się
   jest — Henrich po testach v27: „obecna linia powinna być bardziej uwydatniona,
   bardziej kontrastowa". W ciemnym motywie znaczy to jaśniejsza, w jasnym
   ciemniejsza; tokeny odwracają się same. */
.step-link-fill {
    display: block;
    width: var(--postep);
    height: 100%;
    background-color: var(--text-faint);
}
.step-link.biezacy .step-link-fill {
    background-color: var(--text);
}
/* Strzałki przewijania kropek — pokazywane przez zbudujKropki() dopiero powyżej
   siedmiu kropek. */
.steps-scroll {
    flex: none;
    width: 24px;
    height: 44px;
    padding: 0;
    border: none;
    background: none;
    font-size: 20px;
    line-height: 1;
    color: var(--text-faint);
    cursor: pointer;
}
.steps-scroll:hover {
    color: var(--text);
}
/* Licznik „3 / 6" zniknął z widoku — zostaje wyłącznie dla czytników ekranu
   i dla zgłoszeń błędów (app/report.js czyta go z DOM). */
.step-counter {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}
/* ROW 2 — przyciski. Pole dotyku min. 44px w obu wymiarach (Henrich).
   Odstęp 10→48 px (Henrich, TODO): strzałki mają być rozsunięte na tyle, żeby
   kciuk trafiał w nie bez celowania i nie mylił ich ze środkowym play. Trójka
   zostaje wyśrodkowana — na telefonie 3×44 px + 2×48 px = 228 px, czyli mieści
   się w karcie (354 px przy ekranie 390 px) z zapasem. */
.steps-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 48px;
    margin: 4px 0 10px;
}
/* Pole dotyku 44×44 (dawne strzałki były na to za małe — Henrich).
   Podkreślenie z .light-button:hover na samej ikonie wyglądałoby jak błąd,
   więc zamiast niego zmiana koloru. Flex centruje ikonę w obu osiach — glif
   tekstowy siedział lekko w lewo i w dół. */
.steps-nav button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    min-width: 44px;
    min-height: 44px;
    margin: 0;
    color: var(--text-faint);
}
.steps-nav button:hover:not(:disabled) {
    text-decoration: none;
    color: var(--text);
}
.steps-nav button:disabled {
    opacity: 0.35;
    cursor: default;
}
/* Zaokrąglone końce i złączenia — Henrich: dotychczasowe strzałki były „zbyt
   ostre". Trójkąt odtwarzania jest wypełniony, „daszki" kroków tylko kreską:
   to wystarcza, żeby nie myliły się ze sobą, bez obwódki wokół środkowego.
   22→26 px (Henrich, TODO): daszki kroków zrównane z trójkątem play — jedna
   wielkość dla wszystkich trzech ikon, więc osobna reguła dla .step-play
   przestała być potrzebna. Pole dotyku zostaje 44×44 (.steps-nav button). */
.step-ikona {
    width: 26px;
    height: 26px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ikona-play {
    fill: currentColor;
}
/* Jeden przycisk, trzy stany: odtwórz (domyślnie), pauza w trakcie grania,
   „odtwórz ponownie" dopiero po dobiegnięciu filmu do końca. */
.step-play .ikona-pauza,
.step-play .ikona-odnowa,
.step-play.gra .ikona-play,
.step-play.koniec .ikona-play {
    display: none;
}
.step-play.gra .ikona-pauza {
    display: block;
}
.step-play.koniec .ikona-odnowa {
    display: block;
}
.step-play.koniec.gra .ikona-odnowa {
    display: none;
}
.step-play.koniec.gra .ikona-pauza {
    display: block;
}
/* ROW 3 — zwijane wyjaśnienie kroku. Wzorowane na „Sprawdzaniu obliczeń"
   (.ocena-box), ale BEZ ramki, żeby nie odcinało się od bloku z krokami
   (Henrich, TODO.md). Zajęło miejsce pola "text", które wcześniej było
   podpisem pod filmem. */
.step-explain {
    max-width: 520px;
    /* Więcej powietrza nad i pod (Henrich po testach v20). */
    margin: 18px auto 28px;
    /* Sam przycisk wyśrodkowany pod przyciskami ROW 2 (Henrich po testach v21);
       rozwinięty tekst zostaje wyrównany do lewej, bo to akapit do czytania. */
    text-align: center;
}
/* Sam przycisk jest WĄSKI — szeroki na tyle, ile zajmuje napis ze strzałką.
   Wcześniej ciągnął się na całą szerokość bloku, a strzałkę odpychał na drugi
   koniec (margin-left: auto), przez co między tytułem a nią ział pusty pas.
   inline-flex + wyśrodkowanie kontenerem zamiast tego. */
.step-explain-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 2px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-faint);
    cursor: pointer;
    list-style: none;
}
.step-explain-toggle::-webkit-details-marker {
    display: none;
}
.step-explain-toggle::after {
    content: "›";
    font-size: 18px;
    line-height: 1;
    color: var(--text-faint);
    transition: transform 150ms ease;
}
.step-explain[open] > .step-explain-toggle::after {
    transform: rotate(90deg);
}
.step-explain-toggle:hover {
    color: var(--text);
}
.step-explain:not([open]) .gdy-rozwiniete,
.step-explain[open] .gdy-zwiniete {
    display: none;
}
/* Treść odsunięta od tytułu, żeby rozwinięcie było widać jako osobny akapit,
   a nie doklejoną linijkę (Henrich po testach v20).
   Lewe wcięcie 30 px = tyle samo, co .solution-text-container (Henrich, TODO:
   „sformatować tak jak solution-text-container"). Na telefonie oba akapity
   startują wtedy dokładnie w tym samym miejscu: 18 px karty + 12 px podokna
   + 30 px wcięcia. Pionowe odstępy zostają małe — 30 px z rozwiązania odcina
   tekst od ramki podokna, a tu nad tekstem jest sam przycisk zwijania.
   Reszta typografii (18 px, kolor, rodzina) jest już wspólna, bo dziedziczona. */
.step-explain-tresc {
    padding: 8px 0 10px 30px;
    line-height: 1.5;
    text-align: left;
}
.solution-interactive-container {
    padding-bottom: 30px;
    border-bottom: 1px solid var(--border-subtle); /* 2px→1px (etap 2): grubość nie niosła znaczenia */
    text-align: center;
}
.solution-interactive-container:empty {
    display: none;
}

/* Tytuł arkusza nad pierwszym zadaniem (dawniej w środku #top-bara).
   :empty ukrywa go, dopóki applySheetMeta() (bootstrap.js) go
   nie wypełni — inaczej pusty <h1> zostawiałby odstęp przed pierwszym zadaniem. */
.sheet-title-heading {
    /* Węższa kolumna niż cała strona (Henrich 2026-07-27): pełna szerokość
       ciągnęła tytuł CKE w jedną długą linię przez cały ekran. 32% ≈ 460px przy
       1440px, więc typowy tytuł zawija się na 2–3 wyśrodkowane linie.
       Ograniczenie zdejmowane na telefonie (style/responsive.css) — 32% z 390px
       zostawiłoby po dwa słowa w linii. */
    max-width: 32%;
    margin: 0 auto 20px;
    font-size: 24px;
    font-weight: 600;
    color: var(--text);
    text-align: center;
}
.sheet-title-heading:empty {
    display: none;
}

/* ===== PRZEŁĄCZNIK TRYBU (ćwiczenia / egzamin) =====
   Segmented control pod tytułem arkusza — następca biernego napisu „tryb
   ćwiczenia" z dawnego #top-bara. Dwie połówki w jednym obrysie.
   Aktywny segment był do 2026-07-27 pełną INWERSJĄ (tło --text, tekst --bg):
   w jasnym motywie czarny prostokąt, w ciemnym prawie biały — za mocno na
   drobną kontrolkę (zgłoszenie Henricha). Teraz miękkie wypełnienie
   --segment-bg (base.css): w jasnym JAŚNIEJSZE od czerni, w ciemnym
   CIEMNIEJSZE od bieli. Stan niosą razem tło i font-weight — samo subtelne
   tło byłoby zbyt słabym sygnałem, a koloru akcentu tu nie używamy
   (niebieski znaczy już „Twój wybór w zadaniu").
   Stan ustawia updateModeSwitch() w app/exam.js — klasa .aktywny + aria-pressed.
   Ukryty do wyrenderowania arkusza, jak #egzamin-start-stopka. */
#tryb-przelacznik {
    display: none;
    width: fit-content;
    /* 50px góra / 40px dół — odstępy ustawił Henrich; jedna deklaracja, żeby
       nie wracać do dwóch nadpisujących się `margin`ów. */
    margin: 50px auto 40px;
    overflow: hidden;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pigulka);
}
body.arkusz-wczytany #tryb-przelacznik {
    display: flex;
}
#tryb-przelacznik button {
    padding: 5px 14px;
    border: 0;
    background-color: transparent;
    font-family: inherit;
    font-size: 13px;
    line-height: 1.3;
    white-space: nowrap;
    color: var(--text-faint);
    cursor: pointer;
    transition: background-color 150ms ease, color 150ms ease;
}
#tryb-przelacznik button:hover:not(.aktywny) {
    background-color: var(--bg-muted);
    color: var(--text);
}
#tryb-przelacznik button.aktywny {
    background-color: var(--segment-bg);
    font-weight: 600;
    color: var(--segment-text);
    cursor: default;
}

/* ===== WYKRESY ===== */
canvas {
    margin-top: 10px;
    background-color: var(--canvas-bg);
}

/* ===== WIDŻETY INTERAKTYWNE (solutionWidget) ===== */
.widget {
    padding-top: 10px;
    text-align: center;
}
.widget-title {
    /* Margines z góry: tytuł nie ma się kleić do separatora nad widżetem.
       Margines z dołu podbity 4px → 16px (Henrich, 2026-08-29, zad. 9): przy
       4px polecenie („kliknij na osi…") kleiło się do rysunku i czytało jak
       jego podpis. Płótno dokłada do tego swoje 10px margin-top. */
    margin: 14px 20px 16px;
    font-size: 15px;
    color: var(--text-muted-2);
}
/* Zakładki widżetu (wgZakladki w widgets/_helpers.js): przełączanie widoków
   jednego widżetu, np. dwa zdania zadania 12.1. Aktywna = wybór ucznia,
   stąd niebieski (--accent-blue-strong, patrz COLORS.md). */
.wg-zakladki {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin: 14px 20px 2px;
}
.wg-zakladka {
    padding: 4px 14px;
    font-size: 14px;
    color: var(--text-muted-2);
    background: none;
    border: var(--border-kontrolka) solid var(--border);
    border-radius: var(--radius-kontrolka);
    cursor: pointer;
}
.wg-zakladka-aktywna {
    border-color: var(--accent-blue-strong);
    color: var(--accent-blue-strong);
}
/* Etykieta przy suwaku (np. "a = -1,5" w zad. 13 maja). Szerokość jest STAŁA,
   nie min-width: przy min-width dłuższy napis ("a = -1,5" kontra "a = 3")
   rozpychał kolumnę i suwak skakał w bok podczas przeciągania. Teraz napis
   rośnie w lewo (text-align: right), a suwak stoi w miejscu. Widżet, który
   potrzebuje szerszej etykiety (zad. 14: cały zapis "f(x + 1)"), ustawia
   --wg-etykieta-szer na swoim .widget-controls. */
.wg-suwak-etykieta {
    display: inline-block;
    width: var(--wg-etykieta-szer, 76px);
    text-align: right;
    margin-right: 10px;
    white-space: nowrap;
}
.widget-canvas {
    max-width: 100%;
    touch-action: none; /* przeciąganie palcem nie scrolluje strony */
}
.widget-controls {
    margin-top: 6px;
}
/* Suwaki w widżetach: globalna reguła input { width: 24px } jest za wąska.
   accent-color maluje uchwyt i wypełnienie na fiolet zamiast systemowego
   niebieskiego. Fiolet, bo suwak steruje tym, co rysuje się na płótnie
   (obramowania słupków, wykresy), a niebieski znaczy w tym projekcie
   „zaznaczona odpowiedź". Patrz COLORS.md. */
.widget input[type=range] {
    width: 320px;
    max-width: 90%;
    border: none;
    accent-color: var(--accent-purple);
}
/* Strzałki po bokach suwaka, wstawiane przez wgDodajStrzalkiSuwakow
   (app/widget-helpers.js) już po zbudowaniu widżetu, więc w widgets/ nie ma po
   nich śladu. Stuknięcie przesuwa o jeden krok, przytrzymanie przewija; palcem
   inaczej nie da się trafić w dokładną wartość. Kształt i barwy jak przy
   strzałkach kroków rozwiązania (.steps-nav). */
.wg-suwak-grupa {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
    max-width: 100%;
}
/* Suwak w grupie ma się KURCZYĆ, gdy strzałki nie mieszczą się razem z nim:
   inaczej cały rząd wyjeżdża poza kartę zadania. */
.wg-suwak-grupa input[type=range] {
    flex: 0 1 auto;
    min-width: 0;
    max-width: none;
}
.wg-suwak-strzalka {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Pole dotyku 44px w pionie (zasada Henricha dla przycisków dotykowych).
       W poziomie węższe, bo dwie strzałki zabierają szerokość samemu suwakowi. */
    width: 30px;
    min-height: 44px;
    padding: 0;
    border: none;
    background: none;
    color: var(--text-faint);
    cursor: pointer;
    /* Bez tych trzech przytrzymanie palcem zaznacza tekst i otwiera menu
       kontekstowe, zamiast przewijać wartość. */
    touch-action: manipulation;
    user-select: none;
    -webkit-touch-callout: none;
}
.wg-suwak-strzalka:hover:not(:disabled) {
    color: var(--text);
}
.wg-suwak-strzalka:disabled {
    opacity: 0.3;
    cursor: default;
}
.wg-suwak-ikona {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.widget input[type=number] {
    width: 40px;
}
/* Zad 1: kolory okienek a i b odpowiadają kolorom elementów na osi —
   a (zielone) to środek −a, b (niebieskie) to strzałki odległości. */
.widget input.wg-a {
    border: 2px solid var(--accent-green);
}
.widget input.wg-b {
    border: 2px solid var(--accent-blue-strong);
}
/* Przycisk akcji w widżecie (zad. 33.2 maja: „wystrzel piłeczkę"), w stylu
   przycisków odpowiedzi: lekka ramka, zaokrąglone rogi, tło strony. Stała
   szerokość, bo napis przełącza się na „leci…" i bez niej suwak obok
   przeskakiwałby w bok. Pod przyciskiem jest miejsce na licznik czasu. */
.wg-akcja {
    width: 112px;
    /* Wysokość dwóch linii na stałe: napis przełącza się między dwuwierszowym
       „Wystrzel piłeczkę" a jednowierszowym „Leci…", a bez tego suwak obok
       i licznik pod spodem skakałyby w pionie. Wartość w pikselach, bo przy
       box-sizing: border-box miara w em obejmowałaby też padding i ramkę,
       więc dwuwierszowy napis i tak by ją rozepchnął. */
    min-height: 52px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    color: var(--text);
    font-family: "STIX Two Math", "Lora", serif;
    font-size: 15px;
    line-height: 1.25;
    cursor: pointer;
}
.wg-akcja:hover:not(:disabled) {
    border-color: var(--accent-blue);
}
.wg-akcja:disabled {
    color: var(--text-faint-2);
    cursor: default;
}
/* Kolumna: przycisk, pod nim licznik czasu lotu. */
.wg-akcja-kolumna {
    display: inline-block;
    vertical-align: middle;
    margin-right: 12px;
    text-align: center;
}
.wg-akcja-zegar {
    margin-top: 4px;
    font-size: 14px;
    color: var(--text-muted-2);
}
/* Przycisk resetu w kontrolkach widżetu — nie rozciągamy go jak zwykłych
   .light-button (30% szerokości), tylko trzymamy przy okienkach. */
.widget .wg-reset {
    width: auto;
    margin-left: 14px;
    font-size: 14px;
}
/* Stała wysokość odczytu, żeby widżet nie skakał przy zmianie komunikatu.
   Dwie linijki: wartości w pierwszej, komentarz ✓/✗ w drugiej (po <br>). */
.widget-readout {
    min-height: 3.4em;
    margin-top: 8px;
    /* Spójnie z .rozwiazanie-kroki: ta sama wielkość pisma i ten sam oddech
       między linijkami (odczyt to też rachunek, linijki rozdziela <br>). */
    font-size: 17px;
    line-height: 2.1;
}
.wg-ok {
    color: var(--correct);
}
.wg-zle {
    color: var(--incorrect);
}
.wg-neutral {
    color: var(--text-faint-2);
}
/* Lista wzorów funkcji przedziałami (zad 10) — aktywny przedział podświetlony. */
.widget-formula-list {
    display: inline-block;
    margin-top: 6px;
    text-align: left;
}
.wg-formula {
    padding: 2px 10px;
    border-left: 3px solid transparent;
    color: var(--text-faint-2);
}
.wg-formula.active {
    border-left-color: var(--accent-purple);
    color: var(--text);
    font-weight: bold;
}

/* ===== FORMULARZE ===== */
input {
    width: 24px;
    border: 1px solid var(--border-strong);
    font-size: 18px;
    text-align: center;
}

/* ABY FIREFOX ORAZ CHROME NIE POSIADAŁY STRZAŁEK PRZY INPUCIE LICZBY */
input[type=number] {
    -moz-appearance: textfield;
    appearance: textfield;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    margin: 0;
    -webkit-appearance: none;
    appearance: none;
}

/* ===== FEEDBACK ===== */
.feedback {
    margin-left: 10px;
    font-size: 16px;
}

/* ===== TABELKI DANYCH W TREŚCI ZADAŃ (np. zad 29) ===== */
/* Tabela danych w treści zadania. `display: block` + `overflow-x: auto` (ten sam
   zabieg co przy .katex-display w base.css) daje jej WŁASNY poziomy scroll, gdy
   nie mieści się w karcie — bez tego szeroka tabela rozpycha całą stronę.
   Zmierzone: tabela w zad. 33 dawała 12px poziomego scrolla przy 360px.
   `width: fit-content` trzyma `margin: auto` centrowanie, dopóki tabela się mieści. */
.data-table {
    display: block;
    width: fit-content;
    max-width: 100%;
    margin: 10px auto;
    overflow-x: auto;
    border-collapse: collapse;
}
.data-table th,
.data-table td {
    padding: 6px 12px;
    border: 1px solid var(--border-strong);
    font-weight: normal;
    text-align: center;
}
.data-table th {
    background-color: var(--bg-subtle);
    text-align: left;
}

/* ===== ZGŁASZANIE BŁĘDÓW W ZADANIU =====
   Dyskretny link pod zadaniem + formularz rozwijany W KARCIE zadania
   (app/report.js przenosi jeden wspólny #zglos-blad-overlay do klikniętej
   karty, nad rząd [Podpowiedź][Rozwiązanie]). To NIE jest już modal — nie ma
   przyciemnionego tła ani position:fixed, żeby treść zadania została widoczna
   podczas opisywania błędu (zwłaszcza na telefonie). Wszystkie kolory idą przez
   zmienne z base.css, więc jasny/ciemny motyw działa bez zmian. Toggle w menu
   „⋯" (#zglos-blad-toggle) dodaje body.bez-zglaszania, która chowa wszystkie
   linki. */

/* Link „zgłoś błąd w tym zadaniu" — tekstowy, wzorem badge'a .exercise-score
   trzyma się przygaszonej kolorystyki, żeby nie odciągał uwagi od zadania. */
/* „zgłoś błąd" siedzi w .light-button-container i nosi klasę .light-button,
   więc wygląd (rozmiar, kolor, hover) bierze STAMTĄD — tu zostaje sama klasa
   jako uchwyt dla globalnego wyłącznika niżej. Dawniej był to osobny, mniejszy
   i bledszy napis pod kartą (13px, --text-faint, display:block). */
/* Globalne wyłączenie linku (toggle w menu ⋯). */
body.bez-zglaszania .report-error-link {
    display: none;
}

/* Kontener formularza — zwykły blok w przepływie karty zadania (JS przełącza
   display na block). Bez position/z-index/tła: to element treści, nie nakładka. */
#zglos-blad-overlay {
    display: none;
    margin: 16px 0;
}
/* Właściwe „okno" formularza. Pozostaje position:relative, bo krzyżyk ✕ jest
   pozycjonowany względem niego. Bez max-height — blok rośnie w treści strony,
   a scrolluje się cała strona (na telefonie to znacznie wygodniejsze niż
   wewnętrzny scroll w okienku). */
.zglos-blad-okno {
    position: relative;
    width: 100%;
    padding: 20px 22px;
    border: 1px solid var(--border-window);
    /* Własny token, nie --bg-muted: w ciemnym motywie formularz ma być „czymś
       pomiędzy" tłem strony a resztą paneli — wyraźnie odcięty od karty zadania,
       ale nie jaśniejszy niż trzeba. W jasnym motywie wartość jest ta sama co
       dawniej (#f7f7f7), więc nic się tam nie zmienia. */
    background-color: var(--bg-formularz);
    text-align: left;
    font-size: 16px;
    line-height: 1.55;
    box-sizing: border-box;
}
.zglos-blad-okno h3 {
    margin: 0 40px 4px 0; /* miejsce na krzyżyk w prawym górnym rogu */
}
/* Krzyżyk zamykający — jak w panelach PDF, ale mniejszy (modal, nie okno). */
.zglos-blad-x {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid var(--border-close);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-size: 16px;
    line-height: 1;
    color: var(--text-muted-2);
    cursor: pointer;
}
.zglos-blad-x:hover {
    background-color: var(--bg-hover);
}
.zglos-blad-kontekst {
    margin: 0 0 10px;
    font-size: 14px;
    color: var(--text-faint);
}
.zglos-blad-info {
    margin: 0 0 16px;
    font-size: 14px;
    color: var(--text-muted-2);
}
.zglos-blad-label {
    display: block;
    margin: 12px 0 4px;
    font-size: 14px;
    color: var(--text-muted-2);
}
/* Pigułki kategorii — wybór WIELOKROTNY (app/report.js przełącza .selected na
   klikniętej). Wygląd celowo taki sam jak przycisków odpowiedzi ABCD, żeby
   uczeń rozpoznał interakcję; zaznaczenie = niebieska ramka jak .selected
   w .button-container. Flex-wrap, bo na telefonie muszą się zawijać. */
.zglos-blad-kategorie {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 4px;
}
.zglos-blad-kategorie button {
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 14px;
    line-height: 1.3;
    text-align: left;
    color: var(--text);
    cursor: pointer;
}
.zglos-blad-kategorie button:hover {
    border-color: var(--border-strong);
}
/* Ten sam sposób zaznaczenia co w .button-container button.selected: druga
   „warstwa" ramki przez inset box-shadow, żeby przycisk nie zmieniał rozmiaru. */
.zglos-blad-kategorie button.selected {
    border-color: var(--accent-blue);
    box-shadow: inset 0 0 0 1px var(--accent-blue);
}
/* Pola formularza — jawne width:100%, bo globalna reguła `input { width: 24px }`
   inaczej ściska pole e-mail do niczego (ten sam problem, co .fill-in-input). */
.zglos-blad-okno textarea,
.zglos-blad-okno input[type="email"] {
    width: 100%;
    box-sizing: border-box;
    padding: 8px 10px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 15px;
    text-align: left; /* nadpisuje globalne `input { text-align: center }` */
    color: var(--text);
}
.zglos-blad-okno textarea {
    resize: vertical;
    min-height: 70px;
}
.zglos-blad-okno textarea:focus,
.zglos-blad-okno input[type="email"]:focus {
    outline: none;
    border-color: var(--accent-blue-strong);
}
/* Opis jest obowiązkowy — pusty przy próbie wysyłki dostaje czerwoną ramkę
   i komunikat pod spodem (role="alert"). Oba znikają po wpisaniu pierwszego
   znaku (app/report.js). Ramka błędu musi bić regułę :focus, bo pole dostaje
   wtedy focus — stąd kolejność i dodatkowy selektor z :focus. */
.zglos-blad-okno textarea.zglos-blad-pole-blad,
.zglos-blad-okno textarea.zglos-blad-pole-blad:focus {
    border-color: var(--incorrect);
    box-shadow: inset 0 0 0 1px var(--incorrect);
}
/* Rząd pod textareą: komunikat błędu z lewej, licznik znaków z prawej.
   align-items: flex-start, bo komunikat bywa dwuliniowy na wąskim ekranie,
   a licznik ma wtedy zostać przy górnej krawędzi. */
.zglos-blad-pod-polem {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-top: 6px;
}
.zglos-blad-blad {
    margin: 0;
    font-size: 14px;
    color: var(--incorrect);
}
/* Pusty komunikat nie ma zajmować miejsca w układzie. */
.zglos-blad-blad:empty {
    display: none;
}
/* Licznik „123 / 2000" — margin-left:auto dosuwa go do prawej także wtedy,
   gdy komunikat błędu jest pusty i schowany (:empty). */
.zglos-blad-licznik {
    margin-left: auto;
    flex-shrink: 0;
    font-size: 13px;
    font-variant-numeric: tabular-nums; /* liczby nie „skaczą" przy pisaniu */
    color: var(--text-faint);
}
/* Ostatnie 10% limitu — ostrzeżenie, zanim tekst zacznie się ucinać. */
.zglos-blad-licznik-blisko {
    color: var(--incorrect);
}
/* Honeypot: obecny w DOM, ale poza ekranem — ludzie go nie widzą, boty wypełnią. */
.zglos-blad-hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}
/* Rozwijany podgląd danych technicznych (transparentność). */
.zglos-blad-dane {
    margin-top: 14px;
    font-size: 13px;
    color: var(--text-faint);
}
.zglos-blad-dane summary {
    cursor: pointer;
}
.zglos-blad-dane pre {
    max-height: 180px;
    margin: 8px 0 0;
    overflow: auto;
    padding: 8px 10px;
    border: 1px solid var(--border-subtle);
    background-color: var(--bg-code);
    font-size: 12px;
    line-height: 1.4;
    color: var(--text-muted-2);
    white-space: pre-wrap;
    word-break: break-word;
}
.zglos-blad-akcje {
    display: flex;
    gap: 12px;
    justify-content: flex-end;
    margin-top: 20px;
}
/* „Anuluj" — lekki tekstowy przycisk; „Wyślij" — obwiedziony, wzorem
   #egzamin-koniec-bar (ta sama ramka/padding). */
.zglos-blad-anuluj {
    padding: 8px 14px;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 15px;
    color: var(--text-muted-2);
    cursor: pointer;
}
.zglos-blad-anuluj:hover {
    text-decoration: underline;
}
.zglos-blad-wyslij {
    padding: 8px 16px;
    border: var(--border-kontrolka) solid var(--border);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-family: inherit;
    font-size: 15px;
    white-space: nowrap;
    color: var(--text);
    cursor: pointer;
}
.zglos-blad-wyslij:hover {
    border-color: var(--border-strong);
}
/* Wyszarzony, dopóki opis jest pusty (odswiezPrzyciskWyslij w app/report.js). */
.zglos-blad-wyslij:disabled {
    opacity: 0.5;
    cursor: default;
}
.zglos-blad-wyslij:disabled:hover {
    border-color: var(--border); /* bez reakcji na hover, gdy nieaktywny */
}

/* Toast po wysłaniu — na dole ekranu, pojawia się i znika przez opacity.
   Sukces w zielonej ramce (--correct), błąd w czerwonej (--incorrect). */
.zglos-toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    z-index: 22;
    max-width: calc(100vw - 40px);
    padding: 12px 20px;
    border: 1px solid var(--correct);
    border-radius: var(--radius-kontrolka);
    background-color: var(--bg);
    font-size: 15px;
    color: var(--text);
    box-shadow: var(--shadow-panel);
    transform: translate(-50%, 12px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.zglos-toast.zglos-toast-widoczny {
    transform: translate(-50%, 0);
    opacity: 1;
}
.zglos-toast-blad {
    border-color: var(--incorrect);
}
