/*
Theme Name: DentSmart
Theme URI: https://dentsmart.pl
Description: Motyw DentSmart — redesign „Stride”. Standalone, czysty PHP, bez buildera i bez motywu nadrzednego.
Author: DentSmart
Version: 2.0.0
Requires at least: 6.0
Tested up to: 6.8
Requires PHP: 7.4
Text Domain: dentsmart
*/

/* =========================================================================
   DentSmart — redesign w języku wizualnym „Stride" (getlayers.ai)
   Prototyp. Po akceptacji przenosimy do motywu WP (front-page.php + style.css).

   Co przejmujemy ze wzorca:
   - głęboki elektryczny błękit przechodzący w czerń, rozbłysk światła w centrum
   - bardzo duża, lekka typografia z dwutonowym odsłanianiem przy scrollu
   - pływający pasek nawigacji w kształcie pigułki
   - naprzemienne sekcje: ciemna → biała → ciemna
   - rząd statystyk z pionowymi separatorami
   ========================================================================= */

:root {
  /* Kolor */
  --ink: #05060a;
  --ink-2: #0a0d18;
  --blue: #1733ff;
  --blue-deep: #0a1ab5;
  --blue-soft: #4d6bff;
  --white: #ffffff;
  --paper: #f6f7fb;
  --text-dim: rgba(255, 255, 255, 0.58);
  /* 0.34 dawało 2,97:1 na --ink — poniżej progu AA dla drobnego tekstu
     (pasek nazw, dolny wiersz stopki, numery kroków). 0.5 daje 4,9:1
     i wciąż zostaje wyraźnie poniżej --text-dim. */
  --text-dimmer: rgba(255, 255, 255, 0.5);
  --line-dark: rgba(255, 255, 255, 0.14);
  --line-light: #e3e6ef;
  --ink-on-light: #0a0d18;
  --dim-on-light: #5c6478;

  /* Typografia */
  --font: "Outfit", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Inter", ui-sans-serif, system-ui, sans-serif;

  --t-display: clamp(2.8rem, 7vw, 6.2rem);
  --t-h2: clamp(2rem, 4.4vw, 3.6rem);
  --t-h3: clamp(1.15rem, 1.7vw, 1.45rem);
  --t-body: clamp(0.95rem, 1.05vw, 1.0625rem);
  --t-meta: 0.78rem;
  --t-stat: clamp(2.2rem, 4.6vw, 3.6rem);

  /* Przestrzeń */
  --max: 1440px;
  --pad: clamp(1.25rem, 4vw, 4rem);
  --section: clamp(5rem, 11vh, 9rem);

  /* Ruch — jedna krzywa, trzy czasy */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --t-fast: 260ms;
  --t-base: 520ms;
  --t-slow: 900ms;
  --motion: 1;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* clip, nie hidden — hidden na <body> zabija przewijanie strony,
     a na <html> psuje position:sticky */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--white);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg, canvas { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

.wrap {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.skip-link {
  position: absolute; left: -999px; top: 0; z-index: 999;
  background: var(--white); color: var(--ink);
  padding: .7em 1.2em; border-radius: 0 0 10px 0;
}
.skip-link:focus { left: 0; }

:focus-visible { outline: 2px solid var(--blue-soft); outline-offset: 3px; border-radius: 4px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* ============================ NAWIGACJA ============================ */

.nav {
  position: fixed;
  top: clamp(.75rem, 1.6vw, 1.25rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  width: min(calc(100% - 2 * var(--pad)), var(--max));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  pointer-events: none;
}
.nav > * { pointer-events: auto; }

.nav__group {
  display: flex;
  align-items: center;
  gap: .25rem;
  padding: .35rem .35rem .35rem .9rem;
  background: rgba(10, 13, 24, 0.72);
  border: 1px solid var(--line-dark);
  border-radius: 100px;
  backdrop-filter: blur(14px);
}

.nav__brand {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--font); font-weight: 500; font-size: .95rem;
  letter-spacing: -0.01em; padding-right: .85rem;
  border-right: 1px solid var(--line-dark); margin-right: .5rem;
}
.nav__mark { width: 16px; height: 16px; color: var(--blue-soft); }

.nav__link {
  padding: .5rem .85rem; border-radius: 100px;
  font-size: .85rem; color: var(--text-dim);
  transition: color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.nav__link:hover, .nav__link:focus-visible { color: var(--white); background: rgba(255,255,255,.07); }

.nav__cta {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .75rem 1.4rem; border-radius: 100px;
  background: var(--white); color: var(--ink);
  font-family: var(--font); font-weight: 500; font-size: .88rem;
  /* Pasek jest `fixed` i przejeżdża nad sekcjami jasnymi i ciemnymi.
     Sama biel gubiła się na białej sekcji: przycisk przestawał być
     przyciskiem, a że leży NAD treścią, po cichu zjadał to, co pod nim —
     przy „O nas" znikało słowo „w" z nagłówka. Obwódka i cień rysują
     krawędź na jasnym tle, a na ciemnym są niewidoczne. */
  box-shadow: 0 0 0 1px rgba(5, 6, 10, .10), 0 10px 28px rgba(5, 6, 10, .22);
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.nav__cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 0 0 1px rgba(5, 6, 10, .10), 0 8px 26px rgba(23,51,255,.34);
}

.nav__toggle {
  display: none; background: none; border: 0; cursor: pointer;
  padding: .75rem 1.1rem; border-radius: 100px;
  background: rgba(10,13,24,.72); border: 1px solid var(--line-dark);
  color: var(--white); font-size: .85rem; backdrop-filter: blur(14px);
}

/* Marka w osobnej pigułce pokazuje się dopiero wtedy, gdy chowamy pasek
   z linkami — inaczej logo byłoby na ekranie dwa razy. */
.nav__brand--solo {
  display: none;
  padding: .7rem 1.1rem;
  border: 1px solid var(--line-dark);
  border-radius: 100px;
  background: rgba(10, 13, 24, .72);
  backdrop-filter: blur(14px);
  margin-right: 0;
  border-right: 1px solid var(--line-dark);
}

/* Arkusz menu na wąskim ekranie. Linki i CTA są klonowane skryptem z paska,
   więc nie ma drugiego miejsca, w którym trzeba pamiętać o nowej pozycji. */
.nav__panel {
  position: absolute;
  top: calc(100% + .6rem);
  right: 0;
  min-width: min(80vw, 20rem);
  display: grid;
  gap: .15rem;
  padding: .6rem;
  background: rgba(10, 13, 24, .93);
  border: 1px solid var(--line-dark);
  border-radius: 22px;
  backdrop-filter: blur(14px);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .5);
}
.nav__panel[hidden] { display: none; }
.nav__panel-link {
  padding: .85rem 1rem; border-radius: 14px;
  font-size: .95rem; color: var(--text-dim);
  transition: color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.nav__panel-link:hover, .nav__panel-link:focus-visible {
  color: var(--white); background: rgba(255, 255, 255, .07);
}
.nav__panel-cta {
  margin-top: .35rem;
  padding: .9rem 1rem; border-radius: 14px; text-align: center;
  background: var(--white); color: var(--ink);
  font-family: var(--font); font-weight: 500; font-size: .9rem;
}
/* Powyżej progu pasek pokazuje wszystko sam — arkusz nie ma po co istnieć */
@media (min-width: 60.0625rem) { .nav__panel { display: none; } }

@media (max-width: 60rem) {
  .nav__group { display: none; }
  .nav__brand--solo { display: inline-flex; }
  .nav__toggle { display: block; }
  .nav { justify-content: space-between; }
  /* Przy wąskim ekranie CTA i „Menu" nie mieszczą się obok marki —
     zapis zostaje w arkuszu menu, tu wystarczy przycisk. */
  .nav__cta { display: none; }
}

/* ============================== HERO =============================== */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-block: clamp(6.5rem, 13vh, 9rem) clamp(2rem, 5vh, 3.5rem);
  overflow: hidden;
  /* Elektryczny błękit wchodzi od dołu-lewej, czerń trzyma prawy górny róg */
  /* Błękit ma być elektryczny i skupiony w dolnej lewej ćwiartce, a prawy
     górny róg niemal czarny — to jest rozkład światła ze wzorca. */
  /* Błękit wypełnia lewą połowę i dół kadru, sięgając wysoko przy lewej
     krawędzi; prawy górny róg zostaje niemal czarny. To jest rozkład
     światła ze wzorca — wcześniejsza wersja trzymała błękit za nisko. */
  background:
    radial-gradient(105% 125% at -14% 78%, rgba(48,86,255,1) 0%, rgba(32,64,255,.95) 16%, rgba(22,48,230,.7) 34%, rgba(12,26,140,.3) 58%, rgba(5,6,10,0) 80%),
    radial-gradient(70% 60% at 32% 108%, rgba(80,120,255,.6) 0%, rgba(5,6,10,0) 62%),
    radial-gradient(60% 55% at 62% 96%, rgba(30,60,255,.34) 0%, rgba(5,6,10,0) 66%),
    var(--ink);
}

.hero__canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
}

.hero__inner { position: relative; z-index: 2; }

.hero__title {
  font-family: var(--font);
  font-weight: 300;
  font-size: var(--t-display);
  line-height: 1.02;
  letter-spacing: -0.03em;
  max-width: 16ch;
}

/* Odsłanianie nagłówków — sygnaturowy zabieg wzorca.
 *
 * Zmierzone na klatkach (a102, h62, h66): to NIE jest przełączanie słów
 * między dwoma kolorami. Napis odsłania się LITERA PO LITERZE, a krawędź
 * odsłonięcia jest miękka: kilka znaków przed nią stoi rozmytych i
 * przygaszonych, i dopiero dojeżdżają do ostrości. Na klatce a102 widać
 * to wprost — „Our Por" jest ostre, „t" lekko rozmyte, dalsze znaki giną
 * w rozmyciu.
 *
 * Liczy to CSS, nie JS: skrypt ustawia na nagłówku jedną liczbę --p
 * (ile znaków jest już odsłoniętych), a każdy znak sam wylicza swój stan
 * z własnego numeru --k. Dzięki temu na klatkę przypada jedna zmiana
 * stylu na nagłówek, a nie po jednej na każdy znak.
 *
 * Przygaszenie robi opacity, nie kolor: dotychczasowe --word-dim to i tak
 * był kolor tekstu z niższą alfą, więc wychodzi na to samo, a działa bez
 * osobnych tokenów na jasne i ciemne sekcje.
 */
.word { display: inline-block; }
.ch {
  display: inline-block;
  /* Domyślne --p jest ogromne, żeby bez JS napis stał odsłonięty */
  --d: clamp(0, calc((var(--p, 9999) - var(--k, 0)) / var(--band, 3.4)), 1);
  opacity: calc(var(--dim, .34) + (1 - var(--dim, .34)) * var(--d));
  /* Zmierzone na powiększeniu klatki a102: pierwszy nieostry znak ma smugę
     ok. 4 px przy kroju ok. 60 px, czyli niecałe 0,07em. Na wzorcu dalsze
     znaki znikają nie dlatego, że rozmycie rośnie w nieskończoność, tylko
     dlatego, że schodzą do zerowej krycia — u nas nie schodzą, bo dolny
     stan to czytelna szarość. Mocniejsze rozmycie (próbowaliśmy 0,16em)
     zamazuje ten szary ogon do nieczytelności. */
  filter: blur(calc((1 - var(--d)) * var(--ch-bl, .09em)));
}
/* Nagłówek hero jest widoczny od pierwszej sekundy i nigdy nie dojeżdża do
   pełnego odsłonięcia bez przewinięcia — jego przygaszony ogon stoi więc na
   ekranie na stałe. Rozmycie czytało się tam jak wada renderowania, a nie
   jak zabieg, dlatego hero dostaje sam dwutonowy kontrast, bez nieostrości.
   Miękka krawędź zostaje w nagłówkach odsłanianych scrollem — te przelatują
   przez kadr i rozmycie widać tylko w ruchu. */
.hero .ch { --ch-bl: 0px; }
.section--light,
.section--paper { --dim: .26; }

.hero__eyebrow {
  display: inline-flex; align-items: center; gap: .55rem;
  font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 1.5rem;
}
.hero__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--blue-soft); }

.hero__foot {
  position: relative; z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 2.5rem;
  align-items: end;
}

