:root {
  color-scheme: light dark;
  --bg: #eceff4;
  --surface: #ffffff;
  --panel: #e2e6ee;
  --border: #cdd3de;
  --text: #111111;
  --muted: #3a414c;
  --primary: #064b80;
  --primary-hover: #003f73;
  --primary-contrast: #ffffff;
  --radius: 12px;
  --shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow-raised: 0 2px 8px -2px rgba(15, 23, 42, 0.12), 0 1px 2px rgba(15, 23, 42, 0.06);
  --focus: #1d4ed8;

  --danger: #8f1111;
  --danger-bg: #fef2f2;
  --warning: #6b380d;
  --warning-bg: #fffbeb;
  --success: #064e3b;
  --success-bg: #ecfdf5;

  /* Podświetlenie wiersza tabeli pod myszą i fokusem. W motywie jasnym osobny,
     jaśniejszy odcień — kolor z motywu ciemnego zniknąłby na białym tle. */
  --row-hover: #dfe4ec;
}

/* Paleta ciemna w JEDNYM miejscu, uzywana w dwoch kontekstach:
   1) gdy uzytkownik NIE wybral motywu, a system prosi o ciemny,
   2) gdy uzytkownik wybral ciemny jawnie (data-theme="dark").
   Selektor :where() ma specyficznosc 0, wiec nie podbija wagi regul.
   Dwie kopie tej palety rozjechalyby sie przy pierwszej zmianie kolorow. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0b0f17;
    --surface: #1b2436;
    --panel: #141c2b;
    --border: #2b3852;
    --text: #f8fafc;
    --muted: #cbd5e1;
    --primary: #9fd0ff;
    --primary-hover: #c7e2ff;
    --primary-contrast: #08111f;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-raised: 0 2px 10px -2px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.35);
    --focus: #93c5fd;

    --danger: #fca5a5;
    --danger-bg: rgba(239, 68, 68, 0.12);
    --warning: #fbbf24;
    --warning-bg: rgba(245, 158, 11, 0.12);
    --success: #5eead4;
    --success-bg: rgba(16, 185, 129, 0.12);

    /* Wartość wskazana w uwadze 11, czyli kolor obramowania motywu ciemnego. */
    --row-hover: #2b3852;
  }
}

:root[data-theme="dark"] {
    --bg: #0b0f17;
    --surface: #1b2436;
    --panel: #141c2b;
    --border: #2b3852;
    --text: #f8fafc;
    --muted: #cbd5e1;
    --primary: #9fd0ff;
    --primary-hover: #c7e2ff;
    --primary-contrast: #08111f;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-raised: 0 2px 10px -2px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.35);
    --focus: #93c5fd;

    --danger: #fca5a5;
    --danger-bg: rgba(239, 68, 68, 0.12);
    --warning: #fbbf24;
    --warning-bg: rgba(245, 158, 11, 0.12);
    --success: #5eead4;
    --success-bg: rgba(16, 185, 129, 0.12);

    /* Wartość wskazana w uwadze 11, czyli kolor obramowania motywu ciemnego. */
    --row-hover: #2b3852;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  line-height: 1.5;
  text-wrap: pretty;
}

a {
  color: var(--primary);
  text-decoration: underline;
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.16em;
}
a:hover { color: var(--primary-hover); }

code, pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
}
/* UWAGA 47: srodliniowy `code` ze sciezka API („GET /api/analyses/{analysis_id}/
   validations”) rozpychal cala strone o 129 px przy 320 px. Sciezka nie ma spacji, wiec
   jest jednym tokenem i domyslne lamanie go nie tyka, a `code` w akapicie nie ma nad
   soba zadnego kontenera przejmujacego przepelnienie — inaczej niz `code` w `<pre>`,
   ktore przewija sie poprawnie i tego NIE dotyczy (`pre code` ma zostac w jednej linii).
   Zmierzone po zmianie: nadmiar 0 px na /account/api przy 320 px. */
:not(pre) > code {
  overflow-wrap: anywhere;
  min-width: 0;
}
pre {
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 10px 12px;
  border-radius: 8px;
  overflow-x: auto;
  max-width: 100%;
}
.compact-pre { margin: 6px 0 0; }
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.meta {
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

a:focus,
a:focus-visible,
button:focus,
button:focus-visible,
.button:focus,
.button:focus-visible,
input:focus,
input:focus-visible,
select:focus,
select:focus-visible,
textarea:focus,
textarea:focus-visible,
summary:focus,
summary:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 8px 12px;
  border-radius: 8px;
  box-shadow: var(--shadow);
}
.skip-link:focus,
.skip-link:focus-visible { left: 8px; z-index: 1000; }

.wrapper {
  max-width: 960px;
  margin: 0 auto;
  padding: 0 16px 48px;
}

.toolbar {
  padding: 12px 0;
  display: flex;
  gap: 16px;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.toolbar .brand { margin-right: auto; }

.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  text-decoration: none;
  color: var(--primary);
}
.brand-logo { display: block; width: auto; height: 28px; align-self: center; }
.brand-logo-dark { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brand-logo-light { display: none; }
  :root:not([data-theme="light"]) .brand-logo-dark { display: block; }
}
:root[data-theme="dark"] .brand-logo-light { display: none; }
:root[data-theme="dark"] .brand-logo-dark { display: block; }
/* Wybor jasnego MUSI wygrac z preferencja systemu proszaca o ciemny. */
:root[data-theme="light"] .brand-logo-light { display: block; }
:root[data-theme="light"] .brand-logo-dark { display: none; }
.brand strong { font-size: 20px; letter-spacing: 0.2px; }
.brand small { color: var(--muted); font-size: 12px; }

.toolbar nav ul {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  padding: 0;
  margin: 0;
}

.toolbar a, .toolbar button {
  font-size: 13px;
}
.toolbar nav a:hover { color: var(--primary-hover); }

.toolbar .user {
  color: var(--muted);
  font-size: 13px;
}

.language-switcher {
  display: flex;
  gap: 0;
  /* stretch, nie center: obie kontrolki mają wtedy tę samą wysokość niezależnie
     od tego, która ma wyższą zawartość. Wcześniej przycisk był o 2px niższy od
     listy (uwaga Krzysztofa z 19.08.2026). Stała poprawka paddingu przycisku
     naprawiłaby tylko dzisiejszy stosunek wysokości, a przy większej czcionce
     albo drugiej linii tekstu na przycisku POWIĘKSZAŁABY rozjechanie. */
  align-items: stretch;
  margin: 0;
}
.language-switcher select {
  width: auto;
  min-width: 0;
  font-size: 13px;
  padding: 6px 30px 6px 10px;
  border-radius: 999px 0 0 999px;
}
/* Select i przycisk mają wyglądać jak jedna pigułka (uwaga 3), ale pozostają
   osobnymi kontrolkami: wybór z listy tylko USTAWIA wartość, akcję wykonuje
   widoczny przycisk. Zero listenera `change` — patrz decyzja z 07.2026. */
.language-switcher .button {
  border-radius: 0 999px 999px 0;
  border-left-width: 0;
}
.language-switcher select:focus-visible,
.language-switcher .button:focus-visible {
  position: relative;
  z-index: 1;
}
.language-switcher .button { font-size: 13px; padding: 6px 12px; }

.count-badge {
  display: inline;
  color: inherit;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
}
.count-badge.is-empty { display: none; }

.page-header {
  text-align: center;
  padding: 8px 0 16px;
}
.page-header h1 {
  font-size: 36px;
  line-height: 1.15;
  margin: 0;
  font-weight: 800;
  letter-spacing: -0.01em;
  text-wrap: balance;
  color: var(--primary);
}
.page-header p {
  margin: 8px auto 0;
  max-width: 70ch;
  color: var(--muted);
}
.hero-tagline {
  margin: 0 auto 4px;
  max-width: 60ch;
  font-size: 20px;
  font-weight: 600;
  color: var(--primary);
  text-wrap: balance;
}

main { padding-top: 12px; }

.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* UWAGA 42: „tabele w sekcjach »Niespelnione Kryteria Sukcesu WCAG« i »Zestawienie
     walidacji« rozszerzaja cala strone, co wywoluje przesuwanie jej w dwoch wymiarach.”
     Sekcja jest elementem siatki `.cards`, a element siatki ma domyslnie
     `min-width: auto`, czyli NIE zmniejszy sie ponizej swojego rozmiaru wewnetrznego.
     Skutek: `.table-wrap` w srodku dostawal 887 px zamiast 288 px, wiec jego
     `overflow-x: auto` nie mial czego przewijac — przewijanie „przeciekalo” na cala
     strone. Zmierzone przy 320 px: `.cards` mial 288 px, a `.panel` w nim 921 px
     i strona rozpychala sie o 617 px.
     Kontenery ukladu dostaly `min-width: 0` przy uwadze 44, ale `.panel` wtedy
     pominalem — a to on jest ostatnim ogniwem lancucha nad opakowaniem tabeli. */
  min-width: 0;
}
.panel .inside { padding: 16px; }

.alert {
  border-left: 4px solid var(--border);
  padding: 10px 12px;
  border-radius: 8px;
  margin-bottom: 14px;
}
.alert:last-child { margin-bottom: 0; }
.alert-error {
  border-color: var(--danger);
  background: var(--danger-bg);
}
.alert-warning {
  border-color: var(--warning);
  background: var(--warning-bg);
}
.alert-success {
  border-color: var(--success);
  background: var(--success-bg);
}

form .field { margin: 0 0 14px; }
label { display: block; font-weight: 650; margin: 0 0 6px; }
/* Jedno źródło prawdy dla wyglądu pól (uwaga 8). Typ `text` i `month` wypadały
   wcześniej z tej reguły, więc te same pola w różnych miejscach aplikacji
   wyglądały inaczej. Nowy typ pola dopisuj TUTAJ i w sekcji wąskich ekranów. */
input[type="text"],
input[type="url"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="month"],
textarea {
  width: 100%;
  max-width: 100%;
  font-size: 18px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
}
select {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  font-size: 18px;
  padding: 10px 40px 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
}
input::placeholder { color: var(--muted); }

/* POLA WYŁĄCZONE (uwaga 64a, runda 7). Recenzent: „pola wyłączone (atrybut disabled),
   które wizualnie nie różnią się od tych, w których można coś wpisać. Powinny być
   wyszarzone i wyróżnione też w inny sposób (poza kolorem), żeby było to oczywiste,
   że te pola są nieaktywne”.

   Stan przed zmianą: w całym arkuszu jedyną regułą dla `:disabled` był kursor
   w kartach wyboru (`.check input:disabled`), czyli pola tekstowe, listy i przyciski
   nie miały ŻADNEGO wyróżnienia. Zmierzone: 38 wystąpień `disabled` w 7 szablonach
   (formularz nowej analizy, analiza cykliczna, panele administratora).

   TRZY WYRÓŻNIKI NARAZ, bo prośba recenzenta jest wprost o niezależność od koloru
   (WCAG 1.4.1 Use of Colour — nie wolno przekazywać informacji samym kolorem):
   1. wyszarzenie tła i tekstu (kolor),
   2. przerywane obramowanie (KSZTAŁT — czytelne też w trybie wysokiego kontrastu
      i przy monochromatycznym widzeniu),
   3. kursor `not-allowed` (zachowanie).
   Do tego `opacity` NIE jest tu użyta jako główny nośnik: przygasza całość, w tym
   tekst, i łatwo nią zejść poniżej wymaganego kontrastu 4,5:1. Zamiast tego wprost
   podajemy kolory z tokenów, żeby kontrast dał się zmierzyć.

   Reguła jest KLASOWA — jedna lista selektorów dla pól, list, obszarów tekstu
   i przycisków. Dopisując nowy typ pola, dopisz go TUTAJ i w regule wyglądu wyżej. */
