* {
    box-sizing: border-box;
}

/* Motyw jasny/ciemny (patrz admin/settings.php) — globalne ustawienie
   aplikacji, przełączane klasą .theme-dark na <body> (patrz layout_top.php).
   Zamiast przemalowywać każdy kolor z osobna, kluczowe powierzchnie (tło
   strony, karty/panele, obramowania, hover) korzystają z tych zmiennych —
   pastelowe kolory projektów i kolory semantyczne (błąd/ostrzeżenie) celowo
   zostają bez zmian w obu motywach, są czytelne na obu tłach. */
:root {
    --wt-bg: #f4f5f7;
    --wt-surface: #ffffff;
    --wt-surface-hover: #f0f1f3;
    --wt-selected-bg: #eef1f6;
    --wt-selected-border: #d5dae2;
    --wt-text: #1c1e21;
    --wt-border: #e5e5e5;

    /* Kolory 4 typów komunikatów (patrz .notice niżej) — tak jak
       błąd/ostrzeżenie już wcześniej (patrz komentarz nad tym blokiem),
       CELOWO bez zmiany w trybie ciemnym: te same pastele są czytelne na
       obu tłach, i tak jest już spójnie z resztą kolorów semantycznych. */
    --wt-notice-warning-bg: #fdecea;
    --wt-notice-warning-border: #f5c6c2;
    --wt-notice-warning-text: #7a1710;
    --wt-notice-question-bg: #fff4e0;
    --wt-notice-question-border: #ffd699;
    --wt-notice-question-text: #8a5a00;
    --wt-notice-success-bg: #e6f4ea;
    --wt-notice-success-border: #b7dfc3;
    --wt-notice-success-text: #1b7f3a;
}

body.theme-dark {
    --wt-bg: #24262b;
    --wt-surface: #2f3237;
    --wt-surface-hover: #3a3d43;
    --wt-selected-bg: #33404c;
    --wt-selected-border: #47576a;
    --wt-text: #e8e9eb;
    --wt-border: #43464c;
    /* Włącza natywne ciemne stylowanie przeglądarki (scrollbary, checkboxy,
       kontrolki date/select) — spójniejsze niż zostawienie ich jasnych na
       ciemnym tle, bez potrzeby ręcznego przemalowywania każdej z nich. */
    color-scheme: dark;
}

body {
    margin: 0;
    font-family: 'Epilogue', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    background: var(--wt-bg);
    color: var(--wt-text);
}

button, input, select, textarea {
    font-family: inherit;
}

/* Przygaszony placeholder wszędzie (na wyraźne życzenie) — ten sam idiom co
   już istniejące, węższe reguły (.task-description-textarea/.tasks-add-input
   ::placeholder): kolor tekstu strony, nie domyślny szary przeglądarki,
   przyciemniony przez opacity — theme-aware bez osobnej wartości na jasny/
   ciemny motyw. */
input::placeholder,
textarea::placeholder {
    color: var(--wt-text);
    opacity: 0.4;
}

/* Duży promień (pigułka), na wyraźne życzenie ("jak w Google") — spójny dla
   WSZYSTKICH zwykłych przycisków tekstowych w całej aplikacji (Zapisz/Dodaj/
   Usuń/Anuluj/Zakończ/Przywróć/Zaloguj się/...), bo .danger-button/
   .secondary-button dziedziczą stąd border-radius, nadpisując tylko kolor.
   Osobne, małe przyciski-ikony (.icon-btn/.hint-toggle/.sidebar-toggle/
   .logout-button) mają WŁASNY, mniejszy border-radius (są kwadratowe/okrągłe,
   nie pigułki z tekstem) — te reguły klasowe wygrywają z tą niżej. */
button {
    padding: 0.6rem 1.25rem;
    font-size: 1rem;
    border: none;
    border-radius: 999px;
    background: #2f6feb;
    color: #fff;
    cursor: pointer;
}

button:hover {
    background: #2559c2;
}

.login-page {
    max-width: 360px;
    margin: 10vh auto 0;
    padding: 2rem;
    background: var(--wt-surface);
    color: var(--wt-text);
    border-radius: 8px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

.login-page h1 {
    margin-top: 0;
    font-size: 1.4rem;
    text-align: center;
}

.login-form {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.login-form label {
    font-size: 0.9rem;
    font-weight: 600;
}

/* Ten sam wygląd co pola w Ustawieniach (.settings-select/-number-input/
   -text-input) — na wyraźne życzenie, dla każdego pola formularza na tle
   strony (nie na kolorowym tle, jak edycja projektu na Pulpicie — tamto
   zostaje bez zmian). */
.login-form input:not([type="checkbox"]) {
    padding: 0.4rem 0.6rem;
    font-size: 0.85rem;
    border: 1px solid var(--wt-selected-border);
    border-radius: 6px;
    background: var(--wt-bg);
    color: var(--wt-text);
}

.login-form button {
    margin-top: 0.5rem;
}

.login-remember {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-weight: 400;
}

.login-links {
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
    text-align: center;
}

/* Ujednolicony system komunikatów w całej aplikacji — 4 typy (patrz
   src/Notice.php, funkcje wt_notice()/wt_error_notice()/wt_success_notice()
   — jedno miejsce renderujące znacznik, żeby nie duplikować ikon SVG w
   każdym pliku, który komunikat pokazuje). Normalny rozmiar tekstu i
   interlinia (na wyraźne życzenie — wcześniej różne komunikaty miały różne,
   często ściśnięte wartości). .hint/.read-only-notice (niżej, osobne
   miejsca w kodzie) świadomie WYGLĄDAJĄ tak samo jak .notice-info, ale nie
   są przez nią renderowane — .hint ma własną logikę pokazywania/ukrywania
   (patrz .hint:not([hidden]) i layout.js), a .read-only-notice to stały,
   samodzielny baner bez dynamicznej treści. */
.notice {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.75rem 1rem;
    border: 1px solid;
    border-radius: 8px;
    font-size: 0.95rem;
    line-height: 1.5;
    margin: 0 0 1rem;
}

.notice-icon {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    margin-top: 0.15em;
}

.notice-info {
    background: none;
    border-color: var(--wt-border);
    color: var(--wt-text);
}

.notice-warning {
    background: var(--wt-notice-warning-bg);
    border-color: var(--wt-notice-warning-border);
    color: var(--wt-notice-warning-text);
}

.notice-question {
    background: var(--wt-notice-question-bg);
    border-color: var(--wt-notice-question-border);
    color: var(--wt-notice-question-text);
}

.notice-success {
    background: var(--wt-notice-success-bg);
    border-color: var(--wt-notice-success-border);
    color: var(--wt-notice-success-text);
}

/* Obwódka całego okna, sygnalizująca aktywny timer (patrz assets/js/layout.js
   — kolor ustawiany na żywo z koloru aktualnie uruchomionego projektu, na
   KAŻDEJ stronie, nie tylko na Pulpicie). Domyślnie przezroczysta (brak
   aktywnego timera) — JS przełącza tylko border-color, nigdy nie dodaje/nie
   usuwa tego elementu, więc nie ma czym "mrugnąć" przy przejściu między
   stronami. position:fixed + pointer-events:none, żeby nigdy nie przeszkadzała
   w klikaniu w treść pod spodem; z-index wyżej niż cokolwiek w layoucie.
   Grubość przez --active-border-width (ustawiane inline na <body>, patrz
   layout_top.php) — konfigurowalna w Ustawieniach (admin/settings.php,
   active_border_width_px), 3px to tylko fallback. */
/* border-image-slice: 1 zawsze obecne (nieszkodliwe, dopóki border-image-
   source jest domyślnym "none") — działa dopiero, gdy JS (layout.js,
   setActiveBorderColors()) ustawi border-image-source na conic-gradient
   przy 2+ jednocześnie aktywnych projektach (multitasking, tryb "Grupa" w
   Moich ustawieniach) — pojedynczy aktywny nadal używa zwykłego
   border-color, bez zmian względem wcześniejszego zachowania. */
#active-project-border {
    position: fixed;
    inset: 0;
    z-index: 1000;
    border: var(--active-border-width, 3px) solid transparent;
    border-image-slice: 1;
    pointer-events: none;
    transition: border-color 0.2s ease;
}

/* Alarm "brak aktywnego timera" (Moje ustawienia, idle_alarm_enabled) —
   pełnoekranowa nakładka, niewidoczna dopóki JS (layout.js) nie doda klasy
   .is-flashing. Wyżej niż #active-project-border (z-index), bo ma być
   widoczna NAD obwódką, nie pod nią. */
#idle-alarm-flash {
    position: fixed;
    inset: 0;
    z-index: 1001;
    background: transparent;
    pointer-events: none;
}

#idle-alarm-flash.is-flashing {
    animation: idle-alarm-flash 0.4s ease-in-out 3;
}

@keyframes idle-alarm-flash {
    0%, 100% { background: transparent; }
    50% { background: rgba(220, 38, 38, 0.6); }
}

/* .topbar jest teraz POZA .app-layout (rodzeństwo w .page-shell), żeby przy
   zwijaniu/rozwijaniu bocznego menu przesuwał się tylko content — logo i
   reszta górnego paska mają zostać w miejscu, na wyraźne życzenie (dawniej
   .topbar był zagnieżdżony w .app-main, więc jego lewa krawędź — a więc i
   logo — przesuwała się razem z szerokością sidebaru). */
.page-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.app-layout {
    display: flex;
    flex: 1;
    min-height: 0;
}

/* Sidebar był dotąd na sztywno ciemny (#1c1e21 + jasny tekst) w OBU
   motywach — jedyny fragment apki z hardkodowanymi kolorami zamiast
   zmiennych --wt-*, więc w jasnym motywie wyglądał jak obcy, czarny pasek
   na jasnym tle zamiast delikatnie się z nim różnić. Zgłoszone: "w trybie
   jasnym zrób w analogicznym kolorze do trybu ciemnego — bardzo delikatnie
   szare tło". Bazowe reguły niżej to teraz JASNY motyw (--wt-selected-bg —
   ten sam token co inne subtelnie podświetlone tła w apce), a
   body.theme-dark (patrz niżej, przy każdej regule) przywraca DOKŁADNIE
   dotychczasowy ciemny wygląd bez zmian — ciemny motyw był już dobry,
   zmieniamy tylko jasny. */
.sidebar {
    /* --sidebar-width ustawiane przez layout.js na rzeczywistą szerokość
       treści (najdłuższy link) — 220px to tylko fallback, zanim JS się doliczy. */
    width: var(--sidebar-width, 220px);
    flex-shrink: 0;
    background: var(--wt-selected-bg);
    overflow: hidden;
    transition: width 0.25s ease;
    display: flex;
    flex-direction: column;
}

body.theme-dark .sidebar {
    background: #1c1e21;
}

body.sidebar-collapsed .sidebar {
    width: 0;
}

/* Tryb "mini" — sidebar zwężony do samych ikon (bez pełnego chowania jak
   .sidebar-collapsed wyżej), na wyraźne życzenie: przycisk-strzałka na dole
   sidebaru, osobny od przełącznika ☰ w topbarze. Etykiety linków (.nav-link-
   label) i nagłówek sekcji (.nav-section) znikają, zostają same ikony
   wyśrodkowane — nazwa linku dalej dostępna jako natywny tooltip (title=
   na każdym <a>, patrz layout_top.php). */
/* 36px = wysokość .nav-link, jaką i tak już dawał box-sizing:border-box +
   pionowy padding 0.55rem*2 (8.8px*2) + ikona 18px (35.6px, zaokrąglone) —
   szerokość sidebaru w trybie mini ustawiona na TĘ SAMĄ wartość (zamiast
   dawnych 56px), żeby każda pozycja była kwadratem, na wyraźne życzenie. */
body.sidebar-mini .sidebar {
    width: 36px;
}

body.sidebar-mini .nav-link-label,
body.sidebar-mini .nav-section {
    display: none;
}

body.sidebar-mini .nav-link {
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
    gap: 0;
    height: 36px;
}

/* Fix: ikony wyglądały na "ucięte"/przyklejone do lewej krawędzi zamiast
   wyśrodkowane w trybie mini. Przyczyna: `.sidebar nav` ma `width: max-content`
   (potrzebne w PEŁNYM trybie — sidebar ma dopasować się do najdłuższego
   linku, patrz JS-owe --sidebar-width niżej) — bez etykiet (.nav-link-label
   ukryte w mini) max-content zwężał nav do samej szerokości ikony (~18px),
   więc `justify-content: center` na `.nav-link` centrował ikonę w BOX-IE,
   który i tak już był ciasno dopasowany do niej samej — realnie nic nie było
   wyśrodkowane względem PEŁNEJ szerokości sidebaru, tylko przyklejone do
   lewej krawędzi. Wymuszenie pełnej szerokości na nav w trybie mini pozwala
   `.nav-link` (stretch, domyślne align-items kolumnowego flexa) wypełnić
   całą szerokość sidebaru, a `justify-content: center` faktycznie coś
   centrować. */
body.sidebar-mini .sidebar nav {
    width: 100%;
}

.sidebar-mini-toggle {
    margin-top: auto;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 0.75rem 0;
    background: transparent;
    border: none;
    border-top: 1px solid var(--wt-border);
    color: var(--wt-text);
    cursor: pointer;
}

body.theme-dark .sidebar-mini-toggle {
    border-top-color: rgba(255, 255, 255, 0.08);
    color: #d6d8db;
}

.sidebar-mini-toggle:hover {
    background: var(--wt-selected-border);
}

body.theme-dark .sidebar-mini-toggle:hover {
    background: #2c2f33;
}

.sidebar-mini-toggle .icon {
    width: 18px;
    height: 18px;
    opacity: 0.7;
}

.sidebar-mini-toggle .icon-expand {
    display: none;
}

body.sidebar-mini .sidebar-mini-toggle .icon-collapse {
    display: none;
}

body.sidebar-mini .sidebar-mini-toggle .icon-expand {
    display: block;
}

.sidebar nav {
    display: flex;
    flex-direction: column;
    /* Bez górnego paddingu (na wyraźne życzenie: "usuń odstęp przed
       pierwszą pozycją w menu") — dolny zostaje, robi odstęp przed
       #sidebar-mini-toggle na dole. */
    padding: 0 0 0.75rem;
    width: max-content;
}

.sidebar .nav-section {
    padding: 0.9rem 1rem 0.25rem;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #8a8f98;
    white-space: nowrap;
}

.sidebar .nav-link {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.55rem 1rem;
    color: var(--wt-text);
    text-decoration: none;
    font-size: 0.95rem;
    white-space: nowrap;
}

.sidebar .nav-link:hover {
    background: var(--wt-selected-border);
}

.sidebar .nav-link.active {
    background: #2f6feb;
    color: #fff;
}

/* :not(.active) — bez tego ta reguła (ta sama specyficzność co
   .sidebar .nav-link.active wyżej, ale zdefiniowana później) nadpisywałaby
   też biały tekst aktywnej pozycji na szary, mimo niebieskiego tła (zgłoszone
   po weryfikacji zrzutem ekranu: aktywny link w ciemnym motywie wyglądał na
   przygaszony, niebieskie tło było poprawne, ale tekst już nie). */
body.theme-dark .sidebar .nav-link:not(.active) {
    color: #d6d8db;
}

body.theme-dark .sidebar .nav-link:hover:not(.active) {
    background: #2c2f33;
    color: #fff;
}

/* Monochromatyczne, przygaszone względem nazwy (currentColor + opacity —
   podążają za kolorem tekstu w stanie hover/active, tylko stonowane). */
.nav-link-icon {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    opacity: 0.55;
}

.app-main {
    flex: 1;
    min-width: 0;
}

/* Belka "braku aktywności" (patrz layout_top.php/assets/js/layout.js) —
   **Druga iteracja wyglądu** (na wyraźne życzenie, pierwsza wersja: pełne
   pomarańczowe tło, czarny pogrubiony tekst po prawej, POZA .content):
   przezroczyste tło, cienka pomarańczowa obwódka, wyśrodkowany pomarańczowy
   tekst BEZ pogrubienia (cały, łącznie z licznikiem — stąd brak osobnej
   klasy na wagę czcionki, jeden wspólny styl na całą belkę). Kolor stały
   (nie theme-aware, ten sam `#f59e0b` co poprzednio). Padding/font-size
   dopasowane do `.group-accordion-header` ("belka o rozmiarze takim jak
   belka accordionu"), border-radius/margin-bottom jak `.notice` (ten sam,
   już istniejący wzorzec obramowanej belki na górze contentu). [hidden]
   wystarcza do chowania (JS steruje atrybutem, nie klasą) — brak tu żadnej
   reguły, która by go nadpisywała. */
.inactivity-bar {
    margin: 0 0 1rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid #f59e0b;
    border-radius: 8px;
    color: #f59e0b;
    font-size: 1rem;
    font-weight: 400;
    text-align: center;
    position: relative;
}

/* Przycisk "×" — position:absolute, żeby nie przesuwać wyśrodkowanego
   tekstu belki. padding:0/border:none jawnie (bazowa reguła `button` w tym
   pliku ma domyślny padding/border, patrz fix pozycji 143 dla tej samej
   pułapki z .date-picker-day). currentColor, więc automatycznie ten sam
   bursztynowy kolor co reszta belki, bez osobnej wartości. */
.inactivity-bar-dismiss {
    position: absolute;
    top: 50%;
    right: 0.6rem;
    transform: translateY(-50%);
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: currentColor;
    opacity: 0.65;
    font-size: 1.15rem;
    line-height: 1;
    cursor: pointer;
}

.inactivity-bar-dismiss:hover {
    opacity: 1;
    background: rgba(245, 158, 11, 0.15);
}

.sidebar-toggle {
    background: transparent;
    color: var(--wt-text);
    width: 32px;
    height: 32px;
    padding: 0;
    font-size: 1.2rem;
    line-height: 1;
    border-radius: 6px;
}

.sidebar-toggle:hover {
    background: var(--wt-surface-hover);
}

.topbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1.5rem;
    background: var(--wt-surface);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}

.topbar-left {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-right: auto;
    min-width: 0;
}

/* min-width:0 + overflow/ellipsis — bez tego, w bardzo wąskim oknie
   (Pulpit w małym oknie, patrz WorkTrackMacApp), nazwa się nie zawija (jest
   nowrap), tylko wylewa POZA swój skurczony box i wizualnie nakłada się na
   MULTI/nazwę użytkownika (oba pod nią w DOM/paint order). */
