/* Styl panelu — kolory, krój i typografia nagłówków przeniesione z demo/template.html,
   żeby panel wyglądał jak część tego samego produktu co dokumenty przeglądu.
   Reszta (spine, node, stamp, masthead...) należy do dema i tu jej nie ma. */
:root{
  --ink:#171b1e;
  --ink-2:#4a5457;
  --ink-3:#77817f;
  --paper:#eef1f0;
  --panel:#fbfcfc;
  --panel-2:#f4f6f5;
  --rule:#d5dbd8;
  --rule-2:#e4e9e6;
  --accent:#14504f;
  --accent-ink:#0d3736;
  --accent-wash:#e2ecea;
  --brass:#8f6412;
  --brass-wash:#f5ecd9;
  --oxide:#9c3520;
  --oxide-wash:#f7e5e0;
  --moss:#3a6b4e;
  --moss-wash:#e3eee7;
  --shadow:0 1px 2px rgba(23,27,30,.05), 0 8px 24px rgba(23,27,30,.05);
}
/* Paleta ciemna stoi w DWÓCH blokach o tej samej treści i nie da się tego uniknąć:
   CSS nie umie dzielić jednego zestawu deklaracji między zwykły selektor i regułę
   w `@media`. Pierwszy blok obsługuje wybór „idź za systemem" (domyślny), drugi —
   noc wybraną wprost, także wtedy, gdy system stoi na dniu. Zgodności obu pilnuje
   `tests/test_webapp_motyw.py`; rozjazd znaczyłby, że jeden z dwóch sposobów
   wejścia w ciemny motyw maluje panel inaczej niż drugi. */
@media (prefers-color-scheme:dark){
  /* `:not([data-motyw="dzienny"])` — bez tego wybór dnia byłby nie do utrzymania
     na ciemnym systemie: kliknięcie nic by nie zmieniło. */
  :root:not([data-motyw="dzienny"]){
    --ink:#e8edeb;
    --ink-2:#a3aead;
    --ink-3:#7b8785;
    --paper:#101416;
    --panel:#181d20;
    --panel-2:#1e2427;
    --rule:#2c3438;
    --rule-2:#232a2d;
    --accent:#6fb3ac;
    --accent-ink:#a5d3cd;
    --accent-wash:#17302f;
    --brass:#d1a04a;
    --brass-wash:#302614;
    --oxide:#d98a72;
    --oxide-wash:#331d17;
    --moss:#7fb996;
    --moss-wash:#18291f;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.3);
  }
}
:root[data-motyw="nocny"]{
    --ink:#e8edeb;
    --ink-2:#a3aead;
    --ink-3:#7b8785;
    --paper:#101416;
    --panel:#181d20;
    --panel-2:#1e2427;
    --rule:#2c3438;
    --rule-2:#232a2d;
    --accent:#6fb3ac;
    --accent-ink:#a5d3cd;
    --accent-wash:#17302f;
    --brass:#d1a04a;
    --brass-wash:#302614;
    --oxide:#d98a72;
    --oxide-wash:#331d17;
    --moss:#7fb996;
    --moss-wash:#18291f;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.3);
}

*{box-sizing:border-box}
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font:400 15px/1.6 "IBM Plex Sans","Helvetica Neue",Arial,sans-serif;
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3{font-family:Archivo,"Helvetica Neue",Arial,sans-serif;text-wrap:balance;margin:0}
h1{font-size:clamp(26px,3.4vw,38px);font-weight:700;letter-spacing:-.02em;line-height:1.08}
h2{font-size:19px;font-weight:600;letter-spacing:-.01em}
p{margin:0}
a{color:var(--accent);text-decoration-thickness:1px;text-underline-offset:2px}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}

/* ---------- pasek stanu ---------- */
.pasek{
  display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  padding:14px 22px;border-bottom:2px solid var(--ink);
  background:var(--panel);
}
.pasek .logo{
  font-family:Archivo,sans-serif;font-weight:700;font-size:17px;
  color:var(--ink);text-decoration:none;margin-right:auto;
}
.dioda{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:12px;letter-spacing:.02em;
  border-radius:2px;padding:5px 10px;border:1px solid var(--rule);
}
.dioda.dobra{background:var(--moss-wash);border-color:var(--moss);color:var(--moss)}
.dioda.zla{background:var(--oxide-wash);border-color:var(--oxide);color:var(--oxide)}
.dioda.praca{background:var(--brass-wash);border-color:var(--brass);color:var(--brass)}

main#tresc{max-width:960px;margin:0 auto;padding:28px 22px 72px}

/* Tryb pełnoekranowy — dziś tylko raport. Cały dokument staje się kolumną, belka
   zostaje na górze ze swoją wysokością, a to, co pod nią, dostaje całą resztę. Bez
   `calc(100vh - …)`, bo wysokość belki zależy od tego, czy zawinie się na wąskim
   ekranie, a zgadnięta źle zostawia pasek pustki albo tnie dół. */
body.pelny-ekran{min-height:100vh;display:flex;flex-direction:column}
body.pelny-ekran main#tresc{
  max-width:none;margin:0;padding:0;
  flex:1;display:flex;flex-direction:column;min-height:0;
}

/* Pasek trybu: czyj to raport i jak wrócić do prowadzenia procesu. Na pełnym ekranie
   nie ma nagłówka `h1`, więc to jedyne miejsce, które mówi, co się ogląda — raport
   pokazany na rzutniku bez tego jest raportem nie wiadomo czego. */
.pasek-trybu{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:10px 22px;border-bottom:1px solid var(--rule);background:var(--panel-2);
}
/* `margin:0` nie jest ozdobnikiem: tytuł jest tu `h1`, a ten ma własne marginesy
   pionowe rzędu pół linii — w pasku rozpychałyby go na dwie wysokości. */
.pasek-trybu .tytul{
  margin:0 auto 0 0;font-family:Archivo,sans-serif;font-weight:600;font-size:15px;
  line-height:1.3;color:var(--ink);
}
.pasek-trybu a,
.pasek-trybu button{
  font:inherit;font-size:13px;text-decoration:none;padding:5px 12px;border-radius:3px;
  color:var(--ink-2);background:transparent;border:1px solid var(--rule);cursor:pointer;
}
.pasek-trybu a:hover,
.pasek-trybu button:hover{color:var(--ink);background:var(--panel);border-color:var(--ink-2)}
.pasek-trybu .aktualny{
  color:var(--accent-ink);background:var(--accent-wash);border-color:var(--accent);
  font-weight:600;
}
.pasek-trybu form{display:contents}

