/* =========================================================
   KOPIA Z pc_admin/css/components.css — NIE EDYTUJ TEGO PLIKU
   =========================================================
   Źródło: repozytorium serwera FiveM, commit c609d474
   Odświeżenie: node scripts/sync-design.mjs <ścieżka-do-pc_admin>

   Zmiany rób PO STRONIE GRY. Tutaj i tak zostaną nadpisane przy następnej
   synchronizacji, a rozjazd systemu projektowego to dokładnie ten problem,
   który ten skrypt ma likwidować (web/README.md, punkt 3 parytetu).

   Styl WŁASNY strony (pasek boczny, belka, widoki) siedzi w site.css.
   ========================================================= */

/* =========================================================
   PC ADMIN — KOMPONENTY WSPÓŁDZIELONE
   Jedna definicja na komponent. Wcześniej te same rzeczy były
   rozsiane po sekcjach widoków (przyciski w „topbarze”, pola w „logach”,
   badge w dwóch egzemplarzach) — stąd rozjazd wyglądu między zakładkami.
   ========================================================= */

/* =========================================================
   PRZYCISKI — pole „ALT” (UI Kit EnviRP, koncept A z 28.10.2026)
   Przyciemniony kolor (tło .13), obwódka w tym kolorze (.55), biały napis,
   ikona w kolorze; pod kursorem tło .24. Nigdy pełne kolorowe wypełnienie
   i bez poświat. Kolor mówi, co przycisk robi: niebieski — zwykła akcja,
   zielony — pomoc / potwierdzenie, czerwony — kara / nieodwracalne,
   szary (ghost) — druga, mniej ważna akcja.
   ========================================================= */
.btn {
  --btn-rgb: var(--blue-rgb);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  flex: none;
  height: var(--h-ctl);
  padding: 0 var(--s-3);
  border-radius: var(--r-s);
  border: 1px solid rgba(var(--btn-rgb), .55);
  background: rgba(var(--btn-rgb), .13);
  color: var(--txt);
  font-size: var(--fs-body);
  font-weight: var(--fw-sem);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--e-in);
}
.btn i, .btn svg { font-size: var(--ic-sm); width: auto; flex: none; color: rgb(var(--btn-rgb)); }
.btn svg { width: var(--ic-sm); height: var(--ic-sm); }
.btn:hover { background: rgba(var(--btn-rgb), .24); }
/* Wciśnięcie = lekkie ugięcie z przejściem (kit: scale .98, powrót 160 ms). */
.btn:active { transform: scale(.98); }

/* 28 px ma promień 8 jak chip tej wysokości (proporcja pola ALT z kitu: 34 → 10,
   28 → 8). Do 30.10.2026 mały przycisk miał 10 px i obok chipa wyglądał na
   inny zestaw. To samo .icon-btn--sm niżej. */
.btn--sm { height: var(--h-ctl-sm); padding: 0 var(--s-2); font-size: var(--fs-dense); gap: 6px; border-radius: 8px; }
.btn--lg { height: 40px; padding: 0 var(--s-4); font-size: var(--fs-title); }
.btn--block { width: 100%; }

/* Druga akcja: bez koloru, sama obwódka. */
.btn--ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--txt-rest);
}
.btn--ghost i, .btn--ghost svg { color: var(--mut); }
.btn--ghost:hover { background: var(--panel-3); border-color: var(--line-strong); color: var(--txt); }

/* Główna akcja ekranu: to samo pole ALT, mocniej podbite — bez płyty i poświaty. */
.btn--primary { background: rgba(var(--btn-rgb), .2); font-weight: var(--fw-bold); }
.btn--primary:hover { background: rgba(var(--btn-rgb), .3); }

/* Przycisk-przełącznik („Lista graczy”, „Etykiety”): włączony to niebieskie
   pole ALT jak wybrany element (zasada 15), wyłączony — zwykły ghost. */
.btn[aria-pressed="true"] { background: rgba(77,163,255,.13); border-color: rgba(77,163,255,.55); color: var(--txt); }
.btn[aria-pressed="true"] i { color: var(--blue); }
.btn[aria-pressed="true"]:hover { background: rgba(77,163,255,.24); }

.btn--danger, .btn--danger-soft { --btn-rgb: var(--red-rgb); }
.btn--ok { --btn-rgb: var(--grn-rgb); }
.btn--warn { --btn-rgb: var(--org-rgb); }

/* Wyłączony: kolor „off”, bez tonu. `pointer-events` zostaje, żeby dymek
   z powodem („Brak uprawnień”, „Tylko w grze”) dało się przeczytać —
   wyłączony przycisk i tak nie wysyła kliknięcia. `.is-disabled` (klasa,
   nie atrybut) dalej blokuje kliknięcia, bo nie ma `disabled`.
   Napis w --mut (kontrast ~6:1): do 29.10.2026 --mut-deep (~3:1) i „Utwórz”,
   „Ustaw” czy „Teleportuj” przy pustym polu były nieczytelne. */
.btn[disabled] {
  background: transparent;
  border-color: var(--line);
  color: var(--mut);
  cursor: not-allowed;
}
.btn[disabled] i, .btn[disabled] svg { color: var(--mut); }
.btn[disabled]:hover { background: transparent; }
.btn[disabled]:active { transform: none; }
/* Klasa zamiast atrybutu: ten sam wygląd co [disabled] (było: przezroczystość .45,
   czyli drugi, inny „wyłączony” w tym samym panelu). */
.btn.is-disabled { background: transparent; border-color: var(--line); color: var(--mut); pointer-events: none; }
.btn.is-disabled i, .btn.is-disabled svg { color: var(--mut); }

/* Przycisk-ikona (odśwież, zamknij, − +): ciemny klawisz z UI Kit
   (zr-pausemenu .rank-refresh) — wypełnienie --key bez obwódki, ikona
   --txt-rest, pod kursorem --key-hover. Do 29.10.2026 sama obwódka. */
.icon-btn {
  position: relative;
  width: var(--h-ctl); height: var(--h-ctl);
  flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-s);
  background: var(--key);
  border: 1px solid transparent;
  color: var(--txt-rest);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--e-in);
}
.icon-btn svg { width: var(--ic-md); height: var(--ic-md); }
.icon-btn i { font-size: var(--ic-sm); }
.icon-btn:hover { background: var(--key-hover); color: var(--txt); }
.icon-btn:active { transform: scale(.96); }
/* Wyłączony: jak wyłączony .btn (zasada 30) — ikona --mut (~6:1); do 30.10.2026
   --mut-deep (~3:1), czyli kosz czy zoom „−” ledwo widoczne. */
.icon-btn[disabled] { background: transparent; border-color: var(--line); color: var(--mut); cursor: not-allowed; }
.icon-btn[disabled]:hover { background: transparent; border-color: var(--line); color: var(--mut); }
.icon-btn[disabled]:active { transform: none; }
/* Ghost: akcja w wierszu (kopiuj, kosz) — bez klawisza, ikona szara. */
.icon-btn--ghost { background: transparent; border-color: transparent; color: var(--mut); }
.icon-btn--ghost:hover { background: var(--panel-3); border-color: transparent; }
.icon-btn--sm { width: var(--h-ctl-sm); height: var(--h-ctl-sm); border-radius: 8px; }
.icon-btn.is-danger:hover { background: var(--red-10); color: var(--red); }
.icon-btn__dot {
  position: absolute; top: 6px; right: 6px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--blue);
  box-shadow: 0 0 0 2px var(--panel);
}