.topbar-brand {
    font-weight: 700;
    font-size: 1.15rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

.topbar-brand-short {
    display: none;
}

/* Skrót "WT" zamiast pełnego "WorkTrack" — osobny, szerszy próg niż reszta
   niżej, na wyraźne życzenie (logo ma się skracać wcześniej niż inne
   elementy topbara zaczynają znikać). */
@media (max-width: 800px) {
    .topbar-brand-full {
        display: none;
    }

    .topbar-brand-short {
        display: inline;
    }
}

/* Bardzo wąskie okno (np. WorkTrackMacApp) — mniej istotne elementy
   topbara chowamy, żeby nic się na siebie nie nakładało. "+ Dodaj projekt"
   (teraz kwadratowy przycisk z samym "+", patrz .project-add-button niżej)
   ma WŁASNY, węższy próg (patrz tam) — zajmuje dużo mniej miejsca niż
   dawny tekstowy przycisk, więc nie musi znikać już tutaj. */
@media (max-width: 460px) {
    .date-bar {
        display: none;
    }
}

/* Bardzo wąskie okno — na wyraźne życzenie chowamy CAŁY topbar (logo,
   przełącznik menu, avatar...) i sidebar, żeby zmaksymalizować miejsce na
   treść. Świadomy koszt: poniżej tego progu nie da się już otworzyć menu
   bocznego (przełącznik ☰ żyje w topbarze) ani menu Ustawienia/Wyloguj
   (avatar też w topbarze) — trzeba poszerzyć okno. `.sidebar-collapsed`
   (stan zwinięcia zapamiętany w localStorage, patrz layout.js) i tak nie ma
   tu znaczenia, bo `display: none` działa niezależnie od niego. */
@media (max-width: 450px) {
    .topbar {
        display: none;
    }

    /* Sidebar dalej chowany PONIŻEJ tego progu — ale NIE gdy jest w trybie
       "mini" (pozycja 117): 56px to i tak niemal nic, a to jedyny sposób na
       przełącznik menu skoro cały topbar (razem z ☰) właśnie zniknął — bez
       tego wyjątku zwężenie okna poniżej 450px z włączonym trybem mini
       kasowało nawigację całkowicie, bez żadnego sposobu na jej przywrócenie
       poza ręcznym poszerzeniem okna. Zwykły (pełny) sidebar nadal znika, bo
       zajmuje zbyt dużo miejsca na tak wąskim oknie. */
    .sidebar {
        display: none;
    }

    body.sidebar-mini .sidebar {
        display: flex;
    }
}

/* flex-shrink: 0 — te kontrolki (MULTI/nazwa/wyloguj) mają zostać w pełnym,
   stałym rozmiarze; to .topbar-brand/.date-bar po lewej ma się skurczyć
   pierwszy, nie one. */
.topbar-user {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.9rem;
    flex-shrink: 0;
}

/* Avatar użytkownika w topbarze (kółeczko z inicjałami) zamiast osobnej
   nazwy + przycisku wylogowania — na wyraźne życzenie, "jak w wielu
   narzędziach". Klik rozwija menu (Ustawienia/Wyloguj, patrz layout.js). */
.topbar-avatar-wrap {
    position: relative;
}

/* Szary, w stylu nieaktywnego MULTI (tło = tło topbara, subtelny obrys) —
   na wyraźne życzenie, zamiast niebieskiego akcentu. */
.topbar-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border-radius: 50%;
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    color: var(--wt-text);
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.02em;
}

.topbar-avatar:hover {
    background: var(--wt-surface-hover);
}

.topbar-avatar.active {
    box-shadow: 0 0 0 2px var(--wt-bg), 0 0 0 4px #2f6feb;
}

.topbar-avatar-menu {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    min-width: 160px;
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    padding: 0.35rem;
    z-index: 20;
}

.topbar-avatar-menu-form {
    display: contents;
}

.topbar-avatar-menu-item {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 0.5rem 0.6rem;
    margin: 0;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: var(--wt-text);
    font-size: 0.9rem;
    font-weight: 400;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.topbar-avatar-menu-item:hover {
    background: var(--wt-surface-hover);
}

/* Szybki przełącznik "MULTI" (multitasking_mode) w topbarze, obok nazwy
   użytkownika — na wyraźne życzenie. Domyślnie "w kolorze tła" (to samo tło
   co topbar, --wt-surface — praktycznie niewidoczne wypełnienie) z
   delikatną obwódką (--wt-border, najsubtelniejsza z dostępnych, taka sama
   jak linie w tabelach), żeby nie rzucać się w oczy, dopóki nieaktywny.
   Aktywny (tryb "Grupa"): zielone tło, ten sam odcień co .message (istniejący
   "pozytywny" kolor w aplikacji, nie nowy). Nadpisuje WSZYSTKIE właściwości
   bazowej reguły `button` (pigułka/niebieskie tło/duży padding) — to mały,
   dyskretny przełącznik-odznaka, nie zwykły przycisk akcji. */
/* padding-top < padding-bottom CELOWO (nie pomyłka) — samo align-items:
   center + line-height:1 nie wystarcza przy wersalikach w tej czcionce
   (Epilogue): metryki fontu (ascent/descent w pliku czcionki) nie są
   symetryczne wokół liter, więc "MULTI" i tak wygląda na przesunięte w
   górę mimo poprawnego wyśrodkowania samego boxa. Asymetryczny padding
   fizycznie przesuwa wyśrodkowany flex-item w dół, żeby zrównoważyć ten
   efekt optycznie. */
/* Wysokość + line-height RÓWNE sobie (nie align-items:center na flexie +
   padding, jak w dwóch poprzednich, nieskutecznych próbach) — najbardziej
   niezawodna technika centrowania JEDNEJ linii tekstu w pionie, odporna na
   niesymetryczne metryki czcionki (ascent/descent), które psuły oba
   wcześniejsze podejścia. box-sizing: border-box (globalny reset) liczy
   obwódkę do tej wysokości, nie dokłada jej na wierzch. */
.multitasking-toggle {
    display: inline-block;
    height: 22px;
    line-height: 22px;
    padding: 0 0.6rem;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    border-radius: 999px;
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    color: var(--wt-text);
}

.multitasking-toggle:hover {
    background: var(--wt-surface-hover);
}

.multitasking-toggle.active {
    background: #1b7f3a;
    border-color: #1b7f3a;
    color: #fff;
}

.multitasking-toggle.active:hover {
    background: #166630;
}

.content {
    max-width: 1152px;
    margin: 0;
    padding: 1.5rem;
}

/* Strony, którym ciasno w standardowym .content (na razie: Raport miesiąca —
   siatka 7 kolumn kalendarza, patrz $contentFullWidth w reports/month.php)
   mogą poprosić o pełną szerokość okna zamiast stałego max-width. */
.content-full-width {
    max-width: none;
}

.project-grid {
    display: grid;
    /* min(160px, 100%) — STAŁA szerokość kolumny (nie minmax(...,1fr)), na
       wyraźne życzenie: kafle mają się układać jeden obok drugiego w
       naturalnej szerokości i dopiero gdy zabraknie miejsca, przeskakiwać
       do kolejnego wiersza, bez rozciągania na pełną szerokość wiersza.
       Przy szerokim oknie każda kolumna ma dokładnie 160px (próg obniżony
       z 220px na wyraźne życzenie - przy typowej szerokości telefonu w
       pionie mają się zmieścić 2 kolumny zamiast spadać do jednej; wąski
       kafelek sobie radzi - patrz dwa progi @container przy .project-name
       niżej). Gdy okno jest za wąskie na
       choćby jedną taką kolumnę, próg sam obniża się do 100% szerokości
       kontenera: siatka spada do jednej kolumny i ta kolumna dalej
       swobodnie się kurczy razem z oknem, zamiast wymuszać poziomy scroll.
       To jest DOMYŚLNY, "bez skalowania" wariant (Ustawienia > Pulpit >
       Skalowanie kafli) — wariant "Skalowanie proporcjonalne" nadpisuje tę
       regułę niżej (patrz body.tile-scaling-uniform). */
    grid-template-columns: repeat(auto-fill, min(160px, 100%));
    /* row-gap zmniejszony z 2.25rem — dawny zapas był POTRZEBNY tylko, gdy
       P-nav wystawał POD kafel (patrz .project-hover-actions niżej); w
       obecnym wariancie (nałożony na kafel, bez własnej wysokości w
       przepływie) nie ma już czego rezerwować. */
    gap: 1rem 0.75rem;
}

/* Kafel w całości w kolorze projektu (na wyraźne życzenie — przywrócone po
   krótkiej próbie neutralnego tła z kolorem tylko za nazwą: problem z
   widocznością miał rozwiązać kolorowy, przestronny modal edycji, nie
   przemalowanie kafla, patrz .modal-color-header niżej). --project-color
   ustawiane inline na tym przycisku (patrz project_cell.php) — jeden punkt
   prawdy, który JS-owy wybór koloru (app.js) aktualizuje przez
   style.setProperty(), bez osobnego dotykania znacznika. Ciemny tekst na
   stałe (#20242b) — paleta jest dobrana pod kontrast z ciemnym tekstem
   (patrz Color.php), niezależnie od schematu (poza "Ciemnoszary", patrz
   body.project-colors-dark-gray niżej). */
.project-button {
    padding: 1.25rem 0.75rem;
    font-size: 1rem;
    font-weight: 600;
    background-color: var(--project-color);
    color: #20242b;
    border: none;
    border-radius: 8px;
    min-height: 140px;
    position: relative;
}

.project-button:disabled {
    opacity: 0.75;
    cursor: default;
}

/* Bez tego bazowa reguła `button:hover` (na górze pliku, tło #2559c2) ma
   wyższą specyficzność niż sam `.project-button` (selektor elementu +
   pseudoklasa bije zwykłą klasę) i na hover zalewałaby kafel granatowym
   tłem zamiast koloru projektu (na wyraźne życzenie — bez tego efektu). */
.project-button:hover {
    background-color: var(--project-color);
}

/* Schematy kolorowania "Jasnoszary"/"Ciemnoszary" (patrz admin/settings.php,
   project_color_scheme) — w przeciwieństwie do "Żywe kolory"/"Stonowane",
   które są prawdziwymi paletami przydzielanymi per projekt tylko przy
   tworzeniu/wznawianiu (patrz Color.php), te dwa są płaskie i celowo
   nadpisują też już istniejące projekty, więc przemalowują się natychmiast
   po zmianie ustawienia — stąd override tła z !important. */
body.project-colors-light-gray .project-button {
    background-color: #dcdfe3 !important;
}

body.project-colors-dark-gray .project-button {
    background-color: #4a4e57 !important;
    color: #fff;
}

/* "Żywe kolory" (project_color_scheme=vivid) — paleta ciemnych, nasyconych
   barw (patrz Color.php). Nazwa projektu zostaje ciemna (dziedziczy
   #20242b z .project-button, jak w każdym innym schemacie poza
   "Ciemnoszary") — tylko licznik i koło zębate dostają biały kolor w tym
   schemacie. */
body.project-colors-vivid .project-elapsed,
body.project-colors-vivid .project-gear-icon {
    color: #fff;
}

/* Wyśrodkowana w pionie WZGLĘDEM CAŁEGO PRZYCISKU (position:absolute +
   top:50%/translateY, nie flex/justify-content) — bo .project-button ma
   asymetryczny padding (kolor tła sięga do samych krawędzi, ale licznik i
   koło zębate w rogach zajmują różną wysokość niż górna krawędź), więc
   wyśrodkowanie treści przez flex wewnątrz paddingu wypadałoby wizualnie NIE
   na środku kafla. Wysokość kafla (.project-button min-height) jest stała,
   więc nie potrzeba już rezerwować miejsca na dwie linie w przepływie —
   ta nazwa i tak jest wyjęta z przepływu. */
/* Powiększenie o 20% po starcie (kliknięcie PLAY) i pomniejszenie z powrotem
   po stopie, na wyraźne życzenie — ten sam "sprężysty" cubic-bezier co
   .project-elapsed niżej (lekki przerost), dla spójności animacji w tym
   samym kaflu. translateY(-50%) MUSI zostać w tym samym transform co scale()
   (jedna właściwość, nie da się animować dwóch odrębnych transform
   niezależnie) — inaczej scale() nadpisałoby wyśrodkowanie w pionie. */
/* Grid 1fr/auto/1fr (nie top:50%+translateY) — to jedyny sposób, żeby
   NAPRAWDĘ pogodzić dwa wymagania naraz: .project-name dokładnie na
   środku w pionie SAMEJ SIEBIE (uwzględniając własną wysokość — 1 czy 2
   linie, środkowy wiersz `auto` zawsze wypada idealnie w połowie, bo
   oba sąsiadujące wiersze-rozpychacze są równe, 1fr/1fr), a JEDNOCZEŚNIE
   .project-client-name w stałej odległości (10px) od jej FAKTYCZNEJ
   górnej krawędzi, niezależnie gdzie ta krawędź akurat wypada. Czysta
   pozycja top:50%+translateY (poprzednie dwie próby) nie potrafiła tego
   dać: albo stały punkt względem kafla (nie od nazwy), albo wspólne
   wyśrodkowanie PARY (przesuwało samą nazwę poza środek) — obu na
   wyraźne życzenie odrzucone. */
.project-name-block {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-rows: 1fr auto 1fr;
    padding: 0 0.75rem;
    text-align: center;
    transform: scale(1);
    transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.project-button.active .project-name-block {
    transform: scale(1.2);
}

/* Po powiększeniu o 20% nazwa zawijała się jeszcze wg szerokości PRZED
   powiększeniem — tekst mieszczący się "na styk" w jednej linii wychodził
   poza kafel (zgłoszone: "Pudliszk - Majonez"). Na aktywnym kaflu nazwa
   łamie się więc w szerokości 1/1.2, żeby po scale(1.2) zmieściła się. */
.project-button.active .project-name {
    justify-self: center;
    max-width: calc(100% / 1.2);
}

/* Nazwa klienta na kaflu — zamiast wspólnego nagłówka nad siatką projektów
   (na wyraźne życzenie, oszczędność wysokości): mniejsza od nazwy projektu,
   biały tekst (kafel jest w kolorze projektu, patrz .project-button wyżej),
   lekko przygaszony przez opacity. `grid-row: 1` + `align-self: end` sadza
   ją na SAMYM DOLE górnego rozpychacza — czyli tuż przy faktycznej górnej
   krawędzi `.project-name` (początku środkowego wiersza), niezależnie od
   tego, gdzie ta krawędź akurat jest; `margin-bottom` odsuwa ją o stałe
   10px. Powtarza się na KAŻDYM kaflu klienta z więcej niż jednym projektem —
   świadomy koszt, bez wspólnego nagłówka nie ma już skąd wziąć tej
   informacji inaczej. */
.project-client-name {
    grid-row: 1;
    align-self: end;
    margin-bottom: 10px;
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
    opacity: 0.85;
}

.project-name {
    grid-row: 2;
    font-size: 1.15rem;
    line-height: 1.2em;
}

/* Domyślnie (animacja włączona): wyraźny animowany gradient czerwieni
   zamiast płaskiego obrysu — patrz .project-button.active::after niżej.
   Wyłączona w Ustawieniach (body.no-active-animation) albo dopóki ::after
   się nie doliczy: zwykły, płaski czerwony obrys jako fallback. */
.project-button.active {
    outline: 6px solid #e53935;
    outline-offset: 2px;
    box-shadow: 0 0 10px rgba(229, 57, 53, 0.6);
}

body:not(.no-active-animation) .project-button.active {
    outline: none;
    box-shadow: none;
    position: relative;
}

body:not(.no-active-animation) .project-button.active::after {
    content: "";
    position: absolute;
    inset: -7px;
    border-radius: 14px;
    padding: 7px;
    background: linear-gradient(120deg, #b71c1c, #ff5252, #ffcdd2, #ff5252, #b71c1c);
    background-size: 300% 300%;
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    animation: wt-active-gradient 3s ease-in-out infinite;
    filter: drop-shadow(0 0 6px rgba(229, 57, 53, 0.65));
    pointer-events: none;
}

@keyframes wt-active-gradient {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
}

/* Alternatywna metoda wyróżnienia aktywnego projektu (patrz admin/settings.php,
   active_indicator=gear) — zamiast czerwonego obrysu/gradientu wyżej,
   delikatnie obracające się koło zębate w rogu przycisku. Klasa
   .active-indicator-gear na <body> (patrz layout_top.php) włącza ten tryb:
   wyłącza obrys/gradient i pokazuje ikonę poniżej. */
body.active-indicator-gear .project-button.active {
    outline: none;
    box-shadow: none;
}

body.active-indicator-gear .project-button.active::after {
    display: none;
}

/* Przycina ikonę koła zębatego do obszaru przycisku (patrz .project-gear-icon
   niżej — celowo wystaje poza róg przez transform, ma być "obcięta" jego
   krawędzią, nie swobodnie unosić się nad przyciskiem). Bezpieczne tylko w
   trybie active-indicator-gear — w trybie z obrysem (domyślnym) ::after
   powyżej celowo wychodzi poza przycisk (inset: -7px) i tego nie wolno
   przycinać, dlatego nie ma tu ogólnej reguły overflow:hidden na
   .project-button. */
body.active-indicator-gear .project-button {
    overflow: hidden;
}

/* Koło zębate zawsze w DOM (bez display:none) — chowanie/pokazywanie idzie
   przez opacity+transform, żeby dało się animować "wyjazd zza krawędzi"
   (patrz niżej). W spoczynku (nieaktywny projekt, albo tryb z obrysem)
   siedzi niewidoczne i cofnięte głębiej w róg (translate -60%/60%), a przy
   aktywnym projekcie w trybie gear wyjeżdża do pozycji spoczynkowej
   "wystającej" (translate -22%/22%, obcięte krawędzią przycisku przez
   overflow:hidden wyżej). Zawsze w LEWYM dolnym rogu — licznik (niżej)
   zawsze w prawym, żeby się nigdy nie zderzały. Kolor przez currentColor
   (dziedziczy z .project-button — ciemny albo biały w zależności od
   schematu kolorowania, patrz body.project-colors-* wyżej), nie na sztywno
   biały, żeby był widoczny też na jasnych/płaskich tłach. */
.project-gear-icon {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 64px;
    height: 64px;
    transform: translate(-60%, 60%);
    color: currentColor;
    opacity: 0;
    z-index: 1;
    pointer-events: none;
    transition: opacity 0.3s ease, transform 0.3s ease, width 0.15s ease, height 0.15s ease;
}

body.active-indicator-gear .project-button.active .project-gear-icon {
    opacity: 0.7;
    transform: translate(-22%, 22%);
}

.project-gear-icon-spin {
    width: 100%;
    height: 100%;
    animation: wt-gear-spin 9s linear infinite;
}

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

/* Licznik czasu — rogowa "plakietka", nie stały wiersz pod nazwą (patrz
   .project-name, które teraz jest jedynym elementem centrowanym w przycisku
   przez flex). Zawsze w PRAWYM dolnym rogu (nieaktywny i aktywny — tylko
   koło zębate wyżej ma swój, lewy) — nieaktywny mniejszy i bez sekund,
   aktywny wyraźnie powiększony, pogrubiony i z sekundami (patrz
   .project-elapsed-seconds niżej). Dodatkowy dolny padding na
   .project-button (patrz wyżej) rezerwuje miejsce, żeby ta powiększona
   plakietka nigdy nie nachodziła na nazwę. Kolor przez dziedziczenie z
   .project-button, tak samo jak koło zębate wyżej — mają być tego samego
   koloru. "Sprężysty" cubic-bezier z lekkim przerostem, żeby powiększanie
   przy aktywacji było "przyjemne", nie tylko liniowe. */
.project-elapsed {
    position: absolute;
    right: 0.6rem;
    bottom: 0.5rem;
    z-index: 2;
    font-size: 0.95rem;
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    opacity: 0.9;
    transition: font-size 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), font-weight 0.2s ease;
}

.project-button.active .project-elapsed {
    font-size: 1.8rem;
    font-weight: 700;
    opacity: 1;
}

.project-elapsed-seconds {
    display: none;
    font-size: 70%;
    opacity: 0.5;
}

.project-button.active .project-elapsed-seconds {
    display: inline;
}

/* Kafelek wąski (mniej miejsca w siatce niż zakładały bazowe rozmiary wyżej,
   patrz .project-cell/container-type powyżej) — zębatka i powiększony
   licznik aktywnego projektu, każdy zadokowany w swoim dolnym rogu, zaczynają
   nachodzić na siebie na środku kafelka poniżej pewnej szerokości. Dwa progi,
   coraz mniejsze rozmiary — przy najwęższych kafelkach dodatkowo znika mało
   czytelny w tym rozmiarze dopisek sekund. Wspólne dla obu wariantów
   skalowania (Ustawienia > Pulpit > Skalowanie kafli) - wariant "Skalowanie
   proporcjonalne" rozciąga TYLKO szerokość (patrz body.tile-scaling-uniform
   niżej), wysokość i zawartość kafla zostają bez zmian w obu wariantach. */
@container (max-width: 179px) {
    .project-gear-icon {
        width: 48px;
        height: 48px;
    }

    .project-button.active .project-elapsed {
        font-size: 1.4rem;
    }

    .project-name {
        font-size: 1rem;
    }
}

@container (max-width: 130px) {
    .project-gear-icon {
        width: 36px;
        height: 36px;
    }

    .project-button.active .project-elapsed {
        font-size: 1.1rem;
    }

    .project-button.active .project-elapsed-seconds {
        display: none;
    }

    .project-name {
        font-size: 0.85rem;
    }
}

/* --- Pulpit: wariant "Skalowanie proporcjonalne" (Ustawienia > Pulpit >
   Skalowanie kafli, user_settings.dashboard_tile_scaling = "uniform") ---
   Na wyraźne życzenie TYLKO szerokość kafla rozciąga się na cały wiersz
   (auto-fill/1fr, patrz niżej) - wysokość zostaje stała (.project-button min-height:140px
   z bazowej reguły, bez zmian) i tak samo cała zawartość (czcionka, ikona
   koła zębatego, padding) - wczesniejsza wersja skalowała WSZYSTKO razem
   (aspect-ratio + jednostki cqw), ale przy kilku kaflach na szerokim ekranie
   robiły się nieproporcjonalnie ogromne, więc z tego zrezygnowano.
   minmax(min(160px, 100%), 1fr) zamiast samego minmax(160px, 1fr) - ten sam
   trik co "bez skalowania" (patrz .project-grid wyżej) na dolnej granicy,
   z tym samym obniżonym progiem 160px (zamiast 220px): przy wielu
   kolumnach zachowuje się jak zwykłe minmax(160px, 1fr) (sztywne 160px
   minimum, reszta 1fr), ale gdy zostaje JEDEN kafel w wierszu i okno jest
   węższe niż 160px, min(160px, 100%) w tym miejscu przyjmuje wartość 100%
   (mniejszą niż 160px) - kolumna przestaje mieć sztywne dno i swobodnie
   kurczy się z oknem, dokładnie jak ten sam przypadek w wariancie "bez
   skalowania", zamiast wymuszać poziomy scroll. */
/* auto-fill, NIE auto-fit (poprawka na wyraźne życzenie — kafle w grupie z
   mniejszą liczbą projektów niż w innych grupach robiły się szersze niż
   reszta, zamiast zatrzymać się w tym samym miejscu co pełny wiersz).
   Każda grupa klienta (patrz wt_render_group_projects()) to OSOBNA siatka
   .project-grid, więc liczba kolumn liczy się dla każdej z osobna, z
   szerokości KONTENERA (tej samej dla wszystkich grup) — przy auto-fit
   puste, niewykorzystane kolumny (bo w danej grupie jest mniej kafli, niż
   zmieściłoby się w rzędzie) są usuwane, a ich miejsce rozdziela się (1fr)
   między pozostałe, więc te nieliczne kafle rozciągają się na całą
   szerokość wiersza. auto-fill zostawia te puste kolumny jako niewidoczną,
   zarezerwowaną przestrzeń zamiast je usuwać — kafle w każdej grupie
   dostają dokładnie tę samą szerokość, a krótszy wiersz po prostu kończy
   się wcześniej. */
body.tile-scaling-uniform .project-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr));
}

