/* ═══════════════════════════════════════════════════════════════
   RUCH — świat CZARNEJ GABLOTY

   Trzy postacie ruchu:
     ŚWIATŁO   światło chodzi po czarnej blasze i prześwituje przez
               ciemne szkło: smuga lampy studyjnej, smuga pod kursorem
               (pod płytą matowana przez szkło), odblask na kapsułach,
               błysk na krawędzi płyty, plama na panelu, slajdy
               zapalane po kolei
     TAFLA     szkło osiada na blasze i unosi się nad nią
     PRZESUW   widok przesuwa się, a po jego krawędzi idzie smuga

   Wszystko, co rusza się w liczbie, animuje tylko transform
   i przezroczystość. Treść jest widoczna domyślnie; stan „przed
   ruchem" żyje wyłącznie pod klasą .js. Przy „mniej ruchu" znika
   przesuw i unoszenie, a światło zostaje (sekcja 12).
   ═══════════════════════════════════════════════════════════════ */

/* ── 1. BLACHA I ŚWIATŁO ────────────────────────────────────────
   Czarna anoda jest w cieniu prawie jednolita; drobne, poziome rysy
   szczotkowania widać dopiero tam, gdzie pada światło. Światło na
   szczotkowanym metalu układa się w smugę w poprzek rys — pionową.
   Blacha i światło są stałe względem ekranu, a obie warstwy rysują ten
   sam wzór rys, więc w smudze jaśnieją dokładnie te same rysy.

   Przy wejściu na stronę smuga światła przechodzi przez blachę z lewej
   i zatrzymuje się, jak lampa w studiu. Drugą smugę prowadzi kursor
   (sekcja 5).                                                         */
body::before{
  content:''; position:fixed; inset:0; z-index:0; pointer-events:none;
  background:
    var(--brush) 0 0 / 800px 800px,
    var(--m1);
  will-change:transform;   /* własna warstwa: przewijanie jej nie przemalowuje */
}
/* Światło otoczenia mieszka w osobnej warstwie (element .ambient
   w index.html), bo tylko ono ma prawo się ruszać: blacha ze
   szczotkowaniem stoi, inaczej smuga pod kursorem zapalałaby inne
   rysy niż te, które są na niej narysowane. Ruch jest wolny
   (54 s tam i z powrotem) i idzie samą transformacją. */
