/* ============================================================
   app.css – Packer v2 custom theme
   Styl inspirowany BaseLinker: ciemny sidebar, czyste białe tło,
   niebieski akcent, zwarta nawigacja, płynne animacje.
   ============================================================ */

/* ─── Kolory globalne ─────────────────────────────────────── */
:root {
    --packer-sidebar-bg:        #1b2436;
    --packer-sidebar-border:    rgba(255, 255, 255, 0.06);
    --packer-sidebar-text:      rgba(255, 255, 255, 0.62);
    --packer-sidebar-hover-bg:  rgba(255, 255, 255, 0.07);
    --packer-sidebar-active-bg: rgba(255, 255, 255, 0.12);
    --packer-sidebar-active-border: #3b82f6;
    --packer-sidebar-width:     240px;
    --packer-sidebar-collapsed: 76px;
    --packer-accent:            #1c6ef3;
    --packer-accent-darken:     #1558cc;

    /* Skala typografii OMS — wdrażana etapami, najpierw Orders (lista + karta).
       Rozmiary ikon pozostają niezależne od tej skali. */
    --packer-font-size-xs:       .75rem;    /* 12px — metadane, opisy pomocnicze */
    --packer-font-size-sm:       .8125rem;  /* 13px — zwarte listy i tabele */
    --packer-font-size-md:       .875rem;   /* 14px — standardowa treść UI */
    --packer-font-size-title:    1.5rem;    /* 24px — główny identyfikator */
    --packer-font-weight-aux:    300;       /* informacje drugorzędne */
    --packer-font-weight-regular: 350;      /* podstawowa treść */
    --packer-font-weight-medium: 500;       /* kontrolowane wyróżnienie */
    --packer-font-weight-strong: 650;       /* główny tytuł / suma */

    --tblr-primary:             #1c6ef3;
    --tblr-primary-rgb:         28, 110, 243;
}

/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║  KONTRAST TEKSTU (2026-07-14) — przyciemnienie „szarego" tekstu dla        ║
   ║  czytelności. Zmiana WYŁĄCZNIE kolorów tekstu (bez pogrubień, bez zmiany   ║
   ║  14px). NIE rusza --tblr-secondary → kolory badge/przycisków bez zmian.    ║
   ║                                                                            ║
   ║  ▶▶ ABY PRZYWRÓCIĆ POPRZEDNI WYGLĄD: usuń CAŁY ten blok (do linii          ║
   ║     zamykającej ramkę). Zmienne wrócą wtedy do domyślnych Tabler.          ║
   ║     Domyślne: --tblr-muted #6b7280 · --tblr-body-color #374151 ·           ║
   ║               .text-secondary #6b7280 · nagłówki tabel #6b7280.            ║
   ╚══════════════════════════════════════════════════════════════════════════╝ */
:root:not([data-bs-theme="dark"]) {
    --tblr-muted:            #434b57;        /* było #6b7280 (gray-500 → ciemniej, ~7.6:1) */
    --tblr-muted-rgb:        67, 75, 87;     /* było 107, 114, 128 */
    --tblr-secondary-color:  rgba(17, 24, 39, 0.9); /* fallback .text-muted; było rgba(31,41,55,.75) */
    --tblr-body-color:       #1f2937;        /* tekst główny: było #374151 (gray-700) → bliżej czerni, ~13.6:1 */
    --tblr-emphasis-color:   #111827;        /* tekst wyróżniony/nagłówki: było #374151 → najciemniej (spójna hierarchia) */
}
/* .text-secondary i nagłówki tabel używają --tblr-secondary (współdzielone z badge/btn),
   więc przyciemniamy TYLKO ich tekst, bez ruszania koloru komponentów. */