@media (max-width: 60rem) {
  .hero__foot { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
  .hero__title { max-width: 100%; }
}

.stats { display: flex; gap: clamp(1.25rem, 3vw, 2.75rem); }

.stat { position: relative; padding-left: clamp(1.25rem, 3vw, 2.75rem); }
.stat:first-child { padding-left: 0; }
.stat:not(:first-child)::before {
  content: ""; position: absolute; left: 0; top: .35em; bottom: .9em;
  width: 1px; background: var(--line-dark);
}
.stat__num {
  font-family: var(--font); font-weight: 300;
  font-size: var(--t-stat); line-height: 1; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
/* Etykieta stoi w lewym dolnym rogu hero, czyli na NAJJAŚNIEJSZYM
   fragmencie błękitu — tam --text-dim (.58) schodzi do 3,3:1.
   .76 daje ok. 6,6:1 i nadal czyta się jako podpis pod liczbą. */
.stat__label { font-size: .78rem; color: rgba(255, 255, 255, .76); margin-top: .5rem; }
/* Liczba i jednostka to jedna wartość — nie wolno ich łamać na dwa wiersze */
.stat__num { white-space: nowrap; }

/* Na telefonie trzy statystyki obok siebie dostają po ~90 px na liczbę
   i etykietę. „14 dni" łamało się wtedy na dwa wiersze, przez co trzy
   liczby stały na trzech różnych liniach bazowych, a etykiety schodziły
   po trzy wiersze każda. Poniżej progu układamy je więc w wiersze:
   liczba w stałej kolumnie, etykieta obok, separatory z pionowych
   robią się poziome. */
@media (max-width: 34rem) {
  .stats { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; }
  .stat {
    display: grid;
    grid-template-columns: 4.8rem minmax(0, 1fr);
    align-items: baseline;
    column-gap: .9rem;
    padding: .85rem 0 .95rem;
  }
  .stat:first-child { padding-top: 0; }
  .stat:not(:first-child)::before {
    left: 0; right: 0; top: 0; bottom: auto;
    width: auto; height: 1px;
  }
  .stat__num { font-size: 1.85rem; }
  .stat__label { margin-top: 0; }
}

.hero__copy { max-width: 46ch; justify-self: end; }
@media (max-width: 60rem) { .hero__copy { justify-self: start; } }

.hero__lead { font-size: clamp(1rem, 1.35vw, 1.2rem); font-weight: 500; line-height: 1.45; }
.hero__sub { color: var(--text-dim); margin-top: .9rem; font-size: .95rem; }

.hero__actions { display: flex; align-items: center; gap: 1.25rem; margin-top: 1.75rem; flex-wrap: wrap; }

.btn {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .85rem 1.6rem; border-radius: 100px;
  font-family: var(--font); font-weight: 500; font-size: .9rem;
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease);
}
.btn--light { background: var(--white); color: var(--ink); }
.btn--light:hover { transform: translateY(-1px); box-shadow: 0 10px 30px rgba(23,51,255,.4); }
.btn--blue { background: var(--blue); color: var(--white); }
.btn--blue:hover { transform: translateY(-1px); box-shadow: 0 10px 30px rgba(23,51,255,.45); }
.btn--ghost { border: 1px solid var(--line-dark); color: var(--white); }
.btn--ghost:hover { background: rgba(255,255,255,.06); }

.link-u { position: relative; font-size: .9rem; }
.link-u::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px;
  background: currentColor; transform: scaleX(1); transform-origin: left;
  transition: transform var(--t-fast) var(--ease);
}
.link-u:hover::after { transform-origin: right; transform: scaleX(0); }

/* ========================= SEKCJE OGÓLNE ========================= */

.section { padding-block: var(--section); position: relative; }

/* Sekcja jasna — naprzemienność ciemna/biała jest rytmem całej strony */
.section--light { background: var(--white); color: var(--ink-on-light); }
.section--paper { background: var(--paper); color: var(--ink-on-light); }

.section__head { max-width: 60ch; }
.section__head--center { margin-inline: auto; text-align: center; }

.eyebrow {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 1.25rem;
}
.section--light .eyebrow, .section--paper .eyebrow { color: var(--dim-on-light); }
.eyebrow::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--blue); }

.title {
  font-family: var(--font); font-weight: 300;
  font-size: var(--t-h2); line-height: 1.06; letter-spacing: -0.028em;
}

.sub { margin-top: 1.25rem; color: var(--text-dim); max-width: 52ch; }
.section--light .sub, .section--paper .sub { color: var(--dim-on-light); }
.section__head--center .sub { margin-inline: auto; }

/* ========================== PRZEJŚCIE ============================
   Plansza rozdzielająca dwie jasne sekcje, wzorowana na ekranie „Financial
   momentum" ze Stride: ciemny panel na całą wysokość okna, obiekt w środku,
   zdanie rozbite na dwa rogi — początek zwykłym stopniem u góry z lewej,
   dokończenie wielkim, przygaszonym krojem u dołu z prawej.

   Panel jest pełnoekranowy i zatrzymuje się przy przewijaniu — sekcja pod
   nim na niego najeżdża (patrz „PRZEJŚCIA MIĘDZY SEKCJAMI" niżej).
   ================================================================= */

.przej {
  position: relative;
  overflow: hidden;
  /* Pełna wysokość okna: panel ma się zatrzymać dokładnie na kadrze, bez
     paska tła u dołu. `svh` zamiast `vh`, bo na telefonie pasek adresu
     inaczej odcinałby dolny wiersz. */
  height: 100svh;
  min-height: 560px;
  /* Światło jak na wzorcu: mocny błękit z dołu-lewej, słabszy z góry-prawej,
     środek kadru niemal czarny — obiekt ma się w nim wybijać. */
  background:
    radial-gradient(88% 96% at -8% 94%, rgba(40, 74, 255, .95) 0%, rgba(24, 50, 215, .7) 26%, rgba(12, 26, 140, .3) 52%, rgba(5, 6, 10, 0) 78%),
    radial-gradient(78% 88% at 96% -12%, rgba(58, 100, 255, 1) 0%, rgba(44, 82, 250, .88) 22%, rgba(26, 52, 210, .5) 44%, rgba(12, 26, 140, .18) 64%, rgba(5, 6, 10, 0) 82%),
    var(--ink);
}

.przej__canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
}

.przej__gora {
  position: absolute;
  z-index: 2;
  top: clamp(6.5rem, 14vh, 9.5rem);
  left: var(--pad);
  /* 16ch łamie zdanie dokładnie po „wybiera" — dwa słowa w wierszu, tak jak
     na wzorcu. Jednostka skaluje się z krojem, więc łamanie trzyma się
     na każdym stopniu. */
  max-width: 15ch;
  font-family: var(--font);
  font-weight: 300;
  /* 4,4vw — tyle ma nagłówek na wzorcu (78 px przy kadrze 1780 px). Przy
     większym stopniu polskie słowa wchodzą w obiekt stojący na środku. */
  font-size: clamp(1.9rem, 4.4vw, 3.9rem);
  line-height: 1.02;
  letter-spacing: -0.03em;
}

.przej__dol {
  position: absolute;
  z-index: 2;
  right: var(--pad);
  /* NIE przy samej dolnej krawędzi, choć tam jest na wzorcu. Panel się
     zatrzymuje, a sekcja pod nim najeżdża od dołu — więc to, co stoi
     najniżej, znika pierwsze. Przy dole napis był zakrywany po ok. 8%
     wysokości okna; na 29% od dołu żyje mniej więcej cztery razy dłużej.
     Poziomo się nie zderza: łańcuch trzyma środkowe ~20% szerokości,
     napis stoi przy prawej krawędzi. */
  bottom: 29%;
  font-family: var(--font);
  font-weight: 300;
  font-size: clamp(2.4rem, 6vw, 5.5rem);
  line-height: .95;
  letter-spacing: -0.04em;
  color: rgba(255, 255, 255, .17);
  white-space: nowrap;
}

/* Na wąskim ekranie oba napisy wracają do zwykłego układu i stoją jeden pod
   drugim u góry. Powód jest twardy: na telefonie łańcuch musi zająć jakieś
   35% szerokości, żeby w ogóle było go widać, a „w Google" przy prawej
   krawędzi wchodziło wtedy wprost na dolne ogniwo. Zdanie czytane w dwóch
   wierszach działa tam zresztą lepiej niż rozrzucone po rogach. */
@media (max-width: 60rem) {
  .przej {
    display: grid;
    align-content: start;
    padding: clamp(6.5rem, 14vh, 9.5rem) var(--pad) 0;
  }
  .przej__gora,
  .przej__dol {
    position: static;
    max-width: none;
  }
  .przej__dol {
    margin-top: .35em;
    font-size: clamp(2.6rem, 10vw, 4.4rem);
    color: rgba(255, 255, 255, .26);
  }
}

/* ============================= O NAS =============================
   Przeniesione ze wzorca (sekcja „About us" w Stride) i zmierzone na jego
   klatkach: panel zajmuje ok. 80% wysokości okna, cztery RÓWNE kolumny
   rozdzielone linią 1 px, podpisy przy dolnej krawędzi (ok. 2,6% szerokości
   okna), nagłówek zaczyna się dokładnie na krawędzi trzeciej kolumny.

   Zdjęcie pojawia się po najechaniu na kolumnę i wypełnia ją CAŁĄ — także
   pod podpisem, który wtedy robi się biały. Bez najechania panel jest czysto
   typograficzny.
   ================================================================= */

.onas {
  position: relative;
  overflow: hidden;
  background: var(--white);
  color: var(--ink-on-light);
  /* Bylo `clamp(540px, 82vh, 880px)` — sekcja konczyla sie w polowie kadru
     na wyzszych ekranach i to wlasnie rzucalo sie w oczy. Pelna wysokosc. */
  min-height: 100vh;
  min-height: 100svh;
  /* Nagłówek odsłaniany przy scrollu potrzebuje słabszego przygaszenia —
     sekcja nie jest `.section--light`, więc dziedziczy wersję na ciemne tło. */
  --dim: .26;
  --onas-pad: clamp(1rem, 1.7vw, 1.9rem);
}
.onas .eyebrow { color: var(--dim-on-light); }

/* ---- kolumny ze zdjęciami (warstwa spodnia) ---- */
.onas__cols {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.onas__col {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  border-left: 1px solid var(--line-light);
}
.onas__col:first-child { border-left: 0; }

.onas__img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  /* Lekkie zbliżenie na starcie: kadr „osiada" przy odsłanianiu zamiast
     wskakiwać gotowy. */
  transform: scale(1.06);
  transition: opacity var(--t-base) var(--ease), transform var(--t-slow) var(--ease);
}
/* Wygaszacz pod podpisem — bez niego biały napis ginie na jasnym kadrze */
.onas__col::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(5, 6, 10, .18) 0%, rgba(5, 6, 10, 0) 38%, rgba(5, 6, 10, .66) 100%);
  opacity: 0;
  transition: opacity var(--t-base) var(--ease);
  pointer-events: none;
}
.onas__col:hover .onas__img { opacity: 1; transform: none; }
.onas__col:hover::after { opacity: 1; }

.onas__label {
  position: relative;
  z-index: 1;
  padding: 0 var(--onas-pad) clamp(1.35rem, 3.2vh, 2.3rem);
  font-family: var(--font);
  font-weight: 300;
  font-size: clamp(1.4rem, 2.6vw, 2.6rem);
  line-height: 1.04;
  letter-spacing: -0.028em;
  transition: color var(--t-base) var(--ease);
}
.onas__col:hover .onas__label {
  color: var(--white);
  text-shadow: 0 2px 22px rgba(5, 6, 10, .55);
}

/* ---- warstwa treści (nagłówek, przycisk) ---- */
.onas__front {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* Górna krawędź tekstu tuż pod pływającą nawigacją — jak we wzorcu */
  padding-top: clamp(6.5rem, 14vh, 10rem);
  /* Warstwa nie łapie kursora: dzięki temu najechanie na nagłówek nadal
     odsłania zdjęcie w kolumnie, która pod nim leży. */
  pointer-events: none;
}
/* align-self, bo `.eyebrow` jest inline-flex z wyśrodkowaniem w pionie —
   rozciągnięty na całą wysokość wiersza siatki zjeżdżałby na jego środek. */
.onas__eyebrow {
  grid-column: 1;
  align-self: start;
  padding-inline: var(--onas-pad);
  margin-bottom: 0;
}
.onas__head {
  position: relative;
  grid-column: 3 / span 2;
  padding-left: var(--onas-pad);
  padding-right: var(--pad);
}

