/* =========================================================
   EnviRP — panel administracyjny (WWW): układ i widoki
   =========================================================
   Ten plik zawiera WYŁĄCZNIE to, co należy do strony. Kolory, typografia,
   przyciski, pigułki, chipy, pola, karty i KPI przychodzą z `tokens.css`,
   `base.css` i `components.css` — kopii systemu projektowego z panelu w grze
   (scripts/sync-design.mjs). Tamtych plików nie edytujemy tutaj.

   Dlaczego wspólny system, a osobny układ: administracja pracuje w obu
   interfejsach tego samego dnia, więc przycisk „Zapisz” i znacznik rangi mają
   wyglądać identycznie. Ale okno w grze ma ~1216×660 px i jest oglądane
   w trakcie gry, a strona dostaje pełne okno przeglądarki i bywa otwierana
   na telefonie — więc rozmieszczenie, gęstość i sposób przewijania są tutaj
   dobrane od nowa. Układ jest odbiciem `pc_admin/css/layout.css`, nie kopią:
   te same proporcje i nazwy klas, ale rozciągnięte na okno przeglądarki
   zamiast na pływające okno o stałym rozmiarze.
   ========================================================= */

/* ---------------------------------------------------------
   1. Powłoka: okno = cały ekran
   --------------------------------------------------------- */
/* W grze `.app` to pływające okno (`--app-w` × `--app-h`) na scrimie.
   Tutaj zajmuje całą przeglądarkę: bez ramki, bez cienia, bez zaokrągleń —
   te trzy rzeczy mają sens dla okna leżącego na czymś innym. */
.app {
  width: 100%;
  height: 100vh;
  height: 100dvh;             /* pasek adresu na telefonie nie ma ucinać treści */
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  background: var(--bg);
  border: 0;
  border-radius: 0;
  overflow: hidden;
  box-shadow: none;
  /* Okno pojawia się spod gasnącego ekranu startowego (app.js: leaveBoot).
     Rusza samo przy zdjęciu `hidden` (display: none → grid). */
  animation: fade-in var(--t-slow) var(--e-in) backwards;
}

.backdrop {
  display: none;
  position: fixed; inset: 0;
  background: rgba(4, 6, 10, .6);
  z-index: 30;
}

/* ---------------------------------------------------------
   2. Pasek boczny
   --------------------------------------------------------- */
/* Pasek boczny jak w grze (koncept A, 28.10.2026): jaśniejsza góra, logo
   w kaflu z nazwą, nagłówki grup wersalikami, aktywna pozycja z obwódką. */
.sidebar {
  height: 100%; min-height: 0;
  background: linear-gradient(180deg, var(--top), var(--bg) 220px);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  padding: 0 12px;
  overflow: hidden;
  z-index: 40;
}

/* Wygląd paska jak w grze (pc_admin/css/layout.css, prototyp ENVI Admin):
   logo, filtr z Ctrl + K, cztery zwijane grupy, pozycje 40 px. */
.nav-section__label, .topbar__search, .topbar__me {
  appearance: none; background: transparent; border: 0; cursor: pointer;
}
/* Blok logo ma wysokość belki: środek logo na osi belki i okruszków (jak w grze). */
.sidebar__brand { flex: none; height: var(--topbar-h); padding: 0 8px; display: flex; align-items: center; }
.brand { display: inline-flex; align-items: center; gap: 10px; min-width: 0; border-radius: var(--r-s); text-decoration: none; color: inherit; }
.brand__logo {
  flex: none; width: 38px; height: 38px; padding: 3px;
  object-fit: contain; display: block;
  border-radius: 10px;
  background: rgba(77,163,255,.14); border: 1px solid rgba(77,163,255,.55);
  transition: background-color var(--t-fast) var(--ease);
}
/* Logo prowadzi na Stronę główną: kafel jaśnieje pod kursorem jak pole ALT (jak w grze). */
.brand:hover .brand__logo { background-color: rgba(77,163,255,.24); }
.brand__text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.brand__name { font-family: var(--font-display); font-size: var(--fs-title); font-weight: var(--fw-black); line-height: 1.15; color: var(--txt); white-space: nowrap; }
.brand__sub { font-size: var(--fs-meta); color: var(--mut); white-space: nowrap; }

.sidebar__search {
  flex: none; margin: var(--s-3) 0;
  height: 36px; padding: 0 10px;
  display: flex; align-items: center; gap: var(--s-2);
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-s);
  color: var(--mut); cursor: text;
  transition: border-color var(--t-fast) var(--ease);
}
.sidebar__search:hover { border-color: var(--line-hover); }
.sidebar__search:focus-within { border-color: var(--blue-55); }
.sidebar__search-icon { flex: none; font-size: var(--ic-sm); color: var(--mut); }
.sidebar__search:focus-within .sidebar__search-icon { color: var(--blue); }
.sidebar__search input {
  flex: 1; min-width: 0; background: transparent; border: 0;
  color: var(--txt); font-size: var(--fs-dense); caret-color: var(--txt);
}
.sidebar__search input::placeholder { color: var(--mut); }
.sidebar__search:focus-within .kbd { display: none; }

