/**
 * modal.css — okno modalne. Komponent wielokrotnego użytku.
 *
 * Para do assets/js/modal.js, gdzie stoi cała mechanika otwierania. Wymaga
 * tokens.css i base.css; obwódkę panelu bierze z components/ring.css.
 *
 * KOMPONENT DAJE SAMO OKNO — tło, panel, przycisk zamknięcia, przejście
 * i blokadę przewijania. Nie wie nic o tym, co w nim stanie. Zawartość
 * wstrzykuje ten, kto okno otwiera; dziś doradca kalkulatora i asystent AI.
 * Dlatego panel nie ma paddingu: odstępy przynosi treść, bo tylko ona wie,
 * czy jest paskiem nagłówkowym na całą szerokość, czy formularzem.
 *
 * DLACZEGO NATYWNY <dialog>, A NIE <div role="dialog">
 * Cztery rzeczy, które inaczej trzeba by napisać ręcznie i utrzymać
 * w dwóch niezależnych narzędziach:
 *   - TOP LAYER — okno stoi ponad wszystkim niezależnie od z-index.
 *     Makieta wpisuje z-index:300, żeby przebić sticky nagłówek
 *     (--dc-z-overlay: 50). Tu nie ma ani tej wartości, ani nowego tokenu.
 *   - INERT NA RESZCIE STRONY — to jest prawdziwa pułapka fokusu, nie jej
 *     imitacja. Ręczna pułapka (jak trapFocus() w header-menu.js) łapie
 *     wyłącznie Tab; nie łapie myszy ani wirtualnego kursora czytnika
 *     ekranu. Tu tło jest niedostępne dla wszystkich trzech naraz.
 *   - aria-modal IMPLICITE — atrybutu nie trzeba wypisywać w znaczniku.
 *   - ZWROT FOKUSU na wyzwalacz przy close() — mechanizm przeglądarki.
 *
 * ZNACZNIKI, których komponent oczekuje:
 *
 *   <button data-dc-modal-open="dc-nazwa">…</button>
 *
 *   <dialog class="dc-modal" id="dc-nazwa" aria-label="…">
 *     <div class="dc-modal__panel">
 *       <span class="dc-ring dc-ring--arc" aria-hidden="true"></span>
 *       <button class="dc-modal__close" data-dc-modal-close autofocus>…</button>
 *       … treść konsumenta …
 *     </div>
 *   </dialog>
 *
 * `id` jest tu wyjątkiem od reguły 4 i regułą 4 przewidzianym: okno nie
 * przechodzi przez pętlę template-styleguide.php, a wyzwalacz musi mieć czym
 * wskazać swój cel. `autofocus` na przycisku zamknięcia to domyślne wejście
 * fokusu — jeśli konsument oznaczy nim własne pole, przeglądarka wybierze je
 * sama, bez ani jednej linijki JS.
 *
 * STAN TRZYMA data-dc-modal-state, NIE ZMIENNA W JS
 * Skrypt przełącza atrybut, wszystkie wartości są tutaj — wzorem tilt-card.
 * Powód jest twardszy niż konsekwencja: dialog:not([open]) ma display:none,
 * a [open] przełącza się natychmiast w showModal(), więc czysto CSS-owe
 * przejście wymagałoby @starting-style i transition-behavior: allow-discrete
 * (Safari dopiero od 17.4). Osobny atrybut, zapalany klatkę po otwarciu,
 * działa wszędzie tam, gdzie działa <dialog> — czyli od 2022 r.
 */

/* ==========================================================================
   1. OKNO — tło strony pod panelem
   home.html:1103. W makiecie to <div position:fixed;inset:0>, tu sam <dialog>
   rozciągnięty na okno przeglądarki: przejmuje rolę nakładki, więc kliknięcie
   poza panelem trafia w niego i tylko w niego (modal.js porównuje event.target).
   ========================================================================== */