:root:not([data-bs-theme="dark"]) .text-secondary { color: #434b57 !important; }
:root:not([data-bs-theme="dark"]) .table th       { color: #434b57 !important; }

/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║  FONT UI (2026-07-14) — Public Sans (self-hosted, zero CDN).               ║
   ║  Definicje @font-face: /assets/vendor/public-sans/public-sans.css          ║
   ║  (wpięty w head.php przed Tablerem). Inter zostaje jako font zapasowy.     ║
   ║                                                                            ║
   ║  ▶▶ ABY WRÓCIĆ DO INTER: usuń poniższy blok :root { --tblr-font-* }.       ║
   ║     (font wróci do stacku Tablera = Inter Var/Inter). Opcjonalnie usuń     ║
   ║     też link public-sans.css + 2 preloady w head.php.                      ║
   ╚══════════════════════════════════════════════════════════════════════════╝ */
:root {
    --tblr-font-sans-serif:  "Public Sans", "Inter Var", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --tblr-body-font-family: "Public Sans", "Inter Var", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
/* Tabler ustawia na `body`: font-feature-settings:"liga" 0,"cv03","cv04","cv11".
   To relikt po Interze: `cvXX` (character variants) to featury WYŁĄCZNIE Intera —
   Public Sans nie ma w GSUB żadnego `cvXX`, więc są martwe. Za to `"liga" 0`
   wyłącza ligatury, które Public Sans faktycznie MA. Czyścimy do domyślnych.
   Działa, bo app.css ładuje się PO tabler.min.css (ta sama specyficzność). */
body { font-feature-settings: normal; }

/* ─── Rezerwacja miejsca na scrollbar — bez „skoku" layoutu przy modalu ──────
   Cel bez zmian: modal Bootstrapa chowa pasek przewijania, więc treść nie może
   przeskakiwać w bok. Zmienił się SPOSÓB, bo poprzedni miał gorszy skutek uboczny.

   🔴 `html { overflow-y: scroll }` POWODOWAŁO OSOBNĄ USTERKĘ: otwarcie dowolnego
   modala przewijało stronę na samą górę. Zgłoszone 2026-08-18 na fakturach
   kosztowych, ale dotyczyło CAŁEGO OMS-a — każdego modala w każdym module.

   Mechanizm (ZMIERZONY w konsoli, nie wydedukowany):
   1. W arkuszach **nie ma żadnej reguły `body.modal-open`** — Bootstrap ustawia
      `overflow:hidden` na `<body>` INLINE z JS (`ScrollBarHelper._disableOverFlow`).
   2. Wg CSS Overflow §3.3 `body` przekazuje swój `overflow` do viewportu
      **wyłącznie gdy `<html>` ma `overflow: visible`**. `overflow-y: scroll`
      na `html` tę propagację wyłączało, więc blokada nie zatrzymywała viewportu,
      tylko czyniła `body` osobnym kontenerem przewijania — a dokument tracił
      przewijalną nadwyżkę i pozycję przycinało do 0.
   3. Pomiar (`scrollY` w kolejnych etapach `show()`): `175 → 175 → **0** → 0 → 0`.
      Zero pojawia się **synchronicznie wewnątrz `show()`**, razem z `body.overflow:
      hidden`, a NIE w `shown.bs.modal`. ⛔ To wyklucza `.focus()` na `.modal`
      jako sprawcę — pierwsza, błędna hipoteza; nie wracać do niej.
   4. Kontrola A/B: `documentElement.style.overflowY='visible'` → skok znika;
      przywrócenie `'scroll'` → skok wraca.

   `scrollbar-gutter: stable` rezerwuje miejsce na pasek **nie dotykając
   `overflow`**, więc propagacja działa, blokada dochodzi do viewportu, a ten
   zachowuje pozycję przewijania (normalne zachowanie Bootstrapa).
   Skutek uboczny na plus: na krótkich stronach nie ma już martwego paska.
   ⛔ Nie wracać do `overflow-y: scroll` na `html`.

   ⚠️ Kasowanie kompensacji Bootstrapa TYLKO tam, gdzie gutter jest zarezerwowany:
   Bootstrap liczy szerokość paska jako `innerWidth − documentElement.clientWidth`,
   a przy stałym gutterze ta różnica jest niezerowa ZAWSZE — dokładałby ~15 px
   paddingu do `body` ORAZ do `.modal` (potwierdzone w DevTools: `<div class="modal"
   style="padding-right: 15px">`), przez co treść i okno modala jechałyby w lewo.
   Poza `@supports` zostawiamy kompensację nietkniętą, bo tam realnie chroni. */
html { scrollbar-gutter: stable; }
@supports (scrollbar-gutter: stable) {
    body.modal-open { padding-right: 0 !important; }
    .modal { padding-left: 0 !important; padding-right: 0 !important; }
}

/* ─── Kafelki (chipsy) statusów pakowalni ─────────────────── */
.packing-chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.packing-chip {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .5rem .9rem;
    border: 1px solid var(--tblr-border-color);
    border-radius: 999px;
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-medium);
    text-decoration: none;
    color: var(--tblr-body-color);
    background: #f4f6f9;
    transition: background .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.packing-chip:hover { background: #fff; border-color: var(--tblr-primary); text-decoration: none; }
.packing-chip.active {
    border-color: var(--tblr-primary);
    background: var(--tblr-primary-lt);
    font-weight: var(--packer-font-weight-medium);
    box-shadow: inset 0 0 0 1px var(--tblr-primary);
}
.packing-chip .chip-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.packing-chip .chip-count { font-size: var(--packer-font-size-xs); padding: 1px 8px; border-radius: 999px; font-weight: var(--packer-font-weight-medium); line-height: 1.5; }

/* ─── Notatka do pakowania w kafelku listy (panel pakowania) ───────────────
   Przełącznik „Pokaż notatki do pakowania" przestawia klasę `show-notes` na #packList:
   włączony → pełna treść w kafelku, wyłączony → sama ikonka przy identyfikatorze.
   ⚠️ pack.php NIE jest w wrapperze `.packing-page` (Etap 5 ujednolicania objął
   index/assign/batch), więc scope daje #packList — element wyłącznie tego ekranu. */
.pack-li-note {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    color: var(--tblr-orange, #f76707);
    margin-top: .15rem;
}
/* Selektory z ID wygrywają z `display:-webkit-box` wyżej — bez !important. */
#packList:not(.show-notes) .pack-li-note     { display: none; }
#packList.show-notes       .pack-li-note-ico { display: none; }

/* ─── Typografia ekranów pakowania ─────────────────────────────────────────
   Scope nadają wyłącznie widoki index/assign/batch. Modale są poza wrapperem,
   a rozmiary ikon (klasy fs-*) pozostają niezależne od skali tekstu. */
.packing-page {
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .packing-title {
    font-size: var(--packer-font-size-title);
    font-weight: var(--packer-font-weight-strong);
    line-height: 1.1;
}
.packing-page .small,
.packing-page small {
    font-size: var(--packer-font-size-xs);
}
.packing-page .text-muted.small,
.packing-page small.text-muted {
    font-weight: var(--packer-font-weight-aux);
}
.packing-page strong {
    font-weight: var(--packer-font-weight-medium);
}
.packing-page .fw-medium {
    font-weight: var(--packer-font-weight-regular) !important;
}
.packing-page .fw-semibold,
.packing-page .fw-bold {
    font-weight: var(--packer-font-weight-medium) !important;
}
/* Identyfikator paczki na liście (kolumna „Paczka") — ten sam wzorzec, co numer
   zamówienia + marketplace w views/orders/index.php (.order-num-oms / .order-num-source).
   Klasy WŁASNE i semantyczne, bo `.packing-page .fw-medium` wyżej kasuje wyróżnienie
   przez !important, a po klasach typograficznych nie wolno sięgać z JS. */
.packing-page .packing-batch-name {
    font-size:   var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-medium);
    color:       var(--tblr-primary);
    line-height: 1.25;
}
.packing-page .packing-batch-code {
    font-size:   var(--packer-font-size-xs);
    font-weight: var(--packer-font-weight-aux);
    color:       var(--tblr-muted);
    font-family: var(--tblr-font-monospace, monospace);
    line-height: 1.25;
}
.packing-page .packing-emphasis,
.packing-page .packing-stat-value {
    font-weight: var(--packer-font-weight-strong) !important;
}
.packing-page .packing-stat-value {
    font-size: var(--packer-font-size-title);
    line-height: 1.1;
}
/* Licznik postępu paczki jest przyciskiem filtrującym listę zamówień pod spodem.
   Wygląd spoczynkowy celowo identyczny z dawnym `<div>` — ma nie krzyczeć „przycisk",
   dopóki kursor nie wejdzie. Stan aktywny powtarza traktowanie `.packing-chip.active`. */
.packing-page .packing-stat-btn {
    display: block;
    width: 100%;
    padding: .35rem .25rem;
    border: 1px solid transparent;
    border-radius: var(--tblr-border-radius, .25rem);
    background: transparent;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.packing-page .packing-stat-btn:hover { background: #f4f6f9; }
.packing-page .packing-stat-btn[aria-pressed="true"] {
    border-color: var(--tblr-primary);
    background: var(--tblr-primary-lt);
}
.packing-page .packing-stat-btn-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
}
.packing-page .card-header {
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .card-title,
.packing-page .packing-section-title {
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-medium);
}
.packing-page .form-label {
    font-size: var(--packer-font-size-xs);
    font-weight: var(--packer-font-weight-medium);
}
.packing-page .form-control,
.packing-page .form-select,
.packing-page .input-group-text,
.packing-page .nav-link {
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .btn:not(.btn-sm) {
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .btn-sm {
    font-size: var(--packer-font-size-xs);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .table tbody {
    font-size: var(--packer-font-size-sm);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .table th {
    font-size: var(--packer-font-size-xs);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .badge {
    font-weight: var(--packer-font-weight-regular);
}
/* Badge’e w nagłówku karty zamówienia (stan źródła, status OMS, płatność, numer listu
   + ikona pobrania etykiety) — to rzędy skanowane wzrokiem przy pakowaniu, a globalne
   12 px było na nie za małe. Podbicie o JEDEN krok skali (12 → 13 px), nie więcej:
   ⚠️ scope celowo `… .card-header .badge`, żeby NIE ruszyć `1 szt.` przy pozycjach w treści.
   ⭐ Padding Tablera jest w `em`, więc chip rośnie proporcjonalnie — bez korekt. */
.packing-page .card-header .badge {
    font-size: var(--packer-font-size-sm);
}
.packing-page .pagination {
    font-size: var(--packer-font-size-sm);
    font-weight: var(--packer-font-weight-regular);
}
.packing-page .packing-order-title,
.packing-page .packing-order-sku {
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-medium);
}
.packing-page .packing-order-meta {
    font-size: var(--packer-font-size-xs);
    font-weight: var(--packer-font-weight-aux);
}
.packing-page .packing-product-name {
    font-size: var(--packer-font-size-xs);
    font-weight: var(--packer-font-weight-aux);
}
.packing-page .batch-history-toggle .ti-chevron-down {
    transition: transform .15s ease;
}
.packing-page .batch-history-toggle[aria-expanded="true"] .ti-chevron-down {
    transform: rotate(180deg);
}

/* ─── Picker gwiazdek (lista zamówień) ────────────────────── */
.star-picker {
    position: fixed;
    z-index: 1080;
    display: flex;
    gap: 2px;
    padding: 5px 7px;
    background: #fff;
    border: 1px solid var(--tblr-border-color);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.star-picker .star-opt {
    border: 0;
    background: transparent;
    padding: 3px 5px;
    border-radius: 6px;
    cursor: pointer;
    line-height: 1;
}
.star-picker .star-opt:hover { background: rgba(0, 0, 0, 0.06); }
.star-picker .star-opt i { font-size: 1.3rem; }
.order-star i { font-size: 1.05rem; vertical-align: middle; }

/* ─── Sidebar ─────────────────────────────────────────────── */
.packer-sidebar {
    background-color: var(--packer-sidebar-bg) !important;
    border-right: 1px solid var(--packer-sidebar-border);
    width: var(--packer-sidebar-width);
    min-width: var(--packer-sidebar-width);
    transition: width 0.22s ease, min-width 0.22s ease !important;
    display: flex;
    flex-direction: column;
    overflow-y: auto !important;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.15) transparent;
}
.packer-sidebar::-webkit-scrollbar {
    width: 4px;
}
.packer-sidebar::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,0.15);
    border-radius: 4px;
}

/* Logo */
.packer-sidebar .navbar-brand {
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--packer-sidebar-border);
    padding: 1rem 1rem;
    transition: padding 0.22s ease;
    white-space: nowrap;
    overflow: hidden;
}
.packer-sidebar .sidebar-logo-text {
    transition: opacity 0.18s ease, max-width 0.22s ease;
    max-width: 160px;
    overflow: hidden;
}

/* Sekcje menu (nagłówki) */
.nav-section-label {
    display: block;
    padding: 0.25rem 1rem;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.3);
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 0.18s ease;
}

/* Linki nawigacyjne */
.packer-sidebar .nav-link {
    color: var(--packer-sidebar-text);
    padding: 0.55rem 1rem;
    border-radius: 6px;
    margin: 1px 8px;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    white-space: nowrap;
    overflow: hidden;
    transition: background 0.15s ease, color 0.15s ease, border-left 0.15s ease;
    border-left: 3px solid transparent;
}

.packer-sidebar .nav-link:hover {
    background-color: var(--packer-sidebar-hover-bg);
    color: #fff;
    border-left-color: rgba(59, 130, 246, 0.4);
}

.packer-sidebar .nav-link.active {
    background-color: var(--packer-sidebar-active-bg);
    color: #fff !important;
    border-left-color: var(--packer-sidebar-active-border);
    font-weight: 500;
}

.packer-sidebar .nav-link-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    font-size: 1rem;
    line-height: 1;
}

/* Licznik przy pozycji menu (np. nieprzeczytane SMS-y) — PRZY NAPISIE, nie przy krawędzi.
   🔴 SELEKTOR MUSI ZAWIERAĆ `.navbar-nav` — to nie ozdoba. Tabler ma
      `.navbar .navbar-nav .nav-link .badge { position:absolute; top:.375rem; right:.375rem;
      transform:translate(50%,-50%) }`, a sidebar to `<aside class="navbar navbar-vertical">`
      z `<ul class="navbar-nav">`, więc reguła ŁAPIE i przypina badge do prawej krawędzi
      `nav-link`. Krótszy selektor `.packer-sidebar .nav-link > .badge` ma (0,3,0) i przegrywa
      z (0,4,0) Tablera — więc ustawiał `align-self`/marginesy, ale NIE `position`.
   🪤 **Stąd bardzo mylący objaw:** `getComputedStyle(badge).alignSelf` zwracało `flex-start`
      (nasza reguła działa!), a badge ani drgnął — bo na elemencie `position:absolute`
      `align-self`, `margin-left` i `ms-auto` NIE ROBIĄ NIC. Rozstrzyga
      `getComputedStyle(badge).position`; zmierzone 2026-09-08: `absolute`.
      ⚠️ Dlatego `position: static` + `transform: none` są tu KONIECZNE, nie kosmetyczne.
   ⭐ Bliźnię tej reguły stoi niżej przy `#notifBell`, gdzie robimy DOKŁADNIE ODWROTNIE —
      dzwonek żyje w `.page-header` (poza `.navbar`), więc reguła Tablera go nie łapie
      i tam odtwarzamy ją jawnie. Ta sama reguła, dwa przeciwne kierunki.
   🪤 `.packer-sidebar .nav-link` ma `overflow: hidden` — KONIECZNE do animacji zwijania
      sidebara (tytuł chowa się przez `max-width: 0`), ale przycina wszystko, co wystaje poza
      wiersz; domyślny `.badge` Tablera jest wyższy niż linia tekstu i wyglądał jak pasek.
      ⛔ Nie „naprawiać” tego zdejmując `overflow: hidden` — rozjechałoby zwijanie sidebara.
   ⚠️ W markupie NIE MA `ms-auto` — po powrocie do `position: static` ta klasa
      (`margin-left:auto !important`) znowu odesłałaby licznik na koniec wiersza.
      Ujemny `margin-left` zbija `gap: 0.75rem` z `.nav-link` do ~5 px.
   ⚠️ `align-self: center` — badge stoi RÓWNO z napisem. Wariant `flex-start`
      (indeks górny) był wypróbowany i ODRZUCONY 2026-09-08: przy tak niskim `nav-link`
      unosi go o kilka pikseli, co czyta się jak niedociągnięcie, a nie jak zamysł.
   ⭐ `min-width` — żeby jedna cyfra była kółkiem, a nie wąską pigułką.
   ⚠️ Przy ZWINIĘTYM sidebarze (76 px) badge nie mieści się obok ikony i `overflow: hidden`
      go przycina — tak było też wcześniej; sygnał niesie wtedy dzwonek. */
.packer-sidebar .navbar-nav .nav-link > .badge {
    position: static;
    transform: none;
    flex-shrink: 0;
    align-self: center;
    margin-left: -0.45rem;
    line-height: 1.1;
    padding: 0.1rem 0.35rem;
    font-size: 0.65rem;
    min-width: 1.05rem;
    text-align: center;
}

.packer-sidebar .nav-link-title {
    transition: opacity 0.18s ease, max-width 0.22s ease;
    max-width: 160px;
    overflow: hidden;
}

/* Przycisk toggle sidebar (desktop) */
.sidebar-toggle-btn {
    position: absolute;
    right: -14px;
    top: 50%;
    transform: translateY(-50%);
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--packer-sidebar-bg);
    border: 1px solid var(--packer-sidebar-border);
    color: rgba(255, 255, 255, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 10;
    transition: background 0.15s, color 0.15s, transform 0.15s;
}
.sidebar-toggle-btn:hover {
    background: #2a3a54;
    color: #fff;
}

/* Info usera na dole sidebaru */
.sidebar-user {
    border-top: 1px solid var(--packer-sidebar-border);
    margin-top: auto;
}
.sidebar-user .sidebar-user-info {
    transition: opacity 0.18s ease, max-width 0.22s ease;
    max-width: 150px;
}

/* ─── Tryb zwinięty (collapsed) ───────────────────────────── */
.sidebar-collapsed .packer-sidebar {
    width: var(--packer-sidebar-collapsed) !important;
    min-width: var(--packer-sidebar-collapsed) !important;
}
.sidebar-collapsed .packer-sidebar .sidebar-logo-text,
.sidebar-collapsed .packer-sidebar .nav-link-title,
.sidebar-collapsed .packer-sidebar .nav-section-label,
.sidebar-collapsed .packer-sidebar .sidebar-user-info {
    opacity: 0;
    max-width: 0;
    pointer-events: none;
}
.sidebar-collapsed .packer-sidebar .navbar-brand {
    padding: 1rem 0.85rem;
    justify-content: center;
}
.sidebar-collapsed .packer-sidebar .nav-link {
    justify-content: center;
    gap: 0;
    padding: 0.5rem;
    margin: 1px 8px;
    border-left-color: transparent !important;
}
.sidebar-collapsed .packer-sidebar .nav-link.active {
    background-color: var(--packer-sidebar-active-bg);
}
.sidebar-collapsed .packer-sidebar .nav-link:hover {
    background-color: var(--packer-sidebar-hover-bg);
}
.sidebar-collapsed .packer-sidebar .sidebar-user .avatar {
    margin: 0 auto;
}
.sidebar-collapsed .packer-sidebar .sidebar-user > div {
    justify-content: center;
}
.sidebar-collapsed #sidebarToggleIcon {
    transform: rotate(180deg);
}
#sidebarToggleIcon {
    transition: transform 0.22s ease;
}

/* Tooltip w trybie collapsed – pokazuje tytuł linka */
.sidebar-collapsed .packer-sidebar .nav-link[title] {
    position: relative;
}

/* ─── Page wrapper – dopasowanie do szerokości sidebaru ──── */
.page-wrapper {
    margin-left: var(--packer-sidebar-width);
    transition: margin-left 0.22s ease;
}
@media (min-width: 992px) {
    .sidebar-collapsed .page-wrapper {
        margin-left: var(--packer-sidebar-collapsed) !important;
    }
}

/* ─── Topbar ──────────────────────────────────────────────── */
.page-header {
    background: #fff;
    border-bottom: 1px solid #e6e8ec;
    padding: 0.75rem 0;
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
}
.page-title {
    font-size: 1.1rem;
    font-weight: 600;
    color: #1a1f35;
}
.breadcrumb {
    font-size: 0.78rem;
    margin-bottom: 0.15rem;
}

/* ─── Content area ────────────────────────────────────────── */
.page-body {
    background: #f4f6fb;
    min-height: calc(100vh - 56px - 52px);
    padding: 1.5rem 0;
}

/* ─── Cards ───────────────────────────────────────────────── */
.card {
    border: 1px solid #e6e8ec;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
    border-radius: 8px;
}
.card-header {
    background: var(--tblr-bg-surface-tertiary, #fff);
    border-bottom: 1px solid #f0f2f5;
    padding: 0.85rem 1.25rem;
    font-weight: 600;
    font-size: 0.9rem;
}

/* ─── Tabele ──────────────────────────────────────────────── */
.table {
    font-size: 0.875rem;
}
.table th {
    font-weight: var(--tblr-font-weight-medium, 500);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--tblr-secondary);
    background: var(--tblr-bg-surface-tertiary);
    border-bottom: 2px solid var(--tblr-border-color);
    white-space: nowrap;
}
.table tbody tr {
    transition: background 0.1s ease;
}
.table tbody tr:hover {
    background-color: #f0f4ff;
}

/* ─── Badges ──────────────────────────────────────────────── */
.badge {
    font-size: var(--tblr-badge-font-size, 0.75rem);
    font-weight: var(--tblr-font-weight-medium, 500);
    letter-spacing: 0.04em;
    padding: var(--tblr-badge-padding-y, 0.25em) var(--tblr-badge-padding-x, 0.5em);
    border-radius: var(--tblr-border-radius, 6px);
}

/* ─── Toasty (Packer.notify) ──────────────────────────────── */
/* Wygląd toasta — wcześniej trzymany inline w main.js.
   Te same wartości, przeniesione tu dla spójności i łatwej edycji. */
.packer-toast {
    min-width: 280px;
    max-width: 360px;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}

/* ─── Buttons ─────────────────────────────────────────────── */
/* Kanoniczny zestaw przycisków (Tabler) — używać konsekwentnie:
   • akcja główna           → .btn .btn-primary
   • akcja drugorzędna      → .btn .btn-outline-secondary  lub  .btn .btn-ghost-secondary
   • akcja destrukcyjna     → .btn .btn-danger  lub  .btn .btn-ghost-danger
   • w tabelach / kompaktowo→ dołożyć .btn-sm (+ .btn-icon dla samej ikony)
   Stopka modala: „Anuluj" jako .btn .btn-link .link-secondary .me-auto,
   akcja główna po prawej. */
.btn-primary {
    background-color: var(--packer-accent);
    border-color: var(--packer-accent);
}
.btn-primary:hover {
    background-color: var(--packer-accent-darken);
    border-color: var(--packer-accent-darken);
}

/* ─── Przyciski akcji w tabelach ──────────────────────────── */
/* Ikony wyraźniejsze; blokujemy zawijanie żeby przyciski
   zawsze były w jednej linii obok siebie.                    */
.table td .btn-list,
.list-group-item .btn-list {
    flex-wrap: nowrap;
    gap: .25rem;
}
.table td .btn,
.table td .btn-list .btn,
.list-group-item .btn-list .btn {
    min-width: 2rem;
    min-height: 2rem;
    padding: .3rem .45rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.table td .btn .ti,
.table td .btn-list .btn .ti,
.list-group-item .btn-list .btn .ti {
    font-size: 1.1rem;
    line-height: 1;
}

/* ─── Login page ──────────────────────────────────────────── */
.packer-login-page {
    background: linear-gradient(135deg, #1b2436 0%, #1c3561 100%);
    min-height: 100vh;
}
.packer-login-card {
    border: 0;
    border-radius: 12px;
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.25);
}

/* ─── Animacje wejścia dla kart ───────────────────────────── */
/* ⚠️ BEZ `transform` — to nie jest kosmetyka, tylko czytelność tekstu.
   `translateY()` wypycha kartę na własną warstwę kompozycji, a Chrome renderuje
   wtedy CAŁY tekst w środku bez subpikselowego antyaliasingu (ClearType) i w
   ułamkowych pozycjach → litery się rozmywają. Prawie cały OMS siedzi w `.card`
   (lista zamówień, karta zamówienia, pakowalnia), więc dotyczyło to niemal
   całego interfejsu przy KAŻDYM wejściu na stronę i każdym location.reload().
   `animation-fill-mode: both` dodatkowo zostawiał transform na stałe.
   Sam fade opacity tego nie robi — dlatego animacja zostaje, ruch znika. */
@keyframes cardFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.card {
    animation: cardFadeIn 0.2s ease both;
}
@media (prefers-reduced-motion: reduce) {
    .card { animation: none; }
}

/* ─── Scrollbar w sidebar ─────────────────────────────────── */
.packer-sidebar .navbar-collapse {
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.15) transparent;
}
.packer-sidebar .navbar-collapse::-webkit-scrollbar {
    width: 4px;
}
.packer-sidebar .navbar-collapse::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,0.15);
    border-radius: 4px;
}

/* ─── Responsywność ───────────────────────────────────────── */
@media (max-width: 991.98px) {
    .sidebar-toggle-btn { display: none !important; }
    .packer-sidebar {
        width: 100% !important;
        min-width: unset !important;
        position: relative;
    }
    .sidebar-collapsed .packer-sidebar .nav-link-title,
    .sidebar-collapsed .packer-sidebar .nav-section-label {
        opacity: 1;
        max-width: 200px;
    }
}

/* ─── Dzwonek powiadomień (topbar) ────────────────────────── */
/* Dzwonek = inline SVG (jak Tabler). nav-link „przykleja się" do rozmiaru ikonki
   (bez Tablerowego min-width:2.5rem), żeby czerwona kropka — pozycjonowana jawnie regułą
   `#notifBell .nav-link .badge` poniżej (+ rozmiar 10×10 z Tablerowego .badge:empty) —
   wylądowała dokładnie w prawym-górnym ROGU dzwonka, a nie obok niego. */
#notifBell .nav-link {
    color: var(--tblr-secondary);
    min-width: auto;
    min-height: auto;
    padding: 0.375rem;
}
#notifBell .nav-link:hover { color: var(--packer-accent); }
.notif-bell-svg { width: 24px; height: 24px; display: block; }