/* ---------- lista projektów ---------- */
.pusto{color:var(--ink-2);padding:18px 0}
.kafel{
  display:block;border:1px solid var(--rule);border-radius:3px;background:var(--panel);
  padding:16px 18px;margin-bottom:12px;text-decoration:none;color:inherit;
  box-shadow:var(--shadow);transition:border-color .12s;
}
/* Kafel nie jest już odnośnikiem, więc `:hover` na całości oznaczałby podświetlanie
   czegoś, w co nie da się kliknąć. Podświetla się to, co prowadzi dalej. */
.kafel:hover{border-color:var(--rule-2)}
.kafel-tytul{color:inherit;text-decoration:none}
/* Data w rogu. `float` zamiast pozycjonowania bezwzględnego: tytuł bywa długi i
   zawija się na wąskim ekranie, a element wyjęty z układu wjechałby wtedy pod niego.
   Pływający zostaje w przepływie na tyle, że tekst go opływa. */
.kafel-data{
  float:right;margin-left:14px;
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11px;letter-spacing:.04em;color:var(--ink-3);
  white-space:nowrap;
}
.kafel-tytul:hover{text-decoration:underline;text-underline-offset:3px}

/* Dwa tryby na dole kafla. Operator jest wyborem domyślnym i tak wygląda; raport
   stoi obok jako druga, równie jawna droga — nie schowany w menu, bo to jest jedna
   z dwóch rzeczy, po które ktoś tu przychodzi. */
.kafel-tryby{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0 0}
.kafel .tryb{
  font-size:13px;text-decoration:none;padding:6px 12px;border-radius:3px;
  color:var(--accent-ink);background:var(--accent-wash);
  border:1px solid var(--accent);
  transition:background .12s, border-color .12s;
}
.kafel .tryb:hover{background:var(--accent);color:var(--panel)}
.kafel .tryb-wtorny{
  color:var(--ink-2);background:transparent;border-color:var(--rule);
}
.kafel .tryb-wtorny:hover{color:var(--ink);background:var(--panel-2);border-color:var(--ink-2)}
/* Raportu nie ma: przycisk zostaje klikalny (ekran wyjaśnia, skąd raport się bierze),
   ale nie udaje, że jest co oglądać. */
.kafel .tryb-pusty{color:var(--ink-3);border-style:dashed}
.tryb-dopisek{
  margin-left:6px;
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);
}
.kafel .meta{color:var(--ink-2);font-size:13px;margin-top:4px}
.kafel .stan{margin-top:8px;font-size:13.5px}
.kafel .faza{
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:11px;
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);margin-right:8px;
}
.kafel .headline{margin-top:4px;color:var(--ink-2);font-size:13.5px}

/* ---------- ekran projektu ---------- */
.meta{color:var(--ink-2);font-size:13.5px;margin-top:6px}
.os-faz{
  display:flex;gap:8px;list-style:none;padding:0;margin:22px 0;flex-wrap:wrap;
}
.os-faz .faza{
  display:flex;flex-direction:column;gap:2px;border:1px solid var(--rule);
  border-radius:2px;padding:8px 12px;background:var(--panel-2);
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:11px;
  text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3);
}
.os-faz .faza span{
  font-family:"IBM Plex Sans",sans-serif;text-transform:none;letter-spacing:0;
  font-size:13px;color:var(--ink);
}
.os-faz .faza.approved{background:var(--accent-wash);border-color:var(--accent);color:var(--accent-ink)}
.os-faz .faza.blocked{opacity:.6}

.panel-fazy{
  border:1px solid var(--rule);border-radius:3px;background:var(--panel);
  padding:18px 20px;box-shadow:var(--shadow);
}
.panel-fazy p{margin-top:10px}
.panel-fazy .uspione{color:var(--oxide)}
.podpowiedz{
  margin-top:14px;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;
  color:var(--ink-3);
}

.zaawansowane{
  margin-top:18px;border-top:1px dashed var(--rule);padding-top:12px;
}
.zaawansowane summary{
  cursor:pointer;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);
}
.zaawansowane form{margin-top:12px}
.zaawansowane .ostrzezenie{color:var(--oxide);font-size:13px;margin-bottom:10px}
.zaawansowane label{display:block;margin-bottom:8px;font-size:13.5px;color:var(--ink-2)}
.zaawansowane input{
  display:block;width:100%;margin-top:4px;font:400 14px/1.4 "IBM Plex Sans",sans-serif;
  color:var(--ink);background:var(--panel-2);border:1px solid var(--rule);
  border-radius:2px;padding:7px 9px;
}
.zaawansowane button{
  margin-right:8px;margin-top:4px;background:var(--panel-2);color:var(--ink);
  border:1px solid var(--rule);
}
.zaawansowane button:hover{background:var(--rule-2)}
/* Cofnięcie fazy przeniosło się z szuflady na ekranie projektu, gdzie nie mogło
   zadziałać nigdy (tamta celuje w pierwszą NIE-zatwierdzoną fazę, a `reopen` przyjmuje
   wyłącznie zatwierdzone), na ekran pakietu fazy, którą się cofa. Reguły tu są te same,
   co w tamtej szufladzie — bo to jest ten sam rodzaj rzeczy: schowana akcja o dużych
   konsekwencjach. Selektory zostają rozdzielone, bo oba `details` siedzą teraz na
   różnych ekranach i nie ma po czym ich złączyć poza wyglądem. */
.zaawansowane button[value="reopen"]{border-color:var(--oxide);color:var(--oxide)}
.zaawansowane button[value="reopen"]:hover{background:var(--oxide-wash)}
.cofnij-faze{margin-top:16px;border-top:1px dashed var(--rule);padding-top:12px}
.cofnij-faze summary{
  cursor:pointer;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-3);
}
.cofnij-faze .ostrzezenie{color:var(--oxide);font-size:13px;margin:10px 0}
.cofnij-faze label{display:block;margin-bottom:10px;font-size:13.5px;color:var(--ink-2)}
.cofnij-faze input{
  display:block;width:100%;margin-top:4px;font:400 14px/1.4 "IBM Plex Sans",sans-serif;
  color:var(--ink);background:var(--panel-2);border:1px solid var(--rule);
  border-radius:2px;padding:7px 9px;
}
.cofnij-faze button{
  background:var(--panel-2);border:1px solid var(--oxide);color:var(--oxide);
}
.cofnij-faze button:hover{background:var(--oxide-wash)}
.cofnij-faze form{margin:0}

