/* ============================================================================
   03-kontrolki.css — przyciski i pola. JEDEN wygląd dla całej aplikacji.
   Każda kontrolka ma tę samą wysokość (--h-control) i te same rogi, więc
   wiersz „etykieta + pole + guzik" składa się sam, bez poprawek w widokach.
   ============================================================================ */
/* Strzałka <select> jest TŁEM, a tło nie dziedziczy currentColor — kolor musi
   siedzieć w samym SVG. Stąd jedyny wyjątek od zasady „ciemny tryb sam przez
   tokeny". color-scheme każe przeglądarce rysować natywne części kontrolek
   (rozwinięta lista, checkbox, pasek przewijania) w ciemnej wersji. */
:root { --strzalka: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235d6878' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); }
:root[data-theme="dark"] { --strzalka: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b949e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); color-scheme: dark; }
/* --- PRZYCISKI --------------------------------------------------------------
   Jedna baza dla tekstowego i ikonowego; warianty zmieniają WYŁĄCZNIE kolor
   ramki, tła i tekstu — kolor niesie znaczenie akcji, nie hierarchię. */
/* Wcięcie 0 9px — monolit (linia 1267). Nasze 12 px robiło każdy przycisk
   w aplikacji szerszym niż w v8. */
/* NAPRAWA 11.08.2026 — DOPISANE `.danger-button` i `.primary-button`.
   `public/js/rdzen/ + public/js/ui/` nadaje przyciskowi potwierdzenia klasę
   `danger-button` (albo `button primary`), a w v12 NIE BYŁO dla niej ŻADNEJ
   reguły — ani teraz, ani w kopii sprzed sprzątania. Skutek: przycisk
   „Zamknij bez wstawiania" w oknie potwierdzenia stał goły, bez ramki i bez
   tła, obok normalnie ostylowanego „Wstaw do tekstu".
   To nie jest nowa definicja — obie klasy wchodzą do ISTNIEJĄCEJ rodziny
   przycisków, więc dostają tę samą geometrię i ten sam ton co `.button.danger`
   (reguła niżej). Zero powielonego kodu, zero nowego wyglądu.
   `.primary-button` dopisany profilaktycznie z tego samego powodu: występuje
   w markupie (`public/js/ui/tony-przyciskow.js`) i miałby ten sam problem. */
/* W1: w oknie róg przycisku 12 px na obu ekranach (--okno-przycisk-r z 05-komponenty §3); poza oknem bez zmian. */
.button, .icon-button, .danger-button, .primary-button { display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2); height: var(--h-control); padding: 0 var(--s-4); border: 1px solid transparent; border-radius: var(--okno-przycisk-r, var(--r)); background: var(--btn-2); color: var(--text); font-size: var(--t-control); font-weight: var(--w-bold); line-height: var(--lh-tight); white-space: nowrap; text-decoration: none; cursor: pointer; transition: background var(--szybko), border-color var(--szybko), color var(--szybko); }
/* Hover osobno, bo kasuje podkreślenie z a:hover (reset) — inaczej
   <a class="button"> podkreślałby się jak zwykły link. */
/* RM7 (v15 b14): zwykły przycisk jak `.btn` we wzorze — biały z ramką (--line-strong), pod kursorem jasnoszary; główny
   (akcent), usuwanie (czerwień) i „Anuluj” mają własne reguły niżej. Przycisk-ikona zostaje szarym kwadratem. */
.button { border-color: var(--line-strong); background: var(--surface); }
.button:hover { background: var(--surface-2); text-decoration: none; }
.icon-button:hover { background: var(--btn-2-hover); text-decoration: none; }
/* Wciśnięcie potwierdzone ruchem — jedyna reakcja przed odpowiedzią serwera. */
.button:active, .icon-button:active { transform: scale(.97); }
.button:disabled, .icon-button:disabled { opacity: .5; cursor: not-allowed; transform: none; }
/* PRIMARY = akcja główna okna (Zapisz, Dodaj). */
/* NAPRAWA 11.08.2026 — kolor napisu --accent → --accent-strong.
   Zmierzone: `--accent` (#3b6ef0) na `--accent-soft` (#eaf0fe) daje 3,93:1,
   czyli PONIŻEJ progu czytelności 4,5:1 — a to jest napis na przycisku
   GŁÓWNEJ AKCJI każdego okna („Zapisz", „Dodaj", „Zatwierdź"). `--accent-strong`
   (#2f59d6) na tym samym tle daje 5,22:1. Barwa marki i tło przycisku zostają
   bez zmian, ciemnieje wyłącznie napis. */
/* 13.09.2026 — NOWY SYSTEM (wzorzec telefonu, zatwierdzony dla telefonu i komputera): akcja główna to
   PEŁNY akcent z białym napisem. Jedna na okno / ekran. Blady niebieski z ramką znika. */
.button.primary, .primary-button { border-color: transparent; background: var(--accent); color: var(--on-accent); }
.button.primary:hover, .primary-button:hover { border-color: transparent; background: var(--accent-hover); }
/* Ikona z klasą primary (np. „+ etap" w każdym wierszu listy) to akcja drugiego rzędu — delikatny akcent,
   nie pełny: inaczej na liście stałoby tyle „głównych" przycisków, ile wierszy. */
.icon-button.primary { border-color: transparent; background: var(--accent-soft); color: var(--accent-strong); }
.icon-button.primary:hover { background: color-mix(in srgb, var(--accent) 20%, var(--surface)); }
/* DANGER = działanie nieodwracalne (Usuń, Zamknij bez zapisu). */
/* Krzyżyk zamykający okno liczy się do tej samej rodziny co „usuń" — w v8 też
   jest czerwony (monolit 25417), a JS nie nadaje mu klasy `danger`. */
/* ============================================================================
   KOLORY PRZYCISKÓW — JEDEN SYSTEM NA CAŁĄ APLIKACJĘ (ustalone 11.08.2026)
   ----------------------------------------------------------------------------
   Dotąd nie było reguły, tylko przypadek: „Anuluj" wyglądał jak zwykły
   przycisk, „Zamknij bez wstawiania" nie miał w ogóle stylu, a krzyżyk był
   czerwony. Trzy różne wagi tej samej decyzji „nie zapisuj".
   Od teraz KOLOR NIESIE SKUTEK, nie ważność:

     NIEBIESKI (akcent)  — TO ZAPISUJE.        Zapisz · Dodaj · Zatwierdź · Wyślij
     POMARAŃCZOWY (uwaga)— WYJŚCIE BEZ ZAPISU. Anuluj · Zamknij bez wstawiania
     CZERWONY (danger)   — KASUJE DANE.        Usuń · Wyczyść · krzyżyk zamknięcia
     SZARY (domyślny)    — reszta, bez skutków ubocznych.

   Dlaczego „Anuluj" NIE jest czerwony: czerwień ma znaczyć „tracisz dane
   nieodwracalnie". Anulowanie okna nie kasuje niczego, co już istnieje —
   porzuca tylko niezapisane zmiany. Wrzucenie obu w jeden kolor sprawia, że
   czerwień przestaje ostrzegać, bo widać ją przy każdym oknie.
   Krzyżyk zostaje czerwony celowo — w v8 też jest (monolit 25417), a jako
   jedyny zamyka okno BEZ pytania, więc jest bliżej „kasuję" niż „anuluję". */
/* ⚠️ SYSTEM KOLORÓW Z 11.08.2026 (opis wyżej: pomarańczowe „Anuluj", czerwony krzyżyk) ZASTĄPIONY
   13.09.2026 decyzją właściciela (wzorzec telefonu, „Telefon i komputer"):
     NIEBIESKI pełny — akcja główna (Zapisz, Dodaj, Zatwierdź) · jedna na okno
     SZARY          — wszystko inne, także „Anuluj", „Zamknij" i krzyżyk okna
     CZERWONY       — tylko usuwanie (napis czerwony na jasnoczerwonym tle)
   Klasy .warn / .warn-button zostają w kodzie, ale wyglądają jak zwykły przycisk. */
