/* ============================================================================
   04-uklad.css — szkielet: menu boczne, topbar, treść, szuflada mobilna, nakładki
   ----------------------------------------------------------------------------
   Tylko UKŁAD: rozmieszczenie, przewijanie, stany (zwinięcie, logowanie,
   szuflada). Wygląd komponentów należy do innych plików.
   Wartości = wyłącznie tokeny z 00-tokens.css. Wyjątek: blok „zmienne układu"
   niżej (czego w tokenach nie ma) oraz wartości strukturalne (0, auto, 1fr,
   100%, 100dvh, 1turn) — to nie są decyzje wizualne.
   RAMA = WZÓR WYGLĄDU (RM4–RM6, v15 b14, 04.10.2026): menu z grupami, górny pasek z polem szukania, szyna ikon,
   dolny pasek telefonu — wymiary i kolory wzoru przez tokeny (mapa wzór → program: 00-tokens.css).
   Nazwy stanów zgodne z JS: is-login, sidebar-collapsed, mobile-menu-open,
   is-visible (01-api-theme.js, 10-events.js).
   ============================================================================ */
/* Zmienne układu — czego nie ma w tokenach. Powtórzone dla nakładek, bo #app-busy i #toast-root leżą POZA #app i nie
   dziedziczą po .app-shell. Szerokość menu, szyny ikon, wysokość paska górnego i dolnego są w 00-tokens.css
   (--sidebar-w, --sidebar-w-waski, --topbar-h, --tabbar-h). Warstwy (--z-*) definiuje WYŁĄCZNIE 00-tokens.css. */
.app-shell, .app-busy, .toast-root { --kreska: 1px solid var(--line); /* włos — brak tokenu grubości linii */ --zaslona: color-mix(in srgb, var(--bg) 76%, transparent); --ring-w: 3px; /* grubość pierścienia ładowania */ --ring-czas: 1s; /* pełny obrót — tokeny mają tylko czasy przejść */ }
/* ============================ SZKIELET ==================================== */
/* Przewijają się wnętrza (.nav, .content), nigdy cała strona */
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); height: calc(100dvh / var(--skala-ui)); overflow: hidden; }
/* ============================ MENU BOCZNE ================================= */
/* JEDEN DOM MENU BOCZNEGO (K10, v14): panel, logo, grupy, pozycje, licznik przy pozycji, grupa Kartoteki, stan wąski
   (szyna samych ikon) i arkusz „Więcej” na telefonie (koniec tego pliku).
   RM4 (v15 b14, 04.10.2026): MENU JAK WE WZORZE WYGLĄDU — 232 px, znak „G” + nazwa programu (KONSTRUKT — DS1), cztery grupy z szarym nagłówkiem
   (Praca / Produkcja / Dane / Firma), pozycja 36 px z ikoną 20 px, aktywna = jasne tło akcentu i napis w akcencie
   (nie pełny niebieski kafel), licznik = sama liczba po prawej. Wartości wyłącznie z tokenów. „Zwiń menu” jest w menu
   konta (wzór nie ma go w menu bocznym); stan zwinięty i jego zapamiętanie bez zmian (01-api-theme.js). */
.sidebar { display: flex; flex-direction: column; gap: 2px; min-height: 0; padding: var(--s-3) var(--s-2); background: var(--menu-tlo); border-right: var(--kreska); view-transition-name: menu-boczne; container: menu / inline-size; }
/* ROL-6 (przegląd 26.09): przejście między widokami (renderWithTransition) przenikało CAŁY ekran, więc
   przez chwilę świeciły dwie pozycje menu naraz — stara gasnąca i nowa. Menu przełącza się od razu,
   przenika tylko treść. */
::view-transition-old(menu-boczne), ::view-transition-new(menu-boczne) { animation: none; }
/* --- LOGO: znak (czerwony kwadrat „G” albo grafika z Ustawień — renderBranding w 01-api-theme.js) + nazwa programu --- */
.brand { display: flex; flex: none; align-items: center; gap: var(--s-2); padding: 2px var(--s-2) var(--s-3); overflow: hidden; color: var(--text); font-family: var(--font-tytul); font-size: var(--t-md); font-weight: var(--w-strong); letter-spacing: -.01em; }
.brand-znak, .brand-logo { display: grid; flex: none; place-items: center; width: 30px; height: 30px; border-radius: var(--r-sm); }
.brand-znak { background: var(--logo-znak); color: var(--logo-znak-napis); }
.brand-logo { object-fit: cover; }
.brand strong { overflow: hidden; font: inherit; text-overflow: ellipsis; white-space: nowrap; }
/* DS1 (v15 b23, 05.10.2026): STOPKA PROGRAMU „KONSTRUKT · Made by Bartłomiej Kusztal” — drobne, szare pismo z tokenów.
   Jedna klasa w czterech miejscach: dół menu bocznego, dół arkusza „Więcej” (telefon pionowo), dół menu konta (jedyne
   miejsce przy szynie samych ikon) i pod formularzem logowania. Na szynie napis się nie mieści — chowa się (niżej). */