.pas-blad{
  border:1.5px solid var(--oxide);background:var(--oxide-wash);border-radius:3px;
  padding:16px 18px;margin-bottom:20px;color:var(--oxide);
}
.pas-blad pre{
  white-space:pre-wrap;font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:12.5px;color:var(--ink);background:var(--panel);border:1px solid var(--rule);
  border-radius:2px;padding:10px 12px;margin-top:10px;max-height:220px;overflow:auto;
}

.przycisk, button{
  font:500 14px/1 "IBM Plex Sans",sans-serif;
  background:var(--accent);color:#fff;border:none;border-radius:2px;
  padding:10px 16px;cursor:pointer;text-decoration:none;display:inline-block;
}
.przycisk:hover, button:hover{background:var(--accent-ink)}
.pas-blad button{background:var(--oxide)}

/* ---------- pakiet decyzyjny ---------- */
.link-przeglad{display:inline-block;margin-bottom:16px;font-size:13.5px}

.braki{
  border:1.5px solid var(--brass);background:var(--brass-wash);border-radius:3px;
  padding:16px 18px;margin-bottom:20px;
}
.braki h2{color:var(--brass)}
.braki ul{margin:10px 0 14px;padding-left:20px}
.braki li{margin-bottom:4px;font-size:13.5px}
.braki code{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;font-size:12.5px;
  background:var(--panel);border:1px solid var(--rule);border-radius:2px;padding:1px 5px;
}
.braki button{background:var(--brass)}
.braki button:hover{background:var(--brass);opacity:.85}

.blok{
  border:1px solid var(--rule);border-radius:3px;background:var(--panel);
  padding:16px 20px;margin-bottom:16px;box-shadow:var(--shadow);
}
.blok h2{margin-bottom:10px}
.blok p{margin-top:6px;color:var(--ink-2)}
.blok ul{margin:6px 0 0;padding-left:20px}
.blok li{margin-bottom:4px}
.blok.niepelny{border-color:var(--brass);border-style:dashed}
/* Powód braku, wpisany w blok, do którego należy — zamiast pustego miejsca. */
.blok p.brak-pola{color:var(--brass);font-style:italic}
.blok p.brak-pola code{font-family:"IBM Plex Mono",ui-monospace,monospace;font-style:normal}

.blok table{width:100%;border-collapse:collapse;font-size:13.5px}
.blok table td{border-bottom:1px solid var(--rule-2);padding:8px 6px;vertical-align:top}
.blok table td.id{
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-weight:600;
  color:var(--accent);white-space:nowrap;
}

.blok.wykresy{display:flex;flex-wrap:wrap;gap:16px}
/* Wykresy z tego silnika mają 1462-2075 px szerokości. `figure` był ograniczony do
   280 px, a obraz NIE MIAŁ `max-width` — renderował się więc w naturalnym rozmiarze
   i wychodził poza swój box i poza kolumnę: widać było wycinek, nie wykres. */
.blok.wykresy{display:flex;flex-wrap:wrap;gap:14px}
.blok.wykresy figure{margin:0;flex:1 1 320px;max-width:440px}
.blok.wykresy img{
  display:block;width:100%;max-width:100%;height:auto;
  border:1px solid var(--rule);border-radius:2px;background:var(--panel);
}
.blok.wykresy a:hover img{border-color:var(--accent)}
.blok.wykresy figcaption{
  margin-top:5px;font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11px;color:var(--ink-3);word-break:break-all;
}

/* Okno podglądu wykresu. Obraz dostaje tyle, ile zmieści się w oknie przeglądarki,
   bo po to się je otwiera — miniatura ma 440 px, a plik 1462-2075. `max-height` liczy
   się od wysokości widoku MINUS pasek pod spodem, inaczej przy szerokim wykresie pasek
   z przyciskami wychodzi poza ekran i nie da się kliknąć „następny". */
.podglad{
  max-width:96vw;max-height:96vh;padding:12px;
  border:1px solid var(--rule);border-radius:4px;
  background:var(--panel);color:var(--ink);box-shadow:var(--shadow);
}
.podglad::backdrop{background:rgba(0,0,0,.62)}
.podglad img{
  display:block;max-width:100%;max-height:calc(96vh - 76px);
  width:auto;height:auto;margin:0 auto;
  border:1px solid var(--rule);border-radius:2px;background:var(--paper);
}
.podglad-pasek{
  display:flex;align-items:center;gap:12px;
  margin-top:10px;padding-top:10px;border-top:1px solid var(--rule-2);
}
.podglad-nazwa{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11.5px;color:var(--ink-2);word-break:break-all;
}
/* Licznik odpycha „Zamknij" na prawo i sam zostaje przy nazwie. */
.podglad-licznik{
  margin-right:auto;
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11.5px;color:var(--ink-3);white-space:nowrap;
}
.podglad-krok,.podglad-zamknij{
  font:inherit;cursor:pointer;
  color:var(--ink-2);background:transparent;
  border:1px solid var(--rule);border-radius:3px;
  transition:border-color .12s ease, color .12s ease;
}
.podglad-krok{font-size:20px;line-height:1;padding:2px 12px 5px}
.podglad-zamknij{font-size:12px;font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;padding:6px 12px}
.podglad-krok:hover,.podglad-zamknij:hover{color:var(--ink);border-color:var(--ink-2)}
.podglad form{margin:0}

/* Podgląd jednego pliku: tyle miejsca, ile jest. */
.plik-podglad img{
  display:block;width:100%;height:auto;
  border:1px solid var(--rule);border-radius:3px;background:var(--panel);
}
.pliki-dowodowe{list-style:none;margin:14px 0 0;padding:0;width:100%}
.pliki-dowodowe li{
  display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  padding:6px 0;border-top:1px solid var(--rule-2);
}
.pliki-dowodowe a{color:var(--accent-ink)}
.pliki-dowodowe .niedostepny{color:var(--ink-3)}
.pliki-dowodowe .plik-rozmiar{
  margin-left:auto;
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11px;color:var(--ink-3);
}
/* Tekst pliku: monospace, zawijany, z paskiem tylko wtedy, gdy naprawdę trzeba —
   profil danych ma po 55 kB i czyta się go jak dokument, nie jak wydruk. */