/* Pasek nad tabelą administracyjną z przyciskiem akcji dociśniętym do
   prawej (np. "Dodaj użytkownika") — formularz dodawania nie ma się
   wyświetlać od razu, tylko po kliknięciu (na wyraźne życzenie). */
.admin-list-toolbar {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

/* Wariant z filtrem po LEWEJ i akcjami (Usuń/Dodaj) po PRAWEJ, w jednym
   wierszu — na wyraźne życzenie (Projekty: filtr grupy klientów). Zastępuje
   bazowy flex-end przez space-between; oba dzieci dostają WŁASNY
   display:flex, bo .admin-list-toolbar sam w sobie już nim nie zarządza. */
.admin-list-toolbar-spread {
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
}

.admin-list-toolbar-group-filter {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Grupuje filtr grupy + ikonę sortowania po LEWEJ stronie .admin-list-toolbar-spread. */
.admin-list-toolbar-left {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}

.admin-list-toolbar-actions {
    display: flex;
    gap: 0.5rem;
}

/* Kolumny "Aktywny"/"Kolor" na liście Projektów — stała szerokość, na
   wyraźne życzenie ("ostatnie 2 [kolumny] mają być w tej samej pozycji").
   Każdy klient ma WŁASNĄ <table> (patrz admin/projects.php), więc bez tego
   szerokość kolumn auto-dopasowuje się NIEZALEŻNIE do zawartości każdej
   tabeli osobno (głównie do długości nazw projektów w kolumnie "Projekt") —
   te dwie kolumny wtedy nie są wyrównane w pionie między tabelami różnych
   klientów. Kolumna "Projekt" (bez własnej klasy) zabiera resztę miejsca. */
.admin-table-col-active {
    width: 90px;
}

.admin-table-col-color {
    width: 70px;
}

/* Kolumny "Użytkownik"/"Godziny" w Raporcie projektu, tryb "Wszyscy
   klienci" (patrz reports/project.php) — ten sam problem i ten sam fix co
   .admin-table-col-active/-color wyżej: każdy klient ma WŁASNĄ <table>
   (jedna na grupę w pętli), więc bez stałej szerokości kolumny "Użytkownik"/
   "Godziny" nie są wyrównane w pionie między tabelami różnych klientów —
   zależą od długości nazw projektów w danej tabeli. Też na tabeli trybu
   pojedynczego klienta w tym samym pliku, dla spójności (tam nie ma
   problemu wyrównania, ale identyczny układ kolumn wygląda tak samo w obu
   trybach). */
.admin-table-col-user {
    width: 160px;
}

.admin-table-col-hours {
    width: 100px;
}

/* Opis klienta w liście Klientów (admin/clients.php, na wyraźne życzenie:
   "tyle ile się zmieści bez zawijania") — kolumna ma swobodnie rosnąć wraz
   z resztą tabeli (auto layout, brak stałej szerokości jak kolumny wyżej),
   ale sam TEKST wewnątrz nigdy się nie zawija: `max-width` na `<span>`
   (nie na `<td>` — w tabeli z auto layoutem `max-width` samej komórki nie
   zawsze ogranicza zawartość) + ellipsis, gdy i tak nie mieści się w
   dostępnej szerokości. Pełny opis w `title` (hover), na wypadek ucięcia. */
.admin-table-truncate-cell {
    max-width: 0;
}

.admin-table-truncate {
    display: block;
    max-width: 320px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Karta wokół tabel administracyjnych (Grupy klientów/Klienci/Projekty) —
   ten sam język wizualny co karty w Ustawieniach (.settings-card): tło,
   obrys, zaokrąglone rogi. overflow: hidden obcina rogi tabeli, więc ostatni
   wiersz musi zostać bez własnego border-bottom (inaczej dublowałby się z
   obrysem karty na zaokrąglonym rogu). */
.admin-table-card {
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 10px;
    overflow: hidden;
    margin-bottom: 1.5rem;
}

.admin-table-card .admin-table {
    margin-bottom: 0;
    /* Wiersze przezroczyste (pokazują tło karty pod spodem) — tylko nagłówek
       dostaje własne, jaśniejsze tło, patrz .admin-table-card .admin-table th
       niżej. Dotyczy tylko tabel w karcie (zarządzanie); zwykłe .admin-table
       w Raportach zostają bez zmian, z własnym jednolitym tłem. */
    background: transparent;
}

.admin-table-card .admin-table tbody tr:last-child td {
    border-bottom: none;
}

/* Nagłówek jaśniejszy od (przezroczystych) wierszy — ten sam odcień co
   nagłówek grupy na Pulpicie (.group-accordion-heading), dla spójności.
   Pogrubienie nagłówka to domyślny styl przeglądarki dla <th>, celowo bez
   własnej reguły font-weight. */
.admin-table-card .admin-table th {
    background: var(--wt-surface-hover);
}

/* Nagłówek klienta w tabeli Projektów, POZA samą tabelą (na wyraźne
   życzenie: "jakby każdy klient był osobną tabelą") — każdy klient dostaje
   WŁASNĄ `.admin-table-card`/`<table>` (patrz admin/projects.php), a ten
   nagłówek jest zwykłym `<h3>` nad nią, nie wierszem `<tr><th colspan>` w
   środku jak wcześniej. Ten sam wzorzec "etykiety sekcji" co
   .settings-section-title (mała wersalka, przygaszony szary, patrz pozycja
   61) — celowo współdzielona konwencja, nie nowo wymyślony styl. Pierwszy
   nagłówek bez górnego marginesu (zaraz pod toolbarem Dodaj/Usuń, który ma
   już własny odstęp) — kolejne odsunięte od poprzedniej karty. */
.admin-table-group-heading {
    margin: 1.5rem 0 0.5rem;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #888;
}

/* Od dodania filtra grupy klientów (Projekty) nagłówek+tabela każdego
   klienta siedzą we WŁASNYM `.admin-project-client-group` (żeby JS mógł
   chować/pokazywać całą parę naraz) — `:first-of-type` na samym nagłówku
   pasowałoby więc ZAWSZE (jedyny h3 w swoim wrapperze), zerując odstęp u
   KAŻDEGO klienta zamiast tylko pierwszego na stronie. Stąd `:first-of-type`
   na WRAPPERZE, nie na nagłówku. */
.admin-project-client-group:first-of-type .admin-table-group-heading {
    margin-top: 0;
}

/* Karta wokół formularza "Dodaj ..." pod tabelą — jak .admin-table-card,
   tylko z paddingiem (formularz nie ma własnych wierszy z paddingiem, jak
   .settings-row). */
.admin-add-card {
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 10px;
    padding: 1rem 1.1rem;
    margin-bottom: 1.5rem;
}

.admin-table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 1.5rem;
    background: var(--wt-surface);
    color: var(--wt-text);
}

.admin-table th,
.admin-table td {
    text-align: left;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--wt-border);
    font-size: 0.9rem;
    vertical-align: middle;
}

/* Podświetlenie wiersza pod kursorem — na wyraźne życzenie, tym samym
   subtelnym tłem co zaznaczenie gdzie indziej w apce (--wt-selected-bg). */
.admin-table tbody tr:hover {
    background: var(--wt-selected-bg);
}

.admin-links {
    list-style: none;
    padding: 0;
}

.admin-links li {
    margin-bottom: 0.5rem;
}

.inline-form {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: center;
}

/* Ten sam wygląd co pola w Ustawieniach — patrz komentarz przy .login-form
   input wyżej. */
.inline-form input[type="text"],
.inline-form select {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--wt-selected-border);
    border-radius: 6px;
    background-color: var(--wt-bg);
    color: var(--wt-text);
    font-size: 0.85rem;
}

.stacked-form {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    max-width: 320px;
}

.stacked-form label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.9rem;
    font-weight: 600;
}

.stacked-form input,
.stacked-form select {
    padding: 0.5rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-weight: 400;
}

/* Delikatny wygląd (tylko obrys, bez tła) — na wyraźne życzenie, dotyczy
   WSZYSTKICH przycisków "Usuń" w aplikacji (zbiorcze w listach admina,
   pojedyncze w modalach szczegółów, potwierdzenie w .danger-modal). Bez
   czerwieni — ten sam neutralny wygląd co .secondary-button (tekst/obrys
   theme-aware), rozróżnia je tylko treść etykiety, nie kolor. */
.danger-button {
    background: transparent;
    color: var(--wt-text);
    border: 1.5px solid var(--wt-border);
}

.danger-button:hover {
    background: var(--wt-surface-hover);
}

/* Zbiorczy przycisk "Usuń" w .admin-list-toolbar — nieaktywny, dopóki
   żaden wiersz nie jest zaznaczony (patrz admin_bulk_delete.js). */
.danger-button:disabled {
    background: transparent;
    opacity: 0.4;
    cursor: not-allowed;
}

.danger-button:disabled:hover {
    background: transparent;
}

/* Kolumna checkboxów do zbiorczego zaznaczania (Grupy klientów/Klienci/
   Projekty) — wąska, wyśrodkowana, sam checkbox bez dodatkowego stylu.
   Padding węższy niż bazowy `.admin-table th/td` (0.5rem 0.75rem) — inaczej
   szerokość checkboxa+padding PRZEKRACZAŁA zadeklarowane `width`, więc
   auto-layout tabeli ją ignorował i liczył kolumnę na nowo z zawartości
   (stąd "raz szerzej, raz węższej" — różna w zależności od innych kolumn na
   danej stronie). `width` musi być >= checkbox + ten padding, żeby faktycznie
   zadziałał jako stała szerokość, nie tylko podpowiedź. */
.admin-checkbox-cell {
    width: 2.25rem;
    padding: 0.5rem 0.4rem;
    text-align: center;
}

/* Delikatny wygląd (tylko obrys, bez tła) — ten sam zabieg co .danger-button,
   na wyraźne życzenie, dotyczy WSZYSTKICH przycisków "Anuluj" w aplikacji. */
.secondary-button {
    background: transparent;
    color: var(--wt-text);
    border: 1.5px solid var(--wt-border);
}

.secondary-button:hover {
    background: var(--wt-surface-hover);
}

.project-add-submit:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* Przełącznik on/off zamiast natywnego checkboxa — w całej aplikacji, wszędzie
   tam, gdzie wcześniej był zwykły <input type="checkbox"> (patrz
   admin/settings.php, admin/clients.php, admin/users.php). Checkbox zostaje
   w DOM (dostępność, zachowanie formularza) — tylko wizualnie schowany
   (opacity:0, nie display:none, żeby dało się go dalej zaznaczyć/odznaczyć
   klawiaturą) i przykryty .switch-track/.switch-thumb, sterowanymi czysto
   przez CSS (:checked ~ .switch-track), bez JS. */
.switch-label {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.9rem;
    font-weight: 400;
    margin: 0.15rem 0;
}

.switch {
    position: relative;
    display: inline-flex;
    flex-shrink: 0;
    width: 38px;
    height: 22px;
}

.switch-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    margin: 0;
    cursor: pointer;
    z-index: 1;
}

.switch-track {
    position: absolute;
    inset: 0;
    background: #c7cad0;
    border-radius: 999px;
    pointer-events: none;
    transition: background-color 0.15s ease;
}

.switch-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
    transition: transform 0.15s ease;
}

.switch-input:checked ~ .switch-track {
    background: #2f6feb;
}

.switch-input:checked ~ .switch-track .switch-thumb {
    transform: translateX(16px);
}

.switch-input:focus-visible ~ .switch-track {
    outline: 2px solid #2f6feb;
    outline-offset: 2px;
}

/* Nagłówek strony + przycisk "?" ujawniający podpowiedź (patrz .hint niżej)
   — na wyraźne życzenie: podpowiedzi mają być domyślnie schowane, nie
   zaśmiecać strony kursywnym akapitem na stałe. */
.page-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.hint-toggle {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    padding: 0;
    border-radius: 50%;
    background: var(--wt-selected-bg);
    color: var(--wt-text);
    font-weight: 700;
    font-size: 0.85rem;
    line-height: 1;
}

.hint-toggle:hover {
    background: var(--wt-selected-border);
}

/* :not([hidden]), NIE display: flex bezpośrednio na .hint — inaczej ta
   reguła (autora) nadpisuje domyślny UA-styl atrybutu hidden (display: none)
   NIEZALEŻNIE od specyficzności (autor > user-agent zawsze, nie tylko przy
   remisie), więc podpowiedź byłaby widoczna na starcie, mimo hidden w
   znaczniku. Ten sam wzorzec co .danger-modal niżej. */
.hint:not([hidden]) {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    background: none;
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    padding: 0.75rem 1rem;
    font-size: 0.95rem;
    line-height: 1.5;
    color: var(--wt-text);
    margin-bottom: 1rem;
}

/* Okrągły, tak jak .color-swatch w palecie kolorów na kafelkach Pulpitu
   (na wyraźne życzenie — spójny wygląd wyboru koloru w całej aplikacji).
   Bez obwódki — czyste kółko koloru. Pseudoelementy WebKit/Firefox dla
   wewnętrznego "swatcha" input[type=color] muszą też dostać border-radius,
   inaczej środek renderuje się jako kwadrat mimo zaokrąglenia zewnętrznego
   boxa (to osobny element w cieniu DOM, nie dziedziczy border-radius). */
.color-form input[type="color"] {
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    background: none;
    overflow: hidden;
}

.color-form input[type="color"]::-webkit-color-swatch-wrapper {
    padding: 0;
    border-radius: 50%;
}

.color-form input[type="color"]::-webkit-color-swatch {
    border: none;
    border-radius: 50%;
}

.color-form input[type="color"]::-moz-color-swatch {
    border: none;
    border-radius: 50%;
}

/* --- Topbar: pasek z datą obok nazwy WorkTrack --------------------------- */

.date-bar {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: #9a9fa8;
    min-width: 0;
}

/* Własny kalendarz zamiast natywnego popupu input[type=date] (na wyraźne
   życzenie — natywny nie dawał się ani powiększyć, ani niezawodnie zamknąć
   w WKWebView, patrz WorkTrackMacApp). Komórki dnia wyraźnie większe niż
   typowy natywny picker (~2,5x, na wyraźne życzenie — łatwiej trafić). */
.date-picker-popover {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    margin-top: 0.4rem;
    width: 340px;
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
    padding: 0.75rem;
    color: var(--wt-text);
}

.date-picker-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
}

.date-picker-month-label {
    font-size: 1rem;
    font-weight: 700;
    text-transform: capitalize;
}

/* gap:4px — MUSI być identyczny jak w .date-picker-grid niżej. Bez tego oba
   grida mają 7 kolumn 1fr, ale liczonych na różnej dostępnej szerokości
   (siatka dni odejmuje 6×4px na odstępy, nagłówki nic nie odejmowały) —
   kolumny się rozjeżdżały, narastająco w prawo, więc nagłówki dni tygodnia
   nie pokrywały się z kolumnami dni (zgłoszone razem z "2 dni z prawej
   wychodzą poza okno" — to ten sam efekt, widoczny najsilniej przy
   ostatnich kolumnach). */
.date-picker-weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
    font-size: 0.75rem;
    font-weight: 600;
    color: #888;
    text-align: center;
    margin-bottom: 0.35rem;
}

.date-picker-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
}

