/* ==========================================================================
   gremix.app
   Dwie zmiany: nocna (dyspozytornia) i dzienna (papier operacyjny).
   Kolor nigdy nie jest dekoracją. Bursztyn, zieleń i róż oznaczają stany.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonty. Self-hosted i przycięte do używanych znaków.
   Archivo waży razem 51,9 KB (latin 45,7 plus latin-ext 6,2), Plex Mono
   9,3 KB. Zakresy w font-weight i font-stretch są zawężone do tego, co
   plik faktycznie zawiera, żeby przeglądarka nie syntezowała wag ani
   szerokości, których w nim nie ma.
   Zakresy latin obu krojów są preloadowane w index.html, bo występują nad
   zgięciem. Latin-ext, czyli polskie diakrytyki, celowo nie, żeby nie
   konkurować z LCP.
   font-display: swap plus fallback dopasowany metrycznie: tekst maluje się
   natychmiast fontem systemowym, a podmiana prawie nie rusza układu.
   IBM Plex Mono tylko w jednej wadze (500). Dane różnicuję wersalikami
   i kolorem, nie drugą wagą.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100% 125%;
  font-display: swap;
  src: url('../fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100% 125%;
  font-display: swap;
  src: url('../fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/plexmono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/plexmono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Fallback dopasowany metrycznie. Wartości NIE są przybliżone, tylko
   zmierzone w przeglądarce: ta sama próbka tekstu renderowana w Archivo
   i w Arial przy 100 px dała stosunek szerokości 100.2%, a ascent i descent
   Archivo wynoszą 0.88 i 0.21 em wobec 0.91 i 0.21 w Arialu. Stąd
   ascent-override 87.8%, a nie zgadywane wcześniej 92%. */
@font-face {
  font-family: 'Archivo Fallback';
  src: local('Arial'), local('Helvetica'), local('Segoe UI');
  size-adjust: 100.2%;
  ascent-override: 87.8%;
  descent-override: 20.9%;
  line-gap-override: 0%;
}

/* Zmierzone osobno dla mono, wcześniej ten blok miał wartości przepisane
   z Archivo, czyli z zupełnie innego kroju. Ta sama próbka tekstu przy
   100 px: Plex Mono 2760 px, Consolas 2529 px, czyli Plex jest szerszy
   o 9.1%. Ascent 103 wobec 92, descent 28 wobec 25.
   Strojone pod Consolas, bo to pierwsza pozycja z listy src i jest obecna
   zarówno na Windowsie, jak i na większości maców z pakietem Office.
   Dla Menlo właściwy size-adjust wyniósłby 113%, ale nie da się podać
   dwóch naraz, a mono niesie tu wyłącznie małe etykiety. */
@font-face {
  font-family: 'Plex Mono Fallback';
  src: local('Consolas'), local('Menlo'), local('DejaVu Sans Mono');
  size-adjust: 109.1%;
  ascent-override: 94.4%;
  descent-override: 25.7%;
  line-gap-override: 0%;
}

/* --------------------------------------------------------------------------
   2. Tokeny
   -------------------------------------------------------------------------- */