.plik-tresc{
  margin:0;padding:16px 18px;overflow-x:auto;
  border:1px solid var(--rule);border-radius:3px;background:var(--panel);
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:12.5px;line-height:1.6;white-space:pre-wrap;word-break:break-word;
}
.plik-meta{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11.5px;color:var(--ink-3);margin:0 0 12px;
}
.blok.wykresy figcaption{margin-top:6px;font-size:12.5px;color:var(--ink-3)}

.bramka{
  border:1.5px solid var(--ink);border-radius:3px;background:var(--panel);
  padding:20px 22px;margin-top:24px;box-shadow:var(--shadow);
}
/* Bloker: problem na górze, pole na uzasadnienie POD NIM, na całą szerokość karty.

   Wcześniej nie było tu niczego poza tłem. `label` jest elementem wierszowym, więc
   „Uzasadnienie" i pole lądowały w jednej linii, pole brało szerokość z `.bramka
   textarea` i wychodziło bokiem, a `.bloker-tresc` i `.bloker-brak` nie miały reguł
   wcale. Wyglądało to jak pole wrzucone obok tekstu, a nie jak odpowiedź na niego.

   To zdanie jedzie do `decision.yaml` i do każdej późniejszej fazy jako powód, dla
   którego bramkę otwarto mimo TEJ pozycji — więc pole ma stać dokładnie pod problemem,
   którego dotyczy, i być na tyle duże, żeby nie sugerowało, że wystarczy jedno słowo.

   Rdza zostaje kolorem karty, ale nie kolorem jej tekstu: problem czyta się `--ink`,
   bo to jest treść do przeczytania, a nie ostrzeżenie do rzucenia okiem. Lewa
   krawędź niesie sygnał, tak samo jak przy kaflu „w toku" i przy notatce bramki. */
.bramka .bloker{
  background:var(--oxide-wash);border-color:var(--oxide);border-radius:3px;
  border-left-width:3px;
  padding:12px 16px 14px;margin:0 0 12px;
}
.bramka .bloker legend{color:var(--oxide);font-size:11.5px}
.bloker-tresc{
  margin:0;font-size:13.5px;line-height:1.55;color:var(--ink);
}
/* Etykieta jak każda inna w tym formularzu: własny wiersz, nad polem. */
.bramka .bloker label{
  display:block;margin-top:12px;
  font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-2);
}
.bramka .bloker textarea{
  display:block;width:100%;margin-top:5px;min-height:58px;
  /* `--panel` zamiast `--panel-2`: na tle wash ten drugi znika i pole przestaje
     wyglądać na pole do pisania. */
  background:var(--panel);border-color:var(--oxide);
}
/* Bloker bez zdania po odmowie. Grubsza krawędź, bo to jest stan błędu — a nie
   jedyny sygnał: `aria-invalid` w szablonie mówi to samo czytnikowi ekranu, i pod
   polem stoi zdanie, co się stanie bez uzupełnienia. */
.bramka .bloker-brakuje{border-color:var(--oxide);border-width:2px;border-left-width:4px}
.bloker-brak{
  margin:8px 0 0;font-size:12.5px;font-weight:600;color:var(--oxide);
}
/* Bloker z wpisanym uzasadnieniem przestaje być rdzawy. Przy 6-28 pozycjach to jest
   jedyny sposób, żeby z jednego spojrzenia zobaczyć, ile jeszcze zostało — inaczej
   trzeba schodzić wzrokiem po każdym polu z osobna.

   `--accent`, a nie nowy błękit: ta paleta nie ma niebieskiego, a dokładanie go
   znaczyłoby czwarty kolor o czwartym znaczeniu na jednym ekranie. `--accent` jest
   blado-morski i JUŻ znaczy „rozstrzygnięte" — tym samym tokenem obramowana jest
   faza po zamkniętej bramce. Bloker z napisanym zdaniem jest dokładnie tym: załatwiony.

   `:placeholder-shown` wymaga atrybutu `placeholder` (stąd on w szablonie) i reaguje
   na żywo, w trakcie pisania, bez linii skryptu. Jedna nieścisłość, świadoma: serwer
   robi `.strip()` i puste zdanie odrzuca, a CSS uzna same spacje za wypełnione. Kto
   wpisze spację, dostanie kolor i odmowę — koszt mniejszy niż skrypt pilnujący tego
   w drugim miejscu.

   Wygrywa nad `.bloker-brakuje` specyficznością (0,3,1 vs 0,2,0), i tak ma być:
   dopisanie zdania do blokera, który wrócił bez niego, ma od razu pokazać, że jest
   już czym. Czerwone zdanie pod spodem zostaje — mówi, co się stało przy OSTATNIM
   wysłaniu, a kolor karty mówi, co jest teraz; dwa różne fakty, oba prawdziwe. */
.bramka .bloker:has(textarea:not(:placeholder-shown)){
  border-color:var(--accent);background:var(--accent-wash);
}
.bramka .bloker:has(textarea:not(:placeholder-shown)) textarea{
  border-color:var(--accent);
}
.bramka fieldset{
  border:1px solid var(--rule);border-radius:2px;padding:10px 14px;margin:0 0 12px;
}
.bramka legend{
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;
  color:var(--accent);padding:0 4px;
}
.bramka fieldset label{
  display:inline-flex;align-items:center;gap:4px;margin-right:16px;font-size:13.5px;
}
/* Rozstrzygnięte wyzwanie widać po ramce. Na prawdziwych pakietach jest ich od 6 do
   28 i gate owner przechodzi je po kolei; bez tego jedyną informacją o tym, gdzie
   skończył, jest kropka w radiobuttonie — mniejsza niż znak interpunkcyjny. Kolory
   są te same, co diody w pasku, i znaczą to samo: mech to „w porządku", rdza
   „odrzucone", mosiądz „niezałatwione, wisi dalej".

   `:has()` i grubsza ramka, a nie klasa dokładana skryptem: przeglądarka zna stan
   `:checked` sama i podaje go natychmiast, a panel nie musi dostać trzeciego skryptu.
   Tam, gdzie `:has()` nie działa, ramka zostaje szara — czyli tak, jak było; nic się
   nie psuje, tylko nic nie przybywa.

   Kolor nie jest jedynym nośnikiem: właściwy radiobutton jest zaznaczony i podpisany
   słowem, a `accent-color` barwi samą kropkę. Ramka jest przypomnieniem widocznym z
   odległości całego ekranu, nie jedyną drogą do odczytania werdyktu — bo dla kogoś,
   kto nie rozróżnia zieleni od czerwieni, byłaby wtedy drogą donikąd.

   Sama ramka nie wystarczyła. Ma 1,5 px i biegnie po krawędzi prostokąta, który na
   ekranie pakietu stoi wśród kilkunastu takich samych — z odległości, z jakiej się na
   to patrzy, trzy kolory na włosie obwódki są praktycznie nierozróżnialne. Tło łamie
   się więc też, ale MINIMALNIE: tokeny `*-wash` to najlżejszy krok, jaki ta paleta ma,
   i te same, których używają diody w pasku oraz pasek blokera niżej. Pełny kolor na
   tle zrobiłby z formularza bramki choinkę i przykryłby to, co na nim naprawdę
   krzyczy — blokery. */