/* =========================================================
   ODPOWIEDŹ PRZYCISKU NA AKCJĘ — w toku / zrobione / odmowa
   -----------------------------------------------------------
   Klasy nakłada js/core/motion.js (w grze z Nui.post, na stronie z api.js
   i command.js) na przycisk, którym admin uruchomił zapytanie. Widoki nic
   o tym nie wiedzą. Do tej pory między kliknięciem „Wyrzuć” a toastem nie
   działo się nic: przy wolnym serwerze admin klikał drugi raz.

   Wszystko siedzi w ::after i rusza się WYŁĄCZNIE transformem i opacity —
   w NUI każda inna animowana właściwość brudzi prostokąt i wymusza ponowne
   wysłanie tekstury (patrz .pc-notification w map-tools.css).
   ========================================================= */
.btn.is-busy, .btn.is-done, .btn.is-fail { position: relative; }
.btn.is-busy, .icon-btn.is-busy, .action-btn.is-busy { cursor: progress; }

/* W toku: kreska pod etykietą rośnie i znika — kształt niezależny od
   przycisku, więc ta sama reguła działa na .btn, kaflu akcji i ikonie. */
.btn.is-busy::after, .icon-btn.is-busy::after, .action-btn.is-busy::after {
  content: "";
  position: absolute;
  left: 22%; right: 22%; bottom: 3px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  opacity: .7;
  pointer-events: none;
  animation: act-busy 1.1s var(--e-hud) infinite;
}
@keyframes act-busy {
  0%     { transform: scaleX(0); transform-origin: left center; }
  50%    { transform: scaleX(1); transform-origin: left center; }
  50.1%  { transform: scaleX(1); transform-origin: right center; }
  100%   { transform: scaleX(0); transform-origin: right center; }
}

/* Zrobione / odmowa: obwódka w kolorze wyniku rozchodzi się i gaśnie.
   Kolor wyniku to status (zielony / czerwony), nie interakcja — zgodnie
   z zasadą, że niebieski jest wyłącznie „kliknij mnie”. */
.btn.is-done, .icon-btn.is-done, .action-btn.is-done { --act-rgb: var(--grn-rgb); }
.btn.is-fail, .icon-btn.is-fail, .action-btn.is-fail { --act-rgb: var(--red-rgb); }
.btn.is-done::after, .icon-btn.is-done::after, .action-btn.is-done::after,
.btn.is-fail::after, .icon-btn.is-fail::after, .action-btn.is-fail::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  border: 1px solid rgb(var(--act-rgb));
  pointer-events: none;
  animation: act-ring .7s var(--e-in) forwards;
}
@keyframes act-ring {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(1.06); }
}
/* Odmowa dodatkowo „kręci głową” — czerwień sama bywa za słaba, gdy obok
   stoi piętnaście kafli (ten sam powód co obrys [data-confirm] niżej). */
.btn.is-fail, .icon-btn.is-fail, .action-btn.is-fail { animation: act-shake .36s var(--e-hud); }
@keyframes act-shake {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-3px); }
  40%      { transform: translateX(3px); }
  60%      { transform: translateX(-2px); }
  80%      { transform: translateX(1px); }
}

/* =========================================================
   TONY — jedno źródło kolorów semantycznych
   -----------------------------------------------------------
   Element deklaruje `data-tone="ok|warn|err|info|blue"`, a komponent
   czyta --c (kolor treści), --c-bg (tło) i --c-bd (obrys).
   Wcześniej te trzy zmienne wstrzykiwał JS jako `style="--c:…"`
   z twardymi rgba() — inline style bije każdy selektor, więc dopóki
   tak było, motywu NIE DAŁO SIĘ przemalować bez ruszania widoków.
   ========================================================= */
[data-tone="ok"]   { --c: var(--grn);  --c-bg: var(--grn-10);  --c-bd: var(--grn-26); }
[data-tone="warn"] { --c: var(--org);  --c-bg: var(--org-10);  --c-bd: var(--org-26); }
[data-tone="err"]  { --c: var(--red);  --c-bg: var(--red-10);  --c-bd: var(--red-26); }
[data-tone="info"] { --c: var(--cyn);  --c-bg: var(--cyn-10);  --c-bd: var(--cyn-26); }
[data-tone="blue"] { --c: var(--blue); --c-bg: var(--blue-10); --c-bd: var(--blue-26); }
/* Fiolet zarezerwowany dla ANTICHEATA (bany ElectronAC). Jedyny konsument
   tego tonu — celowo spoza palety statusów, żeby ban od automatu nie zlewał
   się z czerwienią zwykłych banów. Wartości statyczne (rgba), nie color-mix:
   NUI to Chromium 103. */
[data-tone="anticheat"] { --c: #b84dff; --c-bg: rgba(184,77,255,.10); --c-bd: rgba(184,77,255,.26); }

/* Do 29.10.2026 akcja nieodwracalna w profilu gracza dostawała tu własny,
   SŁABSZY obrys ([data-confirm], .26 zamiast .55). Czerwone „Zbanuj” czy
   „Zabij” wyglądały przez to inaczej niż „Samobójstwo” w „Ja” i niż te same
   przyciski na stronie. Skutek mówi sam kolor pola ALT (zasada 1). */

/* =========================================================
   PLAKIETKA — stan (UI Kit EnviRP, koncept A z 28.10.2026)
   Pełny kolor stanu, ciemny tekst, wersaliki w „Archivo Caps” (metryki
   wyrównane, więc litery stoją na środku stałej wysokości), padding tylko
   w poziomie, line-height normal. Bez tonu: szara plakietka (kit chip-mut).
   `.badge--ghost` to NIE stan, tylko neutralny znacznik (ID, kto nadał,
   liczba) — zwykły tekst na szarym polu (chip kitu), bez obwódki.
   ========================================================= */
.badge {
  --badge-bg: #3A4254;
  --badge-ink: #E3E8F0;
  display: inline-flex; align-items: center; gap: 4px;
  flex: none;
  height: 20px;
  padding: 0 7px;
  border: 0;
  border-radius: 6px;
  background: var(--badge-bg);
  color: var(--badge-ink);
  font-family: var(--font-caps);
  font-size: var(--fs-meta);
  font-weight: var(--fw-black);
  letter-spacing: .07em;
  text-transform: uppercase;
  line-height: normal;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.badge i, .badge svg { font-size: var(--ic-xxs); }
.badge[data-tone] { --badge-bg: var(--c); --badge-ink: var(--core-ink); }
.badge--warn { --badge-bg: var(--org);  --badge-ink: var(--core-ink); }
.badge--err  { --badge-bg: var(--red);  --badge-ink: var(--core-ink); }
.badge--ok   { --badge-bg: var(--grn);  --badge-ink: var(--core-ink); }
.badge--info, .badge--blue { --badge-bg: var(--blue); --badge-ink: var(--core-ink); }
/* Ban od ElectronAC: odróżniony od zwykłego czerwonego „Na stałe” —
   przyciemniony czerwony z obwódką, jak „Anticheat” w koncepcie. */
.badge--anticheat {
  background: rgba(var(--red-rgb), .13);
  color: var(--red);
  box-shadow: inset 0 0 0 1px rgba(var(--red-rgb), .45);
}
.badge--ghost {
  background: var(--panel-3);
  color: var(--c, var(--txt-rest));
  font-family: var(--font-ui);
  font-weight: var(--fw-sem);
  letter-spacing: 0;
  text-transform: none;
}

/* =========================================================
   CHIPY — filtry wielokrotnego wyboru
   ========================================================= */
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--h-ctl-sm);
  padding: 0 var(--s-3);
  font-size: var(--fs-dense);
  font-weight: var(--fw-sem);
  background: var(--panel-3);
  color: var(--txt-rest);
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--e-in);
}
/* Obwódka --line-hover jak pola i listy — --line-strong (#2A3243) na rynience
   #1F2533 była pod kursorem prawie niewidoczna (do 30.10.2026). */
