@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500;600&display=swap');

/* ============================================================
   Etykiety QR — arkusz stylów
   Materiał źródłowy: podkład z rolki etykiet, biały papier
   samoprzylepny, druk termiczny, pieczątka kontroli jakości.
   ============================================================ */

:root {
    /* podkład silikonowany, na którym leżą etykiety */
    --surface:        #dde2e7;
    --surface-dark: #c8d0d8;
    /* papier etykiety */
    --label:       #ffffff;
    /* druk termiczny */
    --ink:           #111418;
    --ink-muted:     #5c6672;
    /* powierzchnia odwrócona: pasek nawigacji, nagłówek tabeli, najechany
       przycisk. Osobny token, bo w trybie ciemnym „ciemne tło, jasny tekst”
       nie może brać ciemności z --ink — tam --ink jest KOLOREM TEKSTU
       i jest jasny, więc pasek zrobiłby się świecącą belką. */
    --contrast:         #111418;
    --contrast-text:   #ffffff;

    /* pieczątka kontroli */
    --stamp:        #1258a0;
    /* pieczątka odrzucenia */
    --danger:         #a4243b;
    --danger-bg:     #f7e8ea;
    /* pieczątka „sprawdź to” — słabsza niż odrzut, mocniejsza niż zwykły tekst */
    --warning:          #8a5a00;
    --warning-bg:      #fdf0d5;
    --success-bg:      #e4ecdf;
    --success:          #2f5d2f;
    /* Zieleń wskaźnika siły hasła, JAŚNIEJSZA niż wspólna --success. Tamta
       jest ciemna, bo służy za kolor tekstu w komunikatach; tutaj barwi
       przede wszystkim pasek, czyli element graficzny — a ten przy tak
       ciemnej zieleni wyglądał na wygaszony zamiast na „gotowe". Dwie
       wartości, bo pasek i tekst mają różne progi czytelności: pasek jest
       grafiką (wystarczy kontrast 3:1), a lista wymagań drobnym tekstem,
       który musi utrzymać 4,5:1 na białym. */
    --strength-bar:     #43a047;
    --strength-text:    #2e7d32;

    --font-heading: 'Barlow Condensed', 'Arial Narrow', system-ui, sans-serif;
    --font-body:    'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-mono:      'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

    --radius: 3px;
    --shadow: 0 1px 0 rgba(17, 20, 24, .06), 0 2px 8px rgba(17, 20, 24, .08);
    /* Karta bramy unosi się wyżej niż karty listy. Jako token, bo inaczej
       trzeba by go duplikować razem z całą paletą ciemną. */
    --shadow-raised: 0 2px 4px rgba(17, 20, 24, .08), 0 18px 44px rgba(17, 20, 24, .22);
}

*, *::before, *::after { box-sizing: border-box; }

body {
    margin: 0;
    padding: 0 1rem 5rem;
    background: var(--surface);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 16px;
    line-height: 1.5;
    -webkit-text-size-adjust: 100%;
}

/* ---------- pasek nawigacji: taśma z nadrukiem ---------- */

/* ---------- menu paska: hamburger na wąskim ekranie ---------- */

/* Szeroko: <details> znika jako pudełko i pozycje wracają do paska w jednej
   linii — dokładnie tak, jak było przed hamburgerem.

   Dwie reguły na jedno, bo przeglądarki chowają wnętrze <details> na dwa różne
   sposoby: starsze przez pudełko elementu (rozwiązuje to `display: contents`),
   nowsze przez pseudoelement ::details-content z `content-visibility: hidden`.
   Przeglądarka, która drugiego nie zna, po prostu tę regułę pomija. */
@media (min-width: 52rem) {
    .navbar__menu,
    .navbar__links {
        display: contents;
    }

    .navbar__menu::details-content {
        content-visibility: visible;
        display: contents;
    }

    .navbar__toggle {
        display: none;
    }
}

@media (max-width: 51.999rem) {
    /* Wąsko: menu zajmuje całą szerokość pod znakiem firmowym. */
    .navbar__menu {
        flex-basis: 100%;
    }

    .navbar__toggle {
        display: inline-flex;
        align-items: center;
        gap: .6rem;
        padding: .35rem 0;
        font-family: var(--font-heading);
        font-size: 1rem;
        letter-spacing: .08em;
        text-transform: uppercase;
        color: var(--contrast-text);
        cursor: pointer;
        list-style: none;
    }

    .navbar__toggle::-webkit-details-marker {
        display: none;
    }

    /* Trzy kreski rysowane tłem, nie znakiem „☰” — znak zależy od kroju
       zainstalowanego w systemie i potrafi wypaść jako pusty prostokąt. */
    .navbar__burger {
        display: inline-block;
        width: 1.15rem;
        height: 2px;
        background: currentColor;
        box-shadow: 0 -6px 0 currentColor, 0 6px 0 currentColor;
    }

    .navbar__menu[open] .navbar__burger {
        /* Otwarte menu: kreski zamieniają się w krzyżyk. */
        background: transparent;
        box-shadow: none;
        position: relative;
    }

    .navbar__menu[open] .navbar__burger::before,
    .navbar__menu[open] .navbar__burger::after {
        content: '';
        position: absolute;
        left: 0;
        top: 0;
        width: 1.15rem;
        height: 2px;
        background: currentColor;
    }

    .navbar__menu[open] .navbar__burger::before { transform: rotate(45deg); }
    .navbar__menu[open] .navbar__burger::after  { transform: rotate(-45deg); }

    .navbar__links {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: .85rem;
        padding: .85rem 0 .25rem;
        margin-top: .5rem;
        border-top: 1px solid rgba(255, 255, 255, .15);
    }
}

/* flex-wrap, bo pasek rośnie razem z aplikacją: doszły „Kronika” i „Baza”
   i przy siedmiuset pikselach wyjechał poza ekran, ciągnąc za sobą poziome
   przewijanie całej strony. Zawijanie jest tu jedynym rozwiązaniem, które nie
   każe wybierać, którą pozycję ukryć. */
.navbar {
    max-width: 68rem;
    margin: 0 auto 2rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .75rem 1.5rem;
    padding: 1rem 1.25rem;
    background: var(--contrast);
    color: var(--contrast-text);
    border-radius: 0 0 var(--radius) var(--radius);
}

.navbar__brand {
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 1.15rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    margin-right: auto;
}

.navbar a {
    /* --contrast-text, nie --label.
    
       Oba są białe w trybie jasnym, więc pomyłki nie widać. W ciemnym
       --label to kolor POWIERZCHNI karty i jest ciemna, a pasek stoi na
       --contrast, które ciemne pozostaje — odnośniki robiły się ciemnoszare
       na ciemnym tle i znikały.
    
       Reguła: --label jako kolor tekstu jest poprawna WYŁĄCZNIE tam, gdzie
       tłem jest --stamp albo --danger, bo tamte dwa w trybie ciemnym
       jaśnieją. Na tle --contrast tekst bierze --contrast-text. */
    color: var(--contrast-text);
    text-decoration: none;
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1.05rem;
    letter-spacing: .07em;
    text-transform: uppercase;
    padding: .35rem .1rem;
    border-bottom: 2px solid transparent;
}

.navbar a:hover,
.navbar a:focus-visible { border-bottom-color: var(--contrast-text); }

/* ---------- kontener treści ---------- */

main {
    max-width: 68rem;
    margin: 0 auto;
}

h1 {
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: clamp(2rem, 6vw, 2.75rem);
    letter-spacing: .04em;
    text-transform: uppercase;
    margin: 0 0 .35rem;
}

h2 {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1.5rem;
    letter-spacing: .05em;
    text-transform: uppercase;
    margin: 2.5rem 0 1rem;
}

.subtitle {
    color: var(--ink-muted);
    margin: 0 0 2rem;
    max-width: 46ch;
}

/* Wariant do akapitów WEWNĄTRZ panelu: wyjaśnienie pod nagłówkiem sekcji,
   a nie podtytuł całej strony. Węższy odstęp i pełna szerokość kolumny —
   46ch łamałoby zdanie w połowie panelu i zostawiało pustą prawą stronę. */
.subtitle--small {
    font-size: 0.9375rem;
    margin: 0 0 1.25rem;
    max-width: 68ch;
}

/* ---------- komunikaty ---------- */

