/* ================================================================
   STUDIO MANAGER - FRAMEWORK CSS
   Zunifikowana biblioteka komponentów UI
   v1.1

   SPIS TREŚCI:
   1.  RESET & CSS CUSTOM PROPERTIES (tokens, dark-mode, scales)
   2.  BASE & TYPOGRAPHY (focus-visible, reduced-motion, a11y)
   3.  LAYOUT (sidebar + topbar + main)
   3a. PRZEJSCIA MIEDZY STRONAMI (View Transitions)
   4.  SIDEBAR
   5.  TOPBAR
   6.  DROPDOWN MENUS
   7.  PAGE HEADER & BREADCRUMB
   8.  PANEL (uniwersalny kontener + sloty)
   9.  KPI CARDS
   10. ANALYTICS ROW (chart + report)
   11. TABLE SYSTEM
   12. BUTTONS
   13. BADGES
   14. FORMS (field, input, select, textarea, input-group)
   15. TOGGLE SWITCH (full + mini)
   16. TABS (segmented + page-tabs)
   17. STATUS CARDS (radio cards)
   18. MODAL & OVERLAY
   19. STICKY ACTION BAR
   20. REVEAL / COLLAPSE
   21. LAYOUT HELPERS (2-col form, grid, row)
   21b. TREŚĆ SFORMATOWANA (.rich-text - HTML z edytora Quill)
   22. FOOTER
   23. DATATABLES OVERRIDES
   24. DANGER ZONE
   25. UPLOAD AREA
   26. COLOR PICKER
   27. FORM GRID (settings-style)
   28. HOURS LIST
   29. DOTS MENU
   30. STAT BAR
   31. BULK ACTION BAR
   32. TOAST
   33. EMPTY STATE
   34. LOADING SKELETON
   35. ALERT
   36. SETTINGS PAGE LAYOUT
   37. PLUGIN: TOM SELECT
   38. PLUGIN: FLATPICKR
   39. PLUGIN: FULLCALENDAR
   40. AVATAR UPLOAD
   41. ROLE BADGE
   42. CUSTOM CHECKBOX
   43. RADIO PILLS
   44. TAG CHIPS
   45. DETAIL TABS
   46. FILTER GROUP
   47. CALENDAR (custom week/month grid)
   48. SPOTS INPUT (number stepper)
   49. FLOAT PANEL (sticky preview)
   50. UTILITIES (spacing, text, display, flex)
   51. RESPONSIVE - TABLET & MOBILE
   52. QUILL RICH TEXT EDITOR
   ================================================================ */


/* ================================================================
   1. RESET & CSS CUSTOM PROPERTIES
   ================================================================ */

*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  /* ── Kolory bazowe ── */
  --bg:             #f9f9f9;
  --surface:        #ffffff;
  --border:         #e7eaee;
  --text-primary:   #4b5563;       /* WCAG AA: 7.5:1 vs #fff (było #666 = 5.7:1) */
  --text-secondary: #6b7480;       /* WCAG AA: 4.6:1 (było #8b95a1 = 3.5:1, fail) */
  /* WYLACZNIE dekoracja: placeholder, ikona, tlo. Kontrast 3.0:1 na bieli
     nie spelnia WCAG AA (4.5:1) - na tresci merytorycznej uzywaj
     --text-secondary, nawet gdy tekst ma byc wizualnie stonowany. */
  --text-muted:     #8b95a1;
  --teal:           #1baf92;
  --blue:           #2f8de4;
  --light-blue:     #d9e8f6;
  --light-gray:     #f9f9f9;
  --success:        #1baf92;
  --warning:        #f4b04f;
  --danger:         #ef6b73;

  /* ── Tokeny semantyczne (preferowane w nowym kodzie) ── */
  --color-primary:        var(--teal);
  --color-primary-hover:  color-mix(in srgb, var(--teal) 88%, #000);
  --color-primary-active: color-mix(in srgb, var(--teal) 78%, #000);
  --color-on-primary:     #ffffff;
  --color-on-danger:      #ffffff;
  --color-accent:         var(--blue);
  --color-accent-hover:   color-mix(in srgb, var(--blue) 88%, #000);
  --color-success:        var(--success);
  --color-success-hover:  color-mix(in srgb, var(--success) 88%, #000);
  --color-warning:        var(--warning);
  --color-warning-hover:  color-mix(in srgb, var(--warning) 88%, #000);
  --color-danger:         var(--danger);
  --color-danger-hover:   color-mix(in srgb, var(--danger) 88%, #000);

  /* ── Accent dim (tło pod akcent) ── */
  --accent-dim:     rgba(27,175,146,.11);
  --danger-dim:     rgba(239,107,115,.10);
  --warning-dim:    rgba(244,176,79,.13);
  --success-dim:    rgba(33,200,122,.13);
  --blue-dim:       rgba(47,141,228,.10);

  /* ── Ink: tekst i ikony NA tle *-dim ──
     Moduły zaszywały tu ciemne odcienie (#7a5200, #c98a00, #b23b43...),
     bo na jasnym tincie sam --warning / --danger jest za jasny na tekst.
     W ciemnym motywie ta sama wartość znika, więc ink przełącza stronę:
     ciemniejszy od bazy na świetle, jaśniejszy w ciemności. */
  --accent-ink:     color-mix(in srgb, var(--teal) 80%, #000);
  --warning-ink:    #8a5b00;
  --danger-ink:     #b8353f;
  --success-ink:    #10795f;
  --blue-ink:       #1a6fd4;
  /* Indygo nie jest kolorem marki, ale wraca w odznakach i osiach czasu. */
  --indigo:         #6c7de8;
  --indigo-dim:     rgba(108,125,232,.12);
  --indigo-ink:     #5b4fb8;

  /* Drugi poziom powierzchni: nagłówek tabeli, blok zagnieżdżony w karcie. */
  --surface-2:      #f7f8fa;

  /* ── Layout ── */
  --sidebar-w:      235px;
  --topbar-h:       64px;
  --transition:     .22s ease;
  /* Wysokosc slotu w grafiku - realna wartosc wylicza main.js i ustawia na
     <html>. Ten fallback pilnuje, zeby linie siatki nie znikaly, zanim JS
     zdazy policzyc (calc() z niezdefiniowana zmienna odrzuca cala regule). */
  --slot-h:         30px;

  /* ── Skala radius ── */
  --radius-sm:   4px;
  --radius:      8px;     /* default (alias do --radius-md) */
  --radius-md:   8px;
  --radius-lg:  12px;
  --radius-xl:  16px;
  --radius-full: 999px;

  /* ── Skala spacing (4-bazowa) ── */
  --sp-0:   0;
  --sp-1:   4px;
  --sp-2:   8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  20px;
  --sp-6:  24px;
  --sp-8:  32px;
  --sp-10: 40px;
  --sp-12: 48px;

  /* ── Skala cieni ── */
  --shadow-xs: 0 1px 2px rgba(16,24,40,.04);
  --shadow-sm: 0 1px 2px rgba(16,24,40,.05);
  --shadow:    0 1px 3px rgba(16,24,40,.06), 0 1px 2px rgba(16,24,40,.04);
  --shadow-md: 0 4px 14px rgba(0,0,0,.08);
  --shadow-lg: 0 8px 24px rgba(16,24,40,.10);
  --shadow-xl: 0 20px 60px rgba(0,0,0,.18);

  /* ── Skala z-index ── */
  --z-base:               1;
  --z-sticky:           100;
  --z-topbar:           199;
  --z-sidebar:          200;
  --z-overlay-backdrop: 299;
  --z-sidebar-mobile:   300;
  --z-modal:            400;
  --z-dropdown:         500;
  --z-bulk:             600;
  --z-toast:           9500;
  --z-popover:         9999;

  /* ── Stos dolnych pasków - jedna zmienna na warstwę ──
     Wartość ustawia OBECNOŚĆ elementu (`body:has`), nie klasa panelu: panel
     bez danego paska dostaje 0px sam z siebie i żadna warstwa wyżej nie musi
     wiedzieć, w którym jest panelu. Nowy pasek to nowa zmienna i jeden wiersz
     `body:has()`, zamiast edycji czterech pozostałych.

     Kolejność od dołu: nawigacja -> pasek zapisu -> pasek trybu -> pasek
     pomocy. Toast stoi na GÓRZE ekranu (sekcja 19) i celowo nie należy do
     tego stosu: na dole musiałby ustępować czterem warstwom, a przy
     `--z-toast: 9500` i tak przykryłby pigułkę trybu pomocy.

     `--fw-safe-b`: wcięcie ekranu płaci WYŁĄCZNIE najniższa warstwa. Gdy
     istnieje dolna nawigacja, zmienna zeruje się dla wszystkiego powyżej -
     inaczej każda warstwa doliczałaby te same 34 px drugi raz.

     UWAGA: nie deklaruj tu sumy typu `--fw-stack-bottom: calc(a + b)`.
     Właściwość niestandardowa rozwiązuje `var()` przeciw wartościom
     odziedziczonym NA TYM elemencie, a składniki ustawia dopiero
     `body:has(...)` - na `:root` obie są 0px i suma zamarza na zero. */
  --fw-safe-b:     env(safe-area-inset-bottom, 0px);

  /* Klawiatura ekranowa - OSOBNA warstwa, poza `body:has()`.
     Ustawia ja `main.js` z `visualViewport`; poza telefonem zostaje 0px.

     NIE nalezy do `body:has(.mobile-bottom-nav)`: klawiatura jest otwarta
     takze na stronach BEZ dolnej nawigacji - logowanie klienta, 2FA, reset
     hasla, `/rejestracja` i `/z/{token}`, czyli dokladnie tam, gdzie stoi
     sie w polu przez caly czas wypelniania formularza.

     NIE doliczaja jej `.main` ani zaden element, ktory sie przewija: tam
     dalaby skok ukladu i martwy pas wysokosci klawiatury przy kazdym
     wejsciu w pole. Czytaja ja WYLACZNIE warstwy przypiete do dolu. */
  --fw-stack-kb:   0px;

  --fw-stack-nav:  0px;
  --fw-stack-save: 0px;
  --fw-stack-mode: 0px;

  /* ── Klawiatura ekranowa ──
     Wartość pisze main.js (razem z `--fw-stack-kb`, odczyt w JS: `Flowla.kbInset()`)
     z `window.visualViewport`.
     Bez JS i bez klawiatury zostaje 0px, więc każde `calc()` niżej liczy
     się dokładnie tak, jak przed tą zmianą.

     PO CO: iOS NIE zmniejsza layout viewportu przy otwarciu klawiatury.
     `window.innerHeight`, `100dvh`, `position: fixed` i `bottom: 0` dalej
     mierzą do dolnej krawędzi EKRANU, czyli spod klawiatury. Leżą tam
     wszystkie rzeczy, które ustawiły etapy 2, 3 i 6: stopka modala
     (arkusz dolny), pasek zapisu, pasek trybu, chmurka pomocy
     i zaproszenie PWA. Klawiatura zakrywa je w komplecie.

     To LIŚĆ, nie suma - w odróżnieniu od `--fw-stack-*` wolno ją czytać
     w dowolnym `calc()`, bo nikt jej nie składa z innych zmiennych. */
  --kb-inset: 0px;

  /* ── Focus ring ── */
  --focus-ring: 0 0 0 3px var(--accent-dim);

  /* ── Skala typograficzna ── */
  --fs-3xs:  9px;
  --fs-2xs: 10px;
  --fs-xs:  11px;
  --fs-sm:  12px;
  --fs-nav: 13px;
  --fs-base:14px;
  --fs-md:  16px;
  --fs-lg:  18px;
  --fs-xl:  20px;
  --fs-2xl: 28px;

  /* ── Tokeny kolorów wydarzeń kalendarza (override per-projekt) ── */
  --cal-yoga-bg:       #FFF8ED;
  --cal-yoga-border:   #C9A96E;
  --cal-yoga-text:     #B8913D;
  --cal-pilates-bg:    #EAF9F6;
  --cal-pilates-border:#1BAF92;
  --cal-pilates-text:  #148A73;
  --cal-dance-bg:      #FFF7E6;
  --cal-dance-border:  #F4A833;
  --cal-dance-text:    #C97D10;
  --cal-workshop-bg:   #F5EEFF;
  --cal-workshop-border:#8E5BC4;
  --cal-workshop-text: #6E3FA8;

  color-scheme: light;
}

/* ── Dark mode (opt-in: <html data-theme="dark">) ── */
[data-theme="dark"] {
  --bg:             #0f1115;
  --surface:        #161a22;
  --border:         #2a2f3a;
  --text-primary:   #e5e7eb;
  --text-secondary: #aab1bc;
  --text-muted:     #6b7480;
  --light-gray:     #1f2330;
  --light-blue:     #1c2c44;

  --accent-dim:     rgba(27,175,146,.18);
  --danger-dim:     rgba(239,107,115,.18);
  --warning-dim:    rgba(244,176,79,.18);
  --success-dim:    rgba(27,175,146,.18);
  --blue-dim:       rgba(47,141,228,.18);

  --accent-ink:     color-mix(in srgb, var(--teal) 72%, #fff);
  --warning-ink:    #f0bd6a;
  --danger-ink:     #f39098;
  --success-ink:    #4fd8b4;
  --blue-ink:       #7fb8f2;
  --indigo-dim:     rgba(108,125,232,.20);
  --indigo-ink:     #a5b0f2;

  --surface-2:      #1c212b;

  --shadow-xs: 0 1px 2px rgba(0,0,0,.30);
  --shadow-sm: 0 1px 2px rgba(0,0,0,.40);
  --shadow:    0 1px 3px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.30);
  --shadow-md: 0 4px 14px rgba(0,0,0,.45);
  --shadow-lg: 0 8px 24px rgba(0,0,0,.50);
  --shadow-xl: 0 20px 60px rgba(0,0,0,.60);

  color-scheme: dark;
}

/* ── Auto dark - siatka bezpieczeństwa dla wyłączonego JS ──
   Normalnie o motywie decyduje skrypt w <head> (views/layouts/partials/
   theme-init.php): czyta localStorage, a gdy nic tam nie ma - pyta system
   i ZAWSZE ustawia data-theme na <html>. Ten blok łapie więc tylko sytuację
   bez JS-a: preferencja systemu działa, dopóki nikt nie wymusił jasnego. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:             #0f1115;
    --surface:        #161a22;
    --border:         #2a2f3a;
    --text-primary:   #e5e7eb;
    --text-secondary: #aab1bc;
    --text-muted:     #6b7480;
    --light-gray:     #1f2330;
    --light-blue:     #1c2c44;

    --accent-dim:     rgba(27,175,146,.18);
    --danger-dim:     rgba(239,107,115,.18);
    --warning-dim:    rgba(244,176,79,.18);
    --success-dim:    rgba(27,175,146,.18);
    --blue-dim:       rgba(47,141,228,.18);

    --accent-ink:     color-mix(in srgb, var(--teal) 72%, #fff);
    --warning-ink:    #f0bd6a;
    --danger-ink:     #f39098;
    --success-ink:    #4fd8b4;
    --blue-ink:       #7fb8f2;
    --indigo-dim:     rgba(108,125,232,.20);
    --indigo-ink:     #a5b0f2;

    --surface-2:      #1c212b;

    --shadow-xs: 0 1px 2px rgba(0,0,0,.30);
    --shadow-sm: 0 1px 2px rgba(0,0,0,.40);
    --shadow:    0 1px 3px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.30);
    --shadow-md: 0 4px 14px rgba(0,0,0,.45);
    --shadow-lg: 0 8px 24px rgba(0,0,0,.50);
    --shadow-xl: 0 20px 60px rgba(0,0,0,.60);

    color-scheme: dark;
  }
}

/* ── Reduced motion - wyłącz animacje gdy user prosi ── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ================================================================
   2. BASE & TYPOGRAPHY
   ================================================================ */

body {
  font-family: 'Inter', sans-serif;
  background: var(--bg);
  color: var(--text-primary);
  font-size: var(--fs-base);
  line-height: 1.5;
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { text-decoration: none; color: inherit; }
ul { list-style: none; }

/* ── Focus-visible - keyboard-only ring (mysz nie podświetla) ── */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Screen-reader only helper */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}

/* ── Skip link - pierwszy Tab pomija sidebar i topbar ──
   Widoczny wylacznie po sfokusowaniu; z-index ponad topbarem i sidebarem,
   inaczej wysuwalby sie pod nie. */
.skip-link {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: var(--z-popover);
  padding: 10px 16px;
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-radius: var(--radius);
  font-size: var(--fs-nav);
  font-weight: 600;
  box-shadow: var(--shadow-lg);
  transform: translateY(-200%);
  transition: transform .18s ease;
}
.skip-link:focus,
.skip-link:focus-visible {
  transform: translateY(0);
  outline: 2px solid var(--color-on-primary);
  outline-offset: -4px;
}

/* Kotwica skip linku nie moze rysowac ramki wokol calej strony. */
.main[tabindex="-1"]:focus { outline: none; }


/* ================================================================
   3a. PRZEJSCIA MIEDZY STRONAMI (View Transitions)
   ================================================================

   Panel jest klasycznym MPA - kazde klikniecie w menu to pelne przeladowanie
   dokumentu. Bialy blysk miedzy stronami czytal sie jak zaciecie, choc sama
   strona wracala szybko.

   `@view-transition { navigation: auto }` kaze przegladarce zrobic zrzut
   starego dokumentu, poczekac na nowy i przejsc miedzy nimi animacja. Zero JS
   i zero zmian w PHP. Firefox tego jeszcze nie ma - tam zostaje zwykle
   przeladowanie, bez bledu i bez fallbacku do napisania.

   Sidebar, topbar i dolna nawigacja dostaja wlasne `view-transition-name`,
   wiec wypadaja ze zrzutu korzenia i NIE biora udzialu w przenikaniu: stoja
   w miejscu, jak w aplikacji jednostronicowej. Przenika wylacznie tresc.
   Nazwa musi byc unikalna w dokumencie, wiec kazda wisi na pojedynczym
   elemencie layoutu.
   ================================================================ */

@view-transition { navigation: auto; }

.sidebar           { view-transition-name: fw-sidebar; }
.topbar            { view-transition-name: fw-topbar; }
.mobile-bottom-nav { view-transition-name: fw-mobilenav; }
.toast-container   { view-transition-name: fw-toasts; }
.imp-bar           { view-transition-name: fw-impbar; }
.pa-bar            { view-transition-name: fw-pabar; }

/* Grupa chromu bez animacji: dopoki trwa przejscie, zywy DOM nie jest
   malowany. Domyslne 250 ms grupy przedluzaloby caly ruch, choc sidebar
   i topbar maja w obu dokumentach identyczna geometrie i nie ma co animowac. */
::view-transition-group(fw-sidebar),
::view-transition-group(fw-topbar),
::view-transition-group(fw-mobilenav),
::view-transition-group(fw-toasts),
::view-transition-group(fw-impbar),
::view-transition-group(fw-pabar)  { animation-duration: 0s; }

/* Chrom panelu podmienia sie natychmiast. Bez animacji nie ma czego zauwazyc,
   a zmiana aktywnego linku w menu nie rozmywa sie przez pol sekundy. */
::view-transition-old(fw-sidebar),
::view-transition-old(fw-topbar),
::view-transition-old(fw-mobilenav),
::view-transition-old(fw-toasts),
::view-transition-old(fw-impbar),
::view-transition-old(fw-pabar)   { animation: none; opacity: 0; }

::view-transition-new(fw-sidebar),
::view-transition-new(fw-topbar),
::view-transition-new(fw-mobilenav),
::view-transition-new(fw-toasts),
::view-transition-new(fw-impbar),
::view-transition-new(fw-pabar)   { animation: none; opacity: 1; }

/* Tresc: krotkie wyjscie, nieco dluzsze wejscie z minimalnym podniesieniem.
   Cale przejscie trwa 160 ms (najdluzsza animacja) - powyzej ok. 250 ms ruch
   zaczyna sie czytac jako spowolnienie panelu, a nie jako plynnosc. */
::view-transition-old(root) { animation: fw-page-out  90ms ease-in both; }
::view-transition-new(root) { animation: fw-page-in  160ms cubic-bezier(.22, .61, .36, 1) both; }

@keyframes fw-page-out { to   { opacity: 0; } }
@keyframes fw-page-in  { from { opacity: 0; transform: translateY(6px); } }

/* Blok reduced-motion wyzej tnie czasy animacji do .01ms, ale samego przejscia
   nie wylacza - a to ono odpowiada za ruch. Gasimy je u zrodla. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}


/* ================================================================
   3. LAYOUT (sidebar + topbar + main)
   ================================================================ */

.sidebar {
  position: fixed;
  left: 0; top: 0;
  width: var(--sidebar-w);
  height: 100vh;
  height: 100dvh;
  z-index: var(--z-sidebar);
  transition: transform var(--transition);
}

.topbar {
  position: fixed;
  top: 0;
  left: var(--sidebar-w);
  right: 0;
  height: var(--topbar-h);
  z-index: var(--z-topbar);
  transition: left var(--transition);
}

.main {
  margin-left: var(--sidebar-w);
  margin-top: var(--topbar-h);
  /* Dolny zapas = oddech + wysokosc przypietego paska zapisu
     (`--fw-stack-save`: 63 px przy `.action-bar` / `.settings-save-bar`,
     0 bez paska). Jedno miejsce dla calego panelu - widoki i moduly NIE
     dokladaja wlasnego `padding-bottom` pod pasek. Wczesniej kazdy uklad
     zgadywal swoja liczbe (80 / 88 / 96 px) i przerwa pod ostatnim panelem
     wynosila raz 40, raz 57, raz 73 px. */
  padding: 24px 24px calc(5px + var(--fw-stack-save)) 24px;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  transition: margin-left var(--transition);
}

/* Collapsed sidebar */
body.sidebar-collapsed .sidebar  { transform: translateX(-100%); }
body.sidebar-collapsed .topbar   { left: 0; }
body.sidebar-collapsed .main     { margin-left: 0; }

/* Panel obslugi i panel platformy nie maja stopki - odstep od dolu ekranu
   dawal wczesniej jej padding. Panel klienta stopke ma, wiec zostaje przy
   5 px bazowych. Zmiana tej wartosci wymaga podbicia stalej `chromeParts()`
   w Calendar/assets/calendar.js - siatka grafiku liczy paddingi .main wprost. */
body.admin-panel .main,
body.owner-panel .main { padding-bottom: calc(24px + var(--fw-stack-save)); }


/* ================================================================
   4. SIDEBAR
   ================================================================ */

.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--border);
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  padding-bottom: 24px;
}

.sidebar::-webkit-scrollbar       { width: 4px; }
.sidebar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

/* Logo */
.sidebar-logo {
  display: flex; align-items: center; justify-content: center;
  gap: 10px; padding: 16px 20px;
  font-size: var(--fs-lg); font-weight: 700; color: var(--text-primary);
  border-bottom: 1px solid var(--border); flex-shrink: 0;
}

.sidebar-logo .logo-icon {
  width: 32px; height: 32px; background: var(--teal); border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: var(--fs-md); font-weight: 700; flex-shrink: 0;
}

.sidebar-logo img {
  display: block; max-width: 100%; width: auto; max-height: 150px; object-fit: contain;
}

/* Logo platformy w ciemnym motywie.
   Napis "flowla" jest neutralnym ciemnym szarym, wiec na ciemnym tle ginie -
   liscia to nie dotyczy, bo teal ma kontrast w obie strony. logo-dark.png to
   ten sam plik z przemalowanymi pikselami o zerowej saturacji (#E9ECEF),
   alfa nietknieta. Filtr CSS tu nie wchodzi: rozjasnilby takze lisc.

   Podmiana idzie selektorem atrybutu, a nie druga kotwica w widoku - logo stoi
   w 20 miejscach (trzy sidebary, ekrany logowania, /z/{token}, podglady maili)
   i kazde z nich wypisuje View::logoUrl(). Regula trafia w wartosc domyslna;
   logo wgrane przez studio idzie z /media/pub/ i zostaje nietkniete - obcego
   pliku nie mamy jak przemalowac, a studio z ciemnym logo poprawia je u siebie. */
[data-theme="dark"] img[src$="/assets/img/logo.png"] {
  content: url("../img/logo-dark.png");
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) img[src$="/assets/img/logo.png"] {
    content: url("../img/logo-dark.png");
  }
}

/* Nav */
.sidebar-nav { padding-top: 8px; }

.sidebar-section-label {
  font-size: var(--fs-xs); font-weight: 700; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.8px; padding: 18px 20px 6px;
  pointer-events: none;
}

.sidebar-nav-item { position: relative; }

.sidebar-link {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 20px; margin: 0 10px; border-radius: 6px;
  color: var(--text-secondary); font-size: var(--fs-nav); cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.sidebar-link:hover { background: var(--bg); color: var(--text-primary); }

.sidebar-link.active {
  background: var(--accent-dim); color: var(--teal);
  font-weight: 600; border-left: 4px solid var(--teal);
}

.sidebar-link .bi { font-size: var(--fs-md); flex-shrink: 0; opacity: .7; width: 18px; text-align: center; }
.sidebar-link.active .bi { opacity: 1; }

.sidebar-badge {
  margin-left: auto; background: var(--danger); color: #fff;
  font-size: var(--fs-2xs); padding: 1px 7px; border-radius: 999px; font-weight: 600;
}

.sidebar-arrow {
  margin-left: auto; font-size: var(--fs-sm); opacity: .5;
  transition: transform var(--transition); flex-shrink: 0;
}

.sidebar-nav-item.open > .sidebar-link .sidebar-arrow { transform: rotate(180deg); opacity: .8; }

/* Submenu */
.sidebar-submenu { overflow: hidden; max-height: 0; transition: max-height .3s ease; }
.sidebar-nav-item.open > .sidebar-submenu { max-height: 600px; }

.sidebar-submenu .sidebar-sublink {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 20px 7px 40px;
  font-size: var(--fs-nav); color: var(--text-secondary);
  cursor: pointer; transition: color var(--transition);
}

.sidebar-submenu .sidebar-sublink .bi { flex-shrink: 0; opacity: .7; width: 18px; text-align: center; }
.sidebar-submenu .sidebar-sublink.active .bi { opacity: 1; }

.sidebar-submenu .sidebar-sublink:hover  { color: var(--text-primary); }
.sidebar-submenu .sidebar-sublink.active { color: var(--teal); font-weight: 600; }


/* ================================================================
   5. TOPBAR
   ================================================================ */

.topbar {
  background: var(--surface); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; padding: 0 24px; gap: 8px;
}

/* Hamburger */
.topbar-hamburger {
  width: 34px; height: 34px; background: var(--teal); border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  color: #fff; cursor: pointer; flex-shrink: 0; border: none;
  transition: background var(--transition);
}

.topbar-hamburger .bi { font-size: var(--fs-xl); line-height: 1; display: block; }
.topbar-hamburger:hover { background: var(--color-primary-hover); }

/* Search */
.input-search {
  display: flex; align-items: center; background: var(--bg);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0 12px 0 14px; gap: 8px; width: 240px; height: 34px;
  transition: border-color .18s, box-shadow .18s;
}

.input-search:focus-within { border-color: var(--teal); box-shadow: 0 0 0 3px var(--accent-dim); }
.input-search .bi-search   { color: var(--text-muted); font-size: var(--fs-base); flex-shrink: 0; }
.input-search input         { border: none; background: none; outline: none; font-size: var(--fs-nav); color: var(--text-primary); font-family: inherit; width: 100%; }
.input-search input::placeholder { color: var(--text-muted); }

.input-search-clear {
  display: none; align-items: center; justify-content: center;
  border: 0; background: transparent; width: 18px; height: 18px;
  cursor: pointer; padding: 0; flex-shrink: 0; color: var(--text-secondary);
  font-size: 11px; line-height: 1; transition: background 0.15s;
}
.input-search-clear.visible { display: flex; }

/* ── Wyszukiwarka klienta (Flowla.clientPicker) ──
   Panel wyników pod polem .input-search. Wyjęty z modala kalendarza, bo ten
   sam wybór klienta robią trzy miejsca panelu: zapis na zajęcia, wizyta 1:1
   i przyznanie punktów. Rodzic musi mieć position: relative. */
/* Panel wyników wisi absolutnie pod polem - opakowanie musi być punktem
   odniesienia, inaczej ląduje na krawędzi najbliższego rodzica z position. */
.client-picker-wrap { position: relative; }

.client-picker-drop {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 220px;
  overflow-y: auto;
  z-index: 10;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0 0 8px 8px;
  box-shadow: var(--shadow-md, 0 4px 12px rgba(0, 0, 0, .1));
}

.client-picker-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: background .12s;
}
.client-picker-item:hover,
.client-picker-item.is-active { background: var(--bg); }
.client-picker-item:last-child { border-bottom: none; }

.client-picker-avatar { background: var(--teal); flex-shrink: 0; }
.client-picker-text   { flex: 1; min-width: 0; }
.client-picker-name   { font-weight: 600; font-size: var(--fs-nav, 13px); }
.client-picker-meta {
  font-size: var(--fs-xs, 11px);
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Stan pośredni: ładowanie, za krótka fraza, brak wyników */
.client-picker-msg {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  font-size: var(--fs-xs, 12px);
  color: var(--text-secondary);
}
.client-picker-msg .bi { font-size: var(--fs-base, 14px); color: var(--text-muted); }

/* Okno modalne przycina zawartość (.modal { overflow: hidden }) dla zaokrąglonych
   rogów, a panel wyników wychodzi poniżej pola. Tam, gdzie stoi wyszukiwarka,
   przycinanie musi ustąpić - inaczej podpowiedzi ucinają się w połowie. */
.modal:has(.client-picker-drop) { overflow: visible; }
.client-picker-spin {
  color: var(--color-primary) !important;
  animation: spin .9s linear infinite;
}

/* Top navigation */
.topbar-nav { display: flex; align-items: center; gap: 4px; margin-left: 12px; }

.topbar-nav-item {
  position: relative; padding: 6px 12px; font-size: var(--fs-nav);
  color: var(--text-secondary); cursor: pointer;
  display: flex; align-items: center; gap: 5px;
  border-radius: 6px;
  transition: background var(--transition), color var(--transition);
  user-select: none;
}

.topbar-nav-item:hover,
.topbar-nav-item.open { background: var(--bg); color: var(--text-primary); }

.topbar-nav-item > .bi-chevron-down { font-size: var(--fs-xs); transition: transform var(--transition); }
.topbar-nav-item.open > .bi-chevron-down { transform: rotate(180deg); }

/* Right side */
.topbar-right { display: flex; align-items: center; gap: 4px; margin-left: auto; }

.topbar-icon {
  width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
  color: var(--text-secondary); cursor: pointer; font-size: var(--fs-lg); line-height: 1;
  border-radius: 50%; border: none; background: none; outline: none; padding: 0;
  transition: background var(--transition), color var(--transition); position: relative;
}

.topbar-icon:hover { background: var(--bg); color: var(--text-primary); }

/* Waznosc konta - chip widoczny tylko dla roli rozliczeniowej (topbar.php).
   Kolor wylacznie wzmacnia komunikat: liczba dni stoi w tekscie, wiec bez
   odczytu koloru nic nie ginie (WCAG 1.4.1). Tokeny *-dim / *-ink maja wlasne
   wartosci w ciemnym motywie, wiec chip nie wymaga osobnej reguly. */
.topbar-plan {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 12px 0 10px; margin-right: 4px;
  border-radius: 10px; text-decoration: none;
  font-weight: 500; white-space: nowrap;
  transition: filter var(--transition);
}

.topbar-plan:hover { filter: brightness(.96); }
.topbar-plan .bi { font-size: var(--fs-md); line-height: 1; }

/* Dwa wiersze zamiast jednego: nadwiersz mowi, CO sie konczy, dolny - KIEDY.
   Sama liczba dni ("Zostały 4 dni") nie niosla przedmiotu zdania. Waga 600
   zostaje wylacznie w dolnym wierszu - pogrubiony calosc chipu krzyczal
   mocniej niz waga sprawy w 7. dniu przed koncem. */
.topbar-plan-text  { display: flex; flex-direction: column; gap: 1px; line-height: 1.25; }
.topbar-plan-label { font-size: var(--fs-2xs); font-weight: 500; opacity: .8; }
.topbar-plan-value { font-size: var(--fs-xs);  font-weight: 600; }

.topbar-plan[data-level="warning"] { background: var(--warning-dim); color: var(--warning-ink); }
.topbar-plan[data-level="danger"]  { background: var(--danger-dim);  color: var(--danger-ink); }

/* Ostatnie 3 dni, karencja i konto bez daty: pelne wypelnienie zamiast
   przygaszonego tla - najwiekszy skok wagi wizualnej w topbarze, bez ruchu.
   Puls gra 3 cykle po wejsciu na strone i milknie: staly ruch w pasku
   widocznym na kazdym ekranie przez caly dzien pracy przestaje byc czytany
   po godzinie, a WCAG 2.2.2 wymagalby przycisku zatrzymania powyzej 5 s. */
.topbar-plan[data-level="critical"] {
  background: var(--danger); color: #fff;
  animation: topbar-plan-pulse 700ms ease-out 3;
}

.topbar-plan[data-level="critical"]:hover { filter: brightness(1.06); }

@keyframes topbar-plan-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 45%, transparent); }
  70%  { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* Kolor i ikona niosa komunikat same - puls jest wylacznie wzmocnieniem */
@media (prefers-reduced-motion: reduce) {
  .topbar-plan[data-level="critical"] { animation: none; }
}

/* Do 900px zostaje sama ikona - obok dzwonka i avatara nie ma miejsca na tekst */
@media (max-width: 900px) {
  .topbar-plan { padding: 0; width: 36px; border-radius: 50%; justify-content: center; }
  .topbar-plan-text { display: none; }
}

.topbar .notif-badge {
  min-width: 16px; height: 16px; background : var(--teal); color: #fff;
  font-size: var(--fs-3xs); border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; padding: 0 4px 0 5px; pointer-events: none;
  line-height: 0; text-align: center; box-sizing: border-box;
  position: absolute;
  top: 2px;
  right: 0;
}

.sidebar .notif-badge {
  min-width: 16px; height: 16px; background : var(--teal); color: #fff;
  font-size: var(--fs-3xs); border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; padding: 0 3px; pointer-events: none;
  line-height: 16px; text-align: center; box-sizing: border-box;
}

.notif-badge.red { background: var(--danger); }

/* display:flex powyzej wygrywa z UA [hidden] - bez tego badge nie da sie schowac */
.notif-badge[hidden], .notif-dd-count[hidden] { display: none; }

/* Notification dropdown */
.topbar-notif {
  position: relative; cursor: pointer;
}

.notif-dropdown {
  right: 0; width: 400px; max-width: calc(100vw - 24px); padding: 0;
  display: flex; flex-direction: column; overflow: hidden;
}

.notif-dropdown-header {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 12px 14px 12px 16px; border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.notif-dropdown-title {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-sm); font-weight: 700; color: var(--text-primary);
}

.notif-dd-count {
  display: inline-block; min-width: 20px; height: 18px; padding: 0 6px; border-radius: 999px;
  background: var(--danger); color: #fff;
  font-size: var(--fs-3xs); font-weight: 700; line-height: 18px; text-align: center;
}

.notif-dd-readall {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-xs); font-weight: 500; color: var(--text-secondary);
  background: none; border: none; border-radius: 6px; padding: 5px 8px; cursor: pointer;
  transition: background var(--transition), color var(--transition);
  white-space: nowrap;
}
.notif-dd-readall:hover:not(:disabled) { background: var(--bg); color: var(--color-primary, var(--teal)); }
.notif-dd-readall:disabled { opacity: .4; cursor: default; }

.notif-dropdown-list {
  max-height: min(380px, 60vh); overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--light-gray) transparent;
  flex: 1 1 auto;
}
.notif-dropdown-list::-webkit-scrollbar { width: 6px; }
.notif-dropdown-list::-webkit-scrollbar-track { background: transparent; }
.notif-dropdown-list::-webkit-scrollbar-thumb { background: var(--light-gray); border-radius: 999px; }
.notif-dropdown-list::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

.notif-dropdown-footer {
  border-top: 1px solid var(--border); flex-shrink: 0;
}

.notif-dropdown-all {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 11px 16px; font-size: var(--fs-xs); font-weight: 600;
  color: var(--color-primary, var(--teal));
  transition: background var(--transition);
}
.notif-dropdown-all:hover { background: var(--bg); text-decoration: none; }
.notif-dropdown-all .bi { font-size: var(--fs-2xs); transition: transform var(--transition); }
.notif-dropdown-all:hover .bi { transform: translateX(2px); }

/* Naglowek dnia - przykleja sie przy scrollu, zeby kontekst czasowy nie ginal */
.notif-dd-group {
  position: sticky; top: 0; z-index: 1;
  padding: 7px 16px; background: var(--bg);
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-secondary);
}

