*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ── Paleta ─────────────────────────────────────────────────────────────────
   Ciepły świat serwisu zostaje bez zmian: brąz, krem, mech, papier. Rusza się
   jedna rzecz — piaskowy nabiera nasycenia i dostaje jedno konkretne zadanie.

   Powód jest w tym, że `--moss` niósł wcześniej trzy znaczenia naraz:
   „naciśnij to" (btn-primary), „są miejsca" (badge.open, slots-fill) i
   „potwierdzone". Kolor, który znaczy trzy rzeczy, nie znaczy żadnej. Teraz
   akcję niesie bursztyn, a zieleń została wyłącznie stanem.

   Bursztyn jest z tej samej ćwiartki koła co brąz — i to jest zamierzone.
   Rozdziela je nie odcień, tylko nasycenie i jasność: na `--bark` bursztyn
   daje 7,4:1, czyli świeci mocniej niż dawny piaskowy, nie wychodząc z
   ciepłej rodziny, którą serwis ma własną. */
:root {
  --cream: #f7f3ec;
  --bark:  #2c1f10;
  --moss:  #3d5a3e;
  --sage:  #7a9e7e;
  /* Dawniej #c9a96e. Ta sama rola dekoracyjna co wcześniej — znak marki, łapa,
     akcent w nagłówku, obwódka najbliższej rezerwacji — plus nowa: wypełnienie
     akcji głównej. Wypełnień jest celowo mało; w większości miejsc bursztyn
     jest pismem albo kreską, nie plamą. */
  --sand:  #e8a33d;
  --rust:  #b5451b;
  --paper: #fdfaf5;
  /* Darkened from #8a7f74 (~3.5:1 on --cream, fails WCAG AA) to meet 4.5:1. */
  --muted: #6d6255;
  --radius: 12px;

  /* Bursztyn przyciśnięty — :hover akcji głównej. */
  --sand-press: #cf8b28;
  /* Tekst kładziony na bursztynie. Wartość równa `--bark`, ale nazwa mówi o
     roli: przy zmianie akcentu to ta zmienna pójdzie za nim. Biel odpada —
     na bursztynie daje 2,2:1. */
  --accent-ink: #2c1f10;
  /* Bursztyn na tyle ciemny, żeby unieść małe pismo na jasnym tle. Sam
     `--sand` na papierze karty to 2,1:1; ten daje 6,4:1. */
  --sand-ink: #8a5109;

  /* Kolor odnośnika w tekście. Był nim `--moss`, i to w dwudziestu miejscach
     wpisanych wprost w szablony — czyli zieleń była faktycznym kolorem linków
     całego serwisu. Po rozdzieleniu ról zieleń znaczy „są miejsca", a link
     jest akcją, więc idzie za akcentem.

     Wartość równa `--sand-ink`, ale nazwa własna: to dwie różne role i przy
     zmianie akcentu każda może pójść gdzie indziej.

     Podkreślenie zostaje pod spodem i nie jest ozdobą — barwa nie może być
     jedynym sygnałem, że coś jest odnośnikiem (WCAG 1.4.1). */
  --link: #8a5109;

  /* Ledwie widoczne przyciemnienie, kładzione na cudzym tle.
     Przyciski, które mają odcinać się od karty, ale nie być plamą koloru —
     wygaszone „Zarezerwowane" i najechany „Szczegóły" — miały dotąd wpisane
     `#faf8f4`, czyli konkretny odcień papieru minus odrobina. Działało, dopóki
     karta miała jedno tło. `.card.moja` dołożyła zielone, panel uczestnika
     kremowe, i te przyciski zostały przy swojej bieli — jasną łatą na zielonej
     karcie, dokładnie tam, gdzie napis mówi „to twoje zajęcia".

     Wartość dobrana tak, by na papierze wyszło to samo co dotąd (#faf7f2 wobec
     #faf8f4), a że jest relacją, a nie kolorem, tak samo zachowuje się na
     każdym innym tle. Brąz, nie czerń: przygaszenie ma zostać w ciepłej
     rodzinie serwisu. */
  --przygaszenie: rgba(44, 31, 16, .015);

  /* Miękka zieleń potwierdzenia. Osobna od `--moss`, bo `--moss` bywa
     wypełnieniem pod białym tekstem, a to jest tło pod ciemnym. */
  --potwierdzenie-tlo:   #e7f0e8;
  --potwierdzenie-tekst: #2c5433;
  --potwierdzenie-linia: #c8ddca;

  /* Krój danych. Daty, godziny, ceny i liczniki miejsc stały dotąd w DM Sans
     z proporcjonalnymi cyframi, więc w kolumnie kart nic się nie wyrównywało:
     „03.09.2026" i „11.09.2026" miały różną szerokość. */
  --font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', monospace;
}

/* Wszystko, co jest liczbą, datą albo godziną. `tabular-nums` dokłada się do
   samego mono: stała szerokość znaku to jedno, wyrównany rysunek jedynki i
   siódemki to drugie. */
.dane {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}

/* Hidden until keyboard-focused; lets keyboard/screen-reader users bypass the nav. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--bark);
  color: var(--cream);
  padding: .75rem 1.25rem;
  z-index: 1000;
  border-radius: 0 0 8px 0;
  text-decoration: none;
}
.skip-link:focus {
  left: 0;
}

/* Stronger, consistent focus ring for keyboard users (WCAG 2.4.7).

   Bursztyn, nie zieleń — ostatni element sterujący, który jeszcze świecił
   kolorem stanu. Ciemny wariant, bo wskaźnik skupienia musi mieć 3:1 wobec
   sąsiadującego tła (WCAG 1.4.11): jasny `--sand` na kremowym ma 2,1:1 i by
   nie przeszedł, `--sand-ink` ma 6,2:1. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 3px solid var(--sand-ink);
  outline-offset: 2px;
}

/* Na ciemnym tle ta sama obwódka zawraca na jasny bursztyn. Ciemny ma na
   `--bark` tylko 2,5:1 — czyli obwódka, której nie widać dokładnie tam, gdzie
   jest jedynym sygnałem, że coś jest zaznaczone. Jasny daje 7,4:1.

   To nie jest regresja wprowadzona razem z bursztynem: poprzednia zielona
   obwódka miała na tym samym tle 2,2:1, więc problem był i przedtem — tyle że
   przy jednym kolorze na wszystko nie dało się go zauważyć. */
nav :focus-visible,
.hero :focus-visible {
  outline-color: var(--sand);
}

