/* easysnaps.ai — kierunek 1 "SKALA"
   Stol pomiarowy zakladu fotograficznego. Wylacznie neutralne szarosci (R=G=B).
   Ruch = wywolywanie gestosci: kontrast, jasnosc, ostrosc. */

:root {
  /* PAS GORNY — JEDNA MIARA DLA CALEGO SERWISU (02.08.2026).
     Landing, polityka i panel maja ten sam pas: znak przy lewej krawedzi okna,
     wejscie przy prawej. Wczesniej kazda z tych stron nosila wlasne liczby —
     landing 68 px i 2,2vw, panel 60 px i 2,4vw — wiec przy przejsciu z oferty
     do panelu znak podskakiwal o 8 px i przesuwal sie o 2-3 px w bok. Roznicy
     nikt nie zaprojektowal, ona sie po prostu narosla.
     Odstep od krawedzi jest CELOWO inny niz margines tresci (.shell ma 20 px
     na strone): pas nalezy do OKNA, arkusz do TRESCI. */
  --pas-h: 68px;
  --pas-gut: clamp(16px, 2.2vw, 32px);

  --ink: #0a0a0a;
  --ink-soft: #1c1c1c;
  --graphite: #3a3a3a;
  --slate: #5f5f5f;
  --ash: #9a9a9a;
  --fog: #c8c8c8;
  --line: #d3d3d3;
  --paper: #ececec;
  --paper-hi: #f7f7f7;
  --white: #ffffff;

  --sans: ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* klin szarosci — 11 stopni, os calego kierunku */
  --z0: #0a0a0a;
  --z1: #202020;
  --z2: #333333;
  --z3: #474747;
  --z4: #5c5c5c;
  --z5: #727272;
  --z6: #898989;
  --z7: #a1a1a1;
  --z8: #bcbcbc;
  --z9: #d8d8d8;
  --z10: #f6f6f6;

  --shadow-print: 0 22px 44px -30px rgba(0, 0, 0, 0.55);

  /* skala typograficzna */
  --fs-nano: 0.625rem;
  --fs-micro: 0.6875rem;
  --fs-tiny: 0.75rem;
  --fs-mini: 0.8125rem;
  --fs-small: 0.875rem;
  --fs-sub: 0.9375rem;
  --fs-base: clamp(1rem, 0.95rem + 0.2vw, 1.0625rem);
  --fs-lead: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  --fs-title: 1.125rem;
  --fs-title-lg: clamp(1.2rem, 1.6vw, 1.5rem);
  /* Stopien `metric` (DESIGN.md, 03.09.2026): pojedyncza liczba na karcie
     przyrzadu w panelu wlasciciela. Miedzy tytulem a naglowkiem sekcji, bo
     cztery takie liczby stoja obok siebie w jednym rzedzie. */
  --fs-metric: clamp(1.5rem, 2.4vw, 2rem);
  --fs-cta: clamp(2rem, 4.6vw, 3.4rem);
  --fs-h2: clamp(1.7rem, 3.4vw, 2.7rem);
  --fs-h1: clamp(2.4rem, 6.4vw, 4.6rem);

  --e: cubic-bezier(0.16, 1, 0.3, 1);
  --shell: 1240px;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }

p { margin: 0 0 1em; }
h1, h2, h3 { margin: 0; text-wrap: balance; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--white);
  padding: 12px 18px;
  z-index: 40;
  text-decoration: none;
}
.skip:focus { left: 12px; top: 12px; }

.shell {
  width: min(100% - 40px, var(--shell));
  margin-inline: auto;
}

/* ---------- masthead ---------- */

.masthead {
  position: sticky;
  top: 0;
  z-index: 30;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: saturate(0) blur(9px);
  border-bottom: 1px solid var(--line);
}

/* pasek idzie na cala szerokosc okna: znak przy lewej krawedzi,
   nawigacja i wejscie przy prawej — bez kontenera po srodku */
.masthead__row {
  display: flex;
  align-items: center;
  gap: 24px;
  min-height: var(--pas-h);
  padding-inline: var(--pas-gut);
}

/* Znak wiaze sie z x-wysokoscia kroju, nie ze stala liczba pikseli: w logotypie
   z paczki brandingowej ma dokladnie 1,42 wysokosci litery "e", a odstep do
   napisu 0,45 swojej wysokosci. Zapisane w "ex" trzyma te proporcje przy kazdym
   stopniu pisma — takze w panelu, gdzie pasek jest mniejszy.
   Liczby w CSS nie sa te same, co w ksiedze (1,48ex zamiast 1,42; odstep 0,41
   zamiast 0,45), bo mierza co innego: "ex" to metryka kroju, a wzorzec
   zmierzylismy po tuszu — okragle "e" przelewa sie poza linie x, a napis
   zaczyna sie od lewej odsadki. Po tej korekcie tusz zgadza sie z tuszem:
   obrys 0,109 (wzor 0,108), wypelnienie od 0,300 (0,297), znak do x-wysokosci
   1,417 (1,423), zwis pod linia pisma 0,061 (0,063), odstep 0,447 (0,450). */
.mark {
  --znak: 1.48ex;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--znak) * 0.41);
  text-decoration: none;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-right: auto;
}