.notif-dd-item {
  position: relative; display: flex; align-items: stretch;
  border-bottom: 1px solid var(--border);
  transition: background var(--transition);
}
.notif-dd-item:last-child { border-bottom: none; }
.notif-dd-item:hover { background: var(--bg); }
.notif-dd-item.unread { background: color-mix(in srgb, var(--color-primary, var(--teal)) 5%, transparent); }
.notif-dd-item.unread:hover { background: color-mix(in srgb, var(--color-primary, var(--teal)) 9%, transparent); }

.notif-dd-link {
  display: flex; align-items: flex-start; gap: 10px;
  flex: 1; min-width: 0; padding: 11px 14px 11px 16px;
  color: inherit; text-decoration: none;
}
.notif-dd-link:hover { text-decoration: none; }
.notif-dd-link:focus-visible {
  outline: 2px solid var(--color-primary, var(--teal)); outline-offset: -2px; border-radius: 4px;
}

.notif-dd-icon {
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); color: var(--text-secondary); font-size: 14px; flex-shrink: 0;
}
.notif-dd-icon.tone-client   { background: var(--blue-dim);    color: var(--blue); }
.notif-dd-icon.tone-pass     { background: var(--success-dim); color: var(--success); }
.notif-dd-icon.tone-schedule { background: var(--warning-dim); color: var(--warning); }
.notif-dd-icon.tone-payment  { background: var(--danger-dim);  color: var(--danger); }
.notif-dd-icon.tone-waitlist { background: color-mix(in srgb, var(--color-primary) 10%, transparent); color: var(--color-primary); }
.notif-dd-icon.tone-message  { background: var(--blue-dim);    color: var(--blue); }
.notif-dd-icon.tone-loyalty  { background: var(--success-dim); color: var(--success); }
.notif-dd-icon.tone-system   { background: var(--bg);          color: var(--text-secondary); }

.notif-dd-body { display: block; flex: 1; min-width: 0; }

.notif-dd-title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: var(--fs-sm); font-weight: 500; color: var(--text-primary); line-height: 1.4;
}
.notif-dd-item.unread .notif-dd-title { font-weight: 600; }

.notif-dd-text {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: var(--fs-xs); color: var(--text-secondary); line-height: 1.45; margin-top: 2px;
}

.notif-dd-time {
  display: block; font-size: var(--fs-xs); color: var(--text-secondary); margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

.notif-dd-dot {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; margin-top: 12px;
  background: var(--color-primary, var(--teal));
}

/* Nakladka, nie kolumna - tresc nie przeskakuje miedzy stanem przeczytany/nie */
.notif-dd-mark {
  position: absolute; top: 8px; right: 8px;
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  background: var(--surface); border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
  color: var(--text-secondary); font-size: 13px; opacity: 0;
  transition: opacity var(--transition), background var(--transition), color var(--transition);
}
.notif-dd-item:hover .notif-dd-mark,
.notif-dd-mark:focus-visible { opacity: 1; }
.notif-dd-mark:hover {
  background: var(--bg); color: var(--color-primary, var(--teal));
  border-color: var(--color-primary, var(--teal));
}
.notif-dd-mark:focus-visible { outline: 2px solid var(--color-primary, var(--teal)); outline-offset: 1px; }

/* Szkielet ladowania - stabilna wysokosc zamiast skaczacego spinnera */
.notif-dd-skel { display: flex; align-items: center; gap: 10px; padding: 12px 16px; }
.notif-dd-skel .sk-icon { width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0; }
.notif-dd-skel .sk-lines { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.notif-dd-skel .sk-line { height: 9px; border-radius: 4px; }
.notif-dd-skel .sk-line.short { width: 45%; }
.notif-dd-skel .sk-icon,
.notif-dd-skel .sk-line {
  background: linear-gradient(90deg, var(--bg) 25%, var(--light-gray) 37%, var(--bg) 63%);
  background-size: 400% 100%; animation: notifShimmer 1.3s ease infinite;
}

@keyframes notifShimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@keyframes spin { to { transform: rotate(360deg); } }

.notif-dd-empty {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  text-align: center; padding: 36px 20px; color: var(--text-secondary); font-size: var(--fs-2xs);
}
.notif-dd-empty .bi { font-size: 26px; color: var(--light-gray); margin-bottom: 4px; }
.notif-dd-empty strong { font-size: var(--fs-xs); font-weight: 600; color: var(--text-secondary); }

.notif-dd-retry {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 10px;
  padding: 6px 12px; border: 1px solid var(--border); border-radius: 6px;
  background: none; color: var(--text-secondary); font-size: var(--fs-2xs); cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.notif-dd-retry:hover { background: var(--bg); color: var(--color-primary, var(--teal)); border-color: var(--color-primary, var(--teal)); }
.notif-dd-retry .bi { font-size: var(--fs-2xs); color: inherit; margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .notif-dd-skel .sk-icon,
  .notif-dd-skel .sk-line { animation: none; }
  .notif-dropdown-all:hover .bi { transform: none; }
}

/* Language selector */
.topbar-lang {
  display: flex; align-items: center; gap: 5px; cursor: pointer;
  font-size: var(--fs-nav); color: var(--text-secondary);
  padding: 5px 8px; border-radius: 6px;
  transition: background var(--transition); position: relative; user-select: none;
}

.topbar-lang:hover { background: var(--bg); }

.flag {
  display: inline-block; width: 20px; height: 14px; border-radius: 2px;
  border: 1px solid var(--light-gray); flex-shrink: 0;
  background-size: cover; background-position: center; vertical-align: middle;
}

.flag-en { background: linear-gradient(180deg, #c8102e 33%, #fff 33% 66%, #012169 66%); }
.flag-pl { background: linear-gradient(180deg, #fff 50%, #dc143c 50%); }
.flag-de { background: linear-gradient(180deg, #000 33%, #d00 33% 66%, #ffce00 66%); }

.lang-chevron { font-size: var(--fs-2xs); transition: transform var(--transition); }
.topbar-lang.open .lang-chevron { transform: rotate(180deg); }

.lang-dropdown { right: 0; min-width: 120px; padding: 4px 0; }

.lang-option {
  display: flex; align-items: center; gap: 8px; padding: 8px 14px;
  font-size: var(--fs-nav); color: var(--text-secondary); cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.lang-option:hover  { background: var(--bg); color: var(--text-primary); }
.lang-option.active { color: var(--text-primary); font-weight: 600; }

/* User button */
.topbar-user {
  position: relative; display: flex; align-items: center; gap: 8px;
  cursor: pointer; padding: 5px 10px 5px 16px;
  border-left: 1px solid var(--border); margin-left: 4px;
  border-radius: 0 6px 6px 0;
  transition: background var(--transition); user-select: none;
}

.topbar-user:hover, .topbar-user.open { background: var(--bg); }
.topbar-user:focus, .topbar-notif:focus, .topbar-nav-item:focus, .topbar-lang:focus { outline: none; }
/* Elementy topbara to divy z tabindex, a na nich przegladarka zapala :focus-visible
   takze po klikinieciu mysza - stad ramka przy zwyklym uzyciu. Zamiast obwodki
   podswietlamy tlem: mysz i tak trzyma hover (wiec nic nie miga), a nawigacja
   Tabem nadal pokazuje, gdzie jest kursor. */
.topbar-user:focus-visible, .topbar-notif:focus-visible, .topbar-nav-item:focus-visible, .topbar-lang:focus-visible {
  outline: none; background: var(--bg); border-radius: 6px;
}

.topbar-user .avatar {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--teal); display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: var(--fs-nav); flex-shrink: 0;
}
.topbar-user img.avatar { object-fit: cover; }

.topbar-user .name { font-size: var(--fs-nav); font-weight: 600; color: var(--text-primary); }

.topbar-user > .bi-chevron-down {
  font-size: var(--fs-xs); color: var(--text-secondary);
  transition: transform var(--transition);
}

.topbar-user.open > .bi-chevron-down { transform: rotate(180deg); }


/* ================================================================
   6. DROPDOWN MENUS
   ================================================================ */

.dropdown-menu {
  position: absolute; top: calc(100% + 8px);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  z-index: var(--z-dropdown); opacity: 0; transform: translateY(-6px);
  pointer-events: none; transition: opacity .18s ease, transform .18s ease, visibility .18s;
  min-width: 180px;
  /* visibility, nie tylko opacity - zamkniete menu nie moze lapac Taba
     ani byc czytane przez czytnik ekranu (offsetWidth nadal dziala) */
  visibility: hidden;
}

.dropdown-menu.open { opacity: 1; transform: translateY(0); pointer-events: auto; visibility: visible; }

/* Mega Menu */
.mega-menu {
  left: 0; min-width: 520px; padding: 20px;
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;
}

.mega-col-title {
  font-size: var(--fs-xs); font-weight: 700; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.7px; padding: 0 0 8px 8px;
  grid-column: 1 / -1;
}

.mega-link {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: 6px;
  font-size: var(--fs-nav); color: var(--text-secondary);
  transition: background var(--transition), color var(--transition);
}

.mega-link:hover    { background: var(--bg); color: var(--text-primary); }
.mega-link .bi      { font-size: var(--fs-md); color: var(--teal); }

/* Mega Menu – hidden link (user toggled off) */
.mega-link.mega-link-hidden { display: none; }

/* Mega Menu – edit mode */
.mega-menu-header {
  grid-column: 1 / -1;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 4px 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px;
}

.mega-menu-header-title {
  font-size: var(--fs-xs); font-weight: 700; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.7px;
}


/* Edit panel overlay */
/* Katalog skrotow obejmuje caly panel (ponad 20 pozycji), wiec lista edycji
   musi sie przewijac - inaczej stopka z "Zapisz" ucieka poza ekran.
   Przewija sie .mega-edit-list, nie panel: pasek przewijania konczy sie nad
   stopka, zamiast schodzic wzdluz przyciskow Resetuj/Zapisz. */
.mega-edit-panel {
  grid-column: 1 / -1;
  display: none; flex-direction: column; gap: 0;
  padding-top: 4px;
  max-height: 52vh; overflow: hidden;
}

.mega-edit-list {
  display: flex; flex-direction: column; gap: 0;
  min-height: 0; flex: 1 1 auto;
  overflow-y: auto;
}
.mega-menu.mega-editing .mega-edit-panel { display: flex; }
.mega-menu.mega-editing .mega-link { display: none; }
.mega-menu.mega-editing .mega-col-title { display: none; }

/* Edit item row */
.mega-edit-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 8px; border-radius: 6px;
  font-size: var(--fs-nav); color: var(--text-primary);
  transition: background .12s ease;
  cursor: grab; user-select: none;
}
.mega-edit-item:hover { background: var(--bg); }
.mega-edit-item.mega-dragging {
  opacity: .4; background: var(--bg);
}

.mega-edit-item .mega-drag-handle {
  color: var(--text-secondary); font-size: 14px; cursor: grab;
  flex-shrink: 0;
}
.mega-edit-item .mega-drag-handle:hover { color: var(--text-primary); }

.mega-edit-item .bi { font-size: var(--fs-md); color: var(--teal); flex-shrink: 0; }

.mega-edit-item-label { flex: 1; }

.mega-edit-toggle {
  position: relative; width: 34px; height: 18px;
  background: var(--border); border-radius: 9px; border: none;
  cursor: pointer; transition: background .2s ease; flex-shrink: 0;
}
.mega-edit-toggle::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: #fff; transition: transform .2s ease;
}
.mega-edit-toggle.active { background: var(--teal); }
.mega-edit-toggle.active::after { transform: translateX(16px); }

/* Edit footer - poza obszarem przewijania, wiec bez sticky */
.mega-edit-footer {
  display: flex; justify-content: space-between; align-items: center;
  padding-top: 10px; margin-top: 4px; border-top: 1px solid var(--border);
  grid-column: 1 / -1; flex-shrink: 0;
  background: var(--surface);
}


/* Drag-over indicator */
.mega-edit-item.mega-drag-over {
  /* Kreska miejsca upuszczenia rysowana cieniem, nie ramka. `border-top: 2px`
     dokladal wiersz o 2 px wyzszy, wiec przy kazdym wejsciu kursora lista
     skakala w dol i w gore - element spod kursora uciekal, a wskaznik migal. */
  box-shadow: inset 0 2px 0 0 var(--teal);
}

/* User dropdown */
.user-dropdown { right: 0; min-width: 200px; padding: 8px 0; }

.user-dropdown-header {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px 12px; border-bottom: 1px solid var(--border); margin-bottom: 4px;
}

.user-dropdown-header .avatar-sm {
  width: 38px; height: 38px; border-radius: 50%; background: var(--teal);
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: var(--fs-base); font-weight: 700;
}

.user-dropdown-header .ud-name { font-size: var(--fs-sm); font-weight: 700; }
.user-dropdown-header .ud-email { font-size: var(--fs-2xs); color: var(--text-secondary); margin-top: 1px; }
.user-dropdown-header .ud-role {
  font-size: var(--fs-2xs); color: var(--text-secondary);
  display: inline-flex; align-items: center; gap: 4px; margin-top: 4px;
}
.user-dropdown-header .ud-role .bi { font-size: 11px; color: var(--teal); }

.dropdown-link {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px; font-size: var(--fs-sm); color: var(--text-secondary);
  transition: background var(--transition), color var(--transition);
}

.dropdown-link:hover         { background: var(--bg); color: var(--text-primary); }
.dropdown-link .bi           { font-size: var(--fs-md); width: 18px; text-align: center; }
.dropdown-divider            { height: 1px; background: var(--border); margin: 4px 0; }
.dropdown-link.danger        { color: var(--danger); }
.dropdown-link.danger:hover  { background: var(--danger-dim); color: var(--danger); }

.dropdown-section {
  padding: 8px 16px 4px;
  font-size: var(--fs-2xs); font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: .04em;
}

button.dropdown-link {
  width: 100%; border: none; background: none;
  text-align: left; font-family: inherit; cursor: pointer;
}


/* ================================================================
   7. PAGE HEADER & BREADCRUMB
   ================================================================ */

.page-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 20px;
}

.page-header h1 { font-size: var(--fs-md); font-weight: 700; letter-spacing: 0.5px; }

.breadcrumb {
  /* Zawija sie: przy 320 px trzy czlony z dluzszym biezacym wychodzily poza ekran (F6, BUG-wk-03) */
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px;
  font-size: var(--fs-sm); color: var(--text-secondary);
}

.breadcrumb span         { cursor: pointer; }
.breadcrumb span:hover   { color: var(--text-primary); }
.breadcrumb .sep {
  display: inline-block; width: 16px; height: 16px;
  vertical-align: middle; opacity: .5;
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path stroke='none' d='M0 0h24v24H0z' fill='none'/><path d='M9 6l6 6l-6 6'/></svg>") no-repeat center / contain;
  cursor: default;
}
.breadcrumb .current { color: var(--text-primary); font-weight: 600; }


/* ================================================================
   8. PANEL (uniwersalny kontener treści)
   
   Użycie:
   <div class="page-panel">
     <div class="panel-header">
       <div class="panel-icon"><i class="bi bi-gear"></i></div>
       <h2 class="panel-title">Tytuł</h2>
     </div>
     <div class="panel-body"> ... </div>
   </div>
   ================================================================ */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  margin-bottom: 16px;
  display: flex;
  flex-direction: column;
}

.panel-header {
  padding: 13px 20px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
}

.panel-header h2,
.panel-header .panel-title {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-secondary);
  letter-spacing: .08em;
  text-transform: uppercase;
  flex: 1;
}

/* Meta naglowka panelu: licznik, data, zakres, "(opcjonalnie)".
   Regula jest BAZOWA, nie zagniezdzona w `.panel-title` - ten sam span stoi
   rownie czesto w slocie `.panel-right`, gdzie bez wlasnej reguly dziedziczyl
   14 px z `body` i stal wiekszy oraz ciemniejszy od 12-pikselowego tytulu obok.
   Wlasciwosci dziedziczone z `.panel-title` (wersaliki, waga 700, odstep liter)
   zerujemy tutaj - regula na elemencie zawsze wygrywa z dziedziczeniem. */
.muted-tag {
  font-size: var(--fs-sm);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  /* Niesie tresc (liczniki, daty, "(opcjonalnie)"), wiec --text-secondary -
     --text-muted ma 3.0:1 i jest zarezerwowany dla dekoracji */
  color: var(--text-secondary);
}

/* W tytule stoi w jednej linii z napisem, wiec potrzebuje odstepu.
   W slocie `.panel-right` odstep daje `gap` naglowka */
.panel-title .muted-tag { margin-left: 6px; }

/* Wariant z dużym tytułem (np. page-panel-header) */
.panel-header-lg h2,
.panel-header-lg .panel-title {
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: 0;
  text-transform: none;
}

.panel-header-lg {
  padding: 16px 24px;
}

.panel-icon {
  width: 26px; height: 26px; border-radius: 5px;
  background: var(--accent-dim); color: var(--teal);
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; flex-shrink: 0;
}

.panel-icon.danger  { background: var(--danger-dim);  color: var(--danger); }
.panel-icon.warning { background: var(--warning-dim); color: var(--warning); }
.panel-icon.success { background: var(--success-dim); color: var(--success); }
.panel-icon.blue    { background: var(--blue-dim);    color: var(--blue); }

.panel-body {
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  flex: 1;
}

.panel-body-compact { padding: 16px; gap: 12px; }
.panel-body-flush   { padding: 0; gap: 0; }

.panel-footer {
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

/* Separator wewnątrz panelu */
.panel-divider {
  height: 1px;
  background: var(--border);
  margin: 0 -20px;
}

/* Toolbar */
.panel-toolbar { display: flex; align-items: center; padding: 10px 16px; gap: 12px; flex-wrap: nowrap; }

/* ── panel SLOTS: panel-left / panel-center / panel-right ───────────
   Uniwersalne sloty toolbara. Działa w .panel-header oraz .panel-toolbar.

   <div class="panel-header">
     <div class="panel-icon"><i class="bi bi-gear"></i></div>
     <h2 class="panel-title">Tytuł</h2>
     <div class="panel-left">   <input class="input" placeholder="Szukaj"> </div>
     <div class="panel-center"> <select class="select ts-select">...</select> </div>
     <div class="panel-right">  <button class="btn btn-primary">Dodaj</button> </div>
   </div>

   Każdy slot opcjonalny. Dystrybucja przez auto-marginesy:
   panel-left obok tytułu, panel-center pośrodku, panel-right przy prawej.
   TomSelect ma stałą szerokość (domyślnie 180px); nadpis przez panel-w-sm/md/lg/full.
   ──────────────────────────────────────────────────────────────── */

/* `flex-wrap: wrap`, nie `nowrap`.
   Slot filtrow na `/admin/clients` to 300 + 4x200 + 4x8 = 1132 px. Przy oknie
   1280 px na tresc naglowka zostaje ok. 955 px, a nadmiar 177 px byl OBCINANY
   przez `.panel { overflow: hidden }` - filtry Karnet, Karta i Trener byly
   fizycznie niedostepne juz na laptopie, nie tylko na telefonie. Zawijanie
   przenosi je do drugiego wiersza zamiast kasowac.
   `flex-shrink` zostaje zerowy: kurczenie scisnieloby wyszukiwarke do
   kilkunastu pikseli zamiast przeniesc filtr nizej. */
.panel-left,
.panel-center,
.panel-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  flex-shrink: 0;
  min-width: 0;
}

.panel-left   { margin-right: auto; }
/* Bez sufitu szerokosci `flex-wrap` w slocie nie mial czego zawijac: slot
   rozpychal sie do sumy filtrow (1200 px w Dzienniku), a `.panel` ucinal
   reszte na telefonie i tablecie (BUG-mobA-05). */
.panel-left   { max-width: 100%; }
.panel-center { margin-left: auto; margin-right: auto; }
.panel-right  { margin-left: auto; }

/* Tytuł nie rozpycha headera, gdy są sloty */
.panel-header:has(.panel-left, .panel-center, .panel-right) .panel-title,
.panel-header:has(.panel-left, .panel-center, .panel-right) h2 {
  flex: 0 0 auto;
}

/* Sloty mogą zawijać na wąskim ekranie */
.panel-header { flex-wrap: wrap; row-gap: 8px; }

/* Kontrolki wewnątrz slotów: równa wysokość, sensowne szerokości */
.panel-left  .input, .panel-center .input, .panel-right .input,
.panel-left  .select, .panel-center .select, .panel-right .select {
  height: 34px;
  font-size: var(--fs-sm);
}

.panel-left  .input, .panel-center .input, .panel-right .input { width: 220px; max-width: 100%; }

/* TomSelect: stała szerokość (TS rozpycha się do 100%) */
.panel-left   .ts-wrapper,
.panel-center .ts-wrapper,
.panel-right  .ts-wrapper { width: 180px; min-width: 0; flex-shrink: 0; }

/* Helpery szerokości - daj klasę na slot LUB bezpośrednio na .ts-wrapper / .input */
.panel-w-sm   .ts-wrapper, .ts-wrapper.panel-w-sm,
.panel-w-sm   .input,      .input.panel-w-sm      { width: 140px; }
.panel-w-md   .ts-wrapper, .ts-wrapper.panel-w-md,
.panel-w-md   .input,      .input.panel-w-md      { width: 200px; }
.panel-w-lg   .ts-wrapper, .ts-wrapper.panel-w-lg,
.panel-w-lg   .input,      .input.panel-w-lg      { width: 280px; }
.panel-w-full .ts-wrapper, .ts-wrapper.panel-w-full,
.panel-w-full .input,      .input.panel-w-full    { width: 100%; }

/* Buttony nie zawijają tekstu */
.panel-left .btn, .panel-center .btn, .panel-right .btn { white-space: nowrap; flex-shrink: 0; }


/* ================================================================
   9. KPI CARDS
   ================================================================ */

.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 18px; margin-bottom: 16px;
}

.kpi-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow);
}

.kpi-top {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 16px;
}

.kpi-title { font-size: var(--fs-base); font-weight: 600; color: var(--text-primary); }

.kpi-badge {
  font-size: var(--fs-xs); padding: 2px 10px; border-radius: var(--radius-full);
  background: var(--accent-dim); color: var(--teal); font-weight: 600;
}

.kpi-body  { display: flex; align-items: center; gap: 16px; }
.kpi-donut { flex-shrink: 0; }
.kpi-value { font-size: var(--fs-2xl); font-weight: 700; color: var(--text-primary); }
.kpi-label { font-size: var(--fs-sm); color: var(--text-secondary); margin-top: 2px; }


/* ================================================================
   10. ANALYTICS ROW (chart + report)
   ================================================================ */

.analytics-row {
  display: grid; grid-template-columns: 1fr 340px;
  gap: 0; margin-bottom: 20px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}

.chart-panel { padding: 20px 24px; border-right: 1px solid var(--border); }

.chart-panel-header {
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px;
}

.chart-panel-header h2 { font-size: var(--fs-md); font-weight: 700; }

.chart-area       { width: 100%; }

/* Report page-panel */
.report-panel { padding: 0; display: flex; flex-direction: column; }

.report-tabs {
  display: flex; border-bottom: 1px solid var(--border); padding: 0 20px;
}

.report-tab {
  padding: 14px 16px; font-size: var(--fs-nav); color: var(--text-secondary);
  cursor: pointer; border-bottom: 2px solid transparent; font-weight: 600;
  transition: color var(--transition), border-color var(--transition);
}

.report-tab:hover  { color: var(--text-primary); }
.report-tab.active { color: var(--teal); border-bottom-color: var(--teal); }

.report-section {
  position: relative; padding: 16px 20px; border-bottom: 1px solid var(--border);
}

.report-section-title { font-size: var(--fs-base); font-weight: 700; margin-bottom: 4px; }
.report-section-sub   { font-size: var(--fs-sm); color: var(--text-secondary); }

.report-dl-btn {
  position: absolute; right: 16px; top: 14px;
  background: none; border: none; color: var(--text-secondary);
  cursor: pointer; font-size: var(--fs-md); padding: 2px;
  border-radius: 4px; transition: color var(--transition);
}

.report-dl-btn:hover { color: var(--text-primary); }

.report-metrics {
  display: grid; grid-template-columns: 1fr 1fr; flex: 1;
}

.metric-box {
  padding: 14px 20px; border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
}

.metric-box:nth-child(2n)  { border-right: none; }
/* Ostatni wiersz bez dolnej kreski - niezaleznie od liczby kafli. Stare
   `nth-child(n+3)` zakladalo cztery kafle; przy pieciu (karta studia) piaty
   tracil kreske nad soba (BUG-mobB-09). Ten sam wzorzec co `.stat-bar`. */
.metric-box:last-child,
.metric-box:nth-last-child(2):nth-child(odd) { border-bottom: none; }
.metric-box:last-child:nth-child(odd) { grid-column: 1 / -1; border-right: none; }

.metric-value {
  font-size: var(--fs-xl); font-weight: 700;
  display: flex; align-items: center; gap: 8px;
}

.metric-pct       { font-size: var(--fs-sm); font-weight: 600; }
.metric-pct.up    { color: var(--teal); }
.metric-pct.down  { color: var(--danger); }

.metric-bar {
  width: 60px; height: 3px; background: var(--border);
  border-radius: 3px; margin-top: 6px; overflow: hidden;
}

.metric-bar-fill { height: 100%; border-radius: 3px; }
.metric-label    { font-size: var(--fs-xs); color: var(--text-secondary); margin-top: 4px; }

.report-footer {
  padding: 12px 20px; text-align: center;
  border-top: 1px solid var(--border); margin-top: auto;
}

.report-footer a {
  font-size: var(--fs-nav); color: var(--teal); font-weight: 600;
  display: inline-flex; align-items: center; gap: 4px;
}


/* ================================================================
   11. TABLE SYSTEM
   ================================================================ */

/* Grid row for 2 tables side-by-side */
/* ────────────────────────────────────────────────────────────────
   8a. OPAKOWANIE TABEL - KAZDA SZEROKOSC
   (plan: wyglad-mobilny.md, etap 4)

   `wrapTables()` w main.js opakowuje kazda tabele bezwarunkowo, ale regula
   nadajaca opakowaniu przewijanie siedziala w bloku <=768px. Skutek: na
   iPadzie w pionie (820 px) opakowanie istnialo w DOM i nie robilo NIC,
   a szeroka tabela rozpychala cala strone w poziomie - czyli przewijanie
   w dwoch osiach, wprost przeciw kryterium 1.4.10 WCAG (Reflow).

   Regula stoi teraz poza jakimkolwiek progiem, bo problem tez nie ma progu:
   tabela o 11 kolumnach nie miesci sie rowniez w oknie 1100 px.
   `Flowla.tables` pomija tabele, ktora ma juz przodka przewijajacego
   w poziomie - inaczej 17 wlasnych opakowan panelu platformy dostaloby
   drugi kontener przewijania w srodku pierwszego.

   ────────────────────────────────────────────────────────────────
   TABELA -> LISTA MOBILNA (etap 5)

   PROG LICZY SIE OD SZEROKOSCI KONTENERA, NIE OKNA. Ta sama tabela stoi
   raz w panelu na cala szerokosc, raz w siatce `1fr 1fr` na pulpicie
   platformy, raz w kolumnie 55fr/45fr w Raportach. `@media (max-width: 600px)`
   przy dwoch tabelach obok siebie nie odpali sie NIGDY, mimo ze kazda ma
   realnie 530 px. `@container` mierzy to, co naprawde ogranicza tresc -
   efektem ubocznym iPad w pionie dostaje karty bez ani jednej dodatkowej
   reguly.

   Tryb wybiera klasa na `<table>`, a JS przepisuje go na opakowanie:
     brak klasy       -> karty, gdy tabela ma etykietowalny `<thead>`
     `.table-scroll`  -> tabela z przewijaniem poziomym
     `.table-sticky-1`-> jw. + przyklejona pierwsza kolumna
     `.table-plain`   -> JS nie dotyka wcale

   DOMYSLNIE KARTY, NIE PRZEWIJANIE. Domyslne przewijanie nie naprawia
   niczego bez wejscia w 55 widokow, czyli utrwala stan obecny. Lista tabel
   liczbowych, ktore MUSZA zostac tabelami, jest skonczona i policzalna dzis:
   dwanascie. Drugi argument to asymetria bledu - tabela liczbowa pokazana
   jako karty jest rozwlekla, ale czytelna; lista nazwisk pokazana jako
   tabela z 41 px na kolumne jest bezuzyteczna. */
.table-scroll-wrap {
  container-type: inline-size;
  container-name: fwtbl;
  min-width: 0;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

/* Sygnal "tabela ma dalszy ciag". Klasyczny cien tlem nie zadziala:
   `.table tbody tr` ma wlasne, nieprzezroczyste tlo i by go zamalowalo. */
.table-scroll-wrap.has-x-scroll:not(.at-x-end) {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}

/* Minimum NALEZY DO TRYBU PRZEWIJANIA, nigdy do `.table` globalnie -
   inaczej kazda tabela dwukolumnowa dostaje pasek przewijania na komputerze. */
.table-scroll-wrap.is-scroll > table { min-width: var(--table-min, 720px); }
.table-scroll-wrap.is-scroll > table > thead > tr > th { white-space: nowrap; }

.table-scroll-wrap.is-scroll.tbl-sticky-1 > table > thead > tr > th:first-child,
.table-scroll-wrap.is-scroll.tbl-sticky-1 > table > tbody > tr > td:first-child {
  position: sticky; left: 0; z-index: 1;
  background: var(--surface);
  box-shadow: 1px 0 0 var(--border);
}
.table-scroll-wrap.is-scroll.tbl-sticky-1 > table > thead > tr > th:first-child { z-index: 2; }
/* Wlasne tlo komorki jest konieczne: `.table` maluje pasy na WIERSZU,
   wiec przyklejona komorka bez tla przepuszcza tresc przesuwajaca sie pod nia. */
.table-scroll-wrap.is-scroll.tbl-sticky-1 > table > tbody > tr:nth-child(odd) > td:first-child {
  background: var(--light-gray);
}

.tbl-sortbar { display: none; }

/* Kolumna drugorzedna znika, gdy kolumna tresci schodzi ponizej 900 px.
   Klasa idzie na `<th>`, a JS przepisuje ja na komorki - NIGDY `nth-child`,
   bo to sie sypie przy kazdej zmianie kolejnosci naglowkow i przy
   `columnDefs` z `visible: false`. */
@container fwtbl (max-width: 900px) {
  .table-scroll-wrap > table > thead > tr > .col-hide-md,
  .table-scroll-wrap > table > tbody > tr > .col-hide-md { display: none; }

  /* Pas posredni 600-900 px kolumny tresci: na karty jeszcze za szeroko,
     na pelna tabele juz za waska - wtedy przewijanie zamiast sciskania
     kolumn do kilkunastu pikseli.
     Minimum NIE moze stac poza tym zapytaniem: przy kontenerze szerszym niz
     900 px mala tabela dostalaby pasek przewijania, ktorego dzis nie ma -
     czyli regresja na komputerze w imie telefonu. Ponizej 600 px zeruje je
     blok kart nizej. */
  .table-scroll-wrap.is-cards > table { min-width: var(--table-min, 720px); }
}

@container fwtbl (max-width: 600px) {

  /* Wciecie boczne rowne wypelnieniu karty (12px 14px nizej w tym bloku).
     Bez niego "Sortuj" stalo na samej krawedzi panelu, o 14 px na lewo od
     nazwy klienta pod spodem - a to nazwy sa najmocniejsza linia pionowa
     na tym ekranie. Tabela jest w panelu FLUSH (bez `.panel-body`), wiec
     odstepu nie ma skad odziedziczyc. */
  .table-scroll-wrap.is-cards > .tbl-sortbar {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 14px;
  }
  .table-scroll-wrap.is-cards > .tbl-sortbar .tbl-sortbar-label {
    font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase;
    letter-spacing: .03em; color: var(--text-secondary); flex: none;
  }
  .table-scroll-wrap.is-cards > .tbl-sortbar .tbl-sortbar-select { flex: 1 1 auto; min-width: 0; }
  .table-scroll-wrap.is-cards > .tbl-sortbar .tbl-sortbar-dir {
    flex: none; width: 44px; min-height: 44px; padding: 0; justify-content: center;
  }

  .table-scroll-wrap.is-cards > table {
    min-width: 0;
    border-collapse: separate;
    border-spacing: 0;
  }

  /* Naglowek ukryty WIZUALNIE, nie `display: none`. Przy `display: none`
     znika z drzewa dostepnosci, a w trybie kart nazwy kolumn sa jedynym
     opisem wartosci - czytnik ekranu zostalby z samymi liczbami. */
  .table-scroll-wrap.is-cards > table > thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }

  .table-scroll-wrap.is-cards > table,
  .table-scroll-wrap.is-cards > table > tbody { display: block; width: auto; }

  /* `:nth-child(n)` podnosi specyficznosc do 0-3-3. Bez tego regula przegrywa
     z zebra DataTables (`table.dataTable.display>tbody>tr:nth-child(odd)>*`,
     tez 0-3-3, ale ladowana pozniej) i karty zostaja w pasy. Zero `!important`. */
  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) {
    display: block; position: relative;
    width: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
    margin: 0 0 8px;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr:last-child { margin-bottom: 0; }

  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td {
    display: grid;
    grid-template-columns: minmax(80px, 34%) minmax(0, 1fr);
    gap: 2px 12px;
    align-items: baseline;
    width: auto;
    padding: 5px 0;
    border: 0;
    box-shadow: none;
    font-size: var(--fs-nav);
    text-align: left !important;   /* kasuje .dt-right / .text-right z widoku */
    white-space: normal;
    overflow-wrap: anywhere;
  }

  /* DataTables niesie `padding: 12px !important` i dolna kreske z `!important`
     na kazdej komorce (sekcja DataTables) - bez tego resetu karta listy
     DataTables miala separatory miedzy wierszami i kreske pod menu w rogu
     (BUG-mobB-03). `!important` wymusza baza, nie ta regula. */
  .table-scroll-wrap.is-cards > table.dataTable > tbody > tr:nth-child(n) > td {
    padding: 5px 0 !important;
    border-bottom: 0 !important;
  }
  .table-scroll-wrap.is-cards > table.dataTable > tbody > tr:nth-child(n) > td.is-tbl-title {
    padding: 0 0 2px !important;
    font-size: var(--fs-base) !important;
  }
  .table-scroll-wrap.is-cards > table.dataTable > tbody > tr:nth-child(n) > td.is-tbl-actions {
    padding: 10px 14px !important;
    border-top: 1px solid var(--border) !important;
  }
  .table-scroll-wrap.is-cards > table.dataTable > tbody > tr:nth-child(n) > td.is-tbl-actions-menu,
  .table-scroll-wrap.is-cards > table.dataTable > tbody > tr:nth-child(n) > td.is-tbl-check { padding: 0 !important; border-top: 0 !important; }
  .table-scroll-wrap.is-cards > table.dataTable > tbody > tr:has(> td.is-tbl-actions-menu) > td.is-tbl-title { padding-right: 44px !important; }

  .table-scroll-wrap.is-cards > table > tbody > tr > td::before {
    content: attr(data-label);
    font-size: var(--fs-xs); font-weight: 600; line-height: 1.6;
    color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: .03em;
    overflow-wrap: normal;
  }

  /* Pierwsza kolumna tekstowa to tytul karty - bez etykiety i wiekszym krojem. */
  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td.is-tbl-title {
    display: block;
    padding: 0 0 2px;
    font-size: var(--fs-base); font-weight: 600;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-title::before { display: none; }

  /* Komorka pusta albo z samym mysnikiem: w tabeli trzyma siatke,
     na karcie bylaby linijka "TAGI  -". */
  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td.is-tbl-empty,
  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td.col-hide-sm { display: none; }

  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td.is-tbl-actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    margin: 10px -14px -12px;
    padding: 10px 14px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
    border-radius: 0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px);
  }
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-actions::before { display: none; }
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-actions > div {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    justify-content: flex-start; flex: 1 1 100%;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-actions form {
    display: flex; flex: 1 1 120px; margin: 0;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-actions .btn {
    flex: 1 1 120px; min-height: 44px; justify-content: center;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-actions .table-btn,
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-actions .dots-trigger {
    width: 44px; height: 44px; flex: none;
  }

  /* Komorka z samym menu trzech kropek nie zasluguje na pelna stopke -
     londuje w rogu karty. */
  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td.is-tbl-actions-menu {
    position: absolute; top: 4px; right: 4px;
    width: auto; margin: 0; padding: 0;
    border: 0; background: none;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr:has(> td.is-tbl-actions-menu) > td.is-tbl-title {
    padding-right: 44px;
  }

  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td.is-tbl-check {
    position: absolute; top: 12px; right: 12px;
    width: auto; margin: 0; padding: 0; border: 0;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr > td.is-tbl-check::before { display: none; }
  .table-scroll-wrap.is-cards > table > tbody > tr:has(> td.is-tbl-check) > td.is-tbl-actions-menu {
    right: 40px;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr:has(> td.is-tbl-check) > td.is-tbl-title {
    padding-right: 36px;
  }

  /* Pusty stan DataTables (`td.dt-empty` z `colspan`) nie jest rekordem -
     w ramce karty z etykieta wygladalby jak wiersz danych. */
  .table-scroll-wrap.is-cards > table > tbody > tr:has(> td[colspan]) {
    border: 0; background: none; box-shadow: none; padding: 0; margin: 0;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr:nth-child(n) > td[colspan] {
    display: block; padding: 0; text-align: center !important;
  }
  .table-scroll-wrap.is-cards > table > tbody > tr > td[colspan]::before { display: none; }
}

/* ────────────────────────────────────────────────────────────────
   (dalszy ciag sekcji 8)
   ──────────────────────────────────────────────────────────────── */

/* Dwie tabele obok siebie mialy siatke `1fr 1fr` BEZ ani jednego nadpisania
   w calym projekcie. Na telefonie kazda dostawala ok. 171 px na cztery
   kolumny, a `.table-card { overflow: hidden }` ucinal reszte bez paska
   przewijania. `auto-fit` z minimum rozwiazuje to bez zapytania medialnego:
   kolumny zwijaja sie same, gdy zabraknie miejsca. */
.tables-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 18px; margin-bottom: 20px;
}

.table-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}

.table-card-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px; border-bottom: 1px solid var(--border);
}

.table-card-header h3 { font-size: var(--fs-md); font-weight: 700; }
.table-card-actions    { display: flex; gap: 8px; }

/* Base table */
.table               { width: 100%; border-collapse: collapse; }

.table thead th {
  text-align: left; font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.3px;
  padding: 10px 12px; border-bottom: 1px solid var(--border); background: var(--surface);
}

.table thead th:first-child { padding-left: 20px; }
.table thead th:last-child  { padding-right: 20px; }

.table tbody td {
  padding: 12px; font-size: var(--fs-nav); color: var(--text-primary);
  border-bottom: 1px solid var(--border); vertical-align: middle;
}

.table tbody td:first-child    { padding-left: 20px; }
.table tbody td:last-child     { padding-right: 20px; }
.table tbody tr:last-child td  { border-bottom: none; }
.table tbody tr                { background: var(--surface); }
.table tbody tr:nth-child(odd) { background: var(--light-gray); }
.table tbody tr:hover          { background: var(--bg); }

/* Table cell helpers */
.table-checkbox {
  width: 15px;
  height: 15px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  border: 1.5px solid color-mix(in srgb, var(--text-muted) 55%, transparent);
  border-radius: 3px;
  background: var(--surface);
  position: relative;
  vertical-align: middle;
}
.table-checkbox:checked {
  background: var(--teal);
  border-color: var(--teal);
}
.table-checkbox:checked::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 45%;
  width: 4px;
  height: 8px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -50%) rotate(45deg);
}
.table-checkbox:indeterminate {
  background: var(--teal);
  border-color: var(--teal);
}
.table-checkbox:indeterminate::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 7px;
  height: 2px;
  background: #fff;
  transform: translate(-50%, -50%);
}
.table-col-check  { width: 36px; }
.table-col-actions { width: 96px; text-align: center !important; }

.dt-left   { text-align: left   !important; }
.dt-center { text-align: center !important; }
.dt-right  { text-align: right  !important; }

.table-name-cell { display: flex; align-items: center; gap: 10px; }

.table-avatar {
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: var(--fs-xs); font-weight: 700;
  flex-shrink: 0; letter-spacing: 0.5px;
}
img.table-avatar, img.pg-avatar, img.avatar, img.pg-greeting-avatar, img.pg-header-avatar, img.pg-users-avatar, img.pg-item-avatar {
  object-fit: cover;
}

.table-name  { font-size: var(--fs-nav); font-weight: 600; color: var(--text-primary); }
.table-email { font-size: var(--fs-xs); color: var(--text-secondary); margin-top: 1px; }

.table-country { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-nav); }