.notice, .error {
    max-width: 68rem;
    margin: 0 auto 1.5rem;
    padding: .85rem 1.1rem;
    border-left: 4px solid;
    border-radius: 0 var(--radius) var(--radius) 0;
    font-weight: 500;
}

.notice { background: var(--success-bg); border-color: var(--success); color: var(--success); }
.error      { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }

/* ---------- etykiety na podkładzie: lista dokumentów ---------- */

.card-list {
    display: grid;
    gap: .75rem;
    margin: 0 0 1.5rem;
    padding: 0;
    list-style: none;
}

.card {
    display: grid;
    grid-template-columns: auto auto 1fr auto;
    gap: 1rem;
    align-items: center;
    background: var(--label);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1rem 1.15rem;
    /* nacięcie wykrojnika po lewej */
    border-left: 3px solid var(--surface-dark);
}

.card:has(input:checked) { border-left-color: var(--stamp); }

.card__select {
    width: 1.35rem;
    height: 1.35rem;
    accent-color: var(--stamp);
    cursor: pointer;
}

.card__qr {
    display: block;
    width: 72px;
    height: 72px;
    /* Biała otoczka to strefa cichości kodu QR — bez niej czytnik ma
       kłopot z odnalezieniem krawędzi. Tu jest dekoracyjna, ale utrzymuje
       ten sam kształt, który idzie na wydruk. */
    padding: 4px;
    background: var(--label);
    border: 1px solid var(--surface-dark);
    border-radius: 2px;
    image-rendering: pixelated;
}

.card__body { min-width: 0; }

.card__name {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1.3rem;
    letter-spacing: .02em;
    line-height: 1.2;
    margin: 0 0 .3rem;
    overflow-wrap: anywhere;
}

.card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem .9rem;
    font-size: .85rem;
    color: var(--ink-muted);
    margin: 0;
}

.card__token {
    font-family: var(--font-mono);
    font-size: .8rem;
    letter-spacing: .02em;
    color: var(--stamp);
    word-break: break-all;
}

.card__actions {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    justify-content: flex-end;
}

/* ---------- przyciski i odnośniki-akcje ---------- */

/* Uwaga na swoistość: NIE dodawać tu selektora `button.button`.
   Element + klasa (0,1,1) bije samą klasę (0,1,0), więc taki selektor
   unieważniłby wszystkie modyfikatory --primary, --danger i --maly
   na elementach <button>, zostawiając je w wariancie podstawowym. */
.button {
    display: inline-block;
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    text-decoration: none;
    text-align: center;
    padding: .6rem 1.1rem;
    min-height: 44px;
    line-height: 1.8;
    border: 2px solid var(--ink);
    border-radius: var(--radius);
    background: transparent;
    color: var(--ink);
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}

.button:hover,
.button:focus-visible { background: var(--contrast); color: var(--contrast-text); }

.button--primary {
    background: var(--stamp);
    border-color: var(--stamp);
    color: var(--label);
}

.button--primary:hover,
.button--primary:focus-visible { background: var(--contrast); border-color: var(--contrast); color: var(--contrast-text); }

.button--danger { border-color: var(--danger); color: var(--danger); }

.button--danger:hover,
.button--danger:focus-visible { background: var(--danger); color: var(--label); }

.button--small {
    font-size: .9rem;
    padding: .4rem .75rem;
    min-height: 38px;
    line-height: 1.6;
    border-width: 1px;
}

.action-bar {
    position: sticky;
    bottom: 0;
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
    padding: 1rem 1.15rem;
    background: var(--label);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.action-bar__note {
    color: var(--ink-muted);
    font-size: .9rem;
    margin: 0 auto 0 0;
}

/* ---------- formularze ---------- */

.form {
    background: var(--label);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.5rem;
}

.field { display: block; margin: 0 0 1.25rem; }

.field__label {
    display: block;
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1.05rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    margin: 0 0 .35rem;
}

.field__hint {
    display: block;
    font-size: .85rem;
    color: var(--ink-muted);
    margin: 0 0 .5rem;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="file"],
select {
    width: 100%;
    font-family: var(--font-body);
    font-size: 1rem;
    padding: .65rem .75rem;
    min-height: 44px;
    /* Białe, nie w kolorze podkładu. Szare wypełnienie na białej karcie
       formularza czyta się jak pole wyłączone — a to jest pole do wpisania. */
    background: var(--label);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
    color: var(--ink);
}

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

/* ---------- wybór szablonu etykiet ---------- */

.templates { display: grid; gap: .6rem; margin: 0 0 1.5rem; }

.template {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .9rem 1rem;
    background: var(--surface);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
    cursor: pointer;
}

.template:has(input:checked) {
    background: var(--label);
    border-color: var(--stamp);
    box-shadow: inset 0 0 0 1px var(--stamp);
}

.template input { accent-color: var(--stamp); width: 1.2rem; height: 1.2rem; }

/* ---------- tabela kont ---------- */

.table {
    width: 100%;
    border-collapse: collapse;
    background: var(--label);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow);
}

.table th {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: .95rem;
    letter-spacing: .07em;
    text-transform: uppercase;
    text-align: left;
    padding: .8rem 1rem;
    background: var(--contrast);
    color: var(--contrast-text);
}

.table td {
    padding: .8rem 1rem;
    border-bottom: 1px solid var(--surface);
    vertical-align: middle;
}

.table tr:last-child td { border-bottom: 0; }

.role {
    font-family: var(--font-mono);
    font-size: .75rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: .2rem .5rem;
    border-radius: 2px;
    background: var(--surface);
    color: var(--ink-muted);
}

.role--admin { background: var(--stamp); color: var(--label); }

/* ---------- pusty stan ---------- */

.empty-state {
    background: var(--label);
    border: 2px dashed var(--surface-dark);
    border-radius: var(--radius);
    padding: 3rem 1.5rem;
    text-align: center;
    box-shadow: none;
}

.empty-state p { color: var(--ink-muted); margin: 0 0 1.5rem; }

/* ============================================================
   Strony publiczne — to widzi człowiek stojący przy palecie
   z telefonem w ręku. Duże, kontrastowe, jedno przesłanie.
   ============================================================ */

.scan {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 1.5rem;
    padding: 2rem 1.25rem;
    text-align: center;
}

.scan__title {
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: clamp(1.75rem, 7vw, 2.5rem);
    letter-spacing: .03em;
    text-transform: uppercase;
    margin: 0;
    max-width: 20ch;
}

.scan__body {
    color: var(--ink-muted);
    max-width: 34ch;
    margin: 0;
    font-size: 1.05rem;
}

.scan__image {
    max-width: 100%;
    height: auto;
    background: var(--label);
    padding: .5rem;
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* pieczątka — odbicie stempla na certyfikacie */
.stamp {
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: clamp(1.6rem, 8vw, 2.75rem);
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--danger);
    border: 5px solid var(--danger);
    border-radius: 6px;
    padding: .6rem 1.4rem;
    transform: rotate(-6deg);
    opacity: .88;
    /* nierówność tuszu: stempel nigdy nie odbija się idealnie */
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .35);
}

.stamp--unknown { color: var(--ink-muted); border-color: var(--ink-muted); }

/* ---------- telefon ---------- */

@media (max-width: 40rem) {
    .navbar {
        flex-wrap: wrap;
        gap: .75rem 1.1rem;
        border-radius: 0;
        margin-left: -1rem;
        margin-right: -1rem;
        padding: .9rem 1rem;
    }

    .navbar__brand { flex-basis: 100%; margin-right: 0; }

    .card {
        grid-template-columns: auto auto 1fr;
        row-gap: .75rem;
    }

    .card__actions {
        grid-column: 1 / -1;
        justify-content: flex-start;
    }

    .action-bar { flex-direction: column; align-items: stretch; }
    .action-bar__note { margin: 0 0 .25rem; }
    .button { width: 100%; }
    .card__actions .button { width: auto; }
}

/* ---------- szacunek dla ustawień systemowych ---------- */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}