:root {
  /* Zmiana nocna */
  --night-950: #0E1420;   /* tło */
  --night-900: #121A28;   /* karta na ciemnym */
  --night-800: #1A2434;   /* panel, wiersz tablicy */
  --night-600: #33415A;   /* WYŁĄCZNIE ramka i separator, nigdy tekst */
  --night-text: #E8EEF6;  /* tekst podstawowy,  15.7:1 na --night-950 */
  --night-muted: #8FA0B8; /* tekst wyciszony,    6.92:1 na --night-950
                                                  5.86:1 na --night-800 */

  /* Zmiana dzienna */
  --paper-000: #FFFFFF;   /* powierzchnia karty */
  --paper-050: #EDF1F5;   /* tło */
  --paper-200: #D5DEE8;   /* ramka i separator */
  --ink-900: #111926;     /* tekst podstawowy,  15.5:1 na --paper-050 */
  --ink-500: #4A5A70;     /* tekst wyciszony,    6.19:1 na --paper-050 */

  /* Stany. Kolor niesie znaczenie, nie ozdobę. */
  --amber-500: #F0A93B;   /* zaplanowane, w toku, oraz jedyne CTA */
  --ok-500:    #35B98A;   /* wykonane, na ciemnym    7.42:1 */
  --alert-500: #E0576B;   /* alert, na ciemnym       5.04:1 */
  --ok-700:    #15694A;   /* wykonane, na jasnym     5.87:1 */
  --alert-700: #B32B44;   /* błąd, na jasnym         5.53:1 */
  --amber-700: #B8730A;   /* WYŁĄCZNIE grafika na jasnym tle, próg 3:1.
                             Słupek wykresu na białej karcie: 3.82:1.
                             Do tekstu za jasny, patrz --amber-800. */
  --amber-800: #96590A;   /* tekst bursztynowy na jasnym, próg 4.5:1.
                             Na --paper-050 daje 4.96:1, a na najciemniejszym
                             punkcie siatki 4.53:1, więc przechodzi także
                             wtedy, gdy trafi na kropkę. */
  /* ink-900 na amber-500 (CTA):                     8.77:1 */

  /* Typografia */
  --font-display: 'Archivo', 'Archivo Fallback', Arial, sans-serif;
  --font-body: 'Archivo', 'Archivo Fallback', Arial, sans-serif;
  --font-data: 'Plex Mono', 'Plex Mono Fallback', ui-monospace, monospace;

  --wdth-display: 125;  /* Expanded */
  --wdth-body: 100;     /* Normal   */

  --fs-data: 0.8125rem;
  --track-status: 0.08em;
  --track-display: -0.02em;

  /* Rytm */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --sp-9: 6rem;

  --wrap: 1200px;
  --gutter: clamp(1.25rem, 5vw, 2.5rem);

  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 14px;

  --focus: var(--ink-900);

  /* --- faktura tła ---------------------------------------------------- */
  --grid-dot: rgba(143, 160, 184, 0.09);
  --grid-size: 30px;
  /* Trzecie podejście, tym razem sprawdzone na wyrenderowanych pikselach,
     a nie wyliczone.
     Wersja 1 (1 px, 40 px, 0.03) i wersja 2 (1 px, 40 px, 0.042) były
     niewidoczne. Zliczenie pikseli na zrzucie pokazało dlaczego: kropki
     zajmowały 0.33% powierzchni przy różnicy 9 kroków RGB, czyli 3.8%
     jasności względnej. Na granacie ta sama różnica liczbowa to 57%
     jasności względnej, dlatego noc widać, a dnia nie było.
     Na jasnym tle trzeba nadrobić POKRYCIEM, nie samą siłą koloru:
     kropka 1.5 px zamiast 1 px i raster 30 px zamiast 40 px, przy niskim
     opacity 0.045. Delta L* na kropkę wynosi 3.48, czyli poniżej nocy
     (3.74), ale pokrycie jest większe, bo inaczej na jasnym tle nic nie
     widać. Wybrane wzrokiem na zrzucie krawędzi, po odrzuceniu wariantu
     2 px / 28 px, który wychodził mocniej niż noc. */
  --grid-dot-day: rgba(17, 25, 38, 0.08);
  --grid-size-day: 26px;

  --grid-night: radial-gradient(circle at 1.5px 1.5px, var(--grid-dot) 1.5px, transparent 0);
  --grid-day: radial-gradient(circle at 1.5px 1.5px, var(--grid-dot-day) 1.5px, transparent 0);

  /* Jedyna poświata na całej stronie, pod tablicą zmiany w hero. */
  --glow: radial-gradient(circle, rgba(240, 169, 59, 0.05) 0%, rgba(240, 169, 59, 0.02) 42%, rgba(240, 169, 59, 0) 70%);

  /* Ślady połączeń. Polecenie mówiło 0.04, ale --night-600 leży tak blisko
     tła, że przy tej wartości różnica to 2 kroki RGB i linia nie rysuje się
     wcale. 0.18 daje 10 kroków, czyli dokładnie tyle co siatka kropek, więc
     ślady i kropki mają tę samą wagę. To jedyne miejsce, w którym odszedłem
     od podanej liczby. Zmiana tutaj zmienia obie sekcje naraz. */
  --trace-a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='320' fill='none' stroke='%2333415A' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M0 44 H108 Q124 44 124 60 V188 Q124 204 140 204 H252'/%3E%3Cpath d='M0 268 H62 Q78 268 78 252 V132 Q78 116 94 116 H196 Q212 116 212 100 V52'/%3E%3Cpath d='M212 100 H300 Q316 100 316 116 V244'/%3E%3Ccircle cx='252' cy='204' r='3.5' fill='%2333415A' stroke='none'/%3E%3Ccircle cx='316' cy='244' r='3.5' fill='%2333415A' stroke='none'/%3E%3Ccircle cx='212' cy='52' r='3.5' fill='%2333415A' stroke='none'/%3E%3C/svg%3E");
  --trace-b: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='380' height='280' fill='none' stroke='%2333415A' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M380 40 H272 Q256 40 256 56 V150 Q256 166 240 166 H150'/%3E%3Cpath d='M380 218 H320 Q304 218 304 202 V96 Q304 80 288 80 H214'/%3E%3Ccircle cx='150' cy='166' r='3.5' fill='%2333415A' stroke='none'/%3E%3Ccircle cx='214' cy='80' r='3.5' fill='%2333415A' stroke='none'/%3E%3C/svg%3E");
  --trace-alpha: 0.18;
  /* Na jasnym tle ten sam granatowy rysunek daje chlodna szarosc.
     0.12 to okolo 19 krokow RGB wobec tla, czyli delikatnie, ale widocznie. */
  --trace-alpha-day: 0.12;
}

/* --------------------------------------------------------------------------
   3. Reset i baza
   -------------------------------------------------------------------------- */

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

/* Elementy z atrybutem hidden mają zniknąć nawet wtedy, gdy jakaś reguła
   nadaje im display. Dotyczy drugiej etykiety przycisku, komunikatów pól
   i banku komunikatów formularza. */
[hidden] { display: none !important; }

/* Bez scroll-behavior: smooth. Wchodzi w konflikt z przypięciem
   ScrollTrigger i potrafi ustawić pin w złym miejscu. Skok do kotwicy jest
   natychmiastowy, co dla drogi do formularza jest zaletą, nie stratą. */
html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper-050);
  color: var(--ink-900);
  font-family: var(--font-body);
  font-variation-settings: 'wdth' var(--wdth-body);
  font-weight: 400;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;

  /* Polski ma długie słowa, a nagłówki chodzą w Archivo Expanded, więc
     przy 320 px pojedynczy wyraz potrafi być szerszy niż kolumna i wypycha
     poziomy pasek przewijania. Na przykład "zautomatyzowania" potrzebuje
     307 px przy kolumnie 280 px. break-word łamie wyłącznie wtedy, gdy
     wyraz naprawdę się nie mieści, więc nie rusza normalnego składu.
     Właściwość jest dziedziczona, więc jedna deklaracja zabezpiecza całą
     stronę, w tym długie adresy email w stopce. */
  overflow-wrap: break-word;
}

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

h1, h2, h3 {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 700;
  letter-spacing: var(--track-display);
  line-height: 1.08;
  margin: 0;
  text-wrap: balance;
}

/* SplitText liczy wiersze po realnym łamaniu. text-wrap: balance przelicza
   je po fakcie i rozjeżdża rozbicie, więc nagłówek hero łamie się zwykłym
   trybem. */
.hero__title { text-wrap: normal; }

