/* ============================================================
   Hair & Beauty Concept · style.css
   Palette drawn from the black & botanical-green brand mark
   (leaf logo + the salon's living moss wall)
   ============================================================ */

:root {
  --ink:        #1b1e15;
  --ink-soft:   #55604a;
  --ink-faint:  #8a9179;
  --cream:      #f7f8f2;
  --ivory:      #fcfdf8;
  --sand:       #e9eede;
  --copper:     #5f7d3b;
  --copper-deep:#3f5a26;
  --copper-lite:#93b06a;
  --blush:      #dfe7d1;
  --rose:       #c7d6b0;
  --line:       rgba(27, 30, 21, .12);
  --line-soft:  rgba(27, 30, 21, .07);

  --shadow-sm: 0 4px 18px -8px rgba(40, 55, 26, .28);
  --shadow-md: 0 24px 60px -28px rgba(40, 55, 26, .42);
  --shadow-lg: 0 40px 90px -30px rgba(30, 45, 20, .5);

  --serif: "Cormorant Garamond", Georgia, serif;
  --sans:  "Jost", system-ui, sans-serif;
  --script:"Great Vibes", cursive;

  --ease: cubic-bezier(.22, 1, .36, 1);
  --pad: clamp(1.25rem, 5vw, 6rem);
  --maxw: 1280px;

  /* ---------- Tokeny przycisków ----------
     Wszystkie przyciski (.btn, zakładki .seg__btn, panele CTA) biorą kolor stąd,
     a nie bezpośrednio z --copper — zieleń przycisków jest jaśniejsza i bardziej
     nasycona niż oliwkowa zieleń reszty layoutu (forest green #228B22, wybór klienta).
     Uwaga na przyszłość: biały napis na #228B22 daje kontrast 4.39 przy progu WCAG AA
     4.5 — gdyby to miało być formalnie zgodne, zejdź z --btn-1 do #1f7d1f. */
  --btn-1:    #228b22;              /* jaśniejszy koniec gradientu */
  --btn-2:    #186b18;              /* ciemniejszy koniec gradientu */
  --btn-fg:   #fff;                 /* tekst/ikony na wypełnionym przycisku */
  --btn-ink:  #1a6f1a;              /* akcent na jasnym tle: hover ghosta, tekst odwróconego CTA */
  --btn-glow: rgba(34, 139, 34, .58); /* poświata pod hoverem */

  /* Duże panele CTA („Umów się…”, „Zamów produkty”) idą za przyciskiem, ale mają
     własne tokeny — gdyby zieleń przycisku kiedyś poszła w jaśniejszą stronę,
     nie nadawałaby się na tło całego panelu z białym tekstem. */
  --cta-1:  var(--btn-1);
  --cta-2:  var(--btn-2);
  --cta-fg: #fff;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* UWAGA: żadnego `scroll-behavior: smooth` tutaj. ScrollTrigger przy każdym refreshu
   ustawia scroll na 0, żeby zmierzyć pozycje triggerów — z CSS-owym smooth ta zmiana
   jest animowana, więc pomiar wychodzi ze złej pozycji i pin hero dostaje ujemny start
   (hero zostaje odpięte i zjeżdża w dół). Płynne przewijanie kotwic robi main.js. */
html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--sans);
  color: var(--ink);
  background: var(--cream);
  font-weight: 300;
  line-height: 1.65;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

::selection { background: var(--copper); color: #fff; }

/* ---------- Heart mask helper ---------- */
.heart-mask {
  -webkit-mask: url(images/logos/heart.svg) center / contain no-repeat;
          mask: url(images/logos/heart.svg) center / contain no-repeat;
  background: var(--copper);
  display: inline-block;
}

/* ---------- Shared type ---------- */
.eyebrow {
  font-size: .78rem;
  letter-spacing: .32em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--copper-deep);
  display: inline-flex;
  align-items: center;
  gap: .8em;
}
.eyebrow::before {
  content: "";
  width: 2.2rem; height: 1px;
  background: var(--copper);
  display: inline-block;
}

.section-title {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(2.3rem, 5.5vw, 4.4rem);
  line-height: 1.02;
  letter-spacing: -.01em;
  color: var(--ink);
}
.section-title em {
  font-style: italic;
  color: var(--copper-deep);
}
/* Nazwa salonu ma zostać w jednym kawałku — „Jesteśmy” schodzi do własnej linii,
   żeby „Hair & Beauty Concept” nigdy nie rozpadło się między wierszami. */
.section-title--stack > span { display: block; }
.section-title--stack em    { display: inline-block; }

/* ---------- Buttons ---------- */
.btn {
  font-family: var(--sans);
  font-size: .9rem;
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 1.05em 1.9em;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  gap: .6em;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform .4s var(--ease), box-shadow .4s var(--ease), background .4s var(--ease), color .4s var(--ease);
  position: relative;
  white-space: nowrap;
}
.btn--solid {
  background: linear-gradient(120deg, var(--btn-1), var(--btn-2));
  color: var(--btn-fg);
  box-shadow: var(--shadow-sm);
}
.btn--solid:hover {
  transform: translateY(-3px);
  box-shadow: 0 20px 40px -14px var(--btn-glow);
}
.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}
.btn--ghost:hover {
  border-color: var(--btn-1);
  color: var(--btn-ink);
  transform: translateY(-3px);
}

/* ---------- Reveal animation (only when JS is active; visible by default otherwise) ----------
   Kierunek i skala sterowane zmiennymi, więc warianty (--l, --r, --scale) zmieniają
   tylko te trzy liczby, a timing zostaje wspólny dla całej strony. --d = opóźnienie
   (inline w HTML albo dosypane przez main.js dla rodzeństwa w siatkach). */
html.js .reveal {
  opacity: 0;
  transform: translate3d(var(--rx, 0), var(--ry, 32px), 0) scale(var(--rs, 1));
  transition:
    opacity .95s var(--ease) var(--d, 0s),
    transform 1.05s var(--ease) var(--d, 0s),
    filter .95s var(--ease) var(--d, 0s);
  filter: blur(4px);
  will-change: transform, opacity;
}
html.js .reveal.in {
  opacity: 1;
  transform: none;
  filter: none;
  will-change: auto;
}
/* Rozmycie tylko na lekkich blokach — animowanie filtra na dużych zdjęciach
   wymusza kosztowną rerasteryzację i potrafi ciąć na słabszych telefonach. */
html.js .reveal:has(img) { filter: none; }
html.js .reveal--l { --rx: -44px; --ry: 0; }
html.js .reveal--r { --rx: 44px;  --ry: 0; }
html.js .reveal--scale { --ry: 18px; --rs: .94; }
html.js .reveal--fade { --ry: 0; }
@media (prefers-reduced-motion: reduce) {
  .reveal, .hero-in { opacity: 1 !important; transform: none !important; filter: none !important; }
  * { animation: none !important; scroll-behavior: auto; }
}

/* ============================================================
   NAV
   ============================================================ */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: .85rem var(--pad);
  transition: background .4s var(--ease), box-shadow .4s var(--ease), padding .4s var(--ease);
}
.nav.scrolled {
  background: rgba(253, 250, 246, .82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 1px 0 var(--line-soft);
  padding-top: .5rem;
  padding-bottom: .5rem;
}
.nav__brand { position: relative; z-index: 70; } /* stays above the full-screen mobile menu */
.nav__brand img { height: 56px; width: auto; transition: height .4s var(--ease); filter: drop-shadow(0 2px 8px rgba(0,0,0,.28)); }
.nav.scrolled .nav__brand img { height: 46px; filter: none; }

.nav__links {
  margin-left: auto;
  display: flex;
  /* Sześć pozycji w menu — odstęp i tak musi się ścieśniać, zanim linki zaczną
     napierać na logo i przycisk rezerwacji na węższych laptopach. */
  gap: clamp(1.1rem, 2.2vw, 2.4rem);
}
.nav__links > a,
.nav__links .navprod__trigger {
  font-size: .82rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  font-weight: 400;
  color: var(--ink);
  position: relative;
  padding: .3em 0;
}
.nav__links > a::after,
.nav__links .navprod__trigger::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 0; height: 1.5px;
  background: var(--copper);
  transition: width .4s var(--ease);
}
.nav__links > a:hover::after,
.navprod:hover .navprod__trigger::after,
.navprod:focus-within .navprod__trigger::after { width: 100%; }

.nav__cta { padding: .8em 1.5em; font-size: .8rem; }

/* Booksy CTA that lives inside the mobile full-screen menu only. */
.nav__mobile-cta { display: none; }

.nav__burger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: 0;
  cursor: pointer;
  padding: 6px;
  position: relative;
  z-index: 70; /* stays above the full-screen menu so it can close it */
}
.nav__burger span {
  width: 26px; height: 2px;
  background: var(--ink);
  transition: .35s var(--ease);
}
.nav__burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ------------------------------------------------------------
   NAV NA PODSTRONACH (.nav--sub) — produkty.html itd.
   Ten sam pasek co na stronie głównej, tylko bez ciemnego hero pod spodem:
   jest nieprzezroczysty od pierwszego piksela i PRZYKLEJONY (sticky), nie
   fixed — dzięki temu treść startuje pod nim bez ręcznego odsuwania, a
   pełnoekranowe menu na telefonie nie łapie się w containing block od
   `backdrop-filter` (patrz reguła z :has niżej — robi to samo dla paska
   na stronie głównej). Obsługa: subnav.js.
   ------------------------------------------------------------ */
.nav--sub {
  position: sticky;
  inset: auto;
  top: 0;
  background: var(--cream);
  border-bottom: 1px solid var(--line-soft);
  padding-top: .7rem;
  padding-bottom: .7rem;
}
.nav--sub .nav__brand img { height: 46px; filter: none; }
/* Pozycja bieżąca podkreślona od razu, nie dopiero pod kursorem. */
.nav--sub .nav__links [aria-current="page"] { color: var(--copper-deep); }
.nav--sub .nav__links [aria-current="page"]::after { width: 100%; }

/* `backdrop-filter` na pasku robi z niego containing block dla dzieci
   `position: fixed` — a pełnoekranowe menu na telefonie jest właśnie takim
   dzieckiem i po przescrollowaniu strony zwijało się do wysokości paska.
   Na czas otwartego menu frost znika; spod menu i tak nie było go widać. */
.nav:has(.nav__links.open) { backdrop-filter: none; -webkit-backdrop-filter: none; }
/* ============================================================
   NAV → ROZWIJANE MENU PRODUKTÓW (.navprod)
   Jedna struktura HTML, dwa zachowania: na desktopie dwukolumnowy panel
   (marki | linie marki pod kursorem), na telefonie ta sama lista rozwija
   się W MIEJSCU jako akordeon w pełnoekranowym menu. Marki i linie to
   zwykłe linki do `produkty.html?marka=…` / `?linia=…`, więc bez JS
   menu nadal prowadzi do pełnego katalogu — filtrowanie jest warstwą wyżej.
   ============================================================ */
.navprod { position: relative; display: flex; align-items: center; }
.navprod__trigger { display: inline-flex; align-items: center; gap: .45em; cursor: pointer; }
.navprod__chev {
  width: 10px; height: 7px;
  fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  transition: transform .35s var(--ease);
}
.navprod:hover .navprod__chev,
.navprod:focus-within .navprod__chev,
.navprod.is-open .navprod__chev { transform: rotate(180deg); }

/* Panel wisi pod linkiem. `padding-top` jest MOSTEM dla kursora — bez niego
   przejazd myszką z linku na panel gubi hover i menu mruga. */
.navprod__panel {
  position: absolute;
  top: 100%;
  left: 50%;
  padding-top: 16px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translate(-50%, 10px);
  transition: opacity .32s var(--ease), transform .32s var(--ease), visibility .32s;
}
.navprod:hover .navprod__panel,
.navprod:focus-within .navprod__panel,
.navprod.is-open .navprod__panel {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, 0);
}
.navprod__inner {
  --navprod-c1: 274px;   /* kolumna marek */
  --navprod-c2: 262px;   /* kolumna linii */
  position: relative;
  display: grid;
  grid-template-columns: var(--navprod-c1) var(--navprod-c2);
  grid-template-rows: 1fr auto;
  /* Stała wysokość = najdłuższa lista linii (Schwarzkopf, 10 pozycji). Kolumna
     linii jest pozycjonowana absolutnie, więc nie rozpycha panelu — dzięki temu
     przejazd kursorem między markami nie skacze wysokością. */
  min-height: 436px;
  padding: 1rem;
  background: rgba(253, 250, 246, .97);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--line-soft);
  border-radius: 16px;
  box-shadow: var(--shadow-md);
}
.navprod__brands {
  grid-column: 1;
  grid-row: 1;
  list-style: none;
  margin: 0;
  padding: 0 .8rem 0 0;
  border-right: 1px solid var(--line-soft);
}
.navprod__row {
  display: flex;
  align-items: center;
  gap: .2rem;
  border-radius: 10px;
  transition: background .25s var(--ease);
}
.navprod__brand.is-active > .navprod__row { background: var(--sand); }
.navprod__brandlink {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: .12rem;
  min-width: 0;
  padding: .6rem .7rem;
}
.navprod__name {
  font-family: var(--serif);
  font-size: 1.04rem;
  line-height: 1.2;
  font-weight: 500;
  letter-spacing: .01em;
  text-transform: none;
  color: var(--ink);
}
.navprod__count {
  font-size: .64rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.navprod__brand.is-active .navprod__count { color: var(--copper-deep); }
/* Strzałka przy marce: na desktopie tylko sygnał „tu jest więcej” (rozwija hover),
   na telefonie DOKŁADNIE ten sam element staje się przyciskiem akordeonu. */
.navprod__more {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  margin-right: .2rem;
  padding: 0;
  background: none;
  border: 0;
  color: var(--ink-faint);
  pointer-events: none;
}
.navprod__more svg {
  width: 10px; height: 7px;
  fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  transform: rotate(-90deg);
  transition: transform .35s var(--ease);
}
.navprod__brand.is-active .navprod__more { color: var(--copper-deep); }

.navprod__lineswrap {
  position: absolute;
  top: 1rem;
  /* Dół wyznacza stopka panelu („Wszystkie produkty”) — bez tego najdłuższa
     lista (Schwarzkopf) kładła się na niej ostatnimi pozycjami. */
  bottom: calc(1rem + 52px);
  left: calc(1rem + var(--navprod-c1));
  width: var(--navprod-c2);
  opacity: 0;
  visibility: hidden;
  transform: translateX(-8px);
  transition: opacity .3s var(--ease), transform .3s var(--ease), visibility .3s;
}
.navprod__brand.is-active .navprod__lineswrap { opacity: 1; visibility: visible; transform: none; }
.navprod__lines {
  list-style: none;
  margin: 0;
  padding: 0 0 0 .9rem;
  max-height: 100%;
  overflow-y: auto;
}
.navprod__lines a {
  display: block;
  padding: .36rem .65rem;
  border-radius: 8px;
  font-family: var(--sans);
  font-size: .85rem;
  letter-spacing: .01em;
  text-transform: none;
  color: var(--ink-soft);
  transition: background .25s var(--ease), color .25s var(--ease);
}
.navprod__lines a:hover { background: var(--sand); color: var(--copper-deep); }
.navprod__lines-all a {
  margin-top: .3rem;
  padding-top: .6rem;
  border-top: 1px solid var(--line-soft);
  border-radius: 0;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--copper-deep);
}
.navprod__lines-all a:hover { background: none; text-decoration: underline; text-underline-offset: 3px; }
.navprod__all {
  grid-column: 1 / -1;
  grid-row: 2;
  margin-top: .8rem;
  padding-top: .8rem;
  border-top: 1px solid var(--line-soft);
  text-align: center;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--copper-deep);
}
.navprod__all:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Węższy laptop: panel zwęża obie kolumny, żeby wyśrodkowany pod linkiem nie
   wyjechał za prawą krawędź okna. */
@media (max-width: 1140px) {
  .navprod__inner { --navprod-c1: 214px; --navprod-c2: 206px; min-height: 424px; }
  .navprod__name { font-size: .96rem; }
}

@media (max-width: 900px) {
  /* Telefon: nic nie wisi nad treścią — lista rozwija się w miejscu, wewnątrz
     pełnoekranowego menu. Animacja na `grid-template-rows: 0fr → 1fr`,
     więc nie trzeba zgadywać docelowej wysokości listy. */
  .navprod { flex-direction: column; align-items: stretch; flex: 1 1 0; }
  .navprod.is-open { flex: 0 0 auto; }
  /* Rozwinięte produkty potrzebują wysokości — pozostałe pozycje menu przestają
     wtedy dzielić się wolną przestrzenią i zbijają się do swojej treści. */
  .nav__links.open.has-sub > a,
  .nav__links.open.has-sub .navprod__trigger { flex: 0 0 auto; min-height: 2.5em; }
  .navprod__panel {
    position: static;
    display: grid;
    grid-template-rows: 0fr;
    padding: 0;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: none;
    transition: grid-template-rows .42s var(--ease);
  }
  /* Reguła otwierająca panel na desktopie ma wyższą swoistość i wnosiła tu
     `translate(-50%)` — panel uciekał w lewo poza ekran. Zdejmujemy go wprost. */
  .navprod__panel,
  .navprod:hover .navprod__panel,
  .navprod:focus-within .navprod__panel,
  .navprod.is-open .navprod__panel { transform: none; }
  .navprod.is-open .navprod__panel { grid-template-rows: 1fr; }
  .navprod__inner {
    display: block;
    min-height: 0;
    min-width: 0;
    overflow: hidden;   /* bez tego 0fr nie zwija zawartości */
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .navprod__brands { padding: .3rem 0 0; border-right: 0; }
  .navprod__brand { border-top: 1px solid var(--line-soft); }
  .navprod__brand:first-child { border-top: 0; }
  .navprod__brand.is-active > .navprod__row { background: none; }
  .navprod__row { border-radius: 0; }
  .navprod__brandlink { padding: .65rem .1rem; gap: .15rem; }
  .navprod__name { font-size: clamp(1rem, 2.3vh, 1.3rem); }
  /* Strzałka to teraz realny przycisk — pole dotyku 44 px. */
  .navprod__more {
    width: 44px; height: 44px;
    margin-right: 0;
    color: var(--copper-deep);
    pointer-events: auto;
    cursor: pointer;
  }
  .navprod__brand.is-open .navprod__more svg { transform: rotate(0deg); }
  .navprod__lineswrap {
    position: static;
    display: grid;
    grid-template-rows: 0fr;
    width: auto;
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: grid-template-rows .38s var(--ease);
  }
  .navprod__brand.is-open .navprod__lineswrap { grid-template-rows: 1fr; }
  .navprod__lines {
    min-height: 0;
    max-height: none;
    overflow: hidden;
    margin-left: .25rem;
    padding: 0 0 .45rem .9rem;
  }
  /* Kreska prowadząca tylko przy rozwiniętej liście — na zwiniętej zostawał
     po niej kilkupikselowy ogonek przy każdej marce. */
  .navprod__brand.is-open .navprod__lines { border-left: 1px solid var(--line-soft); }
  .navprod__lines a { padding: .48rem .55rem; font-size: .95rem; }
  .navprod__all { margin: .45rem 0 0; padding: .75rem 0 0; text-align: left; }
}


/* ============================================================
   HERO
   ============================================================ */
/* Hero zawsze mieści się na jednym ekranie: wysokość to 100svh, a rozmiary
   tekstu i odstępy są ograniczane nie tylko szerokością (vw), ale też
   wysokością okna (vh). Bez capa na vh niski ekran laptopa dostaje tytuł
   liczony z szerokości i treść wychodzi poza viewport. */
.hero {
  /* Różnica między dużym a małym viewportem = wysokość chowającego się UI
     przeglądarki (pasek adresu + dolny toolbar). Na desktopie wychodzi 0. */
  --hero-chrome: calc(100lvh - 100svh);
  position: relative;
  width: 100%;
  /* Kadr hero (tło, kolaż, scrim) sięga 100lvh, czyli pełnego ekranu z
     schowanym UI przeglądarki — inaczej po scrollu, gdy dolny pasek Safari
     znika, pod przypiętym (ScrollTrigger pin) hero zostaje biały pas.
     Treść dalej centruje się w obszarze 100svh, bo dodatkowy padding-bottom
     rezerwuje pas, który przy widocznym UI jest zasłonięty. */
  min-height: 100lvh;
  padding: clamp(6.5rem, 12vh, 8.5rem) var(--pad)
           calc(clamp(2.5rem, 6vh, 4rem) + var(--hero-chrome));
  display: flex;
  align-items: center;
  overflow: hidden;
  /* Fallback shown only if hero.jpg fails to load */
  background:
    radial-gradient(90% 70% at 78% 8%, rgba(199,214,176, .55), transparent 60%),
    radial-gradient(70% 60% at 8% 92%, rgba(223,231,209, .55), transparent 55%),
    linear-gradient(180deg, var(--ivory), var(--cream));
}

/* Full-bleed collage background that reveals on scroll (GSAP).
   Rests at scale 1 (fully visible) so the hero is a clean full-screen
   photo even if JS / GSAP never runs. */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}
.hero__bg picture { display: block; width: 100%; height: 100%; }
.hero__bg img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Kadr i przybliżenie przyklejone do góry zdjęcia: przycięcie `cover` i startowy
     zoom z main.js zjadają dół (nogi), a nie głowy. Środek skali lekko nad kadrem
     przesuwa zdjęcie w dół, więc twarze nie chowają się pod białą ramką. */
  object-position: 50% 0%;
  transform-origin: 50% -10%;
  will-change: transform;
}
/* Big white matte around the collage that shrinks to nothing as the hero
   reveals (GSAP animates its border-width). Rests at 0 so without JS the hero
   is a clean, frameless full-screen photo. Sits above the veil so it stays
   pure white, below the copy so text stays on top. */
.hero__frameline {
  position: absolute;
  inset: 0;
  z-index: 2;
  border: 0 solid #fff;
  box-sizing: border-box;
  pointer-events: none;
  --hero-round: 0px;
}
/* Zaokrąglony jest wyłącznie kadr zdjęcia — zewnętrzna krawędź ramki zostaje
   prosta, bo dochodzi do brzegu ekranu. Dlatego nie ruszamy border-radius
   ramki (zaokrąglałby obie krawędzie i wygryzał róg w białym pasie), tylko
   dokładamy warstwę na jej wewnętrznej krawędzi: zaokrąglony prostokąt
   z białą poświatą, która zasypuje kanciaste narożniki kadru.
   inset: 0 liczy się od padding-boxu, więc warstwa sama jedzie za animowaną
   szerokością ramki, a --hero-round (animowane w main.js) gasi zaokrąglenie,
   gdy ramka schodzi do zera i zdjęcie wchodzi na cały ekran. */
.hero__frameline::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--hero-round);
  box-shadow: 0 0 0 40px #fff;
  pointer-events: none;
}

/* Stan startowy animacji hero, ustawiony zanim GSAP w ogóle wystartuje — inaczej
   pierwsza klatka po odświeżeniu pokazuje surowy, pełnoekranowy kolaż (scale 1,
   ramka 0), a dopiero potem skacze do stanu początkowego. Klasę `hero-armed`
   nadaje inline script w <head>; GSAP nadpisuje te wartości stylami inline.
   14vmin = Math.min(innerWidth, innerHeight) * .14 z main.js. */
html.hero-armed .hero__bg img { transform: scale(1.2); }
html.hero-armed .hero__frameline { border-width: 14vmin; --hero-round: 28px; }
/* Light veil that keeps the copy readable over the busy collage */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      rgba(253, 250, 246, .94) 0%,
      rgba(253, 250, 246, .72) 34%,
      rgba(253, 250, 246, .38) 60%,
      rgba(253, 250, 246, .12) 100%),
    linear-gradient(180deg,
      rgba(253, 250, 246, .55) 0%,
      rgba(253, 250, 246, 0) 22%,
      rgba(253, 250, 246, 0) 68%,
      rgba(253, 250, 246, .6) 100%);
}
.hero__grain {
  position: absolute; inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.4'/%3E%3C/svg%3E");
  opacity: .045;
  mix-blend-mode: multiply;
  pointer-events: none;
}
.hero__ring {
  position: absolute;
  z-index: 1;
  width: min(52vw, 620px);
  aspect-ratio: 1;
  right: -12vw; top: 50%;
  transform: translateY(-50%);
  border-radius: 50%;
  border: 1.5px solid rgba(95,125,59, .28);
  box-shadow: inset 0 0 0 22px rgba(95,125,59, .06);
  pointer-events: none;
  animation: spin 60s linear infinite;
}
.hero__ring::after {
  content: "";
  position: absolute; inset: 32px;
  border-radius: 50%;
  border: 1px dashed rgba(95,125,59, .3);
}
@keyframes spin { to { transform: translateY(-50%) rotate(360deg); } }

.hero__inner {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
}
.hero__copy { max-width: 640px; }

.hero__title {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(2.6rem, min(8vw, 9.2vh), 6.6rem);
  line-height: .98;
  letter-spacing: -.02em;
  margin: clamp(.5rem, 1.4vh, 1.1rem) 0 .3rem;
}
.hero__title span { display: block; }
.hero__title em { font-style: italic; color: var(--copper-deep); }
/* Maska linii tytułu — tekst wjeżdża zza dolnej krawędzi wiersza.
   Padding + ujemny margines robią miejsce na ogonki (y, „,”), których
   overflow:hidden inaczej by przyciął. */
.hero__title .hero__line {
  overflow: hidden;
  padding-bottom: .14em;
  margin-bottom: -.14em;
}

/* ---------- Intro hero (odpalane raz, po wczytaniu strony — nie na scroll) ----------
   Stan startowy tylko przy aktywnym JS; klasę `hero-ready` dokłada main.js. */
html.js .hero-in {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  transition:
    opacity .9s var(--ease) var(--d, 0s),
    transform 1.1s var(--ease) var(--d, 0s);
  will-change: transform, opacity;
}
html.js .hero-in--line { transform: translate3d(0, 110%, 0); }
html.hero-ready .hero-in {
  opacity: 1;
  transform: none;
  will-change: auto;
}
/* Gdy intro prowadzi GSAP (patrz main.js), CSS transition musi zejść z drogi —
   inaczej dwa systemy animowałyby te same właściwości naraz. */
html.hero-gsap .hero-in { transition: none; }
/* Strzałka scrolla centruje się translateX(-50%) — nie wolno jej nadpisać samym translateY. */
html.js .hero__scroll.hero-in { transform: translate3d(-50%, 26px, 0); }
html.hero-ready .hero__scroll.hero-in { transform: translateX(-50%); }

.hero__script {
  font-family: var(--script);
  font-size: clamp(1.6rem, min(4.2vw, 4.6vh), 3.1rem);
  color: var(--copper);
  line-height: 1.05;
  margin: .3rem 0 clamp(.8rem, 1.8vh, 1.4rem);
}

.hero__lead {
  max-width: 40ch;
  color: var(--ink-soft);
  font-size: clamp(.95rem, 1.5vh, 1.05rem);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin: clamp(1.1rem, 2.4vh, 2rem) 0 clamp(1.2rem, 3vh, 2.6rem);
}

.hero__stats {
  display: flex;
  align-items: center;
  gap: 1.6rem;
  flex-wrap: wrap;
}
.hero__stats > div:not(.hero__stats-div) { display: flex; flex-direction: column; }
.hero__stats strong {
  font-family: var(--serif);
  font-size: clamp(1.55rem, 2.6vh, 2rem);
  font-weight: 600;
  color: var(--copper-deep);
  line-height: 1;
}
.hero__stats span { font-size: .76rem; letter-spacing: .04em; color: var(--ink-soft); }
.hero__stats-div { width: 1px; height: 34px; background: var(--line); }

/* hero media */
.hero__media { position: relative; height: min(60vh, 560px); }
.hero__frame {
  position: absolute;
  border-radius: 220px 220px 14px 14px;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  border: 6px solid var(--ivory);
}
.hero__frame img { width: 100%; height: 100%; object-fit: cover; }
.hero__frame:nth-child(1) {
  width: 66%; height: 100%;
  right: 4%; top: 0;
  z-index: 2;
  animation: floaty 7s ease-in-out infinite;
}
.hero__frame--2 {
  width: 46%; height: 54%;
  left: 0; bottom: 6%;
  border-radius: 14px 14px 120px 120px;
  z-index: 3;
  animation: floaty 7s ease-in-out infinite reverse;
  animation-delay: -2s;
}
@keyframes floaty { 50% { transform: translateY(-16px); } }

.hero__badge {
  position: absolute;
  z-index: 4;
  top: -6%; left: 2%;
  width: 116px; height: 116px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--ivory);
  box-shadow: var(--shadow-md);
}
.hero__badge .heart-mask { position: absolute; width: 62%; height: 62%; opacity: .16; }
.hero__badge span {
  font-family: var(--script);
  font-size: 1.7rem;
  line-height: .85;
  text-align: center;
  color: var(--copper-deep);
  position: relative;
}