.button[data-action="close-modal"], .button.warn, .warn-button { border-color: transparent; background: var(--btn-2); color: var(--text); }
.button[data-action="close-modal"]:hover, .button.warn:hover, .warn-button:hover { border-color: transparent; background: var(--btn-2-hover); }
.button.danger, .danger-button { border-color: transparent; background: var(--danger-soft); color: var(--danger); }
.button.danger:hover, .danger-button:hover { border-color: transparent; background: color-mix(in srgb, var(--danger) 16%, var(--surface)); }
/* Kosz w wierszu: szary przycisk z czerwoną ikoną — rząd koszy nie tworzy już „ściany czerwieni". */
.icon-button.danger { border-color: transparent; background: var(--btn-2); color: var(--danger); }
.icon-button.danger:hover { border-color: transparent; background: var(--danger-soft); }
.icon-button[data-action="close-modal"] { border-color: transparent; background: var(--btn-2); color: var(--text); }
.icon-button[data-action="close-modal"]:hover { border-color: transparent; background: var(--btn-2-hover); }
/* MINI = guzik w wierszu tabeli: niższy, ten sam język. */
.button.mini { height: var(--h-control-sm); padding: 0 var(--s-2); font-size: var(--t-sm); }
/* Ikonowy = kwadrat; ikonę centruje flex odziedziczony z bazy. */
.icon-button { width: var(--h-control); padding: 0; flex: none; }
/* Ujednolicenie 11.08.2026 — JEDEN rozmiar przycisku-ikony: token --h-ikona (30).
   Kosz w wierszu miał 27, a `.icon-button` obok 32 — 5 px różnicy w jednym wierszu. */
.icon-button.mini { width: var(--h-ikona); height: var(--h-ikona); }

/* ⭐⭐ PRZYCISK POMOCY — JEDEN NA CAŁĄ APLIKACJĘ (18.08.2026).

   Zgłoszenie właściciela: *„ta niebieska ikona to jest przycisk pomocy!!! weź to ogarnij
   jako token przycisku pomocy globalnie"*. Miał rację — ten sam przycisk miał
   PIĘĆ różnych wyglądów, każdy w innym arkuszu:

     · okna (`.modal-help-btn`, 27-wspolne) — niebieskie wypełnione kółko ze znakiem „?";
       to jest ten, który właściciel wskazał jako „przycisk pomocy";
     · pasek kartoteki (`.help-button`, 28-uzupelnienie) — szara przezroczysta ikonka;
     · Zamówienia (`.orders-help-btn`, 13-zamowienia) — szara ikonka, własny kolor podświetlenia;
     · Waluta (public/js/widoki/) — ⛔ style wpisane Z KODU w atrybucie `style`, kwadrat 20 px;
     · Bloki (`.block-help-btn`) — to co innego: przycisk z NAPISEM w stopce okna, zostaje.

   Kanonem jest wygląd z okien, bo stoi w KAŻDYM oknie i to na niego wskazał właściciel.
   Wszystkie miejsca dostają klasę `.help-button`; stare nazwy klas zostają WYŁĄCZNIE
   tam, gdzie niosą POŁOŻENIE (np. dosunięcie do krzyżyka), nigdy wygląd.

   ⛔ Nowy przycisk pomocy = `class="help-button"` i znak „?" jako treść.
   Nie dokładać kolejnej klasy ani własnych stylów — od tego się to zaczęło. */
.help-button, .modal-help-btn, .orders-help-btn {
  display: inline-grid;
  place-items: center;
  width: var(--h-ikona);
  height: var(--h-ikona);
  padding: 0;
  flex: none;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: var(--t-sm);
  font-weight: var(--w-bold);
  line-height: 1;
  vertical-align: middle;
  cursor: pointer;
  transition: background var(--szybko), border-color var(--szybko);
}
.help-button:hover, .modal-help-btn:hover, .orders-help-btn:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 22%, var(--surface));
}
/* Gdyby gdzieś został wariant z ikoną zamiast znaku „?" — ikona bierze kolor
   przycisku przez `currentColor` w masce, a nie zostaje szara. */
.help-button .icon, .orders-help-btn .icon { width: 15px; height: 15px; } .modal-help-btn .icon { width: 18px; height: 18px; }   /* „?” okna = ikona okręgu z „?” 18 px (wzór: .ui-ik .ic) */

/* ⭐⭐ KURSOR: KLIKALNE = RĄCZKA — JEDNA GLOBALNA ZASADA (18.08.2026).

   Zgłoszenie właściciela: „jak się w dropdown najedzie na strzałkę, to kursor
   jest jak do pisania, a nie jak do naciśnięcia — to samo z kaflami".
   ZMIERZONE na żywej stronie, nie wyczute — winni z otwartego kalkulatora:
     · 21× pole combo (kursor „do pisania" na CAŁYM polu ze strzałką — bo strzałka
       to obrazek namalowany NA polu tekstowym, patrz `input.combo-input` wyżej),
     · 8× nagłówek rozwijanej sekcji (summary) — kursor domyślny,
     · 3× kafel etapu na liście projektów (rola przycisku) — kursor domyślny,
     · listy natywne <select> — kursor domyślny.

   ZASADY tego bloku:
     · combo: rączka DOPÓKI pole nie jest w edycji — po kliknięciu (focus) wraca
       kursor tekstowy, bo wtedy człowiek pisze, żeby filtrować. To rozstrzyga
       też strzałkę: ona jest częścią pola, więc dostaje rączkę razem z nim;
     · `[data-action]` = klikalne z definicji (wielki nasłuch kliknięć w 10-events
       łapie wszystko z tym zaczepem) — ALE z wyjątkiem pól do pisania: 13 pól
       tekstowych w aplikacji MA data-action (zmierzone), stąd :not(input/textarea).
       Kratki i suwaki są <input>, więc dostają rączkę osobną regułą niżej;
     · wyłączone kontrolki NIE dostają rączki: reguły `:disabled { cursor: default /
       not-allowed }` mają wyższą wagę i wygrywają — sprawdzone, nie zakładane;
     · celowe wyjątki przeżywają: `.chat-reply-preview { cursor: auto }` (17-czat)
       i `.message-hub-twisty.is-empty { cursor: default }` stoją w arkuszach
       ładowanych PÓŹNIEJ albo mają wyższą wagę — wygrywają z tym blokiem.

   ⛔ Nowy klikalny element = zaczep `data-action` albo role="button" — wtedy rączka
   przychodzi SAMA z tego bloku. Nie dopisywać `cursor: pointer` po arkuszach. */
summary { cursor: pointer; }
select { cursor: pointer; }
.combo-input:not(:focus) { cursor: pointer; }
input[type="checkbox"], input[type="radio"], input[type="range"] { cursor: pointer; }
[data-action]:not(input):not(textarea),
[data-view], [data-tab], [data-stage-expand],
[role="button"], [role="tab"],
th[data-sort], th[data-sort-key] { cursor: pointer; }

/* ⭐ DOKŁADKA 18.08.2026 — zgłoszenie właściciela: „w Blokach najechanie na rekord
   nie daje łapki, a klik otwiera rekord — i takich miejsc jest dużo więcej".

   WIERSZE KARTOTEK — JEDNA REGUŁA DLA WSZYSTKICH. Klik w dowolny wiersz kartoteki
   otwiera edycję przez wspólny nasłuch (10-events: „Klik w dowolny wiersz kartoteki
   = edycja" — wciska ukryty przycisk Edytuj tego wiersza). Czyli klikalny jest
   DOKŁADNIE ten wiersz, który MA przycisk edycji — i to samo mówi selektor:
   `:has` łapie wiersze z przyciskiem, a wiersze puste/grupujące zostają bez łapki.
   Jedna reguła obejmuje Bloki, Materiały, Klientów, Dostawców i każdą PRZYSZŁĄ
   kartotekę, bo wszystkie renderują się w ten sam sposób.
   (`:has` jest już w tym arkuszu — patrz `.combo:has(...)` niżej.) */