p { margin: 0; }
ol, ul, dl, dd { margin: 0; padding: 0; }
li { list-style: none; }

a { color: inherit; }

/* Widoczny focus na każdym elemencie interaktywnym. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 3px;
}
:focus:not(:focus-visible) { outline: none; }

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

.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: -100px;
  z-index: 100;
  padding: var(--sp-3) var(--sp-5);
  background: var(--amber-500);
  color: var(--ink-900);
  font-weight: 600;
  border-radius: var(--r-sm);
  text-decoration: none;
  transition: top 120ms ease;
}
.skip-link:focus { top: var(--sp-4); }

/* --------------------------------------------------------------------------
   4. Zmiany: nocna i dzienna
   Krawędź między nimi jest twarda, bez gradientu.
   -------------------------------------------------------------------------- */

/* Faktura tła.
   Siatka kropek to język kanwy n8n, czyli narzędzia, o którym jest ta
   strona. Dlatego jest wyraźniejsza na zmianie nocnej, gdzie mowa
   o systemach, i prawie znika na dziennej, gdzie mowa do człowieka.
   Wszystko czystym CSS, zero plików graficznych.

   Zmierzone różnice wobec tła, w krokach RGB:
     kropki noc   8    kropki dzień  7    poświata  11
   To jest próg, na którym fakturę się czuje, a nie widzi. */

.shift-night {
  background-color: var(--night-950);
  background-image: var(--grid-night);
  background-size: var(--grid-size) var(--grid-size);
  color: var(--night-text);
  --focus: var(--amber-500);
}

.shift-day {
  background-color: var(--paper-050);
  background-image: var(--grid-day);
  background-size: var(--grid-size-day) var(--grid-size-day);
  color: var(--ink-900);
  --focus: var(--ink-900);
}

/* Sekcja kontaktu zostaje płaska. Formularz ma być najspokojniejszym
   miejscem na stronie, a nie kolejną powierzchnią z rysunkiem. */
#kontakt { background-image: none; }

/* Panele są czytelną powierzchnią i dlatego zostają płaskie. Wszystkie
   mają kryjące tło, więc siatka nie przechodzi pod kartami, tablicą,
   formularzem ani panelem stacku. To jest własność, na której polegamy,
   więc gdyby któryś dostał kiedyś tło półprzezroczyste, faktura wyjdzie
   spod niego i trzeba będzie to naprawić tutaj. */

/* Zakaz faktury pod tekstem mniejszym niż 1 rem. Poniżej są wszystkie
   elementy poniżej tego progu, które siedzą wprost na tle sekcji, a nie
   na panelu. Podkładka jest w dokładnym kolorze tła sekcji, więc nie
   tworzy widocznej krawędzi, tylko wygasza pod sobą kropki. */
/* Etykiety dostają podkładkę przyciętą do samego tekstu, a nie do całej
   szerokości bloku. Pas bez kropek na pół kolumny czytał się jak błąd
   renderowania, wąska plakietka czyta się jak decyzja. Ujemny margines
   kasuje padding, więc nic się nie przesuwa względem siatki układu. */
.shift-night .eyebrow,
.shift-day .eyebrow,
.shift-night .case__stepLabel,
.shift-day .step__num,
.shift-day .step__artifactLabel {
  display: inline-block;
  padding-inline: 0.5em;
  margin-inline-start: -0.5em;
}

.shift-night .eyebrow,
.shift-night .case__stepLabel,
.shift-night .status-badge,
.site-footer__line,
.site-footer__craft,
.site-footer__contact {
  background-color: var(--night-950);
}

.shift-day .eyebrow,
.shift-day .step__num,
.shift-day .step__artifact,
.shift-day .step__artifactLabel {
  background-color: var(--paper-050);
}

/* --------------------------------------------------------------------------
   5. Nagłówek strony i przełącznik języka
   -------------------------------------------------------------------------- */

.site-header {
  position: relative;
  z-index: 20;
  background: var(--night-950);
  color: var(--night-text);
  --focus: var(--amber-500);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--sp-4);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  text-decoration: none;
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 112;
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.125rem;
}

/* Wymiary podane też w atrybutach width i height w HTML, żeby przeglądarka
   zarezerwowała miejsce przed pobraniem obrazu. Zero skoków układu. */
.brand__logo {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
}
.brand__logo--sm { width: 26px; height: 26px; flex-basis: 26px; }

.brand__dot { color: var(--night-muted); font-weight: 500; }

.lang {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--night-600);
  border-radius: var(--r-sm);
  background: var(--night-900);
}

.lang__btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  background: transparent;
  color: var(--night-muted);
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  padding: var(--sp-2) var(--sp-3);
  border-radius: 5px;
  transition: color 140ms ease, background-color 140ms ease;
}
.lang__btn:hover { color: var(--night-text); }
.lang__btn.is-active {
  background: var(--night-800);
  color: var(--amber-500);
}

/* --------------------------------------------------------------------------
   6. Typografia współdzielona
   -------------------------------------------------------------------------- */

.eyebrow {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-500);
  margin-bottom: var(--sp-3);
}
.shift-night .eyebrow { color: var(--amber-500); }

.section { padding-block: clamp(4rem, 10vw, 7.5rem); }

.section__head { max-width: 52ch; margin-bottom: var(--sp-7); }

.section__title { font-size: clamp(1.75rem, 1.2rem + 2.6vw, 2.875rem); }

.section__lede {
  margin-top: var(--sp-4);
  color: var(--ink-500);
  max-width: 52ch;
}
.shift-night .section__lede { color: var(--night-muted); }