input[type="text"]:disabled,
input[type="url"]:disabled,
input[type="email"]:disabled,
input[type="password"]:disabled,
input[type="search"]:disabled,
input[type="number"]:disabled,
input[type="month"]:disabled,
input[type="date"]:disabled,
input[type="time"]:disabled,
textarea:disabled,
select:disabled {
  background: color-mix(in srgb, var(--surface), var(--muted) 18%);
  color: var(--muted);
  border-style: dashed;
  cursor: not-allowed;
}
/* Wyłączony przycisk (np. „Zastosuj filtry”, gdy nic się nie zmieniło) — ten sam
   zestaw sygnałów, żeby stan „nieaktywne” wyglądał w całym narzędziu jednakowo. */
button:disabled,
.button:disabled,
button[disabled],
.button[disabled] {
  background: color-mix(in srgb, var(--surface), var(--muted) 18%);
  color: var(--muted);
  border-style: dashed;
  cursor: not-allowed;
}
/* Pola wyboru i przełączniki: kształtu samej kontrolki nie stylujemy (to widget
   systemowy), więc sygnałem jest wyszarzony i przekreślony PODPIS. Przekreślenie
   działa niezależnie od koloru, a nie zmienia treści dla czytnika ekranu. */
.check input:disabled ~ div label,
.check:has(input:disabled) label,
.option input:disabled + label {
  color: var(--muted);
  text-decoration: line-through;
}
@media (forced-colors: active) {
  /* W trybie wysokiego kontrastu kolory nadaje system; kształt musi zostać nasz. */
  input:disabled,
  textarea:disabled,
  select:disabled,
  button:disabled,
  .button:disabled { border-style: dashed; }
}

.help { color: var(--muted); margin: 6px 0 0; }
.option { margin: 0 0 10px; }
.option input[type="checkbox"],
.option input[type="radio"] { margin-right: 8px; }
.option label {
  display: inline;
  font-weight: 500;
}
/* Kontrolka „Wstrzymaj automatyczne odświeżanie” (lista/raport/analiza/walidatory):
   cel dotykowy min. 24×24 px z odstępem — WCAG 2.5.8 Target Size (Minimum), poziom AA. */
.auto-refresh-control input[type="checkbox"] {
  width: 24px;
  height: 24px;
  margin-right: 10px;
  vertical-align: middle;
}
.meta-list {
  display: grid;
  grid-template-columns: minmax(160px, 220px) 1fr;
  gap: 8px 12px;
  margin: 0 0 16px;
}
.meta-list dt { font-weight: 650; }
.meta-list dd { margin: 0; }

fieldset { border: 0; padding: 0; margin: 1em 0; }
legend { font-weight: 750; margin-bottom: 10px; }
/* Legenda sekcji nadrzędnej nie może być mniejsza od nagłówka, który po niej
   następuje (uwagi 6, 7). Klasa jest przypisywana tylko sekcjom głównym —
   drobne grupy pól zostają w rozmiarze tekstu. */
legend.section-legend {
  font-size: 19px;
  letter-spacing: -0.01em;
}

.buttons { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

/* Odstępy kontekstowe (uwagi o marginesach). Nadawane klasą w konkretnym
   miejscu, nie selektorem rodzeństwa typu `p + button` — taki selektor
   trafiłby w przypadkowe miejsca na innych stronach. */
.spaced-above { margin-top: 1em; }
.spaced-below { margin-bottom: 1em; }

/* Podsumowanie statusow stron nad tabela (uwaga 16 recenzenta: „ile z nich zostalo
   rzeczywiscie przeanalizowanych, a na ilu wystapily bledy”). Poziomo, bo to cztery
   krotkie liczby, ale z zawijaniem — przy 320 px maja isc jedna pod druga zamiast
   rozpychac strone. Lista, nie akapit: czytnik ekranu ogloszi wtedy liczbe pozycji. */
.pages-summary {
  list-style: none;
  padding-left: 0;
  margin: 0.5em 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
}

button, .button, input[type="submit"] {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  border-radius: 999px;
  padding: 10px 18px;
  font-size: 16px;
  cursor: pointer;
  box-shadow: var(--shadow);
}
button:hover, .button:hover, input[type="submit"]:hover {
  border-color: color-mix(in srgb, var(--border), var(--primary) 35%);
}
button.primary, .button.primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-contrast);
}
button.primary:hover, .button.primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}
button.link, .button.link {
  background: transparent;
  border: 0;
  padding: 8px 10px;
  box-shadow: none;
  color: var(--primary);
  text-decoration: underline;
}

.inline-form { display: inline; margin: 0; }
.password-field {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.password-field input { flex: 1 1 auto; }
.password-field .password-toggle { flex: 0 0 auto; white-space: nowrap; }
.clipboard-buffer {
  position: fixed;
  left: -9999px;
  top: 0;
}

.table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
/* UWAGA 45: „jak mamy dluzszy adres e-mail, to on rowniez wychodzi poza sekcje
   z obramowaniem. Zakladam, ze tak samo dzieje sie z kazda z informacji wewnatrz tych
   sekcji.” Krzysztof mial racje co do OGOLNOSCI: nie bylo ZADNEJ reguly lamania dla
   list opisowych, wiec dotyczylo to kazdej dlugiej wartosci bez spacji (adres e-mail,
   adres URL, identyfikator, klucz), a nie tylko maila.
   Zmierzone przy 320 px: `dd` z adresem 94-znakowym mialo 554 px w kontenerze 214 px
   i rozpychalo strone o 307 px. `anywhere`, nie `break-word`, bo adres e-mail jest
   jednym tokenem bez spacji — `break-word` nie ma gdzie go zlamac. `min-width: 0`
   na `dl`, bo lista bywa elementem siatki i bez tego nie zmniejszy sie ponizej
   swojego rozmiaru wewnetrznego. */
dl { min-width: 0; }
dt, dd { min-width: 0; overflow-wrap: anywhere; }

.table-wrap {
  overflow-x: auto;
  max-width: 100%;
  -webkit-overflow-scrolling: touch;
  /* Odstep nad tabela (uwaga 50: `caption` przyklejal sie do poprzedniego akapitu).
     Margines nadajemy OPAKOWANIU, bo `caption` jest czescia tabeli i wlasny
     margin-top na nim nie dziala przewidywalnie w roznych przegladarkach.
     Zmierzone 21.08.2026: 63 z 66 owinietych tabel stoi poza kontenerem z odstepem
     siatki, wiec brak marginesu byl regula, nie wyjatkiem. */
  margin-top: 1em;
}
.table th, .table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
  /* `break-word`, NIE `anywhere`. Zmierzone 21.08.2026 przy 320 px (uwagi 43, 46,
     48, 51): `anywhere` pozwala zlamac slowo w dowolnym miejscu, wiec min-content
     kolumny spada niemal do jednego znaku i przegladarka ZWEZA KOLUMNY, zamiast
     poszerzyc tabele i oddac przewijanie opakowaniu. Ta sama tabela w tym samym
     oknie: anywhere -> tabela 352 px i kolumna opisu 103 px; break-word -> tabela
     783 px i kolumna opisu 442 px. Dlugie tokeny (adresy) nadal sie zawijaja, bo
     break-word lamie slowo, ktore samo nie miesci sie w linii. */
  overflow-wrap: break-word;
}
/* Naglowki kolumn MOGA sie zawijac (uwaga 53: „Uruchomione walidacje”, „Koszt
   walidacji”). Wczesniej hurtowy `white-space: nowrap` na `thead th` trzymal je
   w jednej linii i to one rozpychaly kolumne. Krotkie nazwy, ktore naprawde nie
   powinny sie lamac (np. „API”), opakowuj punktowo klasa `.nowrap`. */
.table-wrap .table td[data-cell="status"],
.table-wrap .table td[data-cell="validator"],
.table-wrap .table td[data-cell="errors"],
.table-wrap .table td[data-cell="warnings"],
.table-wrap .table td[data-cell="http"] { white-space: nowrap; }
/* Punktowe blokowanie zawijania: dla fragmentow, ktore sa JEDNYM tokenem dla
   czytajacego, choc zawieraja znak lamania (uwaga 52: „Miesiac 2026-03” ma sie
   lamac po slowie „Miesiac”, ale nie na kresce w dacie). */
.nowrap { white-space: nowrap; }

/* TABELA Z ZAWIJANYM ADRESEM W PIERWSZEJ KOLUMNIE (uwaga 58, runda 7).
   Dotyczy JEDNEJ tabeli — „Zestawienie walidacji” na stronie analizy — bo recenzent
   wprost napisał, że pozostałe są w porządku, a na „Wykorzystaniu planu” jest tak,
   jak chciał. Włącza się więc klasą, nie zmianą reguły wspólnej dla 66 tabel.

   Zmierzone PRZED zmianą (sonda_runda7_geometria.py, adres 250 znaków):
   przy 320 px tabela 1033 px w opakowaniu 254 px, przy 1280 px 1033 px w 894 px —
   czyli przewijała się poziomo NAWET w szerokim oknie, bo pierwsza kolumna
   rozpychała się do 627 px.

   Trzy rzeczy muszą zadziałać razem, każda usuwa inną przyczynę:
   1. `overflow-x: hidden` na opakowaniu — samo zawijanie nie wystarczy, dopóki
      opakowanie deklaruje gotowość do przewijania;
   2. `table-layout: fixed` + `width: 100%` — bez tego przeglądarka liczy szerokości
      z treści i pierwsza kolumna nadal wygrywa negocjację z pozostałymi;
   3. `overflow-wrap: anywhere` w pierwszej kolumnie — adres bez spacji jest jednym
      tokenem, a odziedziczone `break-word` łamie go dopiero „gdy trzeba”. Tu
      `anywhere` jest bezpieczne, bo szerokości kolumn są już ustalone przez
      `table-layout: fixed`, więc nie ma czego ściskać (odwrotnie niż w uwadze 43,
      gdzie `anywhere` na WSZYSTKICH tabelach zwężało kolumny do kilku znaków).

   BŁĄD, KTÓRY TU POPEŁNIŁEM I POMIAR GO ZŁAPAŁ — wart zapamiętania, bo wygląda
   niewinnie: kolumnom 2-5 dałem najpierw `width: 1%` („niech wezmą tyle, ile
   potrzebują”). Przy `table-layout: fixed` procent jest jednak WIĄŻĄCY, nie
   minimalny: kolumny dostały po 3 px przy 320 px i po 9 px przy 1280 px, a treść
   („finished”, „axe”) wylewała się poza komórkę i była PRZYCINANA przez
   `overflow-x: hidden` opakowania. Czyli usunięcie przewijania kosztowałoby utratę
   treści — gorzej niż stan przed poprawką.

   DRUGA PRÓBA TEŻ BYŁA ZŁA i też ją złapał pomiar: sztywne szerokości w `ch`
   (11+12+7+7) sumują się do ~370 px, więc przy oknie 320 px (opakowanie 254 px) na
   pierwszą kolumnę zostawało 0-1 px i to ONA była przycinana. Wniosek: w tabeli bez
   przewijania szerokości kolumn nie mogą być stałe, muszą być UDZIAŁAMI.

   TRZECIA PRZYCZYNA, bez której udziały też nie wystarczą: kolumny „Walidator”,
   „Status”, „Błędy”, „Ostrzeżenia” mają wyżej wymuszone `white-space: nowrap`
   (uwaga 43), więc ich treść fizycznie nie mieści się w wąskiej kolumnie i wylewa
   poza komórkę. W TEJ tabeli zawijanie musi być dozwolone wszędzie — inaczej
   „bez przewijania” zawsze będzie znaczyło „z uciętą treścią”.

   LEKCJA OGÓLNA: przy `table-layout: fixed` nie ma „niech przeglądarka policzy” —
   każda kolumna dostaje dokładnie to, co jej wpiszemy, a pomiar musi sprawdzać
   scrollWidth KOMÓREK, nie tylko szerokość tabeli. */