/* Dwa kadry: obrys i wypelniony, nasuniete po skosie. Proporcje przepisane
   ze znaku (bok 0,70 pola, grubosc obrysu 0,11, przesuniecie 0,30). Rysowany
   na currentColor, wiec na ciemnej stopce odwraca sie sam — nie potrzeba
   drugiego pliku ani drugiej reguly. Przesuniecie w pionie ustawia go wzgledem
   linii pisma tak, jak stoi w logotypie: dolna krawedz zwisa pod nia o 6%
   wysokosci znaku, a nie laduje na niej rowno. */
.mark__dot {
  position: relative;
  flex: none;
  width: var(--znak);
  height: var(--znak);
  transform: translateY(6.3%);
}
.mark__dot::before,
.mark__dot::after {
  content: "";
  position: absolute;
  width: 70%;
  height: 70%;
}
/* Obrys idzie przez inset box-shadow, nie przez border: przy 13 px znaku
   grubosc wypada 1,43 px, a border Chrome zaokragla w dol do 1 px i rama robi
   sie o 40% za lekka — na dodatek niejednakowo w naglowku i w panelu. */
.mark__dot::before {
  top: 0;
  left: 0;
  box-shadow: inset 0 0 0 calc(var(--znak) * 0.11) currentColor;
}
.mark__dot::after {
  top: 30%;
  left: 30%;
  background: currentColor;
}
/* Najechanie: oba kadry schodza sie ku sobie i pokrywaja co do krawedzi.
   Ten sam gest, co cala usluga — drugie ujecie ma trafic w pierwsze.
   Kazdy jedzie polowe przesuniecia, wiec bryla laduje na srodku pola:
   gdyby ruszal sie tylko wypelniony, znak uciekalby w lewy gorny rog i
   odstep do napisu rosnalby na oczach. */
.mark__dot::before,
.mark__dot::after {
  transition: transform 0.5s var(--e);
}
.mark:hover .mark__dot::before,
.mark:focus-visible .mark__dot::before {
  transform: translate(calc(var(--znak) * 0.15), calc(var(--znak) * 0.15));
}
.mark:hover .mark__dot::after,
.mark:focus-visible .mark__dot::after {
  transform: translate(calc(var(--znak) * -0.15), calc(var(--znak) * -0.15));
}

.mark__tld { color: var(--slate); font-weight: 500; }

/* Haslo przy znaku. Ustawia sie tuz obok logo, wiec to ono — nie znak —
   niesie margines rozpychajacy nawigacje do prawej krawedzi. Znika ponizej
   1100 px, zanim zaczeloby spychac przycisk logowania. */
.tagline {
  display: none;
  margin: 0 auto 0 0;
  padding-left: 18px;
  border-left: 1px solid var(--line);
  color: var(--slate);
  font-family: var(--mono);
  font-size: var(--fs-mini);
  letter-spacing: 0.02em;
}

@media (min-width: 1100px) {
  .masthead__row .mark { margin-right: 0; }
  .tagline { display: block; }
}

.nav {
  display: none;
  gap: 26px;
  font-size: var(--fs-sub);
}
.nav a {
  text-decoration: none;
  padding: 6px 0;
  position: relative;
  color: var(--graphite);
}
.nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s var(--e);
}
.nav a:hover { color: var(--ink); }
.nav a:hover::after { transform: scaleX(1); }

@media (min-width: 900px) { .nav { display: flex; } }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: var(--ink);
  color: var(--white);
  border: 1px solid var(--ink);
  padding: 17px 30px;
  font: inherit;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.22s var(--e), box-shadow 0.22s var(--e), background-color 0.22s var(--e);
}
.btn:hover {
  background: var(--ink-soft);
  transform: translateY(-2px);
  box-shadow: 0 2px 0 0 var(--ink);
}
.btn:active { transform: translateY(0); box-shadow: none; }

.btn--sm { padding: 11px 18px; font-size: var(--fs-sub); }

/* KWOTA NA PRZYCISKU. Pierwsza wersja (06.09.2026) byla o ton cichsza od
   napisu — szara, w najmniejszym stopniu skali. Wlasciciel tego samego dnia
   kazal ja uwidocznic i mial racje: to jest JEDYNA liczba, ktorej na tym
   ekranie jeszcze nie ma nigdzie indziej. Saldo stoi w belce na gorze, liczba
   kadrow nad siatka — a ile zejdzie za te konkretna paczke, mowi wylacznie to
   pole. Ma wiec pelna biel i wage napisu, a odroznia sie krojem, nie
   cichnieciem.

   TABULAR-NUMS, bo liczba zmienia sie przy kazdym zaznaczeniu kadru i nie
   moze przy tym przeskakiwac w prawo i w lewo. Do 14.09.2026 stala tu obok
   rodzina `--mono` — i to ona, a nie skakanie, byla powodem, dla ktorego
   przycisk mial w sobie dwa kroje naraz. Stalej szerokosci cyfr pilnuje
   `tabular-nums`; rodzina kroju nie ma z tym nic wspolnego. */
.btn__kr {
  font-size: var(--fs-mini);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--white);
  padding-left: 12px;
  margin-left: 2px;
  border-left: 1px solid rgba(255, 255, 255, 0.4);
}
.btn--wide { width: 100%; }