/* Prawdziwa przyczyna rozjechanej siatki (zgłoszone ze zrzutem ekranu:
   nagłówki dni tygodnia OK, ale 2 ostatnie kolumny dni wychodzą poza kartę)
   — to <button>, więc BEZ jawnego `padding:0`/`border:0`/`min-width:0`
   dziedziczył globalny `button { padding: 0.6rem 1.25rem; border: none }`
   (patrz reguła bazowa na górze pliku). 40px+ poziomego paddingu na komórkę
   wymuszało min-content > udział 1fr (siatka ma `min-width: auto`
   domyślnie), więc CAŁA siatka rosła ponad szerokość kontenera — nagłówki
   dni (to zwykłe <span>, bez paddingu) nie miały tego problemu, stąd
   pozorne "rozjechanie" tylko siatki dni względem nagłówków. Zweryfikowane
   liczbowo (getComputedStyle) przed i po: 59px → 42px szerokości komórki. */
.date-picker-day {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 44px;
    padding: 0;
    min-width: 0;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--wt-text);
    font-size: 1rem;
    cursor: pointer;
}

.date-picker-day:hover:not(:disabled) {
    background: var(--wt-selected-bg);
}

.date-picker-day.date-picker-day-outside {
    color: #888;
    opacity: 0.45;
}

/* Dzień z zarejestrowanym czasem (na wyraźne życzenie) — lekko pomarańczowe
   tło, WYŁĄCZNIE informacyjne (nie da się kliknąć inaczej niż zwykły dzień).
   Reguła PRZED -today/-selected niżej (ta sama specyficzność, więc kolejność
   w pliku decyduje) — dzień "dziś"/wybrany ma pokazywać SWÓJ stan jako
   nadrzędny, pomarańcz to tylko subtelna podpowiedź na pozostałych dniach. */
.date-picker-day.date-picker-day-has-time {
    background: rgba(249, 115, 22, 0.18);
}

.date-picker-day.date-picker-day-has-time:hover:not(:disabled) {
    background: rgba(249, 115, 22, 0.3);
}

.date-picker-day.date-picker-day-today {
    border: 1.5px solid #2f6feb;
}

.date-picker-day.date-picker-day-selected {
    background: #2f6feb;
    color: #fff;
}

.date-picker-day:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

/* Przycisk, nie samo tekstowe span — klik otwiera kalendarz (patrz
   layout.js), na wyraźne życzenie zamiast osobnej ikony kalendarza. */
.date-bar-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    background: transparent;
    border: none;
    padding: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    border-radius: 4px;
}

.date-bar-text:hover {
    background: var(--wt-selected-bg);
}

/* Dzisiejszy dzień pogrubiony i biały (na wyraźne życzenie). W jasnym
   motywie topbar jest biały, więc tam — najmocniejszy kolor tekstu. */
.date-bar-text-today {
    font-weight: 700;
    color: var(--wt-text);
}

body.theme-dark .date-bar-text-today {
    color: #fff;
}

.date-bar .icon-btn {
    width: 24px;
    height: 24px;
    color: #a7abb3;
}

.date-bar .icon-btn .icon {
    width: 14px;
    height: 14px;
}

.date-bar .icon-btn:hover {
    background: var(--wt-selected-bg);
    color: var(--wt-text);
}

/* Ikona "Dziś" — wyraźniejsza niż pozostałe ikony paska (zgłoszone: "jakaś
   blada"), także gdy już oglądamy dziś (wtedy nieklikalna, bez wyszarzenia). */
.date-bar .icon-btn.date-bar-today-btn {
    color: var(--wt-text);
    opacity: 1;
}

body.theme-dark .date-bar .icon-btn.date-bar-today-btn {
    color: #fff;
}

/* Poprzedni/następny dzień — szersze i wyższe pole kliknięcia (na wyraźne
   życzenie: "łatwiejsze do trafienia"), większa strzałka. */
.date-bar .icon-btn.date-bar-step {
    width: 40px;
    height: 32px;
}

.date-bar .icon-btn.date-bar-step .icon {
    width: 20px;
    height: 20px;
}

.icon-btn-disabled {
    opacity: 0.35;
    cursor: default;
}

.icon-btn-disabled:hover {
    background: transparent !important;
}

.read-only-notice {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    background: none;
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    padding: 0.75rem 1rem;
    font-size: 0.95rem;
    line-height: 1.5;
    color: var(--wt-text);
    margin-bottom: 1rem;
}

/* --- Pulpit: accordion grup klientów i sortowanie ------------------------ */

.dashboard-toolbar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1rem;
}

/* Cała grupa (nagłówek + treść) w jednej delikatnej ramce — border-radius i
   overflow:hidden na kontenerze (nie na nagłówku z osobna) obcinają rogi
   nagłówka/treści jednolicie, więc to one rysują zaokrąglenie, a nie każdy
   z osobna. Tło treści to --wt-surface (jaśniejsze od tła strony), a tło
   samego paska nagłówka jest jeszcze jaśniejsze (--wt-surface-hover) — stąd
   wyraźny, ale spójny z resztą motywu, pasek "header". */
.group-accordion-item {
    margin-bottom: 0.75rem;
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--wt-surface);
    transition: opacity 0.15s ease;
}

.group-accordion-item.dragging {
    opacity: 0.45;
}

.group-accordion-heading {
    margin: 0;
    display: flex;
    align-items: stretch;
    background: var(--wt-surface-hover);
}

/* Nagłówek grupy — klikalny, dowolna liczba grup może być rozwinięta
   naraz (nie tylko jedna, jak wcześniej przy zakładkach) — stan każdej
   grupy jest niezależny, patrz setGroupExpanded() w app.js. */
.group-accordion-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 1;
    min-width: 0;
    background: transparent;
    color: var(--wt-text);
    border: none;
    padding: 0.6rem 0.9rem;
    font-size: 1rem;
    font-weight: 600;
    border-radius: 0;
    text-align: left;
}

/* Nazwa grupy — bez tego (analogicznie do .topbar-brand wyżej) w wąskim
   oknie tekst nie zawija się, tylko wylewa poza skurczony przycisk i nakłada
   się na "+ Dodaj projekt"/uchwyt przeciągania (oba flex-shrink:0, mają
   zostać w pełnym rozmiarze). */
.group-accordion-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* "+ Dodaj projekt" przeniesiony tu z paska klienta (na wyraźne życzenie —
   klient bez projektów w ogóle się już nie pokazuje, patrz
   ProjectRepository::dashboardProjectsForUser(), więc nie ma już GDZIE
   umieścić przycisku per-klient). Zawsze widoczny (nie ukryty do hover jak
   dawny .project-add-cell) — to jedyne miejsce dodawania projektu w całej
   grupie, ma być łatwe do znalezienia, zwłaszcza gdy grupa akurat nie ma
   żadnego widocznego klienta. align-self: center (nie stretch odziedziczone
   z .group-accordion-heading), żeby nie rozciągnęło się na pełną wysokość
   wiersza nagłówka jak sąsiadujące elementy. */
.group-accordion-heading .project-add-button {
    align-self: center;
    flex-shrink: 0;
    margin: 0 0.4rem;
}

/* Kwadratowy przycisk zajmuje dużo mniej miejsca niż dawny tekstowy "+
   Dodaj projekt" — może więc zostać widoczny znacznie węziej niż reszta
   topbara (patrz media query przy .date-bar wyżej, 460px), tylko na
   naprawdę skrajnie wąskie okno chowany (dodawanie projektu wciąż możliwe
   po poszerzeniu okna). */
@media (max-width: 220px) {
    .group-accordion-heading .project-add-button {
        display: none;
    }
}

.group-accordion-header:hover {
    background: var(--wt-selected-bg);
}

/* Kółeczko wokół strzałki — na wyraźne życzenie. box-sizing: content-box
   (nadpisuje globalny reset border-box z góry pliku), żeby padding
   DOKŁADAŁ się do 16px strzałki (okrąg 24px), a nie ujadał jej rozmiaru.
   Gdy w tej grupie jest aktywny (uruchomiony) projekt — JS (app.js,
   applyActiveState()/updateActiveGroupHighlight()) dokłada klasę
   .active-group na .group-accordion-header i ustawia zmienną
   --active-group-color na kolor tego projektu; kółeczko wypełnia się tym
   kolorem, strzałka robi się ciemna dla kontrastu (ten sam wzorzec co ciemny
   tekst na kolorowym kaflu projektu, patrz .project-button). Po zatrzymaniu
   projektu albo przejściu na projekt z innej grupy klasa/zmienna są
   usuwane — wraca standardowy, szary wygląd. */
.group-accordion-chevron {
    box-sizing: content-box;
    display: inline-block;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    padding: 4px;
    border-radius: 50%;
    background: var(--wt-selected-bg);
    color: #888;
    transition: transform 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

.group-accordion-header.active-group .group-accordion-chevron {
    background: var(--active-group-color, var(--wt-selected-bg));
    color: #20242b;
}

.group-accordion-header.expanded .group-accordion-chevron {
    transform: rotate(90deg);
}

/* Uchwyt do przeciągania — osobny element obok przycisku nagłówka (nie w
   jego wnętrzu), żeby przeciąganie nigdy nie kolidowało z klikiem
   zwijającym/rozwijającym grupę. draggable="true" na .group-accordion-item
   jest włączane tylko na czas przeciągania (patrz app.js), zaczynając
   właśnie od tego uchwytu. */
.group-accordion-drag-handle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    flex-shrink: 0;
    color: #888;
    cursor: grab;
}

.group-accordion-drag-handle:hover {
    background: var(--wt-selected-bg);
    color: var(--wt-text);
}

.group-accordion-drag-handle:active {
    cursor: grabbing;
}

.group-accordion-drag-handle svg {
    width: 16px;
    height: 16px;
}

/* Płynne zwijanie/rozwijanie treści grupy — technika grid-template-rows
   0fr/1fr zamiast atrybutu [hidden] (display:none nie da się animować).
   Trzy zagnieżdżone poziomy, nie dwa, są tu celowe:
   .group-accordion-body (grid, 0fr/1fr) → .group-accordion-body-inner
   (overflow:hidden, min-height:0 — dzięki temu wiersz siatki faktycznie
   kurczy się do zera, bez min-height:0 element-dziecko siatki domyślnie nie
   kurczy się poniżej wysokości swojej treści) → .group-accordion-body-content
   (właściwy padding). Padding NIE może siedzieć na -inner: padding zawsze
   fizycznie zajmuje miejsce w pudełku, więc przy 0fr wysokość -inner i tak
   nie zeszłaby poniżej sumy paddingów — musi być o jeden poziom głębiej, żeby
   -inner mógł go w całości przyciąć swoim overflow:hidden. */
/* Fix: zgłoszone jako "ramka nie dopasowuje się do zmiany liczby wierszy
   podczas skalowania okna" — `transition` był tu NA STAŁE, więc KAŻDA zmiana
   liczby wierszy `.project-grid` w środku (np. przez zmianę szerokości okna,
   nie tylko przez klik rozwiń/zwiń) uruchamiała 0.2s animację wysokości.
   Przy CIĄGŁYM przeciąganiu krawędzi okna kolejne odświeżenia layoutu
   przerywały animację poprzedniej klatki, zanim zdążyła dogonić prawdziwą
   wysokość treści — ramka wizualnie "zostawała w tyle" za rzeczywistą
   liczbą wierszy, zamiast nadążać na bieżąco jak zwykły, nieanimowany blok.
   Naprawione: `transition` przeniesiony z bazowej reguły do osobnej klasy
   `.animating`, dokładanej przez `setGroupExpanded()` w app.js TYLKO na czas
   samego kliknięcia (i zdejmowanej po jego zakończeniu) — resize okna nigdy
   nie dotyka tej klasy, więc wysokość wiersza siatki jest wtedy zwykłą,
   natychmiastową wartością `1fr`/`0fr` bez żadnej animacji do dogonienia. */
.group-accordion-body {
    display: grid;
    grid-template-rows: 0fr;
}

.group-accordion-body.animating {
    transition: grid-template-rows 0.2s ease;
}

.group-accordion-body.expanded {
    grid-template-rows: 1fr;
}

.group-accordion-body-inner {
    overflow: hidden;
    min-height: 0;
}

.group-accordion-body-content {
    padding: 0.75rem 0.75rem 0.5rem;
}

/* Pasek narzędzi Pulpitu (sortowanie + "Zwiń/rozwiń wszystkie" + "Zakończ
   puste") jako przyciski TYLKO-ikona. Dawniej kwadratowe, z pełnym obrysem
   i tłem (34px, jak dawny .toolbar-select-button) — na wyraźne życzenie
   ("mniejsze i bardziej delikatne") zmniejszone i odchudzone: bez obrysu,
   przezroczyste tło, przygaszone domyślnie (ten sam idiom co .icon-btn/
   .nav-link-icon/.task-description-toggle gdzie indziej w apce — pełna
   intensywność i tło dopiero na hover, nie na stałe). */
.icon-toolbar-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background-color: transparent;
    color: var(--wt-text);
    opacity: 0.6;
}

.icon-toolbar-button:hover {
    background-color: var(--wt-surface-hover);
    opacity: 1;
}

.icon-toolbar-button .icon {
    width: 15px;
    height: 15px;
}

/* Przycisk "Zwiń/rozwiń wszystkie grupy" — dwie nałożone ikony przełączane
   klasą .all-expanded (ten sam wzorzec co #sidebar-mini-toggle w
   layout.js/app.css): domyślnie widoczna strzałka "w dół" (rozwiń), po
   rozwinięciu WSZYSTKICH grup podmienia się na "w górę" (zwiń). Stan
   ustalany na bieżąco w app.js (updateToggleAllGroupsButton(), wołane przy
   KAŻDEJ zmianie accordionu — klik w nagłówek, klawiatura, Play — nie tylko
   po kliknięciu tego przycisku), więc zawsze odzwierciedla realny stan. */
.icon-collapse-all {
    display: none;
}

#toggle-all-groups.all-expanded .icon-expand-all {
    display: none;
}

#toggle-all-groups.all-expanded .icon-collapse-all {
    display: block;
}

/* Sortowanie — ikona filtra (lejek, powszechnie kojarzony z filtrowaniem/
   sortowaniem) rozwija menu z dwiema opcjami, ten sam wzorzec wizualny co
   .topbar-avatar-menu (pozycjonowany popover pod przyciskiem). */
.toolbar-menu-wrap {
    position: relative;
}

.sort-menu {
    position: absolute;
    top: calc(100% + 0.4rem);
    right: 0;
    min-width: 160px;
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    padding: 0.35rem;
    z-index: 20;
}

.sort-menu-option {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 0.5rem 0.6rem;
    margin: 0;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: var(--wt-text);
    font-size: 0.9rem;
    font-weight: 400;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.sort-menu-option:hover {
    background: var(--wt-surface-hover);
}

.sort-menu-option.active {
    color: #2f6feb;
    font-weight: 600;
}

/* Eksport Raportu miesiąca: "split button" (na wyraźne życzenie) — główny
   segment (link `<a>`, wygląda jak zwykły przycisk, patrz bazowa reguła
   `button` wyżej) eksportuje od razu do domyślnego Excela, wąska strzałka
   obok (osobny element, DOKŁADNIE ten sam wzorzec rozwijania co
   .toolbar-menu-wrap/.sort-menu na Pulpicie, tylko z jedną opcją: CSV)
   rozwija alternatywny format. Oba segmenty stylowane jak jeden spójny
   przycisk (wspólne tło/border-radius tylko na zewnętrznych rogach,
   cienka przerwa pośrodku). */
.export-split-button {
    display: inline-flex;
    align-items: stretch;
}

.export-split-button-main {
    display: inline-flex;
    align-items: center;
    padding: 0.6rem 1.25rem;
    font-size: 1rem;
    background: #2f6feb;
    color: #fff;
    text-decoration: none;
    border-radius: 999px 0 0 999px;
    cursor: pointer;
}

.export-split-button-main:hover {
    background: #2559c2;
}

.export-split-button-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.2rem;
    padding: 0;
    border: none;
    border-left: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 0 999px 999px 0;
    background: #2f6feb;
    color: #fff;
}

.export-split-button-arrow:hover {
    background: #2559c2;
}

.export-split-button-arrow .icon {
    width: 16px;
    height: 16px;
}

/* --- Pulpit: komórka projektu, hover toolbar, edycja/kolor, dodawanie --- */

.project-cell {
    position: relative;
    /* Pozwala .project-gear-icon i .project-elapsed niżej skalować się wg
       @container do RZECZYWISTEJ szerokości kafelka, nie szerokości okna —
       ta sama szerokość okna daje różną szerokość kafelka zależnie od tego,
       ile kolumn akurat zmieści siatka (patrz .project-grid), więc tylko
       container query, nie media query, chroni przed nachodzeniem obu na
       siebie w wąskich kafelkach. */
    container-type: inline-size;
    /* Ta sama wartość co .project-button min-height niżej — czysto
       defensywne, .project-button i tak jest jedynym dzieckiem w przepływie
       i sam już wymusza tę wysokość na rodzicu. */
    min-height: 140px;
    z-index: 0;
}

/* container-type (wyżej) niesie ze sobą layout containment, które tworzy
   WŁASNY kontekst stackowania na KAŻDYM .project-cell — z-index P-nava
   (.project-hover-actions, niżej) działa więc tylko WEWNĄTRZ swojej komórki,
   nie konkuruje bezpośrednio z sąsiednimi komórkami. Efekt: P-nav
   wystający poza dolną krawędź komórki bywał zasłaniany przez kafel z
   NASTĘPNEGO wiersza (późniejszy w DOM, więc malowany na wierzchu całej
   poprzedniej, "zamkniętej" komórki razem z jej wystającą zawartością).
   Podniesienie z-index całej komórki na hover przebija sąsiednie kontek-
   sty stackowania, więc wystający P-nav faktycznie renderuje się na wierzchu. */
.project-cell:hover {
    z-index: 10;
}


/* Duża ikona play/stop, jak w typowych odtwarzaczach wideo (np. miniaturka
   YouTube) — pojawia się natychmiast na hover (czysty CSS, bez opóźnienia
   P-nav), na samym środku przycisku, nad nazwą i licznikiem. Kształt
   (play/stop) przełącza się przez .project-button.active, które i tak już
   ustawia JS (patrz applyActiveState() w app.js). Pozycjonowana bezwzględnie
   względem .project-button (position: relative wyżej) i wyjęta z przepływu,
   żeby jej pojawianie się na hover nie przesuwało nazwy/licznika — dzięki
   temu ten blok zawsze jest wyśrodkowany w pionie w przycisku, niezależnie
   od stanu hover. */
.project-play-icon {
    position: absolute;
    top: 50%;
    left: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.35);
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.85);
    transition: opacity 0.15s ease, transform 0.15s ease;
    pointer-events: none;
    z-index: 1;
}

.project-cell:hover .project-play-icon,
.project-button:focus-visible .project-play-icon {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
}

/* Eksperyment: gdy P-nav (.project-hover-actions, niżej) nakłada się teraz na
   przycisk, najechanie akurat NA belkę ma chować dużą ikonę PLAY/PAUSE pod
   nią — inaczej obie by się nakładały wizualnie. Specyficzność selektora z
   :has() (liczona jak najbardziej specyficzny argument w środku) jest wyższa
   niż zwykłe ".project-cell:hover .project-play-icon" wyżej, więc wygrywa
   bez !important, niezależnie od kolejności reguł w pliku. */
.project-cell:has(.project-hover-actions:hover) .project-play-icon {
    opacity: 0;
}

.project-play-icon svg {
    width: 22px;
    height: 22px;
    fill: #fff;
}

.project-play-icon .icon-stop {
    display: none;
}