@media print {
    .navbar, .action-bar, .card__actions { display: none; }
    body { background: #fff; }
}

/* ==========================================================================
   Brama — ekrany sprzed zalogowania
   ==========================================================================

   Sama karta na podkładzie, bez tła. Był tu wcześniej arkusz A4 z pustymi
   etykietami; zdjęty na życzenie — rozpraszał, zamiast tłumaczyć.

   Nie ma tu też układu dwudzielnego, mimo że tak robi dziś większość paneli.
   Druga kolumna musiałaby czymś być, a przy dwóch polach i jednym przycisku
   wypełniałaby pół ekranu dekoracją.
   ========================================================================== */

.landing {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    /* align-CONTENT, nie align-ITEMS. To pierwsze ustawia ścieżkę siatki
       wewnątrz kontenera, drugie — element wewnątrz ścieżki. Kontener rośnie
       tu przez min-height, ale ścieżka pozostaje wysokości treści i siedzi
       przy górnej krawędzi; wyśrodkowanie elementu w takiej ścieżce nie
       zmienia niczego. Sprawdzone pomiarem: odstęp 104 px u góry przy 358 px
       u dołu, mimo poprawnie policzonego `align-items`.
       
       `safe` sprawia, że przy karcie wyższej od okna wyśrodkowanie ustępuje
       miejsca — inaczej górna krawędź chowa się poza obszarem przewijania. */
    align-content: safe center;
    /* W pionie ustawiamy ŚCIEŻKĘ (align-content), w poziomie ELEMENT
       w ścieżce (justify-items) — i te dwie własności muszą być właśnie tak
       dobrane. `justify-content: center` zwężało ścieżkę do szerokości treści,
       więc `width: min(30rem, 100%)` liczyło 100% od tej zwężonej ścieżki
       i karta wychodziła 274 px zamiast 480. Wyglądało to na źle dobraną
       szerokość, a było zapętleniem: szerokość zależała od siebie samej. */
    justify-items: center;
    padding: 2rem 1.25rem;
    background: var(--surface);
}

/* ---------- zadrukowana etykieta z formularzem ---------- */

.landing__body {
    position: relative;
    z-index: 1;
    /* 24rem wystarczało, dopóki karta leżała na arkuszu pustych etykiet:
       miała czytać się jako jedna z nich, więc musiała być od niego wyraźnie
       mniejsza. Bez arkusza nie ma do czego jej przymierzyć i zostaje sama
       wąska karta na pustym polu. */
    width: min(30rem, 100%);
}

.landing-card {
    background: var(--label);
    border-radius: var(--radius);
    /* To samo nacięcie wykrojnika, co na kartach dokumentów — ekran
       logowania i lista dokumentów mają być tym samym materiałem. */
    border-left: 3px solid var(--stamp);
    box-shadow: var(--shadow-raised);
    padding: 2.5rem 2.25rem 2rem;
}

.landing-card__overline {
    margin: 0 0 1.5rem;
    font-family: var(--font-mono);
    font-size: .7rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-muted);
}

.landing-card__overline::before {
    content: "";
    display: inline-block;
    width: .5em;
    height: .5em;
    margin-right: .6em;
    background: var(--stamp);
    vertical-align: baseline;
}

.landing-card__title {
    margin: 0 0 .35rem;
    font-family: var(--font-heading);
    font-size: 2.1rem;
    font-weight: 600;
    line-height: 1.05;
    letter-spacing: -.01em;
    color: var(--ink);
}

.landing-card__lead {
    margin: 0 0 1.75rem;
    font-size: .9rem;
    line-height: 1.5;
    color: var(--ink-muted);
}

.landing-card__title + .form { margin-top: 1.75rem; }

/* Formularz jest tu już wewnątrz karty — bez tego dostaje własne tło,
   cień i padding, czyli karta w karcie. */
.landing-card .form {
    margin: 0;
    padding: 0;
    background: none;
    box-shadow: none;
}

/* Wersaliki w kroju wąskim to język tej aplikacji, ale na ekranie z dwoma
   polami nagłówek, obie etykiety i przycisk krzyczą naraz. Etykiety pól
   schodzą tu o pół tonu, żeby zostało słychać nagłówek. */
.landing-card .field__label {
    font-family: var(--font-body);
    font-size: .8rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: none;
    color: var(--ink-muted);
}

.landing-card .button--primary {
    width: 100%;
    justify-content: center;
    margin-top: .5rem;
}

/* ---------- drobne linie pod formularzem ---------- */

.landing__links {
    margin: 1.5rem 0 0;
    padding-top: 1.25rem;
    /* Linia cięcia — ta sama, którą oddziela się etykietę od etykiety. */
    border-top: 1px solid var(--surface);
    font-size: .85rem;
    text-align: center;
    color: var(--ink-muted);
}

.landing__links:empty { display: none; }

.landing__links p { margin: 0 0 .4rem; }
.landing__links p:last-child { margin-bottom: 0; }

.landing__links a {
    color: var(--stamp);
    font-weight: 600;
    text-decoration-color: var(--surface-dark);
    text-underline-offset: 3px;
}

.landing__links a:hover,
.landing__links a:focus-visible { text-decoration-color: currentColor; }

.landing__footer {
    margin: 1.25rem 0 0;
    font-family: var(--font-mono);
    font-size: .7rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    text-align: center;
    color: var(--ink-muted);
    opacity: .75;
}

/* ---------- zapamiętanie sesji ---------- */

.remember {
    display: flex;
    gap: .6rem;
    align-items: center;
    margin: 0 0 1.25rem;
    font-size: .9rem;
    color: var(--ink-muted);
    cursor: pointer;
}

.remember input {
    width: 1.1rem;
    height: 1.1rem;
    accent-color: var(--stamp);
    cursor: pointer;
}

/* ---------- ekrany niskie i telefony ---------- */

@media (prefers-reduced-motion: no-preference) {
    .landing-card { animation: wejscie-podniesienie .32s ease-out both; }
}

@keyframes wejscie-podniesienie {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   Tryb ciemny
   ==========================================================================

   Cała aplikacja, nie sam ekran logowania — arkusz jest otokenizowany, więc
   wystarczy przedefiniować paletę.

   Metafora się tu przesuwa i to jest świadome. W dzień patrzysz na etykiety
   leżące na podkładzie; w nocy patrzysz na ekran w słabo oświetlonej hali.
   Utrzymywanie białych kart na ciemnym tle dałoby latarkę w twarz —
   dosłowność metafory nie jest warta oślepiania człowieka na nocnej zmianie.

   Zostaje to, co niesie znaczenie: granatowy stempel, czerwień odrzutu,
   nacięcie wykrojnika na krawędzi karty.
   ========================================================================== */

/* ==========================================================================
   Paleta ciemna — dwa wejścia, jeden zestaw wartości
   ==========================================================================

   Pierwsze wejście: ustawienie systemu, gdy człowiek nie wybrał nic ręcznie.
   Warunek `:not([data-theme="jasny"])` sprawia, że świadomy wybór jasnego
   wygrywa z ustawieniem systemu — bez niego przełącznik nie umiałby wymusić
   jasnego na komputerze ustawionym na ciemny.

   Drugie wejście: wybór zapisany w ciasteczku i wstawiony przez serwer
   w atrybut <html data-theme>.

   Wartości muszą być w obu miejscach IDENTYCZNE, a CSS nie pozwala objąć
   jedną regułą selektora spoza @media. Dlatego blok jest zduplikowany
   i oznaczony znacznikami @paleta-ciemna, a zgodności obu kopii pilnuje
   test MotywTest::testObieKopiePaletyCiemnejSaIdentyczne.
   ========================================================================== */

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="jasny"]) {
        /* @paleta-ciemna:start */
        /* Bez tego przeglądarka rysuje pola wyboru, paski przewijania
           i pola tekstowe w wersji jasnej — na ciemnej karcie zostaje
           świecący biały kwadrat, którego żaden własny styl nie dosięga. */
        color-scheme: dark;

        --surface:        #12161a;
        --surface-dark: #2c333d;
        --label:       #1c2128;
        --ink:           #e6ebf1;
        --ink-muted:     #98a3b1;

        /* Powierzchnia odwrócona zostaje ciemna — jaśniejsza od strony,
           żeby pasek nawigacji dało się od niej odróżnić, ale nie świecąca. */
        --contrast:         #2a323c;
        --contrast-text:   #eef2f7;

        /* Granat z palety dziennej na ciemnym tle schodzi poniżej progu
           czytelności, więc stempel i odrzut idą w górę jasności. Odcień
           zostaje ten sam — to nadal ta sama pieczątka. */
        --stamp:        #6fa9f0;
        --danger:         #f08c9a;
        --danger-bg:     #2c1a1e;
        --warning:          #e2b566;
        --warning-bg:      #2a2213;
        --success:          #8dc78d;
        --strength-bar:     #6bc275;
        --strength-text:    #8dc78d;
        --success-bg:      #1a271a;

        /* Cień na ciemnym tle prawie nie istnieje — uniesienie musi wziąć
           na siebie krawędź. */
        --shadow: 0 0 0 1px rgba(255, 255, 255, .06), 0 2px 10px rgba(0, 0, 0, .5);
        --shadow-raised: 0 0 0 1px rgba(255, 255, 255, .07), 0 18px 44px rgba(0, 0, 0, .6);
        /* @paleta-ciemna:end */
    }

}