.table-wrap-zawijana { overflow-x: hidden; }
.table-url-zawijana { table-layout: fixed; width: 100%; }
/* Zdjęcie `nowrap` z kolumn statusów WYŁĄCZNIE w tej tabeli. Reguła ogólna
   (uwaga 43) zostaje nietknięta dla pozostałych 65 tabel, gdzie przewijanie jest
   właściwym zachowaniem i nowrap poprawia czytelność.
   KOLEJNOŚĆ MA ZNACZENIE: ta reguła zbiorcza stoi PRZED regułą pierwszej kolumny,
   bo obie mają tę samą specyficzność — postawiona po niej nadpisywała `anywhere`
   adresu zwykłym `break-word` i adres znów przestawał się łamać. */
.table-wrap-zawijana .table td[data-cell="status"],
.table-wrap-zawijana .table td[data-cell="validator"],
.table-wrap-zawijana .table td[data-cell="errors"],
.table-wrap-zawijana .table td[data-cell="warnings"],
.table-wrap-zawijana .table td[data-cell="http"],
.table-url-zawijana th,
.table-url-zawijana td {
  white-space: normal;
  overflow-wrap: break-word;
}
/* Udziały, nie stałe szerokości: suma 100%, więc tabela zawsze mieści się w sekcji
   niezależnie od szerokości okna. Adres dostaje najwięcej, bo jest najdłuższy. */
.table-url-zawijana th:first-child,
.table-url-zawijana td:first-child {
  width: 40%;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.table-url-zawijana th:nth-child(2),
.table-url-zawijana td:nth-child(2) { width: 16%; }
.table-url-zawijana th:nth-child(3),
.table-url-zawijana td:nth-child(3) { width: 16%; }
.table-url-zawijana th:nth-child(4),
.table-url-zawijana td:nth-child(4) { width: 14%; }
.table-url-zawijana th:nth-child(5),
.table-url-zawijana td:nth-child(5) { width: 14%; }

/* WĄSKI EKRAN: PRZEWIJANIE WRACA (uwaga 65, runda 8).
   Recenzent skorygował własną uwagę 58 po zobaczeniu jej na telefonie: „nie wziąłem
   pod uwagę użytkowników mobilnych i teraz na wąskim ekranie tabelka jest strasznie
   wąska (i łamie do nowej linii po pojedynczych znakach) (…) na pełnym widoku nie
   ma być przewijania, ale na wąskim ekranie ma być. Pierwsza kolumna niech się łamie
   przy jakimś ograniczeniu (350px?), a pozostałe nie”.

   POMIAR PRZED ZMIANĄ (sonda_runda8_tabela.py, adres 216 znaków): przy 320 px
   kolumna adresu miała 101 px, a adres łamał się na 28 wierszy, z których najdłuższy
   miał 75 px. Przy 375 px: 123 px i 23 wiersze. To dokładnie objaw ze zgłoszenia,
   a wynikał wprost z `table-layout: fixed` z udziałami procentowymi: 40% z 254 px
   to 101 px, niezależnie od tego, jak długi jest adres.

   CZTERY WARIANTY ROZWIĄZANIA PRZEMIERZONE, TRZY ODRZUCONE (sonda_runda8_warianty.py,
   każdy na osobnej stronie, kontrola ważności: stan dzisiejszy MUSI zostać odrzucony):
   - `max-width: 350px` na `td:first-child` przy `table-layout: auto` → kolumna
     adresu SPADŁA do 40 px. Powód jest pouczający: `max-width` ogranicza od GÓRY,
     a algorytm `auto` przy `nowrap` w kolumnach 2-5 i tak ściska kolumnę łamaną do
     minimum. Recenzent prosi o szerokość, przy której adres SIĘ ŁAMIE, czyli
     o ograniczenie od DOŁU.
   - to samo z `max-width` na `inline-block` wewnątrz komórki → identycznie 40 px,
     więc kontener w komórce niczego tu nie ratuje.
   - `table-layout: fixed` + `min-width` na tabeli + `width: 350px` na kolumnie 1 →
     UCIĘTA treść w kolumnach „Walidator” i „Status” we WSZYSTKICH szerokościach,
     także szerokich. Przy `fixed` reszta kolumn dostaje resztę i nie negocjuje.
   - PRZYJĘTE: `min-width` RAZEM z `max-width` na kolumnie 1 przy `table-layout: auto`.
     `min-width` gwarantuje adresowi miejsce (nie zostanie ściśnięty), `max-width`
     pilnuje, żeby nie zajął jednego długiego wiersza, a `nowrap` na kolumnach 2-5
     jest wtedy bezpieczny, bo tabela może się przewinąć.

   PRÓG 856 px JEST ZMIERZONY, NIE WPISANY Z PALCA (sonda_runda8_prog.py, skan co 8 px
   od 320 do 1200): to pierwsza szerokość okna, przy której ten układ przestaje
   potrzebować przewijania (tabela 790 px w opakowaniu 790 px). Poniżej niej
   przewijanie jest koniecznością: wszystkie 67 zmierzonych szerokości poniżej progu
   przewijają się, więc próg rozdziela dwa realnie różne zachowania, a nie dwa
   warianty tego samego. Do pomiaru wzięty NAJDŁUŻSZY realny tekst kolumny 2
   („IBM Equal Access”) — przy krótszej etykiecie próg wyszedłby mniejszy i regresja
   wróciłaby przy innym walidatorze.

   WCAG 1.4.10 (reflow) NIE jest tu naruszone: kryterium zakazuje przewijania
   w DWÓCH wymiarach jednocześnie i wprost wyłącza z tego zakazu treści wymagające
   układu dwuwymiarowego, a tabela danych nim jest. Przewijanie w jednej osi
   w dedykowanym opakowaniu jest zachowaniem zalecanym, nie obejściem.
   `overflow-x: auto` na opakowaniu z `tabindex` nie jest tu potrzebne: to `.table-wrap`
   z natywnie przewijalną zawartością, a dostęp z klawiatury zapewniają linki
   w komórkach. */
@media (max-width: 855px) {
  .table-wrap-zawijana { overflow-x: auto; }
  /* `width: auto` MUSI stać jawnie: bez tego udziały procentowe z reguł powyżej
     obowiązują dalej i algorytm `auto` nie ma czego negocjować. */
  .table-url-zawijana {
    table-layout: auto;
    width: auto;
    min-width: 100%;
  }
  /* SPECYFICZNOŚĆ, NIE KOLEJNOŚĆ — obawa podniesiona w konsultacji i POTWIERDZONA
     pomiarem, warta zapamiętania jako klasa błędu.

     Pierwsza wersja tej reguły miała selektor `.table-url-zawijana th, td`, czyli
     specyficzność (0,1,1). Reguła bazowa dla kolumn statusów to
     `.table-wrap-zawijana .table td[data-cell="status"]` — (0,3,1). Wyższa
     specyficzność wygrywa NIEZALEŻNIE od kolejności w pliku i od tego, że reguła
     stoi w `@media` (media query nie dodaje specyficzności). Skutek zmierzony
     (weryfikacja_obaw_gpt_r8.py): kolumny 2-5 miały `white-space: normal`, mimo
     że reguła z `nowrap` była w arkuszu — czyli deklaracja istniała i nie działała.
     Recenzent poprosił wprost „pozostałe nie [mają się łamać]”, więc to był realny
     defekt, nie kosmetyka.

     Dlatego POWTARZAMY tu bazowy wzorzec selektora: równa specyficzność plus
     późniejsza pozycja w arkuszu daje pewne nadpisanie. Test bramy sprawdza obecność
     tego wzorca, bo sama obecność słowa `nowrap` niczego nie dowodzi.

     LEKCJA OGÓLNA: nadpisując regułę w media query, porównaj SPECYFICZNOŚĆ obu
     selektorów, a nie ich kolejność. Test czytający arkusz jako tekst tego nie
     wykryje — potrzebny jest pomiar `getComputedStyle` w przeglądarce. */
  .table-wrap-zawijana .table td[data-cell="status"],
  .table-wrap-zawijana .table td[data-cell="validator"],
  .table-wrap-zawijana .table td[data-cell="errors"],
  .table-wrap-zawijana .table td[data-cell="warnings"],
  .table-wrap-zawijana .table td[data-cell="http"],
  .table-url-zawijana th,
  .table-url-zawijana td {
    white-space: nowrap;
    width: auto;
  }
  /* Kolumna adresu STOI PO regule zbiorczej — inaczej `nowrap` z niej nadpisałby
     zawijanie adresu. Ta sama pułapka kolejności jest opisana w regułach bazowych. */
  .table-url-zawijana th:first-child,
  .table-url-zawijana td:first-child {
    white-space: normal;
    width: auto;
    min-width: 350px;
    max-width: 350px;
    overflow-wrap: anywhere;
  }
}
.table th { background: color-mix(in srgb, var(--surface), var(--panel) 22%); }
.table tr:last-child td { border-bottom: 0; }
/* Podświetlenie wiersza pod myszą i pod fokusem klawiatury (uwagi 11, 15).
   Kolor przez token, bo wartość z uwagi (#2b3852) to --border motywu ciemnego
   i na jasnym tle podświetlenie byłoby niewidoczne. */
.table tbody tr:hover td,
.table tbody tr:focus-within td {
  background: var(--row-hover);
}
.table tr.is-current td {
  background: color-mix(in srgb, var(--surface), var(--primary) 12%);
}
.table tr.is-current td:first-child {
  box-shadow: inset 3px 0 0 0 var(--primary);
}
.button.link[aria-current="true"] {
  font-weight: 750;
  text-decoration-thickness: 3px;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border-radius: 999px;
  border: 1px solid var(--border);
  font-size: 12px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  background: var(--surface);
}
.badge.running {
  border-color: color-mix(in srgb, var(--primary), transparent 55%);
  background: color-mix(in srgb, var(--primary), var(--surface) 86%);
  color: var(--primary);
}
.badge.finished {
  border-color: color-mix(in srgb, var(--success), transparent 55%);
  background: color-mix(in srgb, var(--success), var(--surface) 88%);
  color: var(--success);
}
.badge.queued { border-color: var(--border); color: var(--muted); }
.badge.error {
  border-color: color-mix(in srgb, var(--danger), transparent 55%);
  background: color-mix(in srgb, var(--danger), var(--surface) 88%);
  color: var(--danger);
}

/* Kontenery ukladu maja jawne `min-width: 0` (uwaga 44). Bez tego element siatki albo
   flexa nie zmniejszy sie ponizej swojego rozmiaru wewnetrznego, wiec dlugi adres
   w srodku rozpycha cala strone NIEZALEZNIE od regul lamania tekstu, a `overflow-x`
   w opakowaniu tabeli nie ma czego przejac. To druga, niezalezna przyczyna tego samego
   objawu i latwo ja przeoczyc, poprawiajac samo lamanie. */
.cards { display: grid; gap: 12px; min-width: 0; }
.stack { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.vstack { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
details { min-width: 0; }
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-raised);
}
.card .inside { padding: 14px 16px; }
.card h2, .card h3 { margin: 0 0 8px; letter-spacing: -0.01em; text-wrap: balance; }
.card h2 { font-size: 19px; font-weight: 700; }
.card h3 { font-size: 16px; font-weight: 700; }

.analysis-list { list-style: none; padding: 0; margin: 0; }
.analysis-item { margin: 0 0 12px; }
.analysis-item:last-child { margin-bottom: 0; }
.analysis-item .row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 16px;
  align-items: baseline;
  justify-content: space-between;
}
.analysis-item .title {
  font-weight: 750;
  word-break: break-word;
}
.analysis-item .meta { color: var(--muted); font-size: 13px; margin-top: 6px; }
.analysis-item .actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  /* Uwaga 40 designera: przyciski akcji przylegaly do wiersza postepu
     w sekcji "Twoje analizy" i w archiwum analiz. */
  margin-top: 0.75em;
}