.chip:hover { color: var(--txt); border-color: var(--line-hover); }
.chip:active { transform: scale(.96); }
/* Wybrany chip = pole ALT (.13 / .55), jak wybrany segment, pozycja menu
   i przycisk-przełącznik. Do 30.10.2026 tło .16. Chip z tonem (data-tone)
   bierze swoje --c-bg / --c-bd. */
.chip.is-active {
  background: var(--c-bg, rgba(77,163,255,.13));
  border-color: var(--c-bd, rgba(77,163,255,.55));
  color: var(--txt);
}
.chip__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--c, var(--blue)); flex: none; }

/* =========================================================
   SEGMENT — filtr jednokrotnego wyboru (status, zakres)
   ========================================================= */
.seg {
  display: inline-flex; gap: 2px;
  padding: 3px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
}
.seg__btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 26px;
  font-family: var(--font-ui);
  padding: 0 var(--s-3);
  background: transparent;
  color: var(--mut);
  border: 0;
  border-radius: 7px;
  font-size: var(--fs-dense);
  font-weight: var(--fw-med);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), transform var(--t-fast) var(--e-in);
}
.seg__btn:hover { color: var(--txt); background: var(--panel-3); }
.seg__btn:active { transform: scale(.96); }
/* Wybrany segment = pole ALT (.13 / .55) — do 30.10.2026 .16 / .5. */
.seg__btn.is-active { background: rgba(77,163,255,.13); color: var(--txt); font-weight: var(--fw-sem); box-shadow: inset 0 0 0 1px rgba(77,163,255,.55); }
.seg__count {
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  color: var(--mut);
}
/* Liczba w wybranym segmencie: sam kolor akcentu, bez prostokąta tła tuż przy
   cyfrach (liczba nie jest plakietką — zasada 22). */
.seg__btn.is-active .seg__count { color: var(--blue); }

/* =========================================================
   PASEK NARZĘDZI — szukajka + filtry nad listą
   Kit: stała kolejność, wszystko na jednej linii bazowej.
   ========================================================= */
.toolbar { display: flex; flex-direction: column; gap: var(--s-3); margin-bottom: var(--s-4); }
.toolbar__row { display: flex; gap: var(--s-2); align-items: center; flex-wrap: wrap; }
/* JEDNA WYSOKOŚĆ W WIERSZU. Pole, lista i segment mają --h-ctl (34 px),
   a przycisk `--sm` i ikona `--sm` 28 px — postawione obok siebie wyglądały
   jak z dwóch różnych zestawów („Ostrzeż” przy polu powodu, „Wyślij” przy
   polu wiadomości). Przycisk stojący ZA polem, listą albo segmentem dostaje
   więc wysokość pola. Selektor rodzeństwa (`~`), bo CEF 103 nie zna :has() —
   a wiersz samych przycisków (nagłówek karty) ma zostać mały.
   Chipy celowo NIE: w wierszu stoją same (filtry), a filtr jednokrotnego
   wyboru obok pola ma być segmentem (`.seg`), który ma już 34 px. */
.toolbar__row > .field ~ .btn,
.toolbar__row > .select ~ .btn,
.toolbar__row > .seg ~ .btn,
.chat-composer > .field ~ .btn { height: var(--h-ctl); }
.toolbar__row > .field ~ .icon-btn,
.toolbar__row > .select ~ .icon-btn { width: var(--h-ctl); height: var(--h-ctl); }

/* =========================================================
   POLE TEKSTOWE / SZUKAJKA
   ========================================================= */
.field {
  position: relative;
  display: flex; align-items: center; gap: var(--s-2);
  flex: 1; min-width: 180px;
  height: var(--h-ctl);
  padding: 0 var(--s-3);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  transition: border-color var(--t-base) var(--ease);
}
/* `flex: 1` służy paskom narzędzi; w pionowym oknie skracało pole do 20 px
   (szukajka w ustawieniach kanałów) — wysokość pola zostaje minimalną.
   Kursor tekstowy na całej ramce: klik w ikonę albo padding stawia kursor
   w polu (js/core/selects.js, web ui.js). Lista z kitu — kursor ręki na całej
   ramce, klik w podpis też ją otwiera. */
.field { min-height: var(--h-ctl); cursor: text; }
.select--custom { cursor: pointer; }
/* Pod kursorem każde pole jaśnieje tak samo jak lista (.select:hover):
   obwódka --line-hover. Do 30.10.2026 szukajka obok listy w tym samym rzędzie
   nie reagowała wcale, a szukajki powłoki jaśniały słabiej (--line-strong).
   Fokus (niżej) wygrywa, bo stoi po hoverze. */
.field:hover { border-color: var(--line-hover); }
.field:focus-within { border-color: var(--blue-55); }
/* .form-input i .rinput mają bazę w arkuszach widoków (world-forms.css,
   ranks.css), wczytywanych po tym pliku — stąd :not(): hover nie może zgasić
   fokusu, błędu ani stanu wyłączonego, które są zdefiniowane później. */
.form-input:hover:not(:focus):not(:disabled):not(.is-invalid):not([aria-invalid="true"]),
.rinput:hover:not(:focus):not(:disabled):not([readonly]) { border-color: var(--line-hover); }
.field__icon { color: var(--mut); display: inline-flex; flex: none; }
.field__icon svg { width: var(--ic-xs); height: var(--ic-xs); }
.field input {
  flex: 1; min-width: 0;
  background: transparent; border: 0;
  color: var(--txt);
  font-size: var(--fs-body);
  caret-color: var(--txt);
}
.field input::placeholder { color: var(--mut); }
.field--grow { flex: 1 1 220px; }
/* `.modal__body` to kolumna flex: `flex: 1` / `1 1 220px` pola stawało się
   tam WYSOKOŚCIĄ (pole 220 px). W oknie pole i lista mają wysokość kontrolki —
   w obu panelach (do 30.10.2026 reguła była tylko w site.css). */
.modal__body > .field,
.modal__body > .select { flex: none; }

/* =========================================================
   SELECT — natywny i własny (cselect)
   ========================================================= */
.select {
  position: relative;
  display: flex; align-items: center; gap: var(--s-2);
  flex: none;
  height: var(--h-ctl);
  padding: 0 var(--s-2) 0 var(--s-3);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  transition: border-color var(--t-base) var(--ease);
}
.select:hover { border-color: var(--line-hover); }
/* Poprzednio: `.select:has(.cselect.is-open)`. CEF 103 (Chromium 103) NIE ZNA
   :has() — cała reguła była w grze martwa, więc pole nigdy nie pokazywało,
   że lista jest rozwinięta. Zastąpione dwiema drogami: klasą dopisywaną
   przez js/core/selects.js (stan autorytatywny) i :focus-within (działa też
   dla natywnego <select> bez cselecta). */