:root[data-theme="ciemny"] {
    /* @paleta-ciemna:start */
    /* Bez tego przeglądarka rysuje pola wyboru, paski przewijania
       i pola tekstowe w wersji jasnej — na ciemnej karcie zostaje
       świecący biały kwadrat, którego żaden własny styl nie dosięga. */
    color-scheme: dark;

    --surface:    #12161a;
    --surface-dark: #2c333d;
    --label:       #1c2128;
    --ink:       #e6ebf1;
    --ink-muted:     #98a3b1;

    /* Powierzchnia odwrócona zostaje ciemna — jaśniejsza od strony,
       żeby pasek nawigacji dało się od niej odróżnić, ale nie świecąca. */
    --contrast:     #2a323c;
    --contrast-text:   #eef2f7;

    /* Granat z palety dziennej na ciemnym tle schodzi poniżej progu
       czytelności, więc stempel i odrzut idą w górę jasności. Odcień
       zostaje ten sam — to nadal ta sama pieczątka. */
    --stamp:    #6fa9f0;
    --danger:     #f08c9a;
    --danger-bg:     #2c1a1e;
    --warning:      #e2b566;
    --warning-bg:      #2a2213;
    --success:      #8dc78d;
    --strength-bar:     #6bc275;
    --strength-text:    #8dc78d;
    --success-bg:      #1a271a;

    /* Cień na ciemnym tle prawie nie istnieje — uniesienie musi wziąć
       na siebie krawędź. */
    --shadow: 0 0 0 1px rgba(255, 255, 255, .06), 0 2px 10px rgba(0, 0, 0, .5);
    --shadow-raised: 0 0 0 1px rgba(255, 255, 255, .07), 0 18px 44px rgba(0, 0, 0, .6);
    /* @paleta-ciemna:end */
}


/* Wydruk zawsze na papierze, niezależnie od ustawień ekranu. Bez tego
   przeglądarka w trybie ciemnym drukowałaby ciemne tła. */
@media print {
    :root {
        --surface: #ffffff;
        --surface-dark: #cccccc;
        --label: #ffffff;
        --ink: #000000;
        --ink-muted: #444444;
        --contrast: #000000;
        --contrast-text: #ffffff;
    }
}

/* ==========================================================================
   Przełącznik motywu
   ==========================================================================

   Formularz, nie przycisk skryptowy — patrz komentarz klasy
   App\Controllers\Motyw. Wygląda inaczej w pasku nawigacji, a inaczej na
   ekranach sprzed zalogowania, więc bazowa klasa jest neutralna, a wygląd
   nadaje kontekst.
   ========================================================================== */

.theme { margin: 0; }

.theme__button {
    font-family: var(--font-heading);
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    color: inherit;
}

/* ---------- w pasku nawigacji ---------- */

.navbar .theme__button {
    font-size: 1.05rem;
    color: var(--contrast-text);
    padding: .35rem .1rem;
    border-bottom: 2px solid transparent;
}

.navbar .theme__button:hover,
.navbar .theme__button:focus-visible { border-bottom-color: var(--contrast-text); }

/* Stan bieżący odróżniamy od słowa „Motyw”, żeby dało się go odczytać
   jednym spojrzeniem, bez czytania całej etykiety. */
.navbar .theme__state { color: var(--stamp); }

/* ---------- pod kartą logowania ---------- */

.landing .theme {
    margin-top: 1.5rem;
    text-align: center;
}

.landing .theme__button {
    font-family: var(--font-mono);
    font-size: .7rem;
    letter-spacing: .1em;
    color: var(--ink-muted);
    text-transform: uppercase;
    padding: .4rem;
}

.landing .theme__button:hover,
.landing .theme__button:focus-visible { color: var(--ink); }

/* Wydruk nie ma czego przełączać. */
@media print {
    .theme { display: none; }
}

/* ==========================================================================
   Formularz w dwóch kolumnach
   ==========================================================================

   Karta formularza sięga pełnej szerokości treści, żeby trzymać siatkę strony
   razem z tabelami. Pojedyncze pole rozciągnięte na 1030 px byłoby jednak
   nieczytelne i puste po prawej — kolumny godzą jedno z drugim: karta trzyma
   siatkę, pole zostaje w rozsądnej szerokości.

   Dokładnie dwie kolumny, nie `auto-fit`. Przy `repeat(auto-fit, minmax(...))`
   liczba kolumn wychodzi z dzielenia szerokości i przy tej karcie wypada tuż
   pod progiem trzech — drobna zmiana wypełnienia albo rozmiaru pisma
   przerzuciłaby układ na trzy kolumny, a cztery pola w trzech kolumnach to
   rząd 3 + 1. Próg wpisany wprost jest przewidywalny.
   ========================================================================== */

@media (min-width: 52rem) {
    .form--columns {
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: 1.5rem;
    }

    /* Nie każde pole ma opis pod etykietą — „Nazwa (login)" ma, sąsiedni
       „E-mail" nie. Bez tego pola wejściowe w jednym rzędzie wypadają na
       różnych wysokościach i rząd wygląda na przypadkowo poskładany.
       
       Komórki siatki i tak rozciągają się do wysokości najwyższej w rzędzie,
       więc wystarczy dosunąć pole wejściowe do dołu komórki: wtedy wyrównują
       się do siebie niezależnie od tego, ile tekstu stoi nad nimi. */
    .form--columns .field {
        display: flex;
        flex-direction: column;
    }

    .form--columns .field input,
    .form--columns .field select {
        margin-top: auto;
    }

    /* Wskaźnik siły hasła dotyczy OBU pól — także tego, czy są takie same —
       więc dostaje własny rząd pod nimi, na pełną szerokość. Wsadzony do
       kolumny podnosiłby ją i rozjeżdżał wyrównanie pól wejściowych. */
    .form--columns > .strength {
        grid-column: 1 / -1;
    }

    /* Przycisk należy do całego formularza, nie do kolumny. */
    .form--columns > button {
        grid-column: 1 / -1;
        justify-self: start;
    }
}

/* ==========================================================================
   Drugi składnik logowania
   ========================================================================== */

.totp__qr {
    display: block;
    width: 220px;
    height: 220px;
    margin: 0 auto 1.5rem;
    /* Biała otoczka to strefa cichości kodu QR — bez niej czytnik ma kłopot
       z odnalezieniem krawędzi. Tu jest potrzebna naprawdę, nie ozdobnie:
       ten kod ma zostać zeskanowany z ekranu. */
    padding: 10px;
    background: #ffffff;
    border-radius: var(--radius);
    image-rendering: pixelated;
}

.totp__manual {
    margin: 0 0 1.5rem;
    font-size: .875rem;
    color: var(--ink-muted);
}

.totp__manual summary {
    cursor: pointer;
    font-weight: 600;
}

.totp__secret {
    font-family: var(--font-mono);
    font-size: 1.05rem;
    letter-spacing: .12em;
    word-break: break-all;
    background: var(--surface);
    border-radius: var(--radius);
    padding: .6rem .8rem;
    margin: .5rem 0 0;
    color: var(--ink);
}

/* ---------- kody zapasowe ---------- */

.backup-codes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: .5rem;
    margin: 0 0 1.5rem;
    padding: 0;
    list-style: none;
}

.backup-codes__code {
    font-family: var(--font-mono);
    font-size: 1.1rem;
    letter-spacing: .1em;
    text-align: center;
    background: var(--label);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
    padding: .6rem .4rem;
    /* Kod przepisuje się z ekranu na kartkę — zaznaczenie ma obejmować
       dokładnie jeden kod, nie całą listę. */
    user-select: all;
}