[data-catalog-table-wrap] tbody tr:has([data-action="edit-catalog"]) { cursor: pointer; }

/* ROZWIJANE WIERSZE I KAFLE spoza wzorca data-action — zaczepy zebrane z nasłuchów
   kliknięć (10-events, public/js/widoki/projekty-lista.js, public/js/czat/, public/js/zadania/), nie zgadnięte:
   zwijanie projektu/etapu/designu/konstrukcji na liście projektów, rozwijanie
   kafla, zakładki osi czasu, karta osoby w wyborze uczestników. */
[data-toggle-project], [data-toggle-stage], [data-toggle-design], [data-toggle-construction],
[data-tile-expand], [data-timeline-tab], [data-project-participant-card] { cursor: pointer; }

/* ⭐ TOKEN `data-klik` — dla KAŻDEGO przyszłego miejsca, które reaguje na klik,
   a nie pasuje do żadnego wzorca wyżej. Dopisujesz atrybut w szablonie i łapka
   przychodzi sama — zamiast kolejnej reguły kursora w kolejnym arkuszu. */
[data-klik] { cursor: pointer; }

/* --- POLA -------------------------------------------------------------------
   Wspólna reguła trzyma pola tekstowe i rozwijane w jednej linii. Checkbox i
   radio wyłączone — nie są pudełkiem na tekst i mają własny rozmiar. */
/* BEZ `min-width: 0` — monolit też go tu nie ma. Z zerem lista rozwijana
   kurczyła się poniżej swojej treści (filtry „Status" i „Rodzaj": 103 px
   zamiast 132). Tam, gdzie pole MA się kurczyć, monolit dodaje `min-width: 0`
   punktowo na konkretnym komponencie — i tak samo robimy my. */
/* Monolit (371): `min-height: 32px` + `padding: 7px 9px` — NIE sztywna wysokość.
   Dzięki temu pole rośnie, gdy treść tego wymaga (np. dłuższa opcja listy),
   zamiast obcinać. Nasze sztywne `height` + 12 px wcięcia dawało inne
   szerokości list rozwijanych niż w v8. */
/* NAPRAWA 11.08.2026 — `line-height: var(--lh-tight)`. Zmierzone na żywej
   stronie: pola miały 32,75 px, a przyciski obok 32 px — pole było o piksel
   wyższe od przycisku w KAŻDYM pasku narzędzi aplikacji (widoczne jako
   nierówna dolna krawędź rzędu). Powód: wysokość naturalna = interlinia
   17,16 px (1,32 × 13) + 14 px wyściółki + 1,6 px ramki = 32,76, czyli WIĘCEJ
   niż `min-height: 32`, więc min-height nic nie robił. Przy 1,25 wychodzi
   31,85 → min-height przejmuje i daje równe 32. Pole nadal ROŚNIE, gdy treść
   tego wymaga (o to chodziło w regule v8 niżej) — zmienia się tylko to, że
   przy jednym wierszu tekstu trafia w tę samą wysokość co reszta kontrolek. */
/* POPR3-pola (28.09.2026): JEDEN wygląd pola wszędzie — tło --pole-tlo (białe / ciemna powierzchnia), ramka --pole-ramka
   (00-tokens). W oknie zmienia się tylko róg (--okno-pole-r = 12 px, 05-komponenty §3). Zmienne --okno-pole-tlo/-ramka/
   -obrys zostały jako furtka dla pól osadzonych w innej kontrolce (np. lista w pasku edytora tekstu, 17-czat) — nikt ich
   nie ustawia dla zwykłych pól. Pola w opakowaniach z własną ramką (szukajki) mają swoje reguły o większej wadze. */
input:not([type="checkbox"]):not([type="radio"]), select, textarea { min-height: var(--h-control); padding: 7px 9px; border: 1px solid var(--okno-pole-ramka, var(--pole-ramka)); border-radius: var(--okno-pole-r, var(--r)); background: var(--okno-pole-tlo, var(--pole-tlo)); box-shadow: var(--okno-pole-obrys, none); color: var(--text); font-size: var(--t-base); line-height: var(--lh-tight); transition: border-color var(--szybko), box-shadow var(--szybko); }
/* 20.10.2026: kropki hasła — pismo systemowe (kropki zwykłego rozmiaru); w kroju programu na telefonie wychodziły ogromne. */
input[type="password"] { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; letter-spacing: .04em; }
/* SUWAKI — JEDEN wygląd dla całej aplikacji, U ŹRÓDŁA (12.08.2026).
   Suwak to nie pole tekstowe: reguła wyżej dawała mu ramkę, białe tło i 32 px
   wysokości, więc w KAŻDYM nowym miejscu trzeba ją było odkręcać osobno — stąd
   „biały pasek z ramką" na tle zaznaczonej konstrukcji. Rysuje go przeglądarka
   sterowana tokenem przez `accent-color`; dzięki temu tor ma natywne WYPEŁNIENIE
   od zera do uchwytu. Własne ::track/::thumb wymagają `appearance: none`, a to
   wypełnienie wyłącza — dlatego ich tu NIE ma.
   ⚠️ Te dwa `:not()` NIE filtrują niczego (suwak nie jest checkboxem ani radiem) —
   wyrównują SPECYFICZNOŚĆ do reguł wyżej: 0-3-1 dla widoków i 0-4-1 dla okien.
   Pierwsza próba szła inaczej: dopisano `:not([type="range"])` do reguł bazowych.
   To podniosło ICH siłę o jeden człon i zbiło kilka reguł kalkulatora (0-3-1),
   przez co znak „%" znów wchodził na cyfry. Reguł bazowych NIE RUSZAĆ. */
input[type="range"]:not([type="checkbox"]):not([type="radio"]),
.modal input[type="range"]:not([type="checkbox"]):not([type="radio"]) { --okno-pole-obrys: none; width: 100%; height: var(--s-3); min-height: 0; margin: 0; padding: 0; border: 0; background: transparent; accent-color: var(--accent-strong); cursor: pointer; }
input[type="range"]:disabled { opacity: .45; cursor: default; }
/* POLA W OKNACH: wyściółka 6/10 i waga 500 (w widokach zostaje bazowe 7/9 i 400).
   To DOCELOWE wartości v8, wyliczone z renderu, nie przepisane z jednej reguły:
   monolit ustawia je trzykrotnie i reguły biją się nawzajem — `.modal input`
   (24995) daje 12,5 px i ramkę `--line`, ale późniejsza reguła dla pól
   tekstowych (25499, czternaście selektorów) podbija z powrotem na 13 px,
   wagę 500 i ramkę `--line-strong`. Efekt końcowy dla każdego WIDOCZNEGO pola
   to właśnie 13 px / 500 / `--line-strong` — i tylko to tu zapisujemy, jedną
   regułą zamiast czternastu. Rozmiar i ramka zostają z reguły bazowej wyżej. */