.project-button.active .project-play-icon .icon-play {
    display: none;
}

.project-button.active .project-play-icon .icon-stop {
    display: block;
}

.project-cell .project-button {
    width: 100%;
}

/* Szybka korekta czasu −/+15 min (na wyraźne życzenie — to samo co −15/+15
   w modalu edycji, tylko od razu na kaflu). Styl jak duża ikona play/stop
   (.project-play-icon wyżej): półprzezroczyste kółko, bez opóźnienia P-nava. Mniejsze (34px) i dosunięte do lewej/prawej
   krawędzi kafla, na tej samej wysokości co play — żeby między nimi zawsze
   został wyraźny odstęp i nie dało się trafić w play przez pomyłkę. To osobne
   przyciski (rodzeństwo .project-button, nie jego dzieci — zagnieżdżony
   <button> jest niepoprawny), więc klik w nie nie przełącza timera. */
.project-time-step {
    position: absolute;
    top: 50%; /* środek kafla, tak jak play */
    width: 34px;
    height: 34px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.35);
    color: #fff;
    font-size: 1.4rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transform: translateY(-50%) scale(0.85);
    transition: opacity 0.15s ease, transform 0.15s ease, background-color 0.15s ease;
    z-index: 4;
}

.project-time-step-minus {
    left: 0.35rem;
}

.project-time-step-plus {
    right: 0.35rem;
}

/* Każdy z dwóch pokazuje się dopiero, gdy kursor jest BLISKO niego (na
   wyraźne życzenie — analogicznie do play, które znika po zjechaniu na bok),
   nie na hover całego kafla. Klasy .near-time-minus/.near-time-plus ustawia
   JS na mousemove (patrz initProjectCell() w app.js, TIME_STEP_NEAR_PX). */
.project-cell.near-time-minus .project-time-step-minus,
.project-cell.near-time-plus .project-time-step-plus,
.project-time-step:focus-visible {
    opacity: 1;
    pointer-events: auto;
    transform: translateY(-50%) scale(1);
}

.project-time-step:hover {
    background: rgba(0, 0, 0, 0.55);
}

.project-cell .project-time-step:disabled {
    cursor: wait;
    opacity: 0.5;
}

/* Kursor na −/+ — play pod spodem nie zareaguje, więc go chowamy (ten sam
   zabieg co przy P-navie niżej). */
.project-cell:has(.project-time-step:hover) .project-play-icon {
    opacity: 0;
}

/* P-nav: pasek akcji projektu, widoczny po najechaniu. Na wyraźne życzenie,
   patrz architecture.md pozycja 98 — zamiast paska NA PEŁNĄ szerokość POD
   kaflem (dawny wariant), teraz same ikony nałożone bezpośrednio na kafel,
   BEZ tła. Tylko Edytuj/Zamknij zostały (kolor przeniesiony do okna edycji,
   pełna konfiguracja to tylko link z panelu admina — patrz project_cell.php)
   — dwie ikony, w PRAWYM GÓRNYM rogu (jedyny wolny róg: lewy dolny ma koło
   zębate trybu "gear", prawy dolny licznik czasu, patrz .project-gear-icon/
   .project-elapsed). Nigdzie nie wystają poza .project-cell, więc problem z
   zasłanianiem przez kafel z kolejnego wiersza (patrz .project-cell:hover
   { z-index: 10 } wyżej) w ogóle nie występuje w tym wariancie. Mimo braku
   tła, obszar tych ikon dalej "zabiera" spod siebie dużą ikonę PLAY/PAUSE
   (patrz reguła z :has() przy .project-play-icon) — inaczej klik trafiałby
   w niewidoczne PLAY/PAUSE zamiast w ikonę pod spodem. */
.project-hover-actions {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 5;
}

.project-hover-actions:not([hidden]) {
    display: flex;
    align-items: center;
    gap: 0;
}

/* 20% większe niż bazowe .icon-btn (na wyraźne życzenie) — TYLKO tu, nie
   globalnie (.icon-btn jest współdzielone m.in. z paskiem daty w topbarze).
   Kolor/hover zostają bazowe (.icon-btn: jasnoszary #e2e4e8 + biały hover na
   rgba(255,255,255,0.15)) — pasują do kolorowego tła kafla. */
.project-hover-actions .icon-btn {
    width: 31px;
    height: 31px;
}

.project-hover-actions .icon-btn .icon {
    width: 18px;
    height: 18px;
}

/* Notyfikator "ma przypięte zadanie" — lewy górny róg (jedyny wolny, patrz
   komentarz przy .project-hover-actions wyżej: prawy górny to P-nav, oba
   dolne zajęte przez gear/licznik czasu). Zwykły czerwony kącik jak na
   ikonach aplikacji telefonu, celowo NA STAŁE czerwony (nie theme-aware,
   ten sam zabieg co belka "braku aktywności" — czerwień jako uniwersalny
   sygnał powiadomienia, niezależny od jasnego/ciemnego motywu i od koloru
   samego kafla). Widoczność/data-task-ids sterowane przez JS
   (tasks.js/updateProjectBadges()) — element jest w znaczniku ZAWSZE
   (project_cell.php), ten sam wzorzec co .project-hover-actions[hidden]. */
.project-task-badge {
    position: absolute;
    /* Na samym narożniku (na wyraźne życzenie), nie wsunięta do środka —
       połowa średnicy w ujemnym top/left, żeby środek kropki wypadał
       dokładnie w rogu kafla, tak jak odznaki na ikonach aplikacji. */
    top: -9.5px;
    left: -9.5px;
    /* 19px (jeszcze 20% mniej niż poprzednie 24px). */
    width: 19px;
    height: 19px;
    padding: 0;
    border: 2px solid var(--wt-surface);
    border-radius: 50%;
    background: #ff3b30;
    z-index: 6;
    cursor: pointer;
    transition: transform 0.15s ease;
}

.project-task-badge:hover {
    transform: scale(1.25);
}

/* Podświetlenie zadania po kliknięciu w notyfikator na kaflu (na wyraźne
   życzenie: "jaśniejsze tło"), ten sam kolor co zaznaczenie gdzie indziej w
   apce (--wt-selected-bg) — znika samo po chwili (patrz setTimeout w
   tasks.js), nie wymaga żadnej akcji użytkownika żeby zniknęło. */
.task-row-highlight {
    background: var(--wt-selected-bg);
    border-radius: 6px;
    transition: background 0.3s ease;
}

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border-radius: 4px;
    background: transparent;
    color: #e2e4e8;
    text-decoration: none;
}

.icon-btn:hover {
    background: rgba(255, 255, 255, 0.15);
}

.icon-btn .icon {
    width: 15px;
    height: 15px;
}

/* Bez obwódki — czyste kółko koloru (na wyraźne życzenie). Stan wybrany
   ma WŁASNY, osobny pierścień (.color-swatch.selected niżej), więc
   usunięcie tej bazowej ramki go nie dotyczy. */
.color-swatch {
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 50%;
    cursor: pointer;
}

.color-swatch.selected {
    box-shadow: 0 0 0 2px #2f6feb;
}

/* --- Wybór koloru: kółko aktualnego koloru + klik otwiera paletę ---
   (na wyraźne życzenie — zastępuje dawny rząd wszystkich kolorów naraz,
   spójne w obu miejscach, gdzie się wybiera kolor projektu: edycja projektu
   na Pulpicie i modal "Dodaj projekt"/"Dopisz czas", patrz
   createColorPicker() w app.js, współdzielone przez oba.) */
.color-picker {
    position: relative;
    display: inline-block;
}

.color-picker-circle {
    width: 28px;
    height: 28px;
    padding: 0;
    border: 2px solid var(--wt-border);
    border-radius: 50%;
    cursor: pointer;
}

/* Pływająca paleta, nad resztą modala (patrz .modal-overlay z-index:100 —
   ta sama warstwa co reszta modala, wystarczy być NAD .color-picker-circle
   w normalnym porządku malowania DOM-u). Zamykana kliknięciem poza nią albo
   Escape (patrz createColorPicker() w app.js) — ten sam idiom co
   .date-picker-popover. `:not([hidden])`, NIE display bezpośrednio na
   .color-picker-palette — inaczej ta reguła (autora) nadpisuje domyślny
   UA-styl atrybutu hidden (ta sama pułapka co przy .hint wcześniej w tym
   pliku). */
.color-picker-palette:not([hidden]) {
    position: absolute;
    top: calc(100% + 0.4rem);
    left: 0;
    z-index: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    width: 168px;
    padding: 0.5rem;
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}

/* --- Nagłówek modala edycji projektu w kolorze projektu (na wyraźne
   życzenie — modal edycji na Pulpicie i modal korekty czasu w Raporcie
   miesiąca mają wyglądać identycznie, patrz createProjectColorHeader() w
   color_picker.js) --- Tło w kolorze projektu, "wylewające się" na krawędzie
   .modal-box przez ujemny margines równy jej paddingowi (1.5rem) —
   .modal-box ma overflow-y:auto (patrz wyżej), które przy okazji przycina tę
   nadwyżkę do jej własnych zaokrąglonych rogów, stąd też border-radius tu
   tylko u góry (dół dociąga się prosto do reszty formularza). */
.modal-color-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin: -1.5rem -1.5rem 1.25rem;
    padding: 1.1rem 1.5rem;
    border-radius: 10px 10px 0 0;
}

.modal-color-header-text {
    min-width: 0;
}

.modal-color-header-client {
    color: #fff;
    opacity: 0.8;
    font-size: 0.8rem;
    font-weight: 600;
    margin-bottom: 0.15rem;
}

/* <input>, nie <span> — na wyraźne życzenie: "nazwa projektu dopiero po
   kliknięciu staje się edytowalna, a bez tego pozostaje normalnie". Zaczyna
   jako readonly (patrz createProjectColorHeader() w color_picker.js) —
   wygląda wtedy jak zwykły tekst (bez tła/obwódki, kursor pointer zamiast
   text-caret), klik odblokowuje edycję w miejscu. */
.modal-color-header-name {
    display: block;
    width: 100%;
    padding: 0.1rem 0;
    border: none;
    border-bottom: 1px solid transparent;
    background: transparent;
    color: #fff;
    font-family: inherit;
    font-size: 1.3rem;
    font-weight: 700;
    cursor: pointer;
}

.modal-color-header-name:not([readonly]) {
    border-bottom-color: rgba(255, 255, 255, 0.6);
    cursor: text;
    outline: none;
}

.modal-color-header-name[readonly]:hover {
    opacity: 0.85;
}

.modal-color-header-color {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3rem;
}

.modal-color-header-color .color-picker-label {
    color: #fff;
    opacity: 0.8;
    font-size: 0.7rem;
}

/* Puste (nie wypełnione aktualnym kolorem, jak bazowe .color-picker-circle)
   — na tle w tym samym kolorze projektu wypełnione kółko byłoby niewidoczne.
   !important, bo createColorPicker() (color_picker.js) ustawia
   background-color inline przy każdej zmianie koloru — inline zawsze
   wygrywa ze zwykłą regułą klasową. */
.color-picker-header .color-picker-circle {
    background-color: transparent !important;
    border-color: rgba(255, 255, 255, 0.75);
}

.color-picker-header .color-picker-circle:hover {
    background-color: rgba(255, 255, 255, 0.15) !important;
}

/* Prawa krawędź, nie lewa (bazowa reguła wyżej) — kółko siedzi w prawym
   górnym rogu nagłówka, paleta rozwijana w lewo zmieściłaby się zawsze w
   granicach modala, w prawo mogłaby wystawać poza niego. */
.color-picker-header .color-picker-palette:not([hidden]) {
    left: auto;
    right: 0;
}

/* Kwadratowy, sama ikona "+" (SVG, nie znak tekstowy — ten renderował się
   nieproporcjonalnie w zależności od fontu) zamiast tekstu "+ Dodaj
   projekt" (na wyraźne życzenie) — ta sama ramka co wcześniej, tylko dużo
   węższy, żeby rzadziej trzeba było go chować w wąskim oknie (patrz media
   query niżej). */
.project-add-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    background: transparent;
    border: 1px solid var(--wt-border);
    color: #888;
    border-radius: 4px;
}

.project-add-button .icon {
    width: 16px;
    height: 16px;
}

.project-add-button:hover {
    background: var(--wt-selected-bg);
    color: var(--wt-text);
}

/* Wariant pełnowymiarowy z etykietą — grupa bez żadnego projektu dzisiaj
   (patrz index.php, `$group['clients'] === []`), zamiast samego tekstu
   informacyjnego jak dawniej. Kompound selektor (2 klasy), żeby bezpiecznie
   przebić bazowe `.project-add-button` (kwadrat 1.6rem/bez etykiety)
   niezależnie od kolejności reguł w pliku — obie klasy siedzą na tym samym
   elemencie. Przerywana obwódka — typowy sygnał "kliknij, żeby dodać" w
   pustym miejscu, odróżnia od reszty przycisków w aplikacji (te mają zawsze
   pełną linię). */
.project-add-button.project-add-button-empty {
    width: 100%;
    height: auto;
    padding: 0.6rem 1rem;
    gap: 0.5rem;
    font-size: 0.95rem;
    color: #888;
    border: 1.5px dashed var(--wt-border);
    border-radius: 8px;
}

.project-add-button.project-add-button-empty:hover {
    color: var(--wt-text);
}

.project-add-button.project-add-button-empty .icon {
    width: 18px;
    height: 18px;
}

/* Ogólny "szkielet" modala na środku ekranu — używany przez modal dodawania
   projektu niżej (tworzony/usuwany dynamicznie w JS) i mógłby posłużyć
   kolejnym w przyszłości. Osobno od .danger-modal/.danger-modal-box (panel
   admina, modal potwierdzenia kasowania) celowo: tamten zawsze jest w DOM
   i chowany atrybutem [hidden] (`.danger-modal:not([hidden]) { display:
   flex }`) — nadpisanie [hidden] jedną wspólną klasą `display: flex`
   byłoby ryzykowne (klasa ma wyższą specyficzność niż natywny styl
   [hidden], złamałoby chowanie), więc prościej i bezpieczniej dla obu
   utrzymać osobny, niemal identyczny wizualnie zestaw reguł niż ich nie
   dublować kosztem ryzyka zepsucia działającego modala kasowania. */
.modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.5);
}

.modal-box {
    background: var(--wt-surface);
    color: var(--wt-text);
    border-radius: 10px;
    padding: 1.5rem;
    max-width: 420px;
    width: 90%;
    max-height: 85vh;
    overflow-y: auto;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}

.modal-box h2 {
    margin: 0;
    font-size: 1.15rem;
}

.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1rem;
}

/* Warianty dla modala edycji czasu z Raportu miesiąca (patrz report_edit.js)
   — "Usuń" ma stać osobno po lewej (odseparowane od Anuluj/Zapisz, żeby nie
   było ich łatwo pokliknąć jedno po drugim), Anuluj/Zapisz zostają razem po
   prawej jak zwykle. Ten sam zabieg co .form-modal-actions-spread w panelu
   admina, tylko pod nazwą pasującą do .modal-actions. */
.modal-actions-spread {
    justify-content: space-between;
}

.modal-actions-right {
    display: flex;
    gap: 0.5rem;
}

/* --- Modal rozwiązania konfliktu przy wyłączaniu wielozadaniowości (patrz
   assets/js/layout.js, przełącznik "MULTI") — 2+ aktywnych timerów naraz,
   trzeba wybrać, który zostaje. Przyciski w kolorze PROJEKTU (ciemny tekst,
   ten sam wzorzec co .project-button/.report-calendar-project — zawsze
   czytelny niezależnie od jasnego/ciemnego motywu), "Zatrzymaj wszystkie"
   celowo ciemny i osobny (na wyraźne życzenie), żeby nie dało się go
   pomylić z żadnym z kolorowych wyborów projektu. */
.multitasking-resolve-hint {
    font-size: 0.85rem;
    color: #888;
    margin: 0.5rem 0 0;
}

.multitasking-resolve-options {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-top: 1rem;
}

.multitasking-resolve-option {
    padding: 0.6rem 0.9rem;
    border-radius: 8px;
    font-weight: 600;
    font-size: 0.9rem;
    text-align: left;
    color: #20242b;
}

.multitasking-resolve-option:hover {
    filter: brightness(0.93);
}

.multitasking-resolve-stop-all {
    background: #20242b;
    color: #fff;
}

.multitasking-resolve-stop-all:hover {
    background: #33383f;
    filter: none;
}

/* --- Modal dodawania projektu — na środku ekranu, otwierany teraz z paska
   grupy (jeden przycisk na całą grupę, nie per klient jak dawniej, patrz
   index.php) — dlatego wybór KLIENTA jest pierwszym krokiem W ŚRODKU
   modala, nie czymś ustalonym z góry. */
.project-add-client-select {
    width: 100%;
    margin-top: 1rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--wt-selected-border);
    border-radius: 6px;
    background-color: var(--wt-bg);
    color: var(--wt-text);
    font-size: 0.85rem;
}

/* Reszta formularza (nazwa/kolor/ostatnie projekty) wyszarzona i
   nieklikalna, dopóki klient nie zostanie wybrany (na wyraźne życzenie) —
   opacity, nie [hidden]/display, żeby miejsce zostawało zarezerwowane i nic
   nie "skakało" po wyborze klienta. */
.project-add-fields[data-disabled="true"] {
    opacity: 0.4;
    pointer-events: none;
}

.project-add-input {
    width: 100%;
    padding: 0.5rem 0.7rem;
    font-size: 1rem;
    border: 2px solid #2f6feb;
    border-radius: 6px;
    margin-top: 1rem;
}

/* Wspólne pola okna projektu (Kod + Opis 4-wierszowy) — patrz
   createProjectFields() w color_picker.js. Etykiety małe i szare (ten sam
   idiom co inne drugorzędne opisy w modalach), żeby okno zostało zwarte. */
.project-modal-fields {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-top: 0.75rem;
}

.project-modal-field {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.project-modal-label {
    font-size: 0.75rem;
    color: #888;
}

.project-modal-description {
    resize: vertical;
    font-family: inherit;
    line-height: 1.35;
}

/* Informacje statystyczne pod nagłówkiem — tylko w oknie z listy projektów
   (admin_project_modal.js): "Klient — Grupa" i liczba sesji. */
.project-modal-meta {
    margin: 0 0 0.15rem;
    font-size: 0.85rem;
    color: #888;
}

/* Opis — pole drugorzędne, świadomie STONOWANE (zwykły .settings-text-input,
   nie gruby niebieski obrys jak nazwa powyżej), żeby nie konkurowało z nią
   o uwagę. */
.project-add-description {
    margin-top: 0.5rem;
}

/* Wiersz "Kolor: [kółko]" — nad kółkiem (patrz .color-picker wyżej) jest
   sam etykieta, ten sam idiom co .report-edit-meta/.project-add-recent-label
   (#888, drugorzędne). Współdzielony przez modal edycji projektu na
   Pulpicie i modal "Dodaj projekt"/"Dopisz czas" (patrz
   createColorPicker()/openEditProjectModal()/openAddProjectModal() w
   app.js). */
.color-picker-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.6rem;
}

.color-picker-label {
    font-size: 0.85rem;
    color: #888;
}

.project-add-recent-label {
    font-size: 0.8rem;
    font-weight: 600;
    color: #888;
    margin-top: 1rem;
    margin-bottom: 0.4rem;
}