.sidebar__nav { flex: 1; min-height: 0; overflow-y: auto; padding: 0 0 var(--s-3); }
/* Pasek menu w prawym paddingu paska bocznego (12 px): kciuk 3 px od pozycji
   i 3 px od krawędzi, pozycje równo ze szukajką z paskiem i bez — jak w grze
   (layout.css). Do 29.10.2026 pasek dotykał zaznaczonej pozycji. */
/* Tylko tam, gdzie działa ::-webkit-scrollbar (gra, Chrome, Edge, Safari):
   tam pasek zawsze zajmuje swoje 12 px. Firefox z paskami nakładanymi nie
   rezerwuje miejsca i treść weszłaby w padding rodzica. */
@supports selector(::-webkit-scrollbar) {
  .sidebar__nav { margin-right: -12px; scrollbar-gutter: stable; }
}
.nav-section { margin-bottom: 10px; }
.nav-section:last-child { margin-bottom: var(--s-1); }
.nav-section__label {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; min-height: 28px;
  padding: 6px var(--s-2) 4px 10px;
  border-radius: var(--r-xs);
  font-size: var(--fs-micro); font-weight: var(--fw-bold);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--mut); text-align: left;
  transition: color var(--t-fast) var(--ease);
}
.nav-section__label:hover { color: var(--txt-rest); }
/* Strzałka na wysokości środka wersalików podpisu: sam flex stawiał ją
   1,4 px niżej (pomiar tuszu 29.10.2026; -1 px zostawia ±0,5 px).
   `top`, nie transform — obrót przy zwijaniu zostaje wokół środka ikony. */
.nav-section__chev { position: relative; top: -1px; font-size: var(--ic-xxs); transition: transform var(--t-base) var(--ease); }
.nav-section.is-collapsed .nav-section__chev { transform: rotate(-90deg); }
.nav-section.is-collapsed > .nav { display: none; }
.sidebar.is-filtering .nav-section.is-collapsed > .nav { display: flex; }
.sidebar.is-filtering .nav-section__chev { visibility: hidden; }
.nav { list-style: none; display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; }
.nav li.is-filtered-out, .nav-section.is-filtered-out { display: none; }