/* przycisk niosacy oferte: akcja + obietnica w jednej plycie */
.btn--offer {
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding: 14px 30px 15px;
  text-align: left;
}
.btn__main { font-size: var(--fs-lead); font-weight: 700; letter-spacing: -0.02em; }
.btn__sub {
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fog);
}
.btn--offer:hover .btn__sub { color: var(--white); }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--graphite);
}
.btn--ghost:hover { background: var(--ink); color: var(--white); border-color: var(--ink); }

/* ---------- type scale ---------- */

/* teza idzie skala systemu do konca: display z DESIGN.md, bez sufitu w polowie */
.hero__title {
  font-size: clamp(2.75rem, 7.4vw, 6rem);
  font-weight: 800;
  line-height: 0.92;
  letter-spacing: -0.045em;
  max-width: 15ch;
  text-wrap: balance;
}

.h2 {
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: 1.06;
  letter-spacing: -0.032em;
  max-width: 18ch;
  margin-bottom: 40px;
}

.lead {
  font-size: var(--fs-lead);
  color: var(--graphite);
  max-width: 54ch;
  margin-top: 26px;
}

.step__no, .field__label {
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.13em;
  font-size: var(--fs-micro);
}


/* ---------- hero ---------- */

.hero {
  padding: 0 0 clamp(44px, 5.5vw, 76px);
  overflow-x: clip;
}

/* kadr zajmuje cala szerokosc okna — bez siatki, bez marginesu, bez ramy */
.hero__bleed { position: relative; display: flex; flex-direction: column; }

.hero__text { padding-top: clamp(28px, 5vw, 44px); }

/* waskie ekrany: klin zamyka kadr, teza schodzi pod spod */
@media (max-width: 899px) {
  .hero__stack { display: contents; }
  .stage { order: 1; }
  .hero__text { order: 2; }
}

/* dopiero gdy starcza miejsca, teza kladzie sie na kadrze */
@media (min-width: 900px) {
  .hero__stack { display: grid; }
  .hero__stack > * { grid-area: 1 / 1; min-width: 0; }

  .hero__text {
    position: relative;
    align-self: end;
    /* Warstwa tezy lezy na kadrze, wiec NIE moze przechwytywac myszy —
       inaczej plama koloru pod kursorem nigdy nie dostanie zdarzen.
       Wskaznik wraca tylko tam, gdzie faktycznie cos sie klika. */
    pointer-events: none;
    padding: clamp(120px, 22vh, 260px) 0 clamp(30px, 3.4vw, 52px);
    /* przycmienie zamiast tla: zero hue, sam spadek gestosci */
    background: linear-gradient(
      180deg,
      rgba(10, 10, 10, 0) 0%,
      rgba(10, 10, 10, 0.34) 30%,
      rgba(10, 10, 10, 0.74) 62%,
      rgba(10, 10, 10, 0.94) 100%
    );
    color: var(--white);
  }

  .hero__text .btn,
  .hero__text a { pointer-events: auto; }

  .hero__text .hero__title { color: var(--white); }
  .hero__text .lead { color: var(--fog); }

  /* na atramencie kontrolki odwracaja sie tak samo jak w sekcji CTA */
  .hero__text .btn {
    background: var(--white);
    color: var(--ink);
    border-color: var(--white);
  }
  .hero__text .btn:hover {
    background: var(--fog);
    border-color: var(--fog);
    box-shadow: 0 2px 0 0 var(--white);
  }
  .hero__text .btn__sub { color: var(--slate); }
  .hero__text .btn--offer:hover .btn__sub { color: var(--ink); }

  .hero__text .btn--ghost {
    background: transparent;
    color: var(--white);
    border-color: var(--fog);
  }
  .hero__text .btn--ghost:hover {
    background: var(--white);
    color: var(--ink);
    border-color: var(--white);
  }
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 34px;
}

/* ---------- kadr hero ---------- */

/* Kadr hero: dwa wywolania tego samego ujecia jedno na drugim.
   Uwaga — tu NIE ma grayscale na opakowaniu: czern i biel jest autorska,
   wywolana w pliku, a nie wymuszona filtrem. */
/* Proporcja kadru = proporcja pliku (3:2), wiec nic nie jest przycinane.
   Zadnego cover, zadnego wycinka — caly kadr, tak jak zostal zrobiony. */
.stage {
  position: relative;
  margin: 0;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--ink);
}

/* Kolor lezy na wierzchu i jest odslaniany plama pod kursorem.
   --mx/--my to pozycja w procentach, --mr promien — wszystkie ustawiane
   z JS wylacznie jako liczby po clampie. */
/* Plama jest elipsa o dwoch niezaleznie oddychajacych promieniach (--mrx/--mry),
   wiec ksztalt caly czas sie przelewa zamiast byc kolem. Krawedz jest o polowe
   krotsza niz wczesniej: pelny kolor siega 62%, zejscie do zera miesci sie
   w ostatnich 38% promienia. */