/* Czerwona kropka w prawym-górnym rogu dzwonka.
   Dzwonek żyje w .page-header (nie w .navbar), więc natywna reguła Tablera
   `.navbar .navbar-nav .nav-link .badge` NIE łapie — odtwarzamy ją tu jawnie,
   scope'owaną do #notifBell. Kotwica: .nav-link.position-relative. */
#notifBell .nav-link .badge {
    position: absolute;
    top: 0.375rem;
    right: 0.375rem;
    transform: translate(50%, -50%);
}

/* Szersze okno powiadomień + nie ucinaj treści (zawijaj zamiast „…"). */
#notifBell .dropdown-menu-card { min-width: 24rem; }
.notif-alert-item { white-space: normal; }
.notif-alert-item .text-truncate { white-space: normal; overflow: visible; text-overflow: clip; }

/* ─── Ustawienia — boczne pionowe menu (list-group jak settings.html) ─────────
   Wspólne dla stron ustawień z bocznym menu (OMS /oms, Pakowalnia /packing/settings).
   Tabler `.list-group-transparent` ma `margin:0 -1.25rem` (wyciąga pozycje do krawędzi
   card-body → tekst i badge „przyklejają się"). Zdejmujemy margines i dajemy pozycjom
   padding + zaokrąglenie (ładny „pill" dla active/hover). */