.hero__scroll {
  position: absolute;
  /* Hero sięga 100lvh, więc strzałkę odsuwamy o wysokość UI przeglądarki —
     inaczej przy widocznym dolnym pasku Safari chowa się za nim. */
  left: 50%; bottom: calc(1.6rem + var(--hero-chrome));
  transform: translateX(-50%);
  width: 26px; height: 42px;
  border: 1.5px solid var(--copper);
  border-radius: 14px;
  z-index: 4;
}
.hero__scroll span {
  position: absolute; left: 50%; top: 8px;
  width: 4px; height: 8px;
  margin-left: -2px;
  background: var(--copper);
  border-radius: 2px;
  animation: scroll 1.7s ease-in-out infinite;
}
@keyframes scroll { 0%{opacity:0;transform:translateY(0)} 40%{opacity:1} 80%{opacity:0;transform:translateY(14px)} }

/* ============================================================
   STRIP MARQUEE
   ============================================================ */
.strip {
  background: var(--ink);
  color: var(--ivory);
  padding: 1.1rem 0;
  overflow: hidden;
  white-space: nowrap;
}
.strip__track {
  display: inline-flex;
  align-items: center;
  gap: 2.4rem;
  animation: slidestrip 34s linear infinite;
}
.strip__track span {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.5rem;
  letter-spacing: .02em;
}
.strip__track i { width: 15px; height: 15px; background: var(--copper-lite); flex: none; }
@keyframes slidestrip { to { transform: translateX(-50%); } }

/* ============================================================
   ABOUT
   ============================================================ */
.about {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(5rem, 10vw, 9rem) var(--pad);
  display: grid;
  grid-template-columns: .92fr 1.08fr;
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: center;
}
.about__img { position: relative; }
.about__frame {
  position: relative;
  /* Proporcje zdjęcia zespołu (1024×1535). Portrety są w tych samych 2:3,
     więc żaden kadr nie jest przycinany — cover to tylko asekuracja. */
  aspect-ratio: 1024 / 1535;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: var(--shadow-md);
  background: var(--sand);
  /* z-index kart zostaje w ramce — nie wychodzi nad serduszko */
  isolation: isolate;
}
.about__track { position: absolute; inset: 0; }
.about__slide {
  position: absolute;
  inset: 0;
  margin: 0;
  overflow: hidden;
  opacity: 0;
}
.about__slide.is-on { opacity: 1; z-index: 2; }
.about__slide img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
  -webkit-user-drag: none;
  user-select: none;
}
.about__slide figcaption {
  position: absolute;
  left: 14px;
  bottom: 14px;
  z-index: 3;
  display: flex;
  align-items: baseline;
  gap: .55em;
  max-width: calc(100% - 28px);
  padding: .45em 1em .5em;
  border-radius: 999px;
  background: rgba(252, 253, 248, .9);
  color: var(--ink);
  box-shadow: var(--shadow-sm);
  white-space: nowrap;
}
.about__slide figcaption b {
  font-family: var(--serif);
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1;
}
.about__slide figcaption span {
  font-size: .64rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- TALIA — karty w wachlarzu, górna odlatuje na spód ----------
   Klasę is-deck dokłada about-show.js; bez JS zostaje jeden kadr.
   --k = głębokość w talii (0 = na wierzchu), --r = obrót; oba ustawia JS. */
.is-deck .about__frame {
  overflow: visible;
  box-shadow: none;
  background: none;
}
.is-deck .about__slide {
  opacity: 1;
  z-index: calc(10 - var(--k, 0));
  border-radius: 18px;
  box-shadow: var(--shadow-md);
  transform-origin: 50% 92%;
  transform: translate3d(0, calc(var(--k, 0) * 10px), 0) rotate(var(--r, 0deg)) scale(calc(1 - var(--k, 0) * .045));
  transition: transform .65s var(--ease);
}
.is-deck .about__slide.is-on { cursor: pointer; }
/* Przyciemnienie kart pod spodem — warstwa zamiast filter: brightness() */
.is-deck .about__slide::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--ink);
  opacity: calc(var(--k, 0) * .16);
  transition: opacity .65s var(--ease);
  pointer-events: none;
}
.is-deck .about__slide figcaption { transition: opacity .4s .3s; }
.is-deck .about__slide:not(.is-on) figcaption { opacity: 0; transition: opacity .15s; }
.is-deck .about__slide.is-throw {
  animation: aboutThrow .8s var(--ease) both;
}
@keyframes aboutThrow {
  0%   { z-index: 20; transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  42%  { z-index: 20; transform: translate3d(-68%, 3%, 0) rotate(-13deg) scale(.96); }
  52%  { z-index: 1; }
  100% { z-index: 1; transform: translate3d(0, calc(var(--k, 0) * 10px), 0) rotate(var(--r, 0deg)) scale(calc(1 - var(--k, 0) * .045)); }
}

@media (prefers-reduced-motion: reduce) {
  .is-deck .about__slide,
  .is-deck .about__slide::after { animation: none !important; transition-duration: .01s !important; }
}

/* Zielony obrys = najgłębsza „karta" talii: ten sam punkt obrotu co wachlarz,
   tylko mocniej odchylona w prawo, więc wystaje zza kart równym klinem
   zamiast przecinać je krzywym, przesuniętym prostokątem. */
.about__img::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1.5px solid var(--copper);
  border-radius: 18px;
  z-index: -1;
  transform-origin: 50% 92%;
  transform: translate3d(0, 14px, 0) rotate(7.5deg) scale(.96);
}
.about__img-tag {
  position: absolute;
  right: -18px; top: -18px;
  width: 74px; height: 74px;
  background: var(--copper);
  filter: drop-shadow(0 10px 20px rgba(63,90,38,.4));
}

.about__copy .eyebrow { margin-bottom: 1.2rem; }
.about__copy p { color: var(--ink-soft); margin-top: 1.2rem; max-width: 52ch; }
.about__copy .section-title { margin-bottom: .4rem; }

.about__quote {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  color: var(--ink);
  line-height: 1.3;
  margin: 2rem 0 .3rem;
  padding-left: 1.4rem;
  border-left: 2px solid var(--copper);
}
.about__sign {
  font-family: var(--script);
  font-size: 2.4rem;
  color: var(--copper-deep);
  line-height: 1;
}
.about__pills {
  display: flex;
  flex-wrap: wrap;
  gap: .7rem;
  margin-top: 2rem;
}
.about__pills span {
  font-size: .8rem;
  letter-spacing: .03em;
  padding: .55em 1.1em;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-soft);
  background: var(--ivory);
}

/* ============================================================
   CENNIK (sekcja .pricing) + karty usług
   Jedna sekcja, dwie podkategorie w zakładkach (.pricing__panel).
   Klasy .services__* zostały przy nagłówku, siatce i CTA — opisują wnętrze
   cennika i są używane w obu zakładkach.
   ============================================================ */
.pricing {
  background: linear-gradient(180deg, var(--ivory), var(--sand));
  padding: clamp(5rem, 10vw, 9rem) var(--pad);
}
.services__head {
  max-width: var(--maxw);
  margin: 0 auto 3.5rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}
.services__head .eyebrow::after {
  content: "";
  width: 2.2rem; height: 1px; background: var(--copper);
}
.services__intro { max-width: 56ch; color: var(--ink-soft); }

.services__grid {
  max-width: var(--maxw);
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.4rem;
}
.svc {
  background: var(--ivory);
  border: 1px solid var(--line-soft);
  border-radius: 18px;
  padding: 0;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform .5s var(--ease), box-shadow .5s var(--ease), border-color .5s var(--ease);
}
.svc:hover {
  transform: translateY(-8px);
  box-shadow: var(--shadow-md);
  border-color: transparent;
}

/* ---- Animated treatment visual band ----
   Każda karta usługi ma własną, w kółko chodzącą animację SVG (broda, brwi,
   makijaż, mezoterapia…) — łącznie kilkadziesiąt równoległych keyframe'ów na
   `fill`, `opacity`, maskach i stroke-dash. To są właściwości NIEkompozytowane:
   każda klatka to repaint na wątku głównym. CSS-owe animacje nie zatrzymują się
   po wyjściu z ekranu, więc na telefonie cała zakładka cennika malowała się
   bez przerwy, także daleko poza kadrem — stąd gubione klatki i widoczne
   przeskoki scrolla akurat w tej sekcji.
   `.is-idle` (nadaje IntersectionObserver w main.js) zamraża karty poza kadrem;
   na ekranie zostają 1–2 aktywne. Wizualnie nic się nie zmienia — pauzujemy
   tylko to, czego użytkownik i tak nie widzi. */
.svc__visual.is-idle,
.svc__visual.is-idle * { animation-play-state: paused; }

.svc__visual {
  height: 108px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  position: relative;
  overflow: hidden;
  color: var(--copper);
  background:
    radial-gradient(120% 130% at 50% 0, rgba(147,176,106,.22), transparent 68%),
    var(--sand);
  border-bottom: 1px solid var(--line-soft);
}
.svc__body { padding: 1.6rem 1.7rem 1.9rem; display: flex; flex-direction: column; flex: 1; }