/* Przewijana, gdy lista ostatnich projektów klienta jest za długa (na
   wyraźne życzenie) — stała maksymalna wysokość zamiast rozpychania modala
   w nieskończoność. */
.project-add-recent-list {
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--wt-border);
    border-radius: 6px;
}

.project-add-recent-empty {
    padding: 0.6rem 0.75rem;
    font-size: 0.85rem;
    color: #888;
}

.project-add-suggestion {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    font-size: 0.9rem;
    cursor: pointer;
}

.project-add-suggestion:hover {
    background: var(--wt-selected-bg);
}

/* Stan wybrany — używany tylko przez modal "Dopisz czas" (dzień miniony,
   patrz openAddPastTimeModal() w app.js), gdzie wybór projektu z listy jest
   TRWAŁYM zaznaczeniem (dalej trzeba wpisać czas), nie natychmiastową akcją
   jak przy wznawianiu zamkniętego projektu (ta sama klasa, inny kontekst). */
.project-add-suggestion.selected {
    background: var(--wt-selected-bg);
    outline: 2px solid #2f6feb;
    outline-offset: -2px;
}

.suggestion-swatch {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* Etykieta "(zakończony)" przy projekcie na liście "Dopisz czas" (dzień
   miniony, teraz obejmuje też zakończone projekty) — przygaszona, ten sam
   odcień co inne drugorzędne podpisy w apce (.form-modal-meta i inne, #888). */
.project-add-suggestion-status {
    color: #888;
    font-size: 0.85em;
}

/* Blokada duplikatu nazwy projektu (na wyraźne życzenie: "Zablokuj i pokaż
   istniejący") — treść ostrzegawcza + klikalna podpowiedź istniejącego
   projektu poniżej (reużywa .project-add-suggestion, patrz wyżej). */
.project-add-duplicate-notice {
    color: var(--wt-notice-warning-text);
    font-size: 0.85rem;
    margin: 0.5rem 0 0;
}

.project-add-duplicate-notice .project-add-suggestion {
    border: 1px solid var(--wt-border);
    border-radius: 6px;
    margin-top: 0.35rem;
}

/* Kod projektu zaraz po nazwie w tabeli Projektów (admin) — na wyraźne
   życzenie, czerwony font (jedyne uzasadnione miejsce poza obwódką
   aktywnego timera, patrz architecture.md pozycja 6 — tu świadomy wyjątek
   na życzenie, nie kolidujące z paletą projektów, bo to zwykły tekst obok
   nazwy, nie kolor samego projektu). Pogrubiony, żeby faktycznie rzucał
   się w oczy przy skanowaniu listy. */
.project-code-badge {
    color: #d32f2f;
    font-weight: 600;
}

/* --- Panel admina: modal potwierdzenia kasowania ------------------------ */

.row-actions {
    display: flex;
    gap: 0.4rem;
}

/* Przyciski WEWNĄTRZ wierszy tabel administracyjnych zmniejszone do 70%
   bazowego rozmiaru (na wyraźne życzenie) — dotyczy WYŁĄCZNIE DRUGORZĘDNYCH
   przycisków akcji w wierszu (np. przełącznik "Aktywny"/kolor w Projektach),
   nie głównej treści wiersza. `:not(.link-button)` wyklucza klikalną
   nazwę/e-mail U ŹRÓDŁA, żeby .link-button (patrz niżej w pliku) był
   NAPRAWDĘ uniwersalny — jedna reguła, obowiązująca wszędzie, bez potrzeby
   podbijania specyficzności per-kontener przy każdym nowym użyciu. Wcześniej
   .link-button próbował "wygrać" z tą regułą samą kolejnością w pliku, co
   nie działa (typ+klasa > sama klasa) — realny, długo niezauważony bug:
   linki w tabelach admina renderowały się w 0.7rem, nie 0.9-1rem, mimo
   wyglądu "zwykłego tekstu" maskującego brak paddingu. Zgłoszone jako "linki
   w listach zarządzania są cały czas mniejsze niż teksty w kolejnych
   kolumnach" — zweryfikowane liczbowo (getComputedStyle) przed naprawą. */
.admin-table button:not(.link-button) {
    padding: 0.42rem 0.875rem;
    font-size: 0.7rem;
}

.danger-modal {
    position: fixed;
    inset: 0;
    z-index: 100;
}

.danger-modal:not([hidden]) {
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.5);
}

.danger-modal-box {
    background: var(--wt-surface);
    color: var(--wt-text);
    border-radius: 10px;
    padding: 1.5rem;
    max-width: 420px;
    width: 90%;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}

.danger-modal-box h2 {
    margin-top: 0;
    color: #b00020;
    font-size: 1.15rem;
}

.danger-modal-message {
    font-weight: 600;
    background: #fdecea;
    border: 1px solid #f5c6c2;
    border-radius: 6px;
    padding: 0.75rem;
    color: #7a1710;
}

.danger-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

.danger-modal-actions button[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* --- Raporty: filtr (zakres dat + wybór projektu/klienta/użytkownika) i
   kalendarz Raportu miesiąca ----------------------------------------------- */

.report-tabs {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--wt-border);
}

.report-tab {
    padding: 0.5rem 0.9rem;
    font-size: 0.95rem;
    font-weight: 600;
    color: #888;
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

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

.report-tab.active {
    color: var(--wt-text);
    border-bottom-color: #2f6feb;
}

.report-filter {
    margin-bottom: 1.5rem;
}

/* Ten sam wygląd co pola w Ustawieniach — patrz komentarz przy .login-form
   input wyżej. */
.report-filter select,
.report-filter input[type="date"] {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--wt-selected-border);
    border-radius: 6px;
    background-color: var(--wt-bg);
    color: var(--wt-text);
    font-size: 0.85rem;
}

.report-summary {
    font-size: 0.95rem;
    margin-bottom: 1rem;
}

/* Pasek z sumą miesiąca + przełącznikiem zaokrąglania (patrz reports/month.php)
   — TYLKO ta strona ma ten przełącznik, stąd osobny wrapper zamiast zmiany
   .report-summary (używanego bez niego na pozostałych 3 raportach). Przełącznik
   graficzny (.switch, na wyraźne życzenie — tak jak wszędzie indziej w
   aplikacji, pozycja 46), nie zwykły tekstowy link jak wcześniej. */
.report-summary-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}

/* Zaokrąglanie tuż obok przycisku eksportu (na wyraźne życzenie) — bez tego
   .report-summary-bar (justify-content: space-between, 3 dzieci) rozkładał
   przełącznik gdzieś pomiędzy sumą a eksportem, nie przy nim. */
.report-summary-actions {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.report-summary-bar .report-summary {
    margin-bottom: 0;
}

.report-summary strong {
    font-variant-numeric: tabular-nums;
}

.report-empty {
    color: #888;
}

.report-month-nav {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

/* Przyciski poprzedni/następny miesiąc wyraźnie większe niż bazowe .icon-btn
   (na wyraźne życzenie: "trudno w nie trafiać") — TYLKO tu, nie globalnie
   (.icon-btn współdzielone m.in. z paskiem daty w topbarze), ten sam wzorzec
   scoped-override co .project-hover-actions .icon-btn. */
.report-month-nav .icon-btn {
    width: 40px;
    height: 40px;
}

.report-month-nav .icon-btn .icon {
    width: 22px;
    height: 22px;
}

.report-month-nav h2 {
    margin: 0;
    text-transform: capitalize;
    min-width: 10ch;
}

/* Alternatywa do wyboru miesiąca — dowolny zakres dat (na wyraźne życzenie,
   "kalendarz podwójny... od-do, jak np. na Bookingu"). Etykieta zakresu
   zajmuje miejsce <h2> z nazwą miesiąca, gdy tryb jest aktywny (patrz
   report_period_control.php — jedno LUB drugie, nigdy oba naraz). */
.report-range-label {
    font-weight: 700;
    font-size: 1.25rem;
}

.report-range-clear-link {
    font-size: 0.85rem;
    color: #888;
}

.report-period-picker {
    position: relative;
    display: inline-flex;
}

/* Popover z dwoma kalendarzami obok siebie — ten sam język wizualny co
   .date-picker-popover w nagłówku (patrz pozycja 143, tam samo dopiero co
   naprawiony błąd wyrównania komórek dnia — .report-range-day niżej dzieli
   z .date-picker-day dokładnie te same reguły, żeby fix obowiązywał w obu
   miejscach naraz). Szerszy niż oryginał (dwa miesiące), więc WŁASNA klasa
   zamiast .date-picker-popover wprost. */
/* Domyślnie lewa krawędź = lewa krawędź przycisku (`left: 0`) — ANI to, ANI
   samo `right: 0` nie wystarcza samodzielnie (zweryfikowane wizualnie): przy
   węższych oknach popover (dwa kalendarze, ~700px) wychodzi poza lewą LUB
   prawą krawędź w zależności od tego, gdzie akurat siedzi przycisk w
   .report-month-nav (zmienne — zależy od długości etykiety miesiąca/zakresu).
   `report_range_picker.js` (positionPopover()) domierza `left` w JS PO
   otwarciu, na podstawie realnej szerokości okna — ta reguła to tylko punkt
   startowy, zanim JS go ewentualnie skoryguje. */
.report-range-popover {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    margin-top: 0.4rem;
    width: min(700px, calc(100vw - 2rem));
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
    padding: 0.75rem;
    color: var(--wt-text);
}

.report-range-calendars {
    display: flex;
    gap: 1.5rem;
    flex-wrap: wrap;
}

.report-range-month {
    flex: 1 1 260px;
    min-width: 0;
}

.report-range-nav-spacer {
    width: 32px;
    height: 32px;
}

/* Sama siatka/komórki dnia — identyczne z .date-picker-grid/.date-picker-day
   w nagłówku (patrz komentarz wyżej), plus stany zakresu (start/koniec/
   pomiędzy), których oryginał nie potrzebuje. */
.report-range-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
}

.report-range-day {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 40px;
    padding: 0;
    min-width: 0;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--wt-text);
    font-size: 0.95rem;
    cursor: pointer;
}

.report-range-day:hover:not(:disabled) {
    background: var(--wt-selected-bg);
}

.report-range-day.report-range-day-outside {
    color: #888;
    opacity: 0.45;
}

/* Dzień z zarejestrowanym czasem — ten sam idiom co .date-picker-day-has-time
   w kalendarzu nagłówka (patrz komentarz tam), reguła też PRZED -today/
   -in-range/-start/-end niżej, żeby stan zakresu/dziś zawsze wygrywał. */
.report-range-day.report-range-day-has-time {
    background: rgba(249, 115, 22, 0.18);
}

.report-range-day.report-range-day-has-time:hover:not(:disabled) {
    background: rgba(249, 115, 22, 0.3);
}

.report-range-day.report-range-day-today {
    border: 1.5px solid #2f6feb;
}

.report-range-day:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

/* Dzień w środku wybranego (lub podglądanego, przed drugim kliknięciem)
   zakresu — prostokątne, stykające się tło (nie pigułka jak start/koniec),
   żeby wizualnie "łączyło" sąsiednie dni w jeden pasek. */
.report-range-day.report-range-day-in-range {
    background: var(--wt-selected-bg);
    border-radius: 0;
}

.report-range-day.report-range-day-start,
.report-range-day.report-range-day-end {
    background: #2f6feb;
    color: #fff;
}

.report-range-day.report-range-day-start {
    border-radius: 8px 0 0 8px;
}

.report-range-day.report-range-day-end {
    border-radius: 0 8px 8px 0;
}

/* Zakres w obrębie JEDNEGO dnia (start === koniec) — pigułka w całości, bez
   przycinania żadnego rogu. */
.report-range-day.report-range-day-start.report-range-day-end {
    border-radius: 8px;
}

.report-range-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--wt-border);
}

@media (max-width: 620px) {
    .report-range-calendars {
        flex-direction: column;
    }
}

.report-calendar {
    display: grid;
    /* Rozciągnięty na 100% szerokości — 7 równych kolumn wypełniających
       cały dostępny .content (patrz pozycja "frame contentu" — poszerzony
       do 1152px), nie stałe 120px jak wcześniej. minmax(0, 1fr), nie samo
       1fr, żeby treść komórki (długa nazwa projektu) mogła się przycinać
       zamiast rozpychać kolumnę szerzej niż siostrzane. */
    /* 8. kolumna (suma tygodnia, na wyraźne życzenie) węższa niż kolumny dni
       — pokazuje tylko jedną liczbę, nie potrzebuje tyle miejsca. */
    grid-template-columns: repeat(7, minmax(0, 1fr)) minmax(0, 0.6fr);
    gap: 0.5rem;
}

.report-calendar-weekday {
    font-size: 0.8rem;
    font-weight: 600;
    color: #888;
    text-align: center;
    padding-bottom: 0.25rem;
}

.report-calendar-day {
    border: 1px solid var(--wt-border);
    border-radius: 6px;
    padding: 0.5rem;
    min-height: 96px;
    background: var(--wt-surface);
}

.report-calendar-day.report-calendar-day-empty {
    background: transparent;
    border-color: transparent;
}

.report-calendar-day.report-calendar-day-today {
    border-color: #2f6feb;
}

/* Sobota/niedziela — delikatnie inny odcień tła niż dni robocze, żeby się
   wyróżniały na pierwszy rzut oka w siatce kalendarza (na wyraźne życzenie). */
.report-calendar-day.report-calendar-day-weekend {
    background: var(--wt-selected-bg);
}

/* Dzień w tym miesiącu, ale jeszcze nie nadszedł — pusty z natury (nie ma
   jak mieć zarejestrowanego czasu), lekko przytłumiony żeby było widać, że
   to nie "brak danych", tylko "jeszcze nie dziś". Dopełnienie do pełnego
   tygodnia (na potrzeby kolumny sumy tygodnia, patrz reports/month.php) —
   dawniej takie dni po prostu nie były renderowane. */
.report-calendar-day.report-calendar-day-future {
    opacity: 0.5;
}

/* Kolumna sumy tygodnia (na wyraźne życzenie) — węższa, wyśrodkowana,
   stonowana (nie ma własnego tła jak dni, żeby nie konkurowała wzrokowo z
   .report-calendar-day-today/-weekend). */
.report-calendar-week-total {
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.85rem;
    color: #888;
    font-variant-numeric: tabular-nums;
}

/* Teraz <a>, nie <div> (na wyraźne życzenie: "kliknięcie w datę... otwierało
   pulpit na tej dacie") — prowadzi na Pulpit tego dnia, tak samo jak klik w
   sam wpis projektu (patrz komentarz przy .report-calendar-project niżej).
   Kolor/dekoracja dziedziczone (bez podkreślenia/niebieskiego linku) — samo
   podkreślenie liczby na hover jako sygnał klikalności, NIE tło (komórka
   dnia ma własne tło — .report-surface/.report-calendar-day-weekend/-today —
   z którym tło hover kolidowałoby/zlewało się w części wariantów). */
.report-calendar-day-number {
    display: flex;
    justify-content: space-between;
    font-weight: 600;
    font-size: 0.85rem;
    margin-bottom: 0.4rem;
    color: inherit;
    text-decoration: none;
}

.report-calendar-day-number:hover span:first-child {
    text-decoration: underline;
}

.report-calendar-day-total {
    font-weight: 400;
    color: #888;
    font-variant-numeric: tabular-nums;
}

/* Teraz <a>, nie <div> — każdy wpis prowadzi na Pulpit tego dnia, żeby dało
   się od razu poprawić czas (patrz reports/month.php, na wyraźne życzenie:
   "jeśli miał naliczony czas — pozwala użytkownikowi na korektę"), zamiast
   duplikować formularz korekty tutaj. Kolor projektu jako TŁO całego wpisu
   (nie mała kropka obok, jak wcześniej) — na wyraźne życzenie, żeby wyglądało
   tak samo jak kafel projektu na Pulpicie: kolorowa "ramka" z nazwą w środku,
   zawsze ciemny tekst (ten sam wzorzec co .project-button, niezależnie od
   jasnego/ciemnego motywu aplikacji). */
/* width:100% tutaj (NIE tylko na wariancie -editable) — zgłoszone jako
   "projekty w widoku Sumarycznie wyglądają inaczej niż w widoku osoby":
   <button> (widok osoby, edytowalny) dostawał width:100% tylko z
   .report-calendar-project-editable niżej, a zwykły <a> (Sumarycznie) jako
   element domyślnie inline kurczył się do szerokości treści zamiast
   rozciągać na całą komórkę dnia jak button — oba warianty MAJĄ wyglądać
   identycznie, różni je tylko zachowanie po kliknięciu. */
.report-calendar-project {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.3rem;
    width: 100%;
    font-size: 0.85rem;
    padding: 0.25rem 0.5rem;
    line-height: 1;
    color: #20242b;
    text-decoration: none;
    border-radius: 4px;
    margin-bottom: 2px;
}

/* Wpis edytowalny (patrz report_edit.js) to <button>, nie <a> — reset
   podstawowych właściwości buttona (width:100% już wyżej, wspólne z <a>).
   Kolor tła zostaje wyłącznie inline (style="background-color") ustawiany
   w PHP, więc — inaczej niż przy .link-button (patrz komentarz przy
   .link-button:hover) — globalne button:hover nie jest w stanie go
   nadpisać: inline styl zawsze wygrywa niezależnie od pseudoklas.
   font-family: inherit (NIE font: inherit) — zgłoszone: "Sumarycznie ma
   właściwy rozmiar tekstu w kafelkach, w pozostałych za duży". Przyczyna:
   `font: inherit` to skrót resetujący TEŻ font-size — nadpisywał
   font-size:0.85rem z .report-calendar-project (ta sama specyficzność,
   ale ta reguła jest niżej w pliku, więc wygrywała) wartością odziedziczoną
   po rodzicu (.report-calendar-day, bez własnego font-size — więc trafiał
   bazowy rozmiar strony, zauważalnie większy). Wariant <a> (Sumarycznie)
   nie ma tej reguły, więc nigdy nie miał tego problemu. */
.report-calendar-project-editable {
    appearance: none;
    border: none;
    text-align: left;
    font-family: inherit;
    cursor: pointer;
}

.report-calendar-project:last-child {
    margin-bottom: 0;
}

/* Modal korekty czasu z Raportu miesiąca (patrz report_edit.js,
   .modal-overlay/.modal-box/.modal-actions — ten sam wzorzec co modal
   dodawania/edycji projektu na Pulpicie, app.js openAddProjectModal()/
   openEditProjectModal()). Te same klasy pola czasu (.report-edit-duration-
   -row/-step/-duration) współdzielone przez wszystkie trzy modale — od
   kiedy edycja projektu na Pulpicie też jest zwykłym modalem (tło
   --wt-surface, nie kolor projektu), nie ma już powodu na osobny wariant. */
.report-edit-meta {
    margin: 0 0 1rem;
    color: #888;
    font-size: 0.85rem;
}

.report-edit-duration-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.75rem;
}

.report-edit-duration-step {
    flex-shrink: 0;
    padding: 0.3rem 0.55rem;
    border: 1px solid var(--wt-selected-border);
    border-radius: 999px;
    background: var(--wt-selected-bg);
    color: var(--wt-text);
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
}

.report-edit-duration-step:hover {
    background: var(--wt-selected-border);
}

.report-edit-duration {
    flex: 1;
    min-width: 0;
    text-align: center;
    font-size: 1.75rem;
    font-weight: 700;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--wt-selected-border);
    border-radius: 6px;
    background-color: var(--wt-bg);
    color: var(--wt-text);
}

