/* =========================================================================
   ORIENT — cała strona wewnątrz zegarka
   Qubatura, 07.08.2026

   Kanwa leży na stałe pod spodem. Sekcje przewijają się nad nią i mają
   po ekranie wysokości — to one dają kamerze drogę do przejechania.
   Żadnego backdrop-filter (dryfuje na iOS Safari).
   ========================================================================= */

:root {
  --perla: #f2f1ef;
  --atrament: #14141a;
  --przygas: #4a4a50;
  --stal: #8e8e93;
  --jasny: #efedea;
  /* Drugi poziom sekcji kontaktu: komórka i godziny. Jedna wartość,
     żeby te dwie rzeczy nie rozjechały się przy kolejnej poprawce —
     to właśnie z takiego rozjazdu wzięło się „brzydko wyszły”. */
  /* ============================ SKALA (Kuba, 25.08, pkt 1a) ============
     Powód zmiany nie był estetyczny, tylko arytmetyczny: nagłówki jechały
     z `vw`, a treść i etykiety miały sztywne piksele. Efekt:

        na 390 px   h1 35,2 → h2 28,5   czyli ×1,24  (miało być „trzy do jednego”)
                    wiodący 16,4 < lista 16,6        czyli krok W GÓRĘ
        na 1280 px  h1 73,6 → wiodący 19,2  ×3,83    urwisko, potem drobny druk

     Kuba: „napisy są takiego pomiędzy, a nie wiem pomiędzy czym”. Właśnie
     tak wygląda skala, która na jednym końcu się skleja, a na drugim pęka.

     Teraz KAŻDY stopień jedzie z szerokością ekranu, więc stosunki są
     mniej więcej stałe: h1→h2 zawsze ×1,47, treść→etykieta zawsze ×1,45.
     I trzy stopnie treści (wiodący, lista, akapit) zeszły do JEDNEGO —
     półtora piksela różnicy nie jest hierarchią, tylko szumem. */
  --stopien-tresc:    clamp(1rem, 0.96rem + 0.18vw, 1.125rem);
  /* Kreska listy i odstęp po niej. Jedna para liczb dla samej kreski
     i dla wcięcia zapowiedzi — inaczej te dwie rzeczy rozjeżdżają się
     przy pierwszej lepszej poprawce i wraca „coś nie pasuje". */
  --kreska-lista: 0.8rem;
  --kreska-odstep: 0.85rem;
  --stopien-etykieta: clamp(0.69rem, 0.66rem + 0.13vw, 0.78rem);
  /* Kontakt korzysta z tego samego stopnia co treść — jedna liczba
     zamiast dwóch, które kiedyś na pewno by się rozjechały. */
  --stopien-dane: var(--stopien-tresc);
  /* DWA KROJE, ALE DRUGI MA ZADANIE — NIE NASTRÓJ.

     Poprzednia próba (szeryf z epoki nad bezszeryfową treścią) padła
     i słusznie: to nie była para, tylko dwa niespokrewnione kroje
     postawione obok siebie. Retro walczyło też ze sceną, która jest
     czysta i precyzyjna, a nie stara. To zastrzeżenie DALEJ obowiązuje.

     Dlatego podział nie idzie po nastroju, tylko po rodzaju informacji:

       GROTESK  — wszystko, co się CZYTA: nagłówki, zdania, guziki.
       MONO     — wszystko, co się ODCZYTUJE: godziny, adres, telefon,
                  rok założenia, etykiety, numery sekcji.

     Mono nie jest tu ozdobą „technologiczną". Jest znakiem, że patrzysz
     na daną, a nie na zdanie — a cała robota zegarmistrza to liczby
     i tolerancje. Przy okazji cyfry stoją w kolumnie same z siebie.

     DLACZEGO GEIST I GEIST MONO:

     - to JEDNA rodzina rysowana razem, więc para jest spójna
       z konstrukcji, a nie z dobrego trafu — dokładnie ta pułapka,
       na której poległ szeryf;
     - oś grubości 100–900. To nie jest zachcianka: jasny tekst na
       ciemnym tle zawsze wygląda grubiej niż ciemny na jasnym, więc
       przy palecie grafitowej trzeba zejść cieniej. Przy kroju zmiennym
       to jedna liczba, a KRÓJ ZOSTAJE TEN SAM (Kuba: „żeby nie zmieniać
       ich w trakcie");
     - polskie znaki sprawdzone twardo, nie na oko: wszystkie 18 liter
       plus cudzysłowy „ ” są w pliku i mają własny rysunek. Połowa
       modnych krojów ma „ł" i „ń" dorabiane automatem i widać to
       dopiero w nagłówku;
     - licencja OFL, self-host, zero zapytań do cudzego serwera.

     WAGA: 25,5 kB + 19,9 kB po podzbiorze do łaciny z polskimi znakami
     (z 169 i 172 kB pełnych plików). Cały zakres grubości w tej cenie. */
  /* ⚠️ ZMIANA 13.08 WIECZOREM — Kuba: „koniecznie zaproponuj czcionki,
     bo teraz to psuje; na początek użyj coś odważnego".

     Geist był poprawny i przez to nijaki: w dużym stopniu czytał się
     jak krój interfejsu, a nie jak głos marki. Miał rację.

     Wchodzi FRAUNCES — i tak, to jest szeryf, mimo że szeryf raz już
     tu poległ. Ale tamten był szeryfem Z EPOKI, retro, i walczył ze
     sceną, która jest czysta i precyzyjna. Fraunces jest czymś innym:
     to współczesny szeryf gazetowy o silnym kontraście grubej i cienkiej
     kreski. Trzy powody, dla których siada akurat tutaj:

     - TARCZA JUŻ MÓWI SZERYFEM. Cyfry rzymskie, IIII zamiast IV —
       nagłówek szeryfowy nie kłóci się ze sceną, tylko ją powtarza;
     - kontrast grubej i cienkiej kreski to typograficzny odpowiednik
       polerowanego i szczotkowanego metalu. Ten sam świat;
     - oś stopnia optycznego (opsz 9–144): w nagłówku krój sam robi się
       ostry i wysmukły, w treści sam grubieje i otwiera się, żeby dało
       się go czytać. Jeden plik, dwa różne zachowania.

     Zostają DWA kroje, tak jak chciałeś. Geist Mono trzyma dane
     (telefon, adres, etykiety), Fraunces całą resztę. Plik Geista
     zostaje w repo, bo powrót do bezszeryfowej treści to jedna linijka
     — ale nie jest nigdzie wołany, więc nikt go nie pobiera. */
  /* ⚠️ TRZY KROJE, NIE DWA — I POWIEM WPROST, DLACZEGO ŁAMIĘ WŁASNĄ
     ZASADĘ (Kuba chciał dwóch).

     Bodoni to didone: kontrast grubej i cienkiej kreski jest ekstremalny,
     bo na tym polega jego uroda. W nagłówku, przy dużym stopniu, to jest
     dokładnie to „odważniej i nowocześniej", o które chodziło. W treści,
     przy 16 pikselach na ciemnym tle, te same włoskowate kreski robią
     się niedoczytelne — i to nie jest kwestia gustu, tylko tego, jak oko
     radzi sobie z cienką jasną linią na ciemnym.

     Dlatego trzeci krój nie jest ozdobą, tylko funkcją:
       BODONI MODA — nagłówki. Głos.
       GEIST       — treść. Czytelność.
       GEIST MONO  — dane. Telefon, adres, etykiety.

     Razem 90 kB po podzbiorze. Fraunces wyleciał z repo, żeby nie leżał
     martwy — wraca jednym poleceniem, jeśli Bodoni okaże się za ostry. */
  --font-naglowek: "Bricolage Grotesque", "Segoe UI", -apple-system, sans-serif;
  --font: "Geist", "Segoe UI", -apple-system, BlinkMacSystemFont,
          "Helvetica Neue", Arial, sans-serif;
  --font-dane: "Geist Mono", ui-monospace, "SF Mono", "Cascadia Mono",
               Consolas, monospace;
}

/* `swap` z rozmysłem: strona ma być czytelna od pierwszej klatki,
   nawet zanim krój dojedzie. Przy scenie 3D, która i tak chwilę się
   składa, niewidoczny tekst byłby drugą rzeczą, na którą się czeka. */
/* Bricolage zamiast Outfita (Kuba, 25.08). Podzbiór łacinka + polskie
   znaki, 44 kB. Oś stopnia optycznego 12–96 ZOSTAJE i nie jest ozdobą:
   przy nagłówku 80 px krój sam się wysmukla, przy 30 px sam otwiera. */
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("../kroje/bricolage.woff2") format("woff2-variations");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist";
  src: url("../kroje/Geist.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("../kroje/GeistMono.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }

/* ⚠️ NA `html` NIE WOLNO USTAWIAĆ `overflow-x: hidden`.
   Zamienia to element w osobny kontener przewijania: strona przewija
   się „w środku", a kamera, która czyta pozycję okna, zostaje w miejscu.
   Efekt: jedzie tekst, scena stoi. Kosztowało to jedno wdrożenie.

   Blokujemy poziom `clip`-em (nie tworzy kontenera) i `touch-action`,
   które mówi przeglądarce wprost: tu przewija się tylko w pionie. */
html {
  -webkit-text-size-adjust: 100%;
  /* Blokada jazdy w bok — na `html`, nie tylko na `body`.
     `clip` NIE jest tym samym co `hidden`: nie tworzy kontenera
     przewijania, więc kamera dalej czyta pozycję okna i scena jedzie
     ze scrollem. `hidden` w tym samym miejscu rozwaliłby całą stronę
     (opis wyżej) — dlatego wolno tu wpisać wyłącznie `clip`. */
  overflow-x: clip;
}

body {
  margin: 0;
  font-family: var(--font);
  /* Stopień optyczny sam dobiera rysunek do wielkości: nagłówek dostaje
     ostry i wysmukły, treść grubszy i otwarty. Bez tego jeden plik
     wyglądałby albo na zbyt cienki w treści, albo na zbyt tępy
     w nagłówku. */
  font-optical-sizing: auto;
  color: var(--atrament);
  background: var(--perla);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
  touch-action: pan-y;
  overscroll-behavior-x: none;
}

