/* =========================================================================
   SystemBeton — warstwa wizualna pod markę AMFIBUD BETON.

   Pisane mobile-first: reguły bazowe obsługują telefon, a `min-width`
   dokłada to, co ma sens dopiero na szerszym ekranie. Zmienne CSS nie
   działają wewnątrz `@media`, więc punkty łamania są literałami.

   Kolejność sekcji: tokeny -> reset -> typografia -> układ -> marka ->
   nawigacja -> komponenty -> ekrany -> utility -> preferencje.
   ========================================================================= */

/* ---------- 1. tokeny (tryb jasny) ---------- */

:root {
    color-scheme: light dark;

    /* Zieleń logo ma 2,36:1 na bieli — nie nadaje się ani na tekst, ani na
       obrys kontrolki (WCAG 1.4.11 wymaga 3:1). Stąd trzy role marki:
       --brand wypełnia, --brand-700/-900 pisze, --brand-soft podkłada. */
    --brand: #7aba3c;
    --brand-600: #5d9a2e;
    --brand-700: #4e7d22;
    --brand-800: #456f1e;
    --brand-900: #3f6a1b;
    --brand-soft: #edf5e3;

    --bg: #f4f6f2;
    --card: #ffffff;
    --card-2: #fafbf8;
    --fg: #1b1d1a;
    --muted: #5c6159;
    --line: #dfe3da;
    --line-strong: #868f82;

    --ok: #2a6b36;
    --ok-soft: #eaf6ec;
    --err: #a3231d;
    --err-soft: #fdecea;
    --warn-bg: #ffe8a3;
    --warn-fg: #3d3000;

    /* accent-color natywnych checkboxów — biały „ptaszek” musi mieć kontrast */
    --ui-accent: #4e7d22;

    /* Alias na czas migracji: `--accent` z poprzedniego motywu. */
    --accent: var(--brand-700);

    --font-sans: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI",
        Roboto, "Noto Sans", "Liberation Sans", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono",
        Menlo, Consolas, "Liberation Mono", monospace;

    --radius-sm: 0.375rem;
    --radius: 0.625rem;
    --radius-lg: 1rem;
    --radius-pill: 999px;

    --shadow-sm: 0 1px 2px rgb(16 20 14 / 0.06), 0 1px 3px rgb(16 20 14 / 0.08);
    --shadow-md: 0 2px 4px rgb(16 20 14 / 0.05), 0 8px 20px -6px rgb(16 20 14 / 0.12);
    --shadow-lg: 0 12px 32px -10px rgb(16 20 14 / 0.18);

    --sp-1: 0.25rem;
    --sp-2: 0.5rem;
    --sp-3: 0.75rem;
    --sp-4: 1rem;
    --sp-5: 1.5rem;
    --sp-6: 2rem;
    --sp-7: 3rem;

    --z-tabbar: 40;
    --z-header: 30;
    --z-error: 1000;

    --page-max: 64rem;
    --tabbar-h: 3.75rem;
    /* Wysokość topbara: logo 2rem + 2×0.75rem paddingu + 1 px obrysu. Od 40rem
       logo rośnie do 2.375rem, więc blok w sekcji 6 podnosi token do 4rem.
       Topbar ma ten token jako min-height, żeby wartość i rzeczywistość się zgadzały. */
    --header-h: 3.625rem;
    /* Szerokość pionowej szyny nawigacji (tablet w poziomie, desktop). */
    --szyna-w: 5.5rem;
    /* Dolna krawędź topbara — jedyny token, do którego przyklejają się szyna
       i panele kreatorów. Wcięcie pod pasek stanu (PWA na tablecie) podnosi
       padding-top topbara ponad --sp-3, stąd korekta. */
    --topbar-dol: calc(var(--header-h) + max(0px, env(safe-area-inset-top, 0px) - var(--sp-3)));
    --tap: 2.75rem; /* 44 px — minimalny cel dotykowy */
    /* Ekran ważenia: operator stoi przy wadze, często w rękawicach roboczych.
       44 px to minimum WCAG, a nie rozmiar, w który da się trafić kciukiem
       w grubej rękawicy — stąd osobny, większy cel dla kafli i klawiatury. */
    --tap-lg: 4rem;

    /* Paleta serii wykresu. To NIE są kolory marki: marka ma jedną zieleń w trzech
       jasnościach, a serie muszą się od siebie różnić odcieniem, nie jasnością.
       Osiem slotów przewalidowano skryptem `validate_palette.js` z umiejętności
       `dataviz` na realnych powierzchniach tej aplikacji (--card w obu motywach):
       pasmo jasności, próg nasycenia, rozróżnialność sąsiednich par przy
       daltonizmie (najgorsza para ΔE 9,1 przy progu 8) i kontrast.

       Kolejność slotów jest mechanizmem, nie kosmetyką — to ona zapewnia, że
       sąsiednie serie są rozróżnialne przy zaburzeniach widzenia barw. Nie
       przestawiać i nie dokładać dziewiątego koloru: ogon kartoteki składa się
       w jedną wyciszoną serię (patrz WykresStanu.razor).

       Trzy slotu na białym tle nie dobijają kontrastu 3:1 (aqua, żółty, magenta) —
       dlatego wykres ma zawsze legendę z wartościami i tabelę pod spodem. */
    --seria-1: #2a78d6;
    --seria-2: #eb6834;
    --seria-3: #1baf7a;
    --seria-4: #eda100;
    --seria-5: #e87ba4;
    --seria-6: #008300;
    --seria-7: #4a3aa7;
    --seria-8: #e34948;
}

/* ---------- 2. tokeny (tryb ciemny) ---------- */

@media (prefers-color-scheme: dark) {
    :root {
        --brand: #8cbf3d;
        --brand-600: #9bcf4e;
        --brand-700: #7aba3c;
        --brand-800: #8cbf3d;
        --brand-900: #9bcf4e;
        --brand-soft: #1f2a16;

        --bg: #121513;
        --card: #1a1e1b;
        --card-2: #232823;
        --fg: #e9ece6;
        --muted: #a3aa9e;
        --line: #2c312d;
        --line-strong: #6a7469;

        --ok: #7dd98f;
        --ok-soft: #16211a;
        --err: #f09189;
        --err-soft: #241716;
        --warn-bg: #4a3b10;
        --warn-fg: #ffe8a3;

        --ui-accent: #8cbf3d;

        /* Cień na ciemnym tle jest niewidoczny — tu karty odróżnia jaśniejsze
           --card plus obrys, a cień tylko podbija głębię. */
        --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5);
        --shadow-md: 0 10px 24px -8px rgb(0 0 0 / 0.65);
        --shadow-lg: 0 18px 40px -12px rgb(0 0 0 / 0.75);

        /* Te same osiem odcieni przestopniowanych pod ciemne tło, nie automatyczne
           odwrócenie jasnych. Zestaw przewalidowany osobno dla --card: #1a1e1b —
           tam wszystkie osiem dobija 3:1. */
        --seria-1: #3987e5;
        --seria-2: #d95926;
        --seria-3: #199e70;
        --seria-4: #c98500;
        --seria-5: #d55181;
        --seria-6: #008300;
        --seria-7: #9085e9;
        --seria-8: #e66767;
    }
}

/* ---------- 3. reset i baza ---------- */

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--fg);
    font: 400 1rem/1.6 var(--font-sans);
    -webkit-tap-highlight-color: transparent;
}

img,
svg,
picture {
    max-width: 100%;
}