.settings-side-nav { margin: 0; }
.settings-side-nav .list-group-item { padding: 0.5rem 0.75rem; border-radius: var(--tblr-border-radius); }
.settings-side-nav .subheader { padding-left: 0.75rem; }

/* ════════════════════════════════════════════════════════════════════════════
   FAKTURY KOSZTOWE — skala typograficzna i zwarta lista
   ────────────────────────────────────────────────────────────────────────────
   Moduł faktur został POMINIĘTY przy Etapach 1–5 ujednolicania typografii — do
   2026-08-18 `app.css` nie miał dla niego ani jednej reguły, a widoki stały na
   utilities Tablera plus dwa wyłamania: inline `font-size:11px` (poniżej
   najmniejszego tokenu 12px) i klasa `fs-10`, która NIE ISTNIEJE ani tutaj,
   ani w `tabler.min.css` — czyli martwy zapis bez żadnego efektu.

   Scope nadaje wyłącznie wrapper `.invoices-page` w widokach modułu. Modale są
   poza wrapperem, a rozmiary ikon pozostają niezależne od skali tekstu.
   ════════════════════════════════════════════════════════════════════════════ */
.invoices-page {
    font-size: var(--packer-font-size-md);
    font-weight: var(--packer-font-weight-regular);
}
.invoices-page .small,
.invoices-page small,
.invoices-page .table th {
    font-size: var(--packer-font-size-xs);
}
.invoices-page .table td {
    font-size: var(--packer-font-size-sm);
}

