﻿/* ── DS Design Tokens ──────────────────────────────────────── */
:root {
  /* Aplikacja ma jedną paletę — jasną, wpisaną w tokeny poniżej, bez wariantu ciemnego.
     Bez tej deklaracji przeglądarka nie ma skąd tego wiedzieć i na systemie ustawionym na tryb
     ciemny rysuje po swojemu wszystko, czego nie malujemy sami: paski przewijania, pola formularzy,
     menu <select>. Efekt to ciemne elementy na jasnym tle — najbardziej widoczne na pasku
     przewijania długiej listy, bo tam sąsiaduje z białą powierzchnią przez całą wysokość.
     Deklaracja dotyczy też ramek osadzonych na stronie (kafelek wyboru konta Google). */
  color-scheme: light;

  /* Paleta Prototypu A (docs/ui-handoff/prototyp-a, decyzja właściciela 2026-09-28). Tło jest ciepło-
     szare, tekst drugiego rzędu ciemniejszy niż dawniej (#525252 zamiast #737373) — przy bazie 14,5 px
     jaśniejszy szary czytał się jak wyłączony. */
  --ds-bg:       #f7f7f6;
  --ds-surface:  #ffffff;
  --ds-ink:      #0a0a0a;
  --ds-ink2:     #262626;
  --ds-muted:    #525252;
  --ds-muted2:   #737373;
  --ds-line:     #e5e5e5;
  --ds-line2:    #f0f0f0;
  /* Ramka kontrolek (przycisk, pole, lista) — o ton mocniejsza niż linie tabeli: kontrolka ma wyglądać
     na klikalną bez najeżdżania (zasada 12 w docs/ui-handoff/README.md). */
  --ds-control-line: #d4d4d4;
  --ds-hover:    #f5f5f4;
  --ds-sel:      #f8fafc;
  --ds-accent:   #0a0a0a;

  /* Pięć stanów, każdy jako para kolor + tło. Kolor niesie stan, nigdy markę:
       todo — „twoja kolej”, rzecz czeka na człowieka i jest w terminie (niebieski, decyzja 2026-09-28);
       late — awaria albo po terminie;
       wait — ktoś ma to uzupełnić albo rozstrzygnąć;
       done — zrobione;
       idle — nic się nie dzieje, informacja. */
  --ds-todo:      #1d4ed8;  --ds-todo-bg: #eff6ff;
  --ds-late:      #b91c1c;  --ds-late-bg: #fef2f2;
  --ds-wait:      #854d0e;  --ds-wait-bg: #fefce8;  --ds-wait-line: #fde68a;
  --ds-done:      #15803d;  --ds-done-bg: #f0fdf4;
  --ds-idle:      #525252;  --ds-idle-bg: #f4f4f5;

  /* Dawne nazwy stanów zostają jako aliasy — czyta je kilkaset reguł i styli inline. */
  --ds-ok:       var(--ds-done);
  --ds-ok-soft:  var(--ds-done);
  --ds-ok-bg:    var(--ds-done-bg);
  --ds-bad:      var(--ds-late);
  --ds-bad-bg:   var(--ds-late-bg);
  /* Żółty belek i obwódek zostaje jasny; jako TEKST idzie --ds-warn-ink (#ca8a04 na bieli ma ~3:1). */
  --ds-warn:     #ca8a04;
  --ds-warn-bg:  var(--ds-wait-bg);
  --ds-warn-ink: var(--ds-wait);
  --ds-info:     var(--ds-todo);
  --ds-info-ink: var(--ds-todo);
  --ds-info-bg:  var(--ds-todo-bg);

  --ds-mono: 'Geist Mono', ui-monospace, Consolas, monospace;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  height: 100%;
}

body {
  height: 100%;
  background: var(--ds-bg);
  font-family: 'Geist', 'Inter', -apple-system, sans-serif;
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--ds-ink);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}
:focus-visible { outline: 2px solid var(--ds-todo); outline-offset: 2px; }

/* ── Skala desktopu (decyzja właściciela 2026-10-13) ─────────────────────────────────────────
   Ekranem bazowym jest laptop 14" (1280 px szerokości CSS przy skalowaniu 150 %). W 100 % lista
   Sprzedaży i Magazynu nie mieściła się na nim, a ten sam ekran przy powiększeniu Chrome 90 %
   wyglądał dobrze — więc skala 90 % jest wpisana w aplikację, a nie zostawiona każdemu do ustawienia.
   `zoom`, a nie przeliczenie kilkuset wymiarów w px: daje dokładnie to, co powiększenie przeglądarki.
   Tylko powłoka desktopowa (_Host.cshtml, ds-desktop) — telefon ma własny arkusz i własne wymiary.

   Dwie rzeczy zoom zmienia poza samą skalą i obie mają tu korektę:
   • jednostki vh/vw są skalowane jak px, więc „86vh” to wizualnie 77 % okna — miejsca, które liczą
     wysokość albo szerokość z okna, dzielą przez --ds-zoom (calc(86vh / var(--ds-zoom)));
   • pozycja ustawiana skryptem z getBoundingClientRect (wizualne px) trafia w element przeskalowany —
     ds-overlay.js dzieli ją przez currentCSSZoom.
   Procenty, inset i flex działają bez zmian (zmierzone w Chromium 152). */
:root { --ds-zoom: 1; }
body.ds-desktop { --ds-zoom: 0.9; zoom: var(--ds-zoom); }

/* ── Menu boczne ─────────────────────────────────────────────
   Nawigacja stała kiedyś w poziomym pasku nad treścią. Powłoka jest nieprzewijalna
   (body { overflow:hidden }), więc te 53 px wysokości szły wprost z listy produktów — a wysokość
   wiersza była już raz przedmiotem optymalizacji (Trello #53). Pionowy pasek oddaje tę wysokość
   i przy okazji ma miejsce na więcej pozycji, niż mieściło się w rzędzie.

   Zwinięte = same ikony, rozwinięte = ikony z podpisami. Stan niesie klasa ds-sidebar-collapsed na
   <body>, wypisywana przez serwer w _Host.cshtml z tego samego ciasteczka, które zapisuje
   przełącznik. Dlatego stan menu w ogóle nie przechodzi przez render Blazora: po F5 przeglądarka
   maluje właściwą szerokość przed pierwszym rysowaniem, więc nie ma czym mrugnąć. */
:root {
  --ds-sidebar-w:            232px;
  --ds-sidebar-w-collapsed:   64px;
}

.ds-shell { height: 100%; display: flex; }

/* Szerokość przełącza się drugą regułą width przy klasie na <body>, a nie podmianą zmiennej pod
   jednym `width: var(...)`. Oba warianty działają; ten jest wybrany, bo zmiennej nikt poza tą jedną
   deklaracją nie czyta, więc pośrednik niczego nie oszczędzał, a ukrywał, gdzie właściwie zapada
   decyzja o szerokości. */
.ds-sidebar {
  flex: 0 0 auto;
  width: var(--ds-sidebar-w);
  transition: width 180ms ease;
  display: flex; flex-direction: column; gap: 14px;
  padding: 16px 12px 12px;
  background: var(--ds-surface);
  border-right: 1px solid var(--ds-line);
  overflow: hidden;               /* przycina podpisy w trakcie zwijania */
  /* position + z-index nie są ozdobą i nie wolno ich „posprzątać”: .ds-filter-backdrop na Produktach
     to position:fixed z inset:0, czyli tło na cały ekran. Leży w kontekście układania paska filtrów
     (.ds-subheader, z-index:10), więc na poziomie dokumentu maluje się na 10 — i dokładnie dlatego
     dawny pasek górny (z-index:20) był klikalny przy otwartym filtrze. Menu bez własnej warstwy
     byłoby blokiem na poziomie 0: pierwsze kliknięcie w nawigację przy otwartym filtrze trafiałoby
     w przezroczyste tło i tylko zamykało filtr. */
  position: relative; z-index: 20;
}
body.ds-sidebar-collapsed .ds-sidebar { width: var(--ds-sidebar-w-collapsed); }
/* Wąskie okno nie ma z czego oddać 232 px — menu wchodzi w tryb ikon niezależnie od ciasteczka.
   Ta sama specyficzność co reguła bazowa, ale stoi niżej w pliku, więc wygrywa; przy zwiniętym
   ciasteczku wygrywa reguła z klasą na <body>, a obie dają tę samą szerokość. */
@media (max-width: 900px) { .ds-sidebar { width: var(--ds-sidebar-w-collapsed); } }

/* Podpis znika przez display:none, nie przez opacity. Wariant „przezroczysty, ale dalej w układzie”
   kusi płynnym zanikaniem, ale rozjeżdża geometrię zwiniętego menu: w wyśrodkowanej główce
   niewidzialna nazwa „Deadstock” dalej liczy się do szerokości i zbija logo z osi, a w stopce
   rezerwuje pusty wiersz pod awatarem. Animowana jest szerokość samego menu i to wystarcza. */
.ds-side-label { white-space: nowrap; }
body.ds-sidebar-collapsed .ds-side-label { display: none; }

/* Główka: logo i przełącznik obok siebie; po zwinięciu jeden pod drugim, oba na osi 28 px
   (10 px paddingu menu + połowa z 36 px światła) — tam, gdzie stają ikony nawigacji. */
/* Wcięcie z lewej 6, nie 10 jak w .ds-nav-item: znak logo ma 22 px, ikona nawigacji 14, więc przy
   równym wcięciu ich środki rozjeżdżają się o (22-14)/2 = 4 px i logo wisi 2 px w lewo od kolumny
   ikon. Różnica wcięć kasuje różnicę rozmiarów — oba środki wypadają na 27 px od krawędzi menu. */
.ds-side-head { display: flex; align-items: center; gap: 8px; padding: 2px 4px 2px 6px; }
body.ds-sidebar-collapsed .ds-side-head { flex-direction: column; align-items: center; gap: 6px; padding: 2px 0; }
.ds-side-head .ds-brand { flex: 1; min-width: 0; }
body.ds-sidebar-collapsed .ds-side-head .ds-brand { flex: 0 0 auto; }

.ds-side-toggle {
  width: 26px; height: 26px; flex-shrink: 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--ds-line); border-radius: 6px;
  background: transparent; color: var(--ds-muted); cursor: pointer;
  transition: background 100ms, color 100ms;
}
.ds-side-toggle:hover { background: var(--ds-line2); color: var(--ds-ink); }
.ds-side-toggle svg { transition: transform 180ms ease; }
body.ds-sidebar-collapsed .ds-side-toggle svg { transform: rotate(180deg); }

.ds-brand { display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
/* Znak marki, nie kwadrat z literą: rysunek jest konturem, więc tło i kolor tekstu odpadają, a
   zostaje sam kolor kreski. 22 px to nie estetyka, tylko warunek wyrównania opisany wyżej —
   zmiana rozmiaru wymaga zmiany wcięcia .ds-side-head, inaczej logo zjeżdża z kolumny ikon. */
.ds-brand-icon {
  width: 22px; height: 22px; flex-shrink: 0;
  display: block; color: var(--ds-ink);
}
.ds-brand-icon svg { display: block; width: 100%; height: 100%; }
.ds-brand-name { font-weight: 600; font-size: 15.5px; color: var(--ds-ink); letter-spacing: -0.2px; }

/* Pozycja menu 42 px wysokości (Prototyp A) — cel wygodny także dla myszy na laptopie. */
.ds-nav { display: flex; flex-direction: column; gap: 3px; }
.ds-nav-item {
  display: flex; align-items: center; gap: 11px;
  height: 42px; padding: 0 10px; font-size: 14.5px; color: var(--ds-ink2);
  text-decoration: none; border-radius: 8px; background: transparent;
  white-space: nowrap; overflow: hidden;
  transition: background 100ms, color 100ms;
}
.ds-nav-item svg { flex-shrink: 0; width: 16px; height: 16px; }
body.ds-sidebar-collapsed .ds-nav-item { justify-content: center; padding: 0; }
.ds-nav-item:hover { background: var(--ds-hover); color: var(--ds-ink); }
.ds-nav-item.active { background: #ececeb; color: var(--ds-ink); font-weight: 600; }
.ds-nav-item.disabled { pointer-events: none; opacity: 0.5; }

/* Czerwony wariant pozycji menu (.ds-nav-item--danger) miał jedno użycie — „Wyczyść dane”. Przycisk
   przeniósł się na /konto, do funkcji administratorskich, i jest tam zwykłym .ds-btn, więc reguła
   nie miała już czego malować. Menu składa się dziś wyłącznie z linków do widoków. */

/* ── Stopka menu: konto ─────────────────────────────────────
   Jedyny element stopki. Nie dla wyglądu: menu konta wysuwa się w górę z pozycji fixed, więc jego
   odstęp od dołu ekranu jest sumą wysokości wszystkiego, co stoi pod nim (patrz .ds-acct-menu).
   Numer wersji, który stał tu wcześniej, przeniósł się do stopki samego menu.

   Przełącznik języka (.ds-lang, .ds-lang-btn) stał tu wcześniej jako para przycisków z flagami;
   dziś jest wierszem w menu konta, ale reguły zostają — nosi je jeszcze arkusz „Więcej” na
   telefonie, gdzie język ma własny wiersz i miejsca na dwie flagi wystarcza. */
.ds-side-foot { display: flex; flex-direction: column; gap: 8px; }

.ds-lang { display: flex; align-items: center; gap: 2px; }
body.ds-sidebar-collapsed .ds-lang { flex-direction: column; align-items: center; }
.ds-lang-btn {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 8px; border-radius: 5px;
  border: 1.5px solid transparent; background: transparent;
  cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; line-height: 1;
  color: var(--ds-ink); opacity: .45;
  transition: opacity 150ms, border-color 150ms, background 150ms;
}
.ds-lang-btn img { border-radius: 2px; flex-shrink: 0; display: block; }
.ds-lang-btn--on { opacity: 1; border-color: var(--ds-accent); background: var(--ds-line2); }

/* ── Menu konta ─────────────────────────────────────────────
   Na zewnątrz widać wyłącznie nazwę konta; wszystko, co się z kontem robi — ustawienia, język,
   zarządzanie użytkownikami, wylogowanie — jest pod nią. Stopka niosła wcześniej cztery niezależne
   kontrolki obok siebie, więc rzecz klikana raz w życiu (język) sąsiadowała z tą, którą klika się
   przez pomyłkę (wylogowanie), a każda kolejna pozycja konta musiałaby walczyć o te same 200 px. */
.ds-acct { position: relative; }

.ds-acct-btn {
  display: flex; align-items: center; gap: 8px;
  /* Ujemny margines i dopełnienie wyrównują tło pod kursorem z powrotem do kolumny ikon nawigacji,
     którą samo tło by rozepchnęło — ta sama sztuczka, co przy dawnym linku konta. */
  margin: -3px -4px; padding: 3px 4px; width: calc(100% + 8px);
  border: none; border-radius: 6px; background: transparent;
  font-family: inherit; text-align: left; color: inherit; cursor: pointer;
  transition: background 100ms;
}
.ds-acct-btn:hover, .ds-acct-btn.is-open { background: var(--ds-line2); }
body.ds-sidebar-collapsed .ds-acct-btn { justify-content: center; margin: 0; padding: 3px 0; width: 100%; }
.ds-acct-btn-name {
  flex: 1; min-width: 0; font-size: 12.5px; color: var(--ds-muted);
  overflow: hidden; text-overflow: ellipsis;
}
.ds-acct-btn-chevron { color: var(--ds-muted2); flex-shrink: 0; }

/* position: fixed, a nie absolute względem .ds-acct: menu boczne ma overflow:hidden (przycina
   podpisy w trakcie zwijania) i przycięłoby też wysunięty panel. Fixed z tego wychodzi, bo
   .ds-sidebar nie ma transformu ani filtra, które robiłyby z niego blok zawierający — ale traci
   zaczep w przycisku, więc odstęp od dołu jest policzony: 12 px dopełnienia menu − 3 px ujemnego
   marginesu .ds-acct-btn + 32 px samego przycisku (26 px kółka + 2×3 px dopełnienia) + 6 px światła.
   Przycisk konta jest jedynym elementem stopki właśnie po to, żeby ta suma miała cztery składniki,
   a nie tyle, ile ktoś kiedyś dołoży pod spodem.
   z-index 26 — nad .ds-filter-backdrop (25), czyli tłem, które zamyka menu kliknięciem obok. */
.ds-acct-menu {
  position: fixed; left: 8px; bottom: 47px; z-index: 26;
  width: 246px; max-width: calc(100vw / var(--ds-zoom) - 16px);
  display: flex; flex-direction: column; gap: 1px; padding: 6px;
  background: var(--ds-surface); border: 1px solid var(--ds-line);
  border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,.20);
}

/* Kto jest zalogowany, pełną nazwą i adresem. Po zwinięciu menu bocznego przycisk pod spodem
   pokazuje samo kółko z inicjałami — to jedyne miejsce, w którym da się sprawdzić, na czyim
   koncie się jest, bez wchodzenia w ustawienia. */
.ds-acct-head {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 8px 10px; margin-bottom: 5px;
  border-bottom: 1px solid var(--ds-line);
}
.ds-acct-head-id { min-width: 0; }
.ds-acct-head-name {
  font-size: 14px; font-weight: 600; color: var(--ds-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ds-acct-head-mail {
  font-family: 'Geist Mono', monospace; font-size: 11.5px; color: var(--ds-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px;
}

.ds-acct-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 7px 8px; border: none; border-radius: 6px; background: transparent;
  font-family: inherit; font-size: 14px; text-align: left; text-decoration: none;
  color: var(--ds-ink2); cursor: pointer;
  transition: background 100ms, color 100ms;
}
.ds-acct-item:hover { background: var(--ds-line2); color: var(--ds-ink); }
.ds-acct-item svg, .ds-acct-item img { flex-shrink: 0; }
.ds-acct-item img { border-radius: 2px; display: block; }
.ds-acct-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Wybrana wartość na zwiniętym wierszu — po odpowiedź „w jakim języku jestem” nie trzeba nic otwierać. */
.ds-acct-item-value { flex-shrink: 0; font-size: 12.5px; color: var(--ds-muted); }
.ds-acct-item-chevron { color: var(--ds-muted2); transition: transform 150ms ease; }
.ds-acct-item-chevron.is-open { transform: rotate(90deg); }

/* Podmenu wysuwane w bok (dziś: język) — jak w menu aplikacji desktopowej. Panel jest w DOM-ie
   zawsze; pokazuje go najechanie ALBO klasa .is-open od kliknięcia, więc działa i myszą, i dotykiem,
   bez dwóch źródeł prawdy o tym, czy jest otwarty. */
.ds-acct-flyout { position: relative; }
/* 11 px = 6 px dopełnienia .ds-acct-menu (zaczep stoi w środku, nie przy krawędzi) + 5 px światła
   między dwoma cieniami. Bez składnika dopełnienia panel wchodzi 1 px POD menu i cień się zlewa. */
.ds-acct-submenu {
  position: absolute; left: calc(100% + 11px); top: -7px; z-index: 1;
  display: none; flex-direction: column; gap: 1px; padding: 6px;
  min-width: 176px;
  background: var(--ds-surface); border: 1px solid var(--ds-line);
  border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,.20);
}
/* Mostek dla kursora przez 5-pikselową szczelinę między menu a podmenu. Bez niego droga myszą do
   wybranej pozycji prowadzi przez obszar, w którym podmenu się zamyka — i pozycja ucieka spod
   kursora w połowie ruchu. Pseudoelement, a nie sklejenie paneli: szczelina jest tu po to, żeby
   dwa cienie nie zlewały się w jeden prostokąt. */
.ds-acct-submenu::before { content: ""; position: absolute; top: 0; bottom: 0; left: -13px; width: 13px; }
.ds-acct-flyout:hover > .ds-acct-submenu,
.ds-acct-flyout.is-open > .ds-acct-submenu { display: flex; }
.ds-acct-flyout:hover > .ds-acct-item,
.ds-acct-flyout.is-open > .ds-acct-item { background: var(--ds-line2); color: var(--ds-ink); }
.ds-acct-item.is-on { color: var(--ds-ink); font-weight: 500; }

.ds-acct-sep { height: 1px; background: var(--ds-line); margin: 5px 0; }

/* Wylogowanie jest jedyną pozycją, która coś kończy — i jedyną klikaną przez pomyłkę. Czerwień
   dopiero pod kursorem: stale czerwony wiersz w menu czyta się jak ostrzeżenie o awarii. */
.ds-acct-item--danger:hover { background: var(--ds-bad-bg); color: var(--ds-bad); }

/* Wersja buildu w stopce menu. Nie jest pozycją — nic nie robi po kliknięciu — więc nie dostaje
   ani tła pod kursorem, ani wskaźnika myszy. */
.ds-acct-version {
  margin: 5px -6px -6px; padding: 7px 14px 5px;
  border-top: 1px solid var(--ds-line);
  font-family: 'Geist Mono', monospace; font-size: 11px; color: var(--ds-muted2);
  user-select: none; cursor: default;
}

/* ── Awatar konta ───────────────────────────────────────────
   Jeden okrąg z inicjałem dla wszystkich miejsc, w których pojawia się człowiek: konto w topbarze,
   opiekun rekordu (Trello #106) w kolumnie tabeli, w panelu rekordu i w menu wyboru. Kolor tła bywa
   nadpisywany inline (liczony z id konta — to on, a nie litera, pozwala rozpoznać opiekuna rzutem
   oka na liście), rozmiar modyfikatorami -sm/-md; domyślne wartości poniżej obsługują topbar. */
.ds-avatar {
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--ds-ink2); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; line-height: 1;
  flex-shrink: 0; user-select: none;
}
.ds-avatar-sm { width: 24px; height: 24px; font-size: 11px; }
.ds-avatar-md { width: 32px; height: 32px; font-size: 13px; }

/* Pusty slot „przypisz opiekuna” — przerywany okrąg z plusem. */
.ds-avatar-add {
  width: 32px; height: 32px; border-radius: 50%;
  border: 1.5px dashed var(--ds-line);
  background: none; cursor: pointer; color: var(--ds-muted);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; padding: 0;
  transition: border-color 120ms, color 120ms, background 120ms;
}
.ds-avatar-add:hover:not(:disabled) {
  border-color: var(--ds-ink2); color: var(--ds-ink2); background: var(--ds-line2);
}
.ds-avatar-btn { background: none; border: none; padding: 0; cursor: pointer; line-height: 0; }
.ds-avatar-btn:disabled, .ds-avatar-add:disabled { cursor: default; opacity: .6; }

/* ── Popover wyboru ─────────────────────────────────────────
   Układ jak w oknie „Członkowie” w Trello: tytuł, ✕, wyszukiwarka, lista pozycji. Nosi go menu
   opiekuna w panelu rekordu i każdy filtr paska nad listą (DsFilterPicker) — te dwa miejsca mają
   wyglądać i zachowywać się identycznie, więc reguły są wspólne, a nie kopiowane. */
.ds-owner-pop {
  position: absolute; z-index: 30; width: 304px; max-width: calc(90vw / var(--ds-zoom));
  background: var(--ds-surface); border: 1px solid var(--ds-line);
  border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.18);
  overflow: hidden;
}
.ds-owner-pop-head {
  display: flex; align-items: center; padding: 8px 8px 8px 36px;
  border-bottom: 1px solid var(--ds-line);
}
.ds-owner-pop-title {
  flex: 1; text-align: center; font-size: 13px; font-weight: 600; color: var(--ds-ink2);
}
.ds-owner-pop-close {
  width: 28px; height: 28px; border: none; background: none; border-radius: 6px;
  cursor: pointer; color: var(--ds-muted); display: inline-flex;
  align-items: center; justify-content: center; flex-shrink: 0;
}
.ds-owner-pop-close:hover { background: var(--ds-line2); color: var(--ds-ink2); }
.ds-owner-pop-body { padding: 10px 12px 12px; }
.ds-owner-pop-label {
  font-size: 11.5px; font-weight: 600; color: var(--ds-muted);
  text-transform: uppercase; letter-spacing: .6px; margin: 10px 0 6px;
}
.ds-owner-option {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: none; border: none; border-radius: 6px; padding: 6px 8px;
  cursor: pointer; font-size: 14px; color: var(--ds-ink);
}
.ds-owner-option:hover:not(:disabled) { background: var(--ds-line2); }
.ds-owner-option:disabled { cursor: default; opacity: .6; }

/* ── Filtr paska nad listą (DsFilterPicker) ─────────────────
   Przycisk rozwijający powyższy popover. Wygląd niesie jedną informację: czy ten filtr coś teraz
   robi. Nieaktywny jest szary i cichy, aktywny dostaje ramkę akcentu, nazwę wybranej pozycji i
   licznik — bo „nic nie wybrane” zastąpiło pozycję „Wszystkie” i musi być widoczne bez otwierania. */
/* z-index nad tłem (25), żeby przejście z jednego otwartego filtra na drugi kosztowało jedno
   kliknięcie. Wartość działa WEWNĄTRZ kontekstu układania paska (.ds-subheader ma z-index: 10),
   więc tło też musi być rysowane w pasku — inaczej przykryłoby wysunięty panel. */
