/* ============================================================================
   site-6b.css — components & layout for the 6b direction.
   Consumes tokens-6b.css only. Load order: fonts-6b → tokens-6b → site-6b.
   ============================================================================ */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* Plynny scroll ZDJETY 20.08: obserwator reveal przerywal kazda plynna animacje i natywny
   skok do #kotwicy nie dochodzil (m.in. dolacz #telefony - poprawka k). Skok natychmiastowy
   dziala niezawodnie, plynny i tak byl martwy. Pomiar: kontakt.html / dolacz-do-nas.html. */
body {
  margin: 0;
  background: var(--paper);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
/* `height: auto` musi tu stać: bez niego atrybut `height` z HTML zostaje wysokością
   renderu, a `width: 100%` ściska samą szerokość — obraz wychodzi zdeformowany.
   Zgłoszenie klubu z 01.09.2026 („zdjeica na kompueterze sal sa zniekszatłcone”):
   zmierzone 12 z 12 zdjęć sal, odchylenie proporcji od −51,5% do −63,6%. Kadrowanie
   świadome (`object-fit`) jest ustawiane niżej, per komponent, i tej reguły nie dotyczy. */
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }
h1, h2, h3, p { margin: 0; }
:focus-visible { outline: var(--focus); outline-offset: 2px; }
::selection { background: var(--gold); color: var(--ink); }

/* ---- Layout primitives ------------------------------------------------- */
.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
.band { position: relative; }
.band--red   { background: var(--red);   color: var(--on-red); }
.band--ink   { background: var(--ink);   color: var(--on-ink); }
.band--paper { background: var(--paper); color: var(--text-body); }
.band__pad   { padding-block: var(--band-y); }
/* Pasmo, które kończy się komponentem dojeżdżającym do krawędzi (pas grup), a po którym
   idzie pasmo w tuszu. Dolny odstęp zostawiał jasny prążek między dwiema ciemnymi masami
   — 52 px na 1440 i 28 px na 390 — i czytał się jak niedokończona sekcja. */
.band--flush-b { padding-bottom: 0; }

/* ---- Shared type atoms ------------------------------------------------- */
.eyebrow {
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-eyebrow);
  letter-spacing: .22em; text-transform: uppercase; color: var(--gold);
}
.eyebrow--red { color: var(--red); }
.display {
  font-family: var(--font-display); font-style: italic; font-weight: 800;
  text-transform: uppercase; line-height: .98; letter-spacing: .002em;
  text-wrap: balance;
}
/* MEGA tier — an architectural type-moment (gauntlet R1). Spend on a FEW anchors
   only; the escalation reads as intent against the quiet label tier around it. */
.display--mega { font-size: var(--fs-mega); line-height: .9; letter-spacing: -.005em; }
.gold-dot { color: var(--gold); }
.lead { font-family: var(--font-body); line-height: 1.7; text-wrap: pretty; }

/* section header with rule (Grupy / Gdzie trenujemy) */
.sec-head {
  display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap;
  border-bottom: 2.5px solid var(--line-strong); padding-bottom: 14px; margin-bottom: 32px;
}
.sec-head__note {
  margin-left: auto; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-eyebrow);
  letter-spacing: .12em; text-transform: uppercase; color: var(--red);
}
.sec-head__lead { max-width: 62ch; font-family: var(--font-body); font-size: var(--fs-body); line-height: 1.8; color: var(--text-body); margin: -14px 0 2px; text-wrap: pretty; }
.sec-head--ink { border-color: rgba(255,255,255,.85); }
/* R12 — „type as punctuation": tytuł sekcji na tier --fs-section (chapter break), nie label. */
.sec-head__title { font-size: var(--fs-section); }

/* ---- Buttons ----------------------------------------------------------- */
.btn {
  --bg: var(--ink); --fg: #fff;
  display: inline-flex; align-items: center; justify-content: center; gap: .5em;
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-15);
  text-transform: uppercase; letter-spacing: .06em; text-decoration: none;
  padding: 15px 26px; border: 0; background: var(--bg); color: var(--fg);
  cursor: pointer; transition: transform var(--dur) var(--ease), filter var(--dur) var(--ease), background var(--dur) var(--ease);
}
.btn:hover { transform: translateY(-2px); filter: brightness(1.04); }
.btn:active { transform: translateY(0); }
.btn--white { background: #fff; color: var(--red); }
.btn--gold  { background: var(--gold); color: var(--ink); }
.btn--ink   { background: var(--ink); color: #fff; }
.btn--red   { background: var(--red); color: #fff; }
.btn--ghost { background: transparent; color: #fff; border: 2px solid #fff; padding: 13px 24px; }
.btn--ghost:hover { background: #fff; color: var(--ink); }
.btn--sm { padding: 12px 18px; font-size: var(--fs-body-sm); }
.btn--lg { padding: 17px 30px; font-size: var(--fs-btn-lg); }
/* stan „zapisy wkrótce” — nieklikalny, przygaszony (obozy bez oferty, poprawki klubu 19.08) */
.btn.is-soon { opacity: .68; cursor: default; pointer-events: none; }

/* złoty „chip" CTA w nawigacji. Skos zdjęty na prośbę klubu 22.08.2026: „wszystkie napisy
   tego typu na stronie są w prostokątach […] bo teraz jest rombem". Reszta chipa bez zmian. */
.chip-cta {
  display: inline-block; background: var(--gold);
  padding: 13px 20px; box-shadow: var(--sh-cta); text-decoration: none; border: 0; cursor: pointer;
  transition: transform var(--dur) var(--ease), filter var(--dur) var(--ease);
}
.chip-cta > span {
  display: block;
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-body);
  text-transform: uppercase; letter-spacing: .05em; color: var(--ink);
}
.chip-cta:hover { filter: brightness(1.05); transform: translateY(-2px); }

/* text link with underline accent */
.link-arrow {
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-eyebrow);
  letter-spacing: .14em; text-transform: uppercase; text-decoration: none;
  border-bottom: 2px solid currentColor; padding-bottom: 3px; white-space: nowrap;
  transition: opacity var(--dur-fast) var(--ease);
}
.link-arrow:hover { opacity: .72; }

/* ============================================================================
   HEADER / NAV  (overlaid on hero; solidifies on scroll)
   ============================================================================ */
.site-header {
  position: absolute; top: 0; right: 0; left: 0; z-index: 50;
  transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease), top var(--dur) var(--ease);
}
/* Gdy pasek ogłoszeń jest obecny, nieprzyklejony nagłówek schodzi pod niego (nadal overlay na hero). */
body:has(.promo) .site-header:not(.is-stuck) { top: var(--promo-h); }
.site-header.is-stuck {
  position: fixed; top: 0; background: var(--ink); box-shadow: var(--sh-header);
}

/* ============================================================================
   PASEK OGŁOSZEŃ  (wąski, zamykany; w normalnym flow nad nagłówkiem, chowa się przy scrollu)
   ============================================================================ */
.promo { position: relative; z-index: 40; background: var(--ink); min-height: var(--promo-h); }
.promo__in { position: relative; display: flex; align-items: center; justify-content: center; gap: 12px; min-height: var(--promo-h); padding-block: 8px; padding-right: 36px; }
.promo__txt { font-family: var(--font-body); font-size: var(--fs-body-sm); color: #fff; text-align: center; letter-spacing: .01em; margin: 0; text-wrap: balance; }
.promo__txt b { color: var(--gold); font-weight: 700; }
.promo__txt a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }
.promo__txt a:hover { color: var(--gold); }
.promo__x { position: absolute; right: 2px; top: 50%; transform: translateY(-50%); width: 30px; height: 30px; display: grid; place-items: center; background: transparent; border: 0; color: rgba(255,255,255,.6); font-size: var(--fs-promo-x); line-height: 1; cursor: pointer; border-radius: var(--r); transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease); }
.promo__x:hover { color: #fff; background: rgba(255,255,255,.1); }
.promo.is-hidden { display: none; }
.nav {
  display: flex; align-items: center; gap: 24px;
  padding-block: 20px;
}
.brand { display: flex; align-items: center; gap: 15px; flex: none; text-decoration: none; }
.brand__crest { height: 58px; width: auto; filter: drop-shadow(var(--sh-crest)); }
.is-stuck .brand__crest { height: 46px; }
.brand__wordmark {
  font-family: var(--font-display); font-style: italic; font-weight: 800;
  font-size: var(--fs-wordmark); letter-spacing: .01em; color: #fff; text-transform: uppercase;
  line-height: .9; text-shadow: var(--tsh-wordmark); white-space: nowrap;
}
.brand__wordmark img { height: 25px; width: auto; }  /* when real wordmark-white.png is dropped in */
.nav__links {
  display: flex; align-items: baseline; gap: 16px; list-style: none; margin: 0; padding: 0;
  font-family: var(--font-ui); font-style: italic; font-weight: 600; font-size: var(--fs-nav);
  text-transform: uppercase; letter-spacing: .05em;
}
.nav__links a { position: relative; color: #fff; text-decoration: none; opacity: .92; text-shadow: var(--tsh-nav); padding-bottom: 3px; transition: opacity var(--dur-fast) var(--ease); }
.nav__links a:hover { opacity: 1; }
.nav__links > li > a:not([aria-current="page"])::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: var(--gold);
  transform: scaleX(0); transform-origin: left; transition: transform var(--dur-mid) var(--ease-out);
}
.nav__links > li > a:not([aria-current="page"]):hover::after { transform: scaleX(1); }
.nav__links > li > a[aria-current="page"] { opacity: 1; border-bottom: 3px solid var(--gold); }

/* --- dropdowny w pasku desktop (Obozy / Klub) --- */
.nav__links .nav__has-sub { position: relative; }
.nav__sub-toggle {
  position: relative;
  font: inherit; letter-spacing: inherit; color: #fff; background: none; border: 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: .35em; padding: 0 0 3px; opacity: .92;
  text-transform: uppercase; text-shadow: var(--tsh-nav); -webkit-tap-highlight-color: transparent;
  transition: opacity var(--dur-fast) var(--ease);
}
.nav__sub-toggle:hover { opacity: 1; }
.nav__has-sub[data-active] > .nav__sub-toggle { opacity: 1; }
/* złote podkreślenie grupy: rośnie na hover, trwałe gdy sekcja aktywna (spójne z linkami).
   Selektor kwalifikowany tagiem `button` — to realny element i jednocześnie sygnał
   „podkreślenie afordancji linku/przycisku", ten sam register co podkreślenie linków wyżej. */
button.nav__sub-toggle::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: var(--gold);
  transform: scaleX(0); transform-origin: left; transition: transform var(--dur-mid) var(--ease-out);
}
.nav__has-sub:hover > .nav__sub-toggle::after,
.nav__has-sub[data-active] > .nav__sub-toggle::after { transform: scaleX(1); }
.nav__caret { font-size: .62em; line-height: 1; transition: transform var(--dur-mid) var(--ease-out); }
.nav__has-sub:hover .nav__caret,
.nav__has-sub:focus-within .nav__caret,
.nav__sub-toggle[aria-expanded="true"] .nav__caret { transform: rotate(180deg); }
.nav__sub {
  position: absolute; top: calc(100% + 8px); left: 50%;
  transform: translateX(-50%) translateY(6px);
  min-width: 224px; list-style: none; margin: 0; padding: 6px 0;
  background: var(--ink); border-top: 3px solid var(--gold); box-shadow: var(--sh-header);
  opacity: 0; visibility: hidden; z-index: 70;
  transition: opacity var(--dur-mid) var(--ease-out), transform var(--dur-mid) var(--ease-out), visibility 0s linear var(--dur-mid);
}
.nav__has-sub:hover .nav__sub,
.nav__has-sub:focus-within .nav__sub,
.nav__sub-toggle[aria-expanded="true"] + .nav__sub {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
  transition: opacity var(--dur-mid) var(--ease-out), transform var(--dur-mid) var(--ease-out);
}
.nav__sub li { margin: 0; }
.nav__sub a {
  display: block; padding: 9px 20px; white-space: nowrap; color: #fff; opacity: .82;
  text-shadow: none; font-style: italic;
  transition: opacity var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.nav__sub a::after { display: none; }  /* dropdown-items nie dostają złotego podkreślenia paska */
.nav__sub a:hover { opacity: 1; background: rgba(255,255,255,.08); color: var(--gold); }
.nav__sub a[aria-current="page"] { opacity: 1; color: var(--gold); }
.nav__tail { margin-left: auto; display: flex; align-items: center; gap: 16px; flex: none; }
.nav__toggle { display: none; }

/* mobile nav panel */
.nav__burger {
  display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer;
  padding: 6px; margin: -6px; -webkit-tap-highlight-color: transparent;
}
.nav__burger span { display: block; width: 24px; height: 2.5px; background: #fff; transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease); }
/* Panel musi się przewijać. Przy obu rozwiniętych akordeonach treść ma 986 px na ekranie
   844 px, a `body` jest w tym czasie zablokowane — bez własnego przewijania ostatnie
   pozycje menu były nieosiągalne. `overscroll-behavior` trzyma gest w panelu. */
.nav-panel {
  display: none; position: fixed; inset: 0; z-index: 60; background: var(--ink);
  padding: 88px var(--gutter) 40px; flex-direction: column; gap: 4px;
  overflow-y: auto; overscroll-behavior: contain;
}
.nav-panel.is-open { display: flex; }
.nav-panel a {
  font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-panel-link);
  text-transform: uppercase; color: #fff; text-decoration: none; padding: 12px 0;
  border-bottom: 1px solid var(--hair-ink);
}
.nav-panel a:last-of-type { border-bottom: 0; }
.nav-panel .btn { margin-top: 18px; }

/* --- akordeony w panelu mobilnym (Obozy / Klub) --- */
.nav-panel__group { border-bottom: 1px solid var(--hair-ink); }
.nav-panel__acc {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-panel-link);
  text-transform: uppercase; color: #fff; background: none; border: 0; cursor: pointer;
  padding: 12px 0; text-align: left; -webkit-tap-highlight-color: transparent;
}
.nav-panel__acc .nav__caret { font-size: .5em; color: var(--gold); }
.nav-panel__group[data-active] > .nav-panel__acc { color: var(--gold); }
.nav-panel__sub { display: none; padding: 2px 0 12px 14px; }
.nav-panel__acc[aria-expanded="true"] + .nav-panel__sub { display: block; }
.nav-panel__sub a {
  display: block; font-family: var(--font-ui); font-style: italic; font-weight: 600; font-size: var(--fs-nav);
  letter-spacing: .04em; text-transform: uppercase; color: var(--on-ink-soft); text-decoration: none;
  padding: 9px 0; border-bottom: 0;
}
.nav-panel__sub a:hover, .nav-panel__sub a[aria-current="page"] { color: var(--gold); }
/* `fixed`, nie `absolute`: panel jest teraz kontenerem przewijania, więc krzyżyk przypięty
   do niego odjeżdżałby z treścią. Panel i tak zajmuje cały ekran, więc kotwica jest ta sama. */
.nav-panel__close {
  position: fixed; top: 22px; right: var(--gutter); background: none; border: 0;
  color: #fff; font-size: var(--fs-2xl); line-height: 1; cursor: pointer; font-family: var(--font-body);
}

/* ============================================================================
   HERO
   ============================================================================ */
.hero { position: relative; min-height: clamp(560px, 62vw, 840px); overflow: hidden; color: #fff; }
.hero__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 22%; }
/* Dolna rampa niesie czytelność copy (hero__inner ma align-items: flex-end, więc tekst siedzi
   przy dolnej krawędzi). Rampa zaczyna się wyżej i kończy mocniej niż w wersji pierwotnej, bo
   kadr home to od 12.08.2026 dzieci w BIAŁYCH judogach — pomiar na starym zdjęciu dawał podpisowi
   7,3:1, na nowym 3,3:1 przy tej samej regule. Podniesienie rampy wraca ponad próg WCAG. */
.hero__grad {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(15,15,16,.5) 0, rgba(15,15,16,.14) 20%, rgba(120,10,14,0) 32%, rgba(120,10,14,.54) 64%, rgba(120,10,14,.88) 100%);
}
.hero__topgrad { position: absolute; left: 0; right: 0; top: 0; height: 150px;
  background: linear-gradient(to bottom, rgba(15,15,16,.72) 0, rgba(15,15,16,.5) 55%, rgba(15,15,16,0) 100%); }