/* ==========================================================================
   Ekran pomocy
   ==========================================================================

   Ten sam materiał, co reszta aplikacji: rozdziały to etykiety na podkładzie,
   z nacięciem wykrojnika na krawędzi. Nacięcie niesie znaczenie — granatowe
   przy rozdziałach dla wszystkich, czerwone przy tych dla administratora,
   bo tam kliknięcia mają cięższe skutki.
   ========================================================================== */

/* Dwie kolumny: przyklejony spis treści i treść. Instrukcja czytana przy
   problemie jest przeskakiwana, nie czytana od góry — spis musi zostać
   na ekranie, gdy człowiek przewija. */
.help-layout {
    display: grid;
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: 3rem;
    align-items: start;
}

@media (max-width: 62rem) {
    .help-layout { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
}

.help-body { min-width: 0; }

.help__toc {
    position: sticky;
    top: 1.5rem;
    font-size: 1rem;
    background: var(--label);
    border-left: 3px solid var(--stamp);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.4rem 1.5rem 1.6rem;
}

@media (max-width: 62rem) { .help__toc { position: static; } }

.help__toc-title {
    font-family: var(--font-mono);
    font-size: .68rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin: 0 0 .8rem;
}

.help__toc ol {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: .55rem;
}

.help__toc a {
    display: block;
    font-size: 1rem;
    line-height: 1.4;
    color: var(--ink);
    text-decoration: none;
    padding: .18rem 0;
    border-bottom: 1px solid transparent;
}

.help__toc a:hover,
.help__toc a:focus-visible { color: var(--stamp); border-bottom-color: var(--stamp); }

.help {
    background: var(--label);
    border-left: 3px solid var(--surface-dark);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 2.4rem 2.5rem 2.1rem;
    margin: 0 0 1.75rem;
    scroll-margin-top: 1.5rem;
}

/* Ten sam nadruk, co na ekranie logowania — mówi, do której szuflady należy
   rozdział, zanim człowiek przeczyta tytuł. */
.help__overline {
    font-family: var(--font-mono);
    font-size: .68rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin: 0 0 .45rem;
}

.help__overline::before {
    content: "";
    display: inline-block;
    width: .5em;
    height: .5em;
    margin-right: .6em;
    background: var(--stamp);
}

.help--admin .help__overline::before { background: var(--danger); }

.help--admin { border-left-color: var(--danger); }

.help__marker {
    font-family: var(--font-mono);
    font-size: .68rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--danger);
    margin: 0 0 .35rem;
}

.help {
    font-size: 1.05rem;
    line-height: 1.75;
}

.help h2 {
    font-family: var(--font-heading);
    font-size: clamp(1.9rem, 4vw, 2.45rem);
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: .02em;
    text-transform: uppercase;
    margin: 0 0 1rem;
}

.help h3 {
    font-family: var(--font-heading);
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    margin: 2rem 0 .6rem;
}

.help p { max-width: 64ch; margin: 0 0 1.15rem; }
.help > *:last-child { margin-bottom: 0; }

.help__list, .help__steps {
    max-width: 64ch;
    margin: 0 0 1.15rem;
    padding-left: 1.4rem;
}

.help__list li, .help__steps li { margin-bottom: .55rem; }

/* Blok, który mówi „tu można coś nieodwracalnie zepsuć". Ten sam czerwony
   stempel, co przy wycofywaniu dokumentu — jeden język w całej aplikacji. */
/* Blok wyjaśniający: czerwony ostrzega przed skutkiem, zielony podpowiada,
   dlaczego warto zrobić tak, a nie inaczej.
   
   Domyślnie na PEŁNĄ szerokość rodzica — notka nad formularzem ma sięgać tak
   samo daleko jak formularz, inaczej wygląda na przypadkowo doklejoną.
   Wewnątrz instrukcji obowiązuje ograniczenie długości wiersza, bo tam czyta
   się akapitami. */
.note {
    background: var(--danger-bg);
    border-left: 3px solid var(--danger);
    border-radius: var(--radius);
    padding: 1.1rem 1.35rem;
    margin: 0 0 1.15rem;
}

.note--good { background: var(--success-bg); border-left-color: var(--success); }

.help .note { max-width: 64ch; }

/* Blok, który tłumaczy, DLACZEGO warto zrobić tak, a nie inaczej. Zielony
   stempel zgody, ten sam co przy udanych operacjach — czerwony ostrzega,
   zielony podpowiada, i te dwa znaczenia nie mieszają się ze sobą. */
/* ---------- zrzuty ekranu ---------- */

.help__shot { margin: 1.25rem 0; }

.help__shot img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
}

.help__shot figcaption {
    font-size: .85rem;
    color: var(--ink-muted);
    margin-top: .5rem;
    max-width: 62ch;
}

/* ---------- ponumerowane kroki ---------- */

.steps {
    counter-reset: krok;
    list-style: none;
    margin: .5rem 0 1.5rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1.6rem;
}

.steps > li {
    counter-increment: krok;
    display: grid;
    grid-template-columns: 2.2rem minmax(0, 1fr);
    gap: 0 1rem;
}

.steps > li::before {
    content: counter(krok);
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 1.5rem;
    line-height: 1.2;
    color: var(--stamp);
    text-align: right;
}

.steps h4 {
    font-family: var(--font-heading);
    font-size: 1.2rem;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    margin: 0 0 .35rem;
}

.steps p { margin: 0; max-width: 64ch; }

/* Nazwa przycisku zacytowana z ekranu. Bez tego zdanie „naciśnij Wgraj
   dokument" zlewa się w jedno i nie widać, gdzie kończy się instrukcja,
   a zaczyna nazwa elementu, którego trzeba szukać wzrokiem. */
.control-name {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: .92em;
    letter-spacing: .06em;
    text-transform: uppercase;
    background: var(--surface);
    border: 1px solid var(--surface-dark);
    border-radius: 2px;
    padding: .1em .45em;
    white-space: nowrap;
}

.help code {
    font-family: var(--font-mono);
    font-size: .9em;
    background: var(--surface);
    border-radius: 2px;
    padding: .1em .35em;
}

.table-wrap { overflow-x: auto; margin: 0 0 1.25rem; }

/* ---------- stan drugiego składnika na liście kont ---------- */

.mfa {
    display: inline-block;
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: .85rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    border-radius: 2px;
    padding: .15rem .5rem;
}

.mfa--on { background: var(--success-bg); color: var(--success); }
.mfa--none { background: var(--surface); color: var(--ink-muted); }

.mfa__codes {
    display: block;
    font-size: .8rem;
    color: var(--ink-muted);
    margin-top: .2rem;
}

/* Zero kodów zapasowych to nie drobiazg: utrata telefonu odetnie tę osobę
   od aplikacji i zostanie tylko reset przez administratora. Widać to na
   liście, zanim ktoś zgubi telefon. */
.mfa__codes--none { color: var(--danger); font-weight: 600; }

/* ---------- wysyłka w toku ---------- */

.button[aria-busy="true"] {
    /* Kursor mówi „nie klikaj", zanim człowiek zdąży spróbować. */
    cursor: progress;
    opacity: .85;
}

.button[aria-busy="true"]::before {
    content: "";
    display: inline-block;
    width: .85em;
    height: .85em;
    margin-right: .55em;
    vertical-align: -.08em;
    border: 2px solid currentColor;
    /* Przezroczysta ćwiartka robi z pierścienia wskazówkę — bez niej obrót
       pełnego okręgu jest niewidoczny. */
    border-top-color: transparent;
    border-radius: 50%;
    animation: kreci-sie .7s linear infinite;
}

@keyframes kreci-sie {
    to { transform: rotate(360deg); }
}

/* Bez ruchu: zostaje sam pierścień, nadal odróżnialny od zwykłego przycisku. */
@media (prefers-reduced-motion: reduce) {
    .button[aria-busy="true"]::before { animation: none; }
}

.button:disabled { pointer-events: none; }

/* ---------- miniatura kodu QR jako odnośnik ---------- */

.card__qr-link {
    display: block;
    /* line-height: 0 — bez tego pod obrazkiem zostaje pasek na zejście
       wiersza i miniatura przestaje trafiać w siatkę karty. */
    line-height: 0;
    border-radius: 2px;
    /* Lupa zamiast łapki: kliknięcie nie prowadzi do dokumentu, tylko
       powiększa sam kod. Od otwierania dokumentu jest przycisk „Otwórz”. */
    cursor: zoom-in;
}