.stopka-programu { flex: none; margin: 0; padding: var(--s-2); color: var(--text-faint); font-size: var(--t-xs); line-height: var(--lh-base); text-align: center; overflow-wrap: anywhere; }
/* W menu bocznym (232 px) cały napis nie mieści się w linii: nazwa i autor stoją w dwóch liniach, bez kropki między nimi. */
.stopka-programu-autor { display: inline-block; }
.stopka-programu-menu { text-align: left; }
.stopka-programu-menu .stopka-programu-autor { display: block; }
.stopka-programu-menu .stopka-programu-kropka { display: none; }
.stopka-programu-konto { grid-column: 1 / -1; }
/* Menu — jedyna część panelu, która się przewija (długa lista kartotek). Pasek przewijania schowany jak we wzorze. */
.nav { display: flex; flex: 1 1 auto; flex-direction: column; gap: 2px; min-height: 0; overflow-x: hidden; overflow-y: auto; scrollbar-width: none; }
.nav > * { flex: none; }
/* Nagłówek grupy. Chowa się sam, gdy osoba nie widzi żadnej pozycji grupy (applyNavAccess). */
.nav-grupa { padding: var(--s-3) var(--s-2) var(--s-1); color: var(--text-faint); font-size: var(--t-xs); font-weight: var(--w-bold); }
.nav-item { display: flex; align-items: center; gap: var(--s-2); width: 100%; min-height: var(--h-control); padding-inline: var(--s-2); border-radius: var(--r-sm); color: var(--menu-tekst); font-size: var(--t-base); font-weight: var(--w-medium); text-align: left; text-decoration: none; transition: background var(--szybko), color var(--szybko); }
/* `display: flex` powyżej wygrywa z resetowym `[hidden] { display: none }` (ta sama specyficzność, późniejsza kolejność
   arkusza) — więc chowanie pozycji menu wg funkcji (applyNavAccess) potrzebuje jawnego dobicia. */
.nav-item[hidden], .nav-grupa[hidden] { display: none; }
.nav-item:hover { background: var(--surface-3); text-decoration: none; }
.nav-item.active { background: var(--menu-aktywna); color: var(--accent-strong); font-weight: var(--w-bold); }
.nav-item .icon, .nav-catalog-group summary .icon { flex: none; width: 20px; height: 20px; }
/* Licznik przy pozycji menu („Zadania”): we wzorze sama liczba po prawej, bez pigułki. Pigułka `.nav-item-badge`
   zostaje dla grup zadań (06-elementy / 15-zadania) — tu tylko licznik w menu bocznym. */
.nav-item-badge { display: inline-grid; place-items: center; min-width: var(--s-5); height: var(--s-5); margin-left: auto; padding: 0 var(--s-1); border-radius: var(--r-pill); background: var(--accent); color: var(--on-accent); font-size: var(--t-xs); font-weight: var(--w-strong); line-height: 1; }
.sidebar .nav-item-badge { display: inline; min-width: 0; height: auto; padding: 0; border-radius: 0; background: none; color: var(--text-faint); font-weight: var(--w-bold); line-height: inherit; }
/* Grupa „Kartoteki" (details/summary) — summary wygląda i reaguje jak pozycja menu. */
.nav-catalog-group summary { display: flex; align-items: center; gap: var(--s-2); min-height: var(--h-control); padding-inline: var(--s-2); border-radius: var(--r-sm); color: var(--menu-tekst); font-size: var(--t-base); font-weight: var(--w-medium); cursor: pointer; list-style: none; transition: background var(--szybko), color var(--szybko); }
.nav-catalog-group summary:hover { background: var(--surface-3); }
/* RM8: strzałka po prawej mówi, że „Kartoteki” się rozwijają; pozycje podrzędne mniejsze (13 px, ikona 16 px) i wcięte. */
.nav-catalog-group summary::after { content: ""; width: 14px; height: 14px; margin-left: auto; background: var(--text-faint); transition: transform var(--szybko); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat; }
.nav-catalog-group[open] summary::after { transform: rotate(180deg); }
.nav-catalog-list { display: flex; flex-direction: column; gap: 2px; padding: 2px 0 var(--s-1) var(--s-4); }
.nav-catalog-list .nav-item { font-size: var(--t-sm); }
.nav-catalog-list .nav-item .icon { width: 16px; height: 16px; }
/* RM8: po ręcznym „Zwiń menu” (menu konta) na dole szyny stoi przycisk „Rozwiń menu” — żeby nie trzeba było szukać go
   w menu konta. Wzór nie ma zwijania na szerokim ekranie, więc przycisk widać tylko w tym stanie (ekran szerszy niż 1180 px). */
.sidebar > .collapse-button { display: none; }
:root:not([data-ui="mobile"]) .sidebar-collapsed .sidebar > .collapse-button { display: flex; flex: none; }
/* 20.10.2026: na tablecie i wąskim oknie (≤ 1180 px) przycisk stoi na dole menu ZAWSZE — zwinięte: strzałka w prawo „rozwiń”,
   rozwinięte (podpisy widać): strzałka w lewo „zwiń”. Wcześniej szyna ikon była tam na stałe i nie dało się odczytać, co jest czym. */