.bramka fieldset:has(input:checked){border-width:1.5px}
.bramka fieldset:has(input[value="accept"]:checked){
  border-color:var(--moss);background:var(--moss-wash);
}
.bramka fieldset:has(input[value="accept"]:checked) input{accent-color:var(--moss)}
.bramka fieldset:has(input[value="reject"]:checked){
  border-color:var(--oxide);background:var(--oxide-wash);
}
.bramka fieldset:has(input[value="reject"]:checked) input{accent-color:var(--oxide)}
.bramka fieldset:has(input[value="defer"]:checked){
  border-color:var(--brass);background:var(--brass-wash);
}
.bramka fieldset:has(input[value="defer"]:checked) input{accent-color:var(--brass)}
.bramka > form > label{display:block;margin:14px 0 4px;font-size:13.5px;color:var(--ink-2)}
.bramka textarea{
  width:100%;font:400 14px/1.5 "IBM Plex Sans",sans-serif;color:var(--ink);
  background:var(--panel-2);border:1px solid var(--rule);border-radius:2px;
  padding:8px 10px;resize:vertical;
}
.bramka button{margin-top:14px;margin-right:10px}
.bramka button[value="rejected"]{background:var(--oxide)}
.bramka button[value="rejected"]:hover{background:var(--oxide);opacity:.85}

/* ---------- materiały ---------- */
.pytania{
  border:1px solid var(--rule);border-radius:3px;background:var(--panel);
  padding:16px 20px;margin-bottom:20px;box-shadow:var(--shadow);
}
/* Pakiet renderowany z markdownu. Zakres znaczników jest zamknięty przez
   `webapp/markdown.py` i to jest cała ta lista — tu nie ma prawa pojawić się nic
   spoza niej, bo renderer niczego innego nie emituje (i żadnych atrybutów).

   Nagłówki dokumentu zaczynają się od `h3`: strona ma już `h1` (nazwa projektu) i
   `h2` (tytuł sekcji), więc wstawienie dokumentu z własnym `h1` dałoby drugą nazwę
   strony, a czytnik ekranu czyta konspekt po poziomach, nie po wyglądzie. */
.dokument{margin-top:12px;font-size:14px;line-height:1.6}
.dokument h3{font-size:17px;margin:18px 0 8px}
.dokument h4{font-size:14.5px;margin:16px 0 6px}
.dokument h5,.dokument h6{font-size:13px;margin:14px 0 6px;color:var(--ink-2)}
.dokument h3:first-child,.dokument h4:first-child{margin-top:0}
.dokument p{margin:0 0 10px}
.dokument ul,.dokument ol{margin:0 0 12px;padding-left:22px}
.dokument li{margin-bottom:4px}
.dokument hr{border:0;border-top:1px solid var(--rule-2);margin:16px 0}
.dokument code{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;font-size:12.5px;
  background:var(--panel-2);border:1px solid var(--rule-2);border-radius:2px;
  padding:1px 4px;word-break:break-word;
}
/* Tabele w pakietach bywają szerokie — przewijanie w poziomie w OBRĘBIE tabeli, a nie
   rozpychanie kolumny 960 px, przez którą przewijałaby się cała strona. */
.dokument table{
  display:block;overflow-x:auto;max-width:100%;
  border-collapse:collapse;margin:0 0 14px;font-size:13px;
}
.dokument th,.dokument td{
  border:1px solid var(--rule-2);padding:6px 10px;text-align:left;vertical-align:top;
}
.dokument th{background:var(--panel-2);font-weight:600}

.materialy{
  border:1.5px solid var(--ink);border-radius:3px;background:var(--panel);
  padding:20px 22px;box-shadow:var(--shadow);
}
.materialy .upuszczalnia{
  display:block;border:1.5px dashed var(--rule);border-radius:3px;
  padding:22px;text-align:center;color:var(--ink-2);font-size:13.5px;
  background:var(--panel-2);cursor:pointer;
}
.materialy .upuszczalnia input[type="file"]{display:block;margin:10px auto 0}
.materialy > label{display:block;margin:14px 0 4px;font-size:13.5px;color:var(--ink-2)}
.materialy textarea{
  width:100%;font:400 14px/1.5 "IBM Plex Sans",sans-serif;color:var(--ink);
  background:var(--panel-2);border:1px solid var(--rule);border-radius:2px;
  padding:8px 10px;resize:vertical;
}
.materialy button{margin-top:14px}

/* ---------- nowy projekt ---------- */
.nowy-projekt{
  border:1.5px solid var(--ink);border-radius:3px;background:var(--panel);
  padding:20px 22px;box-shadow:var(--shadow);
}
.nowy-projekt label{display:block;margin:14px 0 4px;font-size:13.5px;color:var(--ink-2)}
.nowy-projekt input:not([type="file"]),
.nowy-projekt select,
.nowy-projekt textarea{
  width:100%;font:400 14px/1.5 "IBM Plex Sans",sans-serif;color:var(--ink);
  background:var(--panel-2);border:1px solid var(--rule);border-radius:2px;
  padding:8px 10px;resize:vertical;
}
.nowy-projekt .upuszczalnia{
  display:block;border:1.5px dashed var(--rule);border-radius:3px;
  padding:22px;text-align:center;color:var(--ink-2);font-size:13.5px;
  background:var(--panel-2);cursor:pointer;
}
.nowy-projekt .upuszczalnia input[type="file"]{display:block;margin:10px auto 0}
.nowy-projekt button{margin-top:16px;margin-right:10px}
.nowy-projekt button.wtorny{background:var(--panel-2);color:var(--ink);border:1px solid var(--rule)}
.nowy-projekt button.wtorny:hover{background:var(--rule-2)}

/* Zakładki na ekranie zakładania projektu. Ta sama mechanika, co menu listy —
   zwykłe odnośniki, stan w adresie — więc i ten sam wygląd wybranego elementu.
   Kreska pod paskiem robi z nich zakładki, a nie drugi rząd przycisków. */