.dc-modal {
  /* Zdjęcie domyślnych stylów przeglądarki: <dialog> ma własny margin auto,
     max-width/max-height fit-content, obramowanie, padding i białe tło. */
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: var(--dc-space-0);
  border: none;
  color: var(--dc-text);

  /* Oddech między panelem a krawędzią ekranu. Na 390 px to jedyne, co dzieli
     okno od brzegu — panel ma width:100%, więc bez tego dotykałby krawędzi. */
  padding: var(--dc-space-18);

  /* Przyciemnienie i rozmycie siedzą NA <dialog>, nie na ::backdrop, choć
     ::backdrop wygląda na miejsce właściwe. Powód jest zgodnościowy:
     dziedziczenie custom properties do ::backdrop weszło do przeglądarek
     dopiero w 2024 r., a wcześniej var(--dc-overlay) po prostu nic tam
     nie ustawiało — po cichu, bez błędu. <dialog> jest zwykłym elementem
     drzewa i tokeny dziedziczy normalnie. ::backdrop dostaje niżej samą
     przezroczystość, bo domyślny arkusz przeglądarki maluje go
     rgba(0,0,0,.1) i to by się dołożyło do naszego tuszu. */
  background-color: var(--dc-overlay);

  /* home.html:1103 dosłownie, bez scalenia do --dc-blur-md (blur(16px)
     saturate(175%)). Promień rozmycia jest ten sam, ale nasycenie NIE JEST
     wartością nierozróżnialną — 175% zamiast 125% prześwietliło fuel-switch
     na żółto (decisions.md, 2026-08-28). Tu warstwa leży nad CAŁĄ stroną,
     więc podbicie o 50 punktów poszłoby na każdy piksel makiety naraz.
     Trzeci przypadek kandydata „rodzina rozmyć bez saturate()". */
  -webkit-backdrop-filter: blur(16px) saturate(125%);
  backdrop-filter: blur(16px) saturate(125%);

  /* Okno nie przewija się samo — przewija się treść w panelu, bo tylko ona
     wie, która jej część ma zostać na widoku (w makiecie: lista wiadomości
     między paskiem nagłówkowym a polem wpisywania). */
  overflow: hidden;

  /* dcFade z home.html:27 — .25s ease na kryciu. */
  transition: opacity var(--dc-duration-slow) var(--dc-ease);
}

/* Stan zamknięty zostaje na display:none z arkusza przeglądarki, dlatego
   display ustawiamy WYŁĄCZNIE pod [open]. Deklaracja autora bije deklarację
   przeglądarki niezależnie od specyficzności, więc display:flex zapisany
   na samej klasie pokazywałby okno także zamknięte. */
.dc-modal[open] {
  display: flex;
  align-items: center;
  justify-content: center;

  /* Klatka wyjściowa przejścia. [open] jest już ustawione, ale
     data-dc-modal-state jeszcze nie — okno jest w drzewie i niewidoczne. */
  opacity: 0;
}

.dc-modal[data-dc-modal-state='open'] {
  opacity: 1;
}

/* Domyślny arkusz przeglądarki maluje ::backdrop na rgba(0, 0, 0, .1).
   Tusz jest na <dialog> (patrz wyżej), więc tło pod nim musi zejść do zera —
   inaczej oba przyciemnienia się sumują. transparent to słowo kluczowe,
   nie wartość projektowa. */
.dc-modal::backdrop {
  background-color: transparent;
}

/* ==========================================================================
   2. PANEL — samo okno
   home.html:1104 (obrys, cień, wjazd) i 1106 (szkło, wysokość).
   ========================================================================== */