@media (max-width: 1180px) {
  :root:not([data-ui="mobile"]) .sidebar > .collapse-button { display: flex; flex: none; }
  :root:not([data-ui="mobile"]) .app-shell:not(.sidebar-collapsed) .sidebar > .collapse-button .icon { transform: rotate(180deg); }
}
/* --- SZYNA SAMYCH IKON — JEDNA reguła ---------------------------------------
   Menu jest wąskie (76 px, jak we wzorze): po „Zwiń menu” (menu konta), domyślnie na tablecie i zawsze przy ekranie
   do 1180 px. Decyduje sama szerokość panelu: jest kontenerem `menu` i gdy jest węższy niż 120 px, zostają ikony 22 px
   w polu 56 × 44 px, a nagłówki grup, napis logo i licznik znikają. Podpis pozycji (.nav-napis, 01-api-theme.js) jest
   ukryty wizualnie, ale zostaje dla czytnika ekranu; pozycja dostaje dymek z nazwą (dymkiMenu).
   Tura 5 (ergonomia): zapamiętane „zwinięte” nie działa w trybie telefonu (menu jest tam arkuszem „Więcej”). */
:root:not([data-ui="mobile"]) .app-shell.sidebar-collapsed { grid-template-columns: var(--sidebar-w-waski) minmax(0, 1fr); }
@container menu (width < 120px) {
  .sidebar .brand { align-self: center; padding-inline: 0; }
  .sidebar :is(.brand strong, .nav-grupa, .nav-item-badge, .service-banner, .stopka-programu) { display: none; }
  .sidebar :is(.nav-item, summary) { justify-content: center; gap: 0; width: 56px; height: 44px; min-height: 0; margin-inline: auto; padding-inline: 0; font-size: 0; }
  .sidebar :is(.nav-item, summary) .icon { width: 22px; height: 22px; }
  /* POPR2-n13: podpis ukryty wizualnie, ale z normalną wielkością dla czytnika ekranu.
     PZ6 (v15 b20, 05.10.2026): pozycja menu jest punktem odniesienia swojego ukrytego podpisu. Bez tego podpis (position: absolute)
     liczył się od całej strony, nie podlegał przewijaniu listy menu i przy liście wyższej niż okno (telefon poziomo; tablet po
     rozwinięciu kartotek) wydłużał DOKUMENT — całą ramę programu dało się przesunąć palcem poza ekran (679 i 1557 px przy oknie 390 px). */
  .sidebar :is(.nav-item, summary) { position: relative; }
  .sidebar .nav-napis { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); font-size: var(--t-sm); white-space: nowrap; }
  .sidebar .nav-catalog-list { padding-left: 0; }
  .sidebar .nav-catalog-group summary::after { display: none; }
  .sidebar .nav-catalog-list .nav-item .icon { width: 22px; height: 22px; }
}
/* ============================ OBSZAR ROBOCZY ============================== */
.workspace { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* RM5 (v15 b14, 04.10.2026): GÓRNY PASEK JAK WE WZORZE — 60 px, tytuł krojem tytułów 19/700, pole „Szukaj wszędzie”
   260 × 36 z podpowiedzią „Ctrl K”, ikony 36 × 36 (rysunek 20 px) z licznikiem w akcencie, kreska i konto (awatar 32 px
   + imię). Do 1180 px: sama lupa, bez Pomocy (jest w menu konta), awatar bez imienia. Telefon: 9-telefon/1-podstawa.css. */
.topbar { position: sticky; top: 0; z-index: var(--z-topbar); display: flex; flex: none; align-items: center; gap: var(--s-2); height: var(--topbar-h); padding-inline: var(--s-5); background: var(--surface); border-bottom: var(--kreska); }
.topbar-title { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
/* Za długi tytuł kończy się wielokropkiem, a nie ucina w pół litery (23.09.2026). */
.topbar-title h1 { min-width: 0; overflow: hidden; font-family: var(--font-tytul); font-size: var(--t-lg); font-weight: var(--w-strong); letter-spacing: -.01em; line-height: var(--lh-base); text-overflow: ellipsis; white-space: nowrap; }
.top-actions { display: flex; align-items: center; gap: var(--s-2); margin-left: auto; }
/* Pole „Szukaj wszędzie” — przycisk otwierający okno szukania (ui/szukaj-wszedzie.js), nie pole do pisania. */
.szukaj-pole { display: flex; flex: none; align-items: center; gap: var(--s-2); width: 260px; height: var(--h-control); padding: 0 var(--s-3); border-radius: var(--r-sm); background: var(--surface-3); color: var(--text-faint); font-size: var(--t-base); text-align: left; cursor: pointer; }
.szukaj-pole:hover { color: var(--text-muted); }
.szukaj-pole .icon { flex: none; width: 16px; height: 16px; }
.szukaj-pole kbd { margin-left: auto; padding: 0 var(--s-1); border: 1px solid var(--line-strong); border-radius: 5px; font-size: var(--t-xs); white-space: nowrap; }
/* Ikony paska: przezroczyste, szare tło dopiero pod kursorem; licznik w rogu — akcent z obwódką w kolorze paska. */
.top-actions > .icon-button { position: relative; width: var(--h-control); height: var(--h-control); border-radius: var(--r-sm); background: transparent; color: var(--text-muted); }
.top-actions > .icon-button:hover { background: var(--surface-3); }
.top-actions > .icon-button .icon { width: 20px; height: 20px; }
.top-actions > .icon-button .notification-count { top: 1px; right: 0; min-width: 18px; height: 18px; padding: 0 var(--s-1); border: 2px solid var(--surface); border-radius: var(--r-sm); background: var(--accent); color: var(--on-accent); font-size: var(--t-xs); font-weight: var(--w-strong); line-height: 1; box-shadow: none; }
/* Konto: kreska, awatar 32 px, imię. Menu konta (Ustawienia, Podgląd konta, Pomoc, motyw, język, Zwiń menu, Wyloguj): 06-elementy. */
.top-actions > .user-chip { margin-left: var(--s-2); padding-left: var(--s-3); border-left: var(--kreska); font-size: var(--t-base); font-weight: var(--w-bold); }
/* Podgląd konta: przycisk jest w menu konta; w pasku stoi tylko pigułka „Podgląd: …” w TRAKCIE podglądu. */
.top-actions > .view-as:empty { display: none; }
/* MARGINES ROBOCZY CAŁEJ APLIKACJI — jedno miejsce, jedna zmienna: odstęp między krawędzią obszaru treści a zawartością
   KAŻDEGO widoku. RM3 (v15 b14): wartości ze wzoru wyglądu (`.tresc`: 16 px góra, 24 px boki, 32 px dół). */
:root { --margines-roboczy-y: var(--s-4); --margines-roboczy-x: var(--s-5); --margines-roboczy-dol: var(--s-6); }
.content { flex: 1; min-height: 0; overflow-y: auto; padding: var(--margines-roboczy-y) var(--margines-roboczy-x) var(--margines-roboczy-dol); }
/* Sterowanie mobilne — na desktopie nieobecne. `.sidebar .mobile-only-nav-action` (waga 0,2,0) jest KONIECZNE: sam
   `.mobile-only-nav-action` przegrywał z `.theme-toggle` (ten sam ciężar, późniejszy plik). */
.mobile-menu-button, .mobile-only-nav-action, .mobile-tabbar { display: none; }
.sidebar .mobile-only-nav-action { display: none; }
/* ============================ EKRAN LOGOWANIA ============================= */
.app-shell.is-login,
:root:not([data-ui="mobile"]) .app-shell.is-login.sidebar-collapsed { grid-template-columns: minmax(0, 1fr); }   /* 20.10.2026: zwinięte menu (szyna) nie może zwężać ekranu logowania do kolumny 76 px */
.is-login .sidebar, .is-login .topbar, .is-login .mobile-tabbar { display: none; }
.is-login .content { display: grid; place-items: center; padding: var(--s-6); }
/* --- EKRAN LOGOWANIA: formularz, logo, komunikaty ----------------------------- (z dawnego 27, K16)
   Panel bez ramki i cienia: pojedynczy formularz na pustej stronie nie
   potrzebuje pudełka, żeby było wiadomo, gdzie się kończy. */
.login-shell { display: grid; place-items: center; width: 100%; min-height: calc(100dvh / var(--skala-ui)); padding: var(--s-5); }
.login-panel { --panel-w: 360px; display: grid; gap: var(--s-4); width: min(var(--panel-w), 100%); border: 0; background: none; box-shadow: none; }
.login-brand-only { display: grid; place-items: center; min-height: 96px; }
.login-logo-image { max-width: 240px; max-height: 96px; object-fit: contain; }
.login-logo-fallback { color: var(--text); font-size: var(--t-xl); font-weight: var(--w-bold); letter-spacing: .04em; }
.login-fields { display: grid; gap: var(--s-3); }
/* ERG5-06: podpisy „E-mail" / „Hasło" 12 px (ogólna etykieta pola ma 10 px — przy dwóch polach na
   pustej stronie to było najdrobniejsze pismo ekranu). Ta sama wielkość w logowaniu nad oknem. */
.login-fields label, .login-nad-oknem .field > label { font-size: var(--t-sm); }
.login-message { color: var(--text-muted); line-height: var(--lh-base); text-align: center; }
/* Błąd logowania: czerwony, POD polem, którego dotyczy — nie szary jak zwykła informacja. */
.login-blad { color: var(--danger); font-size: var(--t-sm); font-weight: var(--w-medium); line-height: var(--lh-base); }
.login-message.login-blad { text-align: left; }
/* Telefon obrócony poziomo (ten sam warunek co dymki w 05-komponenty): 390 px wysokości nie mieści
   logo 96 px + dwóch pól + przycisków — „Odzyskaj hasło" wypadało pod dolną krawędź. Mniejsze logo. */
:where(:root[data-telefon-poziomo]) .login-shell { padding-block: var(--s-3); }
:where(:root[data-telefon-poziomo]) .login-panel { gap: var(--s-2); }
:where(:root[data-telefon-poziomo]) .login-brand-only { min-height: 0; }
/* DS1: telefon poziomo — formularz zajmuje całą wysokość, więc stopka stoi w wolnym rogu ekranu, poza układem formularza. */
:where(:root[data-telefon-poziomo]) .stopka-programu-logowanie { position: fixed; right: var(--s-3); bottom: var(--s-2); padding: 0; }
:where(:root[data-telefon-poziomo]) .login-logo-image, :where(:root[data-telefon-poziomo]) .login-brand-only .brand-logo { max-height: 48px; }
.login-recovery-button { justify-self: center; padding: 0 var(--s-2); border: 0; background: none; color: var(--accent-strong); font-weight: var(--w-medium); cursor: pointer; }
.login-recovery-button:hover { text-decoration: underline; }
/* Dotyk (telefon i tablet): „Odzyskaj hasło” ma pole klikania jak inne przyciski (było 20 px wysokości). */
:root:is([data-ui="mobile"], [data-ui="tablet"]) .login-recovery-button { min-height: var(--h-control); }
/* ============================ NAKŁADKI ==================================== */
.app-busy { position: fixed; inset: 0; z-index: var(--z-busy); display: grid; place-items: center; background: var(--zaslona); opacity: 0; visibility: hidden; transition: opacity var(--szybko) linear, visibility var(--szybko) linear; }
.app-busy.is-visible { opacity: 1; visibility: visible; }
/* Panel blokady na czas zapisu — szerokość 1:1 z monolitem (linia 19230).
   Bez min-width zwężał się do treści (95 px zamiast 294) i wyglądał jak
   przypadkowy dymek zamiast okna blokującego ekran. */
.app-busy-panel { display: grid; justify-items: center; gap: var(--s-3); min-width: min(360px, calc(100vw / var(--skala-ui) - var(--s-7))); padding: var(--s-6) var(--s-6) var(--s-5); background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--cien-3); text-align: center; }
.app-busy-mark { position: relative; display: grid; place-items: center; width: 116px; height: 116px; }
.app-busy-ring { position: absolute; inset: 0; border: var(--ring-w) solid var(--line); border-top-color: var(--accent-strong); border-radius: var(--r-pill); animation: uklad-obrot var(--ring-czas) linear infinite; }
/* Logo WEWNĄTRZ pierścienia. Bez tej reguły obrazek wchodził w swoim naturalnym
   rozmiarze — a logo firmy ma kilkaset pikseli — i wychodził daleko poza obracający
   się pierścień. Rozmiar jak w v8: mieści się w kole 116 px z zapasem na obwódkę,
   `contain` nie zniekształca logo o dowolnych proporcjach. */
.app-busy-mark img { position: absolute; left: 50%; top: 50%; width: 86px; height: 70px; max-width: none; max-height: none; object-fit: contain; object-position: center; transform: translate(-50%, -50%); }
@keyframes uklad-obrot {
  to { transform: rotate(1turn); }
}
/* G5 (27.09.2026): MAŁY WSKAŹNIK zamiast zasłony — zwykły odczyt i zapis (klasy ustawia setAppBusy w 01-api-theme.js).
   Pigułka na dole, na środku: góra ekranu należy do dymków (.toast-root), prawy dolny róg do powiadomień czatu.
   Warstwa nie ma tła; „odczyt” przepuszcza kliknięcia, „zapis” (.is-blokuje) je przyjmuje do końca zapisu.
   Pigułka pojawia się po 0,2 s — odczyt krótszy niczego nie pokazuje. Duże okno z logo (.app-busy bez .is-maly) — bez zmian. */
.app-busy.is-maly { place-items: end center; padding-bottom: calc(var(--s-4) + var(--busy-dol, 0px)); background: transparent; transition: none; pointer-events: none; }
.app-busy.is-maly.is-blokuje { pointer-events: auto; cursor: progress; }
.app-busy.is-maly .app-busy-panel { display: flex; align-items: center; gap: var(--s-2); min-width: 0; max-width: calc(100vw / var(--skala-ui) - 2 * var(--s-4)); padding: var(--s-2) var(--s-4) var(--s-2) var(--s-3); border: var(--kreska); border-radius: var(--r-pill); box-shadow: var(--cien-2); pointer-events: none; animation: uklad-pojaw var(--szybko) linear .2s both; }
.app-busy.is-maly .app-busy-mark { width: 16px; height: 16px; }
.app-busy.is-maly .app-busy-ring { border-width: 2px; }
.app-busy.is-maly .app-busy-mark img, .app-busy.is-maly .app-busy-fallback, .app-busy.is-maly .app-busy-panel small { display: none; }
.app-busy.is-maly [data-app-busy-text] { font-size: var(--t-sm); font-weight: var(--w-medium); white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }   /* n39b: długi napis (postęp wysyłki) mieści się w ekranie telefonu */
/* POPR-n10: EKRAN STARTOWY — ramka programu i szkielet listy, zanim przyjdą dane (ui/ekran-startowy.js). Menu, przyciski
   paska i dolne zakładki niewidoczne do czasu danych (nie wiadomo jeszcze, co ta osoba może). */
.is-startuje :is(.sidebar .nav, .sidebar .collapse-button, .top-actions, .mobile-tabbar) { visibility: hidden; }
.ekran-startowy { display: grid; gap: var(--s-3); padding: var(--s-4); }
.ekran-startowy-napis { display: flex; align-items: center; gap: var(--s-2); margin: 0; color: var(--text-muted); font-size: var(--t-sm); }
.ekran-startowy-kolko { width: 16px; height: 16px; border: 2px solid var(--line); border-top-color: var(--accent); border-radius: 50%; animation: ekran-startowy-obrot .8s linear infinite; }
.ekran-startowy-lista { display: grid; gap: var(--s-2); }
.ekran-startowy-wiersz { display: grid; grid-template-columns: 72px minmax(0, 3fr) minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-3); align-items: center; min-height: 56px; padding: 0 var(--s-3); border: 1px solid var(--line); border-radius: var(--r); }
.ekran-startowy-wiersz > span { height: 12px; border-radius: var(--r-pill); background: linear-gradient(90deg, var(--surface-2) 0%, var(--surface-3) 50%, var(--surface-2) 100%) 0 0 / 200% 100%; animation: ekran-startowy-blysk 1.4s ease-in-out infinite; }
.ekran-startowy-wiersz > span:nth-child(2) { height: 14px; }
:where(:root[data-ui="mobile"]) .ekran-startowy-wiersz { grid-template-columns: minmax(0, 1fr); gap: var(--s-2); min-height: 88px; padding: var(--s-3); }
:where(:root[data-ui="mobile"]) .ekran-startowy-wiersz > span:nth-child(n+3) { display: none; }
@keyframes ekran-startowy-obrot { to { transform: rotate(360deg); } }
@keyframes ekran-startowy-blysk { from { background-position: 100% 0; } to { background-position: -100% 0; } }
@media (prefers-reduced-motion: reduce) { .ekran-startowy-wiersz > span { animation: none; } .ekran-startowy-kolko { animation-duration: 2.4s; } }
@keyframes uklad-pojaw {
  from { opacity: 0; }
  to { opacity: 1; }
}
/* Stos powiadomień: JEDYNA definicja jest w 05-komponenty.css (góra, na środku).
   Wcześniej stała tu druga, stara wersja „prawy dolny róg". Żadna nie kasowała
   drugiej, więc element dostawał `top` z jednego arkusza i `bottom` z drugiego —
   przy `position: fixed` rozciągało go to na CAŁĄ wysokość ekranu (942 px), a
   `align-items: flex-end` spychało dymek na sam dół. Monolit v8 trzyma jedną
   regułę i jawnie zeruje `right`/`bottom` — tu wystarczy nie dublować. */