.select.is-open,
.select:focus-within { border-color: var(--blue-55); }
.select__label {
  flex: none;
  font-family: var(--font-ui);
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--mut);
  white-space: nowrap;
}
.select select:not(.cselect__native) {
  flex: 1; min-width: 0;
  background: transparent;
  border: 0;
  color: var(--txt);
  font-family: inherit;
  font-size: var(--fs-body);
  padding: 0 20px 0 0;
  appearance: none;
  color-scheme: dark;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238B96AB' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0 center;
  background-size: 14px;
}
.select select:not(.cselect__native) option { background: var(--panel-2); color: var(--txt); }

/* Lista jako POLE FORMULARZA (kreator rang, ustawienia kanału logów): pełna
   szerokość kolumny, bez etykiety w środku — etykietą jest `.rfield__label`
   nad nią. js/core/selects.js dokłada to opakowanie samo wokół `select.rinput`,
   bo natywna lista rozwijana to okno systemu: w ciemnym motywie pokazywała
   jasny tekst na jasnym tle i nie dało się jej przeczytać. */
.select--field { width: 100%; }

.select--custom { padding-right: var(--s-2); }
.select--custom .cselect__native {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.cselect { position: relative; flex: 1; min-width: 0; align-self: stretch; display: flex; align-items: center; }
.cselect__trigger {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-2);
  padding: 0;
  background: transparent; border: 0;
  color: var(--txt);
  font-size: var(--fs-body);
  font-weight: var(--fw-med);
  cursor: pointer;
  text-align: left;
}
.cselect__value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cselect__chev {
  flex: none; width: 14px; height: 14px;
  color: var(--mut);
  transition: transform var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.cselect.is-open .cselect__chev { transform: rotate(180deg); color: var(--blue); }
.cselect__menu {
  position: absolute;
  top: calc(100% + 8px);
  left: -12px; right: -8px;
  z-index: 30;
  min-width: 100%;
  padding: var(--s-1);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-s);
  box-shadow: 0 10px 30px rgba(4,6,10,.42);
  max-height: min(300px, 46vh);
  overflow: hidden auto;
  overscroll-behavior: contain;
  animation: cselect-in var(--t-base) var(--e-in) both;
  transform-origin: top center;
}
.cselect__menu[hidden] { display: none; }
/* Lista wyrasta z pola (skala od krawędzi, przy której leży), a nie
   tylko zjeżdża — wtedy widać, SKĄD się wzięła. */
@keyframes cselect-in    { from { opacity: 0; transform: translateY(-5px) scale(.97) } to { opacity: 1; transform: none } }
@keyframes cselect-in-up { from { opacity: 0; transform: translateY(5px) scale(.97) }  to { opacity: 1; transform: none } }
.cselect--drop-up .cselect__menu {
  top: auto; bottom: calc(100% + 8px);
  transform-origin: bottom center;
  animation-name: cselect-in-up;
}
.cselect__list { list-style: none; display: flex; flex-direction: column; gap: 1px; }
.cselect__option {
  display: flex; align-items: center; gap: var(--s-2);
  height: var(--h-row-sm);
  padding: 0 var(--s-2);
  border-radius: 7px;
  font-size: var(--fs-body);
  color: var(--dim);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.cselect__option:hover { background: var(--panel-3); color: var(--txt); }
.cselect__option.is-active { background: rgba(77,163,255,.13); color: var(--txt); font-weight: var(--fw-sem); }
.cselect__option.is-active::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--blue); flex: none;
}
.cselect__option:not(.is-active)::before { content: ""; width: 5px; flex: none; }

/* =========================================================
   PRZEŁĄCZNIK
   ========================================================= */
.switch { display: inline-flex; align-items: center; gap: var(--s-2); cursor: pointer; }
.switch input { display: none; }
/* Przełącznik z pc_ui (menu.css .pc-menu-check): wyłączony to rynienka
   z obwódką i szarą gałką, włączony pełny akcent z białą gałką. Rozmiar
   34×20 zostaje — rytm pól panelu (34 px). */
