:root {
            /* NOWOŚĆ (Przebudowa wizualna - Material 3 Expressive, styl Android 16): dynamiczna, bogatsza paleta
               w duchu Material You zamiast poprzedniej, płaskiej palety iOS - indygo #4355D8 jako główny akcent,
               "kontenerowe" tony (--*-container / --on-*-container) do delikatnie podbarwionych teł (plakietki
               statusu, kafelki), tak jak role kolorów w Material 3. Nazwy dotychczasowych zmiennych zostają
               niezmienione (setki miejsc w kodzie ich używa) - zmieniają się tylko wartości + dochodzą nowe. */
            /* NAPRAWA (zgłoszone: "w trybie jasnym za bardzo jest biało") - tło strony i tło kart/kontenerów
               (biały) były niemal identyczne (#FAF8FD vs #ffffff, różnica ledwo zauważalna), więc karty nie
               odcinały się od tła i cały ekran wyglądał jak jednolita, płaska biel bez żadnej głębi. W trybie
               ciemnym analogiczna różnica jest wyraźna (#121018 vs #1E1B24) - teraz light ma tę samą, widoczną
               różnicę tonu między tłem strony a białymi kartami, zamiast prawie zerowej. -->
            /* NAPRAWA (zgłoszone: "giną ramki") - poprzedni --border-color (#E3DEEF) był dobrany pod STARE,
               prawie białe tło strony i miał z nim ledwo widoczny kontrast; po jego pociemnieniu (wyżej) ramka
               zlewała się TERAZ z tłem strony (i dodatkowo z kolorową tapetą, gdy jest aktywna). Kolor obramowań
               dobrany pod kątem widoczności na każdym jasnym tle, nie tylko na jednym konkretnym odcieniu. */
            /* NAPRAWA (zgłoszone: "tryb jasny ma być bardziej szary") - poprzednie --bg-color/--border-color
               miały wyraźny fioletowo-lawendowy odcień (niebieski kanał najwyższy w obu: #EDEAF5, #CAC4D0) -
               teraz zneutralizowane w stronę prawdziwej szarości (kanały R/G/B niemal równe), przy zachowaniu
               tej samej jasności/kontrastu co poprzednio (tylko odcień koloru się zmienia, nie głębia). */
            --bg-color: #E9E9EC; --container-bg: #ffffff; --text-color: #1C1B1F; --border-color: #84848C;
            --primary-color: #4355D8; --primary-hover: #2F3FBE; --success-color: #1E9E4C; --logistics-color: #E8792A; --danger-color: #BA1B1B;
            --gps-color: #1D7E96;

            --primary-container: #E1E0FF; --on-primary-container: #00105C;
            --success-container: #B7F2C6; --on-success-container: #00210B;
            --logistics-container: #FFDCB8; --on-logistics-container: #341100;
            --danger-container: #FFDAD4; --on-danger-container: #410001;

            /* NAPRAWA (audyt QA/UX 2026-09-27, kontrast WCAG AA): tekst na --success-color/--gps-color był
               zawsze białym "color:white" wpisanym wprost w znacznikach - w trybie ciemnym te zmienne są
               ŚWIADOMIE jasne (rola M3 "light tone" w dark theme), więc biały tekst na nich staje się
               nieczytelny (np. ~1,7:1). --on-success-color to jedna, stała wartość działająca w OBU trybach
               (sprawdzone: >=4,9:1 na obu odcieniach --success-color) - nie trzeba jej nadpisywać w dark theme
               jak innych zmiennych. --on-gps-color musi się różnić między trybami, bo --gps-color w dark
               theme jest jasny (tekst ciemny), a w light theme ciemniejszy (tekst biały) - patrz blok
               [data-theme="dark"] niżej. */
            --on-success-color: #00210B;
            --on-gps-color: #ffffff;

            /* Promienie i cień - "mocno zaokrąglone kształty" Material 3 Expressive: karty duże, pola średnie,
               przyciski/plakietki pigułkowe (999px = zawsze pełne półkole niezależnie od wysokości elementu). */
            --radius-card: 24px; --radius-field: 18px; --radius-sm: 16px; --radius-pill: 999px;
            --shadow-card: 0 6px 18px -10px rgba(60,50,90,0.28);
        }

        /* Delikatna reakcja wizualna na naciśnięcie przycisku - czysto kosmetyczne, bez żadnych zależności zewnętrznych */
        .btn { transition: filter 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease; }
        .btn:active { filter: brightness(0.92); transform: scale(0.98); box-shadow: 0 1px 1px rgba(0,0,0,0.1); }
        .tfoot-sum td { font-weight: bold; }

        /* ===================== PEŁNOEKRANOWY MODUŁ PODPISU - odporny na obrót ekranu ===================== */
        #signature-fullscreen-modal { position: fixed; inset: 0; z-index: 3000; }
        #signature-fullscreen-modal * { pointer-events: auto; }
        /* NAPRAWA (mikroskopijny podpis na dużych tabletach): sam "aspect-ratio" (ustawiany dynamicznie w JS,
           dopasowany do proporcji pola w szablonie) nie wystarczał - na 13-calowym tablecie canvas mimo
           zachowanych proporcji nadal potrafił rozciągnąć się na całą, ogromną szerokość ekranu, zachęcając do
           rysowania podpisu na wielkiej fizycznej przestrzeni. Bezwzględny górny limit w pikselach (nie w %)
           gwarantuje rozsądny, kompaktowy rozmiar niezależnie od wielkości ekranu - na telefonie i tak zmieści
           się w dostępnym miejscu (min() z 100%), na tablecie nie urośnie ponad ustalony pułap. */
        #signature-fullscreen-canvas { touch-action: none; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; max-width: min(520px, 100%); max-height: min(340px, 100%); }
        /* Zlecenie po terminie - pastelowe podświetlenie, żeby rzucało się w oczy bez agresywnego czerwonego.
           !important konieczne, bo bazowa klasa .card (zdefiniowana niżej w pliku) ma własne tło o tej samej
           specyficzności CSS - bez tego zostałoby ono nadpisane i podświetlenie by nie zadziałało. */
        .plan-overdue { background-color: var(--danger-container) !important; }
        button:disabled { opacity: 0.35; cursor: default !important; }

        [data-theme="dark"] {
            /* NOWOŚĆ (Material 3 Expressive): prawie-czerń z lekkim fioletowym podbarwieniem (zamiast czystej
               #000000) - tak wygląda powierzchnia "surface" w ciemnym motywie Material 3, wciąż bardzo ciemna
               (oszczędność baterii na OLED zostaje), ale ton koloru spójny z resztą palety zamiast neutralnej czerni. */
            --bg-color: #121018; --container-bg: #1E1B24; --text-color: #E7E0EC; --border-color: #6E677A;
            --primary-color: #C2C3FF; --primary-hover: #A8A9FF; --success-color: #7FDB98; --logistics-color: #FFB878; --danger-color: #FFB4A9;
            --gps-color: #7FD4E8;

            --primary-container: #2C3591; --on-primary-container: #E1E0FF;
            --success-container: #123B22; --on-success-container: #B7F2C6;
            --logistics-container: #5C3A0E; --on-logistics-container: #FFDCB8;
            --danger-container: #93000A; --on-danger-container: #FFDAD4;

            --on-gps-color: #00210B;
        }
        * { box-sizing: border-box; font-family: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
        body { background-color: var(--bg-color); color: var(--text-color); margin: 0; padding: 0; transition: background-color 0.3s, color 0.3s; zoom: var(--font-scale, 1); }

        /* Stały pasek statusu na górze każdego widoku (online/offline + synchronizacja) */
        /* NAPRAWA: poprzednie dwa podejścia (margin:auto, potem CSS Grid) z niejasnej przyczyny nie dawały
           efektu u użytkownika mimo poprawnej, wielokrotnie zweryfikowanej składni. Wracamy do najbardziej
           podstawowej, praktycznie niemożliwej do zepsucia techniki: prawa grupa ikon jest przypięta bezwzględną
           pozycją do prawej krawędzi paska, niezależnie od tego, co dzieje się z resztą zawartości. */
        /* NAPRAWA (kwota zarobku nachodziła na ikony statusu): prawa część paska była pozycjonowana
           "position:absolute", więc rosnący tekst po lewej (np. "Zarobek: 1234.56 zł") nie miał jak
           "wiedzieć", że ma się zatrzymać przed nią, i po prostu wchodził pod spód. Teraz to prawdziwy układ
           flex - lewa część kurczy się i obcina tekst wielokropkiem, gdy brakuje miejsca, prawa zawsze w
           pełni widoczna. */
        #app-status-bar { position: sticky; top: 0; z-index: 1000; background: var(--container-bg); border-bottom: 1px solid var(--border-color); padding: 6px 15px; font-size: 12px; min-height: 20px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
        .status-bar-left { display: inline-flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; flex: 1 1 auto; }
        .status-bar-right { flex: 0 0 auto; display: flex; align-items: center; }
        #status-bar-summary { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }
        .conn-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--success-color); box-shadow: 0 0 5px var(--success-color); flex-shrink: 0; transition: background-color 0.3s; }
        .conn-dot.offline { background: var(--danger-color); box-shadow: 0 0 5px var(--danger-color); }
        #conn-label { font-weight: bold; }
        #sync-indicator { cursor: default; }

        .view-section { display: none; padding: 15px; padding-bottom: 30px; max-width: 100%; margin: 0 auto; overflow-x: hidden; }

        /* NOWOŚĆ (na życzenie użytkownika - "na komputerze strona ma być jak dla komputera"): dotąd strona nie
           miała ŻADNEGO ograniczenia szerokości powyżej rozmiaru telefonu - formularze i karty rozciągały się
           na całą szerokość ekranu, dając bardzo długie, niewygodne do czytania linie na monitorze. Jeden,
           wspólny próg szerokości dla WSZYSTKICH widoków - prostsze i bezpieczniejsze niż osobne dostrajanie
           każdej z ~12 zakładek, a i tak daje każdej z nich wyraźnie "komputerowy", wyśrodkowany wygląd.
           Tabela Raportów (jedyny naprawdę szeroki widok) i tak ma własne przewijanie w poziomie (.table-responsive).
           NAPRAWA (na życzenie użytkownika - "szerokość tabeli w raportach ma się dopasowywać do szerokości
           strony na komputerze"): ten wspólny, wąski (880/1040px) kontener okazał się jednak za ciasny właśnie
           dla tej jednej, naprawdę szerokiej tabeli - widok Raportów dostaje więc WŁASNY, dużo szerszy limit
           (patrz #view-reports niżej), żeby tabela realnie rozciągała się wraz z oknem przeglądarki, zamiast
           być ściśnięta w tym samym wąskim kontenerze co formularze. */
        /* NAPRAWA (na życzenie użytkownika - "przyciski export rozliczenie sprawdz ai i zaległy wpis nie
           mieszczą się... jak nie będzie się mieścić ma wyświetlać same ikony a na ekranie komputera ma być z
           napisami"): domyślnie (telefon, wąski ekran) same ikony - teksty przycisków w .btn-label wracają od
           tego samego progu szerokości, od którego reszta aplikacji przełącza się na układ "komputerowy". */
        .reports-header-actions .btn-label { display: none; }
        @media (min-width: 860px) {
            .view-section { max-width: 880px; padding: 28px 24px 40px; }
            .login-wrapper { padding: 40px; }
            .modal-content { padding: 28px; }
            .reports-header-actions .btn-label { display: inline; }
            /* Raporty: kontener rozciąga się razem z oknem (do rozsądnego górnego limitu), zamiast trzymać się
               tej samej wąskiej szerokości co formularze - patrz komentarz przy .view-section wyżej. */
            #view-reports.view-section { max-width: 1600px; }
        }
        @media (min-width: 1200px) {
            .view-section { max-width: 1040px; }
        }
        .view-section.active { display: block; }

        /* NOWOŚĆ (Przebudowa wizualna - Krok B): siatka ikon ekranu głównego, jak springboard iPhone'a */
        #view-home { position: relative; }
        #home-wallpaper-layer { position: fixed; top:0; left:0; right:0; bottom:0; z-index:-1; background-size: cover; background-position: center; display: none; }
        /* NAPRAWA: siatka teraz z jawnie zdefiniowaną wysokością wiersza (nie tylko kolumn) - dzięki temu
           widżet rozpięty na 4 wiersze faktycznie zajmuje tyle samo miejsca co 4 rzędy ikon, a nie dowolną,
           "dopasowaną do treści" wysokość. */
        /* COFNIĘTE: sztywna wysokość wiersza (92px) ucinała zawartość widżetu Etapów, gdy lista zadań była
           dłuższa niż to miejsce - stąd "widżet w połowie na głównej, w połowie poza". Wracam do bezpiecznej,
           potwierdzonej wersji: kolumny stałe, wiersze dopasowują się do treści. */
        /* PONOWNA PRÓBA (ostrożniejsza): minmax zamiast sztywnej wysokości - wiersze mają MINIMUM (żeby ikony
           nie zapadały się węższe niż trzeba), ale mogą URÓSNĄĆ, jeśli widżet rozpięty na kilka wierszy
           potrzebuje więcej miejsca niż standardowe minimum. Bezpieczniejsze niż poprzednia próba ze sztywnym
           92px, która ucinała treść zamiast pozwolić jej urosnąć. */
        /* OSTATECZNE COFNIĘCIE: dwie próby integracji widżetu z siatką (span 4x4) nie powiodły się wizualnie
           (ikony wychodziły poza ekran) - wracam do prostej, wielokrotnie potwierdzonej wersji. Widżet
           "Etapy zleceń" zostaje osobną sekcją POD siatką, nie jej częścią. */
        /* COFNIĘTE: system stron, dowolnego rozmieszczenia i widżetu w siatce usunięty na prośbę użytkownika -
           prosta, stała siatka 4 kolumny, bez edycji układu. */
        .home-icons-grid { display:grid; grid-template-columns: repeat(4, 1fr); gap:18px 10px; margin-top:10px; max-width: 480px; margin-left:auto; margin-right:auto; }
        /* Widżet "Etapy zleceń" jest osobną sekcją pod siatką ikon (nie jej elementem) - bezpieczniejsze
           podejście po dwóch nieudanych próbach integracji z siatką (span 4x4). */
        /* NOWOŚĆ: widżet "Etapy zleceń w toku" jako prawdziwy element siatki 4x4 (4 kolumny szerokości, 4
           wiersze wysokości - te same jednostki co pojedyncza ikona), zamiast osobnej sekcji pod spodem. */
        .home-icon-tile { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; text-align: center; }
        .home-icon-tile span { font-size: calc(11px * var(--home-icon-label-scale, 1)); color: var(--text-color); line-height: 1.2; text-shadow: 0 1px 3px rgba(0,0,0,0.4); }
        /* NAPRAWA (kafelki ogromne na iOS mimo poprawnego wyglądu na komputerze): width:100%+max-width w
           zagnieżdżonym flex/grid potrafi się różnie liczyć na Safari/iOS - zastąpione prostym, stałym
           rozmiarem (clamp reaguje na szerokość EKRANU, nie kontenera, więc liczy się identycznie wszędzie).
           NOWOŚĆ (personalizacja wyglądu): mnożnik --home-icon-scale pozwala użytkownikowi powiększyć/
           pomniejszyć ikony z poziomu Ustawień, niezależnie od tego bazowego, responsywnego rozmiaru. */
        .home-icon-square { width: calc(clamp(48px, 15vw, 64px) * var(--home-icon-scale, 1)); height: calc(clamp(48px, 15vw, 64px) * var(--home-icon-scale, 1)); margin: 0 auto; border-radius: 30%; display: flex; align-items: center; justify-content: center; box-shadow: 0 6px 14px -4px rgba(0,0,0,0.35), inset 0 1px 1px rgba(255,255,255,0.35); transition: transform 0.1s ease; position: relative; overflow: hidden; flex-shrink: 0; opacity: var(--home-icon-opacity, 1); }
        .home-icon-square svg { width: 60%; height: 60%; flex-shrink: 0; }
        .home-icon-square::after { content:''; position:absolute; top:0; left:0; right:0; height:50%; background: linear-gradient(rgba(255,255,255,0.25), rgba(255,255,255,0)); pointer-events:none; }
        .home-icon-tile:active .home-icon-square { transform: scale(0.92); }
        /* NOWOŚĆ (na życzenie użytkownika - "ta cyfra ma być jak na ikonach w ios"): wspólny styl odznaki z
           liczbą na kafelkach ekranu głównego (E-mail, Plany) - czerwone kółko z cienką obwódką w kolorze tła
           strony (odróżnia odznakę od ikony pod nią), tak jak systemowe odznaki iOS. Poza .home-icon-square
           (który ma overflow:hidden, żeby przycinać rogi) - stąd position:absolute na OPAKOWANIU, nie na niej. */
        /* NAPRAWA (zgłoszony błąd: "cyferka miała być biała") - ".home-icon-tile span" (kolor podpisów pod
           ikonami) ma WYŻSZĄ specyficzność niż sama ".ios-badge" (element+klasa kontra tylko klasa), więc
           wygrywał i nadpisywał biały kolor cyfry na --text-color. Selektor niżej łączy obie klasy, więc ma
           specyficzność WYŻSZĄ od tamtej reguły - wygrywa bez potrzeby !important. */
        .ios-badge { position: absolute; top: -4px; right: -4px; background: var(--danger-color); font-size: 11px; font-weight: bold; min-width: 18px; height: 18px; border-radius: 9px; align-items: center; justify-content: center; padding: 0 4px; line-height: 1; border: 2px solid var(--bg-color); box-sizing: content-box; text-shadow: none; }
        .home-icon-tile .ios-badge { color: #fff; }
        h2 { border-bottom: 2px solid var(--primary-color); padding-bottom: 5px; margin-top: 0; }
        h3 { border-bottom: 1px solid var(--border-color); padding-bottom: 5px; margin-top:0;}

        .card { background-color: var(--container-bg); padding: 16px 18px; border-radius: var(--radius-card); box-shadow: var(--shadow-card); margin-bottom: 15px; border: 1px solid var(--border-color); transition: all 0.3s ease;}
        /* NOWOŚĆ (Przebudowa wizualna - dalsze widoki): karty na liście Planów bliżej siebie (bardziej "listowo",
           jak grupowana lista w Ustawieniach iOS). */
        .plan-card { margin-bottom: 8px; }
        .facility-card { margin-bottom: 8px; -webkit-tap-highlight-color: transparent; }
        .facility-card:active { background-color: var(--bg-color); }
        
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; font-size: 14px; }
        input, select, textarea { width: 100%; padding: 12px 14px; border: 1px solid var(--border-color); border-radius: var(--radius-field); background-color: var(--container-bg); color: var(--text-color); font-size: 16px; }
        input:focus, select:focus, textarea:focus { outline: 2px solid var(--primary-color); outline-offset: 1px; }
        /* NAPRAWA (audyt QA/UX 2026-09-27, WCAG 2.4.7 - widoczny fokus): przyciski/linki/pozycje menu nie miały
           ŻADNEGO stylu fokusu - nawigacja klawiaturą (Tab) była niemożliwa do śledzenia wzrokiem. Reguła
           celuje w :focus-visible (nie w każdy :focus), żeby obwódka pojawiała się tylko przy realnej
           nawigacji klawiaturą, nie przy każdym dotknięciu/kliknięciu myszą. */
        a:focus-visible, button:focus-visible, .btn:focus-visible,
        .btn-form-save:focus-visible, .btn-form-cancel:focus-visible, .autocomplete-items div:focus-visible {
            outline: 2px solid var(--primary-color); outline-offset: 2px;
        }
        /* NAPRAWA (pola daty/godziny "rozjeżdżają się" na iOS): puste pole type="time"/type="date" renderuje
           się na Safari/iOS z WIĘKSZĄ wysokością niż wypełnione - Safari inaczej układa wewnętrzny widget
           godzinowy, gdy nie ma jeszcze wybranej wartości. Wymuszenie stałej wysokości i wyrównania eliminuje
           tę różnicę niezależnie od tego, czy pole ma już wartość, czy nie. */
        input[type="time"], input[type="date"], input[type="datetime-local"] { height: 44px; line-height: 24px; padding-top: 10px; padding-bottom: 10px; box-sizing: border-box; -webkit-appearance: none; appearance: none; min-width: 0; overflow: hidden; }
        input:disabled { background-color: #e9ecef; color: #6c757d; border-color: #ced4da; font-weight:bold; }
        [data-theme="dark"] input:disabled { background-color: #2c2c2c; color: #888; border-color: #444; }

        .btn { display: block; width: fit-content; min-width: 160px; max-width: 100%; padding: 12px 26px; border: none; border-radius: var(--radius-pill); font-size: 15px; font-weight: bold; cursor: pointer; margin: 0 auto 10px auto; color: white; background-color: var(--primary-color); text-align: center; box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--primary-color) 65%, transparent); }
        .btn:hover { background-color: var(--primary-hover); }
        .btn-success { background-color: var(--success-color); color: var(--on-success-color); box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--success-color) 65%, transparent); }
        .btn-danger { background-color: var(--danger-color); padding: 6px 14px; font-size: 12px; width: auto; margin:0; box-shadow: none; }
        .btn-logistics { background-color: var(--logistics-color); color: #341100; font-size: 14px; padding: 10px 18px; margin-bottom:0; box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--logistics-color) 55%, transparent); }

        .settings-grid { display: grid; grid-template-columns: 1fr; gap: 15px; }
        @media (min-width: 768px) { .settings-grid { grid-template-columns: 1fr 1fr; } }
        
        .status-bar { background-color: var(--success-container); color: var(--on-success-container); text-align: center; padding: 8px; font-size: 12px; display: none; border-radius: var(--radius-sm); margin-bottom: 15px; transition: 0.3s; }

        .autocomplete { position: relative; display: block; }
        .autocomplete-items { position: absolute; border: 1px solid var(--border-color); z-index: 1500; top: 100%; left: 0; right: 0; width: 100%; background-color: var(--container-bg); max-height: min(320px, 60vh); overflow-y: auto; overflow-x: hidden; border-radius: 0 0 var(--radius-field) var(--radius-field); box-shadow: var(--shadow-card); }
        .plan-search-item:active, .plan-search-item:hover { filter: brightness(0.93); }
        .autocomplete-items div { padding: 10px; cursor: pointer; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; font-size: 15px; min-height: 44px; box-sizing: border-box; }
        .autocomplete-items div:hover { background-color: color-mix(in srgb, var(--primary-color) 10%, transparent); }
        .btn-del-item { background: none; border: none; font-size: 14px; cursor: pointer; padding: 2px 10px; margin-left:10px; border-radius:var(--radius-sm); }
        /* NAPRAWA (audyt QA/UX 2026-09-27, strefy dotyku mobile <44px): .btn-danger i .btn-del-item są gęsto
           upakowane w listach/wierszach tabel i wykonują akcje NIEODWRACALNE (usuwanie) - na telefonie zbyt
           mały obszar dotyku zwiększa ryzyko przypadkowego dotknięcia. Powiększone TYLKO na ekranach dotykowych
           (mobile/tablet), na desktopie z myszką gęstość układu zostaje bez zmian. */
        @media (max-width: 859px) {
            .btn-danger { min-height: 44px; min-width: 44px; padding: 10px 16px; font-size: 13px; }
            .btn-del-item { min-height: 44px; min-width: 44px; padding: 10px; }
        }

        .table-responsive { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: var(--radius-sm); }
        table { width: 100%; border-collapse: collapse; font-size: 12px; min-width: 800px; }
        th, td { border: 1px solid var(--border-color); padding: 8px; text-align: center; }
        th { background-color: var(--primary-color); color: white; }
        .text-left { text-align: left; }
        .row-logistics { font-weight: bold; background-color: color-mix(in srgb, var(--logistics-color) 12%, transparent); }

        /* NAPRAWA (na życzenie użytkownika - "lista w raportach ma być dostosowana do szerokości okna"): tabela
           Raportów (12 kolumn) poniżej progu "komputerowego" (patrz @media(min-width:860px) niżej) przestaje
           przewijać się w poziomie - każdy wiersz zamienia się w samodzielną "kartę" z etykietą nad każdą
           wartością (etykieta z atrybutu data-label ustawianego w renderReports()), więc mieści się w całości
           w szerokości ekranu telefonu zamiast wymagać przewijania w bok. Na komputerze (>=860px) bez zmian -
           normalna, szeroka tabela z .table-responsive.
        */
        @media (max-width: 859px) {
            #view-reports .table-responsive { overflow-x: visible; }
            #view-reports table { min-width: 0; }
            #view-reports thead { display: none; }
            #view-reports tbody, #view-reports tfoot { display: block; }
            /* NAPRAWA (na życzenie użytkownika - "dany cały wpis ma być w ramce pogrubionej aby się odznaczał
               jako jeden wpis"): pogrubiona, wyraźniejsza ramka karty (2px, kolor tekstu zamiast słabo
               widocznego --border-color) - każdy wpis ma teraz jednoznacznie widoczną granicę. */
            #view-reports tbody tr, #view-reports tfoot tr {
                display: block; margin-bottom: 12px; border: 2px solid var(--text-color);
                border-radius: var(--radius-sm); overflow: hidden; background: var(--container-bg);
            }
            #view-reports tbody td, #view-reports tfoot td {
                display: block; text-align: left; border: none; border-bottom: 1px dashed var(--border-color);
                padding: 6px 10px; white-space: normal;
            }
            #view-reports tbody td:last-child, #view-reports tfoot td:last-child { border-bottom: none; }
            #view-reports tbody td[data-label]::before, #view-reports tfoot td[data-label]::before {
                content: attr(data-label); display: block; font-size: 11px; font-weight: bold;
                color: var(--primary-color); margin-bottom: 2px;
            }
            /* NAPRAWA (na życzenie użytkownika - "przyciski akcji mają być zgodne z daną kolumną i mają być
               kosze pod koszami"): "Akcje" to na komputerze 5 osobnych kolumn (patrz renderReports()) - tutaj,
               w trybie "karty", te 5 komórek wraca do wyglądu JEDNEGO wspólnego rzędu przycisków (inline-flex
               zamiast block, bez własnej ramki między nimi), zamiast pokazywać się jako 5 osobnych, pustych linii. */
            #view-reports tbody td.no-print {
                display: inline-flex; flex-wrap: wrap; gap: 4px; text-align: left;
                border-bottom: none; width: auto; padding: 4px 6px;
            }
            #view-reports tfoot td[colspan] { font-weight: bold; text-align: left; }
        }

        /* NAPRAWA (klawiatura zasłaniała okno logowania na telefonach): "100vh" na iOS Safari ignoruje
           wysuniętą klawiaturę ekranową - liczy się do PEŁNEJ wysokości ekranu, nie do tego, co faktycznie
           widać nad klawiaturą. "100dvh" (dynamic viewport height) aktualizuje się razem z klawiaturą - druga
           deklaracja NADPISUJE pierwszą tylko w przeglądarkach, które ją rozumieją (starsze po prostu ją
           ignorują i zostają przy działającym "100vh"), więc to bezpieczne, stopniowe ulepszenie. */
        .login-wrapper { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; min-height: 100dvh; padding: 20px; text-align: center; overflow-y: auto; }

        /* OKNO MODALNE DO EKSORTU CSV */
        .modal-overlay {
            position: fixed; top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.6); display: none;
            align-items: center; justify-content: center; z-index: 2000;
        }
        .modal-content {
            background: var(--container-bg); padding: 22px; border-radius: var(--radius-card);
            width: 90%; max-width: 500px; max-height: 90vh; overflow-y: auto;
            box-shadow: 0 12px 32px -8px rgba(0,0,0,0.35); border: 1px solid var(--border-color);
        }
        .modal-grid {
            display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 15px 0;
        }
        .modal-grid label {
            display: flex; align-items: center; font-weight: normal; font-size: 14px; margin:0; cursor:pointer;
            background: var(--bg-color); padding: 8px 10px; border-radius: var(--radius-sm); border: 1px solid var(--border-color);
        }
        .modal-grid input[type="checkbox"] { width: auto; margin-right: 10px; cursor: pointer; transform: scale(1.2); accent-color: var(--primary-color); }
        input[type="checkbox"] { accent-color: var(--primary-color); }

        /* NOWOŚĆ (redesign Material 3 Expressive): duży przełącznik (switch) dla prawdziwych ustawień "wł/wył" -
           czysto CSS-owa przeróbka zwykłego <input type="checkbox">, bez zmiany znacznika ani logiki JS
           (onchange/checked działają identycznie jak wcześniej). Używać tylko dla pojedynczych ustawień tak/nie,
           NIE dla list wielokrotnego wyboru (np. kolumny eksportu CSV) - tam zwykły checkbox jest właściwy. */
        input.m3-switch { -webkit-appearance: none; appearance: none; width: 46px; height: 28px; min-width: 46px; border-radius: var(--radius-pill); background: var(--border-color); position: relative; cursor: pointer; flex-shrink: 0; margin: 0; vertical-align: middle; transition: background-color 0.2s ease; }
        input.m3-switch::before { content: ''; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.3); transition: transform 0.2s ease; }
        input.m3-switch:checked { background: var(--primary-color); }
        input.m3-switch:checked::before { transform: translateX(18px); }

        /* NOWOŚĆ (redesign Material 3 Expressive): jednolity wygląd przycisków Zapisz/Anuluj/Reset w
           formularzach na całej stronie - wcześniej każdy formularz miał osobno wpisany, "duchowy" przycisk
           (styl Android - bez tła, sam kolorowy tekst+ikona), co po reszcie przebudowy zaczęło odstawać od
           wypełnionych, pigułkowych przycisków wszędzie indziej. Zapisz = wypełniona pigułka (jak każdy inny
           główny przycisk akcji), Anuluj/Reset = pigułka z obwódką, bez wypełnienia - ta sama logika co np.
           przycisk "Anuluj" w oknie wymuszającym stan licznika. */
        .btn-form-save, .btn-form-cancel { display:inline-flex; align-items:center; justify-content:center; gap:6px; width:auto; min-width:0; margin:0; padding:10px 20px; font-size:14px; font-weight:bold; cursor:pointer; border-radius:var(--radius-pill); transition: filter 0.15s ease, transform 0.05s ease; }
        .btn-form-save:active, .btn-form-cancel:active { transform: scale(0.98); }
        .btn-form-save { background:var(--success-color); color:var(--on-success-color); border:none; box-shadow:0 6px 16px -6px color-mix(in srgb, var(--success-color) 55%, transparent); }
        .btn-form-save:active { filter:brightness(0.92); }
        .btn-form-cancel { background:transparent; color:var(--text-color); border:1px solid var(--border-color); }
        .btn-form-cancel:active { background:var(--bg-color); }

        @media print { .no-print { display: none !important; } .view-section { padding: 0; display: block !important; } }
        /* Drukowanie okna podglądu zadania - ukrywa wszystko poza samą treścią modala, chowa przyciski akcji */
        @media print {
            body.printing-plan > *:not(#plan-preview-modal) { display: none !important; }
            body.printing-plan #plan-preview-modal { position: static !important; background: none !important; display: block !important; }
            body.printing-plan #plan-preview-modal .modal-content { box-shadow: none !important; border: none !important; max-width: 100% !important; }
            body.printing-plan #plan-preview-modal button { display: none !important; }
            body.printing-plan #print_header_plan { display: block !important; }
            /* Zabezpieczenie: przy ciemnym motywie tekst bywa jasny (dobry na ekranie, niewidoczny na białej kartce) - na wydruku wymuszamy czarny */
            body.printing-plan #plan-preview-modal, body.printing-plan #plan-preview-modal * { color: #000 !important; }
        }

        /* Drukowanie etykiety QR */
        @media print {
            body.printing-qrlabel > *:not(#qr-label-modal) { display: none !important; }
            body.printing-qrlabel #qr-label-modal { position: static !important; background: none !important; display: block !important; }
            body.printing-qrlabel #qr-label-modal .modal-content { box-shadow: none !important; border: none !important; max-width: 100% !important; padding: 0 !important; display: block !important; }
            body.printing-qrlabel #qr-label-modal button, body.printing-qrlabel .no-print { display: none !important; }
            body.printing-qrlabel, body.printing-qrlabel #qr_label_doc { color: #000 !important; background: white !important; }
            body.printing-qrlabel #qr_label_doc * { color: #000 !important; }
        }

        /* Drukowanie protokołu części uszkodzonej */
        @media print {
            body.printing-damaged > *:not(#damaged-modal) { display: none !important; }
            body.printing-damaged #damaged-modal { position: static !important; background: none !important; display: block !important; }
            body.printing-damaged #damaged-modal .modal-content { box-shadow: none !important; border: none !important; max-width: 100% !important; max-height: none !important; padding: 0 !important; display: block !important; }
            body.printing-damaged #damaged_doc { overflow: visible !important; max-height: none !important; padding: 0 !important; }
            body.printing-damaged #damaged-modal button, body.printing-damaged .no-print { display: none !important; }
            body.printing-damaged, body.printing-damaged #damaged_doc { color: #000 !important; background: white !important; }
            body.printing-damaged #damaged_doc * { color: #000 !important; }
        }

        /* Drukowanie zapotrzebowania na części - tylko treść dokumentu, bez interfejsu aplikacji */
        @media print {
            body.printing-shortage > *:not(#shortage-modal) { display: none !important; }
            body.printing-shortage #shortage-modal { position: static !important; background: none !important; display: block !important; }
            body.printing-shortage #shortage-modal .modal-content { box-shadow: none !important; border: none !important; max-width: 100% !important; max-height: none !important; padding: 0 !important; display: block !important; }
            body.printing-shortage #shortage_doc { overflow: visible !important; max-height: none !important; padding: 0 !important; }
            body.printing-shortage #shortage-modal button, body.printing-shortage .no-print { display: none !important; }
            body.printing-shortage, body.printing-shortage #shortage_doc { color: #000 !important; background: white !important; }
            body.printing-shortage #shortage_doc * { color: #000 !important; }
        }

        /* Drukowanie protokołu: sam dokument, bez nagłówków/stopek przeglądarki (nazwa strony, data, adres, numer strony) */
        #protocol-print-container { display: none; }
        @media print {
            /* NAPRAWA (protokół wymagał ręcznego pomniejszenia skali w opcjach drukowania): brak jawnej reguły
               @page powodował, że przeglądarka mogła źle wyliczać skalowanie obrazu protokołu względem
               fizycznego rozmiaru kartki - obraz próbował wydrukować się w swoim naturalnym rozmiarze
               pikselowym zamiast dopasować się do strony. "size: auto" pozwala przeglądarce użyć domyślnego
               rozmiaru papieru użytkownika, "margin: 0" oddaje protokołowi całą dostępną powierzchnię. */
            @page { size: auto; margin: 0; }
            body.printing-protocol > *:not(#protocol-print-container) { display: none !important; }
            body.printing-protocol nav, body.printing-protocol footer, body.printing-protocol header,
            body.printing-protocol #app-status-bar, body.printing-protocol .modal-overlay,
            body.printing-protocol .no-print, body.printing-protocol .view-section { display: none !important; }
            body.printing-protocol #protocol-print-container { display: block !important; margin: 0; padding: 0; width: 100%; }
            body.printing-protocol #protocol-print-image { display: block; width: 100%; max-width: 100%; height: auto; margin: 0; padding: 0; }
            body.printing-protocol { margin: 0 !important; padding: 0 !important; background: white !important; }
        }

        /* NOWOŚĆ: styl przycisków paska narzędzi edytora notatek - jak w Google Docs: stonowane, szare ikony
           z jasnym podświetleniem tła przy najechaniu/dotknięciu, zamiast kolorowych grup. */
        .instr-toolbar-btn { transition: background-color 0.1s ease; }
        .instr-toolbar-btn:hover { background-color: rgba(60,64,67,0.08) !important; }
        [data-theme="dark"] .instr-toolbar-btn:hover { background-color: rgba(255,255,255,0.12) !important; }

        /* NOWOŚĆ: uniwersalny wskaźnik zapisywania */
        @keyframes global-saving-spin { to { transform: rotate(360deg); } }

        /* NOWOŚĆ (na życzenie użytkownika - "podczas aktualizowania poczty pojawia się napis sprawdzam pocztę,
           usuń napis i daj ikonkę tylko która będzie migać"): pulsująca ikonka zamiast tekstu - patrz
           #email_check_indicator w views/app.php i checkEmailAccountsNow w app.js. */
        @keyframes email-check-blink { 50% { opacity: 0.15; } }
        .email-check-blink { animation: email-check-blink 1s ease-in-out infinite; }