/* ============================ SZYNA IKON (≤1180px) ======================== */
/* RM4 (v15 b14): jak we wzorze — do 1180 px szerokości menu jest szyną samych ikon (76 px), w górnym pasku zostaje sama
   lupa, Pomoc chowa się do menu konta, przy awatarze nie ma imienia. Podpisy chowa reguła „SZYNA SAMYCH IKON” wyżej. */
@media (max-width: 1180px) {
  .szukaj-pole { justify-content: center; width: var(--h-control); padding: 0; }
  .szukaj-pole .icon { width: 20px; height: 20px; }
  .szukaj-pole :is(.szukaj-pole-napis, kbd), .top-actions > .top-pomoc, .top-actions .user-chip-trigger > strong { display: none; }
}
/* Tablet (dotyk) szerszy niż 1180 px ma menu zwinięte domyślnie (01-api-theme.js) — pasek jak przy szynie. */
:root[data-ui="tablet"] .szukaj-pole { justify-content: center; width: var(--h-control); padding: 0; }
:root[data-ui="tablet"] .szukaj-pole .icon { width: 20px; height: 20px; }
:root[data-ui="tablet"] :is(.szukaj-pole-napis, .szukaj-pole kbd, .top-actions > .top-pomoc, .top-actions .user-chip-trigger > strong) { display: none; }
/* ============================ TELEFON ===================================== */
/* Telefon = :root[data-ui="mobile"]: szerokość ≤ 820 px albo telefon obrócony poziomo (FRO-29). Warunek liczy JS (tabela
   PROGI w 00-przed-malowaniem.js i responsive.js) — arkusze nie powtarzają go w @media (K13, v14).
   `:where(…)` = warunek bez wagi: reguła telefonu ma tę samą specyficzność co dawniej w @media.
   RM6 (v15 b14, 04.10.2026) — RAMA TELEFONU JAK WE WZORZE:
     · PIONOWO: bez menu bocznego; górny pasek 56 px (tytuł, powiadomienia, wiadomości), dolny pasek 78 px
       (Pulpit · Projekty · Skanuj QR · Zadania · Więcej), menu = arkusz „Więcej” od dołu z grupami jak w menu.
     · POZIOMO ([data-telefon-poziomo]): jak tablet we wzorze — szyna ikon 76 px zamiast dolnego paska, górny pasek
       z lupą, zadaniami, powiadomieniami, wiadomościami i awatarem. Treść zostaje w układzie telefonu. */