/* --------------------------------------------------------------------------
   7. Przyciski
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 0.875rem 1.5rem;
  min-height: 48px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-family: var(--font-body);
  font-variation-settings: 'wdth' 105;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: transform 140ms ease, background-color 140ms ease, border-color 140ms ease;
}

.btn--primary {
  background: var(--amber-500);
  color: var(--ink-900);  /* 8.77:1 */
}
.btn--primary:hover { transform: translateY(-1px); background: #FFBB55; }
.btn--primary:active { transform: translateY(0); }

.btn--ghost {
  background: transparent;
  border-color: var(--night-600);
  color: var(--night-text);
}
.btn--ghost:hover { border-color: var(--night-muted); transform: translateY(-1px); }

.btn--submit { width: 100%; }
.btn[aria-disabled="true"], .btn:disabled { opacity: 0.6; cursor: progress; transform: none; }

/* --------------------------------------------------------------------------
   8. Hero
   -------------------------------------------------------------------------- */

.hero {
  padding-block: clamp(3rem, 8vw, 6rem) clamp(4rem, 9vw, 7rem);
  overflow: hidden;
  position: relative;

  /* Dwie warstwy nad siatką: poświata pod tablicą i siatka z .shift-night.
     Poświata jest jedyna na całej stronie i wskazuje punkt, na który ma
     paść wzrok. */
  background-image: var(--glow), var(--grid-night);
  background-size: 880px 700px, var(--grid-size) var(--grid-size);
  background-position: var(--glow-x, 72%) var(--glow-y, 46%), 0 0;
  background-repeat: no-repeat, repeat;
}

@media (max-width: 61.999rem) {
  /* Na wąskim ekranie tablica schodzi pod przyciski, więc poświata idzie
     za nią, a nie zostaje w prawym górnym rogu. */
  .hero { --glow-x: 50%; --glow-y: 76%; }
}

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

.hero__inner {
  display: grid;
  /* Jedna kolumna, ale JAWNIE minmax(0, 1fr). Domyślna ścieżka auto rośnie
     do min-content, więc pojedyncze szerokie dziecko, na przykład widget
     Turnstile o sztywnych 300 px, rozpycha całą stronę w poziomie. */
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 4.5rem);
  align-items: start;
}

@media (min-width: 62rem) {
  .hero__inner {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    align-items: center;
  }
}

.hero__title {
  font-size: clamp(2.125rem, 1.3rem + 3.9vw, 4rem);
  margin-bottom: var(--sp-5);
}

.hero__lead {
  color: var(--night-muted);
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.1875rem);
  max-width: 52ch;
  margin-bottom: var(--sp-6);
}

.hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

/* --------------------------------------------------------------------------
   9. Tablica zmiany. Element sygnaturowy.
   Kolumna statusu ma stałą szerokość liczoną z najdłuższego wariantu
   w obu językach: ZAPLANOWANE (11 znaków) kontra SCHEDULED (9).
   Człon z trackingiem domyka odstęp doklejany po każdym z 11 znaków.
   -------------------------------------------------------------------------- */

.board {
  background: var(--night-900);
  border: 1px solid var(--night-600);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: 0 24px 60px -30px rgba(0, 0, 0, 0.9);
}

.board__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--night-600);
  background: var(--night-950);
}

.board__label {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--night-muted);
}

.board__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.board__clock {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.8125rem;
  color: var(--amber-500);
  /* stała szerokość HH:MM, żeby zegar nie ruszał układu */
  min-width: 5ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Dioda stanu. Jedyny ciągły ruch na całej stronie, świadomie, bo tablica
   ma wyglądać na żywą. Animowana wyłącznie przez opacity. Gaśnie przy
   prefers-reduced-motion (reguła zbiorcza na końcu pliku). */
.led {
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  border-radius: 50%;
  background: var(--night-600);
}

/* zielona, spokojne mrugnięcie: tablica pracuje */
.led--ok,
.led[data-state="ok"] {
  background: var(--ok-500);
  animation: ledBlink 2.4s ease-in-out infinite;
}

/* bursztynowa, szybki puls: sekwencja właśnie się wykonuje */
.led[data-state="running"] {
  background: var(--amber-500);
  animation: ledPulse 0.85s ease-in-out infinite;
}

@keyframes ledBlink {
  0%, 70%, 100% { opacity: 1; }
  80% { opacity: 0.18; }
}

@keyframes ledPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}

.board__list { border-top: 0; }

.board__row { position: relative; }
.board__row + .board__row { border-top: 1px solid rgba(51, 65, 90, 0.55); }

/* Bursztynowy pasek na krawędzi wiersza, zapalany na chwilę w momencie,
   w którym wiersz się pojawia. Wygaszany przez opacity, więc nic się nie
   przelicza w układzie. */
.board__row::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--amber-500);
  opacity: 0;
  transition: opacity 240ms ease;
}
.board__row.is-running::before { opacity: 1; }

.board__link {
  display: grid;
  grid-template-columns:
    5ch
    minmax(0, 1fr)
    calc(11ch + 11 * var(--track-status));
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  font-family: var(--font-data);
  font-weight: 500;
  font-size: var(--fs-data);
  line-height: 1.45;
  text-decoration: none;
  color: var(--night-text);
}

a.board__link { transition: background-color 140ms ease; }
a.board__link:hover { background: var(--night-800); }

.board__time {
  color: var(--night-muted);
  font-variant-numeric: tabular-nums;
}

.board__task { min-width: 0; }

.board__status {
  letter-spacing: var(--track-status);
  text-align: right;
  white-space: nowrap;
}

/* Statusy. Jeden kolor, jedno znaczenie. */
[data-status="done"]      .board__status { color: var(--ok-500); }
[data-status="alert"]     .board__status { color: var(--alert-500); }
[data-status="running"]   .board__status { color: var(--amber-500); }
[data-status="scheduled"] .board__status { color: var(--night-muted); }

.board__foot {
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--night-600);
  background: var(--night-950);
  color: var(--night-muted);
  font-size: 0.875rem;
}

