/* =========================================================================
   WARSTWA RUCHU — WSPÓLNA DLA OBU SZABLONÓW (28.07.2026)

   To jest JEDYNE miejsce, w którym definiujemy wzorce ruchu. Wartości (czasy,
   krzywe, dystans) przychodzą z tokenów wstrzykiwanych na `<html>` przez layout
   — dokładnie tą samą drogą co kolory palety (App\Support\Front\SiteMotion).
   Nazwy zmiennych są w obu szablonach TE SAME, więc reguły niżej są wspólne,
   a szablony różnią się wyłącznie wartościami.

   ANIMUJEMY WYŁĄCZNIE `opacity` I `transform`. Nigdy wysokości, szerokości,
   pozycji, marginesów ani odstępów: układ przeliczany w każdej klatce szarpie
   na słabszym telefonie, a parafianie mają stare urządzenia.
   ========================================================================= */

/* ---- Zapas, gdyby layout nie wstrzyknął tokenów (np. widok poza frontem) ---- */
:root {
  --motion-state: 200ms;
  --motion-enter: 420ms;
  --motion-expand: 520ms;
  --motion-ease-enter: cubic-bezier(0.22, 0.61, 0.36, 1);
  --motion-ease-state: cubic-bezier(0.25, 0.1, 0.25, 1);
  --motion-rise: 10px;
  --motion-stagger: 70ms;
  --motion-parallax: 40px;
}

/* =========================================================================
   WZORZEC 1 — POJAWIENIE SIĘ TREŚCI

   TREŚĆ NIGDY NIE STARTUJE JAKO NIEWIDOCZNA. Element oznaczony
   `data-motion-enter` w stanie wyjściowym wygląda NORMALNIE — nie ma tu żadnej
   reguły, która by go ukrywała. Ukrycie włącza dopiero `html[data-motion-ready]`,
   a ten atrybut ustawia skrypt (front/partials/motion-boot). Gdy skrypt się nie
   wykona — bo jest wyłączony, wywalił się albo przeglądarka nie zna
   IntersectionObserver — widać całą stronę, a nie pustkę.

   Element animuje się RAZ: po odsłonięciu obserwator przestaje go śledzić,
   więc powrót w pole widzenia niczego nie odtwarza.
   ========================================================================= */
html[data-motion-ready] [data-motion-enter] {
  transition:
    opacity var(--motion-enter) var(--motion-ease-enter),
    transform var(--motion-enter) var(--motion-ease-enter);
}
html[data-motion-ready] [data-motion-enter]:not([data-motion-in]) {
  opacity: 0;
  transform: translateY(var(--motion-rise));
}
html[data-motion-ready] [data-motion-enter][data-motion-in] {
  opacity: 1;
  transform: none;
}

/* ---- WARIANT LISTOWY: pas wchodzi, a jego elementy doganiają go po kolei ----

   `data-motion-enter-list` na KONTENERZE zamiast atrybutu na każdym dziecku.
   Powód jest praktyczny: pasy strony głównej i wiersze archiwum powstają
   w pętlach po widokach modułów, więc znakowanie pojedynczych elementów
   znaczyłoby dopisanie atrybutu w kilkunastu plikach — i pominięcie go w tym,
   który powstanie jutro. Kontener obowiązuje wszystkie dzieci, także przyszłe.

   Reguła jest CSS-owa, nie skryptowa: gdyby atrybuty dokładał JavaScript po
   wczytaniu, treść mrugnęłaby (pokazać → schować → odsłonić).

   KASKADA MA TWARDY SUFIT. Po piątym elemencie opóźnienie przestaje rosnąć,
   więc ogon kaskady nigdy nie przekracza `--motion-enter` (5 × 70 ms = 350 ms
   przy 420 ms wejścia w Klasyku). Bez sufitu dwunasty kafel długiej listy
   czekałby prawie sekundę — a to już nie jest „wchodzi", tylko „się zacina". */