.hero__inner { position: relative; z-index: 2; display: flex; align-items: flex-end; gap: 30px; min-height: inherit; padding-block: 26px 38px; }
.hero__copy { max-width: 720px; }
.hero__title { font-size: var(--fs-hero); line-height: .86; color: #fff; text-shadow: var(--tsh-hero); }
.hero__sub { margin-top: 16px; max-width: 52ch; font-size: var(--fs-lead); line-height: 1.7; color: rgba(255,255,255,.92); text-shadow: var(--tsh-lead); }
.hero__btns { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 24px; }
.hero__play { margin-left: auto; display: flex; align-items: center; gap: 12px; color: #fff; text-decoration: none; flex: none; }
.hero__play-ring { width: 64px; height: 64px; border-radius: 50%; border: 2.5px solid #fff; display: grid; place-items: center; flex: none; transition: background var(--dur) var(--ease), transform var(--dur) var(--ease); }
.hero__play:hover .hero__play-ring { background: rgba(255,255,255,.16); transform: scale(1.06); }
.hero__play:active .hero__play-ring { transform: scale(.98); }
.hero__play span { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-sm); line-height: 1.35; text-transform: uppercase; letter-spacing: .08em; }

/* ============================================================================
   TICKER (black bar)
   ============================================================================ */
.ticker { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 14px 26px;
  padding-block: 22px; }
.ticker__label { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-sm); letter-spacing: .1em; text-transform: uppercase; color: #fff; }
.ticker__label--gold { color: var(--gold); }
.ticker__info { font-family: var(--font-body); font-size: var(--fs-sm); color: var(--on-ink-soft); }
.ticker__info b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: #fff; }
.ticker__sep { width: 1px; height: 16px; background: var(--gold); opacity: .8; flex: none; }

/* ============================================================================
   DROGA SMOKA (belt path — variant 9b: photo + ladder)
   ============================================================================ */
.droga { display: grid; grid-template-columns: 1.15fr .85fr; align-items: stretch; }
.droga__photo { position: relative; overflow: hidden; min-height: 456px; }
.droga__photo img { width: 100%; height: 100%; min-height: 456px; object-fit: cover; object-position: 50% 30%; }
.droga__photo-cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 60px 34px 26px;
  background: linear-gradient(to top, rgba(15,15,16,.9) 0, rgba(15,15,16,0) 100%);
}
.droga__photo-cap h2 { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-band); text-transform: uppercase; color: #fff; line-height: .95; }
.droga__photo-cap p { margin-top: 12px; max-width: 40ch; font-size: var(--fs-body); line-height: 1.7; color: rgba(255,255,255,.8); }
.droga__panel { background: var(--ink); padding: 40px 34px 36px; display: flex; flex-direction: column; }
.ladder { display: flex; flex-direction: column; margin-top: 4px; }
.ladder__row { display: flex; align-items: center; gap: 14px; padding: 11px 10px; margin: 0 -10px; border-bottom: 1px solid rgba(255,255,255,.1); transition: background-color var(--dur) var(--ease); }
.ladder__row:last-child { border-bottom: 0; }
.ladder__row:hover { background: rgba(255,255,255,.05); } /* pas reaguje: wiersz lekko rozjaśnia (tekstura, nie afordancja klika) */
/* Zawiązany pas: kolor/gradient z .b-* prześwieca przez maskę-sylwetkę (węzeł + końce).
   Na ciemnym panelu bez obrysu (pasy same wybijają się z inku). */
.ladder__belt { width: 44px; height: 32px; flex: none;
  -webkit-mask: var(--belt-tie) center / contain no-repeat;
          mask: var(--belt-tie) center / contain no-repeat; }
.ladder__name { font-family: var(--font-display); font-style: normal; font-weight: 600; font-size: var(--fs-14); color: #fff; text-transform: uppercase; letter-spacing: .03em; }
.ladder__meta { margin-left: auto; font-family: var(--font-body); font-size: var(--fs-micro); color: var(--on-ink-faint); }
.droga__panel .btn { align-self: flex-start; margin-top: 24px; }

/* belt colour fills (shared) */
.b-white  { background: var(--belt-white); }
.b-wy     { background: linear-gradient(90deg, var(--belt-white) 0 50%, var(--belt-yellow) 50%); }
.b-yellow { background: var(--belt-yellow); }
.b-yo     { background: linear-gradient(90deg, var(--belt-yellow) 0 50%, var(--belt-orange) 50%); }
.b-orange { background: var(--belt-orange); }
.b-og     { background: linear-gradient(90deg, var(--belt-orange) 0 50%, var(--belt-green) 50%); }
.b-green  { background: var(--belt-green); }
.b-blue   { background: var(--belt-blue); }
.b-brown  { background: var(--belt-brown); }
.b-black  { background: var(--belt-black); }

/* ============================================================================
   O KLUBIE (about + signup form)
   ============================================================================ */
/* Un-boxed (gauntlet R6): a full-bleed triptych — solid red copy panel | bright true-colour
   studio photo | red signup form, butted with hard vertical seams. No .wrap, no floating
   rectangle, and NO tint on the photo (two fresh critics flagged the earlier over-photo scrim
   as a red wash where the gis went pink and the image dissolved into the form). Photo leads:
   its column is the widest, flanked by the two red rails. Matches the full-bleed rhythm of
   the news stage / coach-strip / group-strip. */
/* Kolumna z opisem urosła (13.08.2026: klub przysłał własny, czterokapitowy tekst „O klubie"),
   więc czerwony panel dostał więcej światła kosztem obu sąsiadów. */
.about-stage { display: grid; grid-template-columns: 1.3fr 1.35fr .9fr; align-items: stretch; }
.about-stage__copy { background: var(--red); display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-end; padding: clamp(30px, 3.4vw, 54px); }
.about-stage__copy .eyebrow { margin-bottom: 12px; }
/* Nagłówek rail-a ma własny stopień skali (--fs-h2-rail), nie --fs-section: przy stałym rozmiarze
   „JUDO DRAGON DLA KAŻDEGO" łamało się na cztery wiersze poniżej ~1200 px (zmierzone: h2 108 →
   215 px), co podnosiło cały tryptyk o ponad 100 px i zostawiało pustą biel obok zdjęcia. Zawijanie
   jest naturalne (bez <br>), żeby punkt łamania szedł za szerokością kolumny, nie za znacznikiem. */
.about-stage__copy h2 { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-h2-rail); text-transform: uppercase; color: #fff; line-height: .96; margin: 0 0 20px; text-wrap: balance; }
.about-stage__copy p { font-size: var(--fs-body); line-height: 1.8; color: #fff; margin: 0 0 14px; text-wrap: pretty; }
.about-stage__copy .btn { margin-top: 10px; }
/* Wysokość tryptyku niesie teraz KOLUMNA ZE ZDJĘCIEM (wcześniej dyktował ją 560px iframe
   formularza; po zamianie na przycisk kolumna zapisu jest niska, więc zdjęcie musi trzymać
   pełny kadr samo — ~750px na desktopie, jak było wcześniej; mobile ma własny override niżej). */
.about-stage__media { position: relative; overflow: hidden; min-height: clamp(500px, 59vw, 760px); }
.about-stage__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 18%; }
/* Wariant „arkusz": studyjna wycinanka na białym tle. Kadrowanie `cover` ucinałoby skrajne
   osoby z grupy (zdjęcie jest prawie kwadratowe, a kolumna wysoka), więc zdjęcie wchodzi
   CAŁE — a białe tło panelu jest przedłużeniem białego tła studia, więc szew zostaje twardy. */
.about-stage__media--sheet { background: #fff; }
.about-stage__media--sheet img { object-fit: contain; object-position: 50% 100%; padding: clamp(16px, 1.8vw, 30px); }
.signup.about-stage__form { align-self: stretch; display: flex; flex-direction: column; justify-content: center; padding: clamp(30px, 3vw, 50px) clamp(26px, 3vw, 44px); }

/* signup form card */
.signup { background: var(--red); padding: 26px 26px 28px; color: #fff; }
.signup h2 { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-signup-h2); text-transform: uppercase; color: #fff; line-height: 1; margin: 0 0 6px; }
.signup__note { font-size: var(--fs-body-sm); color: var(--on-red-soft); margin-bottom: 18px; }
.signup__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
.signup .field { width: 100%; background: #fff; border: 0; padding: 12px 13px; font-family: var(--font-body); font-size: var(--fs-body-sm); color: var(--ink); margin-bottom: 10px; }
.signup__grid .field { margin-bottom: 0; }
.signup .field::placeholder { color: #9a9a9a; }
select.field { color: #9a9a9a; appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--ink) 50%), linear-gradient(135deg, var(--ink) 50%, transparent 50%); background-position: right 15px center, right 10px center; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
select.field:valid { color: var(--ink); }
textarea.field { resize: vertical; min-height: 52px; }
.signup .btn { width: 100%; }
.signup__legal { font-size: var(--fs-micro); line-height: 1.6; color: var(--on-red-soft); margin-top: 12px; }

/* Zapisy w oknie: karta na czerwieni prowadzi do formularza jednym przyciskiem,
   a sam formularz Google otwiera się w ostylowanym oknie (patrz .signup-modal).
   Klient woli formularz „na stronie", ale bez surowego chromu Google na wejściu. */
.signup__open { margin-top: 4px; }
.signup__alt { font-size: var(--fs-body-sm); color: var(--on-red-soft); margin-top: 14px; }
.signup__alt a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }
.signup__alt a:hover { color: var(--gold); }

/* Fallback bez JS: formularz osadzony inline w białym insecie (jak wcześniej), żeby
   zapisy działały nawet z wyłączonym JavaScriptem. Z JS panel ten jest schowany. */
.signup__embed { background: #fff; border-radius: var(--r); padding: 6px; margin-top: 6px; }
.signup__embed iframe { display: block; width: 100%; height: 560px; border: 0; }

/* ------------------------------------------------------------------ ZAPISY: OKNO (MODAL)
   Natywny <dialog>: fokus-trap, Escape i inertność tła daje przeglądarka. Formularz Google
   ładuje się dopiero przy otwarciu (iframe wstrzykiwany z data-signup-src w site-6b.js),
   więc na wejściu strony nie ma żadnego żądania do Google. Nagłówek 6b (czerwień + Kanit)
   oprawia formularz tak, że „google" widać dopiero w środku okna. */
html:has(.signup-modal[open]) { overflow: hidden; }
.signup-modal {
  width: min(560px, 94vw);
  max-width: 94vw;
  max-height: 92vh;
  padding: 0;
  border: 0;
  background: #fff;
  color: var(--ink);
  box-shadow: var(--sh-cta);
  overflow: hidden;
}
.signup-modal[open] { display: flex; flex-direction: column; }
.signup-modal::backdrop { background: rgba(15, 15, 16, .62); }
.signup-modal__head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--red);
  color: #fff;
  padding: 16px 18px 15px 22px;
}
.signup-modal__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 800;
  font-size: var(--fs-signup-h2);
  text-transform: uppercase;
  color: #fff;
  line-height: 1;
  margin: 0;
}
.signup-modal__x {
  margin-left: auto;
  flex: none;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, .12);
  border: 0;
  color: #fff;
  font-family: var(--font-ui);
  font-size: var(--fs-2xl);
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur) var(--ease);
}
.signup-modal__x:hover { background: rgba(255, 255, 255, .24); }
.signup-modal__x:focus-visible { outline: var(--focus); outline-offset: 2px; }
/* Powrót do wyboru lokalizacji stoi w główce obok krzyżyka, a nie nad formularzem —
   inaczej wjeżdżałby w obszar iframe'a, którego wysokości nie kontrolujemy. */
.signup-modal__back {
  margin-left: auto; flex: none; background: rgba(255, 255, 255, .12); border: 0; color: #fff;
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-12); letter-spacing: .04em;
  padding: 9px 12px; cursor: pointer; transition: background var(--dur) var(--ease);
}
.signup-modal__back:hover { background: rgba(255, 255, 255, .24); }
.signup-modal__back:focus-visible { outline: var(--focus); outline-offset: 2px; }
.signup-modal__back + .signup-modal__x { margin-left: 0; }

/* ---- Krok 1 okna zapisu: wybór lokalizacji ------------------------------
   Dwa przyciski regionów, każdy z listą sal — rodzic nie musi wiedzieć, jak klub
   dzieli rejony, wystarczy, że rozpozna swoją miejscowość. */
.signup-modal__step { flex: 1 1 auto; padding: clamp(20px, 2.4vw, 30px) clamp(18px, 2.2vw, 26px); overflow-y: auto; }
.signup-modal__lead { font-size: var(--fs-body-sm); line-height: 1.65; color: var(--text-soft); margin: 0 0 18px; }
.signup-pick { display: grid; gap: 12px; }
.signup-pick__opt {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--white); border: 2px solid var(--line-strong); padding: 16px 18px;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.signup-pick__opt:hover, .signup-pick__opt:focus-visible { border-color: var(--red); background: rgba(200, 28, 36, .04); }
.signup-pick__opt:focus-visible { outline: var(--focus); outline-offset: 2px; }
.signup-pick__name {
  display: block; font-family: var(--font-display); font-style: italic; font-weight: 800;
  text-transform: uppercase; font-size: var(--fs-coach); line-height: 1.05; color: var(--ink);
}
.signup-pick__list { display: block; margin-top: 6px; font-size: var(--fs-body-sm); line-height: 1.55; color: var(--text-mute); }
/* Ten sam kafelek w roli KOTWICY, nie przycisku — używa go wybór regionu na stronie Dokumentów.
   Wariant istnieje, bo zwykły <a> przynosi podkreślenie i kolor odnośnika, a kafelek ma wyglądać
   identycznie w obu rolach. Dwie kolumny dopiero od 640 px: opcje są dwie, a poniżej tej
   szerokości ich listy miejscowości zaczynają się łamać. */
.signup-pick--links .signup-pick__opt { text-decoration: none; color: inherit; }
@media (min-width: 640px) { .signup-pick--links { grid-template-columns: 1fr 1fr; } }
.signup-modal__hint { margin: 16px 0 0; font-size: var(--fs-12); line-height: 1.6; color: var(--text-mute); }
.signup-modal__hint a { color: var(--red); }
.signup-modal__body { flex: 1 1 auto; min-height: 0; background: #fff; overflow: hidden; }
.signup-modal__body iframe {
  display: block;
  width: 100%;
  height: min(74vh, 720px);
  border: 0;
}

/* ------------------------------------------------------------------ CREDO KLUBU
   Trzy słowa filozofii (tekst od klubu, sierpień 2026). Siatka bierze gotowe .cards-3,
   a karta ten sam kotwiczący idiom co .contact__item / .expert: czerwona linia u góry,
   bez pudełka. Zamknięcie to jedno zdanie w displayu plus pieczęć w wersalikach. */
.credo__item { border-top: 3px solid var(--red); padding-top: 18px; }
.credo__word { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-h2-mid); text-transform: uppercase; color: var(--red); line-height: 1; margin-bottom: 10px; }
.credo__gloss { font-size: var(--fs-body); line-height: 1.7; color: var(--text-soft); text-wrap: pretty; }
.credo__close { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-card); color: var(--ink); line-height: 1.3; max-width: 62ch; margin-top: clamp(26px, 3vw, 40px); text-wrap: pretty; }
.credo__stamp { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin-top: 14px; }

/* ============================================================================
   STATS (black bar with stamp watermark)
   ============================================================================ */
.stats { position: relative; overflow: hidden; }
/* `contain` + środek, bo źródłem jest teraz godło z przezroczystym tłem (crest.png);
   kadrowanie „cover" z przesunięciem było strojone pod plik bez alfy. */
.stats__stamp { position: absolute; right: 34px; top: -34px; width: 200px; height: 200px; object-fit: contain; object-position: center; opacity: .12; pointer-events: none; }
.stats__grid { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); }
.stat { padding: 0 24px; }
.stat + .stat { border-left: 1px solid var(--hair-ink); }
.stat:first-child { padding-left: 0; }
.stat__n { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-band); color: #fff; line-height: 1; }
.stat__l { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .16em; text-transform: uppercase; color: var(--gold); margin-top: 8px; }

/* ============================================================================
   KADRA (coaches, on red)
   ============================================================================ */
.kadra__head { display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap; margin-bottom: 22px; }
.kadra__head h2 { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-section); text-transform: uppercase; color: #fff; line-height: 1; margin: 0; }
.kadra__head p { font-size: var(--fs-sm); line-height: 1.6; color: var(--on-red-soft); max-width: 46ch; }
.kadra__head .link-arrow { margin-left: auto; color: #fff; }
.cards-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; align-items: stretch; }
.coach { display: flex; flex-direction: column; }
.coach__photo { position: relative; }
/* Zdjęcie kadry prowadzi do sylwetki trenera (prośba klubu 22.08.2026: „powinna być
   możliwość kliknięcia w trenera […] żeby przeszło do zakładki naszej"). Link owija samo
   zdjęcie z plakietką, nie całą kartę — w treści karty stoi już link z numerem telefonu,
   a linku w linku HTML nie dopuszcza. */
.coach__link { display: block; color: inherit; text-decoration: none; }
.coach__link:focus-visible { outline: 3px solid var(--gold); outline-offset: -3px; }
/* Kadr od GÓRY, nie od środka. Wszystkie trzy zdjęcia kadry to ujęcia całej sylwetki
   (albo ukłon z twarzą przy górnej krawędzi), więc domyślne `50% 50%` ścinało głowy —
   zgłoszone przez klienta i zmierzone: widoczny pas źródła zaczynał się na 15–18% wysokości,
   a czubek głowy siedzi na 0–14%. Kotwiczenie do góry trzyma się na każdej szerokości,
   bo wysokość kadru zmienia się z rozdzielczością, a procent obcięcia nie. */