.dc-modal__panel {
  /* Oba wymagane przez ring.css: warstwa obwódki jest position:absolute
     i musi mieć się do czego przyczepić, a isolation daje jej własny
     kontekst nakładania, żeby nie przebiła treści panelu. */
  position: relative;
  isolation: isolate;

  display: flex;
  flex-direction: column;
  width: 100%;

  /* WYMIARY JEDNEGO ELEMENTU, celowo literałami — nie każda liczba jest
     tokenem. 540 px leży między --dc-measure-md (480) a --dc-measure-lg
     (640), 60 px od bliższego, więc scalenie zmieniłoby wygląd; a token
     powstaje dla wartości wielokrotnego użytku albo osobnej intencji
     (reguła 15), nie dla szerokości jednego okna. 84vh to ułamek okna
     przeglądarki — w tokens.css nie ma takiej kategorii i nie powinna
     powstać dla jednego użycia. Obie wprost z makiety. */
  max-width: 540px;
  max-height: 84vh;

  /* Wysokość dyktuje treść, ale nie może zejść poniżej miejsca na własny
     przycisk zamknięcia — ten jest ułożony bezwzględnie, więc nie rozpycha
     panelu i przy krótszej treści zostałby przycięty przez overflow:hidden
     niżej. Wyliczone z odstępu przycisku od krawędzi (raz u góry, raz
     u dołu) i jego wysokości; żadnej nowej liczby. */
  min-height: calc( var(--dc-space-16) * 2 + var(--dc-space-40) );

  /* Makieta ma dwa promienie: 26 px na obrysie i 24 px na szkle pod nim,
     bo rysuje obwódkę paddingiem 1,6 px. .dc-ring--arc rysuje ją rantem
     z maską NA TYM SAMYM pudełku, więc promień jest jeden. Oba wpadają
     w pasmo --dc-radius-28 (24 / 26 / 28 / 30 / 32 / 34). */
  border-radius: var(--dc-radius-28);
  overflow: hidden;

  /* Nieprzezroczysta powierzchnia w obu motywach. Makieta ciemna ma tu
     dokładnie #1C1A17, czyli --dc-surface co do znaku; jasna ma biel .9,
     scaloną do bieli pełnej. Dzięki temu panel nie ma ani jednej reguły
     [data-theme] (reguła 10).

     ŚWIADOMIE BEZ backdrop-filter, które makieta wpisuje w obu wersjach:
     pod nieprzezroczystym tłem rozmycie tła jest niewidoczne. Dowodzi tego
     sama makieta ciemna — trzyma blur(40px) pod pełnym #1C1A17, gdzie nie
     ma szans nic zrobić. Rozmycie strony daje warstwa wyżej (sekcja 1),
     i to ona jest w tym oknie widoczna. */
  background-color: var(--dc-surface);

  /* Reguła 13 — cień rzucany plus wewnętrzne rozjaśnienie krawędzi. */
  box-shadow: var(--dc-shadow-3xl), var(--dc-shadow-inset-top);

  /* dcRise z home.html:28 — .32s na krzywej makiety, scalone do
     --dc-duration-slower (.35s). */
  transition:
    opacity var(--dc-duration-slower) var(--dc-ease-out),
    transform var(--dc-duration-slower) var(--dc-ease-out);
}

/* Klatka wyjściowa wjazdu: panel wchodzi z dołu i z lekkiego pomniejszenia.
   18 px to --dc-space-18; .97 zostaje liczbą, bo skala nie ma jednostki
   ani kategorii w tokens.css — to geometria ruchu, jak scale3d(1.02)
   w tilt-card.css. */
.dc-modal[open] .dc-modal__panel {
  opacity: 0;
  transform: translateY(var(--dc-space-18)) scale(0.97);
}

.dc-modal[data-dc-modal-state='open'] .dc-modal__panel {
  opacity: 1;
  transform: none;
}

/* ==========================================================================
   3. PRZYCISK ZAMKNIĘCIA
   home.html:1113. W makiecie stoi jako ostatni element paska nagłówkowego;
   tu jest ułożony bezwzględnie, bo pasek nagłówkowy należy do treści,
   a zamykanie do okna. Konsument rezerwuje pod niego miejsce własnym
   odstępem z prawej — komponent nie ma jak tego zrobić za niego, nie
   wiedząc, co w oknie stanie.
   ========================================================================== */

.dc-modal__close {
  position: absolute;

  /* Nad obwódką (--dc-z-base w ring.css) i nad treścią. */
  z-index: var(--dc-z-raised);

  /* Odstęp od krawędzi panelu = padding paska nagłówkowego z makiety
     (16 px w pionie, 18 px w poziomie). Przycisk ląduje więc dokładnie
     tam, gdzie w makiecie, mimo innego sposobu ułożenia. */
  inset-block-start: var(--dc-space-16);
  inset-inline-end: var(--dc-space-18);

  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--dc-space-40);
  height: var(--dc-space-40);
  padding: var(--dc-space-0);

  border: var(--dc-border) solid var(--dc-border-subtle);
  border-radius: var(--dc-radius-full);
  background-color: var(--dc-surface-glass);
  box-shadow: var(--dc-shadow-md), var(--dc-shadow-inset-top);
  color: var(--dc-text-secondary);
  cursor: pointer;

  transition:
    background-color var(--dc-duration-base) var(--dc-ease),
    color var(--dc-duration-base) var(--dc-ease),
    transform var(--dc-duration-base) var(--dc-ease);
}