.nav__item {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  height: 36px; padding: 0 10px;
  border-radius: var(--r-s);
  color: var(--txt-rest); font-size: var(--fs-body); font-weight: var(--fw-sem);
  text-decoration: none; cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.nav__icon { display: inline-flex; justify-content: center; flex: none; width: 18px; color: var(--mut); }
.nav__icon i { font-size: var(--ic-sm); }
.nav__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav__item:hover { background: var(--panel-3); color: var(--txt); }
.nav__item.is-active { background: rgba(77,163,255,.13); box-shadow: inset 0 0 0 1px rgba(77,163,255,.55); color: var(--txt); }
.nav__item.is-active .nav__icon { color: var(--blue); }

.sidebar__footer { flex: none; padding: 12px 0 14px; border-top: 1px solid var(--line); }
/* Zalogowany admin: kafel-studnia jak w grze (.sidebar-me). */
.user { display: flex; align-items: center; gap: 10px; padding: 9px 10px; margin-bottom: var(--s-2); min-width: 0; border-radius: 12px; background: var(--panel-2); border: 1px solid var(--line); }
.user .av { width: 32px; height: 32px; }
.user__meta { min-width: 0; flex: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.user__name { max-width: 100%; font-size: var(--fs-body); font-weight: var(--fw-sem); color: var(--txt); line-height: 16px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Ranga pod nickiem — znacznik w kolorze roli (app.js renderMe), ten sam
   wygląd co w stopce gry (layout.css: .rank-badge). Długa nazwa ucina się
   elipsą. Obwódka to `border`: rankStyle() (ui.js) ustawia `border-color`
   w kolorze roli, a do 30.10.2026 obwódką był niebieski cień — złoty napis
   „Właściciel” stał w niebieskiej ramce. */
.user__rank {
  display: inline-flex; align-items: center; gap: 4px;
  max-width: 100%; height: 20px; padding: 0 6px;
  border: 1px solid rgba(77,163,255,.45); border-radius: 6px;
  background: rgba(77,163,255,.13); color: var(--blue);
  font-size: var(--fs-meta); font-weight: var(--fw-bold); line-height: normal;
}
.user__rank i { flex: none; }

/* ---------------------------------------------------------
   3. Belka górna i obszar treści
   --------------------------------------------------------- */
/* Treść na najciemniejszym tle, karty (--panel) nad nim — jak w grze i w prototypie. */
.main { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: var(--bg); }

.topbar {
  flex: none; height: var(--topbar-h); padding: 0 var(--s-5);
  background: var(--bg); border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
}
.topbar__left { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
.topbar__right { display: flex; align-items: center; gap: 14px; flex: none; }
.icon-btn.topbar__menu { display: none; }

.breadcrumbs { display: flex; align-items: center; gap: 10px; font-size: var(--fs-dense); color: var(--mut); min-width: 0; }
.breadcrumbs__item { white-space: nowrap; }
.breadcrumbs__sep { font-size: var(--ic-xxs); color: var(--mut); opacity: .7; }
.breadcrumbs__item.is-current { color: var(--txt); font-weight: var(--fw-sem); overflow: hidden; text-overflow: ellipsis; }
/* Nowa nazwa zakładki „przewraca się” (app.js: route), jak w grze. */
.breadcrumbs__item.is-flip { animation: flip-text var(--t-base) var(--ease); }

.topbar__clock { font-size: var(--fs-dense); font-variant-numeric: tabular-nums; color: var(--txt-rest); white-space: nowrap; }
/* Skrót do filtra paska bocznego: mała studnia z ikoną i białym klawiszem. */
.topbar__search {
  display: inline-flex; align-items: center; gap: var(--s-2);
  height: 34px; padding: 0 8px 0 11px; border-radius: var(--r-s);
  background: var(--panel-2) !important; border: 1px solid var(--line) !important;
  color: var(--mut);
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.topbar__search i { font-size: var(--ic-sm); }
.topbar__search:hover { color: var(--txt-rest); border-color: var(--line-hover) !important; }
.topbar__divider { width: 1px; height: 20px; background: var(--line); }
.topbar__me {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 4px 8px 4px 4px; border-radius: var(--r-s);
  font-size: var(--fs-dense); color: var(--txt-rest);
  transition: background var(--t-fast) var(--ease);
}
.topbar__me:hover { background: var(--row-hover); }
.topbar__me[disabled] { cursor: default; }
.topbar__me[disabled]:hover { background: transparent; }
.av.topbar__me-av { width: 26px; height: 26px; font-size: var(--fs-micro); }
.topbar__me-name { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 1100px) { .topbar__me-name { display: none; } }
@media (max-width: 900px)  { .topbar__clock, .topbar__search .kbd { display: none; } }

/* Pigułka stanu w nagłówku zeszła na wskaźnik .connstat (components.css,
   kopia z gry). `.status-dot` zostaje — używa jej lista graczy. */
/* Bez poświaty i pulsowania (UI Kit: ruch tylko przy interakcji). */
.status-dot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--grn);
}
/* Stan „poza serwerem” wygrywa z zieloną bazą: site.css ładuje się po
   views.css, więc do 30.10.2026 kropka przy „Poza serwerem” w Zgłoszeniach
   była zielona. */
.status-dot--off { background: var(--mut); }

.content { flex: 1; min-height: 0; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
.view { display: none; }
/* Bez animacji w CSS: strona ma JEDEN `.view` na wszystkie zakładki, więc
   reguła zagrałaby tylko raz, przy starcie. Rozjaśnienie przy każdej
   zmianie zakładki i kaskadę bloków robi assets/js/motion.js. */
.view.is-active {
  --view-pad-r: var(--pad-window);
  flex: 1; min-height: 0; display: block;
  overflow-y: auto; overflow-x: hidden;
  padding: 0 var(--view-pad-r) var(--pad-window) var(--pad-window);
}
/* Pasek przewijania w prawym marginesie, jak w grze (layout.css, zasada 34):
   stałe 12 px rezerwy i o tyle mniejszy padding — treść kończy się 24 px od
   krawędzi z paskiem i bez, a zakładka, która po wczytaniu urośnie, nie
   przeskakuje w bok. Firefox z paskami nakładanymi zostaje przy 24 px. */
@supports selector(::-webkit-scrollbar) {
  .view.is-active { --view-pad-r: calc(var(--pad-window) - 12px); scrollbar-gutter: stable; }
}

/* Nagłówek strony — jak w grze: przyklejony, pełna szerokość widoku. */
.page-header {
  position: sticky; top: 0; z-index: 3;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  margin: 0 calc(-1 * var(--view-pad-r, var(--pad-window))) var(--s-4) calc(-1 * var(--pad-window));
  padding: var(--pad-window) var(--view-pad-r, var(--pad-window)) var(--s-3) var(--pad-window);
  background: var(--bg);
  flex-wrap: wrap;
}
/* Tytuł jak w grze: Archivo 800, bez kafla ikony (koncept A). Wiersz tytułu
   ma wysokość małego przycisku, więc nagłówek z odświeżaniem i bez niego jest
   tak samo wysoki (layout.css w grze). */
.page-header__title {
  margin: 0; display: inline-flex; align-items: center; gap: var(--s-3); min-height: var(--h-ctl-sm);
  font-family: var(--font-display); font-size: var(--fs-page); font-weight: var(--fw-black);
  letter-spacing: -.01em; line-height: var(--lh-tight); color: var(--txt);
}
.page-header__subtitle { margin: 4px 0 0; font-size: var(--fs-dense); color: var(--mut); max-width: 90ch; }
.page-header__actions { display: flex; align-items: center; gap: var(--s-2); flex: none; flex-wrap: wrap; }
/* Pole wyszukiwania w nagłówku ma wysokość przycisków obok niego. Nagłówek
   w grze trzyma wyłącznie małe przyciski (28 px); strona dokłada tu szukajkę,
   która miała 34 px i stała krzywo przy „Odśwież”. Zmniejszamy pole, a nie
   przyciski, bo nagłówek ma zostać tak niski jak w grze. */
.page-header__actions > .field { height: var(--h-ctl-sm); min-width: 160px; }

/* Kafel ikony przy tytule zniknął w koncepcie A, jak w grze (layout.css). */
.page-header .page-icon { display: none; }
.page-icon {
  flex: none; display: inline-grid; place-items: center;
  width: 28px; height: 28px; border-radius: var(--r-xs);
  background: var(--icon-tile); border: 1px solid rgba(255,255,255,.08);
  color: var(--icon-ink); letter-spacing: 0;
}
.page-icon i { font-size: var(--ic-sm); }

/* ODSTEP MIEDZY BLOKAMI — i tylko odstep.
   Wczesniej nazywalo sie to `.section` i bylo bledem, ktory widac bylo na
   kazdej podstronie: `.section` w gotowym zestawie (views.css, kopia z gry)
   jest KONTENEREM GRUPY — ma tlo `--sidebar-bg`, ramke, promien i padding.
   Panel dopisywal do tej samej nazwy `margin-bottom` w mysli, ze deklaruje
   wlasne utility, a w rzeczywistosci dokladal odstep do cudzego komponentu.
   Skutkiem byla ramka wokol kazdego paska narzedzi i kazdej siatki kafli,
   a przy kaflach dodatkowo trzecie tlo (regula `.section .kpi`) — czyli
   pudelko w pudelku w pudelku.
   Prefiks `u-` to konwencja gry (u-num, u-truncate); trzyma utility z dala
   od nazw komponentow. `.section` ZOSTAJE dostepne i dziala jak w grze. */
.u-stack { margin-bottom: var(--s-5); }
.section__hint { font-size: var(--fs-dense); color: var(--mut); }

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

/* Kafel liczbowy NIE MA tu wlasnego stylu i to jest cala poprawka.
   Byl `.statcard`: wlasna karta z wlasnym tlem, w ktorej siedzialo `.kpi`
   — a `.kpi` w srodku `.section` dostawalo z gry jeszcze jedno tlo. Trzy
   powierzchnie jedna na drugiej pod jedna liczba.
   Zestaw z gry ma gotowy komponent na dokladnie to (`.kpi-grid > .kpi`:
   kafel ikony, wartosc, etykieta, podpis) i on jest uzywany — patrz
   statTile() w assets/js/ui.js. Zero nowego CSS, wyglad jak w grze. */

/* ---------------------------------------------------------
   4. Stany, komunikaty, formularze
   --------------------------------------------------------- */
/* .notice (komunikat) od rundy 5 jest wspólny z grą: components.css. */

/* Stan pusty (.empty, .empty--mini, ikona, tytuł, tekst) przychodzi w całości
   z gry — components.css. Do 30.10.2026 stała tu własna baza (padding 48/24,
   bez flexa): mały stan pusty był na stronie o 62 px wyższy niż w grze. */

.formgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--s-4); }
.formgrid .rfield--wide { grid-column: 1 / -1; }
/* Pole formularza (.rfield, .rinput, select.rinput i zapas tła opcji) przychodzi
   z gry — views.css (ranks.css). Do 30.10.2026 stała tu kopia, która jako
   późniejsza wygrywała: inny padding pola i słabszy fokus (.38 zamiast .55). */
/* Stopka formularza bez kreski, jak stopka okna i sekcji (koncept A). */
.formfoot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  margin-top: var(--s-4);
}
.form-note { margin: 0 0 var(--s-4); font-size: var(--fs-dense); line-height: var(--lh-body); color: var(--text-dim); }
.form-note b { color: var(--txt); }

/* ---------------------------------------------------------
   5. Tabele — wzór `.pst` z panelu w grze
   --------------------------------------------------------- */
.tablewrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
.table th {
  position: sticky; top: 0; z-index: 1;
  text-align: left; padding: var(--s-2) var(--s-4);
  font-family: var(--font-ui); font-size: var(--fs-micro);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase; font-weight: var(--fw-bold);
  color: var(--mut); background: var(--panel); border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.table th.u-right, .table td.u-right { text-align: right; }
.table td { padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--row-hover); }
.table--click tbody tr { cursor: pointer; }
.table tr.is-blocked { background: var(--red-10); }
.table tr.is-blocked:hover { background: rgba(255,77,94,.16); }

/* ---------------------------------------------------------
   6. Awatar z kropką obecności (jak na karcie personelu w grze)
   --------------------------------------------------------- */
.av {
  position: relative; flex: none;
  width: 34px; height: 34px; border-radius: 28%;
  display: grid; place-items: center;
  /* Awatar z kitu (zasada 12): bez obwódki, jak w grze. */
  background: var(--av-bg);
  border: 0;
  font-family: var(--font-display); font-size: var(--fs-dense); font-weight: var(--fw-black); color: var(--txt);
}
.av img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.av__init { line-height: 1; }
.av__dot {
  position: absolute; right: -3px; bottom: -3px; z-index: 2;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--mut-deep); border: 2px solid var(--panel);
}
.av__dot.is-online { background: var(--grn); }
/* Niebieska = ma teraz otwarty panel WWW (a nie jest w grze). Zielona
   wygrywa, gdy ktoś jest w obu miejscach — obecność w grze jest ważniejsza
   dla pytania „kto może teraz zareagować na serwerze”. */