/* Poniżej 560 px wiersz łamie się na dwie linie zamiast ściskać kolumny. */
@media (max-width: 35rem) {
  .board__link {
    grid-template-columns: 5ch minmax(0, 1fr);
    grid-template-areas:
      "time status"
      "task task";
    gap: var(--sp-2) var(--sp-3);
    padding: var(--sp-4);
  }
  .board__time { grid-area: time; }
  .board__status { grid-area: status; justify-self: end; }
  .board__task { grid-area: task; }
  .board__head, .board__foot { padding-inline: var(--sp-4); }
}

/* --------------------------------------------------------------------------
   10. Karty
   -------------------------------------------------------------------------- */

.cards { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-5); }

@media (min-width: 48rem) {
  .cards--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 64rem) {
  .cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.card {
  background: var(--paper-000);
  border: 1px solid var(--paper-200);
  border-radius: var(--r-lg);
  padding: clamp(1.5rem, 3vw, 2rem);
  /* Mikrointerakcja, poniżej 200 ms. Wyłącznie transform, więc nie wywołuje
     przeliczenia układu. Gaszona regułą prefers-reduced-motion na końcu. */
  transition: transform 160ms ease, border-color 160ms ease;
}

@media (hover: hover) {
  .card:hover {
    transform: translateY(-2px);
    border-color: #B9C6D4;
  }
}

.card__title {
  font-size: clamp(1.125rem, 1rem + 0.6vw, 1.375rem);
  margin-bottom: var(--sp-3);
}

.card__body { color: var(--ink-500); }

/* Karty mają teraz po dwa akapity, więc potrzebują między nimi odstępu. */
.card__body + .card__body { margin-top: var(--sp-4); }

.card__idx {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--ink-500);
  margin-bottom: var(--sp-3);
}

/* --------------------------------------------------------------------------
   11. Jak to działa, sekcja przypinana
   -------------------------------------------------------------------------- */

.case-pin {
  padding-block: 0 clamp(1rem, 3vw, 2rem);
  position: relative;
}

.case-pin__inner { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   Ślady połączeń. Tylko hero i przypięty case study, bo tylko tam metafora
   przepływu jest żywa. Prowadzone po marginesach, nigdy za akapitami,
   dlatego znikają poniżej 62rem, gdzie układ schodzi do jednej kolumny
   i marginesów już nie ma.
   -------------------------------------------------------------------------- */

.hero::after,
.case-pin::after,
#co-robie::after,
#jak-pracuje::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  z-index: 0;
  display: none;
}

/* Ślady prowadzą po marginesach kompozycji, a tych poniżej 62rem po prostu
   nie ma, bo układ schodzi do jednej kolumny. Dlatego tam znikają. */
@media (min-width: 62rem) {
  .hero::after,
  .case-pin::after,
  #co-robie::after,
  #jak-pracuje::after { display: block; }
}

.hero::after {
  background-image: var(--trace-a);
  background-position: left bottom;
  opacity: var(--trace-alpha);
}

.case-pin::after {
  background-image: var(--trace-b);
  background-position: right top;
  opacity: var(--trace-alpha);
}

/* Zmiana dzienna. Ten sam rysunek, ale przy niższym kryciu granat czyta się
   jako chłodna szarość, więc nie trzeba osobnego pliku. Ślad siedzi przy
   prawej krawędzi, obok nagłówka sekcji, gdzie kompozycja jest pusta.
   Niżej wchodzi pod karty, a te są kryjące, więc po prostu go zasłaniają. */
#co-robie::after,
#jak-pracuje::after {
  background-image: var(--trace-b);
  background-position: right top;
  opacity: var(--trace-alpha-day);
}

#co-robie,
#jak-pracuje { position: relative; }

#co-robie > .wrap,
#jak-pracuje > .wrap { position: relative; z-index: 1; }

.case-pin__inner {
  display: grid;
  /* Jedna kolumna, ale JAWNIE minmax(0, 1fr). Domyślna ścieżka auto rośnie
     do min-content, więc pojedyncze szerokie dziecko, na przykład widget
     Turnstile o sztywnych 300 px, rozpycha całą stronę w poziomie. */
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}

@media (min-width: 62rem) {
  .case-pin__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.case__steps { display: grid; gap: var(--sp-6); }

.case__stepLabel {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber-500);
  margin-bottom: var(--sp-2);
}

.case__stepTitle { font-size: clamp(1.125rem, 1rem + 0.7vw, 1.5rem); margin-bottom: var(--sp-3); }

.case__stepBody { color: var(--night-muted); max-width: 52ch; }

/* Bez JS wszystkie kroki są w pełni czytelne. Wygaszanie nieaktywnych
   włącza dopiero motion.js przez klasę na <html>. */
/* Wygaszanie KOLOREM, nie kryciem.
   Wcześniej nieaktywne kroki miały opacity 0.32, przez co bursztynowa
   etykieta dawała efektywnie 1.97:1, a Lighthouse słusznie to zgłaszał.
   Przeliczenie pokazało, że nawet opacity 0.75 nie wystarcza, żeby treść
   przeszła 4.5:1, więc krycie odpada jako narzędzie.
   Teraz nieaktywny krok jest wyciszony do --night-muted (6.92:1), a aktywny
   dostaje pełny kontrast i bursztyn. Efekt ten sam, każdy stan czytelny. */
.has-motion .case__stepLabel,
.has-motion .case__stepTitle,
.has-motion .case__stepBody {
  color: var(--night-muted);
  transition: color 280ms ease;
}

.has-motion .case__step.is-active .case__stepLabel { color: var(--amber-500); }
.has-motion .case__step.is-active .case__stepTitle { color: var(--night-text); }
.has-motion .case__step.is-active .case__stepBody { color: var(--night-text); }

/* Stan końcowy sekwencji: wszystko wykonane. Wszystkie kroki wracają do
   pełnego kontrastu, a etykiety przechodzą z bursztynu na zieleń. */