/* Ta sama plama tnie dwie rzeczy: w wersji pierwotnej kolorowe zdjecie
   (.scene--color), a w wersji z agentem gorne plotno, na ktorym lezy
   kolorowy kadr razem z bialym obrysem agenta. Definicja jest jedna,
   wiec ksztalt plamy nie moze sie rozjechac miedzy trybami. */
.scene--color,
.agent__plotno--kolor {
  position: absolute;
  inset: 0;
  opacity: 0;
  -webkit-mask-image: radial-gradient(
    ellipse calc(var(--mrx) * 1px) calc(var(--mry) * 1px) at calc(var(--mx) * 1%) calc(var(--my) * 1%),
    #000 0%,
    #000 62%,
    rgba(0, 0, 0, 0.72) 79%,
    rgba(0, 0, 0, 0.22) 92%,
    transparent 100%
  );
  mask-image: radial-gradient(
    ellipse calc(var(--mrx) * 1px) calc(var(--mry) * 1px) at calc(var(--mx) * 1%) calc(var(--my) * 1%),
    #000 0%,
    #000 62%,
    rgba(0, 0, 0, 0.72) 79%,
    rgba(0, 0, 0, 0.22) 92%,
    transparent 100%
  );
  transition: opacity 0.45s var(--e);
}

/* plama zyje tylko wtedy, gdy ktos jej dotyka */
.stage.is-lit .scene--color,
.stage.is-lit .agent__plotno--kolor { opacity: 1; }

/* Kadr zawsze siega obu krawedzi okna — szerokosc jest nienaruszalna.
   Wysokosc bierze to, co mniejsze: pelna proporcja 16:9 (56.25vw) albo tyle,
   ile zostalo pod paskiem. Gdy okno jest nizsze, roznice zabiera gora. */
@media (min-width: 900px) {
  .stage {
    aspect-ratio: auto;
    height: min(56.25vw, calc(100svh - 68px));
  }
}