.av__dot.is-web { background: var(--blue); }
/* Druga linia pod nickiem z Discorda: postać, którą ktoś gra W TEJ CHWILI. */
.person__char { font-size: var(--fs-meta); color: var(--mut); white-space: nowrap; }
.person__char i { font-size: var(--ic-xxs); margin-right: 4px; color: var(--grn); }

/* ---------------------------------------------------------
   7. Logi
   --------------------------------------------------------- */
.logs-layout { display: grid; grid-template-columns: 288px minmax(0, 1fr); gap: var(--s-4); align-items: start; }

.chanpick { position: sticky; top: var(--s-4); max-height: calc(100vh - var(--topbar-h) - var(--s-6)); overflow: auto; }
.chanpick__top { padding: var(--s-3); border-bottom: 1px solid var(--line); }
.chancat { border-bottom: 1px solid var(--line); }
.chancat:last-child { border-bottom: 0; }
.chancat__head {
  width: 100%; display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3); background: none; border: 0; cursor: pointer;
  color: var(--txt-rest); font: var(--fw-sem) var(--fs-dense)/1.3 var(--font-ui); text-align: left;
}
.chancat__head:hover { background: var(--row-hover); color: var(--txt); }
.chancat__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--blue); }
.chancat__n { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--mut); }
.chancat__list { display: none; padding-bottom: var(--s-2); }
.chancat.is-open .chancat__list { display: block; }
.chancat.is-open .chancat__head { color: var(--txt); }
.chanitem {
  width: 100%; display: flex; align-items: center; gap: var(--s-2);
  padding: 6px var(--s-3) 6px var(--s-6); background: none; border: 0; cursor: pointer;
  color: var(--mut); font: var(--fw-reg) var(--fs-dense)/1.35 var(--font-ui); text-align: left;
}
.chanitem:hover { background: var(--row-hover); color: var(--txt-rest); }
.chanitem.is-active { background: var(--blue-10); color: var(--blue); box-shadow: inset 2px 0 0 var(--blue); }
.chanitem__n { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--mut-deep); }