/* bez WebGL zostaje spokojne perłowe tło i zwykły dokument */
body.bez3d {
  background:
    radial-gradient(60vmax 40vmax at 40% 20%, #fff, rgba(255,255,255,0) 60%),
    linear-gradient(168deg, #f7f6f4, #e3e1de);
  background-attachment: fixed;
}

#scena {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  /* ⚠️ `inset: 0` TO BYŁ BŁĄD I KOSZTOWAŁ CAŁY WIECZÓR.

     Na telefonie pasek adresu i dolna belka przeglądarki zabierają
     wysokość, a element ustawiony na stałe obejmuje CAŁE okno — łącznie
     z tym, co siedzi pod paskami. Kanwa była więc wyższa niż to, co
     widać, a zegarek stał na środku KANWY, czyli poniżej środka tego,
     co widać. Na moim symulowanym ekranie wychodził na środku, na
     telefonie Kuby w prawym dolnym rogu — i przez godzinę szukaliśmy
     tego w odległości kamery.

     `dvh` to jednostka zrobiona dokładnie na to: wysokość widocznego
     okna, zmieniająca się razem z chowaniem pasków. */
  height: 100dvh;
  /* ⚠️ ŻADNEGO `width: 100vw`.
     Jednostka `vw` NIE odejmuje paska przewijania, więc kanwa wychodziła
     szersza niż okno — zmierzone: 374 px przy oknie 357. Te siedem
     pikseli to był cały luz, na którym dało się przeciągnąć stronę
     w bok i rozjechać tekst.

     `overflow-x: clip` na `body` tego NIE łapie, bo element ustawiony
     na stałe (`fixed`) wychodzi spod przycinania przodka — trzeba było
     naprawić szerokość, a nie zasłaniać skutek.

     `inset: 0` daje dokładnie prostokąt okna, razem z paskiem. */
  display: block;
  z-index: 0;
  opacity: 0;
  transition: opacity 1100ms ease;
}

body.gotowe #scena { opacity: 1; }
body.bez3d #scena { display: none; }

/* Winieta — kadr ma się otwierać, nie wyglądać na wypełniony. */
.winieta {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 95% at 50% 42%,
      rgba(20, 20, 26, 0) 42%,
      rgba(20, 20, 26, 0.055) 74%,
      rgba(20, 20, 26, 0.13) 100%);
  opacity: 0;
  transition: opacity 1100ms ease;
}

body.gotowe .winieta { opacity: 1; }

/* pod tarczą świat jest ciemny — winieta musi się wtedy odwrócić,
   bo ciemna ramka na ciemnym tle tylko zamula obraz */
body.w-mechanizmie .winieta,
body.ciemno .winieta {
  background:
    radial-gradient(120% 95% at 50% 45%,
      rgba(255, 255, 255, 0) 40%,
      rgba(255, 255, 255, 0.03) 76%,
      rgba(255, 255, 255, 0.07) 100%);
}

/* Ładowanie bez słów: wskazówka obiegająca okrąg. */
.ladowanie {
  position: fixed;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  pointer-events: none;
  transition: opacity 600ms ease;
}

.ladowanie span {
  position: relative;
  width: 40px;
  height: 40px;
  border: 1px solid rgba(20, 20, 26, 0.14);
  border-radius: 50%;
}

.ladowanie span::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 50%;
  width: 1px;
  height: 15px;
  background: rgba(20, 20, 26, 0.55);
  transform-origin: 50% 100%;
  animation: obieg 1.4s linear infinite;
}

@keyframes obieg {
  from { transform: translateX(-50%) rotate(0deg); }
  to   { transform: translateX(-50%) rotate(360deg); }
}

body.gotowe .ladowanie { opacity: 0; }

main { position: relative; z-index: 1; }

/* Zdjęcie tekstów NIE może skrócić strony — wysokość sekcji jest drogą,
   po której jedzie kamera. Chowamy więc zawartość, nie ekrany. */
body.bez-tekstu .ramka > *,
body.bez-tekstu .ramka::before {
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms ease;
}

/* TELEFON NA STAŁE.
   Sama ikona, żeby nie zabierała przestrzeni. Numer wysuwa się spod
   niej dopiero pod kursorem; na telefonie i tak liczy się to, że jest
   zawsze pod kciukiem, a nie że widać cyfry. */
.dzwon {
  position: fixed;
  left: 1rem;
  bottom: calc(1rem + env(safe-area-inset-bottom));
  z-index: 7;
  display: flex;
  align-items: center;
  gap: 0;
  height: 44px;
  padding: 0 0 0 12px;
  border-radius: 22px;
  background: #14141a;
  color: #f2f1ef;
  text-decoration: none;
  box-shadow: 0 6px 20px rgba(20, 20, 26, 0.28);
  overflow: hidden;
  transition: gap 260ms ease, padding 260ms ease, transform 200ms ease;
}

.dzwon svg {
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  fill: currentColor;
  margin-right: 12px;
}

.dzwon__numer {
  max-width: 0;
  opacity: 0;
  white-space: nowrap;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  transition: max-width 280ms ease, opacity 200ms ease,
              margin 280ms ease;
}

.dzwon:hover { transform: translateY(-2px); }

.dzwon:hover .dzwon__numer,
.dzwon:focus-visible .dzwon__numer {
  max-width: 160px;
  opacity: 1;
  margin-right: 16px;
}

/* na dotyku nie ma najechania — pokazujemy numer od razu, ale
   dopiero od szerokości, na której jest na to miejsce */
@media (hover: none) and (min-width: 420px) {
  .dzwon__numer { max-width: 160px; opacity: 1; margin-right: 16px; }
}

/* ------------------------------------------------------------ NAPISZ

   Bliźniak słuchawki, po drugiej stronie kadru. Świadomie SŁABSZY:
   obrys zamiast wypełnienia. Telefon jest u Piotra drogą pierwszą —
   klientela starsza dzwoni — a ten guzik ma być wyjściem dla tych,
   którzy nie zadzwonią, nie konkurencją dla numeru.

   Słowo stoi PRZED ikoną, bo guzik rośnie w lewo: przy krawędzi
   po prawej etykieta rozwijana w drugą stronę wyjeżdżałaby za ekran. */
.pisz {
  position: fixed;
  right: 1rem;
  bottom: calc(1rem + env(safe-area-inset-bottom));
  z-index: 7;
  display: flex;
  align-items: center;
  height: 44px;
  padding: 0 12px;
  border-radius: 22px;
  border: 1px solid rgba(20, 20, 26, 0.22);
  background: rgba(242, 241, 239, 0.86);
  backdrop-filter: blur(8px);
  color: #14141a;
  text-decoration: none;
  box-shadow: 0 6px 20px rgba(20, 20, 26, 0.16);
  transition: transform 200ms ease, opacity 260ms ease, visibility 260ms;
}
.pisz svg { width: 19px; height: 19px; flex: 0 0 19px; fill: currentColor; }
.pisz__slowo {
  font-family: var(--font-dane);
  font-weight: 450;
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin-right: 10px;
}
.pisz:hover { transform: translateY(-2px); }
.pisz:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }

body.w-mechanizmie .pisz,
body.ciemno .pisz {
  border-color: rgba(240, 238, 234, 0.3);
  background: rgba(20, 20, 26, 0.62);
  color: #efedea;
}

/* Formularz w kadrze — guzik schodzi. Zostaje w drzewie (visibility),
   więc nie przeskakuje układ i nie znika z nawigacji klawiaturą skokowo. */