.svc__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .9rem;
}
.svc__num {
  font-family: var(--serif);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: .1em;
  color: var(--copper);
}
.svc__price {
  font-family: var(--sans);
  font-size: .82rem;
  font-weight: 500;
  letter-spacing: .02em;
  color: var(--copper-deep);
  background: var(--sand);
  border: 1px solid var(--line-soft);
  padding: .35em .85em;
  border-radius: 999px;
  white-space: nowrap;
}
.svc h3 {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 1.7rem;
  margin-bottom: .5rem;
}
.svc__desc { color: var(--ink-soft); font-size: .92rem; line-height: 1.55; }
.svc__list { list-style: none; margin-top: 1.2rem; }
.svc__list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .55rem;
  padding: .5em 0;
  border-top: 1px solid var(--line-soft);
  font-size: .88rem;
  transition: opacity .35s var(--ease);
}
.svc__list li > span:first-child { color: var(--ink-soft); flex: 1 1 auto; }
.svc__list li b {
  color: var(--copper-deep);
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Widełki zamienione na stawkę konkretnej osoby — krótki błysk w jej kolorze,
   żeby było widać, że cena to nie ten sam napis co przed filtrem. */
.svc__list li b.is-own-price {
  color: var(--who, var(--copper-deep));
  animation: ownPrice .5s var(--ease);
}
@keyframes ownPrice {
  from { transform: translateY(-3px); opacity: .35; }
  to   { transform: none;             opacity: 1;   }
}
@media (prefers-reduced-motion: reduce) {
  .svc__list li b.is-own-price { animation: none; }
}

/* ---- Kto wykonuje usługę: kropki ---- */
.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: block;
  flex: none;
  background: var(--who, var(--copper));
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .9) inset;
}
/* Paleta wykonawców — wspólna dla kropek i przycisków legendy */
.dot--lidia,    [data-who="lidia"]    { --who: #3f5a26; }
.dot--milena,   [data-who="milena"]   { --who: #93b06a; }
.dot--wiktoria, [data-who="wiktoria"] { --who: #b8942f; }
.dot--oliwia,   [data-who="oliwia"]   { --who: #c07f6e; }
.dot--renata,   [data-who="renata"]   { --who: #7a6ea8; }
.dot--barber,   [data-who="barber"]   { --who: #23281c; }

.svc__by {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex: none;
  padding: 0 .1rem;
}
/* Filtr wykonawcy: usługi spoza jego zakresu (i puste karty) znikają z cennika */
.svc__list li.is-out,
.svc.svc--out { display: none; }
/* Zakładka, w której wybrana osoba nie ma ani jednej usługi: siatka i CTA
   znikają. W praktyce cennik przeskakuje wtedy na drugą zakładkę — to
   zabezpieczenie na wypadek, gdyby ktoś nie miał usług nigdzie. */
.pricing__panel--empty .services__grid,
.pricing__panel--empty .services__cta { display: none; }

/* ---- Legenda wykonawców ---- */
.team-key {
  max-width: var(--maxw);
  margin: -1.4rem auto 2.6rem;
  text-align: center;
}
.team-key__lead {
  max-width: 62ch;
  margin: 0 auto 1.3rem;
  color: var(--ink-soft);
  font-size: .95rem;
  line-height: 1.6;
}
.team-key__lead strong { color: var(--ink); font-weight: 600; }
.team-key__list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .6rem;
}
.team-chip {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .4rem 1.1rem .4rem .45rem;
  background: var(--ivory);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  cursor: pointer;
  text-align: left;
  font-family: var(--sans);
  transition: transform .4s var(--ease), box-shadow .4s var(--ease), border-color .4s var(--ease), background .4s var(--ease);
}
/* Zdjęcie pracownika + kropka w jego kolorze jako badge */
.team-chip__ava {
  position: relative;
  flex: 0 0 auto;
  width: 46px;
  height: 46px;
}
.team-chip__ava img {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: 50%;
  object-fit: cover;
  background: var(--line-soft);
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px var(--line-soft);
  filter: grayscale(1) contrast(1.02);
  transition: filter .5s var(--ease), box-shadow .4s var(--ease), transform .4s var(--ease);
}
.team-chip__ava .dot {
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 13px;
  height: 13px;
  box-shadow: 0 0 0 2px #fff;
}
.team-chip:hover .team-chip__ava img,
.team-chip[aria-pressed="true"] .team-chip__ava img {
  filter: none;
  box-shadow: 0 0 0 2px var(--who, var(--copper));
}
.team-chip[aria-pressed="true"] .team-chip__ava img { transform: scale(1.04); }

/* Brak zdjęcia pracownika → awatar z inicjałem w jego kolorze */
.team-chip__ava--initials {
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--who, var(--copper)) 14%, #fff);
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px var(--line-soft);
  transition: box-shadow .4s var(--ease), transform .4s var(--ease);
}
.team-chip__initials {
  font-family: var(--serif);
  font-size: 1.35rem;
  line-height: 1;
  font-weight: 600;
  color: var(--who, var(--copper));
}
.team-chip:hover .team-chip__ava--initials,
.team-chip[aria-pressed="true"] .team-chip__ava--initials {
  box-shadow: 0 0 0 2px var(--who, var(--copper));
}
.team-chip[aria-pressed="true"] .team-chip__ava--initials { transform: scale(1.04); }

.team-chip__txt { display: flex; flex-direction: column; line-height: 1.25; }
.team-chip__txt b { font-size: .88rem; font-weight: 600; color: var(--ink); }
.team-chip__txt em {
  font-style: normal;
  font-size: .7rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.team-chip:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.team-chip[aria-pressed="true"] {
  background: #fff;
  border-color: var(--who, var(--copper));
  box-shadow: 0 0 0 1px var(--who, var(--copper)), var(--shadow-sm);
}
/* Chip „Wszystkie usługi" — zamiast zdjęcia miniaturka kropek całego zespołu */
.team-chip__ava--all {
  display: grid;
  grid-template-columns: repeat(2, auto);
  justify-content: center;
  align-content: center;
  gap: 4px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px var(--line-soft);
  transition: box-shadow .4s var(--ease);
}
.team-chip__ava--all .dot { position: static; width: 9px; height: 9px; box-shadow: none; }
.team-chip--all:hover .team-chip__ava--all,
.team-chip--all[aria-pressed="true"] .team-chip__ava--all { box-shadow: 0 0 0 2px var(--copper); }

/* Pasek stanu filtra: kto + rezerwacja u niej + wyjście z filtra */
.team-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .75rem 1.1rem;
  margin: 1.4rem auto 0;
  padding: .9rem 1.2rem;
  max-width: 62ch;
  background: var(--ivory);
  border: 1px solid var(--line-soft);
  border-radius: 18px;
}
.team-note[hidden] { display: none; }
.team-note__txt {
  margin: 0;
  font-size: .9rem;
  color: var(--ink-soft);
  line-height: 1.5;
}
.team-note__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .55rem .9rem;
}
.team-note__book { padding: .6rem 1.2rem; font-size: .82rem; white-space: nowrap; }
.team-note__reset {
  background: none;
  border: 0;
  padding: .35rem .2rem;
  cursor: pointer;
  font-family: var(--sans);
  font-size: .82rem;
  letter-spacing: .04em;
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 4px;
  transition: color .3s var(--ease);
}
.team-note__reset:hover { color: var(--copper); }

/* ---- Przełącznik podkategorii (.seg) ----
   Jeden komponent w dwóch miejscach: cennik (fryzjer / kosmetyka)
   i portfolio (włosy / zabiegi). Wygląda jak segmentowany przełącznik:
   dwa kafle w jednej ramce, aktywny na miedzi. */
.seg {
  max-width: 820px;
  margin: 0 auto 2.8rem;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .4rem;
  padding: .45rem;
  background: color-mix(in srgb, var(--ivory) 70%, #fff);
  border: 1px solid var(--line-soft);
  border-radius: 22px;
  box-shadow: var(--shadow-sm);
}
.seg__btn {
  /* Zakładka kosmetyczna nosi id="kosmetologia" — skok z linku nie może jej
     wsunąć pod przyklejony pasek nawigacji. */
  scroll-margin-top: 7rem;
  display: flex;
  align-items: center;
  gap: .8rem;
  text-align: left;
  padding: .85rem 1rem;
  border: 0;
  border-radius: 17px;
  background: transparent;
  cursor: pointer;
  font-family: var(--sans);
  transition: background .35s var(--ease), box-shadow .35s var(--ease), opacity .35s var(--ease);
}
.seg__ico {
  flex: none;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--btn-ink);
  background: color-mix(in srgb, var(--btn-1) 12%, #fff);
  transition: background .35s var(--ease), color .35s var(--ease);
}
.seg__ico svg { width: 19px; height: 19px; }
.seg__txt { display: flex; flex-direction: column; gap: .18rem; min-width: 0; }
.seg__txt b {
  font-size: .95rem;
  font-weight: 600;
  color: var(--ink);
  transition: color .35s var(--ease);
}
.seg__txt em {
  font-style: normal;
  font-size: .66rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  transition: color .35s var(--ease);
}
.seg__btn:hover { background: color-mix(in srgb, var(--btn-1) 8%, transparent); }
.seg__btn[aria-selected="true"] {
  background: linear-gradient(120deg, var(--btn-1), var(--btn-2));
  box-shadow: var(--shadow-sm);
}
.seg__btn[aria-selected="true"]:hover { background: var(--btn-2); }
.seg__btn[aria-selected="true"] .seg__txt b { color: var(--btn-fg); }
.seg__btn[aria-selected="true"] .seg__txt em { color: color-mix(in srgb, var(--btn-fg) 72%, transparent); }
.seg__btn[aria-selected="true"] .seg__ico {
  background: color-mix(in srgb, var(--btn-fg) 20%, transparent);
  color: var(--btn-fg);
}
/* Filtr osoby wygasił tę zakładkę — nadal klikalna: kliknięcie zdejmuje filtr. */
.seg__btn.is-empty { opacity: .4; }

/* Bez JS obie zakładki leżą jedna pod drugą i wszystko jest czytelne;
   chowamy nieaktywną dopiero, gdy skrypt faktycznie działa. */
html.js .pricing__panel:not(.is-active),
html.js .gal-panel:not(.is-active) { display: none; }
.pricing__panel.is-active,
.gal-panel.is-active { animation: panelIn .4s var(--ease) both; }
@keyframes panelIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.pricing__panel-head {
  max-width: var(--maxw);
  margin: 0 auto 2.6rem;
  text-align: center;
}
.pricing__panel-head h3 {
  font-family: var(--serif);
  font-size: clamp(1.45rem, 2.6vw, 1.95rem);
  font-weight: 500;
  color: var(--ink);
}
.pricing__panel-head p {
  max-width: 62ch;
  margin: .7rem auto 0;
  font-size: .95rem;
  color: var(--ink-soft);
}
.pricing__panel-head strong { color: var(--ink); font-weight: 600; }

@media (max-width: 720px) {
  .seg { grid-template-columns: 1fr; }
  .seg__btn { padding: .75rem .9rem; }
}

@media (max-width: 560px) {
  .team-key__list { gap: .45rem; }
  .team-chip { padding: .35rem .85rem .35rem .4rem; gap: .5rem; }
  .team-chip__ava { width: 38px; height: 38px; }
  .team-chip__ava img + .dot { width: 11px; height: 11px; }
  .team-chip__ava--all { gap: 3px; }
  .team-chip__ava--all .dot { width: 8px; height: 8px; }
  .team-note {
    flex-direction: column;
    text-align: center;
    padding: .9rem 1rem;
    border-radius: 16px;
  }
  .team-note__actions { width: 100%; justify-content: center; }
  .team-note__book { flex: 1 1 auto; text-align: center; }
}

/* ---- 08 Stylizacja brwi: regulacja pęsetą → laminacja → połysk ----
   Jedna pętla 16 s, wszystkie warstwy chodzą w tym samym takcie:
     0–6    stan wyjściowy (krzaczaste brwi + luźne włoski)
     6–40   cztery chwyty pęsety, po każdym znika jeden włosek
     36–42  krzaczasty kształt przechodzi w wyprofilowany
     40–80  aplikator jedzie po lewej, potem po prawej brwi (smuga)
     80–93  nasycenie, sheen i iskierki
     93–100 wygaszenie i powrót do stanu wyjściowego
   Procenty pęsety, włosków, aplikatora i maski są ze sobą sparowane —
   przy zmianie jednej wartości trzeba ruszyć całą czwórkę. */
.v-brow__svg { width: auto; height: 88px; display: block; color: var(--copper); }

/* Zarys oczu i rzęs — tło rysunku, bez własnego ruchu. */
.brw-eyes { opacity: .3; }

/* Brwi „przed”: blada, szersza plama; gaśnie, gdy wchodzi wyprofilowany kształt. */
.brw-raw path { fill: var(--copper-lite); }
.brw-raw { opacity: .5; animation: brwRaw 16s ease-in-out infinite; }
@keyframes brwRaw {
  0%,  36%  { opacity: .5; }
  42%, 93%  { opacity: 0;  }
  99%, 100% { opacity: .5; }
}

/* Brwi „po regulacji”: pełny, czysty kształt, ale jeszcze w jaśniejszej zieleni —
   dociemnia je dopiero eliksir, więc laminacja ma co pokazać. */
.brw-set path { fill: var(--copper); }
.brw-set { opacity: 0; animation: brwSet 16s ease-in-out infinite; }
@keyframes brwSet {
  0%,  36%  { opacity: 0; }
  42%, 93%  { opacity: 1; }
  98%, 100% { opacity: 0; }
}

/* Luźne włoski: każdy trzyma się do swojego chwytu, potem podrywa się w górę.
   Reset pozycji robimy pod zgaszonym włoskiem (97 %), żeby nikt go nie zobaczył. */
.brw-hair {
  opacity: .55;
  animation-duration: 16s;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}
.brw-hair--1 { animation-name: brwHair1; }
.brw-hair--2 { animation-name: brwHair2; }
.brw-hair--3 { animation-name: brwHair3; }
.brw-hair--4 { animation-name: brwHair4; }
@keyframes brwHair1 {
  0%,  12%  { opacity: .55; transform: none; }
  15%, 96%  { opacity: 0;   transform: translate(1px, -7px); }
  97%       { opacity: 0;   transform: none; }
  100%      { opacity: .55; transform: none; }
}
@keyframes brwHair2 {
  0%,  19%  { opacity: .55; transform: none; }
  22%, 96%  { opacity: 0;   transform: translate(1px, 7px); }
  97%       { opacity: 0;   transform: none; }
  100%      { opacity: .55; transform: none; }
}
@keyframes brwHair3 {
  0%,  26%  { opacity: .55; transform: none; }
  29%, 96%  { opacity: 0;   transform: translate(-1px, -7px); }
  97%       { opacity: 0;   transform: none; }
  100%      { opacity: .55; transform: none; }
}
@keyframes brwHair4 {
  0%,  33%  { opacity: .55; transform: none; }
  36%, 96%  { opacity: 0;   transform: translate(-1px, 7px); }
  97%       { opacity: 0;   transform: none; }
  100%      { opacity: .55; transform: none; }
}

/* Pęseta: podjeżdża do włoska, przystaje, zaciska się i podrywa go w górę.
   Kolejność chwytów: lewa góra → lewy dół → prawa góra → prawy dół. */
.brw-tw { opacity: 0; animation: brwTweezer 16s cubic-bezier(.5, 0, .5, 1) infinite; }
.brw-tw-arm, .brw-tw-top {
  fill: none;
  stroke: #20241a;
  stroke-width: 1.7;
  stroke-linecap: round;
}
@keyframes brwTweezer {
  0%,   6%  { opacity: 0; transform: translate(27.2px, 8px);    }
  9%,  12%  { opacity: 1; transform: translate(27.2px, 20.2px); }
  15%       { opacity: 1; transform: translate(28.2px, 16px);   }
  17%       { opacity: 1; transform: translate(48.4px, 34px);   }
  19%       { opacity: 1; transform: translate(48.4px, 41px);   }
  22%       { opacity: 1; transform: translate(49.4px, 34px);   }
  24%, 26%  { opacity: 1; transform: translate(92.8px, 20.2px); }
  29%       { opacity: 1; transform: translate(93.8px, 16px);   }
  31%       { opacity: 1; transform: translate(71.6px, 34px);   }
  33%       { opacity: 1; transform: translate(71.6px, 41px);   }
  36%       { opacity: 1; transform: translate(70.6px, 34px);   }
  40%, 100% { opacity: 0; transform: translate(70.8px, 26px);   }
}
/* Zacisk: oba ramiona obracają się wokół górnego łuku, więc czubki spotykają się
   dokładnie na włosku. Te same procenty co znikanie włosków. */
.brw-tw-arm {
  transform-box: fill-box;
  transform-origin: 50% 0;
  animation: brwPinchL 16s ease-in-out infinite;
}
.brw-tw-arm--r { animation-name: brwPinchR; }
@keyframes brwPinchL {
  0%,  10%  { transform: none; }
  12%, 15%  { transform: rotate(-5deg); }
  17%       { transform: none; }
  19%, 22%  { transform: rotate(-5deg); }
  24%       { transform: none; }
  26%, 29%  { transform: rotate(-5deg); }
  31%       { transform: none; }
  33%, 36%  { transform: rotate(-5deg); }
  38%, 100% { transform: none; }
}
@keyframes brwPinchR {
  0%,  10%  { transform: none; }
  12%, 15%  { transform: rotate(5deg); }
  17%       { transform: none; }
  19%, 22%  { transform: rotate(5deg); }
  24%       { transform: none; }
  26%, 29%  { transform: rotate(5deg); }
  31%       { transform: none; }
  33%, 36%  { transform: rotate(5deg); }
  38%, 100% { transform: none; }
}

/* Aplikator z eliksirem — punkty trasy leżą na osi obu brwi. */
.brw-app { opacity: 0; animation: brwApp 16s linear infinite; }
.brw-app-tip  { fill: var(--copper-deep); }   /* końcówka nasiąknięta eliksirem */
.brw-app-body { fill: var(--ivory); stroke: var(--copper-deep); stroke-width: 1.5; }
@keyframes brwApp {
  0%,  40%  { opacity: 0; transform: translate(12px, 46px);     }
  44%       { opacity: 1; transform: translate(11.5px, 37px);   }
  48%       { opacity: 1; transform: translate(21.7px, 31.3px); }
  52%       { opacity: 1; transform: translate(33px, 29.4px);   }
  56%       { opacity: 1; transform: translate(43.6px, 32.4px); }
  59%       { opacity: 1; transform: translate(51.5px, 40.5px); }
  62%       { opacity: 1; transform: translate(60px, 45px);     }
  65%       { opacity: 1; transform: translate(68.5px, 40.5px); }
  68%       { opacity: 1; transform: translate(76.4px, 32.4px); }
  72%       { opacity: 1; transform: translate(87px, 29.4px);   }
  76%       { opacity: 1; transform: translate(98.3px, 31.3px); }
  79%       { opacity: 1; transform: translate(108.5px, 37px);  }
  83%, 100% { opacity: 0; transform: translate(115px, 43px);    }
}

/* Smuga eliksiru: prostokąt maski stoi całą szerokością na lewo od zera, więc
   translateX(n) ustawia jego prawą krawędź dokładnie w x = n — czyli tam, gdzie
   w tej samej klatce jest czubek aplikatora. Stąd `linear` po obu stronach. */
/* Szerokość smugi liczona z kształtu brwi, nie „na oko”: sylwetka jest najgrubsza
   w łuku (ok. 6,6 j.), ale oś pociągnięcia leży odrobinę poniżej jej środka, więc
   kreska 6,4 zostawiała niepomalowany pasek na GÓRNEJ krawędzi łuku (x 19–40,
   ok. 7 % powierzchni) — brew wyglądała na domalowaną tylko od spodu. 9 j. domyka
   kształt w 100 %, 10 daje zapas na antyaliasing. Przelew jest darmowy: cała grupa
   siedzi w `#brwShapeClip`, więc nadmiar i tak przycina sylwetka brwi. */
.brw-gloss-line {
  fill: none;
  stroke: var(--copper-deep);
  stroke-opacity: .85;
  stroke-width: 10;
  stroke-linecap: round;
}
.brw-gloss { animation: brwGlossInk 16s ease-in-out infinite; }
.brw-mask  { animation: brwSweep 16s linear infinite; }
@keyframes brwGlossInk {
  0%,  93%  { opacity: 1; }
  98%, 100% { opacity: 0; }
}
@keyframes brwSweep {
  0%,  44%  { transform: translateX(11.5px);  }
  48%       { transform: translateX(21.7px);  }
  52%       { transform: translateX(33px);    }
  56%       { transform: translateX(43.6px);  }
  59%       { transform: translateX(51.5px);  }
  62%       { transform: translateX(60px);    }
  65%       { transform: translateX(68.5px);  }
  68%       { transform: translateX(76.4px);  }
  72%       { transform: translateX(87px);    }
  76%       { transform: translateX(98.3px);  }
  79%, 96%  { transform: translateX(108.5px); }
  99%, 100% { transform: translateX(11.5px);  }
}

/* Sheen na górnej krawędzi — zapala się dopiero po całej laminacji. */
.brw-sheen {
  fill: none;
  stroke: #fff;
  stroke-width: 2.2;
  stroke-linecap: round;
  opacity: 0;
  animation: brwSheen 16s ease-in-out infinite;
}
@keyframes brwSheen {
  0%,  79%  { opacity: 0;   }
  84%, 92%  { opacity: .55; }
  96%, 100% { opacity: 0;   }
}

/* Iskierki: biała gwiazdka na oliwkowej poświacie, żeby biel czytała się także
   na piaskowym tle karty. Cztery sztuki wchodzą schodkowo. */
.brw-spark-halo { fill: url(#brwSparkGlow); }
.brw-spark-star { fill: #fff; }
.brw-spark {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: brwSpark 16s ease-out infinite;
}
.brw-spark--2 { animation-delay: .3s; }
.brw-spark--3 { animation-delay: .6s; }
.brw-spark--4 { animation-delay: .9s; }
/* Okno iskierek kończy się na 93 %, bo najpóźniejsza ma .9 s opóźnienia (≈5,6 %
   pętli) — dzięki temu ostatnia gaśnie jeszcze przed powrotem do stanu „przed”. */
@keyframes brwSpark {
  0%,  78%  { opacity: 0; transform: scale(.2) rotate(-25deg); }
  82%       { opacity: 1; transform: scale(1.15) rotate(0deg); }
  85%, 89%  { opacity: 1; transform: scale(1) rotate(0deg);    }
  93%, 100% { opacity: 0; transform: scale(.6) rotate(20deg);  }
}

@media (prefers-reduced-motion: reduce) {
  .brw-raw, .brw-set, .brw-hair, .brw-tw, .brw-tw-arm,
  .brw-app, .brw-gloss, .brw-mask, .brw-sheen, .brw-spark { animation: none; }
  /* stan docelowy: wyregulowane, zalaminowane brwi bez narzędzi */
  .brw-raw, .brw-hair, .brw-tw, .brw-app { opacity: 0; }
  .brw-set { opacity: 1; }
  .brw-mask { transform: translateX(120px); }
  .brw-sheen { opacity: .55; }
  .brw-spark { opacity: 1; }
}

/* ---- 07 Barbering & broda: maszynka → nożyczki → brzytwa → glow ----
   Jedna pętla 16 s, wszystkie warstwy chodzą w tym samym takcie:
     0–8     stan wyjściowy (niedbałe włosy, wąsy, broda + 5 odstających włosków)
     8–35    maszynka jedzie od lewej po dole brody i w górę wzdłuż włosów,
             za ostrzem odsłania się fade
     30–36   broda: kształt „przed” → czysty
     36–58   nożyczki: trzy cięcia (włosy, lewy wąs, prawy wąs)
     50–77   brzytwa jedzie po górnym konturze brody i rysuje ostrą krawędź
     56–62   włosy i wąsy: kształt „przed” → czysty
     70–92   pogłębienie koloru, sheen, iskierki
     92–100  wygaszenie i powrót do stanu wyjściowego
   Procenty maszynki (.brb-clip) i maski fade (.brb-mask) są sparowane co do
   klatki — przy zmianie jednej trzeba ruszyć obie. Tak samo brzytwa (.brb-raz)
   i rysowanie konturu (.brb-edge). */
.v-brb__svg { width: auto; height: 90px; display: block; color: var(--copper); }

/* Zarost „przed”: blada, rozrośnięta plama. Broda gaśnie po maszynce,
   włosy i wąsy dopiero po nożyczkach. */
.brb-rough path { fill: var(--copper-lite); }
.brb-beard-rough { opacity: .5; animation: brbBeardRough 16s ease-in-out infinite; }
.brb-top-rough   { opacity: .5; animation: brbTopRough   16s ease-in-out infinite; }
@keyframes brbBeardRough {
  0%,  30%  { opacity: .5; }
  36%, 92%  { opacity: 0;  }
  98%, 100% { opacity: .5; }
}
@keyframes brbTopRough {
  0%,  56%  { opacity: .5; }
  62%, 92%  { opacity: 0;  }
  98%, 100% { opacity: .5; }
}

/* Zarost „po”: czysty kształt, ale jeszcze w jaśniejszej zieleni — dociemnia
   go dopiero finał, więc etap glow ma co pokazać. */
.brb-beard-clean { opacity: 0; animation: brbBeardClean 16s ease-in-out infinite; }
.brb-top-clean   { opacity: 0; animation: brbTopClean   16s ease-in-out infinite; }
@keyframes brbBeardClean {
  0%,  30%  { opacity: 0; }
  36%, 92%  { opacity: 1; }
  97%, 100% { opacity: 0; }
}
@keyframes brbTopClean {
  0%,  56%  { opacity: 0; }
  62%, 92%  { opacity: 1; }
  97%, 100% { opacity: 0; }
}
/* Kolory podajemy wprost (nie przez var()), bo `fill` w keyframes musi się
   interpolować — tak samo jak w karcie 03. */
.brb-clean path { fill: #5f7d3b; animation: brbDeepen 16s ease-in-out infinite; }
@keyframes brbDeepen {
  0%,  70%  { fill: #5f7d3b; }
  78%, 92%  { fill: #3f5a26; }
  99%, 100% { fill: #5f7d3b; }
}

/* Odstające włoski: każdy trzyma się do swojego cięcia, potem odlatuje.
   Reset pozycji robimy pod zgaszonym włoskiem (97 %), żeby nikt go nie zobaczył. */
.brb-stray {
  fill: none;
  stroke: var(--copper-lite);
  stroke-width: 1.5;
  stroke-linecap: round;
  opacity: .6;
  animation-duration: 16s;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}
.brb-stray--1 { animation-name: brbStray1; }
.brb-stray--2 { animation-name: brbStray2; }
.brb-stray--3 { animation-name: brbStray3; }
.brb-stray--4 { animation-name: brbStray4; }
.brb-stray--5 { animation-name: brbStray5; }
@keyframes brbStray1 {   /* maszynka mija dół brody po lewej */
  0%,  13%  { opacity: .6; transform: none; }
  17%, 96%  { opacity: 0;  transform: translate(-3px, 5px); }
  97%       { opacity: 0;  transform: none; }
  100%      { opacity: .6; transform: none; }
}
@keyframes brbStray2 {   /* maszynka mija dół brody po prawej */
  0%,  22%  { opacity: .6; transform: none; }
  26%, 96%  { opacity: 0;  transform: translate(3px, 5px); }
  97%       { opacity: 0;  transform: none; }
  100%      { opacity: .6; transform: none; }
}
@keyframes brbStray5 {   /* cięcie 1 — włosy nad skronią */
  0%,  40%  { opacity: .6; transform: none; }
  44%, 96%  { opacity: 0;  transform: translate(-6px, -3px); }
  97%       { opacity: 0;  transform: none; }
  100%      { opacity: .6; transform: none; }
}
@keyframes brbStray3 {   /* cięcie 2 — lewy wąs */
  0%,  47%  { opacity: .6; transform: none; }
  51%, 96%  { opacity: 0;  transform: translate(-6px, -3px); }
  97%       { opacity: 0;  transform: none; }
  100%      { opacity: .6; transform: none; }
}
@keyframes brbStray4 {   /* cięcie 3 — prawy wąs */
  0%,  54%  { opacity: .6; transform: none; }
  58%, 96%  { opacity: 0;  transform: translate(6px, -3px); }
  97%       { opacity: 0;  transform: none; }
  100%      { opacity: .6; transform: none; }
}

/* Fade: prostokąt maski stoi całą szerokością na lewo od zera, więc
   translateX(n) ustawia jego prawą krawędź dokładnie w x = n — czyli tam, gdzie
   w tej samej klatce jest ostrze maszynki. Stąd `linear` po obu stronach. */
.brb-fade { opacity: 0; animation: brbFade 16s ease-in-out infinite; }
@keyframes brbFade {
  0%,  8%   { opacity: 0; }
  13%, 92%  { opacity: 1; }
  97%, 100% { opacity: 0; }
}
.brb-mask { animation: brbSweep 16s linear infinite; }
@keyframes brbSweep {
  0%,  8%   { transform: translateX(34px); }
  11%       { transform: translateX(36px); }
  14%       { transform: translateX(41px); }
  17%       { transform: translateX(50px); }
  20%       { transform: translateX(60px); }
  23%       { transform: translateX(70px); }
  26%       { transform: translateX(79px); }
  29%       { transform: translateX(84px); }
  32%, 94%  { transform: translateX(87px); }
  99%, 100% { transform: translateX(34px); }
}

/* Maszynka — te same procenty i te same x-y co maska powyżej. Obrót trzyma
   zęby prostopadle do konturu: dodatni po lewej stronie twarzy, ujemny po prawej. */
.brb-clip { opacity: 0; animation: brbClipper 16s linear infinite; }
.brb-clip-body  { fill: var(--ivory); stroke: #20241a; stroke-width: 1.5; stroke-linejoin: round; }
.brb-clip-teeth { fill: #20241a; }
.brb-clip-tick  { fill: none; stroke: var(--ivory); stroke-width: .8; }
.brb-clip-line  { fill: none; stroke: #20241a; stroke-width: 1.2; stroke-linecap: round; }
@keyframes brbClipper {
  0%,   8%  { opacity: 0; transform: translate(34px, 46px)   rotate(62deg);  }
  11%       { opacity: 1; transform: translate(36px, 56px)   rotate(52deg);  }
  14%       { opacity: 1; transform: translate(41px, 66px)   rotate(34deg);  }
  17%       { opacity: 1; transform: translate(50px, 73px)   rotate(16deg);  }
  20%       { opacity: 1; transform: translate(60px, 74.5px) rotate(0deg);   }
  23%       { opacity: 1; transform: translate(70px, 73px)   rotate(-16deg); }
  26%       { opacity: 1; transform: translate(79px, 66px)   rotate(-34deg); }
  29%       { opacity: 1; transform: translate(84px, 56px)   rotate(-52deg); }
  32%       { opacity: 1; transform: translate(86px, 40px)   rotate(-66deg); }
  35%, 100% { opacity: 0; transform: translate(88px, 28px)   rotate(-74deg); }
}

/* Nożyczki: trzy cięcia. Przeskok na prawą stronę (49,5–50,5 %) robimy pod
   zgaszonymi nożyczkami, żeby obrót o 160° nie wyglądał jak piruet. */
.brb-sci { opacity: 0; animation: brbSci 16s cubic-bezier(.5, 0, .5, 1) infinite; }
.brb-sci-arm path, .brb-sci-arm circle {
  fill: none;
  stroke: #20241a;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.brb-sci-nut { fill: #20241a; }
@keyframes brbSci {
  0%,  36%  { opacity: 0; transform: translate(20px, 29px)     rotate(-10deg); }
  38%, 41%  { opacity: 1; transform: translate(24.7px, 34.7px) rotate(-10deg); }
  43%       { opacity: 1; transform: translate(28px, 41px)     rotate(0deg);   }
  45%, 48%  { opacity: 1; transform: translate(32.7px, 47.8px) rotate(10deg);  }
  49.5%     { opacity: 0; transform: translate(36px, 49px)     rotate(10deg);  }
  50.5%     { opacity: 0; transform: translate(84px, 49px)     rotate(170deg); }
  52%, 55%  { opacity: 1; transform: translate(87.3px, 47.8px) rotate(170deg); }
  58%, 100% { opacity: 0; transform: translate(92px, 43px)     rotate(170deg); }
}
/* Domknięcie ostrzy: oba ramiona obracają się wokół śruby w (0,0) — w fill-box
   wypada ona na 38.5 % szerokości i na krawędzi bbox-a każdego ramienia. */
.brb-sci-arm {
  transform-box: fill-box;
  transform-origin: 38.5% 100%;
  animation: brbSnipA 16s ease-in-out infinite;
}
.brb-sci-arm--b { transform-origin: 38.5% 0; animation-name: brbSnipB; }
@keyframes brbSnipA {
  0%,   39%  { transform: none; }
  40%        { transform: rotate(12deg); }
  41.5%, 46% { transform: none; }
  47%        { transform: rotate(12deg); }
  48.5%, 53% { transform: none; }
  54%        { transform: rotate(12deg); }
  55.5%,100% { transform: none; }
}
@keyframes brbSnipB {
  0%,   39%  { transform: none; }
  40%        { transform: rotate(-12deg); }
  41.5%, 46% { transform: none; }
  47%        { transform: rotate(-12deg); }
  48.5%, 53% { transform: none; }
  54%        { transform: rotate(-12deg); }
  55.5%,100% { transform: none; }
}

/* Brzytwa jedzie po konturze policzków — obrót w każdej klatce to styczna do
   krzywej .brb-edge, dzięki czemu ostrze leży na linii, a nie ścina w poprzek. */
.brb-raz { opacity: 0; animation: brbRaz 16s linear infinite; }
.brb-raz-blade { fill: var(--ivory); stroke: #20241a; stroke-width: 1.2; stroke-linejoin: round; }
.brb-raz-grip  { fill: #20241a; }
@keyframes brbRaz {
  0%,  50%  { opacity: 0; transform: translate(38px, 40px)   rotate(58deg);  }
  53%       { opacity: 1; transform: translate(40px, 45px)   rotate(58deg);  }
  56%       { opacity: 1; transform: translate(43.5px, 52px) rotate(46deg);  }
  59%       { opacity: 1; transform: translate(47px, 56.5px) rotate(28deg);  }
  62%       { opacity: 1; transform: translate(53.5px, 59px) rotate(10deg);  }
  65%       { opacity: 1; transform: translate(60px, 60px)   rotate(0deg);   }
  68%       { opacity: 1; transform: translate(66.5px, 59px) rotate(-10deg); }
  71%       { opacity: 1; transform: translate(73px, 56.5px) rotate(-28deg); }
  74%       { opacity: 1; transform: translate(76.5px, 52px) rotate(-46deg); }
  77%, 100% { opacity: 0; transform: translate(80px, 45px)   rotate(-58deg); }
}

/* Kontur rysowany za brzytwą. dasharray ≈ długość ścieżki (≈55 j.). */
.brb-edge {
  fill: none;
  stroke: #3f5a26;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-dasharray: 55;
  stroke-dashoffset: 55;
  animation: brbEdge 16s linear infinite;
}
@keyframes brbEdge {
  0%,  52%  { stroke-dashoffset: 55; opacity: 1; }
  76%, 92%  { stroke-dashoffset: 0;  opacity: 1; }
  97%, 100% { stroke-dashoffset: 0;  opacity: 0; }
}

/* Sheen — zapala się dopiero po całej stylizacji. */
.brb-sheen {
  fill: none;
  stroke: #fff;
  stroke-width: 2.2;
  stroke-linecap: round;
  opacity: 0;
  animation: brbSheen 16s ease-in-out infinite;
}
@keyframes brbSheen {
  0%,  76%  { opacity: 0;  }
  82%, 90%  { opacity: .5; }
  95%, 100% { opacity: 0;  }
}

/* Iskierki: biała gwiazdka na oliwkowej poświacie, żeby biel czytała się także
   na piaskowym tle karty. Okno kończy się na 91 %, bo ostatnia ma .7 s
   opóźnienia (≈4,4 % pętli) — gaśnie tuż przed powrotem do stanu „przed”. */
.brb-spark-halo { fill: url(#brbSparkGlow); }
.brb-spark-star { fill: #fff; }
.brb-spark {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: brbSpark 16s ease-out infinite;
}
.brb-spark--2 { animation-delay: .35s; }
.brb-spark--3 { animation-delay: .7s;  }
@keyframes brbSpark {
  0%,  76%  { opacity: 0; transform: scale(.2) rotate(-25deg);  }
  80%       { opacity: 1; transform: scale(1.15) rotate(0deg);  }
  83%, 87%  { opacity: 1; transform: scale(1) rotate(0deg);     }
  91%, 100% { opacity: 0; transform: scale(.6) rotate(20deg);   }
}

@media (prefers-reduced-motion: reduce) {
  .brb-beard-rough, .brb-top-rough, .brb-beard-clean, .brb-top-clean,
  .brb-clean path, .brb-stray, .brb-fade, .brb-mask, .brb-clip, .brb-sci,
  .brb-sci-arm, .brb-raz, .brb-edge, .brb-sheen, .brb-spark { animation: none; }
  /* stan docelowy: czysty zarost po stylizacji, bez narzędzi */
  .brb-beard-rough, .brb-top-rough, .brb-stray, .brb-clip, .brb-sci, .brb-raz { opacity: 0; }
  .brb-beard-clean, .brb-top-clean, .brb-fade { opacity: 1; }
  .brb-clean path { fill: #3f5a26; }
  .brb-mask  { transform: translateX(87px); }
  .brb-edge  { stroke-dashoffset: 0; }
  .brb-sheen { opacity: .5; }
  .brb-spark { opacity: 1; }
}

/* ---- 01 Scissors ---- */
.v-scissors { width: 92px; height: 60px; color: #20241a; }
.sc-arm { transform-box: view-box; transform-origin: 44px 40px; animation: snipTop 2.2s ease-in-out infinite; }
.sc-arm--bot { animation-name: snipBot; }
@keyframes snipTop { 0%, 100% { transform: rotate(-10deg); } 50% { transform: rotate(0deg); } }
@keyframes snipBot { 0%, 100% { transform: rotate(10deg); } 50% { transform: rotate(0deg); } }

/* ---- 03 Rozjaśnianie: hair from behind — lighten → refleksy → sombré ---- */
.v-hair__svg { width: auto; height: 80px; display: block; }
.hair-base   { fill: #1a1d14; animation: hairLighten 14s ease-in-out infinite; }
.hair-sombre { opacity: 0; animation: hairSombre 14s ease-in-out infinite; }

/* seq 1 — full lightening: whole shape (incl. contour) dark → blonde → hold → dark */
@keyframes hairLighten {
  0%, 4%    { fill: #1a1d14; }
  10%       { fill: #b7c98f; }  /* cool ashy beige blonde (matches site neutrals) */
  28%       { fill: #b7c98f; }
  34%, 100% { fill: #1a1d14; }
}
/* seq 2 — sombré: blonde bottom fading to black on top */
@keyframes hairSombre {
  0%, 48%   { opacity: 0; }
  56%       { opacity: 1; }
  82%       { opacity: 1; }
  90%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hair-base   { animation: none; }
  .hair-sombre { animation: none; opacity: 0; }
  .regen-dmg, .regen-sway, .regen-glint { animation: none; }
  .regen-heal { animation: none; opacity: 1; }
  .regen-dmg  { opacity: 0; }
}

/* ---- 02 Koloryzacja: dye bowl + tint brush (colour changes on every dip) ---- */
@property --dye {
  syntax: '<color>';
  inherits: true;
  initial-value: #5f7d3b;
}
.v-dye { --dye: #5f7d3b; animation: dyeColor 13.5s linear infinite; }
.v-dye__svg { width: 130px; height: 94px; overflow: visible; }
.dye-shadow { fill: rgba(27,30,21, .18); }
.dye-bowl   { fill: #262a1d; }
.dye-handle { fill: #20241a; }
.dye-holder { fill: #20241a; }
.dye-bristles{ fill: var(--dye); }
.dye-paint     { fill: var(--dye); }
.dye-paint-rim { fill: none; stroke: rgba(255, 255, 255, .28); stroke-width: 1; }
.dye-drop { fill: var(--dye); }

.dye-brush   { animation: dyeDip 4.5s cubic-bezier(.5, 0, .5, 1) infinite; }
.dye-drop--1 { animation: dyeDrop1 4.5s ease-in infinite; }
.dye-drop--2 { animation: dyeDrop2 4.5s ease-in infinite; }
.dye-drop--3 { animation: dyeDrop1 4.5s ease-in infinite; animation-delay: .25s; }

@keyframes dyeDip {
  0%   { transform: translateY(-3px); }
  18%  { transform: translateY(16px); }   /* plunge into paint */
  30%  { transform: translateY(19px); }   /* soak */
  42%  { transform: translateY(16px); }
  58%  { transform: translateY(-3px); }   /* lift out */
  100% { transform: translateY(-3px); }   /* hover, dripping */
}
@keyframes dyeColor {
  0%, 7%    { --dye: #5f7d3b; }   /* copper */
  12%, 40%  { --dye: #3f5a26; }   /* deep auburn */
  45%, 73%  { --dye: #93b06a; }   /* warm rose-gold */
  78%, 100% { --dye: #5f7d3b; }   /* back to copper */
}
@keyframes dyeDrop1 {
  0%, 60%   { opacity: 0; transform: translateY(0); }
  64%       { opacity: 1; transform: translateY(0); }
  84%       { opacity: 1; transform: translateY(11px); }
  92%, 100% { opacity: 0; transform: translateY(13px); }
}
@keyframes dyeDrop2 {
  0%, 70%   { opacity: 0; transform: translateY(0); }
  74%       { opacity: 1; transform: translateY(0); }
  90%       { opacity: 1; transform: translateY(10px); }
  96%, 100% { opacity: 0; transform: translateY(12px); }
}
@media (prefers-reduced-motion: reduce) {
  .v-dye, .dye-brush, .dye-drop--1, .dye-drop--2 { animation: none; }
  .dye-drop { opacity: 0; }
}

/* ---- 04 Regeneracja: damaged frayed hair → straight shiny healthy hair ---- */
.v-regen__svg { width: auto; height: 80px; display: block; }

/* damaged state — dull, limp, split ends; visible then snaps away */
.regen-dmg { animation: regenDmgFade 6s ease-in-out infinite; }
.regen-bulb-dmg { fill: #8a9179; stroke: #6a7358; stroke-width: 1; }
.regen-sway {
  transform-box: fill-box;
  transform-origin: 100% 50%;         /* pivot at the follicle end */
  animation: regenSway 1.7s ease-in-out infinite;
}
@keyframes regenSway {
  0%, 100% { transform: rotate(-4deg); }
  50%      { transform: rotate(4deg); }
}
@keyframes regenDmgFade {
  0%, 43%   { opacity: 1; }
  49%       { opacity: 0; }           /* suddenly gone → regenerated */
  94%       { opacity: 0; }
  100%      { opacity: 1; }
}

/* healthy state — clean bulb, single straight glossy strand */
.regen-heal {
  transform-box: fill-box;
  transform-origin: 100% 50%;
  animation: regenHealPop 6s ease-in-out infinite;
}
.regen-bulb-heal  { fill: url(#regenBulb); }
.regen-bulb-shine { fill: #fff; opacity: .45; }
.regen-glint { animation: regenGlint 6s ease-in-out infinite; }
@keyframes regenHealPop {
  0%, 44%   { opacity: 0; transform: scale(.94); }
  50%       { opacity: 1; transform: scale(1.04); }   /* the "nagle" pop */
  56%       { transform: scale(1); }
  92%       { opacity: 1; transform: scale(1); }
  97%, 100% { opacity: 0; transform: scale(.94); }
}
@keyframes regenGlint {
  0%, 53%   { opacity: 0; transform: translateX(0); }
  58%       { opacity: .95; }
  74%       { opacity: .95; transform: translateX(-92px); }  /* shine runs down the strand */
  80%, 100% { opacity: 0; transform: translateX(-92px); }
}

/* ---- 05 Stylizacja: 4 hairstyle silhouettes crossfading (hair1 → hair2 → hair3 → hair4) ---- */
.v-style__svg { width: 100%; height: 92px; display: block; }
.stz {
  /* nested <svg> frames all stacked in the same 120×104 box so they crossfade in place */
  opacity: 0;
  overflow: visible;
  transform-box: fill-box;
  transform-origin: center;
  animation: stzShow 18s cubic-bezier(.65,0,.35,1) infinite;
}
.stz path { fill: #20241a; }  /* kolor nożyczek z sekcji 01 Strzyżenie */
.stz-2 { animation-delay: 4.5s; }
.stz-3 { animation-delay: 9s; }
.stz-4 { animation-delay: 13.5s; }
@keyframes stzShow {
  0%        { opacity: 0; transform: translateY(9px) scale(.88) rotate(-4deg); }
  5%        { opacity: 1; transform: translateY(0)   scale(1)   rotate(0deg); }
  20%       { opacity: 1; transform: translateY(0)   scale(1)   rotate(0deg); }
  25%, 100% { opacity: 0; transform: translateY(-9px) scale(1.06) rotate(4deg); }
}
@media (prefers-reduced-motion: reduce) {
  .stz { animation: none; }
  .stz-1 { opacity: 1; }
}

/* ---- 06 Prostowanie & ondulacja: pasma falują i wygładzają się do prostej ----
   Fala „znika” przez spłaszczenie w poziomie (scaleX wokół środka pasma), więc
   ten sam rysunek czyta się raz jako ondulacja, raz jako prostowanie. */
.v-wave__svg { width: auto; height: 92px; display: block; color: var(--copper); }
.wv {
  transform-box: fill-box;
  transform-origin: center;
  /* Bez tego scaleX ścieśnia także grubość kreski (3.2 × .07 ≈ 0.2 px) —
     wyprostowane pasma stawały się ledwo widocznymi włoskami. */
  vector-effect: non-scaling-stroke;
  /* Bez przesunięć fazowych: trzy pasma pracują równo, więc w każdej klatce
     widać wszystkie trzy, a nie jedno znikające. */
  animation: wvFlat 7s ease-in-out infinite;
}
@keyframes wvFlat {
  0%,  18%  { transform: scaleX(.08);  }  /* proste pasma — klatka wyjściowa */
  42%, 62%  { transform: scaleX(1);    }  /* pełna fala po ondulacji */
  86%, 100% { transform: scaleX(.08);  }  /* i z powrotem gładko */
}
@media (prefers-reduced-motion: reduce) {
  .wv { animation: none; }
}

/* ---- Ikony sekcji kosmetologicznej ----
   Ten sam język co ikony fryzjerskie: cienka kreska w kolorze miedzi i jeden
   spokojny, zapętlony ruch na kartę — nic nie miga i nie odciąga od cennika. */
.v-face__svg, .v-needle__svg, .v-trycho__svg,
.v-lash__svg, .v-wax__svg, .v-makeup__svg {
  width: auto;
  height: 92px;
  display: block;
  color: var(--copper);
}
/* Twarz: suwak „przed / po” jeździ lewo-prawo — po stronie, którą już minął,
   znikają zmarszczki, kropki i reszta niedoskonałości. Kreska i oba prostokąty
   przycinające dostają tę samą klatkę kluczową, więc podział nigdy się nie rozjeżdża. */
.fc-flaws  { opacity: .38; }
.fc-sweep  { opacity: .6; }
.fc-sweep, .fc-mask { animation: fcSweep 8s cubic-bezier(.65, 0, .35, 1) infinite; }
@keyframes fcSweep {
  0%,   6%  { transform: translateX(-26px); }  /* cała twarz „przed”  */
  47%, 59%  { transform: translateX(26px);  }  /* cała twarz „po”     */
  94%, 100% { transform: translateX(-26px); }
}
/* Iskierki leżą w marginesie, którego suwak nigdy nie mija — same przycięcie
   by ich nie zgasiło, więc dostają własne wygaszanie w rytmie suwaka. */
.fc-sparks { opacity: 0; animation: fcSparks 8s ease-in-out infinite; }
@keyframes fcSparks {
  0%,  20%  { opacity: 0;   }
  44%, 58%  { opacity: .55; }
  76%, 100% { opacity: 0;   }
}
/* Mezoterapia: przekrój skóry i sekwencja trzech nakłuć.
   Wszystko chodzi w jednym 9-sekundowym takcie — igła, kropelki, poświaty i finałowy
   rozbłysk mają te same klatki procentowe, więc nic się nie rozjeżdża. Punkty nakłuć
   są na stałe w x = 34 / 60 / 86, a igła dojeżdża do nich przesunięciem o 26 jednostek. */
.mz-epi   { fill: var(--copper-lite); opacity: .16; }
.mz-derm  { fill: var(--copper);      opacity: .2;  }
.mz-line  { fill: none; stroke: var(--copper); stroke-width: 1; opacity: .26; }
.mz-hub   { fill: none; stroke: currentColor; stroke-width: 2.6; }
.mz-shaft { fill: currentColor; }
.mz-dot   { fill: var(--copper-deep); }
.mz-halo  { fill: none; stroke: var(--copper); stroke-width: 2; }
.mz-flash { fill: var(--copper-lite); opacity: 0; }

.mz-dot, .mz-halo {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}

/* Igła: zejście w dół (28 jednostek — czubek ląduje tuż pod kroplą), krótkie
   zatrzymanie w skórze właściwej, powrót do góry i przesuw w prawo. Po trzecim
   nakłuciu wraca nad punkt pierwszy, żeby pętla domknęła się bez przeskoku. */
.mz-needle { animation: mzNeedle 9s cubic-bezier(.65, 0, .35, 1) infinite; }
@keyframes mzNeedle {
  0%,   4%  { transform: translate(0, 0);      }
  11%, 14%  { transform: translate(0, 28px);   }
  20%       { transform: translate(0, 0);      }
  25%       { transform: translate(26px, 0);   }
  32%, 35%  { transform: translate(26px, 28px);}
  41%       { transform: translate(26px, 0);   }
  46%       { transform: translate(52px, 0);   }
  53%, 56%  { transform: translate(52px, 28px);}
  62%, 86%  { transform: translate(52px, 0);   }
  96%, 100% { transform: translate(0, 0);      }
}

/* Kropla preparatu zostaje w punkcie nakłucia i świeci aż do resetu sekwencji. */
.mz-d1 { animation: mzDot1 9s ease-out infinite; }
.mz-d2 { animation: mzDot2 9s ease-out infinite; }
.mz-d3 { animation: mzDot3 9s ease-out infinite; }
@keyframes mzDot1 {
  0%,  12%  { opacity: 0;  transform: scale(.2);   }
  16%       { opacity: 1;  transform: scale(1.35); }
  21%, 88%  { opacity: .9; transform: scale(1);    }
  97%, 100% { opacity: 0;  transform: scale(1);    }
}
@keyframes mzDot2 {
  0%,  33%  { opacity: 0;  transform: scale(.2);   }
  37%       { opacity: 1;  transform: scale(1.35); }
  42%, 88%  { opacity: .9; transform: scale(1);    }
  97%, 100% { opacity: 0;  transform: scale(1);    }
}
@keyframes mzDot3 {
  0%,  54%  { opacity: 0;  transform: scale(.2);   }
  58%       { opacity: 1;  transform: scale(1.35); }
  63%, 88%  { opacity: .9; transform: scale(1);    }
  97%, 100% { opacity: 0;  transform: scale(1);    }
}

/* Poświata wchłaniania: pierścień rozchodzi się od kropli. Drugi pierścień to ta sama
   klatka opóźniona o pół sekundy — poza swoim rozejściem jest przezroczysty, więc
   przesunięcie całej pętli nic nie psuje. */
.mz-h1 { animation: mzHalo1 9s ease-out infinite; }
.mz-h2 { animation: mzHalo2 9s ease-out infinite; }
.mz-h3 { animation: mzHalo3 9s ease-out infinite; }
.mz-halo--b { animation-delay: .5s; }
@keyframes mzHalo1 {
  0%,  13%  { opacity: 0;   transform: scale(.18); }
  20%       { opacity: .45; }
  38%, 100% { opacity: 0;   transform: scale(1.7); }
}
@keyframes mzHalo2 {
  0%,  34%  { opacity: 0;   transform: scale(.18); }
  41%       { opacity: .45; }
  59%, 100% { opacity: 0;   transform: scale(1.7); }
}
@keyframes mzHalo3 {
  0%,  55%  { opacity: 0;   transform: scale(.18); }
  62%       { opacity: .45; }
  80%, 100% { opacity: 0;   transform: scale(1.7); }
}

/* Trzy punkty gotowe — skóra właściwa rozbłyska raz, spokojnie, i sekwencja rusza od nowa. */
.mz-flash { animation: mzFlash 9s ease-in-out infinite; }
@keyframes mzFlash {
  0%,  66%  { opacity: 0;   }
  73%       { opacity: .55; }
  84%, 100% { opacity: 0;   }
}
/* Trychologia: lupa krąży nad pasmami */
.tr-lens {
  transform-box: fill-box;
  transform-origin: center;
  animation: trLens 6s ease-in-out infinite;
}
@keyframes trLens {
  0%, 100% { transform: translate(0, 0);     }
  50%      { transform: translate(6px, -4px); }
}
/* ---- Henna & rzęsy ----
   Jedna zapętlona sekwencja 11 s: pastelowy zarys → henna brwi pędzelkiem →
   trzy pociągnięcia szczoteczką po rzęsach → blask i płynny powrót do zarysu.
   Wszystko dzieli ten sam takt, więc wystarczy pilnować procentów:
     0–6   stan wyjściowy      36–41  pędzelek schodzi
     6–10  pędzelek wjeżdża    44–72  trzy pociągnięcia po rzęsach
    10–36  henna po łuku       72–88  blask i iskierki
                               88–95  wygaszenie nasyconych warstw
   Fiolet Renaty (ten sam co jej kropka w cenniku) wchodzi tylko w finale —
   zieleń niesie rysunek, fiolet jest akcentem. */
.v-lash__svg { --lash-accent: #7a6ea8; }

/* Warstwa wyjściowa: cienka, wyblakła kreska. Brew ma na tym etapie sam obrys. */
.ls-brow-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
  opacity: .3;
}
.ls-lash-base { opacity: .32; }
.ls-eye { opacity: .34; animation: lsEyeInk 11s ease-in-out infinite; }
@keyframes lsEyeInk {
  0%,  40%  { opacity: .34; }
  72%, 88%  { opacity: .92; }
  95%, 100% { opacity: .34; }
}

/* Henna: kształt brwi na pełno, odsłaniany maską jadącą razem z pędzelkiem.
   Maska i pędzelek MUSZĄ mieć te same procenty i tę samą funkcję czasu (linear),
   inaczej farba wyprzedzi włosie albo za nim zostanie. */
.ls-brow-fill { fill: var(--copper-deep); animation: lsBrowInk 11s linear infinite; }
.ls-brow-mask { animation: lsBrowFill 11s linear infinite; }
@keyframes lsBrowInk {
  0%,  88%  { opacity: 1; }
  93%, 100% { opacity: 0; }
}
@keyframes lsBrowFill {
  /* punkty na górnej krawędzi brwi — te same, po których idzie czubek pędzelka */
  0%,  10%  { transform: translateX(25px);   }
  15%       { transform: translateX(32.6px); }
  20%       { transform: translateX(42.9px); }
  25%       { transform: translateX(55.2px); }
  30%       { transform: translateX(69px);   }
  34%       { transform: translateX(84.9px); }
  36%, 93%  { transform: translateX(100px);  }
  /* powrót maski na start dzieje się już przy zgaszonej brwi, więc nikt go nie widzi */
  99%, 100% { transform: translateX(25px);   }
}

.ls-bbrush { animation: lsBrowBrush 11s linear infinite; }
@keyframes lsBrowBrush {
  0%,   6%  { opacity: 0; transform: translate(23px, 46px);   }
  10%       { opacity: 1; transform: translate(25px, 40px);   }
  15%       { opacity: 1; transform: translate(32.6px, 33.3px); }
  20%       { opacity: 1; transform: translate(42.9px, 28.4px); }
  25%       { opacity: 1; transform: translate(55.2px, 25.5px); }
  30%       { opacity: 1; transform: translate(69px, 25px);   }
  34%       { opacity: 1; transform: translate(84.9px, 28.4px); }
  36%       { opacity: 1; transform: translate(97px, 36px);   }
  41%, 100% { opacity: 0; transform: translate(103px, 43px);  }
}
.ls-brush-tip     { fill: var(--copper-deep); }
.ls-brush-ferrule { fill: var(--ivory); stroke: currentColor; stroke-width: 1.6; }
.ls-brush-stem    { fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; }

/* Rzęsy docelowe: najdłuższa ścieżka ma ~13,6 jednostki, więc kreska 15 chowa ją
   w całości, a jednocześnie prawie nie ma jałowego biegu na starcie — zbieganie
   offsetu do zera „wypuszcza” rzęsę od nasady niemal od pierwszej klatki.
   UWAGA: stroke-dashoffset zostaje wyłącznie na grupie i schodzi na ścieżki
   dziedziczeniem. Podanie go także na `path` przebiłoby animację rodzica własną
   wartością i rzęsy stałyby schowane przez całą pętlę. */
.ls-lash-full path {
  stroke: var(--copper-deep);
  stroke-width: 2.1;
  stroke-dasharray: 15;
}
.ls-lash-full {
  stroke-dashoffset: 15;
  animation-duration: 11s;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}
.ls-g1 { animation-name: lsGrow1; }
.ls-g2 { animation-name: lsGrow2; }
.ls-g3 { animation-name: lsGrow3; }
/* stroke-dashoffset dziedziczy się na ścieżki, więc animujemy je grupami po dwie */
@keyframes lsGrow1 {
  0%,  44%  { stroke-dashoffset: 15; }
  52%, 93%  { stroke-dashoffset: 0;  }
  95%, 100% { stroke-dashoffset: 15; }
}
@keyframes lsGrow2 {
  0%,  55%  { stroke-dashoffset: 15; }
  62%, 93%  { stroke-dashoffset: 0;  }
  95%, 100% { stroke-dashoffset: 15; }
}
@keyframes lsGrow3 {
  0%,  65%  { stroke-dashoffset: 15; }
  72%, 93%  { stroke-dashoffset: 0;  }
  95%, 100% { stroke-dashoffset: 15; }
}
/* Reset długości robimy dopiero pod zgaszoną grupą — inaczej rzęsy „wciągałyby się”
   na oczach widza zamiast zniknąć razem z henną. */
.ls-lash-ink { animation: lsLashInk 11s ease-in-out infinite; }
@keyframes lsLashInk {
  0%,  88%  { opacity: 1; }
  93%, 100% { opacity: 0; }
}

/* Szczoteczka: trzy pociągnięcia od nasady w górę, między nimi zejście do kolejnej
   partii rzęs. Obrót jedzie razem z przesunięciem, więc włosie zawsze idzie zgodnie
   z kierunkiem wachlarza — inaczej na skrajnych rzęsach szłoby w poprzek. */
.ls-wand { animation: lsWand 11s ease-in-out infinite; }
@keyframes lsWand {
  0%,  40%  { opacity: 0; transform: translate(37px, 68px) rotate(-20deg); }
  44%       { opacity: 1; transform: translate(39px, 61px) rotate(-20deg); }
  52%       { opacity: 1; transform: translate(36px, 54px) rotate(-20deg); }
  /* środkowe pociągnięcie startuje tuż nad tęczówką — niżej kremowe włosie
     wycinałoby dziurę w jej okręgu */
  55%       { opacity: 1; transform: translate(59px, 56px) rotate(-3deg);  }
  62%       { opacity: 1; transform: translate(58px, 48px) rotate(-3deg);  }
  65%       { opacity: 1; transform: translate(81px, 60px) rotate(18deg);  }
  72%       { opacity: 1; transform: translate(86px, 51px) rotate(18deg);  }
  77%, 100% { opacity: 0; transform: translate(92px, 58px) rotate(18deg);  }
}
.ls-wand-body   { fill: var(--ivory); stroke: currentColor; stroke-width: 2; }
.ls-wand-spiral { fill: none; stroke: currentColor; stroke-width: 1.1; opacity: .55; }
.ls-wand-stem   { fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; }

/* Finał: dwa pierścienie rozchodzą się wokół oka, iskierki błyskają nad brwią.
   Drugi pierścień to ta sama klatka opóźniona o pół sekundy — poza własnym
   rozejściem jest przezroczysty, więc przesunięcie pętli niczego nie psuje. */
.ls-glow {
  fill: none;
  stroke: var(--lash-accent);
  stroke-width: 1.3;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: lsGlow 11s ease-out infinite;
}
.ls-glow--b { animation-delay: .45s; }
@keyframes lsGlow {
  0%,  71%  { opacity: 0;   transform: scale(.62); }
  78%       { opacity: .28; }
  90%, 100% { opacity: 0;   transform: scale(1.1); }
}
.ls-spark {
  fill: var(--lash-accent);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: lsSpark 11s ease-in-out infinite;
}
.ls-spark--b { animation-delay: .5s;  }
.ls-spark--c { animation-delay: .95s; }
@keyframes lsSpark {
  0%,  71%  { opacity: 0;   transform: scale(.35) rotate(-25deg); }
  77%       { opacity: .85; transform: scale(1)   rotate(0deg);   }
  86%, 100% { opacity: 0;   transform: scale(.55) rotate(18deg);  }
}

/* ---- Depilacja woskiem ----
   Jedna zapętlona sekwencja 9 s, ten sam takt dla wszystkich warstw:
     0–18   plaster osiada na skórze      50–72  poświata i błysk po gładkiej skórze
    18–40   wosk „tężeje” (przerwa)       72–86  spokojne przytrzymanie efektu
    40–56   zerwanie ku górze             86–100 włoski odrastają, pętla wraca
   Miód wchodzi wyłącznie w plastrze — reszta rysunku zostaje w zieleni salonu,
   dokładnie tak, jak fiolet jest akcentem tylko w finale karty z henną. */
.v-wax__svg { --wax-honey: #d8b26b; --wax-honey-deep: #a97f34; }

/* Pasmo skóry musi odciąć się od tła karty (piaskowe + zielona poświata), inaczej
   „zbliżenie na skórę” gubi się w płycie — stąd wypełnienie plus cienki obrys.
   Krycie idzie przez fill-opacity, a nie przez opacity: białe warstwy finału leżą
   na tym prostokącie i przygaszenie całej grupy zjadłoby także ich blask. */
.wx-skin {
  fill: var(--copper-lite);
  fill-opacity: .34;
  stroke: var(--copper);
  stroke-width: 1.3;
  stroke-opacity: .3;
}
.wx-surface { fill: none; stroke: var(--copper); stroke-width: 1; opacity: .2; }

/* UWAGA: stroke-dashoffset zostaje wyłącznie na grupie i schodzi na ścieżki
   dziedziczeniem — podany także na `path` przebiłby animację rodzica. Najdłuższy
   włosek ma ~21 jednostek, więc kreska 23 chowa go w całości. */
.wx-hairs {
  fill: none;
  stroke: var(--copper-deep);
  stroke-width: 1.5;
  stroke-linecap: round;
  opacity: .7;
  animation: wxHairs 9s ease-in-out infinite;
}
.wx-hairs path { stroke-dasharray: 23; }
@keyframes wxHairs {
  0%,  40%  { stroke-dashoffset: 0;  }
  /* 3% taktu, czyli 0,27 s na zniknięcie — czyta się jako szarpnięcie, nie jako zanik */
  43%, 86%  { stroke-dashoffset: 23; }
  100%      { stroke-dashoffset: 0;  }
}

/* Plaster: oś odrywania to jego lewy dolny róg (11, 54) w układzie viewBox, więc
   obrót unosi najpierw prawy koniec — pasek schodzi ze skóry, a nie odlatuje płasko.
   Stąd view-box zamiast fill-box: origin podajemy wprost we współrzędnych rysunku. */
.wx-strip {
  transform-box: view-box;
  transform-origin: 11px 54px;
  animation: wxStrip 9s cubic-bezier(.65, 0, .35, 1) infinite;
}
.wx-strip-body {
  fill: var(--wax-honey);
  fill-opacity: .42;         /* półprzezroczysty — włoski prześwitują przez wosk */
  stroke: var(--wax-honey-deep);
  stroke-width: 1.6;
  stroke-opacity: .5;
}
.wx-strip-tab {
  fill: none;
  stroke: var(--wax-honey-deep);
  stroke-width: 1.4;
  stroke-linecap: round;
  opacity: .4;
}
@keyframes wxStrip {
  0%,   6%  { opacity: 0; transform: translateY(-34px) rotate(-6deg);  }
  12%       { opacity: 1;                                              }
  18%, 40%  { opacity: 1; transform: translateY(0)     rotate(0deg);   }
  43%       { opacity: 1; transform: translateY(-2px)  rotate(-11deg); }  /* poderwanie */
  48%       { opacity: 1; transform: translateY(-16px) rotate(-24deg); }
  56%       { opacity: 0; transform: translateY(-52px) rotate(-30deg); }
  /* powrót na pozycję startową dzieje się przy zgaszonym plastrze, więc nikt go nie widzi */
  58%, 100% { opacity: 0; transform: translateY(-34px) rotate(-6deg);  }
}

/* Włoski wyrwane razem z woskiem — pojawiają się dopiero w momencie zerwania. */
.wx-caught {
  fill: none;
  stroke: var(--copper-deep);
  stroke-width: 1.4;
  stroke-linecap: round;
  opacity: 0;
  animation: wxCaught 9s ease-out infinite;
}
@keyframes wxCaught {
  0%,  41%  { opacity: 0;   }
  46%, 52%  { opacity: .55; }
  56%, 100% { opacity: 0;   }
}

/* Poświata: miękka biel rozlana po całym pasmie skóry zaraz po zerwaniu.
   Wypełnienie (gradient rozmywający krawędź) siedzi w atrybucie w HTML — tutaj
   sterujemy wyłącznie kryciem, żeby nie przebić go deklaracją `fill`. */
.wx-glow { opacity: 0; animation: wxGlow 9s ease-out infinite; }
@keyframes wxGlow {
  0%,  44%  { opacity: 0;   }
  51%       { opacity: .55; }
  74%, 100% { opacity: 0;   }
}

/* Błysk: wąski, skośny pas światła przejeżdżający po skórze. Oba końce ruchu leżą
   poza maską skóry, więc reset pętli jest niewidoczny i nie potrzeba wygaszania. */
.wx-sheen { animation: wxSheen 9s ease-in-out infinite; }
@keyframes wxSheen {
  0%,  46%  { transform: translateX(0);      }
  70%, 100% { transform: translateX(175px);  }
}

.wx-spark {
  fill: #fff;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: wxSpark 9s ease-in-out infinite;
}
.wx-spark--b { animation-delay: .35s; }
.wx-spark--c { animation-delay: .7s;  }
@keyframes wxSpark {
  0%,  50%  { opacity: 0;   transform: scale(.35) rotate(-25deg); }
  57%       { opacity: .95; transform: scale(1)   rotate(0deg);   }
  70%, 100% { opacity: 0;   transform: scale(.55) rotate(18deg);  }
}
/* ---- Makijaż & ciało (Glow Look) ----
   Pełny makijaż twarzy w jednej zapętlonej sekwencji 14 s. Wszystkie warstwy liczą
   klatki procentowe z tego samego taktu, więc żaden krok nie wyprzedza drugiego:
      0–9   czysta cera                     58–78  rumieniec (rozkwit + blendowanie)
      6–31  baza — gąbka i maska podkładu   72–90  usta + iskierka błyszczyka
     26–36  rozbłysk wygładzenia            84–96  glow: plusiki i miękkie światło
     33–57  oczy — cień, potem tusz         93–100 wygaszenie i reset
   Kontur twarzy celowo NIE gaśnie: karta nigdy nie jest pusta, a początek każdej
   pętli to dokładnie „czysta cera”, od której zaczyna się opis kroków.
   Róż i nude są akcentem tylko tej karty — tak jak miód w karcie z woskiem
   i fiolet w karcie z henną. Kreska zostaje w zieleni salonu. */
.v-makeup__svg {
  --mk-skin:   #f8ddcd;   /* podkład — brzoskwiniowa cera z grafiki źródłowej      */
  --mk-shadow: #b07d88;   /* cień do powiek — brudny róż                            */
  --mk-blush:  #f0a99c;   /* rumieniec — ciepły łososiowy róż                       */
  --mk-lip:    #d4785c;   /* usta — koral                                           */
  --mk-hair:   #6f8a52;   /* włosy i kok — stały kolor, warstwa bez animacji        */
  --mk-iris:   #5f8a45;   /* tęczówka — zieleń oczu z grafiki źródłowej             */
}

/* Wspólne wygaszenie wszystkich warstw koloru na końcu pętli. Jedna animacja na
   dwie grupy (kolor pod kreską + rzęsy nad kreską) zamiast osobnych klatek gaszących
   w każdej warstwie z osobna — inaczej makijaż znikałby kawałkami. */
.mk-fade { animation: mkFade 14s ease-in-out infinite; }
@keyframes mkFade {
  0%,  93%  { opacity: 1; }
  98%, 100% { opacity: 0; }
}

/* 1. BAZA — kolor jest cały czas w pełni nałożony, widoczność robi wyłącznie maska,
   której dolna krawędź zjeżdża w dół dokładnie za czubkiem gąbki. */
.mk-skin { fill: var(--mk-skin); }

/* Włosy: jedyna warstwa rysunku BEZ animacji — stały kolor przez całą pętlę. Leżą
   nad podkładem, więc czubek głowy nigdy nie łapie koloru bazy. Nie są też częścią
   grupy .mk-fade — przy resecie makijażu fryzura zostaje na miejscu. */
.mk-hair     { fill: var(--mk-hair); }
/* Linia włosów jest w grafice źródłowej pełnoprawnym obrysem, nie delikatnym
   akcentem — stąd waga zbliżona do konturu twarzy i pełne krycie. */
.mk-hairline { fill: none; stroke: var(--copper-deep); stroke-width: 1.6; stroke-linecap: round; }
/* Kok: to samo wypełnienie co włosy plus własny pełny obrys, bo w grafice źródłowej
   koło koka jest domknięte i przykrywa czubek czaszki. */
.mk-bun      { fill: var(--mk-hair); stroke: currentColor; stroke-width: 2; }
/* Tęczówka: zieleń z cienkim ciemnym pierścieniem — inaczej zielone koło rozmywa się
   w jasnej cerze i oko traci punkt ciężkości. */
.mk-iris     { fill: var(--mk-iris); stroke: var(--copper-deep); stroke-width: .95; }
.mk-base-mask {
  transform-box: view-box;
  transform-origin: 0 0;
  animation: mkBaseMask 14s ease-in-out infinite;
}
@keyframes mkBaseMask {
  0%,  9%   { transform: translateY(0);      }  /* krawędź nad czubkiem koka       */
  12%       { transform: translateY(34px);   }  /* czoło spod linii włosów         */
  16%       { transform: translateY(42px);   }  /* brwi i powieki                  */
  20%       { transform: translateY(52px);   }  /* linia oczu                      */
  24%       { transform: translateY(60px);   }  /* policzki                        */
  28%       { transform: translateY(72px);   }  /* usta                            */
  30%, 96%  { transform: translateY(82.5px); }  /* cała twarz, broda kończy się na 87,5 */
  /* powrót maski na start dzieje się przy zgaszonej warstwie, więc nikt go nie widzi */
  99%, 100% { transform: translateY(0);      }
}

/* Gąbka: zygzak z czoła na brodę, ten sam rytm co maska. Obrót dopisany PO translacji,
   przy transform-origin w punkcie (0,0) viewBoxa — dzięki temu kręci gąbką wokół jej
   czubka, a nie wokół środka rysunku (domyślne 50% 50% odniosłoby się do viewBoxa). */
.mk-sponge {
  transform-box: view-box;
  transform-origin: 0 0;
  animation: mkSponge 14s ease-in-out infinite;
}
@keyframes mkSponge {
  0%,   6%  { opacity: 0; transform: translate(56px, 26px) rotate(-6deg); }
  9%        { opacity: 1; transform: translate(48px, 36px) rotate(-4deg); }
  12%       { opacity: 1; transform: translate(72px, 39px) rotate(8deg);  }
  16%       { opacity: 1; transform: translate(44px, 50px) rotate(-8deg); }
  20%       { opacity: 1; transform: translate(76px, 60px) rotate(8deg);  }
  24%       { opacity: 1; transform: translate(46px, 70px) rotate(-8deg); }
  28%       { opacity: 1; transform: translate(68px, 80px) rotate(4deg);  }
  /* zejście z kadru przy gasnącym kryciu — powrót na start nikt już nie widzi */
  32%, 100% { opacity: 0; transform: translate(72px, 93px) rotate(2deg);  }
}
.mk-sponge-body {
  fill: var(--mk-skin);
  fill-opacity: .9;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
}

/* Rozbłysk wygładzenia: wąski skośny pas światła przejeżdżający po twarzy raz,
   zaraz po nałożeniu bazy. Oba końce ruchu leżą poza owalem, więc reset pętli
   jest niewidoczny i nie trzeba osobnego wygaszania. */
.mk-sheen { animation: mkSheen 14s ease-in-out infinite; }
@keyframes mkSheen {
  0%,  26%  { transform: translateX(0);     }
  36%, 100% { transform: translateX(170px); }
}

/* 2. OCZY — aplikator to sama kreska sunąca nad linią oczu; cienie odsłaniają się
   dokładnie wtedy, gdy ją mija (lewy 36–42%, prawy 46–51%). */
.mk-liner {
  transform-box: view-box;
  transform-origin: 0 0;
  fill: none;
  stroke: var(--mk-shadow);
  stroke-width: 1.4;
  stroke-linecap: round;
  animation: mkLiner 14s ease-in-out infinite;
}
/* Punkty 36/42 i 46/51% to DOKŁADNIE końce ścieżek cienia — aplikator musi mijać
   powiekę w tej samej sekundzie, w której cień się z niej wysuwa. */
@keyframes mkLiner {
  0%,  33%  { opacity: 0;  transform: translate(41.8px, 55.2px); }
  36%       { opacity: .9; transform: translate(44.2px, 55.8px); }
  42%       { opacity: .9; transform: translate(55.7px, 57.3px); }
  46%       { opacity: .9; transform: translate(64.3px, 57.3px); }
  51%       { opacity: .9; transform: translate(75.8px, 55.8px); }
  54%, 100% { opacity: 0;  transform: translate(78.2px, 55.2px); }
}
/* Cień: ścieżka ma ~18,3 jednostki, więc kreska 20 chowa ją w całości.
   UWAGA: stroke-dashoffset animujemy na samej ścieżce — na grupie dziedziczyłby się
   na obie powieki naraz i cień pojawiałby się po dwóch stronach jednocześnie. */
.mk-shadow {
  fill: none;
  stroke: var(--mk-shadow);
  stroke-width: 3;
  stroke-linecap: round;
  opacity: .2;
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  animation-duration: 14s;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}
.mk-shadow--l { animation-name: mkShadowL; }
.mk-shadow--r { animation-name: mkShadowR; }
@keyframes mkShadowL {
  0%,  36%  { stroke-dashoffset: 20; }
  42%, 96%  { stroke-dashoffset: 0;  }
  99%, 100% { stroke-dashoffset: 20; }
}
@keyframes mkShadowR {
  0%,  46%  { stroke-dashoffset: 20; }
  51%, 96%  { stroke-dashoffset: 0;  }
  99%, 100% { stroke-dashoffset: 20; }
}

/* Tusz: rzęsy w jednym takcie (53–57%) ciemnieją i wydłużają się. Każda jest lekko
   wygięta i ma ~3,1 jednostki, więc kreska 3,6 chowa każdą w całości — przy prostych
   odcinkach wystarczało 3,4, ale łuk jest dłuższy od swojej cięciwy.
   UWAGA: stroke-dashoffset zostaje na grupie i schodzi na ścieżki dziedziczeniem —
   podany także na `path` przebiłby animację rodzica i rzęsy stałyby schowane. */
.mk-lash-base { opacity: .4; }
.mk-lash-full {
  fill: none;
  stroke: var(--copper-deep);
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-dashoffset: 3.6;
  animation: mkLash 14s ease-out infinite;
}
.mk-lash-full path { stroke-dasharray: 3.6; }
@keyframes mkLash {
  0%,  53%  { stroke-dashoffset: 3.6; }
  57%, 96%  { stroke-dashoffset: 0;   }
  99%, 100% { stroke-dashoffset: 3.6; }
}

/* 3. RUMIENIEC — najpierw zwarta plamka, potem rośnie i blednie: to samo, co robi
   dłoń rozcierająca produkt. Skalowanie idzie od środka elipsy (fill-box), więc
   plama rozchodzi się na boki, a nie zjeżdża w róg policzka.
   Kolor podajemy na stopach gradientu, nie przez currentColor: stop-color:currentColor
   rozwiązuje się względem <defs>, czyli złapałoby zieleń kreski zamiast różu. */
.mk-blush-stop { stop-color: var(--mk-blush); }
.mk-blush {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: mkBlush 14s ease-in-out infinite;
}
.mk-blush--r { animation-delay: .22s; }
@keyframes mkBlush {
  0%,  58%  { opacity: 0;   transform: scale(.4);   }
  63%       { opacity: .95; transform: scale(.85);  }  /* świeża plamka */
  69%       { opacity: .55; transform: scale(1.08); }  /* rozcieranie   */
  74%       { opacity: .8;  transform: scale(.98);  }  /* puls          */
  78%, 100% { opacity: .68; transform: scale(1.03); }  /* wyblendowana  */
}

/* 4. USTA — satynowe wypełnienie wjeżdża pod gotowy obrys warg, więc kreska ust
   zostaje ostra. Animujemy fill-opacity, a nie opacity, żeby nie ruszyć obrysu. */
.mk-lips {
  fill: var(--mk-lip);
  fill-opacity: 0;
  animation: mkLips 14s ease-in-out infinite;
}
@keyframes mkLips {
  0%,  72%  { fill-opacity: 0;   }
  78%, 96%  { fill-opacity: .85; }
  99%, 100% { fill-opacity: 0;   }
}
.mk-gloss {
  fill: #fff;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: mkGloss 14s ease-in-out infinite;
}
@keyframes mkGloss {
  0%,  77%  { opacity: 0;   transform: scale(.2)  rotate(-20deg); }
  81%       { opacity: .95; transform: scale(1)   rotate(0deg);   }
  85%       { opacity: .4;  transform: scale(.8)  rotate(4deg);   }
  88%       { opacity: .9;  transform: scale(1.1) rotate(0deg);   }
  94%, 100% { opacity: 0;   transform: scale(.6)  rotate(14deg);  }
}

/* 5. GLOW — plusiki pulsują dwa razy w oknie 84–96%, każdy z własnym opóźnieniem.
   Poza swoim oknem są przezroczyste, więc przesunięcie fazy niczego nie psuje. */
.mk-spark {
  fill: none;
  stroke: var(--copper-deep);
  stroke-width: 1.5;
  stroke-linecap: round;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: mkSpark 14s ease-in-out infinite;
}
.mk-spark--b { animation-delay: .16s; }
.mk-spark--c { animation-delay: .32s; }
.mk-spark--d { animation-delay: .48s; }
.mk-spark--e { animation-delay: .64s; }
.mk-spark--f { animation-delay: .8s;  }
@keyframes mkSpark {
  0%,  84%  { opacity: 0;   transform: scale(.3)   rotate(-30deg); }
  87%       { opacity: .75; transform: scale(1.05) rotate(0deg);   }
  90%       { opacity: .35; transform: scale(.8)   rotate(6deg);   }
  93%       { opacity: .8;  transform: scale(1.1)  rotate(0deg);   }
  96%, 100% { opacity: 0;   transform: scale(.6)   rotate(20deg);  }
}

/* Finałowe światło: lekko rosnąca poświata nad całą twarzą. Wypełnienie (gradient
   rozmywający krawędź) siedzi w atrybucie w HTML — tutaj sterujemy wyłącznie
   kryciem i skalą, żeby nie przebić go deklaracją `fill`. */
.mk-glow {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: mkGlow 14s ease-out infinite;
}
@keyframes mkGlow {
  0%,  83%  { opacity: 0;  transform: scale(.85); }
  89%       { opacity: .6; transform: scale(1);   }
  98%, 100% { opacity: 0;  transform: scale(1.1); }
}

@media (prefers-reduced-motion: reduce) {
  /* Bez animacji twarz zatrzymuje się na środku: lewa połowa gładka, prawa „przed”. */
  .fc-sweep, .fc-mask, .fc-sparks,
  .mz-needle, .mz-dot, .mz-halo, .mz-flash,
  .tr-lens,
  .mk-fade, .mk-base-mask, .mk-sponge, .mk-sheen, .mk-liner, .mk-shadow,
  .mk-lash-full, .mk-blush, .mk-lips, .mk-gloss, .mk-spark, .mk-glow,
  .wx-hairs, .wx-strip, .wx-caught, .wx-glow, .wx-sheen, .wx-spark,
  .ls-eye, .ls-brow-fill, .ls-brow-mask, .ls-lash-ink, .ls-lash-full,
  .ls-bbrush, .ls-wand, .ls-glow, .ls-spark { animation: none; }
  .fc-sparks { opacity: .55; }
  /* Mezoterapia bez ruchu: igła stoi nad skórą, a trzy punkty są już podane. */
  .mz-dot { opacity: .9; }
  /* Wosk bez ruchu zatrzymuje się na najczytelniejszej klatce — plaster leży na skórze
     z włoskami. Wszystkie warstwy finału mają zerową opacity w stanie bazowym, więc
     samo wyłączenie animacji wystarcza, nic nie trzeba nadpisywać. */
  /* Makijaż bez ruchu zatrzymuje się na efekcie końcowym: gotowa twarz w pełnym
     Glow Look. Podkład wymaga odsunięcia maski poza rysunek, reszta warstw dostaje
     stan docelowy wprost — narzędzia znikają, bo bez ruchu tylko zasłaniałyby twarz. */
  .mk-base-mask { transform: translateY(82.5px); }
  .mk-shadow    { stroke-dashoffset: 0; }
  .mk-lash-full { stroke-dashoffset: 0; }
  .mk-blush     { opacity: .68; }
  .mk-lips      { fill-opacity: .85; }
  .mk-gloss     { opacity: .9; }
  .mk-spark     { opacity: .5; }
  .mk-sponge, .mk-liner { display: none; }
  /* Henna bez ruchu zatrzymuje się na efekcie końcowym: brew wypełniona na całej
     długości (maska odsunięta poza rysunek), rzęsy wypuszczone, narzędzia schowane. */
  .ls-brow-mask { transform: translateX(100px); }
  .ls-lash-full { stroke-dashoffset: 0; }
  .ls-eye { opacity: .92; }
  .ls-spark { opacity: .7; }
  .ls-bbrush, .ls-wand { display: none; }
}

/* ============================================================
   PARTNERZY (strona główna)
   Same logotypy — czarne na białym i o skrajnie różnych proporcjach (ghd 1.66:1,
   Authentic Beauty Concept 1:1), więc każdy dostaje kwadratową płytkę i obrazek
   o konkretnych wymiarach z object-fit. Procentowy max-height nie zadziała:
   wiersz o automatycznej wysokości nie daje mu punktu odniesienia.
   ============================================================ */
.partners {
  background: var(--ivory);
  padding: clamp(4rem, 8vw, 7rem) var(--pad);
}
.partners__head {
  max-width: var(--maxw);
  margin: 0 auto 2.8rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}
.partners__head .eyebrow::after {
  content: "";
  width: 2.2rem; height: 1px; background: var(--copper);
}
.partners__intro { max-width: 56ch; color: var(--ink-soft); }

.partners__row {
  max-width: var(--maxw);
  margin: 0 auto;
  list-style: none;
  padding: 0;
  display: grid;
  /* minmax(0, …) zamiast samego 1fr: domyślne minimum ścieżki to min-content,
     a te logotypy mają po ~1000 px natywnej szerokości, więc kolumny puchły
     do 458 px w kontenerze szerokim na 1280 px i siatka wylewała się z ekranu. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.2rem;
}
.partners__item {
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: 18px;
  display: grid;
  place-items: center;
  padding: 1.6rem;
  transition: border-color .4s var(--ease), box-shadow .4s var(--ease), transform .4s var(--ease);
}
.partners__item:hover {
  border-color: var(--copper);
  box-shadow: var(--shadow-sm);
  transform: translateY(-3px);
}
.partners__item img {
  width: 100%;
  /* Konkretna wysokość w pikselach, nie 100%: wysokość karty wynika z zawartości,
     więc procent nie ma punktu odniesienia i kwadratowe logotypy (ABC, Lavish Care)
     rozpychały swoje karty wyżej niż panoramiczne. Teraz wszystkie cztery są równe. */
  height: 92px;
  object-fit: contain;
  display: block;
  /* Logotypy są mocno kontrastowe — lekkie wyciszenie sadza je w palecie strony,
     pełna czerń wraca pod kursorem. */
  filter: grayscale(1);
  opacity: .72;
  transition: filter .4s var(--ease), opacity .4s var(--ease);
}
.partners__item:hover img { filter: none; opacity: 1; }

.partners__cta {
  max-width: var(--maxw);
  margin: 2.6rem auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .9rem;
}
.partners__cta-note { color: var(--ink-faint); font-size: .88rem; }

.services__cta {
  max-width: var(--maxw);
  margin: 3.5rem auto 0;
  padding: 2.4rem;
  border-radius: 20px;
  /* Panel jedzie z tokenów przycisków — to jest powierzchnia CTA „Rezerwuj”,
     więc musi trzymać kolor razem z przyciskiem, który na nim leży. */
  background: linear-gradient(120deg, var(--cta-1), var(--cta-2));
  color: var(--cta-fg);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  box-shadow: var(--shadow-md);
}
.services__cta p { font-family: var(--serif); font-size: 1.6rem; font-style: italic; }
.services__cta .btn--solid {
  background: var(--ivory);
  color: var(--cta-2);
}
.services__cta .btn--solid:hover { background: #fff; }

/* ============================================================
   PORTFOLIO
   ============================================================ */
.portfolio {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(5rem, 10vw, 9rem) var(--pad);
}
.portfolio__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  margin-bottom: 2.6rem;
}
.portfolio__note { color: var(--ink-soft); max-width: 34ch; }

/* Justified rows (built by pfLayout in main.js).
   CSS multi-column balanced the preview by content, so with mixed portrait /
   landscape photos every column ended at a different height and the grid had a
   ragged bottom edge. Instead JS cuts the photos into rows that all share ONE
   height and each span the full width — so every photo lines up top and bottom
   and the grid closes on a straight line.
   Widths come from flex-grow set to each photo's aspect ratio, so the variety
   of shapes survives (panoramas wide, portraits narrow) and flexbox — not our
   px arithmetic — absorbs the sub-pixel rounding, leaving no seams. */
.portfolio__grid {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.pf-row {
  display: flex;
  gap: 1rem;
  min-width: 0;
}
.portfolio__grid .pf-item {
  min-width: 0;      /* let flex-grow drive the width, not the image's intrinsic size */
  margin-bottom: 0;  /* the row gap handles spacing */
}
/* Fill the row's height exactly. Widths already follow each photo's real
   aspect ratio, so `cover` only trims the gap between a row's natural height
   and the grid's shared one — pfLayout picks that height to keep the trim
   small (~8% typically). The full, uncropped photo is one click away. */
.portfolio__grid .pf-item img { height: 100%; object-fit: cover; }

.pf-item {
  display: block;            /* works whether the item is a <figure> or an <a> */
  break-inside: avoid;
  margin-bottom: 1rem;
  border-radius: 14px;
  overflow: hidden;
  position: relative;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  background: var(--sand);
  color: inherit;
}
.pf-item img {
  width: 100%;
  height: auto; /* scale to column width; the width/height attrs only set aspect-ratio */
  transition: transform .8s var(--ease), filter .6s var(--ease);
}
.pf-item::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 55%, rgba(27,30,21,.5));
  opacity: 0;
  transition: opacity .5s var(--ease);
}
.pf-item .pf-plus {
  position: absolute;
  z-index: 2;
  bottom: 14px; right: 14px;
  width: 40px; height: 40px;
  border-radius: 50%;
  background: rgba(253,250,246,.92);
  color: var(--copper-deep);
  display: grid; place-items: center;
  font-size: 1.4rem;
  opacity: 0;
  transform: scale(.6);
  transition: .5s var(--ease);
}
.pf-item:hover img { transform: scale(1.07); }
.pf-item:hover::after { opacity: 1; }
.pf-item:hover .pf-plus { opacity: 1; transform: scale(1); }

/* Preview grid uses ↗ glyphs — nudge optically */
.pf-item .pf-plus { font-size: 1.15rem; padding-bottom: 2px; }

.portfolio__more {
  margin-top: 2.6rem;
  display: flex;
  justify-content: center;
}

/* Salon gallery sits directly under the portfolio one — trim the doubled
   section padding so the two grids read as one flow, not two blocks. */
.portfolio--salon { padding-top: 0; }

/* ============================================================
   PORTFOLIO PAGE (portfolio.html)
   ============================================================ */
.pf-page { background: var(--cream); }

.pf-top {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .85rem var(--pad);
  background: rgba(247, 248, 242, .82);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line-soft);
}
.pf-top__brand img { height: 38px; width: auto; }

.pf-back {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-family: var(--sans);
  font-weight: 500;
  font-size: .95rem;
  color: var(--ink);
  padding: .5rem .9rem .5rem .7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--ivory);
  transition: .3s var(--ease);
}
.pf-back:hover { background: var(--copper); color: #fff; border-color: var(--copper); transform: translateX(-2px); }
.pf-back__arrow { font-size: 1.15rem; line-height: 1; transition: transform .3s var(--ease); }
.pf-back:hover .pf-back__arrow { transform: translateX(-3px); }

.pf-main {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(2.5rem, 6vw, 4.5rem) var(--pad) clamp(4rem, 8vw, 7rem);
}
.pf-head { margin-bottom: 2.6rem; }
.pf-lead { color: var(--ink-soft); max-width: 46ch; margin-top: .6rem; }

/* JS-driven masonry: fixed flex columns so appending new photos never
   reflows/reorders the ones already on screen. */
.pf-grid {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}
.pf-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.pf-col .pf-item { margin-bottom: 0; } /* the column gap handles spacing */
.pf-item--fade img { opacity: 0; transition: opacity .6s var(--ease), transform .8s var(--ease); }
.pf-item--fade.is-loaded img { opacity: 1; }

/* Infinite-scroll sentinel / loader */
.pf-sentinel {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 70px;
  margin-top: 1.5rem;
}
.pf-sentinel--done { display: none; }
.pf-spinner {
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 3px solid var(--sand);
  border-top-color: var(--copper);
  animation: pfSpin .8s linear infinite;
}
@keyframes pfSpin { to { transform: rotate(360deg); } }

.pf-foot { margin-top: 2.5rem; display: flex; justify-content: center; }
.pf-back--foot { font-size: 1rem; padding: .7rem 1.3rem .7rem 1.1rem; }

/* Depth fade over the LAST row of the HOME PAGE preview grid — it signals
   "there is more behind this" and leads into the "zobacz całą galerię" link.
   Applied as a real mask, so the lowest photo dissolves to transparent at its
   own bottom and no hard image edge can show above the fade.
   The full galleries (portfolio.html / zabiegi.html / salon.html) are NOT
   masked — there every photo has to stay fully visible to the last row. */
.pf-gridwrap { position: relative; }
.portfolio__grid {
  /* Kept short on purpose: the rows now end on a straight line, so a small
     fade is enough to hint at more photos without hiding the last row. */
  --pf-fade: clamp(48px, 6vw, 96px);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--pf-fade)), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - var(--pf-fade)), transparent);
}

@media (prefers-reduced-motion: reduce) {
  .pf-item--fade img { opacity: 1; transition: none; }
  .pf-spinner { animation: none; }
}

/* ============================================================
   REVIEWS
   ============================================================ */
.reviews {
  background: linear-gradient(180deg, var(--cream), var(--ivory));
  padding: clamp(5rem, 10vw, 9rem) 0;
  overflow: hidden;
}
.reviews__head {
  text-align: center;
  padding: 0 var(--pad);
  margin-bottom: 3.2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}
.reviews__intro { max-width: 48ch; color: var(--ink-soft); }

/* ---- Infinite vertical review wall ---- */
.wall {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad);
  display: flex;
  gap: 1.1rem;
  height: min(76vh, 660px);
  overflow: hidden;
  -webkit-mask: linear-gradient(180deg, transparent, #000 11%, #000 89%, transparent);
          mask: linear-gradient(180deg, transparent, #000 11%, #000 89%, transparent);
}
.wall__col { flex: 1 1 0; min-width: 0; }
.wall__inner {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  animation: wallUp var(--dur, 44s) linear infinite;
  will-change: transform;
}
.wall__col--down .wall__inner { animation-name: wallDown; }
.wall:hover .wall__inner { animation-play-state: paused; }
@keyframes wallUp   { from { transform: translateY(0); }        to { transform: translateY(-50%); } }
@keyframes wallDown { from { transform: translateY(-50%); }     to { transform: translateY(0); } }
/* Reduced motion / no-JS-loop fallback: static, fully visible */
.wall--static { height: auto; overflow: visible; -webkit-mask: none; mask: none; }
.wall--static .wall__inner { animation: none; }

.rev {
  background: var(--ivory);
  border: 1px solid var(--line-soft);
  border-radius: 16px;
  padding: 1.5rem 1.6rem;
  box-shadow: var(--shadow-sm);
  display: flex;
  flex-direction: column;
  gap: .85rem;
}
.rev__top { display: flex; align-items: center; gap: .85rem; }
/* ---------- Awatary opinii ----------
   Bez zdjęć klientów (wizerunek / RODO). Zamiast tego generowany znaczek:
   gradient --a → --b pod kątem --ang, na to opcjonalny wzór --pat (kropki, paski,
   kreski), a na wierzchu pierwsza litera imienia w jednym z pięciu krojów.
   Klasę g/p/f dobiera main.js z hasha imienia, więc zestaw jest stały dla osoby,
   a na ścianie wygląda na losowy. Gradienty trzymają ciemniejszy zakres —
   biała litera musi zostać czytelna na każdym z nich. */
.rev__ava {
  width: 46px; height: 46px;
  border-radius: 50%;
  flex: none;
  display: grid; place-items: center;
  color: #fff;
  line-height: 1;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
  border: 2px solid rgba(255, 255, 255, .7);
  box-shadow: 0 3px 10px -4px rgba(30, 45, 20, .55);
  overflow: hidden;
  background-color: var(--a);
  background-image: var(--pat, none), linear-gradient(var(--ang, 135deg), var(--a), var(--b));
  background-size: var(--pat-size, auto), 100% 100%;
}
/* Litera siedzi w spanie, bo każdy krój ma inną wysokość wersalika i inny środek
   optyczny — `place-items: center` centruje pudełko wiersza, nie sam znak.
   --ox / --oy to ręczna korekta na krój (w em, więc skaluje się z font-size). */
.rev__ava > span {
  display: block;
  transform: translate(var(--ox, 0em), var(--oy, 0em));
}
/* Gradienty — ziemista rodzina wokół botanicznej zieleni marki: mech, szałwia,
   morska zieleń, indygo, śliwka, wino, terakota, ochra, przygaszony róż, grafit.
   Wszystkie przygaszone (nic neonowego obok kremowego tła) i schodzące do
   ciemnego końca, żeby biała litera została czytelna na każdym. */
.rev__ava--g1  { --a: #6f9142; --b: #2f4a1e; --ang: 135deg; } /* mech */
.rev__ava--g2  { --a: #7fa38b; --b: #274a3b; --ang: 160deg; } /* szałwia */
.rev__ava--g3  { --a: #4f8c86; --b: #143a3d; --ang: 45deg;  } /* morska zieleń */
.rev__ava--g4  { --a: #6480ac; --b: #232f52; --ang: 145deg; } /* indygo */
.rev__ava--g5  { --a: #8a5f7d; --b: #3a2036; --ang: 200deg; } /* śliwka */
.rev__ava--g6  { --a: #a8505a; --b: #4a1a22; --ang: 20deg;  } /* wino */
.rev__ava--g7  { --a: #bb7450; --b: #5f3020; --ang: 25deg;  } /* terakota */
.rev__ava--g8  { --a: #bb9440; --b: #5c4113; --ang: 100deg; } /* ochra */
.rev__ava--g9  { --a: #8a8f45; --b: #3b4016; --ang: 175deg; } /* oliwka */
.rev__ava--g10 { --a: #6f7468; --b: #24281f; --ang: 190deg; } /* grafit */
/* Wzory: p0 = czysty gradient, dalej kropki, paski pionowe, kreski ukośne. */
.rev__ava--p1 {
  --pat: radial-gradient(rgba(255, 255, 255, .3) 1.1px, transparent 1.2px);
  --pat-size: 7px 7px;
}
.rev__ava--p2 {
  --pat: repeating-linear-gradient(90deg, rgba(255, 255, 255, .16) 0 3px, transparent 3px 9px);
  --pat-size: 100% 100%;
}
.rev__ava--p3 {
  --pat: repeating-linear-gradient(45deg, rgba(255, 255, 255, .22) 0 2.5px, transparent 2.5px 9px);
  --pat-size: 100% 100%;
}
/* p4–p9: wzory z dwóch warstw i z gradientów kątowych/promienistych.
   Wszystkie bez background-position — pozycję dzielą z warstwą gradientu pod
   spodem, więc przesunięcie któregokolwiek kafla rozjechałoby tło. */
.rev__ava--p4 { /* kratka */
  --pat: repeating-linear-gradient(0deg, rgba(255, 255, 255, .17) 0 1px, transparent 1px 8px),
         repeating-linear-gradient(90deg, rgba(255, 255, 255, .17) 0 1px, transparent 1px 8px);
  --pat-size: 100% 100%, 100% 100%;
}
.rev__ava--p5 { /* skośna krata */
  --pat: repeating-linear-gradient(45deg, rgba(255, 255, 255, .16) 0 1.5px, transparent 1.5px 10px),
         repeating-linear-gradient(-45deg, rgba(255, 255, 255, .16) 0 1.5px, transparent 1.5px 10px);
  --pat-size: 100% 100%, 100% 100%;
}
.rev__ava--p6 { /* koncentryczne kręgi */
  --pat: repeating-radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .26) 0 1.2px, transparent 1.2px 6px);
  --pat-size: 100% 100%;
}
.rev__ava--p7 { /* promienie */
  --pat: repeating-conic-gradient(from 0deg at 50% 50%, rgba(255, 255, 255, .13) 0 6deg, transparent 6deg 20deg);
  --pat-size: 100% 100%;
}
.rev__ava--p8 { /* szachownica */
  --pat: conic-gradient(rgba(255, 255, 255, .15) 0 25%, transparent 0 50%, rgba(255, 255, 255, .15) 0 75%, transparent 0);
  --pat-size: 11px 11px;
}
.rev__ava--p9 { /* konfetti — dwie siatki kropek o różnej skali */
  --pat: radial-gradient(rgba(255, 255, 255, .26) 1.5px, transparent 1.6px),
         radial-gradient(rgba(255, 255, 255, .15) .9px, transparent 1px);
  --pat-size: 13px 13px, 7px 7px;
}
/* Kroje pisma — ta sama litera, pięć różnych charakterów.
   Żadnego letter-spacing: przy jednym znaku dokłada tylko światło z prawej,
   czyli spycha literę w lewo.
   Wartości --ox/--oy nie są „na oko": to średnia z pomiaru ink boxa (canvas
   measureText, actualBoundingBox*) po polskich wersalikach — o tyle środek
   rysunku litery mija się w danym kroju ze środkiem pudełka wiersza. */
.rev__ava--f1 { font-family: var(--serif);  font-weight: 600; font-size: 1.2rem; }
.rev__ava--f2 { font-family: var(--serif);  font-weight: 500; font-size: 1.25rem; font-style: italic; --oy: .017em; --ox: -.014em; }
.rev__ava--f3 { font-family: var(--sans);   font-weight: 500; font-size: .95rem;  --oy: .031em; --ox: .009em; }
.rev__ava--f4 { font-family: var(--script); font-weight: 400; font-size: 1.5rem;  --oy: .096em; --ox: -.109em; }
.rev__ava--f5 { font-family: var(--sans);   font-weight: 300; font-size: 1.1rem;  --oy: .021em; }
.rev__meta { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.rev__name { font-weight: 500; font-size: 1rem; color: var(--ink); }
.rev__stars { color: var(--copper); font-size: .82rem; letter-spacing: .12em; }
.rev__src {
  margin-left: auto;
  width: 26px; height: 26px;
  border-radius: 7px;
  object-fit: contain;
  flex: none;
}
.rev__src--booksy { border-radius: 7px; }
/* Logo Google to sam glif (bez kafelka) — dokładamy taki sam kafelek jak ma Booksy */
.rev__src--google {
  box-sizing: border-box;
  padding: 3px;
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: 7px;
}
.rev__text {
  font-size: .95rem;
  color: var(--ink-soft);
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.rev__foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .8rem;
}
.rev__svc {
  font-size: .74rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--copper-deep);
}
/* Wykonawca opinii — ta sama kropka i kolor co w legendzie cennika */
.rev__who {
  display: inline-flex;
  align-items: center;
  gap: .38rem;
  flex: none;
  font-size: .74rem;
  letter-spacing: .04em;
  color: var(--ink-faint);
  white-space: nowrap;
}

.reviews__logos {
  display: flex;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 3rem;
  padding: 0 var(--pad);
}
.reviews__logo {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  padding: .7em 1.3em;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--ivory);
  font-size: .85rem;
  letter-spacing: .03em;
  transition: transform .4s var(--ease), box-shadow .4s var(--ease), border-color .4s var(--ease);
}
.reviews__logo img { width: 22px; height: 22px; border-radius: 5px; object-fit: contain; }
.reviews__logo:hover { transform: translateY(-3px); box-shadow: var(--shadow-sm); border-color: var(--copper); }

/* ============================================================
   CONTACT
   ============================================================ */
.contact {
  position: relative;
  background: var(--ink);
  color: var(--ivory);
  padding: clamp(5rem, 10vw, 9rem) var(--pad);
  overflow: hidden;
}
.contact__grain {
  position: absolute; inset: 0;
  background:
    radial-gradient(60% 60% at 85% 15%, rgba(95,125,59,.28), transparent 60%),
    radial-gradient(50% 50% at 10% 90%, rgba(63,90,38,.24), transparent 60%);
  pointer-events: none;
}
.contact__inner {
  position: relative;
  max-width: var(--maxw);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
}
.contact .eyebrow { color: var(--copper-lite); }
.contact .eyebrow::before { background: var(--copper-lite); }
.contact .section-title { color: var(--ivory); }
.contact .section-title em { color: var(--copper-lite); }
.contact__copy > p { color: rgba(253,250,246,.72); margin-top: 1.2rem; max-width: 44ch; }

.contact__list { list-style: none; margin: 2.4rem 0; display: flex; flex-direction: column; gap: 1.4rem; }
.contact__list li { display: flex; align-items: center; gap: 1.1rem; }
.contact__ic {
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 1px solid rgba(253,250,246,.22);
  display: grid; place-items: center;
  font-size: 1.2rem;
  color: var(--copper-lite);
  flex: none;
}
.contact__list small { display: block; font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: rgba(253,250,246,.5); }
.contact__list a { font-size: 1.15rem; color: var(--ivory); transition: color .3s; }
.contact__list a:hover { color: var(--copper-lite); }

.contact__hours {
  margin: 0 0 2.2rem;
  padding: 1.5rem 1.7rem;
  border: 1px solid rgba(253,250,246,.16);
  border-radius: 16px;
  background: rgba(253,250,246,.04);
  max-width: 420px;
}
.contact__hours-head { display: flex; align-items: center; gap: 1.1rem; margin-bottom: 1rem; }
.contact__hours-head .contact__ic { width: 40px; height: 40px; font-size: 1.05rem; }
.contact__hours-head small { font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: rgba(253,250,246,.55); }
.contact__hours ul { list-style: none; display: flex; flex-direction: column; }
.contact__hours li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: .5rem 0;
  font-size: .95rem;
  border-top: 1px solid rgba(253,250,246,.08);
}
.contact__hours li:first-child { border-top: 0; }
.contact__hours li span { color: rgba(253,250,246,.72); }
.contact__hours li b { color: var(--ivory); font-weight: 500; letter-spacing: .02em; }
.contact__hours li em { color: var(--copper-lite); font-style: normal; font-size: .85rem; letter-spacing: .04em; }

.contact__book { margin-bottom: 2rem; }

.contact__social { display: flex; gap: .8rem; }
.contact__social a {
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 1px solid rgba(253,250,246,.22);
  display: grid; place-items: center;
  color: var(--ivory);
  transition: .4s var(--ease);
}
.contact__social a:hover {
  background: var(--copper);
  border-color: var(--copper);
  transform: translateY(-3px);
}

.contact__map {
  border-radius: 20px;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  border: 6px solid rgba(253,250,246,.08);
  aspect-ratio: 4 / 3.4;
  min-height: 320px;
  /* The iframe's width="600" attribute sets its intrinsic width; without this
     the grid column refuses to shrink below 600px and overflows on mobile. */
  min-width: 0;
  background:
    repeating-linear-gradient(45deg, rgba(253,250,246,.04) 0 12px, rgba(253,250,246,.02) 12px 24px),
    var(--copper-deep);
  display: grid;
}
.contact__map iframe {
  grid-area: 1 / 1;
  position: relative;
  z-index: 1;
  width: 100%; max-width: 100%; min-width: 0; height: 100%; min-height: 320px; border: 0;
  filter: grayscale(.2) contrast(1.02);
}
.contact__map-fallback {
  grid-area: 1 / 1;
  z-index: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  text-align: center;
  padding: 2rem;
  color: var(--ivory);
}
.contact__map-fallback .contact__map-pin { font-size: 2rem; color: var(--copper-lite); }
.contact__map-fallback strong { font-family: var(--serif); font-size: 1.5rem; font-weight: 600; }
.contact__map-fallback span { font-size: .95rem; color: rgba(253,250,246,.8); }
.contact__map-fallback .contact__map-open {
  margin-top: .6rem;
  font-size: .82rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--copper-lite);
}

/* Tap-to-activate overlay — only on touch devices, where an interactive map
   would otherwise trap the page-scroll gesture. On mouse/pointer devices the
   map stays fully interactive and this button never shows. */
.contact__map-activate { display: none; }
@media (hover: none) and (pointer: coarse) {
  .contact__map iframe { pointer-events: none; }
  .contact__map.is-active iframe { pointer-events: auto; }
  .contact__map-activate {
    grid-area: 1 / 1;
    z-index: 2;
    align-self: end;
    justify-self: center;
    margin-bottom: 1rem;
    display: inline-flex;
    align-items: center;
    gap: .5em;
    padding: .7em 1.3em;
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    font-family: var(--sans);
    font-size: .8rem;
    letter-spacing: .04em;
    color: #fff;
    background: rgba(27,30,21, .82);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    box-shadow: var(--shadow-sm);
  }
  .contact__map-activate span { color: var(--copper-lite); font-size: 1.1em; }
  .contact__map.is-active .contact__map-activate { display: none; }
}
/* Bez zgody na cookies przycisk ma `hidden` (cookie-consent.js), a powyższe
   `display: inline-flex` nadpisałoby domyślne `[hidden] { display: none }`. */
.contact__map-activate[hidden] { display: none; }

/* ============================================================
   SOCIAL — parallax band above the footer
   ============================================================ */
.social {
  padding: clamp(4rem, 8vw, 7rem) 0 clamp(5rem, 9vw, 8rem);
}
.social__head {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  margin-bottom: 2.8rem;
}
.social__intro { max-width: 52ch; color: var(--ink-soft); }

.social__content {
  position: relative;
  max-width: 720px;
  margin: 0 auto;
  padding: clamp(3rem, 7vw, 5.5rem) var(--pad);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(2.4rem, 5vw, 3.4rem);
  isolation: isolate;
}

/* ---- Żywe tło (nie blokuje klików) ---- */
.social__decor {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  border-radius: 32px;
}

/* dryfujące plamy światła (aurora) */
.social__blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(48px);
  opacity: .55;
  will-change: transform;
}
.social__blob--1 {
  width: 340px; height: 340px;
  top: -6%; left: 8%;
  background: radial-gradient(circle, rgba(147, 176, 106, .8), transparent 68%);
  animation: socialDrift1 18s ease-in-out infinite;
}
.social__blob--2 {
  width: 300px; height: 300px;
  bottom: -8%; right: 6%;
  background: radial-gradient(circle, rgba(95, 125, 59, .55), transparent 68%);
  animation: socialDrift2 22s ease-in-out infinite;
}
.social__blob--3 {
  width: 260px; height: 260px;
  top: 30%; left: 50%;
  background: radial-gradient(circle, rgba(191, 210, 158, .6), transparent 68%);
  animation: socialDrift3 26s ease-in-out infinite;
}

@keyframes socialDrift1 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50%      { transform: translate(60px, 40px) scale(1.18); }
}
@keyframes socialDrift2 {
  0%, 100% { transform: translate(0, 0) scale(1.1); }
  50%      { transform: translate(-50px, -36px) scale(.9); }
}
@keyframes socialDrift3 {
  0%, 100% { transform: translate(-50%, 0) scale(1); }
  50%      { transform: translate(-40%, -50px) scale(1.15); }
}

/* unoszące się listki */
.social__leaf {
  position: absolute;
  color: rgba(95, 125, 59, .22);
  width: 34px;
  bottom: -60px;
  will-change: transform, opacity;
  animation: socialRise linear infinite;
}
.social__leaf--1 { left: 14%; width: 30px; animation-duration: 15s; animation-delay: 0s; }
.social__leaf--2 { left: 38%; width: 22px; animation-duration: 19s; animation-delay: 4s; }
.social__leaf--3 { left: 66%; width: 38px; animation-duration: 17s; animation-delay: 8s; }
.social__leaf--4 { left: 84%; width: 26px; animation-duration: 21s; animation-delay: 2s; }

@keyframes socialRise {
  0%   { transform: translateY(0) rotate(0deg); opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translateY(-460px) rotate(220deg); opacity: 0; }
}

/* ---- Logo na scenie z obracającą się pieczęcią tekstową ---- */
.social__stage {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(250px, 44vw, 320px);
  height: clamp(250px, 44vw, 320px);
}

/* miękka poświata pod logo — daje głębię i „wypycha" logo do przodu */
.social__halo {
  position: absolute;
  inset: 18%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(147, 176, 106, .5) 0%, rgba(147, 176, 106, .12) 55%, transparent 72%);
  filter: blur(6px);
  animation: socialPulse 6s ease-in-out infinite;
}
@keyframes socialPulse {
  0%, 100% { transform: scale(1);    opacity: .9; }
  50%      { transform: scale(1.08); opacity: 1; }
}

/* okrągły napis wirujący wokół logo.
   Obracamy zawartość WEWNĄTRZ svg, a nie sam element — obrót elementu
   powiększałby jego prostokąt otaczający (kwadrat × √2) i rozpychał stronę
   w poziomie na wąskich ekranach. */
.social__seal {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  color: var(--copper);
}
.social__seal-rot {
  transform-origin: 150px 150px; /* środek viewBox 300×300 */
  animation: socialSpin 38s linear infinite;
}
.social__seal text {
  fill: currentColor;
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}

@keyframes socialSpin { to { transform: rotate(360deg); } }

.social__mark {
  position: relative;
  width: clamp(150px, 26vw, 200px);
  height: clamp(150px, 26vw, 200px);
  display: grid;
  place-items: center;
}
.social__mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 14px 30px rgba(27, 30, 21, .14));
}

/* ---- Platformy w subtelnych kółkach ---- */
.social__platforms {
  display: flex;
  gap: clamp(1.2rem, 4vw, 2.6rem);
  justify-content: center;
  flex-wrap: wrap;
}

.social__platform {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .85rem;
  color: var(--copper);
  text-decoration: none;
  font-size: .88rem;
  font-weight: 600;
  letter-spacing: .04em;
}

.social__ic {
  display: grid;
  place-items: center;
  width: clamp(60px, 12vw, 76px);
  height: clamp(60px, 12vw, 76px);
  border-radius: 50%;
  background: rgba(252, 253, 248, .7);
  border: 1px solid rgba(95, 125, 59, .22);
  box-shadow: 0 6px 20px -10px rgba(27, 30, 21, .3);
  backdrop-filter: blur(4px);
  transition: transform .4s var(--ease), background .4s var(--ease), border-color .4s var(--ease), box-shadow .4s var(--ease);
}
.social__ic svg {
  width: 46%;
  height: 46%;
  color: var(--copper);
  transition: color .4s var(--ease);
}

.social__platform:hover .social__ic {
  transform: translateY(-6px) scale(1.06);
  background: var(--copper);
  border-color: var(--copper);
  box-shadow: 0 16px 30px -12px rgba(63, 90, 38, .6);
}
.social__platform:hover .social__ic svg { color: #fff; }
.social__platform:hover { color: var(--copper-deep); }

@media (prefers-reduced-motion: reduce) {
  .social__blob, .social__leaf, .social__seal-rot, .social__halo {
    animation: none;
  }
  .social__leaf { opacity: .22; }
}

/* ============================================================
   FOOTER
   ============================================================ */
.footer {
  background: var(--ivory);
  padding: 3.5rem var(--pad) 2rem;
}
.footer__top {
  max-width: var(--maxw);
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 2rem;
  flex-wrap: wrap;
  padding-bottom: 2.4rem;
  border-bottom: 1px solid var(--line);
}
.footer__brand { display: flex; align-items: center; gap: 1rem; max-width: 320px; }
.footer__brand img { height: 66px; mix-blend-mode: multiply; }
.footer__brand p { font-family: var(--serif); font-style: italic; font-size: 1.1rem; color: var(--ink-soft); }
.footer__nav { display: flex; gap: 1.6rem; flex-wrap: wrap; }
.footer__nav a { font-size: .82rem; letter-spacing: .1em; text-transform: uppercase; transition: color .3s; }
.footer__nav a:hover { color: var(--copper-deep); }
.footer__social { display: flex; flex-wrap: wrap; gap: 1.2rem; }
.footer__social a { font-size: .82rem; letter-spacing: .06em; color: var(--ink-soft); transition: color .3s; }
.footer__social a:hover { color: var(--copper-deep); }
.footer__bottom {
  max-width: var(--maxw);
  margin: 1.6rem auto 0;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .8rem;
  font-size: .78rem;
  letter-spacing: .04em;
  color: var(--ink-faint);
}
.footer__bottom i { width: 12px; height: 12px; vertical-align: -1px; }

.footer__credit {
  max-width: var(--maxw);
  margin: 1rem auto 0;
  padding-top: 1rem;
  border-top: 1px solid var(--line-soft);
  text-align: center;
  font-size: .74rem;
  letter-spacing: .05em;
  color: var(--ink-faint);
}
.footer__credit a {
  color: var(--copper);
  font-weight: 500;
  transition: color .3s var(--ease);
}
.footer__credit a:hover { color: var(--copper-deep); }

/* ============================================================
   LIGHTBOX
   ============================================================ */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(20,23,15, .92);
  backdrop-filter: blur(6px);
  display: none;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity .35s var(--ease);
}
.lightbox.open { display: flex; opacity: 1; }
.lightbox img {
  max-width: 88vw;
  max-height: 84vh;
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  animation: pop .4s var(--ease);
}
@keyframes pop { from { transform: scale(.94); opacity: 0; } }
/* Kółka sterujące stoją na ciemnym tle lightboxa — mleczno-kremowa zieleń
   (--cream, ten sam kolor co tło strony) czyta się na nim znacznie lepiej niż
   półprzezroczysta biel, którą gubiły jasne zdjęcia. */
.lightbox__close, .lightbox__nav {
  position: absolute;
  background: var(--cream);
  border: 1px solid rgba(27,30,21,.10);
  color: var(--ink);
  cursor: pointer;
  padding: 0;
  display: grid; place-items: center;
  line-height: 1;
  box-shadow: 0 10px 30px -12px rgba(10,14,6,.7);
  transition: .3s var(--ease);
}
.lightbox__close {
  top: 5vh; right: 5vw;
  width: 48px; height: 48px;
  border-radius: 50%;
  font-size: 1.6rem;
}
.lightbox__nav {
  top: 50%; transform: translateY(-50%);
  width: 54px; height: 54px;
  border-radius: 50%;
}
/* Strzałki to SVG, nie glify ‹ ›: znaki typograficzne mają własne sidebearingi
   i metryki linii (Jost ich nie ma, więc leciał fallback systemowy), przez co
   wisiały odsunięte od środka kółka. Ścieżka jest wyśrodkowana w viewBoxie. */
.lightbox__nav svg {
  display: block;
  width: 42%; height: 42%;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.lightbox__nav--prev { left: 3vw; }
.lightbox__nav--next { right: 3vw; }
.lightbox__close:hover, .lightbox__nav:hover {
  background: var(--copper);
  border-color: var(--copper);
  color: var(--cream);
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1080px) {
  .hero__inner { gap: 2rem; }
}
@media (max-width: 900px) {
  .nav__links, .nav__cta { display: none; }
  .nav__burger { display: flex; margin-left: auto; } /* push burger to the right */

  /* Full-screen mobile menu.
     Wszystkie pozycje MUSZĄ się zmieścić bez przewijania także na krótkim ekranie
     (iPhone SE to ~568 px wysokości, a z paskami przeglądarki zostaje ~460 px),
     dlatego typografia i odstępy liczą się od WYSOKOŚCI viewportu (vh), nie od rem.
     Przy 6 pozycjach + CTA daje to ok. 7 × (4,4vh + 2,2vh) ≈ 46vh treści — mieści się
     z zapasem, a na dużym telefonie clamp i tak podbija wszystko do dawnych wartości. */
  .nav__links.open {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    /* NIE justify-content:center ani space-evenly — wyśrodkowany flex, który się
       przepełni, ucina treść u GÓRY i nie da się do niej doscrollować. Rozkładem
       zajmuje się flex-grow na samych pozycjach (niżej): wolną przestrzeń dzieli
       po równo, a gdy jej zabraknie, po prostu nie ma czego dzielić — zamiast
       chować pierwszy link poza kadrem. */
    justify-content: flex-start;
    gap: 0;
    position: fixed;
    inset: 0;
    min-height: 100dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 65;
    background: rgba(253, 250, 246, .98);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    padding: clamp(3.8rem, 10vh, 5.5rem) var(--pad) clamp(1rem, 3.5vh, 2.4rem);
    box-shadow: none;
    animation: menuIn .35s var(--ease);
  }
  /* flex: 1 1 0 → każda pozycja bierze równą część wolnej wysokości, więc menu
     zawsze wypełnia ekran, a nie zbija się w kłębek na środku dużego tabletu.
     Cały wiersz jest klikalny (display:flex + stretch), co przy okazji daje pola
     dotyku grubo powyżej wymaganych 44 px. min-height pilnuje dolnej granicy przy
     ściskaniu — poniżej niej flex-shrink nie zejdzie. */
  .nav__links.open > a,
  .nav__links.open .navprod__trigger {
    flex: 1 1 0;
    min-height: 1.9em;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--serif);
    font-size: clamp(1.25rem, 3.6vh, 2.4rem);
    line-height: 1.1;
    letter-spacing: .02em;
    text-transform: none;
    font-weight: 500;
  }
  .nav__links.open > a::after,
  .nav__links.open .navprod__trigger::after { display: none; }

  /* Highlighted "Rezerwuj" → Booksy inside the full-screen menu */
  /* CTA zostaje przy swoim rozmiarze (flex:none) i siada na dole, pod rozciągniętą
     listą — rozciągnięty razem z linkami zrobiłby się absurdalnie wysoką pastylką. */
  .nav__links.open .nav__mobile-cta {
    display: inline-flex;
    flex: none;
    align-self: center;
    justify-content: center;
    align-items: center;
    gap: .5em;
    margin-top: clamp(.6rem, 2.4vh, 1.6rem);
    padding: clamp(.75em, 1.7vh, 1.05em) 2.6em;
    border-radius: 999px;
    /* Ta pastylka to ten sam przycisk co .btn--solid, tylko poza .btn — musi
       jechać z tokenów przycisków, inaczej zostaje na starej oliwce. */
    background: linear-gradient(120deg, var(--btn-1), var(--btn-2));
    color: var(--btn-fg);
    font-family: var(--sans);
    font-size: clamp(.82rem, 1.9vh, 1rem);
    font-weight: 500;
    letter-spacing: .06em;
    text-transform: uppercase;
    box-shadow: 0 18px 38px -14px var(--btn-glow);
  }
  /* Ekran położony NA BOK: kolumna rozbija się na siatkę, bo w 360 px wysokości
     siedem pozycji pod sobą nie ma szans. Warunek min-width jest tu konieczny —
     bez niego regułę łapał też wąski telefon w pionie (320 × 460), gdzie kolumna
     mieści się bez problemu, a siatka wyglądała na ściśniętą. */
  @media (max-height: 460px) and (min-width: 600px) {
    .nav__links.open {
      flex-flow: row wrap;
      justify-content: center;
      align-content: center;
      column-gap: 2.2rem;
      padding-top: 4rem;
    }
    /* W siatce rozciąganie na wysokość nie ma sensu — pozycje mają zająć tyle,
       ile potrzebują, a układać się w wiersze. */
    .nav__links.open > a,
    .nav__links.open .navprod__trigger { flex: 0 1 auto; min-height: 0; font-size: 1.15rem; }
    .nav__links.open .nav__mobile-cta { flex-basis: 100%; justify-content: center; margin-top: .6rem; }
  }
  @keyframes menuIn {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  .hero { padding-top: clamp(5.5rem, 10vh, 7rem); }
  .hero__ring { opacity: .5; }
  /* On narrow screens the copy spans full width, so use an even top-to-bottom veil */
  .hero__scrim {
    background: linear-gradient(180deg,
      rgba(253, 250, 246, .88) 0%,
      rgba(253, 250, 246, .62) 42%,
      rgba(253, 250, 246, .5) 70%,
      rgba(253, 250, 246, .74) 100%);
  }

  .about { grid-template-columns: 1fr; }
  /* width: 100% — ramka nie ma już szerokości własnej (kadry są absolute),
     a auto-marginesy w siatce zwinęłyby ją do zera */
  .about__img { width: 100%; max-width: 460px; margin: 0 auto; }
  .services__grid { grid-template-columns: repeat(2, 1fr); }
  .partners__row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .contact__inner { grid-template-columns: 1fr; }
  /* Let the map's HEIGHT derive from the (full-width) column via the aspect
     ratio. Keeping min-height here would instead force the WIDTH to
     height x ratio (320 x 16/11 = 465px) and overflow the phone screen. */
  .contact__map { aspect-ratio: 16/11; min-height: 0; }
  .contact__map iframe { min-height: 0; }
  .wall { height: min(70vh, 560px); }
}
@media (max-width: 560px) {
  .hero__title { font-size: clamp(2.3rem, min(13vw, 8.5vh), 4rem); }
  .hero__stats { gap: 1rem; }
  .hero__stats-div { display: none; }
  .services__grid { grid-template-columns: 1fr; }
  .services__cta { flex-direction: column; text-align: center; padding: 2rem 1.4rem; }
  /* .btn ma white-space: nowrap — na wąskich ekranach ten CTA nie mieści się
     wtedy w kolumnie i rozpycha stronę w poziomie. Tu pozwalamy mu zawinąć. */
  .services__cta .btn { white-space: normal; max-width: 100%; }
  .partners__item { padding: 1.1rem; border-radius: 14px; }
  .partners__item img { height: 68px; }
  .footer__top { flex-direction: column; align-items: flex-start; }
  .lightbox__nav { width: 44px; height: 44px; }
  .wall { height: 62vh; gap: .9rem; }
}
@media (max-width: 480px) {
  /* Poniżej ~430px --pad stoi już na swoim minimum (1.25rem = 20px), a wachlarz
     i zielony obrys wystają ~40px poza ramkę — zwężamy talię, żeby zmieściły się
     w ekranie, a lot górnej karty w lewo przycina krawędź sekcji. */
  .about { overflow-x: clip; }
  .about__img { width: 86%; }
}

/* ============================================================
   PODSTRONA PRODUKTY (produkty.html)
   Katalog marek: nagłówek marki + siatka kart produktowych.
   Korzysta z układu podstron (.pf-page/.pf-top/.pf-main), więc powrót,
   logo i szerokość kolumny są identyczne jak w portfolio i galerii salonu.
   ============================================================ */
.prod-jump {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.prod-jump a {
  font-size: .78rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  padding: .55rem 1.05rem;
  transition: color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.prod-jump a:hover {
  color: var(--copper-deep);
  border-color: var(--copper);
  transform: translateY(-2px);
}

/* ------------------------------------------------------------
   PASEK FILTRÓW (buduje go produkty.js z samego katalogu)
   Zastępuje statyczne `.prod-jump`, gdy JS działa: marki jako pastylki,
   a przy każdej strzałka rozwijająca jej linie. Ten sam zestaw wyborów
   co rozwijane menu „Produkty” w nawigacji strony głównej.
   ------------------------------------------------------------ */
/* Filtr chowa sekcje atrybutem `hidden`, a `display` z klasy wygrywa z regułą
   przeglądarki `[hidden] { display: none }` — dlatego wypisane wprost. */
.prod-brand[hidden],
.prod-grid[hidden],
.prod-story[hidden],
.prod-break[hidden],
.prod-list[hidden],
.prod-shelf[hidden],
.prod-line[hidden],
.prod-filter__status[hidden] { display: none; }

/* Wejście z adresu z filtrem: katalog czeka schowany, aż produkty.js odsieje
   sekcje (bezpiecznik z timeoutem siedzi w <head> produkty.html). */
html.prod-filter-pending .prod-brand,
html.prod-filter-pending .prod-break { display: none; }

.prod-filter { margin-bottom: clamp(2.2rem, 4.5vw, 3.4rem); }
.prod-filter__row { display: flex; flex-wrap: wrap; gap: .6rem; }
.prod-filter__chip {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .55rem 1.05rem;
  font-size: .78rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  transition: color .3s var(--ease), border-color .3s var(--ease),
              background .3s var(--ease), transform .3s var(--ease);
}
.prod-filter__chip:hover { color: var(--copper-deep); border-color: var(--copper); transform: translateY(-2px); }
.prod-filter__chip.is-active {
  color: var(--btn-fg);
  background: linear-gradient(120deg, var(--btn-1), var(--btn-2));
  border-color: transparent;
  box-shadow: 0 12px 26px -14px var(--btn-glow);
}
.prod-filter__chip.is-active:hover { color: var(--btn-fg); }

/* Marka to jedna pastylka złożona z linku (cała marka) i strzałki (jej linie). */
.prod-filter__brand { display: inline-flex; align-items: stretch; }
.prod-filter__brand > .prod-filter__chip {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  border-right: 0;
}
.prod-filter__more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  padding: 0;
  background: #fff;
  color: var(--ink-faint);
  border: 1px solid var(--line-soft);
  border-radius: 0 999px 999px 0;
  cursor: pointer;
  transition: color .3s var(--ease), border-color .3s var(--ease), background .3s var(--ease);
}
.prod-filter__more:hover { color: var(--copper-deep); border-color: var(--copper); }
.prod-filter__more svg {
  width: 11px; height: 8px;
  fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  transition: transform .35s var(--ease);
}
.prod-filter__more[aria-expanded="true"] { color: var(--copper-deep); border-color: var(--copper); }
.prod-filter__more[aria-expanded="true"] svg { transform: rotate(180deg); }

.prod-filter__lines {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .38s var(--ease);
}
.prod-filter__lines.is-open { grid-template-rows: 1fr; }
.prod-filter__lines-inner {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  min-height: 0;
  overflow: hidden;   /* bez tego 0fr nie zwija zawartości */
  padding: .75rem .2rem 0;
}
.prod-filter__chip--line {
  padding: .45rem .9rem;
  font-size: .74rem;
  letter-spacing: .02em;
  text-transform: none;
  background: var(--cream);
}
.prod-filter__chip--whole {
  padding: .45rem .9rem;
  font-size: .7rem;
  color: var(--copper-deep);
  background: none;
  border-style: dashed;
}

.prod-filter__status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem .9rem;
  margin-top: 1.1rem;
  font-size: .85rem;
  color: var(--ink-soft);
}
.prod-filter__status strong {
  font-family: var(--serif);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--ink);
}
.prod-filter__clear {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding-bottom: 2px;
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--copper-deep);
  border-bottom: 1px solid var(--copper);
}
.prod-filter__clear:hover { color: var(--ink); border-color: var(--ink); }

@media (max-width: 560px) {
  .prod-filter__chip { padding: .5rem .85rem; font-size: .72rem; }
  .prod-filter__more { width: 38px; }
  .prod-filter__chip--line, .prod-filter__chip--whole { font-size: .7rem; }
}


.prod-brand { margin-bottom: clamp(3.5rem, 7vw, 6rem); }
/* Kotwica z .prod-jump nie może chować nagłówka pod górnym paskiem podstrony. */
.prod-brand { scroll-margin-top: 90px; }

.prod-brand__head {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  padding-bottom: 1.4rem;
  margin-bottom: 1.8rem;
  border-bottom: 1px solid var(--line-soft);
}
/* Ta sama zasada co przy logotypach na stronie głównej: kwadratowa płytka
   i obrazek o konkretnych wymiarach — logotypy mają proporcje od 1:1 do 1.66:1
   i procentowy max-height nie miałby się do czego odnieść. */
.prod-brand__logo {
  flex: 0 0 auto;
  width: 92px;
  height: 92px;
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  padding: .7rem;
}
.prod-brand__logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.prod-brand__txt h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.7rem, 3.4vw, 2.5rem);
  line-height: 1.1;
  color: var(--ink);
  margin-bottom: .5rem;
}
.prod-brand__txt p { color: var(--ink-soft); max-width: 62ch; font-size: .95rem; }

/* ---------- Karty linii: jeden rytm ----------
   Pierwsza wersja rozsuwała karty na siatce 12-kolumnowej (5+4+3, potem
   lustrzanie) i zsuwała co drugą w dół o `--up`. Na moodboardzie to editorial,
   nad katalogiem z 250 pozycjami czyta się jak rozjechany rząd — zwłaszcza że
   kadry przychodzą z CDN-u w różnych proporcjach, więc żadne dwie karty nie
   kończyły się na tej samej wysokości.

   Teraz jest JEDEN rytm: trzy równe karty, ten sam kadr 1:1, ta sama linia
   bazowa opisu, „Zobacz linię” wyrównane w całym rzędzie. Charakter niosą
   typografia i numer z kreską, nie przesunięcia.

   Karta jest LINKIEM do swojej linii (`?linia=…`). */
.prod-story {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.7rem, 3.2vw, 2.8rem) clamp(1.2rem, 2.4vw, 2rem);
  align-items: stretch;
}
/* Komórka i karta rozciągają się na całą wysokość rzędu — dopiero to pozwala
   przykleić „Zobacz linię” do dołu i wyrównać je między kartami. */
.prod-story__cell { display: flex; }
.prod-story__card {
  display: flex;
  flex-direction: column;
  width: 100%;
  color: inherit;
  text-decoration: none;
}
.prod-story__frame {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 20px;
  background: var(--sand);
  /* Jedne proporcje dla WSZYSTKICH kart. Wcześniej kadr brał wymiary z pliku
     i każdy rząd miał inną wysokość zdjęcia. */
  aspect-ratio: 1 / 1;
}
.prod-story__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .9s var(--ease);
}
/* Przyciemnienie dołu kadru: zdjęcia z banku różnią się jasnością, a plakietka
   z logo linii musi mieć na czym siedzieć niezależnie od tego, co na nich jest. */
.prod-story__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(24, 30, 16, .28) 0%, rgba(24, 30, 16, 0) 48%);
  pointer-events: none;
}
.prod-story__card:hover .prod-story__frame img,
.prod-story__card:focus-visible .prod-story__frame img { transform: scale(1.05); }

/* ---------- Ćwiartka koła z logotypem linii ----------
   Wpięta w lewy górny róg kadru: kwadrat z zaokrąglonym TYLKO prawym dolnym
   narożnikiem daje wycinek koła, którego proste krawędzie leżą na krawędziach
   kadru. Średnica jest procentowa, więc skaluje się razem z kadrem.
   UWAGA: reguły muszą stać PO `.prod-story__frame img` i mieć wyższą
   specyficzność — inaczej logo łapie zoom z hovera. Karty bez logo podmarki
   (ghd, Lavish Care) po prostu plakietki nie mają. */
.prod-story__badge {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 32%;
  aspect-ratio: 1;
  border-radius: 0 0 100% 0;
  background: rgba(255, 255, 255, .96);
  box-shadow: 3px 3px 16px -8px rgba(27, 30, 21, .6);
}
.prod-story__frame .prod-story__badge img,
.prod-story__card:hover .prod-story__frame .prod-story__badge img {
  position: absolute;
  left: 15%;
  top: 17%;
  width: 53%;
  height: 40%;
  object-fit: contain;
  transform: none;
}

/* Linie Authentic Beauty Concept nie mają własnych logotypów — zamiast tego
   każda dostaje swój kolor pod wspólnym logo marki (kody od klientki). */
.prod-story__badge--hydrate   { background: #9bb5c4; }
.prod-story__badge--replenish { background: #e8d5b5; }
.prod-story__badge--glow      { background: #e0b3b2; }
.prod-story__badge--cleansing { background: #9aa29b; }
.prod-story__badge--amplify   { background: #b8ceb9; }
.prod-story__badge--oils      { background: #c4b29e; }

.prod-story__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 1.05rem .15rem 0;
}
/* Numer + kreska ciągnąca się do prawej krawędzi karty: porządkuje czytanie
   sekcji i wyznacza wspólną linię startu tekstu we wszystkich kartach. */
.prod-story__nr {
  display: flex;
  align-items: center;
  gap: .7rem;
  font-family: var(--sans);
  font-size: .66rem;
  letter-spacing: .18em;
  color: var(--ink-faint);
  margin-bottom: .55rem;
}
.prod-story__nr::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}
.prod-story__body h3 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.3rem, 2.2vw, 1.7rem);
  line-height: 1.15;
  color: var(--ink);
  transition: color .4s var(--ease);
}
.prod-story__card:hover .prod-story__body h3 { color: var(--copper-deep); }
.prod-story__tag {
  font-size: .66rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--copper-deep);
  margin: .4rem 0 .65rem;
}
.prod-story__desc { color: var(--ink-soft); font-size: .87rem; line-height: 1.65; }
.prod-story__go {
  align-self: flex-start;
  margin-top: auto;
  padding-top: 1rem;
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-size: .72rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--copper-deep);
  /* Podkreślnik rysowany gradientem, nie `text-decoration` — da się go
     animować od zera do pełnej szerokości bez skoku wysokości linii. */
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 1px;
  transition: background-size .45s var(--ease);
}
.prod-story__go svg {
  width: 14px;
  height: 10px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform .45s var(--ease);
}
.prod-story__card:hover .prod-story__go,
.prod-story__card:focus-visible .prod-story__go { background-size: calc(100% - 20px) 1px; }
.prod-story__card:hover .prod-story__go svg { transform: translateX(4px); }
.prod-story__card:focus-visible { outline: 2px solid var(--copper-deep); outline-offset: 6px; border-radius: 22px; }

/* ---------- Pełne listy produktów ----------
   Karty wyżej mówią „po co to jest”, ta warstwa mówi „co konkretnie stoi na
   półce”. Bez zdjęć: nazwa + pojemności, pogrupowane po liniach, żeby dało się
   to skanować wzrokiem zamiast czytać. Kolumny same się układają — przy 30
   pozycjach jedna długa lista byłaby nie do przejścia na desktopie. */
.prod-list {
  margin-top: clamp(2.2rem, 4vw, 3.2rem);
  padding-top: clamp(1.5rem, 3vw, 2.2rem);
  border-top: 1px solid var(--line-soft);
}
/* Logotyp linii przed tytułem listy — ta sama płytka co w nagłówku marki, tylko
   mniejsza. Przy jedenastu listach pod rząd sam tekst za słabo je od siebie
   odróżniał; logo pozwala trafić wzrokiem w OSiS+ czy Silhouette bez czytania.
   Linie bez własnego logotypu (Scalp Clinix, Session Label) zostają na samym
   tytule — flex z jednym dzieckiem układa się tak samo. */
.prod-list__head {
  display: flex;
  align-items: center;
  gap: clamp(.85rem, 2vw, 1.2rem);
  margin-bottom: 1.5rem;
}
/* Płytka rośnie w poziomie razem z logotypem: te znaki mają proporcje od 1:1
   (bc) po 2.6:1 (SILHOUETTE), więc w sztywnym kwadracie szerokie wordmarki
   schodziły do kilkunastu pikseli wysokości i stawały się nieczytelne. Stała
   jest WYSOKOŚĆ znaku, szerokość dobiera się sama. */
.prod-list__logo {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 70px;
  min-width: 70px;
  max-width: 184px;
  padding: .55rem .85rem;
  background: #fff;
  border: 1px solid var(--line-soft);
  border-radius: 12px;
}
.prod-list__logo img {
  height: 100%;
  width: auto;
  max-width: 152px;
  object-fit: contain;
  display: block;
}
.prod-list__head h3 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  line-height: 1.2;
  color: var(--ink);
}
.prod-list__head p {
  color: var(--ink-soft);
  font-size: .88rem;
  max-width: 66ch;
  margin-top: .35rem;
}
.prod-list__cols {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
  gap: 1.6rem 2.2rem;
  align-items: start;
}

.prod-line:not(.prod-line--tiles) h4 {
  font-size: .7rem;
  font-weight: 500;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--copper-deep);
  padding-bottom: .5rem;
  margin-bottom: .2rem;
  border-bottom: 1px solid var(--line-soft);
}
.prod-line ul { list-style: none; margin: 0; padding: 0; }
/* `:not(.prod-line--tiles)` jest tu konieczne, nie kosmetyczne: linia
   przerobiona na kafle nadal składa się z `<li>` wewnątrz `.prod-line`,
   a te reguły dokładały jej kreskę pod każdym kaflem i — przez wyższą
   specyficzność niż `.prod-mark span` — zbijały monogram do 11 px. */
.prod-line:not(.prod-line--tiles) li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .8rem;
  padding: .42rem 0;
  font-size: .88rem;
  line-height: 1.45;
  color: var(--ink);
  border-bottom: 1px solid var(--line-soft);
}
.prod-line:not(.prod-line--tiles) li:last-child { border-bottom: 0; }
/* Pojemności trzymamy w jednej kolumnie po prawej — nie zawijają się, bo to
   krótkie etykiety typu „250 ml · 1000 ml”. */
.prod-line:not(.prod-line--tiles) li span {
  flex: 0 0 auto;
  font-size: .72rem;
  letter-spacing: .04em;
  color: var(--ink-faint);
  white-space: nowrap;
}

.prod-list__note {
  margin-top: 1.1rem;
  font-size: .82rem;
  font-style: italic;
  color: var(--ink-faint);
  max-width: 70ch;
}

/* ---------- Regał: nagłówek linii + siatka kafli ----------
   Wersja poprzednia: piaskowe pole, w nim białe zaokrąglone prostokąty,
   pod każdym wyśrodkowany podpis. Czytało się jak lista z panelu, nie jak
   katalog — a do tego kafle z przełącznikiem pojemności miały `padding: 0`
   i ich packshoty wychodziły wyraźnie większe od sąsiadów w tym samym rzędzie.

   Teraz:
     · linia produktowa dostaje nagłówek jak rozdział — nazwa szeryfowa,
       etykieta w pigułce i licznik pozycji na wspólnej kresce,
     · kafel jest jedną kartą (scena + stopka w środku), nie kadrem
       z podpisem doklejonym pod spodem,
     · KAŻDY kafel ma ten sam kadr, ten sam padding i tę samą stopkę —
       `--stage-pad` jest jednym źródłem prawdy, także dla wariantu
       z przełącznikiem, gdzie padding siedzi na zdjęciach.
   „Równo” jest tu ważniejsze niż każdy pojedynczy efekt. */
.prod-shelf { margin-bottom: clamp(2.4rem, 5vw, 3.6rem); }
/* Gdy WSZYSTKIE linie marki mają już zdjęcia, `.prod-list__cols` znika i lista
   kończy się półką — jej dolny margines dokładałby się wtedy do odstępu przed
   następną marką. */
.prod-shelf:last-child { margin-bottom: 0; }

.prod-shelf__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem 1.4rem;
  flex-wrap: wrap;
  padding-bottom: .8rem;
  margin-bottom: clamp(1.1rem, 2.2vw, 1.5rem);
  border-bottom: 1px solid var(--line);
}
.prod-shelf__title {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  flex-wrap: wrap;
  font-weight: 400;
  min-width: 0;
}
.prod-shelf__name {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.15rem, 2.1vw, 1.5rem);
  line-height: 1.1;
  letter-spacing: -.005em;
  color: var(--ink);
}
/* Druga połowa tytułu („— nawilżenie”, „— odbudowa”) to nie nazwa, tylko
   zadanie linii. W pigułce da się ją przeskanować wzrokiem przez całą stronę. */
.prod-shelf__tag {
  font-size: .63rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--copper-deep);
  padding: .3rem .62rem .26rem;
  border: 1px solid var(--copper-lite);
  border-radius: 999px;
  white-space: nowrap;
}
.prod-shelf__count {
  flex: none;
  font-size: .66rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.prod-shelf__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* `auto-fill`, nie `auto-fit`: przy `auto-fit` linia z dwoma produktami
     rozciągała kafle na pół strony i ten sam szampon wyglądał inaczej na
     każdej półce. `auto-fill` trzyma tory na stałe, więc kafel ma tę samą
     szerokość w CAŁYM katalogu, a krótsza linia po prostu nie domyka rzędu. */
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
  gap: clamp(.7rem, 1.5vw, 1.1rem);
}

/* ---------- Kafel = jedna karta ----------
   Kadr wewnątrz karty MUSI zostać `<button>`: klik otwiera panel z opisem,
   więc potrzebna jest klawiatura, czytnik ekranu i widoczny fokus bez
   dorabiania `role` i `tabindex`. Wygląd hovera siedzi jednak na całej
   karcie (`.prod-item`), bo kliknięcie w nazwę też otwiera panel — patrz
   delegacja zdarzeń w produkty.js. */
.prod-item {
  position: relative;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  background: var(--ivory);
  border: 1px solid var(--line-soft);
  border-radius: 18px;
  overflow: hidden;
  transition: transform .5s var(--ease), box-shadow .5s var(--ease), border-color .5s var(--ease);
}
.prod-item:hover {
  transform: translateY(-5px);
  border-color: var(--copper-lite);
  box-shadow: var(--shadow-sm);
}

/* Scena, na której stoi opakowanie. `contain`, nie `cover` — packshot ma
   pokazać CAŁE opakowanie z etykietą, a kadrowanie ucięłoby nazwę linii
   z butelki. Kadr 4:5 zgadza się z plikami z optimize-product-photos.js. */
.prod-item__img {
  --stage-pad: clamp(.9rem, 2vw, 1.3rem);
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  padding: var(--stage-pad);
  border: 0;
  border-radius: 0;
  cursor: pointer;
  background: linear-gradient(180deg, #fff 0%, #fdfefa 54%, var(--sand) 100%);
  /* Domyka grupę mieszania packshotu do samego kafla. Bez tego `multiply`
     na zdjęciu musi brać pod uwagę wszystko, co leży pod spodem w sekcji —
     przy ~190 kaflach na jednej stronie to niepotrzebna praca przy każdym
     przemalowaniu, a na telefonie widać ją w scrollu. */
  isolation: isolate;
  transition: background .5s var(--ease);
}
.prod-item:hover .prod-item__img {
  background: linear-gradient(180deg, #fff 0%, #f7faf0 46%, var(--blush) 100%);
}
.prod-item__img::after {
  /* Cień „na półce” pod opakowaniem — samo tło nie oddaje tego, że produkt
     na czymś stoi. Elipsa rozmyta, bez ostrych krawędzi. */
  content: "";
  position: absolute;
  left: 26%;
  right: 26%;
  bottom: 8%;
  height: 6%;
  border-radius: 50%;
  background: rgba(40, 55, 26, .18);
  filter: blur(7px);
  opacity: .5;
  transition: opacity .5s var(--ease), transform .5s var(--ease);
  pointer-events: none;
}
.prod-item:hover .prod-item__img::after { opacity: .75; transform: scaleX(1.14); }
.prod-item__img img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Packshoty od producenta mają WYPEŁNIONE białe tło, nie przezroczystość —
     na kremowej scenie rysowały się jako biały prostokąt wokół butelki.
     `multiply` gasi biel do tła (biel × cokolwiek = to cokolwiek), a etykieta
     zostaje nietknięta, bo scena pod nią i tak jest prawie biała. */
  mix-blend-mode: multiply;
  transition: transform .55s var(--ease);
}
.prod-item:hover .prod-item__img img { transform: translateY(-5px) scale(1.045); }
.prod-item__img:focus-visible { outline: 2px solid var(--copper-deep); outline-offset: -3px; }

/* Znaczek w rogu — jedyny sygnał, że kafel się otwiera. Widoczny dopiero
   na hover/fokus, żeby półka 190 kafli nie wyglądała jak panel sterowania.
   Na dotyku hovera nie ma, dlatego pokazujemy go tam na stałe (niżej).

   Świadomie NIE jest to plus w kółku: zielony „+” przy produkcie czyta się
   jak „dodaj do koszyka”, a tu niczego nie sprzedajemy online. Strzałki
   w rogach mówią „powiększ”, czyli dokładnie to, co robi kliknięcie. */
.prod-item__peek {
  position: absolute;
  z-index: 2;
  top: .5rem;
  right: .5rem;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, .94);
  box-shadow: inset 0 0 0 1px var(--line-soft), 0 2px 8px -4px rgba(40, 55, 26, .5);
  color: var(--copper-deep);
  opacity: 0;
  transform: translateY(-5px) scale(.86);
  transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.prod-item__peek svg {
  width: 11px;
  height: 11px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}
.prod-item:hover .prod-item__peek,
.prod-item__img:focus-visible .prod-item__peek { opacity: 1; transform: none; }

/* ---------- Kafel bez packshotu ----------
   Część pozycji ghd i całe Lavish Care nie mają zdjęć od producenta —
   ghd przysłał materiały tylko do bieżącej oferty (Chronos, Sculpt, Chronos
   Curve, Speed, Duet, kosmetyki), starsze modele zostają przy ikonie.
   Zamiast podstawiać im zdjęcie z banku (co sugerowałoby, że TAK wygląda
   opakowanie) dostają
   rysunkową ikonę kategorii na tej samej scenie co packshoty: prostownicę,
   lokówkę, suszarkę, buteleczkę, słoik z pastą. Inicjały (PS, DD, GS…)
   nie mówiły nic — z ikony od razu widać, czym produkt jest.
   Mapa produkt → plik SVG: product-icons.js. */
.prod-item--mark .prod-item__img { background: linear-gradient(180deg, #fff 0%, var(--sand) 100%); }
.prod-item--mark .prod-item__img::after { opacity: 0; }
.prod-item--mark:hover .prod-item__img { background: linear-gradient(180deg, #fff 0%, var(--blush) 100%); }
.prod-mark {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  transition: transform .5s var(--ease);
}
/* Krążek pod monogramem: bez niego inicjały wisiały w pustce i kafel wyglądał
   na niedokończony obok sąsiada z opakowaniem. */
.prod-mark::before {
  content: "";
  position: absolute;
  width: min(64%, 92px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, .82);
  box-shadow: inset 0 0 0 1px var(--line-soft);
}
.prod-mark span {
  position: relative;
  font-family: var(--serif);
  font-size: clamp(1.6rem, 3.6vw, 2.1rem);
  font-weight: 500;
  letter-spacing: .06em;
  line-height: 1;
  color: var(--copper-deep);
  opacity: .8;
}
.prod-item:hover .prod-mark { transform: scale(1.05); }
/* Ikona kategorii w krążku. Selektory są celowo tak długie: `.prod-item__img img`
   rozciąga packshot na całą scenę (100%×100% + multiply) i podnosi go na hover —
   ikona ma zostać rysunkiem w krążku, więc musi te reguły przebić specyficznością,
   a nie `!important`. */
.prod-item--mark .prod-item__img img.prod-mark__icon {
  position: relative;
  z-index: 1;
  /* Rysunki są kadrowane do sylwetki, więc mają różne proporcje — od lokówki
     64×222 po słoik 100×96. Samo `width` puściłoby butelkę na trzy wysokości
     krążka, dlatego ikona dostaje KWADRATOWE pole (`aspect-ratio: 1` przy
     szerokości liczonej z kafla — tak jak średnica krążka) i wpisuje się w nie
     przez `object-fit`. Wysoka wchodzi na wysokość, szeroka na szerokość,
     a wszystkie zajmują ten sam kadr, więc półka nie skacze między kaflami. */
  width: min(50%, 72px);
  height: auto;
  aspect-ratio: 1;
  object-fit: contain;
  mix-blend-mode: normal;
  transform: none;
  transition: none;
}
/* Powiększenie na hover robi krążek (`.prod-mark`), nie sam rysunek — inaczej
   ikona rosłaby dwa razy i wyjeżdżała poza kadr. */
.prod-item--mark:hover .prod-item__img img.prod-mark__icon { transform: none; }

/* ---------- Kafel z kadrem kampanijnym ----------
   Cztery prostownice z edycji SS26 nie mają packshotu w ogóle — producent
   przysłał do nich wyłącznie kampanię. Taki kafel wypełnia się zdjęciem na
   całość, dokładnie jak scena w karcie: kadr na bieli i kadr na różu nie
   dadzą się pogodzić w jednej ramce, więc drugi po prostu zabiera całą.
   Cień „na półce” znika, bo nie ma już opakowania, które by na czymś stało. */
.prod-item--scene .prod-item__img { padding: 0; background: var(--ink); }
.prod-item--scene .prod-item__img::after { opacity: 0; }
.prod-item--scene .prod-item__img img { object-fit: cover; mix-blend-mode: normal; }
/* Samo powiększenie, bez unoszenia: przy kadrze na krycie `translateY`
   odsłaniałby przy dolnej krawędzi pasek tła. */
.prod-item--scene:hover .prod-item__img img { transform: scale(1.06); }

/* ---------- Linia tekstowa przerobiona na kafle ----------
   Ta sama półka co przy packshotach, tylko `.prod-line` siedzi w kolumnie
   `.prod-list__cols`, więc musi z niej wyjść na całą szerokość. */
.prod-line--tiles {
  grid-column: 1 / -1;
  margin-bottom: clamp(2.4rem, 5vw, 3.6rem);
}
.prod-line--tiles:last-child { margin-bottom: 0; }

/* ---------- Stopka kafla ----------
   W środku karty, nie pod nią: nazwa i pojemność należą do produktu, a nie
   do kolumny siatki. Wyrównanie do lewej, nie na środek — wyśrodkowane
   dwuwierszowe nazwy w rzędzie pięciu kafli rwały wspólną linię czytania. */
.prod-item__body {
  /* ŻADNEGO `margin-top: auto`. Karty w rzędzie mają wspólną wysokość (siatka
     rozciąga je domyślnie), a nazwy bywają jedno- i dwuwierszowe. Dosunięcie
     stopki do dołu robiło w niższych kartach białą dziurę między sceną
     a kreską — czyli dokładnie ten „rozjazd”, którego się pozbywamy. Stopka
     siedzi tuż pod sceną, a nadmiar wysokości zostaje pod tekstem, gdzie tło
     karty i tak jest jednolite i nic go nie zdradza. */
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: .75rem .85rem .9rem;
  text-align: left;
  border-top: 1px solid var(--line-soft);
  background: var(--ivory);
}
.prod-item__body h5 {
  font-size: .85rem;
  font-weight: 500;
  line-height: 1.3;
  color: var(--ink);
  transition: color .4s var(--ease);
}
.prod-item:hover .prod-item__body h5 { color: var(--copper-deep); }
/* Pojemność to dana techniczna, nie zdanie — wersaliki z rozstrzeleniem
   odróżniają ją od nazwy bez dokładania koloru czy drugiego kroju. */
.prod-item__body p {
  font-size: .64rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ---------- Przełącznik pojemności, bez JS ----------
   Producent przysyła packshot per pojemność, a te potrafią się różnić FORMĄ,
   nie tylko rozmiarem: Repair Rescue Treatment to tubka w 30 ml i słoik
   w 200 i 500 ml. Sam podpis „30 ml · 200 ml · 500 ml” tego nie odda, a pięć
   osobnych kafli na jedną maskę rozbija półkę.

   Rozwiązanie: jeden kafel = jeden produkt, a pojemności z własnym zdjęciem
   stają się pigułkami przełączającymi kadr. Ukryte `input[type=radio]` leżą
   jako rodzeństwo `__img` i `__body`, więc `:checked ~` sięga i do zdjęcia,
   i do pigułki — całość działa na samym CSS, z klawiaturą i bez JS-a. */

/* Radio musi zostać w drzewie fokusa — stąd clip zamiast display:none. */
.prod-size-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Zdjęcia leżą na sobie w kadrze — przełączenie nie rusza wysokości kafla,
   więc półka nie skacze przy kliknięciu. Padding wchodzi w obrazek (globalne
   `box-sizing: border-box`) i bierze go z `--stage-pad`, czyli DOKŁADNIE tego
   samego, co kafle bez przełącznika. Wcześniej scena miała tu `padding: 0`,
   a zdjęcia własne `.7rem` — i te packshoty wychodziły większe od sąsiadów. */
.prod-item--sizes .prod-item__img { padding: 0; }
.prod-item--sizes .prod-item__img img {
  position: absolute;
  inset: 0;
  padding: var(--stage-pad);
  opacity: 0;
  /* Kopie pod spodem nie mogą przechwytywać kliknięć ani gestu przewijania. */
  pointer-events: none;
  transition: opacity .35s var(--ease), transform .55s var(--ease);
}
.prod-size-radio:nth-of-type(1):checked ~ .prod-item__img img:nth-of-type(1),
.prod-size-radio:nth-of-type(2):checked ~ .prod-item__img img:nth-of-type(2),
.prod-size-radio:nth-of-type(3):checked ~ .prod-item__img img:nth-of-type(3),
.prod-size-radio:nth-of-type(4):checked ~ .prod-item__img img:nth-of-type(4) { opacity: 1; }

.prod-item__sizes {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  margin-top: .1rem;
}
.prod-item__sizes label {
  font-size: .62rem;
  letter-spacing: .06em;
  line-height: 1;
  padding: .34rem .55rem .3rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-faint);
  cursor: pointer;
  transition: color .3s var(--ease), border-color .3s var(--ease), background .3s var(--ease);
}
.prod-item__sizes label:hover { border-color: var(--copper-lite); color: var(--copper-deep); }
.prod-size-radio:nth-of-type(1):checked ~ .prod-item__body label:nth-of-type(1),
.prod-size-radio:nth-of-type(2):checked ~ .prod-item__body label:nth-of-type(2),
.prod-size-radio:nth-of-type(3):checked ~ .prod-item__body label:nth-of-type(3),
.prod-size-radio:nth-of-type(4):checked ~ .prod-item__body label:nth-of-type(4) {
  background: var(--copper);
  border-color: var(--copper);
  color: #fff;
}
.prod-size-radio:nth-of-type(1):focus-visible ~ .prod-item__body label:nth-of-type(1),
.prod-size-radio:nth-of-type(2):focus-visible ~ .prod-item__body label:nth-of-type(2),
.prod-size-radio:nth-of-type(3):focus-visible ~ .prod-item__body label:nth-of-type(3),
.prod-size-radio:nth-of-type(4):focus-visible ~ .prod-item__body label:nth-of-type(4) {
  outline: 2px solid var(--copper-deep);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .prod-item--sizes .prod-item__img img { transition: none; }
  .prod-item, .prod-item__img, .prod-item__img img, .prod-item__peek { transition: none; }
}

.prod-cta {
  margin-top: clamp(1rem, 3vw, 2rem);
  padding: clamp(2rem, 4vw, 3rem);
  background: linear-gradient(120deg, var(--cta-1), var(--cta-2));
  box-shadow: var(--shadow-md);
  border-radius: 22px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.3rem;
}
.prod-cta p {
  font-family: var(--serif);
  font-size: clamp(1.15rem, 2.4vw, 1.55rem);
  font-style: italic;
  color: var(--ivory);
  max-width: 52ch;
}
.prod-cta__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: .8rem; }
.prod-cta .btn--solid { background: var(--ivory); color: var(--cta-2); }
.prod-cta .btn--solid:hover { background: #fff; }
.prod-cta .btn--ghost { color: var(--ivory); border-color: rgba(253, 250, 246, .45); }
.prod-cta .btn--ghost:hover { border-color: var(--ivory); background: rgba(253, 250, 246, .1); }

.prod-disclaimer {
  margin-top: 1.8rem;
  text-align: center;
  font-size: .8rem;
  color: var(--ink-faint);
  max-width: 68ch;
  margin-left: auto;
  margin-right: auto;
}
.prod-disclaimer a { color: var(--copper-deep); text-decoration: underline; text-underline-offset: 2px; }

@media (max-width: 1000px) {
  /* Trzy karty przestają się mieścić — dwie równe kolumny, nadal ten sam kadr. */
  .prod-story { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .prod-story__frame { border-radius: 16px; }
}
@media (max-width: 620px) {
  .prod-story { grid-template-columns: 1fr; gap: 1.7rem; }
}
@media (max-width: 560px) {
  .prod-brand__head { flex-direction: column; align-items: flex-start; gap: 1rem; }
  .prod-brand__logo { width: 72px; height: 72px; }
  .prod-cta__actions { flex-direction: column; width: 100%; }
  /* .btn ma white-space: nowrap — w jednej kolumnie numer telefonu rozpychałby stronę. */
  .prod-cta .btn { white-space: normal; width: 100%; }
  .prod-list__cols { grid-template-columns: 1fr; gap: 1.4rem; }
  .prod-list__logo { height: 50px; min-width: 50px; max-width: 132px; padding: .4rem .6rem; }
  .prod-list__logo img { max-width: 112px; }
  /* Przy `auto-fill minmax(196px, …)` treść węższa niż ~410 px spada do jednej
     kolumny i kafel zajmuje pół ekranu wysokości. Dwie kolumny na sztywno:
     półka zostaje półką, a nie stosem plakatów. */
  .prod-shelf__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
  .prod-shelf__head { gap: .35rem .8rem; padding-bottom: .6rem; }
  .prod-shelf__count { font-size: .6rem; }
  .prod-item { border-radius: 14px; }
  .prod-item__body { padding: .6rem .62rem .72rem; }
  .prod-item__body h5 { font-size: .78rem; }
  .prod-item__peek { width: 24px; height: 24px; top: .4rem; right: .4rem; }
}

/* ============================================================
   PRODUKTY · HERO
   Nagłówek podstrony rozbity na dwie kolumny: tekst z licznikiem
   i wysoki kadr z regału. Kadr wychodzi poza kolumnę treści na prawo,
   żeby strona od pierwszego ekranu nie wyglądała jak dokument.
   ============================================================ */
.prod-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: clamp(1.6rem, 4vw, 3.4rem);
  align-items: center;
}
.prod-hero__txt { padding-bottom: .4rem; }
.prod-hero .pf-lead { max-width: 54ch; }
.prod-hero .pf-lead strong { color: var(--ink); font-weight: 500; }

.prod-hero__facts {
  list-style: none;
  margin: 1.6rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.2rem, 3vw, 2.6rem);
}
.prod-hero__facts li { display: flex; flex-direction: column; gap: .15rem; }
.prod-hero__facts b {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.7rem, 3.4vw, 2.4rem);
  line-height: 1;
  color: var(--copper-deep);
}
.prod-hero__facts span {
  font-size: .72rem;
  letter-spacing: .06em;
  color: var(--ink-faint);
  max-width: 16ch;
}

.prod-hero__art { position: relative; }
.prod-hero__art img {
  width: 100%;
  /* Kadr trzymamy na 4:5 zamiast na natywnych proporcjach pliku: przy szerokiej
     kolumnie 3:4 urosłoby do ~700 px i tekst obok pływałby w pustce. Sufit
     wysokości domyka sprawę na niskich i bardzo szerokich ekranach. */
  aspect-ratio: 4 / 5;
  height: auto;
  max-height: min(70vh, 620px);
  object-fit: cover;
  display: block;
  border-radius: 22px;
  background: var(--sand);
}
.prod-hero__art figcaption {
  margin-top: .6rem;
  font-size: .72rem;
  letter-spacing: .05em;
  color: var(--ink-faint);
  text-align: right;
}
/* Kadr ZOSTAJE w swojej kolumnie. Wcześniej wychodził poza siatkę i dotykał
   prawej krawędzi okna — przy trzech przerywnikach niżej, które też szły na
   pełny bleed, strona miała trzy różne miary i przy scrollu wyglądało to jak
   zbiór przypadkowych kadrów. Teraz wszystko na podstronie stoi na tej samej
   kolumnie `.pf-main` i ma to samo zaokrąglenie 22px. */
@media (max-width: 860px) {
  .prod-hero { grid-template-columns: 1fr; }
  .prod-hero__art { max-width: 420px; }
  .prod-hero__art figcaption { text-align: left; }
}

/* ============================================================
   PRODUKTY · KADRY MIĘDZY REGAŁAMI
   Katalog to ponad trzy tysiące pikseli packshotów. Na granicy każdej
   marki wchodzi zdjęcie atmosfery — jedyny moment, w którym strona
   przestaje być listą.

   Wariantów jest JEDEN. Poprzednio było ich cztery (raz kadr z prawej,
   raz z lewej, raz wąski, raz panorama) i sześć wystąpień w środku sekcji:
   z perspektywy scrolla wyglądało to jak zdjęcia wrzucane losowo w połowie
   półki. Trzy identyczne panoramy, każda dokładnie na styku dwóch marek,
   czytają się jak przerywnik rozdziału.

   Panorama siedzi w tej samej kolumnie co reszta podstrony i ma to samo
   zaokrąglenie co kadr w hero. Pełny bleed (kadr od krawędzi do krawędzi
   okna) wypadł: przy hero wychodzącym w prawo i przerywnikach idących na
   całe okno strona miała trzy różne miary zamiast jednej.
   ============================================================ */
.prod-break {
  position: relative;
  display: block;
  /* Zero ujemnych marginesów: przerywnik ma dokładnie tę samą szerokość co
     nagłówek, pasek marek i półki nad nim. Kadr rozciągnięty na całe okno
     wypadał z rytmu strony — reszta podstrony trzyma się kolumny `.pf-main`. */
  margin: clamp(3rem, 7vw, 5.5rem) 0;
}
.prod-break__frame {
  position: relative;
  overflow: hidden;
  /* To samo zaokrąglenie co kadr w hero i co karty produktów. */
  border-radius: 22px;
  background: var(--sand);
}
.prod-break__frame img {
  width: 100%;
  /* Stała wysokość zamiast proporcji pliku: trzy kadry mają wyglądać na trzy
     odsłony tego samego przerywnika, a nie na trzy różne bloki. */
  height: clamp(200px, 26vw, 340px);
  object-fit: cover;
  display: block;
}
/* Podpis leży NA zdjęciu, więc potrzebuje własnego tła — zdjęcia z banku
   różnią się jasnością i biały tekst raz by ginął, raz krzyczał. */
.prod-break__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(20, 26, 14, .88) 0%, rgba(20, 26, 14, .74) 26%, rgba(20, 26, 14, .4) 52%, rgba(20, 26, 14, 0) 88%);
  pointer-events: none;
}
.prod-break__cap {
  position: absolute;
  /* Podpis odsunięty od ramki kadru, nie od krawędzi ekranu. */
  left: clamp(1.2rem, 3.4vw, 2.6rem);
  right: clamp(1.2rem, 3.4vw, 2.6rem);
  bottom: clamp(1.1rem, 3vw, 2.2rem);
  max-width: 46ch;
}
.prod-break__eyebrow {
  display: block;
  font-size: .66rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .74);
  margin-bottom: .5rem;
}
.prod-break__cap p {
  font-family: var(--serif);
  font-size: clamp(1.15rem, 2.4vw, 1.75rem);
  line-height: 1.3;
  color: #fff;
  text-shadow: 0 1px 20px rgba(15, 20, 10, .45);
}
@media (max-width: 860px) {
  .prod-break__frame { border-radius: 16px; }
  .prod-break__frame img { height: clamp(190px, 46vw, 260px); }
}

/* ============================================================
   PRODUKTY · KARTA PRODUKTU (panel po kliknięciu w zdjęcie)
   Wzór: duża scena ze zdjęciem i pływające pigułki, które mówią
   „do jakich włosów”, zanim ktokolwiek przeczyta opis. Bez ceny —
   cennik potwierdza salon, a strona ma pomóc WYBRAĆ, nie sprzedać.

   Panel żyje w jednym egzemplarzu i jest przepisywany przy każdym
   otwarciu (produkty.js → openCard). 190 kafli × własny modal
   w HTML-u to byłby plik nie do utrzymania.
   ============================================================ */
.pcard[hidden] { display: none; }
.pcard {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: clamp(.75rem, 3vw, 2.2rem);
}
.pcard__scrim {
  position: absolute;
  inset: 0;
  background: rgba(27, 30, 21, .58);
  backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity .35s var(--ease);
  border: 0;
  cursor: pointer;
}
.pcard.is-open .pcard__scrim { opacity: 1; }

.pcard__box {
  position: relative;
  width: min(1040px, 100%);
  max-height: 100%;
  overflow: auto;
  overscroll-behavior: contain;
  background: var(--ivory);
  border-radius: clamp(18px, 3vw, 28px);
  box-shadow: var(--shadow-lg);
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  opacity: 0;
  transform: translateY(18px) scale(.985);
  transition: opacity .35s var(--ease), transform .45s var(--ease);
}
.pcard.is-open .pcard__box { opacity: 1; transform: none; }

.pcard__close {
  position: absolute;
  top: clamp(.6rem, 1.6vw, 1rem);
  right: clamp(.6rem, 1.6vw, 1rem);
  z-index: 4;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-soft);
  border-radius: 50%;
  background: rgba(252, 253, 248, .92);
  color: var(--ink);
  cursor: pointer;
  transition: background .3s var(--ease), color .3s var(--ease), transform .3s var(--ease);
}
.pcard__close:hover { background: var(--copper); color: #fff; border-color: var(--copper); transform: rotate(90deg); }
.pcard__close svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }

/* ---------- Scena ----------
   Ta sama kompozycja co kafel na półce, tylko w większej skali: gradient,
   cień pod opakowaniem, produkt wyśrodkowany. Dzięki temu otwarcie karty
   czyta się jak przybliżenie, a nie jak przejście gdzie indziej. */
.pcard__stage {
  position: relative;
  padding: clamp(1.6rem, 4vw, 2.8rem);
  background: radial-gradient(110% 75% at 50% 18%, #fff 0%, #fdfdf9 42%, var(--sand) 100%);
  display: grid;
  place-items: center;
  min-height: clamp(320px, 44vh, 520px);
  border-radius: clamp(18px, 3vw, 28px) 0 0 clamp(18px, 3vw, 28px);
}
.pcard__brand {
  position: absolute;
  top: clamp(1rem, 2.4vw, 1.6rem);
  left: clamp(1.2rem, 3vw, 2rem);
  font-size: .66rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.pcard__shot {
  position: relative;
  z-index: 1;
  max-width: min(78%, 340px);
  max-height: clamp(260px, 40vh, 440px);
  width: auto;
  height: auto;
  object-fit: contain;
  /* jak na kaflu — białe tło packshotu ma zniknąć w scenie, patrz .prod-item__img img */
  mix-blend-mode: multiply;
  opacity: 0;
  transform: scale(.9) translateY(10px);
  transition: opacity .5s var(--ease) .06s, transform .6s var(--ease) .06s;
}
.pcard.is-open .pcard__shot { opacity: 1; transform: none; }

/* Cień „na półce” siedzi na OPAKOWANIU (wrapper ma wysokość zdjęcia), a nie
   na scenie — inaczej przy niższych packshotach zostawał samotną smugą
   kilkadziesiąt pikseli pod produktem. */
.pcard__shotwrap {
  position: relative;
  display: grid;
  place-items: center;
}
.pcard__shotwrap::after {
  content: "";
  position: absolute;
  left: 16%;
  right: 16%;
  bottom: -10px;
  height: 20px;
  border-radius: 50%;
  background: rgba(40, 55, 26, .2);
  filter: blur(11px);
  opacity: 0;
  transition: opacity .6s var(--ease) .12s;
}
.pcard.is-open .pcard__shotwrap::after { opacity: 1; }

/* Ikona zamiast packshotu — dla pozycji bez zdjęcia od producenta.
   Na scenie NIE ma już krążka: obwódka w tej skali czytała się jak awatar,
   a rysunek w środku wychodził kilka razy mniejszy od sąsiednich opakowań.
   Ikona jedzie więc na `.pcard__shot` — tym samym rozmiarze, cieniu, wejściu
   i wyśrodkowaniu co zdjęcie. Osobno ustawiamy tylko `mix-blend-mode`: SVG
   jest przezroczysty, nie ma białego tła do wygaszenia, a `multiply`
   przygasiłby zieleń na gradiencie sceny.

   UWAGA przy dorzucaniu nowej ikony: `.pcard__shot` skaluje wyłącznie W DÓŁ
   (`max-width`/`max-height` przy `width: auto`). Plik musi więc deklarować
   w korzeniu `width`/`height` WIĘKSZE niż te limity — nasze mają viewBox ×10
   (np. 1000×960) — inaczej rysunek zostanie przy swoim rozmiarze własnym
   i wyląduje na scenie jako znaczek wielkości stu pikseli.

   Krążek na półce zostaje bez zmian — tam trzyma kafle w jednym rytmie. */
.pcard__shot--icon {
  mix-blend-mode: normal;
}

/* Krążek już tylko pod awaryjne inicjały (produkt bez zdjęcia i bez wpisu
   w mapie ikon) — dwie litery same na gradiencie wyglądałyby jak brak treści. */
.pcard__mark {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: min(62%, 210px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, .86);
  box-shadow: inset 0 0 0 1px var(--line-soft);
  font-family: var(--serif);
  font-size: clamp(2.4rem, 6vw, 3.4rem);
  color: var(--copper-deep);
  opacity: 0;
  transform: scale(.9);
  transition: opacity .5s var(--ease) .06s, transform .6s var(--ease) .06s;
}
.pcard.is-open .pcard__mark { opacity: 1; transform: none; }
.pcard__mark--letters { font-family: var(--serif); }

/* ---------- Kadr kampanijny na całą scenę ----------
   Producent sprzętu przysyła dwa gatunki zdjęć. Packshot na bieli gaśnie
   w kremowym tle przez `mix-blend-mode: multiply` i tak działa cały katalog.
   Ale kadr kampanijny — modelka, blat, hero na czerni, różowa edycja SS26 —
   MA własne tło. Wpasowany w środek sceny czytał się jak zdjęcie unoszące
   się nad kremem: widać było jego krawędź i cztery rogi.

   Dlatego scena przełącza się w tryb `is-scene`: zdjęcie wychodzi z kadru
   na całe okno (`cover`, bez marginesu i bez mieszania), a tło sceny
   przyjmuje kolor policzony z ramki zdjęcia — krawędź przestaje istnieć.
   Klasę i `--scene-bg` dokłada produkty.js z `data-scenes` na kaflu.

   `is-scene--light` to ta sama scena, tylko na jasnym zdjęciu (róż SS26,
   marmurowy blat): dymki i podpis marki idą wtedy na ciemny tekst zamiast
   białego. */
.pcard__stage.is-scene {
  background: var(--scene-bg, var(--sand));
  /* Zdjęcie wychodzi poza padding sceny, więc musi się obciąć na jej
     zaokrąglonym rogu. */
  overflow: hidden;
}
/* Bez tego `inset: 0` na zdjęciu liczyłoby się od wrappera (ma `position:
   relative` pod cień „na półce”), a nie od całej sceny. */
.pcard__stage.is-scene .pcard__shotwrap { position: static; }
.pcard__stage.is-scene .pcard__shotwrap::after { display: none; }
.pcard__stage .pcard__shot--scene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: cover;
  /* Nie ma bieli do wygaszenia — `multiply` przybrudziłby całe zdjęcie. */
  mix-blend-mode: normal;
  /* Wejście samą przezroczystością: `scale(.9)` odsłaniałoby przy otwieraniu
     pasy tła po bokach kadru. */
  transform: none;
}
.pcard__stage.is-scene .pcard__brand { color: rgba(255, 255, 255, .72); }
.pcard__stage.is-scene--light .pcard__brand { color: rgba(0, 0, 0, .55); }
/* Dymek bierze kolor zdjęcia i tylko przyciemnia go (albo rozjaśnia) tyle,
   żeby tekst był czytelny — dzięki temu wygląda jak część kadru, a nie jak
   biała naklejka rzucona na kampanię. */
.pcard__stage.is-scene .pcard__pill {
  background: linear-gradient(rgba(0, 0, 0, .34), rgba(0, 0, 0, .34)), var(--scene-bg, #333);
  color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .26), 0 10px 30px -14px rgba(0, 0, 0, .7);
}
.pcard__stage.is-scene .pcard__pill i { background: #fff; }
.pcard__stage.is-scene--light .pcard__pill {
  background: linear-gradient(rgba(255, 255, 255, .62), rgba(255, 255, 255, .62)), var(--scene-bg, #fff);
  color: var(--ink);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1), 0 10px 30px -14px rgba(0, 0, 0, .45);
}
.pcard__stage.is-scene--light .pcard__pill i { background: var(--copper); }
/* Miniatury i strzałki leżą teraz NA zdjęciu, nie na kremowym tle —
   potrzebują własnego cienia, żeby nie zlewały się z jasnym kadrem. */
.pcard__stage.is-scene .pcard__thumb,
.pcard__stage.is-scene .pcard__nav button { box-shadow: 0 6px 20px -8px rgba(0, 0, 0, .65); }

/* ---------- Pasek ujęć ----------
   Producenci sprzętu (ghd) przysyłają do jednego urządzenia po kilka zdjęć:
   pełną sylwetkę, zbliżenie zawiasu, opakowanie, kadr z modelką. Na półce
   zostaje jedno — w karcie reszta wchodzi jako rząd miniatur pod produktem.

   Rząd pojawia się wyłącznie przy dwóch ujęciach i więcej (produkty.js chowa
   go atrybutem `hidden`), więc karty kosmetyków z jednym packshotem wyglądają
   dokładnie tak jak wcześniej.

   Szerokość jest przycięta o miejsce na strzałki nawigacji — te siedzą
   absolutnie w lewym dolnym rogu sceny i rząd miniatur nie może pod nie
   wjechać. */
.pcard__thumbs {
  position: relative;
  z-index: 3;
  display: flex;
  /* JEDEN rząd, nigdy dwa. Chronos ma dziewięć ujęć i zawinięte w drugi rząd
     zasłaniały pół sceny. `width: max-content` + `max-width: 100%` daje przy
     okazji jedno i drugie: przy czterech miniaturach rząd jest wyśrodkowany,
     przy dziewięciu — przewijany w bok, z dziesiątą wystającą zza krawędzi,
     która mówi, że jest tego więcej. */
  flex-wrap: nowrap;
  /* `flex-start`, nie `center`: gdy rząd się mieści, scena i tak wyśrodkowuje
     go swoim `place-items: center`, a gdy nie — wyśrodkowanie w kontenerze
     z przewijaniem obcina pierwsze miniatury tak, że nie da się do nich
     doscrollować. ŻADNEGO `width: max-content` — przy dziewięciu ujęciach
     rozpychało to całą kartę poza szerokość telefonu. */
  justify-content: flex-start;
  gap: .4rem;
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  /* Miejsce na obwódkę aktywnej miniatury — bez tego obcina ją `overflow`. */
  padding: 2px;
  margin-top: clamp(.9rem, 2.4vw, 1.4rem);
}
.pcard__thumbs::-webkit-scrollbar { display: none; }
.pcard__thumb {
  flex: none;
  width: 44px;
  height: 54px;
  padding: 3px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  background: rgba(255, 255, 255, .92);
  cursor: pointer;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .4s var(--ease), transform .4s var(--ease),
              border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.pcard.is-open .pcard__thumb { opacity: 1; transform: none; transition-delay: .3s; }
.pcard__thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* jak na scenie i na kaflu — biel packshotu ma zniknąć w tle miniatury */
  mix-blend-mode: multiply;
}
/* Miniatura kadru kampanijnego wypełnia kafelek tak samo, jak duże zdjęcie
   wypełnia scenę — inaczej w rzędzie packshotów byłaby jedyną z białą
   obwódką dookoła treści. */
.pcard__thumb--scene { padding: 0; overflow: hidden; }
.pcard__thumb--scene img { object-fit: cover; mix-blend-mode: normal; }
.pcard__thumb:hover { border-color: var(--copper); }
.pcard__thumb.is-active {
  border-color: var(--copper);
  box-shadow: inset 0 0 0 1px var(--copper);
}

/* ---------- Pigułki na scenie ----------
   Trzy fakty przypięte wokół produktu, wjeżdżające po kolei. Pierwsza zawsze
   mówi, DO JAKICH WŁOSÓW jest produkt — to pierwsze pytanie przy półce.
   Pozycje są stałe (nie liczone ze zdjęcia): packshoty mają jednakowy kadr
   4:5 z opakowaniem na środku, więc wyliczanie punktów zaczepienia dawałoby
   ten sam efekt kosztem pracy w rAF przy każdym otwarciu. */
.pcard__pill {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  max-width: min(46%, 230px);
  padding: .5rem .8rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, .95);
  box-shadow: 0 10px 30px -14px rgba(40, 55, 26, .5), inset 0 0 0 1px var(--line-soft);
  font-size: .74rem;
  line-height: 1.3;
  color: var(--ink);
  opacity: 0;
  transform: translateY(8px) scale(.94);
  transition: opacity .45s var(--ease), transform .5s var(--ease);
}
.pcard__pill i {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--copper);
}
.pcard.is-open .pcard__pill { opacity: 1; transform: none; }
.pcard.is-open .pcard__pill--1 { transition-delay: .26s; }
.pcard.is-open .pcard__pill--2 { transition-delay: .36s; }
.pcard.is-open .pcard__pill--3 { transition-delay: .46s; }
.pcard.is-open .pcard__pill--4 { transition-delay: .56s; }
/* Pigułki trzymają się blisko opakowania: przy kadrze 4:5 z produktem na środku
   zostaje po ~20% szerokości z każdej strony i tam siedzą etykiety. Rozrzucone
   po rogach sceny przestawały wyglądać na przypięte do czegokolwiek. */
.pcard__pill--1 { left: clamp(.7rem, 2vw, 1.4rem); top: 24%; }
.pcard__pill--2 { right: clamp(.7rem, 2vw, 1.4rem); top: 41%; }
.pcard__pill--3 { left: clamp(1rem, 2.6vw, 2rem); top: 58%; }
.pcard__pill--4 { right: clamp(1rem, 2.6vw, 2rem); top: 72%; }

/* ---------- Kolumna opisu ---------- */
.pcard__info {
  padding: clamp(1.5rem, 3.4vw, 2.6rem);
  display: flex;
  flex-direction: column;
  gap: .9rem;
  align-self: center;
}
.pcard__line {
  font-size: .7rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--copper-deep);
}
.pcard__info h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.6rem, 3.4vw, 2.3rem);
  line-height: 1.1;
  color: var(--ink);
}
.pcard__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
  font-size: .76rem;
  color: var(--ink-faint);
  letter-spacing: .04em;
}
.pcard__meta span + span::before { content: "·"; margin-right: .5rem; color: var(--ink-faint); }
.pcard__desc p {
  color: var(--ink-soft);
  font-size: .93rem;
  line-height: 1.7;
}
.pcard__desc p + p { margin-top: .6rem; }

.pcard__use {
  padding: .95rem 1.1rem;
  background: var(--cream);
  border-radius: 14px;
  border: 1px solid var(--line-soft);
}
.pcard__use h3 {
  font-size: .66rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--copper-deep);
  margin-bottom: .35rem;
}
.pcard__use p { font-size: .85rem; line-height: 1.6; color: var(--ink-soft); }

.pcard__tags {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
}
.pcard__tags li {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .3rem .7rem;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  background: #fff;
  font-size: .72rem;
  color: var(--ink-soft);
}
.pcard__tags li::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--copper-lite);
}

.pcard__foot {
  margin-top: .3rem;
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  align-items: center;
}
.pcard__note { font-size: .74rem; color: var(--ink-faint); max-width: 34ch; }
/* Na desktopie wrapper przycisku nie istnieje dla układu — przycisk i notka
   stoją obok siebie jak wcześniej. Pracuje dopiero na telefonie (niżej). */
.pcard__cta { display: contents; }
.pcard__more { display: none; }

/* Numer odsłonięty po „Zapytaj o dostępność” (komputer i tablet). Zajmuje
   miejsce przycisku i ma podobny obrys, żeby podmiana nie szarpała stopki.
   `[hidden]` wypisane wprost — `.btn` i `display: flex` niżej nadpisałyby
   domyślne `display: none` przeglądarki. */
.pcard__ask[hidden],
.pcard__phone[hidden] { display: none; }
/* Siatka zamiast flexa z zawijaniem: podpis nad numerem, przycisk obok obu.
   Przy flexie podpis na pełny wiersz rozpychał pole na całą szerokość stopki. */
.pcard__phone {
  display: grid;
  grid-template-columns: auto auto;
  align-items: center;
  gap: .1rem 1.1rem;
  padding: .55rem .55rem .55rem 1.3rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  box-shadow: var(--shadow-sm);
  animation: pcardPhoneIn .45s var(--ease);
}
.pcard__phone-label {
  grid-column: 1;
  font-size: .62rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--copper-deep);
}
.pcard__phone-num {
  font-family: var(--serif);
  font-size: 1.55rem;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: .02em;
  color: var(--ink);
  white-space: nowrap;
  user-select: all;
  -webkit-user-select: all;
}
.pcard__copy {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: inline-flex;
  align-items: center;
  gap: .45em;
  padding: .75em 1.2em;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(120deg, var(--btn-1), var(--btn-2));
  color: var(--btn-fg);
  font-family: var(--sans);
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), background .3s var(--ease);
}
.pcard__copy:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -12px var(--btn-glow); }
.pcard__copy.is-done { background: var(--copper-deep); }
.pcard__copy svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Komunikat tylko dla czytnika ekranu — wzrokowo mówi to napis na przycisku. */
.pcard__copied {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
@keyframes pcardPhoneIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Strzałki między produktami tej samej półki — przy sześciu pozycjach
   zamykanie i otwieranie karty za każdym razem jest męczące. */
.pcard__nav {
  position: absolute;
  z-index: 4;
  bottom: clamp(.7rem, 2vw, 1.2rem);
  left: clamp(.8rem, 2.6vw, 1.8rem);
  display: flex;
  gap: .4rem;
}
.pcard__nav button {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-soft);
  border-radius: 50%;
  background: rgba(255, 255, 255, .92);
  color: var(--ink);
  cursor: pointer;
  transition: background .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.pcard__nav button:hover:not(:disabled) { background: var(--copper); color: #fff; border-color: var(--copper); }
.pcard__nav button:disabled { opacity: .35; cursor: default; }
.pcard__nav svg { width: 13px; height: 10px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

@media (max-width: 820px) {
  .pcard__box { grid-template-columns: 1fr; }
  .pcard__stage {
    min-height: clamp(260px, 38vh, 360px);
    border-radius: clamp(18px, 3vw, 28px) clamp(18px, 3vw, 28px) 0 0;
  }
  /* Na wąskim ekranie pigułki nachodziłyby na produkt — schodzą pod scenę
     jako zwykły rząd. Treść ta sama, układ inny. */
  .pcard__stage { grid-template-rows: 1fr auto auto; padding-top: clamp(2.4rem, 8vw, 3.2rem); }
  .pcard__pills {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .35rem;
    margin-top: .9rem;
    /* Scena jest siatką z `place-items: center`, więc jej dzieci dostają
       szerokość `max-content` — bez tego ograniczenia rząd dymków rósłby
       w nieskończoność zamiast zawinąć się do drugiego wiersza. */
    max-width: 100%;
  }
  .pcard__pill {
    position: static;
    max-width: none;
    transform: none;
    box-shadow: inset 0 0 0 1px var(--line-soft);
  }
  .pcard__nav { position: static; margin-top: .8rem; justify-content: center; }

  /* ---------- Przyklejone „Zapytaj o dostępność” ----------
     Na telefonie scena zajmuje prawie cały ekran i przycisk lądował pod
     trzema akapitami — karta wyglądała na skończoną w połowie opisu.
     Teraz przycisk jedzie przyklejony do dołu karty, a tekst nad nim gaśnie
     w tło: urwany akapit plus „Przewiń…” mówią, że pod spodem jest więcej.

     `.pcard__foot` znika z układu, więc przycisk i notka są dziećmi kolumny
     opisu. Dzięki temu sticky trzyma się CAŁEJ kolumny (a nie samej stopki),
     a notka zostaje w treści nad przyciskiem, nie zajmując miejsca na dole. */
  .pcard__info { --pc-pad: 1.5rem; padding: var(--pc-pad) var(--pc-pad) 0; }
  .pcard__foot { display: contents; }
  .pcard__cta {
    order: 1;
    position: sticky;
    bottom: 0;
    z-index: 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .45rem;
    margin: 0 calc(-1 * var(--pc-pad));
    padding: 1.6rem var(--pc-pad) calc(var(--pc-pad) * .8 + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(rgba(252, 253, 248, 0), var(--ivory) 42%);
  }
  .pcard__cta .btn { width: 100%; justify-content: center; }
  /* Tablet w pionie dostaje ten sam przyklejony dół co telefon — numer
     rozciąga się jak przycisk, który zastąpił. */
  .pcard__cta .pcard__phone { width: 100%; justify-content: space-between; }
  .pcard__more {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .64rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--copper-deep);
    opacity: 0;
    transition: opacity .3s var(--ease);
  }
  .pcard.has-more .pcard__more { opacity: 1; }
  .pcard__more svg {
    width: 10px;
    height: 6px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    animation: pcardNudge 1.6s var(--ease) infinite;
  }
}
@keyframes pcardNudge {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(3px); }
}

@media (prefers-reduced-motion: reduce) {
  .pcard__scrim,
  .pcard__box,
  .pcard__shot,
  .pcard__mark,
  .pcard__pill,
  .pcard__thumb,
  .pcard__stage::after { transition: none; }
  .pcard__box { transform: none; }
  .pcard__more svg { animation: none; }
  .pcard__phone { animation: none; }
  .pcard__copy { transition: none; }
}

/* Na dotyku hover nie istnieje — znaczek „powiększ” musi być widoczny od razu,
   inaczej nikt nie odkryje, że kafle się otwierają. */
@media (hover: none) {
  .prod-item__peek { opacity: .85; transform: none; }
}

/* ============================================================
   PRODUKTY · WEJŚCIE ELEMENTÓW W KADR
   Stan spoczynkowy w CSS jest W PEŁNI WIDOCZNY — ukrycie włącza dopiero
   klasa `js-reveal` na <html>, którą dokłada produkty.js. Bez JS-a (albo
   gdy obserwator się nie odpali) katalog po prostu stoi na swoim miejscu,
   a nie znika. `prefers-reduced-motion` w ogóle nie dostaje tej klasy.

   Kafle w półce nie są obserwowane pojedynczo — przy 257 pozycjach byłoby
   to 257 obserwatorów. Wchodzi CAŁA półka, a opóźnienia rozsypuje CSS po
   pozycji kafla w rzędzie. Jeden obserwator na sekcję, ten sam efekt.
   ============================================================ */
/* Bez `will-change`: na tej stronie czeka na wejście w kadr ~90 sekcji naraz
   i tyle samo wymuszonych warstw kompozytora zjada pamięć telefonu bardziej,
   niż zyskuje na płynności. Sama para opacity + transform i tak idzie
   po stronie kompozytora. */
.js-reveal .reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .7s var(--ease), transform .8s var(--ease);
}
.js-reveal .reveal.is-in { opacity: 1; transform: none; }

.js-reveal .reveal .prod-item,
.js-reveal .reveal .prod-story__cell {
  opacity: 0;
  transform: translateY(14px);
}
.js-reveal .reveal.is-in .prod-item,
.js-reveal .reveal.is-in .prod-story__cell {
  opacity: 1;
  transform: none;
  transition: opacity .55s var(--ease), transform .65s var(--ease);
}
/* Sufit na ósmej pozycji: dalsze kafle wchodzą razem z ósmym. Kaskada dłuższa
   niż ~0,4 s przestaje wyglądać na rytm, a zaczyna na powolne ładowanie. */
.js-reveal .reveal.is-in > * > .prod-item:nth-child(1),
.js-reveal .reveal.is-in > .prod-story__cell:nth-child(1) { transition-delay: .02s; }
.js-reveal .reveal.is-in > * > .prod-item:nth-child(2),
.js-reveal .reveal.is-in > .prod-story__cell:nth-child(2) { transition-delay: .07s; }
.js-reveal .reveal.is-in > * > .prod-item:nth-child(3),
.js-reveal .reveal.is-in > .prod-story__cell:nth-child(3) { transition-delay: .12s; }
.js-reveal .reveal.is-in > * > .prod-item:nth-child(4),
.js-reveal .reveal.is-in > .prod-story__cell:nth-child(4) { transition-delay: .17s; }
.js-reveal .reveal.is-in > * > .prod-item:nth-child(5),
.js-reveal .reveal.is-in > .prod-story__cell:nth-child(5) { transition-delay: .22s; }
.js-reveal .reveal.is-in > * > .prod-item:nth-child(6),
.js-reveal .reveal.is-in > .prod-story__cell:nth-child(6) { transition-delay: .27s; }
.js-reveal .reveal.is-in > * > .prod-item:nth-child(n + 7),
.js-reveal .reveal.is-in > .prod-story__cell:nth-child(n + 7) { transition-delay: .32s; }

/* ============================================================
   WYBÓR KALENDARZA (dialog #bookPick)
   Salon ma dwie wizytówki na Booksy, więc „Rezerwuj” najpierw pyta,
   o którą wizytę chodzi. Natywny <dialog>: Esc, focus i tło dostajemy
   od przeglądarki, my dokładamy tylko wygląd i animację wejścia.
   ============================================================ */
.bookpick {
  border: 0;
  background: none;
  color: var(--ink);
  width: min(560px, 100%);
  /* Globalny reset zeruje marginesy wszystkiego, więc `margin: auto` z UA
     (to ono centruje modal) trzeba przywrócić ręcznie — bez tego okno lądowało
     w lewym górnym rogu. Padding trzyma cień karty z dala od krawędzi
     kadru, który przy overflow: auto zostałby przycięty. */
  margin: auto;
  padding: 12px;
  max-height: 100%;
  overflow: auto;
  overscroll-behavior: contain;
}
.bookpick::backdrop {
  background: rgba(27, 30, 21, .55);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.bookpick.is-open::backdrop { opacity: 1; }

.bookpick__box {
  position: relative;
  background: var(--ivory);
  border: 1px solid var(--line-soft);
  border-radius: 24px;
  box-shadow: var(--shadow-md);
  padding: clamp(1.8rem, 4vw, 2.6rem);
  text-align: center;
  opacity: 0;
  transform: translateY(14px) scale(.98);
  transition: opacity .3s var(--ease), transform .35s var(--ease);
}
.bookpick.is-open .bookpick__box { opacity: 1; transform: none; }

.bookpick__x {
  position: absolute;
  top: .9rem; right: .9rem;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: 1px solid var(--line-soft);
  border-radius: 50%;
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
  transition: color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.bookpick__x:hover { color: var(--copper); border-color: var(--copper); transform: rotate(90deg); }

.bookpick .eyebrow { justify-content: center; }
.bookpick__title {
  font-family: var(--serif);
  font-size: clamp(1.6rem, 4vw, 2.1rem);
  font-weight: 500;
  line-height: 1.15;
  margin-top: .6rem;
}
.bookpick__title em { font-style: italic; color: var(--copper); }
.bookpick__lead {
  margin: .7rem auto 1.6rem;
  max-width: 44ch;
  font-size: .9rem;
  color: var(--ink-soft);
}

.bookpick__opts { display: grid; gap: .7rem; }
.bookpick__opt {
  display: flex;
  align-items: center;
  gap: .9rem;
  text-align: left;
  padding: 1rem 1.1rem;
  border: 1px solid var(--line-soft);
  border-radius: 18px;
  background: #fff;
  transition: border-color .35s var(--ease), transform .35s var(--ease), box-shadow .35s var(--ease);
}
.bookpick__opt:hover {
  border-color: var(--copper);
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
}
.bookpick__ico {
  flex: none;
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--copper);
  background: color-mix(in srgb, var(--copper) 12%, #fff);
}
.bookpick__ico svg { width: 21px; height: 21px; }
.bookpick__txt { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.bookpick__txt b { font-size: .96rem; font-weight: 600; color: var(--ink); }
.bookpick__txt em {
  font-style: normal;
  font-size: .78rem;
  line-height: 1.35;
  color: var(--ink-faint);
}
.bookpick__arrow {
  margin-left: auto;
  flex: none;
  color: var(--copper);
  font-size: 1.1rem;
  transition: transform .35s var(--ease);
}
.bookpick__opt:hover .bookpick__arrow { transform: translateX(4px); }

.bookpick__note {
  margin-top: 1.3rem;
  font-size: .8rem;
  color: var(--ink-faint);
}
.bookpick__note a { color: var(--copper-deep); font-weight: 600; }

@media (max-width: 420px) {
  .bookpick__opt { padding: .85rem .9rem; gap: .7rem; }
  .bookpick__ico { width: 36px; height: 36px; }
  .bookpick__txt em { font-size: .72rem; }
}

/* ============================================================
   Stopka prawna podstron galeryjnych i katalogu
   ------------------------------------------------------------
   Podstrony (produkty / portfolio / salon / zabiegi) nie mają pełnej stopki —
   kończą się jednym przyciskiem „Wróć”. Link do polityki prywatności musi być
   jednak dostępny z KAŻDEJ strony (RODO), więc dostaje własną, cichą linię pod
   tym przyciskiem: bez ramki, w kolorze tekstu pomocniczego, żeby nie
   konkurowała z powrotem na stronę główną.
   ============================================================ */
.pf-foot { flex-direction: column; align-items: center; gap: 1.1rem; }
.pf-legal {
  margin: 0;
  font-size: .8rem;
  letter-spacing: .04em;
  color: var(--ink-faint);
}
.pf-legal a { color: inherit; text-underline-offset: 3px; transition: color .25s var(--ease); }
.pf-legal a:hover { color: var(--copper-deep); }