.scene { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Kadr hero jest kotwiczony DOLEM. Gdy okno jest nizsze niz proporcja zdjecia,
   ubywa gora — tam fotograf zostawil zapas sufitu. Dol sceny (meble, wyspa,
   stol) zostaje nietkniety na kazdym ekranie. */
.stage .scene { object-position: 50% 100%; }

/* ---------- agent na pierwszym planie ---------- */

/* Kadr ma dwa tryby, przelaczane atrybutem data-hero na .stage:
     data-hero="agent"  — kolor siedzi wylacznie w sylwetce agenta (wersja biezaca),
     brak atrybutu      — kolor odslania sie plama pod kursorem (wersja pierwotna).
   Obie sciezki zyja obok siebie w jednym kodzie, wiec powrot do poprzedniej
   wersji to skasowanie jednego atrybutu, a nie odkopywanie kopii pliku. */
/* Kolorowe zdjecie schodzi dopiero wtedy, gdy plotno naprawde rysuje.
   Dzieki temu przy prefers-reduced-motion i zanim wideo wystartuje plama
   pod kursorem ma na czym pracowac — odslania wtedy zdjecie, a agent
   stoi obok jako statyczny rysunek. */
.stage[data-hero="agent"].is-ruch .scene--color { display: none; }
.stage:not([data-hero="agent"]) .agent,
.stage:not([data-hero="agent"]) .agent__zrodlo,
.stage:not([data-hero="agent"]) .agent__plotno { display: none; }

/* Kolejnosc warstw w trybie agenta:
     plotno dolne  — czarno-bialy kadr z agentem, w ktorego sylwetce siedzi kolor,
     plotno gorne  — kolorowy kadr z samym obrysem agenta, przyciete plama.
   Pod plama kolor wychodzi wiec wszedzie, takze przez agenta, a jego biala
   kreska zostaje na wierzchu. Plama rusza sie wylacznie maska CSS, wiec jej
   plynnosc nie zalezy od tego, jak czesto przemalowuje sie plotno.
   Kolejnosc trzyma sie wylacznie porzadku w znaczniku — z-index bylby tu
   pulapka, bo .stage nie tworzy kontekstu ukladania i podniosloby to plotno
   takze nad teze lezaca na kadrze. */

.agent {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  /* warstwa jest rysunkiem, nie kontrolka — mysz przechodzi przez nia na wylot */
  pointer-events: none;
}

/* Waga kreski. Generator oddaje obrys grubszy, niz chce ta strona, a obrys jest
   wypelnieniem, nie stroke'iem — nie da sie go zwezyc szerokoscia linii.
   Stroke w kolorze tla maski (deklarowany w znaczniku, razem z pozostalymi
   stalymi maski) zjada bialy pierscien rownomiernie z obu stron, wiec ta jedna
   liczba dziala jak stroke-width w reszcie projektu. Liczy sie w jednostkach
   kadru, wiec kreska skaluje sie razem ze zdjeciem. */
.ag-fig {
  stroke-width: 2;
  stroke-linejoin: round;
}

.agent__linia { fill: var(--white); }

/* Zrodlo maski. Nie jest tresc, tylko dane dla <canvas> — ale nie moze byc
   display:none, bo przegladarka przestaje wtedy dekodowac klatki. Zostaje
   wiec w ukladzie jako punkt. */
.agent__zrodlo {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.agent__plotno {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Dopoki plotno nie odmalowalo pierwszej klatki, na wierzchu jest rysunek SVG.
   Zamiana idzie w jedna strone i dopiero po fakcie, wiec nie ma migniecia
   pustym kadrem, gdy wideo nie wystartuje. */
.stage:not(.is-ruch) .agent__plotno { visibility: hidden; }
.stage.is-ruch .agent { visibility: hidden; }

/* Odbite uzycie obrysu pracuje wylacznie na waskich ekranach. */
.agent__linia--mob { display: none; }

/* ---------- kadr hero na telefonie ----------

   Na waskim ekranie kadr jest CALY KOLOROWY i niesie sam bialy obrys agenta,
   odwrocony tak, ze postac patrzy z telefonem w lewo, w glab pokoju.
   Znika z niego cala maszyneria: wideo, oba plotna i skladanie klatka po klatce.

   Dwa powody, oba twarde.

   Pierwszy: plama koloru pod kursorem jest odpowiedzia na RUCH MYSZY, a na
   telefonie myszy nie ma. Czarno-biale zdjecie, ktorego nie ma czym odslonic,
   pokazuje wiec uslugie fotograficzna w kadrze bez koloru — czyli dokladnie
   odwrotnie, niz mowi reszta strony. Kolor jest tu dowodem dzialania uslugi
   (CLAUDE.md, "Reguly tresci"), wiec na telefonie stoi od razu i w calosci.

   Drugi: skladanie na <canvas> to najbardziej awaryjne miejsce tej strony.
   30.08.2026 Safari na iPhonie po cichu pominelo ctx.filter i zamiast wtopionej
   postaci stal czarny prostokat z biala sylwetka. Naprawione, ale zostawia to
   morał, ktory zapisalismy w zrodla/agent/README.md: kazda nowa wlasciwosc
   plotna ma te sama droge awarii — jest ignorowana bez sladu w konsoli.
   Telefon nie ma z tej maszynerii nic, czego nie da sie oddac obrazkiem, wiec
   jej tam nie ma. Przy okazji nie schodzi na telefon 985 KB pliku maski:
   preload="none" zostaje nietkniete, bo setupAgent() konczy sie wyzej.

   Granica biegnie po 900 px, czyli po tej samej liczbie, ktora klada teze
   na kadrze — jeden przelacznik ukladu, nie dwa. */
@media (max-width: 899px) {
  /* zdjecie czarno-biale i oba plotna nie maja tu nic do roboty */
  .stage[data-hero="agent"] .scene:not(.scene--color),
  .stage[data-hero="agent"] .agent__plotno,
  .stage[data-hero="agent"] .agent__zrodlo { display: none; }

  /* kolor stoi caly czas i nie jest przez nic przycinany */
  .stage[data-hero="agent"] .scene--color,
  .stage[data-hero="agent"].is-ruch .scene--color {
    display: block;
    opacity: 1;
    -webkit-mask-image: none;
    mask-image: none;
  }

  /* zostaje sam rysunek, a w nim sama kreska: kolor w sylwetce byłby powtorzeniem
     tego, co juz lezy pod spodem */
  .stage[data-hero="agent"] .agent,
  .stage[data-hero="agent"].is-ruch .agent { visibility: visible; }
  .agent__kolor { display: none; }
  .agent__linia { display: none; }
  .agent__linia--mob { display: block; }
}

/* MONOCHROM NA PROBKACH #korekty ZDJETY 14.09.2026, razem z wejsciem
   prawdziwych par ze zdjec wlasciciela. Dwie z czterech pozycji — zolte
   swiatlo i przeklamane barwy — SA O KOLORZE, wiec w skali szarosci
   pokazywalyby roznice miedzy dwoma identycznymi obrazkami. Reszta strony
   zostaje monochromatyczna: to jest interfejs, a tamto sa fotografie.
   Kadr hero ma ten sam wyjatek od poczatku (.scene--color). */

/* stopnie klina zostaly juz tylko na ekranie wejscia (.ramp) */
.wedge__step { display: block; }
.wedge__step[data-z="0"]  { background: var(--z0); }
.wedge__step[data-z="1"]  { background: var(--z1); }
.wedge__step[data-z="2"]  { background: var(--z2); }
.wedge__step[data-z="3"]  { background: var(--z3); }
.wedge__step[data-z="4"]  { background: var(--z4); }
.wedge__step[data-z="5"]  { background: var(--z5); }
.wedge__step[data-z="6"]  { background: var(--z6); }
.wedge__step[data-z="7"]  { background: var(--z7); }
.wedge__step[data-z="8"]  { background: var(--z8); }
.wedge__step[data-z="9"]  { background: var(--z9); }
.wedge__step[data-z="10"]  { background: var(--z10); }


/* ---------- bands ---------- */

.band { padding: clamp(72px, 9vw, 132px) 0; border-top: 1px solid var(--line); }
.band--tone { background: var(--paper-hi); }

/* ---------- steps ---------- */

.steps { list-style: none; margin: 0; padding: 0; }

.step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 12px 22px;
  align-items: start;
  padding: 34px 0;
  border-top: 1px solid var(--line);
}
.step:last-child { border-bottom: 1px solid var(--line); }

@media (min-width: 900px) {
  .step {
    grid-template-columns: 84px minmax(0, 1fr) 220px;
    gap: 34px;
    align-items: center;
  }
}

.step__no {
  color: var(--ash);
  font-size: var(--fs-tiny);
  padding-top: 6px;
  font-variant-numeric: tabular-nums;
}

.step__title {
  font-size: var(--fs-title-lg);
  font-weight: 700;
  letter-spacing: -0.022em;
  margin-bottom: 10px;
}
.step__body p { color: var(--graphite); max-width: 62ch; margin: 0; }

.diag {
  grid-column: 1 / -1;
  width: 100%;
  max-width: 220px;
  height: auto;
}
@media (min-width: 900px) { .diag { grid-column: auto; } }

.diag__box { fill: none; stroke: var(--graphite); stroke-width: 1.5; }
.diag__box--fill { fill: var(--fog); }
.diag__slot { fill: var(--ink); }
.diag__arrow, .diag__wave, .diag__tick {
  fill: none;
  stroke: var(--graphite);
  stroke-width: 1.5;
}
.diag__wave { stroke: var(--ink); stroke-width: 2; }

.step:hover .diag__box--fill { fill: var(--ash); }
.diag__box, .diag__box--fill { transition: fill 0.4s var(--e); }

/* ---------- fixes ---------- */

.fixes { list-style: none; margin: 0; padding: 0; }

.fix {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 20px 28px;
  align-items: center;
  padding: 26px 0;
  border-top: 1px solid var(--line);
}
.fix:last-child { border-bottom: 1px solid var(--line); }

.fix__title {
  font-size: var(--fs-title);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 6px;
}
.fix__txt p { color: var(--graphite); margin: 0; max-width: 58ch; font-size: var(--fs-sub); }

/* PARA "PRZED / PO" — dwa pliki obok siebie, zaden filtr.

   Style .chip[data-fix="..."] usuniete 14.09.2026. Udawaly usterke filtrem
   (blur, obrot, rozjasnienie) na zdjeciu bez usterki i zdejmowaly go na
   :hover — patrz komentarz w index.html.

   OBA KADRY WIDAC NARAZ i to jest cala zmiana w zachowaniu. Poprzedni uklad
   pokazywal poprawke wylacznie po najechaniu myszka, wiec na telefonie nie
   istniala; teraz nie ma stanu, ktorego czegos brakuje. */
.para {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  width: clamp(220px, 42vw, 460px);
}
.para__p { margin: 0; min-width: 0; }
.para__p .scene {
  /* Wysokosc idzie ZA PROPORCJA PLIKU, nie za ramka: kadrowanie cudzego
     zdjecia po to, zeby pasowalo do siatki, jest zmiana zdjecia. */
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--line);
  background: var(--ink);
}
.para__e {
  margin-top: 6px;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--graphite);
}
/* "Po" niesie wynik, wiec podpis jest mocniejszy — ale samym ciezarem,
   bez koloru: podpis to interfejs, a nie fotografia. */