.coach__photo img { width: 100%; height: 320px; object-fit: cover; object-position: 50% 0; }
.coach__plate { position: absolute; left: 0; right: 0; bottom: 0; background: var(--ink-scrim); padding: 12px 16px; }
.coach__name { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-coach); text-transform: uppercase; color: #fff; line-height: 1; }
.coach__where { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase; color: var(--gold); margin-top: 5px; }
.coach__body { background: #fff; padding: 16px; font-size: var(--fs-body-sm); line-height: 1.7; color: var(--text-body); flex: 1; }
.coach__body b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--red); }
/* KADRA as one edge-to-edge portrait strip (gauntlet R3): the coach photos bleed to the
   section edges in a continuous band with the names on their ink feet — the hero's
   type-on-photo device at tile scale — over a continuous INK base (not three white
   boxes), so the section reads as one built block on the red wall instead of a card grid.
   All copy is kept: names, locations, phones and the roster stay verbatim. */
/* Ink keyline caps the strip's top edge (gauntlet R7): the leader row keeps its mixed
   ground on purpose — Dominik has only red-wall photos, so his card would otherwise
   dissolve into the red section above (red bg on red wall). The cap closes the block's
   top uniformly, so every card reads as a deliberate portrait set on the wall (ink top +
   ink feet/bodies) rather than floating. Editorial call: both founder-brothers stay. */
.coach-strip { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; align-items: stretch; border-top: 4px solid var(--ink); }
.coach-strip .coach__photo img { height: clamp(360px, 32vw, 470px); }
.coach-strip .coach__body { background: var(--ink); color: var(--on-ink-soft); border-left: 1px solid var(--hair-ink); }
.coach-strip .coach:first-child .coach__body { border-left: 0; }
.coach-strip .coach__body b { color: var(--gold); }
.coach-strip .coach__body a { color: #fff; }

/* ============================================================================
   GRUPY I GRAFIK
   ============================================================================ */
.group-card { background: #fff; }
.group-card img { width: 100%; height: 200px; object-fit: cover; }
.group-card__body { padding: 18px 20px 22px; }
.tag { display: inline-block; background: var(--red); color: #fff; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .14em; text-transform: uppercase; padding: 6px 10px; margin-bottom: 12px; }
.group-card__title { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-card); text-transform: uppercase; color: var(--ink); line-height: 1; margin-bottom: 10px; }
.group-card__meta { font-size: var(--fs-body-sm); line-height: 1.9; color: var(--text-soft); border-top: 1px solid var(--line); padding-top: 10px; }
/* GRUPY as one edge-to-edge age-panel band (gauntlet R4): same un-boxing as KADRA — the
   real group photos butt together and bleed to the section edges, the age badge sits on the
   image, the Kanit group name + factual sublines on a continuous ink base. Kept on the PAPER
   wall (not red) on purpose: KADRA directly above is already the red wall, so a second red
   band here would erase the section boundary — the bright photos on an ink base pop on cream
   and preserve the red→cream→ink rhythm. All copy verbatim. */
.group-strip { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; align-items: stretch; }
.group-strip .group-card { display: flex; flex-direction: column; background: var(--ink); }
.group-card__photo { position: relative; overflow: hidden; }
.group-strip .group-card__photo img { width: 100%; height: clamp(300px, 26vw, 380px); object-fit: cover; display: block; }
/* Punkt ostrości per kafel. Zdjęcia są pionowe (2000×2395 i 1024×1200), rubryka pozioma,
   więc kadr przez środek ścinał głowy: przy 390 px nadmiar to 167 px i 157 px wysokości,
   a twarze siedzą w górnych 5–21% pliku. Wartości policzone z proporcji plików, nie z oka.
   Pierwszy kafel (dziewczynka z maskotką) zostaje na środku — tam kadr jest pełny. */
.group-strip .group-card:nth-child(2) .group-card__photo img { object-position: 50% 14%; }
.group-strip .group-card:nth-child(3) .group-card__photo img { object-position: 50% 4%; }
.group-card__photo .tag { position: absolute; top: 0; left: 0; margin: 0; }
.group-strip .group-card__body { flex: 1; padding: 16px 18px 20px; background: var(--ink); border-left: 1px solid var(--hair-ink); }
.group-strip .group-card:first-child .group-card__body { border-left: 0; }
.group-strip .group-card__title { color: #fff; }
.group-strip .group-card__meta { color: var(--on-ink-soft); border-top-color: var(--hair-ink); }
/* In a continuous strip the per-card hover-lift would tear the band open — keep only the
   image zoom, drop the translate/shadow (applies to KADRA + GRUPY strips). */
.coach-strip .coach:hover, .coach-strip .coach:focus-within,
.group-strip .group-card:hover, .group-strip .group-card:focus-within { translate: 0; box-shadow: none; }

/* ============================================================================
   WIDEO (Zobacz nas)
   ============================================================================ */
.video { display: grid; grid-template-columns: .78fr 1.22fr; align-items: stretch; }
.video__list { padding: 46px 34px 48px 0; display: flex; flex-direction: column; }
.video__list h2 { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-section); text-transform: uppercase; color: #fff; line-height: .98; margin: 0 0 24px; }
/* Wiersze są odnośnikami, nie ozdobą — klient zgłosił, że lista wygląda na wybór filmu,
   a klikać się nie da. Stąd kursor, stan najechania i widoczny fokus. */
.playrow { display: flex; align-items: center; gap: 12px; padding: 11px 0; border-top: 1px solid var(--hair-ink); text-decoration: none; cursor: pointer; transition: background var(--dur) var(--ease); }
.playrow:last-of-type { border-bottom: 1px solid var(--hair-ink); margin-bottom: 22px; }
.playrow:hover, .playrow:focus-visible { background: rgba(255,255,255,.06); }
.playrow:focus-visible { outline: var(--focus); outline-offset: -2px; }
.playrow__tri { width: 0; height: 0; border-left: 9px solid rgba(255,255,255,.5); border-top: 6px solid transparent; border-bottom: 6px solid transparent; flex: none; transition: border-left-color var(--dur) var(--ease), translate var(--dur) var(--ease); }
.playrow--active .playrow__tri, .playrow:hover .playrow__tri { border-left-color: var(--gold); }
/* Drgnięcie w prawo jedzie translate, nie odstępem — układ zostaje nietknięty (jak .camp .more). */
.playrow:hover .playrow__tri, .playrow:hover .playrow__t { translate: 4px; }
.playrow__t { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-body); color: rgba(255,255,255,.82); text-transform: uppercase; letter-spacing: .03em; transition: color var(--dur) var(--ease), translate var(--dur) var(--ease); }
.playrow--active .playrow__t, .playrow:hover .playrow__t { color: #fff; }
/* Znacznik jedzie z aktywnym wierszem: siedzi w każdym, widać go tylko w tym granym. */
.playrow__now { display: none; margin-left: auto; font-size: var(--fs-label); color: var(--gold); }
.playrow--active .playrow__now { display: block; }
.video__list .btn { align-self: flex-start; }
.video__frame { position: relative; min-height: 460px; background: var(--ink); overflow: hidden; }
.video__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Video facade (gauntlet R8): the frame carries the club film's OWN real poster frame + a
   hard red square play-glyph; the live YouTube embed (same film) is swapped in on click.
   Kills the dead black frame that showed before the 3rd-party iframe painted, keeps the
   author's real video, and drops eager 3rd-party load. Sharp corners, brand red — 6b. */
.video__play { position: absolute; inset: 0; width: 100%; height: 100%; display: block; overflow: hidden; text-decoration: none; cursor: pointer; }
.video__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 34%; display: block; transition: scale var(--dur) var(--ease); }
.video__playglyph { position: absolute; top: 50%; left: 50%; translate: -50% -50%; width: clamp(66px, 6vw, 88px); height: clamp(66px, 6vw, 88px); background: var(--red); display: grid; place-items: center; box-shadow: var(--sh-cta); transition: background var(--dur) var(--ease), scale var(--dur) var(--ease); }
.video__playglyph::before { content: ""; width: 0; height: 0; border-left: clamp(20px, 2vw, 26px) solid #fff; border-top: clamp(13px, 1.3vw, 17px) solid transparent; border-bottom: clamp(13px, 1.3vw, 17px) solid transparent; margin-left: 6px; }
.video__play:hover .video__poster, .video__play:focus-visible .video__poster { scale: 1.04; }
.video__play:hover .video__playglyph, .video__play:focus-visible .video__playglyph { background: var(--red-hover); scale: 1.06; }
.video__play:focus-visible { outline: var(--focus); outline-offset: -4px; }

/* ============================================================================
   AKTUALNOŚCI (news) + camps
   ============================================================================ */
/* Full-bleed news stage (gauntlet R2): the squad photo bleeds to the section edges and
   CARRIES the opener, with the mega head keyed straight onto it — the hero's type-on-photo
   device (R1 established the scale; R2 gives it a photograph to punctuate). The scrim is
   the RED WALL, not ink: the club's photos are bright studio white, so an ink gradient
   would go muddy — the "Czerwona ściana" identity IS the legibility bed, and a soft ink
   top/bottom just melts the white frame into the surrounding black band. */
.news__stage {
  position: relative; overflow: hidden; display: flex;
  margin-top: calc(var(--band-y) * -1);            /* cancel band top-pad → flush bleed */
  margin-bottom: clamp(30px, 4vw, 52px);
  /* Wysoka scena, żeby ekipa szła „po całej długości" — pełne sylwetki, nie ucięte na udach
     (zdjęcie jest niemal kwadratowe, więc szeroki kadr mocno je przycinał). */
  min-height: clamp(460px, 70vw, 900px);
}
.news__stage-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 12%;   /* headroom u góry, a niżej odsłania nogi/pasy */
}
.news__stage-grad {
  position: absolute; inset: 0;
  /* Scrim PIONOWY, nie szew w poziomie: zdjęcie ekipy ma iść od krawędzi do krawędzi jak
     w każdej innej sekcji (decyzja usera 13.08.2026 — wcześniejszy butt-join 90deg zasłaniał
     lewe 55% litą czerwienią i sekcja czytała się jako pół-plansza, nie jako pasmo foto).
     Łóżko czytelności dalej robi CZERWIEŃ, nie ink — zdjęcia klubu są jasne, studyjnie białe,
     więc ciemny gradient robi z nich błoto. Ten sam układ co `.hero__grad`: lekkie
     przyciemnienie góry pod nawigację, czysty środek, czerwień narastająca ku dołowi
     pod nagłówek. */
  background: linear-gradient(to bottom,
    rgba(15,15,16,.34) 0%, rgba(15,15,16,.10) 16%,
    rgba(120,10,14,0) 34%, rgba(120,10,14,.50) 62%,
    rgba(160,12,18,.86) 84%, var(--red) 100%);
}
.news__stage-inner {
  position: relative; z-index: 2; align-self: flex-end;
  width: 100%; max-width: var(--wrap); margin-inline: auto;
  padding-inline: var(--gutter); padding-block: clamp(26px, 4vw, 46px);
  display: flex; flex-wrap: wrap; align-items: flex-end;
}
.news__stage-inner h2 {
  flex-basis: 100%; margin: 0 0 8px; max-width: 22ch;
  /* fs-hero-sub, nie fs-mega: masthead newsów NIE może przebijać H1 hero (inwersja hierarchii).
     max-width w ch, nie w %, bo od 13.08.2026 nagłówek siedzi na czerwonym dole zdjęcia,
     a nie w lewym bloku — łamie się po treści, nie po położeniu szwu. */
  font-size: var(--fs-hero-sub); line-height: .95; letter-spacing: -.005em;
  color: #fff; text-shadow: var(--tsh-hero);
}
.news__stage-inner .link-arrow {
  color: #fff; text-shadow: var(--tsh-lead);
  /* Do prawej krawędzi wrapu, nie tuż za nagłówkiem: box h2 kończy się na 22ch, więc bez
     tego „Wszystkie" wisiało w połowie sceny i czytało się jak zgubiony element. Na prawej
     krawędzi jest to znany układ „tytuł działu ↔ zobacz wszystko". */
  margin-left: auto;
}
.news-card { background: #fff; color: var(--ink); text-decoration: none; display: block; }
.news-card img { width: 100%; height: 290px; object-fit: cover; }
.news-card__body { padding: 14px 16px; }
.news-card__kicker { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .12em; color: var(--red); margin-bottom: 5px; }
.news-card__title { font-family: var(--font-display); font-style: normal; font-weight: 600; font-size: var(--fs-17); line-height: 1.25; color: var(--ink); }

.gold-rule { height: 2px; margin-block: 26px 0; background: linear-gradient(90deg, rgba(255,255,255,.2), rgba(242,201,76,.95) 45%, rgba(255,255,255,.2)); transform: skewY(-.35deg); }

.camps { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; margin-top: clamp(28px, 3.5vw, 40px); }
.camp { position: relative; height: 340px; overflow: hidden; text-decoration: none; }
.camp img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transform: scale(var(--iz, 1)); transition: transform var(--dur-slow) var(--ease-out); }
/* Ta sama usterka kadru co w pasie grup: pionowe pliki (2000×2759 i 2000×2306) w poziomej
   rubryce 340 px. Nadmiar to 467 px i 335 px na 1440, więc kadr przez środek ścinał głowy.
   Twarze siedzą w górnych 4–13% pliku; podpis kafla i tak zasłania dół, więc przesunięcie
   w górę nic nie kosztuje. Wartości policzone z proporcji, nie dobrane okiem. */
.camps .camp:nth-child(1) img { object-position: 50% 5%; }
.camps .camp:nth-child(2) img { object-position: 50% 10%; }
.camp:hover img, .camp:focus-visible img { --iz: 1.05; }
.camp__season { position: absolute; left: 0; top: 0; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-15); text-transform: uppercase; letter-spacing: .08em; padding: 10px 18px; }
.camp__season--summer { background: var(--gold); color: var(--ink); }
.camp__season--winter { background: #fff; color: var(--red); }
.camp__cap { position: absolute; left: 0; right: 0; bottom: 0; background: var(--ink-scrim); padding: 16px 20px; }
.camp__cap-row { display: flex; align-items: baseline; }
.camp__cap-row .t { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-card); color: #fff; text-transform: uppercase; }
.camp__cap-row .more { margin-left: auto; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-eyebrow); letter-spacing: .1em; color: var(--gold); text-transform: uppercase; display: inline-block; transition: transform var(--dur-mid) var(--ease-out); }
.camp:hover .camp__cap-row .more, .camp:focus-visible .camp__cap-row .more { transform: translateX(4px); }
.camp__cap p { font-size: var(--fs-12); color: #bbb; margin-top: 4px; }

/* ============================================================================
   RUCH ZDJĘĆ — hover-zoom (baza, działa też bez JS/przy reduced-motion, gdzie
   globalny kill-switch skraca czas do zera). Kadr przycina powiększenie i wjazd
   reveal; skala jedzie przez --iz, żeby komponować się z reveal na scrollu
   (transform reveal = translateY(..) scale(var(--iz))). Reveal → sekcja MOTION.
   ============================================================================ */
.group-card, .news-card, .coach__photo, .about-stage__media { overflow: hidden; }
.group-card img, .news-card img, .coach__photo img, .trener__photo img, .about-stage__media img, .droga__photo img {
  transform: scale(var(--iz, 1));
  transition: transform var(--dur-slow) var(--ease-out);
}
.group-card:hover img, .news-card:hover img, .coach:hover .coach__photo img,
.trener:hover .trener__photo img, .trener:focus-within .trener__photo img,
.about-stage__media:hover img, .droga__photo:hover img { --iz: 1.05; }

/* ============================================================================
   GDZIE TRENUJEMY (locations + map)
   ============================================================================ */
.loc__item { border-top: 1px solid var(--line); padding: 16px 0; }
.loc__item:last-of-type { border-bottom: 1px solid var(--line); }
.loc__name { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-loc); text-transform: uppercase; color: var(--ink); margin-bottom: 4px; }
.loc__detail { font-size: var(--fs-body-sm); line-height: 1.7; color: var(--text-soft); }
.loc__detail b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--red); }
.loc__detail b.soft { color: var(--text-faint); }
.loc__hint { display: flex; align-items: center; gap: 12px; margin-top: 18px; }
.loc__pin { width: 40px; height: 40px; border-radius: 50%; background: var(--ink); border: 2.5px solid #fff; box-shadow: var(--sh-pin); overflow: hidden; flex: none; display: grid; place-items: center; }
.loc__pin img { width: 38px; height: 39px; object-fit: contain; }
.loc__hint p { font-size: var(--fs-12); line-height: 1.55; color: var(--text-mute); }
/* Klastry lokalizacji w rzędzie + mapa na całą szerokość.
   Mapa jest bezpośrednim dzieckiem sekcji (poza .wrap), więc idzie od krawędzi
   do krawędzi bez 100vw — zero ryzyka poziomego przewijania. */
.loc-clusters { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
.loc-clusters .loc__item { border-top: 3px solid var(--red); border-bottom: 0; padding: 14px 0 0; }

/* ---- Interaktywne LOKALIZACJE: akordeon sal (natywne <details>) --------------------
   „Wybierz lokalizację → rozwiń plan sali" (adres, trener, kontakt). Mechanika i tokeny
   spójne z .belt-acc (egzaminy); grupowane po rejonie trenera. Domyślnie widoczne
   (poza BLOCK_SEL reveal), więc otwarcie/kotwica działa bez odsłaniania. */
.loc-group + .loc-group { margin-top: var(--band-y); }
.loc-group__title { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-h2-mid); text-transform: uppercase; color: var(--text); line-height: 1.05; margin: 0 0 2px; }
.loc-group__meta { display: block; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .08em; text-transform: uppercase; color: var(--red); margin-bottom: 16px; }
.loc-acc { display: flex; flex-direction: column; gap: 12px; }
/* Dzielnice w obrębie miasta (A4: Gdynia rozwija się pogrupowana w dzielnice, nie płaską listą). */
.loc-district { display: flex; flex-direction: column; gap: 12px; }
.loc-district + .loc-district { margin-top: 28px; }
.loc-district__title { display: flex; align-items: center; gap: 10px; font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-card); letter-spacing: .06em; text-transform: uppercase; color: var(--text); margin: 0; }
.loc-district__title::before { content: ""; flex: 0 0 auto; width: 18px; height: 3px; background: var(--red); }
.loc-acc__item { background: #fff; border: 1px solid var(--line); }
.loc-acc__item[open] { border-color: var(--line-strong); }
.loc-acc__sum { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 16px; padding: 15px 20px; -webkit-tap-highlight-color: transparent; transition: background-color var(--dur) var(--ease); }
.loc-acc__sum::-webkit-details-marker { display: none; }
.loc-acc__sum:hover { background: rgba(15,15,16,.03); }
.loc-acc__sum:focus-visible { outline: var(--focus); outline-offset: -3px; }
.loc-acc__head { display: flex; flex-direction: column; gap: 3px; }
.loc-acc__name { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-coach); text-transform: uppercase; color: var(--ink); line-height: 1.05; }
.loc-acc__where { font-family: var(--font-body); font-size: var(--fs-body-sm); color: var(--text-mute); }
.loc-acc__caret { margin-left: auto; color: var(--red); font-size: .8em; transition: transform var(--dur-mid) var(--ease-out); flex: none; }
.loc-acc__item[open] .loc-acc__caret { transform: rotate(180deg); }
.loc-acc__body { padding: 4px 20px 20px; border-top: 1px solid var(--line); }
.loc-acc__addr { font-family: var(--font-display); font-style: normal; font-weight: 600; font-size: var(--fs-body); color: var(--red); margin: 12px 0 2px; }
.loc-acc__meta { font-size: var(--fs-body-sm); line-height: 1.65; color: var(--text-soft); margin: 2px 0; }
.loc-acc__meta b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--text); }
.loc-acc__hours { font-size: var(--fs-body-sm); line-height: 1.55; color: var(--text-mute); margin: 8px 0 0; }
.loc-acc__cta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.loc-map-full { position: relative; height: clamp(360px, 54vh, 560px);
  margin-block: clamp(1.25rem, 3vw, 2rem); border-block: 3px solid var(--ink);
  overflow: hidden; background: #e9e5dd; }
.loc-map-full iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }

/* Homepage: mapa jako PODŁOGA sekcji, a karta informacyjna NA NIĄ nachodzi
   (relacja warstw zamiast osobnego, pustego bloku klastrów nad mapą). Mapa styka
   się krawędź w krawędź z sąsiednimi pasmami — koniec kremowej szczeliny.
   Wysokość pasa ustala KARTA, nie mapa: karta zostaje w przepływie, a mapa rozciąga
   się absolutnie na całą sekcję i przyjmuje jej wysokość. Odwrotnie było wcześniej
   (mapa 66vh, karta w warstwie `absolute`) i po dołożeniu dzielnic ze szkołami karta
   urosła ponad pas — zmierzone od 861 px wzwyż: pas 594 px, karta 862–981 px,
   wystawało 134–194 px nad sekcję i tyle samo pod nią, na czerwone CTA. */
.loc-stage { position: relative; background: var(--ink); }
.loc-stage .loc-map-full--stage { position: absolute; inset: 0; height: auto; margin-block: 0; border-block: 0; }
.loc-overlay { position: relative; z-index: 3; display: flex; align-items: center;
  min-height: clamp(460px, 66vh, 660px); padding-block: clamp(32px, 4vw, 56px); pointer-events: none; }
.loc-panel { pointer-events: auto; width: clamp(300px, 34vw, 440px); max-width: 100%;
  background: var(--ink); color: var(--on-ink); border-top: 4px solid var(--red);
  padding: clamp(22px, 2.2vw, 32px); box-shadow: var(--sh-cta); }
.loc-panel .sec-head { margin-bottom: 16px; }
.loc-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: 13px; }
.loc-lines li { font-size: var(--fs-body-sm); line-height: 1.6; color: var(--on-ink-soft);
  border-top: 1px solid var(--hair-ink); padding-top: 12px; }
.loc-lines li:first-child { border-top: 0; padding-top: 0; }
.loc-lines__name { display: block; font-family: var(--font-display); font-style: italic; font-weight: 800;
  text-transform: uppercase; font-size: var(--fs-loc); color: #fff; margin-bottom: 3px; }
.loc-lines a { color: var(--gold); font-family: var(--font-display); font-style: normal; font-weight: 600; text-decoration: none; }
.loc-lines a:hover { text-decoration: underline; }
/* Dzielnica z listą szkół w swojej linii. Klub nazywa miejsca w formularzu zapisu parą
   „dzielnica – szkoła z adresem"; skrót na stronie głównej ma czytać się tak samo. */
.loc-lines__spot { display: block; margin-top: 3px; }
.loc-lines__spot b { color: #fff; font-weight: 700; }
/* Trener prowadzący rejon schodzi do własnej linii — nazwa sali i numer telefonu w jednym
   ciągu czytały się jak jedno zdanie, a to dwie różne informacje. */
.loc-lines__who { display: block; margin-top: 5px; font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-12); letter-spacing: .04em; color: var(--on-ink-mute); }
.loc-panel__more { display: inline-block; margin-top: 16px; color: var(--gold); }
.loc-panel__hint { display: flex; align-items: center; gap: 12px; margin: 16px 0 0;
  font-size: var(--fs-12); line-height: 1.5; color: var(--on-ink-mute); }

/* ============================================================================
   SEASON CTA
   ============================================================================ */
.season { position: relative; display: flex; align-items: center; gap: 30px; }
/* Maskotka w całości w paśmie — band__pad rośnie do jej wysokości; żadnego ujemnego
   marginesu (wcześniej -16px + overflow:hidden ucinały łapy dragonka na szwie band→stopka). */
.season__mascot { width: clamp(112px, 15vw, 190px); height: auto; object-fit: contain; flex: none; }
.season__body h2 { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-section); text-transform: uppercase; color: #fff; line-height: .9; margin: 0; }
.season__body p { font-size: var(--fs-14); line-height: 1.7; color: var(--on-red-soft); margin-top: 10px; max-width: 52ch; }
.season .btn { margin-left: auto; white-space: nowrap; }

/* ============================================================================
   SPONSORZY
   ============================================================================ */
.sponsors { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; }
.sponsors__label { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .2em; text-transform: uppercase; color: var(--text-mute); flex: none; }
.sponsors__grid { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; flex: 1; min-width: 260px; }
/* realny logotyp partnera na jego własnym tle (biel PGZ / magenta Hello Gummies / czerń Elkarent),
   ujednolicony wysokością i cienką ramką — klasyczna ściana sponsorów. */
.sponsors__link {
  display: inline-flex; align-items: center; border: 1px solid var(--line); text-decoration: none; overflow: hidden;
  transition: border-color var(--dur-mid) var(--ease-out), transform var(--dur-mid) var(--ease-out);
}
.sponsors__link:hover, .sponsors__link:focus-visible { border-color: var(--red); transform: translateY(-2px); }
.sponsors__link img { height: 44px; width: auto; display: block; }
.sponsors__more { margin-left: auto; flex: none; }

/* ============================================================================
   PODSTRONY: karty treści (sponsorzy, kontakt) — wspólny język komponentów
   ============================================================================ */
.cards-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: stretch; }

/* Plakaty planów w „Innych sekcjach" stoją obok siebie w dwóch proporcjach naraz — pionowy
   1000×1415 i poziomy 1693×929. Przy równych torach niższy zostawia pod sobą pustkę, więc tory
   dobrane z proporcji obu. Reguła mobilna niżej w tym arkuszu zwija siatkę do jednej kolumny
   poniżej 600 px i ma to zostać — dlatego jest to KLASA, a nie styl liniowy: liniowy bił media
   query i na telefonie zostawiał plakaty w skali znaczka pocztowego. */
.cards-2--plakaty { grid-template-columns: 28fr 72fr; }

/* — sponsor główny (na paśmie ink): realny logotyp PGZ na białej płycie — */
.sponsor-lead { max-width: 72ch; }
.sponsor-lead__logo { display: inline-block; background: #fff; padding: 20px 26px; margin-bottom: 24px; }
.sponsor-lead__logo img { display: block; height: 60px; width: auto; max-width: 100%; }
.sponsor-lead__desc { font-size: var(--fs-lead); line-height: 1.65; color: rgba(255,255,255,.86); margin: 0 0 26px; }

/* — karta partnera (na paśmie paper): logotyp na własnym tle + opis — */
.sponsor-card { background: #fff; border: 1px solid var(--line); padding: 26px 24px; display: flex; flex-direction: column; align-items: flex-start; }
.sponsor-card__logo { height: 72px; display: flex; align-items: center; margin-bottom: 18px; }
.sponsor-card__logo img { max-height: 72px; max-width: 100%; width: auto; display: block; }
.sponsor-card__desc { font-size: var(--fs-body-sm); line-height: 1.7; color: var(--text-body); margin: 0 0 18px; flex: 1; }

/* — dofinansowanie — */
.sponsor-fund { border-top: 3px solid var(--red); padding-top: 22px; max-width: 76ch; }
.sponsor-fund__text { font-size: var(--fs-body); line-height: 1.75; color: var(--text-body); margin-top: 10px; }
.sponsor-fund__text b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--red); }

/* — kontakt: karta danych klubu — */
.contact { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.contact__item { border-top: 3px solid var(--red); padding-top: 16px; }
.contact__k { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .14em; text-transform: uppercase; color: var(--text); margin-bottom: 8px; }
.contact__v { font-size: var(--fs-body); line-height: 1.6; color: var(--text-body); }
.contact__v a { color: var(--red); text-decoration: none; }
.contact__v a:hover { text-decoration: underline; }
.contact__v b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--ink); }
.contact__sub { display: block; font-size: var(--fs-body-sm); color: var(--text-soft); }
/* czytelność listy adresów pod mapą — poprawka klubu 19.08 („adresy pod mapką nieczytelne”).
   Nazwa lokalizacji (contact__k) miała kontrast 4,29:1 @11px — poniżej WCAG AA; adres wyszukiwawczy
   dostaje drobny bump rozmiaru, sub ciemniejszy odcień. Zakres tylko sekcja pod mapą. */
.loc-sec .contact__k   { color: var(--text); }
.loc-sec .contact__v   { font-size: 0.9375rem; }
.loc-sec .contact__sub { color: var(--text-body); }

/* — trenerzy: profile z portretem (podstrona) —
   Portret = realna twarz z sesji klienta (coach-1..8). Parowanie twarz↔nazwisko PROWIZORYCZNE
   (data-verify na karcie), PRÓCZ coach-5 Siejka i coach-6 Błoński (potwierdzone napisem na pasie).
   Alt niepotwierdzonych = generyczny „Trener…", by nie zaklinać nazwiska. Karta: portret, nazwisko, rola, bio.
   Bez ramki i czerwonego paska-akcentu — kotwicą jest pełnokrawędziowy portret (ten sam idiom co .group-card):
   biała płyta zostaje, obrys znika, więc karta czyta się jak zdjęciowy profil, nie jak „bezpieczne pudełko". */
.trener-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: stretch; }
.trener {
  background: #fff;
  padding: 28px 28px 24px; display: flex; flex-direction: column;
}
.trener__photo { margin: -28px -28px 20px; aspect-ratio: 4 / 3; overflow: hidden; background: var(--ink); }
.trener__photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 22%; }
/* Trener bez potwierdzonego portretu dostaje płytę z inicjałami zamiast cudzej twarzy —
   ten sam idiom co .news-card__plate w kronice. Wchodzi zdjęcie, gdy klub je przyśle. */
.trener__plate {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  background: var(--ink); color: var(--gold);
  font-family: var(--font-display); font-style: italic; font-weight: 800;
  font-size: var(--fs-section); letter-spacing: .02em; line-height: 1;
}
.trener__tag {
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label);
  letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin-bottom: 12px;
}
.trener__name {
  font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-card);
  text-transform: uppercase; color: var(--ink); line-height: 1;
}
.trener__role {
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-body-sm);
  color: var(--text-mute); margin-top: 7px; line-height: 1.4;
}
.trener__bio { font-size: var(--fs-body-sm); line-height: 1.75; color: var(--text-body); margin-top: 16px; }
.trener__bio p { margin: 0 0 10px; }
.trener__bio p:last-child { margin-bottom: 0; }
.trener__bio a { color: var(--red); }
.trener__quote {
  font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-coach);
  line-height: 1.2; letter-spacing: .005em; color: var(--red); text-transform: uppercase;
  margin-top: 4px;
}
.trener__foot {
  margin-top: auto; padding-top: 16px; border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px;
}
.trener__loc { font-size: var(--fs-body-sm); line-height: 1.6; color: var(--text-soft); }
.trener__loc b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--ink); }
.trener__tel {
  margin-left: auto; font-family: var(--font-display); font-style: italic; font-weight: 800;
  font-size: var(--fs-15); color: var(--red); text-decoration: none; white-space: nowrap;
}
.trener__tel:hover { text-decoration: underline; }

/* — mediaband: pełnoszerokościowe pasmo ze zdjęciem + opcjonalny podpis (podstrony).
   Umieszczać jako bezpośrednie dziecko sekcji .band (poza .wrap) → od krawędzi do krawędzi. */
.mediaband { position: relative; overflow: hidden; }
.mediaband img { width: 100%; height: clamp(260px, 42vw, 460px); object-fit: cover; display: block; }
.mediaband__cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 48px var(--gutter) 24px;
  background: linear-gradient(to top, rgba(15,15,16,.82), rgba(15,15,16,0));
  font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-h2-mid);
  text-transform: uppercase; color: #fff; line-height: 1; text-wrap: balance;
}

/* — karty opinii ekspertów (Dlaczego judo) — bez ramki: kotwica to czerwona linia
   u góry (ten sam idiom co .contact__item / .sponsor-fund) + cudzysłów w złocie.
   Cytat siedzi wprost na paśmie, nie w białym pudełku — po odsądzeniu „bezpiecznej kraty". */
.experts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; align-items: stretch; }
.expert { border-top: 3px solid var(--red); padding: 20px 0 0; display: flex; flex-direction: column; }
.expert__n { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-band); color: var(--gold); line-height: 1; margin-bottom: 16px; }
.expert__quote { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-card); color: var(--ink); line-height: 1.12; }
.expert__src { margin-top: auto; padding-top: 18px; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase; color: var(--red); line-height: 1.45; }

/* — egzaminy: accordion stopni (native <details>) —
   Każdy pas: swatch koloru + KYU + nazwa; po rozwinięciu część teoretyczna i praktyczna. */
.belt-acc { display: flex; flex-direction: column; gap: 12px; }
.belt-acc__item { background: #fff; border: 1px solid var(--line); }
.belt-acc__item[open] { border-color: var(--line-strong); }
.belt-acc__sum { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 16px; padding: 16px 22px; -webkit-tap-highlight-color: transparent; }
.belt-acc__sum::-webkit-details-marker { display: none; }
.belt-acc__sum:focus-visible { outline: var(--focus); outline-offset: -3px; }
/* Wariant B: zawiązany pas jako plansza-chart na jasnej podstronie egzaminów.
   Obrys (4× drop-shadow) daje krawędź białemu/żółtemu pasowi na papierze. */
.belt-acc__swatch { width: 56px; height: 40px; flex: none;
  -webkit-mask: var(--belt-tie) center / contain no-repeat;
          mask: var(--belt-tie) center / contain no-repeat;
  filter: drop-shadow(.7px 0 0 var(--belt-line)) drop-shadow(-.7px 0 0 var(--belt-line))
          drop-shadow(0 .7px 0 var(--belt-line)) drop-shadow(0 -.7px 0 var(--belt-line)); }
.belt-acc__kyu { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .1em; text-transform: uppercase; color: var(--text-mute); flex: none; }
.belt-acc__name { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-coach); text-transform: uppercase; color: var(--ink); line-height: 1; }
.belt-acc__caret { margin-left: auto; color: var(--red); font-size: .8em; transition: transform var(--dur-mid) var(--ease-out); flex: none; }
.belt-acc__item[open] .belt-acc__caret { transform: rotate(180deg); }
/* Pas reaguje pod kursorem/fokusem: węzeł „dociąga się" (scale — osobna właściwość,
   nie koliduje z filtrem-obrysem swatcha ani z transformem-obrotem karetki). */