.document-report-list {
  display: grid;
  gap: 12px;
}
.document-report-title {
  min-width: min(100%, 18rem);
}
.document-report-title a,
.document-report-url,
.document-report-url-list a,
.document-report-section a {
  overflow-wrap: anywhere;
}
.document-report-details {
  display: grid;
  gap: 10px 16px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin: 14px 0 0;
}
.document-report-details > div {
  min-width: 0;
}
.document-report-details dt,
.document-report-section-label {
  color: var(--muted);
  font-size: 12px;
  font-weight: 750;
  line-height: 1.35;
}
.document-report-details dd {
  margin: 2px 0 0;
  min-width: 0;
  /* `anywhere`, nie `break-word` (uwaga 44): adres bez ani jednej spacji jest jednym
     tokenem, wiec `break-word` zlamie go dopiero, gdy przegladarka uzna to za
     konieczne, a w siatce potrafi zamiast tego rozepchnac kolumne. W TRESCI nie ma
     negocjacji szerokosci z sasiadami, wiec zgoda na lamanie w dowolnym miejscu jest
     tu bezpieczna — inaczej niz w komorce tabeli, gdzie zwezalaby kolumny. */
  overflow-wrap: anywhere;
}
.document-report-details time {
  white-space: nowrap;
}
.document-report-results {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.document-report-section {
  margin-top: 12px;
}
.document-report-section p {
  margin: 2px 0 0;
}
.document-report-url-list {
  margin-top: 2px;
}
.document-report-actions {
  margin-top: 12px;
}
.badge.technical,
.badge.unknown,
.badge.pending {
  border-color: var(--border);
  color: var(--muted);
}

.validator-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  margin-top: 8px;
}
.check {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  cursor: pointer;
}
.check:hover {
  border-color: color-mix(in srgb, var(--border), var(--primary) 35%);
}
/* Cała powierzchnia karty przełącza kontrolkę (bindCheckBoxSurfaces w app.js),
   więc kursor musi to pokazywać na CAŁEJ karcie. Arkusz przeglądarki nadaje
   `input { cursor: default }`, co wygrywa nad dziedziczeniem — stąd jawne
   powtórzenie dla wnętrza karty. */
.check input,
.check label,
.check .desc {
  cursor: pointer;
}
.check input:disabled,
.check input:disabled ~ *,
.check:has(input:disabled) {
  cursor: default;
}
.check input { margin-top: 5px; }
.check .desc { color: var(--muted); font-size: 13px; margin-top: 2px; }

/* WIERSZ „Strona” W ZNALEZISKU (uwaga 63). Adres jest potrzebny tylko wtedy, gdy
   znaleziska NIE sa pogrupowane po stronach — inaczej naglowek grupy juz go podaje
   i powtarzanie go w kazdej pozycji jest szumem, zwlaszcza dla czytnika ekranu.
   Sterujemy tym w CSS, a nie w szablonie, bo przelaczenie widoku odbywa sie bez
   przeladowania strony: skrypt przenosi gotowe elementy `<li>`, wiec warunek
   szablonu juz by sie nie przeliczyl. `display: none` (nie `visibility`), zeby
   wiersz zniknal takze dla czytnika ekranu — inaczej byl by czytany mimo ukrycia. */
#report-results[data-group-mode="url"] .issue-page-row {
  display: none;
}

/* „Zaznacz wszystkie walidatory kodu” stoi NAD siatka walidatorow, ale wizualnie
   nadal byla to taka sama karta jak zwykly walidator (uwaga recenzenta: „wyglada jak
   jeden z walidatorow”). Roznilo ja tylko przerywane obramowanie i przezroczyste tlo,
   co przy 360 px, gdy karty ida jedna pod druga, jest za slabym sygnalem.
   Dokladamy wyroznik TEKSTOWY (grubszy napis), bo dziala takze wtedy, gdy ktos nie
   rozpoznaje koloru ani stylu obramowania. NIE uzywamy `text-transform: uppercase`:
   czesc czytnikow ekranu literuje wtedy wyraz. */
.select-all-check {
  display: inline-flex;
  background: transparent;
  border-style: dashed;
  margin-bottom: 8px;
}
.select-all-check > div > label { font-weight: 750; }