.para__e--po { color: var(--ink); font-weight: 700; }

/* Na waskim ekranie para schodzi POD tekst — i od 19.09.2026 idzie OD
   KRAWEDZI DO KRAWEDZI okna (wlasciciel: "zdjecia przed i po sa male").

   DWIE KOLUMNY PRZESTAJA BYC WARUNKIEM. Stalo tu, ze zestawienie obok
   siebie jest cala trescia tej sekcji. To prawda — ale tylko dopoki oba
   kadry da sie obejrzec. Na telefonie 390 px kazdy mial 175 px szerokosci
   i okolo 131 px wysokosci, a na tym nie widac ani zoltego swiatla, ani
   pionu scian, ani refleksu od trawnika, czyli dokladnie tego, po co ta
   sekcja stoi na stronie. Zestawienie, ktorego nie widac, nie jest
   zestawieniem — jest para znaczkow.

   Kolumna zwija sie sama, gdy dla dwoch zabraknie miejsca: auto-fit
   z progiem 360 px daje JEDNA kolumne na telefonie (kadr na cala szerokosc
   okna) i dwie wszedzie tam, gdzie oba zmieszcza sie sensownie. Zadnej
   szerokosci konkretnego telefonu nie ma tu wpisanej z palca.

   PROG 360 PX, A NIE 300, I TO NIE JEST OKRAGLA LICZBA Z SUFITU. Przy 300
   przelaczenie na dwie kolumny wypadalo przy oknie 610 px i robilo dolek:
   600 px okna dawalo kadr 600 px, a 610 px — 300 px, czyli szersze okno
   pokazywalo o polowe mniejsze zdjecie. Przejscie z jednej kolumny na dwie
   ZAWSZE polowi kadr, wiec nie chodzi o to, zeby dolka nie bylo, tylko
   zeby jego dno nie spadlo nisko: przy 360 px najgorsze miejsce w calym
   zakresie ma 360 px, a nie 300.

   Wyjscie poza siatke robi ujemny margines rowny marginesowi `.shell`
   (20 px z kazdej strony, dopoki okno jest wezsze niz --shell + 40 px).
   Jednostek okna tu nie ma i nie ma byc: 100vw liczy sie razem z paskiem
   przewijania i potrafi dolozyc poziomy pasek calej stronie.

   PROG TO 900 px, NIE 720 — ta sama granica, na ktorej reszta strony
   przechodzi na uklad szeroki (.step, .hero, .cta). Przy 720 wychodzila
   rzecz nie do obrony: telefon 390 px pokazywal kadr 390 px, a tablet
   768 px — 146 px, czyli szerszy ekran dawal cztery razy mniejsze zdjecie.
   Ponizej 900 px cala strona i tak stoi w jednej kolumnie, wiec para nie
   ma sie tu obok czego zmiescic. */