:where(a, button, input, select, textarea, summary,
       [tabindex]:not([tabindex="-1"])):focus-visible {
    outline: 2px solid var(--brand-700);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* FocusOnNavigate (Routes.razor) ustawia focus na <h1> po każdej nawigacji,
   żeby czytnik ekranu zaczynał czytać od nagłówka. Element dostaje przy tym
   tabindex="-1", a przeglądarka rysuje wokół tytułu swój domyślny pierścień —
   wygląda to jak usterka na każdej podstronie. Samo wyłączenie własnego stylu
   nie wystarczy, trzeba zdjąć też domyślny. */
:where([tabindex="-1"]):focus {
    outline: none;
}

::selection {
    background: var(--brand-soft);
    color: var(--brand-900);
}

/* ---------- 4. typografia ---------- */

h1 {
    font-size: clamp(1.75rem, 1.3rem + 2.2vw, 2.5rem);
    line-height: 1.15;
    letter-spacing: -0.022em;
    font-weight: 700;
    text-wrap: balance;
    margin: 0 0 var(--sp-3);
    /* FocusOnNavigate skacze na h1 — nie może wylądować pod sticky headerem. */
    scroll-margin-top: calc(var(--topbar-dol) + 1rem);
}

h2 {
    font-size: clamp(1.15rem, 1.02rem + 0.65vw, 1.375rem);
    line-height: 1.25;
    letter-spacing: -0.012em;
    font-weight: 650;
    margin: 0 0 var(--sp-2);
}

.lead {
    font-size: clamp(1.0625rem, 1rem + 0.3vw, 1.1875rem);
    line-height: 1.6;
    color: var(--muted);
    max-width: 62ch;
    text-wrap: pretty;
}

a {
    color: var(--brand-900);
}

code {
    font-family: var(--font-mono);
    background: var(--card-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.05rem 0.3rem;
    font-size: 0.875em;
}

time {
    font-variant-numeric: tabular-nums;
}

/* ---------- 5. układ ---------- */

.page {
    max-width: var(--page-max);
    margin: 0 auto;
    padding-inline: max(1.25rem, env(safe-area-inset-left), env(safe-area-inset-right));
    /* Rezerwa pod dolny pasek zakładek — bez niej stopka chowa się pod nim. */
    padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

.topbar {
    position: sticky;
    top: 0;
    z-index: var(--z-header);
    min-height: var(--topbar-dol);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-3) 0;
    padding-top: max(var(--sp-3), env(safe-area-inset-top));
    border-bottom: 1px solid var(--line);
    background: color-mix(in srgb, var(--bg) 92%, transparent);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
}

.topbar-right {
    display: flex;
    align-items: center;
    gap: var(--sp-5);
    flex-wrap: wrap;
}

.user-box {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: 0.875rem;
}

.user-box form {
    margin: 0;
}

.user-name {
    color: var(--fg);
    font-weight: 600;
    max-width: 9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Szyna + treść. Osobne opakowanie, nie grid na .page: element gridu jest
   ograniczony do własnego obszaru siatki, więc sticky topbar w gridzie nie
   miałby się gdzie przesunąć. Poniżej progu szyny (sekcja 7) to zwykły blok. */
.uklad {
    flex: 1;
    min-width: 0;
}

.content {
    padding: var(--sp-6) 0;
}

.footer {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-4);
    flex-wrap: wrap;
    padding: var(--sp-5) 0;
    border-top: 1px solid var(--line);
    font-size: 0.875rem;
    color: var(--muted);
}

.skip-link {
    position: absolute;
    left: -9999px;
}

.skip-link:focus-visible {
    left: 1rem;
    top: 1rem;
    z-index: 999;
    padding: var(--sp-2) var(--sp-3);
    background: var(--card);
    border: 1px solid var(--brand-700);
    border-radius: var(--radius-sm);
}

/* Sprite ikon — obecny w DOM, ale niewidoczny. */
.sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* ---------- 6. marka ---------- */

.brand {
    display: block;
    line-height: 0;
    flex: none;
}

.brand img {
    display: block;
    height: 2rem;
    width: auto;
}

@media (min-width: 40rem) {
    :root {
        /* Większe logo = wyższy topbar; token musi za nim nadążyć. */
        --header-h: 4rem;
    }

    .brand img {
        height: 2.375rem;
    }
}

.brand-duze {
    margin-bottom: var(--sp-5);
}

.brand-duze img {
    height: auto;
    width: 100%;
    max-width: 14rem;
    margin-inline: auto;
}

/* ---------- 7. nawigacja ---------- */

/* Jedna nawigacja, dwa położenia. NavMenu renderuje się dwa razy: jako pionowa
   szyna (`.szyna`) i jako dolny pasek zakładek (`.tabbar`). O tym, który jest
   widoczny, decyduje ORIENTACJA, nie szerokość: tablet w poziomie i desktop
   dostają szynę, tablet w pionie i telefon — pasek. Dzięki temu 768×1024
   i 800×1280 (tablety w pionie) nie wpadają w martwe pasmo między progami
   szerokości. Czysty CSS i zwykłe linki — layout renderuje się statycznym SSR,
   więc żaden `@onclick` by tu nie zadziałał. */
.szyna {
    display: none;
}

/* Dolny pasek zakładek: `position: fixed`, poza .page. */
.tabbar {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: var(--z-tabbar);
    display: flex;
    gap: var(--sp-1);
    padding: var(--sp-1) var(--sp-2);
    padding-bottom: calc(var(--sp-1) + env(safe-area-inset-bottom, 0px));
    background: color-mix(in srgb, var(--card) 92%, transparent);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    border-top: 1px solid var(--line);
}

.tab-link {
    flex: 1 1 0;
    min-height: var(--tap);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.1rem;
    padding: 0.375rem 0.25rem;
    border-radius: var(--radius-sm);
    color: var(--muted);
    text-decoration: none;
    font-size: 0.6875rem;
    line-height: 1.1;
    font-weight: 500;
    text-align: center;
}

.tab-link svg {
    width: 1.5rem;
    height: 1.5rem;
    flex: none;
}

.tab-link.active {
    color: var(--brand-900);
    background: var(--brand-soft);
    font-weight: 650;
}

@media (max-width: 22.5rem) {
    .tab-link {
        font-size: 0.625rem;
    }
}

/* Pozycje biurowe (Panel, Prezentacja): na telefonie poza paskiem — cztery
   zakładki mieszczą się czytelnie, piąta się łamie. Wracają od 40rem. */
.tabbar .biurowa {
    display: none;
}

/* Tablet w pionie: pasek zostaje, ale cele rosną do rozmiaru kafli kreatora
   i wchodzi komplet pozycji. */
@media (min-width: 40rem) and (orientation: portrait) {
    :root {
        --tabbar-h: 4.75rem;
    }

    .tabbar {
        justify-content: center;
        padding-inline: var(--sp-4);
    }

    .tabbar .biurowa {
        display: flex;
    }

    .tab-link {
        flex: 0 1 9rem;
        min-height: var(--tap-lg);
        gap: 0.2rem;
        font-size: 0.8125rem;
    }

    .tab-link svg {
        width: 1.75rem;
        height: 1.75rem;
    }
}

/* Warunek szyny — trzymaj w zgodzie z sekcjami 9f i 11 (tam ten sam warunek
   znosi rezerwę pod pasek). Próg 40rem to dolna granica sensu szyny: telefon
   w poziomie (844×390) też ją dostaje, a jej zawartość przewija się wewnątrz. */
@media (min-width: 40rem) and (orientation: landscape) {
    :root {
        /* 5.5rem szyny + 1.5rem odstępu + 64rem kolumny treści. */
        --page-max: 72rem;
    }

    .page {
        padding-bottom: 0;
    }

    .tabbar {
        display: none;
    }

    .uklad {
        display: grid;
        grid-template-columns: var(--szyna-w) minmax(0, 1fr);
        column-gap: var(--sp-5);
    }

    .szyna {
        display: flex;
        flex-direction: column;
        gap: var(--sp-1);
        /* Bez tego element gridu rozciąga się na cały wiersz i sticky nie ma
           się gdzie przesunąć. */
        align-self: start;
        position: sticky;
        top: var(--topbar-dol);
        max-height: calc(100vh - var(--topbar-dol));
        max-height: calc(100dvh - var(--topbar-dol));
        overflow-y: auto;
        /* 0.25rem w poziomie, żeby overflow nie ucinał pierścienia fokusu. */
        padding: var(--sp-3) 0.25rem;
        scrollbar-width: none;
    }

    .szyna-link {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 0.2rem;
        min-height: var(--tap-lg);
        padding: var(--sp-2) var(--sp-1);
        border-radius: var(--radius);
        color: var(--muted);
        text-decoration: none;
        font-size: 0.75rem;
        line-height: 1.1;
        font-weight: 500;
        text-align: center;
    }

    .szyna-link svg {
        width: 1.625rem;
        height: 1.625rem;
        flex: none;
    }

    .szyna-link:hover {
        color: var(--fg);
        background: var(--card-2);
    }

    .szyna-link.active {
        color: var(--brand-900);
        background: var(--brand-soft);
        font-weight: 650;
    }
}

/* ---------- 8. komponenty ---------- */

.card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--sp-4);
    margin: var(--sp-5) 0;
}

@media (min-width: 48rem) {
    .card {
        padding: var(--sp-5);
    }
}

/* Nagłówek ekranu (NaglowekMagazynu): tytuł i opis po lewej, akcje po prawej,
   gdy jest miejsce; na wąskim ekranie akcje schodzą pod opis. */
.naglowek-ekranu {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-2) var(--sp-5);
    margin-bottom: var(--sp-4);
}

.naglowek-tytul {
    flex: 1 1 24rem;
    min-width: 0;
}

.naglowek-tytul .lead {
    margin: 0;
}

/* Pasek akcji ekranu: przyciski obok siebie, zawijane; jedno miejsce zamiast
   akcji rozsianych po akapitach. */
.akcje {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3);
    margin: var(--sp-3) 0;
}

.naglowek-ekranu .akcje {
    margin: 0;
    align-self: center;
}

/* Podzakładki magazynu zawijają się, nie przewijają: pięć pigułek to jeden
   rząd na tablecie i 2–3 na telefonie, a przewijanie poziome chowałoby piątą
   bez żadnej wskazówki. */
.podzakladki {
    margin: 0 0 var(--sp-5);
}

/* Lokalny przełącznik sekcji tuż pod podzakładkami (kartoteki). */
.sekcje-lokalne {
    margin-top: 0;
}