.table-status         { font-size: var(--fs-sm); font-weight: 600; }
.table-status-active  { color: var(--success); }
.table-status-pending { color: var(--warning); }
.table-status-inactive { color: var(--danger); }

.table-actions { display: flex; align-items: center; gap: 2px; justify-content: center; }

.table-btn {
  width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: 6px; color: var(--text-secondary);
  font-size: var(--fs-base); cursor: pointer; padding: 0;
  transition: background var(--transition), color var(--transition);
}

.table-btn:hover                     { background: var(--bg); color: var(--text-primary); }
.table-btn.table-action-danger:hover { background: var(--danger-dim); color: var(--danger); }

/* Product cell */
.product-cell { display: flex; align-items: center; gap: 10px; }

.product-thumb {
  width: 36px; height: 36px; border-radius: 6px;
  background: var(--bg); display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-base);
}

.product-name { font-weight: 600; font-size: var(--fs-nav); }
.product-cat  { font-size: var(--fs-xs); color: var(--text-secondary); }

/* Stars */
.stars       { color: var(--warning); font-size: var(--fs-sm); letter-spacing: 1px; }
.stars-count { color: var(--text-secondary); font-size: var(--fs-sm); }

/* Status badge (dot + text) */
.status-badge { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-sm); font-weight: 600; }
.status-dot   { width: 7px; height: 7px; border-radius: 50%; }

.status-active    { color: var(--success); }
.status-active .status-dot    { background: var(--success); }
.status-low       { color: var(--warning); }
.status-low .status-dot       { background: var(--warning); }
.status-out       { color: var(--danger); }
.status-out .status-dot       { background: var(--danger); }
.status-delivered { color: var(--success); }
.status-delivered .status-dot { background: var(--success); }
.status-pending   { color: var(--warning); }
.status-pending .status-dot   { background: var(--warning); }
.status-completed { color: var(--teal); }
.status-completed .status-dot { background: var(--teal); }
.status-cancelled { color: var(--danger); }
.status-cancelled .status-dot { background: var(--danger); }
.status-inactive  { color: var(--text-secondary); }
.status-inactive .status-dot  { background: var(--text-secondary); }

/* Order cell */
.order-cell { display: flex; align-items: center; gap: 10px; }

.order-avatar {
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: var(--fs-sm); font-weight: 700; flex-shrink: 0;
}

.order-id   { font-weight: 700; font-size: var(--fs-nav); }
.order-name { font-size: var(--fs-xs); color: var(--text-secondary); }

/* Action dots */
.action-btn {
  background: none; border: none; cursor: pointer;
  color: var(--text-secondary); font-size: var(--fs-lg);
  padding: 2px 4px; border-radius: 4px;
  transition: background var(--transition);
}

.action-btn:hover { background: var(--bg); color: var(--text-primary); }

/* Table footer + pagination */
.table-footer {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 20px; border-top: 1px solid var(--border);
  font-size: var(--fs-sm); color: var(--text-secondary);
}

.pagination { display: flex; align-items: center; gap: 4px; }

.page-btn {
  width: 28px; height: 28px; border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-sm); cursor: pointer;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text-secondary); font-family: inherit;
  transition: background var(--transition);
}

.page-btn:hover  { background: var(--bg); }
.page-btn.active { background: var(--teal); color: #fff; border-color: var(--teal); }


/* ================================================================
   12. BUTTONS

   Użycie:
   <button class="btn">Default</button>
   <button class="btn btn-primary">Teal</button>
   <button class="btn btn-danger btn-sm">Usuń</button>
   <button class="btn btn-outline btn-primary">Outline</button>
   <button class="btn btn-clear"><i class="bi bi-x"></i></button>
   ================================================================ */

.btn {
  height: 34px;
  padding: 0 14px; border-radius: 6px; font-size: var(--fs-sm); font-weight: 500;
  cursor: pointer; border: 1px solid var(--border); background: var(--surface);
  color: var(--text-primary); display: inline-flex; align-items: center;
  gap: 5px; font-family: inherit;
  transition: background var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);
  line-height: 1; white-space: nowrap;
  user-select: none;
}

.btn:hover { background: var(--bg); }
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: .5; cursor: not-allowed; pointer-events: none;
}