/* Zwarte wiersze — przy kilkudziesięciu fakturach domyślne `.card-table`
   (0,75 rem góra/dół) rozciągało listę na kilka ekranów. */
.invoices-page .card-table > :not(caption) > * > * {
    padding-top: .45rem;
    padding-bottom: .45rem;
}

/* ⚠️ DWA wyróżnienia w wierszu i ani jednego więcej: numer dokumentu
   (identyfikator, po którym się skanuje listę) oraz kwota. Reszta zostaje
   w wadze podstawowej — patrz zasada „mało pogrubień" w tym pliku. */
.invoices-page .inv-doc-number {
    display: inline-block;
    font-weight: var(--packer-font-weight-strong);
    color: var(--tblr-body-color);
    text-decoration: none;
    white-space: nowrap;
}
.invoices-page .inv-doc-number:hover { text-decoration: underline; }

.invoices-page .inv-amount {
    font-weight: var(--packer-font-weight-strong);
    white-space: nowrap;
}

/* ─── „Do opłacenia": wspólna siatka kolumn dla WSZYSTKICH grup dat ──────────
   🔴 Każda grupa terminu to OSOBNA tabela. Przy domyślnym `table-layout: auto`
   każda z nich liczy szerokości z WŁASNEJ treści, więc kolumny nie trzymają
   pionu między grupami — kontrahent wypadał raz przy lewej krawędzi, raz
   na środku (zgłoszone 2026-08-19). ⛔ `w-1` tego NIE naprawia: to `width: 1%`,
   czyli „zwiń do treści" w obrębie jednej tabeli — a treść w każdej grupie jest
   inna. Do tego pod `table-layout: fixed` `w-1` byłoby wręcz szkodliwe, bo 1%
   zostałoby wzięte dosłownie. Dlatego w TEJ tabeli `w-1` nie występuje.
   Szerokości nadaje wyłącznie poniższy blok — kolumna kontrahenta (2) celowo
   bez `width`, żeby zabrała resztę. */