/* Gwiazdka przy krytycznych Kryteriach Sukcesu (lista analizowanych KS). */
.wcag-sc-critical-mark {
  color: var(--danger, #b3261e);
  font-weight: 700;
  text-decoration: none;
  cursor: help;
}

/* Rozwijane sekcje: odsuń znacznik od krawędzi i powiększ pole klikalne. */
details > summary {
  padding: 8px 10px;
  border-radius: 8px;
  cursor: pointer;
  /* UWAGA 57 (runda 7): „adresy URL w samym raporcie — kiedy są bardzo długie, to
     wychodzą poza obszar, zamiast zostać zawinięte”. Winowajcą był BRAK jakiejkolwiek
     reguły łamania na `summary`, a w raporcie WWW nagłówek grupy to właśnie `summary`
     z pełnym adresem strony.

     Zmierzone przed zmianą przy 320 px (sonda_runda7_geometria.py, pomiar [2]):
     `summary` miało boks 254 px przy treści 613 px, czyli tekst wylewał się 359 px
     poza własny obrys (druga grupa 201 px). Przy 1280 px zjawiska nie ma, dlatego
     zarzut dotyczy wąskich okien i długich adresów z kwerendą.

     `anywhere`, NIE `break-word`: adres bez ani jednej spacji jest jednym tokenem,
     a `break-word` łamie go dopiero, gdy przeglądarka uzna to za konieczne. To ta
     sama decyzja co przy `dd` (uwaga 44) i odwrotna niż w komórce tabeli, gdzie
     `anywhere` ściskałoby kolumny. `min-width: 0` chroni przed drugim, niezależnym
     mechanizmem tego samego objawu: `summary` jest tu elementem układu flex/grid,
     a domyślne `min-width: auto` rozpycha rodzica niezależnie od reguł łamania. */
  overflow-wrap: anywhere;
  min-width: 0;
}

/* MARGINES 8 recenzenta: „kazdy element rozwijalny powinien miec w samej tresci
   margines wewnetrzny z kazdej strony, poniewaz aktualnie styka sie ze wszystkim
   dookola i brakuje wciecia do rozpoznania, co jest rozwinieta trescia”.
   Padding nadajemy KONTENEROWI TRESCI, nigdy calemu `<details>` — inaczej przesunalby
   sie takze `<summary>` razem ze znacznikiem rozwijania, czego recenzent wprost
   nie chcial. */
.details-body {
  padding: 1em 2em;
}
/* Waski ekran: 2em z kazdej strony zjada miejsce na kod i dlugie adresy, ktore i tak
   sa tu najtrudniejsza trescia do zmieszczenia. */
@media (max-width: 640px) {
  .details-body { padding: 0.75em 0.75em; }
}

/* UWAGA 68 (runda 8): TREŚĆ ROZWINIĘCIA POTRZEBUJE PRZESTRZENI I PEŁNEGO FONTU.
   Recenzent: „w zawartości tych rozwinięć, poza tymi, gdzie znajdują się bloki
   <pre>, przydałby się mały padding. Teraz nie mam tam żadnej przestrzeni i wszystko
   się skleja ze sobą (…) w przypadku tych rozwinięć nie powinny być zastosowane klasy
   «meta», które powodują zmniejszenie fontu. W tym miejscu te teksty są mniej
   czytelne, a one trochę stanowią opisanie akapitu pod sobą, ewentualnie są pełną
   informacją (jak w przypadku sekcji «Tekst i kontekst»)”.

   POMIAR PRZED ZMIANĄ (sonda_runda8_rozwiniecia.py, render REALNEGO szablonu
   analysis_report.html z prawdziwą funkcją grupującą): w rozwinięciu „Tekst
   i kontekst” odstęp pod `summary` wynosił 0 px, wcięcie 0 px, a oba akapity treści
   miały font 12 px przy 16 px zwykłego akapitu strony. W „Szczegółach walidatora”
   etykieta „Komunikat” też miała 12 px, choć sam blok `<pre>` stoi już w
   `.details-body` z paddingiem 16/32 px — czyli recenzent trafnie rozpoznał, że
   problem dotyczy WYŁĄCZNIE treści poza blokami `pre`.

   DWIE RZECZY, KTÓRE `meta` ROBI NARAZ, a kwestionowana jest tylko jedna:
   `.meta` daje mniejszy font ORAZ kolor `--muted`. Recenzent pisze o czytelności
   tekstu, nie o kolorze, a wyszarzenie pełni tu realną rolę: odróżnia ETYKIETĘ
   („Komunikat”, „Tekst:”) od treści, którą opisuje. Dlatego nie usuwamy klasy
   z szablonów (byłaby to zmiana w kilkunastu miejscach czterech plików, z ryzykiem
   dla eksportu HTML), tylko ZNOSIMY ZMNIEJSZENIE FONTU tam, gdzie recenzent je
   zgłosił: wewnątrz rozwinięć w znaleziskach. Reszta strony (stopki tabel, podpisy,
   liczniki) zachowuje `meta` bez zmian, bo tam nikt czytelności nie kwestionował.

   PADDING NADAJEMY KONTENEROWI TREŚCI, NIE CAŁEMU `<details>` — ta zasada jest już
   w arkuszu wyżej (margines 8 recenzenta) i tutaj obowiązuje tak samo: przesunięcie
   całego `<details>` odsunęłoby też `<summary>` razem ze znacznikiem rozwijania,
   czego recenzent wprost nie chciał. Dlatego celujemy w RODZEŃSTWO `summary`.

   Bloki `<pre>` wykluczamy z dodatkowego paddingu (`:not(.details-body)` plus reguła
   zerująca dla `pre`), bo mają własne marginesy i własny obszar przewijania —
   dokładanie im wcięcia zwężyłoby miejsce na kod i długie adresy, czyli najtrudniejszą
   tu treść. To też jest wprost w zgłoszeniu: „poza tymi, gdzie znajdują się bloki
   <pre>”. */
.issue-list details > summary ~ *:not(.details-body):not(pre):not(ul):not(ol) {
  padding-left: 0.75em;
  padding-right: 0.75em;
}
/* LISTY są tu WYŁĄCZONE i mają własną regułę przy uwadze 69 niżej: ich wcięcie
   musi zmieścić także punktor, więc wartość jest inna. Wyłączenie przez `:not`
   zamiast nadpisywania jest tu konieczne, patrz komentarz o specyficzności
   w regule uwagi 69.
   Odstęp GÓRNY treści rozwinięcia ustala reguła uwagi 70 niżej (rozdziela
   etykietę od zwykłej treści), więc nie powtarzamy go tutaj. */
/* Pełny rozmiar tekstu w treści rozwinięcia. Kolor `--muted` ZOSTAJE: rozróżnia
   etykietę od opisywanej treści, a jego kontrast wobec tła to 9,7:1 w motywie
   jasnym (#3a414c na #ffffff), czyli z zapasem powyżej wymaganych 4,5:1. */
.issue-list details .meta,
.issue-list details .meta * {
  font-size: inherit;
}

/* UWAGA 69 (runda 9): PUNKTOR LISTY W ROZWINIĘCIU WYCHODZI POZA TREŚĆ.
   Recenzent: „jak są wewnątrz rozwinięć jakieś listy, to one nie uwzględniają
   miejsca na punktor, przez co on nie ma wcięcia i punktor «wystaje» z lewej
   strony. Przez to nie wygląda to, jakby to była część treści w rozwinięciu”.

   TO JEST NASZA REGRESJA Z RUNDY 8, nie zastany defekt. Reguła wyżej
   (`summary ~ *`) nadaje treści rozwinięcia `padding-left: 0.75em`, czyli 12 px.
   Dla akapitu i `div` to działa poprawnie, ale `<ul>` przy domyślnym
   `list-style-position: outside` rysuje punktor W SWOIM PADDINGU — a padding
   listy jest tu tym samym 12 px, którym ustawiamy wcięcie treści. Wcześniej
   lista miała domyślne 40 px przeglądarki i punktor się mieścił.

   POMIAR (sonda_runda9_wyrownanie.py, realny szablon analysis_report.html,
   1280 px, font 16 px): punktor zajmuje 22 px razem z odstępem za nim, czyli
   1,375em. Przy 12 px paddingu jego lewa krawędź stoi 10 px NA LEWO od obrysu
   rozwinięcia, podczas gdy etykieta `.meta` zaczyna się na +12 px — stąd
   rozjazd 22 px, który recenzent widzi jako „wystający punktor”.

   DLACZEGO 2.125em, A NIE „jakaś większa wartość”: to 0.75em wcięcia treści
   (wartość z rundy 8, wspólna dla całego wnętrza rozwinięcia) plus 1.375em
   zmierzonej szerokości punktora. Zmierzone warianty: 1.75em zostawia 6 px
   rozjazdu, 2em zostawia 2 px, 2.125em daje 0 px, 2.25em przechodzi na +2 px
   w drugą stronę. Punktor staje więc dokładnie na linii etykiet, a tekst listy
   wypada za nim — czyli lista czyta się jako część treści rozwinięcia.

   Wartość w `em`, nie w pikselach: punktor skaluje się z rozmiarem tekstu,
   więc przy powiększeniu czcionki wcięcie musi rosnąć razem z nim.

   SPECYFICZNOŚĆ — pułapka, w którą wpadłem przy pierwszym podejściu i którą ten
   arkusz ma już opisaną przy nawigacji: reguła wcięcia treści wyżej ma selektor
   `*:not(.details-body):not(pre)`, a każde `:not()` liczy się jak klasa, więc
   waży (0,3,2). Sama nazwa elementu `ul` waży (0,1,2) i PRZEGRYWA, niezależnie
   od kolejności w pliku. Pierwsza wersja tej poprawki nie dawała więc żadnego
   efektu, choć wyglądała poprawnie. Rozwiązaniem NIE jest podbijanie siły
   (`!important` ani sztuczne dokładanie klas), tylko UCZYNIENIE SELEKTORÓW
   ROZŁĄCZNYMI: tamta reguła wyklucza teraz `ul` i `ol` przez `:not`, więc obie
   opisują inne elementy i nie ma między nimi rywalizacji. */
.issue-list details > summary ~ ul,
.issue-list details > summary ~ ol {
  padding-left: 2.125em;
}
/* Lista zagnieżdżona (na przykład „Znaleziono na stronach” w „Lokalizacji”)
   dziedziczy wcięcie po rodzicu i nie potrzebuje pełnej wartości wyjściowej —
   wystarczy jej miejsce na własny punktor. */
.issue-list details ul ul,
.issue-list details ul ol,
.issue-list details ol ul,
.issue-list details ol ol {
  padding-left: 1.375em;
}

/* UWAGI 70 i 71 (runda 9): HIERARCHIA ODSTĘPÓW WEWNĄTRZ ROZWINIĘCIA.
   Recenzent: „«Popraw co najmniej jeden z poniższych problemów», «Komunikat» są
   trochę jakby były nagłówkami opisującymi zawartość znajdującą się pod nimi.
   Tylko że są tak trochę pościskane, podczas gdy elementy <pre> mają całkiem
   sporo przestrzeni wokół siebie. Burzy to trochę hierarchię wizualną” oraz
   „cała zawartość wewnątrz rozwinięcia powinna mieć większy odstęp u dołu, żeby
   wizualnie oddzielić ją od następnego elementu rozwijalnego”.

   POMIAR STANU PRZED (ta sama sonda): reguła z rundy 8 daje `margin-top: 0.5em`
   WYŁĄCZNIE pierwszemu elementowi po `summary` (selektor `summary + *`). Każdy
   kolejny element treści miał więc 0 px odstępu od poprzednika, a blok `<pre>`
   obok nich 16 px marginesu plus 16 px paddingu otoczki. Dokładnie ten kontrast
   recenzent opisuje: etykieta ściśnięta, `<pre>` z oddechem.

   ROZWIĄZANIE ODDZIELA DWIE ROLE, bo etykieta i akapit treści nie potrzebują
   tego samego. Etykieta zapowiada blok pod sobą, więc dostaje wyraźny odstęp
   GÓRNY (1em) i minimalny dolny — inaczej odkleiłaby się od tego, co opisuje,
   i przykleiła do treści nad sobą. Zwykła treść dostaje pół tej wartości.
   Pierwszy element po `summary` zachowuje mniejszy odstęp (0.5em), bo nagłówek
   rozwinięcia i tak jest wizualnie oddzielony tłem oraz znacznikiem rozwijania.

   Odstęp dolny (uwaga 71) nadajemy `<details>`, nie ostatniemu dziecku:
   ostatnim elementem bywa `<ul>`, `<div class="meta">` albo `.details-body`
   z blokiem `<pre>`, więc celowanie w treść wymagałoby trzech reguł i i tak
   pękłoby przy nowym rodzaju treści. `margin-bottom` na całym rozwinięciu
   działa niezależnie od tego, co jest w środku, i nie przesuwa `<summary>`,
   czego recenzent zabronił już w rundzie 8.

   SPECYFICZNOŚĆ, ta sama pułapka co przy uwadze 69: selektor treści zawiera
   `:not(.details-body):not(pre)` i waży (0,3,2), więc `~ .meta` o wadze (0,2,2)
   sam by go NIE przebił. Dlatego reguła treści wyklucza `.meta` wprost, a każda
   z trzech reguł niżej opisuje rozłączny zbiór elementów: zwykłą treść,
   etykietę oraz pierwszy element po `summary`. */
.issue-list details > summary ~ *:not(.details-body):not(pre):not(.meta) {
  margin-top: 0.5em;
}
/* Etykieta ma odstęp DOLNY tylko wtedy, gdy pod nią coś jeszcze jest: jej zadaniem
   jest trzymać się treści, którą opisuje. Gdy etykieta jest OSTATNIM elementem
   rozwinięcia, ten margines nie ma czego oddzielać, a dolicza się do przestrzeni
   domykającej rozwinięcie (korekta uwagi 71 niżej) i daje 20 px tam, gdzie reszta
   rozwinięć ma 16 — zmierzone, patrz komentarz przy tej korekcie.
   Rozdzielamy więc ZAKRESY przez `:last-child`, a nie dokładamy reguły zerującej:
   osobna reguła `> *:last-child { margin-bottom: 0 }` waży (0,1,2) wobec (0,3,2)
   i PRZEGRYWA — zmierzone, wariant z nią wyszedł bajt w bajt jak wariant bez niej. */
.issue-list details > summary ~ .meta:not(.details-body):not(:last-child) {
  margin-top: 1em;
  margin-bottom: 0.25em;
}
.issue-list details > summary ~ .meta:not(.details-body):last-child {
  margin-top: 1em;
  margin-bottom: 0;
}
/* Pierwszy element po `summary` — niezależnie od tego, czy jest etykietą, czy
   zwykłą treścią — dostaje mniejszy odstęp, bo nagłówek rozwinięcia jest już
   oddzielony tłem i znacznikiem rozwijania. Waga (0,4,2) bije obie reguły
   wyżej, co jest tu zamierzone: to zawężenie do jednego elementu, nie
   rywalizacja o ten sam zbiór. */
.issue-list details > summary + *:not(.details-body):not(pre),
.issue-list details > summary + .meta:not(.details-body) {
  margin-top: 0.5em;
}
/* KOREKTA UWAGI 71 (runda 10): PRZESTRZEŃ MA BYĆ W ŚRODKU OTWARTEGO ROZWINIĘCIA,
   NIE MIĘDZY ZAMKNIĘTYMI. Recenzent po zobaczeniu rundy 9: „Kiedy mówiłem
   o przestrzeni pod treścią w rozwinięciu, miałem na myśli tę treść, która znajduje
   się wewnątrz, jak już rozwiniemy dany element. Między zamkniętymi elementami
   rozwijalnymi nie było wcześniej przestrzeni, ale wyglądało to dobrze. W tej chwili
   między nimi są spore odstępy, przez co moim zdaniem jest to mniej wygodne. Dlatego
   też, jak elementy te są zamknięte, to powinny się znajdować jeden pod drugim, bez
   dodatkowego marginesu dolnego, ale jak już je rozwiniemy, to treść wewnątrz niech
   ma na dole trochę tej przestrzeni (…). Może to być ten 1em, który teraz jest dodany
   między zamknięte elementy rozwijalne”.

   NASZ BŁĄD Z RUNDY 9 BYŁ REALNY, nie tylko inaczej zrozumiany. `margin-bottom: 1em`
   na całym `<details>` działa NIEZALEŻNIE od stanu, więc dawał przestrzeń dokładnie
   tam, gdzie recenzent jej nie chciał, i nie dawał jej tam, gdzie prosił.
   POMIAR STANU PO RUNDZIE 9 (sonda_runda10_zamkniete_otwarte.py, realny szablon
   analysis_report.html, oba stany w jednym przebiegu): rozwinięcia ZAMKNIĘTE miały
   między sobą 16 px, a pusta przestrzeń WEWNĄTRZ otwartego pod ostatnią treścią
   wynosiła 0 px. Czyli odwrotnie niż wymaganie.

   DLACZEGO `padding-bottom` NA `details[open]`, A NIE MARGINES:
   - `[open]` to atrybut, który przeglądarka utrzymuje sama, więc rozróżnienie stanów
     nie wymaga JavaScriptu ani klasy dokładanej przez skrypt (raport HTML jest też
     eksportowany do pliku i tam żaden nasz skrypt nie działa);
   - padding jest przestrzenią WEWNĄTRZ elementu, a recenzent prosi o przestrzeń
     „w treści”, żeby było widać, że rozwinięcie się kończy. Margines dołożyłby ją
     na zewnątrz, czyli między rozwinięciami — czyli powtórzyłby błąd rundy 9;
   - zasada z rundy 8 („żadnego paddingu na `<details>`”) dotyczyła paddingu ze
     WSZYSTKICH stron, bo przesuwa `<summary>` razem ze znacznikiem rozwijania.
     `padding-bottom` stoi POD treścią i `summary` nie dotyka. Zmierzone: pozycja
     `summary` w obu stanach bez zmian.

   BEZ WYJĄTKU DLA OSTATNIEGO ROZWINIĘCIA — i to jest różnica wobec rundy 9, gdzie
   `:last-child` wyjątek MIAŁ. Tam margines dokładał się do przerwy 14 px między
   znaleziskami i robił większą dziurę niż granica znalezisk. Tutaj przestrzeń jest
   wewnątrz, więc niczego nie dokłada. Zmierzone oba warianty: z wyjątkiem ostatnie
   rozwinięcie ma do linii znalezisk 14 px przy 16 px między rozwinięciami wewnątrz
   znaleziska, czyli odstęp WEWNĄTRZ znaleziska byłby większy niż na jego GRANICY —
   odwrócenie hierarchii, którą te rundy porządkują. Bez wyjątku wychodzi 30 px do
   linii, czyli granica znalezisk pozostaje najmocniejszym podziałem.

   Wartość 1em podał recenzent wprost („może to być ten 1em”). */
.issue-list > li > details[open] {
  padding-bottom: 1em;
}
/* IKONY DEKORACYJNE (uwaga 22). `currentColor` znaczy, ze ikona bierze kolor tekstu
   obok, wiec dziala w obu motywach ORAZ w stanie hover bez zadnej dodatkowej reguly —
   dokladnie to recenzent opisal („w kolorze takim jak tekst, rowniez obejmujacym efekt
   hover”). Odstep 0.7em z prawej strony to jego wartosc.
   `flex-shrink: 0` chroni ikone przed sciskaniem przy 320 px, a `vertical-align`
   wyrownuje ja do linii tekstu, nie do dolnej krawedzi. */
.icon {
  color: currentColor;
  fill: currentColor;
  margin-right: 0.7em;
  flex-shrink: 0;
  vertical-align: -0.15em;
}

/* PASEK AKCJI RAPORTU (uwagi 20, 21): powrot po lewej, eksport po prawej.
   Zawijanie zostawione wlaczone, bo przy 320 px dwa elementy nie zmieszcza sie w linii
   i lepiej, zeby zeszly pod siebie, niz zeby rozpychaly strone. */
.report-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.report-actions > .button { display: inline-flex; align-items: center; }
/* Przycisk z ikona musi byc flexem, inaczej `flex-shrink: 0` na ikonie nic nie robi
   i przy 320 px ikona zostaje sciśnieta razem z tekstem. */
.icon-button { display: inline-flex; align-items: center; }

/* SEKCJA „PODSUMOWANIE WYNIKOW" - tekst pisany modelem jezykowym.
   Ikonka czteroramiennych gwiazd siedzi w PRAWYM GORNYM ROGU sekcji, a nie przed
   naglowkiem: recenzent wskazal ten wariant jako spojniejszy z pozostalymi
   sekcjami, bo naglowek zostaje taki sam jak wszedzie.
   Pozycjonowanie absolutne wzgledem `.inside`, wiec ikona nie wchodzi w przeplyw
   tekstu i nie przesuwa naglowka. */
#analysis-summary-panel .inside { position: relative; }
.ai-sparkles {
  position: absolute;
  top: 16px;
  right: 16px;
  /* JASNONIEBIESKI, wprost z propozycji recenzenta. Bierzemy zmienna --primary,
     ktora w motywie ciemnym jest juz jasnoniebieska (#9fd0ff), a w jasnym
     ciemnoniebieska - dzieki temu symbol pozostaje widoczny w OBU motywach.
     Krycie 0,75 realizuje druga czesc uwagi („zeby nie wyrozniał sie tak mocno
     jak bialy naglowek") bez wchodzenia w kontrast tekstu: to element
     DEKORACYJNY, wiec nie podlega wymogowi kontrastu 4,5:1, ale celowo nie
     schodzimy nizej, zeby nie zniknal przy filtrach jasnosci. */
  color: var(--primary);
  opacity: 0.75;
  pointer-events: none;
}
/* Naglowek nie moze wejsc pod ikone przy waskim ekranie - inaczej dlugi tytul
   sekcji wpadnie na symbol. WARTOSC JEST SPRZEZONA Z ROZMIAREM IKONY: 3,2 em to
   szerokosc ikony (32 px, powiekszona 04.09.2026 na prosbe recenzenta z 22 px)
   plus odstep. Zmieniajac `width`/`height` w szablonie, popraw TEZ te liczbe -
   przy 22 px zostawionych tutaj 2,2 em naglowek "Podsumowanie wynikow" wchodzil
   pod symbol na waskim ekranie. */
#analysis-summary-panel h2 { padding-right: 3.2em; }
/* W trybie wymuszonych kolorow (Windows High Contrast) dekoracje maja znikac,
   a nie zostawac w losowym kolorze systemowym. */
@media (forced-colors: active) {
  .ai-sparkles { display: none; }
}
/* OPIS SEKCJI: wyszarzony klasa `help`, ale z PRZYWROCONYM marginesem dolnym.
   Uwaga recenzenta z 04.09.2026, razem z trafna diagnoza skutku ubocznego:
   globalne `.help { margin: 6px 0 0 }` ZERUJE margines dolny, wiec opis
   przykleilby sie do przycisku albo do zwijanego raportu pod nim.
   DLACZEGO OSOBNA REGULA, A NIE ZMIANA `.help`: klasa `help` siedzi w
   kilkudziesieciu miejscach produktu (formularze, ustawienia konta, panel
   administratora), gdzie zerowy margines dolny jest ZAMIERZONY - opis stoi
   tam bezposrednio pod polem, do ktorego sie odnosi. Zmiana globalna
   rozjechalaby te miejsca, a naprawiamy JEDEN akapit. */
#analysis-summary-lead { margin-bottom: 1em; }
/* Zwijana tresc podsumowania: dluzszy tekst czyta sie lepiej w wezszej kolumnie,
   ale NIE ograniczamy jej sztywno w px, zeby nie psuc powiekszenia do 200%. */
.summary-result > .inside { max-width: 70ch; }
.summary-result > .inside h1 { font-size: 1.25rem; }
.summary-result > .inside h2 { font-size: 1.1rem; }
.summary-result > .inside h3 { font-size: 1rem; }

/* Menu eksportu na `<details>`: dziala bez JavaScriptu. Panel jest pozycjonowany
   absolutnie, zeby rozwiniecie NIE zmienialo rozmiaru rodzica — to ta sama pulapka,
   ktora recenzent zglosil w uwadze 41 przy menu „Wiecej” w nawigacji. */
.report-export-menu { position: relative; }
.report-export-menu > summary {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--border);
  background: var(--surface);
  font-weight: 600;
}
.report-export-list {
  position: absolute;
  right: 0;
  z-index: 30;
  min-width: 220px;
  margin: 4px 0 0;
  list-style: none;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel);
  box-shadow: 0 10px 24px rgb(0 0 0 / 28%);
  /* UWAGA 60 (runda 7): „w panelu zawierającym listę z dostępnymi opcjami jest za duży
     margines wewnętrzny (…) usuwając go całkowicie i tak samo lewy padding z listy,
     ale w takim przypadku dobrze byłoby też zaokrąglić rogi pierwszej i ostatniej
     opcji, żeby po nakierowaniu kursorem podświetlenie pasowało do kształtu panelu”.
     Wybrany jego wariant mocniejszy: zero marginesu wewnętrznego. `padding: 0` musi
     być JAWNE, bo `<ul>` ma domyślny lewy padding przeglądarki (u nas ~40 px) i to on
     był głównym źródłem wrażenia „za dużo miejsca”. `overflow: hidden` nie wystarcza
     zamiast zaokrągleń pozycji: obcięłoby cień i tło pod obramowaniem, dlatego rogi
     dostają skrajne pozycje. */
  padding: 0;
}
/* Podświetlenie skrajnych pozycji ma kształt panelu (uwaga 60). Promień o 1 px
   mniejszy niż panelu, żeby tło nie wystawało spod obramowania na łuku. */