.belt-acc__sum { transition: background-color var(--dur) var(--ease); }
.belt-acc__sum:hover { background: rgba(15,15,16,.03); }
.belt-acc__swatch { transition: scale var(--dur) var(--ease); }
.belt-acc__sum:hover .belt-acc__swatch,
.belt-acc__item:focus-within .belt-acc__swatch { scale: 1.1; }
.belt-acc__body { padding: 6px 22px 24px; border-top: 1px solid var(--line); display: grid; grid-template-columns: 1fr 1fr; gap: 30px; }
.belt-part h3 { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 16px 0 12px; }
.belt-part h4 { font-family: var(--font-display); font-style: normal; font-weight: 600; font-size: var(--fs-14); letter-spacing: .02em; text-transform: uppercase; color: var(--ink); margin: 14px 0 6px; }
.belt-terms { margin: 0; }
.belt-terms div { display: flex; gap: 8px; padding: 3px 0; font-size: var(--fs-body-sm); line-height: 1.6; }
.belt-terms dt { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--ink); flex: none; }
.belt-terms dd { margin: 0; color: var(--text-body); }
.belt-part ul { list-style: none; margin: 0; padding: 0; }
.belt-part li { font-size: var(--fs-body-sm); line-height: 1.65; color: var(--text-body); padding: 3px 0; }
.belt-part li b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--ink); }
.belt-note { font-size: var(--fs-body-sm); line-height: 1.7; color: var(--text-body); }
/* Komunikat, który ma być przeczytany zanim rodzic zacznie szukać formularza (np. „oferta
   ukaże się pod koniec listopada") — ta sama nota, tylko z czerwoną krawędzią i wcięciem. */
/* Wyróżnienie idzie krojem i stopniem, nie kreską przy krawędzi — tak jak `.post__lead`
   we wpisie. Nota ma być czytana pierwsza, ale nie ma udawać ramki. */
.belt-note--lead { font-size: var(--fs-lead); line-height: 1.62; color: var(--text); }

/* ---- Kroki zapisu (Dołącz do nas) --------------------------------------- */
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 24px; }
.steps__item { display: grid; grid-template-columns: auto 1fr; gap: 18px; align-items: start; }
.steps__n { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-h2-mid); line-height: .9; color: var(--red); }
/* Znacznik kroku jako pas — progresja biały→żółty→pomarańczowy (motyw „droga smoka”), bez cyfr */
.steps__belt { width: 46px; height: 15px; margin-top: 8px; border: 1px solid var(--belt-line); flex: none; }
.steps__h { font-family: var(--font-display); font-style: normal; font-weight: 800; font-size: var(--fs-card); text-transform: uppercase; letter-spacing: .01em; color: var(--ink); margin: 2px 0 5px; }
.steps__p { font-size: var(--fs-body-sm); line-height: 1.65; color: var(--text-soft); }
.steps__p a { color: var(--red); text-decoration: none; }
.steps__p a:hover { text-decoration: underline; }

/* ---- Punktacja rankingu (Sportowe szkolenie / Zawody) ------------------- */
.scoring { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 16px; }
.scoring__item { border-top: 3px solid var(--gold); padding-top: 14px; }
.scoring__pts { display: block; font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-h2-mid); line-height: .9; color: var(--gold); }
.scoring__lbl { display: block; font-size: var(--fs-body-sm); line-height: 1.5; color: var(--on-ink-mute); margin-top: 9px; }

/* ---- Terminarz zawodów (Sportowe szkolenie) ----------------------------- */
/* Ten sam rig akordeonu co .loc-acc — sala i rocznik to ta sama figura: nagłówek,
   który rozwija listę wierszy. Wiersz jest szerszy niż .sched (data, turniej,
   miejsce, mnożnik), więc ma własną siatkę, a nie wariant .sched__row. */
.cal { display: flex; flex-direction: column; gap: 12px; }
.cal__item { background: #fff; border: 1px solid var(--line); }
.cal__item[open] { border-color: var(--line-strong); }
.cal__sum { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 16px; padding: 15px 20px; -webkit-tap-highlight-color: transparent; transition: background-color var(--dur) var(--ease); }
.cal__sum::-webkit-details-marker { display: none; }
.cal__sum:hover { background: rgba(15,15,16,.03); }
.cal__sum:focus-visible { outline: var(--focus); outline-offset: -3px; }
.cal__head { display: flex; flex-direction: column; gap: 3px; }
.cal__name { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-coach); text-transform: uppercase; color: var(--ink); line-height: 1.05; }
.cal__rocz { font-family: var(--font-body); font-size: var(--fs-body-sm); color: var(--text-mute); }
.cal__caret { margin-left: auto; color: var(--red); font-size: .8em; transition: transform var(--dur-mid) var(--ease-out); flex: none; }
.cal__item[open] .cal__caret { transform: rotate(180deg); }
.cal__body { padding: 2px 20px 18px; border-top: 1px solid var(--line); }
.cal__row { display: grid; grid-template-columns: minmax(88px, 108px) minmax(0, 1fr) minmax(150px, 210px) 52px; gap: 18px; padding: 13px 2px; border-top: 1px solid var(--line); align-items: baseline; }
.cal__row:first-child { border-top: 0; }
.cal__date { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-body); text-transform: uppercase; color: var(--red); line-height: 1.15; }
.cal__what { font-size: var(--fs-body-sm); line-height: 1.5; color: var(--text-soft); }
.cal__what b { display: block; font-family: var(--font-display); font-style: normal; font-weight: 600; font-size: var(--fs-body); color: var(--text); }
.cal__cat { display: block; color: var(--text-mute); margin-top: 2px; }
.cal__place { font-size: var(--fs-body-sm); line-height: 1.5; color: var(--text-soft); }
.cal__rank { justify-self: end; font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-12); letter-spacing: .04em; color: #fff; background: var(--ink); padding: 5px 7px; }
.cal__rank--none { background: none; }

/* ---- Lista dokumentów do pobrania (Dokumenty) --------------------------- */
.doc-group + .doc-group { margin-top: clamp(30px, 4vw, 46px); }
.doc-list { display: grid; margin-top: 14px; }
.doc-item { display: flex; align-items: center; gap: 18px; padding: 17px 14px; border-top: 1px solid var(--line); color: var(--text); text-decoration: none; transition: background var(--dur-fast) var(--ease); }
.doc-item:last-child { border-bottom: 1px solid var(--line); }
.doc-item:hover, .doc-item:focus-visible { background: var(--white); }
.doc-item__type { flex: none; font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-12); letter-spacing: .06em; color: #fff; background: var(--red); padding: 7px 8px; min-width: 48px; text-align: center; }
.doc-item__name { font-family: var(--font-body); font-weight: 500; font-size: var(--fs-body); line-height: 1.4; color: var(--text); }
.doc-item__arrow { margin-left: auto; flex: none; font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-15); color: var(--red); transition: transform var(--dur-mid) var(--ease-out); }
.doc-item:hover .doc-item__arrow, .doc-item:focus-visible .doc-item__arrow { transform: translateX(3px); }

/* ---- Grafik treningów (Plany zajęć, akordeon lokalizacji) --------------- */
.sched { display: grid; margin-top: 16px; }
.sched__row { display: grid; grid-template-columns: minmax(150px, 210px) 1fr; gap: 22px; padding: 16px 4px; border-top: 1px solid var(--line); align-items: baseline; }
.sched__row:last-child { border-bottom: 1px solid var(--line); }
.sched__when { font-family: var(--font-display); font-style: italic; font-weight: 800; font-size: var(--fs-card); text-transform: uppercase; color: var(--red); line-height: 1; }

/* ---- Plany zajęć: plakat klubu obok siatki godzin ------------------------
   Klub wydaje plan jako kolorowy plakat, po jednym na salę, i tak go widzą rodzice — więc
   plakat wchodzi na stronę w oryginale. Siatka godzin zostaje obok, bo robi to, czego obrazek
   nie potrafi: da się ją wyszukać, przeczyta ją czytnik ekranu i stoi tam, gdzie plakatu
   u klubu nie ma (Dragon Arena, SLOT Academy). */
.plan-duo { display: grid; grid-template-columns: minmax(250px, 380px) 1fr; gap: clamp(18px, 2.4vw, 34px); align-items: start; margin-top: 18px; }
.plan-duo .sched { margin-top: 0; }
/* Siatka godzin bywa dwa razy wyższa od plakatu (Banino SP 1 ma pięć dni), więc plakat jedzie
   z ekranem zamiast zostawiać pustą kolumnę. Odstęp od góry mieści przyklejony nagłówek. */
.plan-plakat { margin: 0; position: sticky; top: 96px; }
.plan-plakat a { display: block; }
.plan-plakat img { display: block; width: 100%; height: auto; border: 3px solid var(--ink); box-shadow: var(--sh-card); }
.plan-plakat a:hover img, .plan-plakat a:focus-visible img { box-shadow: var(--sh-cta); }
.plan-plakat__cap { margin-top: 9px; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-12);
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-mute); }

/* Plakat WEWNĄTRZ wpisu aktualności — inne zadanie niż plakat obok grafiku zajęć. Nie przykleja
   się do góry (w treści nie ma czego doganiać) i NIGDY nie rośnie ponad rozmiar własnego pliku,
   bo rozdmuchany plik jest dokładnie tą wadą, którą ta figura naprawia.
   Szerokość bierze się z SAMEGO PLIKU (`width: auto` na obrazie plus `max-content` na figurze),
   a nie z liczby wpisanej w stronę — dzięki temu ta sama reguła działa w statyku i w bloku
   WordPressa, gdzie zapis rdzenia nie niesie ani stylu liniowego, ani atrybutów wymiaru.
   Węższy ekran skraca figurę przez `max-width: 100%`, więc nic nie wyjeżdża w bok. */
.plan-plakat--wpis { position: static; width: max-content; max-width: 100%;
  margin: 0 auto clamp(22px, 2.6vw, 34px); }
.plan-plakat--wpis img { width: auto; max-width: 100%; height: auto; }

/* Odnośnik odzyskany z treści wpisu (WordPress gubił <a> przy przenosinach). Wygląd bierze
   z istniejącego komponentu `.btn` (w HTML: `class="btn btn--ink btn--sm"`) — serwis ma już
   przycisk i nie potrzebuje drugiego. Tutaj zostaje wyłącznie odstęp akapitu. */
.post__cta { margin: 1.1rem 0 1.4rem; }
/* `.post__body a` (czerwień + podkreślenie) stoi wyżej w kaskadzie niż `.btn--ink`, więc bez
   tej pary przycisk wychodził czerwonym podkreślonym napisem na czarnym tle. Zdejmujemy
   dokładnie te dwie własności, nic więcej — reszta wyglądu zostaje w komponencie. */
.post__body .post__cta a.btn { color: var(--white); text-decoration: none; }

/* Wartość zastępcza w treści — MA rzucać się w oczy, żeby nie została na stronie na zawsze.
   Lista miejsc do podmiany: context/do-podmiany-31082026.md */
/* Kafel zastępczy w miejscu brakującego zdjęcia — ma wyglądać jak dziura do wypełnienia,
   nie jak element układu. Zdjęcia sali szkolnej w Pogórzu klub nie przysłał (31.08.2026). */
.foto-placeholder {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .55rem;
  margin: 0; padding: clamp(18px, 3vw, 30px); min-height: 190px; text-align: center;
  border: 3px dashed var(--ink); border-radius: var(--r);
  max-width: 420px;   /* szerokość kolumny galerii obok, żeby kafel nie rozpychał karty */
  background: repeating-linear-gradient(135deg, var(--white) 0 14px, var(--gold) 14px 28px);
}
.foto-placeholder__znak {
  /* `--fs-loc` to ten sam stopień 15 → 19, którym strona pisze nazwy sal; kafel stoi w karcie
     sali, więc bierze krok z jej roli, a nie własny clamp spoza rampy. */
  font-weight: 800; letter-spacing: .14em; font-size: var(--fs-loc); color: var(--ink);
}
.foto-placeholder__opis { max-width: 42ch; font-size: var(--fs-15); line-height: 1.45; color: var(--ink); }

/* Uwaga przy karcie sali z dwoma adresami. */
.loc-acc__uwaga { margin: .45rem 0 0; font-size: .93rem; line-height: 1.5; color: var(--text); }

mark.do-podmiany {
  background: repeating-linear-gradient(135deg, var(--white) 0 10px, var(--gold) 10px 20px);
  color: var(--ink); padding: .12em .5em; border: 1px dashed var(--ink); border-radius: var(--r);
  font-weight: 700; letter-spacing: .01em;
}

/* Lupa — okno z powiększonym plakatem (site-6b.js, setupLupa). Okno buduje JS, więc tych klas
   nie ma w żadnym pliku strony; szukaj ich w skrypcie, nie w HTML-u.
   Rozmiary idą w jednostkach okna (dvh/dvw), bo na telefonie pasek adresu zjada część ekranu
   i vh mierzy wysokość, której tam nie ma. */
.lupa { padding: 0; border: 0; background: transparent; max-width: 100dvw; max-height: 100dvh;
  width: 100dvw; height: 100dvh; overflow: hidden; }
.lupa::backdrop { background: var(--ink-scrim); }
.lupa__ramka { width: 100dvw; height: 100dvh; display: grid; place-items: center; overflow: auto;
  overscroll-behavior: contain; }
.lupa__img { display: block; max-width: calc(100dvw - 32px); max-height: calc(100dvh - 96px);
  width: auto; height: auto; cursor: zoom-in; border: 3px solid var(--ink); background: var(--white); }
/* Stan „rozmiar pliku": zdejmujemy sufity, a przewijaniem zajmuje się ramka. */
.lupa__ramka.is-pelny { place-items: start; }
.lupa__ramka.is-pelny .lupa__img { max-width: none; max-height: none; cursor: zoom-out; }
/* Glif zamknięcia stoi na tym samym stopniu, co zamknięcie panelu nawigacji — ta sama rola
   w oknie na pełnym ekranie. Pole 44 px to minimum dotykowe. */
.lupa__x { position: fixed; top: 12px; right: 12px; z-index: 2; width: 44px; height: 44px;
  display: grid; place-items: center; font-size: var(--fs-2xl); line-height: 1; cursor: pointer;
  color: var(--ink); background: var(--gold); border: 3px solid var(--ink); }
.lupa__x:focus-visible { outline: var(--focus); outline-offset: 3px; }
.lupa__cap { position: fixed; left: 0; right: 0; bottom: 0; margin: 0; padding: 10px 16px;
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-12); letter-spacing: .06em;
  text-align: center; color: var(--on-ink); background: var(--ink-scrim); }

.sched__season { margin: 2px 0 0; font-family: var(--font-display); font-style: italic; font-weight: 800;
  font-size: var(--fs-card); text-transform: uppercase; color: var(--red); line-height: 1; }

/* Kolor wiersza idzie za POZIOMEM grupy, a nie za kafelkiem z konkretnego plakatu: u klubu
   każda sala ma własną paletę (zerówki są zielone w Baninie SP 1, a błękitne w SP 2), więc
   przepisywanie kafelek po kafelku dałoby na jednej stronie dwa znaczenia tego samego koloru.
   Drabina idzie od najmłodszych do najstarszych; wiersz o etykiecie spoza drabiny zostaje
   szary, zamiast dostać kolor na oko.

   Kolor niesie sam KAFELEK z godziną i barwa cyfr — tak, jak na plakatach klubu, gdzie
   godzina siedzi w kolorowym prostokącie. Bez paska-zakładki przy krawędzi: dokładałby
   drugi znacznik do tego samego znaczenia i jest zużytym chwytem. Wartości dobrane
   pomiarem, nie okiem — każda barwa ma ≥4,66 kontrastu na własnym tle, wariant jasny
   ≥6,93 na czerni pasa, a najbliższa para tinów dzieli 9,9 w sRGB (poniżej ~8 kafelki
   przestają być rozróżnialne; pierwsze podejście miało 6,6 między średnią a zaawansowaną). */