.ambient{
  position:fixed; inset:-12vmax; z-index:0; pointer-events:none;
  background:
    radial-gradient(ellipse 30vw 64vh at 24% 32%, rgba(190,206,226,.034), rgba(190,206,226,0) 72%),
    radial-gradient(ellipse 40vw 34vh at 82% 90%, rgba(190,206,226,.022), rgba(190,206,226,0) 72%);
  will-change:transform;
  animation:ambient-drift 54s var(--e-inout) infinite alternate;
}
@keyframes ambient-drift{ to{ transform:translate3d(4vw, -2.5vh, 0) scale(1.05); } }
body::after{
  content:''; position:fixed; inset:0; z-index:0; pointer-events:none;
  background:var(--brush-hi) 0 0 / 800px 800px;
  background-color:rgba(200,214,232,.04);
  -webkit-mask-image:radial-gradient(closest-side, #000, rgba(0,0,0,.4) 42%, transparent);
  mask-image:radial-gradient(closest-side, #000, rgba(0,0,0,.4) 42%, transparent);
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-size:22vw 150vh; mask-size:22vw 150vh;
  -webkit-mask-position:8vw -32vh; mask-position:8vw -32vh;
  opacity:.22;
  animation:studio-light 2.8s var(--e-out) .1s both;
  will-change:transform;
}
@keyframes studio-light{
  from{ -webkit-mask-position:-30vw -32vh; mask-position:-30vw -32vh; opacity:0; }
  35% { opacity:.7; }
  to  { -webkit-mask-position:8vw -32vh; mask-position:8vw -32vh; opacity:.22; }
}
/* Pod zamkniętą przysłoną (sekcja 13) lampa czeka. Zmiana nazwy
   animacji przy zdjęciu klasy zaczyna smugę od nowa, gdy listki się
   rozchodzą; opóźnienie 3,6 s to zapas na wypadek, gdyby skrypt nie wstał. */
.intro-hold body::after{ animation-name:studio-light-late; animation-delay:3.6s; }
@keyframes studio-light-late{
  from{ -webkit-mask-position:-30vw -32vh; mask-position:-30vw -32vh; opacity:0; }
  35% { opacity:.7; }
  to  { -webkit-mask-position:8vw -32vh; mask-position:8vw -32vh; opacity:.22; }
}

/* ── 2. ZMIANA ZAKŁADKI — przesuw i smuga światła ───────────────
   Nowy widok odsłania się od krawędzi, a po tej krawędzi idzie smuga
   światła; stary odjeżdża w bok i gaśnie. Kierunek wynika
   z kolejności zakładek.                                          */
.titleblock{ view-transition-name:titleblock; }
.modebar{ view-transition-name:modebar; }
::view-transition-group(titleblock),
::view-transition-old(titleblock),
::view-transition-new(titleblock),
::view-transition-group(modebar),
::view-transition-old(modebar),
::view-transition-new(modebar){ animation:none; }
::view-transition-old(titleblock),
::view-transition-old(modebar){ display:none; }

::view-transition-old(root){ animation:vt-out .82s var(--e-film) both; }
::view-transition-new(root){ animation:vt-in .82s var(--e-film) both; }
html[data-vt="back"]::view-transition-old(root){ animation-name:vt-out-back; }
html[data-vt="back"]::view-transition-new(root){ animation-name:vt-in-back; }
/* Stary widok gaśnie przezroczystością, nie filtrem: pod nim jest ciemna
   blacha, więc wygląda tak samo, a kosztuje najmniej, co się da. Odchodzi
   w głąb i odwraca się o pięć stopni, jak karta odkładana na bok; nowy
   przychodzi z lekkiego przodu. Obie migawki to jedna warstwa, więc
   perspektywa nie kosztuje nic ponad to, co i tak jedzie. */
@keyframes vt-out     { to{ transform:perspective(1600px) translate3d(-10%, 0, -140px) rotateY(5deg);  opacity:.35; } }
@keyframes vt-out-back{ to{ transform:perspective(1600px) translate3d(10%, 0, -140px) rotateY(-5deg); opacity:.35; } }
/* Nowy widok przychodzi samą krawędzią odsłaniania. Głębia jest tylko
   na odchodzącym: powiększanie migawki nowego widoku kazałoby ją
   rastrować od nowa w innej skali, a to kosztuje pół sekundy na
   wolniejszej maszynie. */
@keyframes vt-in      { from{ clip-path:inset(0 0 0 100%); } to{ clip-path:inset(0); } }
@keyframes vt-in-back { from{ clip-path:inset(0 100% 0 0); } to{ clip-path:inset(0); } }
/* spokojniej: sama krawędź odsłaniania, bez głębi */
@keyframes vt-in-flat      { from{ clip-path:inset(0 0 0 100%); } to{ clip-path:inset(0); } }
@keyframes vt-in-flat-back { from{ clip-path:inset(0 100% 0 0); } to{ clip-path:inset(0); } }

/* Smuga idzie środkiem po krawędzi odsłaniania. W DOM stoi poza
   ekranem, więc gdyby wyszła spod warstwy przejścia, nie zostawia śladu. */
.vt-gate{
  position:fixed; top:0; bottom:0; left:0; z-index:999; width:180px; pointer-events:none;
  view-transition-name:vtgate;
  background:linear-gradient(90deg,
    rgba(236,242,248,0), rgba(236,242,248,.07) 34%, rgba(255,255,255,.6) 50%,
    rgba(236,242,248,.07) 66%, rgba(236,242,248,0));
  transform:translateX(-220px);
}
::view-transition-new(vtgate){ animation:none; }
::view-transition-group(vtgate){ animation:vt-gate .82s var(--e-film) both; }
html[data-vt="back"]::view-transition-group(vtgate){ animation-name:vt-gate-back; }
@keyframes vt-gate     { from{ transform:translateX(calc(100vw - 90px)); } to{ transform:translateX(-90px); } }
@keyframes vt-gate-back{ from{ transform:translateX(-90px); } to{ transform:translateX(calc(100vw - 90px)); } }

/* ── 3. KAPSUŁA POD ZAKŁADKĄ I POD FILTREM ──────────────────────
   Jedna kapsuła przejeżdża do wybranej pozycji, zamiast gasnąć pod
   jedną i zapalać się pod drugą. Pierwsze ustawienie jest bez
   przejazdu (.is-still), żeby kapsuła nie wjeżdżała znikąd.       */
.tabbar__ink{
  position:absolute; left:0; top:50%; z-index:0; height:36px; width:var(--ink-w, 0px); pointer-events:none;
  border-radius:999px;
  background:linear-gradient(180deg, rgba(255,255,255,.15), rgba(255,255,255,.04));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.26), inset 0 0 0 1px rgba(255,255,255,.14), 0 8px 18px -12px rgba(0,0,0,.9);
  transform:translate3d(var(--ink-x, 0px), -50%, 0);
  transition:transform .65s var(--e-out), width .65s var(--e-out), opacity .3s var(--e-out);
}
@media (max-width:900px){ .tabbar__ink{ height:32px; } }
.chips__ink{
  position:absolute; left:0; top:0; z-index:0; pointer-events:none;
  width:var(--ink-w, 0px); height:var(--ink-h, 40px); border-radius:999px;
  background-color:var(--pri-c); background-image:var(--pri-i); box-shadow:var(--pri-edge);
  transform:translate3d(var(--ink-x, 0px), var(--ink-y, 0px), 0);
  transition:transform .65s var(--e-out), width .65s var(--e-out), opacity .3s var(--e-out);
}
.tabbar__ink.is-still, .chips__ink.is-still{ transition:none; }

/* ── 4. ODBLASK — światło przejeżdża po kapsule ─────────────────
   Pod kursorem albo fokusem po kapsule raz przechodzi ukośny odblask.
   Po zejściu wraca bez przejazdu — poza kapsułą i tak go nie widać. */
.act::before{
  content:''; position:absolute; inset:0; z-index:-1; pointer-events:none; border-radius:inherit;
  background:linear-gradient(105deg, rgba(255,255,255,0) 28%, rgba(255,255,255,.42) 47%, rgba(255,255,255,0) 64%);
  transform:translateX(-110%);
}
.act:hover::before, .act:focus-visible::before{ transform:translateX(110%); transition:transform .95s var(--e-out); }
.act[disabled]::before, .act--void::before{ display:none; }
.act--primary::before{ background:linear-gradient(105deg, rgba(255,255,255,0) 28%, rgba(255,255,255,.5) 47%, rgba(255,255,255,0) 64%); }

/* ── 5. PLAMA ŚWIATŁA POD KURSOREM ──────────────────────────────
   Panel szkła jaśnieje tam, gdzie jest kursor, jak szkło pod lampą
   trzymaną w dłoni. Plama to gotowy gradient przesuwany samą transformacją —
   kursor nie przemalowuje panelu. Wielkie płyty sekcji jej nie mają:
   przesuwana warstwa na całej płycie kosztowałaby każdą klatkę.
   Na dotyku plamy nie ma. */
.modal__box--auth, .acct--guest .auth{ position:relative; isolation:isolate; }
.spot{
  position:absolute; left:0; top:0; z-index:-1; pointer-events:none;
  width:640px; height:640px; margin:-320px 0 0 -320px; border-radius:50%;
  background:radial-gradient(closest-side, rgba(255,255,255,.13), rgba(255,255,255,.04) 46%, rgba(255,255,255,0));
  opacity:0; transition:opacity .7s var(--e-out);
}
.is-lit > .spot{ opacity:1; }
/* Na blasze kursor prowadzi smugę światła w poprzek szczotkowania.
   Warstwa z rysami stoi razem z blachą; skrypt przesuwa tylko jej
   maskę, z lekkim opóźnieniem, jak lampa prowadzona ręką. */
.metal-light{
  position:fixed; inset:0; z-index:0; pointer-events:none;
  background:var(--brush-hi) 0 0 / 800px 800px;
  background-color:rgba(200,214,232,.075);
  -webkit-mask-image:radial-gradient(closest-side, #000, rgba(0,0,0,.42) 40%, transparent);
  mask-image:radial-gradient(closest-side, #000, rgba(0,0,0,.42) 40%, transparent);
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-size:340px 1100px; mask-size:340px 1100px;
  -webkit-mask-position:-400px 0; mask-position:-400px 0;
  opacity:0; transition:opacity .9s var(--e-out);
  will-change:transform;
}
.metal-light.is-on{ opacity:.58; }
body.is-locked .metal-light{ opacity:0; }
@media (hover:none), (pointer:coarse){ .metal-light{ display:none; } }

/* ── 6. BŁYSK NA KRAWĘDZI — szkło łapie światło, gdy się pojawia ─
   Ukośny pas światła raz przechodzi przez górę tafli: pierwszej płyty
   zakładki, panelu z taśmą kursu i paneli — zaraz po wejściu.
   Tylko w trakcie przejazdu pas jest osobną warstwą; potem wraca
   poza taflę (wypełnienie „backwards"), więc przewijanie go nie niesie. */
.sec--paper::after, .hero__proof::after, .print::after{
  content:''; position:absolute; top:0; left:-35%; z-index:-1; pointer-events:none;
  width:170%; height:min(60vh, 560px);
  background:linear-gradient(112deg, rgba(255,255,255,0) 38%, rgba(255,255,255,.2) 50%, rgba(255,255,255,0) 62%);
  -webkit-mask-image:linear-gradient(180deg, #000 30%, transparent);
  mask-image:linear-gradient(180deg, #000 30%, transparent);
  transform:translateX(-62%);
}
.sec--paper.is-lead::after{ animation:glint 1.9s .25s var(--e-out) backwards; }
.hero__proof.is-in::after, .print.is-in::after{ animation:glint 1.9s .45s var(--e-out) backwards; }
@keyframes glint{ from{ transform:translateX(-62%); } to{ transform:translateX(62%); } }

/* ── 7. WEJŚCIA ─────────────────────────────────────────────── */
/* tekst i przyciski wyostrzają się, jak obraz pod lupą */
[data-reveal]{ transition:opacity 1s var(--e-out), transform 1s var(--e-out), filter 1s var(--e-out); }
/* rozmycie 6 px: dość, żeby tekst się „wyostrzał", i czterokrotnie taniej
   niż 12 px na dużych blokach */
.js [data-reveal]:not(.is-in){ opacity:0; transform:translate3d(0, 30px, 0); filter:blur(6px); }

/* nagłówek: słowa wyjeżdżają z maski */
.js [data-reveal="words"]:not(.is-in){ opacity:1; transform:none; filter:none; }
.roll-w{
  display:inline-block; overflow:hidden; vertical-align:top;
  padding-block:.08em .12em; margin-block:-.08em -.12em;
}
.roll-w > i{
  display:inline-block; font-style:normal; transform-origin:0 100%;
  transition:transform 1.15s var(--e-out);
  transition-delay:calc(var(--wi, 0) * 65ms);
}
.js [data-reveal="words"]:not(.is-in) .roll-w > i{ transform:translate3d(0, 112%, 0) rotate(4deg); }

/* światło na grawerze: gdy słowa staną, przez stalowe litery przechodzi
   pas światła, słowo po słowie. Na pierwszym ekranie wraca co 9 s. */
.sec--hero .h1.is-in .roll-w > i{ animation:type-sheen 9s var(--e-inout) calc(1.35s + var(--wi, 0) * 90ms) infinite both; }
.sec--dark .h2.is-in .roll-w > i{ animation:type-sheen-once 1.5s var(--e-inout) calc(.9s + var(--wi, 0) * 90ms) 1 both; }
@keyframes type-sheen{
  0%  { background-position:130% 0, 0 0; }
  17% { background-position:-30% 0, 0 0; }
  100%{ background-position:-30% 0, 0 0; }
}
@keyframes type-sheen-once{
  from{ background-position:130% 0, 0 0; }
  to  { background-position:-30% 0, 0 0; }
}

/* panel z taśmą kursu włącza się: z przygaszonego i nieostrego do pełnego światła */
.js .hero__proof:not(.is-in){ filter:blur(6px) brightness(.35); }

/* plakat lekcji: taśma wjeżdża, a slajd zapala się, jakby za nim
   zapalono światło. Ciemność to nakładka, która gaśnie — bez filtra. */
.js [data-reveal="frame"]:not(.is-in){ opacity:0; transform:translate3d(0, 28px, 0); filter:none; }
[data-reveal="frame"] .lesson__img{ transition-delay:inherit; }
[data-reveal="frame"] .lesson__img::after{
  content:''; position:absolute; inset:0; z-index:2; pointer-events:none; border-radius:inherit;
  background:var(--m1); opacity:0;
  transition:opacity 1.2s var(--e-out); transition-delay:inherit;
}
.js [data-reveal="frame"]:not(.is-in) .lesson__img::after{ opacity:.9; }

/* taśma kursu: slajdy zapalają się po kolei, od pierwszej lekcji dla
   początkujących do ostatniej zaawansowanej.

   Krok kaskady nie jest stały, tylko dzielony przez liczbę klatek
   (--n stawia renderer): przy dziesięciu lekcjach zostaje 90 ms jak
   dotąd, przy osiemnastu schodzi do ~60 ms, więc całe zapalanie
   trzyma się półtorej sekundy niezależnie od tego, ile kurs ma lekcji.
   Bez tego każda dołożona lekcja przedłużałaby wejście strony. */
.reel, .lessons, .layers, .inside{ --step:clamp(22ms, calc(1100ms / var(--n, 12)), 90ms); }
.reel__img::after{
  content:''; position:absolute; inset:0; z-index:2; pointer-events:none; border-radius:inherit;
  background:var(--m1); opacity:0;
  transition:opacity 1s var(--e-out);
  transition-delay:calc(var(--i, 0) * var(--step, 90ms) + 350ms);
}
.js .hero__proof:not(.is-in) .reel__img::after{ opacity:.92; }
/* pod lupą przez slajd przechodzi błysk */
.reel__img::before{
  content:''; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(105deg, rgba(255,255,255,0) 30%, rgba(255,255,255,.4) 50%, rgba(255,255,255,0) 70%);
  transform:translateX(-130%);
}
.reel__frame.is-hot .reel__img::before{ animation:slide-glint .9s var(--e-out) both; }
@keyframes slide-glint{ from{ transform:translateX(-130%); } to{ transform:translateX(130%); } }
/* taśmy wchodzą w okienko jak film zakładany do kamery — jedna z lewej,
   druga z prawej, hamując na końcu — a slajdy zapalają się w biegu */
.reel__film{ transition:transform 1.3s var(--e-out) .2s; }
.js .hero__proof:not(.is-in) .reel__track:nth-child(odd) .reel__film{ transform:translate3d(-36px, 0, 0); }
.js .hero__proof:not(.is-in) .reel__track:nth-child(even) .reel__film{ transform:translate3d(36px, 0, 0); }
/* znak etapu: najpierw kreska schodzi przez perforację do klatki,
   dopiero potem pojawia się napis. Napis gaśnie kolorem, nie
   przezroczystością — ta zgasiłaby też kreskę. */
.reel__stage{ transition:color .6s var(--e-out); transition-delay:calc(var(--i, 0) * var(--step, 90ms) + 1000ms); }
.reel__stage::before{
  transform-origin:50% 0;
  transition:transform .8s var(--e-out); transition-delay:calc(var(--i, 0) * var(--step, 90ms) + 760ms);
}
.js .hero__proof:not(.is-in) .reel__stage{ color:transparent; }
.js .hero__proof:not(.is-in) .reel__stage::before{ transform:scaleY(0); }

/* pasek lekcji etapu i taśmy w „Inside": slajdy zapalają się po kolei,
   jak na taśmie kursu — gaśnie tylko ciemna nakładka */
.layer__film i, .inside__film li{ position:relative; }
.layer__film i::after, .inside__film li::after{
  content:''; position:absolute; inset:0; z-index:1; pointer-events:none; border-radius:inherit;
  background:var(--m1); opacity:0;
  transition:opacity .9s var(--e-out); transition-delay:calc(var(--i, 0) * var(--step, 100ms) + 380ms);
}
.js [data-reveal="row"]:not(.is-in) .layer__film i::after,
.js .inside__sheet:not(.is-in) .inside__film li::after{ opacity:.9; }

/* plan: kreski listy dociągają się po kolei, gdy tafla osiada */
.plan__list li::before{ transform-origin:0 50%; transition:transform .7s var(--e-out); transition-delay:calc(var(--i, 0) * 90ms + 520ms); }
.js .plan:not(.is-in) .plan__list li::before{ transform:scaleX(0); }

/* klin kalibracyjny: stopnie wychodzą od jasnej stali do czerni, a po
   nich jedzie pas światła — jak kalibracja przed pierwszym ujęciem */
[data-reveal="wedge"]{ position:relative; overflow:hidden; transition:clip-path 1.6s var(--e-out); }
.js [data-reveal="wedge"]:not(.is-in){ opacity:1; transform:none; filter:none; clip-path:inset(0 100% 0 0); }
.wedge::after{
  content:''; position:absolute; top:0; bottom:0; left:0; width:14%; pointer-events:none;
  background:linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.6) 50%, rgba(255,255,255,0));
  transform:translateX(-110%);
}
.wedge.is-in::after{ animation:wedge-glint 1.7s var(--e-out) .1s backwards; }
@keyframes wedge-glint{ from{ transform:translateX(-110%); } to{ transform:translateX(720%); } }
/* znak w stopce łapie pas światła raz, gdy wchodzi */
.foot__name.is-in{ animation:type-sheen-once 1.6s var(--e-inout) .45s 1 both; }

/* znak wodny przysłony obraca się tak wolno, że ruch widać dopiero po
   chwili patrzenia — mechanizm, który stoi za stroną i cicho pracuje */
.sec__head::after{ animation:iris-turn 150s linear infinite; }
.sec--hero::after{ animation:iris-turn 210s linear infinite reverse; }
@keyframes iris-turn{ to{ transform:rotate(360deg); } }

/* taśma pod pierwszym ekranem sunie w bok, o jeden otwór na 7 s —
   ruch, który widać dopiero, gdy się na niego spojrzy */
.leader i{ animation:leader-run 7s linear infinite; }
@keyframes leader-run{ to{ transform:translate3d(-22px, 0, 0); } }

/* wykres: gdy ramka staje w tafli, przez szkło przechodzi pas światła —
   ta sama zapowiedź, co przy wejściu płyty */
.chart__stage::after{
  content:''; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:linear-gradient(112deg, rgba(255,255,255,0) 38%, rgba(255,255,255,.18) 50%, rgba(255,255,255,0) 62%);
  transform:translateX(-130%);
}
.chart.is-on .chart__stage::after{ animation:chart-glint 1.4s var(--e-out) .1s backwards; }
@keyframes chart-glint{ from{ transform:translateX(-130%); } to{ transform:translateX(130%); } }

/* wiersz: odsłania go krawędź od dołu */
[data-reveal="row"]{ transition:clip-path 1s var(--e-out); }
.js [data-reveal="row"]:not(.is-in){ opacity:1; transform:none; filter:none; clip-path:inset(0 0 100% 0); }
[data-reveal="row"].is-in{ clip-path:inset(-4px -64px); }
[data-reveal="row"] > *{ transition:transform 1s var(--e-out); transition-delay:inherit; }
.js [data-reveal="row"]:not(.is-in) > *{ transform:translate3d(0, 22px, 0); }

/* tafla: kładzie się na blasze, opadając z lekkiego pochylenia */
/* bez rozmycia: tafla to duża płaszczyzna z szerokim cieniem */
[data-reveal="print"]{ transform-origin:50% 100%; }
.js [data-reveal="print"]:not(.is-in){ opacity:0; transform:perspective(1400px) rotateX(9deg) translate3d(0, 44px, 0); filter:brightness(.55); }

/* ── 8. MIGAWKA — okno otwiera się z naciśniętego przycisku ────
   Krąg rośnie z miejsca kliknięcia dokładnie do najdalszego rogu
   okna; treść wyostrza się dopiero, gdy migawka jest otwarta.    */
@property --iris{ syntax:'<length>'; inherits:true; initial-value:0px; }
.js .modal{ --iris:0px; transition:--iris .8s var(--e-out); }
.js .modal.is-open{ --iris:var(--iris-max, 150vmax); }
.js .modal.is-closing{ transition-duration:.38s; transition-timing-function:var(--e-inout); }
.js .modal__box{ clip-path:circle(var(--iris) at var(--ox, 50%) var(--oy, 50%)); }
/* Okno nie jest naklejką: zanim migawka je otworzy, leży odchylone
   w głąb i prostuje się, wchodząc na wierzch. */
.js .modal__box{ transition:transform .72s var(--e-out) .05s; }
.js .modal:not(.is-open) .modal__box{ transform:perspective(1500px) rotateX(7deg) translate3d(0, 12px, -70px); }
.js .modal.is-closing .modal__box{ transition-duration:.34s; transition-delay:0s; }
.js .modal__bg{ opacity:0; transition:opacity .55s var(--e-out); }
.js .modal.is-open .modal__bg{ opacity:1; }
.js .modal.is-closing .modal__bg{ transition-duration:.3s; }
.js .modal__box > :not(.modal__x):not(.spot){
  transition:opacity .7s var(--e-out) .16s, transform .7s var(--e-out) .16s;
}
.js .modal:not(.is-open) .modal__box > :not(.modal__x):not(.spot){ opacity:0; transform:scale(.985); }

/* ── 9. KURSOR — szklana soczewka, nad taśmą lupa ──────────────
   Soczewka nie zastępuje strzałki systemu — idzie za nią. Jasna
   krawędź wewnątrz i ciemna na zewnątrz: widać ją na blasze i na
   szkle. Nad akcją rośnie, nad klatką taśmy staje się lupą.       */
.cursor{
  position:fixed; left:0; top:0; z-index:1250; width:0; height:0; pointer-events:none;
  opacity:0; transition:opacity .3s var(--e-out);
}
.cursor.is-on{ opacity:1; }
.cursor::before{
  content:''; position:absolute; left:0; top:0; width:var(--cs, 34px); height:var(--cs, 34px);
  border-radius:50%; transform:translate(-50%, -50%);
  background:radial-gradient(circle at 34% 28%, rgba(255,255,255,.26), rgba(255,255,255,.05) 58%, rgba(255,255,255,0) 72%);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.88), 0 0 0 1px rgba(0,0,0,.35), 0 8px 20px -8px rgba(0,0,0,.55);
  transition:width .5s var(--e-out), height .5s var(--e-out), transform .2s var(--e-out);
}
.cursor.is-hot{ --cs:52px; }
.cursor.is-loupe{ --cs:92px; }
/* naciśnięcie: soczewka na chwilę mięknie razem z palcem */
.cursor.is-down::before{ transform:translate(-50%, -50%) scale(.82); }
/* podpis lupy stoi pod soczewką, nie na klatce, którą powiększa */
.cursor__label{
  position:absolute; left:0; top:0; transform:translate(-50%, 58px);
  padding:3px 9px; border-radius:999px;
  font-family:var(--f-mono); font-size:11px; letter-spacing:.06em; color:var(--on-metal); white-space:nowrap;
  background:rgba(12,13,16,.86); box-shadow:inset 0 1px 0 rgba(255,255,255,.18), inset 0 0 0 1px rgba(255,255,255,.14), 0 6px 14px -8px rgba(0,0,0,.8);
  opacity:0; transition:opacity .3s var(--e-out);
}
.cursor.is-loupe .cursor__label{ opacity:1; }
@media (hover:none), (pointer:coarse){ .cursor{ display:none; } }

/* ── 10. ODCZYT — treść paska wyostrza się, nie miga ─────────── */
@keyframes ro-in{
  from{ opacity:0; transform:translate3d(0, 8px, 0); filter:blur(4px); }
  to  { opacity:1; transform:none; filter:none; }
}
.ro > *{ animation:ro-in .45s var(--e-out) both; }
.ro > :nth-child(2){ animation-delay:.03s; }
.ro > :nth-child(3){ animation-delay:.06s; }
.ro > :nth-child(4){ animation-delay:.09s; }
.ro > :nth-child(5){ animation-delay:.12s; }

/* ── 11. PRACUJĘ — światło biegnie po dolnej krawędzi kapsuły ── */
.act.is-busy{ pointer-events:none; }
.act.is-busy[disabled]{ opacity:1; cursor:progress; }
.act.is-busy::after{
  content:''; position:absolute; left:18px; right:18px; bottom:5px; height:2px; border-radius:2px;
  background:currentColor; transform-origin:0 50%;
  animation:busy 1.1s var(--e-inout) infinite;
}
@keyframes busy{
  0%   { transform:scaleX(0);  transform-origin:0 50%; }
  50%  { transform:scaleX(1);  transform-origin:0 50%; }
  50.1%{ transform:scaleX(1);  transform-origin:100% 50%; }
  100% { transform:scaleX(0);  transform-origin:100% 50%; }
}

/* ── 12. SPOKOJNIEJ ─────────────────────────────────────────────
   „Mniej ruchu" w systemie (w Windows: wyłączone efekty animacji)
   uspokaja ruch, zamiast go gasić. Strona nie przejeżdża w bok, smuga
   nie przelatuje przez ekran, wejścia nie podnoszą się i nie
   przechylają, taśmy nie unoszą się i nie wjeżdżają z boku, a przysłona
   na starcie nie rozchodzi się, tylko gaśnie (motion.js). Zostaje całe
   światło: odblaski, błyski, plama pod kursorem, zapalanie slajdów,
   migawka, odczyt, kapsuły pod wyborem i światło naciśnięcia.        */
@media (prefers-reduced-motion: reduce){
  /* Anomalii nie da się „uspokoić" — zakłócenie to migotanie, więc
     przy mniej ruchu po prostu go nie ma. */
  .gl-bars, .anomaly{ display:none; }
  .lettering.is-gl{ animation:none; }
  ::view-transition-old(root),
  html[data-vt="back"]::view-transition-old(root){ animation-name:vt-dim; }
  ::view-transition-new(root){ animation-name:vt-in-flat; }
  html[data-vt="back"]::view-transition-new(root){ animation-name:vt-in-flat-back; }
  .vt-gate{ display:none; }
  /* okna otwierają się bez przechyłu, sama migawka */
  .js .modal:not(.is-open) .modal__box{ transform:none; }
  .js [data-reveal]:not(.is-in),
  .js [data-reveal="frame"]:not(.is-in),
  .js [data-reveal="print"]:not(.is-in),
  .js [data-reveal="row"]:not(.is-in) > *{ transform:none; }
  .lesson__film:hover, .layer:hover .layer__film, .layer__strip:focus-visible .layer__film,
  .reel__frame.is-hot .reel__img,
  .js .hero__proof:not(.is-in) .reel__track:nth-child(odd) .reel__film,
  .js .hero__proof:not(.is-in) .reel__track:nth-child(even) .reel__film{ transform:none; }
  /* światło na grawerze przechodzi raz, nie wraca */
  .sec--hero .h1.is-in .roll-w > i{ animation-iteration-count:1; }
  /* światło otoczenia, taśma i znak wodny stoją: to ruch tła */
  .ambient, .leader i, .sec__head::after, .sec--hero::after{ animation:none; }
}
@keyframes vt-dim{ to{ opacity:.4; } }

/* ── 13. OTWARCIE — przysłona z czarnej anody ───────────────────
   Raz na wizytę strona otwiera się jak obiektyw. Na zamkniętej
   przysłonie stoi znak ze stali i łapie pas światła, a pod nim wbiega
   liczba lekcji i minut kursu. Potem siedem listków rozchodzi się,
   obracając, ich krawędzie łapią światło, a znak odlatuje na swoje
   miejsce w pasku. Strona zaczyna swoje wejście, gdy listki ruszają.
   Listek to wielka półpłaszczyzna przesuwana samą transformacją, więc
   przysłona nie maluje się od nowa w żadnej klatce. Prowadzi ją
   motion.js (intro). Bez skryptu jej nie ma (klasę intro-on stawia
   <head>), a gdyby skrypt ruchu nie wstał, gaśnie sama po 3,6 s.   */
.intro{ display:none; }
.intro-on .intro{
  position:fixed; inset:0; z-index:1200; display:block; overflow:hidden; pointer-events:none;
  animation:intro-out .4s var(--e-out) 3.6s forwards;
}
@keyframes intro-out{ to{ opacity:0; visibility:hidden; } }
.intro__iris{ position:absolute; left:50%; top:50%; width:0; height:0; }
/* Brzeg listka przechodzi tuż za środkiem ekranu; siedem listków,
   obróconych co 360/7°, zakrywa cały ekran. Odsunięte od środka
   zostawiają siedmiokątny otwór, a obrót całości skręca go jak
   prawdziwa przysłona. Szczotkowanie leży w kierunku listka, więc
   każdy łapie światło trochę inaczej. */
.intro__iris i{
  position:absolute; left:0; top:-75vmax; width:80vmax; height:150vmax;
  transform-origin:0 50%;
  transform:rotate(calc(var(--k, 0) * 51.4286deg)) translateX(-2vmin);
  background:
    linear-gradient(90deg, rgba(214,224,236,.07), rgba(214,224,236,0) 10vmax),
    var(--brush) 0 0 / 800px 800px,
    var(--m1);
  box-shadow:-36px 0 70px -26px rgba(0,0,0,.95);
  backface-visibility:hidden;
}
/* Krawędź listka łapie światło tylko na brzegu otworu: pas rośnie
   razem z otworem, bo motion.js skaluje go w tym samym takcie, a jego
   końce gasną przy wierzchołkach. */
.intro__iris b{
  position:absolute; left:0; top:50%; width:2px; height:100vmax; margin-top:-50vmax;
  background:linear-gradient(180deg, rgba(236,242,248,0), rgba(236,242,248,.8) 30%, #fff 50%, rgba(236,242,248,.8) 70%, rgba(236,242,248,0));
  transform:scaleY(0);
}
.intro__stage{
  position:absolute; inset:0; display:grid; place-content:center; justify-items:center;
  gap:clamp(14px, 1.6vw, 22px); padding:var(--s-4);
  perspective:1100px;   /* znak wychodzi z głębi, nie z płaskiego powiększenia */
}
/* Monogram wywołuje się pierwszy i leci do paska własnym torem —
   mierzonym tak samo jak lot nazwy, bo w pasku stoi obok niej. */
.intro__logo{ display:block; margin:0; opacity:0; transform-origin:0 0; }
.intro__logo .mark{
  height:clamp(3.04rem, 1.33rem + 6.65vw, 8.08rem);   /* 0,95 stopnia nazwy */
  fill:url(#steel); stroke:url(#steel);
}

/* Znak ma krój, wersaliki i odstęp liter znaku z paska, więc lot do
   paska to samo przesunięcie i skala. */
.intro__mark{
  display:block; margin:0; transform-origin:0 0;
  font-family:var(--f-display); font-weight:900; font-size:clamp(3.2rem, 1.4rem + 7vw, 8.5rem);
  line-height:1; letter-spacing:.02em; text-transform:uppercase; white-space:nowrap;
}
.intro__mark > span{
  display:block; opacity:0;
  color:var(--on-metal);
  background-image:
    linear-gradient(105deg, rgba(255,255,255,0) 42%, rgba(255,255,255,.95) 50%, rgba(255,255,255,0) 58%),
    linear-gradient(180deg, #FFFFFF 0%, #E3E8ED 46%, #98A1AB 100%);
  background-size:260% 100%, 100% 100%;
  background-repeat:no-repeat;
  background-position:130% 0, 0 0;
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent;
}
.intro__count{
  margin:0; min-height:1.2em; opacity:0;
  font-family:var(--f-mono); font-size:var(--t-2xs); font-weight:500; letter-spacing:.08em; text-transform:uppercase;
  color:var(--metal-2); font-variant-numeric:tabular-nums; white-space:nowrap;
}
/* Pasek czeka pod przysłoną: znak, zakładki i konto pojawiają się, gdy
   znak z przysłony doleci na swoje miejsce (motion.js). */
.intro-mark .titleblock__mark,
.intro-mark .titleblock__name,
.intro-mark .titleblock__nav,
.intro-mark .titleblock__acts{ opacity:0; animation:intro-late .4s var(--e-out) 3.6s forwards; }
@keyframes intro-late{ to{ opacity:1; } }

/* ── 14. NACISK — światło rozchodzi się w szkle od palca ─────────
   Kapsuła potwierdza naciśnięcie: w miejscu dotyku szkło jaśnieje,
   a światło biegnie do krawędzi (motion.js, press). Osobny element,
   sama transformacja i przezroczystość, znika po sobie. Zostaje przy
   „mniej ruchu" — to światło, nie przesuw.                         */
.press{
  position:absolute; z-index:-1; pointer-events:none; border-radius:50%;
  background:radial-gradient(closest-side, rgba(255,255,255,.44), rgba(255,255,255,.16) 45%, rgba(255,255,255,0));
}
/* fokus z klawiatury dociąga się do elementu, zamiast wyskoczyć */
@keyframes focus-in{ from{ outline-offset:9px; outline-color:rgba(255,255,255,0); } }
.act:focus-visible, .chip:focus-visible, .tab:focus-visible, .auth__tab:focus-visible,
.linkbtn:focus-visible, .foot__link:focus-visible, .layer__strip:focus-visible, .modal__x:focus-visible{
  animation:focus-in .34s var(--e-out);
}

/* ── 15. ANOMALIA — białe zakłócenie ────────────────────────────
   Marka nazywa się Anomaly, więc sygnał tej strony czasem się rwie.
   Tutaj stoi materiał dwóch warstw, obie tylko na czas zdarzenia:
   pasy zakłóceń nad elementem i rozdarcie przez całą stronę.
   Prowadzi je motion.js (glitch, tear): wstawia je, animuje krokami
   i zdejmuje z DOM, więc nic nie zostaje i nic nie chodzi w pętli.

   Kolor: biel i nic więcej. Klasyczny glitch rozjeżdża kanały na
   czerwień i cyjan — tu byłby jedynym kolorem na stronie, więc
   rozjazd robi biały cień (drop-shadow), a nie barwa.           */
.gl-bars{
  position:fixed; z-index:1150; pointer-events:none; opacity:0;
  background:repeating-linear-gradient(180deg,
    rgba(255,255,255,.9) 0 1px, rgba(255,255,255,.35) 1px 2px, rgba(255,255,255,0) 2px 7px);
  -webkit-mask-image:linear-gradient(180deg, transparent 0 14%, #000 14% 24%, transparent 24% 52%, #000 52% 61%, transparent 61% 86%, #000 86% 94%, transparent 94%);
          mask-image:linear-gradient(180deg, transparent 0 14%, #000 14% 24%, transparent 24% 52%, #000 52% 61%, transparent 61% 86%, #000 86% 94%, transparent 94%);
  will-change:transform, opacity;
}
/* Rozdarcie: jedna jasna linia z miękkim zanikiem nad i pod nią. */
.anomaly{
  position:fixed; left:0; right:0; top:0; height:18vh; z-index:1150; pointer-events:none; opacity:0;
  background:
    linear-gradient(180deg, rgba(255,255,255,0) 0 46%, rgba(255,255,255,.55) 49.4%, rgba(255,255,255,.92) 50%,
      rgba(255,255,255,.55) 50.6%, rgba(255,255,255,0) 54%),
    linear-gradient(180deg, rgba(255,255,255,0) 0 30%, rgba(255,255,255,.07) 50%, rgba(255,255,255,0) 70%);
  will-change:transform, opacity;
}

/* Bryła liter przy zakłóceniu: mnożnik głębi przeskakuje krokami,
   więc napis na chwilę wyjeżdża z metalu, gubi go albo odwraca
   kierunek światła. Zmienna nierejestrowana zmienia się skokowo —
   dokładnie tak, jak ma wyglądać zakłócenie. */
.lettering.is-gl{ animation:gl-depth 280ms steps(1, end); }
@keyframes gl-depth{
  0%  { --zk:1; }
  14% { --zk:2.7; }
  30% { --zk:0; }
  46% { --zk:-1.9; }
  62% { --zk:3.4; }
  78% { --zk:.5; }
  100%{ --zk:1; }
}