.report-export-list li:first-child a { border-radius: 11px 11px 0 0; }
.report-export-list li:last-child a { border-radius: 0 0 11px 11px; }
/* Jedna pozycja = wszystkie cztery rogi (menu bywa krótsze dla gościa). */
.report-export-list li:only-child a { border-radius: 11px; }
.report-export-list li + li { border-top: 1px solid var(--border); }
.report-export-list a {
  display: block;
  padding: 10px 12px;
}
.report-export-list a:hover,
.report-export-list a:focus-visible { background: var(--row-hover); }

/* WCIECIA W ZNALEZISKU (uwaga 26): „sama nazwa bledu bez wciecia, cala zawartosc pod
   nia z wcieciem z lewej (1em), a takze dodatkowe wciecia przy kazdym opisie (<dd>)”.

   DWIE PULAPKI USTALONE POMIAREM 22.08.2026, nie zalozone:

   1. `.issue-card` TO JEST `<dl>` (klasa siedzi na liscie, nie na kontenerze
      znaleziska), a `.issue-title` i `<details>` sa jego RODZENSTWEM w `<li>`.
      Selektory `.issue-card dl` i `.issue-card details` byly wiec martwe.

   2. Wciecia MUSZA stac w TYCH SAMYCH regulach, co istniejace skroty `margin`.
      Pierwsza wersja dodawala osobna regule `.issue-card { margin-left: 1em }` WYZEJ
      w arkuszu, a nizej stalo `.issue-card { margin: 0 0 6px }` — skrot `margin`
      ustawia takze `margin-left`, wiec zerowal wciecie. Sonda pokazala
      `margin-left: 0px` i DWIE pasujace reguly (1em oraz 0px). Wzmocnienie selektora
      byloby tu bledna naprawa: pojawilby sie trzeci konkurent i nastepna osoba
      czytajaca arkusz nie wiedzialaby, ktora regula rzadzi. Dlatego wciecie wchodzi
      do istniejacego skrotu, a nie obok niego. */