:where(:root[data-ui="mobile"]:not([data-telefon-poziomo])) .app-shell { grid-template-columns: minmax(0, 1fr); }
:where(:root[data-ui="mobile"][data-telefon-poziomo]) .app-shell { grid-template-columns: var(--sidebar-w-waski) minmax(0, 1fr); }
/* Przyciemnienie tła pod otwartym arkuszem „Więcej” (::after = ostatnie dziecko #app, przykrywa dolny pasek) */
:where(:root[data-ui="mobile"]:not([data-telefon-poziomo])) .app-shell.mobile-menu-open::after { content: ""; position: fixed; inset: 0; z-index: var(--z-tabbar); background: color-mix(in srgb, var(--text) 38%, transparent); }
/* W arkuszu „Więcej” są rzeczy, których wzór nie trzyma w górnym pasku telefonu: szukanie, pomoc, motyw, język, konto.
   Waga podniesiona tak samo jak w regule ukrywającej, inaczej tamta wygra. */
:where(:root[data-ui="mobile"]:not([data-telefon-poziomo])) .mobile-only-nav-action, :where(:root[data-ui="mobile"]:not([data-telefon-poziomo])) .sidebar .mobile-only-nav-action { display: flex; }
:where(:root[data-ui="mobile"]) .topbar-title { flex: 1 1 auto; }
/* POLA FILTRÓW NA CAŁĄ SZEROKOŚĆ (#39): listy wyboru w paskach narzędzi (Status, Etap, Rodzaj…) brały szerokość z treści
   i ucinały podpowiedź. Na telefonie jedno pole = jeden rząd, jak w aplikacji. */