.zakladki{
  display:flex;gap:4px;flex-wrap:wrap;
  margin:0 0 22px;padding:0 0 0 2px;
  border-bottom:1px solid var(--rule);
}
.zakladki a{
  font-size:13.5px;text-decoration:none;
  padding:9px 14px;margin-bottom:-1px;
  color:var(--ink-2);background:transparent;
  border:1px solid transparent;border-bottom:1px solid var(--rule);
  border-radius:3px 3px 0 0;
  transition:border-color .12s, color .12s, background .12s;
}
.zakladki a:hover{color:var(--ink);background:var(--panel-2)}
/* Wybrana zakładka wchodzi w papier: dolna krawędź znika, żeby było widać, że to
   ta karta leży na wierzchu, a nie kolejny odnośnik. */
.zakladki a[aria-current="true"]{
  color:var(--accent-ink);background:var(--panel);
  border-color:var(--rule);border-bottom-color:var(--panel);
  font-weight:600;
}

/* Dwa sposoby na projekt przykładowy. Każdy jest osobnym formularzem, bo każdy
   wysyła inne zlecenie — i każdy niesie na ekranie własny koszt, żeby nikt nie
   kliknął „na żywo" spodziewając się spaceru po bramkach. */
.przyklad{
  margin:0 0 20px;padding:16px 18px;
  border:1px solid var(--rule);border-radius:3px;background:var(--panel);
}
.przyklad h2{margin:0 0 8px;font-size:15px}
.przyklad > p{margin:0 0 14px;font-size:13.5px;color:var(--ink-2);line-height:1.55}
.przyklad form{
  margin:10px 0 0;padding:12px 14px;
  border:1px solid var(--rule);border-radius:3px;background:var(--panel-2);
}
.przyklad form .meta{margin:0 0 4px}
.przyklad form .wskazowka{margin:0 0 10px}
.przyklad button{margin:0}
.przyklad button.wtorny{background:var(--panel);color:var(--ink);border:1px solid var(--rule)}
.przyklad button.wtorny:hover{background:var(--rule-2)}

/* Tryb raportu. Ekran istnieje po to, żeby czytać jeden dokument i pokazywać go
   innym, więc ramka dostaje tyle wysokości, ile się da, a strona nie dokłada niczego,
   co odciągałoby wzrok. */
/* Ramka bierze całą resztę kolumny — bez własnej wysokości, bez marginesu i bez
   ramki, bo na pełnym ekranie obramowanie dokumentu rysowałoby drugą krawędź tuż
   przy krawędzi okna. */
.raport-ramka{
  display:block;width:100%;flex:1;min-height:0;
  border:0;background:var(--panel);
}
.pusty-raport{
  margin-top:16px;padding:18px 20px;
  border:1px dashed var(--rule);border-radius:3px;background:var(--panel);
}
.pusty-raport p{margin:0 0 10px;font-size:13.5px;line-height:1.6;color:var(--ink-2)}
.pusty-raport p:last-child{margin-bottom:0}
.pusty-raport strong{color:var(--ink)}

/* --- ekran logowania -------------------------------------------------------
   Osobny ekran, ale w tej samej palecie co reszta panelu: wejście i praca mają
   wyglądać jak jeden produkt. Logo jest srebrne i ma kanał alfa, więc siada
   zarówno na jasnym papierze, jak i na ciemnym tle — bez dwóch wariantów pliku. */
body.ekran-logowania{
  display:flex;align-items:center;justify-content:center;
  min-height:100vh;padding:24px;
  /* Delikatny gradient zamiast płaskiego tła — na rzutniku płaskie tło wygląda
     jak niezaładowana strona. */
  background:
    radial-gradient(1200px 600px at 50% -10%, var(--accent-wash), transparent 60%),
    var(--paper);
}
.logowanie{
  width:100%;max-width:380px;
  padding:36px 34px 28px;
  background:var(--panel);
  border:1px solid var(--rule);
  border-top:3px solid var(--accent);
  border-radius:4px;
  box-shadow:var(--shadow);
  text-align:center;
}
.logowanie-znak{
  width:auto;height:96px;object-fit:contain;
  margin-bottom:18px;
}
.logowanie h1{
  font-family:Archivo,sans-serif;font-weight:700;font-size:22px;
  /* Odstęp pod tytułem nosił wcześniej podtytuł; po jego usunięciu przechodzi tutaj,
     żeby nagłówek nie siadał wprost na pierwszym polu formularza. */
  margin:0 0 22px;color:var(--ink);letter-spacing:-.01em;
}
.logowanie form{display:flex;flex-direction:column;gap:14px;text-align:left}
.logowanie label{
  display:flex;flex-direction:column;gap:6px;
  font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-2);
}
.logowanie input{
  font:inherit;font-size:15px;
  padding:10px 12px;
  color:var(--ink);
  background:var(--panel-2);
  border:1px solid var(--rule);border-radius:3px;
  transition:border-color .12s ease, box-shadow .12s ease;
}
.logowanie input:focus{
  outline:none;
  border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-wash);
}
.logowanie button{
  font:inherit;font-weight:600;font-size:15px;
  margin-top:4px;padding:11px 16px;
  color:var(--panel);background:var(--accent);
  border:1px solid var(--accent);border-radius:3px;cursor:pointer;
  transition:background .12s ease;
}
.logowanie button:hover{background:var(--accent-ink);border-color:var(--accent-ink)}
.logowanie-blad{
  margin:0 0 18px;padding:10px 12px;
  font-size:13px;text-align:left;
  color:var(--oxide);background:var(--oxide-wash);
  border:1px solid var(--oxide);border-radius:3px;
}
/* Znak w pasku panelu — mały, obok nazwy produktu. */
.pasek .logo{display:inline-flex;align-items:center;gap:9px}
.pasek .logo img{height:22px;width:auto;object-fit:contain}
.pasek .wyloguj{margin:0}
/* Przyciski paska: „Projekty" i „Wyloguj". Jedna reguła na oba, bo wyglądają tak
   samo i mają tak samo wyglądać. Wcześniej były dwa niemal identyczne bloki, które
   zdążyły się już rozjechać — jeden miał `cursor`, drugi `text-decoration`, jeden
   wyróżniał stronę bieżącą, drugi nie. `text-decoration:none` jest tu dlatego, że
   „Projekty" to `<a>`; na `<button>` nic nie robi. */