.logrow {
  display: grid; grid-template-columns: 88px minmax(0, 1fr) auto;
  gap: var(--s-3); align-items: baseline;
  padding: var(--s-2) var(--s-4); border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.logrow:last-child { border-bottom: 0; }
.logrow:hover, .logrow.is-open { background: var(--row-hover); }
.logrow__t { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--mut); white-space: nowrap; }
.logrow__m { font-size: var(--fs-body); line-height: var(--lh-body); word-break: break-word; }
.logrow__c { display: flex; gap: var(--s-2); align-items: center; flex: none; }
.logrow__who { display: block; font-size: var(--fs-meta); color: var(--mut); margin-top: 3px; }
.logday {
  padding: var(--s-2) var(--s-4); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  background: var(--panel-2);
  font-family: var(--font-mono); font-size: var(--fs-micro);
  letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--mut);
}

.logdet { padding: 0 var(--s-4) var(--s-4) calc(88px + var(--s-3) + var(--s-4)); border-bottom: 1px solid var(--line); background: var(--bg-2, var(--sidebar-bg)); }
.logdet__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s-3) var(--s-5); padding-top: var(--s-3); }
.logdet__k { font-family: var(--font-mono); font-size: var(--fs-micro); letter-spacing: var(--ls-mono); text-transform: uppercase; color: var(--mut-deep); }
.logdet__v { font-size: var(--fs-body); color: var(--txt-rest); word-break: break-all; }

/* Pigułki w treści logu — te same kolory co tokeny w grze (js/views/logs.js). */
/* TOKENY {typ:wartość} — definicja przyjechała z gry (views.css: `.tok`
   plus paleta [data-color]). Własne reguły strony zostały skasowane: dawały
   ten sam napis w innym kolorze i bez ikony, czyli dokładnie ten rozjazd,
   któremu ma zapobiegać kopiowanie systemu projektowego. */

.sev { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; }
.sev--low  { background: var(--mut-deep); }
.sev--mid  { background: var(--blue); }
.sev--high { background: var(--org); }
.sev--crit { background: var(--red); }

.loadmore { padding: var(--s-4); text-align: center; border-top: 1px solid var(--line); }

/* ---------------------------------------------------------
   8. Rangi
   ---------------------------------------------------------
   Kreator rang (karty rang, sekcje i pola uprawnień) przychodzi w całości
   z gry — views.css (ranks.css), ten sam markup i klasy. Do 30.10.2026 stała
   tu starsza kopia, która jako późniejsza wygrywała z grą: licznik sekcji
   i „zaznaczone” w mono z obwódką, kafel priorytetu z obwódką, mniejsza
   strzałka, inne kolory tagów „hierarchia” i nieopisanego uprawnienia. */

/* ---------------------------------------------------------
   9. Modal (ustawienia kanału) — kształt jak w grze
   --------------------------------------------------------- */
/* RUCH okna (wejście animacją, wyjście przejściem) przychodzi z gry —
   views.css, reguły .modal-backdrop / .modal z world-forms.css. Tu stały
   własne, słabsze przejścia (samo przesunięcie o 8 px, bez skali), które
   jako późniejsze nadpisywały tamte: okno na stronie otwierało się inaczej
   niż to samo okno w grze. Zostaje wyłącznie układ strony. */
/* Tło, warstwa, odstęp od krawędzi, szerokość (560 px; szersze okna mają
   własną klasę albo `width` w openModal) i wygląd okna przychodzą z gry —
   views.css. Do 30.10.2026 stały tu własne: tło .62 i okno 760 px, więc to
   samo „Zbanuj” było na stronie o 200 px szersze niż w grze. Strona dokłada
   tylko przewijanie, gdy okno jest wyższe niż przeglądarka. */