:where(:root[data-ui="mobile"]) .toolbar > .field, :where(:root[data-ui="mobile"]) .catalog-toolbar > .field { flex: 1 1 100%; min-width: 0; }
:where(:root[data-ui="mobile"]) .toolbar > .field .combo, :where(:root[data-ui="mobile"]) .combo:has(> .ords2-buyer-select) { width: 100%; max-width: 100%; }
/* Treść Zadań liczyła wysokość z JEDNEGO rzędu paska; na telefonie wysokość bierze z układu (flex kolumny). */
:where(:root[data-ui="mobile"]) .content:has(.zn-page) { height: auto; }
/* Dolny pasek (tylko pionowo). Wygląd według wzoru: 9-telefon/1-podstawa.css §4. */
:where(:root[data-ui="mobile"]:not([data-telefon-poziomo])) .mobile-tabbar { position: fixed; inset-inline: 0; bottom: var(--tabbar-lift, 0px); z-index: var(--z-tabbar); display: flex; align-items: center; background: var(--surface); border-top: var(--kreska); }
/* ============================ MENU NA TELEFONIE: ARKUSZ „WIĘCEJ" ============ */
/* Menu boczne na telefonie PIONOWO = arkusz od dołu (otwiera go „Więcej” w dolnym pasku): uchwyt, tytuł, grupy jak
   w menu (Praca / Produkcja / Dane / Firma / Program), pod każdą kafle 4 w rzędzie (ikona nad podpisem), Kartoteki jako
   rozwijana lista wierszy. RM6: wygląd wg wzoru (`.wiecej-ark`): białe tło arkusza, kafle na szarym tle z ramką. */