/* MARGINES 7: pierwszy `<details>` w znalezisku przyklejal sie do tresci nad nim.
   Celujemy przez `.issue-list > li > details:first-of-type`, bo `<details>` NIE jest
   bezposrednim rodzenstwem `.issue-card` — pomiedzy stoi jeszcze `span.meta` z linkami
   WCAG (sonda: `nextElementSibling` dla `.issue-card` bylo puste). `:first-of-type`
   liczy sie wsrod `<details>` w tym `<li>`, wiec dalsze rozwiniecia zachowuja swoje
   wlasne odstepy. */
.issue-list > li > details:first-of-type { margin-top: 1em; }
details > summary:hover {
  background: color-mix(in srgb, var(--surface), var(--primary) 8%);
}
/* Nagłówek wewnątrz summary (sekcja „Ustawienia analizy”): zachowaj semantykę H2,
   ale renderuj w jednej linii ze znacznikiem rozwijania zamiast jako osobny blok. */
details > summary > h2 {
  display: inline;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}

/* Pole wyboru pliku — czytelne także w trybie odwróconego kontrastu. */
input[type="file"] {
  max-width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
}

.issue-caveat { margin: 0 0 14px; }
#report-details-actions { margin-top: 18px; }
/* Po powrocie ze szczegolow reguly wynik jest przewijany do widoku — zapas od gory,
   by nie przykleic sie do krawedzi. Fokus programowy (tabindex=-1) bez natretnej obwodki. */
details[data-report-url] > summary,
ul[data-report-items] > li { scroll-margin-top: 16px; }
ul[data-report-items] > li:focus { outline: none; }
ul[data-report-items] > li:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* Oprawa listy (brak punktorow, odstepy, separator, numeracja) przeniesiona do
   wspolnej klasy `.issue-list` nizej — uzywa jej TAKZE raport dokumentu (uwagi 55, 56).
   Tu zostaja tylko rzeczy zwiazane z INTERAKCJA raportu WWW: przewijanie do wyniku
   po powrocie ze szczegolow i widoczny fokus. */
.issue-title {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-wrap: balance;
  margin: 4px 0 6px;
}
/* Wciecie 1em z lewej (uwaga 26) wchodzi WPROST do skrotu `margin`, nie osobna regula
   nizej/wyzej — inaczej ten skrot zerowalby margin-left (zmierzone: dwie pasujace
   reguly, 1em i 0px, wygrywala pozniejsza). Kolejnosc: gora prawo dol lewo. */
.issue-card { margin: 0 0 6px 1em; }
.issue-card dt {
  font-weight: 650;
  color: var(--muted);
  font-size: 13px;
  margin-top: 6px;
}
.issue-card dd {
  /* Dodatkowe 1em wciecia opisu wzgledem etykiety (uwaga 26) — w tym samym skrocie
     `margin`, z tego samego powodu co przy `.issue-card` wyzej. */
  margin: 2px 0 0 1em;
  /* Uwaga 44: dlugi adres w tresci znaleziska wychodzil poza sekcje rozwijalna
     i rozpychal cala strone. Tu `anywhere` jest wlasciwe, w przeciwienstwie do
     komorki tabeli: w akapicie nie ma negocjacji szerokosci z sasiednimi kolumnami,
     wiec nierozdzielalny token po prostu MUSI byc zlamany. `min-width: 0` chroni
     przed drugim mechanizmem tego samego objawu: element siatki albo flexa
     z domyslnym `min-width: auto` rozpycha rodzica niezaleznie od lamania tekstu. */
  min-width: 0;
  overflow-wrap: anywhere;
}

/* WSPOLNA OPRAWA LISTY ZNALEZISK dla raportu strony WWW i raportu dokumentu
   (uwagi 55 i 56). Wczesniej raport dokumentu pakowal kazde znalezisko w osobny
   panel: doklada padding, boksy sklejaly sie ze soba, a numer wisial poza boksem
   i przy 320 px zjadal szerokosc opisu. Raport WWW rozdzielal znaleziska pozioma
   linia i to rozwiazanie recenzent wskazal jako lepsze. */