.switch__slider {
  position: relative;
  width: 34px; height: 20px;
  flex: none;
  background: var(--panel-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.switch__slider::after {
  content: ""; position: absolute;
  top: 2px; left: 2px;
  width: 14px; height: 14px;
  background: var(--mut);
  border-radius: 50%;
  transition: transform var(--t-base) var(--e-in), background var(--t-fast) var(--ease);
}
.switch:hover .switch__slider { border-color: var(--line-hover); }
.switch input:checked + .switch__slider { background: var(--blue); border-color: transparent; }
.switch input:checked + .switch__slider::after { transform: translateX(14px); background: #FFFFFF; }
.switch__label { font-size: var(--fs-body); color: var(--dim); }

/* =========================================================
   TOOLTIP „?” — wyjaśnienie zamiast akapitu w interfejsie
   Zamiast trzech linijek opisu pod nagłówkiem zostaje znak zapytania.
   Dymek rysuje js/core/tooltip.js na <body>, więc nie obcina go
   żaden overflow:hidden po drodze.
   ========================================================= */
/* Od 29.10.2026 (runda 5) „?” to chip kitu: rynienka --panel-3 bez obwódki
   i znak w --fs-micro (11 px) — wcześniej 9 px w obwiedzionym kółku, poniżej
   najmniejszego stopnia tekstu. Ujemny margines w pionie: kółko 16 px nie
   podnosi wiersza etykiety (KPI z „?” stoi równo z sąsiednimi). */
.tip {
  display: inline-grid;
  place-items: center;
  width: 16px; height: 16px;
  flex: none;
  margin: -1px 0 -1px 6px;
  vertical-align: middle;
  border-radius: 50%;
  border: 0;
  background: var(--panel-3);
  color: var(--mut);
  font-family: var(--font-ui);
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
  cursor: help;
  transition: color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
/* „?” nie jest przyciskiem: pod kursorem tylko jaśnieje (napis --txt, tło
   klawisza), bez niebieskiego — niebieski znaczy „kliknij”. Do 30.10.2026
   tło --blue-16. */
.tip:hover { color: var(--txt); background: var(--key-hover); }

/* =========================================================
   POWRÓT (.backbar) — jeden wygląd i JEDNO miejsce w obu panelach
   (runda 6, 29.10.2026). Pierwszy rząd pod nagłówkiem zakładki, zawsze
   wysokości pola (34 px), więc „‹ Powrót” stoi w tym samym punkcie bez
   względu na to, co jest obok; z lewej przycisk ghost sm z napisem
   „Powrót”, z prawej akcje widoku (.backbar__end), 16 px do treści.
   Dokąd prowadzi, mówi podpowiedź. Markup składa backBarHtml() w grze
   (js/core/utils.js) i backBar() na stronie (web/assets/js/ui.js).
   Do rundy 6: .player-detail__bar, .logs-nav i „Wróć do logów” z własną
   ikoną SVG — trzy warianty napisu i wysokości.
   ========================================================= */
.backbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-2);
  min-height: var(--h-ctl);
  margin: 0 0 var(--s-4);
}
.backbar__btn { flex: none; }
.backbar__end { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: var(--s-2); min-width: 0; }

/* =========================================================
   KOMUNIKAT (.notice) — jedna definicja dla gry i strony (runda 5).
   Ton tła .10, obwódka .26, jasny napis w tonie. Wcześniej gra miała
   własną w ranks.css (tylko „warn”, napis #FFB020), a strona swoją.
   ========================================================= */
.notice {
  padding: var(--s-3) var(--s-4); margin-bottom: var(--s-4);
  border: 1px solid var(--line-strong); border-radius: var(--r-s);
  background: var(--panel-2); color: var(--dim);
  font-size: var(--fs-body); line-height: var(--lh-body);
  /* Zdanie z serwera bywa z identyfikatorem albo adresem bez spacji. */
  overflow-wrap: anywhere;
}
.notice--warn { background: var(--org-10); border-color: var(--org-26); color: #FFD9A3; }
.notice--err  { background: var(--red-10); border-color: var(--red-26); color: #FFC2C8; }
.notice--info { background: var(--blue-10); border-color: var(--blue-26); color: #BEDCFF; }
.notice b { color: inherit; }

.tooltip {
  position: fixed;
  /* Wyżej niż nakładka /report (.rp-overlay ma 9000) — dymek doklejany do
     <body> musi wygrywać z KAŻDĄ warstwą panelu, inaczej w nakładce po prostu
     go nie widać. */
  z-index: 9500;
  max-width: 300px;
  /* Ciemniej niż karta, z jaśniejszą obwódką — dymek odcina się od każdego
     tła, jak dymek mapy cieplnej w Panelu zarządu (właściciel, 29.10.2026).
     Stoi nad kursorem: js/core/tooltip.js, web/assets/js/tooltip.js. */
  padding: 8px 11px;
  border-radius: var(--r-s);
  background: var(--bg);
  border: 1px solid var(--line-hover);
  box-shadow: var(--sh-2);
  color: var(--txt);
  font-size: var(--fs-dense);
  line-height: var(--lh-body);
  /* Dymek wstawia treść przez textContent, więc bez tego \n zwijało się do
     spacji i rozpiska punktów zaufania leciała jednym akapitem. `pre-line`
     (a nie `pre`) — łamie po \n, ale nadal zawija długie linie w max-width
     i nie utrwala wcięć z szablonu. Żaden istniejący data-tip nie ma \n,
     więc reszta dymków wygląda tak samo jak przedtem. */
  white-space: pre-line;
  /* Długi ciąg bez spacji (licencja, Discord ID, adres pliku) łamie się
     w ramce. Do 30.10.2026 „license:efaa…177d” wychodził za prawą krawędź. */
  overflow-wrap: anywhere;
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.tooltip.is-on { opacity: 1; transform: none; }
/* Dymek w wierszach (data-tip-rows): przygaszona etykieta, wartość obok. */
.tooltip--rows {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: baseline;
  gap: 5px 12px;
  max-width: 360px;
  padding: 10px 12px;
  white-space: normal;
}
.tooltip__k {
  font-size: var(--fs-micro); font-weight: var(--fw-bold);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase;
  color: var(--mut);
}
.tooltip__v { min-width: 0; color: var(--txt); overflow-wrap: anywhere; }
.tooltip__v--ok { color: var(--grn); }
.tooltip__v--err { color: var(--red); }
.tooltip__v--warn { color: var(--org); }
.tooltip__v--mut { color: var(--mut); }
/* Tytuł i przypis na całą szerokość, odcięte od wierszy kreską. */
.tooltip__title {
  grid-column: 1 / -1;
  padding-bottom: 7px; margin-bottom: 2px;
  border-bottom: 1px solid var(--line);
  font-weight: var(--fw-bold); color: var(--txt);
}
.tooltip__note {
  grid-column: 1 / -1;
  padding-top: 7px; margin-top: 2px;
  border-top: 1px solid var(--line);
  color: var(--mut);
}

/* =========================================================
   TYTUŁ SEKCJI I PUSTY STAN — krój wspólny dla gry i panelu WWW
   (strona nie ładuje layout.css, więc te reguły nie mogą tam mieszkać).
   ========================================================= */
.section-title,
.panel__title,
.log-results__title {
  font-family: var(--font-display);
  font-size: var(--fs-title);
  font-weight: var(--fw-black);
  letter-spacing: -.01em;
  color: var(--txt);
}
/* Liczba przy tytule (koncept: .h2 .count) — zwykły tekst Inter 600
   --fs-dense w --mut, bez pola plakietki (zasada 22). Dla miejsc, które
   nie mają własnej klasy licznika. */
.title-count {
  font-family: var(--font-ui); font-size: var(--fs-dense); font-weight: var(--fw-sem);
  letter-spacing: 0; text-transform: none; color: var(--mut);
  font-variant-numeric: tabular-nums;
}
/* Stan pusty — jeden na cały panel (kit: centrowanie wolno TYLKO tu).
   Do 30.10.2026 baza siedziała w layout.css, a strona miała własną w site.css
   (padding 48/24, bez flexa): ten sam stan pusty był w obu panelach innej
   wysokości, a mały (.empty--mini) na stronie o 62 px wyższy. */
.empty {
  margin: var(--s-5) 0;
  padding: var(--s-5) var(--s-4);
  text-align: center;
  display: flex; flex-direction: column; align-items: center;
  /* Pełna szerokość także w rzędzie flex (licencje w profilu): do 31.10.2026
     „Brak licencji” kurczyło się do treści i stało przy lewej krawędzi. */
  width: 100%;
  color: var(--mut);
}
/* Wariant do wnętrza karty/kafla — pełny .empty (margines 24, kafel 46 px,
   akapit) jest za ciężki na pojemnik wysokości 220 px. Tu: ikona + rzeczownik. */
.empty--mini { margin: 0; padding: var(--s-4) var(--s-3); justify-content: center; height: 100%; }
/* Ikona stanu pustego w szarym kaflu z UI Kit (zr-pausemenu .rank-empty-ico):
   46 px, --key, ikona --mut, bez obwódki i poświaty. Ten sam kafel mają
   własne stany puste Zgłoszeń, Przedmiotów i Dokumentów (pliki widoków). */
.empty__art {
  flex: none;
  width: 46px; height: 46px;
  margin: 0 auto var(--s-3);
  display: grid; place-items: center;
  border-radius: 13px;
  background: var(--key);
  color: var(--mut);
  font-size: var(--ic-lg);
}
.empty__art svg { width: var(--ic-lg); height: var(--ic-lg); }
.empty__title {
  margin: 0 0 var(--s-1);
  font-family: var(--font-display);
  font-size: var(--fs-title);
  font-weight: var(--fw-black);
  color: var(--txt);
  overflow-wrap: anywhere;   /* „Brak wyników dla …” z wpisaną frazą */
}
/* margin auto: na stronie .empty to zwykły blok z text-align: center, więc
   akapit z max-width bez niego stawał przy lewej krawędzi pod tytułem. */
.empty__text { margin: 0 auto; font-size: var(--fs-dense); max-width: 42ch; line-height: var(--lh-body); color: var(--mut); overflow-wrap: anywhere; }
/* Mały pusty stan w środku karty: ikona i rzeczownik, bez akapitu. */
/* Mały kafel (36 px) w stanie pustym wewnątrz karty — jak w pustych
   listach Strony głównej, szyny graczy i wątków Zgłoszeń. */
.empty--mini .empty__art { width: 36px; height: 36px; border-radius: 11px; margin-bottom: var(--s-2); font-size: var(--ic-md); }
.empty--mini .empty__art svg { width: var(--ic-md); height: var(--ic-md); }
.empty--mini .empty__title { font-family: var(--font-ui); font-size: var(--fs-dense); font-weight: var(--fw-med); color: var(--mut); }
.empty--mini .empty__text { display: none; }

/* „Pokaż więcej” pod listą stronicowaną (Dokumenty, Przedmioty): wyśrodkowany
   przycisk ghost ze strzałką, jak „Pokaż starsze” w Logach i w Zdjęciach postaci. */
.list-more { display: flex; width: fit-content; margin: var(--s-3) auto var(--s-1); }
.list-more .u-num, .pol-more .u-num { color: var(--mut); font-weight: var(--fw-med); }

/* Filtr menu bez trafień (js/app.js w grze, app.js na stronie): mały stan
   pusty z kaflem lupy, nie luźne zdanie (zasada 37). Wysokość z treści — na
   stronie stan stoi w kolumnie flex paska bocznego, gdzie `height: 100%`
   zająłby cały pasek. */
.empty--mini.nav-empty { flex: none; height: auto; margin: var(--s-1) 0 var(--s-2); }

/* =========================================================
   KARTA — jeden pojemnik na sekcję treści
   ========================================================= */
.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  overflow: hidden;
}
.card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3);
  min-height: 50px;
  padding: 4px 12px 0 16px;
}
.card__title {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-display);
  font-size: var(--fs-title);
  font-weight: var(--fw-black);
  color: var(--txt);
}
.card__title i { color: var(--txt); font-size: var(--ic-sm); }
.card__tools { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.card__body { padding: var(--s-4); }
.card__body--flush { padding: 0; }

/* =========================================================
   KPI — liczba + etykieta mono. Vivid TYLKO na cyfrze (kit rozdz. 10).
   ========================================================= */
.kpi { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.kpi__label {
  font-family: var(--font-ui);
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--mut);
}
.kpi__value {
  font-family: var(--font-display);
  font-size: var(--fs-kpi);
  font-weight: var(--fw-black);
  line-height: 1.1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--txt);
}
.kpi__value--blue { color: var(--blue); }
.kpi__value--red  { color: var(--red); }
.kpi__value--grn  { color: var(--grn); }
.kpi__value--org  { color: var(--org); }
.kpi__value--cyn  { color: var(--cyn); }
/* Szkielet wczytywania (UI Kit: zr-pausemenu .skl): belki w kolorze
   rynienki zamiast spinnera, więc lista ma od razu swoją wysokość.
   Pulsuje sama przezroczystość — w NUI animujemy tylko opacity/transform. */
.skl { display: block; height: 10px; border-radius: 5px; background: var(--panel-3); animation: skl-pulse .9s var(--ease) infinite alternate; }
.skl--thin { height: 8px; }
@keyframes skl-pulse { from { opacity: .45; } to { opacity: 1; } }
/* Szkielet panelu profilu (players.js: detailLoadingHtml, licensesLoadingHtml). */
.rows.is-skeleton .row__ico { background: var(--panel-3); }
.rows.is-skeleton .skl--label { width: 46%; }
.rows.is-skeleton .skl--value { width: 52px; margin-left: auto; }
.skl--lic { width: 96px; height: 26px; border-radius: 8px; }
@media (prefers-reduced-motion: reduce) { .skl { animation: none; } }

/* Liczba przy zerze gaśnie do szarego (UI Kit: „przy zerze liczba gaśnie”).
   Atrybut data-zero stawia zeroAttr() w helperach kafli (gra i strona). */
.kpi[data-zero] .kpi__value,
.stat-tile[data-zero] .stat-tile__value strong,
.tmv-kpi[data-zero] .tmv-value { color: var(--mut); }
.kpi__hint { font-size: var(--fs-meta); color: var(--mut); }

/* =========================================================
   WIERSZ DANYCH — ikona → nazwa → wartość → akcja (kit rozdz. 10)
   ========================================================= */
.row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: var(--h-row);
  padding: 0 var(--s-3);
  border-radius: var(--r-s);
  background: transparent;
  transition: background var(--t-fast) var(--ease);
}
/* Podświetlenie hoveru ma być IDENTYCZNE w każdym wierszu listy.
   Poprzednio `.row + .row { border-radius: 0 }` zostawiało zaokrąglony
   tylko PIERWSZY wiersz — stąd „górny jest zaokrąglony a dolny nie”.
   Kreska rozdzielająca jest teraz pseudoelementem WCIĘTYM o padding, więc
   nie dobija do zaokrąglonych rogów, i znika wokół wiersza pod kursorem —
   inaczej linia przecinałaby narożnik podświetlenia. */
.row { position: relative; }
.row + .row::before {
  content: "";
  position: absolute;
  top: 0; left: var(--s-3); right: var(--s-3);
  height: 1px;
  background: var(--line);
  transition: opacity var(--t-fast) var(--ease);
}
/* Bez podświetlenia pod kursorem: wiersz danych nie jest przyciskiem (zasada 1
   — wygląd nie może zapraszać do kliknięcia). Do 29.10.2026 każdy wiersz
   profilu (Telefon, Saldo, „Obecnie” w studni) jaśniał pod kursorem. Akcja
   w wierszu (.row__actions) ma własny hover przycisku. */
/* Ikona wiodąca. Kit rozdz. 10 narzuca kolejność ikona → nazwa → wartość →
   akcja, więc slot ikony należy do komponentu, a nie do każdego widoku
   z osobna (bez niego trzy widoki dorobiły własne .item-row / .mod-row__icon
   / .row__ico). Kolor bierze z [data-tone] — patrz sekcja TONY. */
.row__ico {
  flex: none;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: var(--r-xs);
  background: var(--c-bg, rgba(255,255,255,.05));
  color: var(--c, var(--mut));
  font-size: var(--ic-xs);
}
.row__ico svg { width: var(--ic-xs); height: var(--ic-xs); }
.row__ico--lg { width: 32px; height: 32px; border-radius: var(--r-s); font-size: var(--ic-sm); }

.row__main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }
.row__name { font-size: var(--fs-body); font-weight: var(--fw-sem); color: var(--txt); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row__sub  { font-size: var(--fs-meta); color: var(--mut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row__value {
  flex: none;
  /* Liczby w kroju interfejsu z cyframi tabelarycznymi; mono tylko dla kluczy i kodów. */
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}
.row__actions { flex: none; display: flex; align-items: center; gap: var(--s-1); }

/* =========================================================
   HIERARCHIA SEKCJI — waga karty
   -----------------------------------------------------------
   Do tej pory `section.panel` był JEDYNYM pojemnikiem w całym panelu:
   siedem paneli na profilu gracza, cztery w „Ja”, trzy w „Pojazdy” —
   każdy z tym samym tłem, tą samą ramką i tym samym tytułem 15/600.
   Nic nie mówiło, na co patrzeć najpierw. To było główne źródło
   „nie wiadomo gdzie patrzeć”.
   Zasada: JEDNA karta --primary na widok (to, po co admin tu wszedł),
   reszta --secondary (wyciszona), rzadkie rzeczy --quiet (zwinięte).
   Selektory dwuklasowe, żeby wygrywały niezależnie od kolejności plików.
   ========================================================= */
/* Warianty niezależne od pojemnika — działają na .panel, .tile, .card
   i czymkolwiek jeszcze. Selektor jest zdublowany (.is-primary.is-primary)
   celowo: to podbija specyficzność do 0,2,0, żeby wariant wygrywał z regułą
   bazową pojemnika (.panel, .tile) zdefiniowaną w PÓŹNIEJSZYM pliku widoków.
   Bez tego każdy widok musiałby robić własną kopię wariantu — a to dokładnie
   ta duplikacja, którą refaktor likwiduje. */
.is-primary.is-primary {
  background: var(--panel);
  border-color: var(--blue-38);
  box-shadow: none;
}
.is-secondary.is-secondary { background: var(--panel); }
.is-quiet.is-quiet {
  background: transparent;
  border-color: var(--line);
  box-shadow: none;
}
.is-quiet .panel__title,
.is-quiet .card__title,
.is-quiet .tile__title { color: var(--dim); }

/* Aliasy dla pojemnika .panel — ta sama treść, stara nazwa. */
.panel.panel--primary   { background: var(--panel); border-color: var(--blue-38); box-shadow: none; }
.panel.panel--secondary { background: var(--panel); }
.panel.panel--quiet     { background: transparent; border-color: var(--line); box-shadow: none; }
.panel.panel--quiet .panel__title { color: var(--dim); }

/* =========================================================
   NAGŁÓWEK SEKCJI — tytuł do lewej, akcja do prawej
   =========================================================
   Tego układu tu BRAKOWAŁO. `.panel__head` był zwykłym blokiem, więc
   `<h2>` rozpychał się na całą szerokość, a przycisk obok tytułu spadał
   do własnego wiersza (widać to było w „Teleportacji": tytuł, pod nim
   samotny niebieski guzik). Jeden widok obszedł to własną klasą
   `.pan-head` — i stąd brał się miszmasz: dwa nagłówki tej samej sekcji
   układały się inaczej zależnie od tego, kto pamiętał o łatce.

   Jedna reguła obsługuje oba kształty nagłówka:
     • sam tytuł               -> siedzi po lewej, bez zmian,
     • tytuł + akcja/licznik   -> rozjeżdżają się na boki,
     • tytuł + podpis          -> podpis łamie się do własnego wiersza
                                  (flex-basis 100%), a nie ląduje obok. */
.panel__head, .card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2) var(--s-3);
}
.panel__head .panel__title, .card__head .card__title { min-width: 0; }

/* Podpis sekcji zostaje TYLKO wtedy, gdy niesie liczbę lub fakt.
   Parafraza tytułu idzie do tooltipa „?” — patrz .tip wyżej. */
.panel__sub, .card__sub {
  font-size: var(--fs-meta);
  color: var(--mut);
  font-variant-numeric: tabular-nums;
  /* Cała szerokość = własny wiersz pod tytułem. Bez tego flex postawiłby
     podpis obok tytułu, po przeciwnej stronie nagłówka. */
  flex: 1 0 100%;
}

/* =========================================================
   STANY ŁADOWANIA (UI KIT, rozdz. 08 „Stany ładowania")
   -----------------------------------------------------------
   Zasada z kitu: skeleton, gdy ZNASZ kształt treści; spinner, gdy nie znasz;
   pasek z procentem dopiero powyżej 3 s. Nigdy pusty ekran bez informacji.
   Receptura .skl i .spin przeniesiona z kitu 1:1.
   Markup generuje js/core/loading.js.
   ========================================================= */
.spin {
  width: 34px; height: 34px;
  border-radius: 50%;
  /* Rynienka kitu pod niebieskim łukiem, bez poświaty (koncept A). */
  border: 3px solid var(--panel-3);
  border-top-color: var(--blue);
  animation: rot .9s linear infinite;
}
@keyframes rot { to { transform: rotate(360deg); } }

/* JEDEN stan ładowania na cały panel. Pierwsze podejście szło w szkielety
   (paski udające wiersze) i było błędne: te widoki mają zupełnie różne
   kształty — master-detail, tabela z paskiem zakładek, siatka KPI, szyna
   ze sceną — a generyczna lista wierszy zapowiadała układ, którego potem
   nie było. Szkielet, który kłamie o kształcie, jest gorszy od uczciwego
   kółka. Kit (rozdz. 08) dopuszcza oba; spinner jest dla przypadku
   „nie znasz kształtu treści”, a przy dziewięciu różnych widokach
   jeden wspólny szkielet go po prostu nie zna. */
.loading {
  flex: 1;
  min-height: 180px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  padding: var(--s-7) var(--s-4);
  color: var(--mut);
  /* Kółko pojawia się z opóźnieniem. Odpowiedź, która przyszła w ciągu
     ~150 ms, podmienia je, zanim zdążyło mignąć — a mignięcie spinnera przy
     każdym przełączeniu zakładki czytało się jak zacięcie, nie jak ładowanie. */
  animation: fade-in var(--t-base) var(--ease) .15s both;
}
.loading__label {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
}

.ldbox {
  display: grid; justify-items: center; align-content: center; gap: var(--s-3);
  padding: var(--s-5) var(--s-4);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  min-width: 130px;
}
.ldbox em {
  font-family: var(--font-ui);
  font-size: var(--fs-micro);
  font-weight: var(--fw-sem);
  font-style: normal;
  color: var(--mut);
}

@media (prefers-reduced-motion: reduce) {
  .spin { animation: none; }
}

/* =========================================================
   UŻYTKI UKŁADU
   ========================================================= */
/* Kontener z własnym scrollem wewnątrz kolumny flex. Dwie rzeczy naraz,
   bo bez `min-height:0` element flexowy nie skurczy się poniżej treści
   i scroll nigdy się nie włączy — to była przyczyna martwych scrolli
   w reportach, monitoringu i konsoli dewelopera. */
.pane-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.pane-fill   { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* Każdy nośnik tekstu w siatce/liście musi umieć się skrócić. Bez tego
   długi nick albo 48-znakowy license hex rozpycha kolumnę i cała siatka
   się rozjeżdża. */
.grid-safe, .grid-safe > * { min-width: 0; }

/* =========================================================
   AVATAR — obrazek z fallbackiem na inicjały
   ========================================================= */
.avatar-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: inherit;
}
.avatar-init { position: relative; z-index: 0; }

/* =========================================================
   Wskaźnik stanu połączeń (.connstat) — nagłówek w grze i na stronie
   =========================================================
   Zastępuje zaszytą na sztywno pigułkę „online", która nie zmieniała się
   nigdy — także we wrześniu 2026, kiedy akcje ze strony od godzin ginęły
   po drodze do serwera gry. Kropka i krótki napis mówią „czy jest dobrze";
   szczegóły (każde łącze osobno, czas i wynik ostatniej synchronizacji)
   pokazuje dymek po najechaniu albo po fokusie z klawiatury.

   Dymek to CSS (:hover / :focus-within), nie js/core/tooltip.js: tamten
   pokazuje wyłącznie tekst, a tu jest tabela kilku wierszy ze stanami. */
/* Kropka i słowo bez ramki, jak „połączono” w koncepcie A — bez paddingu,
   żeby odstępy w belce były równe (14 px). Ostrzeżenie i błąd dostają ramkę
   w kolorze stanu, wysoką jak szukajka obok (34 px, promień 10) — jedna
   wysokość w wierszu. Pod kursorem nic się nie podświetla: wskaźnik nie jest
   przyciskiem, a dymek i tak się pokazuje. Do 30.10.2026: 8 px pustego
   paddingu w stanie OK (odstępy 22 zamiast 14), ramka 28 px obok pola 34 px
   i szare tło pod kursorem. */
.connstat {
  position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--h-ctl); padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-s);
  font-size: var(--fs-dense);
  color: var(--txt-rest);
  white-space: nowrap;
  cursor: default; outline: none;
  transition: border-color var(--t-fast) var(--ease);
}
.connstat[data-state="warn"]  { padding: 0 10px; border-color: rgba(255,162,46,.34); background: rgba(255,162,46,.06); }
.connstat[data-state="error"] { padding: 0 10px; border-color: rgba(255,77,94,.36); background: rgba(255,77,94,.06); }