.mobile-sheet-head { display: none; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar { position: fixed; inset: auto 0 0 0; z-index: var(--z-szuflada); width: auto; max-height: min(82dvh / var(--skala-ui), 760px); padding: 0 0 var(--bezpieczny-dol); border-right: 0; border-radius: var(--arkusz-r) var(--arkusz-r) 0 0; background: var(--surface); transform: translateY(104%); transition: transform var(--czas-arkusz) var(--krzywa-arkusz); box-shadow: var(--cien-2); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .mobile-menu-open .sidebar { transform: translateY(0); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .brand { display: none; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .stopka-programu { padding: 0 var(--s-4) var(--s-3); text-align: center; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .stopka-programu :is(.stopka-programu-autor, .stopka-programu-kropka) { display: inline; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .mobile-sheet-head { position: relative; display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); padding: var(--s-5) var(--s-3) 0 var(--s-4); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .mobile-sheet-head::before { content: ""; position: absolute; top: var(--s-2); left: 50%; width: 40px; height: 4px; border-radius: var(--r-pill); background: var(--line); transform: translateX(-50%); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .mobile-sheet-head strong { font-family: var(--font-tytul); font-size: var(--t-lg); font-weight: var(--w-strong); letter-spacing: -.01em; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .mobile-sheet-head .icon-button { width: var(--h-control); height: var(--h-control); border-color: transparent; border-radius: var(--r-sm); background: transparent; color: var(--text-muted); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-2); padding: 0 var(--s-4) var(--s-5); overflow-y: auto; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-grupa { grid-column: 1 / -1; margin: var(--s-3) 0 0; padding: 0; font-weight: var(--w-strong); }
/* Kafle: ikona nad podpisem, cel dotyku ≥ 68 px. */
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item { flex-direction: column; justify-content: center; gap: var(--s-1); height: auto; min-height: 68px; padding: var(--s-2) var(--s-1); border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); color: var(--text); font-size: var(--t-xs); font-weight: var(--w-bold); line-height: 1.2; text-align: center; box-shadow: none; overflow-wrap: anywhere; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item .icon,
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item > svg { width: 20px; height: 20px; }
/* Podpis dłuższy niż kafel — czcionka dobrana przez public/js/ui/aplikacja-mobilna.js §9, bez łamania słowa. */
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item[data-podpis-dopasowany] { padding-inline: 2px; font-size: var(--podpis-rozmiar, var(--t-xs)); overflow-wrap: normal; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item.active { border-color: var(--accent); color: var(--accent-strong); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item:active { transform: scale(.96); }
/* W arkuszu licznik „Zadania” stoi w rogu kafla. */
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item { position: relative; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .nav-item .nav-item-badge { position: absolute; top: var(--s-1); right: var(--s-2); margin: 0; }
/* Kartoteki: pełna szerokość; „Kartoteki” to wiersz-przycisk ze strzałką (tura 5: szary podpis nie wyglądał na klikalny),
   pod nim jedna lista wierszy ze strzałką w prawo. */
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-group { grid-column: 1 / -1; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-group summary { position: relative; gap: var(--s-3); min-height: 48px; padding: var(--s-2) 44px var(--s-2) var(--s-4); border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); color: var(--text); font-size: var(--t-base); font-weight: var(--w-bold); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-group summary::after { content: ""; position: absolute; top: 50%; right: var(--s-4); width: 16px; height: 16px; margin-top: -8px; background: var(--text-muted); transition: transform var(--czas-nacisk) ease; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-group[open] summary { margin-bottom: var(--s-2); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-group[open] summary::after { transform: rotate(180deg); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-group summary:active { background: var(--surface-3); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; padding: 0; overflow: hidden; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-list .nav-item { position: relative; gap: var(--s-3); min-height: 48px; padding: var(--s-2) 40px var(--s-2) var(--s-4); border-radius: 0; background: transparent; color: var(--text); font-size: var(--t-base); font-weight: var(--w-medium); line-height: 1.2; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-list .nav-item + .nav-item::before { content: ""; position: absolute; top: 0; right: 0; left: 48px; height: 1px; background: var(--line-soft); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-list .nav-item::after { content: ""; position: absolute; top: 50%; right: var(--s-4); width: 14px; height: 14px; margin-top: -7px; background: var(--text-faint); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E") center / contain no-repeat; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-list .nav-item .icon { width: 20px; height: 20px; color: var(--text-muted); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-list .nav-item.active { background: var(--menu-aktywna); color: var(--accent-strong); }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav-catalog-list .nav-item:active { background: var(--surface-3); }
/* Konto w arkuszu (telefon nie ma awatara w górnym pasku): wiersz „Podgląd konta” z listą osób — pełna szerokość. */
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .view-as { grid-column: 1 / -1; }
:root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar .nav > .view-as:empty { display: none; }
/* Ruch ograniczony: arkusz pojawia się przenikaniem, bez wysuwania. */
@media (prefers-reduced-motion: reduce) {
  :root[data-ui="mobile"]:not([data-telefon-poziomo]) .sidebar { transition: opacity 120ms linear; }
}
/* --- Zapis w toku (z dawnego 28, K17) --- */
/* Zapis w toku — kursor mówi „czekaj”, nic nie znika i nic się nie blokuje. */
body.is-app-busy button, body.is-app-busy input, body.is-app-busy select { cursor: progress; }
.app-busy-fallback { display: grid; place-items: center; width: 74px; height: 74px; border-radius: var(--r-sm); background: var(--accent); color: var(--on-accent); font-size: var(--t-3xl); font-weight: var(--w-bold); line-height: 1; }
.app-busy-fallback:empty, .app-busy-fallback[hidden] { display: none; }
/* DRUK WIDOKU (27.10.2026, audyt interfejsu): Ctrl+P na dowolnym widoku drukował całe okno programu — menu, pasek górny i tabelę ucinaną po prawej
   (przewijają się wnętrza, nie strona, a szkielet ma wysokość okna). Druk widoku = sama treść, na całą szerokość kartki, bez przewijania wnętrza.
   Druk kartki cięcia (html.d-druk-kartki) ma własne reguły w 26-magazyn-i-dostawcy.css i zostaje nietknięty. */
@media print {
  html:not(.d-druk-kartki) .sidebar, html:not(.d-druk-kartki) .topbar, html:not(.d-druk-kartki) .mobile-tabbar, html:not(.d-druk-kartki) .toast-root, html:not(.d-druk-kartki) .app-busy { display: none; }
  html:not(.d-druk-kartki) .app-shell, html:not(.d-druk-kartki) .workspace, html:not(.d-druk-kartki) .content { display: block; height: auto; overflow: visible; }
  html:not(.d-druk-kartki) .content { padding: 0; }
}