/* Nagłówek leży nad trzecią i czwartą kolumną, więc odsłonięcie kadru
   w którejś z nich podkłada mu zdjęcie pod ciemny tekst. We wzorcu ten
   przypadek nie wypadł w kadrze, ale u nas jedno ze zdjęć jest bardzo
   jasne (uśmiech) i tekst na nim znika. Wtedy — i tylko wtedy — pod
   tekstem pojawia się mleczna plama. */
.onas__head::before {
  content: "";
  position: absolute;
  inset: -1.6rem -0.5rem -1.6rem -1.6rem;
  z-index: -1;
  background: radial-gradient(66% 58% at 32% 42%,
    rgba(255, 255, 255, .95) 0%, rgba(255, 255, 255, .78) 52%, rgba(255, 255, 255, 0) 82%);
  opacity: 0;
  transition: opacity var(--t-base) var(--ease);
  pointer-events: none;
}
.onas:has(.onas__col:nth-child(3):hover) .onas__head::before,
.onas:has(.onas__col:nth-child(4):hover) .onas__head::before { opacity: 1; }

/* Eyebrow jest drobny — wystarczy mu biała otoczka, niewidoczna na bieli */
.onas__eyebrow { text-shadow: 0 0 12px #fff, 0 0 5px #fff; }
.onas__title { font-size: clamp(1.75rem, 3.2vw, 3rem); max-width: 18ch; }
.onas__lead {
  margin-top: 1.35rem;
  max-width: 40ch;
  color: var(--dim-on-light);
  font-size: .96rem;
}
.onas__btn {
  pointer-events: auto;
  display: inline-flex; align-items: center; gap: .55rem;
  margin-top: 1.85rem;
  padding: .85rem 1.6rem;
  border: 1px solid var(--line-light);
  border-radius: 100px;
  font-family: var(--font); font-weight: 500; font-size: .9rem;
  color: var(--ink-on-light);
  transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.onas__btn:hover { background: var(--paper); border-color: #cfd4e4; }

/* Na wąskim ekranie nie ma kursora, więc nie ma czym odsłaniać kadrów:
   panel rozkłada się na zwykły stos — najpierw tekst, potem kolumny jako
   kafle ze zdjęciami widocznymi od razu. */
@media (max-width: 60rem) {
  .onas { min-height: 0; }
  .onas__front {
    display: block;
    padding: var(--section) var(--pad) clamp(2rem, 5vh, 3rem);
  }
  .onas__eyebrow { padding-inline: 0; margin-bottom: 1.25rem; }
  .onas__head { padding: 0; }
  .onas__title { max-width: 22ch; }
  .onas__cols { position: static; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .onas__col { aspect-ratio: 3 / 4; border-left: 0; border-top: 1px solid var(--line-light); }
  .onas__col:nth-child(even) { border-left: 1px solid var(--line-light); }
  .onas__img { opacity: 1; transform: none; }
  .onas__col::after { opacity: 1; }
  .onas__label { color: var(--white); text-shadow: 0 2px 22px rgba(5, 6, 10, .55); }
}

@media (max-width: 40rem) {
  .onas__cols { grid-template-columns: minmax(0, 1fr); }
  .onas__col { aspect-ratio: 16 / 11; border-left: 0; }
  .onas__col:nth-child(even) { border-left: 0; }
}

/* ========================== USŁUGI ============================== */

.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 1.6vw, 1.5rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}
@media (max-width: 60rem) { .cards { grid-template-columns: minmax(0, 1fr); } }

.card {
  background: var(--paper);
  border: 1px solid var(--line-light);
  border-radius: 20px;
  padding: clamp(1.5rem, 2.4vw, 2.25rem);
  /* --dryf ustawia JS w rytm przewijania (delikatna głębia), --uniesienie
     dokłada najechanie. Dwie osobne zmienne, bo inaczej hover kasowałby
     pozycję ze scrolla i karta by podskakiwała. */
  --dryf: 0px;
  --uniesienie: 0px;
  transform: translate3d(0, calc(var(--dryf) + var(--uniesienie)), 0);
  transition: box-shadow var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.card:hover { --uniesienie: -6px; box-shadow: 0 18px 50px rgba(10,13,24,.09); border-color: rgba(23,51,255,.22); }
/* Karta jest jednocześnie `.reveal`, a tamta reguła ustawia własny
   `transform` — i to ona wygrywa specyficznością. Dlatego oba stany
   dopisujemy tu wprost: przed wejściem zjazd z dołu, po wejściu dryf ze
   scrolla. Inaczej dryf w ogóle by się nie pojawił. */
.js .reveal.card {
  transform: translate3d(0, calc(22px * var(--motion)), 0);
  transition:
    opacity var(--t-base) var(--ease),
    filter var(--t-base) var(--ease),
    box-shadow var(--t-base) var(--ease),
    border-color var(--t-base) var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.js .reveal.card.is-in { transform: translate3d(0, calc(var(--dryf) + var(--uniesienie)), 0); }

/* ---- ikona: rysuje się kreską przy wejściu karty w kadr ----
 *
 * Każda ścieżka ma `pathLength="1"`, więc jeden zestaw wartości dasharray
 * działa niezależnie od tego, jak długa jest w rzeczywistości — inaczej
 * trzeba by mierzyć każdą z osobna w JS.
 */
.card__ico {
  width: 62px; height: 62px; border-radius: 17px;
  display: grid; place-items: center; margin-bottom: 1.6rem;
  background: linear-gradient(150deg, rgba(23,51,255,.14), rgba(23,51,255,.04));
  color: var(--blue);
  transition: background var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.card__ico svg { width: 34px; height: 34px; overflow: visible; }

.js .card__ico .ln {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 900ms var(--ease);
  transition-delay: calc(var(--i, 0) * 90ms + var(--o, 0) * 130ms + 120ms);
}
.js .card.is-in .card__ico .ln { stroke-dashoffset: 0; }

.card__ico .pkt { fill: currentColor; stroke: none; }
.js .card__ico .pkt,
.js .card__ico .kursor {
  opacity: 0;
  transition: opacity 400ms var(--ease);
  transition-delay: calc(var(--i, 0) * 90ms + 700ms);
}
.js .card.is-in .card__ico .pkt,
.js .card.is-in .card__ico .kursor { opacity: 1; }
.card__ico .kursor { fill: currentColor; stroke: none; }

/* Ruch ciągły, celowo powolny: sekcja ma żyć także wtedy, gdy nikt nie
   trzyma na niej kursora, ale trzy migające ikony na białym tle byłyby
   nie do zniesienia. Stąd długie cykle i mała amplituda. */
.js .card.is-in .card__ico .pkt--puls {
  animation: pktPuls 2.8s var(--ease) infinite;
  animation-delay: calc(var(--o, 0) * 220ms);
}
@keyframes pktPuls {
  0%, 62%, 100% { opacity: .32; transform: translateY(0); }
  74% { opacity: 1; transform: translateY(-2.2px); }
}
.js .card.is-in .card__ico .fala {
  fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round;
  animation: falaPuls 3.6s var(--ease) infinite;
}
@keyframes falaPuls {
  0%, 100% { opacity: .2; transform: scaleX(.82); }
  50% { opacity: .75; transform: scaleX(1.06); }
}
.card__ico .fala { transform-origin: 24px 43px; }
.js .card.is-in .card__ico .kursor {
  animation: kursorRuch 4.4s var(--ease) infinite;
}
@keyframes kursorRuch {
  0%, 100% { transform: translate(0, 0); }
  45% { transform: translate(-3.5px, -2.5px); }
  55% { transform: translate(-3.5px, -2.5px); }
}

/* Najechanie: kafel wypełnia się kolorem, ikona bieleje */
.card:hover .card__ico { background: var(--blue); color: #fff; }

.card__title { font-family: var(--font); font-weight: 500; font-size: var(--t-h3); letter-spacing: -0.015em; }
.card__text { margin-top: .75rem; color: var(--dim-on-light); font-size: .95rem; }

/* Wypunktowanie: konkrety pod opisem, żeby karta nie była samym akapitem */
.card__lista {
  margin-top: 1.25rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line-light);
  display: grid;
  gap: .55rem;
  font-size: .86rem;
  color: var(--dim-on-light);
}
.card__lista li { position: relative; padding-left: 1.35rem; }
.card__lista li::before {
  content: "";
  position: absolute; left: 0; top: .52em;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--blue);
  opacity: .55;
}

/* ===================== JAK DZIAŁAMY (kroki) ===================== */

.steps {
  position: relative;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.5rem; margin-top: clamp(2.5rem, 5vw, 4rem);
  --szyna: 0;
}
/* Szyna postępu: jedna linia przez wszystkie cztery kroki, wypełniana
   przewijaniem. Leży NA kreskach kroków (te zostają jako tło), więc nie
   trzeba ich usuwać ani liczyć szerokości kolumn. */
@media (min-width: 60.0625rem) {
  .steps::after {
    content: "";
    position: absolute; left: 0; top: 0;
    height: 1px;
    width: calc(var(--szyna) * 100%);
    background: linear-gradient(90deg, rgba(110,140,255,.25), var(--blue-soft));
    box-shadow: 0 0 12px rgba(110,140,255,.5);
  }
  .section--light .steps::after,
  .section--paper .steps::after {
    background: linear-gradient(90deg, rgba(23,51,255,.25), var(--blue));
    box-shadow: none;
  }
}
@media (max-width: 60rem) { .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 34rem) { .steps { grid-template-columns: minmax(0, 1fr); } }

.step {
  padding-top: 1.5rem;
  border-top: 1px solid var(--line-dark);
  /* Krok „zapala się", gdy minie go szyna. Przejście na kolorze i lekkim
     podniesieniu — bez skoków układu. */
  transition: transform var(--t-slow) var(--ease), opacity var(--t-slow) var(--ease);
  opacity: .55;
}
.step.is-akt { opacity: 1; transform: translateY(-4px); }
.step__num { transition: color var(--t-base) var(--ease); }
.step:not(.is-akt) .step__num { color: var(--text-dimmer); }
/* rgba(10,13,24,.55) dawało 4,29:1 — tuż pod progiem. --dim-on-light jest
   sprawdzony (6,6:1), a różnicę wobec kroku aktywnego niesie kolor
   (niebieski) i krycie całego bloku, nie sam stopień szarości. */
.section--light .step:not(.is-akt) .step__num,
.section--paper .step:not(.is-akt) .step__num { color: var(--dim-on-light); }
.section--light .step, .section--paper .step { border-top-color: var(--line-light); }
.step__num { font-family: var(--font); font-size: .78rem; letter-spacing: .1em; color: var(--blue-soft); }
.section--light .step__num { color: var(--blue); }
.step__title { font-family: var(--font); font-weight: 500; font-size: var(--t-h3); margin-top: .9rem; letter-spacing: -0.015em; }
.step__text { margin-top: .6rem; color: var(--text-dim); font-size: .93rem; }
.section--light .step__text, .section--paper .step__text { color: var(--dim-on-light); }

/* ========================== OPINIE ============================== */

.quotes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1rem, 1.6vw, 1.5rem); margin-top: clamp(2.5rem, 5vw, 4rem); }
@media (max-width: 60rem) { .quotes { grid-template-columns: minmax(0, 1fr); } }

.quote {
  border: 1px solid var(--line-dark); border-radius: 20px;
  padding: clamp(1.5rem, 2.2vw, 2rem);
  background: linear-gradient(160deg, rgba(255,255,255,.045), rgba(255,255,255,.015));
}
.quote__text { font-size: 1rem; line-height: 1.6; }
.quote__who { margin-top: 1.5rem; font-size: .83rem; color: var(--text-dim); }
.quote__stars { color: var(--blue-soft); font-size: .8rem; letter-spacing: .12em; margin-bottom: 1rem; }

/* Sekcja opinii jest BIAŁA, a karta była opisana wyłącznie dla ciemnego tła:
   obwódka i tło z białych alf znikały bez śladu, a podpis (--text-dim, czyli
   biel 58%) schodził na bieli do 1:1 — nazwiska i miasta w ogóle się nie
   renderowały. Wariant jasny robimy tak, jak reszta strony: nadpisaniem
   przy `.section--light`. */
.section--light .quote,
.section--paper .quote {
  border-color: var(--line-light);
  background: linear-gradient(160deg, rgba(10, 13, 24, .035), rgba(10, 13, 24, .012));
}
.section--light .quote__who,
.section--paper .quote__who { color: var(--dim-on-light); }
.section--light .quote__stars,
.section--paper .quote__stars { color: var(--blue); }

/* ========================== MIASTA ============================== */

.cities { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 2rem; }
.city {
  padding: .6rem 1.1rem; border-radius: 100px;
  border: 1px solid var(--line-dark); font-size: .85rem; color: var(--text-dim);
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.city:hover { color: var(--white); border-color: rgba(255,255,255,.32); }

/* ======================== DOMKNIĘCIE ============================ */

.closing {
  position: relative; overflow: hidden;
  padding-block: clamp(6rem, 15vh, 11rem);
  background:
    radial-gradient(90% 120% at 50% 120%, rgba(23,51,255,.85) 0%, rgba(10,26,181,.4) 38%, rgba(5,6,10,0) 72%),
    var(--ink);
  text-align: center;
}
.closing .title { max-width: 20ch; margin-inline: auto; }
.closing__actions { display: flex; gap: 1rem; justify-content: center; margin-top: 2.5rem; flex-wrap: wrap; }

/* ---- chromowane serce w tle domknięcia ----
   Na wzorcu bryła stoi ZA napisem, a przycisk wchodzi na nią. Sekcja musi
   więc być wyraźnie wyższa niż sam tekst, żeby serce miało gdzie stanąć. */
/* Proporcje odczytane ze wzorca: nagłówek ok. 22% wysokości kadru, przycisk
   ok. 32%, a bryła zajmuje pas od ok. 31% do 85% — czyli treść stoi WYSOKO,
   a serce pod nią, wchodząc przyciskom pod spód. Przy treści wyśrodkowanej
   nagłówek lądował na jasnych połówkach serca i znikał. */
.closing {
  /* Wyższa niż kadr, bo treść i bryła muszą się zmieścić JEDNA POD DRUGĄ.
     Na wzorcu blok tekstu zajmuje ok. 15% wysokości sekcji (jeden wiersz
     nagłówka i przycisk), u nas 38% — jest jeszcze nadtytuł, dwuwierszowy
     nagłówek i akapit. Przy równej wysokości serce nie miało gdzie stanąć
     inaczej niż na napisach. */
  min-height: clamp(700px, 102svh, 1120px);
  display: grid;
  align-content: start;
  padding-block: clamp(3.5rem, 9vh, 6rem) clamp(2rem, 5vh, 4rem);
}
.closing__canvas {
  position: absolute;
  left: 50%;
  /* Środek bryły WYRAŹNIE pod przyciskami. Na wzorcu przycisk leży na
     sercu, ale przy naszym bloku tekstu (nadtytuł + dwuwierszowy nagłówek
     + akapit + dwa przyciski) takie zetknięcie czyta się jak zasłanianie,
     nie jak kompozycja — stąd realny odstęp zamiast musnięcia.

     Płótno jest WYŻSZE niż sekcja (122%) i celowo sięga ponad jej górną
     krawędź. Serce wjeżdża z góry i musi mieć dokąd się schować: przy
     płótnie mieszczącym się w sekcji wjazd wyglądałby jak podskok
     w powietrzu, a na wzorcu bryła wchodzi obcięta krawędzią sekcji.
     Sam rozmiar serca w kadrze reguluje `dystans` w script.js — przy
     zmianie wysokości płótna trzeba go przeliczyć w tej samej proporcji. */
  top: 76%;
  transform: translate(-50%, -50%);
  height: min(122%, 1360px);
  width: auto;
  aspect-ratio: 1 / 1;
  z-index: 0;
  pointer-events: none;
}
.closing__tresc { position: relative; z-index: 1; }
/* Napis leży na bryle, więc potrzebuje własnego cienia — inaczej ginie
   w przepalonych refleksach. */
.closing .title,
.closing .sub { text-shadow: 0 2px 30px rgba(5, 6, 10, .75); }
@media (max-width: 60rem) {
  /* Na wąskim ekranie treść zajmuje więcej wysokości, więc serce musi zejść
     niżej — inaczej wchodzi pod nagłówek zamiast pod przyciski. */
  .closing__canvas { height: min(122%, 900px); top: 76%; }
  .closing { padding-bottom: clamp(3rem, 9vh, 6rem); }
}

/* ============================= FAQ ==============================
   Akordeon na `<details>` — bez JS-a i bez ARIA. Wysokości nie animujemy:
   `details` nie da się płynnie rozwinąć bez `::details-content`, które
   jest jeszcze zbyt świeże. Zamiast tego wysokość skacze, a SAMA TREŚĆ
   wjeżdża tym samym ruchem, co reszta strony (przesunięcie + rozmycie).
   W praktyce oko czyta to jako rozwinięcie, a działa wszędzie.
   ================================================================= */

.faq {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  max-width: 62rem;
  margin-inline: auto;
  border-top: 1px solid var(--line-light);
}

.faq__poz { border-bottom: 1px solid var(--line-light); }

.faq__pyt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: clamp(1.15rem, 2.2vh, 1.6rem) 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--font);
  font-weight: 500;
  font-size: clamp(1.02rem, 1.55vw, 1.28rem);
  letter-spacing: -0.012em;
  color: var(--ink-on-light);
  transition: color var(--t-base) var(--ease);
}
/* Domyślny trójkącik znika w obu rodzinach silników */
.faq__pyt::-webkit-details-marker { display: none; }
.faq__pyt::marker { content: ""; }
.faq__pyt:hover { color: var(--blue); }

/* Znak +/− rysowany dwiema kreskami: pionowa chowa się przy otwarciu,
   więc plus zamienia się w minus jednym obrotem, bez podmiany glifu. */
.faq__znak {
  position: relative;
  flex: 0 0 auto;
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 1px solid var(--line-light);
  transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.faq__znak::before,
.faq__znak::after {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 12px; height: 1.5px;
  margin: -.75px 0 0 -6px;
  background: var(--ink-on-light);
  transition: transform var(--t-base) var(--ease), opacity var(--t-base) var(--ease), background var(--t-base) var(--ease);
}
.faq__znak::after { transform: rotate(90deg); }
.faq__pyt:hover .faq__znak { border-color: rgba(23, 51, 255, .35); }
.faq__pyt:hover .faq__znak::before,
.faq__pyt:hover .faq__znak::after { background: var(--blue); }

/* Stan otwarty MUSI stać po regułach najechania i mieć nie mniejszą
   specyficzność. Przy słabszej kreski robiły się niebieskie na niebieskim
   kółku — po kliknięciu kursor zostaje nad znakiem, więc minus znikał
   dokładnie wtedy, kiedy był potrzebny. */
.faq__poz[open] .faq__znak { background: var(--blue); border-color: var(--blue); transform: rotate(180deg); }
.faq__poz[open] .faq__znak::before,
.faq__poz[open] .faq__znak::after { background: #fff; }
.faq__poz[open] .faq__znak::after { transform: rotate(0deg); opacity: 0; }

.faq__odp {
  padding-bottom: clamp(1.15rem, 2.2vh, 1.6rem);
  max-width: 54ch;
  color: var(--dim-on-light);
  font-size: .96rem;
}
/* Linki w odpowiedziach. Reguła bazowa `a { color: inherit; text-decoration:
   none }` sprawia, że bez tego odnośnik w akapicie jest nieodróżnialny od
   zwykłego tekstu — czytelnik nie ma jak go zauważyć. Traktowanie jak w
   prozie wpisu; FAQ stoi wyłącznie na sekcjach jasnych, stąd `--blue`. */
.faq__odp a {
  color: var(--blue);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.faq__odp a:hover { text-decoration-thickness: 2px; }

.js [open] .faq__odp { animation: faqIn 420ms var(--ease) both; }
@keyframes faqIn {
  from { opacity: 0; transform: translateY(calc(-10px * var(--motion))); filter: blur(calc(6px * var(--motion))); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

/* Wiersz jest `.reveal`, a tamta reguła narzuca własny `transform` —
   `details` musi zachować `display: block`, więc nadpisujemy tylko ruch. */
.js .reveal.faq__poz { transform: translateY(calc(14px * var(--motion))); }
.js .reveal.faq__poz.is-in { transform: none; }

/* ========================== STOPKA ============================== */

.footer { padding-block: clamp(3rem, 6vh, 4.5rem) 2rem; border-top: 1px solid var(--line-dark); }
.footer__grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1.4fr; gap: 2.5rem; }
@media (max-width: 60rem) { .footer__grid { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 34rem) { .footer__grid { grid-template-columns: minmax(0,1fr); } }

.footer__brand { font-family: var(--font); font-weight: 500; font-size: 1.1rem; display: inline-flex; align-items: center; gap: .5rem; }
.footer__tag { color: var(--text-dim); margin-top: .9rem; font-size: .9rem; max-width: 34ch; }
.footer__head { font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase; color: var(--text-dimmer); margin-bottom: 1rem; }
.footer__list { display: grid; gap: .6rem; }
.footer__list a, .footer__list li { color: var(--text-dim); font-size: .9rem; }
.footer__list a:hover { color: var(--white); }

/* Profile społecznościowe w stopce.
   Świadomie tylko te profile, które REALNIE istnieją — martwa ikona
   prowadząca do pustego konta kosztuje więcej zaufania, niż daje jej
   obecność. Dołożenie kolejnego serwisu to jedno <li>; styl jest wspólny,
   nie per-serwis, więc nic tu nie trzeba dopisywać. */
.footer__social { display: flex; gap: .6rem; margin-top: 1.35rem; }
.footer__social a {
  display: grid; place-items: center;
  width: 2.5rem; height: 2.5rem; border-radius: 50%;
  border: 1px solid var(--line-dark); color: var(--text-dim);
  transition: color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease);
}
.footer__social a:hover {
  color: var(--white); border-color: rgba(255,255,255,.34);
  background: rgba(255,255,255,.06);
}
.footer__social svg { width: 1rem; height: 1rem; fill: currentColor; }
.footer__bottom {
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--line-dark);
  font-size: .8rem; color: var(--text-dimmer);
}

/* ========================= INTRO HERO ==========================
 *
 * Wzorzec nie pokazuje hero od razu — buduje je z rozmycia. Kolejne
 * elementy wyostrzają się i rozjaśniają w kolejności czytania:
 * najpierw słowa nagłówka jedno po drugim, potem statystyki, akapity
 * i przyciski. Bez przesunięcia w pionie — tekst stoi w miejscu.
 *
 * Całość na animacji CSS, nie na klasie dokładanej skryptem: element
 * odsłania się sam, nawet gdyby JS się nie wykonał. Opóźnienie siedzi
 * w `--d` (milisekundy, bez jednostki), siła rozmycia w `--bl` —
 * duży tekst znosi 16 px, drobny rozpłynąłby się przy takiej wartości.
 */
@keyframes introIn {
  from { opacity: 0; filter: blur(var(--bl, 8px)); }
  to   { opacity: 1; filter: blur(0); }
}
.js .intro {
  animation: introIn var(--dur, var(--t-base)) var(--ease) both;
  animation-delay: calc(var(--d, 0) * 1ms);
}
/* Filtr działa na inline-block; słowa nagłówka są spanami w linii */
.hero .word.intro { display: inline-block; }

/* ===================== ODSŁANIANIE PRZY SCROLLU ================= */

.reveal { opacity: 1; }
.js .reveal {
  opacity: 0;
  transform: translateY(calc(22px * var(--motion)));
  /* Rozmycie na wejściu — na wzorcu karty wchodzą nieostre i dojeżdżają do
     ostrości (klatka f179: „Capital that compounds" jest jeszcze rozmyte,
     f182 już ostre). Sam ruch w górę bez rozmycia czyta się twardziej. */
  filter: blur(calc(9px * var(--motion)));
  transition:
    opacity var(--t-base) var(--ease),
    transform var(--t-base) var(--ease),
    filter var(--t-base) var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}
/* `filter` trzyma element na osobnej warstwie kompozytora — po odegraniu
   wejścia zdejmujemy go całkiem, nie zostawiamy blur(0). */
.js .reveal.is-in { opacity: 1; transform: none; filter: none; }

@media (prefers-reduced-motion: reduce) {
  :root { --motion: 0; --t-base: 240ms; --t-slow: 240ms; }
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-property: opacity !important;
    transition-duration: 240ms !important;
  }
  .ch { opacity: 1; filter: none; }
  .js .intro { animation: none; opacity: 1; filter: none; }
}

/* ===================== REALIZACJE — STOS KART =====================
   Proporcje odczytane z klatek wzorca:
   - karta zajmuje ~50% szerokości kadru, proporcja ~16:10, promień ~22px
   - nazwa nadrukowana na karcie, ok. 23% wysokości od jej górnej krawędzi
   - licznik i przycisk stoją przy krawędziach kadru, na wysokości nazwy
   ================================================================= */

.pf { position: relative; background: var(--ink); }

/* Wysokość napędza scroll: jeden „ekran" na kartę + zapas na wejście
   pierwszej i wyjście ostatniej. 46vh na kartę przy odstępie 1,46 wysokości
   karty daje ruch ok. 1,4× szybszy od samego przewijania — karta wyraźnie
   płynie przez kadr, zamiast wlec się razem ze stroną. */
.pf__scroll { height: calc(var(--pf-count, 12) * 46vh + 100vh); }

.pf__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
}

/* ---- Tło: rozmyta poświata bieżącej karty ------------------------------
 *
 * We wzorcu tło sekcji nie jest stałe — przejmuje kolor karty, która
 * właśnie jest w kadrze (zmierzone na klatkach: przy pastelowej karcie
 * narożniki mają #454855, przy czarnej #0e0e10). To ta sama grafika,
 * powiększona poza kadr i mocno rozmyta.
 *
 * Rozmycie liczone jest na małej powierzchni (30% kadru), a dopiero wynik
 * skalowany czterokrotnie — inaczej blur o promieniu ~90 px na pełnym
 * ekranie kosztuje kilka milisekund na klatkę. Dwie warstwy przenikają
 * się przy zmianie karty, więc nie ma przeskoku koloru.
 */
.pf__bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.pf__bg-l {
  position: absolute;
  left: -10%; top: -10%;
  width: 30%; height: 30%;
  transform: scale(4);
  transform-origin: 0 0;
  /* Powiększenie ma znaczenie. Na wzorcu karty to abstrakcyjne rendery, więc
     rozmycie CAŁEGO obrazka wciąż daje czytelne, wielkie plamy motywu. U nas
     na kartach są zrzuty całych stron — nagłówek, tekst, stopka — i rozmycie
     całości uśredniało się w jednolitą szarość. Kadrujemy więc środek zrzutu,
     tam gdzie w tych demo siedzi główny motyw (ząb z cząstek, las we mgle,
     łuk porcelany), i to on rozlewa się po tle. */
  background-size: 175%;
  background-position: 55% 48%;
  /* Odbarwienie: pomiar wzorca pokazuje, że poświata jest wyraźnie mniej
     nasycona niż sama karta — inaczej sekcja robi się kolorowa. Kontrast
     w drugą stronę: bez niego kadr po rozmyciu spłaszcza się do jednego tonu. */
  filter: blur(22px) saturate(.55) contrast(1.25);
  opacity: 0;
  will-change: opacity;
}

/* Winieta: trzyma krawędzie ciemne, żeby karta i nagłówek nie tonęły
   w poświacie jasnych zrzutów. */
.pf__veil {
  position: absolute; inset: 0; z-index: 1;
  background: radial-gradient(120% 82% at 50% 48%,
    rgba(5, 6, 10, 0) 40%, rgba(5, 6, 10, .14) 72%, rgba(5, 6, 10, .34) 100%);
  pointer-events: none;
}

.pf__title {
  position: absolute;
  top: clamp(5.5rem, 11vh, 8rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  font-family: var(--font);
  font-weight: 300;
  font-size: clamp(2rem, 4.2vw, 3.4rem);
  letter-spacing: -0.028em;
  white-space: nowrap;
  /* Nagłówek jest NAD kartami, dokładnie jak we wzorcu — karta wyjeżdżająca
     przechodzi pod napisem. Cień musi być wyraźny, bo pod spodem przejeżdżają
     zrzuty jasnych stron (PUNKT jest niemal biały). */
  text-shadow: 0 1px 2px rgba(5, 6, 10, .6), 0 2px 26px rgba(5, 6, 10, .9);
}

.pf__stage {
  position: absolute;
  inset: 0;
  z-index: 2;
  /* Punkt zbiegu leży w środku kadru — na klatkach wzorca początek układu
     (karta bez przesunięcia) wypada dokładnie na 50% wysokości, na tej
     samej linii co nazwa, licznik i przycisk.
     Karty jadą po łuku ku kamerze, więc pozycję liczy JS i podaje ją jako
     translate3d w pikselach; tutaj zostaje sama perspektywa. Jej wartość
     też ustawia JS (3,48 × wysokość karty), żeby na wąskim ekranie głębia
     była proporcjonalnie taka sama. */
  /* Rozmiar karty zależy też od WYSOKOŚCI okna, nie tylko od szerokości.
     Stos jest wyśrodkowany w pionie, a tytuł stoi na sztywno pod nawigacją —
     więc na niskim, szerokim ekranie górna krawędź kart podjeżdżała pod
     tytuł i odstęp znikał (przy oknie 900 px zostawało 39 px). Trzeci
     składnik `min()` przycina kartę na niskich ekranach; na wysokich nic
     nie zmienia, bo wygrywa tam 720px. */
  --pf-w: min(50vw, 720px, 72vh);
  perspective: 1600px;
  perspective-origin: 50% 50%;
}

.pf__card {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--pf-w);
  aspect-ratio: 16 / 10;
  margin-left: calc(var(--pf-w) / -2);
  margin-top: calc(var(--pf-w) / 3.2 * -1);
  border-radius: 22px;
  overflow: hidden;
  background: var(--ink-2);
  transform-style: preserve-3d;
  will-change: transform;
  box-shadow: 0 40px 90px rgba(0, 0, 0, .55);
}

@media (max-width: 60rem) {
  .pf__stage { --pf-w: min(84vw, 560px); }
}

.pf__card img { width: 100%; height: 100%; object-fit: cover; }

/* Wzorzec pokazuje karty bez nakładki — nazwa nie jest na nich nadrukowana,
   więc nie ma czego wygaszać. Zostaje sam cienki kant, żeby jasny zrzut nie
   zlewał się z poświatą tła, i delikatne domknięcie u dołu. */
.pf__card::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
  background: linear-gradient(180deg,
    rgba(5, 6, 10, .12) 0%, rgba(5, 6, 10, 0) 26%,
    rgba(5, 6, 10, 0) 68%, rgba(5, 6, 10, .26) 100%);
  pointer-events: none;
}

/* ---- Nazwa realizacji: etykieta stojąca w miejscu ---------------------
 *
 * Sprawdzone na klatkach wzorca: napis siedzi zawsze na 50% wysokości
 * kadru, niezależnie od tego, gdzie akurat jest karta (raz wypada przy
 * jej górnej krawędzi, raz trzy czwarte wysokości niżej). To osobna
 * etykieta, nie tekst nadrukowany na obrazku — dzięki temu karta może
 * przelecieć przez kadr bez ciągnięcia napisu za sobą.
 */
.pf__now {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 4;
  white-space: nowrap;
  font-family: var(--font);
  font-weight: 400;
  font-size: clamp(1.15rem, 1.8vw, 1.65rem);
  letter-spacing: -0.005em;
  color: var(--white);
  text-shadow: 0 1px 2px rgba(5, 6, 10, 1), 0 2px 12px rgba(5, 6, 10, .95);
  will-change: opacity;
}
/* Miękka plama pod napisem. We wzorcu jej nie ma — tam karty to abstrakcyjne
   rendery, na ogół ciemne w środku. U nas na kartach są zrzuty stron, w tym
   zupełnie białe (PUNKT), na których biały napis bez podkładu znika. */
.pf__now::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 27em; height: 6.4em;
  margin: -3.2em 0 0 -13.5em;
  z-index: -1;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(5, 6, 10, .88) 0%, rgba(5, 6, 10, .62) 38%,
    rgba(5, 6, 10, .24) 62%, rgba(5, 6, 10, 0) 80%);
  pointer-events: none;
}