.sched__row { --grupa: var(--line); --grupa-jasny: var(--on-ink); --grupa-tlo: transparent; }
.sched__row[data-grupa="maluchy"]      { --grupa: #2d7a3e; --grupa-jasny: #7fd193; --grupa-tlo: #e6f4e9; }
.sched__row[data-grupa="poczatkujaca"] { --grupa: #1f6e90; --grupa-jasny: #6cc6e8; --grupa-tlo: #e2f1f8; }
.sched__row[data-grupa="kontynuujaca"] { --grupa: #8a5c0e; --grupa-jasny: #e5b552; --grupa-tlo: #faefd6; }
.sched__row[data-grupa="srednia"]      { --grupa: #3b49a8; --grupa-jasny: #95a2f0; --grupa-tlo: #e9ecfd; }
/* „Zaawansowana" pociemniała 26.08.2026 razem z plakatem Areny. Powód jest zmierzony:
   przy poprzednim `#2b3480` para średnia↔zaawansowana dzieliła 8,0 ΔE2000 w barwie napisu
   i 3,6 ΔE w wariancie jasnym na czarnym pasie — dwa poziomy zaawansowania czytały się jak
   jeden. Po zmianie najbliższa para w palecie to 17,4 ΔE (zaawansowana↔masters), a wariant
   jasny 12,3 ΔE od średniej. Kontrast napisu: #171c4d na chipie #dcdfef = 12,10:1,
   #6f7ccf na pasie --ink = 4,98:1. Ta sama barwa stoi na plakacie
   (wp/pomiar/plany/plakat/plakat.css), żeby plakat i spis godzin mówiły jednym kodem. */
.sched__row[data-grupa="zaawansowana"] { --grupa: #171c4d; --grupa-jasny: #6f7ccf; --grupa-tlo: #dcdfef; }
/* Randori jest wspólnym treningiem dwóch grup, nie ósmym poziomem, a ceglasty odcień czytał
   się na tej liście jak ostrzeżenie. Klub poprosił 25.08.2026 o „neutralny kolor" — neutralny
   znaczy bezbarwny, więc grafit. Zmierzone: 7,46 na własnym tle, wariant jasny 9,84 na czerni
   pasa, najbliższa inna barwa 21,1 ΔE2000. */
.sched__row[data-grupa="randori"]      { --grupa: #4a4a50; --grupa-jasny: #b9b9c2; --grupa-tlo: #ececef; }
.sched__row[data-grupa="masters"]      { --grupa: #662970; --grupa-jasny: #d09ada; --grupa-tlo: #f2e4f4; }
.sched__row[data-grupa] .sched__when {
  color: var(--grupa); background: var(--grupa-tlo); padding: 9px 12px; align-self: start;
}
.sched__row[data-grupa] .sched__what b { color: var(--grupa); }
/* Na ciemnym pasie (treningi dodatkowe) te same tła są za jasne — jasny kafelek na czerni
   czyta się jak plama, a nie jak znacznik. Kafelek znika, kolor zostaje w samych cyfrach,
   w wariancie rozjaśnionym pod czerń. */
.band--ink .sched__row[data-grupa] .sched__when {
  background: transparent; color: var(--grupa-jasny); padding: 0; align-self: auto;
}
.band--ink .sched__row[data-grupa] .sched__what b { color: var(--grupa-jasny); }
.sched__what { font-size: var(--fs-body-sm); line-height: 1.55; color: var(--text-soft); }
.sched__what b { font-family: var(--font-display); font-style: normal; font-weight: 600; color: var(--text); }
/* Podgłówek dnia. Sale, w których poniedziałek i środa mają INNE godziny, potrzebują podziału
   na dni; idiom „Środa<br>17:30” z treningów dodatkowych rozdyma listę, gdy dzień ma pięć
   slotów (Dragon Arena, SP nr 1 w Baninie). Grubsza kreska u góry oddziela bloki dni. */
.sched__day {
  font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-12);
  letter-spacing: .1em; text-transform: uppercase; color: var(--red);
  padding: 22px 4px 6px; border-top: 2px solid var(--ink);
}
.sched__day:first-child { padding-top: 2px; border-top: none; }
.sched__day + .sched__row { border-top: none; }
/* Uwaga proceduralna pod grafikiem (zgody, odbiór ze świetlicy) — informacja dla rodzica,
   nie część zegara, więc siedzi poza siatką i mówi cichszym głosem niż wiersze. */
.loc-acc__note {
  font-size: var(--fs-body-sm); line-height: 1.6; color: var(--text-mute);
  margin: 12px 0 0; padding-left: 14px; border-left: 2px solid var(--line-strong);
}

/* ---- Galeria (masonry, auto-responsywna) -------------------------------- */
.gallery { columns: 3 260px; column-gap: 16px; }
.gallery__item { break-inside: avoid; margin: 0 0 16px; overflow: hidden; position: relative; display: block; background: var(--paper); }
.gallery__item img { width: 100%; display: block; transition: transform var(--dur-slow) var(--ease-out); }
.gallery__item:hover img, .gallery__item:focus-visible img { transform: scale(1.05); }
.gallery__item::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 0 0 0 var(--red); transition: box-shadow var(--dur-mid) var(--ease); pointer-events: none; }
.gallery__item:hover::after, .gallery__item:focus-visible::after { box-shadow: inset 0 0 0 4px var(--red); }

/* ---- Archiwum aktualności (kronika klubu od 2015) -----------------------
   Kafel wpisu to ten sam .news-card co na stronie głównej — archiwum nie zakłada
   drugiego komponentu na tę samą rzecz. Różnicę robi wyłącznie niższy kadr (siatka
   jest tu gęsta) oraz plansza zastępcza dla wpisów, które klub opublikował bez
   zdjęcia; pusty prostokąt w rzędzie kafli czyta się jak błąd, rocznik nie. */
.arch-years { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: clamp(26px, 3vw, 40px); }
.arch-years a {
  font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-12); letter-spacing: .08em;
  text-decoration: none; color: var(--ink); background: var(--white);
  border: 2px solid var(--line-strong); padding: 8px 13px;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.arch-years a:hover, .arch-years a:focus-visible { background: var(--red); border-color: var(--red); color: #fff; }
.arch-year + .arch-year { margin-top: clamp(34px, 4vw, 54px); }
.arch .news-card img, .arch .news-card__plate,
.post__more .news-card img, .post__more .news-card__plate { height: 210px; }
.arch .news-card__plate, .post__more .news-card__plate {
  display: flex; align-items: center; justify-content: center; background: var(--ink); color: var(--gold);
  font-family: var(--font-display); font-style: italic; font-weight: 800;
  font-size: var(--fs-h2-mid); letter-spacing: .02em;
}
.news-card__excerpt { font-family: var(--font-body); font-size: var(--fs-body-sm); line-height: 1.6; color: var(--text-mute); margin-top: 7px; }

/* ---- Wpis (podstrona jednej aktualności) --------------------------------
   Wpis ZE ZDJĘCIEM dostaje nagłówek na własnym kadrze (.post-hero, ten sam .hero--sub
   co na podstronach). Wpis BEZ zdjęcia zostaje przy wąskim pasie ink: hero pod jednym
   zdaniem wygląda na obietnicę bez pokrycia, a takich wpisów w archiwum jest sporo. */
/* Tytuł wpisu bez zdjęcia siedzi w rubryce tak szerokiej, jak pozwala okno — przy 390 px to
   358 px — a polskie złożenia bywają dłuższe („Niepełnosprawnych" ma 406 px przy piśmie 38 px)
   i wtedy wyraz był cięty w połowie. Rubryka nie ma dokąd urosnąć, bo jej granicą jest szyba
   telefonu, więc zmienia się stopień pisma, nie szerokość. Sufit 8,2vw wyliczony z najgorszego
   przypadku w serwisie przy 320 px (najciaśniejsza realna szyba), nie z jednej strony: powyżej
   463 px okna nie obowiązuje, bo tam --fs-section jest już mniejsze. Dywizu nie włączamy —
   klub zgłosił właśnie łamanie wyrazów, więc odpowiedzią jest zmieścić wyraz, a nie dzielić go
   ładniej. `break-word` zostaje siatką bezpieczeństwa. */
.post-head__title { font-size: min(var(--fs-section), 8.2vw); color: #fff; margin-top: 10px; max-width: 26ch; overflow-wrap: break-word; }
/* Tytuły wpisów bywają dziesięciowyrazowe (przeniesione z ich WordPressa), więc kadr
   jest niższy niż hero podstrony, a stopień pisma mniejszy niż w .hero__title. */
.post-hero { min-height: clamp(320px, 44vh, 480px); }
/* `anywhere` różni się od `break-word` wyłącznie tym, że pozwala rubryce zwęzić się PONIŻEJ
   najdłuższego słowa — i właśnie to robiło z tytułów „OTRZYMALIŚM / Y / DOFINANSOWA / NIE"
   (prośba klubu 17.08.2026: „tekst się łamie na drugą linijkę"). Przy `break-word` najmniejsza
   sensowna szerokość tytułu to długość najdłuższego wyrazu i rubryka może do niej dorosnąć
   — patrz `min-width: min-content` przy .hero--sub .hero__copy. */
.post-hero__title { font-size: var(--fs-section); max-width: 20ch; overflow-wrap: break-word; }
.post__lead { font-size: var(--fs-lead); line-height: 1.62; color: var(--text); }
/* Kolumna tekstu stoi POŚRODKU rubryki, nie przy jej lewej krawędzi (prośba klubu 17.08.2026:
   „wycentruj tekst … na wszystkich tego typu stronach"). Reguła siedzi na `.post__body`, więc
   łapie artykuł działu i wszystkie wpisy kroniki naraz — jeden komponent, nie strona po stronie.
   Szerokość miary zostaje 66ch: centrowanie zmienia POŁOŻENIE kolumny, nie długość wiersza. */
.post__body { max-width: 66ch; margin-inline: auto; overflow-wrap: anywhere; }
.post__body p { font-family: var(--font-body); font-size: var(--fs-body); line-height: 1.8; margin: 0 0 1.05rem; text-wrap: pretty; }
.post__body h2, .post__body h3, .post__body h4 {
  font-family: var(--font-display); font-style: italic; font-weight: 800; text-transform: uppercase;
  font-size: var(--fs-h2-mid); line-height: 1.06; margin: 1.7rem 0 .7rem;
}
.post__body a { color: var(--red); text-decoration: underline; text-underline-offset: 2px; }
/* Cytat w treści wpisu bierze idiom domu (`.expert__quote`): krój wystawowy, kursywa,
   większy stopień. Bez kreski przy krawędzi — rolę niesie typografia, nie obwódka. */
.post__body blockquote {
  margin: 1.7rem 0; padding: 0;
  font-family: var(--font-display); font-style: italic; font-weight: 800;
  font-size: var(--fs-card); line-height: 1.18; color: var(--ink);
}
.post__body blockquote p { font-family: inherit; font-size: inherit; line-height: inherit; margin: 0 0 .5rem; }
.post__body blockquote p:last-child { margin-bottom: 0; }
.post__body ul { margin: 0 0 1.05rem; padding-left: 20px; }
.post__body li { font-family: var(--font-body); font-size: var(--fs-body); line-height: 1.8; margin-bottom: .3rem; }
.post__films { display: flex; flex-wrap: wrap; gap: 16px; margin-top: clamp(22px, 2.6vw, 32px); }
/* Kafel filmu — kadr z YouTube'a plus ta sama szyba odtwarzania co w sekcji wideo na
   stronie głównej (.video__playglyph). Wpis, który jest filmem, ma wyglądać jak film. */
.post__film { position: relative; display: block; flex: 1 1 320px; max-width: 560px; overflow: hidden; background: var(--ink); text-decoration: none; }
.post__film img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; transition: scale var(--dur) var(--ease); }
.post__film:hover img, .post__film:focus-visible img { scale: 1.04; }
.post__film:focus-visible { outline: var(--focus); outline-offset: -4px; }
.post__gallery { margin-top: clamp(26px, 3vw, 38px); }
.post__nav {
  display: flex; flex-wrap: wrap; gap: 14px 26px; align-items: baseline;
  margin-top: clamp(30px, 3.4vw, 44px); border-top: 2px solid var(--line-strong); padding-top: 18px;
}
/* Strzałki „poprzedni / następny" niosą pełne tytuły sąsiednich wpisów, a te potrafią mieć
   po dziesięć słów. Globalny .link-arrow jest `nowrap`, bo tam etykiety są krótkie („Wszystkie");
   tutaj to samo ustawienie wypychało stronę o 257 px w bok na 390 px — zmierzone, nie ocenione. */
.post__nav .link-arrow { color: var(--red); white-space: normal; max-width: 100%; overflow-wrap: anywhere; }
.post__nav-mid { margin-inline: auto; }
/* Sekcja „Czytaj dalej" domyka wpis: sześć kafli sąsiednich wpisów, a pod nimi ten sam
   pasek poprzedni/następny co dotąd. Kafel jest globalnym .news-card — tutaj tylko
   szew między sekcją treści a sekcją kafli, żeby dwa pasma paper nie zlały się w jedno. */
.post__more { border-top: 1px solid var(--line); padding-top: clamp(30px, 3.4vw, 46px); }
.post__more .post__nav { margin-top: clamp(26px, 3vw, 38px); }

/* ---- Sponsorzy — wpięci w GÓRĘ stopki (nie osobna pusta plansza) --------
   Logotypy siedzą na pasku nad siatką stopki, oddzielone hairline'em — należą
   do „metra" strony, a nie stoją jako samotny czerwony band. Każdy na własnym
   tle marki (bez przemalowania). */
.footer__spons { display: flex; align-items: center; gap: 16px 28px; flex-wrap: wrap;
  padding-bottom: 28px; margin-bottom: 30px; border-bottom: 1px solid var(--hair-ink); }
.footer__spons-label { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label);
  letter-spacing: .2em; text-transform: uppercase; color: var(--gold); flex: none; }
.footer__spons-grid { flex: 1; min-width: 240px; gap: 14px; }
.footer__spons .sponsors__link { border-color: rgba(255,255,255,.28); background: #fff; }
.footer__spons .sponsors__link:hover, .footer__spons .sponsors__link:focus-visible { border-color: var(--gold); }
.footer__spons-more { margin-left: auto; flex: none; color: #fff; }

/* ============================================================================
   AKCENTY DZIECIĘCE — pasek progresji pasów + Dragonek-przewodnik
   Reużywa istniejące --belt-* i .b-* (patrz DROGA SMOKA) oraz maskotkę z .season.
   Warstwa radości dla najmłodszych; kanon 6b (ostre kąty, czerwień/ink/złoto)
   nietknięty — to akcent, nie zmiana świata.
   ============================================================================ */
/* Pasek pasów kyu (biały→żółty→pomarańcz→zielony→niebieski) jako separator sekcji.
   Pełna szerokość — wstawiany jako bezpośrednie dziecko <main>, poza .wrap.
   Segmenty rozdzielone 2px szczeliną ink: czyta się jak pasy na wieszaku, nie tęcza. */
.belt-strip { display: flex; height: 10px; gap: 2px; background: var(--ink); }
.belt-strip > span { flex: 1 1 0; }
.belt-strip--bold { height: 15px; }


/* Dragonek zerkający z rogu bandu. Dekoracyjny (aria-hidden → reveal go pomija,
   bo JS omija aria-hidden), nie łapie kliknięć. Host musi być position:relative. */
.peek-host { position: relative; }
.dragonek-peek { position: absolute; bottom: 0; right: var(--gutter); z-index: 2;
  width: clamp(84px, 9vw, 128px); height: auto; pointer-events: none; }
.dragonek-peek--left { right: auto; left: var(--gutter); }

/* ============================================================================
   PODSTRONY — foto-hero (wariant hero na podstrony) + feature-split (foto+copy)
   Reużywa cały aparat .hero (obraz, gradienty, copy). Krótszy kadr, dodany eyebrow,
   bez play-ringu. .feat = uniwersalny split zdjęcie↔tekst dla sekcji podstron.
   ============================================================================ */
/* Podstrony — POWRÓT do pełnoszerokościowego foto-hero (decyzja usera 2026-08-09: „zdjęcie
   od lewej do prawej, tekst nałożony BEZ boksu"). Cofa grid-boks R9 do wariantu A wybranego
   wcześniej przez usera przez rendered demo: zdjęcie edge-to-edge pod całą sekcją (bazowy
   .hero__img absolute inset:0 znów obowiązuje), copy nałożone na wzmocniony LEWY scrim (tekst
   jest po lewej, a kadry bywają jasne — poziomy fade ink→transparent daje czytelność bez
   przyciemniania całego zdjęcia). Tekst dostaje z powrotem text-shadow (nad foto). Tylko CSS —
   markup (foto + grad + wrap.inner) był pełnoszerokościowy od początku, dlatego zarówno R9,
   jak i ten powrót, są CSS-only i kaskadują na wszystkie 14 podstron. Header podstron jest
   solidny (.band--ink), więc .hero__topgrad zbędny — nie przyciemniamy góry zdjęcia. */
.hero--sub { min-height: clamp(440px, 54vw, 640px); background: var(--ink); }
.hero--sub .hero__topgrad { display: none; }
/* Szerokość kolumny copy: `42ch` było resztką po grid-boksie R9 i liczy się w znakach fontu
   TEKSTOWEGO (340 px), a tytuł jedzie fontem display na 60–68 px. Efekt zgłoszony przez klienta
   jako „przycina się": jednowyrazowe tytuły wychodziły poza kolumnę na jasną część kadru i ginęły
   biel-na-bieli — pomiar 12.08.2026 dał „Lokalizacje" 434,6 px w boksie 340,2 px przy 1920.
   Kolumna dostaje 30rem = 480 px, czyli najdłuższy zmierzony tytuł plus zapas na skos kursywy,
   a plateau scrimu jedzie za nią, żeby copy w całości siedziało na ciemnym. */
.hero--sub .hero__grad {
  background: linear-gradient(90deg, rgba(15,15,16,.88) 0, rgba(15,15,16,.78) 30%, rgba(15,15,16,.56) 52%, rgba(15,15,16,.12) 72%, rgba(15,15,16,0) 88%);
}
/* 30rem to szerokość PREFEROWANA, nie sufit nie do ruszenia: `min-width: min-content` pozwala
   rubryce urosnąć dokładnie tyle, ile potrzeba na najdłuższy wyraz tytułu, i ani piksela więcej.
   Strony, którym 30rem wystarcza (137 ze 152), zostają nietknięte — rosną tylko te, na których
   wyraz inaczej zostałby przecięty w połowie. Minimum bije maksimum w domykaniu szerokości,
   więc kolejność deklaracji nie ma tu znaczenia. */
.hero--sub .hero__copy { max-width: 30rem; min-width: min-content; }
.hero--sub .hero__title { font-size: var(--fs-hero-sub); text-shadow: var(--tsh-hero-sub); }
.hero .eyebrow { text-shadow: var(--tsh-lead); }
.hero--sub .eyebrow, .hero--sub .hero__sub { text-shadow: var(--tsh-lead); }
.hero__copy .eyebrow { margin-bottom: 8px; }

/* R13 — .feat: pełnokrawędziowy TWARDY SZEW (foto ↔ paper), nie pływające pudełko w wrap.
   Było: 2 kolumny w .wrap z przerwą (gap) → zdjęcie w oknie treści, martwy margines papieru
   z boku = generyczny „obraz + tekst" CMS. Teraz: wyłamanie z .wrap na 100vw (globalne
   overflow-x:hidden chroni przed poziomym scrollem), zdjęcie do krawędzi viewportu wypełnia
   pasmo, butt-join z kolumną tekstu na papierze — ten sam idiom co sub-hero R9 / maszt R11.
   Tekst zostaje na papierze (NIGDY na jasnym zdjęciu → zero washu); zero ramki/side-tabu. */
.feat { width: 100vw; margin-inline: calc(50% - 50vw); display: grid; grid-template-columns: 1fr 1fr; gap: 0; align-items: stretch; }
.feat__media { position: relative; overflow: hidden; align-self: stretch; min-height: clamp(320px, 40vw, 520px); }
.feat__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.feat__body { align-self: center; padding: clamp(30px, 4vw, 60px) clamp(24px, 5vw, 68px); }
.feat--flip .feat__media { order: 2; }

/* ============================================================================
   FOOTER
   ============================================================================ */
.footer { padding-top: 46px; }
.footer__grid { display: grid; grid-template-columns: 1.2fr .8fr .8fr .9fr; gap: 32px; padding-bottom: 34px; }
.footer__brand { display: flex; align-items: center; gap: 15px; margin: 0 0 18px -2px; }
.footer__brand .brand__crest { height: 50px; filter: none; }
.footer__brand .brand__wordmark { font-size: var(--fs-wordmark-sm); text-shadow: none; }
.footer__brand .brand__wordmark img { height: 21px; }
.footer__addr { font-size: var(--fs-body-sm); line-height: 1.85; color: var(--on-ink-mute); }
.footer__col h3 { font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-label); letter-spacing: .18em; text-transform: uppercase; color: var(--gold); margin: 0 0 14px; }
.footer__col ul { list-style: none; margin: 0; padding: 0; font-size: var(--fs-body-sm); line-height: 2.1; color: var(--on-ink-mute); }
.footer__col a { color: inherit; text-decoration: none; transition: color var(--dur-fast) var(--ease); }
.footer__col a:hover { color: #fff; }
.footer__bar { border-top: 1px solid var(--hair-ink-soft); padding: 18px 0; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; font-size: var(--fs-micro); color: var(--on-ink-faint); }
.footer__social { margin-left: auto; display: flex; gap: 18px; font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-social); letter-spacing: .14em; text-transform: uppercase; color: #fff; }
.footer__social a { text-decoration: none; transition: color var(--dur-fast) var(--ease); }
.footer__social a:hover { color: var(--gold); }

/* anchor offset for the sticky header */
main > section[id], #obozy, #zapis { scroll-margin-top: 84px; }
/* Karta trenera jest celem kotwicy ze strony głównej — musi odsłonić się spod
   przyklejonego nagłówka tak samo jak sekcja. */
.trener[id] { scroll-margin-top: 84px; }

/* skip link */
.skip { position: absolute; left: -999px; top: 0; z-index: 100; background: var(--gold); color: var(--ink); padding: 10px 16px; font-family: var(--font-ui); font-weight: 600; }
.skip:focus { left: 8px; top: 8px; }

/* ============================================================================
   RESPONSIVE
   ============================================================================ */
/* Przełącznik nawigacji schodzi pod burgera przy 1069 px, nie przy 860 px jak reszta układu.
   Powód jest zmierzony, nie estetyczny. Po „Przedszkolak" (#75) próg stał na 999 px. Paczka #79
   zamienia zakładkę „Sportowe szkolenie" (dotąd pod Klub) na rozwijaną zakładkę top-level
   „Zawody" — pasek dostaje SIÓDMĄ pozycję najwyższego poziomu. Zmierzone tym samym przyrządem
   (Playwright, bundlowany Chromium): przycisk „Zapisz się" wyjeżdża poza kadr o 27 px przy 1000 px
   i o 4 px przy 1024 px; czysty fit (pasek mieści się w kontenerze) wraca dopiero od ~1066 px.
   Ściskanie odstępów nie wystarcza — brakuje ~63 px, a luzy paska to ~80 px razem i reszta
   musiałaby wyjść z kroju pisma. Panel mobilny niesie ten sam zestaw pozycji, więc zamiana
   paska na burgera niczego nie ukrywa. Przenosimy WYŁĄCZNIE ten przełącznik (999→1069) —
   pozostałe progi układu zostają na 860 px. */
@media (max-width: 1069px) {
  .nav__links { display: none; }
  .nav__burger { display: flex; }
}

@media (max-width: 1024px) {
  .video__list { padding: 40px 30px 40px 0; }
  /* Tryptyk „O klubie" trzyma się do 860 px, ale między 861 a 1024 rail z opisem jest wąski
     i wysoki, więc kolumna zdjęcia robi się bardzo smukła i przy `contain` biel zbiera się
     w całości nad grupą (zmierzone przy 900 px: ~500 px pustego). Środkujemy zdjęcie w pionie
     — biel rozkłada się po równo i panel czyta się jak plansza, nie jak dziura. */
  .about-stage__media--sheet img { object-position: 50% 50%; }
}
@media (max-width: 860px) {
  .nav__links { display: none; }
  .nav__burger { display: flex; }
  .nav { padding-block: 16px; }
  .brand__crest { height: 44px; }
  .is-stuck .brand__crest { height: 40px; }
  .brand__wordmark { font-size: var(--fs-wordmark-sm); }
  .nav__tail { gap: 13px; }
  .droga { grid-template-columns: 1fr; }
  .droga__photo, .droga__photo img { min-height: 300px; }
  /* Plan zajęć na wąskim ekranie: plakat nad siatką godzin, oba na pełną szerokość —
     tak samo, jak klub pokazuje go na telefonie. Przyklejanie znika, bo w jednej kolumnie
     plakat przykleiłby się nad własną siatką godzin. */
  .plan-duo { grid-template-columns: 1fr; }
  .plan-plakat { position: static; }
  /* Gdzie trenujemy: karta schodzi z warstwy NAD mapą na blok NAD mapą (stack).
     Mapa wraca wtedy do przepływu (na desktopie leży absolutnie jako podłoga pasa),
     bo w stacku ma stać POD kartą, a nie za nią. */
  .loc-overlay { position: static; display: block; min-height: 0; padding-block: 0; }
  .loc-panel { width: auto; }
  .loc-stage .loc-map-full--stage { position: relative; inset: auto; height: clamp(320px, 56vh, 460px); }
  .about-stage { grid-template-columns: 1fr; }
  .about-stage__media { order: -1; min-height: clamp(340px, 72vw, 460px); }
  /* Sub-hero na mobile: pełnoszerokościowe foto, tekst nałożony przy dolnej krawędzi (flex-end)
     — scrim przełącza się z poziomego (lewego) na PIONOWY (dół), żeby copy było czytelne bez
     boksu na wąskim ekranie. Zdjęcie edge-to-edge, zgodnie z powrotem do wariantu A. */
  .hero--sub { min-height: clamp(440px, 100vw, 600px); }
  .hero--sub .hero__grad {
    background: linear-gradient(to bottom, rgba(15,15,16,.58) 0, rgba(15,15,16,.40) 38%, rgba(15,15,16,.58) 70%, rgba(15,15,16,.9) 100%);
  }
  .hero--sub .hero__inner { padding-block: 30px clamp(30px, 9vw, 46px); }
  /* News masthead na mobile: ten sam scrim PIONOWY co na desktopie, tylko czerwień wchodzi
     wyżej — nagłówek łamie się tu na trzy–cztery wiersze i potrzebuje głębszego łóżka.
     Twardy szew 44% zniknął razem z desktopowym butt-joinem (13.08.2026): zdjęcie ma iść
     od krawędzi do krawędzi i pozostać zdjęciem, a nie kończyć się linią. */
  .news__stage-grad {
    background: linear-gradient(to bottom,
      rgba(15,15,16,.34) 0%, rgba(15,15,16,.10) 14%,
      rgba(120,10,14,0) 30%, rgba(120,10,14,.55) 52%,
      rgba(160,12,18,.88) 74%, var(--red) 92%);
  }
  .news__stage-inner h2 { max-width: none; }
  /* Na wąskim ekranie nagłówek zajmuje całą szerokość, więc „Wszystkie" i tak spada do
     własnego wiersza — wraca pod lewą krawędź, zamiast odjeżdżać samotnie w prawo. */
  .news__stage-inner .link-arrow { margin-left: 0; }
  .cards-3 { grid-template-columns: 1fr 1fr; }
  /* Mobile: stack twardego szwu — zdjęcie pełne 100vw u góry, papierowy blok tekstu pod spodem
     (butt-join, bez przerwy); padding tekstu niesie .feat__body. */
  .feat { grid-template-columns: 1fr; }
  .feat__media { min-height: clamp(220px, 56vw, 320px); }
  .feat--flip .feat__media { order: 0; }
  .contact { grid-template-columns: 1fr 1fr; }
  .video { grid-template-columns: 1fr; }
  .video__list { padding: 32px 0 28px; }
  .video__frame { min-height: 240px; }
  .stat__n { font-size: var(--fs-2xl); }
}
@media (max-width: 600px) {
  /* kompaktowy nagłówek — chip i hamburger muszą się zmieścić przy 390px */
  .nav { gap: 10px; }
  .brand { gap: 9px; }
  .brand__crest { height: 40px; }
  .is-stuck .brand__crest { height: 38px; }
  .brand__wordmark { font-size: var(--fs-wordmark-xs); }
  .nav__tail { gap: 10px; }
  .chip-cta { padding: 10px 13px; }
  .chip-cta > span { font-size: var(--fs-12); letter-spacing: .03em; }
  .cards-3 { grid-template-columns: 1fr; }
  .cards-2 { grid-template-columns: 1fr; }
  .trener-grid { grid-template-columns: 1fr; }
  .experts { grid-template-columns: 1fr; }
  .scoring { grid-template-columns: 1fr 1fr; }
  .sched__row { grid-template-columns: 1fr; gap: 4px; }
  /* Kafelek godziny w jednej kolumnie nie ma się rozciągać przez cały wiersz — inaczej
     kolor przestaje być znacznikiem grupy, a staje się tłem paska. */
  .sched__row[data-grupa] .sched__when { justify-self: start; margin-bottom: 4px; }
  /* Terminarz na telefonie: data w jednej linii z mnożnikiem, turniej i miejsce pod spodem —
     cztery kolumny przy 390px dawałyby zawijanie w każdej komórce. */
  .cal__body { padding-inline: 14px; }
  .cal__row { grid-template-columns: 1fr auto; gap: 2px 12px; }
  /* mnożnik wraca do wiersza daty — bez tego auto-placement zsuwa go pod miejsce */
  .cal__rank { grid-row: 1; grid-column: 2; }
  .cal__what, .cal__place { grid-column: 1 / -1; }
  .cal__place { color: var(--text-mute); }
  /* :not(:empty) — rubryka bez miejsca zostaje pusta, zamiast pokazywać sam znacznik */
  .cal__place:not(:empty)::before { content: "· "; }
  .belt-acc__body { grid-template-columns: 1fr; gap: 8px; }
  .contact { grid-template-columns: 1fr; }
  .stats__grid { grid-template-columns: 1fr 1fr; gap: 18px; }
  /* Na telefonie siatka schodzi do dwóch kolumn i godło 200×200 kładzie się na WSZYSTKICH
     czterech liczbach (zmierzone na 360, 390 i 430 px). Znak wodny nie ma tu wolnego rogu,
     więc znika — liczby są treścią, godło ozdobą. */
  .stats__stamp { display: none; }
  .stat { padding: 0; }
  .stat + .stat { border-left: 0; }
  .camps { grid-template-columns: 1fr; }
  /* 220 px zostawiało nad podpisem tylko ~120 px czystego kadru, a trzy twarze na zdjęciu
     rodzinnym rozciągają się na ~160 px — dzieci lądowały za przyciemnieniem. 300 px mieści
     całą grupę nad podpisem; strona rośnie o 160 px, kadr przestaje ciąć ludzi. */
  .camp { height: 300px; }
  .loc-clusters { grid-template-columns: 1fr; gap: 0; }
  .loc-map-full { height: clamp(300px, 60vh, 420px); }
  .season { flex-wrap: wrap; gap: 14px; }
  .season .btn { margin-left: 0; }
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 24px; }
  .footer__brand-col { grid-column: 1 / -1; }
  .sponsors__more { margin-left: 0; }
  .footer__spons { gap: 14px; }
  .footer__spons-more { margin-left: 0; }
  .sec-head { flex-direction: column; align-items: flex-start; gap: 8px; }
  .sec-head__note { margin-left: 0; }
  .kadra__head .link-arrow { margin-left: 0; }
  .hero__play { display: none; }
  .coach__photo img { height: 240px; }
  .coach-strip { grid-template-columns: 1fr; }
  .coach-strip .coach__body { border-left: 0; border-top: 1px solid var(--hair-ink); }
  .coach-strip .coach:first-child .coach__body { border-top: 0; }
  .group-strip { grid-template-columns: 1fr; }
  .group-strip .group-card__body { border-left: 0; }
  /* Na telefonie CTA rozlewa przyciski na całą szerokość → róg nie jest wolny.
     Maskotka schodzi więc na środek dołu, w zarezerwowaną przestrzeń pod treścią
     (centrowanie marginesem, nie transformem — bob-transform zostaje wolny). */
  .peek-host { padding-bottom: 104px; }
  .dragonek-peek, .dragonek-peek--left { left: 50%; right: auto; margin-left: -38px; width: 76px; }
  .belt-strip { height: 8px; }
}
@media (max-width: 400px) {
  .signup__grid { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr; }
  /* Najdłuższe słowo w nagłówkach sekcji („bezpieczeństwo" na Dokumentach) ma przy 320 px
     szerokość 321 px, a rubryka tekstu 288 px — strona dostaje 17 px poziomego przewijania.
     Łamania słowa nie wolno użyć: bramka `lamane-slowa.mjs` (paczka #73) słusznie zgłasza
     wyraz przecięty na dwie linijki. Zostaje krój: stopień --fs-section-xs (33 px) daje temu słowu
     279 px, czyli 9 px zapasu, i nie rusza niczego powyżej 400 px. */
  .sec-head__title { font-size: var(--fs-section-xs); }
  /* Wiersz pasa w akordeonie egzaminów też nie mieści się przy 320 px: godło 56 px, stopień,
     nazwa („Pas żółto-pomarańczowy" ma 151 px) i strzałka razem z odstępami dają 300 px przy
     244 px rubryki, więc strona wyjeżdża o 24 px.
     Nazwa dostaje własny wiersz pod godłem i stopniem, a nie ciaśniejszy odstęp: samo
     `min-width: 0` mieści wiersz w szybie, ale wpuszcza tekst POD strzałkę — zmierzone
     przecięcie 5 px przy „Pas żółto-pomarańczowy". Wariant z zawijaniem daje przecięcie 0 px
     i wyjazd 0 px, kosztem 27 px wysokości wiersza wyłącznie na ekranach ≤400 px.
     Krój, kolejność czytania i kolor bez zmian. */
  .belt-acc__sum { gap: 10px; padding: 14px; flex-wrap: wrap; }
  .belt-acc__caret { order: 2; }
  .belt-acc__name { flex: 1 1 100%; order: 3; min-width: 0; }
}

/* ============================================================================
   MOTION — thesis: "committed entry, firm settle" (judo, not bounce).
   Two authored moments: (1) the hero poster composes in [focal];
   (2) the Droga-smoka belt ladder ties on in sequence [content is a progression].
   Everything else here is feedback/continuity. All transform/opacity/clip-path.
   Values (dur/ease) come from tokens-6b.css; keyframe geometry lives here.
   Entrances are gated behind no-preference; the scroll reveal also needs html.js
   so a failed script can never leave content hidden.
   ============================================================================ */

@keyframes heroPhoto { from { transform: scale(1.05); } to { transform: scale(1); } }
@keyframes titleIn   { from { opacity: 0; transform: translateY(14px); clip-path: inset(0 0 100% 0); }
                       to   { opacity: 1; transform: none;             clip-path: inset(0 0 0 0); } }
@keyframes riseIn    { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes fadeIn    { from { opacity: 0; } to { opacity: 1; } }
@keyframes dotPop    { from { opacity: 0; transform: scale(.2); } to { opacity: 1; transform: scale(1); } }
@keyframes stickDown { from { transform: translateY(-100%); } to { transform: translateY(0); } }
@keyframes panelIn     { from { opacity: 0; } to { opacity: 1; } }
@keyframes panelItemIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes dragonekBob { 0%, 100% { transform: translateY(0) rotate(-1.5deg); } 50% { transform: translateY(-7px) rotate(1.5deg); } }
@keyframes tickerScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } } /* -50% = jedna z dwóch bliźniaczych grup → pętla bez szwu */

/* Sticky header drops in rather than blinking (homepage only — subpages have a static header). */
.site-header.is-stuck { animation: stickDown var(--dur-mid) var(--ease-out); }

/* Mobile nav panel: the overlay fades, its links stagger in (a list appearing as a list).
   Neutralised to instant by the reduced-motion block below. */
.nav-panel.is-open { animation: panelIn var(--dur-mid) var(--ease-out); }
.nav-panel.is-open > a,
.nav-panel.is-open > .nav-panel__group { animation: panelItemIn var(--dur-mid) var(--ease-out) both; }
.nav-panel.is-open > *:nth-child(2) { animation-delay: .04s; }
.nav-panel.is-open > *:nth-child(3) { animation-delay: .08s; }
.nav-panel.is-open > *:nth-child(4) { animation-delay: .12s; }
.nav-panel.is-open > *:nth-child(5) { animation-delay: .16s; }
.nav-panel.is-open > *:nth-child(6) { animation-delay: .20s; }
.nav-panel.is-open > *:nth-child(7) { animation-delay: .24s; }
.nav-panel.is-open > *:nth-child(8) { animation-delay: .28s; }

@media (prefers-reduced-motion: no-preference) {
  /* ---- (1) HERO — the one authored focal sequence (pure CSS, runs on load) ---- */
  .hero__img            { animation: heroPhoto var(--dur-hero) var(--ease-out) both; }
  .site-header .nav     { animation: fadeIn var(--dur-slow) var(--ease-out) both; }
  .hero__title          { animation: titleIn .7s var(--ease-out) both; animation-delay: .1s; }
  .hero__title .gold-dot{ display: inline-block; animation: dotPop var(--dur-slow) var(--ease-out) both; animation-delay: .8s; } /* the accent lands last */
  .hero__sub            { animation: riseIn .6s var(--ease-out) both; animation-delay: .24s; }
  .hero__btns           { animation: riseIn .6s var(--ease-out) both; animation-delay: .34s; }
  .hero__play           { animation: fadeIn .6s var(--ease-out) both; animation-delay: .44s; }

  /* ---- (2) DROGA SMOKA belt ladder — reveals on scroll; html.js keeps it safe ---- */
  html.js .ladder__row  { opacity: 0; transform: translateY(10px);
                          transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
  html.js .ladder__belt { clip-path: inset(0 100% 0 0);
                          transition: clip-path var(--dur-slow) var(--ease-out), scale var(--dur) var(--ease);
                          transition-delay: inherit, 0s; } /* clip-path w takt wiersza; scale (hover) natychmiast */
  html.js .ladder.is-in .ladder__row  { opacity: 1; transform: none; }
  html.js .ladder.is-in .ladder__belt { clip-path: inset(0 0 0 0); }
  /* Wiersz reaguje pod kursorem/fokusem: węzeł pasa „dociąga się" (scale niezależny
     od clip-path reveala i transformu wjazdu — ten sam trik co hover-lift na translate). */
  html.js .ladder__row:hover .ladder__belt,
  html.js .ladder__row:focus-within .ladder__belt { scale: 1.08; }
  html.js .ladder.is-in .ladder__row:nth-child(1)  { transition-delay: 0s;   }
  html.js .ladder.is-in .ladder__row:nth-child(2)  { transition-delay: .06s; }
  html.js .ladder.is-in .ladder__row:nth-child(3)  { transition-delay: .12s; }
  html.js .ladder.is-in .ladder__row:nth-child(4)  { transition-delay: .18s; }
  html.js .ladder.is-in .ladder__row:nth-child(5)  { transition-delay: .24s; }
  html.js .ladder.is-in .ladder__row:nth-child(6)  { transition-delay: .30s; }
  html.js .ladder.is-in .ladder__row:nth-child(7)  { transition-delay: .36s; }
  html.js .ladder.is-in .ladder__row:nth-child(8)  { transition-delay: .42s; }

  /* ---- (3) KAŻDE ZDJĘCIE — wjeżdża w kadr, gdy wejdzie w viewport ----
     site-6b.js dodaje .is-in (i --rvd na kaskadę w rzędzie). :where() trzyma
     bazę nisko w specyficzności, żeby wyjątki (loga) nadpisały ją bez wojny;
     scale(var(--iz)) zostawia miejsce hover-zoomowi. Hero (własna sekwencja)
     i elementy aria-hidden są pominięte już w selektorze / w JS. */
  html.js main img:where(:not(.hero__img)):where(:not([aria-hidden="true"])) {
    opacity: 0;
    transform: translateY(22px) scale(var(--iz, 1));
    transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
    transition-delay: var(--rvd, 0s);
  }
  html.js main img.is-in { opacity: 1; transform: translateY(0) scale(var(--iz, 1)); }

  /* Logotypy i drobne znaki w okrągłych/wąskich kadrach: sam fade, bez wjazdu. */
  html.js .sponsors__link img,
  html.js .loc__pin img { transform: none; }

  /* ---- (4) BLOKI TREŚCI — wchodzą w kadr przy scrollu (site-6b.js → .is-in) ----
     Ten sam język, co reszta: „committed entry, firm settle". Rise+fade z DOMYŚLNIE
     widocznego stanu (gated html.js + no-preference → brak JS/reduced = nic nie chowa).
     Rodzeństwo w siatce/liście kaskaduje przez --rvd (lista jako lista, delay cap 5). */
  html.js .feat__copy, html.js .feat__body,
  html.js .group-card, html.js .news-card, html.js .coach, html.js .trener,
  html.js .camp, html.js .gallery__item, html.js .sponsors__link, html.js .loc-item,
  html.js .doc-item, html.js .steps__item, html.js .scoring__item,
  html.js .stat, html.js .signup {
    opacity: 0;
    transform: translateY(24px);
    /* Wjazd (opacity/transform) niesie kaskadę --rvd; hover (translate/box-shadow)
       dostaje własny, krótki czas bez delaya — stąd delaye per-property. */
    transition: opacity var(--dur-slow) var(--ease-out) var(--rvd, 0s),
                transform var(--dur-slow) var(--ease-out) var(--rvd, 0s),
                translate var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
  }
  html.js .feat__copy.is-in, html.js .feat__body.is-in,
  html.js .group-card.is-in, html.js .news-card.is-in, html.js .coach.is-in, html.js .trener.is-in,
  html.js .camp.is-in, html.js .gallery__item.is-in, html.js .sponsors__link.is-in, html.js .loc-item.is-in,
  html.js .doc-item.is-in, html.js .steps__item.is-in, html.js .scoring__item.is-in,
  html.js .stat.is-in, html.js .signup.is-in {
    opacity: 1;
    transform: none;
  }

  /* Nagłówek sekcji: tytuł „wychodzi zza linii" (clip-wipe — spójne z hero-title
     i drabiną pasów), notka wjeżdża chwilę później. Kontener .sec-head niesie tylko
     .is-in (nie chowa się sam) → tytuł/notka są jedynymi ruchami, bez dublowania. */
  html.js .sec-head__title {
    clip-path: inset(0 0 108% 0);
    transform: translateY(10px);
    transition: clip-path var(--dur-slow) var(--ease-out),
                transform var(--dur-slow) var(--ease-out);
  }
  html.js .sec-head.is-in .sec-head__title { clip-path: inset(0 0 0 0); transform: none; }
  html.js .sec-head__note {
    opacity: 0; transform: translateY(8px);
    transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
    transition-delay: .12s;
  }
  html.js .sec-head.is-in .sec-head__note { opacity: 1; transform: none; }

  /* Zdjęcie WEWNĄTRZ karty nie wjeżdża osobno — jedzie z kartą; zostaje hover-zoom (--iz). */
  html.js .group-card img, html.js .news-card img, html.js .coach img, html.js .trener img,
  html.js .camp img, html.js .gallery__item img, html.js .sponsors__link img, html.js .loc-item img {
    opacity: 1;
    transform: scale(var(--iz, 1));
    transition: transform var(--dur-slow) var(--ease-out);
    transition-delay: 0s;
  }

  /* ---- Dragonek-przewodnik: spokojny idle „oddech" (ambient, nie wjazd) ---- */
  .dragonek-peek { transform-origin: 50% 100%; animation: dragonekBob 5.5s var(--ease) infinite; }

  /* ---- Ticker w ruchu: sygnaturowe czarne pasmo przewija się poziomo ----
     site-6b.js owija treść w dwie identyczne grupy i ustawia --ticker-dur wg
     szerokości (stała prędkość px/s). Dwie grupy + translateX(-50%) = pętla bez
     szwu; trailing sep + padding-right grupy zrównują odstęp na złączeniu z
     odstępem wewnętrznym. Pauza pod kursorem/fokusem (czytelność). Bez JS lub
     przy reduced-motion klasa .ticker--live nie powstaje → pasmo zostaje statyczne. */
  /* edge-fade: mask alpha (kolor = kanał krycia, opaque=widoczne) → token palety, nie hardcode */
  .ticker--live { justify-content: flex-start; flex-wrap: nowrap; gap: 0; overflow: hidden;
    -webkit-mask: linear-gradient(90deg, transparent 0, var(--white) 6%, var(--white) 94%, transparent 100%);
            mask: linear-gradient(90deg, transparent 0, var(--white) 6%, var(--white) 94%, transparent 100%); }
  .ticker--live .ticker__marquee { display: flex; flex: none; align-items: center; will-change: transform;
    animation: tickerScroll var(--ticker-dur, 32s) linear infinite; }
  .ticker--live .ticker__group { display: flex; align-items: center; gap: 14px 26px; padding-right: 26px; flex: none; }
  .ticker--live:hover .ticker__marquee,
  .ticker--live:focus-within .ticker__marquee { animation-play-state: paused; }

  /* ---- Zapisy: okno wjeżdża (fade + delikatne osiadanie), tło ściemnia ----
     @starting-style + transition na display/overlay (allow-discrete) daje wjazd
     natywnego <dialog> bez JS-owych klas. Pod reduced-motion (blok wyżej zeruje
     czasy) okno po prostu pojawia się od razu. */
  .signup-modal {
    opacity: 0;
    transform: translateY(10px) scale(.985);
    transition: opacity var(--dur-mid) var(--ease-out),
                transform var(--dur-mid) var(--ease-out),
                overlay var(--dur-mid) var(--ease-out) allow-discrete,
                display var(--dur-mid) var(--ease-out) allow-discrete;
  }
  .signup-modal[open] { opacity: 1; transform: none; }
  @starting-style { .signup-modal[open] { opacity: 0; transform: translateY(10px) scale(.985); } }
  .signup-modal::backdrop {
    opacity: 0;
    transition: opacity var(--dur-mid) var(--ease-out),
                overlay var(--dur-mid) var(--ease-out) allow-discrete,
                display var(--dur-mid) var(--ease-out) allow-discrete;
  }
  .signup-modal[open]::backdrop { opacity: 1; }
  @starting-style { .signup-modal[open]::backdrop { opacity: 0; } }
}

/* ============================================================================
   HOVER — karty ze zdjęciem unoszą się pod kursorem (feedback „to jest żywe").
   Zdjęcie w karcie zoomuje już przez --iz (reguły wyżej); tu unosi się CAŁA karta
   + cień. focus-within/focus-visible dają tę samą reakcję z klawiatury.
   Unoszenie jedzie na `translate` (nie `transform`) świadomie: `transform` należy
   do wjazdu — reguła .is-in ustawia mu `none` z wyższą specyficznością, więc lift
   na transformie byłby martwy. `translate` to osobna właściwość, komponuje się z
   `transform: none` i ma własny, krótki czas przejścia.
   Reduced-motion (blok niżej) zeruje czas → zmiana natychmiastowa, bez animacji.
   ============================================================================ */
.group-card, .news-card, .camp, .coach, .trener {
  transition: translate var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.group-card:hover, .news-card:hover,
.camp:hover, .camp:focus-within,
.coach:hover, .coach:focus-within,
.trener:hover, .trener:focus-within {
  translate: 0 -4px;
  box-shadow: var(--sh-card);
}

/* reduced motion */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   RANKING KLUBOWY — tabela zawodników w akordeonie kategorii.
   Rusztowanie akordeonu (.cal__item / .cal__sum / .cal__body) bierzemy z terminarza,
   bo zachowanie jest identyczne, a klub prosił wprost o „podział jak przy terminarzu
   zawodów" (mail 20.08.2026). Nowe są tylko kolumny wiersza — terminarz ma inne.
   Elementem jest <table>, nie siatka div-ów: to są dane tabelaryczne, więc czytnik
   ekranu ma wiązać liczbę z nagłówkiem kolumny, a nie czytać ciąg liczb.
   ───────────────────────────────────────────────────────────────────────────── */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

.rank__bilans { font-size: var(--fs-body-sm); color: var(--text-mute); margin: 0 0 14px; }
.rank__scroll { overflow-x: auto; }
.rank { width: 100%; border-collapse: collapse; text-align: left; }
.rank th, .rank td { padding: 10px 2px; border-top: 1px solid var(--line); font-weight: 400; vertical-align: baseline; }
.rank thead th { border-top: 0; padding-top: 0; font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-12); letter-spacing: .04em; text-transform: uppercase; color: var(--text-mute); }
.rank .rank__poz { width: 52px; font-family: var(--font-display); font-style: italic; font-weight: 800; color: var(--red); }
.rank .rank__kto { font-family: var(--font-display); font-weight: 600; color: var(--text); }
.rank .rank__pkt { width: 84px; text-align: right; font-family: var(--font-ui); font-weight: 700; color: var(--text); }
.rank .rank__sku { width: 104px; text-align: right; font-size: var(--fs-body-sm); color: var(--text-mute); }
.rank thead .rank__pkt, .rank thead .rank__sku { text-align: right; }
.rank thead .rank__poz { font-style: normal; color: var(--text-mute); }

@media (max-width: 700px) {
  /* Skuteczność wypada pierwsza: to liczba pochodna, a nazwisko i punkty muszą zostać. */
  .rank .rank__sku, .rank thead .rank__sku { display: none; }
  .rank th, .rank td { padding: 9px 2px; }
}

/* Zdjęcia sali pod planem zajęć (prośba klubu z 25.08.2026, sześć maili „foty …").
   Rig masonry bierzemy z galerii — ten sam `.gallery`, więc kafelek zachowuje się identycznie
   jak w albumach i nie powstaje drugi wygląd na to samo. Różnica jest jedna: wewnątrz akordeonu
   kolumna jest węższa niż na stronie galerii, więc próg zawijania schodzi z 260 px na 220 px. */
.loc-acc__foto { margin-top: 22px; }
.loc-acc__foto .gallery { columns: 3 220px; margin-top: 10px; }