.modal input:not([type="checkbox"]):not([type="radio"]), .modal select, .modal textarea { padding: 6px 10px; font-weight: var(--w-medium); }
/* Poświata zamiast grubszej ramki — grubsza ramka przesuwa treść o piksel. */
/* W1: w oknie fokus jak na telefonie — ramka akcentu, poświata 3 px (zmienne z 05-komponenty §3). Tło zostaje --pole-tlo. */
input:focus, select:focus, textarea:focus { border-color: var(--okno-pole-fokus-ramka, var(--accent-strong)); box-shadow: 0 0 0 var(--okno-pole-fokus-pierscien, var(--s-1)) color-mix(in srgb, var(--accent) 18%, transparent); }
/* --- POLE WEWNĄTRZ OPAKOWANIA Z RAMKĄ (szukajki, „Dla kogo", pole kwoty) -----
   Zasada: podświetla się OPAKOWANIE, nigdy pole w środku. Pole nie ma własnej
   ramki (patrz reguły `:not(...)` w arkuszach 14/15/17), więc gdyby dostało
   poświatę albo systemowy `outline` z resetu, narysowałby się KWADRAT bez
   zaokrągleń w środku zaokrąglonego opakowania. Dlatego zdejmujemy z niego
   jedno i drugie, a ramkę i poświatę bierze na siebie rodzic przez
   `:focus-within` (reguła niżej — jedna dla wszystkich opakowań). */
/* NAPRAWA 11.08.2026 — `.search` WYPISANE z tej grupy.
   Zmierzone na zywej stronie: opakowanie `.search` ma `border: 0px none` i
   `border-radius: 0px` (definiuje je tylko `position: relative`, arkusz 06),
   a mimo to dostawalo poswiate `0 0 0 4px`. Poswiata na elemencie bez
   zaokraglenia rysuje sie jako PROSTOKAT 340x33 z ostrymi rogami — stad
   „podswietlenie szukajki inne niz w liscie rozwijanej".
   Pozostale opakowania w tej grupie MAJA wlasna ramke i `--r`, wiec dla nich
   wzorzec „podswietla sie rodzic" jest poprawny i zostaje.
   Pole w `.search` wraca do zwyklej reguly `input:focus` wyzej — dostaje
   zaokraglona poswiate 9 px, identyczna jak kazde inne pole i jak combobox. */
.tree-search input:focus, .tree-search input:focus-visible,
.timeline-search input:focus, .timeline-search input:focus-visible,
.mh-search input:focus, .mh-search input:focus-visible,
.checklist-add input:focus, .checklist-add input:focus-visible,
.asel-input:focus, .asel-input:focus-visible { outline: none; box-shadow: none; border-color: transparent; }
/* W1: opakowanie ma własną ramkę — pole w środku nie dostaje włosowego obrysu skórki okna. */
.tree-search, .timeline-search, .mh-search, .checklist-add, .asel-input { --okno-pole-obrys: none; }
.tree-search:focus-within, .timeline-search:focus-within, .mh-search:focus-within,
.checklist-add:focus-within, .asel-trigger:focus-within { border-color: var(--accent-strong); box-shadow: 0 0 0 var(--s-1) color-mix(in srgb, var(--accent) 18%, transparent); }
/* Wyłączone pole ma WYGLĄDAĆ na wyłączone, ale zostać czytelne — jego wartość
   nadal jest informacją dla użytkownika. */
/* BŁĄD (W1, 27.09, zgłoszenie b14): ta reguła (0,1,1) przegrywała z regułą pól wyżej (0,2,1) — wyłączone pole
   („Dział (z funkcji)” w oknie osoby, plan produkcji u Dyrektora) wyglądało jak zwykłe pole do wpisania.
   `:not()` wyrównuje wagę do reguł pól (0,3,1). POPR3-pola: jedno tło zablokowane w oknie i poza nim (00-tokens). */
input:disabled:not([type="checkbox"]):not([type="radio"]), select:disabled, textarea:disabled { --okno-pole-obrys: none; border-color: var(--pole-ramka-zablokowane); background: var(--pole-tlo-zablokowane); color: var(--pole-napis-zablokowane); cursor: not-allowed; }
/* Pole TYLKO DO ODCZYTU wygląda na wyliczone, nie na puste pole do wpisania (skan 13.09.2026: „Waga" bloku,
   „Numer projektu (auto)", ceny wyliczone — białe jak zwykłe pola, a pomoc mówi „nie wpisujesz"). Tekst
   zostaje pełnym kolorem: to informacja, nie wyłączona kontrolka. */
/* POPR3-pola: pole tylko do odczytu = pole zablokowane (szare tło, słabsza ramka, kursor not-allowed) — w oknie, w widoku
   i na telefonie tak samo. Wyjątek: pole wyboru, które samo jest tylko do odczytu, a otwiera listę (combo, lista osób). */
input[readonly]:not([type="checkbox"], [type="radio"], [type="range"], [type="hidden"], [data-combo-input], .combo-input, .asel-input), textarea[readonly] { --okno-pole-obrys: none; border-color: var(--pole-ramka-zablokowane); background: var(--pole-tlo-zablokowane); cursor: not-allowed; }
::placeholder { color: var(--text-faint); }
/* Textarea rośnie w dół, więc wysokość kontrolki jest dla niej tylko minimum. */
textarea { height: auto; min-height: calc(var(--h-control) * 2); padding: var(--s-2) var(--s-3); line-height: var(--lh-base); resize: vertical; }
/* Natywna strzałka wygląda inaczej w każdej przeglądarce — podmieniamy ją na
   własną i rezerwujemy jej miejsce po prawej. Ta sama reguła obsługuje pole
   comboboxa, żeby obie listy wyglądały identycznie. */
/* `padding-right` MUSI być większy niż miejsce zajęte przez strzałkę: odsunięcie
   od krawędzi (--s-3) + jej szerokość (--s-4) + odstęp od tekstu (--s-2).
   Sztywne --s-6 (32 px) było mniejsze od tej sumy, więc w wąskich polach —
   „Tekst", „Skala” w pasku edytora — tekst wchodził pod strzałkę. Liczone
   z tokenów, więc zmiana rozmiaru strzałki nie rozjedzie tego ponownie. */
/* NAPRAWA 11.08.2026 — combobox nie mial strzalki. Zmierzone na zywej stronie:
   `background-image: none`, `padding-right: 9px`. Przyczyna to SPECYFICZNOSC:
   regula bazowa pol `input:not([type=checkbox]):not([type=radio])` ma (0,2,1),
   a `.combo-input` tylko (0,1,0) — baza wygrywala i nadpisywala padding-right
   z powrotem na 9px, wiec strzalka nie miala gdzie sie zmiescic.
   Powtarzamy tu wzorzec bazy, zeby wyjsc na (0,3,1) i wygrac uczciwie
   specyficznoscia, a nie `!important`.
   Miejsce na strzalke = odsuniecie (--s-3) + szerokosc (--s-4) + odstep od
   tekstu (--s-2) = 36 px. Strzalka ma 16x16 i siedzi 12 px od krawedzi, wiec
   NIE wchodzi na tekst i nie wypelnia pola. */
select,
input.combo-input:not([type="checkbox"]):not([type="radio"]) { appearance: none; padding-right: calc(var(--s-3) + var(--s-4) + var(--s-2)); background-image: var(--strzalka); background-repeat: no-repeat; background-position: right var(--s-3) center; background-size: var(--s-4) var(--s-4); }
/* E3 (27.09.2026): lista, której wybrana wartość się nie mieści, kończy się wielokropkiem zamiast cięcia w pół litery
   (Użytkownicy na telefonie: „wszystkie kon:”, „Kierownik Pro”). Pełną wartość widać po otwarciu listy. */