/* Licznik i przycisk — na tej samej linii co nazwa, przy krawędziach kadru */
.pf__count,
.pf__btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
}
.pf__count {
  left: var(--pad);
  font-family: var(--font);
  font-size: .82rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.pf__btn {
  right: var(--pad);
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .7rem 1.3rem; border-radius: 100px;
  border: 1px solid var(--line-dark);
  font-family: var(--font); font-size: .84rem; color: var(--white);
  background: rgba(10, 13, 24, .5);
  backdrop-filter: blur(10px);
  transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.pf__btn:hover { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.32); }

/* Na wąskim ekranie karta zajmuje prawie całą szerokość, więc licznik
   i przycisk nie zmieszczą się obok niej — schodzą pod spód. */
@media (max-width: 48rem) {
  .pf__count, .pf__btn {
    top: auto; bottom: clamp(1.5rem, 5vh, 3rem);
    transform: none;
  }
  .pf__count { left: var(--pad); }
  .pf__btn { right: var(--pad); }
}

/* ==================== PRZEJŚCIA MIĘDZY SEKCJAMI ====================
 *
 * Sprawdzone na klatkach wzorca (a98–a110, f173–f182, z198–z210) i wyszło
 * inaczej, niż zakładaliśmy wcześniej:
 *
 * 1. ZWYKŁE STYKI SĄ TWARDE. Ani ciemna sekcja nad jasną, ani jasna nad
 *    ciemną nie mają zaokrągleń — krawędź idzie prostą linią na całą
 *    szerokość okna (powiększenie narożników z klatek a98 i f179 nie
 *    pokazuje śladu łuku). Zaokrąglony jest tylko HERO, i to dlatego, że
 *    na wzorcu jest wsuniętym panelem z marginesem z każdej strony —
 *    a nie dlatego, że tak wyglądają styki sekcji.
 *
 * 2. PANEL PEŁNOEKRANOWY SIĘ ZATRZYMUJE, a następna sekcja NA NIEGO
 *    NAJEŻDŻA. Pomiar na klatkach: między f173 a f179 biała sekcja
 *    podjechała o 330 px, a napis „Financial momentum" pod nią przesunął
 *    się o 1 px. To samo na styku formularza z domknięciem (z206→z210:
 *    ciemny panel +93 px, biały tekst 0 px). Sekcje WYŻSZE niż okno tak
 *    nie robią — te przewijają się normalnie (a98→a110: wszystko jedzie
 *    równo o 104 px).
 * ================================================================= */

/* Hero: wsunięty panel, jak na wzorcu */
.hero {
  border-bottom-left-radius: clamp(18px, 2vw, 30px);
  border-bottom-right-radius: clamp(18px, 2vw, 30px);
}

/* --- Panel pełnoekranowy z najazdem --------------------------------
 *
 * `.slot` obejmuje parę: panel + sekcję, która ma na niego najechać.
 * Panel jest `sticky` w obrębie tej pary, więc zatrzymuje się na górze
 * okna i stoi dokładnie tak długo, jak długo sekcja pod nim się na niego
 * wsuwa — a puszcza w chwili, gdy zakryje go w całości. Nie wymaga to ani
 * ujemnych marginesów, ani doliczania wysokości: pole manewru `sticky`
 * jest z definicji równe wysokości sekcji najeżdżającej.
 */
.slot { position: relative; }
.slot > .przej { position: sticky; top: 0; z-index: 0; }
.slot > .przej + * { position: relative; z-index: 1; }

/* ===================== PASEK NAZW REALIZACJI ======================
   Dwa rzędy płynące w przeciwne strony. Zawartość wstrzykuje JS,
   ale sam ruch jest w CSS — dzięki temu bez JS pasek po prostu stoi.
   ================================================================= */

.ticker {
  background: var(--ink);
  padding-block: clamp(1.75rem, 4vh, 3rem);
  overflow: hidden;
  display: grid;
  gap: .9rem;
  border-bottom: 1px solid rgba(255, 255, 255, .06);
}

.ticker__row { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); }