body.formularz-widac .pisz {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Guzik i podpowiedź „przewiń” siedzą przy tej samej krawędzi.
   Na wąskim ekranie podpowiedź ustępuje — jest tymczasowa, guzik nie. */
@media (max-width: 380px) {
  .pisz__slowo { display: none; }
  .pisz { padding: 0 13px; }
}

/* ------------------------------------------------------- formularz */

.pytanie {
  width: 100%;
  max-width: 46ch;
  margin-top: 2.4rem;
  text-align: left;
}

.pytanie__tytul {
  font-size: 0.66rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: #6a6a70;
  margin-bottom: 0.6rem;
}

.pytanie__wstep {
  font-size: 0.9rem;
  color: #4a4a50;
}

.pole { display: block; margin-bottom: 0.9rem; }

.pole > span {
  display: block;
  font-size: 0.62rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #6a6a70;
  margin-bottom: 0.35rem;
}

.pole input,
.pole textarea {
  width: 100%;
  font: inherit;
  font-size: 0.95rem;
  padding: 0.7rem 0.85rem;
  color: #14141a;
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid rgba(20, 20, 26, 0.18);
  border-radius: 2px;
  resize: vertical;
}

.pole input:focus,
.pole textarea:focus {
  outline: none;
  border-color: #14141a;
}

.pytanie__stan {
  font-size: 0.82rem;
  color: #4a4a50;
  min-height: 1.2em;
  margin-top: 0.8rem;
}
body.w-mechanizmie .pytanie__wstep,
body.ciemno .pytanie__wstep,
body.w-mechanizmie .pytanie__stan,
body.ciemno .pytanie__stan { color: #a9a7a2; }
body.w-mechanizmie .pole input,
body.ciemno .pole input,
body.w-mechanizmie .pole textarea,
body.ciemno .pole textarea {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(240, 238, 234, 0.28);
  color: #efedea;
}

/* Podpowiedź do przewijania — kreska sunąca w dół po torze.
   Nie „strzałka w kółku", bo to wygląda jak przycisk i ludzie w to
   klikają zamiast przewijać. Znika po pierwszym ruchu. */
.podpowiedz {
  position: fixed;
  left: 50%;
  bottom: calc(1.6rem + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  pointer-events: none;
  transition: opacity 700ms ease;
}

.podpowiedz--znika { opacity: 0; }

.podpowiedz__slowo {
  font-size: 0.6rem;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: #6a6a70;
  text-indent: 0.36em;
}

.podpowiedz__tor {
  position: relative;
  width: 1px;
  height: 46px;
  background: rgba(20, 20, 26, 0.16);
  overflow: hidden;
}

.podpowiedz__tor i {
  position: absolute;
  left: 0;
  width: 1px;
  height: 16px;
  background: #14141a;
  animation: sunie 2.1s cubic-bezier(0.6, 0, 0.4, 1) infinite;
}

@keyframes sunie {
  0%   { top: -18px; opacity: 0; }
  22%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { top: 48px; opacity: 0; }
}
body.w-mechanizmie .podpowiedz__slowo,
body.ciemno .podpowiedz__slowo { color: #9a9894; }
body.w-mechanizmie .podpowiedz__tor,
body.ciemno .podpowiedz__tor { background: rgba(240, 238, 234, 0.2); }
body.w-mechanizmie .podpowiedz__tor i,
body.ciemno .podpowiedz__tor i { background: #efedea; }

/* ------------------------------------------------------------- ekrany */

.ekran {
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: 12vh 1.5rem;
}

/* Treść hero siedzi u GÓRY, nie na środku. Zegarek ustawia się wtedy
   pod nią, lekko poniżej środka kadru — i to jest lepszy układ niż
   wyśrodkowanie obu rzeczy na sobie. */
.ekran--hero {
  min-height: 100svh;
  align-items: flex-start;
  /* Kuba, 13.08 w nocy: „hero powinno zaczynać się wcześniej, żeby tekst
     nie zabierał aż tyle". 9vh na telefonie to prawie sto pikseli pustki
     nad pierwszą linijką — a tę pustkę i tak mamy nad głową w postaci
     paska przeglądarki. Zejście na 4vh oddaje tarczy jedną trzecią
     tego, co tekst zajmował. */
  padding-top: 4vh;
}

/* dodatkowa droga dla kamery na zejście pod tarczę */
.ekran--ciemny { min-height: 130svh; }
.ekran--koniec { min-height: 110svh; }

.ramka {
  position: relative;
  width: min(100%, 1180px);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  /* Stopień treści dla całej ramki. Dziedziczeniem, nie selektorem —
     patrz komentarz przy `.ramka > p`. */
  font-size: var(--stopien-tresc);
}

/* Poduszka pod tekstem. Treść leży na ruchomej scenie, więc czytelność
   nie może zależeć od tego, co akurat przejeżdża pod spodem. Zwykły
   gradient, nie backdrop-filter — ten dryfuje na iOS Safari. */
.ramka::before {
  content: "";
  position: absolute;
  inset: -14% -10%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(62% 58% at 50% 50%,
    rgba(242, 241, 239, 0.94) 0%,
    rgba(242, 241, 239, 0.72) 45%,
    rgba(242, 241, 239, 0) 78%);
  transition: background 500ms ease;
}
/* ⚠️ TU BYŁA TA CZARNA ELIPSA (Kuba, 13.08).

   Poduszka była owalem wyśrodkowanym na tekście. Na jasnym tle uchodziła,
   bo perła na perle jest niewidoczna — ale na graficie ciemny owal na
   jaśniejszej scenie ma WYRAŹNY KSZTAŁT i czyta się jak plama położona
   na obrazku. Do tego ta reguła miała wyższą wagę niż późniejsza,
   przesuwająca poduszkę w lewo, więc wersja ciemna nadal siedziała
   na środku, mimo że tekst dawno stąd wyszedł.

   Zamiast owalu — ZASŁONA BEZ KSZTAŁTU. Gradient biegnie poziomo od
   lewej krawędzi i gaśnie do zera, a maska wygasza go też u góry i u
   dołu, więc nigdzie nie ma widocznego brzegu. Czyta się jak naturalne
   opadanie światła po stronie przeciwnej do lampy, a nie jak podkładka.
   To zresztą prawda o tej scenie: światło pada z jednej strony. */
body.w-mechanizmie .ramka::before,
/* ⚠️ PODUSZKA MUSI SIĘGAĆ DALEJ W PRAWO NIŻ SIĘGA TEKST — Kuba, 14.08:
   „zwróć uwagę, czy ten szary H2 nie gubi się na tarczy".

   Gubił się i da się to pokazać liczbą. Kontrast tekstu do tła wzdłuż
   wiersza: 12,2:1 przy lewej krawędzi, 4,4:1 tam, gdzie zaczyna się
   tarcza, i 3,6:1 na końcu linii — czyli ostatnie słowa schodziły
   poniżej progu 4,5:1, od którego zaczyna się czytelny tekst.

   Przyczyna nie jest w kolorze tekstu. Poduszka gasła do zera zbyt
   szybko, a tarcza zrobiła się dziś jaśniejsza (ujednolicony rant
   podniósł jej jasność o 24 poziomy), więc miejsce, w którym gradient
   już nie pracuje, wypadło dokładnie pod końcówkami wierszy. */
body.ciemno .ramka::before {
  inset: -34% 26% -34% -60%;
  background: linear-gradient(90deg,
    rgba(9, 9, 11, 0.90) 0%,
    rgba(9, 9, 11, 0.88) 42%,
    rgba(9, 9, 11, 0.70) 72%,
    rgba(9, 9, 11, 0.34) 88%,
    rgba(9, 9, 11, 0) 100%);
  -webkit-mask-image: linear-gradient(180deg,
    transparent 0%, #000 16%, #000 84%, transparent 100%);
  mask-image: linear-gradient(180deg,
    transparent 0%, #000 16%, #000 84%, transparent 100%);
}

/* Wszystko na środku, także na dużym ekranie. Układy do lewej i do
   prawej rozbijały rytm: oko skakało na boki, a scena pod spodem
   i tak jest wyśrodkowana. Wersja z telefonu okazała się lepsza. */
/* ================================================== TEKST PRZY KRAWĘDZI

   Trzy warianty wyśrodkowania sprowadzały się i tak do jednego: wszystko
   na środku. To jest układ, który sam z siebie wygląda na złożony
   domyślnie — nie dlatego, że jest brzydki, tylko dlatego, że nie
   podejmuje żadnej decyzji.

   Teraz treść stoi przy LEWEJ krawędzi, a prawa strona zostaje pusta.
   To nie jest gust: zegarek siedzi w kadrze bliżej prawej, więc tekst
   po lewej go nie zasłania, a pustka po prawej przestaje być brakiem
   i staje się miejscem, w którym coś widać. Tak układa się okładki
   i strony tytułowe — jedna mocna krawędź, reszta oddycha. */
.ramka--srodek,
.ramka--lewo,
.ramka--prawo {
  align-items: flex-start;
  text-align: left;
}

/* Treść dostaje delikatną poduszkę, żeby czytała się nad sceną.
   Nie backdrop-filter — zwykły gradient, który nie dryfuje na iOS. */
/* Kolumna węższa niż była (46 → 40 znaków). Wiersz dłuższy niż mniej
   więcej czterdzieści znaków oko gubi przy powrocie do początku
   następnej linii — a tu czyta się nad ruchomym tłem, więc margines
   błędu jest mniejszy niż na zwykłej stronie. */
.ramka--lewo > *,
.ramka--prawo > *,
.ramka--srodek > * {
  max-width: 40ch;
}

/* Poduszka pod tekstem idzie za nim do lewej. Gdy była wyśrodkowana,
   przy tekście przy krawędzi rozjaśniała pół kadru po prawej — czyli
   dokładnie to miejsce, które ma zostać puste. */
.ramka::before {
  inset: -34% 26% -34% -60%;
  background: linear-gradient(90deg,
    rgba(242, 241, 239, 0.94) 0%,
    rgba(242, 241, 239, 0.90) 42%,
    rgba(242, 241, 239, 0.55) 72%,
    rgba(242, 241, 239, 0) 100%);
  -webkit-mask-image: linear-gradient(180deg,
    transparent 0%, #000 16%, #000 84%, transparent 100%);
  mask-image: linear-gradient(180deg,
    transparent 0%, #000 16%, #000 84%, transparent 100%);
}

/* Na wąskim ekranie nie ma pustej prawej strony do oddania — tekst
   zajmuje całą szerokość, ale nadal stoi przy lewej. */
@media (max-width: 720px) {
  .ramka--lewo > *,
  .ramka--prawo > *,
  .ramka--srodek > * { max-width: 100%; }
  /* Na telefonie tekst zajmuje całą szerokość, więc zasłona z boku
     gasłaby dokładnie tam, gdzie tekst jeszcze stoi. Idzie więc z góry
     w dół — i przy okazji oddaje dolną część kadru scenie. */
  .ramka::before {
    inset: -30% -12% -18% -12%;
    background: linear-gradient(180deg,
      rgba(9, 9, 11, 0.92) 0%,
      rgba(9, 9, 11, 0.86) 52%,
      rgba(9, 9, 11, 0.40) 82%,
      rgba(9, 9, 11, 0) 100%);
    -webkit-mask-image: none;
    mask-image: none;
  }
}

.ramka--lewo,
.ramka--prawo {
  max-width: 1180px;
}

/* ------------------------------------------------------------ typografia */

h1, h2 {
  font-family: var(--font-naglowek);
  /* 280, nie 300: krój zmienny daje dowolną wartość, a nie tylko te
     z listy. Przy tej skali litery mają być cienką kreską — grubość
     dokłada masę, a masy dokłada już scena pod spodem. */
  /* Szeryf o silnym kontraście potrzebuje WIĘCEJ grubości niż grotesk,
     nie mniej: przy 280 cienkie kreski znikały na ciemnym tle i napis
     robił się dziurawy. 330 trzyma je przy życiu, a gruba kreska nadal
     jest smukła. */
  /* ODWAŻNIEJ (Kuba, 13.08): 330 było „ładne", a nie mocne. Fraunces
     przy 520 pokazuje pełny kontrast grubej i cienkiej kreski — dopiero
     wtedy nagłówek ma ciężar i staje się głosem, a nie podpisem. */
  /* Didone znosi duzo wiecej grubosci niz zwykly szeryf — dopiero
     przy 600 kreska gruba robi sie naprawde gruba, a cienka zostaje
     wloskowata. To ten kontrast jest tu trescią. */
  font-weight: 600;
  /* Ściągnięte światło między literami i ciasna interlinia — tak
     składa się nazwy na przyrządach pomiarowych. To stąd bierze się
     wrażenie precyzji, a nie z ozdobnego kroju. */
  /* Szeryfowi nie ściągamy światła tak mocno jak groteskowi — szeryfy
     same w sobie łączą litery w linię i przy ciasnym składzie zaczynają
     się zlepiać. */
  /* Przy większej grubości szeryf potrzebuje ciaśniejszego składu,
     inaczej rozłazi się na szerokość. */
  /* Geometryczny grotesk lubi ciasny skład — okrągłe litery same
     w sobie zostawiają dużo światła, więc trzeba je przybliżyć,
     inaczej nagłówek rozłazi się na szerokość. */
  letter-spacing: -0.02em;
  line-height: 1.06;
  margin: 0 0 1.1rem;
  /* ⚠️ ŻADNEGO `font-feature-settings: "case"` NA NAGŁÓWKU.
     Włączyłem to pod grotesk, gdzie `case` tylko podnosi nawiasy
     i myślniki na wysokość wersalików. We Fraunces ta sama funkcja
     podmienia rysunek liter i cały nagłówek wychodził kapitalikami —
     „STARE ZEGARY I ZEGARKI" zamiast „Stare zegary i zegarki". Widać
     to było od razu po jednym szczególe: polskie „ę" zostawało małe,
     bo nie ma dla niego kapitalika. Funkcje OpenType są nazwane tak
     samo, ale robią to, co postanowił rysownik kroju. */
}

/* SKALA. Skok między stopniami ma być SŁYSZALNY — nagłówek do treści
   jak trzy do jednego, nie jak półtora. Przy zbyt gładkiej skali strona
   wygląda na złożoną domyślnie, choćby krój był najlepszy. */
/* Większy stopień razem z grubością — sama grubość bez skali robi
   z nagłówka klocek, a nie oświadczenie. */
/* ⚠️ DOLNA WARTOŚĆ NIE JEST GUSTEM, TYLKO GRANICĄ. „Zegarmistrzowski”
   ma w Bricolage’u 7,67 em, a `.ekran` zabiera 1,5rem z każdej strony,
   czyli 48 px. Na ekranie 375 px (iPhone SE, mini) zostaje 327 px —
   więcej niż 42,6 px stopnia i słowo pęka na dwie linie albo, przy
   `overflow-x: clip`, zostaje UCIĘTE bez śladu. Stąd 2,5rem. */
h1 { font-size: clamp(2.5rem, 1.2rem + 5.6vw, 5rem); }
h2 { font-size: clamp(1.7rem, 0.82rem + 3.8vw, 3.4rem); }

p { margin: 0 0 1rem; }

/* ⚠️ TU STAŁO `.ramka > p { font-size: … }` I BYŁ TO BŁĄD.
   Selektor klasa+element ma specyficzność (0,0,1,1) i bił `.metryka`
   oraz `.nad` (0,0,1,0) — również te ustawione w `@media`, bo zapytanie
   medialne NIE podnosi specyficzności. Adres na telefonie skakał z 9,7
   na 16,1 px, przestawał mieścić się w jednej linii, a że ma
   `flex-wrap: nowrap`, po prostu wyjeżdżał poza kadr i był ucinany.
   Stopień treści idzie więc DZIEDZICZENIEM z `.ramka`, a elementy
   z własnym rozmiarem nie muszą się z niczym bić. */
.ramka > p { line-height: 1.62; }

/* NUMER SEKCJI I ETYKIETA — to jest dana, więc idzie w mono.
   Rozstrzelony wersalik: drobny druk z tarczy zegarka. */
.nad {
  font-family: var(--font-dane);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.26em;
  font-size: var(--stopien-etykieta);
  color: var(--stal);
  margin-bottom: 1.3rem;
  font-variant-numeric: tabular-nums;
}

.znak {
  width: clamp(185px, 32vw, 280px);
  height: auto;
  opacity: 0.92;
}

/* ⚠️ ADRES BYŁ WYŚRODKOWANY W LEWEJ KOLUMNIE — i to, a nie sam adres,
   wyglądało źle (Kuba, 14.08: „lokalizacja jest ok i jedyna słuszna,
   ale to podkreślenie i brak jakiegoś akcentu wygląda źle").

   Cała treść hero stoi przy lewej krawędzi, a `justify-content: center`
   ustawiał adres na środku czterdziestoznakowej kolumny. Przy dwóch
   liniach dawało to dwa różne wcięcia, żadne niezwiązane z nagłówkiem —
   stąd wrażenie, że coś jest podkreślone albo przesunięte przez pomyłkę.

   Do tego akcent, którego naprawdę nie było: krótka kreska przed
   miastem. Ta sama, którą sekcje mają przy numerach — więc adres
   przestaje być samotnym napisem i staje się pierwszą pozycją tej
   samej listy. */
.metryka {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: center;
  gap: 0.7rem;
  margin: 1rem 0 1.6rem;
  /* Adres, miasto i rok to DANE — mono, nie grotesk. */
  font-family: var(--font-dane);
  font-weight: 400;
  font-size: var(--stopien-etykieta);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--przygas);
}

/* Druga linia obrony przed wykrywaczem adresów na iOS. Sam `meta
   format-detection` wystarcza w Safari, ale ta sama funkcja siedzi też
   w widoku stron wewnątrz innych aplikacji (Messenger, Instagram, Mail),
   a tam meta bywa ignorowana. Wtedy system opakowuje tekst we własny
   element z atrybutem `x-apple-data-detectors` — i tylko po nim da się
   go rozpoznać. Kolor i podkreślenie zdejmujemy, klikalność zostaje. */
a[x-apple-data-detectors],
.metryka a {
  color: inherit !important;
  text-decoration: none !important;
  -webkit-text-decoration: none !important;
  font: inherit !important;
  pointer-events: none;
}

.metryka i {
  width: 3px; height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.5;
}

/* ⚠️ ADRES ŁAMAŁ SIĘ NA DWIE LINIE NIE DLATEGO, ŻE JEST DŁUGI.
   Wszystkie dzieci ramki mają `max-width: 40ch`, a 40ch liczy się
   w KROJU TEGO ELEMENTU — czyli w mono z tracklingiem 0,2em, gdzie
   jeden znak zajmuje jakieś 1,2 szerokości znaku. Trzydzieści jeden
   liter wychodziło więc poza limit i miasto zostawało samo w wierszu
   z wiszącą kropką na końcu. Adres to jedna informacja i ma stać
   w jednej linii — na telefonie łamanie wraca, bo tam nie ma wyboru. */
/* ⚠️ ADRES NA TELEFONIE WYGLĄDAŁ INACZEJ NIŻ NA MONITORZE (Kuba, 15.08:
   „różni się adres w desktop od adresu w mobile, pomysł który zrobiłeś
   w desktop jest chyba ok").

   Różnica nie była decyzją, tylko resztką po dwóch osobnych poprawkach:
   listwa (druga kreska do krawędzi okna) powstała wieczorem 14.08 i od
   razu wjechała w `min-width: 721px`, bo mierzyłem ją na monitorze.
   Na telefonie zostawał więc adres z kreską TYLKO z lewej — czyli
   dokładnie ta wersja, którą Kuba wcześniej nazwał „dopisaną na kolanie".

   Listwa idzie teraz na obie szerokości. Warunek jest jeden: adres musi
   zmieścić się w JEDNEJ linii, bo złamany na dwa wiersze przestaje być
   listwą. Stąd stopień liczony ze szerokości okna zamiast stałego —
   na 390 px schodzi z 0,66 na ~0,63 rem, na 320 px do podłogi 0,58,
   i przy każdej z tych szerokości wiersz ma jeszcze zapas na kreskę. */
.metryka { max-width: 100%; flex-wrap: nowrap; }
/* ⚠️ NA TELEFONIE ADRES IDZIE NA ŚRODEK (Kuba, 15.08: „zostało to
   podkreślenie, nie wydaje mi się, żeby to było ok… rozłóż na mobile
   na środek cały ten adres, bo teraz trochę krzywo — na pewno musimy
   przybić adres na środek, bo zależy od telefonu. Kreski lewa prawa
   są super, robi to robotę").

   Objaw nazwany dokładnie: „zależy od telefonu". Przy adresie dosuniętym
   do lewej kreska z lewej ma stałą długość, a kreska z prawej dobiera
   resztę (`flex: 1`) — więc na każdej szerokości ekranu wychodzi INNA
   proporcja obu i za każdym razem inaczej krzywa. Wyśrodkowanie z dwiema
   kreskami po `flex: 1` znosi to z definicji: obie zawsze równe, bo
   dzielą tę samą resztę.

   Wyjątek od zasady „na telefonie wszystko przy lewej krawędzi" jest
   świadomy: to nie jest treść, tylko listwa nad treścią. Nagłówek
   i lista dalej stoją przy lewej. */
@media (max-width: 720px) {
  .metryka {
    font-size: clamp(0.58rem, 2.6vw, 0.66rem);
    gap: 0.55rem;
    justify-content: center;
  }
  /* ⚠️ Reguły dla samych kresek stoją NIŻEJ w pliku, za ich definicjami
     bazowymi — inaczej nie działają. Tu byłyby przed `.metryka::before`
     z `flex: none`, a przy równej specyficzności rozstrzyga kolejność.
     Objaw: lewa kreska zostawała krótka i sztywna, prawa brała całą
     resztę, więc adres wyglądał na niewyśrodkowany, choć centrowanie
     działało poprawnie od pierwszej próby. */

  /* EKSPOZYCJA — wariant wybrany przez Kubę ze zrzutu z trzema
     poziomami (`render/39-adres-warianty.png`): większy stopień,
     cięższa kreska liter, miasto na przodzie, ulica z tyłu.

     Mono w wersalikach znosi to bez robienia się krzykliwym, bo i tak
     czyta się jak dana z przyrządu, a nie jak nagłówek. */
  /* ⚠️ `flex-wrap: nowrap` NIE CHRONI PRZED ZŁAMANIEM TEKSTU. Pilnuje
     tylko tego, żeby elementy listwy nie zeszły do drugiego rzędu —
     a łamał się tekst WEWNĄTRZ jednego z nich („ul. / Blachnickiego 3").
     To dwa różne zawijania i trzeba wyłączyć oba. Widać to od razu
     po objawie: kreski zostały w jednym rzędzie, rozjechał się sam
     napis. */
  .metryka span { white-space: nowrap; }

  /* ⚠️ WIĘKSZY STOPIEŃ ZABIJA TU KRESKI, A TO ONE ROBIĄ ROBOTĘ.
     Policzone na 374 px (tyle zostaje po pasku przewijania):

       stopień   tekst+odstępy   zostaje na obie kreski
       0,72 rem      ~300 px            ~29 px  (po 14 — niewidoczne)
       0,66 rem      ~271 px            ~58 px  (po 29 — listwa)

     Adres ma 30 znaków i mono z tracklingiem zjada 1,2 szerokości znaku
     na literę, więc każdy krok stopnia w górę zabiera kreskom dwa razy
     tyle, ile daje literom. A Kuba powiedział wprost, co tu działa:
     „kreski lewa prawa są super, robi to robotę".

     „Grubsze" bierzemy więc z WAGI (500) i z tonu, nie z szerokości —
     mono w wersalikach przy cięższej kresce czyta się mocniej, nie
     zajmując ani jednego piksela więcej. */
  .metryka {
    font-size: clamp(0.58rem, 2.6vw, 0.66rem);
    font-weight: 500;
    letter-spacing: 0.20em;
    gap: 0.5rem;
  }
  .metryka span:first-of-type { font-weight: 500; }
  body.ciemno .metryka span:first-of-type { color: #efeee9; }
  body.ciemno .metryka span:last-of-type { color: #a3a19c; }

  /* Kropka między miastem a ulicą oddycha. To jedyne miejsce, gdzie
     „niech się coś dzieje" (Kuba) nie jest ozdobnikiem: na stronie
     zegarmistrza pulsujący punkt czyta się jak dwukropek z tarczy.
     Bardzo wolno i bardzo płytko — ma się zauważyć przy drugim
     spojrzeniu, nie przy pierwszym.

     ⚠️ Animowane są `opacity` i `transform`, nie `width`/`background` —
     tylko te dwie właściwości przeglądarka rusza bez przeliczania
     układu strony. Kropka wisi nad sceną WebGL, więc każda klatka
     wymuszająca przeliczenie kosztowałaby dokładnie tam, gdzie nas
     na to nie stać. */
  .metryka i { animation: metryka-puls 4s ease-in-out infinite; }
}

@keyframes metryka-puls {
  0%, 100% { opacity: 0.35; transform: scale(1); }
  50%      { opacity: 1;    transform: scale(1.25); }
}
@media (prefers-reduced-motion: reduce) {
  .metryka i { animation: none; }
}

/* Kreska wiodąca. Nie ozdobnik: bez niej mono w wersalikach z dużym
   światłem czyta się jak etykieta zawieszona w powietrzu, a z nią
   ma od czego wystartować — dokładnie tak, jak numery sekcji. */
.metryka::before {
  content: "";
  width: 1.5rem;
  height: 1px;
  background: currentColor;
  opacity: 0.55;
  flex: none;
}

/* ⚠️ ADRES „WISIAŁ" MIMO KRESKI — Kuba, 14.08 wieczorem: „samo miejsce
   jest super, ale to jest takie dopisane na kolanie".

   Kreska z lewej dała mu początek, ale nie dała KOŃCA — a napis, który
   zaczyna się od znaku i urywa w pustce, czyta się jak notatka na
   marginesie. Strona nie ma klasycznego nagłówka (i dobrze, Kuba to
   lubi), więc adres jest jedyną rzeczą w tym pasie i musi sam zrobić
   sobie belkę.

   Druga kreska biegnie od tekstu do prawej krawędzi EKRANU, nie kolumny:
   `flex: 1` w linii, która nie łamie się na desktopie. Dzięki temu pas
   adresu domyka kadr z obu stron i czyta się jak celowa listwa, a nie
   jak coś doklejonego. Gaśnie po drodze, żeby nie przecinać sceny na pół
   ani nie konkurować z rantem koperty. */
.metryka::after {
  content: "";
  flex: 1;
  /* bez tego kreska nie oddaje miejsca tekstowi przy wąskim oknie —
     `flex: 1` w linii bez zawijania potrafi wypchnąć adres poza kadr */
  min-width: 0;
  height: 1px;
  margin-left: 0.4rem;
  background: linear-gradient(90deg,
    currentColor 0%,
    color-mix(in srgb, currentColor 45%, transparent) 55%,
    transparent 100%);
  opacity: 0.5;
}

/* NA TELEFONIE OBIE KRESKI SĄ RÓWNE — bo obie biorą `flex: 1` i dzielą
   tę samą resztę miejsca. To jest cała odpowiedź na „zależy od telefonu"
   (Kuba, 15.08): przy stałej kresce z lewej i elastycznej z prawej
   proporcja obu zmieniała się z każdą szerokością ekranu, więc na
   każdym urządzeniu listwa wyglądała inaczej krzywo. Dwie elastyczne
   są symetryczne z definicji, niezależnie od tego, ile miejsca zostało.

   Gasną KU KRAWĘDZIOM, nie ku tekstowi: przy tekście mają być mocne,
   żeby go trzymać, a przy brzegu ekranu rozpłynąć się w tło, zamiast
   urywać się kreską w pustce. */
@media (max-width: 720px) {
  .metryka::before {
    flex: 1;
    min-width: 0;
    width: auto;
    margin-right: 0.15rem;
    opacity: 0.5;
    background: linear-gradient(270deg,
      currentColor 0%,
      color-mix(in srgb, currentColor 45%, transparent) 55%,
      transparent 100%);
  }
  .metryka::after { margin-left: 0.15rem; }
}

/* Pas adresu ma sięgać krawędzi okna, a nie kończyć się razem
   z kolumną tekstu — inaczej listwa urywa się w połowie kadru
   i wraca to samo wrażenie „dopisane". Odsunięcie równa się
   wypełnieniu sekcji, więc na telefonie jest inne niż na monitorze. */
.ekran--hero .metryka {
  width: calc(100% + 1.5rem);
  margin-right: -1.5rem;
}
/* ⚠️ NA TELEFONIE LISTWA MUSI WYCHODZIĆ W OBIE STRONY, nie tylko w prawą.
   Na monitorze adres stoi przy lewej krawędzi kolumny, więc rozciąganie
   go w prawo jest dokładnie tym, czego trzeba. Po wyśrodkowaniu ta sama
   reguła robi z listwy figurę niesymetryczną: prawa kreska dostaje
   dodatkowe 22 px, lewa nic. Zmierzone: 34 px z lewej, 41 z prawej —
   mało, a widać, bo oko porównuje dwie rzeczy leżące w jednej linii. */
@media (max-width: 720px) {
  .ekran--hero .metryka {
    /* ⚠️ `max-width: 100%` (ustawione wyżej, żeby adres nie wychodził
       poza kolumnę 40 znaków) ŚCINAŁO to rozszerzenie do zera: element
       dostawał ujemne marginesy, ale szerokość zostawała szerokością
       ramki, więc listwa nie rosła — tylko przesuwała się w lewo.
       Objaw był mylący: kreski 34 i 41 px wyglądały na błąd centrowania,
       a centrowanie działało poprawnie od początku. */
    max-width: none;
    width: calc(100% + 2.8rem);
    margin-left: -1.4rem;
    margin-right: -1.4rem;
  }
}

/* Miasto jaśniej niż ulica. Adres to dwie informacje o różnej wadze:
   „gdzie w Polsce" czyta się z metra, „pod którym numerem" dopiero
   wtedy, gdy ktoś już się wybiera. Jeden ton różnicy to załatwia
   bez dokładania ani jednego elementu. */
body.ciemno .metryka span:first-of-type { color: #d8d6d1; }

/* Zdanie wiodące: jedyny stopień między nagłówkiem a treścią. Bez niego
   skok z 3,7 rem na 1 rem jest urwiskiem, a nie hierarchią. */
.wiodacy {
  /* Zapowiedź listy, więc CISZEJ od niej — to ona ma być mocna. */
  color: var(--stal);
  /* ⚠️ RÓWNA SIĘ Z TEKSTEM POZYCJI, NIE Z KRESKAMI (Kuba, 25.08:
     „mam wrażenie, że tam coś do siebie nie pasuje").

     Zmierzone: nagłówek, to zdanie i kreski zaczynały się na 22,4 px,
     a same hasła 22 px dalej. Blok miał więc DWIE lewe krawędzie,
     a to, co tłumaczyło tę drugą — kreska — jest celowo ciche.
     Po zrównaniu zapowiedź i lista mają jedną krawędź, a kreski
     wiszą poza nią jako znaki pozycji. Odstęp liczony z tych samych
     zmiennych co lista, więc nie rozjedzie się przy zmianie kreski. */
  padding-left: calc(var(--kreska-lista) + var(--kreska-odstep));
  font-size: var(--stopien-tresc);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: -0.006em;
}

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

/* ================================================== LISTA USŁUG NA HOME

   Kuba, 14.08 wieczorem: „to wymienianie rzeczy, które robimy, nie ma
   wagi, jakoś to wygląda cienko… wiesz, jak masz dalej 01, 02 i że hero
   i opis, to jest ok".

   Zgoda i wiadomo dlaczego: pięć krótkich fraz jedna pod drugą, tym
   samym stopniem i kolorem co zdanie wiodące, nie tworzy ŻADNEJ
   struktury — oko dostaje pięć osobnych linijek zamiast jednej rzeczy.
   Sekcje niżej mają numer, nagłówek i opis, czyli hierarchię, i dlatego
   czytają się jako coś zbudowanego.

   Dwa warianty do wyboru okiem, przełączane jedną klasą w HTML:
     .lista--numery  — mono 01–05 przy każdej pozycji, tak jak numery
                       sekcji; system się domyka, lista staje się
                       spisem treści usług
     .lista--kreski  — myślnik i włoskowata linia pod pozycją; bliżej
                       karty technicznej niż spisu

   Wspólne dla obu: większy stopień i więcej powietrza. Krótkie hasła
   znoszą to bez szkody dla tarczy, bo i tak kończą się w lewej trzeciej
   kadru (sprawdzone na zrzucie). */
.lista--hero li {
  display: flex;
  align-items: baseline;
  gap: var(--kreska-odstep);
  padding: 0.52rem 0;
  font-size: var(--stopien-tresc);
  letter-spacing: -0.004em;
}

/* ⚠️ NUMERY ODPADŁY (Kuba, 14.08): „źle, że to numery są, jakoś fajnie
   by trzeba to wymienić, ale nie cyframi".

   Zgadzam się z powodem, choć sam je zaproponowałem: cyfra przy usłudze
   sugeruje KOLEJNOŚĆ albo ważność, a tu nie ma ani jednego, ani drugiego
   — naprawa zegarka nie jest „pierwsza", a grawerowanie „piąte".
   Przy sekcjach numer znaczy „gdzie jesteś na stronie" i dlatego działa;
   przy usługach znaczyłby coś nieprawdziwego.

   Zamiast tego dwa warianty bez cyfr:

   .lista--strumien — wszystko w jednym ciągu, rozdzielone kropką w mono.
     Pięć krótkich haseł przestaje być pięcioma linijkami i staje się
     jednym zdaniem o tym, czym zakład się zajmuje. Ciężar bierze się
     z zagęszczenia, nie z ozdobników.

   .lista--kolumny — dwie kolumny z myślnikiem. Z pięciu cienkich wierszy
     robi się prostokąt tekstu, czyli bryła, którą oko czyta jako jedną
     rzecz. Zajmuje o połowę mniej wysokości i odsłania więcej tarczy. */
.lista--strumien {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.75rem;
  max-width: 34ch;
}
.lista--strumien li {
  display: inline;
  padding: 0.18rem 0;
  font-size: 1.06rem;
}
.lista--strumien li + li::before {
  content: "·";
  font-family: var(--font-dane);
  color: var(--stal);
  margin-right: 0.75rem;
}

.lista--kolumny {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 2.4rem;
  max-width: 46ch;
}
.lista--kolumny li {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.42rem 0;
  font-size: 1.0rem;
}
.lista--kolumny li::before {
  content: "";
  flex: none;
  width: 0.85rem;
  height: 1px;
  background: currentColor;
  opacity: 0.55;
  transform: translateY(-0.35em);
}

/* ⚠️ DWIE KOLUMNY ODPADŁY PO OBEJRZENIU NA OBU EKRANACH (Kuba, 15.08:
   „dwie linijki usług i sprzedaży to źle wygląda, ja bym to wszystko
   zostawił, ale listę wypisał w jednej").

   Miał rację i warto wiedzieć DLACZEGO, bo dwie kolumny rozwiązywały
   prawdziwy problem („lista nie ma wagi") i wciąż wyglądały źle:

   - hasła nie są równej długości. „grawerowanie" ma 12 znaków,
     „zegarki naręczne, mechaniczne i kwarcowe" — 40. W siatce 1fr 1fr
     krótkie hasło zostawia po sobie pół kolumny pustki, a długie łamie
     się na dwa wiersze. Prostokąt, który miał z tego wyjść, wychodził
     poszarpany.
   - kolumna druga wchodziła w prawą połowę kadru, a ta należy do sceny
     (zasada z 13.08). Na telefonie dochodziło do tego, że dwie kolumny
     na 390 px to dwie kolumny po ~17 znaków — czyli każde hasło łamane.

   Jeden słupek wraca do układu sprzed 14.08, ale NIE wraca „cienko":
   waga bierze się teraz z zagęszczenia wierszy (0,52 → 0,34 rem) i z
   tego, że wiersz nie ma prawa się złamać (max-width liczony w znakach
   pod najdłuższe hasło). Pięć wierszy stojących ciasno jeden pod drugim
   to blok tekstu; pięć wierszy rozstrzelonych to lista zakupów. */
.lista--slupek {
  display: grid;
  grid-template-columns: 1fr;
  /* pod najdłuższe hasło plus kreska — żeby żadna pozycja nie łamała
     się na dwa wiersze, bo to jedyna rzecz, która ten blok rozbija */
  max-width: 44ch;
}
/* ⚠️ DWIE KLASY W SELEKTORZE, NIE JEDNA — I TO NIE JEST OZDOBNIK.
   Dalej w pliku (przy regułach wspólnych) stoi `.lista li` z własnym
   stopniem 0,95 rem. Ma DOKŁADNIE tę samą specyficzność co
   `.lista--slupek li` (jedna klasa + element), więc rozstrzyga
   kolejność w pliku — i wygrywa reguła wspólna, bo jest niżej.
   Kosztowało to trzy zrzuty: zmieniałem `clamp` i mierzyłem stopień,
   który był ustawiany gdzie indziej. Sonda pokazała 15,2 px przy
   ustawieniu 14,08 px i dopiero to zdradziło sprawę. */
.lista.lista--slupek li {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.34rem 0;
  /* ⚠️ Stopień liczony ze szerokości okna, a nie stały. Zmierzone na
     zrzucie 390 px: przy 1,02 rem najdłuższe hasło („zegarki naręczne,
     mechaniczne i kwarcowe" — 40 znaków) potrzebuje ~328 px, a po
     odjęciu wypełnienia i kreski zostaje ~320. Łamał się więc JEDEN
     wiersz z pięciu i to wystarczyło, żeby blok przestał być blokiem:
     cztery pozycje w linii i jedna w dwóch czyta się jak błąd składu,
     nie jak lista.

     ⚠️ 3,9 vw było ZA MAŁO O JEDEN PASEK PRZEWIJANIA. Policzone
     mieściło się na styk (316 px potrzebne, 320 dostępne), a zrzut
     dalej pokazywał złamany wiersz — bo okno szerokie na 390 px ma
     jeszcze pasek przewijania i tekstowi zostaje 375. Zapas liczony
     na styk to nie zapas. */
  /* ⚠️ 25.08 — TEN OSOBNY, MNIEJSZY STOPIEŃ ZOSTAŁ ZDJĘTY.
     Powstał po to, żeby najdłuższe hasło nie łamało się na dwa wiersze,
     i tę pracę wykonał. Ale kosztem: lista miała 14,1 px przy zdaniu
     wiodącym 16,1 — czyli ×1,14, dokładnie ta „mielizna”, którą właśnie
     likwidujemy w całej stronie. Dwa stopnie różniące się o dwa piksele
     nie są hierarchią.

     Pomiar z pliku Geista: najdłuższe hasło ma 20,04 em, więc przy
     stopniu treści potrzebuje 322 px. Na 390 px dostępne jest 322 —
     czyli styk, a styk to nie zapas. Na 375 px (SE, mini) łamie się.

     Świadomie na to idziemy: hasło ma własne pole w układzie giętkim,
     więc drugi wiersz wchodzi pod TEKST, nie pod kreskę — to zwykłe
     wcięcie wiszące, a nie błąd składu. Jeśli Kuba uzna inaczej, tańsza
     poprawka jest w treści niż w skali: „naręczne” dubluje się z tym,
     że następna pozycja mówi „zegary”, więc jedno słowo mniej zdejmuje
     problem na zawsze. */
  font-size: inherit;

  /* 1b — „MOCNIEJ, NIE WIĘKSZE” (Kuba, 25.08). Hasła dostają pełną moc
     koloru i o pół stopnia więcej wagi; przygaszone zostaje to, co jest
     etykietą, czyli zdanie wiodące i same kreski. Wcześniej lista i jej
     zapowiedź miały ten sam kolor i tę samą wagę — pięć linijek bez
     żadnej struktury, stąd „wygląda cienko”. */
  font-weight: 450;
  color: var(--atrament);
}
body.w-mechanizmie .lista--slupek li,
body.ciemno .lista--slupek li { color: #f2f1ef; }

/* ⚠️ KRESKA WRACA DO WIDOCZNOŚCI. Przygasiłem ją do 0,55 przy „mocniej"
   (25.08) i to był błąd: to ONA tłumaczy, dlaczego hasła są wcięte.
   Niewidoczna zostawiała uskok bez powodu. Cicha ma być względem
   hasła, a nie względem tła. */
.lista.lista--slupek li::before { opacity: 0.85; width: var(--kreska-lista); }

/* „Naręczne" ZOSTAJE — Kuba, 25.08: „tak to się profesjonalnie nazywa".
   Skoro treść się nie skraca, to najdłuższe hasło będzie się łamać na
   wąskich telefonach. Można za to zadbać, ŻEBY ŁAMAŁO SIĘ RÓWNO:
   bez tego drugi wiersz zostawał jednym sierotą („kwarcowe"), a to
   właśnie sierota czyta się jak błąd składu, nie samo złamanie. */
.lista--slupek li { text-wrap: balance; }
@media (max-width: 720px) {
  /* Kreska i odstęp też oddają milimetry — na telefonie mają być
     znakiem pozycji, a nie wcięciem. */
  .lista.lista--slupek li { gap: var(--kreska-odstep); }
}
.lista.lista--slupek li::before {
  content: "";
  flex: none;
  width: 0.85rem;
  height: 1px;
  background: var(--stal);
  opacity: 0.75;
  transform: translateY(-0.35em);
}

.lista--numery { counter-reset: usluga; }
.lista--numery li { counter-increment: usluga; }
.lista--numery li::before {
  content: counter(usluga, decimal-leading-zero);
  font-family: var(--font-dane);
  font-size: 0.62rem;
  letter-spacing: 0.24em;
  color: var(--stal);
  /* stała szerokość, żeby hasła stały w jednej kolumnie — inaczej
     numer „przesuwa" tekst i cała lista faluje na lewej krawędzi */
  flex: none;
  width: 2.1rem;
  transform: translateY(-0.1em);
}

.lista--kreski li { border-bottom: 1px solid currentColor; }
.lista--kreski li:last-child { border-bottom: none; }
.lista--kreski li::before {
  content: "";
  flex: none;
  width: 1.1rem;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
  transform: translateY(-0.35em);
}
body.ciemno .lista--kreski li { border-bottom-color: rgba(198, 196, 191, 0.16); }

.lista li {
  position: relative;
  padding: 0.36rem 0;
  color: var(--przygas);
  font-size: 0.95rem;
}

/* Lista bez myślników po bokach — przy wyśrodkowaniu kreska z lewej
   przestaje mieć sens i tylko przekrzywia rytm. */

/* Numer telefonu to najczystsza dana na tej stronie i największa
   liczba w kadrze — mono, cyfry tabelaryczne, światło rozsunięte,
   żeby dało się go przeczytać z ekranu na raz. */
/* DWA POZIOMY, NIE CZTERY (pkt 8). Duży numer i jeden wspólny stopień
   dla całej reszty — komórki i godzin. Zmienna, żeby te dwie rzeczy nie
   miały szans się rozjechać przy kolejnej poprawce. */
.numery { display: flex; flex-direction: column; align-items: center; gap: 0.15rem; }

/* ⚠️ WAGA 300 BYŁA BŁĘDEM PRZY TEJ SKALI. Cienki mono przy 29 px robi się
   szkieletowy — kreska jest tak wąska, że na graficie zaczyna prześwitywać.
   To był drugi powód, dla którego numer wyglądał „brzydko”, obok skoków. */
.telefon {
  font-family: var(--font-dane);
  font-weight: 450;
  font-size: clamp(1.9rem, 1.1rem + 3.2vw, 3rem);
  letter-spacing: 0.01em;
  margin: 0;
}
.telefon a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 0.2rem;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(20, 20, 26, 0.2);
  font-variant-numeric: tabular-nums;
}

/* Komórka pod stacjonarnym. Piotr chciał, żeby duży numer został
   stacjonarny — więc komórka nie może z nim konkurować rozmiarem,
   ale musi być klikalna jednym kciukiem: stąd pełna wysokość dotyku
   przy mniejszym stopniu. */
/* Komórka NIE jest trzecim rozmiarem, tylko drugim numerem. Różnicę
   niesie etykieta „kom.”, nie stopień pisma — a przy okazji ta etykieta
   mówi to, czego dwa gołe numery jeden pod drugim nie mówiły wcale. */
.komorka {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.55rem;
  font-family: var(--font-dane);
  font-weight: 400;
  font-size: var(--stopien-dane);
  letter-spacing: 0.02em;
  margin: 0;
}
.komorka__et {
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--stal);
}
.komorka a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(20, 20, 26, 0.18);
  font-variant-numeric: tabular-nums;
}

/* Godziny jako dwie kolumny: dzień z lewej, godzina z prawej.
   Szerokość wiązana treścią (max-content), nie procentem — inaczej
   na szerokim ekranie kolumny rozjeżdżają się na pół kadru
   i przestają być parą. */
.godziny {
  display: grid;
  grid-template-columns: max-content max-content;
  gap: 0.3rem 1.1rem;
  margin: 1.3rem 0 0;
  font-family: var(--font-dane);
  font-size: var(--stopien-dane);
  justify-content: center;
}
.godziny dt {
  text-align: right;
  color: var(--przygas);
  letter-spacing: 0.02em;
}
.godziny dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.godziny__sz { opacity: 0.55; }

.mail {
  margin-top: 1.1rem;
  font-family: var(--font-dane);
  font-size: 0.85rem;
  letter-spacing: 0.01em;
  overflow-wrap: anywhere;
}
.mail a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(20, 20, 26, 0.18);
  padding-bottom: 0.15rem;
}

/* --------------------------------------------------------------- guziki */

.akcje { margin-top: 1.6rem; }

.btn {
  display: inline-flex;
  align-items: baseline;
  gap: 0.55rem;
  padding: 0.95rem 1.7rem;
  border-radius: 2px;
  text-decoration: none;
  font-size: 0.94rem;
  transition: transform 180ms ease, box-shadow 180ms ease;
}

.btn--pelny {
  background: var(--atrament);
  color: var(--perla);
  box-shadow: 0 6px 18px rgba(20, 20, 26, 0.2);
}

.btn--pelny:hover { transform: translateY(-2px); }

.btn__et {
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-size: 0.66rem;
  opacity: 0.72;
}

.btn__nr { font-variant-numeric: tabular-nums; }

.zjazd {
  margin-top: 3rem;
  font-size: 0.6rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--stal);
  animation: tchnienie 2.6s ease-in-out infinite;
}

@keyframes tchnienie {
  0%, 100% { opacity: 0.35; transform: translateY(0); }
  50% { opacity: 0.9; transform: translateY(5px); }
}

/* ------------------------------------------------------------ ciemność

   Kamera schodzi pod tarczę, więc tekst musi się przewrócić na jasny.
   Klasę na <body> ustawia scena, bo tylko ona wie, gdzie jest kamera. */
body.w-mechanizmie,
body.ciemno {
  color: var(--jasny);
}
body.w-mechanizmie .nad,
body.ciemno .nad,
/* Odrobinę jaśniej niż było (#a9a7a2). Zdanie wiodące leży na scenie,
   a nie na papierze — hierarchia wobec białego nagłówka zostaje, bo
   różnica jest nadal wyraźna, ale margines na jasne miejsca sceny rośnie.
   Sama poduszka tego nie załatwia: ona pracuje na tle, a to jest druga
   strona tego samego ułamka. */
body.w-mechanizmie .wiodacy,
body.ciemno .wiodacy,
body.w-mechanizmie .lista li,
body.ciemno .lista li { color: #c6c4bf; }
body.w-mechanizmie strong,
body.ciemno strong { color: #fff; }

.ekran--ciemny,
.ekran--koniec,
.ekran { transition: color 500ms ease; }

/* ------------------------------------------------------------- STOPKA

   Trzy piętra o różnym ciężarze, nie jedna zbita linijka:
   znak → co można kliknąć → dane rejestrowe → podpis.
   Kreska u góry oddziela ją od kontaktu; bez niej stopka czytałaby się
   jako dalszy ciąg sekcji, a to jest inny rejestr. */
.stopka {
  margin-top: 3.4rem;
  padding-top: 1.6rem;
  border-top: 1px solid rgba(20, 20, 26, 0.14);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.05rem;
  color: var(--stal);
}
body.w-mechanizmie .stopka,
body.ciemno .stopka { border-top-color: rgba(240, 238, 234, 0.16); }

.stopka__znak {
  width: 112px;
  height: auto;
  opacity: 0.5;
  /* Plik jest w bieli — na jasnym tle trzeba go odwrócić, inaczej znika. */
  filter: invert(1);
}
body.w-mechanizmie .stopka__znak,
body.ciemno .stopka__znak { filter: none; opacity: 0.42; }

/* Cel dotyku pełne 44 px. Odnośniki stopki miały 23 px — najmniejsze
   na całej stronie, a klientela Piotra jest starsza. */
.stopka__linki {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 1.6rem;
}
.stopka__linki a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--font-dane);
  font-weight: 450;
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
}
.stopka__linki a:hover { color: var(--atrament); }
body.w-mechanizmie .stopka__linki a:hover,
body.ciemno .stopka__linki a:hover { color: #efedea; }

/* Dane rejestrowe. Mono, bo na tej stronie mono znaczy „dana”.
   Wersalika NIE ma: nazwisko i nazwa ulicy w rozstrzelonych wielkich
   literach robią się nieczytelne, a to jest do przeczytania, nie do
   ozdoby. Cyfry tabelaryczne — NIP ma stać równo. */
.stopka__dane {
  margin: 0;
  max-width: 46ch;
  text-align: center;
  font-family: var(--font-dane);
  font-size: 0.7rem;
  line-height: 1.7;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
}

/* Podpis celowo najcichszy element na stronie — to strona Piotra,
   nie nasza wizytówka. Odnośnik podkreślony dopiero pod kursorem. */
.stopka__podpis {
  margin: 0;
  text-align: center;
  font-family: var(--font-dane);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.62;
}
.stopka__podpis a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.stopka__podpis a:hover,
.stopka__podpis a:focus-visible { border-bottom-color: currentColor; }

.stopka__wersja { display: block; margin-top: 0.5rem; opacity: 0.7; }

/* ------------------------------------------------------------ TELEFON

   Na wąskim ekranie treść stoi ZAWSZE na środku. Układy do lewej
   i do prawej mają sens tam, gdzie obok tekstu zostaje miejsce na
   scenę — na telefonie tego miejsca nie ma i tekst tylko przyklejał
   się do krawędzi. */
@media (max-width: 720px) {
  .pytanie { text-align: left; }

  /* podpowiedź trochę wyżej, żeby nie wchodziła pod pasek przeglądarki */
  .podpowiedz { bottom: calc(4.6rem + env(safe-area-inset-bottom)); }
  .podpowiedz__tor { height: 38px; }
}

@media (prefers-reduced-motion: reduce) {
  .zjazd { animation: none; }
  /* zegar i scena chodzą zawsze — to jest treść, nie ozdoba */
}

/* --------------------------------------------------- drobny druk = mono

   Wszystko, co jest etykietą, oznaczeniem albo liczbą, idzie w mono —
   nie dlatego, że „technicznie wygląda", tylko dlatego, że wtedy jednym
   spojrzeniem widać, co jest zdaniem, a co daną. Grotesk mówi, mono
   podaje. */
.pytanie__tytul,
.pole > span,
.podpowiedz__slowo,
.btn__et,
.btn__nr,
.dzwon__numer,
.stopka {
  font-family: var(--font-dane);
  font-variant-numeric: tabular-nums;
}

/* Mono jest z natury szersze i optycznie cięższe od grotesku w tym samym
   stopniu — przy rozstrzelonym wersaliku trzeba mu ująć grubości,
   inaczej etykieta krzyczy głośniej niż nagłówek nad nią. */
.pytanie__tytul,
.pole > span,
.podpowiedz__slowo,
.btn__et {
  font-weight: 450;
}

/* Cyfry w treści też mają stać w kolumnie — inaczej „8,3 na 10 ze 119
   opinii" rozjeżdża się w rytmie. */
p, .lista li { font-variant-numeric: tabular-nums; }


/* ====================================================== PALETA GRAFITOWA

   Kuba wybral grafit 13.08. Klasa `ciemno` wchodzi na `body` ze sceny
   (paleta zna swoja jasnosc, wiec to ona o tym decyduje, a nie CSS).

   Reguly ciemnego tekstu byly juz napisane dla sekcji w mechanizmie
   i sa sprawdzone — dostaly wiec blizniaczy selektor zamiast drugiej
   kopii. Tu zostaja tylko rzeczy, ktore dotycza CALEJ strony. */

body.ciemno {
  background: #0d0e11;
  color: var(--jasny);
}

/* GRUBOSC. Jasny tekst na ciemnym tle rozlewa sie optycznie i wyglada
   grubiej niz ten sam stopien na jasnym — to nie zludzenie, tylko
   sposob, w jaki oko odbiera jasne krawedzie. Krój zmienny pozwala to
   odrobic jedna liczba, bez zmiany kroju. */
/* Na ciemnym tle jasny tekst rozlewa się optycznie — ale szeryf
   o silnym kontraście ma jeszcze drugi problem: to jego CIENKIE kreski
   nikną pierwsze. Dlatego schodzimy mniej, niż schodziliśmy przy
   grotesku, a treść wręcz podnosimy. */
/* Na ciemnym schodzimy tylko odrobine: przy didone to CIENKIE kreski
   nikna pierwsze, wiec zbyt duze scienienie rozbiloby litery. */
/* Na ciemnym o krok niżej: geometryczne, zamknięte kształty (o, e, a)
   rozlewają się optycznie mocniej niż litery szeryfowe. */
body.ciemno h1,
body.ciemno h2 { font-weight: 545; }
body.ciemno { font-weight: 350; }
body.ciemno .nad { font-weight: 420; }

/* Winieta na ciemnym musi sie odwrocic — ciemna ramka na ciemnym tle
   tylko zamula obraz. Reguła istniała dla mechanizmu, tu obowiazuje
   od poczatku. */

/* Sluchawka: czarna pigulka znika na graficie. Odwracamy ja. */
body.ciemno .dzwon {
  background: #efedea;
  color: #14141a;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}

/* ⚠️ WYJATEK, KTORY NIE JEST ESTETYCZNY.

   Klientela zakladu to w duzej czesci starsi ludzie. Cienki jasny tekst
   na ciemnym tle czyta im sie gorzej — to fizjologia, nie gust. Sekcja
   kontaktu gra wiec wedlug innych zasad niz reszta strony: wiekszy
   stopien, wieksza grubosc, pelny kontrast. Nikt tam nie podziwia
   sceny; ktos chce przeczytac numer i zadzwonic.

   Jesli kiedykolwiek bedziemy to „porzadkowac dla spojnosci" — to jest
   miejsce, ktorego sie nie rusza. */
body.ciemno .ekran--koniec .ramka { color: #fff; }

/* ⚠️ TU STAŁO OSOBNE USTAWIENIE NUMERU DLA TŁA CIEMNEGO — waga 400
   i własny clamp. Powstało, bo podstawa miała wagę 300, która na graficie
   robiła się nitką. Podstawa ma teraz 450, więc powód zniknął, a dwa
   różne rozmiary tego samego numeru zostawały jako pułapka: grafit jest
   tłem DOMYŚLNYM, więc każda poprawka wpisana wyżej i tak byłaby tutaj
   nadpisana i wyglądałaby na niedziałającą. Jeden numer, jedna skala. */

body.ciemno .telefon a {
  color: #fff;
  border-bottom-color: rgba(255, 255, 255, 0.45);
}

body.ciemno .ekran--koniec h2 { font-weight: 320; }

body.ciemno .ekran--koniec .nad,
body.ciemno .ekran--koniec p { color: #d6d4cf; }


/* ============================================================== ZIARNO

   Warstwa nad całym kadrem — nad sceną i nad tekstem.

   Ziarno siedziało dotąd w shaderze nieba, więc rozbijało pasy na tle,
   ale metalu nie dotykało wcale. A stopnie widać najmocniej właśnie na
   dużych, gładkich powierzchniach stali: rehaut rozciąga kilkanaście
   poziomów jasności na kilkaset pikseli i przejście rozwarstwia się
   w widoczne pasy. Ziarno miesza je z powrotem — to jest ta sama
   sztuczka, którą druk stosuje od stu lat, żeby z kilku farb zrobić
   ciągły ton.

   Szum rysowany przez `feTurbulence` w adresie danych: zero zapytań
   do sieci, zero plików. Kafel 220 px jest na tyle duży, że
   powtórzenie nie układa się w kratę.

   `overlay` zamiast zwykłej przezroczystości: rozjaśnia ciemne piksele
   i przyciemnia jasne zamiast kłaść szarą mgłę na całość — dzięki temu
   czerń zostaje czernią. */
.ziarno {
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: 0.055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='z'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23z)'/%3E%3C/svg%3E");
  background-repeat: repeat;
}

/* Na ciemnym tle stopnie widać wyraźniej niż na jasnym — oko rozróżnia
   więcej odcieni przy dole skali. Dlatego przy graficie ziarno mocniejsze. */
body.ciemno .ziarno { opacity: 0.075; }

/* Treść w szeryfie potrzebuje odrobinę więcej powietrza między
   wierszami niż grotesk — kreszki szeryfów zagęszczają wiersz optycznie. */
p, .lista li { line-height: 1.6; }

/* ⚠️ Reguły ciemnego tekstu pisaliśmy pierwotnie dla sekcji o warsztacie,
   a tamta sekcja nie ma metryki ani stopki — więc przy przenoszeniu ich
   na całą stronę te dwa elementy zostały z barwą pisaną pod perłową biel
   (#4a4a50) i po prostu wsiąkły w tło. Widać to było dopiero na zrzucie:
   adres i rok założenia w hero były praktycznie niewidoczne. */
body.ciemno .metryka { color: #b6b4af; }
body.ciemno .metryka i { opacity: 0.7; }
body.ciemno .stopka { color: #8b8985; }
body.ciemno .stopka a { color: #b6b4af; }

/* Znacznik wersji — ma być czytelny, ale nie ma udawać treści. */
.stopka__wersja { opacity: 0.65; }
body.ciemno .stopka__wersja { color: #9b998f; }

/* Na telefonie sekcje miały po 12vh wypełnienia z góry i z dołu — czyli
   ćwierć ekranu oddaną na marginesy, których na tak małej powierzchni
   nie stać nas trzymać. Scena i tak daje oddech. */
@media (max-width: 720px) {
  .ekran { padding: 7vh 1.4rem; }
  .ekran--hero { padding-top: 3.5vh; }

  /* ⚠️ NA TELEFONIE PODUSZKA MUSI IŚĆ Z GÓRY W DÓŁ, NIE Z LEWA W PRAWO.

     Zmierzone na zrzucie 390 px: kontrast listy usług do tła spadał na
     prawej połowie do 1,7–2,4:1 przy progu 4,5:1. Czyli połowa każdej
     pozycji była praktycznie nie do przeczytania na tarczy.

     Przyczyna jest w geometrii, nie w kolorze: na monitorze tekst zajmuje
     lewą trzecią kadru, więc gradient poziomy ma gdzie wygasnąć, zanim
     dojdzie do zegarka. Na telefonie tekst idzie przez CAŁĄ szerokość —
     poziomy gradient nie ma jak go osłonić, bo osłania tylko to, co przy
     lewej krawędzi. Osłona musi więc lecieć wzdłuż tekstu, czyli w pionie,
     i gasnąć dopiero pod ostatnią linijką.

     Efekt uboczny jest pożądany: zegarek zostaje odsłonięty w dolnej
     części ekranu, czyli dokładnie tam, gdzie i tak na niego patrzysz. */
  body.ciemno .ramka::before {
    /* Pudełko schodzi 22 % niżej niż tekst — nie dla osłony, tylko po to,
       żeby wygaszanie miało na czym się rozłożyć. Przy krótkim zapasie
       ostatnia pozycja listy wypadała w miejscu, gdzie gradient już nie
       pracuje (zmierzone: 3,08:1), a skrócenie samego wygaszania robi
       z osłony widoczny prostokąt na tarczy. */
    inset: -14% -12% -22% -12%;
    background: linear-gradient(180deg,
      rgba(9, 9, 11, 0.93) 0%,
      rgba(9, 9, 11, 0.90) 60%,
      rgba(9, 9, 11, 0.62) 84%,
      rgba(9, 9, 11, 0) 100%);
    -webkit-mask-image: none;
    mask-image: none;
  }
}


/* ------------------------------------------------------------- DYPLOM

   Dokument ma być obecny, a nie pokazywany. Stąd brak ramki, brak cienia
   i brak jakiegokolwiek pojemnika — winieta jest w samym pliku, w kanale
   alfa, więc obraz kończy się rozmyciem, a nie krawędzią. */
.dyplom {
  margin: 1.8rem 0 0;
  max-width: 560px;
}
.dyplom img {
  display: block;
  width: 100%;
  height: auto;
  opacity: 0.82;
}
/* Podpis to DANA, tak jak godziny i numer — mono, rozstrzelony wersalik,
   ten sam stopień co etykiety sekcji. Żadnego nowego szczebla w skali. */
.dyplom figcaption {
  margin-top: 0.7rem;
  font-family: var(--font-dane);
  font-weight: 500;
  font-size: var(--stopien-etykieta);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--stal);
}

/* Zdanie, które zdejmuje obawę o koszt zapytania. Ten sam stopień co
   reszta treści — mocniejszy kolor i pół stopnia wagi, nic więcej. */
.obietnica {
  margin-top: 1.15rem;
  font-weight: 450;
  color: var(--atrament);
}
body.w-mechanizmie .obietnica,
body.ciemno .obietnica { color: #f2f1ef; }

/* ------------------------------------------------- RODZAJ SPRAWY (pkt 9)

   Radio bez wyglądu radia. Kółko po lewej stronie etykiety zawsze
   ciągnie wzrok w pionową kolumnę i zamienia cztery hasła w ankietę —
   a to jest dokładnie to wrażenie, którego formularz ma nie robić.
   Zaznaczenie niesie więc samo pole guzika: obrys → wypełnienie. */
.rodzaj {
  border: 0;
  padding: 0;
  margin: 0 0 1.15rem;
  min-width: 0;   /* fieldset ma własną szerokość minimalną i bez tego
                     potrafi rozepchnąć formularz na wąskim ekranie */
}
.rodzaj legend {
  padding: 0;
  margin-bottom: 0.55rem;
  font-family: var(--font-dane);
  font-weight: 450;
  font-size: var(--stopien-etykieta);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--stal);
}
.rodzaj__guziki { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.rodzaj__opcja { display: inline-flex; }
/* Samo pole radia znika z widoku, ale ZOSTAJE w drzewie — inaczej
   przestaje działać klawiatura i czytnik ekranu. */
.rodzaj__opcja input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.rodzaj__opcja span {
  display: inline-flex;
  align-items: center;
  min-height: 44px;          /* cel dotyku, tak jak reszta strony */
  padding: 0 0.9rem;
  border-radius: 2px;
  border: 1px solid rgba(20, 20, 26, 0.22);
  font-family: var(--font-dane);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, color 160ms ease;
}
.rodzaj__opcja:hover span { border-color: currentColor; }
.rodzaj__opcja input:focus-visible + span { outline: 2px solid currentColor; outline-offset: 2px; }
.rodzaj__opcja input:checked + span {
  background: var(--atrament);
  border-color: var(--atrament);
  color: var(--perla);
}

body.w-mechanizmie .rodzaj__opcja span,
body.ciemno .rodzaj__opcja span { border-color: rgba(240, 238, 234, 0.28); }
body.w-mechanizmie .rodzaj__opcja input:checked + span,
body.ciemno .rodzaj__opcja input:checked + span {
  background: #efedea;
  border-color: #efedea;
  color: #14141a;
}

/* Wabik na automaty. Ukryty tak, żeby bot go widział w kodzie, a człowiek
   nigdy — z klawiatury też nie da się w niego wejść (tabindex -1).
   ⚠️ Nie `display:none`: część automatów pomija pola całkiem ukryte. */
.wabik {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.pytanie__nota {
  margin-top: 1rem;
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--przygas);
}
.pytanie__nota a { color: inherit; }

/* Guzik w trakcie wysyłki — widać, że coś się dzieje, i nie da się
   kliknąć drugi raz. */
.btn[disabled] { opacity: 0.55; cursor: progress; }