.btn-primary                { background: var(--teal); color: #fff; border-color: var(--teal); }
.btn-primary:hover          { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }

.btn-blue                   { background: var(--blue); color: #fff; border-color: var(--blue); }
.btn-blue:hover             { background: var(--color-accent-hover); border-color: var(--color-accent-hover); }

.btn-success                { background: var(--teal); color: #fff; border-color: var(--teal); }
.btn-success:hover          { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }

.btn-warning                { background: var(--warning); color: #fff; border-color: var(--warning); }
.btn-warning:hover          { background: var(--color-warning-hover); border-color: var(--color-warning-hover); }

.btn-danger                 { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn-danger:hover           { background: var(--color-danger-hover); border-color: var(--color-danger-hover); }

.btn-info                   { background: var(--light-blue); color: var(--blue); border-color: var(--light-blue); }
.btn-info:hover             { background: color-mix(in srgb, var(--light-blue) 88%, #000); }

.btn-gray                   { background: var(--light-gray); color: var(--text-primary); border-color: var(--light-gray); }
.btn-gray:hover             { background: color-mix(in srgb, var(--light-gray) 88%, #000); }

/* Ghost / icon-only button */
.btn-clear {
  width: 28px; height: 28px; padding: 0;
  background: none; border-color: transparent;
  color: var(--text-secondary); border-radius: 6px; justify-content: center;
}

.btn-clear:hover         { background: var(--light-gray); color: var(--text-primary); }
.btn-clear.btn-sm        { width: 22px; height: 22px; }
.btn-clear.btn-lg        { width: 36px; height: 36px; }

/* Outline variants */
.btn-outline                { background: transparent; }

.btn-outline.btn-primary        { color: var(--teal); border-color: var(--teal); }
.btn-outline.btn-primary:hover  { background: var(--accent-dim); }

.btn-outline.btn-blue           { color: var(--blue); border-color: var(--blue); }
.btn-outline.btn-blue:hover     { background: var(--blue-dim); }

.btn-outline.btn-success        { color: var(--teal); border-color: var(--teal); }
.btn-outline.btn-success:hover  { background: var(--success-dim); }

.btn-outline.btn-warning        { color: var(--warning); border-color: var(--warning); }
.btn-outline.btn-warning:hover  { background: var(--warning-dim); }

.btn-outline.btn-danger         { color: var(--danger); border-color: var(--danger); }
.btn-outline.btn-danger:hover   { background: var(--danger-dim); }

.btn-outline.btn-info           { color: var(--blue); border-color: var(--light-blue); }
.btn-outline.btn-info:hover     { background: var(--light-blue); }

.btn-outline.btn-gray           { color: var(--text-secondary); border-color: var(--border); }
.btn-outline.btn-gray:hover     { background: var(--light-gray); }

/* Size variants */
.btn-sm { height: 26px; padding: 0 10px; font-size: var(--fs-2xs); border-radius: 5px; gap: 4px; }
.btn-lg { height: 42px; padding: 0 20px; font-size: var(--fs-nav); border-radius: 8px; gap: 7px; }

/* Button with spinner */
.btn .spinner {
  width: 14px; height: 14px; border: 2px solid rgba(255,255,255,.3);
  border-top-color: #fff; border-radius: 50%;
  animation: spin .6s linear infinite;
}

.btn.loading { pointer-events: none; opacity: .7; }

@keyframes spin { to { transform: rotate(360deg); } }


/* ================================================================
   13. BADGES

   Użycie:
   <span class="badge">Default</span>
   <span class="badge badge-success">Aktywny</span>
   <span class="badge badge-outline badge-danger">Usunięty</span>
   <span class="badge badge-sm badge-warning">Nowy</span>
   ================================================================ */

.badge {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px 10px; border-radius: 12px;
  font-size: var(--fs-xs); font-weight: 600;
  white-space: nowrap; user-select: none; border: none; gap: 4px;
  background: var(--teal); color: #fff;
}

.badge-primary   { background: var(--blue);       color: #fff; }
.badge-secondary { background: var(--success-dim); color: var(--success); }
.badge-tertiary  { background: var(--indigo-dim); color: var(--indigo-ink); }
.badge-success   { background: var(--teal);       color: #fff; }
/* Ciemny tekst na pelnym tle ostrzezenia: bialy na #f4b04f dawal 1,88:1 w obu
   motywach (F6, decyzja wlasciciela z 23.10.2026). Stala barwa, nie --warning-ink -
   ta w ciemnym motywie jest jasna, bo pracuje na przygaszonym tle. */
.badge-warning   { background: var(--warning);    color: #3a2600; }
.badge-danger    { background: var(--danger);     color: #fff; }
.badge-info      { background: var(--light-blue); color: var(--blue); }
.badge-gray      { background: var(--light-gray); color: var(--text-secondary); }
.badge-accent    { background: var(--accent-dim); color: var(--teal); }
.badge-highlight { background: var(--warning-dim); color: var(--warning-ink); }

.badge-sm { padding: 2px 6px; font-size: var(--fs-3xs); border-radius: 8px; }
.badge-lg { padding: 6px 14px; font-size: var(--fs-sm); border-radius: 14px; }

/* Outline badges */
.badge-outline                { background: transparent; border: 1.5px solid; color: var(--teal); border-color: var(--teal); }
.badge-outline.badge-primary  { color: var(--blue);           border-color: var(--blue); }
.badge-outline.badge-success  { color: var(--teal);           border-color: var(--teal); }
.badge-outline.badge-warning  { color: var(--warning-ink);    border-color: var(--warning); }  /* sam --warning na bialym = 1,9:1 */
.badge-outline.badge-danger   { color: var(--danger);         border-color: var(--danger); }
.badge-outline.badge-info     { color: var(--blue);           border-color: var(--blue); }
.badge-outline.badge-gray     { color: var(--text-secondary); border-color: var(--border); }
.badge-outline.badge-accent    { color: var(--teal);           border-color: var(--teal); }
.badge-outline.badge-secondary { color: var(--success);        border-color: var(--success); }
.badge-outline.badge-tertiary  { color: var(--indigo-ink);    border-color: var(--indigo); }
.badge-outline.badge-highlight { color: var(--warning);        border-color: var(--warning); }


/* ================================================================
   14. FORMS (field, input, select, textarea, checkbox, radio)
   
   Użycie:
   <div class="field">
     <label class="label">Email <span class="req">*</span></label>
     <input class="input" type="email" placeholder="jan@example.com">
     <span class="hint">Podaj aktywny adres email</span>
     <span class="field-error">To pole jest wymagane</span>
   </div>
   ================================================================ */

/* ════════════════════════════════════════════════════════════
   STANDARD POLA FORMULARZA - JEDNO ZRODLO DLA CALEGO PANELU

   Dwie liczby i nic wiecej:

     6 px   etykieta -> kontrolka      (`.field { gap }`)
     16 px  pole -> pole, sekcja -> sekcja
            (`.panel-body`, `.form-grid`, `.form-stack`)

   ZADEN MODUL NIE MA PRAWA TEGO NADPISAC. Przed ujednoliceniem w panelu
   krazyly cztery wartosci naraz: 14 px z `.panel-body`, 16 px z rejestracji,
   18 px z `.form-grid` i 0 px w golym `<form>`, ktory nie daje odstepu
   zadnemu ze swoich dzieci. Do tego dwa moduly logowania dokladaly polu
   `.label { margin-bottom: 5px }`, wiec etykieta stala 11 px od kontrolki
   zamiast 6. Kazdy nowy ekran dziedziczyl te wartosc, ktora akurat mial
   rodzic - i tak samo kazda kolejna niespojnosc.

   Nowe pole nie potrzebuje ANI JEDNEJ linii CSS: `<div class="field">`
   z etykieta i kontrolka w srodku, cale w kontenerze `.form-stack`
   (albo w `.panel-body` / `.form-grid`, ktore maja ten sam odstep).
   ════════════════════════════════════════════════════════════ */

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Pionowy rytm pol tam, gdzie rodzicem jest goly `<form>` albo `<div>`.
   `.panel-body` i `.form-grid` daja to samo 16 px z wlasnego `gap`, wiec
   dokladanie im `.form-stack` niczego nie zmienia i nie jest bledem. */
.form-stack {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Kontener z `gap` MUSI zerowac marginesy narzedziowe dzieci - inaczej
   `.mt-16` sumuje sie z odstepem kontenera i wychodzi 32 px w miejscu,
   gdzie reszta formularza trzyma 16. */
.form-stack > .mt-8,
.form-stack > .mt-12,
.form-stack > .mt-16,
.form-stack > .mt-20,
.form-stack > .mt-24 { margin-top: 0; }

/* Zwiniety `.reveal` to `grid-template-rows: 0fr`, a NIE `display: none` -
   zostaje elementem ukladu, wiec `gap` rezerwowalby mu 16 px pustki takze
   wtedy, gdy sekcja jest schowana. Odstep nad rozwinieta sekcja daje jej
   wlasny `.reveal-inner`.

   Dotyczy KAZDEGO kontenera z `gap: 16px`, nie tylko `.form-stack`:
   `.panel-body` i `.form-grid` trzymaja ten sam odstep, wiec zwiniety
   `.reveal` na koncu panelu powiekszal jego dolny padding z 20 na 36 px. */
.form-stack > .reveal,
.panel-body > .reveal,
.form-grid  > .reveal { margin-top: -16px; }
.form-stack > .reveal.open,
.panel-body > .reveal.open,
.form-grid  > .reveal.open { margin-top: 0; }

.label {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: .05em;
  text-transform: uppercase;
}

/* Gwiazdka pola wymaganego. Regula stala kiedys na `.label .req`, wiec ta sama
   gwiazdka w tytule panelu (`.panel-title`, ogloszenia platformy) dziedziczyla
   szarosc tytulu - jeden znak w dwoch kolorach zaleznie od rodzica. */
.req { color: var(--teal); margin-left: 1px; }

/* Ikona w etykiecie (Instagram, Facebook...) - bez tego kurczy sie do rozmiaru
   etykiety, czyli --fs-xs. Przejete z usunietego .form-label (A211). */
.label .bi { font-size: var(--fs-base); }

/* .form-input jest ALIASEM .input - siatka bezpieczenstwa, nie system.
   Rownolegly zestaw (.form-group / .form-label / .form-input / .form-hint)
   mial wlasna, recznie powielona kopie tych samych deklaracji i przez to
   rozjezdzal sie z .input przy kazdej zmianie - stad brak stanow
   disabled/readonly.

   Widoki zostaly przepiete na .field / .label / .input / .textarea / .select
   / .hint (A211), wiec .form-group, .form-label i .form-hint juz nie istnieja.
   Alias .form-input zostaje na wypadek widoku skopiowanego ze starego wzorca,
   ale UWAGA: dziala tylko dla <input>. Textarea i select maja wlasne klasy
   (.textarea, .select) i .form-input nada im wysokosc pola jednoliniowego. */
/* WSPOLNA PODSTAWA WSZYSTKICH POL. Lista selektorow jest tu ISTOTNA:
   `.textarea` bez `font-family` spada do domyslnej czcionki `<textarea>`,
   czyli do MONOSPACE - i notatka wewnetrzna zaczyna wygladac jak terminal
   obok pol pisanych Interem. Ten blok raz juz zostal rozerwany przez regule
   dopisana miedzy `.textarea,` a `.select {`; jesli dokladasz cos obok,
   dopisuj PO klamrze zamykajacej, nigdy w srodku listy. */
.input,
.form-input,
.textarea,
.select {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-primary);
  font-family: 'Inter', sans-serif;
  font-size: var(--fs-nav);
  font-weight: 400;
  outline: none;
  transition: border-color .18s, box-shadow .18s;
}

/* TOM SELECT PRZEPISUJE KLASY POLA NA SWOJ `<div class="ts-wrapper">`.
   Skutek: `.select` laduje na wrapperze i - odkad klasa ma wlasna ramke,
   wysokosc i strzalke - rysuje je DOOKOLA gotowej kontrolki. Na ekranie
   wychodzi select w selekcie: dwie ramki, dwie strzalki, podwojna wysokosc.
   Wrapper nie jest polem formularza, tylko pojemnikiem ukladu, wiec
   zdejmujemy z niego caly wyglad. Swoistosc 0,2,0 wygrywa z `.select`
   takze w blokach `(pointer: coarse)`, wiec `!important` nie jest potrzebny.
   Ta sama pulapka czeka na kazda przyszla klase pola dopisana do `.ts-select`. */
.ts-wrapper.select {
  height: auto;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  cursor: default;
}

/* GOLY `<select class="select">` MUSI WYGLADAC JAK KONTROLKA TOM SELECT.
   Podstawa wyzej daje kolory, ramke i kroj; brakowalo WYSOKOSCI, WYPELNIENIA
   i STRZALKI, wiec przegladarka rysowala wlasny widget: inny krok wewnetrzny,
   inna strzalka, inna wysokosc niz `.ts-control` obok. Na telefonie bilo to
   w oczy najmocniej, bo pasek sortowania trybu kart stoi tuz obok filtrow,
   ktore tom-select juz przejal.
   Wartosci sa PRZEPISANE z `.ts-control` (sekcja Tom Select). `appearance: none`
   jest warunkiem: bez niego Safari i tak dorysuje swoja. */
.select {
  height: 34px;
  padding: 0 32px 0 12px;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 16 16'%3E%3Cpath fill='%238b95a1' d='M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  cursor: pointer;
}

.input,
.form-input { height: 34px; padding: 0 12px; background: var(--bg); }

.input.hide-spinner::-webkit-outer-spin-button,
.input.hide-spinner::-webkit-inner-spin-button,
.form-input.hide-spinner::-webkit-outer-spin-button,
.form-input.hide-spinner::-webkit-inner-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }
.input.hide-spinner[type=number],
.form-input.hide-spinner[type=number] { -moz-appearance: textfield; appearance: textfield; }

/* Disabled / readonly - pole wylaczone wygladalo jak czynne, dopoki
   .form-input nie mial wlasnych stanow. */
.input:disabled,
.textarea:disabled,
.select:disabled,
.form-input:disabled,
.input[readonly],
.textarea[readonly],
.form-input[readonly] {
  opacity: .55;
  cursor: not-allowed;
  background: var(--light-gray);
}
.input.fp-date[readonly],
.input.fp-time[readonly],
.input.flatpickr-input[readonly],
.form-input.fp-date[readonly],
.form-input.fp-time[readonly],
.form-input.flatpickr-input[readonly] {
  opacity: 1;
  cursor: pointer;
  background: var(--bg);
}

.textarea {
  padding: 10px 12px;
  resize: vertical;
  min-height: 80px;
  line-height: 1.6;
}

.select {
  height: 34px;
  padding: 0 32px 0 12px;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 16 16'%3E%3Cpath fill='%238b95a1' d='M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}

.input:focus,
.form-input:focus,
.textarea:focus,
.select:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px var(--accent-dim);
}

/* Error state */
.input.has-error,
.form-input.has-error,
.textarea.has-error,
.select.has-error {
  border-color: var(--danger);
}

.input.has-error:focus,
.form-input.has-error:focus,
.textarea.has-error:focus,
.select.has-error:focus {
  box-shadow: 0 0 0 3px var(--danger-dim);
}

.field-error {
  font-size: var(--fs-xs);
  color: var(--danger);
  display: none;
}

.field.has-error .field-error { display: block; }

/* Hint */
.hint {
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}

/* Wyrozniona wartosc w podpowiedzi (np. "Wybrany kolor: #1baf92").
   Przejete z usunietego .form-hint (A211). */
.hint strong { color: var(--text-primary); }

/* Character counter */
.char-counter {
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  text-align: right;
}

/* Input group - icon/text prefix/suffix inline with input */
.input-group {
  display: flex;
  align-items: center;
  height: 34px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color .18s, box-shadow .18s;
}

.input-group:focus-within {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px var(--accent-dim);
}

.input-group .input {
  height: 100%; border: none; background: none; box-shadow: none;
}

.input-group-text {
  padding: 0 12px;
  font-size: var(--fs-nav);
  color: var(--text-secondary);
  white-space: nowrap;
  flex-shrink: 0;
}


/* Inline field - label + input side by side */
.field-inline {
  flex-direction: row;
  align-items: center;
  gap: 12px;
}

.field-inline .label {
  min-width: 120px;
  margin-bottom: 0;
}


/* ================================================================
   15. TOGGLE SWITCH

   Użycie:
   <div class="toggle-row">
     <div class="toggle-info">
       <div class="toggle-name">Powiadomienia email</div>
       <div class="toggle-sub">Otrzymuj potwierdzenia rezerwacji</div>
     </div>
     <input type="checkbox" id="t1" class="toggle" checked>
     <label for="t1" class="toggle-track"></label>
   </div>
   ================================================================ */

.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.toggle-row + .toggle-row { margin-top: 8px; }

.toggle-info { flex: 1; min-width: 0; }
.toggle-name { display: block; font-size: var(--fs-nav); font-weight: 500; color: var(--text-primary); user-select: none; cursor: pointer; }
.toggle-sub  { font-size: var(--fs-xs); color: var(--text-secondary); margin-top: 1px; }

/* Slot na wartosc (liczba, tekst, jednostka) miedzy opisem a przelacznikiem.
   Stala szerokosc trzyma wszystkie kontrolki w jednej pionowej osi, takze
   w wierszach bez przelacznika. Pusty slot w wierszu czysto boolowskim tez
   ma trzymac te os - dlatego nie ma :empty { display:none }. */
.toggle-control {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  flex-shrink: 0;
  min-width: 200px;
}

/* Wiersz ma tlo --bg, wiec pole na tym samym tle gubi krawedz - stad --surface */
.toggle-control .input { width: 76px; padding: 0 8px; background: var(--surface); }
.toggle-control .toggle-control-text { width: 190px; padding: 0 12px; text-align: left; }
.toggle-unit { font-size: var(--fs-xs); color: var(--text-secondary); min-width: 22px; }

/* Wiersz bez przelacznika - sama wartosc */
.toggle-row--value .toggle-control { min-width: 0; }

/* Wylaczony przelacznik wygasza wartosc i ikone */
.toggle-row.is-off .toggle-control { opacity: .45; }
.toggle-row.is-off > .panel-icon { background: var(--light-gray); color: var(--text-muted); }

@media (max-width: 768px) {
  .toggle-row { flex-wrap: wrap; }
  .toggle-row .toggle-info { flex: 1 1 100%; order: 1; }
  .toggle-row > .panel-icon { order: 0; }
  .toggle-row .toggle-control { order: 2; min-width: 0; margin-left: auto; }
  .toggle-row > input.toggle + .toggle-track { order: 3; }
}

/* Checkbox schowany wizualnie, ale nadal w kolejnosci Tab - display:none
   wyrzucalo przelaczniki z nawigacji klawiatura w calym panelu. */
input.toggle {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.toggle-track {
  width: 38px; height: 22px; border-radius: 999px;
  background: var(--border); position: relative;
  cursor: pointer; flex-shrink: 0;
  transition: background .2s, box-shadow .2s;
}

input.toggle:focus-visible + .toggle-track {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--teal) 35%, transparent);
}

.toggle-track::after {
  content: '';
  position: absolute;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; top: 3px; left: 3px;
  transition: transform .2s;
  box-shadow: 0 1px 3px rgba(0,0,0,.25);
}

input.toggle:checked + .toggle-track { background: var(--teal); }
input.toggle:checked + .toggle-track::after { transform: translateX(16px); }


/* ================================================================
   16. TABS

   Użycie:
   <div class="tabs-bar">
     <button class="tab active">Ustawienia</button>
     <button class="tab">Poziomy</button>
     <button class="tab">Klienci</button>
   </div>
   ================================================================ */

.tabs-bar {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 3px;
  /* 16 px - jeden odstep sekcji w calym panelu, jak `.panel` i `.kpi-row` */
  margin-bottom: 16px;
}

/* Filter/action bar above content - tabs on left, actions on right */
.page-tabs-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.page-tabs-bar .tabs-bar { margin-bottom: 0; }
.page-tabs-bar .tab { flex: none; padding: 5px 14px; font-size: var(--fs-sm); white-space: nowrap; }

.tab {
  flex: 1;
  padding: 9px 16px;
  font-size: var(--fs-nav);
  font-weight: 500;
  color: var(--text-secondary);
  background: transparent;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  transition: background var(--transition), color var(--transition), box-shadow var(--transition);
  text-align: center;
}

.tab:hover { color: var(--text-primary); }

.tab.active {
  background: var(--surface);
  color: var(--text-primary);
  font-weight: 600;
  box-shadow: var(--shadow);
}

.tab .tabs-bar-count {
  display: inline-block;
  margin-left: 5px;
  font-size: var(--fs-2xs);
  font-weight: 700;
  background: var(--border);
  color: var(--text-secondary);
  padding: 1px 6px;
  border-radius: 20px;
  line-height: 1.6;
  transition: background .15s, color .15s;
}

.tab.active .tabs-bar-count {
  background: color-mix(in srgb, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
}



/* ================================================================
   17. STATUS CARDS (radio cards)
   
   Użycie:
   <div class="status-cards">
     <label class="status-card">
       <input type="radio" name="status" value="published" checked>
       <span class="status-radio"></span>
       <span class="status-card-icon published"><i class="bi bi-globe2"></i></span>
       <div class="status-card-text">
         <div class="status-card-name">Opublikowany</div>
         <div class="status-card-desc">Widoczny dla klientów</div>
       </div>
     </label>
   </div>
   ================================================================ */

.status-cards {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.status-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color .18s, background .18s;
}

.status-card:hover { border-color: var(--text-muted); }

.status-card input[type="radio"] { display: none; }

.status-card:has(input:checked) {
  border-color: var(--teal);
  background: var(--accent-dim);
}

.status-radio {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--border);
  flex-shrink: 0; position: relative;
  transition: border-color .18s;
}

.status-card:has(input:checked) .status-radio { border-color: var(--teal); }

.status-radio::after {
  content: ''; width: 7px; height: 7px;
  border-radius: 50%; background: var(--teal);
  opacity: 0; transition: opacity .18s;
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
}

.status-card:has(input:checked) .status-radio::after { opacity: 1; }

.status-card-icon {
  width: 30px; height: 30px; border-radius: 7px;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-base); flex-shrink: 0;
}

.status-card-icon.published { background: var(--success-dim); color: var(--success-ink); }
.status-card-icon.draft     { background: color-mix(in srgb, var(--text-secondary) 13%, transparent); color: var(--text-secondary); }
.status-card-icon.scheduled { background: var(--warning-dim); color: var(--warning-ink); }

.status-card-text { flex: 1; }
.status-card-name { font-size: var(--fs-nav); font-weight: 600; color: var(--text-primary); }
.status-card-desc { font-size: var(--fs-xs); color: var(--text-secondary); margin-top: 1px; }


/* ================================================================
   18. MODAL & OVERLAY

   Zalecane użycie - przez SM.modal (main.js):

   SM.modal.confirm({
     title: 'Usuń element', icon: 'danger', iconClass: 'bi-trash3',
     message: 'Tej operacji nie można cofnąć.',
     confirmLabel: 'Usuń', confirmVariant: 'danger',
     onConfirm: function() { //akcja  }
   });

   Deklaratywne otwieranie:
   <button data-modal-open="myModal">Otwórz</button>

   <div class="overlay" id="myModal">
     <div class="modal">
       <div class="modal-header">
         <div class="modal-icon danger"><i class="bi bi-trash3"></i></div>
         <div class="modal-title">Usuń element</div>
         <button class="modal-close" data-modal-close><i class="bi bi-x-lg"></i></button>
       </div>
       <div class="modal-body">Czy na pewno chcesz usunąć?</div>
       <div class="modal-footer">
         <button class="btn" data-modal-close>Anuluj</button>
         <button class="btn btn-danger">Usuń</button>
       </div>
     </div>
   </div>
   ================================================================ */

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(10,12,18,.45);
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity .22s ease;
}

.overlay.open { opacity: 1; pointer-events: all; }

.modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  width: 400px;
  max-width: 90vw;
  padding: 0;
  overflow: hidden;
  transform: translateY(12px) scale(.97);
  transition: transform .25s ease;
}

.overlay.open .modal { transform: translateY(0) scale(1); }

/* Modal - ikona inline z tytułem, wyśrodkowana pionowo */
.modal-icon {
  width: 36px; height: 36px; border-radius: 9px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 17px;
}

.modal-icon.danger  { background: var(--danger-dim);  color: var(--danger); }
.modal-icon.accent  { background: var(--accent-dim);  color: var(--teal); }
.modal-icon.warning { background: var(--warning-dim); color: var(--warning); }
.modal-icon.success { background: var(--success-dim); color: var(--success); }

.modal-header {
  display: flex; align-items: center; gap: 12px;
  padding: 20px 24px 16px 24px;
  border-bottom: 1px solid var(--border);
}
.modal-header .modal-title {
  flex: 1;                     /* zajmuje resztę miejsca, przylega do ikony */
  text-align: left;
}
.modal-header .modal-close { flex-shrink: 0; }

.modal-title {
  font-size: var(--fs-md); font-weight: 700;
  color: var(--text-primary); line-height: 1.2;
}

.modal-body {
  padding: 20px 24px;
  font-size: var(--fs-nav); color: var(--text-secondary);
  line-height: 1.65;
}

.modal-body strong { color: var(--text-primary); }
.modal-body p { margin: 0; }

.modal-actions { display: flex; gap: 8px; justify-content: flex-end; }


/* Confirm input inside modal (e.g. type DELETE to confirm) */
.modal-confirm-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  height: 34px;
  padding: 0 12px;
  font-size: var(--fs-nav);
  font-family: 'Inter', sans-serif;
  color: var(--text-primary);
  outline: none;
  margin-bottom: 16px;
  transition: border-color .18s;
}

.modal-confirm-input:focus { border-color: var(--teal); }

/* Modal sizes */
.modal-sm { width: 340px; }
.modal-lg { width: 520px; }
.modal-xl { width: 680px; }
.modal-2xl { width: 900px; }

.modal-close {
  flex-shrink: 0; background: none; border: none; cursor: pointer;
  color: var(--text-secondary); font-size: 16px; padding: 2px 4px;
  border-radius: 4px; line-height: 1;
  transition: background var(--transition), color var(--transition);
}
.modal-close:hover { background: var(--bg); color: var(--text-primary); }

/* Modal footer (alias modal-actions dla spójności) */
.modal-footer {
  display: flex; gap: 8px; justify-content: flex-end;
  padding: 16px 24px;
  border-top: 1px solid var(--border);
}

/* Wariant: przyciski po lewej */
.modal-footer.left { justify-content: flex-start; }

/* Divider wewnątrz modala */
.modal-divider {
  border: none; border-top: 1px solid var(--border);
  margin: 18px -24px;
}

/* Modal z przewijaniem (długa treść) */
.modal-scrollable .modal-body {
  max-height: 40vh; overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: 4px; margin-right: -4px;
}


/* ================================================================
   19. STICKY ACTION BAR
   
   Użycie:
   <div class="action-bar">
     <div class="action-bar-left">
       <i class="bi bi-info-circle"></i> Autosave: 14:22
     </div>
     <div class="action-bar-right">
       <button class="btn">Anuluj</button>
       <button class="btn btn-primary">Zapisz</button>
     </div>
   </div>
   ================================================================ */

.action-bar {
  position: fixed;
  bottom: 0;
  left: var(--sidebar-w);
  right: 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 14px 24px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  display: flex;
  align-items: center;
  justify-content: space-between;
  z-index: var(--z-sticky);
  transition: left var(--transition);
}

body.sidebar-collapsed .action-bar { left: 0; }

.action-bar-left {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); color: var(--text-secondary);
}

/* Komunikat "Masz niezapisane zmiany" - JS przelacza go przez style.display = ''
   albo atrybutem `hidden`, wiec domyslny display musi byc tu, a nie inline w widoku. */
.action-bar-info {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); color: var(--text-secondary);
  line-height: 1;
}

/* [hidden] MUSI UKRYWAC KOMPONENTY, KTORE SAME USTAWIAJA `display`.
   Regula `[hidden] { display: none }` z arkusza przegladarki ma najnizsza
   moc, wiec `.action-bar-info { display: inline-flex }` (0,1,0) ja bila:
   Aktualnosci pokazywaly "Masz niezapisane zmiany" od otwarcia formularza
   (BUG-dotest-03). Moduly dopisywaly to samo po swojemu (website.css,
   settings.css, calendar.css, owner.css) - tu stoi raz dla komponentow
   z tego arkusza. Utility z `!important` (`.d-flex`) dalej wygrywa.
   `.panel` - pusty "Spis treści" regulaminu (`#tocPanel hidden`) stał na
   ekranie klienta; `.toggle-row` - wiersz "Wymagaj ponownej akceptacji"
   w Regulaminach chowany przez legal-terms.php. */
.action-bar-info[hidden],
.field[hidden],
.form-stack[hidden],
.form-grid[hidden],
.row[hidden],
.alert[hidden],
.panel[hidden],
.toggle-row[hidden] { display: none; }

/* Wysokosc paska zapisu na szerokim ekranie - warstwa 1 wspolnego stosu
   dolnych paskow (`--fw-stack-save`, sekcja `:root`): 1 px ramki + 14 px
   + 34 px przycisku + 14 px. Bez niej stos byl zerowy powyzej 768 px i pasek
   trybu pomocy (help-mode.css) stawal NA pasku akcji, zaslaniajac przyciski
   zapisu (BUG-dotest-05). Telefon nadpisuje wartosc w bloku <=768 px. */
body:has(.action-bar),
body:has(.settings-save-bar) { --fw-stack-save: 63px; }

.action-bar-info i { color: var(--warning); font-size: 14px; }

.action-bar-right {
  display: flex; align-items: center; gap: 8px;
}


/* ================================================================
   20. REVEAL / COLLAPSE

   Użycie:
   <div class="reveal" id="advancedOptions">
     <div class="reveal-inner">
       ...zawartość...
     </div>
   </div>

   JS: document.getElementById('advancedOptions').classList.toggle('open');
   ================================================================ */

.reveal {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows .32s ease, opacity .25s ease;
}

.reveal.open {
  grid-template-rows: 1fr;
  opacity: 1;
}

.reveal > .reveal-inner {
  overflow: hidden;
}

.reveal-inner {
  padding-left: 16px;
  border-left: 2px solid var(--accent-dim);
  display: flex;
  flex-direction: column;
  gap: 12px;
}


/* ================================================================
   21. LAYOUT HELPERS

   .form-layout       - 2-kolumnowy layout (65% / 35%)
   .form-col-main     - lewa kolumna (formularze)
   .form-col-side     - prawa kolumna (sticky sidebar)
   .grid-2 / .grid-3  - proste gridy
   .row                - flex row z gap
   ================================================================ */

.form-layout {
  display: flex;
  gap: 24px;
  align-items: flex-start;
}

.form-col-main {
  flex: 0 0 65%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.form-col-main > form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.form-col-side {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  position: sticky;
  top: calc(var(--topbar-h) + 20px);
}

/* Odstepy miedzy panelami daje `gap` kolumny, wiec wlasny margines panelu
   dokladalby drugi odstep. Regula byla powielona w calendar.css, tickets.css
   i discounts.css - jedno miejsce wystarczy dla kazdego formularza 2-kolumnowego. */
.form-col-main .panel,
.form-col-side .panel { margin-bottom: 0; }

/* Simple grid helpers */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
/* Odstep daje `gap` - wlasny margines panelu dokladalby drugi */
.grid-2 > .panel { margin-bottom: 0; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }

/* Flex row */
.row {
  display: flex;
  gap: 14px;
}

.row > .field { flex: 1; }


/* ================================================================
   21b. TREŚĆ SFORMATOWANA (.rich-text) - HTML z edytora Quill
   ----------------------------------------------------------------
   JEDNA klasa dla każdego miejsca, które wypisuje treść z edytora
   (`View::sanitizeHtml()`): regulamin, aktualności, ogłoszenia platformy,
   changelog, odpowiedzi FAQ. Quill 2 zapisuje KAŻDĄ listę płasko jako
   `<ol>` z `<li data-list="bullet|ordered">`, wcięcie jako `ql-indent-N`,
   a wyrównanie jako `ql-align-*` - numerację i punktory rysuje CSS-em.
   Bez tych reguł lista punktowana wychodziła z numerami „3.”, „4.”,
   a wcięcie i wyśrodkowanie znikały. Wcześniej port reguł miał tylko
   regulamin (terms.css), więc każde inne miejsce pokazywało inny dokument
   niż edytor.

   Klasa ustala STRUKTURĘ (listy, wcięcia, wyrównanie, odstępy bloków),
   a kolor i krój dziedziczy z kontekstu - pasek ogłoszenia, kafel
   aktualności i kartka regulaminu wyglądają po swojemu. Kontekst stroi
   ją zmiennymi zamiast nadpisywać reguły:
     --rt-gap     odstęp pod akapitem, listą, cytatem (domyślnie .75em)
     --rt-marker  kolor punktora / numeru (domyślnie kolor tekstu)
     --rt-accent  kreska cytatu (domyślnie kolor wiodący studia)
     --rt-muted   kolor tekstu cytatu (domyślnie kolor tekstu)
     --rt-link    kolor odnośnika (domyślnie kolor wiodący studia)

   Punktor rysuje `li[data-list]::before`, a nie `span.ql-ui` Quilla:
   część ścieżek zapisu (FAQ) wycina pusty `<span>`, a znacznik na samym
   `li` działa w obu przypadkach. Lista bez `data-list` (treść sprzed
   Quilla 2, wklejka) dostaje zwykłe znaczniki przeglądarki.
   ================================================================ */

.rich-text {
  overflow-wrap: break-word;
  counter-reset: list-0 list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8;
}
/* Akapit albo nagłówek między listami zaczyna numerację od nowa - tak samo
   jak w edytorze (quill.snow.css) */
.rich-text :is(p, h1, h2, h3, h4, blockquote, pre) {
  counter-reset: list-0 list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8;
}

.rich-text p,
.rich-text ol,
.rich-text ul,
.rich-text blockquote,
.rich-text pre { margin: 0 0 var(--rt-gap, .75em); }
.rich-text > :last-child { margin-bottom: 0; }

.rich-text :is(h1, h2, h3, h4) {
  font-weight: 700;
  line-height: 1.35;
  margin: 1em 0 .4em;
  color: inherit;
}
.rich-text > :first-child { margin-top: 0; }
.rich-text h1 { font-size: 1.5em; }
.rich-text h2 { font-size: 1.3em; }
.rich-text h3 { font-size: 1.15em; }
.rich-text h4 { font-size: 1em; }

.rich-text a {
  color: var(--rt-link, var(--color-primary));
  text-decoration: underline;
  text-underline-offset: 2px;
}
.rich-text a:hover { text-decoration-thickness: 2px; }

.rich-text blockquote {
  padding: .35em 0 .35em 1em;
  border-left: 3px solid var(--rt-accent, var(--color-primary));
  color: var(--rt-muted, inherit);
}

.rich-text pre {
  white-space: pre-wrap;
  font-family: 'SFMono-Regular', Consolas, monospace;
  font-size: .92em;
  padding: .6em .8em;
  background: var(--bg);
  border-radius: var(--radius);
  overflow-x: auto;
}
.rich-text hr { border: 0; border-top: 1px solid var(--border); margin: 1em 0; }

/* Listy: `ul { list-style: none }` z resetu gasi znaczniki, więc lista bez
   `data-list` dostaje je jawnie */
.rich-text ol,
.rich-text ul { padding-left: 1.5em; }
.rich-text ul > li:not([data-list]) { list-style-type: disc; }
.rich-text ol > li:not([data-list]) { list-style-type: decimal; }
.rich-text li::marker { color: var(--rt-marker, inherit); }

.rich-text li[data-list] {
  list-style-type: none;
  position: relative;
  padding-left: 1.5em;
  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8;
}
/* Pusty nośnik punktora z edytora - punktor rysuje sam `li` */
.rich-text li > .ql-ui { display: none; }
/* Szerokość netto znacznika = 0 (-1.5em + 1.2em + .3em): tekst zaczyna się
   tam, gdzie w edytorze, a znacznik wchodzi w wcięcie `li` */
.rich-text li[data-list]::before {
  display: inline-block;
  margin-left: -1.5em;
  margin-right: .3em;
  width: 1.2em;
  text-align: right;
  white-space: nowrap;
  color: var(--rt-marker, inherit);
  font-variant-numeric: tabular-nums;
}
.rich-text li[data-list="bullet"]::before    { content: '\2022'; }
.rich-text li[data-list="checked"]::before   { content: '\2611'; }
.rich-text li[data-list="unchecked"]::before { content: '\2610'; }
.rich-text li[data-list="ordered"] { counter-increment: list-0; }
.rich-text li[data-list="ordered"]::before { content: counter(list-0, decimal) '. '; }

/* Poziomy wcięcia: numeracja jak w Quillu (a. / i. / 1. na zmianę),
   każdy poziom zeruje głębsze */
.rich-text li[data-list].ql-indent-1 { counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8; }
.rich-text li[data-list].ql-indent-2 { counter-reset: list-3 list-4 list-5 list-6 list-7 list-8; }
.rich-text li[data-list].ql-indent-3 { counter-reset: list-4 list-5 list-6 list-7 list-8; }
.rich-text li[data-list].ql-indent-4 { counter-reset: list-5 list-6 list-7 list-8; }
.rich-text li[data-list].ql-indent-5 { counter-reset: list-6 list-7 list-8; }
.rich-text li[data-list].ql-indent-6 { counter-reset: list-7 list-8; }
.rich-text li[data-list].ql-indent-7 { counter-reset: list-8; }
.rich-text li[data-list].ql-indent-8 { counter-reset: none; }

.rich-text li[data-list="ordered"].ql-indent-1 { counter-increment: list-1; }
.rich-text li[data-list="ordered"].ql-indent-2 { counter-increment: list-2; }
.rich-text li[data-list="ordered"].ql-indent-3 { counter-increment: list-3; }
.rich-text li[data-list="ordered"].ql-indent-4 { counter-increment: list-4; }
.rich-text li[data-list="ordered"].ql-indent-5 { counter-increment: list-5; }
.rich-text li[data-list="ordered"].ql-indent-6 { counter-increment: list-6; }
.rich-text li[data-list="ordered"].ql-indent-7 { counter-increment: list-7; }
.rich-text li[data-list="ordered"].ql-indent-8 { counter-increment: list-8; }
.rich-text li[data-list="ordered"].ql-indent-1::before { content: counter(list-1, lower-alpha) '. '; }
.rich-text li[data-list="ordered"].ql-indent-2::before { content: counter(list-2, lower-roman) '. '; }
.rich-text li[data-list="ordered"].ql-indent-3::before { content: counter(list-3, decimal) '. '; }
.rich-text li[data-list="ordered"].ql-indent-4::before { content: counter(list-4, lower-alpha) '. '; }
.rich-text li[data-list="ordered"].ql-indent-5::before { content: counter(list-5, lower-roman) '. '; }
.rich-text li[data-list="ordered"].ql-indent-6::before { content: counter(list-6, decimal) '. '; }
.rich-text li[data-list="ordered"].ql-indent-7::before { content: counter(list-7, lower-alpha) '. '; }
.rich-text li[data-list="ordered"].ql-indent-8::before { content: counter(list-8, lower-roman) '. '; }

.rich-text .ql-indent-1:not(.ql-direction-rtl) { padding-left: 3em; }
.rich-text .ql-indent-2:not(.ql-direction-rtl) { padding-left: 6em; }
.rich-text .ql-indent-3:not(.ql-direction-rtl) { padding-left: 9em; }
.rich-text .ql-indent-4:not(.ql-direction-rtl) { padding-left: 12em; }
.rich-text .ql-indent-5:not(.ql-direction-rtl) { padding-left: 15em; }
.rich-text .ql-indent-6:not(.ql-direction-rtl) { padding-left: 18em; }
.rich-text .ql-indent-7:not(.ql-direction-rtl) { padding-left: 21em; }
.rich-text .ql-indent-8:not(.ql-direction-rtl) { padding-left: 24em; }
.rich-text li.ql-indent-1:not(.ql-direction-rtl) { padding-left: 4.5em; }
.rich-text li.ql-indent-2:not(.ql-direction-rtl) { padding-left: 7.5em; }
.rich-text li.ql-indent-3:not(.ql-direction-rtl) { padding-left: 10.5em; }
.rich-text li.ql-indent-4:not(.ql-direction-rtl) { padding-left: 13.5em; }
.rich-text li.ql-indent-5:not(.ql-direction-rtl) { padding-left: 16.5em; }
.rich-text li.ql-indent-6:not(.ql-direction-rtl) { padding-left: 19.5em; }
.rich-text li.ql-indent-7:not(.ql-direction-rtl) { padding-left: 22.5em; }
.rich-text li.ql-indent-8:not(.ql-direction-rtl) { padding-left: 25.5em; }
.rich-text .ql-direction-rtl { direction: rtl; }

.rich-text .ql-align-center  { text-align: center; }
.rich-text .ql-align-right   { text-align: right; }
.rich-text .ql-align-justify { text-align: justify; }


/* ================================================================
   22. FOOTER
   ================================================================ */

.app-footer {
  display: flex; justify-content: flex-end; align-items: center;
  font-size: var(--fs-sm); color: var(--text-secondary);
  padding: 8px 0; margin-top: auto;
}
.footer-social-icon { color: var(--text-secondary); margin-left: 6px; font-size: 14px; transition: color var(--transition); }
.footer-social-icon:hover { color: var(--color-primary); }


/* ================================================================
   23. UTILITIES
   ================================================================ */

/* ── Spacing ── */
.pt-0  { padding-top:    0px  !important; }
.pt-5  { padding-top:    5px  !important; }
.pt-10 { padding-top:   10px  !important; }
.pt-15 { padding-top:   15px  !important; }
.pt-20 { padding-top:   20px  !important; }

.pb-0  { padding-bottom:  0px  !important; }
.pb-5  { padding-bottom:  5px  !important; }
.pb-10 { padding-bottom: 10px  !important; }
.pb-15 { padding-bottom: 15px  !important; }
.pb-20 { padding-bottom: 20px  !important; }

.mt-0  { margin-top:     0px  !important; }
.mt-5  { margin-top:     5px  !important; }
.mt-8  { margin-top:     8px  !important; }
.mt-10 { margin-top:    10px  !important; }
.mt-15 { margin-top:    15px  !important; }
.mt-16 { margin-top:    16px  !important; }
.mt-20 { margin-top:    20px  !important; }

.mb-0  { margin-bottom:  0px  !important; }
.mb-5  { margin-bottom:  5px  !important; }
.mb-10 { margin-bottom: 10px  !important; }
.mb-15 { margin-bottom: 15px  !important; }
.mb-20 { margin-bottom: 20px  !important; }

/* ── Gap helpers ── */
.gap-0  { gap:  0px !important; }
.gap-4  { gap:  4px !important; }
.gap-8  { gap:  8px !important; }
.gap-12 { gap: 12px !important; }
.gap-16 { gap: 16px !important; }
.gap-20 { gap: 20px !important; }
.gap-24 { gap: 24px !important; }

/* ── Text ── */
.text-muted   { color: var(--text-secondary) !important; }
.text-primary { color: var(--text-primary)   !important; }
.text-teal    { color: var(--teal)           !important; }
.text-danger  { color: var(--danger)         !important; }
.text-success { color: var(--success)        !important; }
.text-warning { color: var(--warning)        !important; }
.text-blue    { color: var(--blue)           !important; }
.text-center  { text-align: center           !important; }
.text-right   { text-align: right            !important; }
.text-sm      { font-size: var(--fs-sm)      !important; }
.text-xs      { font-size: var(--fs-xs)      !important; }
.text-bold    { font-weight: 700             !important; }
.text-semibold { font-weight: 600            !important; }

/* ── Display ── */
.d-none    { display: none      !important; }
.d-flex    { display: flex      !important; }
.d-grid    { display: grid      !important; }
.d-block   { display: block     !important; }
.d-inline  { display: inline    !important; }

/* ── Flex helpers ── */
.flex-1       { flex: 1          !important; }
.flex-wrap    { flex-wrap: wrap   !important; }
.items-center { align-items: center     !important; }
.items-start  { align-items: flex-start !important; }
.items-end    { align-items: flex-end   !important; }
.justify-between { justify-content: space-between !important; }
.justify-center  { justify-content: center        !important; }
.justify-end     { justify-content: flex-end       !important; }

/* ── Width ── */
.w-full { width: 100% !important; }
.w-auto { width: auto !important; }

/* ── HR Dividers ── */
hr.hr,
hr.hr-dashed,
hr.hr-gradient {
  border: none;
  margin: var(--sp-6) 0;
  height: 1px;
}

/* Wariant 1: prosta linia (domyślny) */
hr.hr {
  background: var(--border);
}

/* Wariant 2: linia przerywana */
hr.hr-dashed {
  background: none;
  border-top: 1px dashed var(--border);
  height: 0;
}

/* Wariant 3: gradient fade (zanika na krawędziach) */
hr.hr-gradient {
  background: linear-gradient(90deg, transparent, var(--border) 20%, var(--border) 80%, transparent);
}

/* Wersja tealowa (akcentowa) */
hr.hr-teal        { background: var(--accent-dim); }
hr.hr-dashed.hr-teal { border-top-color: var(--teal); border-top-style: dashed; background: none; }
hr.hr-gradient.hr-teal { background: linear-gradient(90deg, transparent, var(--accent-dim) 20%, var(--accent-dim) 80%, transparent); }


/* ================================================================
   DATATABLES v2 OVERRIDES
   - Ukryj wbudowane search/length, użyj custom #client-search
   - Paginacja: full_numbers z ikonami Bootstrap Icons
   - Brak obramowań tabeli, brak podświetlania sortowanej kolumny
   ================================================================ */

/* ── Hide built-in search & length (use custom #client-search) ── */
div.dt-search,
div.dt-length { display: none !important; }

/* ── Wskaznik "Ladowanie" (processing) ──
   Kropki vendora bierze `--dt-row-selected` (na sztywno niebieski Bootstrapa,
   trojka RGB - koloru studia w hex tam nie wstawimy), wiec tlo idzie wprost
   z koloru wiodacego. Opoznienie 400 ms: tabele serverSide dociagaja pierwsza
   strone osobnym zapytaniem i bez niego box migal przy kazdym odswiezeniu.
   `both` trzyma opacity 0 przez czas opoznienia; DataTables przelacza
   `display`, wiec animacja startuje od nowa przy kazdym zapytaniu. */
div.dt-processing { animation: dt-processing-in .15s ease 400ms both; }
div.dt-processing > div:last-child > div { background: var(--color-primary); }
@keyframes dt-processing-in { from { opacity: 0; } to { opacity: 1; } }

table.dataTable thead > tr > th.dt-orderable-asc:hover,
table.dataTable thead > tr > th.dt-orderable-desc:hover,
table.dataTable thead > tr > td.dt-orderable-asc:hover,
table.dataTable thead >tr > td.dt-orderable-desc:hover {
    outline: 0 !important;
}

/* ── Table borders reset ── */
table.dataTable { border-collapse: collapse !important; }
table.dataTable, table.dataTable th, table.dataTable td { border: none !important; }
table.dataTable thead th {
  border-bottom: 1px solid var(--border) !important;
  background: var(--surface) !important;
  font-size: var(--fs-xs) !important;
  font-weight: 600 !important;
  color: var(--text-secondary) !important;
  text-transform: uppercase !important;
  letter-spacing: 0.3px !important;
  padding: 10px 12px !important;
  font-family: 'Inter', sans-serif !important;
}
table.dataTable thead th:first-child { padding-left: 20px !important; }
table.dataTable thead th:last-child { padding-right: 20px !important; }

table.dataTable tbody td {
  border-bottom: 1px solid var(--border) !important;
  padding: 12px !important;
  font-size: var(--fs-nav) !important;
  color: var(--text-primary) !important;
  vertical-align: middle !important;
}
table.dataTable tbody td:first-child { padding-left: 20px !important; }
table.dataTable tbody td:last-child { padding-right: 20px !important; }
table.dataTable tbody tr:last-child td { border-bottom: none !important; }

table.dataTable tbody tr { background: var(--surface) !important; }
table.dataTable tbody tr:hover td { background-color: color-mix(in srgb, var(--color-primary) 6%, transparent) !important; }

/* ── Row selected ── */
table.dataTable tbody tr.row-selected td { background-color: color-mix(in srgb, var(--color-primary) 6%, transparent) !important; }

/* ── Hide sorting column highlight ── */
table.dataTable.no-footer { border-bottom: none !important; }
table.dataTable tbody td.sorting_1,
table.dataTable tbody td.sorting_2,
table.dataTable tbody td.sorting_3 { background-color: inherit !important; }

/* ── Date columns always left-aligned (override DataTables auto numeric/date right-align) ── */
table.dataTable thead th.dt-type-date,
table.dataTable tbody td.dt-type-date,
table.dataTable thead th.col-date,
table.dataTable tbody td.col-date,
table.dataTable thead th[data-type="date"],
table.dataTable tbody td[data-type="date"] { text-align: left !important; }

/* ── Znaczniki sortowania - JEDNO zrodlo dla trzech paneli ──
   Dwa blade trojkaty przy prawej krawedzi kolumny, aktywny kierunek
   ciemniejszy. Te same reguly rysuja dwa rodzaje naglowkow:
     - DataTables (sortowanie w przegladarce): `span.dt-column-order`,
       stan w klasach `dt-orderable-*` / `dt-ordering-*`,
     - naglowek sortowany NA SERWERZE (listy ze stronicowaniem po stronie
       serwera, Platform::sortTh()): `th.th-sort > a > span.th-sort-order`,
       stan w `aria-sort`.
   Arkusz DataTables rysuje trojkaty po swojemu - tutaj sa nadpisane
   (`!important`, jak cala ta sekcja), wiec wyglad zalezy wylacznie od
   tego pliku. Warunki "ktory trojkat" zostaja jak u DataTables: kolumna
   sortowalna tylko w jedna strone pokazuje jeden trojkat.
   Pozycja absolutna, zeby znacznik nie zmienial wysokosci naglowka. */
table.dataTable thead th,
th.th-sort {
  position: relative !important;
}

table.dataTable thead .dt-column-order,
th.th-sort .th-sort-order {
  position: absolute !important;
  top: 50% !important;
  bottom: 0 !important;
  right: 8px !important;
  width: 12px !important;
  transform: translateY(-50%) !important;
  opacity: .3 !important;
}

table.dataTable thead .dt-column-order::before,
table.dataTable thead .dt-column-order::after,
th.th-sort .th-sort-order::before,
th.th-sort .th-sort-order::after {
  position: absolute !important;
  left: 0 !important;
  display: block;
  font-size: .8em !important;
  line-height: 9px !important;
  opacity: .125 !important;
}

table.dataTable thead th.dt-orderable-asc .dt-column-order::before,
table.dataTable thead th.dt-ordering-asc .dt-column-order::before,
th.th-sort .th-sort-order::before {
  content: "▲" !important;
  content: "▲" / "" !important;
  bottom: 50% !important;
}

table.dataTable thead th.dt-orderable-desc .dt-column-order::after,
table.dataTable thead th.dt-ordering-desc .dt-column-order::after,
th.th-sort .th-sort-order::after {
  content: "▼" !important;
  content: "▼" / "" !important;
  top: 50% !important;
}

table.dataTable thead th.dt-ordering-asc .dt-column-order::before,
table.dataTable thead th.dt-ordering-desc .dt-column-order::after,
th.th-sort[aria-sort="ascending"] .th-sort-order::before,
th.th-sort[aria-sort="descending"] .th-sort-order::after {
  opacity: .6 !important;
}

/* Naglowek sortowany na serwerze to link - klikalny w calej komorce, jak
   w DataTables (`a::before` rozciaga go na `th`). Kolumna wyrownana do
   prawej dostaje miejsce na trojkaty, inaczej liczba stalaby pod nimi. */
th.th-sort { cursor: pointer; }

th.th-sort > a {
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

th.th-sort > a::before {
  content: '';
  position: absolute;
  inset: 0;
}

th.th-sort.dt-right,
th.th-sort.text-right { padding-right: 28px; }

/* ── Info row ── */
div.dt-info {
  font-size: var(--fs-sm) !important;
  color: var(--text-secondary) !important;
  font-family: 'Inter', sans-serif !important;
  padding: 18px 0 0 20px;
}

/* ── Layout rows ── */
.dt-layout-row { display: flex !important; align-items: center !important; }
.dt-layout-row:last-child {
  justify-content: space-between !important;
  flex-wrap: nowrap !important;
  padding: 12px 20px !important;
  border-top: 1px solid var(--border) !important;
  margin-top: 0 !important;
}

.dt-layout-bottom {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: nowrap !important;
  align-items: center !important;
  justify-content: space-between !important;
}
.dt-layout-bottom > div {
  width: auto !important;
  flex: 0 0 auto !important;
  max-width: none !important;
}
.dt-layout-bottom > div.dt-end {
  margin-left: auto !important;
}

/* ── Pagination ── */
div.dt-paging {
  display: flex !important; align-items: center !important;
  flex-wrap: nowrap !important;
  gap: 4px !important; justify-content: flex-end !important;
  padding: 10px 20px 0 0;
}

div.dt-paging .dt-paging-button {
  display: inline-flex !important; align-items: center !important; justify-content: center !important;
  width: 38px !important; height: 32px !important; padding: 0 !important;
  flex: 0 0 32px !important;
  line-height: 1 !important; border-radius: 6px !important;
  font-size: var(--fs-sm) !important; font-weight: 400 !important;
  font-family: 'Inter', sans-serif !important; cursor: pointer !important;
  border: 1px solid var(--border) !important;
  background: var(--surface) !important; color: var(--text-secondary) !important;
  margin: 0 2px !important; vertical-align: middle !important;
  box-sizing: border-box !important; float: none !important;
  transition: background var(--transition), color var(--transition), border-color var(--transition) !important;
}

div.dt-paging .dt-paging-button:hover:not(.disabled) {
  background: var(--bg) !important; color: var(--text-primary) !important;
}

div.dt-paging .dt-paging-button.current,
div.dt-paging .dt-paging-button.current:hover,
div.dt-paging .dt-paging-button.current:focus {
  background: var(--teal) !important; color: var(--surface) !important; border-color: var(--teal) !important;
  text-shadow: none !important; -webkit-text-fill-color: var(--surface) !important;
  font-weight: 600 !important;
}
div.dt-paging .dt-paging-button.current *,
div.dt-paging .dt-paging-button.current:hover * {
  color: var(--surface) !important; -webkit-text-fill-color: var(--surface) !important;
}

div.dt-paging .dt-paging-button.disabled {
  opacity: 0.35 !important; cursor: default !important; pointer-events: none !important;
}

div.dt-paging .dt-paging-button i {
  display: flex !important; align-items: center !important; justify-content: center !important;
  font-size: 14px !important; line-height: 1 !important;
}

/* ── DataTables na wąskim ekranie (plan: wyglad-mobilny.md, etap 8) ──
   Vendor ma własny blok mobilny (`@media screen and (max-width: 767px)`
   w `dataTables.dataTables.min.css`), który układa wiersze layoutu w pion.
   Kasuje go jednak `.dt-layout-row { display: flex !important }` z linii 3259
   powyżej - reguła potrzebna na komputerze, żeby licznik i paginacja stały
   w jednej linii.

   Ten blok MUSI stać PO tamtej regule. Przy `!important` po obu stronach
   i równej specyficzności rozstrzyga kolejność - blok wstawiony wyżej
   w pliku byłby martwy i wyglądałby na działający wyłącznie w narzędziach
   dewelopera. To ten sam mechanizm, który blok naprawia. */
@media (max-width: 768px) {
  div.dt-container div.dt-layout-row:not(.dt-layout-table) {
    display: block !important;
  }
  div.dt-container div.dt-layout-cell { text-align: left !important; }

  /* Paginacja przewija się w poziomie zamiast rozpychać stronę: przy
     kilkunastu stronach wyników numery nie zmieszczą się na 390 px nigdy. */
  div.dt-paging {
    padding: 10px 0 0;
    overflow-x: auto;
    justify-content: flex-start !important;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  div.dt-paging::-webkit-scrollbar { display: none; }
  div.dt-paging .dt-paging-button { flex: none !important; min-width: 40px !important; }
}


/* ================================================================
   DANGER ZONE (np. na stronie konta)
   ================================================================ */

.danger-zone {
  background: var(--surface);
  border: 1.5px solid var(--danger);
  border-radius: var(--radius);
  overflow: hidden;
}

.danger-zone-header {
  background: var(--danger-dim);
  padding: 12px 20px;
  display: flex; align-items: center; gap: 8px;
  border-bottom: 1px solid color-mix(in srgb, var(--danger) 20%, transparent);
}

.danger-zone-header i { color: var(--danger); font-size: var(--fs-md); }
.danger-zone-header span { font-size: var(--fs-xs); font-weight: 600; color: var(--danger); text-transform: uppercase; letter-spacing: .06em; }

.danger-zone-body { padding: 20px; }

.danger-zone-body p {
  font-size: var(--fs-nav); color: var(--text-secondary); margin-bottom: 4px;
}

.danger-zone-body .sub {
  font-size: var(--fs-xs); color: var(--text-secondary); margin-bottom: 14px;
}


/* ================================================================
   UPLOAD AREA (drag & drop file upload box)

   Użycie:
   <label class="upload-area" for="fileUpload">
     <i class="bi bi-cloud-upload upload-area-icon"></i>
     <div class="upload-area-text">
       <strong>Kliknij, aby wgrać</strong> lub przeciągnij plik tutaj<br>
       PNG, JPG - maks. 2 MB · zalecane 400×200 px
     </div>
     <img class="upload-preview" id="preview" alt="Podgląd">
   </label>
   <input type="file" id="fileUpload" accept="image/*" style="display:none">
   ================================================================ */

.upload-area {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 28px 20px;
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  cursor: pointer;
  transition: border-color .2s, background .2s;
  text-align: center;
  position: relative;
  min-height: 120px;
}

.upload-area:hover,
.upload-area.dragover {
  border-color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 4%, transparent);
}

.upload-area-icon {
  font-size: 28px;
  color: var(--text-secondary);
  opacity: .5;
}

.upload-area:hover .upload-area-icon { color: var(--teal); opacity: .8; }

.upload-area-text {
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.upload-area-text strong {
  color: var(--teal);
  font-weight: 600;
}

.upload-preview {
  display: none;
  max-width: 200px;
  max-height: 100px;
  object-fit: contain;
  border-radius: 6px;
  margin-top: 6px;
}

.upload-preview[src]:not([src=""]) { display: block; }

.upload-area.has-preview .upload-area-icon,
.upload-area.has-preview .upload-area-text { display: none; }

.upload-preview-wrap {
  position: relative;
  display: inline-block;
}

.upload-remove {
  position: absolute; top: -6px; right: -6px;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--danger);
  color: var(--color-on-danger);
  border: 2px solid var(--bg);
  cursor: pointer;
  display: none;
  align-items: center; justify-content: center;
  font-size: 14px; line-height: 1;
  z-index: 2;
  transition: background .2s;
  padding: 0;
}
.upload-remove:hover { background: var(--color-danger-hover); }
.upload-area.has-preview .upload-remove { display: flex; }


/* ================================================================
   COLOR PICKER (preset swatches + custom input)

   Użycie - presety + custom:
   <div class="color-presets">
     <button type="button" class="color-swatch selected" style="background:#1baf92" data-color="#1baf92" title="Teal"></button>
     <button type="button" class="color-swatch" style="background:#6c7de8" data-color="#6c7de8" title="Indigo"></button>
     ...
     <span class="color-separator">
       <span class="color-separator-label">lub zdefiniuj własny</span>
       <input type="color" class="color-input-custom" value="#1baf92" title="Własny kolor">
     </span>
   </div>

   Użycie - sam custom (bez presetów):
   <input type="color" class="color-input-custom" value="#ffffff">

   JS (main.js): kliknięcia w preset + sync custom input obsługiwane globalnie.
   Callback po zmianie: nasłuchuj event "colorchange" na kontenerze .color-presets
   lub na samym .color-input-custom.
   ================================================================ */

.color-presets {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.color-swatch {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform .15s, border-color .15s;
  flex-shrink: 0;
  position: relative;
}

.color-swatch:hover { transform: scale(1.12); }

.color-swatch.selected { border-color: var(--text-primary); }

.color-swatch.selected::after {
  content: '\F26E';
  font-family: 'bootstrap-icons';
  color: #fff;
  font-size: 12px;
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.color-presets .color-separator {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: 8px;
  padding-left: 16px;
  border-left: 1px solid var(--border, #ddd);
}

.color-presets .color-separator-label {
  font-size: 12px;
  color: var(--text-secondary, #888);
  white-space: nowrap;
  user-select: none;
}

.color-input-custom {
  width: 34px;
  height: 34px;
  border: 1px solid transparent;
  border-radius: 50%;
  padding: 2px;
  cursor: pointer;
  background: transparent;
  flex-shrink: 0;
  transition: transform .15s, border-color .15s;
}

.color-input-custom:hover { transform: scale(1.12); }

.color-input-custom::-webkit-color-swatch-wrapper { padding: 0; }
.color-input-custom::-webkit-color-swatch { border: none; border-radius: 50%; }
.color-input-custom::-moz-color-swatch { border: none; border-radius: 50%; }

.color-hex-value {
  font-family: 'Courier New', monospace;
  font-size: var(--fs-sm, 13px);
  color: var(--text-secondary);
}


/* ================================================================
   FORM GRID (settings-style 2-col form grid)

   Sama siatka. Pole, etykieta i podpowiedz to .field / .label / .input
   z sekcji FORMS - rownolegly zestaw .form-group / .form-label / .form-hint
   zostal usuniety (A211), bo powielal te same deklaracje i rozjezdzal sie
   z oryginalem.

   Użycie:
   <div class="form-grid">
     <div class="field">
       <label class="label">Nazwa</label>
       <input class="input" type="text">
     </div>
     <div class="field form-span-2">
       <label class="label">Opis</label>
       <textarea class="textarea">...</textarea>
     </div>
   </div>

   Warianty: .form-grid-3, .form-grid-3-1/2/3
   ================================================================ */

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.form-grid-3 { grid-template-columns: 1fr 1fr 1fr; }
.form-grid-3-1 { grid-template-columns: 2fr 1fr 1fr; }

.form-span-2 { grid-column: span 2; }
.form-span-3 { grid-column: span 3; }

/* Range slider */
input[type="range"].form-range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  background: var(--border);
  border-radius: var(--radius-full);
  outline: none;
  cursor: pointer;
  transition: background .18s;
}

input[type="range"].form-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--teal);
  border: 2px solid var(--surface);
  box-shadow: 0 1px 4px rgba(0,0,0,.2);
  cursor: pointer;
  transition: box-shadow .18s, transform .18s;
  margin-top: -6px;
}

input[type="range"].form-range::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--teal);
  border: 2px solid var(--surface);
  box-shadow: 0 1px 4px rgba(0,0,0,.2);
  cursor: pointer;
  transition: box-shadow .18s, transform .18s;
}

input[type="range"].form-range::-webkit-slider-thumb:hover {
  box-shadow: 0 0 0 4px var(--accent-dim), 0 1px 4px rgba(0,0,0,.2);
  transform: scale(1.1);
}

input[type="range"].form-range::-moz-range-thumb:hover {
  box-shadow: 0 0 0 4px var(--accent-dim), 0 1px 4px rgba(0,0,0,.2);
  transform: scale(1.1);
}

input[type="range"].form-range:focus-visible::-webkit-slider-thumb {
  box-shadow: var(--focus-ring), 0 1px 4px rgba(0,0,0,.2);
}

input[type="range"].form-range:focus-visible::-moz-range-thumb {
  box-shadow: var(--focus-ring), 0 1px 4px rgba(0,0,0,.2);
}

input[type="range"].form-range::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--radius-full);
}

input[type="range"].form-range::-moz-range-track {
  height: 6px;
  background: var(--border);
  border-radius: var(--radius-full);
  border: none;
}

input[type="range"].form-range::-moz-range-progress {
  background: var(--teal);
  height: 6px;
  border-radius: var(--radius-full);
}

.form-section { margin-bottom: 20px; }
.form-section:last-child { margin-bottom: 0; }

/* W `.panel-body` odstep miedzy sekcjami daje juz `gap` panelu (16 px) -
   margines sekcji sumowal sie z nim i z tytulem do 44 px. Zostaje gap
   i 8 px nad tytulem z kreska: 24 px (decyzja wlasciciela 13.09.2026). */
.panel-body > .form-section { margin-bottom: 0; }

.form-section-title {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: .05em;
  margin: 8px 0 16px;
}

.form-section-title::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

.form-section:first-child .form-section-title {
  margin-top: 0;
}


/* ================================================================
   HOURS LIST (godziny otwarcia - divy z flexem, togglem i time inputami)

   Użycie:
   <div class="hours-list">
     <div class="hours-row">
       <span class="hours-day-name">Poniedziałek</span>
       <label class="toggle-mini"><input type="checkbox" checked><span class="toggle-slider"></span></label>
       <div class="hours-time-group">...</div>
     </div>
   </div>
   ================================================================ */

.hours-list {
  display: flex;
  flex-direction: column;
}

.hours-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-nav);
}

.hours-row:last-child {
  border-bottom: none;
}

.hours-day-name {
  width: 140px;
  flex-shrink: 0;
  font-weight: 500;
  color: var(--text-primary);
}

.hours-time-input {
  height: 34px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  font-family: 'Inter', sans-serif;
  font-size: var(--fs-sm);
  color: var(--text-primary);
  outline: none;
  transition: border-color .18s;
  width: 100px;
}

.hours-time-input:focus { border-color: var(--teal); }
.hours-time-input:disabled { opacity: .4; cursor: not-allowed; }

.hours-sep {
  padding: 0 8px;
  color: var(--text-secondary);
  font-size: var(--fs-xs);
}

.hours-closed {
  font-size: var(--fs-xs);
  color: var(--danger);
  font-weight: 600;
}

/* Godziny otwarcia w dwoch kolumnach - panel pelnej szerokosci.
   Kolumny dziela dni po znaczeniu (robocze / weekend), a nie po polowie
   listy, wiec sa nierowne - naglowek grupy to tlumaczy. */
.hours-list-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 40px;
  align-items: start;
}