.pasek .akcja-paska{
  font:inherit;font-size:12px;font-weight:600;
  letter-spacing:.04em;text-transform:uppercase;
  padding:6px 12px;cursor:pointer;
  color:var(--ink-2);background:transparent;text-decoration:none;
  border:1px solid var(--rule);border-radius:3px;
  transition:border-color .12s ease, color .12s ease;
}
.pasek .akcja-paska:hover{color:var(--ink);border-color:var(--ink-2)}

/* Przełącznik motywu: trzy przyciski jako jeden segment, nie trzy osobne guziki.
   Wspólna ramka i brak przerw między nimi mówią, że to jeden wybór z trzech, a nie
   trzy niezależne akcje — tego samego pasek używa dla „Projekty" i „Wyloguj",
   które NIE są jednym wyborem i dlatego stoją osobno. */
.motyw{margin:0}
.motyw-grupa{display:flex}
.motyw-grupa .akcja-paska{border-radius:0;margin-left:-1px}
.motyw-grupa .akcja-paska:first-of-type{border-radius:3px 0 0 3px;margin-left:0}
.motyw-grupa .akcja-paska:last-of-type{border-radius:0 3px 3px 0}
/* Wybrany stan. `aria-pressed` niesie tę samą informację dla czytnika ekranu, więc
   selektor celuje w atrybut, a nie w dołożoną klasę — jedno źródło, nie dwa. */
.motyw-grupa .akcja-paska[aria-pressed="true"]{
  color:var(--ink);border-color:var(--ink-2);background:var(--panel-2);
  z-index:1;
}

/* Projekt, nad którym worker właśnie pracuje. Mosiądz, ten sam co dioda „w toku"
   w pasku — to ma być ten sam sygnał, tylko przy konkretnym projekcie. */
.kafel-w-toku{
  border-color:var(--brass);
  box-shadow:inset 3px 0 0 var(--brass), var(--shadow);
}
.kafel-w-toku:hover{border-color:var(--brass)}
.znacznik-w-toku{
  display:inline-block;vertical-align:middle;margin-left:10px;
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;
  color:var(--brass);background:var(--brass-wash);
  border:1px solid var(--brass);border-radius:2px;
  padding:3px 8px;
}

/* Faza rozstrzygnięta: pakiet zostaje do oglądania, bramka nie jest do klikania. */
.bramka.rozstrzygnieta{border-color:var(--accent)}
.bramka.rozstrzygnieta .notatka{
  margin-top:10px;padding:12px 14px;
  background:var(--panel-2);border-left:3px solid var(--accent);border-radius:2px;
  white-space:pre-wrap;
}
.adjudykacja{
  display:grid;grid-template-columns:auto 1fr;gap:4px 14px;
  margin:14px 0 0;font-size:13.5px;
}
.adjudykacja dt{
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:11px;
  text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3);padding-top:3px;
}
.adjudykacja dd{margin:0;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12.5px}
.bramka.rozstrzygnieta .override{
  margin-top:14px;padding:12px 14px;
  color:var(--brass);background:var(--brass-wash);
  border:1px solid var(--brass);border-radius:2px;
}

/* Pasek faz jako nawigacja — klikalne są tylko fazy, które mają już pakiet.

   Faza klikalna musi wyglądać na klikalną w KAŻDYM stanie. Pierwsza wersja dawała
   tylko `cursor:pointer` i hover zmieniający `border-color` na `var(--accent)` — a
   faza `approved` ma to obramowanie już w spoczynku (linia 119), więc najechanie na
   nią nie zmieniało dosłownie niczego. Zatwierdzone Define wyglądało wtedy tak samo
   jak zablokowane Improve i sprawiało wrażenie wyłączonego.

   Stąd oznaka niezależna od stanu: kropkowane podkreślenie klucza fazy, przy
   najechaniu ciągłe i akcentowe, plus zmiana tła i podkreślenie dolnej krawędzi.
   Nic z tego nie koliduje z tłem ani obramowaniem żadnego stanu. */
.os-faz .faza a{
  display:flex;flex-direction:column;gap:2px;
  color:inherit;
  text-decoration:underline dotted;
  text-decoration-color:var(--ink-3);
  text-underline-offset:3px;
}
.os-faz .faza.klikalna{cursor:pointer;transition:background .12s, box-shadow .12s}
.os-faz .faza.klikalna:hover{
  background:var(--panel);
  box-shadow:inset 0 -2px 0 var(--accent);
}
.os-faz .faza.klikalna:hover a{
  text-decoration:underline solid;
  text-decoration-color:var(--accent);
}

/* Faza oglądana w tej chwili — wyróżniona, żeby pasek mówił także "gdzie jestem",
   a nie tylko "dokąd można". Nie jest linkiem, więc nie reaguje na hover. */
.os-faz .faza.aktualna{
  border-color:var(--ink);
  background:var(--panel);
  box-shadow:inset 0 -2px 0 var(--ink);
}
.os-faz .faza.aktualna span{font-weight:600}

/* Powrót do ekranu projektu z pakietu i z materiałów. */
.powrot{
  display:inline-block;margin-bottom:10px;
  font-size:13px;color:var(--ink-2);text-decoration:none;
}
.powrot:hover{color:var(--accent);text-decoration:underline}

/* Przegląd wewnątrz panelu. Ramka, bo wygenerowany dokument niesie własny arkusz
   definiujący te same zmienne co panel — wklejony wprost przemalowałby motyw
   wszystkiego poniżej. Wysoka i z własnym przewijaniem: to jest lektura, nie podgląd. */
.przeglad-ramka{
  display:block;width:100%;height:calc(100vh - 220px);min-height:520px;
  margin-top:16px;
  border:1px solid var(--rule);border-radius:3px;
  background:var(--panel);
  box-shadow:var(--shadow);
}

/* Komunikat odmowy. `showModal()` daje tło i pułapkę na fokus; gdyby skryptu nie
   było, element ma `open` i zostaje zwykłym, widocznym panelem — brak skryptu nie
   może znaczyć braku komunikatu. */
dialog.komunikat{
  max-width:440px;padding:22px 24px;
  color:var(--ink);background:var(--panel);
  border:1px solid var(--oxide);border-top:3px solid var(--oxide);border-radius:4px;
  box-shadow:var(--shadow);
}
dialog.komunikat::backdrop{background:rgba(23,27,30,.45)}
dialog.komunikat h2{margin:0 0 8px;color:var(--oxide);font-size:17px}
dialog.komunikat p{margin:0 0 18px;color:var(--ink-2);line-height:1.5}
dialog.komunikat form{margin:0}
dialog.komunikat button{background:var(--oxide)}
dialog.komunikat button:hover{background:var(--oxide);opacity:.88}
/* Bez skryptu `<dialog open>` stoi w toku strony — niech przynajmniej nie przykleja
   się do lewej krawędzi. */