input.combo-input:not([type="checkbox"]):not([type="radio"]) { text-overflow: ellipsis; }
/* STRZAŁKI POLA LICZBOWEGO ZDJĘTE W CAŁEJ APLIKACJI.
   Przeglądarka dokłada do `input[type=number]` parę strzałek góra/dół. W polu
   szerokim na 36-70 px (filtry kolumn, pola stanu w tabelach) zajmują one
   większość szerokości i zasłaniają wpisaną liczbę — użytkownik nie widzi,
   co wpisał. Do tej pory zdejmowały je trzy osobne arkusze, każdy dla swojego
   okna; tu jest jedna reguła dla wszystkich pól liczbowych.
   Liczbę i tak wpisuje się z klawiatury, a strzałki krok po kroku są w tej
   aplikacji bezużyteczne (ceny, wagi, pojemności to nie liczniki). */
input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Checkbox i radio zostają NATYWNE — accent-color barwi je tokenem, a ptaszek,
   tryb ciemny i kontrast rysuje przeglądarka. Mniej kodu, więcej zgodności. */
input[type="checkbox"], input[type="radio"] { width: var(--s-4); height: var(--s-4); margin: 0; flex: none; accent-color: var(--accent-strong); cursor: pointer; }
/* --- POLE FORMULARZA --------------------------------------------------------
   Grid, nie flex: potomek domyślnie rozciąga się na całą szerokość, więc
   kontrolka wypełnia pole bez ani jednego width: 100%. */
/* Monolit (linia 1252): grid + gap 3px, BEZ `min-width: 0`. Nasze zero
   pozwalało polu kurczyć się poniżej treści — w pasku narzędzi wychodziło
   103 px zamiast 132 px. */
.field { display: grid; gap: 3px; }
/* Podpowiedź pod polem: format, jednostka, skutek zmiany. */
.field-hint { font-size: var(--t-sm); color: var(--text-muted); }
/* --- COMBO: pole tekstowe z listą podpowiedzi ------------------------------ */
.combo { position: relative; min-width: 0; }
/* ⛔⛔ POLE COMBO MUSI WYPEŁNIAĆ SWÓJ POJEMNIK — 18.08.2026, zgłoszenie właściciela:
   „dlaczego te dropdowny dalej są na sobie".
   ZMIERZONE: pole tekstowe combo nie miało NIGDZIE szerokości poza oknami
   (`.modal .combo-input { width: 100% }` działa tylko w oknach), więc w kartotece
   brało szerokość WRODZONĄ przeglądarki (~184–204 px, zależnie od czcionki)
   i ROZLEWAŁO SIĘ poza swój pojemnik: lista „Normy" (kolumna 124 px) wchodziła
   60 px w kolumnę pieniędzy, a pole funkcji w wierszu osoby nachodziło 36 px
   na cennik — 24 kolizje w samej kartotece Użytkowników.
   Wrapper `.combo` zawsze dostaje szerokość od otoczenia (kolumny, siatki,
   paska), więc pole w środku ma ją po prostu wypełniać — wszędzie, nie tylko
   w oknach. Reguła z okien zostaje (nie szkodzi), ale przestaje być jedyna. */
.combo-input { width: 100%; min-width: 0; }
/* Natywny <select> zostaje w DOM jako nośnik wartości dla formularza —
   widoczne są input i panel, on sam nie. */
.combo-native { display: none; }
/* MUSI zostać `fixed` i BEZ własnych top/left — skrypt combo wylicza pozycję
   listy względem OKNA PRZEGLĄDARKI i wpisuje ją stylem inline. Przy `absolute`
   te same liczby liczą się od pola (bo `.combo` jest `relative`) i lista odjeżdża
   o własną pozycję — u nas było 62 px w prawo i 213 px w dół. Tak samo w v8. */
/* Ujednolicenie 11.08.2026 — wyściółka panelu pływającego = --s-2 (była --s-1). */
.combo-panel { position: fixed; z-index: var(--z-lista); display: none; max-height: 264px; overflow-y: auto; padding: var(--s-2); border: 1px solid var(--line-strong); border-radius: var(--r); background: var(--surface); box-shadow: var(--cien-2); }
.combo.is-open .combo-panel { display: block; }
.combo-option { display: block; width: 100%; padding: var(--s-2) var(--s-3); border-radius: var(--r-sm); background: none; color: var(--text); text-align: left; cursor: pointer; }
/* Ten sam wygląd dla myszy i klawiatury — is-active nadaje skrypt strzałkami. */
/* NAPRAWA 11.08.2026 — opcja z klasa is-disabled (nadaje ja public/js/okna/,
   styluje ją `.is-disabled` w 06-elementy.css) reagowala na kursor jak klikalna. */
.combo-option:not(.is-disabled):hover, .combo-option.is-active:not(.is-disabled) { background: var(--surface-3); }
.combo-empty { padding: var(--s-2) var(--s-3); color: var(--text-muted); }
/* Flaga kraju przy numerze kierunkowym — dwa miejsca, dwie płaskie reguły.
   Skrypt combo podaje wyłącznie `src` obrazka; wymiary i położenie są tutaj. */
.combo-flaga { width: var(--flaga-w); height: var(--flaga-h); margin-right: var(--s-2); vertical-align: text-bottom; }
/* Panel podpowiedzi adresu. Wyglada i zachowuje sie jak lista combo, ale nie
   siedzi w `.combo`, wiec pokazanie go wlacza wlasna klasa. Natywny <datalist>
   nie nadawal sie tutaj: jego liste rysuje przegladarka i w oknie projektu
   stawiala ja kilkaset pikseli obok pola. */
.adres-panel.is-open { display: block; }
/* Wskaźnik pobierania map w nagłówku. Pokazuje się tylko na czas pobierania,
   więc nie zajmuje miejsca na stałe — `hidden` wystarcza za całe sterowanie. */
.maps-download-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  height: var(--h-control-sm);
  padding: 0 var(--s-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: var(--t-sm);
  cursor: pointer;
}
.maps-download-badge:hover { background: var(--surface-3); color: var(--text); }
.maps-download-badge[hidden] { display: none; }
/* G4: ten sam znacznik dla zapisów bez internetu — czerwony napis, gdy serwer odrzucił zapis z kolejki. */
.maps-download-badge.is-blad { border-color: var(--danger); color: var(--danger); }
.maps-download-badge { white-space: nowrap; }
.kolejka-offline-krotko { display: none; }

/* Pasek pobierania map populacji. Nie <progress>, bo ten element w każdej
   przeglądarce wygląda inaczej i nie przyjmuje tokenów motywu. */
.population-maps-progress {
  overflow: hidden;
  height: var(--s-2);
  margin: var(--s-2) 0;
  border-radius: var(--r-pill);
  background: var(--surface-3);
}
.population-maps-progress span {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--accent);
  transition: width .3s ease;
}
.combo-flaga-pole { position: absolute; top: 50%; left: var(--s-2); width: var(--flaga-w); height: var(--flaga-h); transform: translateY(-50%); pointer-events: none; }
/* Tekst odsuwamy `text-indent`, a NIE `padding-left`: padding pola jest już
   ustawiony przez `.modal input:not(...)` o wyższej specyficzności, więc
   dokładanie tam trzeciej reguły byłoby licytacją selektorów. Wcięcia tekstu
   nie ustawia nic innego w całej aplikacji — jedna reguła, zero konfliktu. */
.combo:has(.combo-flaga-pole) .combo-input { text-indent: calc(var(--flaga-w) + var(--s-2)); }

/* ============================================================================
   TELEFON — POLA MUSZĄ MIEĆ 16 px (12.09.2026, audyt 12.09)
   ----------------------------------------------------------------------------
   Safari na iPhonie SAM POWIĘKSZA CAŁĄ STRONĘ, gdy dotkniesz pola o czcionce
   mniejszej niż 16 px. Pola brały `--t-base` (13 px), więc każde wejście w pole
   przeskalowywało widok i trzeba było go ręcznie cofać — przy wypełnianiu
   formularza projektu kilkanaście razy pod rząd.

   Nie ruszamy tokenu `--t-base`: to bazowy rozmiar tekstu CAŁEJ aplikacji i jego
   zmiana przeskalowałaby wszystko. Podnosimy tylko same kontrolki i tylko na
   telefonie. Na tej szerokości większe pole jest zresztą wygodniejsze do dotknięcia.

   16 px to nie estetyka, tylko PRÓG SAFARI — poniżej włącza się powiększanie.
   `--h-control` zostaje bez zmian; 16 px mieści się w 32 px wysokości kontrolki.
   Telefon = :root[data-ui="mobile"] (≤ 820 px albo telefon poziomo — tabela PROGI w 00-przed-malowaniem.js).
   ============================================================================ */