.btn {
    font: inherit;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding: 0.45rem 1rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--card);
    color: var(--fg);
    text-decoration: none;
    cursor: pointer;
}

.btn:hover:not(:disabled) {
    border-color: var(--brand-600);
    background: var(--brand-soft);
}

.btn:disabled {
    opacity: 0.6;
    cursor: default;
}

/* Przycisk główny odwraca się między motywami. W jasnym „limonka + ciemny
   tekst” dałaby czytelną etykietę, ale granica przycisku miałaby 2,36:1
   względem białej karty. W ciemnym odwrotnie: #4e7d22 na #1a1e1b to 3,44:1,
   czyli ciemnozielony przycisk znika. */
.btn-primary {
    background: var(--brand-700);
    border-color: var(--brand-700);
    color: #ffffff;
    font-weight: 600;
}

.btn-primary:hover:not(:disabled) {
    background: var(--brand-800);
    border-color: var(--brand-800);
    color: #ffffff;
}

@media (prefers-color-scheme: dark) {
    .btn-primary,
    .btn-primary:hover:not(:disabled) {
        color: #10140f;
    }
}

.btn-link {
    font: inherit;
    padding: 0;
    border: 0;
    background: none;
    color: var(--muted);
    text-decoration: underline;
    cursor: pointer;
}

.btn-link:hover:not(:disabled) {
    color: var(--fg);
}

.btn-link:disabled {
    opacity: 0.6;
    cursor: default;
}

.field {
    margin-bottom: var(--sp-4);
}

.field label {
    display: block;
    margin-bottom: var(--sp-1);
    font-size: 0.875rem;
    color: var(--muted);
}

/* font-size nigdy poniżej 1rem: iOS Safari przybliża stronę przy focusie
   na input mniejszy niż 16 px i już z tego zoomu nie wychodzi. */
.field input,
.field select,
.field textarea {
    font: inherit;
    width: 100%;
    min-height: var(--tap);
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--card);
    color: var(--fg);
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
    outline: 2px solid var(--brand-700);
    outline-offset: 1px;
}

.inline-field {
    display: flex;
    align-items: flex-end;
    gap: var(--sp-3);
    flex-wrap: wrap;
}

.inline-field label {
    flex-basis: 100%;
}

.inline-field input {
    max-width: 18rem;
}

.form-error,
.validation-message {
    color: var(--err);
    font-size: 0.875rem;
}

.form-error {
    margin: 0 0 var(--sp-4);
    padding: var(--sp-3);
    background: var(--err-soft);
    border-radius: var(--radius-sm);
}

.badge {
    display: inline-block;
    margin-left: var(--sp-2);
    padding: 0.05rem 0.45rem;
    border: 1px solid var(--brand-600);
    border-radius: var(--radius-pill);
    color: var(--brand-900);
    background: var(--brand-soft);
    font-size: 0.75rem;
    font-weight: 600;
}

.perms {
    margin: var(--sp-4) 0;
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

.perms legend {
    padding: 0 var(--sp-1);
    color: var(--muted);
    font-size: 0.8125rem;
}

label.inline {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    margin-right: var(--sp-4);
    font-size: 0.9375rem;
    white-space: nowrap;
    cursor: pointer;
}

label.inline input,
.perm-chip input {
    width: 1.15rem;
    height: 1.15rem;
    flex: none;
    accent-color: var(--ui-accent);
}

/* ---------- 9. ekrany ---------- */

/* 9a. logowanie */

.auth-shell {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: var(--sp-5);
    padding: var(--sp-6) max(1.25rem, env(safe-area-inset-left), env(safe-area-inset-right));
    background:
        radial-gradient(60rem 40rem at 50% -10%,
            color-mix(in srgb, var(--brand) 14%, transparent), transparent 70%),
        var(--bg);
}

.auth-card {
    width: min(26rem, 100%);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: clamp(1.5rem, 1rem + 3vw, 2.5rem);
}

.auth-card h1 {
    font-size: 1.375rem;
    text-align: center;
    margin-bottom: var(--sp-5);
}

.auth-card .btn-primary {
    width: 100%;
}

/* 9b. pulpit */

.tiles {
    display: grid;
    gap: var(--sp-4);
    grid-template-columns: 1fr;
    margin: var(--sp-5) 0;
}

@media (min-width: 48rem) {
    .tiles {
        grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    }
}

.tile {
    display: grid;
    align-content: start;
    gap: var(--sp-1);
    padding: var(--sp-4);
    min-height: 6.5rem;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    color: var(--fg);
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

.tile:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
    border-color: var(--brand-600);
}

.tile svg {
    width: 1.75rem;
    height: 1.75rem;
    color: var(--brand-900);
    margin-bottom: var(--sp-1);
}

.tile-title {
    font-weight: 650;
    font-size: 1.0625rem;
}

.tile-desc {
    font-size: 0.875rem;
}

.status-dot {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    margin-right: var(--sp-2);
    border-radius: 50%;
    background: currentColor;
}

/* 9c. konta pracowników

   Poniżej 64rem każdy wiersz tabeli jest kartą: 11 kolumn (konto, osiem
   uprawnień, stan, akcje) nie mieści się na żadnym telefonie ani tablecie.
   Semantyka tabeli przy `display:grid` znika, ale nic nie tracimy — każdy
   checkbox ma `aria-label` z nazwą uprawnienia I konta. Gdyby ktoś usunął
   ten atrybut, tryb kart przestanie być dostępny. */

/* Poziome przewijanie siedzi na opakowaniu, nie na <table> — inaczej `sticky`
   na komórce pierwszej kolumny jest zawodne między silnikami. Działa na każdej
   szerokości: tabele magazynu (rejestr — 7 kolumn, kartoteki — 6 z inputami)
   nie mieszczą się ani na telefonie, ani na tablecie w pionie. Wyjątek: tabela
   kont poniżej 64rem jest stosem kart z cieniem — pudełko przewijania ucinałoby
   cienie, a karty i tak nie mają czego przewijać. */
.table-scroll {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    /* Kontener przewijania musi być blokiem zawierającym dla elementów
       `position: absolute` w środku (np. `.visually-hidden` w nagłówkach tabeli) —
       inaczej wystają poza kadr i rozpychają dokument w poziomie, mimo że sama
       tabela przewija się w środku. */
    position: relative;
}

.table-scroll:has(> .accounts) {
    overflow: visible;
}

.accounts {
    display: block;
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

.accounts thead {
    display: none;
}

.accounts tbody {
    display: block;
}

.accounts tbody tr:not(.expander) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
    gap: var(--sp-1) var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    margin-bottom: var(--sp-3);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}

.accounts tbody tr.owner {
    border-color: var(--brand-600);
    background: color-mix(in srgb, var(--brand) 7%, var(--card));
}

.accounts th,
.accounts td {
    display: block;
    border: 0;
    padding: 0;
    text-align: left;
}

.accounts tbody th[scope="row"],
.accounts tbody td:not(.perm-cell) {
    grid-column: 1 / -1;
}

.accounts tbody th[scope="row"] {
    padding-bottom: var(--sp-3);
    margin-bottom: var(--sp-2);
    border-bottom: 1px solid var(--line);
    font-size: 1.0625rem;
    font-weight: 650;
}

.accounts tbody th .muted {
    display: block;
    font-weight: 400;
    font-size: 0.8125rem;
}

/* Etykieta obok checkboxa jest częścią <label>, więc cały chip jest
   klikalny — stąd cel dotykowy 44 px zamiast 16 px samego pola. */
.perm-chip {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    min-height: var(--tap);
    padding: 0.25rem var(--sp-2);
    margin-inline: calc(var(--sp-2) * -1);
    border-radius: var(--radius-sm);
    font-size: 0.9375rem;
    cursor: pointer;
}

.perm-chip:has(input:checked) {
    background: var(--brand-soft);
    color: var(--brand-900);
    font-weight: 600;
}

.perm-chip:has(input:disabled) {
    cursor: default;
    opacity: 0.65;
}

.perm-chip-name {
    line-height: 1.25;
}

.accounts .actions {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    flex-wrap: wrap;
    margin-top: var(--sp-3);
}

.accounts tr.expander {
    display: block;
    margin: calc(var(--sp-3) * -1 - 1px) 0 var(--sp-3);
}

.accounts tr.expander td {
    padding: var(--sp-4);
    background: var(--card-2);
    border: 1px solid var(--line);
    border-top: 0;
    border-radius: 0 0 var(--radius) var(--radius);
}

@media (min-width: 64rem) {
    /* Tabela z osobną kolumną na każde uprawnienie jest z natury szersza niż
       kolumna tekstu (--page-max). Zamiast skazywać właściciela na poziome
       przewijanie, pozwalamy tej jednej karcie rozepchnąć się do krawędzi okna.
       Marginesem, nie transformem — transform tworzy containing block i zabiłby
       `sticky` na pierwszej kolumnie. */
    .card-accounts {
        /* Wyjście poza kolumnę treści wyłącznie w prawo, do krawędzi okna minus
           margines strony — w lewo nie, bo tam stoi szyna. 0.75rem rezerwy na
           pasek przewijania, którego 100vw nie odejmuje. Gdy okno jest węższe
           niż --page-max, wyrażenie jest dodatnie i min() zeruje margines. */
        max-width: 78rem;
        margin-inline-end: min(0px, calc((min(var(--page-max), 100vw) - 100vw) / 2 + 0.75rem));
    }

    /* Tabela kont wraca z kart do tabeli, więc i jej opakowanie wraca
       do przewijania. */
    .table-scroll:has(> .accounts) {
        overflow-x: auto;
    }

    .accounts {
        display: table;
        font-size: 0.9375rem;
    }

    .accounts thead {
        display: table-header-group;
    }

    .accounts tbody {
        display: table-row-group;
    }

    .accounts tbody tr:not(.expander) {
        display: table-row;
        padding: 0;
        margin: 0;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        background: transparent;
    }

    .accounts th,
    .accounts td {
        display: table-cell;
        padding: 0.625rem 0.75rem;
        border-bottom: 1px solid var(--line);
        vertical-align: middle;
    }

    .accounts thead th {
        font-size: 0.8125rem;
        color: var(--muted);
        font-weight: 600;
        vertical-align: bottom;
    }

    .accounts tbody th[scope="row"] {
        position: sticky;
        left: 0;
        z-index: 1;
        background: var(--card);
        white-space: nowrap;
        font-size: inherit;
        margin: 0;
    }

    .accounts tbody tr.owner {
        background: color-mix(in srgb, var(--brand) 8%, transparent);
    }

    .accounts tbody tr.owner th[scope="row"] {
        background: color-mix(in srgb, var(--brand) 8%, var(--card));
    }

    .perm-col span {
        display: inline-block;
        max-width: 7rem;
        line-height: 1.2;
    }

    .perm-cell {
        text-align: center;
    }

    .perm-chip {
        display: inline-flex;
        margin: 0;
    }

    .perm-chip:has(input:checked) {
        background: none;
        font-weight: inherit;
    }

    /* Na desktopie etykietę niesie nagłówek kolumny. */
    .perm-chip-name {
        display: none;
    }

    .accounts .actions {
        display: table-cell;
        white-space: nowrap;
        margin: 0;
    }

    .accounts .actions .btn {
        margin-right: var(--sp-3);
    }

    .accounts tr.expander {
        display: table-row;
        margin: 0;
    }

    .accounts tr.expander td {
        border-radius: 0;
        border-left: 0;
        border-right: 0;
    }
}

/* Gęsta siatka chipów tylko przy myszy: na tablecie w poziomie (iPad to dokładnie
   64rem, ale wskaźnik jest zgrubny) chip zachowuje cel dotykowy 44 px. */
@media (min-width: 64rem) and (pointer: fine) {
    .perm-chip {
        min-height: 0;
        padding: 0;
    }
}

/* 9d. magazyn kruszyw

   Tabele magazynu są węższe niż siatka uprawnień (najszersza ma siedem kolumn),
   więc nie zamieniamy ich w karty — wystarczy poziome przewijanie na opakowaniu
   `.table-scroll`. Docelowym urządzeniem jest tablet w poziomie, gdzie mieszczą
   się w całości. */

.stany,
.dokumenty,
.kartoteka,
.pozycje {
    width: 100%;
    border-collapse: collapse;
    /* Cyfry o stałej szerokości: kolumna z tonażem ma się czytać w pionie,
       a nie skakać przy każdej zmianie stanu. */
    font-variant-numeric: tabular-nums;
}

.stany th,
.stany td,
.dokumenty th,
.dokumenty td,
.kartoteka th,
.kartoteka td,
.pozycje th,
.pozycje td {
    padding: 0.625rem 0.75rem;
    border-bottom: 1px solid var(--line);
    text-align: left;
    vertical-align: middle;
}

.stany thead th,
.dokumenty thead th,
.kartoteka thead th,
.pozycje thead th {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--muted);
    white-space: nowrap;
}