.ticker__track {
  display: flex;
  gap: 2.5rem;
  width: max-content;
  animation: ticker 44s linear infinite;
}
.ticker__row--rev .ticker__track { animation-direction: reverse; animation-duration: 52s; }

.ticker__item {
  display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--font); font-size: .92rem; letter-spacing: -0.01em;
  color: var(--text-dimmer);
  white-space: nowrap;
}
.ticker__item::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--blue-soft); opacity: .6;
}

@keyframes ticker {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .ticker__track { animation: none; }
}

/* =========================================================================
   PODSTRONY
   Wszystko poniżej dotyczy oferta.html / realizacje.html / kontakt.html.
   Podstrony jadą na tych samych tokenach co strona główna i na tym samym
   rytmie ciemna → jasna → ciemna. Różnica jest jedna: zamiast hero na
   płótnie mają płaską planszę tytułową (`.ph`) — trzy podstrony nie
   potrzebują trzech scen 3D, a każda kosztowałaby 600 kB biblioteki.
   ========================================================================= */

/* Aktywna pozycja w pasku. Na stronie głównej nawigacja prowadziła wyłącznie
   do kotwic, więc nie było czego oznaczać; przy czterech dokumentach
   „gdzie jestem" przestaje być oczywiste. */
.nav__link[aria-current="page"] {
  color: var(--white);
  background: rgba(255, 255, 255, .09);
}
.nav__panel-link[aria-current="page"] { color: var(--white); background: rgba(255, 255, 255, .09); }