:where(:root[data-ui="mobile"]) input:not([type="checkbox"]):not([type="radio"]),
:where(:root[data-ui="mobile"]) select,
:where(:root[data-ui="mobile"]) textarea {
  font-size: var(--t-md);
}

/* ============================================================================
   TABLET (DOTYK) — CEL DLA PALCA CO NAJMNIEJ 40 px (tura 5, ergonomia 26.09.2026)
   ----------------------------------------------------------------------------
   Tablet w poziomie dostawał wymiary komputera (przyciski 27–32 px). Wysokości
   kontrolek przychodzą teraz z tokenów (00-tokens.css, blok „DOTYK": przycisk 44,
   w wierszu tabeli 40). Ta jedna reguła łapie resztę — przyciski i pola, którym
   widok wpisał własny wymiar (strzałki stron, „Brak etapów ⌄", wiersze lejka na
   Pulpicie, „Dodatkowe kontakty").
   Komputer z myszą (pointer: fine) i telefon (data-ui="mobile") nie widzą tej reguły.
   ============================================================================ */
:root[data-ui="tablet"] :is(button, summary, select, [role="button"], input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"])) {
  min-width: var(--h-control-sm);
  min-height: var(--h-control-sm);
}
/* Zakładki i przełączniki segmentowe na tablecie: wysokość zakładki (--h-tab, 44 px jak na telefonie), a nie 40 px z reguły
   wyżej — Harmonogram (zakładki etapów), Zadania (Moje / Zlecone / Gotowe), Zamówienia, Użytkownicy (tura 3 E1, 27.09).
   `height`, nie `min-height`: reguła wyżej ma wyższą szczegółowość (pola z :not(...)) i jej 40 px wygrywało. */
:root[data-ui="tablet"] :is(.segmented > button, .timeline-tab, .tab, .dash-tab, .zn-sub .views > button, .hg-przelacznik .views > button, .ords2-rail-views > button) { height: var(--h-tab); }
/* === POLE WYMAGANE, NIEWYPEŁNIONE ========================================== (z dawnego 27, K16)
   Udokumentowana funkcja: przy zapisie JS dokłada klasę WPROST NA KONTROLKĘ.
   Czerwona zostaje ramka (cienka, nie outline — gruba linia przesuwa treść),
   a .field-alarm dorzuca trzy mrygnięcia poświaty i gaśnie. JS nie robi focusu,
   żeby :focus nie zjadł migania. Fokus na czerwonym polu = czerwona poświata,
   nie niebieska — inaczej pole „naprawia się" samym kliknięciem.
   `:not([type=…])` NIE jest ozdobą: bazowa reguła pól w 03-kontrolki ma dokładnie
   taki kształt, więc bez tego samego pancerza czerwień przegrywa specyficzność
   z domyślną ramką. To jedyny sposób wygrać bez wymuszania priorytetu. */
input.field-required-missing:not([type="checkbox"]):not([type="radio"]), select.field-required-missing, textarea.field-required-missing,
input.pl-data-natywne.field-required-missing + .pl-data-pole { border-color: var(--danger); }
/* W1: w oknie pole wymagane ma czerwoną ramkę i czerwony obrys 1 px — tak samo na komputerze i telefonie. */
input.field-required-missing:not([type="checkbox"]):not([type="radio"]), select.field-required-missing, textarea.field-required-missing { box-shadow: var(--okno-pole-brak-obrys, none); }
/* Polskie pole daty (15-zadania.css): czerwień i miganie pokazuje widoczny przycisk, nie schowane pole. */
input.pl-data-natywne.field-required-missing.field-alarm + .pl-data-pole { animation: pole-wymagane 0.45s ease-in-out 3; }
input.field-required-missing:not([type="checkbox"]):not([type="radio"]):focus, select.field-required-missing:focus, textarea.field-required-missing:focus { border-color: var(--danger); box-shadow: var(--okno-pole-brak-fokus, 0 0 0 var(--s-1) color-mix(in srgb, var(--danger) 22%, transparent)); }
.field-required-missing.field-alarm { animation: pole-wymagane 0.45s ease-in-out 3; }
/* Jedna iteracja = jeden pełny błysk (off→on→off), więc 3 iteracje = 3 mrygnięcia
   i koniec na „off": zostaje sama czerwona ramka. */
@keyframes pole-wymagane {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 0%, transparent); }
  50% { box-shadow: 0 0 var(--s-2) 2px color-mix(in srgb, var(--danger) 65%, transparent); }
}
/* Tu też NIE ma bloku `prefers-reduced-motion` — z tego samego powodu co
   w 01-reset: użytkownik ma to ustawienie włączone w Windows, a miganie pola
   wymaganego jest SYGNAŁEM, o który prosił, nie ozdobą do wyciszenia. */
/* === WARIANTY PRZYCISKÓW, TONY I WGRYWANIE PLIKÓW =========================== (z dawnego 27, K16)
   Trzy klasy, których JS używa zamiast .button — ta sama geometria, żeby
   w jednym pasku nie stały guziki różnej wysokości. .upload-button to <label>
   z ukrytym <input type=file> rozciągniętym na całą powierzchnię: klikalne
   jest CAŁE pudełko, a natywny „Wybierz plik" nigdy się nie pokazuje. */
/* Wcięcie 0 9px jak w bazowym przycisku monolitu (linia 1267) — nasze 12 px
   robiło każdy przycisk paska górnego o 7 px szerszym niż w v8 (Pomoc, Dark,
   PL — razem ~22 px na całym pasku). */
.ghost-button, .upload-button, .theme-toggle { display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2); height: var(--h-control); padding: 0 9px; white-space: nowrap; cursor: pointer; border: 1px solid transparent; border-radius: var(--okno-przycisk-r, var(--r)); background: var(--btn-2); color: var(--text); font-size: var(--t-base); font-weight: var(--w-bold); text-decoration: none; transition: background var(--szybko), border-color var(--szybko), color var(--szybko); }
.ghost-button:hover, .upload-button:hover, .theme-toggle:hover { background: var(--btn-2-hover); text-decoration: none; }
.ghost-button:active, .upload-button:active, .theme-toggle:active { transform: scale(.97); }
/* Ton niesie znaczenie akcji, wygląd liczy się z tonu — nowy kolor to linijka. */
/* NAPRAWA 11.08.2026 — TON DZIAŁA NA WSZYSTKICH PRZYCISKACH, NIE TYLKO NA `.ghost-button`.
   `public/js/ui/tony-przyciskow.js` przechodzi po WSZYSTKICH przyciskach w dokumencie
   (`querySelectorAll("button, a.button, a.primary-button, a.ghost-button,
   a.danger-button")`) i stempluje im `data-btn-tone` na podstawie tego, co robią.
   CSS czytał ten atrybut wyłącznie na `.ghost-button` — czyli na jednym
   wariancie z czterech. Skutek zgłoszony przez użytkownika: „Zapisz" w oknie
   kalkulacji ma `data-btn-tone="green"` i mimo to jest BIAŁY, bo żadna reguła
   go nie łapie. To samo dotyczyło „Anuluj" z `data-btn-tone="red"`.
   Teraz atrybut obowiązuje na każdym przycisku, więc system tonów wreszcie
   robi to, co miał robić od początku.

   MIEJSCE TONÓW W SYSTEMIE KOLORÓW (całość opisana w 03-kontrolki.css):
     niebieski `.button.primary` — GŁÓWNA akcja okna, jedna na okno
     zielony `[data-btn-tone="green"]` — pozostałe akcje zapisujące
     czerwony `[data-btn-tone="red"]` i `.danger` — kasuje dane
     pomarańczowy `[data-action="close-modal"]` — wyjście bez zapisu
   Napis liczony jako `--ton` zmieszany z `--text` w 70% — sam ton na własnym
   12-procentowym tle daje kontrast poniżej progu czytelności (ta sama zasada
   co w pigułkach statusu, 05-komponenty.css). */