.invoices-page .inv-groups-table { table-layout: fixed; }
.invoices-page .inv-groups-table th:nth-child(1) { width: 20rem; }   /* Numer + typ  */
.invoices-page .inv-groups-table th:nth-child(3) { width: 8rem; }    /* Data faktury */
.invoices-page .inv-groups-table th:nth-child(4) { width: 9.5rem; }  /* Kwota brutto */
.invoices-page .inv-groups-table th:nth-child(5) { width: 4rem; }    /* XML          */
.invoices-page .inv-groups-table th:nth-child(6) { width: 11rem; }   /* Termin       */
.invoices-page .inv-groups-table th:nth-child(7) { width: 8.5rem; }  /* Akcja        */

/* Etykieta typu stoi OBOK numeru, nie pod nim. Selektor sąsiedztwa trafia
   wyłącznie w ten układ — pigułki w osobnym `<div>` (NIP, źródło faktury
   w „Do przypisania") zachowują odstęp od góry. */
.invoices-page .inv-doc-number + .inv-tag {
    margin-top: 0;
    margin-left: .4rem;
    vertical-align: middle;
}

/* Szara pigułka drugorzędna (źródło faktury, NIP kontrahenta).
   Świadomie NIE `bg-blue-lt`: przy 56 wierszach kolorowy badge w każdym
   z nich przestaje cokolwiek wyróżniać i robi się szumem. */