.hours-group {
  display: flex;
  flex-direction: column;
}

.hours-group-title {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: .05em;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

/* Kazda kolumna konczy sie bez kreski - inaczej krotsza wisi w powietrzu */
.hours-group .hours-row:last-child { border-bottom: none; }

@media (max-width: 1024px) {
  .hours-list-2col { grid-template-columns: 1fr; column-gap: 0; }
  .hours-group + .hours-group { margin-top: 20px; }
}

/* Toggle mini (inline - for hours table, compact toggle) */
.toggle-mini {
  position: relative;
  display: inline-block;
  width: 34px;
  height: 20px;
  cursor: pointer;
}

.toggle-mini input { display: none; }

.toggle-slider {
  position: absolute;
  inset: 0;
  background: var(--border);
  border-radius: 999px;
  transition: background .2s;
}

.toggle-slider::after {
  content: '';
  position: absolute;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: #fff;
  top: 3px; left: 3px;
  transition: transform .2s;
  box-shadow: 0 1px 3px rgba(0,0,0,.25);
}

.toggle-mini input:checked + .toggle-slider { background: var(--teal); }
.toggle-mini input:checked + .toggle-slider::after { transform: translateX(14px); }


/* ================================================================
   DOTS MENU (context menu - three dots dropdown)

   Użycie:
   <div class="dots-wrap">
     <button class="dots-trigger"><i class="bi bi-three-dots-vertical"></i></button>
     <div class="dots-menu">
       <button class="dots-menu-item"><i class="bi bi-eye"></i> Podgląd</button>
       <button class="dots-menu-item"><i class="bi bi-pencil"></i> Edytuj</button>
       <div class="dots-menu-divider"></div>
       <button class="dots-menu-item danger"><i class="bi bi-trash3"></i> Usuń</button>
     </div>
   </div>
   ================================================================ */

.dots-wrap { position: relative; display: inline-block; }

.dots-trigger {
  background: none; border: 1px solid transparent; border-radius: 5px;
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--text-secondary); font-size: 16px;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.dots-trigger:hover,
.dots-trigger.open {
  background: var(--bg); border-color: var(--border); color: var(--text-primary);
}

.dots-menu {
  position: fixed; z-index: var(--z-popover);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  min-width: 175px; padding: 4px 0;
  opacity: 0; transform: translateY(-6px);
  pointer-events: none; transition: opacity .18s ease, transform .18s ease;
}

.dots-menu.open { opacity: 1; transform: translateY(0); pointer-events: auto; }

.dots-menu-item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px; font-size: var(--fs-sm); color: var(--text-primary);
  cursor: pointer; transition: background var(--transition);
  border: none; background: none; width: 100%; text-align: left; font-family: inherit;
}

.dots-menu-item:hover { background: var(--bg); }
.dots-menu-item.danger { color: var(--danger); }
.dots-menu-divider { height: 1px; background: var(--border); margin: 4px 0; }


/* ================================================================
   STAT BAR (row of key metrics above table)

   Użycie:
   <div class="stat-bar">
     <div class="stat-item"><span class="stat-num">247</span> Klientów</div>
     <div class="stat-item"><span class="stat-num">189</span> Aktywnych</div>
   </div>
   ================================================================ */

.stat-bar {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 10px 20px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  row-gap: 6px;
}

.stat-item {
  display: flex;
  align-items: center;
  gap: 5px;
  padding-right: 18px;
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}

.stat-item + .stat-item {
  padding-left: 18px;
  border-left: 1px solid var(--border);
}

.stat-num {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-primary);
}


/* ================================================================
   BULK ACTION BAR (floating bar for multi-select actions)

   Użycie:
   <div class="bulk-bar" id="bulkBar">
     <span class="bulk-label">Zaznaczono: <strong>3</strong></span>
     <span class="bulk-sep"></span>
     <button class="bulk-btn"><i class="bi bi-envelope"></i> Email</button>
     <button class="bulk-btn danger"><i class="bi bi-trash3"></i> Usuń</button>
   </div>

   JS: el.classList.add('visible');
   ================================================================ */

.bulk-bar {
  position: fixed;
  bottom: -90px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 10px 18px;
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: var(--z-bulk);
  transition: bottom .28s cubic-bezier(.4, 0, .2, 1);
  white-space: nowrap;
}

.bulk-bar.visible { bottom: 24px; }

.bulk-label {
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-primary); padding-right: 4px;
}

.bulk-sep {
  width: 1px; height: 20px; background: var(--border); flex-shrink: 0;
}

.bulk-btn {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 12px; border: 1px solid var(--border); border-radius: 6px;
  background: transparent; color: var(--text-primary);
  font-size: var(--fs-sm); font-family: inherit; cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.bulk-btn:hover { background: var(--bg); }
.bulk-btn.danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.bulk-btn.danger:hover { background: var(--danger-dim); }


/* ================================================================
   TOAST NOTIFICATION

   Użycie:
   <div class="toast-container" id="toastContainer">
     <div class="toast toast-success show">
       <i class="bi bi-check-circle-fill toast-icon"></i>
       Zmiany zostały zapisane pomyślnie.
     </div>
   </div>
   ================================================================ */

.toast-container {
  position: fixed;
  top: calc(var(--topbar-h) + 12px);
  right: 20px;
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.toast {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal);
  border-radius: var(--radius);
  padding: 12px 16px;
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-sm);
  color: var(--text-primary);
  transform: translateX(140%);
  transition: transform .3s cubic-bezier(.4, 0, .2, 1);
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 320px;
}

.toast.show { transform: translateX(0); }

.toast-icon { font-size: 18px; flex-shrink: 0; }

.toast-success             { border-left-color: var(--teal); }
.toast-success .toast-icon { color: var(--teal); }
.toast-error,
.toast-danger              { border-left-color: var(--danger); }
.toast-error .toast-icon,
.toast-danger .toast-icon  { color: var(--danger); }
.toast-warning             { border-left-color: var(--warning); }
.toast-warning .toast-icon { color: var(--warning); }
.toast-info                { border-left-color: var(--text-secondary); }
.toast-info .toast-icon    { color: var(--text-secondary); }


/* ================================================================
   EMPTY STATE (placeholder when table/list is empty)

   Użycie:
   <div class="empty-state">
     <i class="bi bi-people empty-icon"></i>
     <div class="empty-title">Brak klientów</div>
     <div class="empty-sub">Dodaj pierwszego klienta</div>
     <button class="btn btn-primary mt-10">Dodaj klienta</button>
   </div>
   ================================================================ */

.empty-state {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 20px;
  gap: 10px;
}

.empty-state.visible { display: flex; }

.empty-icon {
  font-size: 48px;
  color: var(--text-secondary);
  opacity: .35;
  line-height: 1;
}

.empty-title {
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--text-secondary);
  margin-top: 4px;
}

.empty-sub {
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  opacity: .65;
}


/* ================================================================
   LOADING SKELETON (shimmer placeholders)

   Użycie:
   <div class="skeleton-row">
     <div class="skeleton-circle shimmer" style="width:36px;height:36px"></div>
     <div class="sk-col flex-1">
       <div class="skeleton-block shimmer" style="width:140px;height:12px"></div>
       <div class="skeleton-block shimmer" style="width:90px;height:10px"></div>
     </div>
   </div>
   ================================================================ */

.skeleton-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 0;
  border-bottom: 1px solid var(--border);
}

.skeleton-circle {
  border-radius: 50%;
  flex-shrink: 0;
}

.sk-col {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.skeleton-block { border-radius: 4px; }

.shimmer {
  background: linear-gradient(90deg, var(--border) 25%, rgba(255,255,255,.06) 50%, var(--border) 75%);
  background-size: 200% 100%;
  animation: shimmerAnim 1.4s infinite;
}

@keyframes shimmerAnim {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}


/* ================================================================
   ALERT (inline notification bar - info, success, warning, danger)

   Użycie:
   <div class="alert alert-warning">
     <i class="bi bi-exclamation-triangle-fill"></i>
     Masz niezapisane zmiany.
   </div>

   <div class="alert alert-success">
     <i class="bi bi-check-circle-fill"></i>
     Dane zostały zapisane pomyślnie.
   </div>

   <div class="alert alert-danger">
     <i class="bi bi-x-circle-fill"></i>
     Wystąpił błąd podczas zapisywania.
   </div>

   <div class="alert alert-info">
     <i class="bi bi-info-circle-fill"></i>
     Nowa wersja jest dostępna.
   </div>

   Animowane pojawienie: dodaj .alert-collapsible + .visible
   ================================================================ */

.alert {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-radius: var(--radius);
  font-size: var(--fs-sm);
  font-weight: 500;
  margin-bottom: 16px;
  line-height: 1.5;
}

.alert .bi { font-size: var(--fs-md); flex-shrink: 0; }
/* Treść z edytora w pasku (ogłoszenie platformy): odnośnik i kreska cytatu
   w kolorze wariantu paska, ciaśniejsze odstępy bloków */
.alert .rich-text { --rt-link: inherit; --rt-accent: currentColor; --rt-gap: .4em; }

/* Komunikat wielolinijkowy: ikona trzyma sie pierwszego wiersza, nie srodka
   bloku. Bylo pieciokrotnie powtorzonym style="align-items:flex-start". */
.alert-top { align-items: flex-start; }

/* Ikona ma wlasny wiersz rowny wierszowi tekstu (fs-sm x 1.5), inaczej
   wiekszy glif (fs-md) rozpycha swoj line-box i staje ponad pierwsza linia. */
.alert-top > .bi { line-height: calc(var(--fs-sm) * 1.5); }

/* ── Warning (default / unsaved) ── */
.alert-warning {
  background: var(--warning-dim);
  border: 1px solid color-mix(in srgb, var(--warning) 30%, transparent);
  color: var(--warning-ink);
}

/* ── Danger ── */
.alert-danger {
  background: var(--danger-dim);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  color: var(--danger);
}

/* ── Success ── */
.alert-success {
  background: var(--success-dim);
  border: 1px solid color-mix(in srgb, var(--success) 30%, transparent);
  color: var(--success-ink);
}

/* ── Info ── */
.alert-info {
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-secondary);
}

/* ── Teal / accent ── */
.alert-teal {
  background: var(--accent-dim);
  border: 1px solid color-mix(in srgb, var(--color-primary) 25%, transparent);
  color: var(--color-primary-active);
}

/* ── Collapsible (animated show/hide) ── */
.alert-collapsible {
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  padding-top: 0;
  padding-bottom: 0;
  margin-bottom: 0;
  transition: opacity .25s, max-height .3s, margin .3s, padding .3s;
}

.alert-collapsible.visible {
  opacity: 1;
  max-height: 80px;
  padding-top: 10px;
  padding-bottom: 10px;
  margin-bottom: 16px;
}

/* ── Zamkniecie alertu ──
   Kolor bierze z `currentColor` wariantu, wiec jeden przycisk siedzi poprawnie
   w warning, danger, success, info i teal - bez kopii per kolor. Wczesniej
   pasek ogloszen platformy zamykalo `.btn .btn-sm`, czyli szara pastylka
   z napisem, ktora w ciepłym pasku awarii czytala sie jak obcy element.
   Sam znak X wystarcza; tresc dla czytnika ekranu niesie aria-label. */
.alert-close {
  margin-left: auto;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  opacity: .65;
  cursor: pointer;
  transition: opacity .15s, background-color .15s;
}

.alert-close:hover {
  opacity: 1;
  background: color-mix(in srgb, currentColor 12%, transparent);
}

.alert-close:focus-visible {
  opacity: 1;
  outline: 2px solid currentColor;
  outline-offset: 1px;
}

.alert-close .bi { font-size: var(--fs-sm); }

/* Zamkniecie w formularzu POST - sam <form> nie moze rozpychac paska. */
.alert > form.alert-close-form {
  margin-left: auto;
  line-height: 0;
}

/* Pasek ogloszen platformy idzie przez cala szerokosc panelu, wiec na szerokim
   monitorze wiersz robil sie dluzszy niz 200 znakow i oko gubilo powrot do
   poczatku nastepnej linii. Ograniczenie siedzi na samej TRESCI, nie na pasku:
   tlo ma zostac pelnej szerokosci, inaczej komunikat przestaje czytac sie jak
   pas systemowy nad panelem, a zaczyna jak zwykla karta.

   Ikona i X ida do gory zamiast na srodek: przy trzech wierszach tresci
   wysrodkowana ikona stala przy przypadkowym zdaniu, a nie przy naglowku,
   ktory opisuje. */
[data-platform-announcement]       { align-items: flex-start; padding: 12px 16px; }
[data-platform-announcement] > .bi { margin-top: 2px; }
[data-platform-announcement] > form.alert-close-form { margin-top: 2px; }

[data-platform-announcement] > div {
  max-width: 90ch;
  display: flex; flex-direction: column; gap: 4px;
}

/* Naglowek dostaje wlasny rytm - `strong` w linii 1.5 sklejal sie z trescia.
   Tresc schodzi o krok w tle: bez tego hierarchie niosla sama grubosc czcionki
   i pasek czytal sie jak jeden blok, w ktorym pierwszy wiersz przypadkiem jest
   pogrubiony. Ten sam zabieg co nadwiersz chipu waznosci w topbarze. */
[data-platform-announcement] > div > strong   { line-height: 1.3; }
[data-platform-announcement] > div > .text-sm { opacity: .88; }


/* ================================================================
   SETTINGS PAGE LAYOUT

   Użycie:
   <div class="settings-layout">
     <div class="settings-content">...panele...</div>
   </div>
   <div class="settings-save-bar">
     <span class="settings-save-bar-hint">Info</span>
     <button class="btn">Anuluj</button>
     <button class="btn btn-primary">Zapisz</button>
   </div>
   ================================================================ */

.settings-layout {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.settings-content {
  display: flex;
  flex-direction: column;
}

.settings-save-bar {
  position: fixed;
  bottom: 0;
  left: var(--sidebar-w);
  right: 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 14px 24px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  z-index: var(--z-sticky);
  transition: left var(--transition);
}

body.sidebar-collapsed .settings-save-bar { left: 0; }

.settings-save-bar-hint {
  margin-right: auto;
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}


/* ================================================================
   PLUGIN: TOM SELECT - override styles
   CDN:  tom-select/dist/css/tom-select.min.css
   ================================================================ */

.ts-wrapper select { visibility: hidden !important; height: 0 !important; position: absolute !important; }

.ts-wrapper { width: 100%; }

.ts-control {
  height: 34px !important;
  min-height: 34px !important;
  padding: 0 32px 0 12px !important;
  background: var(--bg) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-nav) !important;
  color: var(--text-primary) !important;
  box-shadow: none !important;
  display: flex !important;
  align-items: center !important;
  cursor: pointer !important;
  outline: none !important;
  transition: border-color .18s, box-shadow .18s !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 16 16'%3E%3Cpath fill='%238b95a1' d='M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 12px center !important;
}

.ts-wrapper.focus .ts-control {
  border-color: var(--teal) !important;
  box-shadow: 0 0 0 3px var(--accent-dim) !important;
}

.ts-control .placeholder {
  color: var(--text-secondary) !important;
  font-size: var(--fs-nav) !important;
  line-height: 1 !important;
}

.ts-control > .item {
  color: var(--text-primary) !important;
  font-size: var(--fs-nav) !important;
  font-family: 'Inter', sans-serif !important;
  margin: 0 !important;
  padding: 0 !important;
  line-height: 1 !important;
}

/* Wybor POJEDYNCZY nie ma czego zawijac - zawartosc to zawsze jedna wartosc.
   Dluga nazwa ("Katarzyna Nowakowska-Kowalska") ma zostac przycieta znakiem `…`,
   a nie podniesc pola: `.ts-control` to pole, nie akapit, wiec druga linia
   rozjezdza wysokosci selectow stojacych obok siebie w pasku filtrow i wchodzi
   pod strzalke (`background-position: right 12px center`).
   Listy WIELOKROTNE zostaja nietkniete - tam tagi maja rosnac w kolejne wiersze. */
.ts-wrapper.single .ts-control { height: 34px !important; }
.ts-wrapper.single .ts-control > .item,
.ts-wrapper.single .ts-control .placeholder {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Hide default caret/arrow */
.ts-control::after { display: none !important; }
.ts-control .ts-dropdown-arrow { display: none !important; }

/* Miejsce na strzalke - regula odbijajaca vendora.
   tom-select.min.css ma `.ts-control:not(.rtl){padding-right:max(...)!important}`,
   czyli specyficznosc 0,2,0 z !important. Nasza bazowa `.ts-control` wyzej ma
   0,1,0, wiec prawy padding przepadal i strzalka (background-position: right 12px)
   ladowala na tekscie. Widac to bylo wszedzie tam, gdzie wrapper ma szerokosc
   z tresci, a przy stalej szerokosci (sloty `.panel-left` / `.panel-right`)
   bug siedzial utajony. Wartosc ta sama, co w regule bazowej. */
.ts-wrapper .ts-control:not(.rtl) { padding-right: 32px !important; }

/* Single-select: hide search input inside control */
.ts-wrapper.single .ts-control > input { opacity: 0 !important; width: 0 !important; padding: 0 !important; position: absolute !important; }

/* Dropdown */
.ts-dropdown {
  background: var(--surface) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow-lg) !important;
  margin-top: 7px !important;
  font-family: 'Inter', sans-serif !important;
  z-index: 9999 !important;
}
.ts-wrapper.focus,
.ts-wrapper.dropdown-active { z-index: 9999 !important; position: relative; }

.ts-dropdown .ts-dropdown-content { padding: 4px !important; scrollbar-width: thin !important; scrollbar-color: var(--teal) transparent !important; }
.ts-dropdown .ts-dropdown-content::-webkit-scrollbar { width: 6px !important; }
.ts-dropdown .ts-dropdown-content::-webkit-scrollbar-track { background: transparent !important; }
.ts-dropdown .ts-dropdown-content::-webkit-scrollbar-thumb { background: var(--teal) !important; border-radius: 3px !important; }
.ts-dropdown .ts-dropdown-content::-webkit-scrollbar-thumb:hover { background: var(--color-primary-hover) !important; }


/* Hide default controlInput search (direct children of .ts-dropdown-content), keep plugin inputs */
.ts-dropdown > .ts-dropdown-content > input { display: none !important; }

/* dropdown_input plugin styling */
.ts-dropdown .dropdown-input-wrap {
  padding: 6px !important;
  border-bottom: 1px solid var(--border) !important;
}
.ts-dropdown .dropdown-input {
  display: block !important;
  width: 100% !important;
  padding: 6px 10px !important;
  border: 1px solid var(--border) !important;
  border-radius: calc(var(--radius) - 2px) !important;
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-sm) !important;
  outline: none !important;
  background: var(--surface) !important;
  color: var(--text-primary) !important;
}
.ts-dropdown .dropdown-input:focus {
  border-color: var(--teal) !important;
}

.ts-dropdown .option {
  padding: 8px 10px !important;
  border-radius: calc(var(--radius) - 2px) !important;
  color: var(--text-primary) !important;
  cursor: pointer !important;
  font-size: var(--fs-nav) !important;
  transition: background .12s !important;
}

.ts-dropdown .option:hover,
.ts-dropdown .option.active {
  background: var(--bg) !important;
  color: var(--text-primary) !important;
}

.ts-dropdown .option.selected {
  background: var(--accent-dim) !important;
  color: var(--teal) !important;
  font-weight: 600 !important;
}

/* ── Optgroup headers (labels in <optgroup>) ── */
.ts-dropdown .optgroup-header {
  padding: 8px 10px 4px !important;
  font-size: var(--fs-xs) !important;
  font-weight: 700 !important;
  text-transform: uppercase !important;
  letter-spacing: 0.05em !important;
  color: var(--text-secondary) !important;
  cursor: default !important;
  user-select: none !important;
}

.ts-dropdown .optgroup + .optgroup .optgroup-header {
  margin-top: 4px !important;
  border-top: 1px solid var(--border) !important;
  padding-top: 10px !important;
}

.ts-dropdown .optgroup .option {
  padding-left: 16px !important;
}

/* Remove plugin - kill the generated border-left on .item .remove */
.ts-wrapper.plugin-remove_button .item .remove {
  border-left: none !important;
}

/* ── Multi-select (chips/tags) ──
   Auto-height container, items rendered as pill chips with × remove button.
   Reusable: any <select multiple class="ts-select"> with TomSelect + remove_button plugin. */
.ts-wrapper.multi .ts-control {
  height: auto !important;
  min-height: 34px !important;
  padding: 4px 32px 4px 6px !important;
  flex-wrap: wrap !important;
  gap: 4px !important;
  align-items: center !important;
  cursor: text !important;
}

.ts-wrapper.multi.has-items .ts-control { padding-left: 4px !important; }
.ts-wrapper.multi.has-items .ts-control > input::placeholder { color: transparent !important; }

.ts-wrapper.multi .ts-control > .item {
  display: inline-flex !important;
  align-items: center !important;
  gap: 6px !important;
  height: 24px !important;
  line-height: 22px !important;
  padding: 0 8px !important;
  margin: 0 !important;
  background: var(--accent-dim) !important;
  border: 1px solid var(--teal) !important;
  border-radius: 6px !important;
  color: var(--teal) !important;
  font-size: var(--fs-sm) !important;
  font-weight: 500 !important;
  white-space: nowrap !important;
  cursor: default !important;
}

.ts-wrapper.multi .ts-control > .item.active {
  background: var(--teal) !important;
  border-color: var(--teal) !important;
  color: #fff !important;
}

.ts-wrapper.multi.plugin-remove_button .item .remove {
  position: static !important;
  display: inline-grid !important;
  place-items: center !important;
  width: 14px !important;
  height: 14px !important;
  padding: 0 !important;
  margin-left: 2px !important;
  border: none !important;
  border-radius: 3px !important;
  background: transparent !important;
  color: var(--teal) !important;
  font-size: 12px !important;
  line-height: 1 !important;
  text-decoration: none !important;
  cursor: pointer !important;
  transition: background .12s, color .12s !important;
  vertical-align: middle !important;
  overflow: hidden !important;
}

.ts-wrapper.multi.plugin-remove_button .item .remove:hover {
  background: var(--teal) !important;
  color: #fff !important;
}

/* Inline input inside multi control (for typing/search/create) */
.ts-wrapper.multi .ts-control > input {
  flex: 1 1 80px !important;
  min-width: 80px !important;
  height: 24px !important;
  line-height: 24px !important;
  margin: 0 !important;
  padding: 0 4px !important;
  font-size: var(--fs-nav) !important;
  font-family: 'Inter', sans-serif !important;
  color: var(--text-primary) !important;
  background: transparent !important;
  border: none !important;
  outline: none !important;
  box-shadow: none !important;
}

/* Hide caret arrow on multi-select (it acts like a tag input, not a dropdown) */
.ts-wrapper.multi .ts-control { background-image: none !important; padding-right: 8px !important; }
/* Bez strzalki nie ma czego omijac - cofamy zapas 32px z reguly odbijajacej
   vendora. Ta sama specyficznosc (0,3,0), ale nizej w pliku, wiec wygrywa. */
.ts-wrapper.multi .ts-control:not(.rtl) { padding-right: 8px !important; }

/* "Create new tag" hint row in dropdown */
.ts-dropdown .create {
  padding: 8px 10px !important;
  border-radius: calc(var(--radius) - 2px) !important;
  color: var(--text-secondary) !important;
  font-size: var(--fs-nav) !important;
  cursor: pointer !important;
}
.ts-dropdown .create:hover,
.ts-dropdown .create.active { background: var(--bg) !important; color: var(--text-primary) !important; }
.ts-dropdown .create strong { color: var(--teal) !important; }

/* No results */
.ts-dropdown .no-results {
  padding: 10px !important;
  font-size: var(--fs-sm) !important;
  color: var(--text-secondary) !important;
  text-align: center;
}

/* ── Dropdown-up (opens above control) ──
   Usage: add class "ts-dropdown-up" to <select> or its .ts-wrapper parent. */
.ts-wrapper.ts-dropdown-up {
  position: relative !important;
}
.ts-wrapper.ts-dropdown-up .ts-dropdown {
  top: auto !important;
  bottom: 100% !important;
  margin-top: 0 !important;
  margin-bottom: 7px !important;
  box-shadow: 0 -8px 24px rgba(0,0,0,.10) !important;
}


/* ================================================================
   PLUGIN: FLATPICKR - override styles
   CDN:  flatpickr/dist/flatpickr.min.css
   ================================================================ */

.flatpickr-input {
  background: var(--bg) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  color: var(--text-primary) !important;
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-nav) !important;
  height: 34px !important;
  padding: 0 12px !important;
  outline: none !important;
  transition: border-color .18s, box-shadow .18s !important;
  width: 100%;
}

.flatpickr-input:focus {
  border-color: var(--teal) !important;
  box-shadow: 0 0 0 3px var(--accent-dim) !important;
}

/* Calendar popup */
.flatpickr-calendar {
  background: var(--surface) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow-lg) !important;
  font-family: 'Inter', sans-serif !important;
  padding: 0 !important;
}

.flatpickr-calendar::before,
.flatpickr-calendar::after { display: none !important; }

/* Month header */
.flatpickr-months {
  background: var(--bg) !important;
  border-radius: var(--radius) var(--radius) 0 0 !important;
  padding: 14px 0 !important;
  align-items: center !important;
  position: relative !important;
}

.flatpickr-month {
  background: transparent !important;
  color: var(--text-primary) !important;
  fill: var(--text-primary) !important;
}

.flatpickr-current-month {
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-nav) !important;
  font-weight: 700 !important;
  color: var(--text-primary) !important;
  padding-top: 8px !important;
}

.flatpickr-current-month select,
.flatpickr-current-month input {
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-nav) !important;
  color: var(--text-primary) !important;
  background: transparent !important;
}

/* Nav arrows */
.flatpickr-prev-month, .flatpickr-next-month {
  display: flex !important;
  align-items: center !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
  height: 34px !important;
}
.flatpickr-prev-month svg, .flatpickr-next-month svg { fill: var(--text-secondary) !important; }
.flatpickr-prev-month:hover svg, .flatpickr-next-month:hover svg { fill: var(--teal) !important; }

/* Weekday labels */
.flatpickr-weekday {
  background: transparent !important;
  color: var(--text-secondary) !important;
  font-size: var(--fs-xs) !important;
  font-weight: 700 !important;
  font-family: 'Inter', sans-serif !important;
}

/* Day cells */
.flatpickr-day {
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-sm) !important;
  color: var(--text-primary) !important;
  border-radius: 6px !important;
  border: none !important;
  line-height: 39px !important;
  height: 39px !important;
}

.flatpickr-day:hover {
  background: var(--bg) !important;
  color: var(--text-primary) !important;
}

.flatpickr-day.today {
  background: transparent !important;
  border: 1.5px solid var(--teal) !important;
  color: var(--teal) !important;
  font-weight: 700 !important;
}

.flatpickr-day.selected,
.flatpickr-day.selected:hover {
  background: var(--teal) !important;
  border-color: var(--teal) !important;
  color: #fff !important;
  font-weight: 700 !important;
}

.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay {
  color: var(--border) !important;
}

/* Time picker */
.flatpickr-time {
  border-top: 1px solid var(--border) !important;
  height: 72px !important;
  max-height: 72px !important;
  padding: 0 8px !important;
  gap: 0 !important;
}

/* Wrapper - flex kolumna: arrowUp → input → arrowDown */
.flatpickr-time .numInputWrapper {
  flex: 1 !important;
  position: static !important;
  height: 72px !important;
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: var(--radius) !important;
  transition: background .15s !important;
  overflow: visible !important;
}

.flatpickr-time .numInputWrapper:hover {
  background: var(--bg) !important;
}

/* Strzałki - wyciągnięte z absolute, ustawione flex */
.flatpickr-time .arrowUp,
.flatpickr-time .arrowDown {
  position: relative !important;
  left: auto !important;
  right: auto !important;
  top: auto !important;
  bottom: auto !important;
  transform: none !important;
  width: 100% !important;
  height: 20px !important;
  flex: 0 0 20px !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 5px !important;
  cursor: pointer !important;
  opacity: 0 !important;
  transition: opacity .15s, background .15s !important;
  padding: 0 !important;
  border: none !important;
  line-height: 1 !important;
}

/* arrowUp jako pierwszy (DOM: input, arrowUp, arrowDown - order koryguje kolejność) */
.flatpickr-time .arrowUp   { order: -1 !important; }
.flatpickr-time input.numInput { order: 0 !important; }
.flatpickr-time .arrowDown { order: 1 !important; }

/* Pole input */
.flatpickr-time input.numInput {
  position: static !important;
  flex: 0 0 28px !important;
  height: 28px !important;
  width: 100% !important;
  background: transparent !important;
  color: var(--text-primary) !important;
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-md) !important;
  letter-spacing: .5px !important;
  text-align: center !important;
  line-height: 28px !important;
  border: none !important;
  outline: none !important;
  padding: 0 !important;
}
.flatpickr-time input.flatpickr-hour {
  font-weight: normal !important;
}
.flatpickr-time input.numInput:focus {
  box-shadow: none !important;
  color: var(--text-primary) !important;
}

.flatpickr-time input.numInput::selection {
  background: transparent !important;
  color: var(--text-primary) !important;
}

/* Separator : */
.flatpickr-time .flatpickr-time-separator,
.flatpickr-time .flatpickr-am-pm {
  color: var(--text-secondary) !important;
  font-family: 'Inter', sans-serif !important;
  font-size: 20px !important;
  font-weight: 700 !important;
  align-self: center !important;
  flex-shrink: 0 !important;
  height: 72px !important;
  line-height: 72px !important;
  padding: 0 2px !important;
}

/* Trójkąty */
.flatpickr-time .arrowUp::after,
.flatpickr-time .arrowDown::after {
  content: '' !important;
  position: absolute !important;
  left: 50% !important;
  transform: translateX(-50%) !important;
  width: 0 !important;
  height: 0 !important;
}

.flatpickr-time .arrowUp::after {
  top: 50% !important;
  margin-top: -3px !important;
  border-left: 5px solid transparent !important;
  border-right: 5px solid transparent !important;
  border-bottom: 6px solid var(--text-secondary) !important;
  border-top: none !important;
}

.flatpickr-time .arrowDown::after {
  top: 50% !important;
  margin-top: -3px !important;
  border-left: 5px solid transparent !important;
  border-right: 5px solid transparent !important;
  border-top: 6px solid var(--text-secondary) !important;
  border-bottom: none !important;
}

/* Pokaż strzałki po najechaniu na wrapper */
.flatpickr-time .numInputWrapper:hover .arrowUp,
.flatpickr-time .numInputWrapper:hover .arrowDown {
  opacity: 1 !important;
}

/* Hover strzałka */
.flatpickr-time .arrowUp:hover,
.flatpickr-time .arrowDown:hover {
  background: var(--accent-dim) !important;
  opacity: 1 !important;
}

.flatpickr-time .arrowUp:hover::after {
  border-bottom-color: var(--teal) !important;
}

.flatpickr-time .arrowDown:hover::after {
  border-top-color: var(--teal) !important;
}



/* ================================================================
   PLUGIN: FULLCALENDAR - override styles
   CDN:  @fullcalendar/core + @fullcalendar/daygrid + @fullcalendar/timegrid
   ================================================================ */

/* Toolbar */
.fc .fc-toolbar {
  padding: 12px 18px !important;
  gap: 12px;
}

.fc .fc-toolbar-title {
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-sm) !important;
  font-weight: 600 !important;
  color: var(--text-primary) !important;
}

/* Toolbar buttons */
.fc .fc-button {
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-xs) !important;
  font-weight: 600 !important;
  padding: 6px 14px !important;
  border-radius: 6px !important;
  border: 1px solid var(--border) !important;
  background: var(--surface) !important;
  color: var(--text-primary) !important;
  box-shadow: none !important;
  text-transform: none !important;
  transition: background var(--transition), color var(--transition) !important;
}

.fc .fc-button:hover {
  background: var(--bg) !important;
  color: var(--text-primary) !important;
}

.fc .fc-button-active,
.fc .fc-button:active {
  background: var(--teal) !important;
  color: #fff !important;
  border-color: var(--teal) !important;
}

.fc .fc-button:focus { box-shadow: 0 0 0 3px var(--accent-dim) !important; }

.fc .fc-today-button {
  border-color: var(--teal) !important;
  color: var(--teal) !important;
  background: transparent !important;
}

.fc .fc-today-button:hover {
  background: var(--teal) !important;
  color: #fff !important;
}

.fc .fc-today-button:disabled {
  opacity: .4 !important;
  cursor: default !important;
}

/* Button groups */
.fc .fc-button-group > .fc-button {
  border-radius: 0 !important;
}

.fc .fc-button-group > .fc-button:first-child { border-radius: 6px 0 0 6px !important; }
.fc .fc-button-group > .fc-button:last-child  { border-radius: 0 6px 6px 0 !important; }

/* Table headers */
.fc th {
  font-family: 'Inter', sans-serif !important;
  font-size: 10px !important;
  font-weight: 600 !important;
  text-transform: uppercase !important;
  letter-spacing: .06em !important;
  color: var(--text-secondary) !important;
  background: var(--surface) !important;
  border-color: var(--border) !important;
  padding: 0;
}

.fc td { border-color: var(--border) !important; }

/* Today column */
.fc .fc-day-today {
  background: color-mix(in srgb, var(--color-primary) 3.5%, transparent) !important;
}

.fc .fc-col-header-cell.fc-day-today {
  color: var(--teal) !important;
}

.fc .fc-daygrid-day-number,
.fc .fc-col-header-cell-cushion {
  font-family: 'Inter', sans-serif !important;
  color: var(--text-primary) !important;
  text-decoration: none !important;
}

.fc .fc-day-today .fc-daygrid-day-number {
  background: var(--teal) !important;
  color: #fff !important;
  border-radius: 50% !important;
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700;
}

/* Events */
.fc .fc-event {
  border-radius: 5px !important;
  border: none !important;
  border-left: 3px solid var(--teal) !important;
  padding: 4px 7px !important;
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-xs) !important;
  cursor: pointer !important;
  transition: box-shadow var(--transition) !important;
}

.fc .fc-event:hover {
  box-shadow: var(--shadow-md) !important;
  z-index: 10 !important;
}

.fc .fc-event-title {
  font-weight: 600 !important;
  font-size: var(--fs-xs) !important;
}

.fc .fc-event-time {
  font-size: 10px !important;
  font-weight: 500 !important;
  opacity: .75;
}

/* Time grid - hour labels */
.fc .fc-timegrid-slot-label {
  font-family: 'Inter', sans-serif !important;
  font-size: 11px !important;
  font-weight: 500 !important;
  color: var(--text-secondary) !important;
  font-variant-numeric: tabular-nums;
  padding-right: 10px !important;
  vertical-align: top !important;
}


.fc .fc-timegrid-slot { height: 48px !important; }

/* White rows - remove ALL slot backgrounds (major + minor + label + bg) */
.fc .fc-timegrid-slot,
.fc .fc-timegrid-slot-lane,
.fc .fc-timegrid-slot-label,
.fc-theme-standard .fc-timegrid-slot-minor,
.fc .fc-timegrid-col-bg { background: var(--surface) !important; }