.card__qr-link:hover .card__qr,
.card__qr-link:focus-visible .card__qr {
    border-color: var(--stamp);
}

/* Odstęp pod akapitem wprowadzającym. Klasa, nie atrybut style — polityka
   bezpieczeństwa treści (CSP) blokuje style wpisane w atrybut, a robi to
   po cichu: akapit po prostu przykleja się do tego, co pod nim. */
.field__hint--spaced  { margin-bottom: 1rem; }

/* ---------- wskaźnik siły hasła ---------- */

/* Buduje go skrypt, więc bez JavaScriptu nie ma tu nic — a formularz działa
   jak dotąd: reguły stoją w opisie pod etykietą, a odrzucenie wyjaśnia
   serwer. Wskaźnik pokazuje to samo, co sprawdzi serwer, więc nie zastępuje
   walidacji, tylko oszczędza wysłanie formularza w ciemno. */
/* Dolny odstęp większy niż pod zwykłym polem: przycisk zapisu stał tuż pod
   listą wymagań i czytał się jak jej ostatnia pozycja. */
.strength { margin: .6rem 0 1.75rem; }

.strength__meter {
    display: flex;
    gap: 4px;
    margin: 0 0 .4rem;
}

.strength__segment {
    flex: 1;
    height: 5px;
    border-radius: var(--radius);
    background: var(--surface-dark);
    /* Kolor zmienia się skokowo przy każdym znaku; bez przejścia pasek
       miga. 120 ms to na tyle mało, że nie opóźnia odczytu. */
    transition: background-color .12s ease-out;
}

/* Barwa całego paska bierze się z poziomu, nie z pojedynczego segmentu:
   pasek ma czytać się jako JEDNA informacja, a nie cztery niezależne. */
.strength__meter--1 .strength__segment--on { background: var(--danger); }
.strength__meter--2 .strength__segment--on { background: var(--warning); }
.strength__meter--3 .strength__segment--on,
.strength__meter--4 .strength__segment--on { background: var(--strength-bar); }

.strength__verdict {
    margin: 0 0 .35rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--ink-muted);
    /* Miejsce zajęte z góry: bez tego pojawienie się zdania przy pierwszym
       znaku przesuwa w dół drugie pole i przycisk. */
    min-height: 1.2em;
}

.strength__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: .15rem 1rem;
    font-size: .85rem;
}

.strength__item {
    color: var(--ink-muted);
    padding-left: 1.2rem;
    position: relative;
}

/* Znak PRZED kolorem, nie zamiast niego: samo zielone i czerwone rozróżnia
   tylko ten, kto widzi te barwy. Ptaszek i krzyżyk niosą tę samą informację
   bez koloru, a czytnik ekranu odczyta je jako część tekstu pozycji. */
.strength__item::before {
    position: absolute;
    left: 0;
    content: '·';
}

.strength__item--ok      { color: var(--strength-text); }
.strength__item--ok::before      { content: '✓'; }

.strength__item--missing { color: var(--ink-muted); }
.strength__item--missing::before { content: '✗'; }

.strength__item--idle::before    { content: '·'; }
.card__meta--spaced { margin-bottom: 1.25rem; }

/* Wylogowanie jest formularzem, nie odnośnikiem — zmienia stan, więc nie może
   dać się wywołać samym odwiedzeniem adresu. Ma jednak wyglądać dokładnie jak
   pozostałe pozycje paska; człowiek nie ma powodu widzieć tej różnicy. */
.navbar__logout { margin: 0; display: inline; }

.navbar__logout button {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 1.05rem;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--contrast-text);
    background: none;
    border: 0;
    padding: .35rem .1rem;
    border-bottom: 2px solid transparent;
    cursor: pointer;
}

.navbar__logout button:hover,
.navbar__logout button:focus-visible { border-bottom-color: var(--contrast-text); }

/* ---------- okno potwierdzenia ---------- */

/* ---------- dziennik aplikacji ---------- */

/* Panel: pasek narzędzi, tabela i stopka w jednej ramie. Jedna powierzchnia
   z ramką zamiast trzech osobnych bloków pływających po stronie — dzięki temu
   widać, że filtry, lista i licznik mówią o tym samym zbiorze. */
.panel {
    background: var(--label);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    overflow: hidden;
}

.panel__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .6rem 1.25rem;
    padding: .75rem .9rem;
    border-bottom: 1px solid var(--surface-dark);
}

/* Przełącznik segmentowy: kilka wykluczających się widoków tego samego
   zbioru. Nie przyciski — to nie są akcje, tylko wybór, na co patrzymy. */
.segment {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem;
    padding: .15rem;
    background: var(--surface);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
}

.segment__item {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .3rem .65rem;
    font-family: var(--font-heading);
    font-size: .88rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--ink-muted);
    border-radius: var(--radius);
}

.segment__item:hover {
    color: var(--ink);
    background: var(--surface-dark);
}

.segment__item--selected {
    color: var(--ink);
    background: var(--label);
    box-shadow: var(--shadow);
}

.segment__count {
    font-family: var(--font-mono);
    font-size: .68rem;
    opacity: .7;
}

/* Wybór dnia: etykieta i lista w jednej ramce, tak jak pole szukania —
   oba są „nastawami”, więc mają wyglądać jak jedna rodzina kontrolek. */
.day-picker {
    display: flex;
    align-items: center;
    gap: .4rem;
}

/* JEDNA WYSOKOŚĆ dla wszystkiego, co stoi w pasku filtrów.

   Lista rodzaju brała globalne `min-height: 44px` z reguły dla input/select,
   a pole wyszukiwania NIE — bo `input[type="search"]` nie stoi na tamtej
   liście selektorów. Jedno wychodziło więc o kilkanaście pikseli wyższe od
   drugiego i pasek wyglądał na poskładany z dwóch różnych formularzy.
   Wyrównujemy do wysokości małego przycisku, bo to on wyznacza ton w tym
   rzędzie i stoi obok obu pól. */
.day-picker__input {
    display: flex;
    align-items: center;
    gap: .5rem;
    min-height: 38px;
    padding: .25rem .5rem .25rem .6rem;
    background: var(--surface);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
}

.day-picker__input:focus-within {
    border-color: var(--stamp);
}

.day-picker__label {
    font-family: var(--font-heading);
    font-size: .8rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-muted);
    /* Etykieta stoi OBOK listy, nie nad nią, więc złamana na dwie linie
       rozpycha cały pasek w pionie. „Na stronie” to dwa słowa i łamało się
       dokładnie tak. */
    white-space: nowrap;
}

.day-picker select {
    padding: .1rem 0;
    /* Zdejmujemy 44 px odziedziczone z globalnej reguły pól — o wysokości
       decyduje otoczka, nie sama lista. */
    min-height: 0;
    font-family: var(--font-mono);
    font-size: .8rem;
    color: var(--ink);
    background: none;
    border: 0;
}

.day-picker select:focus {
    outline: none;
}

.search {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex: 1 1 18rem;
}

.search__input {
    display: flex;
    align-items: center;
    gap: .45rem;
    flex: 1 1 auto;
    min-height: 38px;
    padding: .3rem .6rem;
    background: var(--surface);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
}

.search__input:focus-within {
    border-color: var(--stamp);
}

.search__icon {
    font-size: 1rem;
    line-height: 1;
    color: var(--ink-muted);
}

.search input {
    width: 100%;
    padding: 0;
    font-size: .85rem;
    color: var(--ink);
    background: none;
    border: 0;
}

.search input:focus {
    outline: none;
}

/* Nagłówek kolumn i wiersze dzielą tę samą siatkę — to ona robi z listy
   tabelę. Bez wspólnej siatki nagłówek stałby nad niczym.

   ALE KOLUMNY DEFINIUJE KAŻDA TABELA U SIEBIE, nie ta reguła. Trzy tabele
   (dziennik, kronika, struktura bazy) dzielą tu wygląd, a mają różną liczbę
   kolumn. Gdy kolumny stały w regule wspólnej, wąskoekranowy wariant DZIENNIKA
   po cichu przestawiał także kronikę — bo selektory były równie szczegółowe,
   więc wygrywał ten stojący niżej w pliku. Nagłówek kroniki lądował wtedy
   w czterech torach zamiast pięciu, piąta kolumna zawijała się pod spód,
   a opis zdarzenia ściskało do kilkunastu pikseli. */