/* 6 px jak kropka serwera w stopce paska bocznego i kropka chipa (było 7). */
.connstat__dot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--mut-deep);
}
[data-state="ok"]    > .connstat__dot { background: var(--grn); }
[data-state="warn"]  > .connstat__dot { background: var(--org); }
[data-state="error"] > .connstat__dot { background: var(--red); }
/* Pulsuje tylko to, co wymaga uwagi. Zielona kropka, która mruga wiecznie,
   uczy nie patrzeć na kropkę — a o to właśnie chodziło w poprzedniej. */
.connstat[data-state="warn"]  > .connstat__dot,
.connstat[data-state="error"] > .connstat__dot { animation: pulse-dot 1.6s var(--ease) infinite; }
.connstat[data-state="warn"]  .connstat__label { color: var(--org); }
.connstat[data-state="error"] .connstat__label { color: var(--red); }

/* Materiał każdego dymku (zasada 32, .tooltip): tło --bg, obwódka --line-hover,
   cień --sh-2. Do 30.10.2026 tło --panel-3 i --line-strong, a dziedziczone
   `nowrap` ucinało dłuższe opisy łączy na krawędzi dymku. */
.connstat__pop {
  display: none;
  position: absolute; top: calc(100% + 6px); right: 0;
  z-index: 9400;
  width: 340px; padding: var(--s-3);
  border-radius: var(--r-s);
  background: var(--bg);
  border: 1px solid var(--line-hover);
  box-shadow: var(--sh-2);
  font-family: var(--font-ui); text-transform: none; letter-spacing: normal;
  white-space: normal;
  color: var(--txt); text-align: left;
}
/* Mostek nad dymkiem: bez niego przejście kursorem przez 6 px szczeliny
   gasiło :hover i dymek znikał, zanim dało się do niego dojechać. */