.ds-filter { position: relative; z-index: 26; }
.ds-filter-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 9px; max-width: 260px;
  background: var(--ds-bg); border: 1px solid var(--ds-line); border-radius: 6px;
  font-family: inherit; font-size: 13px; color: var(--ds-ink); cursor: pointer;
  transition: border-color 120ms, background 120ms;
}
.ds-filter-btn:hover { border-color: var(--ds-ink2); }
.ds-filter-btn-label { color: var(--ds-muted); white-space: nowrap; }
.ds-filter-btn-value {
  font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Licznik dostaje się na przycisk dopiero od dwóch wybranych pozycji (patrz DsFilterPicker): przy
   jednej przycisk pisze jej nazwę, więc „1” obok byłoby powtórzeniem tego samego cyfrą. */
.ds-filter-btn-count {
  min-width: 16px; padding: 0 4px; border-radius: 8px;
  background: var(--ds-accent); color: #fff; flex-shrink: 0;
  font-family: 'Geist Mono', monospace; font-size: 11px; line-height: 16px; text-align: center;
}
.ds-filter-btn-chevron { color: var(--ds-muted); flex-shrink: 0; margin-left: auto; }
/* Pusty slot filtra — ten sam przerywany okrąg z plusem, co przy rekordzie bez opiekuna, tylko
   zmniejszony do wysokości przycisku. Kółko jest tu <span>-em wewnątrz przycisku, więc kursor i
   stan hover bierze z niego; własnego :hover nie potrzebuje. */
.ds-filter-add { width: 18px; height: 18px; margin-left: auto; border-width: 1.2px; }
.ds-filter-btn:hover .ds-filter-add { border-color: var(--ds-ink2); color: var(--ds-ink2); }
.ds-filter-btn.is-active {
  border-color: var(--ds-accent); background: var(--ds-surface);
}
.ds-filter-btn.is-active .ds-filter-btn-label::after { content: ":"; }
.ds-filter-pop { left: 0; top: calc(100% + 6px); }
/* Wysokość pod NAJDŁUŻSZĄ listę o stałej długości — osiem statusów — żeby ta, której nie da się
   skrócić wyszukiwarką, mieściła się bez przewijania pionowego. Ograniczenie zostaje, bo listy
   opiekunów i importów rosną w nieskończoność; tam pasek przewijania jest właściwą odpowiedzią. */
.ds-filter-options { max-height: 340px; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.ds-filter-option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Ptaszek wyboru zajmuje miejsce zawsze, widoczny jest tylko przy wybranej pozycji — inaczej
   kliknięcie przesuwałoby końcówkę wiersza w lewo o jego szerokość (patrz DsFilterPicker.razor). */
.ds-filter-option-check {
  margin-left: auto; flex-shrink: 0; color: var(--ds-ink2); visibility: hidden;
}
.ds-filter-option-check.is-on { visibility: visible; }
/* Liczba przy pozycji filtra — ile wierszy pokaże. */
.ds-filter-option-count { flex-shrink: 0; font-size: 12px; color: var(--ds-muted); font-family: 'Geist Mono', monospace; }

.ds-filter-clear {
  width: 100%; margin-top: 8px; padding: 6px 8px;
  background: none; border: 1px solid var(--ds-line); border-radius: 6px;
  font-family: inherit; font-size: 13px; color: var(--ds-muted); cursor: pointer;
}
.ds-filter-clear:hover { border-color: var(--ds-ink2); color: var(--ds-ink); }
/* Tło zamykające otwarty filtr kliknięciem obok. Leży pod popoverem (z-index 30) i pod samymi
   przyciskami filtrów, żeby przejście z jednego filtra na drugi kosztowało jedno kliknięcie,
   a nie dwa (zamknij, otwórz). */
.ds-filter-backdrop { position: fixed; inset: 0; z-index: 25; background: none; }

.ds-th-sort { cursor: pointer; }
.ds-th-sort:hover { color: var(--ds-ink2); }

/* ── Page layout ───────────────────────────────────────────── */
/* main wypełnia całą wysokość powłoki; szerokość bierze to, co zostaje po menu bocznym.
   Było: height: calc(100% - 53px) — 53 to była wysokość paska górnego wpisana z ręki w dwóch
   miejscach naraz (tu i w .ds-subheader). Powłoka jest teraz flexem w poziomie, więc wysokość
   bierze się ze stretcha, a szerokość z flex:1 — żadna z tych liczb nie musi już nigdzie stać.
   min-width:0 jest obowiązkowe: bez niego tabela produktów rozepchnęłaby main poza ekran zamiast
   dostać własny suwak poziomy. */
main {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* ds-page wypełnia main; bez overflow:hidden — przewijaniem zajmuje się .ds-content */
.ds-page { flex: 1; min-height: 0; display: flex; flex-direction: column; }

.ds-subheader {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  min-height: 52px; padding: 6px 20px;
  border-bottom: 1px solid var(--ds-line);
  background: var(--ds-surface);
  /* position:relative, nie sticky. Sticky było tu martwe od początku: pasek jest RODZEŃSTWEM
     kontenera przewijania (.ds-content { overflow:auto }), a nie jego zawartością, więc nie miał
     od czego się odkleić — top:53px nie robił nic poza wiązaniem układu z wysokością nieistniejącego
     już paska górnego. Zostaje samo position, bo z-index:10 jest nośny: buduje kontekst układania,
     w którym mieszczą się tło filtrów (25) i wysunięte panele (30). Bez niego tło filtra uciekłoby
     na poziom dokumentu i przykryło menu boczne. */
  position: relative; z-index: 10;
  flex-shrink: 0;
}
/* Tytuł ekranu 21 px, jak nagłówek w Prototypie A — pasek nad listą jest jedynym miejscem, które mówi,
   gdzie się jest (menu boczne po zwinięciu pokazuje same ikony). */
.ds-subheader-title { font-size: 21px; font-weight: 600; letter-spacing: -.015em; line-height: 1.2; }
.ds-subheader-meta  { font-size: 12.5px; color: var(--ds-muted); font-family: 'Geist Mono', monospace; margin-top: 2px; }
.ds-spacer { flex: 1; }

/* ── Liczba w pasku nad tabelą ──────────────────────────────────────────────
   Liczba stoi tuż przy swoim podpisie i nic nie rezerwuje z góry.

   Było inaczej: każda liczba miała `min-width: 4ch` z wyrównaniem do prawej, czyli miejsce na
   katalog do 9999 pozycji. Powód był układowy — pasek jest jednym rzędem flexa, więc szerokość
   liczby przesuwa wszystko, co za nią stoi, i przy wpisywaniu w wyszukiwarkę pasek drgał. Kosztem
   była pustka między krawędzią plakietki a krótką liczbą, przez którą „3 Niepełne” wyglądało na
   dosunięte do prawej, jakby plakietka miała dwa wyrównania naraz. Ten koszt został zgłoszony
   dwukrotnie jako usterka wyglądu i to rozstrzyga: rezerwacja znika, drganie przy filtrowaniu
   wraca jako świadoma cena.

   `tabular-nums` zostaje, bo jest za darmo — wyrównuje cyfry między sobą także w foncie
   proporcjonalnym (pasek nie jest monospace), więc liczby o tylu samo cyfrach nie ruszają niczym. */
.ds-count {
  font-variant-numeric: tabular-nums;
}

.ds-search {
  display: flex; align-items: center; gap: 8px;
  flex: 1; max-width: 420px; margin: 0 16px; min-height: 36px;
  border: 1px solid var(--ds-control-line); border-radius: 6px;
  padding: 0 10px; background: var(--ds-surface);
}
.ds-search:focus-within { border-color: var(--ds-todo); box-shadow: 0 0 0 3px #dbeafe; }
.ds-search input {
  flex: 1; border: none; outline: none; background: transparent;
  font-size: 14px; color: var(--ds-ink); font-family: inherit;
}
/* Krzyżyk czyszczący pole. Chowa się i pokazuje SAM CSS-em, bez udziału serwera: `:placeholder-shown`
   trafia w pole puste, a `~` sięga po rodzeństwo stojące za inputem. To nie jest sprytniejszy zapis
   tego, co mógłby zrobić Blazor — to warunek, żeby DsSearchBox dalej nie renderował się na literę
   (Trello #96). Selektor wymaga niepustego placeholdera, o który dba EffectivePlaceholder. */
.ds-search-clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; flex-shrink: 0; padding: 0;
  border: none; border-radius: 4px; background: transparent;
  color: var(--ds-muted); cursor: pointer;
  transition: background 100ms, color 100ms;
}
.ds-search-clear:hover { background: var(--ds-line2); color: var(--ds-ink); }
.ds-search input:placeholder-shown ~ .ds-search-clear { display: none; }

.ds-search kbd {
  font-size: 11px; color: var(--ds-muted); font-family: 'Geist Mono', monospace;
  background: var(--ds-surface); padding: 1px 5px;
  border: 1px solid var(--ds-line); border-radius: 3px;
}

/* 12/20 px, jak .pad w Prototypie A. Było 14/16 (Trello #53: przy oknie 1280×600 szerszy padding
   zabierał zapas potrzebny tabeli) — na dole 24 px, żeby ostatni wiersz nie przyklejał się do krawędzi. */
.ds-content { padding: 12px 20px 24px; display: flex; flex-direction: column; gap: 14px; min-width: 0; overflow: auto; min-height: 0; }

/* ── Loading ───────────────────────────────────────────────── */
.ds-progress-card {
  padding: 18px 20px; background: var(--ds-surface);
  border: 1px solid var(--ds-line); border-radius: 10px;
  box-shadow: 0 2px 8px rgba(0,0,0,.04);
}
.ds-spinner {
  width: 16px; height: 16px; border: 2px solid var(--ds-line2);
  border-top-color: var(--ds-ink); border-radius: 50%;
  animation: spin 0.8s linear infinite; flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Input panel ───────────────────────────────────────────── */
.ds-input-bar {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 12px 8px 16px;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px;
  font-size: 14px;
}
.ds-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.ds-dot-ok { background: var(--ds-ok); }
.ds-mono  { font-family: 'Geist Mono', monospace; }

.ds-input-panel {
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px; padding: 18px;
}
.ds-input-header {
  display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;
}
.ds-input-label { font-size: 14.5px; font-weight: 600; }
.ds-input-hint  { font-size: 13px; color: var(--ds-muted); margin-top: 2px; }
.ds-textarea {
  width: 100%; resize: vertical; padding: 10px 12px;
  font-family: 'Geist Mono', monospace; font-size: 14px; line-height: 1.6;
  color: var(--ds-ink); background: var(--ds-surface);
  border: 1px solid var(--ds-control-line); border-radius: 6px; outline: none;
}
.ds-textarea:focus { border-color: var(--ds-todo); box-shadow: 0 0 0 3px #dbeafe; }

/* ── Tabs ──────────────────────────────────────────────────── */
/* Zakładki (DsTabs) — JEDEN pasek w całej aplikacji (decyzja właściciela 2026-10-02, Prototyp A .tabs/.tab/.cnt):
   zakładki strony pod tytułem i zakładki panelu rekordu. Linia pod zakładkami to cień wewnętrzny, nie border-bottom:
   przy przewijaniu w bok border z ujemnym marginesem zakładki wychodził poza obszar przewijania i robił pionowy suwak
   o 1 px. Panel różni się tylko tłem (.dhead+.tabs w prototypie). */
.ds-tabs {
  display: flex; gap: 2px; flex-shrink: 0; padding: 0 20px; background: var(--ds-surface);
  box-shadow: inset 0 -1px 0 var(--ds-line);
  overflow-x: auto; overflow-y: hidden;
}
.ds-tabs--panel { background: #fafafa; margin-bottom: 14px; }
.ds-tab {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: flex-start; gap: 8px;
  height: 40px; padding: 0 12px; font: inherit; font-size: 14px; font-weight: 400;
  color: #404040; background: transparent; border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer; transition: color 120ms, border-color 120ms;
  white-space: nowrap;
}
.ds-tab:hover { color: var(--ds-ink); }
.ds-tab.is-active { color: var(--ds-ink); font-weight: 600; border-bottom-color: var(--ds-ink); }
/* „!” — na zakładce czeka czynność (OrderFlow.SectionsNeedingAction). */
.ds-tab-flag {
  display: inline-grid; place-items: center; width: 15px; height: 15px; border-radius: 50%;
  background: var(--ds-bad); color: #fff; font-size: 11px; font-weight: 800; line-height: 1;
}

/* Licznik przy zakładce, pozycji menu i w nagłówku sekcji. Kolor przez modyfikator stanu. */
.ds-cnt {
  display: inline-grid; place-items: center;
  height: 22px; min-width: 22px; padding: 0 7px; border-radius: 11px;
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: var(--ds-idle-bg); color: var(--ds-idle);
}
.ds-cnt--todo { background: var(--ds-todo-bg); color: var(--ds-todo); }
.ds-cnt--late { background: var(--ds-late-bg); color: var(--ds-late); }
.ds-cnt--wait { background: var(--ds-wait-bg); color: var(--ds-wait); }
.ds-cnt--done { background: var(--ds-done-bg); color: var(--ds-done); }
.ds-tab-body { padding: 2px 0 4px; }
/* Zakładka z siatką (Diagnostyka): linia filtrów nad ramką, ramka rośnie do dołu okna. */
.ds-tab-body--grid { flex: 1 0 auto; min-height: 0; display: flex; flex-direction: column; gap: 8px; }

/* ── Buttons ───────────────────────────────────────────────── */
/* Wysokość jest stała (34 px), a nie wynikiem paddingu: przyciski stoją w rzędach obok pól (36 px)
   i list, a przycisk z samą ikoną ma mieć tę samą wysokość co przycisk z napisem. min-height, nie
   height — przycisk z dwoma wierszami tekstu (rzadko, ale bywa w oknach) ma urosnąć, nie uciąć się. */
.ds-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 34px; padding: 0 10px; font-size: 13px; font-weight: 500; line-height: 1.2;
  color: var(--ds-ink); background: var(--ds-surface);
  border: 1px solid var(--ds-control-line); border-radius: 6px;
  cursor: pointer; font-family: inherit; text-decoration: none;
  transition: background 100ms, border-color 100ms;
  white-space: nowrap;
}
.ds-btn:hover { background: var(--ds-hover); }
.ds-btn:disabled { opacity: 0.4; cursor: default; }
.ds-btn-primary {
  color: #fff; background: var(--ds-ink); border-color: var(--ds-ink);
}
.ds-btn-primary:hover { background: #262626; border-color: #262626; }
.ds-btn-sm { min-height: 28px; padding: 0 9px; font-size: 12.5px; }
.ds-btn-lg { min-height: 40px; padding: 0 14px; font-size: 14px; }
/* Czerwony obrys — akcja, która coś kończy (anuluj zamówienie, usuń). Wypełniony — tylko przycisk
   „Tak” w potwierdzeniu takiej akcji, żeby decyzja i jej zapowiedź nie wyglądały tak samo. */
.ds-btn-red { color: var(--ds-late); border-color: #fca5a5; }
.ds-btn-red:hover { background: var(--ds-late-bg); }
.ds-btn-redfill { color: #fff; background: var(--ds-late); border-color: var(--ds-late); }
.ds-btn-redfill:hover { background: #991b1b; border-color: #991b1b; }
.ds-btn-link {
  border-color: transparent; background: transparent; color: #404040;
  text-decoration: underline; text-underline-offset: 3px; padding: 0 6px;
}
.ds-btn-link:hover { background: transparent; color: var(--ds-ink); }
.ds-btn-ghost { border-color: transparent; background: transparent; }
.ds-btn-ghost:hover { background: var(--ds-hover); }

/* ── Category chips ────────────────────────────────────────── */
.ds-chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ds-chips-label {
  font-size: 12px; color: var(--ds-muted);
  text-transform: uppercase; letter-spacing: 0.6px; margin-right: 4px;
}
.ds-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; font-size: 13px; font-weight: 500;
  color: var(--ds-muted); background: transparent;
  border: 1px solid var(--ds-line); border-radius: 999px;
  cursor: pointer; font-family: inherit;
  transition: all 100ms;
}
.ds-chip--on { color: var(--ds-ink); background: var(--ds-surface); border-color: var(--ds-ink); }
.ds-chip-count { font-family: 'Geist Mono', monospace; font-size: 11.5px; color: var(--ds-muted); }

/* ── Table ─────────────────────────────────────────────────── */
.ds-table-info {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 16px; border-bottom: 1px solid var(--ds-line);
  font-size: 14px; color: var(--ds-muted);
}
.ds-table-info strong { color: var(--ds-ink); }

/* Suma po prawej stronie paska nad listą (obrót klientów, netto/VAT/brutto faktur) — informacja, nie filtr,
   więc bez ramki plakietki. */
.ds-table-info-sums { margin-left: auto; color: var(--ds-ink2); font-size: 13px; white-space: nowrap; }
.ds-table-info-sums .ds-num { font-weight: 600; }

/* ── Doładowywanie listy partiami ───────────────────────────────
   Wybór rozmiaru partii: dwa przyciski jako jeden przełącznik, nie <select>. Wartości są dwie
   i obie mają być widoczne bez rozwijania — rozwijana lista chowałaby połowę stanu za kliknięciem
   i zajmowała tyle samo miejsca. */
.ds-pagesize { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; }
.ds-pagesize-label { font-size: 12px; color: var(--ds-muted); white-space: nowrap; }
.ds-pagesize-opt {
  font-family: 'Geist Mono', monospace; font-size: 12px; line-height: 1;
  padding: 4px 8px; cursor: pointer;
  color: var(--ds-muted); background: var(--ds-surface);
  border: 1px solid var(--ds-line); border-radius: 4px;
}
.ds-pagesize-opt:hover:not(:disabled) { color: var(--ds-ink); border-color: var(--ds-ink2); }
.ds-pagesize-opt:disabled { cursor: wait; opacity: .6; }
.ds-pagesize-opt--on { color: var(--ds-ink); border-color: var(--ds-ink); font-weight: 600; }

/* Stopka pod ostatnim wierszem. Przewija się razem z listą (jest w tym samym pojemniku), więc
   pojawia się dokładnie tam, gdzie użytkownik dojechał — a nie przykleja się do dołu ekranu,
   gdzie obiecywałaby więcej pozycji także wtedy, gdy nikt nie doszedł do końca. */
.ds-loadmore {
  display: flex; flex-direction: column; align-items: center;
  padding: 14px 16px 18px; gap: 2px;
}
.ds-loadmore-btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; padding: 7px 16px; cursor: pointer;
  color: var(--ds-ink2); background: var(--ds-surface);
  border: 1px solid var(--ds-line); border-radius: 6px;
}
.ds-loadmore-btn:hover:not(:disabled) { color: var(--ds-ink); border-color: var(--ds-ink2); }
.ds-loadmore-btn:disabled { cursor: wait; color: var(--ds-muted); }
/* Wartownik: bez wysokości własnej treści, ale nie zerowej — element o zerowej wysokości bywa
   pomijany przez IntersectionObserver, więc automatyczne doładowanie po cichu przestawałoby działać
   i zostawałby sam przycisk. */
.ds-loadmore-sentinel { height: 1px; width: 100%; }
/* Nagłówek kolumny zwykłą pisownią, 12 px / 600, na bieli (Prototyp A): wersaliki z rozstrzeleniem
   zabierały szerokość, której wąskie kolumny nie mają, a tło odcinało nagłówek od tabeli, która i tak
   stoi w ramce. */
/* Szerokość 100% − 1 px: tabela z border-collapse wystaje w Chromium o 1 px poza pojemnik przy KAŻDEJ szerokości
   (zmierzone 960–1250 px: scrollWidth = clientWidth + 1), więc poziomy suwak Magazynu stał zawsze, także przy
   presecie Standard, który mieści się z zapasem. Suwak ma się pojawiać tylko wtedy, gdy min-width tabeli
   (StockTableMinWidth) naprawdę przekracza okno (decyzja właściciela 2026-09-29). */
.ds-table {
  width: calc(100% - 1px); border-collapse: collapse; font-size: 14px;
  table-layout: fixed;
}
.ds-table th {
  text-align: left; padding: 7px 9px;
  font-size: 12px; font-weight: 600; color: var(--ds-muted);
  background: var(--ds-surface); border-bottom: 1px solid var(--ds-line);
  white-space: nowrap;
}
.ds-table td { padding: 7px 9px; border-bottom: 1px solid var(--ds-line2); vertical-align: middle; }
.ds-table tbody tr:last-child td { border-bottom: none; }

/* Wykrzyknik przy nieudanym wywołaniu. Zastąpił napis („error”, „timeout”), który powtarzał się w co
   drugim wierszu i rozpychał kolumnę, niosąc tyle, co sam kolor. Kółko, a nie goły znak: pojedynczy
   wykrzyknik przy nazwie operacji czyta się jak literówka w tekście, a nie jak plakietka stanu.
   Powód stoi w title — kto pyta „co konkretnie”, i tak zatrzymuje się na tym wierszu. */
.ds-fail-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 5px; vertical-align: -2px;
  border-radius: 50%; background: var(--ds-bad-bg); border: 1px solid var(--ds-bad);
  color: var(--ds-bad); font-size: 11px; font-weight: 700; line-height: 1;
  cursor: help; flex-shrink: 0;
}

/* Wykres słupkowy na osi czasu (widok „Wykres” w Kosztach AI). Sam rysunek jest w SVG i skaluje się
   viewBoxem, więc tutaj zostaje tylko obudowa i stan pusty. */
.ds-chart { width: 100%; }
.ds-chart svg { overflow: visible; }              /* podpisy osi wychodzą poza pole rysunku */
.ds-chart-empty {
  padding: 48px 12px; text-align: center;
  font-size: 14px; color: var(--ds-muted);
}

/* ── Tabela produktów: szerokość i próg responsywny (Trello #53) ──────────────
   Padding 14 → 10 px w poziomie: przy 13 kolumnach te 4 px z każdej strony to
   ~100 px na wiersz oddane na marginesy zamiast na treść — dokładnie ten rząd
   wielkości, którego brakowało, żeby tabela zmieściła się bez suwaka. W pionie
   8 → 6 px, bo wysokość wiersza przekłada się 1:1 na to, ile produktów widać.

   min-width = suma stałych szerokości <th> (1028 px) + 200 px minimum na kolumnę
   „Nazwa”, która jako jedyna nie ma wymiaru i zbiera wolne miejsce. Zmiana
   szerokości którejkolwiek kolumny w Produkty.razor wymaga poprawienia tych
   dwóch liczb — inaczej albo wraca suwak, albo zostaje niewykorzystany pas.

   Poniżej progu znika „Data importu” (-106 px). Punkt wyjścia to 1300 px: moment,
   w którym pełny układ przestaje się mieścić w oknie, bo 1228 + 32 px paddingu
   .ds-content + ramka + pionowy suwak ≈ 1280 px, a szerokość suwaka zależy od
   systemu, więc próg stoi wyżej niż wyliczenie. Kolumna jest schowana, a nie
   usunięta — na szerokim ekranie wraca sama.

   Te 1300 liczyło się z założenia, że cała szerokość okna należy do treści. Odkąd po lewej stoi
   menu, ta sama tabela przestaje się mieścić o jego szerokość wcześniej — próg przesuwa się więc
   dokładnie o tyle, ile menu zabiera: 1300 + 232 dla rozwiniętego, 1300 + 64 dla zwiniętego.
   Media query nie czyta zmiennych CSS, więc stanem menu jest klasa na <body>; specyficzność
   „body.x .ds-table--products” (0,2,1) bije bazę (0,1,0) bez !important.

   Skutek uboczny jest zamierzony: między 1365 a 1532 px zwinięcie menu ODDAJE „Datę importu”,
   czyli przełącznik ma widoczny sens także wtedy, gdy nikt nie szuka miejsca na nazwę.

   Kolumny Status i Allegro (172 + 138 px) scaliły się w jedną Status (236 px), więc obie szerokości
   minimalne zeszły o 74 px. Progi zostają: tabela mieści się teraz z zapasem, a nie na styk. */
/* 2026-09-25: −72 px („Pewność” zdjęta), +96 px („Cena zakupu”), +12 px (lejek przy Cenniku), +16 px
   (lejek przy Opiekunie). */
/* 2026-09-28 (Prototyp A): kolumna „Produkt” wchłonęła EAN i miniaturę, doszły przycisk następnego kroku
   (176 px) i menu kolumn; kolumny poza Produktem, Dostępnymi i Statusem wybiera menu, więc minimum liczy
   się dla presetu Kierownik. */
/* Minimum szerokości liczy się z widocznych kolumn (Produkty.Columns.cs, StockTableMinWidth) — inline. */
/* Nazwa z WMS pod nazwą produktu (decyzja właściciela 2026-09-25) — mniejsza i cichsza od nazwy,
   ucinana wielokropkiem jak cała komórka; pełna treść w tooltipie. */
.ds-wms-name {
  font-size: 11.5px; color: var(--ds-muted); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── Wiersz Magazynu (Prototyp A, p40_stock.js) ─────────────────────────────────────────────────
   Produkt: miniatura 40 px, nazwa, nazwa z WMS i kod · producent monospace’em. */
.ds-prod { display: flex; align-items: center; gap: 12px; min-width: 0; }
.ds-prod-thumb { position: relative; flex-shrink: 0; width: 40px; height: 40px; }
.ds-prod-thumb-add {
  width: 40px; height: 40px; padding: 0; border-radius: 6px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ds-line2); border: 1px dashed var(--ds-muted2); color: var(--ds-muted);
}
.ds-prod-thumb-add:hover:not(:disabled) { background: var(--ds-line); border-color: var(--ds-ink2); color: var(--ds-ink2); }
.ds-prod-thumb-add:disabled { cursor: not-allowed; opacity: .55; }
.ds-prod-thumb img {
  position: absolute; inset: 0; width: 40px; height: 40px; object-fit: contain; cursor: pointer;
  border-radius: 6px; border: 1px solid var(--ds-line); background: #fff;
}
.ds-prod-main { min-width: 0; flex: 1; }
.ds-prod-name { font-weight: 500; color: var(--ds-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-prod-code {
  font-size: 12.5px; color: var(--ds-muted); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ds-prod-code .ds-btn-link { height: auto; padding: 0; font-size: 12.5px; }
.ds-ean-preview { color: var(--ds-muted2); border-bottom: 1px dotted var(--ds-muted2); }
/* Nazwa z WMS / z importu pod nazwą produktu. Plakietka „WMS” z prototypu (.wmsn) usunięta jako zbędna
   (decyzja właściciela 2026-10-01). */
.ds-wmsn {
  font-size: 12px; color: var(--ds-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ds-table--products td.ds-num,
.ds-table--products th.ds-num { text-align: right; }
.ds-stock-cell-n { font-size: 17px; font-weight: 600; font-family: 'Geist', sans-serif; line-height: 1.2; }
.ds-cell-sub { font-size: 12px; font-weight: 400; }
/* Linia podpisu w wąskiej komórce: jedna linia z wielokropkiem (pełna treść w title), nigdy łamanie słów
   na kolejne linie — to one robiły z wiersza listy wiersz trzy razy wyższy od sąsiadów. */
/* Długi komunikat w komórce listy (Diagnostyka): najwyżej dwie linie, reszta w title. Pełny tekst bez limitu
   robił z jednego wiersza 200+ px i lista przestawała być listą. */
.ds-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; overflow-wrap: anywhere; }
.ds-cell-line { display: block; max-width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-cell-input { height: 34px; width: 100%; max-width: 110px; text-align: right; }
select.ds-cell-input { max-width: none; text-align: left; }
/* Komórka do edycji w miejscu (cena, cennik) czyta się jak tekst; ramka dopiero pod kursorem. */
.ds-cellbtn {
  display: inline-flex; flex-direction: column; align-items: flex-start; gap: 0; max-width: 100%; min-width: 0;
  padding: 3px 6px; margin: -3px -6px; border: 1px dashed transparent; border-radius: 6px;
  background: none; font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.ds-cellbtn--num { align-items: flex-end; text-align: right; margin-left: auto; }
.ds-cellbtn:hover:not(:disabled) { border-color: var(--ds-control-line); background: var(--ds-hover); }
.ds-cellbtn:disabled { cursor: not-allowed; opacity: .55; }
/* Status: pigułka etapu, tag kanału, flaga — a pod spodem jedno zdanie „dlaczego”. */
.ds-pills { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ds-why {
  display: flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 12.5px; color: var(--ds-ink2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ds-why.is-late { color: var(--ds-late); }
.ds-why.is-wait { color: var(--ds-wait); }
.ds-tag--wait { background: var(--ds-wait-bg); color: var(--ds-wait); }
.ds-tag-btn { border: 0; cursor: pointer; font: inherit; font-size: 12px; }
.ds-tag-btn:hover { filter: brightness(.95); }

/* ── Ikona kanału w wierszu Magazynu: gdzie towar jest wystawiony (status w dymku, klik = klik w wiersz).
   Kształt w bloku `.ds-chan` niżej (przy pasku statusu), barwy kanału — `.ds-chan--allegro` zaraz po nim. ── */
.ds-row--sel td { background: var(--ds-sel); }
/* Menu „⋯” wiersza — pod przyciskiem, do lewej (prawy brzeg tabeli). Tło zamykające leży pod nim. */
.ds-rmenu-cell { position: relative; text-align: center; }
.ds-rmenu {
  position: absolute; right: 8px; top: calc(100% - 6px); z-index: 30; min-width: 230px;
  display: flex; flex-direction: column; padding: 6px; text-align: left;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0,0,0,.14);
}
.ds-rmenu > button, .ds-rmenu > a {
  display: block; width: 100%; padding: 8px 10px; border: 0; border-radius: 6px; background: none;
  font: inherit; font-size: 14px; color: var(--ds-ink); text-align: left; text-decoration: none; cursor: pointer;
}
.ds-rmenu > button:hover:not(:disabled), .ds-rmenu > a:hover { background: var(--ds-hover); }
.ds-rmenu > button:disabled { color: var(--ds-muted2); cursor: not-allowed; }
.ds-rmenu > .is-bad { color: var(--ds-late); }
/* Linia nad tabelą Magazynu niesie też rozmiar partii — na prawym brzegu. */
.ds-fline .ds-pagesize { margin-left: auto; }

/* Publish-in-flight badge in the Allegro column: a short "Wysyłanie" label plus the
   properties button. The step text and N/M counter used to sit in a CSS hover popup
   here; they moved into the properties modal, because hovering the row mid-send made
   a progress panel flash unbidden. Only the button's hover affordance remains. */
.ds-publish-info-btn    { transition: background 120ms, color 120ms; }
.ds-publish-info-btn:hover { background: var(--ds-line2); color: var(--ds-ink2); }
.ds-row { cursor: pointer; transition: background 80ms; }
.ds-row:hover td { background: var(--ds-sel); }
/* Wiersz, do którego wrócił fokus po zamknięciu panelu rekordu (dsFocusRow). Znak jest ten sam,
   który nosi wiersz z otwartym panelem: belka na krawędzi, nie tło — tło mówi już "zaznaczony
   checkboxem", a dwa znaczenia na jednym nośniku czyta się jak jedno. Outline zdjęty, bo na <tr>
   przeglądarki rysują go rozbieżnie; belka na pierwszej komórce jest tu idiomem od początku. */
.ds-row--open td,
.ds-row:focus td { background: var(--ds-bg); }
.ds-row--open td:first-child,
.ds-row:focus td:first-child { box-shadow: inset 3px 0 0 var(--ds-accent); }
.ds-row:focus { outline: none; }
.ds-cell-img { width: 72px; padding: 4px 8px !important; }
.ds-product-img {
  width: 60px; height: 40px; object-fit: contain; border-radius: 4px;
  border: 1px solid var(--ds-line); background: #fff; display: block;
}
.ds-product-img-ph {
  width: 60px; height: 40px; border-radius: 4px;
  border: 1px solid var(--ds-line); background: var(--ds-line2);
  display: flex; align-items: center; justify-content: center;
  color: var(--ds-muted2);
}
.ds-cell-erp { width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ds-muted); font-size: 13px; }
.ds-cell-ean { width: 135px; white-space: nowrap; }
.ds-cell-name { min-width: 200px; overflow: hidden; }
.ds-cell-conf { width: 110px; white-space: nowrap; }
.ds-cell-cat  { width: 193px; white-space: nowrap; }
.ds-cell-status { width: 120px; white-space: nowrap; }
.ds-cell-chev { width: 28px; text-align: center; color: var(--ds-muted2); }

.ds-ean { font-family: 'Geist Mono', monospace; font-size: 13px; color: var(--ds-ink2); letter-spacing: 0.2px; }
.ds-name-primary { font-weight: 500; color: var(--ds-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-name-desc { font-size: 12px; color: var(--ds-muted); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-alt-badge {
  margin-left: 8px; font-size: 11.5px; color: var(--ds-muted);
  font-family: 'Geist Mono', monospace; font-weight: 500;
  padding: 1px 6px; border: 1px solid var(--ds-line); border-radius: 3px;
  white-space: nowrap;
}

/* Confidence bar */
.ds-conf { display: inline-flex; align-items: center; gap: 8px; }
.ds-conf-pct { font-family: 'Geist Mono', monospace; font-size: 13px; font-weight: 500; min-width: 26px; text-align: right; font-variant-numeric: tabular-nums; }
.ds-conf-track { position: relative; width: 48px; height: 4px; background: var(--ds-line2); border-radius: 2px; }
.ds-conf-fill  { position: absolute; inset: 0; border-radius: 2px; }

/* Status segmented */
.ds-status-seg { display: inline-flex; border: 1px solid var(--ds-line); border-radius: 6px; overflow: hidden; background: var(--ds-surface); }
/* ── Drawer ────────────────────────────────────────────────── */
.ds-drawer {
  background: var(--ds-surface); border-left: 1px solid var(--ds-line);
  display: flex; flex-direction: column; overflow: hidden;
  min-height: 0;
}

/* ── Porównanie kandydatów (Trello #81) ────────────────────────
   Kolumna = kandydat, wiersz = cecha. Style są tutaj, a nie inline w Shared/DsCandidateCompare.razor,
   bo sticky, hover i pseudoelementy nie dają się zapisać atrybutem style. Poprzedni, martwy blok
   .ds-candidate* opisywał pionowe karty, których ten widok już nie renderuje. */
.ds-cmp { padding: 14px 0 14px 20px; }
.ds-cmp-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-right: 20px; margin-bottom: 10px;
}
.ds-cmp-count { font-size: 12px; color: var(--ds-muted); text-transform: uppercase; letter-spacing: .6px; }
.ds-cmp-toggle {
  background: none; border: 1px solid var(--ds-line); border-radius: 4px;
  padding: 3px 9px; font-size: 12px; font-family: inherit; color: var(--ds-muted);
  cursor: pointer; transition: color 120ms, border-color 120ms; white-space: nowrap;
}
.ds-cmp-toggle:hover { color: var(--ds-ink); border-color: var(--ds-muted2); }

/* Przewijanie w bok żyje tutaj: przy większej liczbie kandydatów kolumny mają zostać czytelne,
   a nie ścisnąć się do nieczytelnej szerokości. */
.ds-cmp-scroll { overflow-x: auto; padding-right: 20px; }
/* Bez overflow i bez zaokrągleń na samej siatce: overflow (choćby hidden dla promienia) czyni z niej
   najbliższy kontener przewijania, a wtedy przyklejona kolumna etykiet przestaje się przyklejać —
   jej punktem odniesienia musi zostać .ds-cmp-scroll. */
.ds-cmp-grid {
  display: grid; min-width: min-content;
  border: 1px solid var(--ds-line); background: var(--ds-surface);
}
.ds-cmp-cell {
  padding: 8px 10px; font-size: 13px; color: var(--ds-ink2); line-height: 1.45;
  border-bottom: 1px solid var(--ds-line2); border-left: 1px solid var(--ds-line2);
  min-width: 0; overflow-wrap: anywhere;
}
/* Kolumna etykiet zostaje na ekranie, gdy kolumny kandydatów jadą w bok — bez niej po dwóch
   przesunięciach nie wiadomo, co się porównuje. */
.ds-cmp-lbl {
  position: sticky; left: 0; z-index: 2;
  border-left: none; background: var(--ds-bg);
  font-size: 12px; color: var(--ds-muted);
}
.ds-cmp-head { background: var(--ds-bg); border-bottom: 1px solid var(--ds-line); }
.ds-cmp-head-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ds-cmp-rank {
  font-size: 11px; font-weight: 600; color: var(--ds-muted);
  font-family: 'Geist Mono', monospace;
  padding: 1px 5px; border: 1px solid var(--ds-line); border-radius: 3px;
}
.ds-cmp-conf {
  display: flex; align-items: center; gap: 6px;
  font-size: 11.5px; font-weight: 600; font-family: 'Geist Mono', monospace;
}
.ds-cmp-conf-track {
  display: block; width: 44px; height: 4px; border-radius: 2px;
  background: var(--ds-line2); overflow: hidden;
}
.ds-cmp-conf-fill { display: block; height: 100%; }
.ds-cmp-pick {
  margin-top: 6px; padding: 3px 10px; font-size: 12px; font-family: inherit; font-weight: 500;
  background: var(--ds-ink); color: #fff; border: none; border-radius: 4px; cursor: pointer;
  transition: opacity 120ms;
}
.ds-cmp-pick:hover { opacity: .85; }
.ds-cmp-chosen {
  display: inline-block; margin-top: 6px; padding: 3px 10px;
  font-size: 12px; font-weight: 500; color: #fff;
  background: var(--ds-ok-soft); border-radius: 4px;
}
.ds-cmp-noean  { display: inline-block; margin-top: 6px; font-size: 12px; color: var(--ds-muted2); }
/* Różnica = powód, dla którego ta siatka istnieje; wspólne wiersze mają zniknąć w tle. */
.ds-cmp-cell.is-diff { background: var(--ds-warn-bg); }
/* Wybrany kandydat: cała jego kolumna na własnym tle, plus zielona plakietka i podkreślenie w
   nagłówku. Wcześniej była to czarna kreska przy lewej krawędzi kolumny — a że wybrany stoi zwykle
   pierwszy, kreska wypadała między nazwami cech a pierwszym kandydatem i czytała się jako linia
   podziału tabeli, nie jako zaznaczenie. Tło kolumny wygrywa z tłem różnicy (reguła niżej), żeby
   kolumna czytała się jako jedna całość — który wiersz się różni, mówi i tak przyklejona etykieta. */
.ds-cmp-cell.is-chosen { background: var(--ds-ok-bg); }
.ds-cmp-head.is-chosen { border-bottom: 2px solid var(--ds-ok-soft); }
.ds-cmp-cell.is-chosen .ds-cmp-name { color: var(--ds-ok-soft); }
.ds-cmp-name { font-weight: 600; color: var(--ds-ink); }
.ds-cmp-mono { font-family: 'Geist Mono', monospace; font-size: 12px; }
.ds-cmp-empty { color: var(--ds-muted2); }
.ds-cmp-thumbs { display: flex; flex-wrap: wrap; gap: 6px; }
.ds-cmp-thumbs img {
  width: 48px; height: 48px; object-fit: contain; background: #fff;
  border: 1px solid var(--ds-line); border-radius: 4px; cursor: pointer;
}
.ds-cmp-desc-rank {
  font-family: 'Geist Mono', monospace; font-size: 11px; color: var(--ds-muted2);
  margin-bottom: 4px;
}
.ds-cmp-desc { font-size: 12.5px; color: var(--ds-muted); line-height: 1.5; }

/* ── Źródła danych (Trello #85) ────────────────────────────
   Link do karty producenta jest przypisem, a nie treścią: ma dać się kliknąć i zniknąć z pola
   widzenia, kiedy się go nie potrzebuje. Stąd rozmiar 10.5px i kolor stonowany — nazwa produktu
   i EAN muszą zostać najgłośniejsze w kolumnie kandydata.

   Plakietka źródła wewnętrznego wygląda INACZEJ niż link i celowo nie udaje, że jest klikalna:
   nie ma podkreślenia, nie ma kursora-łapki, nie zmienia się pod myszą. Gdyby wyglądała jak link,
   pierwszą reakcją byłoby kliknięcie i wniosek, że aplikacja jest zepsuta. */
.ds-src {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px;
  font-size: 11.5px; line-height: 1.4;
}
.ds-src-label { color: var(--ds-muted2); }
.ds-src-link {
  display: inline-flex; align-items: center; gap: 4px; max-width: 100%;
  padding: 1px 6px; border-radius: 4px;
  border: 1px solid var(--ds-line); background: var(--ds-surface);
  color: var(--ds-muted); text-decoration: none;
}
.ds-src-link:hover { color: var(--ds-ink); border-color: var(--ds-muted2); text-decoration: underline; }
.ds-src-ico  { font-size: 10px; line-height: 1; }
.ds-src-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 130px; }
.ds-src-more { color: var(--ds-muted2); }
.ds-src-internal {
  display: inline-block; padding: 1px 6px; border-radius: 4px;
  border: 1px dashed var(--ds-line); color: var(--ds-muted2);
  cursor: default; white-space: nowrap;
}
/* „Brak danych" to trzeci stan, nie odmiana źródła wewnętrznego — stąd brak ramki. Plakietka
   z ramką czyta się jak etykieta czegoś, co istnieje; tutaj nie istnieje nic i ma to wyglądać
   jak pusta rubryka, a nie jak nazwa źródła. */
.ds-src-none { color: var(--ds-muted2); cursor: default; white-space: nowrap; }

.ds-comment-section { display: flex; flex-direction: column; gap: 6px; }
/* ── Allegro publishing animation ─────────────────────────── */
@keyframes al-cw     { to { transform: rotate(360deg);  } }
@keyframes al-ccw    { to { transform: rotate(-360deg); } }
@keyframes al-wrench {
  0%,100% { transform: rotate(-20deg); }
  50%     { transform: rotate(20deg);  }
}
@keyframes al-spark {
  0%   { opacity:1; transform:scale(.4) translate(0,0); }
  100% { opacity:0; transform:scale(1)  translate(var(--sx,12px),var(--sy,-16px)); }
}
@keyframes al-shimmer {
  0%   { background-position:-200% center; }
  100% { background-position: 200% center; }
}
@keyframes al-fadein { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }
@keyframes al-bar    { from { width:0%; } }

/* Gears — applied directly via style attr for reliable transform-origin */
.al-gear { display:block; flex-shrink:0; }

/* Sparks container */
.al-sparks { position:relative; width:12px; height:12px; margin:0 2px; }
.al-spark  {
  position:absolute; top:50%; left:50%;
  width:5px; height:5px; border-radius:50%; background:#ffcc00;
  animation:al-spark .8s ease-out infinite;
}
.al-spark:nth-child(2) { animation-delay:.27s; --sx:-10px; --sy:-12px; }
.al-spark:nth-child(3) { animation-delay:.54s; --sx:14px;  --sy:-8px;  }

/* Labels */
.al-title {
  font-size:14.5px; font-weight:700;
  background:linear-gradient(90deg,#ff5a00,#ff9040,#ff5a00);
  background-size:200%;
  -webkit-background-clip:text; -webkit-text-fill-color:transparent;
  animation:al-shimmer 2.2s linear infinite;
}
.al-product { font-size:13px; font-weight:500; color:var(--ds-ink2);
              animation:al-fadein .3s ease; white-space:nowrap;
              overflow:hidden; text-overflow:ellipsis; max-width:340px; margin:4px 0 2px; }
.al-step    { font-size:12.5px; color:#ff5a00; display:flex; align-items:center;
              justify-content:center; gap:5px; animation:al-fadein .3s ease; }
.al-counter { font-size:12px; color:var(--ds-muted); font-family:'Geist Mono',monospace; margin:6px 0 3px; }
.al-progress-bar  { height:4px; background:var(--ds-line2); border-radius:2px; overflow:hidden; }
.al-progress-fill { height:100%; background:linear-gradient(90deg,#ff5a00,#ffaa55);
                    border-radius:2px; transition:width .5s ease; animation:al-bar .5s ease; }

/* ── Modal ─────────────────────────────────────────────────── */
.ds-modal-backdrop {
  position: fixed !important;
  top: 0 !important; left: 0 !important;
  width: 100% !important; height: 100% !important;
  background: rgba(10, 10, 10, .3);
  display: flex !important; align-items: center !important; justify-content: center !important;
  z-index: 9999 !important;
  overflow: hidden;
}
.ds-modal {
  background: var(--ds-surface); border: 1px solid var(--ds-line);
  border-radius: 10px; width: 480px; max-width: calc(100% - 32px);
  box-shadow: 0 24px 60px rgba(0,0,0,.25);
  display: flex; flex-direction: column;
  max-height: calc(90vh / var(--ds-zoom)); overflow-y: auto;
  transition: width .25s ease;
}
/* Publishing: +25% */
.ds-modal--publishing { width: 600px; }
.ds-modal--wide      { width: 620px; }
/* Done / results: +50% szerokości względem bazowej */
.ds-modal--done { width: 1080px; }
.ds-modal-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  padding: 18px 20px 14px; border-bottom: 1px solid var(--ds-line);
}
.ds-modal-body  { padding: 18px 20px; display: flex; flex-direction: column; gap: 0; }
.ds-modal-footer {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 14px 20px; border-top: 1px solid var(--ds-line);
}

/* DsLoading — kręciołek z napisem; wariant opisuje miejsce i zachowuje dotychczasowe odstępy. */
.ds-loading { display: flex; align-items: center; gap: 8px; color: var(--ds-muted); }
.ds-loading--page   { padding: 60px; text-align: center; justify-content: center; gap: 10px; }
.ds-loading--panel  { padding: 40px; text-align: center; justify-content: center; gap: 10px; align-items: normal; }
.ds-loading--inline { font-size: 14px; }
.ds-loading--row    { padding: 16px; }

/* DsModal — okna potwierdzeń i podglądów w Magazynie (szerokość i wysokość idą parametrem). */
.ds-dialog-backdrop {
  position: fixed; inset: 0; z-index: 300; background: rgba(10,10,10,.3);
  display: flex; align-items: center; justify-content: center; outline: none;
}
.ds-dialog {
  background: var(--ds-surface); border: none; border-radius: 10px;
  padding: 16px 18px; box-shadow: 0 24px 60px rgba(0,0,0,.25);
}
.ds-dialog-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.ds-dialog-title { font-weight: 600; font-size: 19px; letter-spacing: -.01em; }
.ds-dialog-title--warn   { color: var(--ds-warn); }
.ds-dialog-title--ink    { color: var(--ds-ink); }
.ds-dialog-title--indigo { color: #4338ca; }
.ds-dialog-subtitle { font-size: 12px; color: var(--ds-muted); margin-top: 2px; }
.ds-dialog-close {
  background: none; border: none; cursor: pointer; color: var(--ds-muted); padding: 2px; flex-shrink: 0;
}
.ds-dialog-foot { display: flex; gap: 8px; justify-content: flex-end; }

/* Radzen notification toasts. Allegro validation errors carry long unbreakable tokens
   ("ConstraintViolationException.ValidReturnPolicy", "AvailableStockMustEqualToZero");
   a per-product publish failure dumps the whole thing into a toast, which pushed the
   toast off the right edge of the screen. Cap the width and force wrapping. */
.rz-notification { max-width: min(460px, calc(100vw / var(--ds-zoom) - 32px)); box-sizing: border-box; }
/* Wygląd toastu z Prototypu A: ciemna karta z białym tekstem. Zmienne ustawiamy na kontenerze, nie
   w :root — arkusz Radzena jest wczytywany PO site.css i jego :root wygrałby kolejnością. Kolor tła
   dalej niesie wagę komunikatu: błąd jest ciemnoczerwony, ostrzeżenie ciemnobursztynowe, a zwykła
   informacja i sukces — atramentem. */
.rz-notification {
  --rz-notification-border-radius: 10px;
  --rz-notification-padding: 12px 16px;
  --rz-notification-shadow: 0 10px 30px rgba(0,0,0,.25);
  --rz-notification-success-background-color: var(--ds-ink);
  --rz-notification-success-color: #fff;
  --rz-notification-success-icon-color: #86efac;
  --rz-notification-info-background-color: var(--ds-ink);
  --rz-notification-info-color: #fff;
  --rz-notification-info-icon-color: #93c5fd;
  --rz-notification-warning-background-color: #422006;
  --rz-notification-warning-color: #fff;
  --rz-notification-warning-icon-color: #fde68a;
  --rz-notification-error-background-color: #7f1d1d;
  --rz-notification-error-color: #fff;
  --rz-notification-error-icon-color: #fecaca;
  font-size: 14px;
}
.rz-notification a, .rz-notification .ds-btn-link { color: #93c5fd; }
.rz-notification, .rz-notification * {
  overflow-wrap: anywhere;
  word-break: break-word;
  white-space: normal;
}

/* The marker is assigned only to automatically closing toasts, and carries their duration. It
   deliberately targets the
   element carrying NotificationMessage.Style, so the strip remains available whether Radzen puts
   that style on the wrapper or on the notification item. */
[style*="--ds-notification-countdown"] {
  position: relative !important;
  overflow: hidden !important;
}
[style*="--ds-notification-countdown"]::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  height: 3px;
  background: rgba(255, 255, 255, .45);   /* toast jest ciemny (patrz .rz-notification) */
  transform-origin: left center;
  animation: ds-notification-countdown var(--ds-notification-countdown) linear forwards;
}
@keyframes ds-notification-countdown {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* ── Description HTML (candidate compare, Allegro preview) ─────── */
.ds-desc-html p { margin: 0 0 6px; }
.ds-desc-html p:last-child { margin-bottom: 0; }
.ds-desc-html ul, .ds-desc-html ol { margin: 4px 0 6px; padding-left: 18px; }
.ds-desc-html li { margin: 1px 0; }
.ds-desc-html b, .ds-desc-html strong { color: var(--ds-ink2); }

/* ── Allegro section editor (Opis Allegro, Trello #112) ─────── */
.ds-allegro-edit {
  border: 1px solid var(--ds-line2); border-radius: 4px; padding: 8px 10px;
  min-height: 120px; font-size: 14px; line-height: 1.5; color: var(--ds-ink2);
  background: var(--ds-surface); outline: none;
}
.ds-allegro-edit:focus { border-color: var(--ds-line); box-shadow: none; }   /* subtle — no dark/thick ring */
.ds-allegro-edit.ds-allegro-bad { border-color: #f5c77e; background: #fffbeb; }
.ds-allegro-edit h1 { font-size: 16.5px; font-weight: 700; margin: 2px 0 4px; }
.ds-allegro-edit h2 { font-size: 14.5px; font-weight: 700; margin: 4px 0 3px; }
.ds-allegro-edit p { margin: 0 0 4px; }
.ds-allegro-edit ul, .ds-allegro-edit ol { margin: 3px 0 4px; padding-left: 18px; }

/* Live preview mimics the Allegro listing look */
.ds-allegro-preview h1 { font-size: 17.5px; font-weight: 700; margin: 4px 0 6px; color: var(--ds-ink); }
.ds-allegro-preview h2 { font-size: 15px; font-weight: 700; margin: 8px 0 4px; color: var(--ds-ink); }

/* Small square buttons: formatting toolbar, section controls, add-kind chips */
.ds-fmt-btn, .ds-sec-btn {
  min-width: 24px; height: 22px; padding: 0 6px; font-size: 12px; line-height: 1;
  border: 1px solid var(--ds-line); border-radius: 3px; background: var(--ds-surface);
  color: var(--ds-ink2); cursor: pointer;
}
.ds-fmt-btn:hover, .ds-sec-btn:hover:not(:disabled) { background: var(--ds-bg); }
.ds-sec-btn:disabled { opacity: .35; cursor: default; }
.ds-sec-btn-danger:hover:not(:disabled) { border-color: #dc2626; color: #dc2626; }
.ds-add-kind {
  font-size: 12px; padding: 5px 12px; border: 1px solid var(--ds-line); border-radius: 4px;
  background: var(--ds-surface); color: var(--ds-ink2); cursor: pointer;
}
.ds-add-kind:hover { background: var(--ds-bg); border-color: var(--ds-accent); }

/* Kreator ↔ Podgląd segmented toggle */
.ds-seg { display: inline-flex; border: 1px solid var(--ds-line); border-radius: 5px; overflow: hidden; }
.ds-seg-btn {
  font-size: 11.5px; padding: 3px 10px; border: none; background: var(--ds-surface);
  color: var(--ds-muted); cursor: pointer;
}
.ds-seg-btn + .ds-seg-btn { border-left: 1px solid var(--ds-line); }
.ds-seg-on { background: var(--ds-ink); color: #fff; font-weight: 600; }
/* Duży przełącznik (Prototyp A, .seg): 36 px, wybrany na ciemno. */
.ds-seg--lg { border-color: var(--ds-control-line); border-radius: 8px; align-self: flex-start; }
.ds-seg--lg > button {
  height: 36px; padding: 0 14px; border: none; background: var(--ds-surface); font: inherit; font-size: 13px;
  color: var(--ds-ink2); cursor: pointer;
}
.ds-seg--lg > button + button { border-left: 1px solid var(--ds-control-line); }
.ds-seg--lg > button.on { background: var(--ds-ink); color: #fff; }
/* Ustawienie wł./wył. (DsToggle, 2026-10-13): przełącznik lewo/prawo, tytuł z plakietką stanu, opis pod spodem.
   Karta włączona dostaje zieloną belkę z lewej; wariant bez karty to wiersz formularza. */
.ds-toggle { display: flex; align-items: flex-start; gap: 14px; }
.ds-toggle--card {
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-left: 3px solid var(--ds-line);
  border-radius: 8px; padding: 14px 20px 14px 17px;
}
.ds-toggle--card.ds-toggle--on { border-color: var(--ds-done); }
.ds-toggle-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ds-toggle-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 14.5px; font-weight: 600; color: var(--ds-ink); }
.ds-toggle:not(.ds-toggle--card) .ds-toggle-title { font-size: 13.5px; font-weight: 500; }
.ds-toggle-state {
  font-size: 11px; font-weight: 600; line-height: 16px; padding: 0 7px; border-radius: 999px;
  border: 1px solid var(--ds-control-line); color: var(--ds-muted);
}
.ds-toggle--on .ds-toggle-state { border-color: var(--ds-done); color: var(--ds-done); background: var(--ds-done-bg); }
.ds-toggle-hint { font-size: 12.5px; color: var(--ds-muted); max-width: 78ch; }
.ds-toggle-detail { font-size: 12px; color: var(--ds-muted2); max-width: 78ch; }
.ds-switch {
  position: relative; flex: none; width: 40px; height: 22px; margin-top: 1px; padding: 0; border: none;
  border-radius: 999px; background: var(--ds-control-line); cursor: pointer; transition: background .15s;
}
.ds-switch--on { background: var(--ds-ink); }
.ds-switch:disabled { opacity: .5; cursor: default; }
.ds-switch:focus-visible { outline: 2px solid var(--ds-todo); outline-offset: 2px; }
.ds-switch-knob {
  position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: transform .15s;
}
.ds-switch--on .ds-switch-knob { transform: translateX(18px); }
/* Certyfikat KSeF (Ustawienia → Faktury, 2026-10-13): dwa ponumerowane pola plików i hasło klucza w jednej ramce. */
.ds-ksef-files { display: flex; flex-direction: column; border: 1px solid var(--ds-line); border-radius: 8px; }
.ds-ksef-file { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.ds-ksef-file + .ds-ksef-file { border-top: 1px solid var(--ds-line); }
.ds-ksef-step {
  flex: none; width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
  font-size: 12.5px; font-weight: 600; background: var(--ds-line2); color: var(--ds-ink2);
}
.ds-ksef-step--done { background: var(--ds-done-bg); color: var(--ds-done); }
.ds-ksef-file-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ds-ksef-file-title { font-size: 13.5px; font-weight: 600; color: var(--ds-ink); }
.ds-ksef-ext { font-family: var(--ds-mono); font-size: 11.5px; font-weight: 500; color: var(--ds-muted); margin-left: 4px; }
.ds-ksef-file-name { font-size: 12.5px; color: var(--ds-ink2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-ksef-file-name--none { color: var(--ds-muted); white-space: normal; }
.ds-ksef-file-error { font-size: 12.5px; color: var(--ds-bad); }
/* Licznik ilości: − pole + */
.ds-stepper { display: inline-flex; align-items: stretch; }
.ds-stepper > button {
  width: 36px; border: 1px solid var(--ds-control-line); background: var(--ds-surface); font-size: 16px; cursor: pointer;
}
.ds-stepper > button:first-child { border-radius: 6px 0 0 6px; border-right: 0; }
.ds-stepper > button:last-child { border-radius: 0 6px 6px 0; border-left: 0; }
.ds-stepper > .ds-input { border-radius: 0; }
/* Import: baner odczytu, treść źródłowa, pasek skanowania. */
.ds-imp-banner {
  display: flex; align-items: center; gap: 10px 14px; flex-wrap: wrap; flex-shrink: 0;
  padding: 12px 14px; margin-bottom: 10px; border: 1px solid var(--ds-line); border-radius: 10px;
  background: var(--ds-surface); font-size: 13.5px;
}
.ds-imp-src {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: 'Geist Mono', monospace; font-size: 12px; color: var(--ds-muted);
  background: var(--ds-line2); padding: 2px 6px; border-radius: 4px;
}
.ds-imp-scan {
  display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; flex-shrink: 0;
  padding: 14px; border: 1px solid var(--ds-line); border-radius: 10px; background: var(--ds-surface);
}

/* Image slot: photo with hover-✕ */
.ds-img-wrap { position: relative; display: inline-block; width: 100%; }
.ds-img {
  width: 100%; max-height: 180px; object-fit: contain; display: block;
  border: 1px solid var(--ds-line); border-radius: 6px; background: #fff;
}
.ds-img-x {
  position: absolute; top: 5px; right: 5px; width: 22px; height: 22px; line-height: 1;
  border: none; border-radius: 50%; background: rgba(17,17,17,.72); color: #fff; cursor: pointer;
  font-size: 13px; opacity: 0; transition: opacity 120ms; display: flex; align-items: center; justify-content: center;
}
.ds-img-wrap:hover .ds-img-x { opacity: 1; }
.ds-img-x:hover { background: #dc2626; }

/* Empty image slot: dashed "+" tile (Allegro-style add-photo field) */
.ds-img-add {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 96px; border: 1.5px dashed var(--ds-line); border-radius: 6px;
  background: var(--ds-bg); color: var(--ds-muted); cursor: pointer; text-align: center; padding: 10px;
}
.ds-img-add:hover { border-color: var(--ds-accent); color: var(--ds-ink2); }
.ds-img-plus { font-size: 28.5px; line-height: 1; font-weight: 300; }

/* Image picker popover: gallery thumbs + upload tile */
.ds-img-picker { margin-top: 6px; padding: 8px; border: 1px solid var(--ds-line); border-radius: 6px; background: var(--ds-surface); }
.ds-img-thumb {
  width: 46px; height: 46px; object-fit: contain; border: 1px solid var(--ds-line);
  border-radius: 4px; background: #fff; cursor: pointer;
}
.ds-img-thumb:hover { border-color: var(--ds-accent); }
.ds-img-upload {
  width: 46px; height: 46px; display: flex; align-items: center; justify-content: center;
  border: 1.5px dashed var(--ds-line); border-radius: 4px; background: var(--ds-bg);
  color: var(--ds-muted); cursor: pointer;
}
.ds-img-upload:hover { border-color: var(--ds-accent); color: var(--ds-ink2); }

/* Allegro Sales Center-style section card: layout variants + actions + bottom toolbar */
.ds-sec-card { border: 1px solid var(--ds-line); border-radius: 8px; background: var(--ds-surface); }
.ds-sec-top {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 10px; border-bottom: 1px solid var(--ds-line2);
}
.ds-layouts { display: flex; gap: 2px; }
.ds-lay {
  width: 30px; height: 26px; display: flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: 5px; background: none; color: var(--ds-muted); cursor: pointer;
}
.ds-lay:hover { background: var(--ds-bg); color: var(--ds-ink2); }
.ds-lay-on { color: #e07b39; background: #fff4ec; border-color: #f3c9a8; }   /* Allegro orange */
.ds-sec-actions { display: flex; gap: 1px; }
.ds-act {
  width: 28px; height: 26px; display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 5px; background: none; color: var(--ds-muted); cursor: pointer;
}
.ds-act:hover:not(:disabled) { background: var(--ds-bg); color: var(--ds-ink); }
.ds-act:disabled { opacity: .3; cursor: default; }
.ds-sec-body { display: flex; gap: 12px; align-items: flex-start; padding: 12px; }

/* Inline model: the whole description is one document; sections are read-only until clicked */
.ds-allegro-doc {
  background: #fff; border: 1px solid var(--ds-line); border-radius: 8px;
  padding: 20px 22px; max-width: 1280px; margin: 0 auto;   /* +25% względem 900px */
  display: flex; flex-direction: column; gap: 4px;
}
.ds-sec-view {
  border: 1px dashed transparent; border-radius: 6px; padding: 6px 8px; margin: 0 -8px;
  cursor: text; transition: border-color 100ms;
}
.ds-sec-view:hover { border-color: var(--ds-line); }   /* jasna, przerywana obwódka; tło bez zmian */
.ds-sec-editing { margin: 4px 0; border-style: dashed; box-shadow: none; }   /* jasna przerywana ramka, nie ciemna */

/* Text field joins its bottom toolbar into one control (like Allegro) */
.ds-sec-body .ds-allegro-edit { border-radius: 5px 5px 0 0; border-bottom: none; min-height: 120px; }
.ds-textbar {
  display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
  border: 1px solid var(--ds-line); border-radius: 0 0 5px 5px; padding: 3px 4px; background: var(--ds-surface);
}
.ds-tb {
  min-width: 26px; height: 26px; padding: 0 6px; display: inline-flex; align-items: center; justify-content: center;
  border: none; border-radius: 4px; background: none; color: var(--ds-ink2); cursor: pointer; font-size: 13px;
}
.ds-tb:hover:not(:disabled) { background: var(--ds-bg); }
.ds-tb:disabled { opacity: .3; cursor: default; }   /* wyszarzone undo/redo gdy akcja niemożliwa */
.ds-tb-style { font-size: 12.5px; white-space: nowrap; }
.ds-tb-sep { width: 1px; height: 18px; background: var(--ds-line); margin: 0 3px; }
.ds-styledrop { position: relative; }
.ds-styledrop-menu {
  position: absolute; bottom: 30px; left: 0; z-index: 20; min-width: 150px;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 6px;
  box-shadow: 0 6px 20px rgba(0,0,0,.14); padding: 4px; display: flex; flex-direction: column;
}
.ds-styledrop-menu button {
  text-align: left; border: none; background: none; padding: 6px 8px; border-radius: 4px;
  cursor: pointer; color: var(--ds-ink2);
}
.ds-styledrop-menu button:hover { background: var(--ds-bg); }

/* "DODAJ KOLEJNY WIERSZ" link-button */
.ds-add-row {
  align-self: center; margin-top: 4px; border: none; background: none; cursor: pointer;
  color: var(--ds-accent); font-size: 12.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; padding: 8px 12px;
}
.ds-add-row:hover { text-decoration: underline; }

/* Full-height maximize/restore handle on the drawer's left edge */
.ds-maxbar {
  position: absolute; left: 0; top: 0; bottom: 0; width: 14px; z-index: 5;
  border: none; border-right: 1px solid var(--ds-line2); background: var(--ds-bg);
  color: var(--ds-muted); cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
.ds-maxbar:hover { background: var(--ds-line2); color: var(--ds-ink); }

/* ── Odzyskiwanie połączenia (#components-reconnect-modal) ─────
   Zerwany obwód zatrzymuje w Blazor Serverze całą stronę, więc ten komunikat jest jedyną rzeczą,
   która wtedy jeszcze działa — i jedyną, która może powiedzieć, że nic nie jest zepsute na stałe.

   Widoczność wisi na atrybucie data-ds-rc, nie na klasach dopisywanych przez framework: powód
   rozpisany przy samym elemencie w _Host.cshtml. Selektor po id, żeby wygrał bez !important.

   Przyciski są schowane w pierwszych sekundach. Zerwanie zwykle mija samo, a przycisk pokazany od
   razu podpowiada klikanie w sytuacji, w której klikać nie ma po co — i zamienia mrugnięcie w awarię
   wymagającą decyzji. Odsłania je klasa ds-rc-slow: po 20 s ponowień albo od razu po nieudanej próbie. */
.ds-rc {
  position: fixed; inset: 0; z-index: 10000;
  display: none; align-items: center; justify-content: center;
  padding: 20px; background: rgba(10, 10, 10, .32);
}
#components-reconnect-modal[data-ds-rc="down"],
#components-reconnect-modal[data-ds-rc="failed"],
#components-reconnect-modal[data-ds-rc="rejected"] { display: flex; }

.ds-rc-card {
  display: flex; align-items: center; gap: 14px;
  width: 100%; max-width: 460px; padding: 16px 18px;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 10px;
  box-shadow: 0 20px 48px rgba(0, 0, 0, .22);
}
.ds-rc-spinner { width: 20px; height: 20px; border-width: 2.5px; }
.ds-rc-text  { flex: 1; min-width: 0; }
.ds-rc-title { font-size: 14.5px; font-weight: 600; color: var(--ds-ink); }
.ds-rc-note  { font-size: 12.5px; color: var(--ds-muted); margin-top: 2px; line-height: 1.45; }

.ds-rc-actions { display: none; flex-direction: column; gap: 6px; flex-shrink: 0; }
.ds-rc.ds-rc-slow .ds-rc-actions { display: flex; }
/* Nieudana próba niczego nie kręci — kręciołek znaczyłby, że coś jeszcze trwa. */
#components-reconnect-modal[data-ds-rc="failed"] .ds-rc-spinner { display: none; }

/* ── Blazor error UI (preserve) ────────────────────────────── */
#blazor-error-ui {
  background: lightyellow; bottom: 0; box-shadow: 0 -1px 2px rgba(0,0,0,.2);
  display: none; left: 0; padding: .6rem 1.25rem .7rem; position: fixed; width: 100%; z-index: 1000;
}
#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: .75rem; top: .5rem; }
.blazor-error-boundary {
  background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbmsiPjxwYXRoIGQ9Ik0yNjMuNTA2IDUxQzI2NC43MTcgNTEgMjY1LjgxMyA1MS40ODM3IDI2Ni42MDYgNTIuMjY1OEwyNjcuMDUyIDUyLjc5ODcgMjY3LjUzOSA1My42MjgzIDI5MC4xODUgOTIuMTgzMSAyOTAuNTQ1IDkyLjc5NSAyOTAuNjU2IDkyLjk5NkMyOTAuODc3IDkzLjUxMyAyOTEgOTQuMDgxNSAyOTEgOTQuNjc4MiAyOTEgOTcuMDY1MSAyODkuMDM4IDk5IDI4Ni42MTcgOTlMMjQwLjM4MyA5OUMyMzcuOTYzIDk5IDIzNiA5Ny4wNjUxIDIzNiA5NC42NzgyIDIzNiA5NC4zNzk5IDIzNi4wMzEgOTQuMDg4NiAyMzYuMDg5IDkzLjgwNzJMMjM2LjMzOCA5My4wMTYyIDIzNi44NTggOTIuMTMxNCAyNTkuNDczIDUzLjYyOTQgMjU5Ljk2MSA1Mi43OTg1IDI2MC40MDcgNTIuMjY1OEMyNjEuMiA1MS40ODM3IDI2Mi4yOTYgNTEgMjYzLjUwNiA1MVoiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==) no-repeat 1rem/1.8rem, #b32121;
  padding: 1rem 1rem 1rem 3.7rem; color: white;
}
.blazor-error-boundary::after { content: "An error has occurred."; }

/* ── Page-level error boundary ─────────────────────────────────
   Fallback shown by Shared/PageErrorBoundary.razor when a component throws. Deliberately calm and
   text-only: the user is not the audience for the failure, the log is. Its job is to say "this part
   is broken, the rest still works" without looking like data loss. */
.ds-error-boundary {
  max-width: 560px; margin: 48px auto; padding: 24px;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px;
  text-align: center;
}
.ds-error-boundary h2 { margin: 0 0 8px; font-size: 16.5px; font-weight: 600; color: var(--ds-ink); }
.ds-error-boundary p  { margin: 0 0 16px; font-size: 14.5px; line-height: 1.5; color: var(--ds-muted); }

/* ── Diagnostyka ───────────────────────────────────────────────
   Ekran administratora: zdarzenia, błędy, transkrypcje AI. Wszystko tu jest wariantem czegoś, co
   już istnieje w aplikacji — plakietka to ds-tag w kolorze skutku, kafelek to ta sama karta co na
   pulpicie — żeby ekran diagnostyczny nie wyglądał jak doklejony z innego systemu. */

/* Plakietka skutku. Kolory te same, którymi status jest wypisany w tabeli produktów, więc
   „czerwone” znaczy tu dokładnie to samo co tam. */
/* Pigułka stanu (Prototyp A): kolorowe tło bez obwódki i kropka w kolorze tekstu. Kropka nie jest
   ozdobą — przy wydruku w odcieniach szarości albo dla daltonisty to ona odróżnia pigułkę od zwykłej
   etykiety. Pięć tonów = pięć stanów z tokenów; ok/bad/warn/muted to dawne nazwy tych samych tonów. */
.ds-pill {
  display: inline-flex; align-items: center; gap: 6px;
  height: 24px; padding: 0 10px; font-size: 12.5px; font-weight: 500; line-height: 1;
  border-radius: 12px; white-space: nowrap; border: none;
  background: var(--ds-idle-bg); color: var(--ds-idle);
}
.ds-pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.ds-pill--sm { height: 20px; padding: 0 8px; font-size: 12px; gap: 5px; }

/* Role konta na liście Użytkowników (uprawnienia per użytkownik, 2026-10-07): komórka-przycisk ze znacznikami ról,
   okno „Role konta” z polem wyboru i opisem przy każdej roli, pola wyboru w formularzu „Dodaj użytkownika”. */
.ds-roles-cell { display: inline-flex; flex-wrap: wrap; gap: 4px; align-items: center; padding: 3px; margin: -3px;
                 background: none; border: 1px solid transparent; border-radius: 6px; cursor: pointer; text-align: left; }
.ds-roles-cell:hover:not(:disabled) { border-color: var(--ds-line); background: var(--ds-bg); }
.ds-roles-cell:disabled { cursor: default; opacity: .6; }
.ds-roles-cell .ds-pill { height: 20px; padding: 0 8px; font-size: 12px; gap: 5px; }
.ds-roles-row { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; }
.ds-roles-row input { margin-top: 3px; }
.ds-roles-name { display: block; font-size: 14px; font-weight: 600; color: var(--ds-ink); }
.ds-roles-desc { display: block; font-size: 12.5px; color: var(--ds-muted); line-height: 1.45; }
.ds-roles-pick { display: flex; flex-wrap: wrap; gap: 6px 16px; border: 0; padding: 0; margin: 0; }
.ds-roles-pick legend { width: 100%; padding: 0; margin-bottom: 4px; font-size: 12px; font-weight: 500; color: var(--ds-ink2); }
.ds-roles-opt { display: inline-flex; gap: 6px; align-items: center; font-size: 13px; color: var(--ds-ink); cursor: pointer; }
.ds-pill--todo, .ds-pill--info { background: var(--ds-todo-bg); color: var(--ds-todo); }
.ds-pill--late, .ds-pill--bad  { background: var(--ds-late-bg); color: var(--ds-late); }
.ds-pill--wait                 { background: var(--ds-wait-bg); color: var(--ds-wait); }
.ds-pill--done, .ds-pill--ok   { background: var(--ds-done-bg); color: var(--ds-done); }
.ds-pill--idle, .ds-pill--muted { background: var(--ds-idle-bg); color: var(--ds-idle); }

/* Wiersz z błędem. Samo lewe obramowanie, nie tło całego wiersza: przy dwustu wierszach, z których
   połowa jest czerwona, tło zamienia tabelę w płachtę i przestaje cokolwiek wyróżniać. */
.ds-row--bad td:first-child { box-shadow: inset 2px 0 0 var(--ds-bad); }

.ds-empty-cell { padding: 24px; text-align: center; color: var(--ds-muted); font-size: 14px; }

.ds-link { color: var(--ds-ink2); text-decoration: none; border-bottom: 1px solid var(--ds-line); }
.ds-link:hover { color: var(--ds-ink); border-bottom-color: var(--ds-muted); }

.ds-details-toggle { cursor: pointer; font-size: 12px; color: var(--ds-muted); }
.ds-details-toggle:hover { color: var(--ds-ink2); }

/* Blok treści: JSON szczegółów zdarzenia, prompt, odpowiedź modelu. Zawijanie jest konieczne —
   prompt bywa jedną linią na kilkanaście tysięcy znaków i bez tego rozpycha całą stronę w poziomie. */
.ds-pre {
  margin: 4px 0 0; padding: 8px 10px; max-height: 220px; overflow: auto;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 6px;
  font-family: 'Geist Mono', ui-monospace, monospace; font-size: 12.5px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; color: var(--ds-ink2);
}
.ds-pre--tall { max-height: 340px; }

.ds-transcript-label {
  font-size: 12px; color: var(--ds-muted); text-transform: uppercase; letter-spacing: .6px;
  margin: 10px 0 0;
}
/* Transkrypcja ucięta przy zapisie (AiTranscript). */
.ds-transcript-warn { margin-bottom: 8px; color: var(--ds-warn); font-size: 13px; }

/* ── Diagnostyka: uzupełnienia ─────────────────────────────────
   Kolumny o stałej szerokości muszą przycinać treść, nie rozpychać tabeli. Bez tego długi e-mail
   aktora wypychał kolumnę „Kto" na sąsiedni „Produkt" — komórka tabeli domyślnie rośnie do
   najdłuższego słowa, a szerokość w <th> jest wtedy tylko sugestią. */
.ds-truncate { max-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* max-width: 0 to sztuczka dla KOMÓRKI tabeli (komórka bierze szerokość kolumny, a tekst się ucina). Na
   elemencie w środku komórki ta sama reguła daje mu zerową szerokość — tekst znika w całości. Tak zniknęła
   nazwa nabywcy w rejestrze faktur (a kupujący i pozycja na liście zamówień). Wewnątrz komórki szerokość
   wyznacza komórka. */
.ds-truncate:not(td):not(th) { max-width: 100%; min-width: 0; }
span.ds-truncate:not(td) { display: inline-block; vertical-align: bottom; }

/* Odwołanie do innego rekordu (RecordLink): etykieta otwiera go w tej karcie, ikonka obok — w nowej.
   Ikonka jest przygaszona do najechania na wiersz, żeby lista nie była usiana strzałkami, ale nie znika
   całkiem — na dotyku nie ma najechania. */
.ds-reclink { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; min-width: 0; vertical-align: baseline; }
.ds-reclink-label {
  color: inherit; text-decoration: none; border-bottom: 1px solid var(--ds-line);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.ds-reclink-label:hover { color: var(--ds-ink); border-bottom-color: var(--ds-ink2); }
.ds-reclink-new {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 18px; height: 18px; border-radius: 4px; color: var(--ds-muted2); opacity: .55;
  transition: opacity 120ms, background 120ms, color 120ms;
}
/* Najechanie na samą etykietę nie podświetla ikonki (decyzja właściciela 2026-09-29) — ikonka reaguje na
   siebie i na wiersz tabeli. */
tr:hover .ds-reclink-new, .ds-reclink-new:focus-visible { opacity: 1; color: var(--ds-muted); }
.ds-reclink-new:hover { background: var(--ds-line2); color: var(--ds-ink); }
.ds-nowrap   { white-space: nowrap; }
.ds-num      { font-variant-numeric: tabular-nums; }
.ds-muted    { color: var(--ds-muted); }

/* Brak to nie awaria. Żółty, bo ktoś ma to uzupełnić — czerwony znaczy „program się wywalił”
   i jest zarezerwowany dla zakładki Wyjątki. Rozróżnienie ma być widoczne z drugiego końca pokoju,
   bo od niego zależy, czy ktoś rzuca wszystko i sprawdza, czy dopisuje sobie na listę zadań. */
.ds-pill--warn { background: var(--ds-wait-bg); color: var(--ds-wait); }
.ds-row--warn td:first-child { box-shadow: inset 2px 0 0 var(--ds-warn); }

/* Mrugnięcie polem, do którego prowadzi przycisk przy błędzie publikacji (Trello #163).
   Delikatne z rozmysłem: chodzi o „to ten wiersz", a nie o alarm — dwa przebiegi obramowania
   i tła, bez ruchu i bez zmiany rozmiaru, żeby nie przesunąć układu w trakcie czytania.
   prefers-reduced-motion wyłącza pulsowanie, ale zostawia jednorazowe podświetlenie: informacja
   „to jest to pole" nie jest efektem ozdobnym i nie wolno jej zgubić. */
@keyframes ds-blink {
    0%, 100% { border-color: var(--ds-line2); background: var(--ds-surface); }
    25%, 75% { border-color: #f59e0b;         background: #fffbeb;           }
}
.ds-blink {
    animation: ds-blink 0.55s ease-in-out 2;
}
@media (prefers-reduced-motion: reduce) {
    .ds-blink {
        animation: none;
        border-color: #f59e0b;
        background: #fffbeb;
    }
}

/* ── Siatka potwierdzenia importu (Trello #182) ──────────────────────────────
   Komórka edytowalna wygląda jak komórka, a nie jak formularz: bez obwódki w spoczynku, z obwódką
   pod kursorem i w fokusie. Pola z ramką przy każdej z pięciu kolumn i trzystu wierszach zamieniają
   tabelę w ścianę prostokątów, w której nie widać tych kilku faktycznie wymagających uwagi — a cały
   ten ekran istnieje po to, żeby te kilka było widać. Podświetlenia stanu (żółte „ilość z domysłu”,
   czerwone „nazwa jest kodem”) idą stylem inline z wiersza i wygrywają, bo dotyczą konkretnej
   komórki, a nie klasy. */
.ds-cell-input {
  width: 100%; box-sizing: border-box;
  padding: 3px 6px; font: inherit; font-size: 14px;
  color: var(--ds-ink); background: transparent;
  border: 1px solid transparent; border-radius: 4px;
}
.ds-cell-input:hover { border-color: var(--ds-line); }
.ds-cell-input:focus {
  outline: none; background: var(--ds-surface);
  border-color: var(--ds-ink2);
}
/* Strzałki „w górę/w dół” przy ilości zabierają połowę wąskiej kolumny i podbijają liczbę o jeden,
   czyli o krok bez znaczenia przy stanie magazynowym. Liczbę i tak się tu wpisuje. */
.ds-cell-input[type="number"] { -moz-appearance: textfield; }
.ds-cell-input[type="number"]::-webkit-outer-spin-button,
.ds-cell-input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ══ Magazyn na dokumentach i nawigacja modułu sprzedaży ══════════════════════════════════════════ */

/* Grupy menu bocznego („Ogólne”, „Zaawansowane”, „Administracja”). Podpis znika po zwinięciu razem z pozostałymi podpisami
   (ds-side-label); wtedy grupę oddziela kreska — nagłówek bez tekstu byłby pustym rzędem. */
/* Podpis grupy zwykłą pisownią, 12 px (Prototyp A) — wersaliki krzyczały głośniej niż pozycje, które opisują. */
.ds-nav-sec {
  margin: 14px 10px 4px;
  font-size: 12px; font-weight: 500;
  color: var(--ds-muted);
}
/* Pierwsza grupa („Ogólne”) stoi tuż pod logo — pełny odstęp grupy odsuwałby menu bez powodu. */
.ds-nav-sec:first-child { margin-top: 4px; }
.ds-nav-sep { display: none; height: 1px; margin: 8px 6px; background: var(--ds-line2); }
body.ds-sidebar-collapsed .ds-nav-sep { display: block; }
.ds-nav-grow { flex: 1; }
.ds-nav-item { position: relative; }
/* Licznik przy pozycji menu — ten sam kształt co .ds-cnt przy zakładkach. Bez modyfikatora szary;
   --todo niebieski („twoja kolej”), --warn żółty (decyzja), --bad czerwony (po terminie). */
.ds-nav-count {
  min-width: 22px; height: 22px; padding: 0 7px; border-radius: 11px; flex-shrink: 0;
  display: inline-grid; place-items: center; margin-left: auto;
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: var(--ds-idle-bg); color: var(--ds-idle);
}
.ds-nav-count--todo, .ds-nav-count--info { background: var(--ds-todo-bg); color: var(--ds-todo); }
.ds-nav-count--warn { background: var(--ds-wait-bg); color: var(--ds-wait); }
.ds-nav-count--bad { background: var(--ds-late-bg); color: var(--ds-late); }
body.ds-sidebar-collapsed .ds-nav-count {
  position: absolute; top: 3px; right: 2px; height: 16px; min-width: 16px; padding: 0 4px; font-size: 10.5px;
}
body.ds-sidebar-collapsed .ds-nav-count {
  position: absolute; top: 1px; right: 4px; min-width: 14px; height: 14px; padding: 0 3px; font-size: 10px;
}

/* Komórka „Dostępne” — przycisk, nie pole: stan zmienia się dokumentem, nie wpisem (I12). */
.ds-stock-cell {
  display: inline-flex; flex-direction: column; align-items: flex-end; gap: 1px;
  padding: 2px 6px; border: 1px dashed transparent; border-radius: 4px;
  background: none; font: inherit; font-size: 12.5px; font-family: 'Geist Mono', monospace; cursor: pointer;
}
.ds-stock-cell:hover { border-color: var(--ds-line); background: var(--ds-line2); }
.ds-stock-cell-sub { font-size: 10.5px; color: var(--ds-muted); white-space: nowrap; }
.ds-stock-cell-flag {
  display: inline-block; padding: 0 6px; border-radius: 999px; font-size: 10.5px; font-family: 'Geist', sans-serif;
  background: var(--ds-warn-bg); color: var(--ds-warn-ink); border: 1px solid #f5d77a; white-space: nowrap;
}
.ds-stock-filter { color: var(--ds-warn-ink); gap: 6px; }

/* Kolumna Status — jeden status rekordu (ProductLifecycle): etap, podstatus, flaga, ikonki kanałów. */
.ds-life { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; }
.ds-life-head { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
.ds-life-stage { font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.ds-life-sub { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-height: 16px; }
.ds-life-detail {
  font-size: 11.5px; color: var(--ds-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.ds-life-progress { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-life-steps { display: inline-flex; gap: 2px; }
.ds-life-steps > span { width: 8px; height: 3px; border-radius: 1px; }
.ds-life-publish { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: 600; white-space: nowrap; }
.ds-life-chanrow { display: inline-flex; align-items: center; gap: 2px; flex-shrink: 0; }
/* Flaga „Wymaga działania” — czerwony znacznik, nie etap. */
.ds-life-flag {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 14px; height: 14px; border-radius: 50%; cursor: help;
  background: var(--ds-bad); color: #fff; font-size: 11px; font-weight: 700; line-height: 1;
}
.ds-life-act {
  background: none; border: none; padding: 2px; line-height: 0; color: var(--ds-muted);
  border-radius: 4px; cursor: pointer;
}
.ds-life-act:hover:not(:disabled) { background: var(--ds-line2); color: var(--ds-ink2); }
.ds-life-act:disabled { opacity: .35; cursor: not-allowed; }
.ds-life-act--ok { color: var(--ds-ok); }
.ds-life-act--ok:hover:not(:disabled) { background: var(--ds-ok-bg); color: var(--ds-ok); }
/* Ikonka kanału: litera kanału, kolor = stan oferty w tym kanale. */
.ds-chan {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 16px; height: 16px; padding: 0; border-radius: 4px; cursor: pointer;
  border: 1px solid var(--ds-line); background: var(--ds-surface); color: var(--ds-muted);
  font: 700 10.5px/1 'Geist', sans-serif;
}
.ds-chan--live { border-color: rgba(59,130,246,.45); background: rgba(59,130,246,.12); color: #1d4ed8; }
.ds-chan--bad { border-color: var(--ds-bad); background: var(--ds-bad-bg); color: var(--ds-bad); }
/* Allegro w barwach Allegro: pomarańczowy kwadrat z białym „a”, jak ich ikona (decyzja właściciela 2026-10-08;
   ten sam znak co DsIcons.Allegro). MUSI stać po `.ds-chan` — ta sama waga selektora, więc wygrywa późniejszy
   (wcześniej stał przed nim i ikona rysowała się szarą literą). */
.ds-chan--allegro {
  border-color: #ff5a00; background: #ff5a00; color: #fff;
  font: 800 11.5px/1 'Geist', 'Arial', sans-serif; text-transform: lowercase; user-select: none;
}
/* Oferta zakończona / wycofana / odrzucona: kanał był, ale dziś nic tam nie wisi. */
.ds-chan--off { opacity: .4; filter: grayscale(.4); }
/* Kanał w liście Sprzedaży: znak + nazwa. */
.ds-chan-name { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.ds-attention-filter { color: var(--ds-bad); gap: 6px; }
.ds-attention-filter.is-active { border-color: var(--ds-bad); background: var(--ds-bad-bg); }
/* Pasek statusu w panelu rekordu. */
.ds-life-strip {
  display: flex; flex-direction: column; gap: 6px; padding: 10px 20px;
  border-bottom: 1px solid var(--ds-line); background: var(--ds-bg); flex-shrink: 0;
}
.ds-life-strip-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 13px; }
.ds-life-strip .ds-life-stage { font-size: 14px; }
.ds-life-strip .ds-life-detail { font-size: 12.5px; white-space: normal; overflow: visible; }
.ds-life-strip-attention { display: flex; align-items: flex-start; gap: 6px; font-size: 12.5px; color: var(--ds-bad); }
.ds-life-strip-sales { color: var(--ds-ink2); }
.ds-life-strip-sales a { color: var(--ds-accent); font-weight: 600; text-decoration: none; }
.ds-life-strip-sales a:hover { text-decoration: underline; }
/* Arkusz filtrów na telefonie: podstatus zaraz za chipem swojego etapu, z lżejszym obrysem. */
.ds-chip--child { border-style: dashed; }
.ds-stock-filter.is-active { border-color: var(--ds-warn); background: var(--ds-warn-bg); }

/* Zakładka „Ruchy”. */
.ds-stock-tile { padding: 12px 14px; background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px; }
.ds-stock-tile--main { border-color: var(--ds-ink); }
.ds-stock-tile-k { font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ds-muted); }
.ds-stock-tile--main .ds-stock-tile-k { color: var(--ds-ink); }
.ds-stock-tile-n { font-size: 26.5px; font-weight: 600; line-height: 1.2; }
.ds-stock-tile-s { font-size: 12.5px; color: var(--ds-muted); }
.ds-stock-discrepancy {
  padding: 14px 16px; border-radius: 8px;
  background: var(--ds-warn-bg); border: 1px solid #f5d77a; color: #713f12;
}
.ds-stock-form {
  display: flex; flex-direction: column; gap: 10px; padding: 14px 16px;
  border: 1px solid var(--ds-line); border-radius: 8px; background: var(--ds-bg);
}
.ds-stock-kind {
  display: inline-block; padding: 0 6px; border-radius: 4px;
  font-family: 'Geist Mono', monospace; font-size: 11.5px; font-weight: 500;
  background: var(--ds-line2); color: var(--ds-ink2);
}
.ds-stock-doclink {
  border: 0; background: none; padding: 0; font: inherit; font-family: 'Geist Mono', monospace; font-size: 12.5px;
  color: var(--ds-ink2); cursor: pointer;
  text-decoration: underline; text-decoration-color: var(--ds-muted2); text-underline-offset: 2px;
}
.ds-stock-doclink:hover { color: var(--ds-ink); }

/* Rejestr „Ruchy magazynowe” — okno dokumentu z sekcją importu. Szersze niż okno samego dokumentu,
   bo niesie drugą tabelę (wiersze importu ze statusem i treścią z listy). */
.ds-modal--register { width: min(960px, calc(100vw / var(--ds-zoom) - 32px)); }

/* Okno połączenia duplikatu — decyzja o stanie: dodać sztuki czy ustawić stan (remanent). Konflikt z
   wysyłkami od poprzedniego remanentu dostaje ramkę ostrzeżenia i wymaga zaznaczenia potwierdzenia. */
.ds-merge-stock {
  display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; padding: 10px 12px;
  border: 1px solid var(--ds-line); border-radius: 6px; font-size: 13px; color: var(--ds-ink2);
}
.ds-merge-stock-title { font-weight: 600; color: var(--ds-ink); }
.ds-merge-stock-facts { color: var(--ds-muted); font-size: 12.5px; }
.ds-merge-stock-option { display: flex; align-items: center; gap: 8px; cursor: pointer; flex-wrap: wrap; }
.ds-merge-stock-conflict {
  padding: 8px 10px; border: 1px solid var(--ds-warn); background: var(--ds-warn-bg); border-radius: 6px;
  font-size: 12.5px; line-height: 1.45;
}

/* Pusty stan z jedną czynnością — pusty magazyn, sprzedaż bez połączenia. */
.ds-stock-empty {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  padding: 48px 24px; text-align: center; color: var(--ds-muted); font-size: 14.5px;
}

/* Pulpit: rozbieżności do decyzji — link, bo liczba bez drogi do tego, co liczy, jest ślepym zaułkiem. */
.ds-stock-banner {
  display: flex; align-items: center; gap: 10px; margin-bottom: 16px; padding: 10px 14px;
  border-radius: 8px; background: var(--ds-warn-bg); border: 1px solid #f5d77a; color: #713f12;
  font-size: 14px; text-decoration: none;
}
.ds-stock-banner:hover { border-color: var(--ds-warn); }

/* ── Sprzedaż: lista zamówień i teczka ─────────────────────────────────────
   Kolor niesie termin nadania (czerwony = po terminie, żółty = ≤ 24 h), a nie stan: stan jest pigułką,
   termin jest tym, co przy zaniedbaniu kosztuje ocenę sprzedawcy. */
.ds-so { display: flex; flex-direction: column; gap: 12px; min-height: 0; }

/* ── Kafelki zadań: jedna kolorystyka (decyzja właściciela 2026-09-25) ─────
   Pulpit, Sprzedaż i Magazyn — ta sama reguła (TaskTileTones): nic nie czeka = zielony z kreską zamiast
   zera na delikatnie zielonym tle, czeka w terminie = niebieski na jasnoniebieskim, czeka i termin
   wysyłki minął = czerwony na jasnoczerwonym. Stoi PO definicjach kafelków, żeby tło tonu wygrało
   z ich białym tłem przy tej samej specyficzności. */
.ds-task-clear   { background: var(--ds-ok-bg);   border-color: rgba(34,197,94,.45); }
.ds-task-waiting { background: var(--ds-info-bg); border-color: rgba(37,99,235,.35); }
.ds-task-late    { background: var(--ds-bad-bg);  border-color: rgba(220,38,38,.4); }
.ds-task-clear:hover   { border-color: var(--ds-ok); }
.ds-task-waiting:hover { border-color: var(--ds-info); }
.ds-task-late:hover    { border-color: var(--ds-bad); }
.ds-task-decision { background: var(--ds-wait-bg); border-color: var(--ds-wait-line); }
.ds-task-decision:hover { border-color: var(--ds-warn); }
.ds-task-clear   .ds-stock-tile-n { color: var(--ds-ok-soft); }
.ds-task-waiting .ds-stock-tile-n { color: var(--ds-info-ink); }
.ds-task-late    .ds-stock-tile-n { color: var(--ds-bad); }
.ds-task-clear   .ds-stock-tile-s { color: var(--ds-ok-soft); }
.ds-task-late    .ds-stock-tile-s { color: var(--ds-bad); }

/* ── Pulpit (Prototyp A) ───────────────────────────────────────────────────
   Dwie sekcje (Zamówienia, Magazyn) po trzy kafelki. Kafelek jest BIAŁY — stan niesie pigułka w rogu,
   a przy „po terminie” dodatkowo czerwona liczba i ramka. Kolorowe tło kafelków (dawny pulpit) przy
   sześciu naraz robiło z ekranu tęczę, w której czerwony nie wyróżniał się niczym. Przycisk na dole to
   czynność, nie miejsce („Pakuj”), i trzyma się dołu kafelka, żeby rząd przycisków był jedną linią. */
.ds-subheader-date { font-size: 14.5px; color: var(--ds-muted); }
.ds-h2 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -.01em; }
.ds-dash-group { display: flex; flex-direction: column; gap: 10px; }
/* Kafelki pulpitu: najwyżej 4 w wierszu (decyzja właściciela 2026-10-12), węziej 3, potem 2. */
.ds-grid-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 1200px) { .ds-grid-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ds-tile {
  display: flex; flex-direction: column; gap: 12px; min-width: 0;
  padding: 12px 14px; border: 1px solid var(--ds-line); border-radius: 8px;
}
/* Po tonach kafelków zadań (.ds-task-*), żeby ich kolorowe tło nie wygrało kolejnością. */
.ds-tile, .ds-tile:is(.ds-task-clear, .ds-task-waiting, .ds-task-late, .ds-task-decision) { background: var(--ds-surface); }
.ds-tile--bad, .ds-tile--bad:hover { border-color: #fecaca; }
.ds-tile-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.ds-tile-k { font-size: 15.5px; font-weight: 600; line-height: 1.3; }
.ds-tile-n { font-size: 36.5px; font-weight: 600; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.ds-task-late .ds-tile-n { color: var(--ds-late); }
.ds-tile > .ds-btn { margin-top: auto; width: 100%; }
@media (max-width: 900px) { .ds-grid-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Kafelek-filtr nad rejestrem (Prototyp A, .tiles4/.tbtn) — Faktury. Wybrany: gruba ramka zamiast tła. */
.ds-tiles4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.ds-tbtn {
  display: flex; flex-direction: column; gap: 6px; text-align: left; min-width: 0;
  padding: 14px 16px; background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px;
  font: inherit; color: inherit; cursor: pointer;
}
.ds-tbtn:hover { border-color: var(--ds-muted2); }
.ds-tbtn.is-on, .ds-tbtn.is-on:hover { border: 2px solid var(--ds-ink); padding: 13px 15px; }
.ds-tbtn-k { font-size: 14.5px; font-weight: 600; }
.ds-tbtn > b { font-size: 28.5px; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.ds-tbtn > b.is-late { color: var(--ds-late); }
.ds-tbtn > .ds-muted { font-size: 13px; }
@media (max-width: 900px) { .ds-tiles4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Pasek ostrzeżenia nad treścią (Prototyp A, .alert). Kolor = stan; przycisk dosunięty do prawej. */
.ds-alert {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 10px 8px 12px; border-radius: 8px; font-size: 14.5px;
}
.ds-alert > svg { flex-shrink: 0; }
.ds-alert > .ds-btn { margin-left: auto; }
.ds-alert--late { background: var(--ds-late-bg); border: 1px solid #fecaca; color: #7f1d1d; }
.ds-alert--wait { background: var(--ds-wait-bg); border: 1px solid var(--ds-wait-line); color: #422006; }
.ds-alert--todo { background: var(--ds-todo-bg); border: 1px solid #bfdbfe; color: #1e3a8a; }
/* Oferty Allegro spoza DS w „Decyzjach” Magazynu: nagłówek z przyciskiem i lista ofert pod nim. */
.ds-outside { align-items: flex-start; }
.ds-outside-body { flex: 1; min-width: 0; }
.ds-outside-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ds-outside-head > :last-child { margin-left: auto; }
.ds-outside-list { margin: 8px 0 0; padding: 0 0 0 18px; display: grid; gap: 4px; }
.ds-outside-list a { color: inherit; font-weight: 600; }
.ds-outside-meta { display: block; font-size: 12px; opacity: .8; overflow-wrap: anywhere; }
.ds-so-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* Lista w białej ramce z paskiem liczby i plakietek na górze — ten sam układ co lista w Magazynie. */
.ds-so-listbox { background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px; overflow: hidden; }
.ds-so-listbox .ds-so-scroll { border: none; border-radius: 0; }
.ds-so-info { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: flex-start; }
.ds-so-sync { display: inline-flex; align-items: center; gap: 6px; }
.ds-so-sync-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ds-ok); }
.ds-so-sync-dot--bad { background: var(--ds-bad); }
.ds-so-scroll { overflow-x: auto; background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px; }
/* Lista zamówień Prototypu A: kolumny wybiera menu „Kolumny”, więc szerokość minimalna to zestaw
   „Magazynier” (~900 px); przy „Kierowniku” tabela dostaje suwak poziomy w swojej ramce (.ds-tbl). */
.ds-so-table { min-width: 900px; }
/* W panelu klienta: bez kolumny „Kupujący” — mieści się w poszerzonym panelu bez przewijania w bok. */
.ds-so-table--embedded { min-width: 780px; }
.ds-so-table tbody tr { cursor: pointer; }
.ds-so-table tbody tr:hover td { background: var(--ds-sel); }
/* Zaznaczenie checkboxem to TŁO (README §4.2 — belka na krawędzi mówi „otwarty”). */
.ds-row.is-selected td { background: #f1f5f9; }
.ds-so-content { display: flex; gap: 10px; align-items: center; min-width: 0; }
.ds-so-content > div:last-child { min-width: 0; flex: 1; }
.ds-so-l2 { font-size: 12.5px; color: var(--ds-muted); }
.ds-so-flags { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.ds-so-flag {
  display: inline-block; padding: 0 6px; border-radius: 4px; font-size: 11.5px;
  background: var(--ds-line2); color: var(--ds-ink2);
}
.ds-so-flag--warn { background: var(--ds-warn-bg); color: var(--ds-warn-ink); }
.ds-so-due { font-weight: 500; }
.ds-so-due--overdue { color: var(--ds-bad); font-weight: 600; }
.ds-so-due--today { color: var(--ds-warn-ink); }

/* Teczka zamówienia = panel produktu z Magazynu: wewnątrz szarego obszaru pod paskiem strony (host
   daje kontener position:relative), szerokość w stylu elementu (750 px / poszerzony), to samo tło. */
.ds-so-shell { position: absolute; inset: 0; z-index: 200; display: flex; outline: none; }
.ds-so-backdrop { flex: 1; background: rgba(0, 0, 0, .12); }
.ds-so-folder {
  flex-shrink: 0; min-height: 0; max-width: 100%;
  background: var(--ds-surface); border-left: 1px solid var(--ds-line);
  display: flex; flex-direction: column; box-shadow: -6px 0 24px rgba(0, 0, 0, .18);
  transition: width 160ms ease;
}
.ds-so-iconbtn { background: none; border: none; cursor: pointer; color: var(--ds-muted); padding: 2px; line-height: 0; }
.ds-so-iconbtn:hover { color: var(--ds-ink); }
/* Kontener, w którym teczka się wysuwa — ten sam układ, który ma lista w Magazynie. */
.ds-so-host { flex: 1; min-height: 0; position: relative; display: flex; flex-direction: column; }
.ds-so-host > .ds-content { flex: 1; }
.ds-so-strip {
  display: flex; flex-direction: column; gap: 8px; padding: 12px 20px; flex-shrink: 0;
  background: var(--ds-bg); border-bottom: 1px solid var(--ds-line);
}

.ds-so-confirm {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 10px 12px;
  border-radius: 8px; background: var(--ds-warn-bg); border: 1px solid var(--ds-warn); color: var(--ds-warn-ink); font-weight: 500;
}

.ds-so-message { padding: 10px 12px; border-radius: 8px; background: var(--ds-warn-bg); border: 1px solid var(--ds-warn); }
.ds-so-sec {
  display: flex; flex-direction: column; gap: 4px; padding: 0 14px 12px;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 10px; overflow: hidden;
}
.ds-so-sec-k { font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ds-muted); margin-bottom: 4px; }
/* Nagłówek karty: pierwszy napis sekcji staje się belką z ikoną na całą szerokość. */
.ds-so-sec > .ds-so-sec-k:first-child {
  display: flex; align-items: center; gap: 8px; margin: 0 -14px 8px; padding: 9px 14px;
  background: var(--ds-line2); border-bottom: 1px solid var(--ds-line); color: var(--ds-ink2); font-size: 12px; font-weight: 700;
}
.ds-so-sec-icon { flex-shrink: 0; color: var(--ds-muted); }
/* Kolejny krok — jednolicie szara karta (decyzja właściciela 2026-09-25: bez zielonej i czarnej belki). */
.ds-so-next {
  display: flex; flex-direction: column; gap: 4px; padding: 10px 12px;
  background: var(--ds-line2); border: 1px solid var(--ds-line); border-radius: 8px;
}
.ds-so-next-k { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ds-muted); }
.ds-so-next-title { font-size: 15.5px; font-weight: 600; line-height: 1.35; }
.ds-so-next .ds-inv-row { margin-top: 6px; }
/* Rzecz do zrobienia w danej zakładce (przypisać produkt, wystawić fakturę, zaznaczyć „Wysłane”). */
.ds-so-todo { padding: 7px 10px; border-radius: 6px; background: var(--ds-warn-bg); color: var(--ds-warn-ink); font-size: 13px; }
/* Sekcje w ciele teczki — bez kart z belką: mały nagłówek i wiersze, jak w panelu produktu. */
.ds-so-folder .ds-so-sec { padding: 0; border: none; border-radius: 0; background: none; overflow: visible; gap: 6px; }
.ds-so-folder .ds-so-sec > .ds-so-sec-k:first-child {
  margin: 0 0 4px; padding: 0; background: none; border: none;
  font-size: 11px; font-weight: 600; letter-spacing: .04em; color: var(--ds-muted);
}
/* Zwrot zostaje obramowaną kartą — to decyzja do podjęcia, nie dane do przeczytania. */
.ds-so-folder .ds-so-return { padding: 10px 12px; border: 1px solid var(--ds-line); border-radius: 8px; }
.ds-so-row {
  display: flex; align-items: flex-start; gap: 8px; padding: 7px 9px;
  border: 1px solid var(--ds-line); border-radius: 6px; background: var(--ds-bg);
}
.ds-so-row-k { flex: 0 0 35.2%; min-width: 0; font-size: 12.5px; line-height: 18px; color: var(--ds-ink2); font-weight: 500; }
.ds-so-row-v { flex: 1; min-width: 0; font-size: 14px; line-height: 18px; overflow-wrap: anywhere; }
.ds-so-reminders { margin: 6px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 4px; }
.ds-so-reminders li { padding: 6px 10px; border-radius: 6px; background: var(--ds-warn-bg); color: var(--ds-warn-ink); font-size: 13px; }
.ds-so-facts { display: grid; grid-template-columns: 130px minmax(0, 1fr); gap: 6px 14px; margin: 0; }
.ds-so-facts dt { color: var(--ds-muted); font-size: 13px; }
.ds-so-facts dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.ds-so-bad { color: var(--ds-bad); font-weight: 500; }
.ds-so-subhead { margin-top: 8px; font-size: 12px; font-weight: 600; color: var(--ds-muted); text-transform: uppercase; letter-spacing: .05em; }
.ds-so-parcel { display: flex; flex-direction: column; gap: 2px; padding: 4px 0; }
.ds-so-line { display: flex; gap: 10px; align-items: flex-start; padding: 8px 0; border-bottom: 1px dashed var(--ds-line2); }
.ds-so-line:last-child { border-bottom: none; }
.ds-so-line--unlinked { box-shadow: inset 2px 0 0 var(--ds-warn); padding-left: 8px; }
/* Zwrot w teczce (etap 7) — na górze, w neutralnej ramce także przy „do decyzji”: bez pomarańczowej obwódki
   i żółtej belki (decyzja właściciela 2026-10-13, jak w Zwrotach Magazynu). */
.ds-so-return-line { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; padding: 6px 0; border-bottom: 1px dashed var(--ds-line2); }
.ds-so-return-qty { display: flex; flex-direction: column; gap: 2px; font-size: 12px; color: var(--ds-muted); }
.ds-so-return-qty input { width: 72px; }
/* Niezaznaczona pozycja zwrotu „nie wróciła” — wyszarzona, do odczytu (decyzja właściciela 2026-10-12). */
.ds-so-return-line--off { opacity: .5; }
/* Lista zwrotów Magazynu: treść komórki nie wychodzi poza kolumnę — inaczej poziomy suwak pojawia się zawsze. */
.ds-ret-table td { overflow: hidden; }
.ds-ret-table td .ds-pill { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.ds-ret-table td .ds-mono, .ds-ret-table td .ds-prod-code { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Ilości od magazyniera w wierszu pozycji — do odczytu, obok pól decyzji sprzedawcy (2026-10-12). */
.ds-so-return-qty--ro b { display: block; min-width: 72px; padding: 6px 0; color: var(--ds-ink); font-weight: 600; }
/* Rozwinięcie wiersza listy Sprzedaży: ocena magazynu i miniatury zdjęć zwrotu. */
.ds-so-row-return { display: flex; flex-direction: column; gap: 3px; margin-top: 3px; }
.ds-so-row-return-ev { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.ds-so-row-return-ev .ds-ret-thumb { width: 28px; height: 28px; }
.ds-flag--ok { color: var(--ds-done); }
.ds-so-return-check { width: 16px; height: 16px; margin-top: 3px; align-self: flex-start; cursor: pointer; }
.ds-inv-row--end { justify-content: flex-end; }
.ds-so-return-cost { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ds-muted); }
.ds-so-return-cost input { width: 90px; }
.ds-so-return-reject, .ds-so-return-refund { display: flex; flex-direction: column; gap: 6px; padding: 8px 0; }
.ds-so-return-inspection { display: flex; flex-direction: column; gap: 4px; padding: 6px 0; }
/* Zakładka „Zwroty” Magazynu (decyzje właściciela 2026-10-11/12): tabela jak lista produktów i karta zwrotu
   w panelu rekordu — tak samo na komputerze i na telefonie przy otwartej paczce. */
.ds-ret-empty { padding: 32px 0; text-align: center; color: var(--ds-muted); }
/* Paczka zwrotna (2026-10-12): gdzie jest — śledzenie przewoźnika, zapasowo status zwrotu Allegro (ReturnParcelBadge). */
.ds-ret-parcel { display: flex; align-items: center; gap: 5px; margin-top: 4px; font-size: 12px; line-height: 1.3; color: var(--ds-idle); }
.ds-ret-parcel-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.ds-ret-parcel--done { color: var(--ds-done); font-weight: 600; }
.ds-ret-parcel--todo { color: var(--ds-todo); font-weight: 600; }
.ds-ret-parcel--late { color: var(--ds-late); font-weight: 600; }
/* „W drodze” bez żółtego (decyzja właściciela 2026-10-12): zwroty mają kolory reszty Magazynu — neutralny tekst. */
.ds-ellipsis { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-ret-card { display: flex; flex-direction: column; gap: 8px; max-width: 760px; width: 100%; }
.ds-ret-sec { display: flex; flex-direction: column; gap: 8px; padding: 10px 0; border-top: 1px solid var(--ds-line2); }
.ds-ret-sec-title { font-size: 12px; font-weight: 600; color: var(--ds-muted); text-transform: uppercase; letter-spacing: .05em; }
.ds-ret-verdicts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ds-ret-verdict-btn { min-height: 48px; font-size: 15px; justify-content: center; }
.ds-ret-verdict-btn--on { border: 2px solid var(--ds-done); background: var(--ds-done-bg); color: var(--ds-done); }
.ds-ret-verdict-btn--warn { border-color: var(--ds-wait); background: var(--ds-wait-bg); color: var(--ds-wait); }
.ds-ret-verdict { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ds-ret-photo-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.ds-ret-photo-actions .ds-btn { cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.ds-ret-thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 8px; }
.ds-ret-thumbs--small { grid-template-columns: repeat(auto-fill, minmax(64px, 84px)); }
.ds-ret-thumb { display: block; aspect-ratio: 1; border-radius: 6px; overflow: hidden; background: var(--ds-line2); }
.ds-ret-thumb img { width: 100%; height: 100%; object-fit: cover; }
.ds-ret-note { padding: 6px 8px; border-radius: 6px; background: var(--ds-line2); }
.ds-ret-note-text { white-space: pre-wrap; }
.ds-so-line-img { width: 44px; height: 44px; flex-shrink: 0; border-radius: 6px; background: var(--ds-line2); overflow: hidden; }
.ds-so-line-img img { width: 100%; height: 100%; object-fit: contain; }
.ds-so-line-name { font-weight: 500; }
.ds-so-unlinked { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; font-size: 13px; color: var(--ds-warn-ink); }
.ds-so-unlinked > .ds-btn { align-self: flex-start; }
.ds-so-link-picker { display: flex; flex-direction: column; gap: 4px; color: var(--ds-ink); }
.ds-so-cand {
  display: flex; align-items: center; gap: 10px; text-align: left; min-width: 0;
  padding: 6px 8px; border: 1px solid var(--ds-line); border-radius: 6px; background: var(--ds-surface); font: inherit; cursor: pointer;
}
.ds-so-cand-img { width: 36px; height: 36px; flex-shrink: 0; border-radius: 5px; background: var(--ds-line2); overflow: hidden; }
.ds-so-cand-img img { width: 100%; height: 100%; object-fit: contain; }
.ds-so-cand-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ds-so-cand:hover { border-color: var(--ds-ink); }
.ds-so-totals { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
.ds-so-totals > div { display: flex; justify-content: space-between; }
.ds-so-total { font-weight: 600; font-size: 15.5px; }
.ds-so-address { margin-top: 4px; line-height: 1.45; }
.ds-so-timeline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.ds-so-timeline li { display: flex; gap: 8px; flex-wrap: wrap; }

/* ── Sprzedaż: zamówienie ręczne, kartoteka klientów ──────────────────────────────────────────── */
.ds-btn-danger { border-color: var(--ds-bad); color: var(--ds-bad); }
.ds-btn-danger:hover:not(:disabled) { background: var(--ds-bad-bg); }

.ds-mo { max-width: 860px; display: flex; flex-direction: column; gap: 14px; padding-bottom: 24px; }
.ds-mo-sec { background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 10px; padding: 16px 18px; }
/* Tytuł karty (Prototyp A, .h3): 15 px, półgruby — nie etykieta kapitalikami. */
.ds-mo-sec > .ds-so-sec-k:first-child { font-size: 15px; font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--ds-ink); margin-bottom: 10px; }
.ds-mo-picker { position: relative; max-width: 460px; margin-bottom: 10px; }
.ds-mo-cands { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; max-height: 260px; overflow-y: auto; }
.ds-mo-lines { width: 100%; }
.ds-mo-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; }
.ds-mo-field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 500; color: var(--ds-ink2); }
.ds-mo-field--wide { grid-column: 1 / -1; }
/* Pole formularza (Prototyp A, .inp): białe, 36 px, ramka kontrolki, niebieski fokus. `.ds-input` to ta
   sama kontrolka pod krótszą nazwą — nowe ekrany używają jej, stare zostają przy .ds-mo-input.
   --need: pole, które człowiek MA uzupełnić (niebieska ramka „twoja kolej”); --err: wartość odrzucona. */
.ds-mo-input, .ds-input {
  font: inherit; font-size: 14px; font-weight: 400; color: var(--ds-ink); background: var(--ds-surface);
  min-height: 36px; border: 1px solid var(--ds-control-line); border-radius: 6px; padding: 0 9px; outline: none;
}
textarea.ds-mo-input, textarea.ds-input { padding: 8px 10px; resize: vertical; }
.ds-mo-input:focus, .ds-input:focus { border-color: var(--ds-todo); box-shadow: 0 0 0 3px #dbeafe; }
.ds-mo-input:disabled, .ds-input:disabled { background: var(--ds-hover); color: var(--ds-muted); }
.ds-input--need { border-color: var(--ds-todo); background: #fbfdff; }
.ds-input--err { border-color: var(--ds-late); }
.ds-mo-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* ── Kartoteka klientów: panel klienta ──────────────────────────────────────── */
.ds-cu-table { min-width: 760px; }
/* Karta produktu (Prototyp A): wiersz pola „etykieta — wartość” w Ogólnych i galeria w Zdjęciach. */
.ds-prow {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 7px 9px;
  border: 1px solid var(--ds-line); border-radius: 6px; background: var(--ds-bg);
}
.ds-prow-l { min-width: 0; font-size: 12.5px; color: var(--ds-ink2); font-weight: 500; }
.ds-cellbtn--need { border-color: var(--ds-todo); background: #fbfdff; }
.ds-photos { display: flex; flex-wrap: wrap; gap: 10px; }
.ds-photo {
  position: relative; width: 150px; height: 150px; padding: 0; overflow: hidden; cursor: pointer;
  border: 1px solid var(--ds-line); border-radius: 8px; background: #fff;
}
.ds-photo img { width: 100%; height: 100%; object-fit: contain; }
.ds-photo:hover { border-color: var(--ds-muted2); }
.ds-photo-main {
  position: absolute; left: 6px; bottom: 6px; padding: 1px 6px; border-radius: 4px;
  background: rgba(10,10,10,.72); color: #fff; font-size: 11px;
}
/* „Dodaj” i „Wyszukaj zdjęcia” — kwadratowe kafle na końcu galerii karty produktu (jak przed 2026-09-29). */
.ds-photo--add {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  border-style: dashed; background: var(--ds-surface); color: var(--ds-muted); font: inherit; font-size: 13px;
  text-align: center;
}
.ds-photo--add:hover { color: var(--ds-ink); }
.ds-photo--add:disabled, .ds-photo--add.is-busy { cursor: wait; opacity: .6; }
.ds-photo--add.is-busy { pointer-events: none; }
/* ✕ usuwający zdjęcie — widoczny po najechaniu na kafel (i zawsze na ekranie dotykowym, gdzie nie ma najechania). */
.ds-photo-wrap { position: relative; border-radius: 8px; }
.ds-photo-wrap[draggable="true"] .ds-photo { cursor: grab; }
.ds-photo-wrap.is-dragging { opacity: .35; }
.ds-photo-wrap.is-drop { outline: 2px dashed var(--ds-ink); outline-offset: 2px; }
.ds-photo-del {
  position: absolute; top: 6px; right: 6px; z-index: 2; width: 24px; height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center; border: none; border-radius: 50%;
  background: rgba(10,10,10,.6); color: #fff; cursor: pointer; opacity: 0; transition: opacity 120ms, background 120ms;
}
.ds-photo-wrap:hover .ds-photo-del, .ds-photo-del:focus-visible { opacity: 1; }
.ds-photo-del:hover { background: var(--ds-bad); }
.ds-photo-del:disabled { cursor: wait; }
@media (hover: none) { .ds-photo-del { opacity: 1; } }
.ds-stock4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
/* Paleta koloru kółka na stronie Konto (Prototyp A). */
.ds-swatches { display: flex; gap: 8px; flex-wrap: wrap; }
.ds-swatch {
  width: 32px; height: 32px; border-radius: 50%; border: 3px solid transparent; cursor: pointer; padding: 0;
  outline: 2px solid #fff; outline-offset: -5px; color: #fff; font-size: 11px; font-weight: 700;
}
.ds-swatch.on { border-color: var(--ds-ink); }
.ds-diag-recalc { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 12px 0; }
.ds-cu-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 12.5px; margin-top: 2px; }
.ds-cu-sec { padding: 16px 20px; border-bottom: 1px solid var(--ds-line); display: flex; flex-direction: column; gap: 10px; }
.ds-cu-sec-head { display: flex; align-items: center; gap: 10px; }
.ds-cu-sec-head .ds-so-sec-k { margin: 0; }
.ds-cu-facts { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 6px 14px; font-size: 14.5px; margin: 0; }
.ds-cu-facts dt { color: var(--ds-muted); font-size: 13px; }
.ds-cu-facts dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.ds-cu-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.ds-cu-stat { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid var(--ds-line); border-radius: 8px; }
.ds-cu-stat b { font-size: 17.5px; font-weight: 600; }
/* Portal platformy (/platforma): liczby nad listą firm i dane firmy w karcie — te same kafelki co karta klienta. */
.ds-pf-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 12px; }
.ds-pf-fields { display: grid; grid-template-columns: 170px minmax(0, 1fr); gap: 8px 16px; margin: 4px 0 0; font-size: 14px; }
.ds-pf-fields dt { color: var(--ds-muted); font-weight: 400; }
.ds-pf-fields dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 640px) { .ds-pf-fields { grid-template-columns: minmax(0, 1fr); gap: 2px; } .ds-pf-fields dd { margin-bottom: 8px; } }
/* Sesja wsparcia: baner nad stroną firmy (nie do schowania) i dziennik wejść platformy. */
.ds-support-banner { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 16px; background: var(--ds-wait-bg); color: var(--ds-wait); border-bottom: 1px solid var(--ds-wait-line); font-size: 13.5px; }
.ds-support-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ds-wait); flex: none; }
.ds-support-text { flex: 1; min-width: 0; }
.ds-support-meta { color: var(--ds-ink2); margin-left: 4px; }
.ds-pf-log .ds-table td { vertical-align: top; }
.ds-pf-writes { margin: 0; padding: 4px 0 4px 18px; display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; }
.ds-pf-form { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--ds-line2); }
.ds-pf-form-row { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin-top: 8px; }
.ds-pf-form-row label { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; color: var(--ds-muted); }
.ds-pf-grow { flex: 1; min-width: 200px; }
.ds-pf-note { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap; padding: 10px 12px; border-radius: 8px; font-size: 13.5px; margin-bottom: 12px; }
.ds-pf-note--bad { background: var(--ds-late-bg); color: var(--ds-late); }
/* Pierwsze kroki nowej firmy na Pulpicie (onboarding). */
.ds-start { background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px; padding: 14px 16px; margin-bottom: 14px; }
.ds-start-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.ds-start-title { font-size: 15px; font-weight: 600; }
.ds-start-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ds-start-step { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-top: 1px solid var(--ds-line2); font-size: 13.5px; }
.ds-start-step:first-child { border-top: 0; }
.ds-start-mark { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--ds-control-line); display: inline-flex; align-items: center; justify-content: center; font-size: 11px; flex: none; }
.ds-start-step--done .ds-start-mark { background: var(--ds-done-bg); color: var(--ds-done); border-color: var(--ds-done); }
.ds-start-step--done .ds-start-text b { color: var(--ds-muted); font-weight: 500; }
.ds-start-text { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; }

/* ── Faktury: rejestr i sekcja faktury w teczce ─────────────────────────────
   „Nie wysłano do KSeF” świeci na żółto — jedyna liczba na tym ekranie z konsekwencją prawną. */
.ds-inv-tiles { grid-template-columns: repeat(auto-fill, minmax(200px, 240px)); }
.ds-inv-ksef { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; white-space: nowrap; }
.ds-inv-ksef--ok { color: var(--ds-ok-soft); }
.ds-inv-ksef--warn { color: var(--ds-warn-ink); font-weight: 600; }
button.ds-link { background: none; border: 0; border-bottom: 1px solid var(--ds-line); padding: 0; font: inherit; cursor: pointer; }
.ds-inv-export { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.ds-inv-export-help { margin: 0; padding-left: 18px; font-size: 13px; color: var(--ds-muted); line-height: 1.5; }
.ds-inv-doc { display: flex; flex-direction: column; gap: 8px; }
.ds-inv-doc-head { font-weight: 600; }
.ds-inv-warn { padding: 8px 12px; border-radius: 6px; background: var(--ds-warn-bg); color: var(--ds-warn-ink); font-size: 14px; }
.ds-inv-bad { padding: 8px 12px; border-radius: 6px; background: var(--ds-bad-bg); color: var(--ds-bad); font-size: 14px; }
.ds-inv-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* Nabywca faktury: jawny wybór „Osoba prywatna / Firma” nad danymi i źródłem danych. */
.ds-inv-buyer { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border: 1px solid var(--ds-line); border-radius: 8px; }
.ds-inv-buyer-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.ds-inv-buyer-data { line-height: 1.45; }
/* Wysyłka z teczki (zakładka „Dostawa”): formularz paczek i „Mam już numer nadania”. Formularz to karta sekcji
   (.ds-fsec) z mocniejszą ramką — to jest czynność, którą właśnie się robi. */
.ds-ship-form { border: 1.5px solid var(--ds-ink); gap: 10px; }
.ds-ship-dims { display: grid; grid-template-columns: repeat(4, minmax(90px, 140px)); gap: 8px; align-items: end; }
.ds-ship-dims .ds-mo-field--wide { grid-column: span 2; }
.ds-ship-sent { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 8px; max-width: 720px; }
.ds-ship-parcel { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border: 1px solid var(--ds-line); border-radius: 8px; background: #fcfcfb; }
.ds-ship-parcel-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ds-ship-parcel-head .ds-btn { width: auto; flex: 0 0 auto; color: var(--ds-muted); }
/* Podgląd wielkości wg Allegro, liczony przy każdym znaku. */
.ds-ship-size { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; font-size: 13px; color: var(--ds-ink2); }
.ds-ship-size-k { font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .4px; color: var(--ds-muted2); }
.ds-ship-size--empty { color: var(--ds-muted2); }
.ds-ship-size--ok { color: var(--ds-done); }
.ds-ship-size--bad { color: var(--ds-late); font-weight: 500; }
@media (max-width: 640px) { .ds-ship-dims { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Okno „Wskaż produkt z magazynu”: pozycja zamówienia u góry, wyszukiwarka, kandydaci jako duże karty. */
.ds-link-dialog { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.ds-link-line { padding: 10px 12px; border-radius: 8px; background: var(--ds-todo-bg); display: flex; flex-direction: column; gap: 2px; }
.ds-link-line-k { font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .4px; color: var(--ds-todo); }
.ds-link-line-name { font-size: 15px; font-weight: 600; color: var(--ds-ink); }
.ds-link-cands { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; max-height: calc(52vh / var(--ds-zoom)); padding: 2px; }
.ds-link-cand {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; min-width: 0;
  padding: 10px 12px; border: 1px solid var(--ds-line); border-radius: 10px; background: var(--ds-surface);
  font: inherit; color: var(--ds-ink); cursor: pointer; transition: border-color 120ms, box-shadow 120ms, background 120ms;
}
.ds-link-cand:hover:not(:disabled) { border-color: var(--ds-ink); box-shadow: 0 2px 10px rgba(0,0,0,.06); }
.ds-link-cand:focus-visible { outline: 2px solid var(--ds-todo); outline-offset: 1px; }
.ds-link-cand:disabled { opacity: .6; cursor: default; }
.ds-link-cand-img { width: 64px; height: 64px; flex-shrink: 0; border-radius: 8px; background: var(--ds-line2); overflow: hidden; }
.ds-link-cand-img img { width: 100%; height: 100%; object-fit: contain; }
.ds-link-cand-text { flex: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ds-link-cand-name { font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.ds-link-cand-stock { flex-shrink: 0; font-size: 12.5px; font-weight: 600; padding: 3px 8px; border-radius: 999px; background: var(--ds-done-bg); color: var(--ds-done); white-space: nowrap; }
.ds-link-cand-stock.is-empty { background: var(--ds-idle-bg); color: var(--ds-muted); }
.ds-link-empty { padding: 28px 12px; text-align: center; color: var(--ds-muted); border: 1px dashed var(--ds-line); border-radius: 10px; }
.ds-inv-row .ds-mo-input { flex: 1; min-width: 220px; }
/* ── Faktury: rejestr w układzie Magazynu ─────────────────────────────────
   Jedna biała karta z licznikiem i tabelą, która przewija się w środku z przyklejonym nagłówkiem — jak lista
   produktów, a nie ds-so-scroll z Zamówień, która rośnie razem ze stroną. */
.ds-inv-listbox {
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px;
  display: flex; flex-direction: column; flex: 1; min-height: 0;
}
.ds-inv-scroll { flex: 1; min-height: 0; overflow: auto; }
.ds-inv-scroll thead { position: sticky; top: 0; z-index: 2; }

/* ── Faktury: panel faktury ──────────────────────────────────────────────── */
.ds-invd-sub { font-size: 13px; color: var(--ds-muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-invd-sep { color: var(--ds-muted2); }
.ds-invd-body { flex: 1; overflow-y: auto; min-height: 0; padding: 16px 20px; display: flex; flex-direction: column; gap: 18px; font-size: 14.5px; }
.ds-invd-sec { display: flex; flex-direction: column; gap: 6px; }
.ds-invd-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 16px; margin: 0; }
.ds-invd-facts dt { font-size: 11.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ds-muted); }
.ds-invd-facts dd { margin: 2px 0 0; }
.ds-invd-parties { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.ds-invd-party { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid var(--ds-line); border-radius: 8px; min-width: 0; overflow-wrap: anywhere; }
.ds-invd-table-wrap { overflow-x: auto; border: 1px solid var(--ds-line); border-radius: 8px; }
.ds-invd-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.ds-invd-table th {
  text-align: left; font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ds-muted); padding: 7px 10px; background: var(--ds-bg); border-bottom: 1px solid var(--ds-line);
}
.ds-invd-table td { padding: 7px 10px; border-bottom: 1px solid var(--ds-line2); vertical-align: top; }
.ds-invd-table tbody tr:last-child td { border-bottom: none; }
.ds-invd-table tfoot td { border-top: 1px solid var(--ds-line); font-weight: 600; }
.ds-invd-r { text-align: right !important; white-space: nowrap; }
.ds-invd-order { display: flex; flex-direction: column; gap: 12px; }
/* Formularz korekty: ilość zwrotu w wierszu, przełącznik dostawy, podgląd kwot z tego samego budowniczego. */
.ds-invd-click { cursor: pointer; }
.ds-invd-click:hover td { background: var(--ds-bg); }
.ds-invd-qty { width: 80px; text-align: right; padding: 4px 6px; }
.ds-invd-check { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; cursor: pointer; }
.ds-invd-preview { padding: 8px 12px; border-radius: 6px; background: var(--ds-bg); border: 1px solid var(--ds-line); font-weight: 600; }
.ds-invd-order-head { display: flex; align-items: center; gap: 8px; font-size: 15.5px; }
.ds-invd-order a.ds-btn { text-decoration: none; }
@media (max-width: 640px) {
  .ds-invd-facts, .ds-invd-parties { grid-template-columns: minmax(0, 1fr); }
}

/* ── Raport sprzedaży (/raport-sprzedazy) ──
   Następca maila z panelu sprzedaży: te same sekcje i te same kolory statusów, w tokenach aplikacji. */
.ds-sr { display: flex; flex-direction: column; gap: 16px; }
.ds-sr-meta { font-size: 13px; color: var(--ds-muted); }
.ds-sr-warn { color: var(--ds-warn); font-weight: 600; }
.ds-sr-muted { font-size: 14px; color: var(--ds-muted); }
.ds-sr-note { font-size: 12.5px; color: var(--ds-muted); line-height: 1.6; margin-top: -6px; }
.ds-sr-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.ds-sr-tile {
  background: var(--ds-surface); border: 1px solid var(--ds-line);
  border-radius: 10px; padding: 14px 16px; min-width: 0;
}
/* Prototyp A (.tile): etykieta zwykłym tekstem 14,5 px, nie kapitalikami. */
.ds-sr-tile-label { font-size: 14.5px; font-weight: 500; color: var(--ds-ink2); }
.ds-sr-tile-value { font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.3px; padding-top: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-sr-tile-note { font-size: 12.5px; color: var(--ds-muted); padding-top: 5px; }
.ds-sr-card { background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 10px; padding: 16px 18px; min-width: 0; }
.ds-sr-card-head { margin-bottom: 12px; }
.ds-sr-card-title { font-size: 16.5px; font-weight: 600; }
.ds-sr-card-sub { font-size: 14px; color: var(--ds-muted); padding-top: 3px; }
.ds-sr-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.ds-sr-minis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-top: 14px; }
.ds-sr-minis--money { grid-template-columns: repeat(2, 1fr); margin: 0 0 12px; }
.ds-sr-mini { background: var(--ds-line2, #f5f5f5); border: 1px solid var(--ds-line); border-radius: 8px; padding: 10px 12px; min-width: 0; }
.ds-sr-mini-value { font-size: 17.5px; font-weight: 700; font-variant-numeric: tabular-nums; padding-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-sr-pill { display: inline-block; border-radius: 999px; padding: 5px 13px; font-size: 14px; font-weight: 600; border: 1px solid; }
.ds-sr-pill--accelerating { color: var(--ds-ok); background: var(--ds-ok-bg); border-color: var(--ds-ok); }
.ds-sr-pill--slowing { color: var(--ds-bad); background: var(--ds-bad-bg); border-color: var(--ds-bad); }
.ds-sr-pill--stable { color: var(--ds-muted); background: var(--ds-surface); border-color: var(--ds-line); }
.ds-sr-bar-row { display: grid; grid-template-columns: 130px 1fr 60px; align-items: center; gap: 10px; padding: 4px 0; font-size: 14px; }
.ds-sr-bar-row:has(.ds-sr-bar-label--wide) { grid-template-columns: minmax(120px, 1.3fr) 1fr 110px; }
.ds-sr-bar-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.ds-sr-bar-track { height: 14px; background: #f4f4f5; border-radius: 4px; }
/* Słupki w jednym kolorze (Prototyp A, HBar): jedna seria, kolor „informacja”, nie ocena. */
.ds-sr-bar { height: 14px; border-radius: 4px; min-width: 2px; background: var(--ds-todo) !important; }
.ds-sr-bar-value { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.ds-sr-bar-value--money { font-weight: 600; white-space: nowrap; }
.ds-sr-alert { padding: 9px 12px; border-radius: 6px; font-size: 14px; margin-bottom: 10px;
  background: var(--ds-warn-bg); color: var(--ds-warn); border: 1px solid var(--ds-warn); }
.ds-sr-alert--bad { background: var(--ds-bad-bg); color: var(--ds-bad); border-color: var(--ds-bad); }
.ds-sr-empty { padding: 20px; text-align: center; color: var(--ds-muted); font-size: 14px; }
.ds-sr-rank { display: inline-block; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 6px;
  font-size: 12px; font-weight: 700; background: var(--ds-line2, #f5f5f5); color: var(--ds-muted); }
.ds-sr-rank--top { background: #ff5a00; color: #fff; }
@media (max-width: 900px) {
  .ds-sr-grid2 { grid-template-columns: 1fr; }
  .ds-sr-tiles, .ds-sr-minis { grid-template-columns: repeat(2, 1fr); }
}
.ds-invd-iconbtn { display: inline-flex; align-items: center; padding-left: 8px; padding-right: 8px; }

/* ── Wiadomości z Allegro (Wiadomosci.razor, ChatPanel.razor) ─────────────────────────────────
   Dwa panele: lista rozmów i rozmowa. Dymki kupującego po lewej na białym, nasze po prawej na
   ciemnym — ta sama czerń, co przycisk główny, bo to NASZ głos. Lista przewija się sama, pole
   wiadomości stoi na dole i rośnie z tekstem (chat.js). */
.ds-nav-count--info { background: var(--ds-info-bg); color: var(--ds-info-ink); border: 1px solid #bfdbfe; }

.ds-chat-page {
  flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(280px, 340px) 1fr;
  background: var(--ds-bg);
}
.ds-chat-inbox {
  display: flex; flex-direction: column; min-height: 0; border-right: 1px solid var(--ds-line); background: var(--ds-surface);
}
.ds-chat-inbox-head { display: flex; flex-direction: column; gap: 10px; padding: 14px 14px 10px; border-bottom: 1px solid var(--ds-line2); }
.ds-chat-inbox-title { display: flex; align-items: center; gap: 8px; font-size: 15.5px; font-weight: 600; }
/* .ds-search niesie margines i max-width paska narzędzi Magazynu — w nagłówku skrzynki pole stoi w kolumnie
   równo z przełącznikiem pod nim, więc oba zdejmujemy (inaczej pole jechało 16 px w prawo i wystawało). */
.ds-chat-inbox-head .ds-search { flex: none; width: auto; max-width: none; margin: 0; }
.ds-chat-filters { display: flex; gap: 6px; }
.ds-chat-filter {
  padding: 4px 10px; border-radius: 999px; border: 1px solid var(--ds-line); background: var(--ds-surface);
  font: inherit; font-size: 12.5px; color: var(--ds-muted); cursor: pointer;
}
.ds-chat-filter:hover { color: var(--ds-ink2); border-color: var(--ds-muted2); }
.ds-chat-filter.is-on { background: var(--ds-ink); border-color: var(--ds-ink); color: #fff; }
.ds-chat-inbox-error { margin: 8px 12px 0; padding: 6px 10px; border-radius: 6px; background: var(--ds-warn-bg); color: var(--ds-warn-ink); font-size: 12.5px; }
.ds-chat-threads { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 0; }
.ds-chat-thread {
  display: flex; gap: 10px; align-items: center; width: 100%; padding: 10px 14px; text-align: left;
  background: none; border: none; border-left: 3px solid transparent; cursor: pointer; font: inherit; color: inherit;
}
.ds-chat-thread:hover { background: var(--ds-hover); }
/* Prototyp A (.thread): wątki rozdzielone linią, wybrany na szaro — bez paska z lewej. */
.ds-chat-thread { border-left: 0; border-bottom: 1px solid var(--ds-line2); padding: 12px 16px; gap: 12px; }
.ds-chat-thread.is-selected { background: #ececeb; }
/* Tytuł skrzynki stoi na pasku strony (ds-chat-top); w nagłówku skrzynki zostaje na telefonie. */
body:not(.ds-mobile) .ds-chat-inbox-title { display: none; }
.ds-chat-thread-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ds-chat-thread-top, .ds-chat-thread-bottom { display: flex; align-items: center; gap: 8px; min-width: 0; }
.ds-chat-thread-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14.5px; font-weight: 500; }
.ds-chat-thread-time { flex-shrink: 0; font-size: 12px; color: var(--ds-muted); font-family: 'Geist Mono', monospace; }
.ds-chat-thread-preview { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; color: var(--ds-muted); }
.ds-chat-thread-login { color: var(--ds-muted2); }
.ds-chat-thread-topic { display: flex; align-items: center; gap: 5px; min-width: 0; font-size: 12.5px; color: var(--ds-ink2); }
.ds-chat-thread-topic svg { flex-shrink: 0; color: var(--ds-muted); }
.ds-chat-thread-topic span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-chat-thread.is-unread .ds-chat-thread-name { font-weight: 700; }
.ds-chat-thread.is-unread .ds-chat-thread-preview { color: var(--ds-ink2); font-weight: 500; }
.ds-chat-thread.is-unread .ds-chat-thread-time { color: var(--ds-info-ink); font-weight: 600; }
.ds-chat-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ds-todo); flex-shrink: 0; }
.ds-chat-more { padding: 10px 14px 16px; display: flex; justify-content: center; }

.ds-chat-avatar {
  position: relative; width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0; overflow: hidden;
  display: inline-grid; place-items: center; color: #fff; font-size: 15.5px; font-weight: 600;
}
.ds-chat-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: var(--ds-surface); }
.ds-chat-avatar--sm { width: 28px; height: 28px; font-size: 12px; }

.ds-chat-main { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.ds-chat-center {
  flex: 1; display: flex; flex-direction: column; gap: 10px; align-items: center; justify-content: center;
  padding: 28px 20px; text-align: center; color: var(--ds-muted); font-size: 14px;
}
.ds-chat-center--error { color: var(--ds-bad); }
.ds-chat-empty svg { color: var(--ds-muted2); }
.ds-chat-pick { height: 100%; }

.ds-chat { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--ds-bg); }
.ds-chat-head {
  display: flex; align-items: center; gap: 10px; padding: 10px 16px; min-height: 58px;
  background: var(--ds-surface); border-bottom: 1px solid var(--ds-line);
}
.ds-chat-back { display: none; background: none; border: none; padding: 6px; margin-left: -6px; cursor: pointer; color: var(--ds-ink2); }
.ds-chat-head-id { min-width: 0; }
.ds-chat-head-name { font-size: 15.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-chat-head-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; font-size: 12.5px; color: var(--ds-muted); }

.ds-chat-state { margin: 16px; padding: 14px 16px; border-radius: 10px; background: var(--ds-warn-bg); border: 1px solid #f5d77a; display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.ds-chat-state-title { font-weight: 600; color: var(--ds-warn-ink); }
.ds-chat-state-body { font-size: 14px; color: var(--ds-ink2); line-height: 1.45; }

.ds-chat-list { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 16px 16px; display: flex; flex-direction: column; gap: 2px; overscroll-behavior: contain; }
.ds-chat-older, .ds-chat-start { display: flex; justify-content: center; padding: 4px 0 10px; }
.ds-chat-start { font-size: 12px; color: var(--ds-muted2); }
.ds-chat-older-btn { background: none; border: 1px solid var(--ds-line); border-radius: 999px; padding: 4px 12px; font: inherit; font-size: 12.5px; color: var(--ds-muted); cursor: pointer; }
.ds-chat-older-btn:hover { color: var(--ds-ink2); background: var(--ds-surface); }
.ds-chat-day { display: flex; justify-content: center; margin: 12px 0 6px; }
.ds-chat-day span { font-size: 12px; color: var(--ds-muted); background: var(--ds-line2); padding: 2px 10px; border-radius: 999px; }

.ds-chat-row { display: flex; flex-direction: column; margin-top: 8px; }
.ds-chat-row--cont { margin-top: 2px; }
.ds-chat-row--them { align-items: flex-start; }
.ds-chat-row--me { align-items: flex-end; }
.ds-chat-bubble {
  max-width: min(72%, 560px); padding: 8px 12px 6px; border-radius: 14px; font-size: 14.5px; line-height: 1.45;
  overflow-wrap: anywhere; box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
.ds-chat-row--them .ds-chat-bubble { background: #f1f1f0; border: 0; border-bottom-left-radius: 4px; box-shadow: none; }
.ds-chat-row--me .ds-chat-bubble { background: var(--ds-ink); color: #fff; border-bottom-right-radius: 4px; }
.ds-chat-row--them.ds-chat-row--cont .ds-chat-bubble { border-top-left-radius: 6px; }
.ds-chat-row--me.ds-chat-row--cont .ds-chat-bubble { border-top-right-radius: 6px; }
.ds-chat-bubble--pending { opacity: .72; }
.ds-chat-bubble--failed { opacity: 1; background: var(--ds-bad-bg) !important; color: var(--ds-ink) !important; border: 1px solid var(--ds-bad); }
.ds-chat-bubble--blocked { background: var(--ds-bad-bg) !important; color: var(--ds-ink) !important; border: 1px dashed var(--ds-bad) !important; }
.ds-chat-subject { font-weight: 600; margin-bottom: 2px; }
.ds-chat-text { white-space: pre-wrap; }
.ds-chat-note { font-size: 12.5px; opacity: .75; font-style: italic; }
.ds-chat-refs { margin-top: 4px; font-size: 12.5px; opacity: .85; }
.ds-chat-row--me .ds-chat-refs a { color: #fff; }
.ds-chat-meta { display: flex; justify-content: flex-end; align-items: center; gap: 6px; margin-top: 2px; font-size: 11.5px; opacity: .6; font-family: 'Geist Mono', monospace; }
.ds-chat-meta-bad { color: var(--ds-bad); opacity: 1; font-family: inherit; }
.ds-chat-bubble--failed .ds-chat-meta, .ds-chat-bubble--blocked .ds-chat-meta { opacity: 1; }
.ds-chat-sending { display: inline-flex; align-items: center; gap: 4px; }
.ds-chat-row--me .ds-chat-sending .ds-spinner { border-color: #fff; border-top-color: transparent; }
.ds-chat-failed-actions { display: flex; gap: 6px; margin-top: 4px; }
.ds-chat-failed-actions .ds-btn { padding: 3px 10px; font-size: 12.5px; }

.ds-chat-files { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 2px; }
.ds-chat-image { display: block; border-radius: 10px; overflow: hidden; line-height: 0; border: 1px solid var(--ds-line); background: var(--ds-surface); }
.ds-chat-image img { max-width: 220px; max-height: 220px; object-fit: cover; }
.ds-chat-file {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 5px 9px; border-radius: 8px;
  background: var(--ds-line2); color: var(--ds-ink2); border: 1px solid var(--ds-line); font-size: 13px; text-decoration: none;
}
a.ds-chat-file:hover { border-color: var(--ds-muted2); }
.ds-chat-row--me .ds-chat-file { background: rgba(255,255,255,.12); color: #fff; border-color: rgba(255,255,255,.2); }
.ds-chat-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.ds-chat-file-size { font-size: 11.5px; opacity: .7; white-space: nowrap; }
.ds-chat-file--off { opacity: .7; }
.ds-chat-file--staged { background: var(--ds-surface); }
.ds-chat-file--staged button { background: none; border: none; cursor: pointer; color: var(--ds-muted); padding: 2px; line-height: 0; }
.ds-chat-file--staged button:hover { color: var(--ds-bad); }

.ds-chat-newbelow {
  position: absolute; left: 50%; bottom: 96px; transform: translateX(-50%); z-index: 3;
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; border-radius: 999px; border: none;
  background: var(--ds-info); color: #fff; font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
  box-shadow: 0 4px 14px rgba(37,99,235,.3);
}

.ds-chat-compose { padding: 8px 16px 10px; background: var(--ds-surface); border-top: 1px solid var(--ds-line); }
.ds-chat-staged { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.ds-chat-input-error { margin-bottom: 6px; font-size: 13px; color: var(--ds-bad); }
.ds-chat-composer {
  display: flex; align-items: flex-end; gap: 6px; padding: 5px 5px 5px 6px;
  border: 1px solid var(--ds-line); border-radius: 18px; background: var(--ds-bg); transition: border-color 120ms, background 120ms;
}
.ds-chat-composer:focus-within { border-color: var(--ds-ink2); background: var(--ds-surface); }
.ds-chat-input {
  flex: 1; min-height: 30px; max-height: 180px; resize: none; border: none; outline: none; background: transparent;
  font: inherit; font-size: 14.5px; line-height: 1.45; padding: 5px 4px; color: var(--ds-ink);
}
.ds-chat-attach {
  position: relative; width: 32px; height: 32px; flex-shrink: 0; border-radius: 50%; display: grid; place-items: center;
  color: var(--ds-muted); cursor: pointer;
}
.ds-chat-attach:hover { background: var(--ds-line2); color: var(--ds-ink2); }
.ds-chat-attach input[type=file] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; }
.ds-chat-send {
  width: 32px; height: 32px; flex-shrink: 0; border-radius: 50%; border: none; display: grid; place-items: center;
  background: var(--ds-ink); color: #fff; cursor: pointer; transition: opacity 120ms, transform 120ms;
}
.ds-chat-send:hover:not(:disabled) { transform: scale(1.06); }
.ds-chat-send:disabled { opacity: .25; cursor: default; }
.ds-chat-hint { display: flex; justify-content: space-between; gap: 8px; margin-top: 5px; padding: 0 6px; font-size: 11.5px; color: var(--ds-muted2); }

/* W teczce zamówienia: rozmowa w karcie zakładki, o stałej wysokości — przewija się ona, nie teczka. */
.ds-so-sec--chat { padding: 0; height: min(620px, calc(100vh / var(--ds-zoom) - 300px)); min-height: 380px; }
.ds-chat--compact .ds-chat-list { padding: 10px 12px 12px; }
.ds-chat--compact .ds-chat-compose { padding: 8px 10px 8px; }
.ds-chat--compact .ds-chat-bubble { max-width: 82%; }
.ds-chat--compact .ds-chat-newbelow { bottom: 86px; }
/* ── Szuflada produktu: materiał z magazynu (telefon, ekran „Uzupełnij”) ──────────────────────── */
.ds-whm { padding: 7px 9px; border: 1px solid var(--ds-line); border-radius: 6px; background: var(--ds-bg); font-size: 12.5px; }
.ds-whm-title { font-size: 11.5px; font-weight: 600; letter-spacing: .3px; text-transform: uppercase; color: var(--ds-muted); margin-bottom: 5px; }
.ds-whm-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 5px; color: var(--ds-ink2); }
.ds-whm-ok { color: var(--ds-ok); }
.ds-whm-photos { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 6px; }
.ds-whm-photos img { width: 64px; height: 64px; object-fit: cover; border-radius: 5px; border: 1px solid var(--ds-line2); background: #fff; }
.ds-whm-note { margin-top: 4px; padding-left: 7px; border-left: 2px solid var(--ds-line); color: var(--ds-ink); white-space: pre-wrap; }
.ds-whm-note-meta { display: block; font-size: 11.5px; color: var(--ds-muted); }

/* ══ Wspólne komponenty Prototypu A (DsPop, DsColFilter, DsFilterLine, DsColumnsMenu, DsPageTabs) ═════════
   Wzorzec: docs/ui-handoff/prototyp-a/src/p01_extra.css (v8). Popover rysuje się w warstwie nad całą
   aplikacją (SectionOutlet w MainLayout), więc z-index jest liczony od <body>, nie od tabeli. */
.ds-popx {
  z-index: 80; background: var(--ds-surface); border: 1px solid var(--ds-control-line); border-radius: 8px;
  box-shadow: 0 14px 36px rgba(0,0,0,.16), 0 2px 6px rgba(0,0,0,.06);
  padding: 6px; display: flex; flex-direction: column; overflow: auto; font-size: 14px;
}
.ds-optlist { display: flex; flex-direction: column; gap: 1px; }
.ds-optx {
  display: flex; align-items: center; gap: 8px; min-height: 36px; padding: 6px 10px 6px 6px;
  border-radius: 7px; cursor: pointer; color: var(--ds-ink2); line-height: 1.3;
}
.ds-optx:hover, .ds-optx.hi { background: var(--ds-hover); }
.ds-optx.on { color: var(--ds-ink); font-weight: 600; }
.ds-optx.dis { opacity: .55; cursor: default; }
.ds-optx:focus-visible { outline: 2px solid var(--ds-todo); outline-offset: -2px; }
.ds-optx input[type="checkbox"] { width: 16px; height: 16px; margin: 0 2px; accent-color: var(--ds-ink); }
/* Ptaszek zajmuje miejsce zawsze — inaczej wybór przesuwałby tekst w bok (README §4.1, reguła 6). */
.ds-optx-t { width: 18px; flex-shrink: 0; display: grid; place-items: center; color: var(--ds-todo); }
.ds-optx-l { flex: 1; min-width: 0; }
.ds-optx-sub { display: block; font-size: 12px; font-weight: 400; color: var(--ds-muted); }
.ds-optx-n { margin-left: auto; font-size: 12px; font-weight: 400; color: var(--ds-muted2); font-variant-numeric: tabular-nums; }
.ds-cf-h { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 8px 8px; }
.ds-cf-t {
  font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ds-muted2);
  padding: 8px 8px 4px;
}
.ds-cf-g + .ds-cf-g { border-top: 1px solid var(--ds-line2); margin-top: 4px; }
.ds-cf-search { padding: 0 8px 6px; }
.ds-cf-search .ds-input { width: 100%; min-height: 34px; font-size: 13px; }

/* Nagłówek z lejkiem: nazwa, strzałka i lejek w jednej linii. Aktywny filtr barwi cały nagłówek. */
.ds-thi { display: inline-flex; align-items: center; gap: 4px; }
th.is-filtered { color: var(--ds-todo); }
.ds-table th[style*="text-align:right"] .ds-thi { justify-content: flex-end; width: 100%; }
.ds-cfb {
  width: 24px; height: 24px; border-radius: 6px; border: 1px solid transparent; background: transparent;
  display: inline-grid; place-items: center; color: #a3a3a3; cursor: pointer; margin-left: 2px; padding: 0;
}
.ds-cfb:hover { background: var(--ds-hover); color: var(--ds-ink2); }
.ds-cfb.open { border-color: var(--ds-control-line); background: var(--ds-surface); color: var(--ds-ink2); }
.ds-cfb.on { background: var(--ds-todo-bg); color: var(--ds-todo); }

/* Linia nad tabelą: liczba i chipy aktywnych filtrów. */
/* Pasek postępu nad listą — przeładowanie po zmianie zakładki albo filtra; stara lista zostaje pod nim. */
.ds-progress { position: absolute; left: 0; right: 0; top: 0; height: 3px; overflow: hidden; z-index: 3; border-radius: 8px 8px 0 0; }
.ds-progress::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: -40%; width: 40%;
  background: var(--ds-todo); animation: ds-progress 1.1s ease-in-out infinite;
}
@keyframes ds-progress { to { left: 100%; } }
.ds-fline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 30px; font-size: 13px; }
.ds-fline-extra { margin-left: auto; }
.ds-fchip {
  display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 4px 0 10px; border-radius: 14px;
  background: var(--ds-todo-bg); color: var(--ds-todo); font-size: 12.5px; font-weight: 500;
}
.ds-fchip button {
  width: 22px; height: 22px; border-radius: 11px; border: none; background: transparent; color: inherit;
  cursor: pointer; display: inline-grid; place-items: center; padding: 0;
}
.ds-fchip button:hover { background: rgba(29,78,216,.12); }

/* Siatka administratora (Koszty AI, Diagnostyka) w układzie Magazynu: biała ramka, przewijanie wewnątrz, nagłówek
   przyklejony do góry. Pod wykresem i kafelkami ramka ma minimum wysokości — przy niskim oknie przewija się cała
   strona, a lista dalej pokazuje sensowny kawałek, zamiast ścisnąć się do dwóch wierszy. */
.ds-grid-box {
  position: relative; display: flex; flex-direction: column; flex: 1 0 420px; min-height: 0;
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 8px;
}
/* Wypełnia resztę okna (rodzic musi mieć wolne miejsce — .ds-content z flex:1); przy niskim oknie nie zapada się
   poniżej kilku wierszy, tylko przewija stronę. */
.ds-grid-box--fill { flex: 1 1 0; min-height: 260px; }
.ds-grid-scroll { flex: 1; min-height: 0; overflow: auto; border-radius: 8px; }
.ds-grid-head { position: sticky; top: 0; z-index: 2; }
/* Fakty wywołania AI w panelu rekordu: etykieta i wartość w dwóch kolumnach, pary obok siebie. */
.ds-aicall-facts {
  display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr);
  gap: 6px 14px; margin: 0; font-size: 13.5px;
}
.ds-aicall-facts dt { color: var(--ds-muted); }
.ds-aicall-facts dd { margin: 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }

/* Menu „Kolumny”: ikonka w ostatnim nagłówku, zestawy ról nad listą kolumn. */
th.ds-colth { text-align: right; padding-right: 6px; }
.ds-colbtn { min-height: 28px; width: 28px; padding: 0; color: var(--ds-muted); }
.ds-colbtn:hover, .ds-colbtn[aria-expanded="true"] { color: var(--ds-ink); background: #efefed; }
.ds-colmenu-presets { display: flex; gap: 6px; padding: 0 8px 8px; flex-wrap: wrap; border-bottom: 1px solid var(--ds-line2); margin-bottom: 4px; }

/* Tabela w ramce, rosnąca na wysokość ekranu (.tbl.grow) — lista nie kończy się w połowie okna. */
.ds-tbl { border: 1px solid var(--ds-line); border-radius: 8px; background: var(--ds-surface); overflow-x: auto; }
.ds-tbl.grow { flex: 1 0 auto; min-height: 240px; }
.ds-tbl > table { min-width: 900px; }
.ds-empty { padding: 40px; text-align: center; color: var(--ds-muted); }

/* Pasek czynności na zaznaczonych (.bulk): ciemny, przyklejony do dołu obszaru treści. */
/* Pasek zaznaczenia: jasny, w tonie „todo” (decyzja właściciela 2026-09-29 — czarny był zbyt ciężki);
   przyciski zostają zwykłymi białymi .ds-btn. */
.ds-bulk {
  position: sticky; bottom: 0; margin-top: auto; z-index: 5;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 16px; background: var(--ds-todo-bg); color: var(--ds-ink);
  border: 1px solid #bfdbfe; border-radius: 8px; box-shadow: 0 -2px 12px rgba(0,0,0,.06);
}
.ds-bulk-note { color: var(--ds-muted); }

/* Flaga pod nazwą w wierszu (.flag): żółta „ktoś ma to zrobić”, czerwona awaria, niebieska wiadomość. */
.ds-flag { display: flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--ds-wait); margin-top: 3px; }
.ds-flag--bad { color: var(--ds-late); }
.ds-flag--todo { color: var(--ds-todo); }
.ds-flag--idle { color: var(--ds-muted); }
.ds-thumb {
  width: 40px; height: 40px; border-radius: 6px; border: 1px solid var(--ds-line); flex-shrink: 0;
  background: #f1f1f0; display: grid; place-items: center; font-size: 10.5px; color: var(--ds-muted2); overflow: hidden;
}
.ds-thumb img { width: 100%; height: 100%; object-fit: contain; background: #fff; }
.ds-tag {
  display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: 5px;
  font-size: 12px; font-weight: 500; background: var(--ds-idle-bg); color: var(--ds-idle); border: none;
}

/* ── Karty zamówień i produktów na telefonie (Prototyp A, .mcard) ───────────────────────────────────── */
.ds-mcards { display: flex; flex-direction: column; gap: 10px; }
.ds-mcard {
  background: var(--ds-surface); border: 1px solid var(--ds-line); border-radius: 12px; padding: 14px;
  display: flex; flex-direction: column; gap: 10px; cursor: pointer;
}
.ds-mcard-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ds-mcard-due { margin-left: auto; font-size: 12.5px; font-weight: 600; color: var(--ds-todo); }
.ds-mcard-due.is-late { color: var(--ds-late); }
.ds-mcard-body { display: flex; gap: 10px; align-items: center; min-width: 0; }

/* Wiersze „klucz — wartość” (Prototyp A, .kv) i lista punktów w oknie potwierdzenia. */
.ds-stack { display: flex; flex-direction: column; gap: 6px; }
.ds-kv { display: flex; justify-content: space-between; gap: 24px; padding: 5px 0; border-bottom: 1px solid var(--ds-line2); font-size: 14px; }
.ds-kv > span:first-child { color: var(--ds-muted); }
.ds-kv > span:last-child { text-align: right; }
.ds-points { margin: 12px 0 0; padding-left: 18px; line-height: 1.55; font-size: 14px; }

/* ══ Panel rekordu Prototypu A (DsDrawer, DsStageBar, kolejny krok, sekcje formularza, oś czasu) ══════════
   Wzorzec: .drawer.wide, .dhead, .steps.compact, .nextwrap, .fsec, .tl w docs/ui-handoff/prototyp-a. Panel
   leży nad CAŁĄ aplikacją (decyzja 2026-09-28) — tło przykrywa też menu boczne. Przewija się cały panel:
   nagłówek z krokiem przewija się razem z treścią, bo bywa wysoki (formularz paczek, zwrot). */
.ds-dshade { position: fixed; inset: 0; background: rgba(10,10,10,.22); z-index: 190; }
.ds-pdrawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(780px, calc(100vw / var(--ds-zoom))); z-index: 200;
  background: var(--ds-surface); box-shadow: -8px 0 32px rgba(0,0,0,.16);
  display: flex; flex-direction: column; overflow-y: auto; outline: none;
  padding-top: env(safe-area-inset-top, 0px);
}
.ds-pdrawer--wide { width: min(1080px, calc(100vw / var(--ds-zoom))); }
.ds-dhead { background: #fafafa; border-bottom: 1px solid var(--ds-line); padding-bottom: 10px; flex-shrink: 0; }
/* Samą górę panelu rekordu zajmuje pasek etapów z ✕ obok; tytuł stoi pod nim (decyzja właściciela 2026-09-29). */
/* Pozioma kreska oddziela pasek etapów od nazwy produktu (decyzja właściciela 2026-09-29). */
.ds-dh-top {
  display: flex; align-items: center; gap: 12px; padding: 8px 12px 8px 20px; background: var(--ds-surface);
  border-bottom: 1px solid var(--ds-line);
}
.ds-dh-top .ds-steps { flex: 1; min-width: 0; padding: 0; }
.ds-dh-top + .ds-dh { padding-top: 14px; }
.ds-dh {
  display: flex; align-items: center; gap: 12px; padding: 18px 20px 16px;
  background: var(--ds-surface); border-bottom: 1px solid var(--ds-line); margin-bottom: 10px;
}
.ds-dh-title { flex: 1; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; min-width: 0; }
/* Nazwa w jednej linii z wielokropkiem — plakietki etapu i kanałów stoją obok niej i nie zawijają się pod spód. */
.ds-dh-title { flex-wrap: nowrap; }
.ds-dh-title h2 {
  margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -.01em;
  min-width: 0; flex: 0 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ds-dh-title > :not(h2) { flex-shrink: 0; white-space: nowrap; }
.ds-head-acts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
/* Opiekun w Ogólnych karty produktu: awatar + imię, menu wyboru pod spodem. */
.ds-dh-owner { position: relative; display: inline-flex; align-items: center; gap: 6px; min-height: 36px; font-size: 13.5px; }
.ds-dhead .ds-alert { margin: 10px 20px 0; }
/* Nagłówek bez niczego pod tytułem (karta produktu bez ostrzeżenia) kończy się na tytule — bez pustego szarego
   paska między nazwą a zakładkami (decyzja właściciela 2026-09-30). Odstęp dostaje dopiero to, co pod tytułem stoi. */
.ds-dhead { padding-bottom: 0; }
.ds-dhead > .ds-dh:last-child { margin-bottom: 0; border-bottom: 0; }
.ds-dhead > :last-child:not(.ds-dh) { margin-bottom: 10px; }
.ds-icon {
  width: 40px; height: 40px; border-radius: 6px; border: none; background: transparent; flex-shrink: 0;
  display: grid; place-items: center; color: #404040; cursor: pointer; padding: 0;
}
.ds-icon:hover { background: var(--ds-hover); }
/* Ikona w linii tekstu (kopiuj numer zamówienia Allegro). */
.ds-icon--sm { width: 28px; height: 28px; }

/* Pasek etapów: belka + nazwa etapu (bez podstatusu). Bieżący etap wypełnia się na niebiesko do --part.
   „past” = etap zaliczony, ale już nieaktualny (wyprzedany przy wiszącej ofercie): wyszarzona belka i napis. */
.ds-steps { display: flex; gap: 10px; padding: 0 20px; }
.ds-step { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.ds-bar { height: 4px; border-radius: 2px; background: var(--ds-line); }
.ds-bar--done { background: var(--ds-done); }
.ds-bar--bad { background: var(--ds-late); }
.ds-bar--past { background: color-mix(in srgb, var(--ds-muted2) 45%, var(--ds-line)); }
.ds-bar--now { background: linear-gradient(90deg, var(--ds-todo) var(--part, 40%), var(--ds-line) var(--part, 40%)); }
.ds-stepline { font-size: 14px; color: var(--ds-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-stepline b { font-size: 15px; font-weight: 600; color: var(--ds-muted2); }
.ds-stepline--done b { color: var(--ds-ink); }
.ds-stepline--now b { color: var(--ds-todo); }
.ds-stepline--bad b { color: var(--ds-late); }
.ds-stepline--past b { color: var(--ds-muted2); }
.ds-stepbtn { cursor: pointer; border-radius: 4px; }
.ds-stepbtn:hover b, .ds-stepbtn:focus-visible b { text-decoration: underline; text-underline-offset: 2px; }

/* Kolejny krok pod paskiem etapów. Ramka atramentu = teraz; żółta = decyzja; czerwona = awaria. */
.ds-nextwrap { margin: 10px 20px 0; display: flex; flex-direction: column; gap: 8px; }
.ds-nextwrap:empty { display: none; }
.ds-next-meta { font-size: 12.5px; }
.ds-next-meta.is-late { color: var(--ds-late); font-weight: 600; }
.ds-nextbody { border: 1.5px solid var(--ds-ink); border-radius: 8px; padding: 10px 12px; display: flex; flex-direction: column; gap: 8px; background: var(--ds-surface); }
.ds-nextbody--wait { border-color: var(--ds-wait); }
.ds-nextbody--bad { border-color: var(--ds-late); }
.ds-nextline { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; font-size: 13px; padding: 4px 0; }
.ds-row-btns { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ds-confirm .ds-row-btns { justify-content: flex-end; }

/* Potwierdzenie czynności nieodwracalnej (Prototyp A, Confirm): panel w treści, nie okno nad nią. */
.ds-confirm {
  background: var(--ds-surface); border: 1px solid var(--ds-control-line); border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0,0,0,.16); padding: 16px 18px; display: flex; flex-direction: column; gap: 10px;
}
.ds-confirm h4 { margin: 0; font-size: 16px; }
.ds-confirm ul { margin: 0; padding-left: 18px; font-size: 14px; line-height: 1.55; }

/* Zakładki panelu wprost pod nagłówkiem — na tle nagłówka. */
.ds-pdrawer .ds-mdetail-tabs { padding: 0 20px; margin: 0; background: #fafafa; flex-shrink: 0; }

/* Treść zakładki: karty sekcji (.fsec) i dwie kolumny kart (.ogrid). */
.ds-pform { padding: 12px 20px 18px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ds-fsec, .ds-fplain {
  border: 1px solid #ededeb; border-radius: 8px; padding: 10px 12px; background: var(--ds-surface);
  display: flex; flex-direction: column; gap: 8px; min-width: 0; scroll-margin-top: 90px;
}
.ds-fsec h3 { margin: 0; font-size: 14.5px; font-weight: 600; }
.ds-fsec-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.ds-fsec-head h3 { flex: 1; }
.ds-fsec.flash { box-shadow: 0 0 0 3px #bfdbfe; }
/* Pole formularza z etykietą nad spodem (.fld) i rząd czterech pól (.f4) — karta produktu, „Ogólne”. */
.ds-fld { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ds-fld > label { font-size: 12px; font-weight: 500; color: var(--ds-ink2); }
.ds-fld-hint { font-size: 12.5px; color: var(--ds-muted); }
.ds-f4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
/* Notatki produktu na karcie w Magazynie (wewnętrzne / dla klienta) — na całą szerokość karty, jedna pod drugą. */
.ds-product-notes { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.ds-product-notes .ds-textarea { width: 100%; resize: vertical; }
/* Połowa dawnej wysokości (decyzja 2026-10-12): 3 wiersze .ds-textarea. */
.ds-product-notes textarea.ds-textarea { height: calc((3 * 1.6em + 22px) / 2); min-height: 0; }
/* Cennik dostawy i opiekun w jednym rzędzie Ogólnych. */
.ds-f2 { display: grid; grid-template-columns: minmax(0, 420px) minmax(0, 1fr); gap: 8px 16px; align-items: start; }
/* Wartość edytowana w miejscu (cena, cennik, producent) wygląda jak pole, choć klik dopiero je otwiera. */
.ds-input-btn { display: flex; align-items: center; width: 100%; min-width: 0; text-align: left; cursor: pointer; }
.ds-input-btn:hover { border-color: var(--ds-muted2); }
.ds-input-select {
  padding-right: 28px; background-repeat: no-repeat; background-position: right 9px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 16 16' fill='none' stroke='%23737373' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
}
/* ── Lista rozwijana: DsSelect i lista Producentów — jeden wygląd w całej aplikacji (decyzja właściciela
   2026-09-29: „taka sama jak w Producentach”). Natywny <select> rozwijał się w oknie systemu, którego nie da się
   ostylować. Panel (.ds-dd) i pozycje (.ds-dd-opt) są wspólne; DsSelect stawia panel w DsPop (warstwa nad
   aplikacją, nad panelem rekordu i oknami), lista Producentów — pod polem (.ds-dd--inline). */
.ds-sel { display: flex; align-items: center; gap: 8px; }
.ds-sel-v { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-sel-ph { color: var(--ds-muted); }
.ds-sel-chev { flex-shrink: 0; color: var(--ds-muted); transition: transform .12s; }
.ds-sel[aria-expanded="true"]:not(.ds-sel--cell) { border-color: var(--ds-todo); box-shadow: 0 0 0 3px #dbeafe; }
.ds-sel[aria-expanded="true"] .ds-sel-chev { transform: rotate(180deg); }
.ds-sel:disabled { cursor: not-allowed; }
/* W komórce siatki: etykieta jak cena — ramka dopiero pod kursorem albo przy rozwiniętej liście. */
.ds-sel--cell { flex-direction: row; width: 100%; }
.ds-sel--cell[aria-expanded="true"] { border-color: var(--ds-control-line); background: var(--ds-hover); }
.ds-dd {
  background: var(--ds-surface); border: 1px solid var(--ds-line2); border-radius: 5px;
  box-shadow: 0 6px 18px rgba(0,0,0,.13); overflow: auto; font-size: 13px;
}
.ds-popx.ds-dd { z-index: 1100; padding: 0; border-radius: 5px; box-shadow: 0 6px 18px rgba(0,0,0,.13); display: block; }
.ds-dd--inline { position: absolute; z-index: 30; top: calc(100% + 3px); left: 0; right: 0; max-height: 210px; }
.ds-dd-opt {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 9px; border: 0;
  border-bottom: 1px solid var(--ds-line); background: transparent; color: var(--ds-ink);
  font: inherit; font-size: 13px; line-height: 1.35; text-align: left; cursor: pointer;
}
.ds-dd-opt:last-child { border-bottom: 0; }
.ds-dd-opt:hover, .ds-dd-opt:focus-visible { background: var(--ds-hover); outline: none; }
.ds-dd-opt.on { font-weight: 600; }
.ds-dd-none { color: var(--ds-muted); }
.ds-dd-l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-dd-t { flex-shrink: 0; color: var(--ds-todo); }
/* Wyszukiwarka DsSelect (Searchable) stoi na górze przewijanej listy; kreska oddziela grupy (popularni przewoźnicy). */
.ds-dd-search { position: sticky; top: 0; z-index: 1; padding: 6px; background: var(--ds-surface); border-bottom: 1px solid var(--ds-line); }
.ds-dd-search .ds-input { width: 100%; min-height: 32px; font-size: 13px; }
.ds-dd-sep { height: 0; margin: 3px 9px; border-top: 1px dashed var(--ds-muted); }
.ds-dd-note { padding: 8px 9px; font-size: 12px; line-height: 1.45; color: var(--ds-muted); }
.ds-dd-note--bad { color: var(--ds-bad); }
.ds-dd-note--sub { padding: 0 9px 8px; font-size: 11.5px; line-height: 1.4; }
.ds-dd-act {
  display: block; width: 100%; padding: 7px 9px; text-align: left; border: 0; border-top: 1px solid var(--ds-line);
  background: #eff6ff; color: #1d4ed8; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.ds-dd-act:hover { background: #dbeafe; }
/* Pole z listą pod spodem (Producent): lista liczy „100%” od pola, a nie od całej kolumny formularza, w której
   pod polem bywa jeszcze podpowiedź — inaczej lista wisiała kilkanaście pikseli pod polem. */
.ds-ddwrap { position: relative; }
@media (max-width: 900px) { .ds-f4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .ds-f4, .ds-f2 { grid-template-columns: minmax(0, 1fr); } }
/* Tytuł oferty ma do 75 znaków: pole rośnie z treścią, zamiast ucinać ją na wąskim ekranie. */
#ds-product-name { field-sizing: content; min-height: 36px; }
.ds-f3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 12px; }
@media (max-width: 700px) { .ds-f3 { grid-template-columns: minmax(0, 1fr); } }
.ds-ogrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; align-items: start; }
.ds-ocol { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ds-kv--last { border-bottom: none; }
.ds-text-late { color: var(--ds-late); }
textarea.ds-notebig { min-height: calc((3 * 1.45em + 18px) * 1.75); width: 100%; }
@media (max-width: 900px) {
  .ds-ogrid { grid-template-columns: minmax(0, 1fr); }
  .ds-steps { overflow-x: auto; }
  .ds-step { min-width: 120px; }
}

/* Oś czasu historii (Prototyp A, .tl): dzień, godzina, kropka zdarzenia. */
.ds-tl { display: flex; flex-direction: column; }
.ds-tl-date {
  font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ds-muted);
  padding: 14px 0 8px; border-bottom: 1px solid var(--ds-line2); margin-bottom: 6px;
}
.ds-tl-day:first-child .ds-tl-date { padding-top: 2px; }
.ds-tl-item { display: grid; grid-template-columns: 14px 52px minmax(0, 1fr); column-gap: 6px; padding: 7px 0; position: relative; }
.ds-tl-item::before { content: ""; position: absolute; left: 4px; top: 22px; bottom: -4px; width: 1px; background: var(--ds-line); }
.ds-tl-day .ds-tl-item:last-child::before { display: none; }
.ds-tl-dot { width: 9px; height: 9px; border-radius: 50%; margin-top: 6px; background: var(--ds-done); }
.ds-tl-dot.idle { background: #a3a3a3; }
.ds-tl-dot.bad { background: var(--ds-late); }
.ds-tl-time { font-family: var(--ds-mono); font-size: 12.5px; color: var(--ds-muted); padding-top: 1px; }
.ds-tl-what { font-size: 14.5px; line-height: 1.4; }
.ds-tl-sub { font-size: 12.5px; color: #404040; margin-top: 3px; }
/* Mała lista rozwijana w wierszu parametru (26 px, jak pola obok). */
.ds-sel--sm { min-height: 26px; height: 26px; padding: 2px 6px 2px 7px; font-size: 12.5px; }

/* ── Powiadomienia o zamówieniach na stronie konta (PushNotificationsCard) ── */
.ds-push-card-text { font-size: 13px; color: var(--ds-muted); line-height: 1.55; }
.ds-push-card-detail { font-family: 'Geist Mono', monospace; font-size: 11.5px; color: var(--ds-muted2); word-break: break-word; }
.ds-push-card-note { font-size: 13px; color: var(--ds-muted2); }
.ds-push-card-ok { font-size: 13px; color: var(--ds-ok); line-height: 1.5; }
.ds-push-card-warn { font-size: 13px; color: var(--ds-bad); line-height: 1.5; }

/* ── Reklamacje (karta „Reklamacja” w teczce, 2026-10-12) ─────────────────────────────────────────── */
.ds-so-complaint { display: flex; flex-direction: column; gap: 8px; }
.ds-so-complaint--urgent { border-color: var(--ds-bad) !important; }
.ds-so-complaint--urgent > .ds-so-sec-k:first-child { background: var(--ds-bad-bg, #fdecec); color: var(--ds-bad, #b42318); }
.ds-so-complaint-chat { height: min(460px, calc(100vh / var(--ds-zoom) - 360px)); min-height: 280px; flex: none; border: 1px solid var(--ds-line); border-radius: 8px; overflow: hidden; }
.ds-so-complaint-part { border-top: 1px solid var(--ds-line); padding-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.ds-so-complaint-form { display: flex; flex-direction: column; gap: 8px; }
.ds-so-complaint-confirm { border: 1px solid var(--ds-line); border-radius: 8px; padding: 8px 12px; background: var(--ds-bg); }
.ds-radio { display: flex; align-items: center; gap: 6px; font-size: 13px; padding: 2px 0; }
.ds-chat-bubble--allegro { background: var(--ds-warn-bg); }
.ds-so-complaint-row { color: var(--ds-ink); }
.ds-so-complaint-row--urgent { color: var(--ds-bad, #b42318); font-weight: 600; }

/* ── Oferty produktu (wspólna pula, 2026-10-14): tabela bez przewijania i okno nowej oferty / zestawu ────────────── */
.ds-offers { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 12.5px; }
.ds-offers th { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--ds-muted);
  text-align: left; padding: 0 8px 8px; border-bottom: 1px solid var(--ds-line); white-space: nowrap; }
.ds-offers td { padding: 10px 8px; border-bottom: 1px solid var(--ds-line); vertical-align: top; }
.ds-offers tr.is-gone td { color: var(--ds-muted); }
.ds-offers .num { text-align: right; font-family: var(--ds-mono); white-space: nowrap; }
.ds-offers-title { font-weight: 500; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.ds-offers-sub { margin-top: 2px; font-family: var(--ds-mono); font-size: 11px; color: var(--ds-muted); }
.ds-offers-main { display: inline-block; margin-right: 6px; padding: 0 6px; border-radius: 3px; background: var(--ds-ink); color: #fff;
  font-size: 10.5px; line-height: 16px; vertical-align: 1px; }
.ds-offers-part { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ds-offers-part .x { color: var(--ds-muted); }
.ds-offers-acts { display: flex; justify-content: flex-end; gap: 4px; }
/* Wiersz oferty rozwija szczegóły (skład, suma, stan) pod sobą. */
.ds-offers-row { cursor: pointer; }
.ds-offers-row:hover td, .ds-offers-row.is-open td { background: var(--ds-hover); }
.ds-offers-row.is-open td { border-bottom-color: transparent; }
.ds-offers-detail td { padding: 0 8px 14px; background: var(--ds-hover); }
.ds-offers-details { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 16px; align-items: start; }
@media (max-width: 760px) { .ds-offers-details { grid-template-columns: minmax(0, 1fr); } }
.ds-offers-details-parts { display: flex; flex-direction: column; gap: 6px; }
.ds-offers-details-part { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto auto; gap: 12px; align-items: center;
  padding: 8px 10px; border: 1px solid var(--ds-line); border-radius: 8px; background: var(--ds-surface); }
.ds-offers-details-num { display: flex; flex-direction: column; align-items: flex-end; font-size: 11.5px; color: var(--ds-muted); white-space: nowrap; }
.ds-offers-details-num b { font-family: var(--ds-mono); font-size: 13px; color: var(--ds-ink); }
.ds-offers-details-sum { background: var(--ds-surface); }
.ds-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; padding: 0;
  border: 1px solid var(--ds-line); border-radius: 6px; background: var(--ds-surface); color: var(--ds-ink2); cursor: pointer;
  text-decoration: none; }
.ds-icon-btn:hover { border-color: var(--ds-muted2); color: var(--ds-ink); }
.ds-icon-btn:disabled { opacity: .5; cursor: default; }
.ds-icon-btn--danger:hover { border-color: var(--ds-bad); color: var(--ds-bad); }
.ds-offers-confirm td { padding-top: 0; }
.ds-offers-confirm-box { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 12px; border-radius: 8px;
  background: var(--ds-wait-bg); color: var(--ds-wait); font-size: 12.5px; }
.ds-offers-kind { display: inline-block; margin-right: 4px; padding: 0 6px; border-radius: 3px; border: 1px solid var(--ds-line);
  font-family: 'Geist', 'Inter', -apple-system, sans-serif; font-size: 10.5px; line-height: 16px; color: var(--ds-ink2); background: var(--ds-bg); }
@media (max-width: 760px) { .ds-offers .col-price { display: none; } }

/* Odstęp i linia nad Anuluj/Wystaw: stopka okna nie ma własnego marginesu, a bez niego przyciski stały na ostatnim polu. */
.ds-oc { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 22px; font-size: 13px;
  margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--ds-line); }
@media (max-width: 860px) { .ds-oc { grid-template-columns: minmax(0, 1fr); } }
.ds-oc-h { display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px; }
.ds-oc-h b { font-size: 13px; }
.ds-oc-h span { color: var(--ds-muted); font-size: 12px; }
.ds-oc-items { display: flex; flex-direction: column; gap: 8px; counter-reset: oc; }
.ds-oc-item { display: grid; grid-template-columns: 26px 56px minmax(0, 1fr) auto auto 28px; align-items: center; gap: 12px;
  padding: 10px 12px; border: 1px solid var(--ds-line); border-radius: 10px; background: var(--ds-surface); }
.ds-oc-num { width: 26px; height: 26px; border-radius: 50%; background: var(--ds-ink); color: #fff; font-size: 12px; font-weight: 600;
  display: flex; align-items: center; justify-content: center; }
.ds-oc-thumb { width: 56px; height: 56px; border-radius: 8px; border: 1px solid var(--ds-line); background: #fff; object-fit: contain;
  display: flex; align-items: center; justify-content: center; color: var(--ds-muted2); overflow: hidden; }
.ds-oc-thumb--sm { width: 40px; height: 40px; border-radius: 6px; }
.ds-oc-name { font-weight: 500; line-height: 1.3; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.ds-oc-meta { margin-top: 3px; font-size: 11.5px; color: var(--ds-muted); }
.ds-oc-meta .bad { color: var(--ds-bad); }
.ds-oc-units { display: inline-flex; align-items: center; gap: 6px; color: var(--ds-muted); font-size: 12px; }
.ds-oc-units .ds-stepper > button { width: 30px; height: 32px; font-size: 15px; }
/* min-height z .ds-input (36 px) wygrywał z height — pole było wyższe niż − i +. Bez strzałek przeglądarki: od tego są − i +. */
.ds-oc-units .ds-input { width: 54px; height: 32px; min-height: 32px; text-align: center; padding: 0 4px; -moz-appearance: textfield; }
.ds-oc-units .ds-input::-webkit-outer-spin-button,
.ds-oc-units .ds-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ds-oc-line { min-width: 76px; text-align: right; font-family: var(--ds-mono); font-size: 12.5px; }
.ds-oc-search { position: relative; margin-top: 12px; }
.ds-oc-search > svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--ds-muted); pointer-events: none; }
.ds-oc-search > .ds-input { width: 100%; height: 40px; padding-left: 36px; font-size: 13.5px; }
.ds-oc-hits { margin-top: 6px; border: 1px solid var(--ds-line); border-radius: 10px; overflow: hidden; max-height: 300px; overflow-y: auto; }
.ds-oc-hit { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 8px 12px;
  background: var(--ds-surface); }
.ds-oc-hit + .ds-oc-hit { border-top: 1px solid var(--ds-line); }
.ds-oc-hit.is-addable { cursor: pointer; }
/* Tytuł, cennik i komunikat Allegro pod obiema kolumnami — każde pole dwa razy szersze niż kolumna boczna. */
.ds-oc-bottom { grid-column: 1 / -1; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px 22px; }
.ds-oc-bottom > .ds-oc-note { grid-column: 1 / -1; }
@media (max-width: 860px) { .ds-oc-bottom { grid-template-columns: minmax(0, 1fr); } }
.ds-oc-hit:hover { background: var(--ds-hover); }
.ds-oc-empty { padding: 14px; color: var(--ds-muted); text-align: center; font-size: 12.5px; }
.ds-oc-side { display: flex; flex-direction: column; gap: 16px; }
.ds-oc-card { padding: 14px; border: 1px solid var(--ds-line); border-radius: 10px; background: var(--ds-bg); }
.ds-oc-big { font-size: 26px; font-weight: 600; line-height: 1.1; font-family: var(--ds-mono); }
.ds-oc-row { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; padding: 3px 0; }
.ds-oc-row .v { font-family: var(--ds-mono); }
.ds-oc-row--total { margin-top: 6px; padding-top: 8px; border-top: 1px solid var(--ds-line); font-weight: 600; font-size: 14px; }
.ds-oc-field { display: flex; flex-direction: column; gap: 5px; }
.ds-oc-field > label { font-size: 12px; color: var(--ds-muted); display: flex; justify-content: space-between; }
.ds-oc-field .ds-input { height: 36px; width: 100%; }
.ds-oc-suffix { position: relative; }
.ds-oc-suffix > span { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); color: var(--ds-muted); font-size: 12.5px; }
.ds-oc-suffix > .ds-input { padding-right: 34px; text-align: right; font-family: var(--ds-mono); }
/* Komunikat Allegro niesie długie ścieżki pól bez spacji — łamane w kolumnie, nie wystające poza nią. */
.ds-oc-note { font-size: 12px; line-height: 1.4; overflow-wrap: anywhere; }
.ds-oc-note--bad { color: var(--ds-bad); }
.ds-oc-note--warn { color: var(--ds-wait); }
.ds-oc-note--ok { color: var(--ds-ok); }