.modal-backdrop { overflow: auto; }
/* Odstęp powłoki WWW musi zostawić miejsce na wąski podgląd Dokumentów. */
@media (max-width: 600px) {
  .modal-backdrop.dcm-modal { padding: 12px; }
}
/* Pole wprost w oknie (`.modal__body > .field`) ma wysokość kontrolki —
   reguła jest od 30.10.2026 wspólna z grą (components.css). */

/* ---------------------------------------------------------
   10. Ekran startowy i logowania
   --------------------------------------------------------- */
.boot {
  position: fixed; inset: 0; z-index: 70;
  display: grid; place-content: center; justify-items: center; gap: var(--s-5);
  background: var(--bg);
}
.boot__logo { opacity: .9; }
/* Ekran startowy gaśnie nad wchodzącym oknem albo ekranem logowania
   (app.js: leaveBoot usuwa go po --t-base), zamiast zniknąć jednym kadrem. */
.boot.is-leaving {
  animation: fade-out var(--t-base) var(--e-out) forwards;
  pointer-events: none;
}

/* Ekran wejścia jak okno panelu w grze i okno modalne (koncept A): tło okna
   bez poświat, karta z ramką 2 px w kolorze serwera i jaśniejszą górą,
   tytuł Archivo 800, stopka jako etykieta — bez kreski i bez mono. */
.gate {
  min-height: 100vh; display: grid; place-items: center; padding: var(--s-5);
  background: var(--bg);
}
.gate__card {
  width: min(420px, 100%); text-align: center;
  padding: var(--s-7) var(--s-6) var(--s-6);
  border-radius: var(--r-l);
  background: linear-gradient(180deg, var(--top), var(--panel) 160px);
  border: 2px solid rgba(77,163,255,.75);
  box-shadow: 0 10px 30px rgba(4,6,10,.42);
  /* To samo wejście co okno modalne (modal-in z views.css, kopia z gry). */
  animation: modal-in var(--t-enter) var(--e-in) backwards;
}
.gate__logo { width: 88px; height: 88px; object-fit: contain; margin: 0 auto var(--s-4); display: block; }
.gate__title {
  margin-bottom: var(--s-2);
  font-family: var(--font-display); font-size: var(--fs-page); font-weight: var(--fw-black);
  letter-spacing: -.01em; color: var(--txt);
}
.gate__text { color: var(--mut); font-size: var(--fs-body); max-width: 32ch; margin: 0 auto var(--s-5); line-height: var(--lh-body); }
.gate__foot {
  margin-top: var(--s-5);
  font-size: var(--fs-micro); font-weight: var(--fw-bold);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase; color: var(--mut);
}
/* Ikona stanu w kaflu 46 px (promień 13) jak kafel stanu pustego, ale w tonie
   ostrzeżenia (.14, kit: „kafel ikony w tonie”) — do 30.10.2026 goła ikona,
   jedyna w panelu bez kafla. */
.gate__icon {
  width: 46px; height: 46px;
  margin: 0 auto var(--s-4); display: grid; place-items: center;
  border-radius: 13px;
  background: rgba(var(--org-rgb), .14);
  font-size: var(--ic-lg); color: var(--org);
}
/* Logowanie przez Discorda: pole ALT w kolorze Discorda (tło .13, obwódka .55,
   biały napis, znak w kolorze), jak każdy przycisk w panelu. Do 30.10.2026
   pełne fioletowe wypełnienie — jedyny pełny kolorowy przycisk (kit go zakazuje). */