.grid__head,
.grid__row {
    display: grid;
    align-items: center;
    gap: .75rem;
}

.grid__head {
    padding: .55rem .9rem;
    font-size: .72rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-muted);
    background: var(--surface);
    border-bottom: 1px solid var(--surface-dark);
}

.grid__entry + .grid__entry {
    border-top: 1px solid var(--surface-dark);
}

.grid__row {
    padding: .72rem .9rem;
    cursor: pointer;
    list-style: none;
}

.grid__row--muted {
    cursor: default;
}

/* Domyślna strzałka <details> stoi przed treścią i rozbija siatkę kolumn. */
.grid__row::-webkit-details-marker {
    display: none;
}

.grid__entry:hover {
    background: var(--surface);
}

details.grid__entry[open] {
    background: var(--surface);
}

/* Odznaka poziomu zamiast gołego słowa: kolor niesie tu informację, a kształt
   pigułki oddziela nazwę poziomu od treści komunikatu. */
.badge {
    justify-self: start;
    padding: .12rem .45rem;
    font-family: var(--font-mono);
    font-size: .66rem;
    letter-spacing: .06em;
    color: var(--ink-muted);
    background: var(--surface-dark);
    border-radius: 999px;
}

.badge--error  { color: var(--danger); background: var(--danger-bg); }
.badge--warning { color: var(--warning);  background: var(--warning-bg); }

/* ---------- barwy zdarzeń kroniki ---------- */

/* Każde zdarzenie ma własny odcień, ale odcienie NIE SĄ przypadkowe: dobrane
   są rodzinami, żeby kolor coś znaczył, zanim ktokolwiek przeczyta napis.
   Czerwienie to rzeczy niepokojące, zielenie — dokumenty, błękity — wejścia
   i telefon, fiolety — hasła, purpury — zmiany w strukturze i uprawnieniach.
   Dziewiętnaście barw dobranych losowo byłoby konfetti, nie informacją.
   
   Cała kolorystyka wychodzi z JEDNEJ liczby (--hue) i dwóch reguł na motyw,
   zamiast trzydziestu ośmiu tokenów w palecie. Jasność i nasycenie zostają
   stałe, więc żadna odznaka nie wypadnie ciemniejsza ani bledsza od reszty. */
.event {
    /* Wartość zapasowa dla zdarzenia dopisanego w przyszłości bez własnego
       odcienia — wygląda wtedy jak dotąd, szaro, zamiast zniknąć. */
    --hue: 215;

    color: hsl(var(--hue) 60% 30%);
    background: hsl(var(--hue) 65% 93%);
}

/* wejścia i telefon — błękity */
.event--logowanie          { --hue: 210; }
.event--wylogowanie        { --hue: 228; }
.event--telefon_sparowany  { --hue: 192; }
.event--telefon_wymieniony { --hue: 176; }

/* hasła — fiolety */
.event--haslo_zmienione     { --hue: 265; }
.event--haslo_reset_zadanie { --hue: 283; }
.event--haslo_zresetowane   { --hue: 248; }
.event--haslo_link_wyslany  { --hue: 294; }

/* dokumenty — zielenie */
.event--dokument_wgrany      { --hue: 138; }
.event--dokument_podmieniony { --hue: 96; }
.event--dokument_wycofany    { --hue: 62; }

/* konta i uprawnienia — purpury i błękit */
.event--konto_utworzone   { --hue: 158; }
.event--konto_przywrocone { --hue: 120; }
.event--rola_zmieniona    { --hue: 305; }
.event--baza_zaktualizowana { --hue: 325; }

/* rzeczy, przy których warto się zatrzymać — czerwienie i pomarańcze */
.event--konto_skasowane       { --hue: 28; }
.event--dziennik_wyczyszczony { --hue: 40; }
.event--drugi_skladnik_zdjety { --hue: 14; }
.event--logowanie_nieudane    { --hue: 2; }
.event--klucz_awaryjny        { --hue: 350; }
.event--konto_usuniete_trwale { --hue: 336; }
.event--kopia_pobrana         { --hue: 48; }
.event--haslo_nadane          { --hue: 21; }

/* Wariant nocny stoi TUTAJ, nie przy palecie ciemnej osiemset linii wyżej:
   selektory `.event` mają równą szczegółowość, więc decyduje kolejność
   w pliku. Reguła zapisana przy palecie przegrywała z jasną i odznaki
   świeciły jasnymi plamami na ciemnym tle.

   Dwa bloki, bo motyw ma dwa źródła — ustawienie systemu i świadomy wybór
   zapisany w atrybucie <html data-theme>. To ten sam podział, co w palecie. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="jasny"]) .event {
        color: hsl(var(--hue) 70% 74%);
        background: hsl(var(--hue) 40% 20%);
    }
}

:root[data-theme="ciemny"] .event {
    color: hsl(var(--hue) 70% 74%);
    background: hsl(var(--hue) 40% 20%);
}

/* Data przygaszona, godzina mocna: przy jednym dniu na stronie data jest tłem,
   a szuka się zawsze godziny. */
.time {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    font-family: var(--font-mono);
    font-size: .76rem;
}

.time__day   { color: var(--ink-muted); font-size: .68rem; }
.time__hour { color: var(--ink); }

/* Jedna linijka i wielokropek. Całość czeka po rozwinięciu — wiersz ma
   pozwolić PRZEBIEC listę, nie przeczytać ją w całości. */
.grid__text {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: .88rem;
    color: var(--ink);
}

.grid__mark {
    justify-self: end;
    font-size: .85rem;
    line-height: 1;
    color: var(--ink-muted);
    transition: transform .12s ease;
}

.grid__entry:hover .grid__mark {
    color: var(--ink);
}

details.grid__entry[open] > .grid__row .grid__mark {
    transform: rotate(90deg);
}

/* Szczegół bierze całą szerokość, a przynależność do wiersza nad nim pokazuje
   kreska przy lewej krawędzi. Wcięcie równe kolumnom wyglądało porządnie, ale
   zabierało kilkanaście centymetrów dokładnie tej treści, która potrzebuje ich
   najbardziej — długich ścieżek w stosie wywołań. */
.grid__detail {
    margin: 0 .9rem .7rem 1.6rem;
    padding: .2rem 0 0 .8rem;
    border-left: 1px solid var(--surface-dark);
}

.grid__full {
    margin: 0 0 .5rem;
    font-size: .88rem;
    line-height: 1.6;
    color: var(--ink);
    overflow-wrap: anywhere;
}

.grid__count {
    margin: 0 0 .35rem;
    font-size: .7rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-muted);
}

/* Tło etykiety, nie podkładu: rozwinięty wiersz jest już wcięty w podkład,
   więc stos na tym samym kolorze rozpływałby się w niczym. */
.stack {
    padding: .5rem .6rem;
    background: var(--label);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
    overflow-x: auto;
}

/* Stos wywołań bez zawijania — ścieżka przełamana w połowie przestaje być
   ścieżką. Przewijanie zamyka się tutaj, więc strona nie jeździ w poziomie. */
.stack__row {
    font-family: var(--font-mono);
    font-size: .72rem;
    line-height: 1.7;
    white-space: pre;
    color: var(--ink-muted);
}

/* Struktura bazy: dwie kolumny i blok „czeka” nad listą wykonanych. */
/* Dziennik: poziom, godzina, komunikat, strzałka. */
.grid__head--log,
.grid__row--log {
    grid-template-columns: 6.5rem 7rem minmax(0, 1fr) 1.5rem;
}

.grid__head--schema,
.grid__row--schema {
    grid-template-columns: 10rem minmax(0, 1fr);
}

.schema__pending {
    padding: 1rem .9rem;
    border-bottom: 1px solid var(--surface-dark);
    box-shadow: inset 3px 0 0 var(--warning);
}

.schema__heading {
    margin: 0 0 .5rem;
    font-size: .95rem;
    color: var(--ink);
}

.schema__list {
    margin: 0 0 .9rem;
    padding-left: 1.1rem;
    font-family: var(--font-mono);
    font-size: .78rem;
    line-height: 1.8;
    color: var(--ink-muted);
}