.connstat__pop::before {
  content: ''; position: absolute; left: 0; right: 0; top: -8px; height: 8px;
}
.connstat:hover .connstat__pop,
.connstat:focus-within .connstat__pop { display: block; animation: pop-in var(--t-fast) var(--e-in); transform-origin: top right; }

.connstat__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2);
  margin-bottom: var(--s-2);
  font-size: var(--fs-dense); font-weight: var(--fw-bold);
}
.connstat__time { font-size: var(--fs-meta); font-weight: 400; font-variant-numeric: tabular-nums; color: var(--mut); }

.connstat__row {
  display: flex; align-items: flex-start; gap: var(--s-2);
  padding: 6px 0;
  border-top: 1px solid var(--line);
}
.connstat__row > .connstat__dot { margin-top: 5px; }
.connstat__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.connstat__name { font-size: var(--fs-dense); font-weight: var(--fw-sem); }
.connstat__detail { font-size: var(--fs-meta); line-height: 1.4; color: var(--mut); }
.connstat__hint {
  margin: var(--s-2) 0 0; padding-top: var(--s-2);
  border-top: 1px solid var(--line);
  font-size: var(--fs-meta); line-height: 1.4; color: var(--mut);
}

/* =========================================================
   STOPKA „KTO I KIEDY” — osoba, czas, dodatki; każde ze swoją ikoną
   ========================================================= */
