/* ═══════════════════════════════════════════════════════════════════════════
   PTS - ruch i przejścia

   Zasada nadrzędna: ruch ma prowadzić wzrok, a nie zabierać czas. Nic tutaj
   nie opóźnia dostępu do treści, nic nie miga i nic nie wymaga czekania.
   Każdy efekt ma stan spoczynku, w którym strona jest w pełni czytelna,
   więc wyłączony JavaScript albo `prefers-reduced-motion` nie zostawiają
   pustych miejsc.

   Ładowany po `wizual.css`.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══ NAGŁÓWEK, KTÓRY SIĘ ODKLEJA ════════════════════════════════════════
   Nad pierwszym ekranem pasek jest listwą przy krawędzi okna. Po zejściu
   niżej odrywa się od krawędzi i unosi jako panel: strona pod nim przestaje
   być przecięta linią przez całą szerokość, a nawigacja zaczyna wyglądać
   jak coś, co leży NA treści, a nie jak rama wokół niej.

   Stan przełącza `app.js` klasą, a nie samo przewijanie: przejście ma się
   zdarzyć raz na przekroczenie progu.                                      */
.nav {
  transition: background-color 0.4s var(--ease),
              box-shadow 0.4s var(--ease),
              padding-top 0.45s var(--ease-out);
}

.nav__inner {
  transition: max-width 0.5s var(--ease-out),
              padding-inline 0.5s var(--ease-out),
              border-radius 0.45s var(--ease),
              background-color 0.4s var(--ease),
              box-shadow 0.45s var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  .nav, .nav__inner { transition: none; }
}

/* ═══ ZDJĘCIE HERO ODDYCHA ═══════════════════════════════════════════════
   Bardzo powolne zbliżenie. Ruch jest na granicy dostrzegalności i o to
   chodzi: pierwszy ekran przestaje być fotografią, a zaczyna być ujęciem.
   Parallax przy przewijaniu obsługuje `ruch.php`, więc tutaj tylko skala. */
.hero-blok:not(.ma-przod) .wp-block-cover__image-background {
  animation: pts-oddech 24s ease-out forwards;
}

@keyframes pts-oddech {
  from { transform: scale(1.18); }
  to   { transform: scale(1.06); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-blok .wp-block-cover__image-background { animation: none; }
}

/* ═══ OŚ SEKCJI ══════════════════════════════════════════════════════════
   Pionowa oś ze szczeblami po lewej stronie: pokazuje, gdzie jest czytelnik
   i pozwala przeskoczyć do sekcji. Rysunek jest wprost z toru kolejowego,
   więc mówi o firmie coś, czego nie powie żaden pasek postępu.

   Buduje ją `app.js` na podstawie sekcji obecnych na stronie, więc dodanie
   sekcji w edytorze dokłada szczebel samo. Bez JavaScriptu osi po prostu
   nie ma i nic z treści nie ginie.                                         */
.os {
  position: fixed;
  left: clamp(1rem, 2.2vw, 2.5rem);
  top: 50%;
  transform: translateY(-50%);
  z-index: 40;
  display: none;
  align-items: center;
  gap: 0.9rem;
  pointer-events: none;
}

/* Prog liczony od szerokosci kolumny tresci, nie z oka: przy 1200 px os
   siadala na pierwszej kolumnie rejestru i pionowy napis wchodzil w nazwe
   pozycji. Kolumna ma 1280 px, wiec os potrzebuje okna szerszego o dwa
   swoje pasy. */
@media (min-width: 1560px) {
  .os { display: flex; }
}

.os__tor {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  pointer-events: auto;
}

/* Szczebel jest przyciskiem, więc musi mieć pole trafienia większe niż
   sama kreska. Kreskę rysuje pseudoelement, a przycisk zostaje przezroczysty
   i wysoki na tyle, żeby dało się w niego trafić myszą. */
.os__szczebel {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  width: 1.6rem;
  height: 0.9rem;
  display: grid;
  place-items: center;
  cursor: pointer;
}

.os__szczebel::before {
  content: "";
  width: 100%;
  height: 2px;
  background: var(--navy-line);
  transform-origin: left center;
  transition: background-color 0.25s ease, transform 0.25s ease;
}

.os__szczebel:hover::before,
.os__szczebel:focus-visible::before {
  background: var(--orange);
  transform: scaleX(1.3);
}

.os__szczebel[aria-current="true"]::before {
  background: var(--orange);
}

/* Na ciemnym tle szczeble muszą być jaśniejsze od tła, nie ciemniejsze. */
.os.na-ciemnym .os__szczebel::before { background: rgba(255, 255, 255, 0.28); }
.os.na-ciemnym .os__szczebel[aria-current="true"]::before,
.os.na-ciemnym .os__szczebel:hover::before { background: var(--orange); }

/* Znacznik pozycji: pomarańczowy prostokąt jadący po torze. Przesuwa go
   `app.js`, ustawiając `--pozycja` w pikselach. */
.os__znacznik {
  position: absolute;
  left: -0.35rem;
  top: 0;
  width: 0.7rem;
  height: 0.9rem;
  background: var(--orange);
  transform: translateY(var(--pozycja, 0px));
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}

.os__napis {
  writing-mode: vertical-rl;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-3);
  max-height: 16rem;
  overflow: hidden;
  white-space: nowrap;
  transition: color 0.25s ease;
}