/* Remove gray behind scrollbar (left gutter + right edge) */
.fc .fc-scrollgrid,
.fc .fc-scroller-harness,
.fc .fc-scroller-harness-liquid { background: var(--surface) !important; }

.fc-scrollgrid-sync-inner { 
  padding: 10px 0;
}

/* Vertical lines between day columns - target slot lane cells, not event wrapper */
.fc .fc-timegrid-slot-lane { border-left: 1px solid var(--border) !important; }
.fc .fc-timegrid-divider { padding: 0 !important; height: 0 !important; }

/* Now indicator - hidden (remove nowIndicator:true from JS to disable fully) */
.fc .fc-timegrid-now-indicator-line { display: none !important; }
.fc .fc-timegrid-now-indicator-arrow { display: none !important; }

/* Scrollbar */
.fc .fc-scroller::-webkit-scrollbar       { width: 4px; }
.fc .fc-scroller::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

/* Remove default FullCalendar borders on outer wrapper */
.fc { border: none !important; }

/* Month view: more events link */
.fc .fc-more-link {
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-xs) !important;
  color: var(--teal) !important;
  font-weight: 600 !important;
}

/* Popover */
.fc .fc-popover {
  background: var(--surface) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow-lg) !important;
}

.fc .fc-popover-header {
  background: var(--bg) !important;
  font-family: 'Inter', sans-serif !important;
  font-size: var(--fs-sm) !important;
  font-weight: 600 !important;
  color: var(--text-primary) !important;
  padding: 8px 12px !important;
}


/* ================================================================
   AVATAR UPLOAD (click-to-change with overlay)

   Użycie:
   <div class="avatar-upload">
     <label class="avatar-circle" for="avatarFile">
       <span class="avatar-initials">MM</span>
       <div class="avatar-overlay">
         <i class="bi bi-camera"></i>
         <span>Zmień</span>
       </div>
     </label>
     <input type="file" id="avatarFile" accept="image/*" style="display:none">
     <div class="avatar-upload-info">
       <strong>Michał Majrowski</strong>
       <span>JPG, PNG - maks. 2 MB</span>
     </div>
   </div>
   ================================================================ */

.avatar-upload {
  display: flex;
  align-items: center;
  gap: 20px;
}

.avatar-circle {
  position: relative;
  width: 80px; height: 80px;
  border-radius: 50%;
  background: var(--teal);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  overflow: hidden;
  flex-shrink: 0;
}

.avatar-initials {
  font-size: 24px; font-weight: 700; color: #fff;
  user-select: none;
}

.avatar-circle img {
  width: 100%; height: 100%; object-fit: cover;
  position: absolute; inset: 0;
}

.avatar-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0,.52);
  border-radius: 50%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 2px;
  opacity: 0;
  transition: opacity .2s;
}

.avatar-circle:hover .avatar-overlay { opacity: 1; }
.avatar-overlay .bi { font-size: 18px; color: #fff; }
.avatar-overlay span { font-size: 10px; font-weight: 600; color: rgba(255,255,255,.85); letter-spacing: 0.2px; }

.avatar-upload-info { display: flex; flex-direction: column; gap: 4px; }
.avatar-upload-info strong { font-size: var(--fs-base); font-weight: 600; color: var(--text-primary); }
.avatar-upload-info span   { font-size: var(--fs-xs); color: var(--text-secondary); }

.login-thumb {
  position: relative;
  width: 90px; height: 60px;
  border-radius: 6px;
  overflow: hidden;
  cursor: pointer;
  flex-shrink: 0;
  border: 1px solid var(--border);
}
.login-thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.login-thumb-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0,.52);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 2px;
  opacity: 0;
  transition: opacity .2s;
}
.login-thumb:hover .login-thumb-overlay { opacity: 1; }
.login-thumb-overlay .bi { font-size: 16px; color: #fff; }
.login-thumb-overlay span { font-size: 9px; font-weight: 600; color: rgba(255,255,255,.85); letter-spacing: 0.2px; }

.login-thumb-remove {
  position: absolute; top: 3px; right: 3px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: rgba(0,0,0,.6);
  border: none;
  color: #fff;
  font-size: 12px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  z-index: 2;
  transition: background .15s;
}
.login-thumb-remove:hover { background: var(--danger); }


/* ================================================================
   ROLE BADGE (pill badge for roles/permissions)

   Użycie:
   <span class="role-badge"><i class="bi bi-shield-check"></i> Administrator</span>
   <span class="role-badge role-badge-blue"><i class="bi bi-person"></i> Instruktor</span>
   ================================================================ */

.role-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  background: var(--accent-dim);
  color: var(--teal);
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 700;
  border: 1px solid color-mix(in srgb, var(--color-primary) 20%, transparent);
}

.role-badge-blue    { background: var(--blue-dim);    color: var(--blue);    border-color: color-mix(in srgb, var(--blue) 20%, transparent); }
.role-badge-warning { background: var(--warning-dim); color: var(--warning); border-color: color-mix(in srgb, var(--warning) 20%, transparent); }
.role-badge-danger  { background: var(--danger-dim);  color: var(--danger);  border-color: color-mix(in srgb, var(--danger) 20%, transparent); }
.role-badge-gray    { background: var(--light-gray);  color: var(--text-secondary); border-color: var(--border); }


/* ================================================================
   CUSTOM CHECKBOX (styled checkbox with checkmark)

   Użycie:
   <label class="check-label">
     <input type="checkbox" checked>
     <span class="check-box"></span>
     <span>Zgoda na przetwarzanie danych <span class="check-required">*</span></span>
   </label>
   ================================================================ */

.check-label {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-size: var(--fs-nav);
  color: var(--text-primary);
  user-select: none;
}

.check-label:last-child { margin-bottom: 0; }
.check-label input[type="checkbox"] { display: none; }

.check-box {
  flex-shrink: 0;
  width: 16px; height: 16px;
  border: 1.5px solid var(--border);
  border-radius: 3px;
  background: var(--bg);
  position: relative;
  transition: border-color .15s, background .15s;
}

.check-label input:checked + .check-box {
  background: var(--teal);
  border-color: var(--teal);
}

.check-box::after {
  content: '';
  width: 9px; height: 5px;
  border-left: 1.5px solid #fff;
  border-bottom: 1.5px solid #fff;
  opacity: 0;
  transition: opacity .15s;
  position: absolute;
  top: 40%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-45deg);
}

.check-label input:checked + .check-box::after { opacity: 1; }

.check-required { color: var(--danger); }


/* ================================================================
   RADIO PILLS (inline pill radio buttons)

   Użycie:
   <div class="radio-pills">
     <label class="radio-pill">
       <input type="radio" name="contact" value="email" checked>
       <i class="bi bi-envelope"></i> Email
     </label>
     <label class="radio-pill">
       <input type="radio" name="contact" value="sms">
       <i class="bi bi-phone"></i> SMS
     </label>
   </div>
   ================================================================ */

.radio-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.radio-pill {
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  font-size: var(--fs-nav);
  color: var(--text-primary);
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: border-color .15s, background .15s;
  user-select: none;
}

.radio-pill input[type="radio"] { display: none; }

.radio-pill:has(input:checked) {
  border-color: var(--teal);
  background: var(--accent-dim);
  color: var(--teal);
}



/* ================================================================
   TAG CHIPS (toggleable checkbox chips - for class types, tags)

   Użycie:
   <div class="tag-chips">
     <label class="tag-chip">
       <input type="checkbox" checked>
       <span class="tag-dot"></span>
       Joga
     </label>
     <label class="tag-chip">
       <input type="checkbox">
       <span class="tag-dot"></span>
       Pilates
     </label>
   </div>
   ================================================================ */

.tag-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tag-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 13px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
  user-select: none;
  background: var(--bg);
}

.tag-chip input[type="checkbox"] { display: none; }

.tag-chip:has(input:checked) {
  border-color: var(--teal);
  color: var(--teal);
  background: var(--accent-dim);
}

.tag-dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.tag-chips-nodot .tag-dot { display: none; }


/* ================================================================
   DETAIL TABS (underline tabs with page-panel body - client detail style)

   Użycie:
   <div class="detail-tabs-bar">
     <button class="detail-tab active">Przegląd</button>
     <button class="detail-tab">Historia</button>
     <button class="detail-tab">Notatki</button>
   </div>
   <div class="detail-panels">
     <div class="detail-panel active">...content...</div>
     <div class="detail-panel">...content...</div>
   </div>
   ================================================================ */

.detail-tabs-bar {
  display: flex;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius) var(--radius) 0 0;
  padding: 0 20px;
}

.detail-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 20px 16px;
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  border: none; background: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  font-family: inherit;
  transition: color var(--transition), border-color var(--transition);
}

.detail-tab > i { font-size: 1.05em; line-height: 1; }

.detail-tab:focus { outline: none; }
.detail-tab:hover { color: var(--text-primary); }

.detail-tab.active {
  color: var(--teal);
  border-bottom-color: var(--teal);
  font-weight: 600;
}

.detail-panels {
  padding: 20px;
}

.detail-panel { display: none; }
.detail-panel.active { display: block; }


/* ================================================================
   FILTER GROUP (connected toggle buttons - instructor style)

   Użycie:
   <div class="filter-group">
     <button class="filter-group-btn active">Wszyscy</button>
     <button class="filter-group-btn">Aktywni</button>
     <button class="filter-group-btn">Nieaktywni</button>
   </div>
   ================================================================ */

.filter-group {
  display: flex;
  gap: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.filter-group-btn {
  height: 34px;
  padding: 0 16px;
  font-size: var(--fs-sm);
  font-weight: 500;
  font-family: inherit;
  border: none;
  border-left: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background .15s, color .15s;
  line-height: 1;
  display: flex;
  align-items: center;
  gap: 6px;
}

.filter-group-btn:first-child { border-left: none; }

.filter-group-btn.active {
  background: var(--accent-dim);
  color: var(--teal);
}

.filter-group-btn.active-danger {
  background: var(--danger-dim);
  color: var(--danger);
}

.filter-group-btn:not(.active):not(.active-danger):hover {
  background: var(--bg);
  color: var(--text-primary);
}

/* Count badge inside filter-group-btn */
.filter-group-btn .fg-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  font-size: var(--fs-2xs);
  font-weight: 700;
  background: var(--light-gray);
  color: var(--text-secondary);
  margin-left: 5px;
  transition: background .15s, color .15s;
}

.filter-group-btn.active .fg-count {
  background: var(--teal);
  color: #fff;
}

.filter-group-btn.active-danger .fg-count {
  background: var(--danger);
  color: #fff;
}

/* Small variant - like btn-sm */
.filter-group.filter-group-sm .filter-group-btn {
  height: 26px;
  padding: 0 10px;
  font-size: var(--fs-xs);
  border-radius: 0;
}


/* ================================================================
   24. CALENDAR
   ================================================================ */

/* ── Week label ── */
.cal-week-label {
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-primary);
  min-width: 158px; text-align: center; font-variant-numeric: tabular-nums;
}
.cal-btn-today { border-color: var(--teal) !important; color: var(--teal) !important; }
.cal-btn-today:hover { background: var(--teal) !important; color: #fff !important; }

/* ── Outer container ── */
.cal-outer {
  padding: 0; overflow: hidden; margin-bottom: 20px;
  display: flex; flex-direction: column;
}
.cal-layout { display: flex; flex: 1; min-height: 0; overflow: hidden; }

/* ── Grid wrapper ── */
.cal-grid-wrap { flex: 1; display: flex; flex-direction: column; min-width: 0; overflow: hidden; }

/* ── Day header row ── */
.cal-grid-head {
  display: grid; grid-template-columns: var(--cal-gutter, 52px) repeat(7, 1fr);
  border-bottom: 2px solid var(--border); background: var(--surface); flex-shrink: 0;
}
.cal-grid-head-gutter { border-right: 1px solid var(--border); }
.cal-dh { padding: 10px 8px 9px; text-align: center; border-left: 1px solid var(--border); }
.cal-dh-day {
  display: block; font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-secondary); line-height: 1;
}
.cal-dh-num {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; font-size: 15px; font-weight: 700; line-height: 1;
  color: var(--text-primary); margin: 4px auto 0; border-radius: 50%;
}
.cal-dh.is-today .cal-dh-day { color: var(--teal); }
.cal-dh.is-today .cal-dh-num { background: var(--teal); color: #fff; }

/* ── Time grid (scrollable) ── */
.cal-time-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; }
/* Rynna godzin przez zmienna, bo silnik ustawia te sama wartosc INLINE
   (calendar-grid.js) - literal wpisany w atrybut `style` nie da sie
   nadpisac zadnym `@media` bez `!important`. Musi isc przez zmienna
   w OBU miejscach naraz: naglowek i tresc rozjechane o osiem pikseli
   przestaja stac w pionie. */
.cal-time-content { display: grid; grid-template-columns: var(--cal-gutter, 52px) repeat(7, 1fr); }

/* ── Hour labels ── */
.cal-hour-labels { border-right: 1px solid var(--border); }
.cal-hour-label {
  padding: 4px 8px 0; display: flex; align-items: flex-start; justify-content: center;
  font-size: 10px; color: var(--text-secondary); font-variant-numeric: tabular-nums;
  box-sizing: border-box;
}

/* ── Day columns ── */
.cal-day-col {
  position: relative; border-left: 1px solid var(--border);
  background-image: repeating-linear-gradient(
    to bottom, transparent 0%, transparent calc(100% - 1px), var(--border) calc(100% - 1px)
  );
}
.cal-day-col.is-today {
  background-color: color-mix(in srgb, var(--color-primary) 3%, transparent);
  background-image: repeating-linear-gradient(
    to bottom, transparent 0%, transparent calc(100% - 1px), color-mix(in srgb, var(--color-primary) 15%, transparent) calc(100% - 1px)
  );
}
.cal-day-col::after {
  content: ''; position: absolute; left: 0; right: 0;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0px, transparent calc(var(--slot-h) - 1px),
    color-mix(in srgb, var(--border) 50%, transparent) calc(var(--slot-h) - 1px), color-mix(in srgb, var(--border) 50%, transparent) var(--slot-h)
  );
  top: 0; bottom: 0;
  background-size: 100% calc(var(--slot-h) * 2);
  pointer-events: none;
}

/* ── Now indicator ── */
.cal-now-line {
  position: absolute; left: 0; right: 0; height: 2px;
  background: var(--teal); z-index: 5; pointer-events: none;
}
.cal-now-line::before {
  content: ''; position: absolute; left: -4px; top: -4px;
  width: 10px; height: 10px; border-radius: 50%; background: var(--teal);
}

/* ── Class blocks ── */
.cal-block {
  position: absolute; left: 3px; right: 3px; border-radius: 5px;
  padding: 5px 7px; overflow: hidden; cursor: pointer;
  border-left: 3px solid transparent;
  transition: box-shadow var(--transition);
  display: flex; flex-direction: column;
  container-type: size;
  z-index: 1;
}
.cal-block:hover { box-shadow: 0 2px 12px color-mix(in srgb, var(--color-primary) 10%, transparent); z-index: 10; }

.cal-block-title {
  font-size: 11px; font-weight: 700; color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.25; flex-shrink: 0;
}
.cal-block-meta {
  font-size: 10px; color: var(--text-secondary); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 0;
}
.cal-block-bottom {
  display: flex; align-items: center; gap: 5px; margin-top: auto; padding-top: 3px; flex-shrink: 0;
}
.cal-block-occ { font-size: 10px; font-weight: 700; }
.cal-block-instructor-sm { display: none; font-size: 10px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

@container (max-height: 40px) {
  .cal-block-meta { display: none; }
  .cal-block-occ { display: none; }
  .cal-block-instructor-sm { display: inline; }
}
@container (max-height: 20px) {
  .cal-block-bottom { display: none; }
  .cal-block-title { font-size: 10px; }
}

/* ── Event type colours (tokeny: --cal-*-bg/-border/-text) ── */
.cal-yoga     { background: var(--cal-yoga-bg);     border-left-color: var(--cal-yoga-border); }
.cal-yoga.is-full { background: color-mix(in srgb, var(--cal-yoga-bg) 70%, var(--cal-yoga-border)); opacity: .85; }
.cal-pilates  { background: var(--cal-pilates-bg);  border-left-color: var(--cal-pilates-border); }
.cal-dance    { background: var(--cal-dance-bg);    border-left-color: var(--cal-dance-border); }
.cal-workshop { background: var(--cal-workshop-bg); border-left-color: var(--cal-workshop-border); }

.cal-block-occ-yoga     { color: var(--cal-yoga-text); }
.cal-block-occ-pilates  { color: var(--cal-pilates-text); }
.cal-block-occ-dance    { color: var(--cal-dance-text); }
.cal-block-occ-workshop { color: var(--cal-workshop-text); }

/* ── Tags ── */
.cal-tag {
  display: inline-block; font-size: 8px; font-weight: 800; letter-spacing: .05em;
  padding: 1px 5px; border-radius: 3px; line-height: 1.5;
}
.cal-tag-full   { background: var(--cal-yoga-border); color: #fff; }
.cal-tag-almost { background: var(--warning-dim); color: var(--warning-ink); }

/* ── Side page-panel ── */
.cal-side-panel {
  width: 300px; flex-shrink: 0; border-left: 1px solid var(--border);
  display: flex; flex-direction: column; background: var(--surface); overflow-y: auto;
  /* Ukryty domyślnie - slide-in z prawej */
  transform: translateX(100%);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1),
              opacity   0.28s cubic-bezier(0.4, 0, 0.2, 1),
              width     0.28s cubic-bezier(0.4, 0, 0.2, 1);
  opacity: 0;
  width: 0;
  overflow: hidden;
}
.cal-side-panel.is-open {
  transform: translateX(0);
  opacity: 1;
  width: 300px;
  overflow-y: auto;
}

.cal-panel-head {
  padding: 16px 18px 13px; border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 7px; position: relative;
}
.cal-panel-close {
  position: absolute; top: 13px; right: 14px; background: none; border: none;
  cursor: pointer; color: var(--text-secondary); font-size: 14px; padding: 4px;
  line-height: 1; border-radius: 4px; transition: background var(--transition), color var(--transition);
}
.cal-panel-close:hover { background: var(--bg); color: var(--text-primary); }
.cal-panel-name { font-size: 15px; font-weight: 700; color: var(--text-primary); padding-right: 26px; line-height: 1.25; }
.cal-type-badge {
  display: inline-block; padding: 3px 11px; border-radius: 20px;
  font-size: var(--fs-xs); font-weight: 600;
  background: var(--cal-yoga-bg); color: var(--cal-yoga-text);
  border: 1px solid color-mix(in srgb, var(--cal-yoga-border) 30%, transparent);
  align-self: flex-start;
}
.cal-type-badge.cal-pilates  { background: var(--cal-pilates-bg);  color: var(--cal-pilates-text);
                               border-color: color-mix(in srgb, var(--cal-pilates-border) 30%, transparent); }
.cal-type-badge.cal-dance    { background: var(--cal-dance-bg);    color: var(--cal-dance-text);
                               border-color: color-mix(in srgb, var(--cal-dance-border) 30%, transparent); }
.cal-type-badge.cal-workshop { background: var(--cal-workshop-bg); color: var(--cal-workshop-text);
                               border-color: color-mix(in srgb, var(--cal-workshop-border) 30%, transparent); }
.cal-panel-body { padding: 16px 18px; flex: 1; display: flex; flex-direction: column; gap: 13px; }
.cal-panel-row { display: flex; align-items: flex-start; gap: 11px; }
.cal-panel-row-icon { color: var(--text-secondary); font-size: 15px; margin-top: 1px; flex-shrink: 0; }
.cal-panel-row-label { font-size: 11px; color: var(--text-secondary); margin-bottom: 2px; }
.cal-panel-row-value { font-size: var(--fs-sm); font-weight: 600; color: var(--text-primary); line-height: 1.4; }
.cal-panel-divider { border: none; border-top: 1px solid var(--border); margin: 2px 0; }
.cal-occ-title { font-size: var(--fs-sm); font-weight: 600; color: var(--text-primary); margin-bottom: 8px; }
.cal-occ-bar-track { background: var(--bg); border-radius: 20px; height: 5px; margin-bottom: 12px; }
.cal-occ-bar-fill { height: 5px; border-radius: 20px; background: var(--teal); }
.cal-avatars { display: flex; align-items: center; }
.cal-mini-av {
  width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--surface);
  display: flex; align-items: center; justify-content: center;
  font-size: 8px; font-weight: 800; color: #fff; margin-left: -6px;
}
.cal-mini-av:first-child { margin-left: 0; }
.cal-mini-av.more { background: var(--light-gray); color: var(--text-secondary); font-size: 9px; }
.cal-panel-actions {
  padding: 13px 18px 18px; display: flex; flex-direction: column; gap: 7px;
  border-top: 1px solid var(--border);
}
.cal-panel-actions .btn { width: 100%; justify-content: center; }

/* ── Month view ── */
.cal-month-grid {
  display: flex; flex-direction: column; flex: 1; overflow: hidden;
}

.cal-month-head {
  display: grid; grid-template-columns: repeat(7, 1fr);
  border-bottom: 2px solid var(--border); border-left: 1px solid var(--border);
  background: var(--surface); flex-shrink: 0;
}
.cal-month-head-cell {
  padding: 9px 8px; text-align: center;
  font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-secondary);
  border-left: 1px solid var(--border);
}
.cal-month-head-cell:first-child { border-left: none; }

.cal-month-body {
  display: grid; grid-template-columns: repeat(7, 1fr);
  flex: 1; overflow-y: auto; align-content: start;
  border-left: 1px solid var(--border);
}

.cal-month-cell {
  border-left: 1px solid var(--border); border-bottom: 1px solid var(--border);
  min-height: 110px; padding: 6px; display: flex; flex-direction: column; gap: 3px;
  background: var(--surface);
}
.cal-month-cell:nth-child(7n+1) { border-left: none; }
.cal-month-cell.other-month { background: var(--bg); }
.cal-month-cell.other-month .cal-month-cell-num { color: var(--text-secondary); opacity: .45; }
.cal-month-cell.is-today { background: color-mix(in srgb, var(--color-primary) 3%, transparent); }

.cal-month-cell-num {
  font-size: 12px; font-weight: 700; color: var(--text-primary);
  width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; flex-shrink: 0; line-height: 1;
}
.cal-month-cell-num.is-today { background: var(--teal); color: #fff; }

.cal-month-cell-events { display: flex; flex-direction: column; gap: 2px; flex: 1; overflow: hidden; }

.cal-month-ev {
  display: flex; align-items: center; gap: 4px; padding: 2px 5px;
  border-radius: 4px; cursor: pointer; overflow: hidden;
  border-left: 3px solid transparent;
  transition: box-shadow var(--transition), filter var(--transition);
  flex-shrink: 0;
}
.cal-month-ev:hover { box-shadow: 0 2px 12px color-mix(in srgb, var(--color-primary) 10%, transparent); filter: brightness(.97); }

/* Dziedziczy kolory z .cal-yoga / .cal-pilates etc. (tokeny --cal-*) */
.cal-month-ev.cal-yoga     { background: var(--cal-yoga-bg);     border-left-color: var(--cal-yoga-border); }
.cal-month-ev.cal-yoga.is-full { background: color-mix(in srgb, var(--cal-yoga-bg) 70%, var(--cal-yoga-border)); opacity: .85; }
.cal-month-ev.cal-pilates  { background: var(--cal-pilates-bg);  border-left-color: var(--cal-pilates-border); }
.cal-month-ev.cal-dance    { background: var(--cal-dance-bg);    border-left-color: var(--cal-dance-border); }
.cal-month-ev.cal-workshop { background: var(--cal-workshop-bg); border-left-color: var(--cal-workshop-border); }

.cal-month-ev-time {
  font-size: 9px; font-weight: 700; color: var(--text-secondary);
  white-space: nowrap; flex-shrink: 0; font-variant-numeric: tabular-nums;
}
.cal-month-ev-title {
  font-size: 10px; font-weight: 600; color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── Calendar drag & drop ── */
.cal-block       { cursor: grab; }
.cal-month-ev    { cursor: grab; }

.cal-drop-target {
  background-color: color-mix(in srgb, var(--color-primary) 8%, transparent) !important;
  outline: 2px dashed color-mix(in srgb, var(--color-primary) 50%, transparent);
  outline-offset: -2px;
}

/* ================================================================
   SPOTS INPUT (number stepper - increment / decrement + input)

   Bez labela:
   <div class="spots-wrap">
     <button class="spots-btn" data-num-target="x" data-num-step="-1"><i class="bi bi-dash"></i></button>
     <input class="spots-input" type="number" id="x" value="15" min="1">
     <button class="spots-btn" data-num-target="x" data-num-step="1"><i class="bi bi-plus"></i></button>
   </div>

   Z labelem (np. "dni", "godz."):
   <div class="spots-wrap">
     <button class="spots-btn" ...><i class="bi bi-dash"></i></button>
     <div class="spots-field">
       <input class="spots-input" type="number" id="x" value="7" min="0">
       <span class="spots-label">dni</span>
     </div>
     <button class="spots-btn" ...><i class="bi bi-plus"></i></button>
   </div>
   ================================================================ */

.spots-wrap { display: flex; align-items: center; }

.spots-btn {
  width: 36px; height: 38px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-secondary);
  font-size: 16px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: border-color .15s, background .15s, color .15s;
  flex-shrink: 0;
}
.spots-wrap > .spots-btn:first-child { border-radius: var(--radius) 0 0 var(--radius); margin-right: -1px; }
.spots-wrap > .spots-btn:nth-of-type(2) { border-radius: 0 var(--radius) var(--radius) 0; margin-left: -1px; }

.spots-btn:hover { border-color: var(--teal); background: var(--accent-dim); color: var(--teal); z-index: 2; position: relative; }

/* Bare input (no label) */
.spots-input {
  width: 64px; height: 38px;
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--bg);
  color: var(--text-primary);
  font-family: 'Inter', sans-serif;
  font-size: 18px; font-weight: 700;
  text-align: center; outline: none;
  transition: border-color .15s, box-shadow .15s;
}
.spots-input:focus { border-color: var(--teal); box-shadow: 0 0 0 3px var(--accent-dim); z-index: 1; position: relative; }

/* Wrapper with unit label - absolute label inside input */
.spots-field { position: relative; }

.spots-field > .spots-input {
  width: 78px;
  padding-right: 36px;
  padding-left: 6px;
  text-align: center;
}

.spots-label {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--text-secondary);
  pointer-events: none;
  white-space: nowrap;
  user-select: none;
}

/* Hide native number spinner */
.spots-input::-webkit-inner-spin-button,
.spots-input::-webkit-outer-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }
.spots-input { -moz-appearance: textfield; appearance: textfield; }

/* ================================================================
   NUM-UNIT - compact number input + unit label
   <div class="num-unit">
     <input class="input num-input" type="number" value="20">
     <span class="num-label">dni</span>
   </div>
   ================================================================ */
.num-unit { display: flex; align-items: center; gap: 8px; }
/* Bez !important: .num-input stoi w pliku ponizej .input, wiec przy rownej
   specyficznosci i tak wygrywa kolejnoscia. !important tylko zmuszal moduly
   do odpowiadania wlasnym !important przy kazdej korekcie szerokosci. */
.num-input { width: 72px; text-align: center; padding-left: 18px; }
.num-label { font-size: 13px; color: var(--text-secondary); white-space: nowrap; }


/* ================================================================
   FLOAT PANEL (pływający page-panel podglądu - sticky sidebar widget)

   Użycie:
   <div class="form-col-side">
     <span class="float-panel-label">
       <i class="bi bi-eye"></i> Podgląd
     </span>
     <div class="float-panel">
       <div class="float-panel-accent" id="myAccentBar"></div>
       <div class="float-panel-body">
         <!-- treść specyficzna dla strony -->
       </div>
       <div class="float-panel-footer">
         <i class="bi bi-info-circle"></i>
         Tekst stopki
       </div>
     </div>
   </div>

   Warianty:
   .float-panel-body-flush  - bez paddingu (np. gdy treść ma własny)
   ================================================================ */

.float-panel-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: .08em;
  display: flex;
  align-items: center;
  gap: 5px;
}

.float-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: var(--shadow);
}

.float-panel-accent {
  height: 4px;
  background: var(--teal);
  transition: background .3s;
}

.float-panel-body {
  padding: 20px;
  display: flex;
  flex-direction: column;
}

.float-panel-body-flush { padding: 0; }

.float-panel-footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 16px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  text-align: center;
}


/* ================================================================
   AVATAR COLOR PALETTE (10 brand-friendly colors)

   Użycie - działa na .avatar, .table-avatar, .order-avatar, .cal-mini-av:
   <div class="table-avatar avatar-c1">KM</div>
   <div class="table-avatar avatar-c2">PZ</div>

   Tokeny (override per-projekt): --avatar-c1..--avatar-c10
   Hash helper (deterministyczny kolor z imienia/ID - w JS):
     SM.avatarColor(name) → 'avatar-c' + (hash(name) % 10 + 1)
   ================================================================ */

:root {
  --avatar-c1:  #6c7de8;  /* indygo */
  --avatar-c2:  #f4b04f;  /* bursztyn */
  --avatar-c3:  #ef6b73;  /* koral */
  --avatar-c4:  #1baf92;  /* teal */
  --avatar-c5:  #2f8de4;  /* niebieski */
  --avatar-c6:  #9b59b6;  /* fiolet */
  --avatar-c7:  #e67e22;  /* pomarańcz */
  --avatar-c8:  #27ae60;  /* zielony */
  --avatar-c9:  #ec4899;  /* róż */
  --avatar-c10: #16a085;  /* turkus */
}