/* Treść z metaParts() (utils.js). Kolor i rozmiar daje element rodzica
   (.modh-row__meta, stopka karty), tu tylko odstępy i ikony. Wzór: .dcm-meta
   w Dokumentach. */
.meta-line { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 4px 12px; min-width: 0; }
.meta-line > span { display: inline-flex; align-items: center; gap: 5px; min-width: 0; overflow-wrap: anywhere; }
.meta-line i { flex: none; font-size: var(--ic-xs); }

/* =========================================================
   KLAWISZ — skrót klawiszowy w pasku bocznym i w belce (Ctrl K)
   ========================================================= */
/* Biały klawisz (UI Kit): wyłącznie podpowiedź klawiatury, nigdy przycisk. */
.kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 19px; height: 19px; padding: 0 6px 1px;
  border: 0;
  border-radius: 6px;
  background: #FFFFFF;
  box-shadow: inset 0 -2px 0 #AEB8C7;
  font-family: var(--font-display); font-size: var(--fs-meta); font-weight: var(--fw-black);
  letter-spacing: .04em; line-height: 1;
  color: var(--core-ink);
  white-space: nowrap;
}

/* =========================================================
   ZAKŁADKI KRESKOWE — przełączanie treści widoku (prototyp ENVI Admin)
   Równy rząd, cienkie podkreślenie aktywnej; bez wypełnionego segmentu.
   ========================================================= */
/* overflow-y: hidden — samo overflow-x: auto robi z osi pionowej `auto`, a
   kreska aktywnej zakładki wystaje 1 px w dół: w grze stał obok zakładek
   pionowy pasek przewijania. Poziomy też schowany; kółko i gładzik dalej
   przewijają długi rząd. */
.utabs { display: flex; align-items: flex-end; gap: 22px; margin: 0 0 var(--s-4); border-bottom: 1px solid var(--line); overflow-x: auto; overflow-y: hidden; }
.utabs::-webkit-scrollbar { display: none; }
.utabs__btn {
  appearance: none; background: none; border: 0; cursor: pointer;
  position: relative; flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 10px 2px 12px;
  font-size: var(--fs-body); font-weight: var(--fw-sem);
  color: var(--mut);
  transition: color var(--t-fast) var(--ease);
}
.utabs__btn:hover { color: var(--txt-rest); }
.utabs__btn.is-active { color: var(--txt); }
.utabs__btn::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; border-radius: 2px; background: var(--blue);
  opacity: 0; transform: scaleX(.7);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.utabs__btn.is-active::after { opacity: 1; transform: none; }
.utabs__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 2px;
  background: transparent;
  font-size: var(--fs-meta); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums;
  color: var(--mut);
}
.utabs__btn.is-active .utabs__count { color: var(--blue); }
/* Pusty licznik (zero) nie zajmuje miejsca: inaczej kreska aktywnej
   zakładki wystaje za napis o szerokość licznika. */
.utabs__count:empty { display: none; }
/* Zmiana zakładki: poprzednia lista zostaje przygaszona, aż przyjdzie nowa.
   Bez kółka ładowania pomiędzy, więc widok zmienia wysokość raz, nie dwa. */
[role="tabpanel"][aria-busy="true"] { opacity: .5; pointer-events: none; transition: opacity var(--t-fast) var(--ease); }