.btn--discord {
  --btn-rgb: 88, 101, 242;
  width: 100%; justify-content: center;
}
/* Znak Discorda w jaśniejszym odcieniu tonu: #5865F2 na ciemnym tle jest za ciemny. */
.btn--discord svg { color: #8B95F7; }

/* ---------------------------------------------------------
   11. Powiadomienia
   ---------------------------------------------------------
   Własnego wyglądu tu NIE MA: stos to `.notify-stack` z `.pc-notification`
   z gry (views.css, map-tools.css) — ta sama karta, kafel ikony, pasek czasu,
   wjazd z boku i wyjście (ui.js: toast). Strona przestawia tylko miejsce:
   w grze stos liczy róg okna panelu, tu okno to cała strona — prawy górny
   róg pod belką. Do września 2026 stały tu `.toasts` / `.toast`: inny
   kształt, dolny róg i znikanie skokiem. */
.notify-stack { --notify-top: 72px; --notify-right: 0px; }

/* ---------------------------------------------------------
   12. Responsywność
   --------------------------------------------------------- */
@media (max-width: 1200px) {
  .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .logs-layout, .ranks-layout { grid-template-columns: 1fr; }
  .chanpick { position: static; max-height: none; }
}

@media (max-width: 900px) {
  /* Pasek boczny wysuwany — ten sam wzorzec co w grze przy wąskim oknie. */
  .app { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; left: 0; top: 0; bottom: 0;
    width: var(--sidebar-w);
    transform: translateX(-100%);
    transition: transform var(--t-base) var(--ease);
  }
  .app.is-mobile-open .sidebar { transform: none; }
  .app.is-mobile-open .backdrop { display: block; animation: fade-in var(--t-base) var(--e-in); }
  .icon-btn.topbar__menu { display: inline-flex; }
  .topbar { padding: 0 var(--s-4); }
  .view.is-active { --view-pad-r: var(--s-4); padding: 0 var(--view-pad-r) var(--s-4) var(--s-4); }
  @supports selector(::-webkit-scrollbar) {
    .view.is-active { --view-pad-r: calc(var(--s-4) - 12px); }
  }
  .page-header { margin: 0 calc(-1 * var(--view-pad-r)) var(--s-4) calc(-1 * var(--s-4)); padding: var(--s-4) var(--view-pad-r) var(--s-3) var(--s-4); }
  .grid--4, .grid--3, .grid--2 { grid-template-columns: 1fr; }
  .logrow { grid-template-columns: minmax(0, 1fr) auto; }
  .logrow__t { grid-column: 1 / -1; }
  .logdet { padding-left: var(--s-4); }
}

/* Zębatka przy kanale w drzewie — pojawia się przy najechaniu, żeby lista
   kanałów zostawała listą kanałów, a nie rzędem ikon. */
.chanitem__gear { flex: none; color: var(--mut-deep); opacity: 0; transition: opacity var(--t-fast) var(--ease); }
.chanitem:hover .chanitem__gear { opacity: 1; }
.chanitem__gear:hover { color: var(--blue); }


/* ---------------------------------------------------------
   13. Profil administratora
   --------------------------------------------------------- */
/* Nazwa działania kolorowana rodziną: kara / pomoc / narzędzie. Kolor niesie
   informację, której nie da się odczytać z nazwy — „Przewrócenia"
   i „Reanimacje" wyglądają na liście tak samo, a znaczą co innego.
   Ta sama reguła obowiązuje w panelu w grze (css/views/staff-players.css). */
.apr__act { font-weight: var(--fw-sem); }
.apr__act[data-family="kara"]      { color: var(--red); }
.apr__act[data-family="pomoc"]     { color: var(--grn); }
.apr__act[data-family="narzedzie"] { color: var(--cyn); }
.apr__act[data-family="inne"]      { color: var(--dim); }
.apr__key { display: block; font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--mut-deep); }

/* ---------------------------------------------------------
   14. Kanał komend: Gracze i Moderacja
   ---------------------------------------------------------
   Widoki, które rozmawiają z serwerem gry (PLATFORM.md §11). Kształt
   przycisków, chipów i tabel przychodzi z systemu projektowego (components.css
   kopiowane z gry) — tutaj są WYŁĄCZNIE rzeczy, których w nim nie ma: siatka
   akcji, lista klucz–wartość i drobne narzędzia tekstowe. */

/* Narzędzia tekstowe. Były używane w widokach (u-mut w „Administracji")
   zanim ktokolwiek je zdefiniował — stąd tutaj, a nie w kopiowanym
   components.css, gdzie zniknęłyby przy najbliższej synchronizacji. */
.u-mut, .u-dim { color: var(--mut); }
.u-strong { font-weight: var(--fw-sem); color: var(--txt); }
.u-small  { font-size: var(--fs-dense); }
.u-bad    { color: var(--red); }
.u-ok     { color: var(--grn); }
tr.is-muted { opacity: .55; }

/* Licznik przy chipie filtra — liczba w nawiasie czytałaby się jak część
   etykiety, więc dostaje własne tło. */
.chip__count {
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(255,255,255,.07);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  color: var(--dim);
}
.chip.is-active .chip__count { background: rgba(255,255,255,.14); color: inherit; }

/* Siatki akcji na graczu tu NIE MA i to jest cała poprawka: `.action-group`
   i `.action-grid` przyjeżdżają gotowe z zestawu (views.css, kopia z gry).
   Panel miał obok własne `.actions-group` / `.actions-grid` — te same reguły
   pod nazwą różniącą się jedną literą, więc profil gracza wyglądał podobnie,
   ale nigdy tak samo. Podświetlenie włączonego przełącznika jest w zestawie. */

/* Lista klucz–wartość w profilu gracza. */
.kvlist { display: flex; flex-direction: column; gap: 6px; }
.kv {
  display: flex; align-items: baseline; gap: var(--s-3);
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}
.kv:last-child { border-bottom: 0; padding-bottom: 0; }
.kv__k { flex: none; width: 120px; font-size: var(--fs-dense); color: var(--mut); }
.kv__v { min-width: 0; word-break: break-all; }

/* Kartoteka: bany i ostrzeżenia jednym strumieniem, jak w grze. */
/* Wpis to studnia z odstępem 4 px, jak .modh-row w profilu w grze. */
.feed { display: flex; flex-direction: column; gap: 4px; }
.feed__item {
  display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap;
  padding: var(--s-2) var(--s-3);
  border-radius: 12px;
  background: var(--panel-2);
}

/* Etykieta pola w oknie akcji. */
.form__label {
  display: block;
  margin-bottom: 6px;
  font-size: var(--fs-dense);
  font-weight: var(--fw-sem);
  color: var(--dim);
}

/* ---------------------------------------------------------
   15. Prymitywy układu współdzielone z panelem w grze
   ---------------------------------------------------------
   Trzy reguły z pc_admin/css/layout.css, których NIE kopiuje
   scripts/sync-design.mjs — bo layout.css jako całość opisuje okno leżące
   na grze (pasek boczny 260 px, wysokość 660 px), a strona ma własny układ.
   Te trzy są jednak potrzebne widokom, które przyjechały razem z views.css
   (nagłówek logów), więc stoją tutaj. Gdyby kiedyś urosły, przenieś je
   po stronie gry do components.css i skasuj stąd. */