.report-edit-error {
    color: var(--wt-notice-warning-text);
    font-size: 0.85rem;
    margin: 0.5rem 0 0;
}

/* z-index tylko na hover (nie na stałe) — żeby dymek podpowiedzi (patrz
   niżej) nie chował się pod kolejną komórką kalendarza dalej w DOM, ten sam
   wzorzec co .project-cell:hover na Pulpicie. */
.report-calendar-project:hover {
    filter: brightness(0.93);
    z-index: 5;
}

.report-calendar-project-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Kod projektu po nazwie, w kalendarzu/dymku/podsumowaniu Raportu miesiąca
   (na wyraźne życzenie) — mniejszy i kursywą, żeby odróżnić od nazwy.
   Naturalnie ucina się razem z nazwą przy wąskim oknie — nie osobna reguła,
   po prostu ten sam <span> co nazwa (patrz $fmtCode() w month.php), więc
   dziedziczy już istniejący text-overflow:ellipsis rodzica. Domyślnie kolor
   dziedziczony (dymek/podsumowanie — tło NIE jest kolorem projektu, tylko
   zwykłe tło strony, więc biały byłby nieczytelny w jasnym motywie). */
.report-project-code {
    font-size: 0.85em;
    opacity: 0.75;
}

/* W kaflu kalendarza tło ZAWSZE jest kolorem projektu (patrz
   .report-calendar-project, style="background-color:...") — na wyraźne
   życzenie kod tam ma być biały, żeby wyraźnie odróżniał się od ciemnej
   nazwy (.report-calendar-project ma color:#20242b, dobrany pod czytelność
   na kolorowym/żywym tle). */
.report-calendar-project-name .report-project-code {
    color: #fff;
    opacity: 0.85;
}

.report-calendar-project-hours {
    flex-shrink: 0;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    opacity: 0.8;
}

/* Dymek z pełną nazwą projektu + klientem/użytkownikiem/godzinami, na
   najechanie kursorem — zgłoszone: nazwa w komórce
   (.report-calendar-project-name, wyżej) się przycina w wąskim oknie
   (kolumny kalendarza są wąskie, patrz .report-calendar). Tło
   --wt-selected-bg (NIE --wt-surface — ten sam odcień co tło komórki dnia,
   .report-calendar-day, więc dymek by się z nią zlewał gdy nachodzi na
   sąsiednią komórkę) — subtelnie jaśniejsze/wyróżnione, ten sam token co
   inne "podświetlone" elementy w apce, zwykłe kolory tekstu strony (nie
   odwrócone). Domyślnie ukryty (opacity+visibility, nie display:none —
   potrzebna płynna, natychmiastowa reakcja na hover/focus, bez czekania na
   natywny tooltip przeglądarki — który już nie istnieje, atrybut title na
   wpisie USUNIĘTY na wyraźne życzenie, dublował się wizualnie z tym
   dymkiem). */
.report-calendar-project-tooltip {
    position: absolute;
    left: 50%;
    top: 100%;
    transform: translate(-50%, 6px);
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    background: var(--wt-selected-bg);
    color: var(--wt-text);
    border: 1px solid var(--wt-border);
    font-size: 0.95rem;
    font-weight: 400;
    line-height: 1.3;
    white-space: normal;
    width: max-content;
    max-width: 240px;
    text-align: left;
    padding: 0.35rem 0.6rem;
    border-radius: 6px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.12s ease;
    z-index: 1;
}

/* Nazwa projektu pogrubiona, na własnej linii. */
.report-calendar-project-tooltip-name {
    font-weight: 600;
}

.report-calendar-project-tooltip::after {
    content: '';
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-bottom-color: var(--wt-selected-bg);
}

.report-calendar-project:hover .report-calendar-project-tooltip,
.report-calendar-project:focus-visible .report-calendar-project-tooltip {
    opacity: 1;
    visibility: visible;
}

/* Podsumowanie pod kalendarzem (na wyraźne życzenie): suma miesiąca (już
   widoczna wyżej w .report-summary-bar, ale tu jeszcze raz, wygodnie po
   przejrzeniu całego kalendarza) + suma per projekt, sortowana od
   największej. */
.report-month-summary {
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--wt-border);
}

.report-month-summary-title {
    font-size: 1.1rem;
    margin: 0 0 0.75rem;
}

.report-project-summary-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-width: 420px;
}

.report-project-summary-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--wt-border);
}

.report-project-summary-item:last-child {
    border-bottom: none;
}

.report-project-summary-swatch {
    flex-shrink: 0;
    width: 12px;
    height: 12px;
    border-radius: 50%;
}

.report-project-summary-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.report-project-summary-hours {
    flex-shrink: 0;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Etykieta klienta przy nazwie projektu w podsumowaniu (patrz
   reports/month.php) — tylko gdy raport jest odfiltrowany do JEDNEJ osoby
   (nie "Sumarycznie"), na wyraźne życzenie. Tło = kolor projektu na 40%
   przezroczystości (Color::withAlpha(), PHP inline style) — zwykły kolor
   tekstu strony, nie biały jak na kaflu projektu (tło tu jest tylko
   subtelną plamą, nie pełnym nasyconym kolorem). */
.report-project-summary-client {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--wt-text);
    vertical-align: middle;
}

/* Rozliczenie względem referencyjnej liczby godzin (na wyraźne życzenie,
   patrz ReferenceHoursSummary/reports/month.php) — prosta tabelka
   etykieta/wartość/podpowiedź, stylistycznie spójna z resztą Raportu
   miesiąca (te same tokeny koloru co .report-month-summary wyżej), nie
   kopiująca kolorystyki z dostarczonego przez użytkownika zrzutu (tylko
   TREŚĆ miała być wzorem, nie wygląd — na wyraźne życzenie). */
.reference-summary {
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--wt-border);
}

.reference-summary-table {
    border-collapse: collapse;
    width: 100%;
    max-width: 640px;
    margin-bottom: 1rem;
}

.reference-summary-table th,
.reference-summary-table td {
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--wt-border);
    text-align: left;
    font-weight: 400;
    font-size: 0.9rem;
}

.reference-summary-table th {
    background: var(--wt-surface-hover);
    width: 42%;
}

.reference-summary-value {
    font-variant-numeric: tabular-nums;
}

.reference-summary-strong {
    font-weight: 700;
}

.reference-summary-hint {
    border: none;
    background: transparent;
    color: var(--wt-text);
    opacity: 0.6;
    font-size: 0.8rem;
    font-style: italic;
}

.reference-summary-final th,
.reference-summary-final td.reference-summary-value {
    font-weight: 700;
}

/* --- Strona Ustawienia — sekcje/karty w stylu ustawień aplikacji desktopowej
   (np. Claude), zamiast płaskiej listy <label> jedna pod drugą jak w
   .stacked-form (ta zostaje bez zmian, dalej używana w admin/users.php). */
.settings-form {
    display: flex;
    flex-direction: column;
    gap: 1.75rem;
    max-width: 640px;
}

/* .settings-form (wyżej) nie jest używana w markupie (settings.php ma
   płaskie <section class="settings-section">, część wewnątrz wspólnego
   <form>, część poza nim) — bez tej reguły sekcje nie miały MIĘDZY SOBĄ
   żadnego odstępu poza marginesem samego nagłówka, więc nagłówek kolejnej
   sekcji wizualnie "siadał" tuż pod poprzednią kartą. */
.settings-section {
    max-width: 640px;
    margin-bottom: 1.75rem;
}

.settings-section:last-child {
    margin-bottom: 0;
}

.settings-section-title {
    margin: 0 0 0.5rem;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #888;
}

.settings-card {
    background: var(--wt-surface);
    border: 1px solid var(--wt-border);
    border-radius: 10px;
    overflow: hidden;
}

.settings-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 0.85rem 1.1rem;
    border-bottom: 1px solid var(--wt-border);
}

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

.settings-row-main {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}

.settings-row-title {
    font-size: 0.9rem;
    font-weight: 500;
}

.settings-row-description {
    font-size: 0.78rem;
    color: #888;
    line-height: 1.5;
}

.settings-row-control {
    flex-shrink: 0;
}

/* Warianty dla wierszy z polem tekstowym/hasłami (Moje ustawienia — konto),
   nie tylko select/przełącznik/liczba jak w resztach .settings-row. */
.settings-row-control-wide {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.settings-row-control-wide .settings-text-input {
    width: 220px;
}

.settings-row-stacked {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
}

.settings-password-fields {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    width: 100%;
    max-width: 320px;
}

.settings-select,
.settings-number-input,
.settings-text-input {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--wt-selected-border);
    border-radius: 6px;
    background-color: var(--wt-bg);
    color: var(--wt-text);
    font-size: 0.85rem;
}

/* Strzałka rozwijania — jednolita dla WSZYSTKICH <select> w aplikacji (na
   wyraźne życzenie: "wygląda jak pole tekstowe, ale ze strzałką", nie
   generyczny wygląd przeglądarki). Każdy select ma już swój OSOBNY blok z
   padding/border/tłem/font-size dla swojego kontekstu (patrz wyżej
   .settings-select, .inline-form select...) — tu dopisane jest TYLKO to, co
   jest wspólne i nie zależy od kontekstu: appearance:none + własna strzałka.
   Stąd też każdy z tamtych bloków musiał przejść z `background:` (skrót,
   resetowałby tę strzałkę) na `background-color:`. Strzałka na sztywno w
   #888 (istniejący w apce odcień stonowanego tekstu) — czytelna na jasnym i
   ciemnym tle bez potrzeby dwóch wersji SVG per motyw. */
.settings-select,
.report-filter select,
.inline-form select,
.project-add-client-select,
.task-pin-group-select,
.tasks-group-filter,
.tasks-user-filter {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.65rem center;
    background-size: 13px;
    padding-right: 2rem;
    cursor: pointer;
}

select:focus-visible {
    outline: 2px solid #2f6feb;
    outline-offset: 1px;
}

select:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.settings-number-input {
    width: 5rem;
    text-align: right;
}

/* Pole tekstowe w tym samym stylu co .settings-select/.settings-number-input
   — używane poza samą stroną Ustawień, w modalach dodawania/edycji
   użytkownika (na wyraźne życzenie: "w stylu który jest w ustawieniach"). */
.settings-text-input {
    width: 100%;
}

.settings-save-button {
    align-self: flex-start;
}

/* Na wąskim oknie tytuł+opis i kontrolka (select/pole/przełącznik) nie
   mieściły się obok siebie w jednym wierszu — .settings-row-control ma
   flex-shrink: 0 (nie ma się czym skurczyć), więc przy zbyt wąskiej
   dostępnej przestrzeni WYCHODZIŁ poza .settings-card (overflow: hidden),
   czyli znikał całkowicie, mimo że tytuł/opis renderowały się poprawnie —
   dokładnie to zgłoszone jako "elementy nachodzą na siebie". Poniżej progu
   kontrolka schodzi pod tekst, na pełną szerokość, zamiast obok niego. */
@media (max-width: 560px) {
    .settings-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.6rem;
    }

    .settings-row-control {
        width: 100%;
    }

    .settings-select,
    .settings-number-input,
    .settings-text-input {
        width: 100%;
    }

    .settings-row-control-wide {
        flex-direction: column;
        align-items: stretch;
        width: 100%;
    }

    .settings-row-control-wide .settings-text-input {
        width: 100%;
    }
}

/* --- Modale formularzy (dodawanie/edycja użytkownika) — ten sam bezpieczny
   wzorzec [hidden] co .danger-modal (patrz tam), osobny zestaw klas z tego
   samego powodu: nie dublować wspólną klasą z .modal-overlay (JS-owy modal
   dodawania projektu na Pulpicie), żeby nie ryzykować obu na raz. */
.form-modal {
    position: fixed;
    inset: 0;
    z-index: 100;
}

.form-modal:not([hidden]) {
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.5);
}

.form-modal-box {
    background: var(--wt-surface);
    color: var(--wt-text);
    border-radius: 10px;
    padding: 1.5rem;
    max-width: 420px;
    width: 90%;
    max-height: 85vh;
    overflow-y: auto;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}

.form-modal-box h2 {
    margin: 0 0 1.1rem;
    font-size: 1.15rem;
}

.form-modal-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-bottom: 1rem;
}

.form-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1.2rem;
}

/* Warianty dla modala szczegółów projektu — "Usuń" ma stać osobno po lewej
   (odseparowane od Anuluj/Zapisz, żeby nie było ich łatwo pokliknąć jedno po
   drugim), Anuluj/Zapisz zostają razem po prawej jak zwykle. */
.form-modal-actions-spread {
    justify-content: space-between;
}

.form-modal-actions-right {
    display: flex;
    gap: 0.5rem;
}

.form-modal-meta {
    margin: 0 0 0.35rem;
    font-size: 0.8rem;
    color: #888;
}

/* Nazwa (projektu/grupy/klienta/e-maila) na uproszczonej liście — otwiera
   modal ze szczegółami/edycją (na wyraźne życzenie: dopiero po jej wyborze,
   nie od razu w wierszu). Wygląda jak zwykły tekst — nie jak link (bez
   podkreślenia, bez koloru akcentu) — dopiero po najechaniu kursorem
   przekolorowuje się na niebiesko, sygnalizując, że jest klikalna.
   JEDYNE, uniwersalne źródło prawdy dla wyglądu tej klasy — działa wszędzie
   w apce bez wyjątków per-strona/kontener (żadna inna reguła jej nie dotyka;
   `.admin-table button` wyżej w pliku ma jawny `:not(.link-button)` właśnie
   po to, żeby nigdy nie musiała z nią rywalizować specyficznością). Jeśli
   kiedyś trzeba zmienić wygląd linków w całym serwisie naraz — to jest
   jedyne miejsce do edycji. */
.link-button {
    background: none;
    border: none;
    padding: 0;
    color: inherit;
    font-weight: 400;
    text-decoration: none;
    cursor: pointer;
    /* O jeden stopień mniejszy niż 1rem, na wyraźne życzenie — z powrotem
       zrównany z bazowym rozmiarem tekstu w tabelach admina (.admin-table
       td, 0.9rem), więc wygląda jak reszta wiersza, nie ponad nią. */
    font-size: 0.9rem;
}

/* Drugorzędna, wyciszona etykieta obok klikalnej nazwy w tabeli admina (np.
   "nieaktywny" przy e-mailu użytkownika) — ten sam odcień co inne wyciszone
   podpisy w apce (#888, patrz .form-modal-meta i inne). */
.status-muted-badge {
    margin-left: 0.4rem;
    font-size: 0.75rem;
    color: #888;
}

.link-button:hover {
    /* Bez tego globalne button:hover (wyżej) wygrywało specyficznością na
       samej właściwości background — .link-button (bez :hover) ustawia
       background:none, ale to nie "chroni" stanu hover, tam trzeba go
       zadeklarować jeszcze raz. Efekt bez tego: niebieskie TŁO przy
       najechaniu zamiast samego koloru tekstu. */
    background: none;
    color: #2f6feb;
}

/* --- Zadania (panel globalny, patrz layout_top.php/tasks.js) --------------- */

/* Pływający przycisk, prawy dolny róg, na KAŻDEJ stronie — pod #active-
   project-border (z-index 1000), nad zwykłą treścią. */
.tasks-toggle {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 90;
    width: 52px;
    height: 52px;
    padding: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}

.tasks-toggle .icon {
    width: 22px;
    height: 22px;
}

/* Biały krążek z ciemnym tekstem (zamiast czerwieni — konsekwentnie z resztą
   apki, patrz .danger-button) — kontrast wystarcza, żeby się wyróżnić na
   niebieskim przycisku. */
.tasks-toggle-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    border-radius: 999px;
    background: #fff;
    color: #1c1e21;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    box-shadow: 0 0 0 2px #2f6feb;
}

/* Zakotwiczony w TYM SAMYM rogu co przycisk (na wyraźne życzenie — panel MA
   zasłaniać przycisk pod sobą, nie omijać go). Otwieranie/zamykanie przez
   klasę .is-open (NIE atrybut hidden — transform/opacity nie animuje się z
   display:none, a poza tym to ta sama pułapka z [hidden] co gdzie indziej w
   tym pliku). Wjazd/wyjazd Y (translateY, nie scale — na wyraźne życzenie,
   poprzednia wersja "wyskakiwała z rogu" jak z Docka; ta wjeżdża z dołu jak
   typowy "bottom sheet") — panel jest i tak zakotwiczony przez `bottom: 0`,
   więc `translateY(100%)` przesuwa go dokładnie o własną wysokość w dół,
   czyli całkiem pod dolną krawędź okna. visibility z opóźnieniem, żeby
   panel realnie znikał z drzewa dostępności/tabulacji dopiero PO animacji
   zamykania, ale pojawiał się w nim NATYCHMIAST przy otwieraniu.
   `cubic-bezier(0.22, 1, 0.36, 1)` — zwykłe, spokojne wyhamowanie (ease-out),
   BEZ przerostu/odbicia na końcu (na wyraźne życzenie — pierwsza wersja tej
   animacji użyła "sprężystej" krzywej z resztą apki, tu uznanej za zbyt
   żywiołową dla dużego panelu wjeżdżającego z krawędzi ekranu). */
/* Dosunięty do samej krawędzi okna (prawo/dół, na wyraźne życzenie) —
   right/bottom: 0 zamiast odstępu. Zaokrąglenie w OBU górnych rogach (na
   wyraźne życzenie — dawniej tylko lewy, jedyny który realnie "wystawał" w
   głąb strony przy animacji z rogu; przy wjeździe z dołu oba górne rogi są
   tą samą, widoczną krawędzią "wieczka" panelu, więc mają wyglądać spójnie
   mimo że prawy dalej styka się z krawędzią okna). max-width/max-height bez
   odejmowania 2rem — nie ma już zarezerwowanego marginesu wokół panelu. */
.tasks-panel {
    position: fixed;
    right: 0;
    bottom: 0;
    z-index: 95;
    width: 798px;
    max-width: 100vw;
    max-height: min(560px, 100vh);
    background: var(--wt-surface);
    color: var(--wt-text);
    display: flex;
    flex-direction: column;
    border-radius: 14px 14px 0 0;
    border: 1px solid var(--wt-border);
    /* Dwie warstwy — ciasny, ciemniejszy "kontaktowy" cień przy samej
       krawędzi (odcina panel od tła nawet gdy oba są podobnie jasne/ciemne,
       zgłoszone jako "zlewa się z zawartością strony") + szerszy, delikatny
       dla wrażenia uniesienia. Do tego 1px obrys (var(--wt-border)) — cień
       sam bywa za subtelny w jasnym motywie, obrys daje twardą krawędź
       niezależnie od tła pod spodem. */
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3), 0 16px 40px rgba(0, 0, 0, 0.35);
    /* Bez opacity (na wyraźne życzenie — "tylko slide down, bez fadeout") —
       chowanie/pokazywanie to WYŁĄCZNIE translateY, panel jest cały czas w
       pełni nieprzezroczysty, znika wyłącznie przez zsunięcie się pod dolną
       krawędź okna. */
    transform: translateY(100%);
    visibility: hidden;
    pointer-events: none;
    transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear 0.4s;
}

.tasks-panel.is-open {
    transform: translateY(0);
    visibility: visible;
    pointer-events: auto;
    transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear 0s;
}