.has-motion #case-flow.is-complete .case__stepTitle,
.has-motion #case-flow.is-complete .case__stepBody { color: var(--night-text); }

.has-motion #case-flow.is-complete .case__stepLabel {
  color: var(--ok-500);
  transition: color 400ms ease;
}

/* --------------------------------------------------------------------------
   12. Dla kogo
   Nacisk na niszę buduję hierarchią, nie ornamentem.
   -------------------------------------------------------------------------- */

.who { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-5); }

/* Trzy karty o równej wadze. Wcześniej turystyka zajmowała dwa wiersze
   i była kotwicą sekcji, przez co całość czytała się jako "robię tylko
   turystykę". Teraz wyróżnia ją sama krawędź i plakietka, więc przewaga
   zostaje, a zakres kompetencji przestaje wyglądać na wąski. */
@media (min-width: 48rem) {
  .who { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 62rem) {
  .who { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.card--featured {
  background: var(--paper-000);
  border-color: var(--paper-200);
  border-left: 4px solid var(--amber-700);  /* nie --amber-500, 3.82:1 zamiast 2.01:1 */
  display: flex;
  flex-direction: column;
}

/* Plakietka niesie teraz zdanie, a nie etykietę, więc schodzi z kroju
   monospace i z wersalików. Język tablicy jest zarezerwowany dla trzech
   miejsc na stronie i to nie jest jedno z nich. */
.card__flag {
  display: inline-block;
  align-self: start;
  font-family: var(--font-body);
  font-variation-settings: 'wdth' 105;
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-900);        /* 8.77:1 na --amber-500 */
  background: var(--amber-500);
  padding: 0.32rem 0.75rem;
  border-radius: 999px;
  margin-bottom: var(--sp-4);
}

.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: auto; padding-top: var(--sp-5); }

.chip {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  color: var(--ink-500);
  border: 1px solid var(--paper-200);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
  background: var(--paper-050);
}

.chips--night { padding-top: 0; }
.chips--night .chip {
  color: var(--night-muted);
  border-color: var(--night-600);
  background: var(--night-950);
}

/* Agencje. Najmniejsza grupa odbiorców, więc dostaje jedno zdanie, ale
   w osobnym pudełku, żeby nie leżało jako doklejony akapit pod kartami.
   Kształt pigułki, biała powierzchnia i bursztynowa kropka odróżniają je
   od kart, nie konkurując z nimi wielkością. */
.agencies {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-6);
  padding: 0.7rem 1.35rem 0.7rem 1.1rem;
  background: var(--paper-000);
  border: 1px solid var(--paper-200);
  border-radius: var(--r-lg);
  font-size: 0.9375rem;
  color: var(--ink-500);
  max-width: 100%;
}

/* Pełna pigułka dopiero wtedy, gdy zdanie mieści się w jednej linii.
   Przy dwóch liniach zaokrąglenie 999px wygląda jak błąd, nie jak kształt. */
@media (min-width: 46rem) {
  .agencies { border-radius: 999px; }
}

.agencies::before {
  content: '';
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  border-radius: 50%;
  background: var(--amber-700);
}

.agencies__label {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 112;
  font-weight: 700;
  color: var(--ink-900);
}

/* --------------------------------------------------------------------------
   13. Jak pracuję
   -------------------------------------------------------------------------- */

.steps { display: grid; gap: 0; max-width: 68ch; }

/* Trzy kolumny: numer, opis, artefakt. Poniżej 900 px artefakt schodzi pod
   opis, ale zostaje wcięty pod nim, a nie pod numerem. */
.step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "num  body"
    ".    artifact";
  gap: var(--sp-2) var(--sp-5);
  padding-block: var(--sp-5);
  border-top: 1px solid var(--paper-200);
}
.step:last-child { border-bottom: 1px solid var(--paper-200); }

.step__num { grid-area: num; }
.step__body { grid-area: body; }
.step__artifact { grid-area: artifact; }

@media (min-width: 56.25rem) {
  .steps { max-width: none; }
  .step {
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 30ch);
    grid-template-areas: "num body artifact";
    align-items: start;
    gap: var(--sp-5);
  }
}

/* Artefakt jest wyciszony celowo. To nie jest druga treść, tylko dowód, że
   po każdym kroku zostaje coś namacalnego. */
.step__artifact {
  color: var(--ink-500);
  font-size: 0.875rem;
  line-height: 1.5;
  padding-top: var(--sp-1);
}

.step__artifactLabel {
  display: block;
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber-800);  /* nie --amber-700, ten jest za jasny na tekst */
  margin-bottom: var(--sp-1);
}

.step__num {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  color: var(--ink-500);
  padding-top: 0.35rem;
  min-width: 2ch;
}

.step__title { font-size: 1.125rem; margin-bottom: var(--sp-2); }
.step__body p { color: var(--ink-500); }

/* --------------------------------------------------------------------------
   13a. Pas z odnośnikiem do formularza

   Pomiar pokazał, że na telefonie między przyciskiem w hero a formularzem
   było prawie 15 ekranów przewijania bez ani jednego wejścia do formularza.
   Trzy pasy dzielą tę drogę na cztery odcinki po mniej więcej cztery ekrany.
   Każdy niesie inny argument, więc nie czytają się jak powtórka.

   Celowo NIE jest to belka przyklejona do dołu ekranu. Brief zakazywał
   wyskakujących okien i nachalnych elementów, a przyklejona belka należy do
   tej samej rodziny. Pas siedzi w treści i pojawia się tam, gdzie czytający
   właśnie dostał powód, żeby napisać.
   -------------------------------------------------------------------------- */

.cta-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-6);
  margin-top: var(--sp-7);
  padding: var(--sp-5) clamp(1.25rem, 3vw, 2rem);
  border-radius: var(--r-lg);
  background: var(--paper-000);
  border: 1px solid var(--paper-200);
}