/* ⚠️ 13.09.2026 — ZIELEŃ WYCOFANA (decyzja właściciela: jeden system przycisków, wzorzec telefonu).
   Ton „green" (zapis/dodanie) daje teraz AKCJĘ GŁÓWNĄ — pełny niebieski — ale tylko tam, gdzie akcja
   główna ma sens: w stopce okna i w potwierdzeniu. Poza stopką („+ Wiersz", „Dodaj etap" w liście)
   zielony ton wygląda jak zwykły szary przycisk, żeby na ekranie nie było kilku „głównych" akcji.
   Ton „red" (usuwanie) = czerwony napis na jasnoczerwonym tle. Komentarze niżej opisują stary system. */
[data-btn-tone="red"] { --ton: var(--danger); }
.button[data-btn-tone="red"], .ghost-button[data-btn-tone="red"], .upload-button[data-btn-tone="red"],
.primary-button[data-btn-tone="red"], .danger-button[data-btn-tone="red"] { border-color: transparent; background: var(--danger-soft); color: var(--danger); }
.button[data-btn-tone="red"]:hover, .ghost-button[data-btn-tone="red"]:hover, .upload-button[data-btn-tone="red"]:hover,
.primary-button[data-btn-tone="red"]:hover, .danger-button[data-btn-tone="red"]:hover { border-color: transparent; background: color-mix(in srgb, var(--danger) 16%, var(--surface)); }
.icon-button[data-btn-tone="red"] { background: var(--btn-2); color: var(--danger); }
.modal > footer [data-btn-tone="green"]:is(.button, .ghost-button, .primary-button),
.app-confirm-actions [data-btn-tone="green"]:is(.button, .ghost-button, .primary-button) { border-color: transparent; background: var(--accent); color: var(--on-accent); }
.modal > footer [data-btn-tone="green"]:is(.button, .ghost-button, .primary-button):hover,
.app-confirm-actions [data-btn-tone="green"]:is(.button, .ghost-button, .primary-button):hover { background: var(--accent-hover); }
/* Stopka kalkulatora ma własne pomocnicze zapisy (profil kosztów, „Zapisz wersję") — zostają szare.
   Niebieskie są tylko dwie akcje oznaczone w markupie jako główne: „Zapisz" i „Generuj prezentację" (decyzja 12.08.2026). */
.modal > footer.project-cost-footer [data-btn-tone="green"]:not(.primary, .presentation-copy-link-button):is(.button, .ghost-button, .primary-button) { background: var(--btn-2); color: var(--text); }
.modal > footer.project-cost-footer [data-btn-tone="green"]:not(.primary, .presentation-copy-link-button):is(.button, .ghost-button, .primary-button):hover { background: var(--btn-2-hover); }
/* Okno potwierdzenia usunięcia: „Usuń" to jedyna akcja główna tego okna, więc jest pełny czerwony
   (jasnoczerwony obok szarego „Anuluj" wyglądał na słabszy od anulowania). Poza tym oknem usuwanie zostaje jasnoczerwone. */
.app-confirm-actions [data-confirm-ok]:is([data-btn-tone="red"], .danger-button, .button.danger) { border-color: transparent; background: var(--danger); color: var(--on-accent); }
.app-confirm-actions [data-confirm-ok]:is([data-btn-tone="red"], .danger-button, .button.danger):hover { background: color-mix(in srgb, var(--danger) 85%, var(--text)); }
/* Główna akcja okna wygrywa z tonem — `.button.primary` zostaje niebieski
   nawet jeśli skrypt nada mu ton. Jedna akcja główna na okno. */
.button.primary[data-btn-tone]:not([data-btn-tone="red"]) { border-color: transparent; background: var(--accent); color: var(--on-accent); }
/* …Z JEDNYM WYJĄTKIEM: ZAPIS JEST ZAWSZE ZIELONY (11.08.2026).
   ZGŁOSZENIE: „przyciski zapisz mają być zielone, bo teraz w Edytuj projekt jest
   niebieski". ZMIERZONE: „Zapisz projekt" MIAŁ już poprawny ton zielony
   (`data-btn-tone="green"`), ale reguła linijkę wyżej przemalowywała go na
   niebiesko — tło rgb(234,240,254), napis rgb(47,89,214).
   Zasada „jedna akcja główna = niebieska" jest tu podporządkowana ważniejszej:
   KOLOR MA MÓWIĆ, CO PRZYCISK ROBI. Zielony = zapisuje. Jeśli akcją główną okna
   jest zapis, to zielony wygrywa — inaczej ten sam „Zapisz" byłby zielony
   w jednym oknie, a niebieski w drugim, zależnie od tego, czy ktoś nadał mu
   klasę akcji głównej.
   Ta sama szczegółowość co reguła wyżej (0,3,0), więc decyduje kolejność —
   dlatego stoi bezpośrednio po niej, a nie gdzieś dalej w pliku. */
.button.primary[data-btn-tone="green"]:hover { background: var(--accent-hover); }
/* TA SAMA ZASADA DLA POMARAŃCZOWEGO — dopisane 11.08.2026.
   ZMIERZONY DEFEKT (szkicownik, na żywej stronie): zaraz po otwarciu okna
   „Anuluj" miał tło rgb(255,243,207), czyli pomarańczowe zgodnie z systemem.
   Po pierwszym przebiegu `public/js/ui/tony-przyciskow.js` dostawał `data-btn-tone="red"`
   i robił się CZERWONY — skrypt klasyfikuje po SŁOWIE („anuluj" łapie się
   w RED_TEXT), a nie po tym, co przycisk faktycznie robi. Ten sam guzik miał
   więc dwa kolory, zależnie od tego, czy skrypt zdążył przelecieć po renderze.
   To łamie ustaloną kolorystykę (03-kontrolki.css:66): pomarańczowy = WYJŚCIE
   BEZ ZAPISU, czerwony = KASUJE DANE. „Anuluj" w szkicowniku niczego nie
   kasuje — zamyka okno, więc ma być pomarańczowy.
   Zasada identyczna jak linijkę wyżej: jeśli ktoś nadał przyciskowi JAWNĄ
   klasę koloru, to była decyzja, a nie przypadek — automat jej nie nadpisuje.
   ZAKRES: wyłącznie przyciski z klasą `.warn`. Zwykłe „Anuluj" bez tej klasy
   zachowują się dokładnie jak dotąd — ta zmiana nie przemalowuje okien
   w reszcie aplikacji. */
.button.warn[data-btn-tone] { border-color: transparent; background: var(--btn-2); color: var(--text); }
.button.warn[data-btn-tone]:hover { background: var(--btn-2-hover); }
/* WYŁĄCZONY PRZYCISK GŁÓWNY WYGLĄDA JAK KAŻDY WYŁĄCZONY (przegląd decyzji v14, 26.09). „Dodaj etap” bez wolnych
   etapów albo bez prawa zostawał pełnym akcentem i tylko bladł (opacity 0,5) — wyglądał jak jasnoniebieski przycisk
   do kliknięcia, a obok wyłączone „Przelicz wszystkie projekty” i kosz są szare. Teraz szare tło przycisku
   zwykłego + przygaszenie z `.button:disabled` (03-kontrolki). Stoi po regułach tonu, bo ma tę samą wagę co
   `.button.primary[data-btn-tone]` wyżej — także najechanie kursorem nie zmienia koloru. */