dialog.komunikat:not(:modal){margin:20px auto}

/* Wskazówki przy polach briefu. Etykieta stoi teraz osobno (przez `for`), więc
   odstępy trzeba ustawić na niej, nie na opakowaniu. */
.nowy-projekt label{margin:18px 0 4px}
.nowy-projekt label:first-of-type{margin-top:0}
.nowy-projekt .wskazowka{
  margin:6px 0 0;
  font-size:12.5px;line-height:1.5;color:var(--ink-3);
}
.nowy-projekt .wskazowka strong{color:var(--ink-2)}
.nowy-projekt .wskazowka code{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;font-size:11.5px;
  background:var(--panel-2);border:1px solid var(--rule-2);border-radius:2px;
  padding:1px 4px;
}
/* Pole plików jest etykietą-upuszczalnią, więc jego wskazówka idzie pod ramką. */
.nowy-projekt .upuszczalnia{margin-top:18px}

/* Ikony faz na kaflu: D M A I C ze stanem.

   Stan rozróżniają trzy rzeczy naraz — kolor, wypełnienie i obramowanie — bo sam
   kolor przepada na rzutniku i u kogoś nierozróżniającego barw. Słowami mówi go
   `title` i tekst dla czytnika ekranu. */
.tylko-dla-czytnika{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0;
}
.ikony-faz{
  display:flex;gap:6px;list-style:none;padding:0;margin:12px 0 0;
}
.ikona-fazy{
  position:relative;
  display:flex;align-items:center;justify-content:center;
  width:26px;height:26px;
  font-family:Archivo,"Helvetica Neue",Arial,sans-serif;
  font-size:12px;font-weight:700;letter-spacing:.02em;
  border:1px solid var(--rule);border-radius:3px;
  background:var(--panel-2);color:var(--ink-3);
}
/* Zamknięta: pełne wypełnienie — z daleka widać, ile projektu jest za sobą. */
.ikona-fazy.approved{
  background:var(--accent);border-color:var(--accent);color:var(--panel);
}
/* Czeka na człowieka: mocna ramka, żeby wołała. */
.ikona-fazy.ready_for_review{
  background:var(--brass-wash);border:2px solid var(--brass);color:var(--brass);
}
/* Czeka na dane z zakładu: ta sama rodzina co wyżej, ale przerywana — czekanie
   jest na kimś innym. */
.ikona-fazy.needs_info{
  background:var(--brass-wash);border:1px dashed var(--brass);color:var(--brass);
}
.ikona-fazy.rejected{
  background:var(--oxide-wash);border-color:var(--oxide);color:var(--oxide);
}
/* Run przerwany to awaria maszyny, nie etap pracy — jedyny stan, w którym nie poszło
   coś z runem, a nie z materiałem. Rdza jak przy „odesłana do zespołu", bo obie znaczą
   „wróć tu"; kreska przerywana odróżnia je od siebie bez trzeciego koloru. */
.ikona-fazy.run_failed{
  background:var(--oxide-wash);border:1.5px dashed var(--oxide);color:var(--oxide);
}
.ikona-fazy.blocked{opacity:.45}
/* pending / composed / ran zostają na wyglądzie domyślnym: w toku, nic do decyzji. */

/* Pakiet syntetyczny. Kolor rdzy jak przy odesłaniu i przerwanym runie — wszystkie
   trzy znaczą „nie bierz tego za dobrą monetę" — ale rysunek inny: pas pełnej
   szerokości nad treścią, żeby dało się go przeczytać ZANIM przeczyta się pakiet. */
.pas-mock{
  margin:0 0 18px;padding:10px 14px;
  border:1px solid var(--oxide);border-left:4px solid var(--oxide);border-radius:3px;
  background:var(--oxide-wash);color:var(--oxide);
  font-size:13.5px;line-height:1.55;
}
.pas-mock code{background:transparent;color:inherit}
/* Na liście i na osi faz starcza znacznik: tam się nie czyta pakietu, tylko wybiera,
   w co wejść. */
.znacznik-mock{
  margin-left:8px;padding:1px 7px;border-radius:2px;vertical-align:middle;
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:10.5px;letter-spacing:.08em;font-weight:600;
  background:var(--oxide-wash);color:var(--oxide);border:1px solid var(--oxide);
}
/* Ikona fazy: kreskowana obwódka dokłada się do koloru stanu, zamiast go zastępować —
   pochodzenie pracy i stan fazy to dwa niezależne fakty i oba mają być widoczne. */
.ikona-fazy.syntetyczna{outline:1.5px dashed var(--oxide);outline-offset:2px}

/* Zlecenie, które się udało, ale zrobiło co innego, niż się prosiło. Nie błąd —
   więc nie rdza — ale i nie cisza. */
.pas-uwaga{
  margin:0 0 16px;padding:10px 14px;
  border:1px solid var(--brass);border-left:4px solid var(--brass);border-radius:3px;
  background:var(--brass-wash);color:var(--brass);font-size:13.5px;line-height:1.55;
}

/* Menu listy: kolejność i filtr stanu. Zwykłe odnośniki, więc każdy wybór jest
   adresem — da się go zakładkować i przeżywa odświeżanie co 5 s. */
.menu-listy{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin:0 0 18px;padding:10px 12px;
  border:1px solid var(--rule);border-radius:3px;background:var(--panel);
}
.menu-etykieta{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  font-size:11px;text-transform:uppercase;letter-spacing:.08em;
  color:var(--ink-3);
}
.menu-etykieta:not(:first-child){margin-left:10px}
.menu-listy a{
  font-size:13px;text-decoration:none;
  padding:5px 10px;
  color:var(--ink-2);background:transparent;
  border:1px solid transparent;border-radius:3px;
  transition:border-color .12s, color .12s;
}
.menu-listy a:hover{color:var(--ink);border-color:var(--rule)}
/* Wybrane: to nie jest odnośnik do gdzie indziej, tylko stan, w którym się jest. */
.menu-listy a[aria-current="true"]{
  color:var(--accent-ink);background:var(--accent-wash);border-color:var(--accent);
  font-weight:600;
}