.shift-night .cta-band {
  background: var(--night-900);
  border-color: var(--night-600);
}

.cta-band__text {
  flex: 1 1 22rem;
  min-width: 0;
  margin: 0;
  color: var(--ink-500);
  font-size: 0.9375rem;
}

.shift-night .cta-band__text { color: var(--night-muted); }

.cta-band .btn { flex: 0 0 auto; }

/* Na wąskim ekranie przycisk idzie pod tekst i bierze pełną szerokość,
   bo to jedyny cel tego pasa i nie ma powodu, żeby był mały. */
@media (max-width: 34rem) {
  .cta-band { flex-direction: column; align-items: stretch; }
  /* Bez tego flex-basis 22rem z układu poziomego staje się bazową
     WYSOKOŚCIĄ tekstu i rozpycha pas o ponad 300 px pustki. */
  .cta-band__text { flex: 0 0 auto; }
  .cta-band .btn { width: 100%; }
}

/* --------------------------------------------------------------------------
   13b. Wizualizacje, ręcznie pisany inline SVG
   Zero bibliotek wykresów. Kolory przełączają się razem ze zmianą, bo te
   same kształty muszą działać i na granacie, i na papierze.
   -------------------------------------------------------------------------- */

.viz {
  display: block;
  width: 100%;
  height: auto;
  margin-top: var(--sp-5);

  /* zmiana dzienna, czyli biała karta */
  --viz-accent: var(--amber-700);   /* 3.82:1 na białym */
  --viz-alert: var(--alert-700);    /* 6.28:1 */
  --viz-bar: var(--night-600);      /* 10.27:1 */
  --viz-line: var(--paper-200);
  --viz-text: var(--ink-500);
}

.shift-night .viz {
  --viz-accent: var(--amber-500);
  --viz-alert: var(--alert-500);
  --viz-bar: var(--night-600);
  --viz-line: var(--night-600);
  --viz-text: var(--night-muted);
}

.viz__label text,
.viz__tick {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 10px;
  fill: var(--viz-text);
}

/* --- schemat workflow --- */
.viz__wire path { stroke: var(--viz-line); }
.viz__node rect {
  fill: none;
  stroke: var(--viz-line);
  stroke-width: 1.5;
}
.viz--flow .viz__label text { font-size: 11px; }

/* --- sparkline --- */
.viz__threshold {
  stroke: var(--viz-line);
  stroke-width: 1.5;
  stroke-dasharray: 4 4;
}
.viz__line {
  stroke: var(--viz-bar);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.viz__alert circle { fill: var(--viz-alert); }

/* --- słupki --- */
.viz__bars rect { fill: var(--viz-bar); }
.viz__bars .viz__bar--own { fill: var(--viz-accent); }

/* --- podpis pod wykresem --- */
.viz__caption {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-top: var(--sp-3);
  font-size: 0.8125rem;
  color: var(--ink-500);
}
.shift-night .viz__caption { color: var(--night-muted); }

.viz__num {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: var(--track-display);
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}
.shift-night .viz__num { color: var(--night-text); }

/* --------------------------------------------------------------------------
   14. O mnie
   -------------------------------------------------------------------------- */

.about {
  display: grid;
  /* Jedna kolumna, ale JAWNIE minmax(0, 1fr). Domyślna ścieżka auto rośnie
     do min-content, więc pojedyncze szerokie dziecko, na przykład widget
     Turnstile o sztywnych 300 px, rozpycha całą stronę w poziomie. */
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 4rem);
  align-items: start;
}

@media (min-width: 55rem) {
  .about { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
  /* Sekcja ma trzy dzieci, a kolumny dwie, więc bez jawnego rozstawienia
     drugi panel spadał do wiersza pod tekstem zamiast pod pierwszy panel.
     Tekst rozciąga się na oba wiersze, panele siedzą jeden pod drugim. */
  .about__copy { grid-column: 1; grid-row: 1 / span 2; }
  .about__side { grid-column: 2; }
}

/* Portret w medalionie plus plakietka dostępności. */
.about__person {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin-bottom: var(--sp-6);
}

.avatar {
  display: block;
  flex: 0 0 auto;
  width: 140px;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  /* Pierścień rysowany cieniem, a nie ramką, żeby nie wchodził w wymiary
     pudełka i nie psuł idealnego koła. */
  box-shadow: 0 0 0 2px var(--night-600);
}

.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

@media (min-width: 55rem) {
  .avatar { width: 200px; }
}

/* Trzecie i ostatnie użycie języka tablicy na stronie. Nigdzie więcej. */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  color: var(--ok-500);
  border: 1px solid var(--night-600);
  border-radius: 999px;
  padding: 0.35rem 0.75rem;
  white-space: nowrap;
}

.status-badge__dot {
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  border-radius: 50%;
  background: var(--ok-500);
}

.about__body {
  color: var(--night-muted);
  max-width: 56ch;
  margin-top: var(--sp-4);
}

.about__side {
  border: 1px solid var(--night-600);
  border-radius: var(--r-lg);
  background: var(--night-900);
  padding: clamp(1.5rem, 3vw, 2rem);
}

.about__sideLabel {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--night-muted);
  margin-bottom: var(--sp-4);
}

/* Druga kolumna w „O mnie" kończyła się na panelu stacku, a lewa leciała
   dalej, przez co zostawało około 500 px pustki. Ten panel ją wypełnia
   i przy okazji odpowiada na pytania, które i tak padają przed napisaniem. */
.about__side + .about__side { margin-top: var(--sp-5); }

.facts { display: grid; gap: var(--sp-4); }

.facts dt {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber-500);
  margin-bottom: var(--sp-1);
}

.facts dd {
  color: var(--night-text);
  font-size: 0.9375rem;
  line-height: 1.5;
}