.button.primary:disabled, .button.primary[data-btn-tone]:disabled, .primary-button:disabled { border-color: transparent; background: var(--btn-2); color: var(--text); box-shadow: none; }
.upload-button { position: relative; overflow: hidden; }
.upload-button input:not([type="checkbox"]):not([type="radio"]) { position: absolute; inset: 0; width: 100%; height: 100%; padding: 0; opacity: 0; cursor: inherit; }
.theme-toggle.active { border-color: var(--accent-strong); background: var(--accent-soft); color: var(--accent-strong); }
/* Pasek „przycisk + nazwa pliku": guzik ma stałą szerokość, nazwa bierze resztę. */
.upload-strip { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--s-3); }
.pdf-upload-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
/* Wersje mini — wiersz tabeli i komórka BOM-u. `:not([hidden])` jest konieczne:
   bez niego display bije [hidden] z 01-reset i schowany „Usuń" byłby widoczny. */
.upload-button.mini-upload, .mini-remove { height: var(--h-control-sm); padding: 0 var(--s-2); font-size: var(--t-xs); }
.mini-remove:not([hidden]) { display: inline-flex; align-items: center; cursor: pointer; border: 1px solid color-mix(in srgb, var(--danger) 40%, var(--line)); border-radius: var(--r-sm); background: var(--danger-soft); color: var(--danger); font-weight: var(--w-bold); }
.mini-remove:hover { background: color-mix(in srgb, var(--danger) 18%, var(--surface)); }
/* === LISTA WYSZUKIWALNA (podmiana natywnego <select>) ====================== (z dawnego 27, K16)
   Panel jest position:fixed i pozycjonowany przez JS przy kontrolce, więc
   nie wypada poza okno modala ani nie tnie go overflow rodzica. */
.searchable-select-popover { position: fixed; z-index: var(--z-dymek); display: grid; grid-template-rows: auto minmax(0, 1fr); gap: var(--s-1); min-width: 0; max-width: min(560px, calc(100vw / var(--skala-ui) - var(--s-4))); max-height: min(360px, calc(100dvh / var(--skala-ui) - var(--s-5))); background: none; }
.searchable-select-filter { width: 100%; box-shadow: 0 0 0 var(--s-1) color-mix(in srgb, var(--accent) 18%, transparent); }
/* Ujednolicenie 11.08.2026 — gap 3px → --s-1 (4 px); pozycje listy rozwijanej
   to ta sama rola co w `.plan-assignee-list` (25) i `.user-menu` (06). */
.searchable-select-list { display: grid; gap: var(--s-1); min-height: 0; overflow: auto; padding: var(--s-2); border: 1px solid var(--line-strong); border-radius: var(--r); background: var(--surface); box-shadow: var(--cien-2); }
.searchable-select-option, .searchable-select-empty { min-width: 0; min-height: var(--h-control-sm); padding: var(--s-1) var(--s-2); border: 1px solid transparent; border-radius: var(--r-sm); color: var(--text); text-align: left; }
.searchable-select-option { cursor: pointer; }
.searchable-select-option:hover, .searchable-select-option.is-active { border-color: color-mix(in srgb, var(--accent) 35%, transparent); background: var(--surface-3); }
.searchable-select-option.is-selected { border-color: var(--accent-strong); background: var(--accent-soft); color: var(--accent-strong); font-weight: var(--w-bold); }
.searchable-select-empty { color: var(--text-muted); text-align: center; }
/* 14.09.2026 — JEDNA AKCJA GŁÓWNA NA EKRAN (system przycisków, telefon i komputer).
   Przyciski powtarzane w wierszach listy („Zamów" przy każdej firmie) i „dodaj kolejny element" pod listami
   („Nowe zadanie", „Nowy temat", „Nowa notatka") są akcjami wiersza/listy, nie akcją główną ekranu:
   jasnoniebieskie tło z niebieskim napisem. Pełny niebieski zostaje dla zapisu formularza obok (np. „Zleć zadanie").
   `:is([data-btn-tone], :not([data-btn-tone]))` podnosi wagę ponad regułę tonu `.button.primary[data-btn-tone]` wyżej. */
:is(.ords2-firm-act, .zn-tree-add, .notes2-add) .button.primary:is([data-btn-tone], :not([data-btn-tone])):not([data-btn-tone="red"]),
.note-panel-body > .note-new-btn.button.primary:is([data-btn-tone], :not([data-btn-tone])):not([data-btn-tone="red"]) { border-color: transparent; background: var(--accent-soft); color: var(--accent-strong); }
:is(.ords2-firm-act, .zn-tree-add, .notes2-add) .button.primary:is([data-btn-tone], :not([data-btn-tone])):not([data-btn-tone="red"]):hover,
.note-panel-body > .note-new-btn.button.primary:is([data-btn-tone], :not([data-btn-tone])):not([data-btn-tone="red"]):hover { background: color-mix(in srgb, var(--accent) 18%, var(--surface)); }/* Okno Design: w stopce obok „Zapisz zmiany" stał drugi pełny niebieski „Generuj prezentację". Zapis to akcja główna okna;
   prezentacja jest akcją dodatkową — jasnoniebieska (telefon i komputer, 14.09.2026). W kalkulatorze bez zmian (decyzja 12.08). */
/* POPR2-x02 (28.09.2026): jedna zasada stopki okien projektu (Design, Oferta, Koszty) — główny jest zapis, a „Generuj prezentację”
   we wszystkich trzech to ten sam drugi, jasnoniebieski przycisk (bez klasy primary; w Kosztach było pełne niebieskie). */
.modal > footer :is(.design-footer-presentation-button, .project-cost-presentation-button).button { border-color: transparent; background: var(--accent-soft); color: var(--accent-strong); }
.modal > footer :is(.design-footer-presentation-button, .project-cost-presentation-button).button:hover { background: color-mix(in srgb, var(--accent) 18%, var(--surface)); }
/* POPR-n22: „Kopiuj link” (zielony ton = „prezentacja gotowa”) w stopkach Design i Oferty był drugim pełnym niebieskim obok akcji
   głównej — jest akcją dodatkową, jasnoniebieską jak „Generuj prezentację” w Designie. POPR2-x02: w Kosztach tak samo (wyjęty z szarej reguły stopki kosztów). */
.modal > footer .presentation-copy-link-button[data-btn-tone="green"].button { border-color: transparent; background: var(--accent-soft); color: var(--accent-strong); }
/* --- Przycisk trzeciorzędny i grupa listy combo (z dawnego 28, K17) --- */
/* Przycisk trzeciorzędny: sam obrys, bez tła. */
.button.ghost { border-color: var(--line-strong); background: transparent; color: var(--text-muted); }
.button.ghost:hover { background: var(--surface-2); color: var(--text); }
/* Ujednolicenie 11.08.2026 — podpis nagłówkowy do kanonu (--t-2xs / --w-heavy /
   .04em). Było 11 px / 600 / .05em — NAJRZADSZE rozstrzelenie w aplikacji,
   przez co nagłówek grupy w liście rozwijanej był wyraźnie luźniejszy od
   pozostałych podpisów. Wersaliki zostają: to nagłówek sekcji `<optgroup>`,
   czyli podpis grupy, a nie wartość do wyboru. */
.combo-group { padding: var(--s-2) var(--s-3) 2px; color: var(--text-muted); font-size: var(--t-2xs); font-weight: var(--w-heavy); }