.issue-list {
  list-style: none;
  padding-left: 0;
  margin: 0;
  counter-reset: znalezisko;
}
.issue-list > li {
  counter-increment: znalezisko;
  /* Miejsce na numer wstawiany przez ::before. `ch` skaluje sie z rozmiarem
     tekstu, wiec numer nie rozjedzie sie przy wiekszej czcionce. */
  padding-left: 3.2ch;
  position: relative;
  /* UWAGA 66 (runda 8): „w raporcie www teraz wygląda to dobrze, ale przy raporcie
     dokumentu wciąż widzę, jak długie adresy URL w znaleziskach (po «Dokument —»)
     wychodzą poza obramowanie sekcji”.

     PRZYCZYNA, ustalona pomiarem i lekturą kodu walidatora, NIE zgadnięta: „Dokument
     —” nie jest ozdobnikiem szablonu. Walidator Office skleja komunikat jako
     `f"{location}{context}: {message}"`, gdzie `context` to `f" — {resource_url}"`
     (src/utilitia/validations/office.py, funkcja `_message`). Pełny adres siedzi więc
     WEWNĄTRZ treści komunikatu i trafia do zwykłego `<p>`, dla którego nie było
     żadnej reguły łamania. Raport WWW był już naprawiony (uwaga 57 dała łamanie na
     `summary` i na `dd`), a raport dokumentu pokazuje ten sam adres w innym miejscu
     układu i dlatego objaw został.

     DLACZEGO REGUŁA STOI TU, A NIE W WIDOKU DOKUMENTU: `.issue-list` jest wspólną
     oprawą listy znalezisk dla raportu WWW i raportu dokumentu (uwagi 55, 56).
     Adres może wystąpić w treści KAŻDEGO komunikatu walidatora, nie tylko Office
     (veraPDF podaje ścieżki, Ace adresy w publikacji), więc łatanie pojedynczego
     akapitu w jednym szablonie zostawiłoby ten sam defekt w pozostałych.

     POMIAR PRZED ZMIANĄ (sonda_runda8_dokument.py, render REALNEGO szablonu
     document_validator.html): przy 320 px akapit komunikatu wylewał się 1424 px poza
     obrys sekcji, a cała strona rozpychała się o 1408 px. Przy 768 px nadal 1304 px.
     `anywhere`, nie `break-word`: adres z długim tokenem podpisu nie ma ani jednej
     spacji, a `break-word` łamie słowo dopiero „gdy trzeba” — to ta sama decyzja co
     przy `dd` (uwaga 44) i przy `summary` (uwaga 57).

     WAŻNE O DANYCH POMIAROWYCH: pierwsza wersja sondy używała adresu z wieloma
     myślnikami i pokazała „objawu nie ma”, bo przeglądarka łamie taki adres nawet
     bez naszej reguły. Objaw odsłonił dopiero adres z długim nierozdzielalnym
     tokenem, czyli realny przypadek z systemów obiegu dokumentów. Zbyt przyjazne
     dane testowe maskują defekt tak samo skutecznie jak brak testu. */
  min-width: 0;
  overflow-wrap: anywhere;
}
.issue-list > li + li {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
/* Numer liczony licznikiem CSS, a nie punktorem `<ol>`: dzieki temu da sie go
   przeniesc nad tresc na waskim ekranie i nie jest czytany jako element listy
   dwa razy. */
.issue-list > li::before {
  content: counter(znalezisko) ".";
  position: absolute;
  left: 0;
  top: 14px;
  color: var(--muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.issue-list > li:first-child::before { top: 0; }

/* POZIOM ZNALEZISKA: kolor ORAZ ikona, nigdy sam kolor (WCAG 1.4.1 — kolor nie
   moze byc jedynym nosnikiem informacji). Ikona idzie przez `::before`, wiec jest
   dekoracyjna i czytnik ekranu jej nie oglosi; nazwa poziomu zostaje zwyklym
   tekstem, ktory czytnik przeczyta normalnie. Zalegla uwaga z listy zbiorczej:
   „bledy w raporcie sa bardzo zbite i ciezko je rozroznic”. */
.level-badge {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-weight: 600;
}
.level-badge::before {
  content: "\25CF";
  font-size: 11px;
  line-height: 1;
}
/* Kolory z palety motywu (istnieja w wariancie jasnym I ciemnym, wiec bez wartosci
   zapasowych — te tylko zamaskowalyby literowke w nazwie zmiennej). */
.level-badge.error { color: var(--danger); }
.level-badge.error::before { content: "\2716"; }
.level-badge.warning { color: var(--warning); }
.level-badge.warning::before { content: "\26A0"; }
.level-badge.info { color: var(--muted); }
.level-badge.info::before { content: "\2139"; }

/* Waski ekran: numer nad trescia, zeby opis dostal pelna szerokosc (uwaga 55c). */
@media (max-width: 480px) {
  .issue-list > li {
    padding-left: 0;
    padding-top: 26px;
  }
  .issue-list > li + li { padding-top: 30px; }
  .issue-list > li::before {
    position: static;
    display: block;
    margin-bottom: 2px;
  }
}

footer {
  margin-top: 26px;
  color: var(--muted);
  font-size: 12px;
}
footer nav { margin-top: 8px; }
footer .footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  list-style: none;
  padding: 0;
  margin: 0;
}
footer a { color: inherit; }
footer a:hover { color: var(--text); }

.legal-content {
  display: grid;
  gap: 12px;
}
.legal-content h2,
.legal-content h3,
.legal-content h4 {
  margin-bottom: 0;
}
.legal-content ol,
.legal-content ul {
  margin-top: 0;
  padding-left: 1.8rem;
}
.legal-content li {
  background: var(--panel);
  margin-top: 0.35rem;
  padding-left: 0.15rem;
}

@media (max-width: 520px) {
  .page-header h1 { font-size: 32px; }
  /* 16px, żeby iOS nie powiększał widoku przy wejściu w pole.
     Lista typów musi być zgodna ze wspólną regułą wyglądu pól powyżej. */
  input[type="text"],
  input[type="url"],
  input[type="email"],
  input[type="password"],
  input[type="search"],
  input[type="number"],
  input[type="month"],
  textarea {
    font-size: 16px;
  }
}

@media (forced-colors: active) {
  :root {
    --bg: Canvas;
    --surface: Canvas;
    --panel: Canvas;
    --border: CanvasText;
    --text: CanvasText;
    --muted: CanvasText;
    --primary: LinkText;
    --primary-hover: LinkText;
    --primary-contrast: ButtonText;
    --focus: Highlight;
    /* W trybie wymuszonego kontrastu kolory ustala system. */
    --row-hover: Canvas;
  }

  .panel,
  .card,
  .check,
  .table,
  button,
  .button,
  input,
  input[type="file"],
  select,
  textarea {
    box-shadow: none;
  }

  button.primary,
  .button.primary {
    background: ButtonFace;
    border-color: ButtonText;
    color: ButtonText;
  }
}

/* Natywne widgety przegladarki (pola, listy, paski przewijania) czytaja
   color-scheme, nie nasze zmienne. Bez tego jawnie wybrany motyw zostawia
   jasne pole tekstowe na ciemnym tle. */
:root[data-theme="dark"] { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }

/* Przelacznik motywu w stopce: trzy przyciski jako jedna grupa. */
.theme-switcher {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 8px;
  margin: 8px 0 0;
  padding: 0;
  border: 0;
}
.theme-switcher legend {
  padding: 0;
  margin: 0 0 6px;
  font-size: 12px;
  color: var(--muted);
}
.theme-switcher .theme-option {
  font: inherit;
  font-size: 12px;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
.theme-switcher .theme-option:hover { border-color: var(--primary); }
/* Stan wybrany czytany przez aria-pressed, a widoczny NIE tylko kolorem:
   pogrubienie i obramowanie 2px dzialaja tez w trybie wymuszonych kolorow. */
.theme-switcher .theme-option[aria-pressed="true"] {
  border-width: 2px;
  border-color: var(--primary);
  font-weight: 700;
}
@media (forced-colors: active) {
  .theme-switcher .theme-option[aria-pressed="true"] {
    border-color: Highlight;
  }
}

/* Menu „Więcej” w nawigacji (uwaga 1 Krzysztofa, wariant 3).
   HISTORIA DWOCH ITERACJI, wazna przy kolejnej zmianie tego obszaru:
   pierwotnie panel byl absolutny TYLKO powyzej 560 px, a nizej wchodzil w uklad
   strony, zeby przy 320 px nie wychodzil poza ekran. Uwaga 41 pokazala, ze ta
   ostroznosc kosztowala wiecej, niz dawala: panel w przeplywie powiekszal rodzica,
   wiec pasek przelamywal sie po rozwinieciu. Teraz panel jest absolutny ZAWSZE,
   a wyjscie poza ekran blokuja `max-width` i kotwiczenie do calej nawigacji. */
/* Panel menu kotwiczymy do CALEJ nawigacji, nie do waskiego `<li>` (uwaga 41).
   Powod: `right: 0` liczy sie od prawej krawedzi kontekstu pozycjonowania. Gdy byl
   nim sam `<li class="nav-more">` o szerokosci 94 px, panel o `min-width: 220px`
   siegal 126 px w LEWO od niego i na waskim ekranie wychodzil poza krawedz okna.
   `nav` konczy sie w tym samym miejscu (przycisk „Wiecej” jest ostatni), wiec panel
   nadal jest wyrownany do prawej, ale ma pod soba pelna szerokosc paska. */
.toolbar nav { position: relative; }
.nav-more { position: static; }
.nav-more-toggle { display: inline-flex; align-items: center; gap: 4px; }
/* Strzałka jest dekoracją: kierunek stanu czyta się z aria-expanded. */
.nav-more-toggle::after {
  content: "";
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
}
.nav-more.is-open > .nav-more-toggle::after {
  border-top: 0;
  border-bottom: 5px solid currentColor;
}
/* KRYTYCZNE: `display: flex` na tym elemencie NADPISUJE atrybut `hidden`
   (przegladarkowe [hidden]{display:none} ma te sama specyficznosc, ale stoi
   wczesniej). Zmierzone przed poprawka: lista z `hidden` miala wysokosc
   294 px, widoczne odnosniki i po ustawieniu fokusu byla osiagalna tabulacja,
   czyli uzytkownik klawiatury wpadal w niewidoczne menu. Reguly ukladu MUSZA
   wiec byc warunkowane brakiem `hidden`.

   SELEKTOR MUSI BYC TAK SAMO MOCNY jak regula ukladu ponizej. Pierwsza wersja
   uzywala `.nav-more-list[hidden]` (0,2,0) i PRZEGRALA z
   `.toolbar nav ul.nav-more-list` (0,2,2), ktora sam wzmocnilem, zeby pobic
   `.toolbar nav ul`. Zmierzone na plikach POBRANYCH Z BETY: display=flex przy
   hidden, wysokosc 79 px, a tabulacja wchodzila w dwie pozycje ukrytego menu.
   Poprawka jednej rzeczy przegonila poprawke drugiej - stad `[hidden]` z tym
   samym prefiksem. */
.toolbar nav ul.nav-more-list[hidden] { display: none; }
/* Selektor MUSI byc co najmniej tak mocny jak `.toolbar nav ul` (0,2,1),
   inaczej panel dziedziczy `gap: 12px` i `align-items: center` z paska
   nawigacji. Zmierzone przed poprawka: gap=12px zamiast 2px,
   align-items=center, czyli pozycje wysrodkowane i rozstrzelone na 295 px
   wysokosci. Widac to bylo na zrzucie, testy na samym CSS tego nie lapaly. */
.toolbar nav ul.nav-more-list {
  list-style: none;
  margin: 6px 0 0;
  padding: 6px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-raised);
  min-width: 220px;
  /* UWAGA 41: panel jest wyjety z przeplywu na KAZDEJ szerokosci, nie tylko powyzej
     560 px. Wczesniej ponizej tego progu zostawal w przeplywie, wiec jego
     `min-width: 220px` wchodzila do rozmiaru `<li class="nav-more">` i po rozwinieciu
     pasek przelamywal sie inaczej. Zmierzone przy 320 px: przycisk „Wiecej” skakal
     z (48,214) na (84,0), a rodzic puchl z 94 na 220 px. Przy 480 px przycisk
     przypadkiem nie drgal, ale rodzic puchl identycznie — defekt byl wiec szerszy
     niz jeden ekran, stad brama pilnuje SZEROKOSCI RODZICA, nie pozycji przycisku.

     `max-width` z `calc` domyka drugi, zastany defekt: przy dokladnie 560 px panel
     szerszy niz zapas z prawej wystawal za krawedz okna, bo samo `right: 0` nie
     ogranicza szerokosci. Odjete 24 px to margines paska po obu stronach. */
  position: absolute;
  right: 0;
  /* POZYCJA PIONOWA — UWAGA 59 (runda 7): „rozwinięte menu pojawia się strasznie
     nisko. Problemem jest reguła `top: 100%`. Po jej usunięciu `top` dostaje wartość
     `auto` i menu pojawia się tuż pod opcją Więcej”.

     Recenzent miał rację w całości, także co do rozwiązania. `top: 100%` liczy się od
     kontekstu pozycjonowania, a nim jest CAŁA nawigacja (`.toolbar nav` ma
     `position: relative` — konieczne dla uwagi 41), nie przycisk „Więcej”. Dla
     zalogowanego po „Więcej” stoją jeszcze „Zalogowano: adres” i „Wyloguj”, więc pasek
     łamie się na dwa wiersze i panel spadał pod OBA. Zmierzone (sonda_runda7_menu.py,
     odstęp między dolną krawędzią przycisku a górą panelu, pasek zalogowanego z logo
     i długim adresem): 135 px przy 320 px i 51 px przy 1280 px; po zmianie 8 px.

     DROGA, KTÓRĄ ODRZUCILIŚMY, i to jest tu najważniejsze do zapamiętania: najpierw
     zamiast `auto` wstawiliśmy kotwiczenie CSS (`anchor-name` + `top: anchor(bottom)`),
     bo `auto` przy pozycjonowaniu absolutnym zostawia element tam, gdzie stałby
     w przepływie — obawa była, że pozycja zacznie zależeć od układu paska. Dwa pomiary
     ją obaliły:
     1. `top: auto` daje 8 px STABILNIE w 21 układach (7 szerokości okna od 320 do
        1600 px × 3 warianty paska: długi adres z logo, krótki adres, bez logo), przy
        zerowym puchnięciu rodzica i bez wychodzenia poza okno;
     2. kotwiczenie WYWRACAŁO NASZ WŁASNY SELFCHECK: walidator CSS (VNU) zgłasza
        „Property anchor-name doesn't exist”, „Property position-anchor doesn't exist”
        i „anchor(bottom) is not a top value” — czyli arkusz UTILITII przestawał być
        poprawnym CSS-em. Narzędzie badające zgodność ze standardami nie może emitować
        stylów, których walidator odrzuca; to nie jest kwestia gustu, tylko naszej
        wiarygodności. Objaw wyszedł dopiero w CI, bo Chromium renderuje to bez skargi.
     LEKCJA OGÓLNA: nowa, „ładniejsza” właściwość CSS bywa droższa niż prosty zapis —
     zanim ją wprowadzisz, sprawdź, czy przechodzi przez WALIDATOR, nie tylko przez
     przeglądarkę. */
  top: auto;
  z-index: 20;
  max-width: calc(100vw - 24px);
}
.toolbar nav ul.nav-more-list a {
  display: block;
  padding: 6px 10px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--text);
}
.toolbar nav ul.nav-more-list a:hover { background: var(--row-hover); }
/* Pozycja pod fokusem klawiatury wyróżniona tak samo jak pod myszą — obrys
   fokusu zostaje, to tylko dodatkowe tło, żeby było widać, gdzie jesteśmy. */
.toolbar nav ul.nav-more-list a:focus-visible { background: var(--row-hover); }
@media (forced-colors: active) {
  .toolbar nav ul.nav-more-list { border-color: CanvasText; }
  .toolbar nav ul.nav-more-list a:hover,
  .toolbar nav ul.nav-more-list a:focus-visible {
    background: Highlight;
    color: HighlightText;
  }
}