/* Odtwarzanie zapamiętanego stanu "otwarty" przy starcie strony (patrz
   openPanel(skipSave, skipAnimation) w tasks.js) ma pokazać panel OD RAZU,
   bez animacji z rogu — zgłoszone jako "po odświeżeniu już otwarte,
   powtarzanie animacji irytuje". */
.tasks-panel.no-transition {
    transition: none !important;
}

/* Wysokość belki dopasowana do pozycji bocznego menu (.nav-link: padding
   0.55rem, ikony 18px) — zgłoszone finalnie jako "jeszcze niższa (...)
   tak jak przyciski w menu bocznym wystarczy", po wcześniejszym zejściu
   z 0.9rem na 0.3rem, co i tak było za dużo przy tak dużym przycisku
   Zamknij. Tło niebieskie (#2f6feb), tak jak pływający przycisk otwierający
   panel — na wyraźne życzenie, żeby nagłówek wizualnie kojarzył się z tym
   samym przyciskiem. Biały tekst (dziedziczony przez h2/ikonę niżej) —
   kolor NA SZTYWNO, nie var(--wt-text), bo tło jest zawsze niebieskie
   niezależnie od jasnego/ciemnego motywu apki (ten sam wzorzec co
   .project-task-badge na Pulpicie — czerwień NA STAŁE, nie theme-aware). */
.tasks-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.55rem 1.1rem;
    background: #2f6feb;
    color: #fff;
    border-radius: 14px 0 0 0;
    flex-shrink: 0;
}

.tasks-panel-header-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.tasks-panel-header-title > .icon {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
}

.tasks-panel-header h2 {
    margin: 0;
    font-size: 1.05rem;
    flex-shrink: 0;
}

/* Filtr osoby w nagłówku (patrz tasks.js) — NIE deklarujemy tu `display`
   (select jest domyślnie inline-block), żeby atrybut `hidden` (chowany, gdy
   lista osób jeszcze się nie wczytała) realnie działał — ta sama pułapka co
   przy .tasks-panel-overlay/.hint wcześniej w tym pliku, tym razem
   obeszliśmy ją, po prostu nie dotykając `display`. */
.tasks-group-filter,
.tasks-user-filter {
    /* 130px ucinało dłuższe nazwy grup/osób bez żadnego śladu, że coś
       zostało obcięte (zgłoszone: "jest wąski i ucina w nim tekst") —
       poszerzone, plus text-overflow jako siatka bezpieczeństwa dla nazw
       i tak dłuższych niż nowy limit (kropki zamiast twardego obcięcia). */
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: 0.2rem 2rem 0.2rem 0.5rem;
    font-size: 0.8rem;
    background-color: transparent;
    color: inherit;
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-radius: 999px;
}

/* .icon-btn bazowo ma jasny kolor dobrany pod kolorowe/ciemne tła (kafle
   projektów, topbar) — pasuje więc już samym kolorem do niebieskiego tła
   nagłówka, ale i tak nadpisany tu jawnie (kolor NA SZTYWNO, nie zmienna
   motywu) dla pewności niezależnie od przyszłych zmian .icon-btn gdzie
   indziej. Rozmiar przeszedł 26px -> 34px -> 58px (kolejne zgłoszenia "za
   mały"), a potem z powrotem w dół do 34px, gdy okazało się, że sama belka
   ma być niska jak pozycja bocznego menu — 58px już się tam nie mieściło
   bez nadmiernego paddingu. 34px to nadal wyraźnie więcej niż bazowe
   26px, więc pierwotne zgłoszenie "za mały" zostaje zaadresowane. */
.tasks-panel-header .icon-btn {
    width: 34px;
    height: 34px;
    color: #fff;
    opacity: 0.85;
}

.tasks-panel-header .icon-btn .icon {
    width: 18px;
    height: 18px;
}

.tasks-panel-header .icon-btn:hover {
    opacity: 1;
    background: rgba(255, 255, 255, 0.15);
}

.tasks-panel-body {
    overflow-y: auto;
    /* Bez tego (overflow-x liczy się jako 'auto', nie 'visible', bo
       overflow-y już nie jest 'visible' — reguła CSS Overflow) przyciski,
       które nie mieszczą się w wierszu, dawałyby poziomy scrollbar zamiast
       po prostu znikać za krawędzią panelu, patrz .task-title/.task-tag
       wyżej. */
    overflow-x: hidden;
    padding: 0.5rem 0.75rem 0.75rem;
}

.tasks-panel-loading {
    color: var(--wt-text);
    opacity: 0.6;
    padding: 0.5rem;
}

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

.task-row {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 0.4rem;
    padding: 0.35rem 0.25rem;
    border-radius: 8px;
    border-left: 3px solid transparent;
    /* Rozdziela zadania cienką linią, na wyraźne życzenie — tym samym
       wzorcem co .tasks-panel-header (border-bottom + var(--wt-border)). */
    border-bottom: 1px solid var(--wt-border);
}

.task-row:hover {
    background: var(--wt-surface-hover);
}

/* Zgłoszone jako "nie widać co przeciągam" (samo opacity:0.4 na tle listy
   było za subtelne) — teraz wyraźnie "uniesiony" wiersz: cień, lekkie
   podbicie w górę, kolorowa kreska po lewej (ten sam niebieski co primary
   button), tło niezależne od zwykłego hover. */
.task-row-dragging {
    opacity: 0.95;
    background: var(--wt-surface-hover);
    border-left-color: #2f6feb;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
    transform: translateY(-1px) scale(1.01);
}

.task-drag-handle {
    cursor: grab;
    opacity: 0.4;
    flex-shrink: 0;
    font-size: 0.9rem;
    line-height: 1;
    width: 12px;
}

.task-drag-handle-spacer,
.task-checkbox-spacer {
    flex-shrink: 0;
    visibility: hidden;
}

.task-checkbox-spacer {
    width: 18px;
    height: 18px;
}

/* Zwykły natywny checkbox, NIE .switch (przełącznik on/off użyty gdzie
   indziej w apce) — świadomy wyjątek: to gęsta lista wielu wierszy, gdzie
   checkbox/"odhaczenie" to inny idiom niż pojedynczy przełącznik ustawienia. */
.task-checkbox {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
}

/* flex:0 0 auto (bez shrink ANI grow) — nazwa zadania nigdy nie jest
   przycinana, na wyraźne życzenie: gdy wiersza brakuje miejsca, ma się
   kurczyć/znikać NAJPIERW tag przypiętego projektu (patrz .task-tag niżej),
   a dopiero gdyby i to nie wystarczyło — mają znikać za krawędzią panelu
   przyciski z prawej (Play/Usuń/strzałka opisu, patrz overflow-x:hidden na
   .tasks-panel-body), nigdy sama nazwa. Dawniej (`flex:0 1 auto` +
   ellipsis) tytuł przycinał się PIERWSZY, zanim tag zdążył się skurczyć —
   odwrotna kolejność niż oczekiwana. */
.task-title {
    flex: 0 0 auto;
    overflow: visible;
    text-overflow: clip;
    white-space: nowrap;
    cursor: text;
}

/* flex-grow 20 (NIE 1) — w wierszu jest jeszcze .task-row-spacer z
   flex-grow:1 (dosuwa tag/Play/Usuń do prawej w trybie podglądu, patrz
   wyżej); przy równych 1:1 oba dzieliłyby wolne miejsce po połowie, więc
   pole edycji realnie wypełniało tylko połowę dostępnej szerokości
   (zgłoszone: "w trybie edycji nie korzysta" z poszerzonego panelu).
   Wysoki grow sprawia, że pole zgarnia niemal całość, spacer prawie nic. */
.task-title-input {
    flex: 20 1 60px;
    min-width: 60px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: text;
    background: transparent;
    color: inherit;
    border: none;
    border-bottom: 1px solid var(--wt-border);
    border-radius: 0;
    padding: 0.1rem 0;
    font-size: inherit;
}

.task-row-spacer {
    flex: 1 1 0;
    min-width: 0;
}

.task-title-input:focus {
    outline: none;
}

/* Zadanie ukończone — tytuł przekreślony, przygaszony, żeby cała sekcja
   "Ukończone" była wyraźnie odróżnialna na pierwszy rzut oka. */
.tasks-list-completed .task-title {
    text-decoration: line-through;
    opacity: 0.55;
}

/* --- Tag przypiętego projektu (klient/projekt) ------------------------------ */

/* display:inline-block (NIE inline-flex) + height/line-height RÓWNE sobie —
   ten sam wzorzec i to samo uzasadnienie co .multitasking-toggle (MULTI)
   wyżej w tym pliku: align-items:center na flexie centruje BOX, ale nie
   ratuje optycznego wyśrodkowania glifów (asymetryczny ascent/descent
   czcionki) — stąd zgłoszenie "nie wyśrodkowane, zrób jak w MULTI".
   padding-top/bottom celowo 0, cały pionowy rozmiar z height/line-height. */
.task-tag {
    flex-shrink: 0;
    display: inline-block;
    vertical-align: middle;
    max-width: 220px;
    height: 26px;
    line-height: 26px;
    padding: 0 0.65rem;
    background: transparent;
    color: #1c1e21;
    border: 1px solid var(--wt-border);
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 700;
}

/* filter, NIE background — kolor tła przypiętego tagu przychodzi jako STYL
   INLINE (patrz tagHtml() w tasks.js, kolor projektu), który zawsze wygrywa
   z regułą klasową niezależnie od :hover, więc samo `background` tu nie
   miałoby żadnego efektu na pokolorowanym tagu. */
.task-tag:hover {
    background: var(--wt-surface-hover);
    filter: brightness(0.92);
}

.task-tag-dot {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

/* Kolejność, w jakiej wiersz zadania oddaje miejsce, gdy go brakuje (na
   wyraźne życzenie, wcześniej ograniczone tylko do @media max-width:450px —
   teraz zachowanie bazowe, niezależne od szerokości okna):
   1. NAJPIERW kurczy się (aż do wizualnego zniknięcia) tag przypiętego
      projektu — jedyny element z flex-shrink:1 w wierszu.
   2. Gdy i to nie wystarczy, drag-handle/checkbox/strzałka opisu/Play/Usuń
      (wszystkie flex-shrink:0 w swoich bazowych regułach) i reszta tagu
      znikają za prawą krawędzią panelu — `overflow-x: hidden` na
      .tasks-panel-body (niżej) obcina, zamiast pokazywać poziomy scrollbar.
   3. Nazwa zadania (.task-title, `flex:0 0 auto`) NIGDY się nie kurczy/nie
      przycina — ma zostać w pełni czytelna zawsze kosztem punktów 1-2.
   `:not(.task-tag-empty)` celowo — ikonka "przypnij projekt" (brak
   przypiętego projektu) ma zostać w stałym rozmiarze, to jedyny sposób
   przypięcia, nie ma znikać. */
.task-tag:not(.task-tag-empty) {
    flex-shrink: 1;
    min-width: 0;
}

/* display:block (nie domyślny inline <span>) — .task-tag jest teraz
   inline-block (patrz wyżej), więc bez tego overflow/ellipsis by nie
   zadziałało; block wewnątrz inline-block wypełnia dostępną szerokość
   (ograniczoną przez max-width/padding rodzica), a line-height:inherit
   utrzymuje tę samą wysokość linii co .task-tag (technika centrowania z MULTI). */
.task-tag-text {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: inherit;
}

/* Brak przypiętego projektu — ikonka pinezki, TYM SAMYM stylem co .task-play
   obok niej (okrągły, bez obrys, zawsze widoczny — nie chowany za hover jak
   .task-delete: to jedyny sposób przypięcia projektu, ma być łatwo
   zauważalny, nie ukryty; zgłoszone jako "przycisk zniknął", gdy poprzednia
   wersja była schowana do hover). */
.task-tag-empty {
    width: 22px;
    height: 22px;
    line-height: 22px;
    max-width: none;
    padding: 0;
    border-radius: 50%;
    border-color: transparent;
    opacity: 0.55;
    color: var(--wt-text);
}

.task-tag-empty .icon {
    width: 13px;
    height: 13px;
    vertical-align: middle;
}

.task-tag-empty:hover {
    opacity: 1;
    background: var(--wt-surface-hover);
}

/* Uruchamia timer na przypiętym projekcie (patrz startProjectFromTag() w
   tasks.js) — widoczny zawsze (nie chowany za hover jak .task-delete), to
   akcja pierwszoplanowa, nie porządkowa. */
.task-play {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    background: transparent;
    color: var(--wt-text);
    opacity: 0.55;
    border-radius: 50%;
}

.task-play .icon {
    width: 12px;
    height: 12px;
}

.task-play:hover {
    opacity: 1;
    background: var(--wt-surface-hover);
}

.task-play:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

.task-delete {
    flex-shrink: 0;
    opacity: 0;
    color: var(--wt-text);
}

.task-row:hover .task-delete,
.task-row:focus-within .task-delete {
    opacity: 0.6;
}

.task-delete:hover {
    opacity: 1 !important;
    background: var(--wt-surface-hover);
}

/* Strzałka rozwijania dłuższego opisu (patrz descriptionToggleHtml() w
   tasks.js) — zawsze widoczna, obraca się o 180° gdy opis jest rozwinięty
   (ten sam efekt co strzałki list rozwijanych gdzie indziej w apce).
   .has-content (zadanie MA już opis) — kolor akcentu zamiast neutralnego
   przygaszonego, na wyraźne życzenie: ma być od razu widać, które zadania
   mają opis, bez rozwijania każdego po kolei. */
.task-description-toggle {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    color: var(--wt-text);
    opacity: 0.5;
}

/* Mocniejsze wyróżnienie niż sam kolor tekstu (zgłoszone) — pełne
   niebieskie kółko, biała strzałka w środku, ten sam niebieski co reszta
   akcentów w apce (#2f6feb). */
.task-description-toggle.has-content {
    background: #2f6feb;
    color: #fff;
    opacity: 1;
    border-radius: 50%;
}

.task-description-toggle.has-content:hover {
    background: #2559c2;
}

.task-description-toggle .icon {
    width: 14px;
    height: 14px;
    transition: transform 0.15s ease;
}

.task-description-toggle.expanded .icon {
    transform: rotate(180deg);
}

.task-description-toggle:hover {
    opacity: 1;
    background: var(--wt-surface-hover);
}

/* Osobny blok POD wierszem (nie wewnątrz .task-row) — ma zająć pełną
   szerokość panelu, nie tylko to, co zostaje w wąskim flex-rowie. */
.task-description-row {
    padding: 0 0.25rem 0.5rem;
}

/* Tryb podglądu (domyślny) — zwykły tekst (renderowany z linkifyDescription()
   w tasks.js, wykryte URL-e/ścieżki owinięte w .task-description-link +
   przycisk kopiowania) — klik gdziekolwiek indziej w tym bloku przełącza na
   .task-description-textarea (patrz startDescriptionEdit()). */
.task-description-view {
    padding: 0.5rem;
    background: var(--wt-bg);
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    font-size: 0.85rem;
    white-space: pre-wrap;
    word-break: break-word;
    cursor: text;
    min-height: 2.4em;
}

.task-description-empty {
    color: var(--wt-text);
    opacity: 0.4;
    font-style: italic;
}

.task-description-link {
    color: #2f6feb;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.task-description-copy {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    margin-left: 0.15rem;
    background: transparent;
    color: var(--wt-text);
    opacity: 0.55;
    border-radius: 4px;
    vertical-align: -3px;
}

.task-description-copy .icon {
    width: 12px;
    height: 12px;
}

.task-description-copy:hover {
    opacity: 1;
    background: var(--wt-surface-hover);
}

.task-description-copy.copied {
    color: #1b7f3a;
    opacity: 1;
}

.task-description-textarea {
    width: 100%;
    padding: 0.5rem;
    background: var(--wt-bg);
    color: inherit;
    border: 1px solid var(--wt-border);
    border-radius: 8px;
    font-size: 0.85rem;
    font-family: inherit;
    resize: vertical;
}

.task-description-textarea:focus {
    outline: none;
    border-color: #2f6feb;
}

.task-description-textarea::placeholder {
    color: var(--wt-text);
    opacity: 0.4;
}

/* Wiersz dodawania — zawsze ostatni w liście aktywnych zadań (patrz
   render() w tasks.js), nie kolejny formularz pod listą, żeby wyglądał jak
   naturalne przedłużenie listy, nie osobny element. */
.task-add-row {
    background: transparent !important;
}

.tasks-add-form {
    flex: 1 1 60px;
    min-width: 60px;
}

.tasks-add-input {
    width: 100%;
    padding: 0.15rem 0;
    background: transparent;
    color: inherit;
    border: none;
    border-radius: 0;
    font-size: 0.85rem;
}

.tasks-add-input:focus {
    outline: none;
}

.tasks-add-input::placeholder {
    color: var(--wt-text);
    opacity: 0.4;
}

.tasks-completed-toggle {
    margin-top: 0.4rem;
    padding: 0.2rem 0.25rem;
    background: none;
    color: var(--wt-text);
    opacity: 0.65;
    font-size: 0.8rem;
    font-weight: 400;
    border-radius: 0;
}

.tasks-completed-toggle:hover {
    background: none;
    opacity: 1;
}

.tasks-list-completed {
    margin-top: 0.15rem;
}

/* --- Popup wyboru projektu do przypięcia (modal, patrz report_edit.js dla
   tego samego wzorca — tworzony/usuwany z DOM, nie panel sterowany klasą) --- */

.task-pin-modal {
    max-width: 380px;
}

.task-pin-filter,
.task-pin-group-select {
    width: 100%;
    margin-top: 0.75rem;
}

.task-pin-clients {
    max-height: 40vh;
    overflow-y: auto;
    margin-top: 0.75rem;
}

.task-pin-client {
    margin-bottom: 0.75rem;
}

.task-pin-client:last-child {
    margin-bottom: 0;
}

.task-pin-client-name {
    margin: 0 0 0.35rem;
    font-size: 0.82rem;
    font-weight: 600;
    opacity: 0.75;
}

.task-pin-projects {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.task-pin-project-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.5rem;
    background: transparent;
    color: inherit;
    border-radius: 6px;
    font-size: 0.88rem;
    text-align: left;
}

.task-pin-project-option:hover {
    background: var(--wt-surface-hover);
}

.task-pin-empty {
    opacity: 0.6;
    font-size: 0.85rem;
}

/* "Kafle bez czasu jako obrys" (Moje ustawienia, dim_empty_tiles) — kafel
   z 0 min w przeglądanym dniu: tło strony + obramowanie 2px w kolorze
   projektu (na wyraźne życzenie; zastąpiło nieczytelne przygaszenie tła
   do 30%). !important — schematy szare i "Żywe kolory" też ustawiają
   tło/kolory z !important. Obramowanie jako inset box-shadow, żeby nie
   zmieniać rozmiaru kafla. Kafel aktywny nigdy nie ma tej klasy.
   .project-cell-empty ustawia app.js (renderAllElapsed). */
body.dim-empty-tiles .project-cell-empty .project-button,
body.dim-empty-tiles .project-cell-empty .project-button:hover {
    background-color: var(--wt-bg) !important;
    box-shadow: inset 0 0 0 2px var(--project-color);
    color: var(--wt-text) !important;
}

body.dim-empty-tiles .project-cell-empty .project-button .project-elapsed,
body.dim-empty-tiles .project-cell-empty .project-button .project-client-name {
    color: var(--wt-text) !important;
}