/* Domknięcie sekcji usług na stronie głównej — stąd prowadzi jedyne
   przejście do cennika. */
/* Używana i na ciemnych, i na jasnych sekcjach — stąd para tokenów.
   Baza jest pod czerń, jak wszędzie w tym arkuszu. */
.cards__stopka {
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
  text-align: center;
  color: var(--text-dim);
  font-size: .95rem;
}
.cards__stopka .link-u { color: var(--blue-soft); font-weight: 500; margin-left: .35rem; }
.section--light .cards__stopka,
.section--paper .cards__stopka { color: var(--dim-on-light); }
.section--light .cards__stopka .link-u,
.section--paper .cards__stopka .link-u { color: var(--blue); }

/* ===================== PLANSZA TYTUŁOWA PODSTRONY ================== */

.ph {
  position: relative;
  overflow: hidden;
  padding-block: clamp(8rem, 17vh, 11.5rem) clamp(3.5rem, 8vh, 6rem);
  /* Ten sam rozkład światła co w domknięciu strony głównej, tylko płycej:
     błękit wchodzi z dołu i gaśnie w połowie wysokości, żeby nie zabierał
     uwagi treści, która zaczyna się od razu pod planszą. */
  background:
    radial-gradient(88% 130% at 12% 118%, rgba(30, 62, 255, .85) 0%, rgba(14, 32, 170, .42) 38%, rgba(5, 6, 10, 0) 74%),
    radial-gradient(60% 90% at 88% -20%, rgba(48, 86, 255, .34) 0%, rgba(5, 6, 10, 0) 70%),
    var(--ink);
  border-bottom-left-radius: clamp(18px, 2vw, 30px);
  border-bottom-right-radius: clamp(18px, 2vw, 30px);
}
.ph__title { font-size: var(--t-display); max-width: 17ch; }
.ph__lead {
  margin-top: 1.4rem;
  max-width: 54ch;
  color: var(--text-dim);
  font-size: clamp(1rem, 1.25vw, 1.12rem);
  line-height: 1.55;
}
.ph__actions { display: flex; align-items: center; gap: 1.25rem; margin-top: 2.25rem; flex-wrap: wrap; }

/* Okruszki: dwa poziomy, więc bez rozbudowanego szlaku — chodzi wyłącznie
   o wyjście z podstrony jednym kliknięciem. */
.okruch { margin-bottom: 1.5rem; font-size: var(--t-meta); color: var(--text-dimmer); }
.okruch a { color: var(--text-dim); }
.okruch a:hover { color: var(--white); }
.okruch span { margin-inline: .5rem; }

/* ============================ PAKIETY ============================== */

.pak {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 1.6vw, 1.5rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
  /* Karty równej wysokości, przycisk zawsze przy dolnej krawędzi —
     inaczej trzy różne długości list rozjeżdżają linię przycisków. */
  align-items: stretch;
}
@media (max-width: 60rem) { .pak { grid-template-columns: minmax(0, 1fr); } }

.pak__karta {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--line-light);
  border-radius: 20px;
  padding: clamp(1.5rem, 2.4vw, 2.25rem);
  transition: box-shadow var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.pak__karta:hover { box-shadow: 0 18px 50px rgba(10, 13, 24, .09); border-color: rgba(23, 51, 255, .22); }

/* Jeden wyróżniony wariant — reszta ma być wyraźnie podrzędna wizualnie,
   bo to on jest domyślnym wyborem. */
.pak__karta--top {
  position: relative;
  background: var(--white);
  border-color: rgba(23, 51, 255, .38);
  box-shadow: 0 22px 60px rgba(23, 51, 255, .12);
}
.pak__znacznik {
  position: absolute;
  top: -.75rem; left: clamp(1.5rem, 2.4vw, 2.25rem);
  padding: .3rem .8rem;
  border-radius: 100px;
  background: var(--blue);
  color: #fff;
  font-family: var(--font); font-weight: 500;
  font-size: .72rem; letter-spacing: .06em; text-transform: uppercase;
}

.pak__nazwa {
  font-family: var(--font); font-weight: 500; font-size: var(--t-h3);
  letter-spacing: -0.015em;
}
.pak__cena {
  font-family: var(--font); font-weight: 300;
  font-size: clamp(2.1rem, 3.6vw, 2.9rem);
  line-height: 1.05; letter-spacing: -0.03em;
  margin-top: .85rem;
  font-variant-numeric: tabular-nums;
}
.pak__cena small { font-size: .38em; font-weight: 400; letter-spacing: 0; margin-left: .35em; color: var(--dim-on-light); }
.pak__abon { margin-top: .5rem; font-size: .9rem; color: var(--dim-on-light); }

.pak__lista {
  margin-top: 1.5rem;
  padding-top: 1.35rem;
  border-top: 1px solid var(--line-light);
  display: grid; gap: .6rem;
  font-size: .89rem;
  color: var(--ink-on-light);
}
.pak__lista li { position: relative; padding-left: 1.55rem; }
/* Znaczek jest rysowany, nie pisany: „✓" i „✗" w treści czytnik ekranu
   przeczytałby jako znaki, a nie jako „jest / nie ma”. Stan niesie klasa,
   a dla czytnika zostaje słowo w `.visually-hidden`. */
.pak__lista li::before {
  content: "";
  position: absolute; left: 0; top: .34em;
  width: 15px; height: 15px; border-radius: 50%;
  background: rgba(23, 51, 255, .12);
}
.pak__lista li::after {
  content: "";
  position: absolute; left: 4px; top: .62em;
  width: 7px; height: 4px;
  border-left: 1.6px solid var(--blue);
  border-bottom: 1.6px solid var(--blue);
  transform: rotate(-45deg);
}
/* Pozycji spoza pakietu NIE przygaszamy dodatkowo: --dim-on-light plus
   opacity .8 dawało 3,63:1, czyli poniżej progu AA. Że czegoś nie ma,
   niesie przekreślony znaczek, a nie stopień szarości. */
.pak__lista li.nie { color: var(--dim-on-light); }
.pak__lista li.nie::before { background: rgba(10, 13, 24, .07); }
.pak__lista li.nie::after {
  left: 4.5px; top: .55em;
  width: 6px; height: 6px;
  border: 0;
  background:
    linear-gradient(currentColor, currentColor) center/100% 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center/1.5px 100% no-repeat;
  color: rgba(10, 13, 24, .45);
  transform: rotate(45deg);
}

/* Przycisk zawsze na dole karty — `auto` na górnym marginesie wyrównuje
   linię przycisków mimo różnej długości list. */
.pak__btn {
  margin-top: auto;
  padding-top: 1.6rem;
}
.btn--outline {
  border: 1px solid var(--line-light);
  color: var(--ink-on-light);
  background: transparent;
  width: 100%; justify-content: center;
}
.btn--outline:hover { background: var(--white); border-color: #cfd4e4; }
.pak__karta--top .btn--outline { background: var(--blue); border-color: var(--blue); color: #fff; }
.pak__karta--top .btn--outline:hover { background: var(--blue-deep); border-color: var(--blue-deep); }

/* ==================== STRONA BEZ ABONAMENTU ======================== */

.bezab {
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: center;
  padding: clamp(1.75rem, 3vw, 2.5rem);
  border: 1px dashed #c9cfe4;
  border-radius: 20px;
  background: var(--paper);
}
@media (max-width: 48rem) { .bezab { grid-template-columns: minmax(0, 1fr); } }
.bezab__cena {
  text-align: center;
  font-family: var(--font); font-weight: 300;
  font-size: clamp(2.1rem, 3.6vw, 2.9rem);
  line-height: 1.05; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.bezab__cena span { display: block; margin-top: .5rem; font-size: .3em; letter-spacing: 0; color: var(--dim-on-light); }
.bezab__cena .btn { margin-top: 1.25rem; }

/* ====================== TABELA PORÓWNAWCZA ========================= */

/* Tabela nie mieści się na telefonie i nie da się jej sensownie zwinąć —
   ma trzy kolumny liczb do porównania. Przewija się więc poziomo we
   WŁASNYM kontenerze, żeby nie ciągnąć za sobą całej strony. */
.tabela__ramka {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  overflow-x: auto;
  border: 1px solid var(--line-light);
  border-radius: 18px;
  /* Cień krawędziowy: pokazuje, że tabela ma ciąg dalszy w bok. Bez niego
     na telefonie widać trzy kolumny z czterech i nic nie mówi, że reszta
     jest o przesunięcie palcem stąd. Warstwy `local` jadą razem z treścią
     i zasłaniają cień, gdy dojedzie się do krawędzi; warstwy `scroll` stoją
     w miejscu i są tym cieniem. */
  background:
    linear-gradient(90deg, var(--white) 40%, rgba(255, 255, 255, 0)) left / 30px 100% no-repeat local,
    linear-gradient(90deg, rgba(255, 255, 255, 0), var(--white) 60%) right / 30px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(10, 13, 24, .16), rgba(10, 13, 24, 0)) left / 13px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(10, 13, 24, .16), rgba(10, 13, 24, 0)) right / 13px 100% no-repeat scroll,
    var(--white);
}
.tabela { width: 100%; min-width: 620px; border-collapse: collapse; font-size: .9rem; }
.tabela th, .tabela td { padding: .85rem 1.1rem; text-align: center; }
.tabela th:first-child, .tabela td:first-child { text-align: left; }
.tabela thead th {
  position: sticky; top: 0;
  background: var(--white);
  border-bottom: 1px solid var(--line-light);
  font-family: var(--font); font-weight: 500; font-size: .92rem;
}
.tabela thead th.top { color: var(--blue); }
.tabela tbody th {
  text-align: left;
  background: var(--paper);
  font-size: .74rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dim-on-light);
  font-weight: 500;
}
.tabela td { border-bottom: 1px solid #eef0f6; color: var(--dim-on-light); }
.tabela td:first-child { color: var(--ink-on-light); }
.tabela .top { background: rgba(23, 51, 255, .04); }
.tabela .tak { color: var(--blue); font-size: 1.05rem; }
.tabela .nie { color: rgba(10, 13, 24, .38); }

/* ======================= DLA KOGO / ZWROT ========================== */

.kogo { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1rem, 1.6vw, 1.5rem); margin-top: clamp(2.5rem, 5vw, 4rem); }
@media (max-width: 60rem) { .kogo { grid-template-columns: minmax(0, 1fr); } }
.kogo__poz { padding-top: 1.5rem; border-top: 1px solid var(--line-dark); }
.section--light .kogo__poz, .section--paper .kogo__poz { border-top-color: var(--line-light); }
.kogo__nazwa { font-family: var(--font); font-weight: 500; font-size: var(--t-h3); letter-spacing: -0.015em; }
.kogo__opis { margin-top: .8rem; color: var(--text-dim); font-size: .93rem; }
.section--light .kogo__opis, .section--paper .kogo__opis { color: var(--dim-on-light); }
/* Odnośnik w opisie. Reguła bazowa `a { color: inherit; text-decoration:
   none }` czyni go nieodróżnialnym od tekstu — ten sam przypadek co w
   odpowiedziach FAQ. `.kogo` stoi na sekcjach ciemnych ORAZ jasnych,
   więc potrzebna jest para tokenów, jak przy `.cards__stopka`. */