.invoices-page .inv-tag {
    display: inline-block;
    margin-top: .15rem;
    padding: 1px 6px;
    border-radius: var(--tblr-border-radius);
    background: var(--tblr-bg-surface-secondary, #f1f3f5);
    font-size: var(--packer-font-size-xs);
    color: var(--tblr-secondary);
    white-space: nowrap;
}

/* Numer KSeF w CAŁOŚCI, łamany — tak jak w v1. Ucinanie do 32 znaków
   dawało nieczytelną sieczkę, po której i tak nie dało się niczego poznać. */
.invoices-page .inv-ksef {
    font-family: var(--tblr-font-monospace);
    font-size: var(--packer-font-size-xs);
    line-height: 1.25;
    color: var(--tblr-secondary);
    word-break: break-all;
}

/* Termin płatności — TEKST z kolorem pilności, a nie stały `<input type=date>`
   w każdym wierszu (tamto zamieniało listę w formularz). Pole pojawia się
   dopiero po kliknięciu ołówka; wzorzec przeniesiony z v1.

   🔴 Układ flex nadajemy TUTAJ, a nie klasami `d-flex`/`gap-1` w widoku.
   Tabler definiuje utility jako `display: flex !important`, więc reguła
   ukrywająca bez `!important` PRZEGRYWA — tekst zostawał widoczny, a pole
   daty dokładało się pod nim jako „drugie okienko". ⛔ Nie wracać do `d-flex`
   na tych dwóch elementach i nie leczyć tego przez `!important` po naszej
   stronie — wojna na `!important` kończy się tak samo, tylko później. */
.invoices-page .inv-term-view,
.invoices-page .inv-term-edit {
    display: flex;
    align-items: center;
    gap: .25rem;
    white-space: nowrap;
}
.invoices-page .inv-term-edit { display: none; }
.invoices-page .inv-term.editing .inv-term-view { display: none; }
.invoices-page .inv-term.editing .inv-term-edit { display: flex; }

/* ── Wejście do wersji mobilnej (Dashboard) ─────────────────────────────────
   Widoczne WYŁĄCZNIE na urządzeniu dotykowym o wąskim ekranie.

   🔑 `pointer: coarse` opisuje URZĄDZENIE (główny wskaźnik to palec), a nie rozmiar okna.
   Sama szerokość była złym kryterium: zwężone okno przeglądarki na komputerze to nie telefon,
   więc przycisk pojawiał się tam, gdzie nikt nie chce aplikacji mobilnej — i przeczył zasadzie,
   że o interfejsie decyduje WYBÓR użytkownika, a nie domysł systemu.
   ℹ️ Laptop z ekranem dotykowym raportuje `fine` (liczy się wskaźnik PODSTAWOWY), więc go to nie
   dotyczy. Tablet w poziomie (>992 px) też nie zobaczy przycisku — tam wejściem jest adres. */
.mobile-switch { display: none; }

@media (pointer: coarse) and (max-width: 991.98px) {
    .mobile-switch { display: block; }
}

/* ─── Podświetlenie pola wskazanego przez Packer.pointAt() ──────────────────────
   ⚠️ Sam kolor NIE wystarcza (daltonizm, małe pole na długiej stronie) — działa wyłącznie
   razem z przewinięciem i ustawieniem kursora, które robi helper.
   ⛔ Klasa jest GLOBALNA (nie scoped do modułu) — tę samą podpowiedź stosują już Reklamacje
   i Zwroty; scoped wersja wymusiłaby kopię reguły przy każdym kolejnym module. */
/* ⚠️ PODWOJONA KLASA I `:focus` W SELEKTORZE — to nie jest literowka.
   Helper ustawia kursor na polu, a Tabler ma `.form-control:focus` / `.form-select:focus`
   o specyficzności (0,2,0), czyli WYŻSZEJ niż pojedyncza klasa (0,1,0). Bez tego żółta
   obwódka trzymała się tylko do momentu zadziałania focus() (~320 ms), a potem zastępował ją
   zwykły niebieski ring — czyli komunikat „pole jest podświetlone" przestawał być prawdziwy.
   Podwojenie klasy daje (0,3,0) i wygrywa BEZ `!important`. Zmierzone na produkcji 2026-09-08. */
.packer-needs-attention.packer-needs-attention,
.packer-needs-attention.packer-needs-attention:focus {
    border-color: var(--tblr-warning);
    box-shadow: 0 0 0 .25rem rgba(var(--tblr-warning-rgb), .3);
    transition: box-shadow .15s ease, border-color .15s ease;
}
@media (prefers-reduced-motion: reduce) {
    .packer-needs-attention.packer-needs-attention { transition: none; }
}

/* ── Kopiowanie kliknięciem w tekst (.copy-field) ─────────────────────────────
   Obsługa w main.js. Kursor był wcześniej ustawiany JS-em w products.js; po przeniesieniu
   handlera do main.js musi go dawać CSS, inaczej pole wygląda na nieklikalne.
   ⛔ Bez podkreślenia na hover — pakowalnia (`.copy-sku`) sygnalizuje klikalność wyłącznie
   kursorem i tooltipem, a dwa różne zachowania dla tej samej czynności wyglądają przypadkowo.
   Kropkowana linia była tu przez chwilę i została świadomie zdjęta (2026-09-14). */
.copy-field { cursor: pointer; }

/* ── Modal „Zamów kuriera" — grupy metod dostawy (#pkShipments) ───────────────
   Nagłówek grupy i wiersz zamówienia to w markupie te same `.list-group-item`,
   więc bez tego bloku czytały się jak dwa równorzędne wiersze — zgłoszone
   z produkcji 2026-09-21, zaraz po wdrożeniu grupowania.

   🔑 Rozróżnia je TŁO + WCIĘCIE, nie pogrubienie ani wersalik. Nazwa metody bywa
   długa („Allegro miniKurier24 InPost pobranie”) i w bold/uppercase zamienia się
   w krzyk — a to nadal tylko etykieta sekcji, nie dana operacyjna.
   ⭐ Najważniejsze jest wcięcie wiersza: odsuwa jego checkbox od checkboxa grupy.
   Gdy oba stały w tej samej kolumnie, nie było widać, który zaznacza całą metodę. */
#pkShipments .pk-group-head {
    background: var(--tblr-bg-surface-secondary, #f1f3f5);
    padding-top: .375rem;
    padding-bottom: .375rem;
    font-size: var(--packer-font-size-sm);
    font-weight: var(--packer-font-weight-medium);
}

#pkShipments .pk-group-count {
    font-size: var(--packer-font-size-xs);
    color: var(--tblr-secondary);
    white-space: nowrap;
}

#pkShipments .pk-group-item {
    padding-left: 2.25rem;
}