.os.na-ciemnym .os__napis { color: var(--on-navy-3); }

@media (prefers-reduced-motion: reduce) {
  .os__znacznik { transition: none; }
}

/* ═══ WEJŚCIE NAGŁÓWKÓW SEKCJI ═══════════════════════════════════════════
   Nagłówek wjeżdża spod maski, tak samo jak nagłówek hero. Maska ma zapas
   z góry i z dołu, bo interlinia nagłówków jest ciasna i pole wiersza bywa
   niższe od liter, które w nim stoją; bez zapasu obcinałaby ogonki.        */
.js-motion .section h2[data-anim-tekst] > span {
  transform: translateY(105%);
}

.section h2[data-anim-tekst] {
  display: block;
  overflow: hidden;
  padding-block: 0.14em;
  margin-block: -0.14em;
}

.section h2[data-anim-tekst] > span {
  display: block;
  transition: transform 0.85s cubic-bezier(0.16, 1, 0.3, 1);
}

.section h2[data-anim-tekst].jest > span {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js-motion .section h2[data-anim-tekst] > span { transform: none; }
  .section h2[data-anim-tekst] > span { transition: none; }
}

/* ═══ PRZEJŚCIE MIĘDZY PODSTRONAMI ═══════════════════════════════════════
   Natywne przejście widoku przeglądarki. Przeglądarki, które go nie znają,
   po prostu przeładowują stronę jak dotąd - nie ma tu żadnej zależności
   ani kodu do utrzymania. Ruch jest krótki, bo dłuższy zamienia się
   w oczekiwanie.                                                          */
@view-transition {
  navigation: auto;
}

::view-transition-old(root) {
  animation: pts-zanik 0.22s ease-out both;
}