/* Kronika: pięć kolumn — kiedy, kto, co, szczegóły, adres. Poza liczbą kolumn
   korzysta z tych samych reguł co dziennik, bo to ta sama czynność: lista
   zdarzeń z filtrem i stopką. */
.grid__head--audit,
.grid__row--audit {
    grid-template-columns: 7rem 8rem 11rem minmax(0, 1fr) 8rem;
}

.audit__actor {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: .82rem;
    color: var(--ink);
}

.audit__address {
    justify-self: end;
    font-family: var(--font-mono);
    font-size: .7rem;
    color: var(--ink-muted);
}

/* Trzydzieści cztery ery kolumn stałych nie zmieszczą się w wąskim panelu —
   zostawiają opisowi kilkanaście pikseli i wielokropek zamiast zdania. Progi
   są więc dwa i oddają miejsce po kolei: najpierw adres, potem cały układ. */
@media (max-width: 76rem) {
    /* Adres znika pierwszy: przydaje się przy jednej sprawie na sto,
       a zabiera miejsce potrzebne zawsze. */
    .grid__head--audit,
    .grid__row--audit {
        grid-template-columns: 6.5rem 7rem 10rem minmax(0, 1fr);
    }

    .grid__head--audit span:last-child,
    .audit__address {
        display: none;
    }
}

@media (max-width: 56rem) {
    /* Opis schodzi do własnego wiersza i wreszcie ma całą szerokość.
       Nagłówek kolumn znika, bo nad układem dwuwierszowym niczego już nie
       opisuje — a zostawiony kłamałby o tym, co jest pod nim. */
    .grid__head--audit {
        display: none;
    }

    .grid__row--audit {
        grid-template-columns: auto auto minmax(0, 1fr);
        row-gap: .35rem;
    }

    .grid__row--audit .grid__text {
        grid-column: 1 / -1;
        white-space: normal;
        line-height: 1.5;
    }
}


.panel__footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .5rem 1rem;
    padding: .65rem .9rem;
    background: var(--surface);
    border-top: 1px solid var(--surface-dark);
}

.panel__count {
    margin: 0;
    font-size: .8rem;
    color: var(--ink-muted);
}

.panel__file::before {
    content: '·';
    margin: 0 .35rem;
}

.panel__empty {
    margin: 0;
    padding: 2rem .9rem;
    text-align: center;
    color: var(--ink-muted);
}

/* Przycisk, który wygląda jak odnośnik: rzecz robiona raz na długi czas
   i stojąca w stopce nie może krzyczeć tak samo jak akcje w pasku. Kolor
   odrzutu zostaje, bo operacja jest nieodwracalna. */
.link-button {
    padding: .25rem 0;
    font-family: inherit;
    font-size: .8rem;
    color: var(--ink-muted);
    background: none;
    border: 0;
    border-bottom: 1px solid transparent;
    cursor: pointer;
}

.link-button:hover {
    border-bottom-color: currentColor;
}

.link-button--danger {
    color: var(--danger);
}

/* Przełącznik roli pod odznaką, nie obok — tak samo jak podpis „własnej roli
   nie zmienisz” przy własnym koncie. Obok odznaki stykał się z nią bez odstępu
   i w każdym wierszu wypadał w innym miejscu, zależnie od długości roli. */
.role__change {
    display: block;
    margin-top: .25rem;
    font-size: .75rem;
}

.pager {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.pager__step {
    padding: .25rem .6rem;
    font-size: .82rem;
    text-decoration: none;
    color: var(--ink);
    background: var(--label);
    border: 1px solid var(--surface-dark);
    border-radius: var(--radius);
}

.pager__step:hover {
    background: var(--surface-dark);
}

.pager__step--disabled {
    color: var(--ink-muted);
    background: none;
    border-color: transparent;
}

.pager__number {
    font-family: var(--font-mono);
    font-size: .75rem;
    color: var(--ink-muted);
}

@media (max-width: 44rem) {
    /* Na wąskim ekranie kolumna poziomu zjada połowę miejsca na treść.
       Zostaje kolor nacięcia i godzina. WYŁĄCZNIE dziennik — kronika ma
       własne progi, a wspólny selektor przestawiał ją tu po cichu. */
    .grid__head--log,
    .grid__row--log {
        grid-template-columns: 5.5rem minmax(0, 1fr) 1.5rem;
    }

    .grid__head--log span:first-child,
    .grid__row--log .badge {
        display: none;
    }

    .grid__detail {
        margin-left: .9rem;
    }
}

/* Konto skasowane: przygaszony wiersz i wyraźny znacznik przy nazwie.
   Widoczne, bo jego nazwa i adres nadal blokują zakładanie nowego — ale
   wyraźnie odsunięte od kont żywych, żeby lista nie myliła. */
.account--deleted td {
    opacity: .55;
}

.tag--deleted {
    display: inline-block;
    margin-left: .5rem;
    padding: .05rem .4rem;
    font-family: var(--font-mono);
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--danger);
    border: 1px solid var(--danger);
    border-radius: var(--radius);
}

/* Nagłówek listy: tytuł po lewej, przycisk akcji po prawej.

   Przycisk trzyma się góry, na wysokości tytułu — nie środka bloku, bo
   podtytuł bywa dwuwierszowy i przycisk skakałby w pionie zależnie od
   długości zdania. Przy wąskim ekranie kolumny zawijają się pod siebie. */
.list-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem 1.5rem;
    flex-wrap: wrap;
}

.list-header__actions:empty {
    display: none;
}

/* Okno z formularzem zakładania konta.

   Kartą jest SAMO OKNO, a nie formularz w środku. Przy odwrotnym podziale
   nagłówek „Nowe konto” zostawał poza kartą i wisiał na przyciemnionym tle,
   a krzyżyk odlatywał w róg okna zamiast trzymać się rogu karty. */
.form-dialog {
    width: min(56rem, calc(100vw - 2.5rem));
    max-height: calc(100vh - 3rem);
    overflow-y: auto;
    padding: 1.75rem 1.85rem 1.5rem;
    background: var(--label);
    color: var(--ink);
    border: 0;
    border-radius: var(--radius);
    box-shadow: var(--shadow-raised);
}

/* Formularz w oknie nie rysuje już własnej karty — byłaby kartą w karcie. */
.form-dialog .form {
    padding: 0;
    background: none;
    box-shadow: none;
}

.form-dialog::backdrop {
    background: rgba(17, 20, 24, .55);
}

.form-dialog .new-account {
    position: relative;
}

.form-dialog h2 {
    margin-top: 0;
}

.form-dialog__close {
    position: absolute;
    top: -.15rem;
    right: 0;
    padding: .1rem .5rem;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--ink-muted);
    background: none;
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
}

.form-dialog__close:hover {
    color: var(--ink);
}

.confirm {
    max-width: min(34rem, calc(100vw - 2.5rem));
    padding: 1.75rem 1.85rem 1.5rem;
    background: var(--label);
    color: var(--ink);
    border: 0;
    /* Czerwone nacięcie wykrojnika — ten sam znak, którym aplikacja oznacza
       operacje nieodwracalne w instrukcji i przy rozdziałach dla
       administratora. Okno mówi, czym jest, zanim człowiek przeczyta tytuł. */
    border-left: 3px solid var(--danger);
    border-radius: var(--radius);
    box-shadow: var(--shadow-raised);
}

.confirm::backdrop {
    background: rgba(17, 20, 24, .55);
}

.confirm__title {
    font-family: var(--font-heading);
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: .03em;
    text-transform: uppercase;
    margin: 0 0 .6rem;
}

.confirm__body {
    margin: 0 0 1.5rem;
    max-width: 52ch;
    line-height: 1.65;
    color: var(--ink-muted);
    /* Podsumowanie pól przy zakładaniu konta przychodzi jako osobne wiersze
       w textContent. pre-line zachowuje te przejścia, a nadmiarowe spacje
       z szablonu i tak zwija — teksty stałe wyglądają jak dotąd. */
    white-space: pre-line;
}

.confirm__actions {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
}

/* Anulowanie stoi pierwsze i jest tym, na czym startuje skupienie: przy
   pytaniu o rzecz nieodwracalną odruch ma prowadzić do wycofania się. */
.confirm__actions .button { margin: 0; }