html[data-motion-ready] [data-motion-enter-list] > * {
  transition:
    opacity var(--motion-enter) var(--motion-ease-enter),
    transform var(--motion-enter) var(--motion-ease-enter);
}
html[data-motion-ready] [data-motion-enter-list] > *:not([data-motion-in]) {
  opacity: 0;
  transform: translateY(var(--motion-rise));
}
html[data-motion-ready] [data-motion-enter-list] > *[data-motion-in] {
  opacity: 1;
  transform: none;
}
html[data-motion-ready] [data-motion-enter-list] > *:nth-child(2) { transition-delay: calc(var(--motion-stagger) * 1); }
html[data-motion-ready] [data-motion-enter-list] > *:nth-child(3) { transition-delay: calc(var(--motion-stagger) * 2); }
html[data-motion-ready] [data-motion-enter-list] > *:nth-child(4) { transition-delay: calc(var(--motion-stagger) * 3); }
html[data-motion-ready] [data-motion-enter-list] > *:nth-child(5) { transition-delay: calc(var(--motion-stagger) * 4); }
/* Sufit: piąty i każdy następny czeka tyle samo. */
html[data-motion-ready] [data-motion-enter-list] > *:nth-child(n + 6) { transition-delay: calc(var(--motion-stagger) * 5); }

/* =========================================================================
   WZORZEC 2 — ZMIANA STANU

   Najechanie, wciśnięcie, fokus. Reguły stanów mieszkają w arkuszach szablonów
   (to ich wygląd), a stąd biorą wyłącznie CZAS i KRZYWĄ. Te dwa aliasy istnieją
   po to, żeby dało się je zapisać jednym skrótem tam, gdzie przejście dotyczy
   całej deklaracji.
   ========================================================================= */
.motion-state {
  transition-duration: var(--motion-state);
  transition-timing-function: var(--motion-ease-state);
}

/* =========================================================================
   WZORZEC 3 — ROZWINIĘCIE

   Element zwinięty staje się widoczny. BEZ ANIMOWANIA WYSOKOŚCI: zamknięty stan
   to `visibility: hidden` + przezroczystość + przesunięcie, a miejsce w układzie
   rozstrzyga arkusz szablonu (np. `display`), nie ten plik. `visibility` zmienia
   się skokowo — z opóźnieniem przy zamykaniu, żeby treść zdążyła zgasnąć, i bez
   opóźnienia przy otwieraniu.

   Tak jak wyżej: ukrycie działa dopiero, gdy skrypt oznaczy dokument jako gotowy.
   Bez skryptu przełącznik i tak by nie zadziałał, więc treść zostaje widoczna.
   ========================================================================= */
html[data-motion-ready] [data-motion-reveal] {
  transition:
    opacity var(--motion-expand) var(--motion-ease-enter),
    transform var(--motion-expand) var(--motion-ease-enter),
    visibility 0s linear 0s;
}
html[data-motion-ready] [data-motion-reveal]:not([data-motion-open]) {
  opacity: 0;
  visibility: hidden;
  transform: translateY(calc(var(--motion-rise) * -1));
  transition-delay: 0s, 0s, var(--motion-expand);
}
html[data-motion-ready] [data-motion-reveal][data-motion-open] {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* =========================================================================
   SYSTEMOWE OGRANICZENIE RUCHU — JEDNO MIEJSCE, POZIOM TOKENÓW

   `!important` nie jest tu ozdobą: tokeny są wstrzykiwane INLINE na `<html>`,
   a styl inline wygrywa ze zwykłą regułą arkusza. Bez `!important` ta blokada
   nie zadziałałaby wcale.

   Druga, mocniejsza blokada jest w skrypcie: przy włączonym ograniczeniu ruchu
   NIE ustawia on `data-motion-ready`, więc nic się nigdy nie ukrywa i nie ma
   czego odsłaniać — elementy pojawiające się przy przewijaniu są po prostu
   widoczne od razu.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-state: 0.01ms !important;
    --motion-enter: 0.01ms !important;
    --motion-expand: 0.01ms !important;
    --motion-rise: 0px !important;
    --motion-stagger: 0s !important;
    /* Zerowy zakres = paralaksa przestaje istnieć, bez osobnej reguły przy
       samym efekcie. */
    --motion-parallax: 0px !important;
  }

  /* SIATKA BEZPIECZEŃSTWA dla ruchu, który NIE idzie przez tokeny (zaszyte
     czasy przejść w arkuszach szablonów). Jeden zapis dla OBU szablonów —
     oba ładują ten plik.

     Do 31.07.2026 były trzy różne: tokeny tutaj, `transition-duration: 0.01ms`
     w Klasyku i `transition: none` w Nowoczesnym. Ta trzecia jest gorsza od
     pozostałych: całkowicie wyłączony przejście NIE emituje zdarzenia
     `transitionend`, więc skrypt, który na nie czeka, zawisa. Dlatego wszędzie
     SKRACAMY czas do zera, zamiast wyłączać mechanizm.

     `scroll-behavior: auto` odbiera płynne przewijanie przy skokach do kotwic —
     to też jest ruch, którego użytkownik prosił nie robić. */
  *, *::before, *::after {
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}