.stany tbody th[scope="row"],
.dokumenty tbody th[scope="row"],
.kartoteka tbody th[scope="row"] {
    font-weight: 600;
    font-size: inherit;
    margin: 0;
    /* Pierwsza kolumna zostaje w kadrze przy przewijaniu w poziomie. Tło jest
       konieczne — bez niego przewijane komórki prześwitują przez nagłówek wiersza. */
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--card);
}

/* Komórka akcji: przyciski-linki dostają cel dotykowy 44 px, bo w tabeli
   dotyka się ich kciukiem tak samo jak kafli. */
.dokumenty .actions,
.kartoteka .actions,
.pozycje .actions {
    white-space: nowrap;
    text-align: right;
    padding-block: var(--sp-1);
}

.dokumenty .actions .btn-link,
.kartoteka .actions .btn-link,
.pozycje .actions .btn-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
    padding: 0 var(--sp-2);
}

.stany tbody th .muted,
.dokumenty tbody td .muted {
    display: block;
    font-size: 0.8125rem;
    font-weight: 400;
}

.liczba {
    text-align: right;
    white-space: nowrap;
}

.stany tfoot th,
.stany tfoot td,
.pozycje tfoot th,
.pozycje tfoot td {
    padding: 0.625rem 0.75rem;
    border-top: 2px solid var(--line-strong);
    border-bottom: 0;
    font-weight: 700;
    text-align: left;
}

.stany tfoot td,
.pozycje tfoot td {
    text-align: right;
}

/* Dokument wycofany korektą zostaje w rejestrze — historii nie kasujemy — ale nie
   ma udawać obowiązującego. */
.dokumenty tbody tr.wycofany th[scope="row"],
.dokumenty tbody tr.wycofany td {
    color: var(--muted);
    text-decoration: line-through;
}

.dokumenty tbody tr.wycofany .badge {
    text-decoration: none;
}

.dokumenty abbr {
    text-decoration: none;
    font-weight: 600;
}

.szczegoly {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--sp-1) var(--sp-3);
    margin: 0 0 var(--sp-3);
}

.szczegoly dt {
    color: var(--muted);
    font-size: 0.875rem;
}

.szczegoly dd {
    margin: 0;
}

/* Pola edycji w kartotece mają być widoczne jako pola, ale nie rozpychać wiersza
   do wysokości formularza. */