@media (max-width: 899px) {
  .fix { grid-template-columns: minmax(0, 1fr); }
  .para {
    width: auto;
    margin-inline: -20px;
    grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
    gap: 18px 10px;
  }
  /* Podpis wchodzi NAD swoj kadr, odkad kadry potrafia stanac jeden pod
     drugim. Zostawiony pod spodem, "przed" wypadalby w szparze miedzy
     dwoma zdjeciami i wzrok przykleilby go do dolnego — czyli podpisywalby
     nie to zdjecie, ktore opisuje. Kolejnosc w kodzie zostaje bez zmiany,
     odwraca sie wylacznie widok. */
  .para__p { display: flex; flex-direction: column-reverse; }
  .para__e { margin-top: 0; margin-bottom: 6px; padding-inline: 20px; }
}
/* Reguly .soon usuniete 28.07.2026 razem z sekcja #wkrotce
   "Czego jeszcze nie ma" — patrz komentarz w index.html. */

/* Style opinii (.voices, .voice*) usuniete 31.08.2026 razem z sekcja #opinie
   "Kto juz tak pracuje" — patrz komentarz w index.html. Wylecialy tez .note
   oraz .split/.split__aside/.h2--tight, uzywane wylacznie przez ta sekcje.

   Przy okazji zniknelo powtorzenie: caly blok opinii stal w tym pliku DWA
   RAZY, linia w linie (706-786 i 790-870), a druga kopia przywlekla ze soba
   martwe style galerii (.grid4, .card*, .scene--raw, .scene--fixed), ktore
   komentarz miedzy kopiami oglaszal jako usuniete 28.07.2026. Arkusz mowil
   wiec o sobie nieprawde. Dwie kopie tej samej reguly nie daja sie utrzymac
   w zgodzie: poprawka trafia do jednej z nich i przestaje dzialac, kiedy
   ktos zmieni kolejnosc. .scene bez modyfikatorow ZOSTAJE — uzywa go kadr
   hero i probki w #korekty.

/* ---------- cta ---------- */

.cta {
  background: var(--ink);
  color: var(--white);
  padding: clamp(72px, 9vw, 128px) 0;
}

.cta__grid { display: grid; gap: clamp(36px, 5vw, 72px); }
@media (min-width: 900px) {
  .cta__grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); align-items: start; }
}

.cta__title {
  font-size: var(--fs-cta);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.038em;
  max-width: 14ch;
}
.cta__lead { margin-top: 20px; color: var(--fog); max-width: 46ch; }

.form { display: grid; gap: 18px; }

.field { display: grid; gap: 7px; }
.field__label { color: var(--slate); }

.field__input {
  font: inherit;
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--fog);
  padding: 15px 16px;
  border-radius: 0;
  transition: border-color 0.25s var(--e), box-shadow 0.25s var(--e);
}
.field__input::placeholder { color: var(--slate); }
.field__input:hover { border-color: var(--slate); }
.field__input:focus-visible {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 1px var(--ink);
}
.field__input[aria-invalid="true"] {
  border-color: var(--ink);
  box-shadow: inset 3px 0 0 0 var(--ink);
}

.field__hint { margin: 0; font-size: var(--fs-mini); color: var(--slate); }

.field__err {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  color: var(--ink);
  min-height: 0;
}
.field__err:empty { display: none; }

.cta .btn {
  background: var(--white);
  color: var(--ink);
  border-color: var(--white);
}
.cta .btn:hover {
  background: var(--fog);
  border-color: var(--fog);
  box-shadow: 0 2px 0 0 var(--white);
}
.cta .btn__sub { color: var(--slate); }
.cta .btn--offer:hover .btn__sub { color: var(--ink); }

.cta__act { display: grid; gap: 16px; align-content: start; }

.cta__fine {
  margin: 0;
  font-size: var(--fs-mini);
  color: var(--fog);
  max-width: 44ch;
}
.cta__alt { margin: 0; font-size: var(--fs-sub); color: var(--ash); }

.link {
  color: var(--white);
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}
.link:hover { text-decoration-thickness: 2px; }

.form__note {
  margin: 0;
  font-size: var(--fs-mini);
  color: var(--ash);
  min-height: 1.4em;
}
.form__note[data-state="ok"] { color: var(--white); }

/* ---------- ekran wejscia ---------- */

.back {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--slate);
  text-decoration: none;
  border-bottom: 1px solid var(--fog);
  padding-bottom: 3px;
  transition: color 0.3s var(--e), border-color 0.3s var(--e);
}
.back:hover { color: var(--ink); border-color: var(--ink); }