/* style-hover z makiety przeniesiony świadomie: reguła 14 każe sprawdzić,
   czy element jest interaktywny, zanim się go przeniesie — a to jest
   przycisk, więc obietnica interaktywności jest prawdziwa. */
.dc-modal__close:hover {
  background-color: var(--dc-surface);
  color: var(--dc-text);
  transform: rotate(90deg);
}

/* Kliknięcie = nacisk (ranga 4, przycisk ikonowy — design-audit.md, C.1):
   zmniejszenie plus cień wciśnięcia dołożony do cienia spoczynku. Pod
   kursorem krzyżyk jest już obrócony, więc obrót zostaje w transformacji —
   bez niego naciśnięcie cofałoby go skokiem; na ekranie dotykowym (bez
   najazdu) jest samo zmniejszenie. */
.dc-modal__close:active {
  transform: scale(0.97);
  box-shadow: var(--dc-shadow-md), var(--dc-shadow-inset-press);
}

.dc-modal__close:hover:active {
  transform: rotate(90deg) scale(0.97);
}

/* ==========================================================================
   4. BLOKADA PRZEWIJANIA STRONY POD OKNEM
   showModal() blokuje interakcję z tłem, ale NIE przewijanie — top layer
   nie ma z przewijaniem nic wspólnego. Makieta nie rozwiązuje tego wcale:
   nie ma w home.html ani overflow:hidden na body, ani scrollbar-gutter.
   ========================================================================== */

/* Atrybut i zmierzona szerokość paska trafiają na <html> z modal.js.

   SKOK UKŁADU: samo overflow:hidden zabiera pionowy pasek przewijania,
   a wtedy obszar treści rośnie o jego szerokość i cała strona przeskakuje
   w prawo w tej samej klatce, w której otwiera się okno. Padding oddaje
   dokładnie tyle, ile zabrał pasek — liczbę mierzy skrypt, bo szerokość
   paska zależy od systemu i ustawień użytkownika, nie od projektu.
   Nagłówek jest position:sticky, czyli zostaje w normalnym przepływie
   i przesuwa się razem z resztą, więc też nie skacze.

   Odrzucona alternatywa: scrollbar-gutter: stable na html w base.css.
   Jedna linijka zamiast tego mechanizmu, ale rezerwuje pas na KAŻDEJ
   stronie, także krótkiej i bez paska — zmiana globalna dla efektu
   potrzebnego w dwóch miejscach. */
html[data-dc-modal-lock] {
  overflow: hidden;
  padding-inline-end: var(--dc-modal-scrollbar, var(--dc-space-0));
}

/* ==========================================================================
   5. ZGODA NA RUCH — reguła 12
   Komponent nie ma ruchu ciągłego (obrót obwódki zatrzymuje ring.css
   własnym blokiem), więc chodzi wyłącznie o przejścia.

   ZDEJMUJEMY transition-property, NIE transition-duration. Globalny wyłącznik
   z base.css tnie czas do 0,01 ms znacznikiem !important, więc każdy zapis
   czasu w tym pliku byłby martwy — przegrałby z nim niezależnie od
   specyficzności. Właściwości globalny blok nie dotyka, a jej zdjęcie znaczy
   „nie ma tu żadnego przejścia" mocniej i niezależnie od tego, co robi
   base.css.

   Musi iść w parze z modal.js, który czyta tę samą media query: bez przejścia
   nie ma zdarzenia transitionend, więc w trybie spokojnym skrypt nie ma na co
   czekać i zamyka okno od razu. Gdyby jedno z dwóch zostało bez drugiego,
   zamykanie wisiałoby aż do czasu zapasowego.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .dc-modal,
  .dc-modal__panel,
  .dc-modal__close {
    transition-property: none;
  }
}