body {
  font-family: 'DM Sans', sans-serif;
  background: var(--cream);
  color: var(--bark);
  /* Column flex with a growing <main> keeps the footer at the bottom of the
     window on short pages and directly under the content on long ones. Not
     position:fixed — that would leave it covering the page while scrolling,
     and what it carries (the notice link, art. 16(1) DSA) needs to be
     reachable, not permanently in the way.

     dvh second so it wins where supported: on mobile 100vh is the viewport
     with the browser chrome hidden, which leaves the page a little taller
     than the screen and scrolling by exactly the height of the toolbar. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* Takes the slack, so the footer is pushed down rather than floated up. */
body > main { flex: 1 0 auto; }

nav {
  background: var(--bark);
  padding: .75rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .5rem 1rem;
  min-height: 64px;
  position: sticky;
  top: 0;
  z-index: 100;
}
.nav-brand {
  font-family: 'Fraunces', serif;
  font-size: 1.5rem;
  color: var(--cream);
  text-decoration: none;
  letter-spacing: -0.02em;
}
.nav-brand span { color: var(--sand); }
.paw-icon { color: var(--sand); vertical-align: -3px; }

/* Łapa w nagłówku strony — rozmiar oddany krojowi zamiast pikselom.

   Proporcja jest przepisana z paska nawigacji, gdzie łapa ma 24px przy foncie
   1.5rem, czyli równo 1em. Na nagłówkach stała 20px przy foncie 2rem i wypadała
   o połowę za mała względem liter, obok których stoi.

   Musi to być 1em, a nie wyliczone 32px: `.page-title` schodzi pod media query
   do 1.5rem, więc sztywna wartość byłaby na telefonie za duża dokładnie o tę
   różnicę. Tak łapa trzyma jedną proporcję na każdej szerokości i przy każdym
   nagłówku, w którym ktoś jej użyje.

   Wcięcie też w em, z tego samego powodu: -3px przy 24px to -0.125em. */
.paw-icon.paw-tytul {
  width: 1em;
  height: 1em;
  vertical-align: -.125em;
}
/* Inline Tabler line icons used in place of emoji in session/booking metadata. */
.ti-icon { vertical-align: -.18em; flex-shrink: 0; }
.nav-links {
  display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; row-gap: .5rem;
  flex: 1 1 auto;
  /* Extra breathing room after the brand so a single link (e.g. the
     trainer's "Panel trenera") doesn't read as an extension of the logo. */
  margin-left: 1.5rem;
  padding-left: 1.5rem;
  border-left: 1px solid rgba(247,243,236,.15);
}
.nav-links a {
  color: var(--cream);
  text-decoration: none;
  font-size: 1rem;
  font-weight: 500;
  opacity: 0.88;
  transition: opacity .2s, color .2s;
  white-space: nowrap;
}
/* Shift to the brand's accent color on hover/focus — the same signal used
   for every other interactive control — instead of just brightening, which
   read the same as a hover on plain (non-interactive) brand text. */
.nav-links a:hover, .nav-links a:focus-visible { opacity: 1; color: var(--sand); }
/* Which panel you are on. Underline rather than a colour swap, so it stays
   distinguishable from the hover state a pointer is producing at the time. */
.nav-links a.active { opacity: 1; border-bottom: 2px solid var(--sand); padding-bottom: .15rem; }
/* Count of what needs a decision, next to its own link. Only ever shown when
   non-zero: a permanent "0" trains the eye to skip the spot. */
.nav-count { background: var(--rust); color: #fff; font-size: .72rem; border-radius: 999px; padding: .05rem .4rem; margin-left: .3rem; vertical-align: .05em; }
.nav-links .btn-nav {
  background: var(--sand);
  color: var(--bark);
  padding: .4rem 1rem;
  border-radius: 20px;
  opacity: 1;
  font-weight: 500;
}
/* Przyciśnięcie przyciemnia bursztyn, nie zmienia go w zieleń.
   Wcześniej najechanie przerzucało pigułkę z bursztynu na ciemny mech — skok
   przez pół koła barw, przy którym przycisk na moment przestawał wyglądać na
   ten sam element. Do tego zieleń znaczy teraz „są miejsca", więc najechanie
   na rejestrację zapalało kolor mówiący o czymś zupełnie innym.
   Brąz na przyciśniętym bursztynie: 5,6:1. */
.nav-links .btn-nav:hover { background: var(--sand-press); color: var(--bark); }

.avatar-menu { position: relative; flex-shrink: 0; }
.avatar-menu > summary {
  list-style: none;
  cursor: pointer;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--sand);
  color: var(--bark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: .9rem;
  overflow: hidden;
  transition: box-shadow .15s;
}
.avatar-menu > summary::-webkit-details-marker,
.avatar-menu > summary::marker { display: none; content: ""; }
.avatar-menu > summary:hover,
.avatar-menu[open] > summary { box-shadow: 0 0 0 3px rgba(232,163,61,.45); }
.avatar-menu img.avatar-photo { width: 100%; height: 100%; object-fit: cover; }
.avatar-menu-dropdown {
  position: absolute;
  right: 0;
  top: calc(100% + .6rem);
  min-width: 170px;
  background: var(--paper);
  border: 1px solid #e8e0d5;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(44,31,16,.18);
  padding: .4rem;
  display: flex;
  flex-direction: column;
  z-index: 200;
}
nav .avatar-menu-dropdown a {
  color: var(--bark);
  opacity: 1;
  padding: .55rem .75rem;
  border-radius: 6px;
  font-weight: 400;
}
nav .avatar-menu-dropdown a:hover { background: var(--cream); }

.container { max-width: 1100px; margin: 0 auto; padding: 2rem 1.5rem; }
.page-title {
  font-family: 'Fraunces', serif;
  font-size: 2rem;
  margin-bottom: 1.5rem;
}

/* Nagłówek, który ogłasza wynik operacji, a nie nazywa stronę. Dwa warianty,
   bo są tylko dwa wyniki: rezerwacja doszła do skutku albo została odwołana.

   Kolor niesie tu stan, zgodnie z podziałem ustalonym na górze pliku —
   bursztyn zostaje akcją, więc nagłówek pomalowany na akcję czytałby się jak
   trzeci przycisk na stronie, na której są już dwa.

   Zieleń jest ta sama, co na pigułce `.badge.booked`, a nie `--moss`. Obie
   przechodzą kontrast na papierze karty z zapasem (7,4:1 i 8,3:1), więc nie
   chodzi o czytelność — chodzi o to, żeby znacznik „Zarezerwowane" na karcie
   zajęć i strona potwierdzenia mówiły o tym samym jednym kolorem. Dotąd
   `--potwierdzenie-tekst` żył w całym serwisie w jednym miejscu, i akurat nie
   na stronie, która jest potwierdzeniem. */
.page-title.potwierdzone { color: var(--potwierdzenie-tekst); }
.page-title.anulowane    { color: var(--rust); }

.card {
  background: var(--paper);
  border-radius: var(--radius);
  padding: 1.5rem;
  border: 1px solid #e8e0d5;
  transition: box-shadow .2s, transform .2s;
  display: flex;
  flex-direction: column;
  height: 100%;
}
.card:hover { box-shadow: 0 4px 20px rgba(44,31,16,.08); transform: translateY(-2px); }
.form-card.card:hover { box-shadow: none; transform: none; }
/* Kolumny rozciągają się do krawędzi kontenera, zamiast być zamknięte na
   400px i wyśrodkowane. Przy zamknięciu siatka nigdy nie dotykała krawędzi —
   przy szerokości 1052px karty zaczynały się 116px na prawo od nagłówka i
   paska filtrów, więc sterowanie wisiało obok listy, a nie nad nią.

   `auto-fill`, nie `auto-fit`: przy jednym wyniku auto-fit zwija puste tory i
   rozciąga samotną kartę na całą szerokość, co wygląda jak inny komponent, a
   nie jak lista o jednym elemencie. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 1.25rem;
}

.badge {
  display: inline-block;
  font-size: .75rem;
  font-weight: 500;
  padding: .2rem .65rem;
  border-radius: 20px;
  /* Znacznik ma zostać pigułką. Złamany na dwa wiersze w wąskiej kolumnie
     „Status" robił się owalną plamą — promień 20px jest większy niż połowa
     wysokości jednego wiersza, więc przy dwóch wierszach zaokrąglenie
     przestaje czytać się jako pigułka i zaczyna jako kleks. Niech raczej
     kolumna się poszerzy. */
  white-space: nowrap;
  /* --sage default replaced with --moss: white-on-sage is ~2.9:1 (fails WCAG AA). */
  background: var(--moss);
  color: #fff;
}
.badge.full  { background: var(--rust); }
.badge.open  { background: var(--moss); }
/* „Zarezerwowane" zeszło z wypełnienia na miękką pigułkę, bo bursztyn znaczy
   teraz „naciśnij to" i pełny bursztynowy znacznik czytałby się na karcie jako
   drugi przycisk. Podział wychodzi przy okazji sensowniejszy niż sam kolor:
   pełne znaczniki mówią o zajęciach (ile miejsc, czy są), miękki mówi o tobie
   (masz tu rezerwację). */
.badge.booked {
  background: var(--potwierdzenie-tlo);
  color: var(--potwierdzenie-tekst);
  box-shadow: inset 0 0 0 1px var(--potwierdzenie-linia);
}

.btn {
  display: inline-block;
  padding: .6rem 1.4rem;
  border-radius: 8px;
  font-size: .9rem;
  font-weight: 500;
  cursor: pointer;
  border: none;
  text-decoration: none;
  /* <button> centres its label on its own; <a class="btn"> inherits the
     alignment of whatever it sits in. Invisible while a button is only as wide
     as its text, and obvious the moment one is stretched to full width — which
     is how a link ended up reading left while the real button above it read
     centred, in the same card. Set here rather than on that one link, so the
     next full-width link-button does not repeat it. */
  text-align: center;
  transition: background .2s, transform .15s;
}
.btn:hover { transform: translateY(-1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

/* Jedna akcja główna na ekran, w kolorze marki. Ciemne pismo, nie białe:
   biel na bursztynie daje 2,2:1, brąz 7,4:1.

   Waga zostaje przy 500, czyli tej co reszta przycisków. Wersja z 600 była
   błędem: DM Sans jest wczytany w wagach 300/400/500, więc przeglądarka nie
   miała rysunku sześćsetki i pogrubiała pięćsetkę syntetycznie — rozlewając
   kontur zamiast go zagęścić. Na szerokim przycisku było to widać wprost.
   Prawdziwej sześćsetki nie dociągam: to kolejny plik do pobrania przed
   pierwszym rysowaniem strony, a ciemne pismo na bursztynie i tak ma 7,4:1. */
.btn-primary { background: var(--sand); color: var(--accent-ink); }
.btn-primary:hover { background: var(--sand-press); }

/* Obrys zamiast wypełnienia. Wcześniej oba warianty były pełne — piaskowe
   „Szczegóły" i zielone „Zarezerwuj" stały na karcie z równą siłą, więc nie
   było widać, co jest na niej głównym ruchem. Teraz widać: wypełnione jest
   dokładnie jedno pole.

   Obrys kładziony wewnętrznym cieniem, nie `border` — `.btn` ma
   `border: none` i wysokość z paddingu, a dołożona krawędź powiększyłaby
   przycisk o 3px i rozjechała go w rzędzie z sąsiadem. */
.btn-secondary {
  background: transparent;
  color: var(--bark);
  box-shadow: inset 0 0 0 1.5px #ddd6cc;
}
.btn-secondary:hover { background: var(--przygaszenie); box-shadow: inset 0 0 0 1.5px var(--sand); }

/* Ten sam przycisk na ciemnym nagłówku. Obrys musi zawrócić na jasny —
   wersja wyżej rysowałaby na brązie kreskę prawie niewidoczną, a pismo w
   kolorze `--bark` zniknęłoby w tle zupełnie. */
.hero .btn-secondary {
  color: var(--cream);
  box-shadow: inset 0 0 0 1.5px rgba(247, 243, 236, .35);
}
.hero .btn-secondary:hover {
  background: rgba(247, 243, 236, .08);
  box-shadow: inset 0 0 0 1.5px var(--sand);
  color: var(--sand);
}

.btn-danger { background: #fce8e3; color: var(--rust); }
.btn-danger:hover { background: var(--rust); color: #fff; }
.btn-sm { padding: .35rem .85rem; font-size: .82rem; }

/* Przycisk, którego nie da się nacisnąć — „Brak miejsc", „Zarezerwowane",
   „Zapisy wstrzymane".

   Wcześniej robiło to inline'owe `opacity: .4` na samym tekście, co dawało
   około 2,3:1 wobec tła karty, czyli poniżej progu czytelności. Nie miało to
   znaczenia, dopóki tę samą treść powtarzał znacznik w rogu — ale teraz to
   jedyne miejsce, w którym ten stan pada, więc musi być czytelny: `--muted`
   na tym tle daje 5,5:1.

   Wygaszony, ale nie przezroczysty: ma wyglądać na wyłączony, nie na
   wczytujący się. */
.btn-nieaktywny {
  background: var(--przygaszenie);
  color: var(--muted);
  box-shadow: inset 0 0 0 1.5px #e8e0d5;
  cursor: not-allowed;
}
/* `.btn:hover` podnosi każdy przycisk o piksel — ten ma stać. */
.btn-nieaktywny:hover { transform: none; background: var(--przygaszenie); }

/* Nawigacja do miejsca zajęć. inline-flex zamiast inline-block, żeby ikona
   stała równo z tekstem zamiast siadać na linii bazowej. */
.btn-nawiguj { display: inline-flex; align-items: center; gap: .35rem; }

.form-card { max-width: 520px; margin: 2rem auto; }
.confirm-wrap { max-width: 520px; margin: 3rem auto; text-align: center; }
.form-group { margin-bottom: 1.25rem; }
/* Odstęp nad nagłówkiem znacznie większy niż pod nim (40 vs 12 px), żeby prawo
   bliskości związało go z sekcją, którą otwiera, a nie z poprzednią. Przy
   poprzednich 36 vs 24 stosunek wynosił 1,5:1 i formularz czytał się jak
   płaska lista pól z pogrubieniami. Pod nagłówkiem jest też ciaśniej niż
   między polami (20 px) — nagłówek należy do swojej grupy. */
.form-section-title {
  font-family: 'Fraunces', serif;
  font-size: 1.05rem;
  /* 700, nie 600: Fraunces jest wczytany w wagach 400 i 700, więc szescsetkę
     przeglądarka pogrubiała syntetycznie — rozlewając kontur zamiast go
     zagęścić. */
  font-weight: 700;
  color: var(--bark);
  margin: 2.5rem 0 .75rem;
}
.form-section-title:first-child { margin-top: 0; }
/* Bez tego margin-top wiersza wygrywa przy zwijaniu marginesów i odstęp pod
   nagłówkiem zależy od tego, co akurat stoi niżej: 24 px przed wierszem
   dwukolumnowym, 18 przed zwykłym polem. Ma wynikać z reguły, nie z przypadku. */
.form-section-title + .form-row-2col { margin-top: 0; }
.form-actions { display: flex; gap: 1rem; margin-top: 1.5rem; }

.toggle-switch {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 26px;
  flex-shrink: 0;
}
.toggle-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}
.toggle-switch .toggle-slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: #ddd6cc;
  border-radius: 999px;
  transition: background-color .2s;
}
.toggle-switch .toggle-slider::before {
  content: "";
  position: absolute;
  width: 20px;
  height: 20px;
  left: 3px;
  top: 3px;
  background: #fff;
  border-radius: 50%;
  transition: transform .2s;
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
.toggle-switch input:checked + .toggle-slider {
  background: var(--moss);
}
.toggle-switch input:checked + .toggle-slider::before {
  transform: translateX(18px);
}
.toggle-switch input:focus-visible + .toggle-slider {
  outline: 3px solid var(--sand-ink);
  outline-offset: 2px;
}
.toggle-switch input:disabled + .toggle-slider {
  opacity: .6;
  cursor: not-allowed;
}
.form-row-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-top: 1.5rem; }

/* Label/value pairs in the privacy notice. A description list rather than a
   table, because these are not tabular data — which also keeps them clear of
   the dark `th` styling meant for the booking and dashboard tables. */
.notice-list { margin: 0; }
.notice-list dt {
  font-size: .85rem;
  font-weight: 600;
  color: var(--bark);
  padding-top: 1.1rem;
  border-top: 1px solid #e8e0d5;
}
.notice-list dt:first-child { padding-top: 0; border-top: none; }
.notice-list dd {
  margin: .4rem 0 1.1rem;
  font-size: .88rem;
  line-height: 1.6;
  color: var(--bark);
}
.notice-list dd:last-child { margin-bottom: 0; }
@media (min-width: 720px) {
  /* Labels beside their text once there is room for both to breathe. */
  .notice-list { display: grid; grid-template-columns: 230px minmax(0, 1fr); column-gap: 1.5rem; }
  .notice-list dt { grid-column: 1; }
  .notice-list dd { grid-column: 2; margin: 0 0 1.1rem; padding-top: 1.1rem; border-top: 1px solid #e8e0d5; }
  .notice-list dd:first-of-type { padding-top: 0; border-top: none; }
}

/* Trainer profile: account settings beside the business identity, its rules
   and its documents. One column below 900px — side by side is only an
   improvement while both columns are wide enough to read comfortably. */
.profile-grid { display: grid; gap: 1.5rem; align-items: start; }
.profile-column { display: flex; flex-direction: column; gap: 1.5rem; }
@media (min-width: 900px) {
  .profile-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
@media (max-width: 640px) {
  /* Paired fields stop pairing before they get too narrow to type into. */
  .form-row-2col { grid-template-columns: 1fr; gap: 0; }
}
label { display: block; font-size: .85rem; font-weight: 500; margin-bottom: .4rem; color: var(--muted); }
input, textarea, select {
  width: 100%;
  padding: .65rem .9rem;
  border: 1.5px solid #ddd6cc;
  border-radius: 8px;
  font-family: inherit;
  font-size: .95rem;
  background: #fff;
  color: var(--bark);
  transition: border-color .2s;
}
input:focus, textarea:focus, select:focus {
  /* No outline:none here — keep the focus-visible ring defined above for keyboard users. */
  /* Za obwódką skupienia, nie w zieleni: to drugi sygnał tego samego zdarzenia
     i ma być z tej samej rodziny co pierwszy. */
  border-color: var(--sand-ink);
}
input:disabled, textarea:disabled, select:disabled {
  background: #e5e0d5;
  color: var(--muted);
  border-color: #ddd6cc;
  cursor: not-allowed;
}
textarea { resize: vertical; min-height: 80px; }

/* A checkbox or radio sits beside its wording, not above it like a field
   label. The box also has to opt out of the input sizing above: width:100%
   stretches it across the whole row and squeezes the sentence into a narrow
   column. */
.consent {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  margin: 0;
  cursor: pointer;
  font-size: .84rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--bark);
}
/* Standing in the form's own rhythm rather than inside a bordered box, as
   it does on the booking page — there it is the last thing in a card that
   supplies its own padding, here it would otherwise sit flush against the
   submit button with no gap at all. */
.consent-field {
  margin-bottom: 1.25rem;
  padding: .7rem .85rem;
  border: 1px solid #e8e0d5;
  border-radius: 8px;
  background: #faf8f4;
}

.consent input[type="checkbox"],
.consent input[type="radio"] {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
  margin: .12rem 0 0;
  padding: 0;
  border: none;
  background: none;
  accent-color: var(--moss);
}

.session-meta {
  font-size: .85rem;
  color: var(--muted);
  margin: .65rem 0;
  display: flex;
  gap: .45rem 1.1rem;
  flex-wrap: wrap;
  line-height: 1.5;
}
.session-meta span::before { margin-right: .3rem; }

/* Termin na własnym tle. Wyróżniony jest jeden fakt, nie trzy: po tytule
   pierwsze pytanie brzmi „kiedy", a podłożenie tła pod wszystkim po kolei
   znosi się nawzajem i robi z karty pasiastą listę.

   Data i czas trwania stoją w jednym kaflu, bo odpowiadają na to samo
   pytanie. Miejsce zostaje gołym tekstem — to osobna sprawa i osobny wiersz.

   Krycie tła jest niskie celowo: kafel ma prowadzić wzrok, a nie krzyczeć.
   Kolor tekstu wraca z `--muted` na `--bark`, bo to jego waga w hierarchii
   podniosła się, nie tylko podkład. */
.session-meta .termin {
  display: inline-flex;
  align-items: center;
  gap: .1rem .9rem;
  flex-wrap: wrap;
  background: #f4efe4;
  border-radius: 7px;
  padding: .3rem .6rem;
  color: var(--bark);
}
.session-title {
  font-family: 'Fraunces', serif;
  font-size: 1.2rem;
  margin-bottom: .25rem;
}

/* Nagłówek karty zajęć: tytuł i znacznik stanu.

   Znacznik stoi w prawym górnym rogu karty, tytuł zaczyna się pod nim. Oba
   dostają przez to całą szerokość i przestają o nią walczyć — wcześniej
   siedziały w jednym wierszu, a że znacznik miał `flex-shrink: 0`, „10
   wolnych miejsc" rezerwowało sobie połowę karty na stałe i tytuł łamał się
   na trzy wiersze obok pigułki wiszącej w powietrzu.

   Kotwicą jest `.card-head`, nie `.card`. To pierwsze dziecko karty, więc
   jego róg jest rogiem wnętrza karty — a przy kotwiczeniu do `.card`
   położenie znacznika musiałoby powtarzać jej padding i zmieniać się razem
   z nim pod media query.

   `padding-top` robi miejsce na pigułkę: 1,375rem jej wysokości plus odstęp.
   `nowrap` na znaczniku jest tu warunkiem, a nie kosmetyką — złamany na dwa
   wiersze wyszedłby poza zarezerwowane miejsce i nasunął się na tytuł.

   Kolejność w DOM zostaje tytuł → znacznik, czyli tak, jak ma to przeczytać
   czytnik ekranu. Pozycjonowanie zmienia tylko obraz. */
.card-head { margin-bottom: .5rem; }
.card-head > a { display: block; text-decoration: none; color: inherit; }
.card-head .session-title { margin-bottom: 0; transition: color .15s; }

/* Zajęcia, na które już jesteś zapisany.
   Wyróżniona jest cała karta, a nie znacznik w rogu, bo to jedyna informacja
   na tej karcie, która mówi o czytelniku, a nie o zajęciach — reszta
   (termin, miejsce, cena, obłożenie) jest taka sama dla wszystkich. Status
   względem odbiorcy stoi więc poziom wyżej niż atrybut w środku.

   Lewa krawędź, nie obwódka dookoła: ten sam wzorzec, co `.access-card.pending`
   w panelu trenera. Że to rezerwacja, mówi też nieaktywny przycisk na dole —
   barwa nie jest tu jedynym nośnikiem. */
.card.moja {
  border-left: 3px solid var(--sage);
  background: #f2f7f0;
}
.slots-bar {
  margin: .75rem 0;
  background: #e8e0d5;
  border-radius: 4px;
  height: 6px;
  overflow: hidden;
}
.slots-fill {
  height: 100%;
  width: var(--w, 0%);
  background: var(--moss);
  border-radius: 4px;
  transition: width .4s;
}
.slots-fill.almost { background: var(--sand); }
.slots-fill.full   { background: var(--rust); }

/* Obłożenie zajęć — linia pod paskiem miejsc.
   Przejęła rolę znacznika z rogu karty: „8 wolnych miejsc" w pigułce, pasek
   i „0/8 miejsc zajętych" mówiły to samo trzy razy, każde innym językiem.
   Została jedna liczba, w trzech wzajemnie wzmacniających się kodowaniach —
   położenie (pasek), wielkość (liczba) i barwa.

   Barwa nie może być jedynym nośnikiem stanu (WCAG 1.4.1), więc przy dwóch
   górnych progach dochodzi dopisek — „ostatnie wolne", „komplet". Dzięki
   temu ktoś, kto nie odróżnia zieleni od rdzy, czyta to samo co reszta. */
.miejsca {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .85rem;
  font-weight: 500;
  margin-bottom: .75rem;
  color: var(--moss);
}
/* Sama liczba mocniejsza od podpisu: to ona się zmienia i na nią patrzy oko,
   „miejsc zajętych" jest tylko jednostką. */
.miejsca b { font-weight: 500; }
.miejsca-podpis { font-weight: 400; }
.miejsca.malo { color: var(--sand-ink); }
.miejsca.brak { color: var(--rust); }
/* Wstrzymane zapisy stają w tym samym miejscu co obłożenie i zamiast niego.
   Linia odpowiada na pytanie „czy się dostanę", a nie „ile jest krzeseł" —
   a przy wstrzymanych zapisach „2/8 miejsc zajętych" byłoby nieprawdą w
   jedynym znaczeniu, w jakim ktoś to czyta. Pasek nad nią też znika: rysowałby
   wolne miejsca, których nie da się zająć. */
.miejsca.wstrzymane { color: var(--rust); }

.hero {
  background: var(--bark);
  color: var(--cream);
  padding: 4rem 1.5rem;
  text-align: center;
  /* Układ odniesienia dla linki w tle (`.art-naglowek`) i jej przycięcie. Bez
     `relative` rysunek kotwiczy się do całej strony i kładzie na serwisie —
     `overflow` jest drugim zamkiem na ten sam błąd. */
  position: relative;
  overflow: hidden;
}
.hero h1 {
  font-family: 'Fraunces', serif;
  font-size: clamp(2rem, 5vw, 3.5rem);
  margin-bottom: .75rem;
}
.hero h1 em { font-style: normal; color: var(--sand); }
.hero p { opacity: .75; max-width: 480px; margin: 0 auto 2rem; }

table { width: 100%; border-collapse: collapse; background: var(--paper); border-radius: var(--radius); overflow: hidden; }
th { background: var(--bark); color: var(--cream); font-weight: 500; font-size: .85rem; padding: .75rem 1rem; text-align: left; }
td { padding: .75rem 1rem; border-bottom: 1px solid #ede6da; font-size: .9rem; }
tr:last-child td { border: none; }
tr:hover td { background: #f5f0e8; }

.actions { display: flex; gap: .5rem; flex-wrap: wrap; }

.dashboard-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}
.filter-group { display: flex; align-items: center; gap: .5rem; }
.filter-group label { font-weight: 500; }
.filter-search-row { display: flex; gap: .4rem; }
.filter-search-row input { padding: .4rem .6rem; }
/* Lupa i podpis w jednym wierszu. `.btn` jest inline-block, więc dopóki
   przycisk był wypełniony, złamanie podpisu pod ikonę ginęło w plamie koloru —
   po przejściu na obrys obrys narysował je jako wysoki, wąski prostokąt. */
.filter-submit {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  white-space: nowrap;
  flex-shrink: 0;
}
.filter-group select { padding: .4rem .6rem; }
.filter-clear { font-size: .85rem; color: var(--muted); }

/* Wariant dla listy publicznej. W panelu trenera rozsunięcie na przeciwne
   krawędzie jest trafne: szukanie zawęża, sortowanie porządkuje — to dwa różne
   rodzaje kontrolki. Tutaj oba filtry robią to samo z tą samą listą, więc
   pół ekranu odstępu między nimi sugerowałoby różnicę, której nie ma. */
.dashboard-filters.filters-grouped { justify-content: flex-start; gap: 2rem; }

/* Nagłówek listy: etykieta i skala w jednym wierszu, wspólna krawędź z
   filtrami i siatką pod spodem. */
.listing-head {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
.listing-count { font-size: .85rem; color: var(--muted); margin: 0; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* Pigułka, nie goły kwadracik. Ten filtr odpowiada na pytanie, które zadaje
   sobie każdy przeglądający — „gdzie mogę się jeszcze zapisać" — a jako samo
   pole 13px obok obramowanego inputu i wypełnionego przycisku był najcichszym
   elementem paska, czyli hierarchia przeczyła jego wadze.

   Cała pigułka jest etykietą, więc kliknięcie łapie tekst, nie tylko pole. */
.filter-check {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  cursor: pointer;
  font-size: .88rem;
  padding: .4rem .75rem;
  /* Ta sama krawędź co pola formularzy obok, żeby pigułka czytała się jako
     kontrolka z tej samej rodziny. */
  border: 1.5px solid #ddd6cc;
  border-radius: 999px;
  background: transparent;
  white-space: nowrap;
  transition: background .15s, border-color .15s, color .15s;
}
.filter-check:hover { border-color: var(--sand); }
.filter-check input { cursor: pointer; margin: 0; }
/* Stan włączony widoczny z odległości, na której nie da się dostrzec ptaszka.

   W kolorze paska nawigacji, nie w zieleni — ta sama zasada, co przy
   przełączniku Lista/Kalendarz. Włączony filtr to stan kontrolki, a nie stan
   zajęć; zielona pigułka stała nad siatką kart, w której zieleń znaczy „są
   miejsca", i czytała się jak jeszcze jeden znacznik dostępności zamiast jak
   sterowanie. Krem na brązie: 14,5:1. */
.filter-check:has(input:checked) {
  background: var(--bark);
  border-color: var(--bark);
  color: var(--cream);
  font-weight: 500;
}

/* Stronicowanie listy zajęć. Wyśrodkowane pod siatką kart, bo lista kończy się
   równo — strzałki po bokach pozycji czytają się jak oś czasu, którą są. */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-top: 2rem;
  flex-wrap: wrap;
}
.pager-pos { font-size: .85rem; color: var(--muted); font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.title-row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.cell-title-status { display: none; }
/* The one booking you need to act on soonest. Marked by weight and a ring
   rather than another colour — the card already carries a green edge for
   "active", and a second hue there would read as a different kind of thing
   rather than as the same kind, first in line. */
.next-booking { box-shadow: 0 0 0 2px var(--sand); }
.next-booking:hover { box-shadow: 0 0 0 2px var(--sand), 0 4px 20px rgba(44,31,16,.08); }
/* Says what the ring means. Without it the highlight is decoration, and on a
   card more than a fortnight out there is no relative date to explain it
   either. */
.next-booking-label {
  font-size: .72rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  /* Ciemny bursztyn, nie `--sand`: sam akcent na papierze karty daje 2,1:1,
     czyli podpis obwódki był praktycznie nieczytelny. Obwódka zostaje w pełnym
     bursztynie — jej nikt nie czyta, ona ma być widoczna. */
  color: var(--sand-ink);
  font-weight: 500;
  margin-bottom: .4rem;
}
/* Blisko terminu to naglące, nie dostępne — bursztyn, nie zieleń. */
.when-soon { color: var(--sand-ink); font-weight: 500; }

.empty-state { text-align: center; padding: 3rem; color: var(--muted); }
.empty-state p { margin-bottom: 1rem; }

/* Rysunki w pustych stanach (`_art.html`).

   `non-scaling-stroke` trzyma grubość kreski w pikselach ekranu, a nie
   w jednostkach rysunku. Bez tego oba znaki nie mogłyby mieć jednej kreski:
   pies jest rysowany na siatce 24 jednostek, smycz na 200, więc to samo
   `stroke-width` dałoby w każdym z nich zupełnie inną grubość — a skalowanie
   rozjeżdżałoby ją dalej. */
.art-rysunek {
  display: block;
  height: auto;
  max-width: 100%;
  margin: 0 auto 1.5rem;
}
.art-rysunek [class^="art-"] { vector-effect: non-scaling-stroke; }

/* Kontur w neutralnej, nie w zieleni.
   Zieleń niesie w serwisie znaczenie („są miejsca"), a pusty stan to brak
   danych, nie stan danych — rysunek nie ma nic zapowiadać. Mech przygaszony
   do 52% dawał przy tym szarozieloną plamę, jedyny chłodny element na ciepłej
   stronie. `--muted` jest szarością tej palety: cicha i z rodziny. */
.art-rysunek .art-ksztalt { stroke: var(--muted); opacity: .55; stroke-width: 2.2; }
/* Jedyna nasycona barwa, wydana raz — na karabińczyku. Bursztyn, bo to kolor
   marki; ciemniejszy wariant, bo sam `--sand` na kremowym ma 2,1:1 i przy
   kresce 2,4px byłby ledwie widoczny. */
.art-rysunek .art-akcent  { stroke: var(--sand-ink); opacity: .85; stroke-width: 2.4; fill: none; }
.art-rysunek .art-ziemia  { stroke: var(--muted); opacity: .28; stroke-width: 1; }

/* Linka w tle nagłówka. Leży pierwsza w drzewie, więc rodzeństwo dostaje
   `position: relative` i maluje się po niej — inaczej tekst trafiłby pod
   rysunek. Jedna farba w dwóch kryciach: na prawie czarnym `--bark` mech
   i rdza z wersji jasnej byłyby niewidoczne. */
.art-naglowek {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.hero > :not(.art-naglowek) { position: relative; }
.art-naglowek [class^="art-"] { vector-effect: non-scaling-stroke; }
/* Odrobinę śmielej niż pierwsza wersja (.30/.16): rysunek zszedł w pusty pas
   pod przyciskiem, więc nie konkuruje już z tekstem i stać go na obecność. */
.art-naglowek .art-ksztalt { stroke: var(--sand); opacity: .38; stroke-width: 2.2; }
.art-naglowek .art-akcent  { stroke: var(--sand); opacity: .60; stroke-width: 2.4; }
.art-naglowek .art-ziemia  { stroke: var(--sand); opacity: .18; stroke-width: 1; }
.hidden { display: none !important; }

/* Collapsed-by-default mobile alternative to the bookings table — hidden
   here, shown only under the mobile breakpoint (see media query below). */
.bookings-accordion { display: none; }
.booking-card {
  background: var(--paper);
  border: 1px solid #ddd2c0;
  border-radius: var(--radius);
  box-shadow: 0 1px 4px rgba(44,31,16,.06);
  margin-bottom: .75rem;
  overflow: hidden;
}
.booking-card:last-child { margin-bottom: 0; }
.booking-card summary {
  list-style: none;
  cursor: pointer;
  padding: .85rem 1rem;
  display: flex;
  align-items: baseline;
  gap: .5rem;
}
.booking-card summary::-webkit-details-marker { display: none; }
.booking-chevron {
  margin-left: auto;
  color: var(--muted);
  flex-shrink: 0;
  display: inline-flex;
  transition: transform .15s;
}
.booking-card[open] .booking-chevron { transform: rotate(180deg); }
.booking-index { color: var(--muted); font-size: .85rem; flex-shrink: 0; }
.booking-summary-text { font-size: .92rem; }
.booking-card-details {
  padding: 0 1rem 1rem;
  border-top: 1px solid #f0ebe3;
  font-size: .85rem;
  color: var(--muted);
}
.booking-card-details p { margin: .6rem 0 0; }
.booking-card-details .mini-label { font-weight: 600; color: var(--bark); }
.map-preview {
  border-radius: 8px;
  margin-bottom: 1.5rem;
}
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.actions-nowrap { flex-wrap: nowrap; }

@media (max-width: 640px) {
  /* Ten jeden przycisk dostaje pełny cel dotykowy, bo naciska się go na
     dworze, w drodze, często jedną ręką — druga trzyma smycz. btn-sm daje
     28px; zalecane minimum to 44. Na myszy 28px wystarcza w zupełności, więc
     podniesienie zostaje przy ekranach, gdzie chodzi o kciuk. */
  .btn-nawiguj { min-height: 44px; padding-left: 1.1rem; padding-right: 1.1rem; }

  nav { padding: .6rem 1rem; gap: .4rem .75rem; }
  .nav-brand { font-size: 1.25rem; order: 1; }
  /* Keep the avatar pinned next to the brand on its own top row; push the
     rest of the nav links onto a full-width row underneath. */
  .avatar-menu { order: 2; }
  /* The brand/link separator only makes sense on the same row — once
     nav-links wraps to its own full-width row below, drop it. */
  .nav-links { order: 3; flex-basis: 100%; gap: .6rem; margin-left: 0; padding-left: 0; border-left: none; }
  .nav-links a { font-size: .9rem; }
  .nav-links .btn-nav { padding: .35rem .8rem; }

  .container { padding: 1.25rem 1rem; }
  .page-title { font-size: 1.5rem; }
  .hero { padding: 2.5rem 1.25rem; }

  .card { padding: 1.1rem; }
  .card-grid { grid-template-columns: 1fr; }
  .form-card { margin: 1rem auto; }
  .confirm-wrap { margin-top: 1rem; }
  .form-row-2col { grid-template-columns: 1fr; gap: 1.25rem; }
  /* Short number fields (slots, price) still fit side by side on a phone —
     only the wider date/duration row needs to stack. */
  .form-row-2col.form-row-compact { grid-template-columns: 1fr 1fr; gap: .75rem; }

  /* Tighten vertical rhythm — less scrolling between fields on small screens. */
  .form-group { margin-bottom: .9rem; }
  /* Ta sama proporcja co na szerokim ekranie, tylko ciaśniej. */
  .form-section-title { margin: 2rem 0 .6rem; }
  .form-section-title:first-child { margin-top: 0; }

  /* Keep the primary action reachable without scrolling to the very bottom
     of a long form. Bleeds to the card's edges (negative margin matches the
     mobile .card padding above) so it reads as a full-width bar. */
  .form-actions {
    position: sticky;
    bottom: 0;
    margin: 1.5rem -1.1rem -1.1rem;
    padding: .85rem 1.1rem;
    background: var(--paper);
    border-radius: 0 0 var(--radius) var(--radius);
    box-shadow: 0 -4px 12px rgba(44,31,16,.08);
    z-index: 10;
  }

  table { font-size: .82rem; }
  th, td { padding: .5rem .6rem; }

  /* Search + sort side by side instead of stacked full-width — halves the
     vertical space this bar takes on narrow screens. */
  .dashboard-filters { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; margin-bottom: .75rem; }
  .filter-group { flex-direction: column; align-items: stretch; gap: .3rem; }
  .filter-group label { font-size: .8rem; }
  /* Keep input + submit in one row, matching the select's row height —
     shrink the button to an icon so both still fit in a half-width column. */
  .filter-search-row { flex-direction: row; gap: .3rem; }
  .filter-search-row input { flex: 1; min-width: 0; }
  .filter-submit { padding: .4rem .55rem; flex-shrink: 0; }
  .filter-submit-label { display: none; }
  .filter-clear { font-size: .78rem; }

  /* Lista publiczna wraca do jednej kolumny. Podział na pół jest trafny dla
     pary „szukaj + sortowanie", bo select jest wąski — ale tutaj drugą połowę
     zajmuje pigułka z pełnym zdaniem, przez co pole wyszukiwania ściskało się
     do około 60px i pokazywało samo „Nazwa". Pole jest tu ważniejsze, więc
     dostaje całą szerokość, a pigułka schodzi pod nie. */
  .dashboard-filters.filters-grouped { grid-template-columns: 1fr; gap: .75rem; }
  .filters-grouped .filter-group { flex-direction: row; align-items: center; gap: .5rem; }
  .filters-grouped .filter-search-row { flex: 1; min-width: 0; }

  .listing-head { gap: .5rem; margin-bottom: .75rem; }

  .actions-nowrap { flex-wrap: wrap; }

  /* Turn data-dense tables into stacked cards instead of a cramped,
     sideways-scrolling grid — each row becomes a card, each cell a
     labeled line (label comes from the td's data-label attribute). */
  table.table-cards, table.table-cards thead, table.table-cards tbody,
  table.table-cards th, table.table-cards td, table.table-cards tr {
    display: block;
  }
  table.table-cards { width: 100%; }
  table.table-cards thead {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  table.table-cards tr {
    display: flex;
    flex-wrap: wrap;
    margin-bottom: 1.1rem;
    background: var(--paper);
    border: 1px solid #ddd2c0;
    border-radius: var(--radius);
    box-shadow: 0 1px 4px rgba(44,31,16,.06);
    overflow: hidden;
  }
  table.table-cards tr:last-child { margin-bottom: 0; }
  /* Each cell defaults to its own full-width line; Miejsca + Cena are short
     enough to share one row instead. */
  table.table-cards td { flex: 1 1 100%; }
  table.table-cards td.cell-slots, table.table-cards td.cell-price,
  table.table-cards td.cell-dog, table.table-cards td.cell-breed { flex: 1 1 45%; }
  table.table-cards td {
    border-bottom: 1px solid #f0ebe3;
    padding: .5rem .85rem;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .4rem;
  }
  table.table-cards tr:hover td { background: none; }
  table.table-cards td:last-child { border-bottom: none; }
  table.table-cards td[data-label]::before {
    content: attr(data-label) ":";
    flex-shrink: 0;
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
  }

  /* Title acts as the card's heading — give it visual priority instead of
     burying it as just another labeled field, and drop the redundant label. */
  table.table-cards td.cell-title {
    padding-top: .75rem;
    padding-bottom: .6rem;
  }
  table.table-cards td.cell-title::before { display: none; }
  table.table-cards td.cell-title strong {
    font-family: 'Fraunces', serif;
    font-size: 1.15rem;
    /* Patrz `.form-section-title` — Fraunces nie ma wczytanej szescsetki. */
    font-weight: 700;
  }

  /* The action buttons are self-explanatory; the "Akcje:" label is noise. */
  table.table-cards td.cell-actions::before { display: none; }

  /* Status reads as a compact badge, not a labeled fact — show it next to
     the title (like the public session list does) instead of its own line,
     and hide the now-redundant dedicated Status cell. */
  table.table-cards td.cell-status { display: none; }
  .cell-title-status { display: inline-flex; }

  /* Row ordinal is redundant once each row is its own visually separated
     card, and would otherwise show up as a stray "#: 1" line. */
  table.table-cards td.cell-index { display: none; }

  /* The hidden inline-edit row holds a form, not a labeled fact — let it lay
     out on its own terms instead of being forced into the label:value flex
     row every other cell uses. */
  table.table-cards td.cell-edit-form { display: block; }

  /* Bookings list: swap the table for a collapsed-by-default accordion —
     only the owner, dog, and breed show until a card is tapped open. */
  .bookings-table-wrap { display: none; }
  .bookings-accordion { display: block; }
}

/* Site footer. Deliberately quiet: what it carries is a legal obligation
   rather than something anyone is meant to be nudged towards. */
.site-footer {
  margin-top: 4rem;
  padding: 1.5rem;
  border-top: 1px solid #e8e0d5;
  text-align: center;
  font-size: .8rem;
}
.site-footer a { color: var(--muted); text-decoration: none; }
.site-footer a:hover { color: var(--link); text-decoration: underline; }

/* Legal documents. Long, numbered, read once and then searched — so the
   numbering has to stay visible next to what it numbers, and the measure has
   to stay short enough that the eye finds the next line. */
/* 36rem holds about 72 characters per line at this size — measured, not
   guessed. At the container's full width a line ran to 95, which is past
   the point where the eye stops finding the start of the next one. */
.legal { max-width: 36rem; margin: 0 auto; }

/* Regulamin osadzony na stronie zgody, zamiast schowany za linkiem. Pole ma
   swoją wysokość, bo dokument ma 350 wierszy: wylany na stronę zepchnąłby
   przycisk akceptacji tak daleko, że nikt by go nie znalazł — i wyszłoby na
   to samo co link, tylko gorzej.

   Ramka i tło oddzielają cudzy tekst od naszego. overscroll-behavior zatrzymuje
   przewijanie w środku zamiast przenosić je na stronę, gdy dojedzie się do
   końca — inaczej dojechanie do dołu regulaminu przerzuca widok gdzie indziej.
   Wysokość w dvh, żeby na telefonie liczył się realny ekran, a nie ten sprzed
   schowania paska adresu. */
.legal-scroll {
  max-width: none;
  max-height: min(28rem, 45dvh);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1rem 1.25rem;
  border: 1.5px solid #ddd6cc;
  border-radius: 8px;
  background: #fdfcfa;
  font-size: .88rem;
}
/* Widoczne skupienie, bo to pole da się sfokusować klawiaturą (tabindex="0")
   i bez tego nie widać, że strzałki będą przewijać właśnie je. */
.legal-scroll:focus-visible { outline: 2px solid var(--sand-ink); outline-offset: 2px; }
/* Dokument zaczyna się od razu, bez odstępu odziedziczonego z pełnej strony. */
.legal-scroll > :first-child { margin-top: 0; }
.legal .legal-meta { font-size: .82rem; color: var(--muted); margin: -.75rem 0 1.75rem; }
.legal h2 { font-size: 1.05rem; margin: 2rem 0 .6rem; }
.legal ol { padding-left: 1.5rem; }
.legal li { margin-bottom: .7rem; font-size: .92rem; line-height: 1.65; }
.legal li::marker { color: var(--muted); font-size: .85rem; }
.legal a { color: var(--link); }
/* The one paragraph that decides how everything else is read. */
.legal-callout { border-left: 3px solid var(--sand); background: #faf8f4; padding: .9rem 1.1rem; margin-bottom: 1.5rem; }
.legal-callout p { font-size: .92rem; line-height: 1.65; margin: 0; }
.site-footer .footer-sep { color: var(--muted); margin: 0 .4rem; }

/* Trainer's participant list. Cards rather than a table: each row carries a
   free-text message and two forms, which a table row has nowhere to put. */
.access-card { cursor: default; }
/* The hover lift on .card belongs to things you click. These are worked on
   in place, and every card twitching under the pointer is noise. */
.access-card:hover { box-shadow: none; transform: none; }
.access-card.pending { border-left: 3px solid var(--sand); }
.access-head { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap; }
/* The participant's own words, kept as typed — their line breaks are theirs. */
.access-quote {
  white-space: pre-wrap;
  font-size: .92rem;
  line-height: 1.6;
  margin: .7rem 0 0;
  padding: .7rem .9rem;
  background: #faf8f4;
  border-radius: 8px;
}
.access-actions { display: flex; gap: 1.25rem; flex-wrap: wrap; margin-top: 1rem; }
.access-decide { display: flex; flex-direction: column; gap: .3rem; flex: 1; min-width: 16rem; }
.access-decide label { font-size: .78rem; color: var(--muted); }
.access-decide input {
  padding: .45rem .6rem;
  font-family: inherit;
  font-size: .88rem;
  border: 1px solid #d6cfc6;
  border-radius: 8px;
  background: var(--paper);
}
.access-buttons { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .2rem; }

/* Count beside a section heading. Quieter than the heading it qualifies —
   it answers "how many", which is never the first thing being read. */
.section-count { color: var(--muted); font-weight: 400; font-size: .9em; }

/* Settled participants: one row each, not one card each. The form behind a
   disclosure, because these rows need no action and a page of open text
   fields buries the one person who is actually waiting. */
/* Wyszukiwarka uczestników. Pełna szerokość, bo szuka się w niej nazwiska, a
   nie wpisuje kodu pocztowego — wąskie pole ucinałoby to, co się wpisało. */
.access-search { margin-bottom: 1.5rem; }
.access-search label {
  display: block;
  font-size: .82rem;
  font-weight: 500;
  margin-bottom: .35rem;
}
.access-search input { width: 100%; padding: .55rem .8rem; }
.access-search-count {
  font-size: .8rem;
  color: var(--muted);
  /* Stała wysokość, żeby pojawienie się liczby nie przesuwało całej listy w
     dół w trakcie pisania — czyli dokładnie wtedy, gdy wzrok jest na liście. */
  min-height: 1.2em;
  margin: .35rem 0 0;
}

.access-list { border: 1px solid #e8e0d5; border-radius: var(--radius); background: var(--paper); overflow: hidden; }
.access-row + .access-row { border-top: 1px solid #f0ebe3; }
.access-row > summary {
  display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
  padding: .7rem 1rem; cursor: pointer; list-style: none; font-size: .92rem;
}
.access-row > summary::-webkit-details-marker { display: none; }
.access-row > summary:hover { background: #faf8f4; }
.access-row[open] > summary { background: #faf8f4; border-bottom: 1px solid #f0ebe3; }
.access-name { font-weight: 500; }
.access-mail { color: var(--muted); font-size: .86rem; overflow-wrap: anywhere; }
/* Pushed right so the dates line up down the list and the eye can run along
   the column instead of hunting for each one after a name of random length. */
.access-when { color: var(--muted); font-size: .82rem; margin-left: auto; white-space: nowrap; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.access-toggle { color: var(--link); font-size: .82rem; white-space: nowrap; }
.access-row[open] .access-toggle { color: var(--muted); }
/* Its own line under the name, so a long reason does not squeeze the columns
   the eye runs down. Full width via flex-basis, not a separate block, because
   it has to stay inside the summary to show while the row is closed. */
.access-reason { flex-basis: 100%; font-size: .85rem; color: var(--muted); line-height: 1.5; white-space: pre-wrap; }
.access-body { padding: .9rem 1rem 1.1rem; }
/* Its own line under the form, not a column beside it — it explains what the
   button does, so it belongs below the button rather than across from it. */
.access-hint { flex-basis: 100%; font-size: .78rem; color: var(--muted); margin: .25rem 0 0; line-height: 1.5; }

@media (max-width: 640px) {
  .access-decide { min-width: 0; flex-basis: 100%; }
  /* Buttons go full width one under the other — side by side at this size
     they end up too narrow to read and too close to tap apart. */
  .access-buttons { flex-direction: column; }
}

/* ── Kalendarz trenera ──────────────────────────────────────────────────────
   Siatka tydzień × godziny. Bloki są pozycjonowane absolutnie w procentach
   kolumny dnia, co pozwala nakładającym się zajęciom stanąć obok siebie —
   sam powód, dla którego ten widok istnieje. */
.cal-head { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem; margin-bottom: 1rem; }
.cal-week { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin: 0 0 1.5rem; }
.cal-nav { display: flex; gap: .4rem; align-items: center; }
.cal-range { color: var(--muted); font-size: .9rem; margin: 0; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* Przelacznik widoku. Segmentowany, a nie dwa osobne przyciski: to jeden
   wybor o dwoch stanach, a nie dwie niezalezne akcje — i wyglad ma to
   powiedziec, zanim ktos przeczyta etykiety. */
.view-switch {
  display: inline-flex;
  border: 1px solid #e0d8cb;
  border-radius: 8px;
  overflow: hidden;
  background: var(--paper);
  margin-bottom: 1.25rem;
}
.view-switch a {
  padding: .45rem 1.1rem;
  font-size: .88rem;
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  background: transparent;
}
.view-switch a + a { border-left: 1px solid #e0d8cb; }
.view-switch a:hover { background: var(--cream); color: var(--bark); }
/* Aktywny segment w barwie paska nawigacji, nie w zieleni.
   Zieleń znaczy w serwisie „są miejsca" — a na tym samym ekranie, w tabeli
   obok, stoi w niej znacznik „Otwarte". Wypełniony zielony segment o
   podobnym kształcie czytał się więc jako ten sam rodzaj informacji, choć
   mówi coś zupełnie innego: nie o zajęciach, tylko o tym, który widok jest
   otwarty. To jest element sterujący, czyli ta sama rodzina co nawigacja —
   i dostaje jej kolor. */
.view-switch a.active { background: var(--bark); color: var(--cream); }
.view-switch a.active:hover { background: var(--bark); color: var(--cream); }

/* Poziomy przewijak, nie zwężanie kolumn: siedem dni ściśniętych poniżej
   pewnej szerokości przestaje być czytelne, a wtedy i tak wchodzi agenda. */
.cal-grid-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cal-grid {
  display: grid;
  grid-template-columns: 3.5rem repeat(7, minmax(7rem, 1fr));
  min-width: 54rem;
  border: 1px solid #e8e0d5;
  border-radius: var(--radius);
  background: var(--paper);
  overflow: hidden;
}
.cal-corner { border-bottom: 1px solid #e8e0d5; border-right: 1px solid #f0ebe3; }
.cal-day-head {
  display: flex; flex-direction: column; align-items: center; gap: .1rem;
  padding: .5rem .25rem;
  border-bottom: 1px solid #e8e0d5;
  border-right: 1px solid #f0ebe3;
  font-size: .82rem;
}
.cal-day-head:last-child { border-right: none; }
.cal-day-name { text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-size: .72rem; }
.cal-day-date { font-weight: 500; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.cal-day-head.today .cal-day-date { color: var(--moss); }
/* Tint dnia dzisiejszego mieszany z palety: --sage położona na --paper
   (12% w nagłówku, ~5% w kolumnie), a nie neutralna zieleń. Poprzednie
   #f2f6f2/#f9fbf9 miały kanał B równy R — zieleń zimną, która na ciepłym
   kremowym tle czytała się jak obca, szarawa mgiełka. W palecie serwisu
   niebieski jest zawsze najniżej i tint musi to dziedziczyć. */
.cal-day-head.today { background: #edefe7; }

/* Godzina ma stałą wysokość, więc wysokość siatki wynika z liczby wierszy —
   ta sama liczba, na której liczone są procenty bloków. */
.cal-hours, .cal-col { --wiersz: 3.6rem; }
.cal-hours { border-right: 1px solid #e8e0d5; }
.cal-hour {
  height: var(--wiersz);
  position: relative;
  font-size: .72rem;
  color: var(--muted);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.cal-hour span { position: absolute; top: -.45rem; right: .4rem; background: var(--paper); padding: 0 .15rem; }
.cal-col { position: relative; border-right: 1px solid #f0ebe3; }
.cal-col:last-child { border-right: none; }
/* 8% szałwii, nie 5%: przy słabszej mieszance kolumna miała kontrast 1,04 do
   tła karty, czyli nie było jej widać wcale i reguła nic nie robiła. Celowo
   słabsza od nagłówka (1,07 wobec 1,11) — kolumna zajmuje wielokrotnie większą
   powierzchnię, więc przy równej sile zaczyna dominować siatkę. */
.cal-col.today { background: #f3f3eb; }
.cal-slot { height: var(--wiersz); border-bottom: 1px solid #f4f0e9; }
.cal-slot:last-child { border-bottom: none; }

.cal-block {
  position: absolute;
  display: flex; flex-direction: column; gap: .05rem;
  overflow: hidden;
  padding: .25rem .4rem;
  border-radius: 6px;
  background: var(--moss);
  color: var(--cream);
  text-decoration: none;
  font-size: .74rem;
  line-height: 1.25;
  /* Odstęp między sąsiadującymi blokami robiony obwódką w kolorze tła, a nie
     marginesem: margines liczyłby się od szerokości procentowej i dwa bloki
     obok siebie rozjechałyby się względem siatki godzin. */
  box-shadow: 0 0 0 1px var(--paper);
}
.cal-block:hover { background: #2e4430; }
.cal-block.full { background: var(--sand); color: var(--bark); }
.cal-block.full:hover { background: #b8955c; }
.cal-block.cancelled { background: #efe7e3; color: var(--rust); text-decoration: line-through; }
.cal-time { font-weight: 500; opacity: .9; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.cal-title { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-meta { opacity: .75; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Agenda zastępuje siatkę poniżej breakpointa. Siedmiu kolumn nie da się
   sensownie ścisnąć do szerokości telefonu, a przewijanie w bok gubi oś
   godzin — więc zamiast okaleczonej siatki lepszy jest inny widok tych
   samych danych. */
.cal-agenda { display: none; }
.cal-agenda-day {
  font-family: 'Fraunces', serif; font-size: 1.02rem;
  margin: 1.5rem 0 .6rem;
}
.cal-agenda-day span { color: var(--muted); font-size: .85rem; font-weight: 400; }
.cal-agenda-day.today { color: var(--moss); }
.cal-agenda-item {
  display: flex; flex-direction: column; gap: .15rem;
  padding: .7rem .9rem; margin-bottom: .5rem;
  border-radius: 8px;
  border-left: 3px solid var(--moss);
  background: var(--paper);
  text-decoration: none;
  color: var(--bark);
  font-size: .88rem;
}
.cal-agenda-item .cal-meta { color: var(--muted); opacity: 1; }
.cal-agenda-item.full { border-left-color: var(--sand); }
.cal-agenda-item.cancelled { border-left-color: var(--rust); opacity: .7; }
.cal-agenda-item.cancelled .cal-title { text-decoration: line-through; }

/* 911px, nie okrągłe 860: siatka ma min-width 54rem (864px), a kontener po
   1.5rem marginesu z każdej strony, więc żeby zmieściła się bez przewijania
   w bok, okno musi mieć 912px. Przy niższej granicy w przedziale 861–911
   pokazywała się siatka, której i tak nie dało się objąć wzrokiem bez
   przesuwania — a od tego jest agenda. */
@media (max-width: 911px) {
  .cal-grid-wrap { display: none; }
  .cal-agenda { display: block; }
}

/* ── Podgląd dnia w formularzu zajęć ────────────────────────────────────────
   Pasek godzin z tym, co trener ma już tego dnia, i z blokiem właśnie
   tworzonych zajęć. Kolizji nie zabraniamy — pokazujemy ją, bo dwie grupy
   naraz bywają zamierzone. */
/* Pole nad podglądem nie rezerwuje już dolnego odstępu — inaczej sumuje się on
   z marginesem podglądu i wychodzi 30 px, czyli więcej niż między niezależnymi
   polami. A podgląd należy do tego pola i ma być bliżej niż one. */
.form-row-2col:has(+ .day-preview) .form-group { margin-bottom: 0; }

.day-preview {
  /* Blisko pola daty, ale nie na styk: stykające się krawędzie czytają się jak
     jeden element. Pod spodem tyle, co między zwykłymi polami. */
  margin-top: .75rem;
  margin-bottom: 1.25rem;
  border: 1px solid #e8e0d5;
  border-radius: var(--radius);
  background: #faf8f4;
  padding: .9rem 1rem 1.1rem;
}
.day-preview-head { font-size: .85rem; color: var(--muted); margin: 0 0 .2rem; }
.day-preview-clash {
  font-size: .85rem; line-height: 1.5; color: #8a5109;
  background: #fef3e2; border-radius: 8px;
  padding: .5rem .7rem; margin: .5rem 0 0;
}

/* Wysokość stała, a bloki pozycjonowane w procentach — ten sam sposób co w
   tygodniowej siatce, tylko jedna kolumna zamiast siedmiu. */
.day-strip {
  position: relative;
  /* Godzina tej samej wysokości, dopóki dzień jest krótki — ale z sufitem.
     Zajęcia o 10:00 i o 20:50 rozciągają pasmo na czternaście godzin, z czego
     większość jest pusta, a pasek wyższy od reszty formularza przestaje być
     podglądem i zaczyna być głównym elementem strony. Procenty liczą się od
     rzeczywistej wysokości, więc długi dzień po prostu się zagęszcza. */
  height: clamp(7rem, calc(var(--godzin, 6) * 1.85rem), 15rem);
  margin: .9rem 0 0 3.2rem;
  border-left: 1px solid #e0d8cb;
}
.day-tick { position: absolute; left: 0; width: 100%; border-top: 1px dashed #ece5da; }
.day-tick i {
  position: absolute; left: -3.2rem; top: -.5rem;
  font-style: normal; font-size: .7rem; color: var(--muted);
}
/* Dwie kolumny: po lewej to, co już jest, po prawej tworzone zajęcia. Na
   pełnej szerokości nowy blok zasłaniałby dokładnie te, z którymi koliduje —
   czyli jedyne, na co warto tu spojrzeć. */
.day-block {
  /* left i width wyliczane w JS: lewa kolumna dzieli się na tory, kiedy coś
     się nakłada. Prawa jest zawsze jedna, więc siedzi w klasie .new. */
  position: absolute;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  padding: .1rem .4rem;
  border-radius: 5px;
  font-size: .74rem; line-height: 1.35;
  background: #e4ece4; color: var(--bark);
  box-shadow: inset 0 0 0 1px #cfdccf;
}
.day-block b { font-weight: 600; margin-right: .2rem; }
/* Tworzone zajęcia na wierzchu i w kolorze akcji — to je się właśnie stawia. */
.day-block.new { left: 57%; width: 43%; background: var(--moss); color: var(--cream); box-shadow: none; }
.day-block.clash { background: #fce8e3; color: var(--rust); box-shadow: inset 0 0 0 1px #eec7ba; }

/* ── Strony treściowe (/o-nas, /dla-trenerow) ───────────────────────────────
   Strony, które tłumaczą serwis, zamiast go obsługiwać. Mają własny rytm, bo
   czyta się je z góry na dół jeden raz — w odróżnieniu od listy zajęć czy
   panelu, które się skanuje. */

/* Miara wiersza. Nagłówki i akapity siedzą w kolumnie ~68 znaków, ale siatka
   kart wewnątrz sekcji celowo z niej wychodzi na pełną szerokość kontenera —
   karty czyta się jako zestaw, nie jako tekst ciągły. */
.tresc-sekcja { margin-bottom: 3.5rem; }
.tresc-sekcja:last-of-type { margin-bottom: 2rem; }
.tresc-sekcja > h2 { margin-bottom: 1rem; }
.tresc-sekcja > p,
.tresc-sekcja > ul,
.tresc-sekcja > ol,
.tresc-sekcja > .zasady { max-width: 44rem; }
.tresc-sekcja p { line-height: 1.7; margin-bottom: 1rem; }
.tresc-lead { font-size: 1.08rem; }

/* Kroki. Numeracja jest tu treścią, a nie ozdobą: to kolejność, w której się
   je wykonuje, więc licznik jest widoczny i wyprowadzony przed tekst. */
.kroki {
  list-style: none;
  counter-reset: krok;
  display: grid;
  gap: 1.25rem;
  padding: 0;
  max-width: none;
}
.kroki li {
  counter-increment: krok;
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  gap: 0 1rem;
  align-items: baseline;
}
.kroki li::before {
  content: counter(krok);
  font-family: var(--font-mono);
  font-size: 1.1rem;
  font-weight: 500;
  color: var(--sand-ink);
  grid-row: span 2;
  /* Cyfra siada na linii bazowej nagłówka obok, a nie na górnej krawędzi
     bloku — inaczej wisi nad nim o kilka pikseli i rozjeżdża rząd. */
  align-self: baseline;
}
.kroki h3 { font-family: 'Fraunces', serif; font-size: 1.15rem; margin-bottom: .2rem; }
.kroki p { margin: 0; grid-column: 2; }

/* Nagłówek karty na stronie treściowej. Ikona i tekst w jednym wierszu, ikona
   w kolorze marki — na tych stronach karta jest argumentem, a nie rekordem. */
.karta-tytul {
  font-family: 'Fraunces', serif;
  font-size: 1.1rem;
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .75rem;
}
.karta-tytul .ti-icon, .karta-tytul .paw-icon { color: var(--sand-ink); flex-shrink: 0; }

.lista-cech, .lista-granic { list-style: none; padding: 0; margin: 0 0 1.25rem; }
.lista-cech li {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: .55rem;
  font-size: .95rem;
  line-height: 1.6;
}
/* Ptaszek rysowany znakiem, nie obrazkiem: lista ma kilkanaście pozycji na
   dwóch stronach i każdy plik byłby tu kosztem bez zysku. */
.lista-cech li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--moss);
  font-weight: 600;
}
.lista-granic li {
  padding-left: 1.5rem;
  position: relative;
  margin-bottom: .9rem;
  line-height: 1.7;
}
.lista-granic li::before {
  content: "—";
  position: absolute;
  left: 0;
  color: var(--muted);
}

/* Warunki współpracy. Wyróżnione lewą krawędzią, bo to jedyny akapit na tej
   stronie, którego przeoczenie kończy się rozczarowaniem po obu stronach. */
.zasady {
  border-left: 3px solid var(--sand);
  background: var(--paper);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 1.2rem 1.4rem .4rem;
}

/* Odnośniki w treści. Bez tego dziedziczą domyślny kolor przeglądarki —
   na stronie /o-nas link do mechanizmu zgłoszeń wychodził fioletowy, bo raz
   odwiedzony dostawał :visited. Reszta serwisu tego nie widziała, bo kazdy
   szablon ustawiał kolor u siebie, w atrybucie style.

   `:not(.btn)` jest tu warunkiem, a nie ostrożnością: `.tresc a` ma
   specyficzność 0-1-1, a `.btn-primary` 0-1-0, więc bez tego reguła wygrywała
   z przyciskiem i malowała ciemny bursztyn na bursztynowym wypełnieniu —
   2,99:1, poniżej progu czytelności. */
.tresc a:not(.btn), .zasady a:not(.btn) { color: var(--link); }

.claim-stopka {
  text-align: center;
  font-family: var(--font-mono);
  font-size: .8rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 3rem;
}

/* Komunikat błędu nad formularzem. Do tej pory każdy szablon niósł te same
   deklaracje w atrybucie style — pięć kopii jednej reguły. */
.komunikat-blad {
  background: var(--rust);
  color: #fff;
  padding: .75rem 1rem;
  border-radius: 8px;
  margin-bottom: 1.25rem;
  font-size: .9rem;
  max-width: 44rem;
}

@media (max-width: 640px) {
  .tresc-sekcja { margin-bottom: 2.5rem; }
  .kroki li { grid-template-columns: 1.8rem minmax(0, 1fr); gap: 0 .75rem; }
}

/* Podpowiedź pod polem formularza — krótkie zdanie, które tłumaczy pole albo
   daje wyjście komuś, kto nie może go wypełnić. Ciszej niż etykieta i wyraźnie
   związane z polem nad sobą, a nie z następnym. */
.pole-podpowiedz {
  font-size: .82rem;
  line-height: 1.5;
  color: var(--muted);
  margin: .4rem 0 0;
}
.pole-podpowiedz a { color: var(--link); }