.avatar-c1  { background: var(--avatar-c1)  !important; color: #fff !important; }
.avatar-c2  { background: var(--avatar-c2)  !important; color: #fff !important; }
.avatar-c3  { background: var(--avatar-c3)  !important; color: #fff !important; }
.avatar-c4  { background: var(--avatar-c4)  !important; color: #fff !important; }
.avatar-c5  { background: var(--avatar-c5)  !important; color: #fff !important; }
.avatar-c6  { background: var(--avatar-c6)  !important; color: #fff !important; }
.avatar-c7  { background: var(--avatar-c7)  !important; color: #fff !important; }
.avatar-c8  { background: var(--avatar-c8)  !important; color: #fff !important; }
.avatar-c9  { background: var(--avatar-c9)  !important; color: #fff !important; }
.avatar-c10 { background: var(--avatar-c10) !important; color: #fff !important; }
.avatar-c-muted { background: var(--text-muted) !important; color: #fff !important; }
.avatar-c-dark  { background: #1a1a1a !important;          color: #fff !important; }


/* ================================================================
   INPUT PASSWORD (text input + reveal/hide eye toggle)

   Użycie:
   <div class="input-password">
     <input type="password" class="input" id="apiKey" value="secret">
     <button type="button" class="input-password-toggle" data-target="apiKey" aria-label="Pokaż/ukryj">
       <i class="bi bi-eye"></i>
     </button>
   </div>

   JS (już w main.js - SM.passwordToggle):
   document.querySelectorAll('.input-password-toggle').forEach(btn => {
     btn.addEventListener('click', () => {
       const i = document.getElementById(btn.dataset.target);
       i.type = i.type === 'password' ? 'text' : 'password';
       btn.querySelector('i').className = 'bi bi-eye' + (i.type === 'text' ? '-slash' : '');
     });
   });
   ================================================================ */

.input-password {
  position: relative;
  width: 100%;
}

.input-password .input {
  padding-right: 38px;
  width: 100%;
}

.input-password-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.input-password-toggle:hover {
  background: var(--light-gray);
  color: var(--text-primary);
}

.input-password-toggle i {
  font-size: 16px;
  line-height: 1;
}

/* Legacy aliases (z settings-payments - zachowanie wstecznej kompatybilności) */
.input-password-wrap { position: relative; width: 100%; }
.input-password-wrap .input { padding-right: 38px; width: 100%; }
.input-reveal-btn {
  position: absolute; top: 50%; right: 6px;
  transform: translateY(-50%);
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; background: transparent; border: 0; border-radius: 6px;
  color: var(--text-secondary); cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.input-reveal-btn:hover { background: var(--light-gray); color: var(--text-primary); }
.input-reveal-btn i { font-size: 16px; line-height: 1; }


/* ================================================================
   BADGE STATUS (uniwersalny pill - karnet, subskrypcja, status klienta)

   Użycie:
   <span class="badge-status badge-status-active">Aktywny</span>
   <span class="badge-status badge-status-expiring">Wygasający</span>
   <span class="badge-status badge-status-inactive">Nieaktywny</span>
   <span class="badge-status badge-status-none">Brak</span>

   Różnica vs .badge:
   - .badge: solid backgrounds (mocniejsze, dla CTA-like akcentów)
   - .badge-status: subtle dim backgrounds (dla tabel - nie krzyczy)
   ================================================================ */

.badge-status {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--radius-full);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
  user-select: none;
}

.badge-status-active   { background: var(--success-dim); color: var(--success); }
.badge-status-expiring { background: var(--warning-dim); color: var(--warning-ink); }
.badge-status-inactive { background: var(--light-gray);  color: var(--text-secondary); }
.badge-status-none     { background: var(--danger-dim);  color: var(--danger); }
.badge-status-pending  { background: var(--warning-dim); color: var(--warning-ink); }
.badge-status-completed { background: var(--accent-dim); color: var(--teal); }
.badge-status-gray     { background: var(--light-gray);  color: var(--text-secondary); }
.badge-status-blue     { background: var(--blue-dim);    color: var(--blue); }


/* Legacy PL aliases removed 2026-05-13 - migrated to:
   .badge-aktywny    → .badge-status-active
   .badge-wygasajacy → .badge-status-expiring
   .badge-nieaktywny → .badge-status-inactive
   .badge-brak       → .badge-status-none
*/


/* ================================================================
   CELL TEXT HELPERS (kolorowanie wartości w komórkach tabeli)

   Użycie:
   <td class="cell-text-danger">3 miesiące temu</td>
   <td class="cell-text-warn">6 tygodni temu</td>
   <td class="cell-text-success">dziś</td>
   <td class="cell-text-muted">-</td>
   ================================================================ */

.cell-text-danger  { color: var(--danger);  font-weight: 500; }
.cell-text-warn    { color: var(--warning-ink); font-weight: 500; }
.cell-text-success { color: var(--success); font-weight: 500; }
.cell-text-muted   { color: var(--text-secondary); }

/* Legacy PL aliases removed 2026-05-13 - migrated to:
   .cl-visit-old → .cell-text-danger
   .cl-visit-mid → .cell-text-warn
*/


/* ================================================================
   EXTENDED UTILITIES
   Dodatkowe klasy utility - rozszerzenie sekcji 23.
   Generowane z audytu inline styles (2026-05-13).
   ================================================================ */

/* ── Grid extended ── */
.grid-5 { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
.grid-6 { display: grid; grid-template-columns: repeat(6, 1fr); gap: 16px; }

/* ── Text size extended ── */
.text-base    { font-size: var(--fs-base) !important; }
.text-nav     { font-size: var(--fs-nav)  !important; }
.text-md      { font-size: var(--fs-md)   !important; }
.text-lg      { font-size: var(--fs-lg)   !important; }
.text-xl      { font-size: var(--fs-xl)   !important; }
.text-2xl     { font-size: var(--fs-2xl)  !important; }
.text-2xs     { font-size: var(--fs-2xs)  !important; }
.text-3xs     { font-size: var(--fs-3xs)  !important; }

/* ── Text misc ── */
.text-left       { text-align: left          !important; }
.text-uppercase  { text-transform: uppercase !important; }
.text-normal     { font-weight: 400          !important; }
.text-nowrap     { white-space: nowrap        !important; }
.text-truncate   { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.text-inherit    { color: inherit             !important; }
.lh-1           { line-height: 1             !important; }
.lh-tight       { line-height: 1.25          !important; }
.lh-normal      { line-height: 1.5           !important; }

/* ── Padding all sides ── */
.p-0   { padding:  0    !important; }
.p-1   { padding:  4px  !important; }
.p-2   { padding:  8px  !important; }
.p-3   { padding: 12px  !important; }
.p-4   { padding: 16px  !important; }
.p-5   { padding: 20px  !important; }
.p-6   { padding: 24px  !important; }
.p-8   { padding: 32px  !important; }

/* ── Padding left / right ── */
.pl-0  { padding-left:   0    !important; }
.pl-4  { padding-left:  16px  !important; }
.pl-6  { padding-left:  24px  !important; }
.pr-0  { padding-right:  0    !important; }
.pr-4  { padding-right: 16px  !important; }
.pr-6  { padding-right: 24px  !important; }
.px-0  { padding-left: 0; padding-right: 0       !important; }
.px-2  { padding-left: 8px; padding-right: 8px   !important; }
.px-3  { padding-left: 12px; padding-right: 12px !important; }
.px-4  { padding-left: 16px; padding-right: 16px !important; }
.px-6  { padding-left: 24px; padding-right: 24px !important; }
.py-0  { padding-top: 0; padding-bottom: 0       !important; }
.py-2  { padding-top: 8px; padding-bottom: 8px   !important; }
.py-3  { padding-top: 12px; padding-bottom: 12px !important; }
.py-4  { padding-top: 16px; padding-bottom: 16px !important; }
.py-6  { padding-top: 24px; padding-bottom: 24px !important; }

/* ── Margin left / right / auto ── */
.ml-0    { margin-left:   0    !important; }
.ml-auto { margin-left:  auto  !important; }
.mr-0    { margin-right:  0    !important; }
.mr-auto { margin-right: auto  !important; }
.mx-auto { margin-left: auto; margin-right: auto !important; }

/* ── Margin top ── */
.mt-0  { margin-top:  0    !important; }
.mt-4  { margin-top:  4px  !important; }
.mt-8  { margin-top:  8px  !important; }
.mt-12 { margin-top: 12px  !important; }
.mt-16 { margin-top: 16px  !important; }
.mt-20 { margin-top: 20px  !important; }
.mt-24 { margin-top: 24px  !important; }
.mt-32 { margin-top: 32px  !important; }

/* ── Margin bottom ── */
.mb-0  { margin-bottom:  0    !important; }
.mb-4  { margin-bottom:  4px  !important; }
.mb-8  { margin-bottom:  8px  !important; }
.mb-12 { margin-bottom: 12px  !important; }
.mb-16 { margin-bottom: 16px  !important; }
.mb-20 { margin-bottom: 20px  !important; }
.mb-24 { margin-bottom: 24px  !important; }
.mb-32 { margin-bottom: 32px  !important; }

/* ── Margin Y axis ── */
.my-0  { margin-top:  0    !important; margin-bottom:  0    !important; }
.my-4  { margin-top:  4px  !important; margin-bottom:  4px  !important; }
.my-8  { margin-top:  8px  !important; margin-bottom:  8px  !important; }
.my-12 { margin-top: 12px  !important; margin-bottom: 12px  !important; }
.my-16 { margin-top: 16px  !important; margin-bottom: 16px  !important; }
.my-20 { margin-top: 20px  !important; margin-bottom: 20px  !important; }
.my-24 { margin-top: 24px  !important; margin-bottom: 24px  !important; }
.my-32 { margin-top: 32px  !important; margin-bottom: 32px  !important; }

/* ── Gap extended ── */
.gap-2  { gap:  2px !important; }
.gap-6  { gap:  6px !important; }
.gap-10 { gap: 10px !important; }
.gap-32 { gap: 32px !important; }

/* ── Background ── */
.bg-page        { background: var(--bg)          !important; }
.bg-surface     { background: var(--surface)     !important; }
.bg-accent-dim  { background: var(--accent-dim)  !important; }
.bg-danger-dim  { background: var(--danger-dim)  !important; }
.bg-warning-dim { background: var(--warning-dim) !important; }
.bg-success-dim { background: var(--success-dim) !important; }
.bg-blue-dim    { background: var(--blue-dim)    !important; }
.bg-teal        { background: var(--teal)        !important; }
.bg-danger      { background: var(--danger)      !important; }
.bg-success     { background: var(--success)     !important; }
.bg-warning     { background: var(--warning)     !important; }
.bg-blue        { background: var(--blue)        !important; }
.bg-light-gray  { background: var(--light-gray)  !important; }
.bg-transparent { background: transparent         !important; }
.bg-white       { background: #ffffff             !important; }

/* ── Border ── */
.border         { border: 1px solid var(--border)  !important; }
.border-0       { border: none                     !important; }
.border-top     { border-top: 1px solid var(--border) !important; }
.border-bottom  { border-bottom: 1px solid var(--border) !important; }

/* ── Border radius ── */
.rounded-sm   { border-radius: var(--radius-sm)   !important; }
.rounded      { border-radius: var(--radius)       !important; }
.rounded-md   { border-radius: var(--radius-md)    !important; }
.rounded-lg   { border-radius: var(--radius-lg)    !important; }
.rounded-xl   { border-radius: var(--radius-xl)    !important; }
.rounded-full { border-radius: var(--radius-full)  !important; }
.rounded-0    { border-radius: 0                   !important; }

/* ── Shadows ── */
.shadow-xs { box-shadow: var(--shadow-xs) !important; }
.shadow-sm { box-shadow: var(--shadow-sm) !important; }
.shadow    { box-shadow: var(--shadow)    !important; }
.shadow-md { box-shadow: var(--shadow-md) !important; }
.shadow-lg { box-shadow: var(--shadow-lg) !important; }
.shadow-xl { box-shadow: var(--shadow-xl) !important; }
.shadow-0  { box-shadow: none             !important; }

/* ── Overflow ── */
.overflow-hidden  { overflow: hidden  !important; }
.overflow-auto    { overflow: auto    !important; }
.overflow-y-auto  { overflow-y: auto  !important; }
.overflow-x-auto  { overflow-x: auto  !important; }

/* ── Position ── */
.relative { position: relative !important; }
.absolute { position: absolute !important; }
.sticky   { position: sticky   !important; }

/* ── Sizing ── */
.w-50     { width: 50%  !important; }
.h-full   { height: 100% !important; }
.min-h-0  { min-height: 0 !important; }

/* ── Cursor ── */
.cursor-pointer  { cursor: pointer  !important; }
.cursor-default  { cursor: default  !important; }

/* ── Opacity ── */
.opacity-50 { opacity: .5  !important; }
.opacity-70 { opacity: .7  !important; }

/* ── Transitions ── */
.transition { transition: all var(--transition) !important; }

/* ── Flex direction ── */
.flex-col     { flex-direction: column !important; }
.flex-row     { flex-direction: row    !important; }
.flex-shrink-0 { flex-shrink: 0        !important; }
.flex-grow     { flex-grow: 1          !important; }

/* ── Display inline-flex ── */
.d-inline-flex { display: inline-flex !important; }

/* ── Gap row/column specific ── */
.row-gap-8  { row-gap: 8px  !important; }
.row-gap-16 { row-gap: 16px !important; }
.col-gap-8  { column-gap: 8px  !important; }
.col-gap-16 { column-gap: 16px !important; }

/* ── Margin all-sides ── */
.m-0              { margin: 0 !important; }

/* ── Margin right ── */
.mr-4             { margin-right: 4px  !important; }
.mr-6             { margin-right: 6px  !important; }

/* ── Min / max width ── */
.min-w-0          { min-width: 0 !important; }

/* ── Inset ── */
.inset-0          { inset: 0 !important; }

/* ── Align items stretch ── */
.items-stretch    { align-items: stretch !important; }

/* ── Cursor ── */
.cursor-pointer   { cursor: pointer !important; }

/* ── Object fit ── */
.object-cover     { object-fit: cover !important; }

/* ── Font weight 500 ── */
.text-medium      { font-weight: 500 !important; }

/* ── Text decoration ── */
.text-decoration-none { text-decoration: none !important; }

/* ── Line height ── */
.lh-normal        { line-height: 1.5 !important; }
.lh-relaxed       { line-height: 1.6 !important; }

/* ── Width helpers ── */
.w-80             { width: 80px !important; }

/* ── Component-level utility ── */
.label-caps       { text-transform: uppercase !important; letter-spacing: .6px; font-size: var(--fs-2xs) !important; color: var(--text-secondary) !important; }
.link-plain       { color: inherit !important; text-decoration: none !important; }


/* ================================================================
   25. RESPONSIVE - TABLET & MOBILE
   ================================================================ */

/* ── Mobile bottom navigation ── */
.mobile-bottom-nav {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: calc(56px + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: var(--surface);
  border-top: 1px solid var(--border);
  z-index: var(--z-topbar);
  align-items: stretch;
}

.mnav-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 6px 0;
  font-size: 10px;
  font-weight: 500;
  color: var(--text-secondary);
  text-decoration: none;
  border: none;
  background: none;
  cursor: pointer;
  font-family: inherit;
  transition: color var(--transition);
  -webkit-tap-highlight-color: transparent;
}

.mnav-item i { font-size: 20px; line-height: 1; }
.mnav-item:active { color: var(--teal); }
.mnav-item.active { color: var(--teal); font-weight: 600; }

/* Kotwica dla kropki zaległości oraz cel dotykowy: pięć kafli na 390 px daje
   78x56 px, czyli powyżej progu 44 px z WCAG 2.5.8. */
.mnav-item { position: relative; min-height: 56px; }

/* Etykieta w jednej linii. Bez tego angielskie „My classes" i „Notifications"
   w komórce 78 px zawijają się w drugi wiersz i rozjeżdżają wysokość paska. */
.mnav-item span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Kropka zaległości na „Więcej". Liczba w kaflu 78 px byłaby nieczytelna -
   sam fakt „coś czeka" wystarcza, a co dokładnie mówi `.sidebar-badge`
   w szufladzie. */
.mnav-dot {
  position: absolute;
  top: 6px;
  left: calc(50% + 8px);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
  box-shadow: 0 0 0 2px var(--surface);
}

/* ── Mobile search overlay ── */
.mobile-search-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.45);
  z-index: var(--z-modal);
  align-items: flex-start;
  justify-content: center;
  padding-top: 12px;
  opacity: 0;
  transition: opacity .2s ease;
}

.mobile-search-overlay.open {
  display: flex;
  opacity: 1;
}

.mobile-search-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  width: calc(100% - 24px);
  max-width: 480px;
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 10px 16px;
  box-shadow: var(--shadow-lg);
}

.mobile-search-bar > i { color: var(--text-muted); font-size: 18px; flex-shrink: 0; }

.mobile-search-input {
  flex: 1;
  border: none;
  background: none;
  font-size: 16px;
  font-family: inherit;
  color: var(--text-primary);
  outline: none;
  min-width: 0;
}

.mobile-search-input::placeholder { color: var(--text-muted); }

.mobile-search-close {
  width: 36px;
  height: 36px;
  border: none;
  background: none;
  color: var(--text-secondary);
  font-size: 16px;
  cursor: pointer;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.mobile-search-close:active { background: var(--bg); }

/* Search trigger: hidden by default, shown on small mobile */
.mobile-search-trigger { display: none; }

/* ── Body scroll lock (iOS Safari) ── */
body.scroll-locked {
  position: fixed;
  width: 100%;
  overflow: hidden;
}

/* Lista Tom Select i kalendarz Flatpickr otwarte w modalu: oba sa dziecmi <body>
   i licza `top` od okna, a zablokowane <body> stoi `top: -Y`. Bez tej poprawki
   otwieraly sie Y pikseli nad polem - po przewinieciu strony nad ekranem
   (F6, BUG-wk-01: sprzedaz karnetu z telefonu). Zmienna ustawia lockBodyScroll(). */
body.scroll-locked > .flatpickr-calendar { margin-top: var(--scroll-lock-y, 0px); }
/* 7 px to odstep listy od pola z bazowej reguly `.ts-dropdown` (tam z !important). */
body.scroll-locked > .ts-dropdown { margin-top: calc(7px + var(--scroll-lock-y, 0px)) !important; }

/* ── Sidebar overlay backdrop (mobile drawer) ── */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  z-index: var(--z-overlay-backdrop);
  opacity: 0;
  transition: opacity var(--transition);
  pointer-events: none;
}

.sidebar-overlay.visible {
  opacity: 1;
  pointer-events: all;
}

/* ── Tablet (≤ 1024px) ── */
@media (max-width: 1024px) {
  :root { --sidebar-w: 210px; }

  /* Zawężone do topbaru - patrz komentarz przy regule ≤480px. Bez tego
     szerokość 180 px dostawało każde pole tej klasy, także te, którym widok
     nadał własną (szukajka klientów ma 300 px, filtr obecności 240 px). */
  .topbar .input-search { width: 180px; }
  .topbar-nav   { display: none; }
  .main         { padding: 20px 20px calc(5px + var(--fw-stack-save)); }
  body.admin-panel .main,
  body.owner-panel .main { padding-bottom: calc(20px + var(--fw-stack-save)); }

  .action-bar { padding: 12px 20px; }

  /* ── Siatki pomocnicze: zwijanie liczone, nie zgadywane ──
     (plan: wyglad-mobilny.md, etap 4)

     Kolumna treści na iPadzie w pionie to 820 - 210 (sidebar) - 40 (padding)
     = 570 px. `.grid-3` dawało tam 3 x 180 px, `.grid-4` 4 x 132 px - poniżej
     progu czytelności dla kafla z etykietą i wartością. Pas 769-1024 px nie
     miał na to ANI JEDNEJ reguły: wszystko czekało na próg 768 px.

     `auto-fit` z minimum zamiast stałej liczby kolumn: siatka zwija się sama,
     gdy zabraknie miejsca, i działa tak samo przy zwiniętym sidebarze, gdzie
     to samo okno daje o 210 px więcej. `min()` w minimum chroni przed
     przepełnieniem, gdy kontener jest węższy niż samo minimum. */
  .grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
  .grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); }

  /* ── Paski zakładek w paśmie 769-1024 px ──
     Przewijanie `.tabs-bar` i zawijanie `.page-tabs-bar` siedziały wyłącznie
     w bloku ≤768 px, więc karta studia z pięcioma zakładkami wypychała
     dokument w poziomie na iPadzie w pionie.

     OBA selektory naraz, nie jeden: `.page-tabs-bar .tab` ma `flex: none`
     i `white-space: nowrap`, więc samo zawinięcie rodzica przenosi przyciski
     do drugiego rzędu, ale `<nav class="tabs-bar">` dalej nie ma jak się ani
     skurczyć, ani przewinąć. */
  .tabs-bar {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tabs-bar::-webkit-scrollbar { display: none; }
  .tabs-bar .tab { flex: none; }
  .page-tabs-bar { flex-wrap: wrap; }
}

/* ── Tablet w pionie (769-1024 px): panel boczny jako szuflada ──
   Staly panel boczny (210 px) zostawial treści iPada w pionie ok. 580 px, a moduly
   przechodza na uklad mobilny dopiero przy <= 768 px: karty, formularze i stopki
   przycinaly sie na kilkunastu ekranach (F6, decyzja wlasciciela z 23.10.2026).
   Ta sama szuflada co na telefonie (hamburger -> `mobile-open`, tlo, `inert`);
   uklad tresci zostaje desktopowy, tylko na pelnej szerokosci. Dolna nawigacja
   i pozostale reguly <= 768 px tego zakresu nie dotycza. */
@media (min-width: 769px) and (max-width: 1024px) {
  :root { --sidebar-w: 260px; }

  .sidebar-overlay { display: block; }

  .sidebar,
  body.sidebar-collapsed .sidebar {
    transform: translateX(-100%);
    z-index: var(--z-sidebar-mobile);
  }
  /* Cien tylko przy otwartej szufladzie - schowana wystawiala go przy lewej krawedzi */
  .sidebar.mobile-open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0, 0, 0, .15); }

  .topbar,
  body.sidebar-collapsed .topbar { left: 0; }

  .main,
  body.sidebar-collapsed .main { margin-left: 0; }

  .action-bar,
  body.sidebar-collapsed .action-bar,
  .settings-save-bar,
  body.sidebar-collapsed .settings-save-bar { left: 0; }
}

/* ── Mobile (≤ 768px) ── */
@media (max-width: 768px) {
  :root { --sidebar-w: 260px; }

  /* Reveal overlay backdrop */
  .sidebar-overlay { display: block; }

  /* Sidebar: off-screen drawer - always, regardless of sidebar-collapsed */
  .sidebar,
  body.sidebar-collapsed .sidebar {
    transform: translateX(-100%);
    z-index: var(--z-sidebar-mobile);
  }

  /* JS adds .mobile-open to slide in; cien tylko przy otwartej szufladzie -
     schowana wystawiala go przy lewej krawedzi ekranu */
  .sidebar.mobile-open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0, 0, 0, .15); }

  /* Topbar, main, action-bar: always full-width on mobile */
  .topbar,
  body.sidebar-collapsed .topbar {
    left: 0;
  }

  /* ── STOS DOLNYCH PASKÓW ──
     Każda warstwa deklaruje SWOJĄ wysokość, a czyta sumę niższych. Wartość
     bierze się z obecności elementu w DOM, więc panel bez danego paska
     dostaje 0px sam z siebie. Definicje zmiennych: sekcja `:root`. */

  body:has(.mobile-bottom-nav) {
    --fw-stack-nav: calc(56px + env(safe-area-inset-bottom, 0px));
    --fw-safe-b: 0px;              /* wcięcie opłacone przez nawigację */
  }

  /* 1 px ramki + 12 px + 44 px przycisku + 12 px */
  body:has(.action-bar),
  body:has(.settings-save-bar) { --fw-stack-save: 69px; }

  /* Pigułka trybu: ok. 52 px + 8 px przerwy. Oba paski nigdy nie występują
     razem (`.imp-bar` to panel klienta, `.pa-bar` to panel obsługi), więc
     dzielą jedną zmienną. */
  body:has(.imp-bar),
  body:has(.pa-bar) { --fw-stack-mode: 60px; }

  /* ── Warstwa 0: nawigacja ──
     Przy otwartej klawiaturze pasek SCHODZI Z DROGI. Stoi na `bottom: 0`
     z `z-index` topbaru, wiec inaczej zostaje POD klawiatura i zjada jej
     dolna krawedz, a jednoczesnie zabiera 56 px z i tak scisnietego kadru.
     Zjazd, nie `display: none`: znikniecie przestawiloby uklad strony
     w trakcie pisania. */
  body.kb-open .mobile-bottom-nav {
    transform: translateY(100%);
    pointer-events: none;
  }

  .mobile-bottom-nav {
    display: flex;
    transition: transform .18s ease;
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }

  /* Hamburger chowamy tam, gdzie „Więcej" faktycznie go zastępuje.
     Warunek po OBECNOŚCI paska, nie po klasie panelu: poprzednia wersja
     (`body.client-panel`) wymagała ręcznego pilnowania, bo wcześniejsza
     wersja globalna zabrała panelowi obsługi całą nawigację - sidebar jest
     odsunięty poza ekran, a jedyny przycisk otwierający szufladę był ukryty.
     Teraz ta awaria jest niemożliwa: gdzie nie ma paska, zostaje hamburger. */
  body:has(.mobile-bottom-nav) .topbar-hamburger { display: none; }
  .topbar-hamburger { width: 44px; height: 44px; }   /* WCAG 2.5.8 */

  /* ── Warstwa 1: pasek zapisu ── */
  .action-bar,
  body.sidebar-collapsed .action-bar,
  .settings-save-bar,
  body.sidebar-collapsed .settings-save-bar {
    left: 0;
    bottom: calc(var(--fw-stack-nav) + var(--fw-stack-kb));
    padding: 12px 16px;
    padding-bottom: calc(12px + var(--fw-safe-b));
  }
  /* Przy klawiaturze nawigacja zjezdza (warstwa 0), wiec pasek siada wprost
     na klawiaturze - inaczej wisial 56 px nad nia (BUG-mobA-10). Tylko pasek,
     nie `--fw-stack-nav`: ta zmienna liczy tez zapas `.main` i jej zmiana
     przestawialaby uklad w trakcie pisania. */
  body.kb-open .action-bar,
  body.kb-open .settings-save-bar {
    bottom: var(--fw-stack-kb);
  }
  /* Przewiniecie do pola (fokus, nawigacja klawiatura) zostawia miejsce na
     pasek zapisu - inaczej pole wpisywane przy klawiaturze stawalo POD paskiem
     (mobA-10, zgloszone z emulacji). Liczby wprost, nie zmienne stosu: te
     siedza na <body>, a `scroll-padding` czyta <html>. Pasek z napisem
     "Niezapisane zmiany" ma na telefonie ok. 91 px (nie 69 jak w stosie) -
     stad 110 px z oddechem; bez klawiatury dochodzi 56 px dolnej nawigacji. */
  html:has(.action-bar),
  html:has(.settings-save-bar) { scroll-padding-bottom: calc(166px + env(safe-area-inset-bottom, 0px)); }
  html:has(body.kb-open .action-bar),
  html:has(body.kb-open .settings-save-bar) { scroll-padding-bottom: 110px; }

  /* ── Warstwa 2: pasek trybu ──
     Reguły dla `.imp-bar` i `.pa-bar` NIE stoją tutaj, tylko w blokach
     `<style>` ich własnych partiali. Powód jest twardy: te bloki renderują
     się w `<body>`, czyli PO tym arkuszu, a selektor `.imp-bar` ma tę samą
     specyficzność (0-1-0) - reguła stąd przegrałaby kolejnością i wyglądałaby
     na działającą tylko w narzędziach dewelopera. Zmienne stosu są globalne,
     więc partial czyta je bez żadnej dodatkowej zależności. */

  /* ── Zapas w treści: liczony, nie zgadywany ──
     Każdy selektor ma specyficzność 0-2-1 - tyle samo, co
     `body.admin-panel .main { padding-bottom: calc(20px + ...) }` z bloku
     ≤1024px. Ten blok stoi niżej w pliku, więc wygrywa kolejnością. Skrócenie
     tego do samego `.main` (0-1-0) cofa poprawkę BEZ ŻADNEGO BŁĘDU w konsoli:
     admin traci wysokość dolnej nawigacji i ostatnie pole formularza chowa
     się pod paskami. */
  body.admin-panel .main,
  body.owner-panel .main,
  body.client-panel .main,
  body.sidebar-collapsed .main {
    margin-left: 0;
    padding: 16px 16px calc(16px + var(--fw-stack-nav) + var(--fw-stack-save));
  }

  /* ── GRAFIK: TRYB DNIA (plan: wyglad-mobilny.md, etap 6) ──
     Wezsza rynna godzin - przy jednej kolumnie dnia kazdy piksel liczy sie
     w nazwie zajec. Silnik czyta te sama zmienna. */
  :root { --cal-gutter: 44px; }

  /* Dzien przewija sie RAZEM ZE STRONA.
     Lancuch `overflow: hidden` na `.cal-outer` / `.cal-layout` /
     `.cal-grid-wrap` czynil z kazdego z nich kontener przewijania, wiec
     `position: sticky` na pasku dni nie mial wzgledem czego zadzialac.
     Jednoczesnie `.cal-time-scroll` ma `flex: 1` bez wysokosci, wiec jego
     `overflow-y` nigdy niczego nie przycinal - to bylo przewijanie strony
     udajace przewijanie wewnetrzne. */
  .cal-outer,
  .cal-layout,
  .cal-grid-wrap,
  .cal-time-scroll { overflow: visible; }
  .cal-time-scroll { flex: none; }

  /* Pasek dni przykleja sie pod topbarem. */
  .cal-grid-head--strip {
    position: sticky;
    top: calc(var(--topbar-h, 64px) + env(safe-area-inset-top, 0px));
    z-index: var(--z-sticky, 100);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
  }

  /* Jedna kolumna stoi tuz przy rynnie - jej `border-left` plus `border-right`
     rynny dawalyby podwojna kreske. */
  .cal-day-col--solo { border-left: 0; }

  /* Miesiac na telefonie odpowiada na pytanie „ktore dni sa zajete",
     nie „co jest w srode o 18" - wpisy i tak nie miescily sie w komorce. */
  .cal-month-cell { min-height: 44px; }
  .cal-month-cell-events { display: none; }

  /* Sidebar: touch-friendly nav items */
  .sidebar-link { padding: 12px 20px; }
  .sidebar-link.active { padding-left: 16px; }

  /* Sidebar: active state feedback for touch */
  .sidebar-link:active { background: var(--accent-dim); }

  /* Form layout: stack on mobile */
  .form-layout { flex-direction: column; }
  .form-col-main { flex: none; width: 100%; }
  .form-col-side { position: static; width: 100%; }

  /* Grid helpers: stack on mobile */
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  /* `span 2` w jednokolumnowej siatce dokladal NIEJAWNA druga kolumne - pola
     pojedyncze stawaly parami i sciskaly sie do kilkudziesieciu pikseli. */
  .form-span-2, .form-span-3 { grid-column: 1 / -1; }

  /* Row: stack on mobile */
  .row { flex-direction: column; }

  /* Mega menu: single column on mobile */
  .mega-menu { min-width: 0; grid-template-columns: 1fr; padding: 12px; }

  /* Tabs: horizontal scroll */
  .tabs-bar { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .tabs-bar::-webkit-scrollbar { display: none; }
  .tabs-bar .tab { flex: none; }

  /* Page tabs bar: wrap */
  .page-tabs-bar { flex-wrap: wrap; }

  /* Opakowanie tabel przeniesione POZA ten blok - patrz sekcja 8a.
     Nakładał je JS na każdej szerokości, a `overflow-x` dostawało tylko
     poniżej 768 px, więc na iPadzie opakowanie istniało i nic nie robiło. */

  /* Inputs/buttons: touch-friendly sizes */
  .btn { min-height: 44px; }
  .btn-sm { min-height: 36px; padding: 0 12px; font-size: var(--fs-xs); }
  .btn-clear { width: 36px; height: 36px; }
  .btn-clear.btn-sm { width: 28px; height: 28px; }
  /* font-size < 16px sprawia, ze iOS Safari zoomuje strone przy kazdym
     kliknieciu w pole. .form-input to alias .input (patrz sekcja FORMS). */
  .input { height: 44px; font-size: 16px; }
  .select { height: 44px; font-size: 16px; }
  .textarea { font-size: 16px; }
  .form-input { height: 44px; font-size: 16px; }

  /* Topbar compact */
  .topbar .input-search { width: 150px; }
  .topbar-user .name { display: none; }

  /* Page header: stack */
  .page-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    margin-bottom: 16px;
  }

  /* Panels: tighter padding */
  .panel-body { padding: 14px; }
  .panel-footer { padding: 12px 14px; }

  /* Status cards: stack vertically */
  .status-cards { flex-direction: column; gap: 8px; }

  /* Radio pills: wrap */
  .radio-pills { flex-wrap: wrap; gap: 6px; }

  /* ts-select: hide dropdown arrow */
  .ts-control {
    background-image: none !important;
  }
  /* Padding osobno i z pelna specyficznoscia - inaczej przegrywa z vendorem
     tak samo, jak przegrywala regula bazowa. Dotyczy single i multi. */
  .ts-wrapper .ts-control:not(.rtl) { padding-right: 12px !important; }

  /* ── Pasek liczb nad lista: siatka 2x2 zamiast lamanego wiersza ──
     `.stat-bar` to `flex-wrap: wrap`, wiec ostatnia pozycja spadala do drugiej
     linii SAMA - z odziedziczonym po `.stat-item + .stat-item` wcieciem 18 px
     i kreska z lewej, czyli jako sierota z kreska w powietrzu.

     PROG TO 768, NIE 480. Pasek czterech pozycji potrzebuje okolo 490 px
     i lamie sie duzo wczesniej, niz zaczyna sie "maly telefon". Przy 480 px
     zostawala dziura: tabela byla juz w trybie kart (kontener <= 600 px),
     a pasek nad nia dalej sie lamal. Dwa progi, dwie osie - i wlasnie w tym
     pasie widac bylo blad.

     Cztery kolumny odpadly po wyliczeniu: przy 390 px zostaje ~90 px na
     pozycje, a "Wszyscy klienci" potrzebuje ~110 px i zlamaloby sie w srodku
     komorki. Podzialki rysujemy od nowa, bo `.stat-item + .stat-item` opisuje
     JEDEN RZAD - w siatce dawalo kreske takze na poczatku drugiego wiersza. */
  .stat-bar {
    display: grid;
    /* minmax(0, ...) - rowne kolumny takze przy dlugiej etykiecie; `stretch` -
       komorki wiersza tej samej wysokosci, wiec kreski sie nie schodkuja
       (bazowe `align-items: center` przechodzilo do siatki, BUG-mobB-06) */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: stretch;
    padding: 0;
    row-gap: 0;
  }
  .stat-bar .stat-item:nth-child(n) {
    padding: 10px 14px;
    border-left: 0;
    border-bottom: 1px solid var(--border);
  }
  /* Pionowa podzialka tylko miedzy kolumnami - czyli na kazdej PARZYSTEJ
     pozycji, niezaleznie od liczby pozycji. */
  .stat-bar .stat-item:nth-child(2n) { border-left: 1px solid var(--border); }

  /* Pozioma podzialka znika w OSTATNIM WIERSZU. Paski maja w projekcie od 1 do
     6 pozycji (najwiecej zdrowie systemu), wiec liczba wierszy nie jest znana
     z gory: `nth-child(n+3)` opisywalby wylacznie zestaw czteropozycyjny.
     `:last-child` lapie koniec zawsze, a para `:nth-last-child(2):nth-child(odd)`
     doklada jego sasiada z lewej - ten stoi w ostatnim wierszu tylko wtedy,
     gdy pozycji jest parzyscie. */
  .stat-bar .stat-item:last-child,
  .stat-bar .stat-item:nth-last-child(2):nth-child(odd) { border-bottom: 0; }

  /* Pozycja nieparzysta na koncu zostalaby sama w polowie wiersza, z pusta
     polowa obok. Niech zajmie caly wiersz - dotyczy takze paskow
     jednopozycyjnych (lista studiow). */
  .stat-bar .stat-item:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* Pasek liczb na tablecie (769-1024 px): jeden przewijany wiersz zamiast
   lamania z sierota i kreska w powietrzu na poczatku drugiej linii
   (BUG-mobB-06). Siatka 2x2 zostaje dla telefonu. */
@media screen and (min-width: 769px) and (max-width: 1024px) {
  .stat-bar { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .stat-bar::-webkit-scrollbar { display: none; }
  .stat-item { flex: none; white-space: nowrap; }
}

/* Grupa filtrow przewija sie w poziomie zamiast ucinac ostatnie przyciski
   (`overflow: hidden` bazy, BUG-mobA-06) - telefon i tablet (820 px tez
   ucinal). Zaokraglenie zostaje na ramce. */
@media screen and (max-width: 1024px) {
  .filter-group { overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .filter-group::-webkit-scrollbar { display: none; }
  .filter-group-btn { flex: none; white-space: nowrap; }
}

/* ── Small mobile (≤ 480px) ── */
@media (max-width: 480px) {
  /* `.topbar .input-search`, NIE samo `.input-search`.
     Reguła miała schować wyszukiwarkę TOPBARU i zastąpić ją przyciskiem
     `.mobile-search-trigger` (komentarz w topbar.php:145). Bez zawężenia
     chowała jednak KAŻDE pole tej klasy - a jest ich w 20 widokach: szukajka
     klientów, filtr wiadomości, wyszukiwarka na liście obecności, dziennik
     zdarzeń, zgłoszenia, lojalność, wszystkie ekrany platformy i strona
     wyników wyszukiwania globalnego. Na telefonie te ekrany traciły swoją
     jedyną kontrolkę filtrowania, bez śladu w konsoli. */
  .topbar .input-search { display: none; }
  .mobile-search-trigger { display: flex; }
  .topbar       { padding: 0 12px; gap: 6px; }
  .topbar-lang  { display: none; }

  .topbar-icon  { width: 36px; height: 36px; font-size: var(--fs-base); }

  .notif-dropdown { min-width: 0; width: calc(100vw - 24px); right: -8px; }

  /* Action bar: stack, buttons stretch */
  .action-bar       { flex-direction: column-reverse; gap: 8px; align-items: stretch; }
  .action-bar-right { justify-content: flex-end; }
  .action-bar-left  { justify-content: flex-start; }
  .action-bar .btn  { flex: 1; justify-content: center; }

  /* Pasek zapisu rośnie razem z ułożeniem w kolumnę:
     1 px ramki + 12 + 44 + 8 + 44 + 12 = 121 px.
     Dotyczy WYŁĄCZNIE `.action-bar` - `.settings-save-bar` nie dostaje tu
     `column-reverse` i zostaje przy 69 px. Ustawienie jednej wartości dla
     obu zostawiłoby w Ustawieniach 52 px martwego pasa. */
  body:has(.action-bar) { --fw-stack-save: 121px; }

  /* Panel headers compact */
  .panel-header { padding: 12px 14px; }
  .panel-body   { padding: 12px 14px; }

  /* ── Kafle metryk (panel platformy) w jednej kolumnie ──
     `.report-metrics` to siatka 2x2, w której etykieta jest ZDANIEM, nie
     podpisem ("MRR - ostatnie opłacone zamówienie każdego konta..."). Przy
     dwóch kolumnach na 390 px zostaje ~150 px na komórkę i zdanie rozjeżdża
     się na osiem linii, a wartość obok stoi w pustce.

     Obramowania NIE mogą zostać takie, jak są: `:nth-child(2n)` zdejmuje
     prawą krawędź co drugiego kafla, a `:nth-child(n+3)` dolną od trzeciego
     - oba warunki opisują siatkę DWUKOLUMNOWĄ i w jednej kolumnie rysują
     krawędzie w losowych miejscach. Stąd pełne przestawienie, nie sama
     zmiana `grid-template-columns`.

     `:nth-child(n)` na końcu selektora podnosi swoistość do 0,3,0 - równa
     zapisowi wyżej nie wystarczy, bo zależność od kolejności w pliku jest
     dokładnie tym, co pęka przy pierwszej reorganizacji arkusza. */
  .report-metrics { grid-template-columns: 1fr; }

  .report-metrics .metric-box:nth-child(n) {
    padding: 12px 14px;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
  .report-metrics .metric-box:last-child:nth-child(n) { border-bottom: none; }
}


/* ================================================================
   25a. OŚ WSKAŹNIKA - `(pointer: coarse)`, BEZ PROGU SZEROKOŚCI
   (plan: wyglad-mobilny.md, etap 4)

   DWA PYTANIA, KTÓRE BYŁY SKLEJONE W JEDNĄ LICZBĘ.
   Blok ≤768px robił naraz UKŁAD (szuflada, stos kolumn, dolna nawigacja)
   i ROZMIARY CELÓW (przyciski 44 px, pola 44 px z krojem 16 px). To dwie
   prostopadłe osie i sklejenie ich miało konkretną ofiarę: iPad w pionie
   ma 810-834 px, więc dostawał układ desktopowy „z myszą w założeniu" -
   przyciski 34 px, małe 26 px, pola 34 px z krojem 13 px (czyli automatyczny
   zoom iOS przy każdym dotknięciu pola). To urządzenie, na którym trener
   przy sali odklikuje obecność palcem.

   Rozmiary należą do wskaźnika, nie do szerokości okna. `(pointer: coarse)`
   pyta o wskaźnik PODSTAWOWY, więc łapie telefon i tablet, a nie łapie myszy.
   Ryzyko regresji na komputerze wynosi zero: mysz nie dopasowuje tego bloku.

   `(pointer: coarse)`, NIE `(any-pointer: coarse)`. `any-pointer` łapie
   każde urządzenie wskazujące podpięte do maszyny, więc laptop z ekranem
   dotykowym i myszą dostałby przyciski 44 px przy pracy myszą. Świadoma
   konsekwencja wyboru: laptop dotykowy z gładzikiem zostaje przy rozmiarach
   myszkowych.

   Blok stoi PO bloku ≤480px i niczego nie usuwa - duplikaty `.btn`/`.input`
   w bloku ≤768px zostają i są nieszkodliwe (ta sama wartość).

   REGUŁA NA PRZYSZŁOŚĆ: wysokości kontrolek, cele dotykowe i kroje pól idą
   WYŁĄCZNIE tutaj. Jedno odstępstwo „bo akurat pasowało do progu szerokości"
   i za pół roku iPad znowu dostanie przyciski 26 px.
   ================================================================ */

@media (pointer: coarse) {

  /* ── Cele dotykowe ──
     WCAG 2.2, kryterium 2.5.8: minimum 24x24 px, zalecane 44x44. */
  .btn        { min-height: 44px; }
  .btn-sm     { min-height: 36px; padding: 0 12px; font-size: var(--fs-xs); }
  .btn-lg     { min-height: 48px; }
  .btn-clear  { width: 36px; height: 36px; }
  .btn-clear.btn-sm { width: 32px; height: 32px; }

  /* Przyciski akcji w wierszu tabeli miały 28x28 px przy odstępie 2 px -
     formalnie ponad progiem 24 px, praktycznie poza zasięgiem kciuka.
     Tu mieszczą się operacje nieodwracalne (usunięcie klienta), a na
     telefonie nie chroni ich precyzja myszy. */
  .table-btn    { width: 40px; height: 40px; }
  .table-actions { gap: 4px; }

  /* Pole wyboru w tabeli: 15 px kontrolki to poniżej połowy progu WCAG.
     Powiększamy samą kontrolkę, a nie tylko obszar klikalny - inaczej
     użytkownik nie widzi, gdzie ma trafić. */
  .table-checkbox { width: 22px; height: 22px; }

  /* Krzyżyk czyszczący pole wyszukiwania miał 18x18 px. */
  .input-search-clear { width: 32px; height: 32px; font-size: var(--fs-sm); }

  /* ── Pola formularzy ──
     `font-size` MUSI wynosić 16 px. Poniżej tej wartości iOS Safari zoomuje
     całą stronę przy każdym wejściu w pole i nie cofa zoomu po wyjściu. */
  .input,
  .form-input,
  .select        { height: 44px; font-size: 16px; }
  .textarea      { font-size: 16px; }
  .modal-confirm-input { height: 44px; font-size: 16px; }
  /* Edytor Quill (zgloszenie, aktualnosc, regulamin) to tez pole - mial 13 px
     i iOS powiekszal ekran przy fokusie (audyt F1, BUG-f1-admcore-04). */
  .ql-editor     { font-size: 16px; }

  /* Paginacja i zakladki - trzy klasy, ktorych blok rozmiarow nie obejmowal.
     `.page-btn` ma 28 px, `.filter-group-btn` 34 px, a `.page-tabs-bar .tab`
     dopelnienie 5 px; wszystkie ponizej progu WCAG 2.5.8. Paginacja jest
     najgorszym przypadkiem: numery stron stoja obok siebie, wiec maly cel
     obok malego celu to trafienie w sasiednia strone wynikow.
     Dotyczy WSZYSTKICH trzech paneli, nie tylko platformy. */
  .page-btn         { min-width: 40px; min-height: 40px; }
  .filter-group-btn { min-height: 40px; }
  .page-tabs-bar .tab { padding: 10px 14px; min-height: 44px; }

  /* ── Nawigacja ── */
  .sidebar-link          { padding: 12px 20px; }
  .sidebar-link.active   { padding-left: 16px; }
  .sidebar-link:active   { background: var(--accent-dim); }
  .topbar-hamburger      { width: 44px; height: 44px; }

  /* ── Małe cele: obszar dotyku większy niż rysunek (F6, BUG-wk-05) ──
     Ikony topbara miały 36 px, menu „⋮” 30 px, okruszki 18 px wysokości,
     próbki koloru 30 px. Tam, gdzie rysunek ma zostać mały (okruszki w linii
     tytułu, kółka kolorów w rzędzie), rośnie wyłącznie niewidoczny `::before`. */
  .topbar-icon           { width: 44px; height: 44px; }
  .topbar .input-search  { height: 40px; }
  .dots-trigger          { width: 40px; height: 40px; }
  .breadcrumb a          { position: relative; }
  .breadcrumb a::before  { content: ''; position: absolute; inset: -13px -4px; }
  /* -6 px przy odstępie 12 px między kółkami: obszary stykają się, nie zachodzą */
  .color-swatch::before  { content: ''; position: absolute; inset: -6px; border-radius: 50%; }

  /* Telefon do 400 px: chip ważności + pięć ikon 44 px + awatar nie mieszczą się
     w pasku - menu konta wystawało poza ekran już przy 36 px (340/320 na iPhonie SE,
     380/375 z chipem). Ikony mają tu 40 px, odstęp 2 px, węższe dopełnienie konta,
     a strzałka przy awatarze znika (sam awatar dalej otwiera menu). */
  @media (max-width: 400px) {
    .topbar-icon               { width: 40px; height: 40px; }
    .topbar-right              { gap: 2px; }
    .topbar-user               { padding: 5px 4px 5px 8px; }
    .topbar-user > .bi-chevron-down { display: none; }
  }

  /* ── Zakładki: przewijanie zamiast ściskania ── */
  .tabs-bar { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .tabs-bar::-webkit-scrollbar { display: none; }
  .tabs-bar .tab { flex: none; }

  /* ── Tom Select: strzałka rozwijania zabiera miejsce na dotyku ── */
  .ts-control { background-image: none !important; }
  .ts-wrapper .ts-control:not(.rtl) { padding-right: 12px !important; }

  /* Tom Select rysuje wlasna kontrolke zamiast <select>, wiec regula
     `.input, .form-input, .select` wyzej w tym bloku jej nie dotyka. Bazowa
     `.ts-control` (sekcja Tom Select) niesie `height: 34px !important` i kroj
     13 px - !important z arkusza wygrywa z kazda regula bez niego, takze tutaj.
     Stad te same wartosci drugi raz, z !important. Listy wielokrotnego wyboru
     zostaja nietkniete: `.ts-wrapper.multi .ts-control` ma 0,3,0 i rosnie sama. */
  .ts-control { height: 44px !important; min-height: 44px !important; font-size: 16px !important; }
  .ts-control > .item,
  .ts-control .placeholder { font-size: 16px !important; }

  /* Wiersz listy rozwijanej: 8 px odstepu i 13 px kroju dawaly cel okolo 34 px. */
  .ts-dropdown .option { min-height: 44px !important; display: flex !important; align-items: center !important; font-size: 16px !important; }

  /* ── Tom Select: rozmiar kontrolki i listy ──
     Blok wyżej podnosi `.input`, `.form-input` i `.select` - a 97 pól panelu
     NIE jest żadnym z nich. Tom Select podmienia `<select>` na `.ts-control`,
     który ma na sztywno 34 px i krój 13 px (sekcja Tom Select, ok. w. 4336),
     czyli 10 px poniżej progu WCAG 2.5.8 i 3 px poniżej granicy, od której
     iOS Safari przestaje zoomować stronę. Wiersz rozwiniętej listy mierzy
     33 px, a krzyżyk zdejmujący tag 14x14 px.
     `!important` jest tu konieczny - reguła bazowa też go niesie, bo odbija
     `!important` vendora. */
  .ts-control,
  .ts-wrapper.multi .ts-control { min-height: 44px !important; height: auto !important; font-size: 16px !important; }
  /* `height: auto` wyzej jest dla list WIELOKROTNYCH (tagi rosna w wiersze).
     Wybor pojedynczy zlapal je przy okazji i zawijal dluga wartosc na dwie
     linie - stad rozne wysokosci trzech filtrow w jednym pasku. */
  .ts-wrapper.single .ts-control { height: 44px !important; }
  .ts-control > .item,
  .ts-control .placeholder { font-size: 16px !important; }
  .ts-dropdown .option,
  .ts-dropdown .create {
    min-height: 44px !important; display: flex !important;
    align-items: center !important; font-size: 16px !important;
  }

  /* Pole wpisywania listy wielokrotnej JEST widoczne i przyjmuje tekst -
     poniżej 16 px iOS zoomuje całą stronę przy wejściu w nie i nie cofa zoomu. */
  .ts-wrapper.multi .ts-control > input { height: 32px !important; line-height: 32px !important; font-size: 16px !important; }
  .ts-wrapper.multi .ts-control > .item { height: 32px !important; line-height: 30px !important; }
  .ts-wrapper.multi.plugin-remove_button .item .remove { width: 24px !important; height: 24px !important; font-size: 14px !important; }

  /* ── Quill: pasek narzędzi ──
     Vendor daje przyciskom 28x24 px i sadza je na `float: left` BEZ odstępu.
     Największy pasek (regulamin, aktualności, ogłoszenia platformy) to
     12 przycisków i 2 listy, czyli ~546 px kontrolek przy ~318 px, które
     zostają z ekranu 390 px po odjęciu marginesów strony i panelu.
     Pasek zawija się już dziś (`.ql-formats` jest `inline-block`); tutaj
     dostaje jawne wiersze i cel 36 px - tyle, ile blok wyżej przyznaje
     `.btn-sm`, i o 12 px powyżej progu WCAG.
     ŚWIADOMIE BEZ `overflow-x: auto`: pasek przewijany w poziomie obcinałby
     rozwinięcie listy „Tekst / Nagłówek § / Podsekcja", bo
     `.ql-picker-options` jest `position: absolute` w jego środku - jeden
     problem zamieniłby się w drugi, gorszy.
     ŚWIADOMIE BEZ SKRACANIA ZESTAWU: `ql-align-*` i `ql-indent-*` mają porty
     w `ClientPortal/assets/terms.css:131-142`, czyli są formatami żywymi,
     które klient realnie widzi w regulaminie. */
  .ql-toolbar.ql-snow { display: flex; flex-wrap: wrap; align-items: center; row-gap: 4px; padding: 6px 8px; }
  .ql-toolbar.ql-snow::after { display: none; }
  .ql-toolbar.ql-snow .ql-formats { display: flex; align-items: center; margin-right: 10px; }
  .ql-snow.ql-toolbar button,
  .ql-snow .ql-toolbar button { width: 36px; height: 36px; padding: 7px; float: none; }
  .ql-toolbar.ql-snow .ql-picker { height: 36px; }
  .ql-toolbar.ql-snow .ql-picker-label::before { line-height: 34px; }
  .ql-snow .ql-picker-options .ql-picker-item { padding: 10px 8px; }

  /* ── Flatpickr: dzień kalendarza, nawigacja miesiąca, pole godziny ──
     Dzień ma 39 px, strzałki miesiąca 34 px. Wysokość pola godziny rośnie do
     84 px, bo strzałki krok-w-górę/w-dół odsłaniane hoverem (blok
     `@media (hover: none)` niżej) potrzebują 26 px zamiast 20. */
  .flatpickr-day { height: 44px !important; line-height: 44px !important; font-size: var(--fs-nav) !important; }
  .flatpickr-prev-month,
  .flatpickr-next-month { height: 44px !important; width: 44px !important; justify-content: center !important; }
  .flatpickr-time { height: 84px !important; max-height: 84px !important; }
  .flatpickr-time .numInputWrapper { height: 84px !important; }
  .flatpickr-time .arrowUp,
  .flatpickr-time .arrowDown { height: 26px !important; flex: 0 0 26px !important; }
  .flatpickr-time .flatpickr-time-separator,
  .flatpickr-time .flatpickr-am-pm { height: 84px !important; line-height: 84px !important; }

  /* ── Pigułki wyboru i chipy ──
     `.radio-pill` (padding 6/12 przy kroju 13 px) mierzy ~33 px, `.tag-chip`
     ~30 px. Oba są kontrolkami wyboru, nie ozdobą - `.badge` zostaje mały,
     bo jest wyłącznie napisem. */
  .radio-pill { min-height: 44px; padding: 0 16px; font-size: var(--fs-base); }
  .tag-chip   { min-height: 40px; padding: 0 16px; font-size: var(--fs-nav); }

  /* ── Przełączniki ──
     `.toggle-track` to 38x22 px, `.toggle-mini` 34x20 px - ten drugi siedzi
     41 razy w JEDNEJ tabeli (Ustawienia > Powiadomienia, trzy kanały na
     wiersz). Rosną obie części, tor i gałka: powiększony sam obszar kliknięcia
     nie mówi użytkownikowi, gdzie ma trafić. */
  .toggle-track { width: 52px; height: 30px; }
  .toggle-track::after { width: 24px; height: 24px; top: 3px; left: 3px; }
  input.toggle:checked + .toggle-track::after { transform: translateX(22px); }

  .toggle-mini { width: 46px; height: 26px; }
  .toggle-mini .toggle-slider::after { width: 20px; height: 20px; top: 3px; left: 3px; }
  .toggle-mini input:checked + .toggle-slider::after { transform: translateX(20px); }

  /* ── Zdjęcie: krzyżyk zdejmujący podgląd miał 26x26 px, a kasuje wybór ── */
  .upload-remove { width: 40px; height: 40px; top: -10px; right: -10px; font-size: var(--fs-md); }
}

/* ── DOMKNIĘCIE OSI WSKAŹNIKA (etap 7) ────────────────────────────────────
   Trzy klasy, przez które blok wyżej nie przechodził. Wszystkie mają
   specyficzność albo `!important` wyższe od `.input`, więc reguła 44/16
   z sekcji 25a je omijała - i omijała cicho. */

@media (pointer: coarse) {

  /* 1. POLA DATY I GODZINY - najszerszy przypadek, ok. 44 pola.
     `.flatpickr-input` ma `height: 34px !important` i `font-size` z tokenu
     `--fs-nav` (13 px), też z `!important`. Kluczowe jest to, CO SIĘ DZIEJE
     NA TELEFONIE: `main.js` przełącza flatpickr w tryb mobilny
     (`disableMobile: matchMedia('(pointer: fine)')`), a biblioteka tworzy
     wtedy natywny `<input type="date">` z klasą skopiowaną z oryginału -
     czyli razem z `flatpickr-input`. Natywne pole dziedziczy więc 34 px
     i 13 px, i iOS zoomuje przy każdym dotknięciu daty.
     `.flatpickr-mobile` nie ma w projekcie ANI JEDNEJ reguły (grep: 0). */
  .flatpickr-input,
  .flatpickr-mobile {
    height: 44px !important;
    font-size: 16px !important;
  }

  /* 2. POLE W GRUPIE Z PRZYCISKIEM (waluta, jednostka, przyrostek).
     `.input-group { height: 34px }` plus `.input-group .input { height: 100% }`
     (0-2-0) bije regułę z sekcji 25a. Bez podniesienia PUDEŁKA samo
     podniesienie kroju do 16 px zaczęłoby PRZYCINAĆ tekst - poprawka
     mobilna pogorszyłaby wygląd. Dotyczy ok. 21 pól w Karnetach, Rabatach
     i Ustawieniach. */
  .input-group { height: 44px; }
  .input-group .input { font-size: 16px; }

  /* 4. WYSZUKIWARKI LIST I TOPBARU. `.input-search input` (0-1-1) ma krój
        `--fs-nav` (13 px) i reguła `.input` z sekcji 25a go nie dotyczy -
        iOS przybliżał stronę przy każdym dotknięciu szukajki (BUG-mobA-07,
        adm2-12). Pole wpisywania Tom Select dziedziczy 16 px po `.ts-control`. */
  .input-search input { font-size: 16px; }
  .ts-control input   { font-size: 16px !important; }

  /* 3. Pole liczbowe o stałej szerokości. 72 px przy kroju 16 px mieści
        trzy cyfry - dokładamy zapas, bo limity bywają czterocyfrowe. */
  .num-input { width: 88px; }
}


/* AKCJE ODSŁANIANE HOVEREM - naprawiane W MODUŁACH, nie tutaj.
 *
 * Audyt naliczył szesnaście akcji dostępnych wyłącznie przez najechanie
 * kursorem: usunięcie zdjęcia profilowego, oznaczenie powiadomienia jako
 * przeczytanego, skasowanie powiadomienia, usunięcie slotu dostępności,
 * podpowiedź z opisem zajęć w grafiku. Na ekranie dotykowym hover nie
 * istnieje, więc te przyciski nie są schowane - one tam nie istnieją.
 *
 * Poprawka nie może stać w tym pliku z dwóch powodów. Po pierwsze wszystkie
 * te klasy mają prefiks `.pg-`, czyli z definicji należą do podstrony
 * (konwencja projektu). Po drugie arkusze modułów ładują się PO tym pliku,
 * więc globalna reguła o równej specyficzności i tak by przegrała - musiałaby
 * nieść `!important`, czyli dług, który ktoś kiedyś odziedziczy.
 *
 * Każdy moduł dostaje własny blok `@media (hover: none)` obok reguły, którą
 * odwraca. Pytanie brzmi „czy hover w ogóle istnieje", nie „jak wąski jest
 * ekran" - to samo lekarstwo obejmuje więc tablet i telefon, a myszy nie
 * dotyka. */


/* ================================================================
   25b. MODAL NA WĄSKIM I NISKIM EKRANIE
   (plan: wyglad-mobilny.md, etap 2)

   Stan przed tą sekcją: `.overlay` centruje flexem przy `inset: 0` i NIE MA
   `overflow`, a `.modal` ma `overflow: hidden` i ZERO `max-height`. Treść
   wyższa od okna była ucinana symetrycznie z góry i z dołu, więc poza kadrem
   znajdowały się JEDNOCZEŚNIE nagłówek z krzyżykiem i stopka z przyciskami,
   a `overflow: hidden` odbierał ostatnią drogę - przewinięcie wnętrza.
   Okno, którego nie da się ani obsłużyć, ani zamknąć.

   `.modal-scrollable .modal-body { max-height: 40vh }` ratowało 9 z 60
   wywołań `modal.open()`. Zmierzony przykład: modal podglądu zajęć
   (`calendar.js:424`) ma 490-550 px przy 548 px widocznego kadru iPhone'a SE.

   Zasięg reguł: do 1024 px włącznie, czyli także iPad w pionie - tam mieści
   się mniej, niż sugeruje szerokość. Układ na dużym ekranie zostaje
   nietknięty; wysoki modal na niskim laptopie to osobna sprawa i osobna
   decyzja, bo dotyka wersji, która jest dopracowana.
   ================================================================ */

@media (max-width: 1024px) {

  /* Nakładka przewija się sama i przestaje centrować w pionie: przy treści
     wyższej od okna centrowanie flexem wypycha OBA końce poza kadr, i to
     bez możliwości dojechania - `align-items: center` nie pozwala przewinąć
     do przeciętego górnego brzegu. */
  .overlay {
    align-items: flex-start;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Dolne wypełnienie o wysokość klawiatury. Nakładka przewija się sama,
       więc zapas na jej dole jest JEDYNYM miejscem, z którego modal ma
       z czego zejść w górę - `margin` na modalu zjadłoby centrowanie,
       a `bottom` nie działa przy `align-items`. */
    padding: 16px 0 calc(16px + var(--kb-inset, 0px));
  }

  /* dvh, nie vh: na mobilnym Safari `100vh` to wysokość BEZ paska adresu,
     więc modal liczony w vh wystaje dokładnie o jego wysokość. */
  .modal {
    /* `100dvh` mierzy do dolnej krawędzi EKRANU, a nie do kadru nad
       klawiaturą - iOS nie zmniejsza layout viewportu. Bez odjęcia
       `--kb-inset` modal dostaje sufit wyższy od widocznego pola i jego
       nagłówek z krzyżykiem wychodzi poza kadr - czyli wraca dokładnie
       ta awaria, którą naprawił etap 2, tylko wywołana klawiaturą. */
    max-height: calc(100dvh - 32px - var(--kb-inset, 0px));
    display: flex;
    flex-direction: column;
    margin: auto 0;
  }

  /* Nagłówek i stopka trzymają wysokość, przewija się środek. Bez tego flex
     ściska nagłówek i tytuł znika, zamiast przewinąć treść. */
  .modal-header,
  .modal-footer { flex-shrink: 0; }

  /* Przewijanie wnętrza BEZ warunku `.modal-scrollable`. Ta klasa jest
     deklaracją autora modala, a przekroczenie ekranu jest faktem, którego
     autor nie zna - zależy od urządzenia, nie od treści. */
  .modal-body {
    overflow-y: auto;
    overscroll-behavior: contain;
    min-height: 0;
  }

  /* Wariant „przewijalny" miał sufit 40vh liczony od okna. Wewnątrz modala,
     który ma już własny sufit, ten drugi zabiera połowę dostępnej wysokości
     i zostawia pustkę pod treścią. */
  .modal-scrollable .modal-body { max-height: none; }
}

@media (max-width: 768px) {

  /* Arkusz dolny zamiast okienka na środku: pełna szerokość, przyklejony do
     dolnej krawędzi, zaokrąglony tylko u góry. Powód nie jest estetyczny -
     przyciski stopki lądują w zasięgu kciuka, a nie na środku ekranu.
     Wszystkie warianty rozmiaru (.modal-sm ... .modal-2xl) mają na telefonie
     tę samą szerokość, bo szerokość ekranu jest tu jedynym ograniczeniem. */
  /* Arkusz dolny stoi na `align-items: flex-end`, więc wypełnienie od dołu
     przesuwa go w górę dokładnie o wysokość klawiatury - razem ze stopką,
     którą etap 2 celowo przykleił do dolnej krawędzi. Bez tego etap 2
     przeniósł przyciski dokładnie tam, gdzie ląduje klawiatura. */
  .overlay { align-items: flex-end; padding: 0 0 var(--kb-inset, 0px); }

  .modal,
  .modal-sm,
  .modal-lg,
  .modal-xl,
  .modal-2xl {
    width: 100%;
    max-width: none;
    margin: 0;
    border-radius: var(--radius-lg, 12px) var(--radius-lg, 12px) 0 0;
    border-bottom: 0;
    /* jw. - sufit arkusza liczy się od kadru NAD klawiaturą */
    max-height: calc(100dvh - 24px - var(--kb-inset, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }

  /* Stopka przykleja się do dołu arkusza - przy długiej treści przyciski
     zostają widoczne, zamiast czekać na końcu przewijania. */
  .modal-footer {
    position: sticky;
    bottom: 0;
    background: var(--surface);
  }

  /* Przyciski stopki na całą szerokość i w kolejności odwróconej: akcja
     główna wypada wtedy niżej, czyli bliżej kciuka.
     Tylko `.modal-footer` - `.modal-actions` istnieje w arkuszu, ale w całym
     markupie i JS nie występuje ani razu (grep: 0 w `modules`, `views`
     i `public/assets/js`). Stopkę składa `_footerHtml()` w main.js i zawsze
     wypisuje `.modal-footer`. */
  .modal-footer {
    flex-direction: column-reverse;
    align-items: stretch;
  }

  .modal-footer .btn { width: 100%; justify-content: center; min-height: 44px; }

  /* Pole potwierdzenia (wpisz USUŃ) stało poza regułą `.input`, więc miało
     34 px wysokości i font poniżej 16 px - iOS Safari zoomował stronę przy
     każdym potwierdzeniu usunięcia. */
  .modal-confirm-input { height: 44px; font-size: 16px; }
}


/* ================================================================
   26. GLOBAL SELECTION DISABLE
   ================================================================ */

/*
*, *::before, *::after {
  user-select: none;
  -webkit-user-select: none;
}
*/

input, textarea, [contenteditable="true"] {
  user-select: text;
  -webkit-user-select: text;
}

::selection {
  background: color-mix(in srgb, var(--color-primary) 25%, transparent);
  color: inherit;
}

input::selection, textarea::selection, [contenteditable="true"]::selection {
  background: var(--color-primary);
  color: var(--surface);
}

/* ================================================================
   52. QUILL RICH TEXT EDITOR
   ================================================================ */
.flowla-quill { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface); transition: border-color .18s, box-shadow .18s; }
.flowla-quill:focus-within { border-color: var(--teal); box-shadow: 0 0 0 3px var(--accent-dim); }
.flowla-quill.has-error { border-color: var(--danger); }
.ql-toolbar.ql-snow { border: none !important; border-bottom: 1px solid var(--border) !important; background: var(--bg); font-family: 'Inter', sans-serif; padding: 8px 10px; }
.ql-container.ql-snow { border: none !important; font-family: 'Inter', sans-serif; font-size: var(--fs-nav); }
.ql-editor { min-height: 220px; padding: 14px 16px; line-height: 1.75; color: var(--text-primary); outline: none !important; }
.ql-editor:focus { outline: none !important; box-shadow: none !important; border: none !important; }
.ql-container.ql-snow:focus-within { outline: none !important; box-shadow: none !important; }
.ql-editor.ql-blank::before { color: var(--text-secondary); font-style: normal; }
.ql-editor h2 { font-size: var(--fs-md); font-weight: 700; margin: 20px 0 8px; color: var(--text-primary); }
.ql-editor h3 { font-size: var(--fs-base); font-weight: 600; margin: 16px 0 6px; }
.ql-editor p { margin: 0 0 10px; }
.ql-editor ol, .ql-editor ul { padding-left: 22px; margin: 0 0 10px; }
.ql-editor li { margin-bottom: 4px; }
/* Vendor daje .ql-snow a { color: #06c } - link w edytorze ma wygladac jak po publikacji (.pg-faq-answer a) */
.ql-snow .ql-editor a { color: var(--color-primary); text-decoration: underline; text-underline-offset: 2px; }
.ql-snow .ql-stroke { stroke: var(--text-secondary); }
.ql-snow .ql-fill, .ql-snow .ql-stroke.ql-fill { fill: var(--text-secondary); }
.ql-snow .ql-picker { color: var(--text-secondary); }
.ql-snow .ql-picker-label { border-color: transparent !important; }
.ql-snow .ql-picker.ql-header { width: 120px; }
.ql-snow button:hover .ql-stroke,
.ql-snow button.ql-active .ql-stroke,
.ql-snow .ql-picker-label:hover .ql-stroke,
.ql-snow .ql-picker-label.ql-active .ql-stroke,
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke { stroke: var(--teal) !important; }
.ql-snow button:hover .ql-fill,
.ql-snow button.ql-active .ql-fill,
.ql-snow button:hover .ql-stroke.ql-fill,
.ql-snow button.ql-active .ql-stroke.ql-fill { fill: var(--teal) !important; }
.ql-snow button:hover, .ql-snow button.ql-active,
.ql-snow .ql-picker-label:hover, .ql-snow .ql-picker-label.ql-active,
.ql-snow .ql-picker-item:hover, .ql-snow .ql-picker-item.ql-selected,
.ql-snow .ql-picker.ql-expanded .ql-picker-label { color: var(--teal) !important; }
.ql-snow .ql-picker-label:hover::before, .ql-snow .ql-picker-label.ql-active::before,
.ql-snow .ql-picker-item:hover::before, .ql-snow .ql-picker-item.ql-selected::before { color: var(--teal) !important; }
.ql-snow .ql-picker-options { background: var(--surface); border-color: var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
.ql-snow .ql-tooltip { background: var(--surface); border-color: var(--border); color: var(--text-primary); box-shadow: var(--shadow); border-radius: var(--radius); }
.ql-snow .ql-tooltip a { color: var(--teal) !important; }
.ql-snow .ql-tooltip input[type="text"] { background: var(--bg); border-color: var(--border); color: var(--text-primary); border-radius: var(--radius-sm); }
.ql-snow .ql-tooltip::before { content: 'Odwiedź:' !important; }
.ql-snow .ql-tooltip[data-mode="link"]::before { content: 'Wpisz URL:' !important; }
.ql-snow .ql-tooltip a.ql-action::after { content: 'Edytuj' !important; }
.ql-snow .ql-tooltip.ql-editing a.ql-action::after { content: 'Zapisz' !important; }
.ql-snow .ql-tooltip a.ql-remove::before { content: 'Usuń' !important; }
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before { content: 'Tekst'; }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { content: 'Nagłówek §'; }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { content: 'Podsekcja'; }


/* AKCJA ODSLANIANA HOVEREM - na dotyku hover NIE ISTNIEJE, wiec przycisk
   z `opacity: 0` nie jest tam schowany, tylko nie istnieje. Pytanie brzmi
   "czy hover w ogole jest", nie "jak waski jest ekran": to samo lekarstwo
   obejmuje tablet i telefon, a myszy nie dotyka.
   Wzorzec przeniesiony z `Calendar/assets/calendar.css:1035`, gdzie kosz przy
   notatce byl JEDYNYM miejscem w projekcie zrobionym poprawnie.
   (plan: wyglad-mobilny.md, etap 4) */
@media (hover: none) {
  .avatar-overlay,
  .login-thumb-overlay { opacity: 1; }

  /* Strzałki krok-w-górę / krok-w-dół przy godzinie stoją na `opacity: 0`
     i wychodzą dopiero z `.numInputWrapper:hover` (sekcja flatpickr, w. 4776
     i 4862). Na dotyku nie ma czego najechać, więc z pola godziny zostaje samo
     wpisywanie z klawiatury - a przez to pole przechodzi każde ustawienie
     terminu: zajęcia, wizyta 1:1, okno zapisów, dostępność trenera.
     Geometria (26 px zamiast 20, pole 84 px zamiast 72) siedzi w bloku
     `(pointer: coarse)` w sekcji 25a - tu zostaje samo odsłonięcie. */
  .flatpickr-time .arrowUp,
  .flatpickr-time .arrowDown { opacity: 1 !important; }
}


/* ================================================================
   27. WYDRUK - JEDEN BLOK NA CAŁY PANEL
   (plan: wyglad-mobilny.md, etap 8)

   DLACZEGO WYDRUK NALEŻY DO ETAPU MOBILNEGO. Strona A4 minus marginesy
   ma około 670 px szerokości CSS, więc KAŻDA reguła napisana dla telefonu
   łapie się także na papierze. Mechanizm jest opisany wprost w
   `modules/ClientPortal/assets/terms.css`. Po etapach 1-7 znaczy to trzy
   konkretne awarie wydruku, których wcześniej nie było:
     - dolna nawigacja (etap 6) drukuje się na każdej stronie panelu,
     - tryb kart tabel (etap 5) zamienia listę klientów w słupek kartek,
     - modal (etap 2) wychodzi na papier jako arkusz dolny pełnej szerokości.

   Do tej pory `@media print` istniał w czterech miejscach lokalnych
   (Clients, Settings x2, ClientPortal/terms) i każde wypisywało własną
   listę chromu do ukrycia. Ten blok jest podstawą: moduł dokłada do niego
   wyłącznie to, co ma własnego (własny nagłówek dokumentu, węższy krój,
   ukrycie swoich narzędzi redakcji).

   BLOK STOI NA KOŃCU PLIKU CELOWO. Sekcje 25, 25a i 25b są wyżej, a przy
   równej specyficzności rozstrzyga kolejność źródłowa - dzięki temu
   praktycznie cały blok obywa się bez `!important`.

   PREFIKS `body ` PRZY KLASACH MODUŁÓW NIE JEST OZDOBĄ. `.imp-bar`
   i `.pa-bar` renderują się jako bloki <style> w swoich partialach, czyli
   w <body> - PO tym arkuszu. `.hm-*` (help-mode.css) i `.pwa-invite`
   (install-prompt.css) to assety modułów, ładowane też po nim. Reguła
   `.imp-bar { display: none }` ma specyficzność 0-1-0, dokładnie tyle samo
   co `.imp-bar { display: flex }` w partialu - i przegrałaby kolejnością,
   wyglądając na działającą tylko w narzędziach dewelopera.
   `body .imp-bar` to 0-1-1 i wygrywa niezależnie od kolejności.
   ================================================================ */

@media print {

  @page { margin: 14mm; }

  /* Papier jest biały także wtedy, gdy panel działa w ciemnym motywie.
     Motyw to zestaw ZMIENNYCH na <html>, więc wystarczy zawrócić same
     tokeny - nie ma potrzeby dotykać ani jednej reguły kolorystycznej
     w reszcie arkusza.
     Trzeci selektor nie jest nadmiarowy: `:root:not([data-theme="light"])`
     z bloku `prefers-color-scheme` (w. 281) ma specyficzność 0-2-0, czyli
     WYŻSZĄ niż samo `:root`. Bez powtórzenia go tutaj wydruk z komputera
     bez JS i z ciemnym motywem systemu wychodziłby czarny. */
  :root,
  [data-theme="dark"],
  :root:not([data-theme="light"]) {
    --bg:             #f9f9f9;
    --surface:        #ffffff;
    --border:         #e7eaee;
    --text-primary:   #1f2937;
    --text-secondary: #4b5563;
    --text-muted:     #6b7480;
    --light-gray:     #f2f3f5;
    --light-blue:     #d9e8f6;
    --shadow-xs: none; --shadow-sm: none; --shadow:    none;
    --shadow-md: none; --shadow-lg: none; --shadow-xl: none;
  }

  html, body {
    background: #fff;
    /* Odznaki stanu, mapa obłożenia sal i paski postępu NIOSĄ treść w tle.
       Bez tego przeglądarka wypiera tła domyślnie i z mapy zostaje sama
       siatka pustych kwadratów. */
    -webkit-print-color-adjust: exact;
            print-color-adjust: exact;
  }

  /* Blokada przewijania modala zostawiała na <body> `position: fixed`,
     czyli JEDNĄ stronę wydruku i uciętą resztę dokumentu. */
  body.scroll-locked { position: static; overflow: visible; }

  /* ── 1. CHROM APLIKACJI ──
     Wszystko, czego na papierze nie da się kliknąć. */
  body .sidebar,
  body .sidebar-overlay,
  body .topbar,
  body .mobile-bottom-nav,
  body .mobile-search-overlay,
  body .skip-link,
  body .toast-container,
  body .action-bar,
  body .settings-save-bar,
  body .dots-menu,
  body .dropdown-menu,
  body .tbl-sortbar,
  body .imp-bar,
  body .pa-bar,
  body .hm-bar,
  body .hm-backdrop,
  body .hm-bubble,
  body .pwa-invite { display: none; }

  /* Treść na całą szerokość strony. Selektory 0-2-1 - tyle samo, co
     `body.admin-panel .main` w bloku <=768px; ten blok stoi niżej, więc
     wygrywa kolejnością. Samo `.main` (0-1-0) PRZEGRAŁOBY i treść
     zostałaby wcięta o 260 px nieistniejącego sidebara - bez żadnego
     błędu w konsoli, po prostu z pustym pasem na lewej krawędzi kartki. */
  body.admin-panel .main,
  body.owner-panel .main,
  body.client-panel .main,
  body.sidebar-collapsed .main,
  body .main {
    margin: 0;
    padding: 0;
    width: auto;
  }

  /* ── 2. TABELA ZOSTAJE TABELĄ ──
     Tryb kart (sekcja 8a) siedzi W CAŁOŚCI wewnątrz `@container fwtbl`.
     Nie przebijamy kilkunastu reguł po kolei - ODBIERAMY KONTENER.
     Bez nazwanego kontenera `fwtbl` w drzewie żadne z tych zapytań nie ma
     się do czego odnieść i cały tryb kart znika naraz, razem z ukrywaniem
     kolumn `.col-hide-md`. Jedna deklaracja zamiast listy nadpisań, która
     rozjeżdżałaby się przy każdej zmianie w sekcji 8a.
     `overflow: visible`, bo `overflow-x: auto` na papierze nie przewija -
     tylko ucina; `mask-image` to sygnał „tabela ma dalszy ciąg", który
     na kartce wyblakłby prawą kolumnę. */
  body .table-scroll-wrap {
    container-type: normal;
    overflow: visible;
    -webkit-mask-image: none;
            mask-image: none;
  }

  /* Kolumna akcji i belka filtrow listy na papierze sa martwe, a wypychaly
     tabele poza szerokosc kartki - `.panel { overflow: hidden }` ucinal
     wtedy prawa krawedz (BUG-mobB-04). DataTables niesie 12 px dopelnienia
     z `!important`; na kartce wystarczy wezsze. */
  body th.is-tbl-actions-th,
  body td.is-tbl-actions,
  body .dots-trigger,
  body .pg-own-filters,
  body .filter-group,
  body .panel-header .panel-left:has(input, select) { display: none; }
  body table.dataTable thead th,
  body table.dataTable tbody td { padding: 6px 8px !important; }

  /* ── 3. MODAL ──
     Zamknięte nakładki NIE MOGĄ wrócić do układu. `.overlay` jest
     `position: fixed`, więc dziś nie zajmuje miejsca, a na jednej stronie
     panelu potrafi ich leżeć kilkanaście. `position: static` bez tego
     warunku wydrukowałby treść każdej z nich, jedna pod drugą. */
  body .overlay:not(.open) { display: none; }

  body .overlay.open {
    position: static;
    display: block;
    background: none;
    padding: 0;
    opacity: 1;
    overflow: visible;
  }

  body .overlay.open .modal,
  body .overlay.open .modal-sm,
  body .overlay.open .modal-lg,
  body .overlay.open .modal-xl,
  body .overlay.open .modal-2xl {
    width: auto;
    max-width: none;
    max-height: none;
    margin: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding-bottom: 0;
    overflow: visible;
  }

  /* Bez tego treść wyższa od arkusza zostaje ucięta na wysokości ekranu -
     `overflow-y: auto` z sekcji 25b na papierze nie ma czym przewijać. */
  body .modal-body { max-height: none; overflow: visible; }

  /* Przyciski i krzyżyk na papierze są martwe, a stopka arkusza dolnego
     zabierałaby całą szerokość strony na dwa nieklikalne prostokąty
     (`flex-direction: column-reverse` z sekcji 25b). */
  body .modal-footer,
  body .modal-close { display: none; }

  /* ── 4. DROBIAZGI ──
     Wiersz nie dzieli się między strony, a nagłówek tabeli wraca na
     każdej kolejnej. Świadomie BEZ `break-inside: avoid` na `.panel`:
     panel dłuższy od strony potrafi wtedy zostawić całą pustą kartkę. */
  tr     { break-inside: avoid; }
  thead  { display: table-header-group; }

  /* Podział strony przed sekcją, która sama o to poprosi w widoku. */
  .print-break { break-before: page; }

  /* Odsłonięte adresy linków to szum: panel linkuje wewnętrznie,
     a `/admin/clients/1421` nic czytelnikowi nie mówi. */
  a { text-decoration: none; color: inherit; }
}