.kartoteka input[type="text"],
.kartoteka input[type="number"],
.kartoteka input:not([type]) {
    width: 100%;
    min-width: 6rem;
    min-height: var(--tap);
    padding: 0.375rem 0.5rem;
    font: inherit;
    color: var(--fg);
    background: var(--card-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

.kartoteka input[type="number"] {
    min-width: 5rem;
}

/* Przełącznik kartotek. Zwykłe przyciski, bo to stan komponentu, a nie adres —
   trasy per kartoteka dawałyby cztery wpisy w historii przeglądarki na jedno
   przejrzenie ekranu. */
.sekcje {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin: var(--sp-4) 0;
}

.sekcja {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--tap);
    padding: 0 var(--sp-4);
    font: inherit;
    text-decoration: none;
    color: var(--fg);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.sekcja.aktywna {
    color: var(--brand-900);
    background: var(--brand-soft);
    border-color: var(--brand);
    font-weight: 600;
}

/* Filtry układają się w kolumnę na wąskim ekranie i w rząd, gdy jest miejsce. */
.filtry {
    display: grid;
    gap: var(--sp-3);
}

@media (min-width: 48rem) {
    .filtry {
        grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
        align-items: end;
    }

    .filtry h2 {
        grid-column: 1 / -1;
    }
}

.filtry .field {
    margin: 0;
}

.filtry select,
.filtry input[type="date"] {
    width: 100%;
    min-height: var(--tap);
    padding: 0 0.5rem;
    font: inherit;
    color: var(--fg);
    background: var(--card-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

/* 9e. kreatory dotykowe: ważenie (WASM) i inwentaryzacja (InteractiveServer)

   Ekrany obsługiwane w terenie, na tablecie, jedną ręką. Wszystko jest tu większe niż
   w reszcie aplikacji i ułożone w jedną kolumnę — operator ma dotykać dużych celów,
   a nie celować w pola.

   `.kreator` to alias `.wazenie`, nie przemianowanie: reguły ekranu ważenia zostają
   bajt w bajt takie, jakie przeszły weryfikację w przeglądarce, a nowy ekran nie nosi
   klasy nazwanej od cudzej funkcji. Reszta sekcji jest nieopakowana i obie strony biorą
   ją po samych nazwach klas. */

.wazenie,
.kreator {
    max-width: 34rem;
    margin-inline: auto;
}

/* Od 48rem (tablet w obu orientacjach) kreator ma dwie kolumny: kafle albo
   lista i pola po lewej, urządzenie wpisu (numpad, przycisk) po prawej,
   przyklejone pod topbarem. Klasa `szeroki` włącza szerszą kolumnę tylko na
   krokach, które mają z niej pożytek — przycisk „Dalej” na 60rem nie jest
   lepszym celem niż na 34rem. */
@media (min-width: 48rem) {
    .wazenie.szeroki,
    .kreator.szeroki {
        max-width: 60rem;
    }

    .kreator-uklad {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 18rem;
        column-gap: var(--sp-5);
        /* Warunek sticky w gridzie: bez tego panel rozciąga się na cały wiersz
           i nie ma się gdzie przesunąć. */
        align-items: start;
    }

    .kreator-panel {
        position: sticky;
        top: calc(var(--topbar-dol) + var(--sp-4));
        /* Siatka bezpieczeństwa: gdy panel jest wyższy niż okno, przewija się
           w środku zamiast chować dolny rząd pod krawędzią. */
        max-height: calc(100vh - var(--topbar-dol) - var(--sp-4) * 2);
        max-height: calc(100dvh - var(--topbar-dol) - var(--sp-4) * 2);
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .kreator-panel .numpad {
        margin-top: 0;
    }
}

@media (min-width: 64rem) {
    .kreator-uklad {
        grid-template-columns: minmax(0, 1fr) 22rem;
    }
}

.wazenie h1,
.kreator h1 {
    margin-top: var(--sp-2);
}

/* Pasek kolejki jest zawsze widoczny: brak zasięgu to normalny stan pracy i operator
   ma o nim wiedzieć bez szukania, ale bez alarmowania — to nie jest błąd. Ten sam
   kształt niesie pasek postępu spisu: jedna informacja o stanie pracy, nad treścią. */
.pasek-kolejki,
.pasek-postepu {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    margin-bottom: var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    background: var(--card);
    font-size: 0.875rem;
}

.pasek-kolejki.czeka {
    border-color: var(--warn-bg);
    background: var(--warn-bg);
    color: var(--warn-fg);
}

.pasek-kolejki.czeka .btn-link {
    color: var(--warn-fg);
}

/* Komplet spisanych pozycji: potwierdzenie, nie alarm — sam kolor, bez zmiany tła. */
.pasek-postepu.pelny {
    border-color: var(--ok);
    color: var(--ok);
}

.kafle {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: var(--sp-3);
    margin: var(--sp-4) 0;
}

.kafle-male {
    grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}

.kafel {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    min-height: var(--tap-lg);
    padding: var(--sp-4);
    font: inherit;
    text-align: left;
    color: var(--fg);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
}

.kafel:active {
    background: var(--brand-soft);
}

.kafel.wybrany {
    border-color: var(--brand);
    background: var(--brand-soft);
    box-shadow: none;
}

.kafel-kod {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--brand-900);
    font-weight: 700;
}

.kafel-tytul {
    font-size: 1.0625rem;
    font-weight: 650;
    line-height: 1.25;
}

.kafel-opis {
    font-size: 0.8125rem;
    color: var(--muted);
}

/* Cofanie w kreatorze i na podglądzie dokumentu: link, ale z celem dotykowym. */
.wstecz {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
    margin-bottom: var(--sp-3);
}

.tryby {
    display: flex;
    gap: var(--sp-2);
    margin: var(--sp-4) 0;
}

/* Przełącznik trybu wagi to ta sama pigułka co `.sekcja` (sekcja 9d), tylko
   rozciągnięta na pół szerokości — jeden wygląd dla jednego pojęcia. */
.tryby .sekcja {
    flex: 1 1 0;
    padding-inline: var(--sp-3);
}

.pole-wagi {
    margin-bottom: var(--sp-4);
}

.pole-wagi label {
    display: block;
    margin-bottom: var(--sp-1);
    font-size: 0.875rem;
    color: var(--muted);
}

/* Wyświetlacz ważenia. Cyfry są duże i o stałej szerokości, bo tę liczbę operator
   porównuje wzrokiem ze wskazaniem wagi pomostowej, stojąc metr od tabletu. */
.pole-liczbowe {
    width: 100%;
    min-height: var(--tap-lg);
    padding: 0 var(--sp-3);
    font-family: var(--font-mono);
    font-size: 1.75rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
    color: var(--fg);
    background: var(--card-2);
    border: 2px solid var(--line);
    border-radius: var(--radius);
}

.pole-liczbowe.aktywne {
    border-color: var(--brand);
    background: var(--card);
}

.netto-podglad {
    font-size: 1.125rem;
    text-align: right;
    margin: 0 0 var(--sp-4);
}

.netto-podglad strong {
    font-family: var(--font-mono);
    font-size: 1.5rem;
    font-variant-numeric: tabular-nums;
}

.numpad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--sp-2);
    margin: var(--sp-4) 0;
}

.numpad-klawisz {
    min-height: var(--tap-lg);
    font: inherit;
    font-size: 1.5rem;
    font-variant-numeric: tabular-nums;
    color: var(--fg);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
}

.numpad-klawisz:active {
    background: var(--brand-soft);
    box-shadow: none;
}

.numpad-kasuj {
    color: var(--err);
}

.przelacznik-klawiatury {
    text-align: center;
    margin-bottom: var(--sp-4);
}

/* Przycisk kończący krok. Pełna szerokość i wysokość celu dotykowego, bo to jedyna
   rzecz, w którą operator celuje z ręką w powietrzu. */
.btn-duzy {
    display: block;
    width: 100%;
    min-height: var(--tap-lg);
    font-size: 1.125rem;
}

.podsumowanie {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--sp-2) var(--sp-4);
    margin: var(--sp-4) 0 var(--sp-5);
    padding: var(--sp-4);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

.podsumowanie dt {
    color: var(--muted);
    font-size: 0.875rem;
}

.podsumowanie dd {
    margin: 0;
    font-weight: 600;
}

.podsumowanie-ilosc {
    font-family: var(--font-mono);
    font-size: 1.25rem;
    font-variant-numeric: tabular-nums;
}

.potwierdzenie {
    color: var(--ok);
}

.numer-dokumentu {
    font-family: var(--font-mono);
    font-size: 1.75rem;
    font-weight: 700;
    margin: var(--sp-2) 0;
}

/* 9f. arkusz inwentaryzacji

   Spis z natury to jedna lista kruszyw i jedno urządzenie wpisu pod nią. Wiersz jest
   przyciskiem pełnej szerokości (nie komórką tabeli), bo operator wybiera go kciukiem,
   a cztery kolumny liczb nie mieszczą się w kolumnie 34 rem — stąd siatka dwóch rzędów. */

.arkusz {
    /* Rezerwa wysokości pod przyklejony do dołu panel: bez niej ostatnie wiersze
       kończą się pod nim i nie da się ich dotknąć. Wartość jest stała, nie mierzona —
       ma tylko wystarczyć, a pomiar wymagałby ResizeObservera. Domyślnie zero, bo
       panel przykleja się do dołu tylko w jednym przypadku (niżej); od 48rem stoi
       w drugiej kolumnie i przykleja się do góry (blok na końcu sekcji). */
    --wys-panelu: 0px;
}

/* Panel przykleja się do dołu WYŁĄCZNIE wtedy, gdy jest niski — czyli w trybie
   klawiatury systemowej. Z otwartym numpadem jest wyższy niż telefon i przyklejony
   zasłaniał całą listę, a jego dolny rząd chował się pod paskiem zakładek (sprawdzone
   na 390×844). Wtedy panel płynie z treścią, a operator dojeżdża do niego sam:
   `FocusAsync()` na wyświetlaczu przewija go w pole widzenia po dotknięciu wiersza. */
.arkusz:not(.z-numpadem) {
    --wys-panelu: 9rem;
}

.arkusz:not(.z-numpadem) .panel-wpisu {
    position: sticky;
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
    z-index: 20;
}

.arkusz-lista {
    list-style: none;
    display: grid;
    gap: var(--sp-2);
    margin: var(--sp-3) 0 0;
    padding: 0 0 var(--wys-panelu, 0px);
}

/* Stopka arkusza: przycisk podsumowania i komunikaty. Poniżej 48rem płynie za
   panelem, w siatce od 48rem stoi pod listą — w obu przypadkach z odstępem, bo
   przycisk pełnej szerokości sklejony z ostatnim wierszem czyta się jak jego część. */
.arkusz-stopka {
    margin-top: var(--sp-4);
}

.wiersz-spisu {
    display: grid;
    width: 100%;
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "nazwa spisano"
        "ksiega roznica"
        "uwaga uwaga";
    align-items: baseline;
    gap: 0.15rem var(--sp-3);
    min-height: var(--tap-lg);
    padding: var(--sp-3) var(--sp-4);
    font: inherit;
    text-align: left;
    color: var(--fg);
    background: var(--card);
    border: 1px solid var(--line);
    /* Lewa krawędź zawsze 3 px, żeby oznaczenie „policzony” zmieniało kolor,
       a nie układ wiersza. */
    border-left-width: 3px;
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    scroll-margin-bottom: var(--wys-panelu, 0px);
    cursor: pointer;
}

.wiersz-spisu:active {
    background: var(--brand-soft);
}

/* Kolejność ma znaczenie: wiersz aktywny i policzony naraz ma mieć zieloną krawędź
   postępu i markową ramkę „tu piszę”. */
.wiersz-spisu.aktywny {
    border-color: var(--brand);
    background: var(--brand-soft);
    box-shadow: none;
}

.wiersz-spisu.policzony {
    border-left-color: var(--ok);
}

.wiersz-spisu.blad {
    border-color: var(--err);
    background: var(--err-soft);
}

.wiersz-spisu.statyczny {
    cursor: default;
}

.wiersz-nazwa {
    grid-area: nazwa;
    font-size: 1.0625rem;
    font-weight: 650;
    line-height: 1.25;
}

.wiersz-kod {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    font-weight: 700;
    color: var(--brand-900);
    margin-left: var(--sp-2);
}

.wiersz-ksiega {
    grid-area: ksiega;
    font-size: 0.8125rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.wiersz-spisano {
    grid-area: spisano;
    font-family: var(--font-mono);
    font-size: 1.25rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

.wiersz-roznica {
    grid-area: roznica;
    font-family: var(--font-mono);
    font-size: 0.9375rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

/* Nadwyżka i niedobór mają inną wagę operacyjną i nie mogą wyglądać tak samo. */
.wiersz-roznica.dodatnia {
    color: var(--ok);
}

.wiersz-roznica.ujemna {
    color: var(--err);
}

.wiersz-roznica.zerowa {
    color: var(--muted);
}

.wiersz-uwaga {
    grid-area: uwaga;
    margin-top: var(--sp-1);
    font-size: 0.8125rem;
    color: var(--muted);
}

/* Ostrzeżenie ma być widoczne tam, gdzie da się je poprawić jednym dotknięciem,
   a nie dopiero na podsumowaniu. */
.znacznik-uwagi {
    display: inline-block;
    margin-left: var(--sp-2);
    padding: 0 0.4rem;
    border-radius: var(--radius-pill);
    background: var(--warn-bg);
    color: var(--warn-fg);
    font-size: 0.8125rem;
}

/* Urządzenie wpisu: jedno na ekranie, wyraźnie oddzielone od listy, bo to ono decyduje,
   dokąd lecą cyfry. Przyklejanie do dołu włącza reguła wyżej — tylko dla niskiego
   wariantu; offset jak w #blazor-error-ui, bo dolny pasek zakładek jest `fixed`. */
.panel-wpisu {
    margin-top: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    background: var(--card);
    border: 1px solid var(--brand);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}

.panel-wpisu.pusty {
    border-color: var(--line);
    box-shadow: var(--shadow-sm);
}

.panel-wpisu.pusty p {
    margin: 0;
}

.panel-naglowek {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin-bottom: var(--sp-2);
}

.panel-towar {
    font-size: 1.0625rem;
    font-weight: 650;
}

/* Odpowiednik .netto-podglad z ważenia, ale różnica niesie znak i musi mieć kolor —
   stąd osobna klasa zamiast ponownego użycia tamtej. */
.roznica-podglad {
    margin: var(--sp-2) 0;
    text-align: right;
    color: var(--muted);
}

.roznica-podglad strong {
    font-family: var(--font-mono);
    font-size: 1.375rem;
    font-variant-numeric: tabular-nums;
}

.roznica-podglad.dodatnia strong {
    color: var(--ok);
}

.roznica-podglad.ujemna strong {
    color: var(--err);
}

.panel-akcje {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-top: var(--sp-3);
}

/* Kolor ostrzegawczy, nie błędu: te komunikaty niczego nie blokują, mają kazać
   spojrzeć drugi raz. */
.ostrzezenia {
    margin: var(--sp-4) 0;
    padding: var(--sp-3) var(--sp-4);
    background: var(--warn-bg);
    color: var(--warn-fg);
    border-radius: var(--radius);
}

.ostrzezenia p {
    margin: 0 0 var(--sp-2);
}

.ostrzezenia ul {
    margin: 0;
    padding-left: 1.25rem;
}

.ostrzezenia li + li {
    margin-top: var(--sp-1);
}

/* 9g. kontrakty wydobycia
   Miernik realizacji to jedyny element, który ocenia kolorem (patrz decisions.md,
   2026-09-03): kontrakt ma zły kierunek, więc od 90% pasek dostaje tint
   ostrzegawczy, komplet — kolor potwierdzenia, zaległy — kolor błędu. Wartości
   stoją obok w tekście, kolor tylko przyspiesza czytanie. */
.miernik {
    position: relative;
    display: block;
    min-width: 6rem;
    height: 0.625rem;
    border-radius: var(--radius-pill);
    background: var(--line);
    overflow: hidden;
}

.miernik-wypelnienie {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: inherit;
    background: var(--brand-700);
}

.miernik.uwaga .miernik-wypelnienie {
    background: var(--warn-fg);
}

.miernik.pelny .miernik-wypelnienie {
    background: var(--ok);
}

.miernik.zalegly .miernik-wypelnienie {
    background: var(--err);
}

/* Zakończony decyzją właściciela: wypełnienie w kolorze tekstu wyciszonego —
   ani sukces, ani alarm; reszta paska zostaje pusta, bo tyle świadomie nie
   zrealizowano. */
.miernik.zakonczony .miernik-wypelnienie {
    background: var(--muted);
}

.miernik.wycofany .miernik-wypelnienie {
    background: var(--muted);
}

.kontrakty .miernik {
    margin-top: var(--sp-1);
}

/* Zakres dat w jednej linii — łamany na trzy wiersze rozpychał tabelę w pionie. */
.kontrakty td.okres {
    white-space: nowrap;
}

/* Odznaka stanu: warianty poza marką dla stanów, które wymagają spojrzenia. */
.badge.uwaga {
    border-color: var(--warn-fg);
    color: var(--warn-fg);
    background: var(--warn-bg);
}

.badge.err {
    border-color: var(--err);
    color: var(--err);
    background: var(--err-soft);
}

.badge.ok {
    border-color: var(--ok);
    color: var(--ok);
    background: var(--ok-soft);
}

.badge.muted {
    border-color: var(--line-strong);
    color: var(--muted);
    background: transparent;
}

/* Wiersz „poza kontraktem": wydobycie bez pokrycia, kolorem błędu w liczbie. */
.kontrakty tr.poza td.liczba,
.kontrakty tr.poza th {
    color: var(--err);
}

/* Podsumowanie kontraktu na tablecie: zdanie pod ilością, nie osobna sekcja. */
.podsumowanie-kontrakt {
    font-size: 0.875rem;
    color: var(--muted);
}

/* Kafle kontraktów na panelu: tytuł, miernik, liczby — czytane z odległości. */
.kontrakt-kafle {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: var(--sp-3);
    margin-top: var(--sp-3);
}

.kontrakt-kafel {
    padding: var(--sp-3);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--card-2);
}

.kontrakt-kafel-tytul {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-2);
    align-items: baseline;
    font-weight: 650;
}

.kontrakt-kafel-liczby {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
    font-size: 0.875rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.kontrakt-kafel .miernik {
    margin-top: var(--sp-2);
}

/* Limity backendu mają być widoczne przed wysyłką, a nie wracać jako 400. */
.licznik-znakow {
    display: block;
    margin-top: var(--sp-1);
    text-align: right;
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

/* Warunek szyny (sekcja 7): pasek zakładek znika, więc offset przestaje mieć sens. */
@media (min-width: 40rem) and (orientation: landscape) {
    .arkusz:not(.z-numpadem) .panel-wpisu {
        bottom: 0;
    }
}

/* Tablet (obie orientacje): lista po lewej, panel wpisu po prawej, przyklejony
   pod topbarem — operator widzi listę i klawiaturę naraz. Ten sam markup i ta sama
   maszyna stanów, wyłącznie układ. Stopka arkusza (przycisk podsumowania) stoi
   pod listą, obok panelu. */
@media (min-width: 48rem) {
    .arkusz {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 18rem;
        grid-template-areas:
            "lista panel"
            "stopka panel";
        /* Nadmiar wysokości panelu (rozpiętego na dwa wiersze) ma iść w całości
           do wiersza stopki — z dwoma wierszami `auto` siatka dzieliłaby go po
           równo i między listą a przyciskiem robiła się pusta luka. */
        grid-template-rows: auto 1fr;
        column-gap: var(--sp-5);
        /* Warunek sticky w gridzie: bez tego panel rozciąga się na cały wiersz. */
        align-items: start;
    }

    .arkusz-lista {
        grid-area: lista;
    }

    .arkusz-stopka {
        grid-area: stopka;
    }

    /* Oba selektory mają specyficzność (0,3,0) jak reguła przyklejania do dołu —
       inaczej tamten `bottom` wygrałby z `bottom: auto` i panel drżałby między
       górą a dołem. */
    .arkusz:not(.z-numpadem) .panel-wpisu,
    .arkusz.z-numpadem .panel-wpisu {
        grid-area: panel;
        position: sticky;
        top: calc(var(--topbar-dol) + var(--sp-4));
        bottom: auto;
        z-index: 20;
        /* Siatka bezpieczeństwa: panel z numpadem i otwartą uwagą bywa wyższy niż
           okno tabletu w poziomie — wtedy przewija się w środku. */
        max-height: calc(100vh - var(--topbar-dol) - var(--sp-4) * 2);
        max-height: calc(100dvh - var(--topbar-dol) - var(--sp-4) * 2);
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .arkusz,
    .arkusz:not(.z-numpadem) {
        --wys-panelu: 0px;
    }

    /* Ciaśniej w pionie, żeby cały panel mieścił się w oknie 1024×768. */
    .panel-wpisu .numpad {
        margin: var(--sp-3) 0;
    }

    .panel-wpisu .przelacznik-klawiatury {
        margin-bottom: 0;
    }

    .panel-wpisu .roznica-podglad {
        margin: var(--sp-1) 0;
    }
}

@media (min-width: 64rem) {
    .arkusz {
        grid-template-columns: minmax(0, 1fr) 22rem;
    }
}

/* ---------- 9g. panel właściciela ---------- */

/* Presety okresu. Własny kontener, bo w siatce .filtry każdy przycisk zająłby
   osobną komórkę. Na szerokim ekranie dostają cały rząd pod datami: wciśnięte
   w jedną kolumnę auto-fit układały się w pionową kolumienkę obok pól. */
.okresy {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

@media (min-width: 48rem) {
    .filtry .okresy {
        grid-column: 1 / -1;
    }
}

.okresy .btn {
    padding: 0 var(--sp-3);
}

/* Jedna liczba, po którą właściciel tu przychodzi. Cyfry proporcjonalne, nie
   tabelaryczne: przy tym stopniu równa szerokość znaków rozstrzeliwuje liczbę. */
.hero {
    display: grid;
    gap: var(--sp-1);
    margin: var(--sp-6) 0;
}

.hero-etykieta {
    font-size: 0.9375rem;
    color: var(--muted);
}

.hero-liczba {
    font-size: 2.75rem;
    font-weight: 650;
    line-height: 1.1;
    color: var(--fg);
}

@media (min-width: 48rem) {
    .hero-liczba {
        font-size: 3.5rem;
    }
}

.kpi {
    display: grid;
    gap: var(--sp-4);
    grid-template-columns: 1fr;
    margin: var(--sp-4) 0 var(--sp-6);
}

/* 9.5rem, nie 12: pięć kafli przepływu ma się zmieścić w jednym rzędzie w kolumnie
   treści obok szyny (na 1024 px zostaje ok. 857 px: 5 × 9.5rem + 4 odstępy = 824),
   bo to jeden ciąg — od wydobycia do zużycia. Piąty zawinięty pod spód czytałby się
   jak osobna kategoria. Sprawdzone pomiarem, nie z głowy: 10rem jeszcze się zawijało. */
@media (min-width: 34rem) {
    .kpi {
        grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
    }
}

.kpi-kafel {
    display: grid;
    align-content: start;
    gap: var(--sp-1);
    padding: var(--sp-4);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.kpi-etykieta {
    font-size: 0.875rem;
    color: var(--muted);
}

.kpi-liczba {
    font-size: 1.5rem;
    font-weight: 650;
}

.kpi-delta,
.kpi-podpis {
    font-size: 0.8125rem;
}

/* Etykieta przełącznika wykresu jest zdaniem, nie słowem. Wspólne `label.inline`
   ma `nowrap`, bo służy krótkim filtrom stojącym w jednym rzędzie — tutaj to samo
   `nowrap` wypychało stronę poza ekran telefonu. */
.inline.przelacznik {
    margin-right: 0;
    white-space: normal;
}

/* ---------- 9h. wykres stanu ---------- */

.wykres {
    margin: var(--sp-4) 0 var(--sp-2);
}

.wykres-plotno {
    overflow-x: auto;
}

.wykres svg {
    display: block;
    width: 100%;
    height: auto;
    /* Poniżej tej szerokości rysunek przestaje cokolwiek pokazywać: proporcje viewBox
       spłaszczyłyby go na telefonie do stu pikseli wysokości. Zamiast tego przewija się
       poziomo we własnym pudełku — strona zostaje nieruchoma. */
    min-width: 30rem;
    /* Krój i wielkość tekstu w SVG dziedziczy się z dokumentu, ale rodzina już nie —
       bez tego etykiety osi wypadłyby domyślną szeryfową czcionką przeglądarki. */
    font-family: var(--font-sans);
}

@media (min-width: 48rem) {
    .wykres svg {
        min-width: 0;
    }
}

/* Odświeżenie przygasza poprzedni rysunek, zamiast zamieniać go w szkielet.
   Migający placeholder przy każdej zmianie okresu skacze układem strony. */
.wykres.laduje {
    opacity: 0.55;
    transition: opacity 0.15s ease;
}

.wykres-podpis {
    font-size: 0.8125rem;
}

.wykres-legenda {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin: 0 0 var(--sp-4);
    padding: 0;
    list-style: none;
}

/* Legenda jest sterowaniem, nie ozdobą — stąd prawdziwe przyciski z aria-pressed.
   Seria odznaczona zostaje na liście: znikająca pozycja kazałaby szukać, czym się
   przed chwilą wyłączyło. */
.seria {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding: 0 var(--sp-3);
    font: inherit;
    color: var(--muted);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.seria.wlaczona {
    color: var(--fg);
    border-color: var(--line-strong);
}

.seria-znacznik {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: var(--radius-pill);
    /* Kolor serii siedzi na znaczniku, nigdy na tekście: aqua czy żółty są
       nieczytelne jako litery na powierzchni karty. */
    opacity: 0.35;
}

.seria.wlaczona .seria-znacznik {
    opacity: 1;
}

.seria-wartosc {
    font-variant-numeric: tabular-nums;
    font-size: 0.8125rem;
    color: var(--muted);
}

/* Tabela wykresu bywa długa na cały okres — własne okno przewijania trzyma ją
   w karcie, zamiast rozciągać stronę na kilka ekranów. */
.wykres-tabela {
    max-height: 22rem;
    overflow: auto;
}

.wykres-tabela thead th {
    position: sticky;
    top: 0;
    background: var(--card);
}

/* ---------- 10. utility ---------- */

.muted {
    color: var(--muted);
}

.ok {
    color: var(--ok);
}

.err {
    color: var(--err);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ---------- 11. pasek błędu Blazora ---------- */

#blazor-error-ui {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    /* Nad dolnym paskiem zakładek, nie pod nim. */
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
    z-index: var(--z-error);
    padding: var(--sp-3) var(--sp-5);
    background: var(--warn-bg);
    color: var(--warn-fg);
    box-shadow: 0 -1px 3px rgb(0 0 0 / 0.2);
}

#blazor-error-ui .dismiss {
    position: absolute;
    right: 0.75rem;
    top: 0.6rem;
    cursor: pointer;
}

/* Warunek szyny (sekcja 7): bez paska nie ma czego omijać. */
@media (min-width: 40rem) and (orientation: landscape) {
    #blazor-error-ui {
        bottom: 0;
    }
}

/* ---------- 12. preferencje użytkownika ---------- */

@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }

    .tile:hover {
        transform: none;
    }
}

/* Widoczne wyłącznie na wydruku: nagłówek z firmą i numerem — kartka bez
   topbara musi sama powiedzieć, czym jest. Reguła bazowa stoi PRZED blokiem
   print: przy równej swoistości wygrywa późniejsza, więc odwrotna kolejność
   chowałaby nagłówek także na wydruku (wyszło pod emulateMedia). */
.tylko-druk {
    display: none;
}

@media print {
    .topbar,
    .szyna,
    .tabbar,
    .footer,
    #blazor-error-ui,
    /* Sterowanie ekranem nie jest treścią wydruku: przyciski, filtry, podzakładki,
       linki eksportu, cofanie i całe karty poprawki i korekty (`.bez-druku`) —
       same ukryte przyciski zostawiały na kartce „Jako autor możesz jeszcze
       poprawić ten dokument…". */
    .bez-druku,
    .naglowek-ekranu .akcje,
    .podzakladki,
    .sekcje-lokalne,
    .filtry,
    .eksport,
    .wstecz,
    .btn,
    .btn-link,
    .historia .badge {
        display: none !important;
    }

    .uklad {
        display: block;
    }

    .page {
        padding: 0;
        max-width: none;
    }

    .tylko-druk {
        display: block;
    }

    .card {
        break-inside: avoid;
        box-shadow: none;
        border: 1px solid #999;
    }

    .table-scroll {
        overflow: visible;
    }

    a {
        color: inherit;
        text-decoration: none;
    }
}

.naglowek-druku {
    font-weight: 700;
    margin: 0 0 var(--sp-3);
}

/* ---------- 13. pojazd przy wadze, cele wydania, cofnięcie, historia, eksport ---------- */

/* Numer rejestracyjny: mono, duży, z odstępem — operator czyta go z tablicy
   samochodu stojącego kilka metrów dalej i porównuje wzrokiem. */
.kafel-rejestracja {
    font-family: var(--font-mono);
    font-size: 1.375rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--brand-900);
}

.rejestracja {
    font-family: var(--font-mono);
    letter-spacing: 0.04em;
}

/* Opis pojazdu pod numerem, nie za nim — „WX 12345Wywrotka" czytało się jak
   jeden ciąg (zrzut raportu przewozów). Kod kruszywa obok nazwy dostaje odstęp
   z tego samego powodu („Piasek 0–2PIA0-2" na wydruku raportu). */
.pozycje th .rejestracja + .muted {
    display: block;
    font-weight: 400;
}

.pozycje th .muted:not(.rejestracja + .muted) {
    margin-left: var(--sp-2);
    font-weight: 400;
}

/* Kafle pojazdów mieszczą numer i dwa wiersze opisu; węższe niż kafle typów
   dokumentów, bo na tablecie w poziomie ma ich stanąć cztery w rzędzie. */
.kafle-pojazdy {
    grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
}

/* Kafel-decyzja („Bez samochodu", „Inny samochód", „Inny cel"): ten sam kształt,
   ale przerywany obrys — to wyjście awaryjne, nie pozycja kartoteki. */
.kafel-neutralny {
    border-style: dashed;
    background: var(--card-2);
}

/* Węzeł betoniarski to cel większości kursów RW — wyróżniony tintem marki,
   żeby operator trafiał w niego bez czytania. */
.kafel-wezel {
    border-color: var(--brand-700);
}

.kafel-wezel .kafel-tytul {
    color: var(--brand-900);
}

.kafle-cele .kafel-opis {
    text-transform: lowercase;
}

/* Pole numeru obcego samochodu: duże jak wyświetlacz wagi, ale wyrównane do lewej
   i z odstępem liter — to tekst, nie liczba. */
.pole-rejestracji {
    width: 100%;
    min-height: var(--tap-lg);
    padding: 0 var(--sp-3);
    font-family: var(--font-mono);
    font-size: 1.5rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--fg);
    background: var(--card-2);
    border: 2px solid var(--line);
    border-radius: var(--radius);
}

.pole-rejestracji:focus {
    border-color: var(--brand);
    background: var(--card);
}

/* Podpis kroku: który samochód jest w toku — jedna linia nad kaflami. */
.kontekst-kroku {
    margin: 0 0 var(--sp-3);
}

.podpowiedz-tary {
    margin: var(--sp-1) 0 0;
    font-size: 0.875rem;
}

.kreator-panel .btn-duzy + .btn-duzy {
    margin-top: var(--sp-3);
}

/* Cofnięcie pomyłki: przycisk drugorzędny pod „Nowy dokument", kolorem błędu
   tylko w obrysie — ma być łatwy do znalezienia, ale nie konkurować z główną
   drogą (następny samochód). Potwierdzenie jest osobnym, wyraźnym pudełkiem. */
.btn-cofnij {
    margin-top: var(--sp-4);
    color: var(--err);
    border-color: var(--err);
    background: transparent;
}

.btn-niebezpieczny {
    color: #fff;
    background: var(--err);
    border-color: var(--err);
}

.potwierdz-cofniecie {
    margin-top: var(--sp-4);
    padding: var(--sp-4);
    border: 2px solid var(--err);
    border-radius: var(--radius);
    background: var(--card);
}

.potwierdz-cofniecie p {
    margin: 0 0 var(--sp-3);
    font-weight: 650;
}

.potwierdz-cofniecie .btn-duzy + .btn-duzy {
    margin-top: var(--sp-3);
}

.potwierdzenie-wycofania {
    color: var(--err);
}

/* Lista kolejki offline pod paskiem: każdy wpis z opisem i usuwaniem za potwierdzeniem. */
.pasek-akcje {
    display: flex;
    gap: var(--sp-3);
}

.kolejka-lista {
    list-style: none;
    margin: calc(-1 * var(--sp-3)) 0 var(--sp-4);
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--card);
}

.kolejka-lista li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    min-height: var(--tap);
}

.kolejka-lista li + li {
    border-top: 1px solid var(--line);
}

.kolejka-lista li > span:first-child {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.kolejka-lista .potwierdz {
    display: flex;
    gap: var(--sp-2);
}

/* Data zakończenia pod odznaką stanu, nie obok: obok łamała się na dwa wiersze
   i czytała jak część etykiety. */
.kontrakty td .badge + .okres {
    display: block;
    margin-top: var(--sp-1);
    font-size: 0.8125rem;
}

/* Zakończenie kontraktu: osobna sekcja w panelu edycji, oddzielona linią, bo to
   decyzja z powodem i podpisem, a nie kolejne pole formularza. */
.zakonczenie {
    margin-top: var(--sp-4);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--line);
}

.zakonczenie h3 {
    margin: 0 0 var(--sp-2);
    font-size: 1rem;
}

.zakonczenie .btn {
    margin-top: var(--sp-2);
}

/* Historia zmian: lista pól po zmianie w komórce, bez wypunktowania. */
.historia .zmiany {
    list-style: none;
    margin: 0;
    padding: 0;
}

.historia .zmiany li + li {
    margin-top: var(--sp-1);
}

/* Link eksportu przy tytule karty: mały, po prawej od nagłówka. */
h2 .eksport {
    margin-left: var(--sp-3);
    font-size: 0.875rem;
    font-weight: 400;
    vertical-align: middle;
}

/* ---------- 14. mapa zakładu ---------- */

/* Ekran startowy operatora: rycina zakładu z punktami wystawienia dokumentu.
   Rysunek jest w SVG (`aria-hidden`), a cały tekst i cała interakcja w nakładce HTML
   nad nim — stąd kontener pozycjonowany i punkty w procentach tego samego prostokąta. */

.mapa {
    max-width: 64rem;
    margin-inline: auto;
}

/* Sam tytuł, bez zdania prowadzącego: mapa ma się zmieścić na tablecie w całości,
   a każdy wiersz nagłówka zabiera punkt z dołu rysunku. */
.mapa-naglowek {
    margin-bottom: var(--sp-3);
}

@media (min-width: 48rem) {
    .mapa-naglowek h1 {
        margin: 0;
    }
}

.mapa-plotno {
    overflow-x: auto;
    /* Pierścień fokusu punktu leżącego przy krawędzi nie może wpaść pod obcięcie. */
    padding: 3px;
    margin-inline: -3px;
}

.mapa-rama {
    position: relative;
    /* Proporcja płótna przychodzi z komponentu, żeby nakładka miała dokładnie ten sam
       prostokąt co rysunek, jeszcze zanim SVG zdąży się wyrenderować. */
    /* Siedem celów po `--tap-lg` musi się zmieścić bez nachodzenia na siebie; poniżej
       tej szerokości mapa przewija się poziomo we własnym pudełku, jak wykres stanu. */
    min-width: 52rem;
    background: var(--card);
    border-radius: var(--radius);
}

.mapa-rysunek {
    display: block;
    width: 100%;
    height: 100%;
    /* SVG nie dziedziczy rodziny kroju z dokumentu. Tu tekstu nie ma, ale reguła
       zostaje na wypadek dopisania podpisu wprost do rysunku. */
    font-family: var(--font-sans);
}

/* Podpis miejsca: nazwa tego, co pod nim narysowane. Kapitaliki z rozstrzelaniem —
   tak podpisuje się rycinę, i tak odróżnia się nazwę miejsca od nazwy czynności. */
.mapa-miejsce {
    position: absolute;
    transform: translate(-50%, -50%);
    white-space: nowrap;
    font-size: 0.8125rem;
    font-weight: 650;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
    pointer-events: none;
}

.mapa-miejsce-mala {
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.1em;
}

/* Punkt wystawienia dokumentu. Kod dokumentu monospace jak na kaflach kreatora,
   pod nim czynność jednym słowem — operator w rękawicach czyta ją bez zatrzymania. */
.mapa-punkt {
    position: absolute;
    transform: translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.125rem;
    min-width: 6.5rem;
    min-height: var(--tap-lg);
    padding: var(--sp-2) var(--sp-3);
    background: var(--card);
    border: 2px solid var(--fg);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    text-align: center;
}

.mapa-punkt:hover {
    background: var(--brand-soft);
}

.mapa-punkt:active {
    background: var(--brand-soft);
    border-color: var(--brand-700);
}

.mapa-punkt:focus-visible {
    border-color: var(--brand-700);
}

.mapa-punkt-kod {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: var(--brand-900);
}

.mapa-punkt-nazwa {
    font-size: 0.9375rem;
    font-weight: 650;
    line-height: 1.15;
    color: var(--fg);
}

/* Dokumenty, których rysunek nie obejmuje: dziś pusto, ale trzeci magazyn albo nowy
   typ nie może zniknąć operatorowi z oczu tylko dlatego, że nie ma swojego miejsca. */
.mapa-poza {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3);
    margin-top: var(--sp-4);
}

@media (min-width: 48rem) {
    .mapa-rama {
        min-width: 0;
    }
}