.kogo__opis a {
  color: var(--blue-soft);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.section--light .kogo__opis a,
.section--paper .kogo__opis a { color: var(--blue); }
.kogo__opis a:hover { text-decoration-thickness: 2px; }

.kogo__dla { margin-top: 1rem; font-size: .84rem; color: var(--blue-soft); }
.section--light .kogo__dla, .section--paper .kogo__dla { color: var(--blue); }

/* Stoi na sekcjach jasnych (zwrot z inwestycji, rynek lokalny na stronach
   miejskich) — a baza jest pod czerń, jak reszta arkusza. Bez pary poniżej
   akapit znikał całkowicie: biel 58% na bieli to 1,01:1. */
.zwrot__wstep { max-width: 62ch; margin-top: 1.25rem; color: var(--text-dim); }
.section--light .zwrot__wstep,
.section--paper .zwrot__wstep { color: var(--dim-on-light); }
.zwrot__gwiazdka { margin-top: 1.75rem; font-size: .8rem; color: var(--text-dimmer); max-width: 62ch; }
.section--light .zwrot__gwiazdka,
.section--paper .zwrot__gwiazdka { color: var(--dim-on-light); }

/* ===================== REALIZACJE — SIATKA ========================= */

.prace {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, 2vw, 2rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}
@media (max-width: 62rem) { .prace { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 40rem) { .prace { grid-template-columns: minmax(0, 1fr); } }

.praca { display: flex; flex-direction: column; }
.praca__kadr {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--ink-2);
  border: 1px solid var(--line-dark);
}
.praca__kadr img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--t-slow) var(--ease);
}
.praca:hover .praca__kadr img { transform: scale(1.035); }
.praca__chip {
  position: absolute; left: .85rem; bottom: .85rem;
  padding: .35rem .8rem; border-radius: 100px;
  background: rgba(10, 13, 24, .62);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, .16);
  font-size: .74rem; letter-spacing: .04em;
}
.praca__nazwa {
  font-family: var(--font); font-weight: 500; font-size: var(--t-h3);
  letter-spacing: -0.015em; margin-top: 1.15rem;
}
.praca__opis { margin-top: .6rem; color: var(--text-dim); font-size: .92rem; }
.praca__link { margin-top: 1rem; font-size: .86rem; color: var(--blue-soft); display: inline-flex; align-items: center; gap: .4rem; }

/* UWAGA przy dokładaniu komponentów: --text-dim i --text-dimmer to biel
   z alfą, a --blue-soft jest dobrany pod czerń. Na `.section--light` biel
   znika bez śladu, a jasny błękit spada do 3,6:1. Każdy nowy element
   z tymi tokenami potrzebuje więc pary — tak jak `.sub`, `.step__text`
   czy `.quote__who` wyżej. Siatka realizacji stoi na białym, więc:  */
.section--light .praca__opis,
.section--paper .praca__opis { color: var(--dim-on-light); }
.section--light .praca__link,
.section--paper .praca__link { color: var(--blue); }
/* Chip leży na ciemnej plamie NA zdjęciu, więc jest biały niezależnie
   od tego, na jakiej sekcji stoi karta. */
.praca__chip { color: var(--white); }
.praca__link::after { content: "↗"; font-size: .9em; }
.praca a:focus-visible { outline-offset: 5px; }

/* ========================== FORMULARZ ============================== */

.kont {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: clamp(2rem, 4vw, 4rem);
  align-items: start;
  margin-top: clamp(2.5rem, 5vw, 4rem);
}
@media (max-width: 60rem) { .kont { grid-template-columns: minmax(0, 1fr); } }

/* Odstęp od akapitu wprowadzającego — bez niego pierwsza etykieta klei się
   do zdania nad nią i czyta jak jego dalszy ciąg. */
.kont__form { margin-top: clamp(1.75rem, 3vw, 2.5rem); }
.form { display: grid; gap: 1.15rem; }
.form__para { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.15rem; }
@media (max-width: 40rem) { .form__para { grid-template-columns: minmax(0, 1fr); } }

.pole { display: grid; gap: .45rem; }
/* Etykieta widoczna, nie w placeholderze: placeholder znika przy pisaniu
   i po wypełnieniu formularza nie wiadomo już, co jest w którym polu. */