::view-transition-new(root) {
  animation: pts-wejscie 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes pts-zanik {
  to { opacity: 0; }
}

@keyframes pts-wejscie {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* ═══ WYRÓWNANIE ═════════════════════════════════════════════════════════
   Sekcje idą do lewej krawędzi kolumny, tak jak na landingu produktu.
   Wyśrodkowany blok tekstu wygląda na zaproszenie, a nie na materiał
   techniczny, i przy dłuższym akapicie oko gubi początek wiersza.
   Wezwanie do kontaktu zostaje wyśrodkowane celowo: tam chodzi o jeden
   gest, a nie o czytanie.                                                  */
article .section,
article .section h2,
article .section > p,
article .section .wp-block-column > p,
article .section .card,
article .section .card h3,
article .section .card p {
  text-align: left;
}

article .section .wp-block-list { text-align: left; }


/* ═══ RUCH STEROWANY PRZEWIJANIEM ════════════════════════════════════════
   Wszystko poniżej liczy przeglądarka z pozycji elementu w oknie, bez
   udziału JavaScriptu (`animation-timeline`). Różnica wobec warstwy
   `[data-anim]` jest widoczna od razu: tamta odpala przejście raz, kiedy
   element wejdzie w kadr, ta wiąże obraz z ruchem kółka myszy. Strona
   przestaje być zbiorem slajdów, które się wyświetlają, a staje się czymś,
   co reaguje na czytanie.

   Warstwa jest w całości nadmiarowa. Przeglądarka, która nie zna osi
   przewijania, dostaje warstwę `[data-anim]` z `app.js` i widzi to samo,
   tylko skokowo. Przy prośbie o ograniczenie ruchu nie startuje nic z tego.

   Uwaga przy zmianach: animacja ma pierwszeństwo przed zwykłą deklaracją
   w arkuszu, więc każdy `transform` przypisany tu elementowi wyłącza jego
   `transform` z pozostałych plików. Stąd wykluczenia przy zdjęciach, które
   mają własne powiększenie na najechanie.                                  */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* ── Wejście sekcji ───────────────────────────────────────────────────
       Zastępuje skok `[data-anim].is-in`: ten sam gest, tylko rozłożony na
       drogę przewijania. Kierunki naprzemienne z `ruch.php` schodzą z drogi
       - sekcja pełnej szerokości wjeżdżająca bokiem i tak musiała je mieć
       zamienione na pionowe, żeby nie wypychać strony poza krawędź.        */
    .js-motion [data-anim] {
      animation: pts-wjazd linear both;
      animation-timeline: view();
      animation-range: entry 4% entry 52%;
    }

    /* Kolumny wewnątrz sekcji mają własną oś, więc kaskada bierze się sama
       z tego, że stoją w różnych miejscach - bez opóźnień liczonych w PHP.
       Droga jest krótsza niż u rodzica, inaczej wejścia mnożą się przez
       siebie i kolumna dochodzi do pełnej widoczności długo po sekcji.     */
    .js-motion .wp-block-column[data-anim] {
      animation-name: pts-wjazd-krotki;
      animation-range: entry 12% entry 62%;
    }

    /* ── Rejestr, tor i zakres: wiersz po wierszu ─────────────────────────
       Pozycje rejestru są wierszami tabeli, a nie kartami, więc wchodzą jak
       wiersze: jedna po drugiej, w rytmie przewijania.                     */
    .js-motion :is(.rejestr__poz, .tor__przystanek, .zakres__grupa, .ramy li, .obszary__poz) {
      animation: pts-wiersz linear both;
      animation-timeline: view();
      /* Okno skrócone (Iwona, 2026-09-08: „coś haczy przy skrolowaniu" na
         wartościach): przy 58% wysoki wiersz jechał i doganiał czytelnika
         przez pół ekranu. Teraz domyka się zaraz po wejściu w kadr. */
      animation-range: entry 5% entry 32%;
    }

    /* Węzeł na torze zapala się dopiero wtedy, gdy przystanek jest czytany.
       Sam tor rysunkowo jest linią kolejową, więc światło idzie po niej
       tak, jak jedzie pociąg.                                              */
    .js-motion .tor .tor__przystanek::before {
      animation: pts-wezel linear both;
      animation-timeline: view();
      animation-range: entry 20% entry 70%;
    }

    /* ── Hero warstwowe: przestrzeń z dwóch kopii kadru ────────────────
       Pierwszy plan ucieka szybciej niż tło, bo tak zachowuje się to, co
       jest bliżej oka. Napis stoi między nimi i wychodzi zza pojazdu
       w miarę przewijania - różnica dróg jest całym efektem głębi.

       Oś to oś dokumentu, a nie elementu: hero stoi na górze strony, więc
       jego własny zakres widoczności zaczyna się w połowie, zanim ktokolwiek
       cokolwiek przewinie.                                                  */
    .js-motion .hero-blok.ma-przod .wp-block-cover__image-background {
      animation: pts-hero-tlo linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100vh;
    }

    .js-motion .hero-blok__przod {
      animation: pts-hero-przod linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100vh;
    }

    /* ── Hero: kamera odjeżdża ────────────────────────────────────────────
       Treść pierwszego ekranu odpływa w górę i gaśnie, zamiast po prostu
       wyjechać poza kadr.

       Oś jest osią samej treści, a nie dokumentu. Przy osi dokumentu zanik
       liczył się od pierwszego piksela przewinięcia i przyciski gasły,
       stojąc jeszcze w połowie ekranu. Tutaj punktem odniesienia jest
       wyjście treści górą, z niewielkim wyprzedzeniem.                     */
    .js-motion .hero-blok .wp-block-cover__inner-container {
      animation: pts-hero-odjazd linear both;
      animation-timeline: view();
      animation-range: exit 15% exit 100%;
    }

    /* Podpowiedź „przewiń" znika po pierwszym obrocie kółka - dalej nie ma
       już czego podpowiadać. */
    .js-motion .hero-blok__scroll {
      animation: pts-gasnie linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 22vh;
    }

    /* ── Siatka w tle sekcji granatowej dryfuje ──────────────────────────
       Siatka modułowa z księgi (s. 17) przesuwa się wolniej niż treść, więc
       sekcja zyskuje głębię, mimo że nic w niej nie zmienia rozmiaru.
       Ruch jest w skali jednej kratki; większy zaczyna migotać na krawędzi
       maski.                                                               */
    .js-motion .section--navy::before {
      /* Zapas nad i pod sekcją, bo przesuwana warstwa musi mieć czym zakryć
         krawędź. Ruch idzie transformem, a nie pozycją tła: pozycja tła
         odmalowuje całą sekcję przy każdej klatce przewijania. */
      inset: -3rem 0;
      animation: pts-dryf linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    /* ── Zdjęcia żyją w kadrze ────────────────────────────────────────────
       Kadr przesuwa się wolniej niż strona, więc zdjęcie zachowuje się jak
       widok za oknem, a nie jak naklejka. Powiększenie jest stałe i musi
       być większe od wychylenia, inaczej przy krawędzi wychodzi tło.

       Bez zdjęć z powiększeniem na najechanie (`.ph--zoom`) i bez galerii:
       tam `transform` należy już do innego efektu.                         */
    .js-motion article .section figure.wp-block-image.ph:not(.ph--zoom) > img,
    .js-motion .opowiesc__kadr > img {
      animation: pts-kadr linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    /* Akapit wiodący rozjaśniający się przy czytaniu usunięty (Iwona,
       2026-09-07): dwutonowy tekst czytał się jak dwa różne kolory pisma,
       a na białej tablicy dofinansowań gradient z tokenów ciemnej skóry
       robił akapit niewidzialnym. Cała treść ma jeden kolor.               */
  }
}

@keyframes pts-wjazd {
  from { opacity: 0; transform: translateY(38px) scale(0.988); }
  to   { opacity: 1; transform: none; }
}

@keyframes pts-wjazd-krotki {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

@keyframes pts-wiersz {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

@keyframes pts-wezel {
  from { transform: scale(0.2); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

@keyframes pts-hero-tlo {
  from { transform: scale(1.04) translateY(0); }
  to   { transform: scale(1.04) translateY(-3%); }
}

@keyframes pts-hero-przod {
  from { transform: scale(1.04) translateY(0); }
  to   { transform: scale(1.04) translateY(-11%); }
}

@keyframes pts-hero-odjazd {
  /* Pierwsza ćwiartka bez zmian: przy krótkim, przypadkowym ruchu kółka
     nagłówek nie ma prawa zniknąć. */
  0%, 22% { opacity: 1; transform: none; }
  100%    { opacity: 0; transform: translateY(-64px) scale(0.97); }
}

@keyframes pts-gasnie {
  from { opacity: 1; }
  to   { opacity: 0; }
}

@keyframes pts-dryf {
  from { transform: translateY(-2.75rem); }
  to   { transform: translateY(2.75rem); }
}

@keyframes pts-kadr {
  from { transform: scale(1.14) translateY(-2.6%); }
  to   { transform: scale(1.14) translateY(2.6%); }
}


/* ═══ SZUFLADA MOBILNA: POZYCJE WCHODZĄ PO KOLEI ═════════════════════════
   Szuflada zjeżdża z góry jako płyta, a pozycje dochodzą zaraz za nią.
   Opóźnienia są krótkie: przy sześciu pozycjach i kroku większym niż ~60 ms
   ostatnia z nich pojawia się już po tym, jak ktoś zdążył sięgnąć palcem.

   Stan spoczynku jest widoczny - opóźnienie dotyczy tylko wejścia. Bez
   `js-motion` (brak skryptu, prośba o ograniczenie ruchu) pozycje stoją
   na miejscu od pierwszej klatki.                                          */
.js-motion .drawer.is-open .drawer__links a {
  animation: pts-pozycja-menu 0.42s var(--ease-out) both;
}

.js-motion .drawer.is-open .drawer__links a:nth-child(1) { animation-delay: 0.10s; }
.js-motion .drawer.is-open .drawer__links a:nth-child(2) { animation-delay: 0.16s; }
.js-motion .drawer.is-open .drawer__links a:nth-child(3) { animation-delay: 0.22s; }
.js-motion .drawer.is-open .drawer__links a:nth-child(4) { animation-delay: 0.28s; }
.js-motion .drawer.is-open .drawer__links a:nth-child(5) { animation-delay: 0.34s; }
.js-motion .drawer.is-open .drawer__links a:nth-child(n+6) { animation-delay: 0.40s; }

@keyframes pts-pozycja-menu {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .js-motion .drawer.is-open .drawer__links a { animation: none; }
}

/* ═══ MAGNES POD WEZWANIEM DO DZIAŁANIA ══════════════════════════════
   Przesunięcie liczy `app.js` i podaje zmiennymi, a arkusz składa je z tym,
   co przycisk ma w spoczynku. Bez zmiennych `translate` jest zerowe, więc
   brak skryptu nie zostawia przycisku przesuniętego.

   `translate` zamiast `transform` celowo: `transform` na tych przyciskach
   należy już do podniesienia przy najechaniu i jedno wykluczałoby drugie. */
.nav__akcja,
.hero-blok .wp-block-button__link,
.cta .wp-block-button__link {
  translate: var(--magnes-x, 0) var(--magnes-y, 0);
  transition: translate 0.35s var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
  .nav__akcja,
  .hero-blok .wp-block-button__link,
  .cta .wp-block-button__link { translate: none; }
}