.page-header__lead { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
.page-header__copy { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.page-header__lead .page-header__title { margin: 0; }
.page-header__lead .page-header__subtitle { margin: 0; }
/* .section-title przychodzi z components.css (Archivo 800, jak w grze);
   kopia z grubością 700 zostawała tu po rundzie 3 i ją nadpisywała. */

/* ---------------------------------------------------------
   16. Rozkład prac (Statystyki)
   ---------------------------------------------------------
   Panel w grze rysuje w tym miejscu wykres SVG na całą szerokość okna.
   Strona dostaje węższą kolumnę i pełne przewijanie, więc te same liczby
   czytają się lepiej jako lista pasków — jedno spojrzenie i widać, która
   praca dominuje, bez najeżdżania na punkty wykresu. */
.bars { display: flex; flex-direction: column; gap: 6px; }
.bar { display: grid; grid-template-columns: 150px minmax(0, 1fr) 48px; align-items: center; gap: var(--s-3); }
.bar__name { font-size: var(--fs-dense); color: var(--dim); }
.bar__track { height: 8px; border-radius: 999px; background: rgba(255,255,255,.05); overflow: hidden; }
.bar__fill { display: block; height: 100%; border-radius: 999px; background: var(--blue); }
.bar__n { text-align: right; font-family: var(--font-mono); font-size: var(--fs-dense); }

@media (max-width: 720px) {
  .bar { grid-template-columns: 110px minmax(0, 1fr) 40px; }
}

/* ---------------------------------------------------------
   17. Widoki wypełniające ekran (mapa, monitorowanie, zgłoszenia…)
   ---------------------------------------------------------
   Widoki przeniesione z gry mają `flex: 1` na swoim pojemniku (`.map-wrap`,
   `.mon-layout`, `.rep-layout`), bo w grze rodzicem jest kolumna flex
   o wysokości okna. Tutaj rodzicem był `#viewRoot` — zwykły blok — więc
   `flex: 1` nie miało na czym zadziałać i mapa kurczyła się do wysokości
   swojej treści, czyli szyny bocznej. Stąd „mapa jest malutka”.

   Jedna linijka to naprawia dla wszystkich naraz: `#viewRoot` staje się
   kolumną flex wypełniającą pole przewijania, a `flex: 1` znów znaczy
   „zajmij resztę wysokości pod nagłówkiem”, dokładnie jak w grze. */
#viewRoot {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}
/* Strona główna wypełnia okno, a jej karty przewijają się W ŚRODKU — tak jak
   w grze (widok „pane”). Klasę zakłada i zdejmuje views/dashboard.js. */
#viewRoot.is-fill { height: 100%; }
#viewRoot.is-fill > #dashRoot { flex: 1 1 auto; min-height: 460px; display: flex; flex-direction: column; }   /* próg jak w grze (dashboard-reports.css) */
/* Stała wysokość pozwala przewijać listę wewnątrz szyny i utrzymuje
   stopki podglądów w oknie, także gdy lista ma wielu graczy. */
#viewRoot.monitoring-view { height: 100%; min-height: 0; }

/* WWW przewija stronę, a wspólna tabela Moderacji tylko własne kolumny. */
#viewRoot.mod-view > #mdBody { min-width: 0; }
.mod-view .mod-tabs { flex: none; }
.mod-view #mdTab { min-width: 0; }
.mod-view #modList { min-width: 0; }
.mod-view .mod-table-scroll { max-height: 640px; }
/* Lista i „Nadaj karę” mają wysokość treści — przewija się strona, nie sekcja.
   Wąskie okno przeglądarki stawia formularz pod listą. */
.mod-view .mod-layout { align-items: start; }

/* Administracja: filtr składu to lista z kitu (.select + .cselect) w kontenerze
   #staffFilters, szeroka jak w grze. */
.prv-view #staffFilters { flex: 0 1 220px; min-width: 0; }
.prv-view #staffFilters > .select { width: 100%; }
@media (max-width: 1000px) { .mod-view .mod-layout { grid-template-columns: minmax(0, 1fr); } }

/* WWW ma przewijaną stronę; długie listy Policji przewijają się wewnątrz karty. */
#viewRoot.pol-view > #polBody { min-width: 0; }
.pol-view .pol-layout { align-items: start; overflow: visible; }
.pol-view .pol-list { max-height: 620px; overflow: auto; }

/* Dolna granica na wypadek bardzo niskiego okna: mapa 260 px wysokości jest
   bezużyteczna, lepiej niech strona się przewinie. */
.map-wrap, .mon-layout, .rep-layout { min-height: 460px; }
.chat-layout, .items-layout { min-height: 420px; }

/* Pasek błędu na kafelku podglądu. Zostaje ostatni dobry obraz, a pod nim
   powód, dla którego nie ma nowszego — cichy brak klatek wygląda jak
   zawieszony panel, a nie jak problem z klientem gracza. */
.mon-tile__err {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 4px var(--s-2);
  background: rgba(255,77,94,.16);
  border-top: 1px solid var(--red-26);
  color: #FFC2C8;
  font-size: var(--fs-meta);
  text-align: center;
}