.auth { padding: clamp(44px, 7vw, 96px) 0 clamp(64px, 8vw, 112px); }

.auth__grid { display: grid; gap: clamp(40px, 6vw, 80px); align-items: start; }
@media (min-width: 940px) {
  .auth__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.86fr); }
}

.auth__title {
  font-size: var(--fs-cta);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.038em;
  max-width: 14ch;
}

.auth__big {
  margin: clamp(28px, 4vw, 44px) 0 0;
  font-size: var(--fs-h1);
  font-weight: 800;
  line-height: 0.9;
  letter-spacing: -0.045em;
  border-top: 1px solid var(--ink);
  padding-top: 22px;
}
.auth__big-sub {
  margin: 12px 0 0;
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--slate);
}

.auth__list {
  list-style: none;
  margin: clamp(26px, 3.4vw, 40px) 0 0;
  padding: 0;
  max-width: 46ch;
}
.auth__list li {
  padding: 14px 0 14px 26px;
  border-top: 1px solid var(--line);
  color: var(--graphite);
  font-size: var(--fs-sub);
  position: relative;
}
.auth__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 22px;
  width: 10px;
  height: 1px;
  background: var(--ink);
}

.ramp {
  display: grid;
  grid-template-columns: repeat(11, 1fr);
  height: 12px;
  margin-top: clamp(30px, 4vw, 48px);
}

.auth__card {
  background: var(--paper-hi);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-print);
}

.tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }

.tab {
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line);
  padding: 18px 12px;
  font-family: var(--mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--slate);
  cursor: pointer;
  transition: color 0.25s var(--e), background-color 0.25s var(--e), border-color 0.25s var(--e);
}
.tab:hover { color: var(--ink); background: var(--white); }
.tab[aria-selected="true"] {
  color: var(--ink);
  background: var(--white);
  border-bottom-color: var(--ink);
}

.panel { padding: clamp(22px, 3vw, 34px); }

.panel__aside { margin: 0; font-size: var(--fs-mini); }

.check { display: flex; align-items: flex-start; gap: 11px; }
.check__box {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  accent-color: var(--ink);
}
.check__label { font-size: var(--fs-mini); color: var(--graphite); }

.link--ink { color: var(--ink); }

/* ---------- foot ---------- */

.foot { background: var(--ink); color: var(--fog); border-top: 1px solid var(--z1); padding: 30px 0 44px; }
.foot__row { display: flex; flex-wrap: wrap; gap: 16px; justify-content: space-between; align-items: center; }
.mark--foot { color: var(--white); margin-right: 0; }
.mark--foot .mark__tld { color: var(--ash); }

/* wariant jasny — stopka ekranu wejscia */
.foot--light {
  background: var(--paper);
  color: var(--slate);
  border-top: 1px solid var(--line);
}
.mark--quiet { color: var(--ink); margin-right: 0; }
.mark--quiet .mark__tld { color: var(--slate); }
.foot__legal { margin: 0; font-size: var(--fs-mini); }

/* ---------- ruch ---------- */

/* Sekcje nie wjezdzaja z dolu — one sie wywoluja.
   Gestosc dochodzi od plaskiej szarosci do pelnej skali, bez ruchu w pionie.
   Stan wyjsciowy dostaja wylacznie elementy, ktorych przy starcie nie widac
   (klasa nadawana przez skrypt) — to, co juz namalowane, nigdy nie gasnie. */
.motion .reveal.will-develop {
  opacity: 0.42;
  filter: contrast(0.34) brightness(1.12);
  transition:
    opacity 0.5s var(--e),
    filter 0.62s var(--e);
}
.motion .reveal.will-develop.is-in {
  opacity: 1;
  filter: none;
}

/* rodzenstwo w liscie wywoluje sie po kolei — z twardym sufitem opoznienia */
.motion .step.will-develop:nth-child(2),
.motion .fix.will-develop:nth-child(2),
.motion .card.will-develop:nth-child(2) { transition-delay: 70ms; }
.motion .step.will-develop:nth-child(3),
.motion .fix.will-develop:nth-child(3),
.motion .card.will-develop:nth-child(3) { transition-delay: 140ms; }
.motion .step.will-develop:nth-child(4),
.motion .fix.will-develop:nth-child(4),
.motion .card.will-develop:nth-child(4) { transition-delay: 190ms; }

/* ---------- wejscie strony ----------
   Cala strona rozjasnia sie raz, miekko, przez dwie sekundy.
   Animacja jest czysto CSS-owa i startuje od stanu widocznego w 0%:
   gdyby cokolwiek poszlo nie tak, tresc i tak zostaje na ekranie. */
@keyframes page-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

body {
  animation: page-in 2s cubic-bezier(0.33, 0, 0.2, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  body { animation: none; }
  .motion .reveal,
  .motion .reveal.will-develop,
  .motion .reveal.will-develop.is-in {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
    transition-delay: 0s;
  }
  /* .chip .scene wypadlo 14.09.2026 — probki nie maja juz zadnego przejscia,
     bo nie maja juz stanu, miedzy ktorym mialyby przechodzic. */
  .btn, .nav a::after, .mark__dot::before, .mark__dot::after {
    transition: none;
  }
}