.pole > label { font-size: .84rem; color: var(--dim-on-light); }
.pole .wym { color: var(--blue); }
.pole input, .pole select, .pole textarea {
  width: 100%;
  /* 16 px minimum — poniżej Safari na iPhonie samo przybliża stronę
     przy wejściu w pole i użytkownik zostaje z rozjechanym układem. */
  font: inherit; font-size: 1rem;
  /* 48 px wysokości: powyżej progu 44 px dla celu dotykowego */
  padding: .8rem 1rem;
  color: var(--ink-on-light);
  background: var(--white);
  border: 1px solid var(--line-light);
  border-radius: 12px;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.pole textarea { min-height: 8.5rem; resize: vertical; }
.pole input:focus, .pole select:focus, .pole textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(23, 51, 255, .14);
}
.pole input[aria-invalid="true"], .pole textarea[aria-invalid="true"] { border-color: #c0392b; }

/* Komunikat stoi POD swoim polem, nie w zbiorczej liście na górze —
   inaczej trzeba wodzić wzrokiem tam i z powrotem. */
.pole-blad { font-size: .82rem; color: #c0392b; }
.pole-blad::before { content: "⚠ "; }

.form__zgoda { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .7rem; align-items: start; font-size: .84rem; color: var(--dim-on-light); }
.form__zgoda input { width: 20px; height: 20px; margin-top: .12rem; accent-color: var(--blue); }

.form__stopka { display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap; }
.form-status { font-size: .9rem; color: #c0392b; }
.form-status.is-ok { color: #17803d; }
.form-status:empty { display: none; }

/* Panel obok formularza: konkrety, po które klient tu przyszedł */
.dane { display: grid; gap: 1.5rem; }
.dane__poz { display: grid; gap: .3rem; padding-top: 1.25rem; border-top: 1px solid var(--line-light); }
.dane__poz:first-child { padding-top: 0; border-top: 0; }
.dane__etykieta { font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase; color: var(--dim-on-light); }
.dane__wartosc { font-family: var(--font); font-weight: 500; font-size: 1.12rem; letter-spacing: -0.01em; }
.dane__wartosc a:hover { color: var(--blue); }

.dostaniesz {
  margin-top: 2rem;
  padding: clamp(1.35rem, 2.2vw, 1.85rem);
  border-radius: 18px;
  background: var(--paper);
  border: 1px solid var(--line-light);
}
.dostaniesz__head { font-family: var(--font); font-weight: 500; font-size: 1.02rem; letter-spacing: -0.01em; }
.dostaniesz ul { margin-top: 1rem; display: grid; gap: .6rem; font-size: .89rem; color: var(--dim-on-light); }
.dostaniesz li { position: relative; padding-left: 1.35rem; }
.dostaniesz li::before {
  content: ""; position: absolute; left: 0; top: .52em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--blue); opacity: .55;
}

/* Domknięcie bez bryły 3D. `.closing` rezerwuje ponad wysokość okna, bo na
   stronie głównej musi zmieścić serce pod treścią; podstrony three.js nie
   ładują, więc ta rezerwa zrobiłaby z sekcji pustą planszę. */
.closing--plaskie {
  min-height: 0;
  padding-block: clamp(4.5rem, 11vh, 8rem);
  align-content: center;
}
.closing--plaskie .title,
.closing--plaskie .sub { text-shadow: none; }

/* Kadr realizacji jest `<span>`, nie `<div>` — cała karta jest jednym
   linkiem, a link nie może obejmować bloków. Stąd `display: block`. */
.praca > a { display: block; color: inherit; }
.praca__kadr { display: block; }
.praca__chip { display: inline-flex; align-items: center; }

/* =========================================================================
   STRONY MIEJSKIE
   Osiem stron pod frazy „stomatolog [miasto]" plus hub, który je ze sobą
   wiąże. Hub stoi na każdej z nich i na cenniku — to on odbudowuje
   linkowanie wewnętrzne, które przed redesignem szło ze strony głównej.
   ========================================================================= */

/* ---- Rynek lokalny: trzy liczby ---- */
.rynek {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 2rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}
@media (max-width: 48rem) { .rynek { grid-template-columns: minmax(0, 1fr); } }

.rynek__poz { padding-top: 1.5rem; border-top: 1px solid var(--line-dark); }
.section--light .rynek__poz,
.section--paper .rynek__poz { border-top-color: var(--line-light); }
.rynek__num {
  font-family: var(--font); font-weight: 300;
  font-size: clamp(1.9rem, 3.4vw, 2.8rem);
  line-height: 1.05; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.rynek__opis { margin-top: .75rem; font-size: .9rem; color: var(--text-dim); }
.section--light .rynek__opis,
.section--paper .rynek__opis { color: var(--dim-on-light); }

/* Cztery kolumny — Warszawa ma cztery bloki dzielnicowe, reszta trzy */
@media (min-width: 60.0625rem) { .kogo--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* ---- Hub miast ----
   Bieżące miasto zostaje w siatce, ale jako etykieta, nie link: inaczej
   każda z ośmiu stron linkowałaby sama do siebie, a czytnik ekranu
   zapowiadałby link prowadzący donikąd. */
.hub {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .75rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}
/* Osiem miast rozkłada się na 4+4, a nie 6+2 — dwa sieroce kafle w drugim
   rzędzie czytały się jak niedokończona siatka. */
@media (min-width: 62rem) {
  .hub { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.hub__poz {
  display: block;
  padding: 1rem 1.15rem;
  border: 1px solid var(--line-light);
  border-radius: 14px;
  background: var(--white);
  font-family: var(--font); font-weight: 500; font-size: .95rem;
  letter-spacing: -0.01em;
  color: var(--ink-on-light);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.hub__poz:hover { border-color: rgba(23, 51, 255, .32); box-shadow: 0 8px 24px rgba(23, 51, 255, .1); }
.hub__poz span {
  display: block; margin-top: .3rem;
  font-family: var(--font-body); font-weight: 400; font-size: .8rem;
  color: var(--blue);
}
.hub__poz--tu {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}
.hub__poz--tu span { color: rgba(255, 255, 255, .78); }

/* ---- Klaster usług: jeden wiersz linków domykający stronę miejską ---- */
.klaster {
  margin-top: clamp(2rem, 4vw, 3rem);
  text-align: center;
  font-size: .95rem;
  color: var(--dim-on-light);
  line-height: 2;
}
.klaster a { color: var(--blue); font-weight: 500; }
.klaster a:hover { text-decoration: underline; text-underline-offset: 3px; }
/* Kropka rozdzielająca — ozdobna, ale ma być widoczna: --line-light na
   --paper to praktycznie ten sam ton. */
.klaster__sep { margin-inline: .5rem; color: #b9c0d4; }

/* Przejście z karty usługi na jej podstronę. Stoi pod wypunktowaniem, żeby
   nie konkurowało z treścią karty — to link „czytaj dalej", nie CTA. */
.card__wiecej { margin-top: 1.25rem; font-size: .88rem; }
.card__wiecej .link-u { color: var(--blue); font-weight: 500; }

/* =========================================================================
   TREŚĆ DŁUGA — wpisy blogowe, dokumenty prawne, case studies
   Wszystko poniżej dotyczy stron, na których liczy się czytanie, a nie
   efekt: blog, polityka prywatności, regulamin, opisy realizacji.
   ========================================================================= */

/* ---- Proza: wspólna typografia dla wpisu i dokumentu prawnego ----
   Miara wiersza 68ch — powyżej oko gubi początek następnego wiersza,
   poniżej tekst zaczyna skakać przy dłuższych polskich słowach. */
.proza {
  max-width: 68ch;
  color: var(--ink-on-light);
  font-size: 1.02rem;
  line-height: 1.75;
}
.proza > * + * { margin-top: 1.15em; }
.proza h2 {
  font-family: var(--font); font-weight: 400;
  font-size: clamp(1.45rem, 2.4vw, 1.9rem);
  line-height: 1.2; letter-spacing: -0.02em;
  margin-top: 2.4em;
}
.proza h3 {
  font-family: var(--font); font-weight: 500;
  font-size: clamp(1.12rem, 1.7vw, 1.32rem);
  letter-spacing: -0.015em;
  margin-top: 1.9em;
}
.proza h2 + p, .proza h3 + p { margin-top: .7em; }
.proza a { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.proza a:hover { text-decoration-thickness: 2px; }
.proza strong { font-weight: 500; color: var(--ink-on-light); }
.proza ul, .proza ol { display: grid; gap: .5rem; margin-top: 1em; }
.proza ol { counter-reset: poz; }
.proza li { position: relative; padding-left: 1.5rem; }
.proza ul > li::before {
  content: ""; position: absolute; left: .15rem; top: .68em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--blue); opacity: .55;
}
.proza ol > li::before {
  counter-increment: poz; content: counter(poz) ".";
  position: absolute; left: 0; top: 0;
  font-family: var(--font); color: var(--blue); font-size: .92em;
}
.proza hr { margin: 2.4em 0; border: 0; border-top: 1px solid var(--line-light); }
.proza blockquote {
  margin-top: 1.6em; padding-left: 1.35rem;
  border-left: 2px solid var(--blue);
  color: var(--dim-on-light);
}
/* Tabele we wpisach przewijają się we własnym kontenerze — patrz `.tabela` */
.proza table { width: 100%; border-collapse: collapse; font-size: .93rem; margin-top: 1.4em; }
.proza th, .proza td { padding: .7rem .9rem; text-align: left; border-bottom: 1px solid var(--line-light); }
.proza thead th { font-family: var(--font); font-weight: 500; background: var(--paper); }
.proza td:first-child { color: var(--ink-on-light); }

/* ---- Wpis blogowy ---- */
.wpis { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: start; }
.wpis__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  font-size: var(--t-meta); letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-dim);
}
.wpis__meta span::after { content: "·"; margin-left: .6rem; opacity: .5; }
.wpis__meta span:last-child::after { content: none; }
.wpis__kat {
  padding: .3rem .75rem; border-radius: 100px;
  border: 1px solid var(--line-dark);
  letter-spacing: .08em;
}

/* ---- „Czytaj też" ----
   Trzy wpisy z tego samego klastra tematycznego. Sekcja MUSI się
   wyrenderować zawsze: na produkcji dobierała po kategorii i przy wpisie
   samotnym w swojej kategorii nie pokazywała się wcale — przez co trzy
   najnowsze teksty nie miały ani jednego linku wychodzącego (ZAD. 53). */
.czytaj { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1rem, 1.6vw, 1.5rem); margin-top: clamp(2rem, 4vw, 3rem); }
@media (max-width: 60rem) { .czytaj { grid-template-columns: minmax(0, 1fr); } }
.czytaj__poz {
  display: block;
  padding: 1.35rem;
  border: 1px solid var(--line-light);
  border-radius: 18px;
  background: var(--white);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.czytaj__poz:hover { border-color: rgba(23, 51, 255, .28); box-shadow: 0 10px 30px rgba(10, 13, 24, .07); }
.czytaj__kat { font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase; color: var(--blue); }
.czytaj__tyt {
  margin-top: .6rem;
  font-family: var(--font); font-weight: 500; font-size: 1.02rem;
  line-height: 1.35; letter-spacing: -0.012em;
  color: var(--ink-on-light);
}

/* ---- Lista wpisów na blogu ---- */
.blogi { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1.25rem, 2vw, 2rem); margin-top: clamp(2.5rem, 5vw, 4rem); }
@media (max-width: 62rem) { .blogi { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 40rem) { .blogi { grid-template-columns: minmax(0, 1fr); } }
.blogi__poz {
  display: flex; flex-direction: column;
  padding: clamp(1.35rem, 2.2vw, 1.85rem);
  border: 1px solid var(--line-light);
  border-radius: 20px;
  background: var(--paper);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.blogi__poz:hover { border-color: rgba(23, 51, 255, .28); box-shadow: 0 14px 40px rgba(10, 13, 24, .08); }
.blogi__tyt {
  margin-top: .85rem;
  font-family: var(--font); font-weight: 500;
  font-size: clamp(1.08rem, 1.5vw, 1.25rem);
  line-height: 1.3; letter-spacing: -0.015em;
}
.blogi__lead { margin-top: .7rem; font-size: .92rem; color: var(--dim-on-light); }
.blogi__wiecej { margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line-light); font-size: .86rem; color: var(--blue); }

/* ---- Case study na stronie realizacji ---- */
.case {
  padding-top: clamp(2.5rem, 5vw, 4rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid var(--line-light);
}
.case__glowa { display: flex; align-items: flex-end; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
.case__nad { font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase; color: var(--dim-on-light); }
.case__nazwa { font-family: var(--font); font-weight: 400; font-size: clamp(1.6rem, 2.8vw, 2.3rem); letter-spacing: -0.025em; margin-top: .5rem; }
.case__klient { margin-top: .35rem; font-size: .92rem; color: var(--dim-on-light); }
.case__opis { margin-top: 1.5rem; max-width: 68ch; color: var(--dim-on-light); }
.case__siatka {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
}
@media (max-width: 60rem) { .case__siatka { grid-template-columns: minmax(0, 1fr); } }
.case__etykieta { font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase; color: var(--dim-on-light); margin-bottom: .9rem; }
.case__zakres { display: grid; gap: .55rem; font-size: .92rem; color: var(--dim-on-light); }
.case__zakres li { position: relative; padding-left: 1.4rem; }
.case__zakres li::before {
  content: ""; position: absolute; left: 0; top: .5em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--blue); opacity: .55;
}
.case__stack { display: flex; flex-wrap: wrap; gap: .4rem; }
.case__stack span {
  padding: .35rem .8rem; border-radius: 100px;
  border: 1px solid var(--line-light); background: var(--white);
  font-size: .8rem; color: var(--dim-on-light);
}
.case__wyniki { display: flex; flex-wrap: wrap; gap: clamp(1.25rem, 3vw, 2.5rem); margin-top: 1.75rem; }
.case__wynik { }
.case__wynik b { display: block; font-family: var(--font); font-weight: 300; font-size: 1.9rem; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.case__wynik span { font-size: .82rem; color: var(--dim-on-light); }

/* ---- Strona 404 ---- */
.e404 { display: grid; place-items: center; text-align: center; min-height: 72svh; }
.e404__kod {
  font-family: var(--font); font-weight: 300;
  font-size: clamp(4.5rem, 16vw, 11rem);
  line-height: .9; letter-spacing: -0.05em;
  color: rgba(255, 255, 255, .16);
}
.e404__linki { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin-top: 2.25rem; }

/* ---- Sześć kroków współpracy (jak-dzialamy.html) ----
   Bloki jeden pod drugim, numer w osobnej kolumnie. To jest ciąg
   chronologiczny — siatka kazałaby go skanować wzrokiem zamiast czytać. */
.kroki { display: grid; gap: 0; }
.krok {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  padding-block: clamp(1.75rem, 3.5vw, 2.75rem);
  border-top: 1px solid var(--line-light);
}
.krok:first-child { border-top: 0; padding-top: 0; }
@media (max-width: 40rem) { .krok { grid-template-columns: minmax(0, 1fr); gap: .75rem; } }
.krok__num {
  font-family: var(--font); font-weight: 300;
  font-size: clamp(1.9rem, 3.2vw, 2.6rem);
  line-height: 1; letter-spacing: -0.03em;
  color: var(--blue);
  font-variant-numeric: tabular-nums;
}
.krok__tytul { font-family: var(--font); font-weight: 500; font-size: var(--t-h3); letter-spacing: -0.015em; }
.krok__czas { margin-top: .3rem; font-size: var(--t-meta); letter-spacing: .08em; text-transform: uppercase; color: var(--dim-on-light); }
.krok__opis { margin-top: .9rem; max-width: 62ch; color: var(--dim-on-light); font-size: .96rem; }
.krok__zysk {
  margin-top: 1.1rem; padding-top: .9rem;
  border-top: 1px solid var(--line-light);
  max-width: 62ch;
  font-size: .89rem; color: var(--dim-on-light);
}
.krok__zysk span {
  display: block; margin-bottom: .3rem;
  font-size: var(--t-meta); letter-spacing: .1em; text-transform: uppercase;
  color: var(--blue);
}

/* ---- Odnośniki prawne w dolnym pasku stopki ---- */
.footer__prawne { display: flex; gap: 1.25rem; flex-wrap: wrap; }
.footer__prawne a:hover { color: var(--white); }

/* Tabela wewnątrz prozy trzyma typografię prozy, nie tabeli porównawczej */
.proza .tabela__ramka { margin-top: 1.4em; }
.proza .tabela { min-width: 34rem; font-size: .92rem; }
.proza .tabela th, .proza .tabela td { text-align: left; padding: .7rem .9rem; }
.proza .tabela thead th { position: static; }

/* ==================================================================
   PEŁNA WYSOKOŚĆ EKRANU DLA SEKCJI
   Sekcje bywały niższe niż ekran — najbardziej rzucało się to w oczy
   przy „O nas", które kończyło się w połowie kadru. Odtąd żaden blok
   w <main> nie jest niższy niż widoczna wysokość okna, a treść stoi
   w pionie na środku.

   `svh` zamiast `vh`, bo na telefonie `vh` liczy się do PEŁNEJ wysokości
   okna razem z chowanym paskiem adresu — sekcja wystawałaby poza ekran
   i pierwszy kadr zawsze byłby ucięty.
   ================================================================== */

#main > section:not(.closing),
/* Wyłączone jest TYLKO domknięcie z sercem (samo `.closing`). Płaski wariant
   na podstronach ma własne `min-height: 0`, więc bez tej linii zrobiłby się
   niską sekcją — wraca do reguły pełnej wysokości. */
#main > section.closing--plaskie,
#main > header,
/* `.slot` to kontener pozycjonujący (sticky przejście + sekcja pod nim),
   a nie pasek — sekcja „O nas" siedzi właśnie w nim i bez tej linii
   reguła by jej nie objęła. `.przej` zostaje wyłączone: to sticky panel
   przejścia, który sam pilnuje swojej wysokości. */
#main > .slot > section:not(.przej) {
  min-height: 100vh;
  min-height: 100svh;
}

/* Pasek przewijanych nazw realizacji ma zostać paskiem. */
#main > .ticker {
  min-height: 0;
}

/* Centrowanie w pionie tylko tam, gdzie sekcja ma zwykły przepływ.
   Hero i stos realizacji sterują układem same (sticky, canvas) — flex
   by im to rozbił. Treść wyższa niż ekran po prostu rozpycha sekcję,
   więc nic nie ucieka poza kadr. */
/* `.closing` WYŁĄCZONE świadomie. Ma własne `display:grid; align-content:start`,
   bo tekst musi stać wysoko, a chromowane serce ma stanąć POD nim. Wyśrodkowanie
   w pionie spychało tekst na górną połowę bryły i serce przestawało być pod
   tekstem. Sekcja i tak jest wyższa niż kadr z własnego `min-height`. */
#main > section:not(.hero):not(.pf):not(.ticker):not(.closing),
#main > header:not(.hero) {
  display: flex;
  flex-direction: column;
  justify-content: center;
}


/* ---- kafle „O nas" jako linki ----
   Wcześniej były martwymi kolumnami dekoracyjnymi: cztery podpisy i zdjęcie
   odsłaniane najechaniem, bez żadnego celu. Prowadzą teraz do treści, która
   już istnieje (rozdziały na /o-nas/, proces, realizacje) — zamiast mnożyć
   cienkie podstrony o tym samym.

   Zdjęcie odsłania się też przy fokusie z klawiatury: skoro kafel jest
   linkiem, osoba idąca tabem musi widzieć, gdzie stoi. */
a.onas__col {
  color: inherit;
  text-decoration: none;
}
a.onas__col:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: -4px;
}
a.onas__col:focus-visible .onas__img { opacity: 1; transform: none; }
a.onas__col:focus-visible::after { opacity: 1; }
a.onas__col:focus-visible .onas__label {
  color: var(--white);
  text-shadow: 0 2px 22px rgba(5, 6, 10, .55);
}

/* ==================================================================
   DODATKI MOTYWU — poza prototypem
   Prototyp był statyczną pokazówką i nie miał ani banera zgody, ani
   danych strukturalnych. Produkcja ma jedno i drugie, więc port bez
   nich byłby cofnięciem. Poniżej baner w języku wizualnym redesignu.
   ================================================================== */

.zgoda {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 200;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem 1.5rem;
  padding: 1rem 1.5rem;
  background: var(--ink-2);
  border-top: 1px solid var(--line-dark);
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: 0.85rem;
  line-height: 1.55;
  transform: translateY(101%);
  transition: transform 0.35s ease;
}
.zgoda.jest { transform: translateY(0); }
.zgoda p { margin: 0; max-width: 62ch; }
/* --blue-soft na tle banera daje 4,49:1 — o włos pod progiem AA, dokładnie
   ta pułapka, o której mówi CLAUDE.md. Jaśniejszy ton (6,19:1) plus
   podkreślenie, bo w banerze zgody to jedyny odnośnik i musi być widoczny
   również dla kogoś, kto nie rozróżnia niebieskiego od szarego. */
.zgoda a {
  color: #7089ff;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.zgoda__akcje { display: flex; gap: 0.5rem; flex-shrink: 0; }
.zgoda__btn {
  border: 0;
  border-radius: 100px;
  padding: 0.6rem 1.4rem;
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  background: var(--blue);
  color: var(--white);
  transition: background 0.2s ease;
}
.zgoda__btn:hover { background: var(--blue-deep); }
.zgoda__btn--cichy { background: transparent; color: var(--text-dim); border: 1px solid var(--line-dark); }
.zgoda__btn--cichy:hover { background: rgba(255, 255, 255, 0.06); }

@media (prefers-reduced-motion: reduce) {
  .zgoda { transition: none; }
}