.about__sideNote {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--night-600);
  color: var(--night-muted);
  font-size: 0.9375rem;
}

/* --------------------------------------------------------------------------
   15. Kontakt i formularz
   -------------------------------------------------------------------------- */

.contact {
  display: grid;
  /* Jedna kolumna, ale JAWNIE minmax(0, 1fr). Domyślna ścieżka auto rośnie
     do min-content, więc pojedyncze szerokie dziecko, na przykład widget
     Turnstile o sztywnych 300 px, rozpycha całą stronę w poziomie. */
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 4rem);
  align-items: start;
}

@media (min-width: 55rem) {
  .contact { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.contact__lead { color: var(--ink-500); margin-top: var(--sp-4); max-width: 46ch; }

.contact__proof {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-3);
  align-items: start;
  margin-top: var(--sp-6);
  padding: var(--sp-4) var(--sp-5);
  background: var(--paper-000);
  border: 1px solid var(--paper-200);
  border-radius: var(--r-md);
  font-size: 0.9375rem;
  color: var(--ink-500);
}

.contact__proofDot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ok-700);
  margin-top: 0.45rem;
}

.contact__direct {
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--paper-200);
}

.contact__directLabel {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-500);
  margin-bottom: var(--sp-3);
}

.contact__directLinks { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5); }

.contact__directLinks a {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.9375rem;
  color: var(--ink-900);
  text-decoration: none;
  border-bottom: 2px solid var(--amber-500);
  padding-bottom: 1px;
}
.contact__directLinks a:hover { background: rgba(240, 169, 59, 0.18); }

.form {
  background: var(--paper-000);
  border: 1px solid var(--paper-200);
  border-radius: var(--r-lg);
  padding: clamp(1.5rem, 4vw, 2.25rem);
  display: grid;
  gap: var(--sp-5);
}

.field { display: grid; gap: var(--sp-2); }

.field__label {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-500);
}

.field__input {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink-900);
  background: var(--paper-050);
  border: 1px solid var(--paper-200);
  border-radius: var(--r-sm);
  padding: 0.75rem 0.875rem;
  min-height: 48px;
  width: 100%;
  transition: border-color 140ms ease, background-color 140ms ease;
}
.field__input:hover { border-color: #B9C6D4; }
.field__input:focus { background: var(--paper-000); border-color: var(--ink-500); }

.field__input--area { resize: vertical; min-height: 8.5rem; }

.field__input[aria-invalid="true"] { border-color: var(--alert-700); }

.field__error {
  font-size: 0.875rem;
  color: var(--alert-700);   /* 5.53:1 na jasnym */
  font-weight: 500;
}

/* Honeypot. Poza ekranem, poza tabulacją, poza czytnikiem ekranu. */
.hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  pointer-events: none;
}

/* Rezerwacja miejsca pod widget Turnstile, żeby jego pojawienie się nie
   przesunęło przycisku. Zmierzone w przeglądarce: widget zajmuje 73 px,
   nie deklarowane w dokumentacji 65 px. Rezerwujemy 78 px z zapasem, bo
   nadmiar to kilka pikseli światła, a niedomiar to skok układu. */
.form__turnstile { min-height: 78px; }

/* Widget w wariancie normal ma sztywne 300 px szerokości i nie da się go
   zwęzić. Wnętrze formularza to szerokość okna minus dwa marginesy .wrap
   (2 x 20 px) minus dwa paddingi formularza (2 x 24 px), czyli okno minus
   88 px. Trzysta pikseli mieści się dopiero od 388 px okna.
   Poniżej tego progu przechodzimy na wariant compact, 150 x 140 px.
   TEN SAM PRÓG jest odpytywany w main.js przez matchMedia, więc rezerwacja
   miejsca i faktycznie wyrenderowany rozmiar nie mogą się rozjechać. */
@media (max-width: 24.25rem) {
  .form__turnstile { min-height: 150px; }
}

.form__status {
  font-size: 0.9375rem;
  font-weight: 500;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  border: 1px solid transparent;
}
.form__status[data-state="error"] {
  color: var(--alert-700);
  background: #FCEEF1;
  border-color: #F0C6CE;
}
.form__status[data-state="success"] {
  color: var(--ok-700);
  background: #E7F6EF;
  border-color: #B9E2CF;
}

.form__note { font-size: 0.8125rem; color: var(--ink-500); }

/* --------------------------------------------------------------------------
   16. Stopka
   -------------------------------------------------------------------------- */

.site-footer { padding-block: var(--sp-7); }

.site-footer__inner { display: grid; gap: var(--sp-2); }

.site-footer__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 112;
  font-weight: 700;
  font-size: 1.125rem;
}

.site-footer__line { color: var(--night-muted); font-size: 0.9375rem; }

.site-footer__contact {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  margin-top: var(--sp-2);
}

.site-footer__contact a {
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.875rem;
  color: var(--amber-500);
  text-decoration: none;
  border-bottom: 1px solid rgba(240, 169, 59, 0.4);
}
.site-footer__contact a:hover { border-bottom-color: var(--amber-500); }

.site-footer__craft {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--night-600);
  font-family: var(--font-data);
  font-weight: 500;
  font-size: 0.75rem;
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: var(--night-muted);
  max-width: 46ch;
}

/* --------------------------------------------------------------------------
   17. Ruch
   .reveal nie ma tu żadnego stanu początkowego. Treść jest widoczna,
   zanim GSAP w ogóle się załaduje. Stan startowy animacji ustawia
   dopiero motion.js.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* Kroki nie są już wygaszane kryciem, tylko kolorem, więc przy
     wyłączonym ruchu wystarczy przywrócić im pełny kontrast. */
  .has-motion .case__stepLabel { color: var(--amber-500); }
  .has-motion .case__stepTitle,
  .has-motion .case__stepBody { color: var(--night-text); }
}
