/* =============================================
   CTA BANNER — Inline conversion strip
   Used on: hero.html, oferta-okna.html (×3)
   ============================================= */
.cta-banner {
  position: relative;
  padding: 96px 24px 56px;
  background: var(--white);
  overflow: hidden;
}

.cta-banner__container {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  background:
    linear-gradient(to bottom, rgba(255,249,241,0) 0%, var(--white) 60%),
    linear-gradient(to right, rgba(var(--orange-rgb), 0.18) 0%, rgba(var(--orange-end-rgb), 0.18) 100%);
  border: none;
  border-radius: 20px;
  padding: 85px 88px;
}

.cta-banner__heading {
  font-size: var(--heading-size);
  font-weight: var(--heading-weight);
  color: var(--black);
  letter-spacing: -0.03em;
  line-height: 1.3;
}

.cta-banner__heading span {
  color: inherit;
}

.cta-banner__sub {
  font-size: 17px;

  font-weight: 400;
  color: var(--dark-gray);
  line-height: 1.7;
  max-width: 560px;
  margin-bottom: 16px;
}

/* ---------- Stats row ---------- */
.cta-banner__stats {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 48px;
  margin: 24px 0 32px;
  width: 100%;
  max-width: 720px;
}

.cta-banner__stat {
  display: flex;
  align-items: center;
  gap: 8px;
}

.cta-banner__stat-number {
  font-size: 47px;
  font-weight: 420;
  color: var(--black);
  letter-spacing: -0.02em;
  line-height: 1;
  white-space: nowrap;
}

.cta-banner__stat-label {
  font-size: 15px;
  font-weight: 500;
  color: var(--dark-gray);
  line-height: 1.35;
  text-align: left;
}

.cta-banner__stat-divider {
  width: 1px;
  height: 48px;
  background: var(--border-color);
  flex-shrink: 0;
}

/* Button size — matches hero */
.ihb--cta {
  font-size: 19px;
  padding: 18px 44px;
}

/* ---------- Full-width image variant (bottom of landing page) ----------
   Doubled class (0,2,0) so `padding: 0` outranks the `.cta-banner` (0,1,0)
   padding rules — including the one inside the ≤768px block, which would
   otherwise win on source order and inset the image behind a cream frame. */
.cta-banner.cta-banner--image {
  padding: 0;
  background: var(--white);
  overflow: hidden;
  position: relative;
}

.cta-banner--image .cta-banner__image-full {
  display: block;
  width: 100%;
  height: 700px;
  object-fit: cover;
  transform-origin: center center;
  animation: cta-image-zoom 12s cubic-bezier(0.2, 0, 0.8, 1) infinite alternate;
  animation-play-state: paused;
  will-change: transform;
}

.cta-banner--image.in-view .cta-banner__image-full {
  animation-play-state: running;
}

@keyframes cta-image-zoom {
  0%   { transform: scale(1); }
  100% { transform: scale(1.175); }
}

@media (prefers-reduced-motion: reduce) {
  .cta-banner--image .cta-banner__image-full {
    animation: none;
  }
}

.cta-banner--image::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--dark-bg);
  opacity: 0.125;
  pointer-events: none;
}

.cta-banner--image::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 75% at 0% 100%, rgba(21, 21, 21, 0.65), rgba(21, 21, 21, 0) 75%);
  pointer-events: none;
  z-index: 1;
}

.cta-banner__image-content {
  position: absolute;
  inset: 0;
  z-index: 2;
  max-width: var(--max-width-wide);
  margin: 0 auto;
  padding: 0 24px 80px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 48px;
}

.cta-banner__image-text {
  display: flex;
  flex-direction: column;
  max-width: 720px;
}

.cta-banner__image-label {
  display: inline-block;
  font-size: 15px;
  font-weight: 500;
  color: var(--white);
  letter-spacing: 0.02em;
  margin-bottom: 20px;
  text-transform: capitalize;
}

.cta-banner__image-label::before { content: "// "; }

.cta-banner__image-heading {
  font-size: 66px;
  font-weight: var(--heading-weight);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--white);
  margin: 0;
  max-width: 11ch;
}

/* White CTA button (on dark image overlay) — matches site .ihb radius */
.cta-banner--image .ihb--cta {
  background: var(--white);
  background-image: none;
  border-color: transparent;
  flex-shrink: 0;
}

.cta-banner--image .ihb--cta:hover,
.cta-banner--image .ihb--cta.touch-hover {
  background: var(--white);
  background-image: none;
  border-color: transparent;
}

.cta-banner--image .ihb--cta .ihb__text,
.cta-banner--image .ihb--cta::after {
  color: var(--dark-gray);
}

@media (max-width: 1024px) {
  .cta-banner--image .cta-banner__image-full {
    height: auto;
    min-height: 440px;
  }

  /* Uniform inset — same reasoning as the ≤768px block: once the layout
     stacks (text top, button bottom), a 0 top padding pins the label to the
     edge. Equal on all four sides. */
  .cta-banner__image-content {
    padding: 24px;
    flex-direction: column;
    align-items: flex-start;
    gap: 28px;
  }

  /* Rozmiar hero (.hero__heading @≤1024px), nie rozmiar naglowka sekcji —
     patrz komentarz przy wartosci mobilnej nizej. */
  .cta-banner__image-heading {
    font-size: 44px;
  }
}

@media (max-width: 768px) {
  .cta-banner--image .cta-banner__image-full {
    min-height: 520px;
  }

  /* Uniform 20px inset: the label was pinned to the top edge (padding-top: 0)
     while the button sat 40px off the bottom. Equal insets on all four sides
     give the label the same breathing room the button has, and drop the button
     closer to the bottom edge. */
  .cta-banner__image-content {
    padding: 20px;
    gap: 24px;
  }

  /* Rozmiar HERO (.hero__heading @≤768px = 38px), a NIE naglowka sekcji (32px).
     Desktopowe 66px to dokladnie rozmiar hero, wiec mobile i tablet ida za tym
     samym wzorcem — przy 28px naglowek ginal na zdjeciu 520px wysokosci.
     SUFIT: `max-width: 11ch` daje przy 38px pudelko 278.7px, a przy 320px
     szerokosci okna dostepne jest 280px — czyli 1.3px zapasu. Podnoszac ten
     rozmiar, przemierz oba te wymiary, bo inaczej naglowek zlamie sie na 3 linie. */
  .cta-banner__image-heading {
    font-size: 38px;
  }

}

/* ---------- Salon-visit variant (replaces old dark variant) ----------
   White background, 2-column layout: left content (heading + checkmark-bullet
   list + button), right square 600×600 image. Desktop: side-by-side. Mobile:
   heading → image → list → button (via `order`). Completely overrides the
   base .cta-banner layout because this variant has a different HTML shape. */
.cta-banner--dark {
  padding: 100px 24px;
  background: #ECE8DF;
}

/* Grid matches .process__container (6fr 5fr, gap 64px, max-width-wide)
   so the image's column aligns with the process timeline width. */
.cta-banner--dark .cta-banner__container {
  max-width: var(--max-width-wide);
  margin: 0 auto;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
  display: grid;
  grid-template-columns: 6fr 5fr;
  align-items: center;
  gap: 64px;
  text-align: left;
}

.cta-banner--dark .cta-banner__content {
  display: flex;
  flex-direction: column;
}

.cta-banner--dark .trust-badge__stars {
  font-size: 18.4px;
}

.cta-banner--dark .trust-badge__avatars {
  display: inline-flex;
  align-items: center;
}

.cta-banner--dark .trust-badge__avatars img {
  width: 37.4px;
  height: 37.4px;
  border-radius: 50%;
  border: 2px solid #fff;
  object-fit: cover;
  background: #fff;
}

.cta-banner--dark .trust-badge__avatars img + img {
  margin-left: -11px;
}

.cta-banner--dark .cta-banner__heading {
  font-size: 44px;
  font-weight: var(--heading-weight);
  color: var(--black);
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 36px;
}

.cta-banner--dark .cta-banner__heading span {
  color: inherit;
}

.cta-banner--dark .cta-banner__list {
  list-style: none;
  padding: 0;
  margin: 0 0 88px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.cta-banner--dark .cta-banner__list-item {
  display: flex;
  /* `flex-start`, a NIE `baseline` — punktor jest teraz ikona (SVG), a linia
     bazowa elementu zastepowanego to jego dolna krawedz, wiec przy `baseline`
     plakietka wystawalaby ponad linie tekstu. Pionowe wysrodkowanie wzgledem
     PIERWSZEJ linii robi `margin-top` na samej ikonie (patrz nizej). */
  align-items: flex-start;
  gap: 18px;
  font-size: 17px;
  font-weight: 500;
  color: var(--dark-gray);
  line-height: 1.5;
}

/* Punktor listy: bialy ptaszek na pomaranczowej plakietce (rozeta o 8
   wybrzuszeniach). Rozmiar idzie przez `--check-size`, bo ta sama liczba
   steruje takze wyrownaniem w pionie — `1.5em` to wysokosc linii tekstu
   (`line-height: 1.5`), wiec ikona laduje dokladnie w jej srodku. */
.cta-banner--dark .cta-banner__check {
  --check-size: 22px;
  flex-shrink: 0;
  width: var(--check-size);
  height: var(--check-size);
  margin-top: calc((1.5em - var(--check-size)) / 2);
}

.cta-banner--dark .cta-banner__check svg {
  display: block;
  width: 100%;
  height: 100%;
}

.cta-banner--dark .cta-banner__check-badge {
  fill: var(--orange);
}

.cta-banner--dark .cta-banner__check-mark {
  fill: none;
  stroke: var(--white);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cta-banner--dark .cta-banner__media {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  /* Make the media a size container so the overlay badge can scale
     proportionally via `cqw` units (1 cqw = 1% of the image width). */
  container-type: inline-size;
}

/* Floating info pill over the image — mirrors .showroom__badge pattern
   but with white bg + dark-gray text and icon on the right. Float animation
   pauses when the section is off-screen (see common.js .in-view observer). */
/* All badge dimensions expressed in cqw so they scale 1:1 with the image width.
   Reference (600px image): 37px→6.17cqw, 25px→4.17cqw, 22/30→3.67/5cqw, 20→3.33cqw,
   title 20→3.33cqw, hours 28→4.67cqw, icon 67→11.17cqw, svg 33→5.5cqw. */
.cta-banner--dark .cta-banner__badge {
  position: absolute;
  bottom: 6.17cqw;
  left: -6.17cqw;
  display: flex;
  align-items: center;
  gap: 4.17cqw;
  padding: 3.67cqw 5cqw;
  background: var(--white);
  border-radius: 3.33cqw;
  z-index: 2;
  box-shadow: 0 20px 44px -14px rgba(0, 0, 0, 0.28);
  animation: cta-badge-float 3s ease-in-out infinite;
  animation-play-state: paused;
  will-change: transform;
  contain: layout style;
}

.cta-banner--dark.in-view .cta-banner__badge {
  animation-play-state: running;
}

/* `translate`, a NIE `transform` — animacja zawsze wygrywa z transition na tej
   samej wlasciwosci, wiec dopoki float siedzial w `transform`, notatka nie
   miala czym wjechac przy kaskadzie wejscia (klatka 0% aplikowalaby wartosc
   nawet przy `animation-play-state: paused`). `translate` to osobna
   wlasciwosc, wiec float i reveal wspolistnieja. Ta sama poprawka co przy
   `why-us-float` — NIE zamieniaj tego z powrotem na `transform`. */
@keyframes cta-badge-float {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -8px; }
}

.cta-banner--dark .cta-banner__badge-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.cta-banner--dark .cta-banner__badge-title {
  font-size: 3.33cqw;
  font-weight: 500;
  color: var(--dark-gray);
  line-height: 1.3;
}

.cta-banner--dark .cta-banner__badge-hours {
  font-size: 4.67cqw;
  font-weight: 700;
  color: var(--dark-gray);
  line-height: 1.2;
  letter-spacing: -0.01em;
}

.cta-banner--dark .cta-banner__badge-icon {
  flex-shrink: 0;
  order: 2;
  width: 11.17cqw;
  height: 11.17cqw;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(var(--orange-rgb), 0.12);
  border-radius: 50%;
  color: var(--orange);
}

.cta-banner--dark .cta-banner__badge-icon svg {
  width: 5.5cqw;
  height: 5.5cqw;
}

.cta-banner--dark .cta-banner__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 48px -18px rgba(0, 0, 0, 0.18);
}

.cta-banner--dark .cta-banner__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.cta-banner--dark .ihb--cta {
  font-size: 17px;
  padding: 16px 40px;
}

/* Secondary CTA inside the salon-visit banner: white bg, dark-gray text,
   simple hover — only the arrow shifts, no gradient animation. */
.cta-banner--dark .ihb--secondary {
  background: var(--white);
  background-image: none;
  border: 1.5px solid var(--border-color);
  color: var(--dark-gray);
}

.cta-banner--dark .ihb--secondary .ihb__text,
.cta-banner--dark .ihb--secondary::after {
  color: var(--dark-gray);
}

.cta-banner--dark .ihb--secondary:hover,
.cta-banner--dark .ihb--secondary.touch-hover {
  background: var(--white);
  background-image: none;
  border-color: var(--border-color);
}

/* Phone-reveal: first click slides the label out right and slides the
   phone number in from the left; a second click follows the `tel:` href. */
.cta-banner--dark .ihb--secondary .ihb__slot {
  position: relative;
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  z-index: 11;
}

.cta-banner--dark .ihb--secondary .ihb__text,
.cta-banner--dark .ihb--secondary .ihb__phone {
  display: inline-block;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s ease;
  color: var(--dark-gray);
}

.cta-banner--dark .ihb--secondary .ihb__phone {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  text-align: center;
  transform: translateX(-110%);
  opacity: 0;
  font-weight: 700;
  letter-spacing: -0.005em;
}

.cta-banner--dark .ihb--secondary.is-revealed .ihb__text {
  transform: translateX(110%);
  opacity: 0;
}

.cta-banner--dark .ihb--secondary.is-revealed .ihb__phone {
  transform: translateX(0);
  opacity: 1;
}

/* ---- Responsive ---- */
/* === Tablet (≤1024px) === */
@media (max-width: 1024px) {
  .cta-banner__heading {
    font-size: 36px;
  }

  .cta-banner__sub {
    font-size: 16px;
  }

  .cta-banner__stats {
    flex-direction: column;
    gap: 20px;
  }

  .cta-banner__stat {
    justify-content: center;
  }

  .cta-banner__stat-divider {
    width: 80px;
    height: 1px;
  }

  /* Salon-visit variant: stack columns, reorder (heading → image → list → button) */
  .cta-banner--dark {
    padding: 72px 24px;
  }

  .cta-banner--dark .cta-banner__container {
    grid-template-columns: minmax(0, 1fr);
    gap: 36px;
    justify-items: start;
  }

  .cta-banner--dark .cta-banner__content {
    display: contents;
  }

  .cta-banner--dark .cta-banner__heading {
    order: 1;
    margin-bottom: 0;
    font-size: 36px;
  }

  .cta-banner--dark .cta-banner__media {
    order: 2;
    width: 100%;
    max-width: 520px;
    height: auto;
    aspect-ratio: 1 / 1;
    justify-self: center;
  }

  .cta-banner--dark .cta-banner__list {
    order: 3;
    margin-bottom: 0;
  }

  .cta-banner--dark .cta-banner__actions {
    order: 4;
  }
}

/* === Mobile (≤768px) === */
@media (max-width: 768px) {
  .cta-banner {
    padding: 44px 16px;
  }

  .cta-banner__container {
    padding: 72px 24px 72px;
    border-radius: 13px;
  }

  .cta-banner__heading {
    font-size: 30px;
  }

  .cta-banner__sub {
    font-size: 15px;
  }

  .cta-banner__stats {
    flex-direction: column;
    gap: 20px;
  }

  .cta-banner__stat {
    justify-content: center;
  }

  .cta-banner__stat-number {
    font-size: 32px;
  }

  .cta-banner__stat-divider {
    width: 80px;
    height: 1px;
  }

  .ihb--cta {
    font-size: 16px;
    padding: 14px 36px;
  }

  /* Salon-visit variant: tighten for mobile */
  .cta-banner--dark {
    padding: 56px 16px;
  }

  .cta-banner--dark .cta-banner__container {
    padding: 0;
    border-radius: 0;
    gap: 28px;
  }

  .cta-banner--dark .cta-banner__heading {
    font-size: 30px;
    line-height: 1.2;
  }

  .cta-banner--dark .cta-banner__list {
    gap: 8px;
  }

  .cta-banner--dark .cta-banner__list-item {
    font-size: 15px;
    gap: 14px;
  }

  .cta-banner--dark .cta-banner__check {
    --check-size: 19px;
  }

  .cta-banner--dark .cta-banner__media {
    max-width: 100%;
    border-radius: var(--radius-md);
  }

  /* On mobile the image spans the full viewport width minus the 16px section
     padding, so the desktop `left: -6.17cqw` overhang pushed the badge past the
     screen's left edge. Inset it INSIDE the image instead. */
  .cta-banner--dark .cta-banner__badge {
    left: 4.17cqw;
    bottom: 4.17cqw;
    max-width: calc(100% - 8.34cqw);
  }
}

/* =============================================
   CTA BANNER --dark — KASKADA WEJSCIA
   Sekcja „Odwiedz nasz salon" (hero + 8 stron oferty „dla domu").
   Kolejnosc z zyczenia klienta:
     1. opinie (`.trust-badge`)
     2. naglowek + zdjecie (`.cta-banner__media`) RAZEM
     3. lista ze strzalkami (`.cta-banner__list`)
     4. przyciski CTA + notatka na zdjeciu (`.cta-banner__badge`) RAZEM

   Ruch przepisany 1:1 z wejscia hero na stronie glownej (0.7s ease-out,
   30px, z dolu) — tak jak why-us.css v63, services.css v37,
   showroom.css v63 i testimonial-quote.css v8. NIE wracaj tu do
   `--reveal-ease` (expo-out): upycha ~90% drogi w ~20% czasu i czyta sie
   jako szarpniecie.

   **Klasa stanu to `.is-shown`, a NIE `.is-revealed`** — ta druga jest w
   tej sekcji juz zajeta przez zupelnie inna mechanike: `.ihb--secondary.
   is-revealed` przesuwa napis „Konsultacja telefoniczna" i wysuwa numer
   telefonu (modul „CTA Phone Reveal" w common.js). Uzycie tej samej nazwy
   dzialaloby dzis przypadkiem (reveal siedzi na `__actions`, nie na samym
   przycisku), ale pierwsza proba odslonienia samego przycisku wywolalaby
   od razu animacje telefonu.

   Notatka (`__badge`) lezy WEWNATRZ `.cta-banner__media`, wiec dopoki
   zdjecie ma `opacity: 0`, jest niewidoczna niezaleznie od wlasnego stanu
   — dlatego moze miec pozniejszy trigger bez zadnych sztuczek z z-index.
   Jej float jedzie na `translate` (patrz komentarz przy keyframes).

   Trigger jest POZYCYJNY (modul „CTA Banner Dark Reveal" w common.js);
   sekcja ma ~600px wysokosci, wiec przy czasowej kaskadzie ostatnie grupy
   wypalilyby sie pod dolna krawedzia okna.
   ============================================= */
.cta-banner--dark {
  /* Strojenie tempa: zmieniaj TYLKO te wartosci — odstepy miedzy elementami
     w jednej klatce IO rozdaje eoStagger w common.js (STEP_MS tam). */
  --ctad-reveal-dur: 0.7s;
  --ctad-ease: ease-out;
  --ctad-shift: 30px;
}

/* Sekcja sama sie nie animuje — animuja sie jej dzieci. `.section-fade`
   zostaje w HTML (9 stron), ale jej fade jest tu wygaszony: (0,2,0) bije
   `.section-fade` (0,1,0) z base.css. Nie usuwaj tej reguly „bo nic nie
   robi" — bez niej cala sekcja znow wjedzie jednym blokiem POD kaskada. */
.cta-banner--dark.section-fade,
.cta-banner--dark.section-fade.visible {
  opacity: 1;
  transform: none;
  transition: none;
}

.cta-banner--dark .trust-badge,
.cta-banner--dark .cta-banner__heading,
.cta-banner--dark .cta-banner__media,
.cta-banner--dark .cta-banner__list-item,
.cta-banner--dark .cta-banner__actions,
.cta-banner--dark .cta-banner__badge {
  opacity: 0;
  transform: translateY(var(--ctad-shift));
  transition: opacity var(--ctad-reveal-dur) var(--ctad-ease),
              transform var(--ctad-reveal-dur) var(--ctad-ease);
}

/* `.trust-badge` dziedziczy z hero.css `animation: fadeInLeft … both` —
   animacja ZAWSZE wygrywa z deklaracja, wiec bez tego wiersza plakietka
   opinii jest widoczna od zaladowania strony (wypelnienie `both` trzyma
   `opacity: 1`) i cala kaskada zaczyna sie od drugiej grupy. Wejscie hero
   nie ma tu sensu — sekcja lezy daleko pod pierwszym ekranem, wiec
   animacja i tak wypalala sie niewidoczna. */
.cta-banner--dark .trust-badge {
  animation: none;
}

/* Notatka wjezdza Z LEWEJ (zyczenie klienta), reszta sekcji z dolu.
   Regula ma te sama specyficznosc (0,2,0) co zbiorczy stan ukryty wyzej,
   wiec wygrywa WYLACZNIE kolejnoscia w zrodle — nie przenos jej ponad
   tamta. Reguly `.is-shown` maja (0,3,0), wiec dalej przebijaja obie.
   Poziomy ruch nie robi przewijania w bok, bo `.cta-banner` ma
   `overflow: hidden`; notatka i tak wystaje juz poza lewa krawedz zdjecia
   (`left: -6.17cqw`), wiec wyjezdza po prostu spod tej krawedzi. */
.cta-banner--dark .cta-banner__badge {
  transform: translateX(calc(-1 * var(--ctad-shift)));
}

/* Punkty listy wlatuja Z LEWEJ, JEDEN PO DRUGIM — 1:1 z punktami sekcji
   „Proces Wspolpracy" (process.css `.process-stack__values .process__value`),
   bo klient wskazal tamte jako wzorzec. Odstepy rozklada modul „CTA Banner
   Dark Reveal" w common.js (przez eoStagger): kazdy punkt dostaje
   `.is-shown` osobno, na tej samej osi czasu co reszta kaskady.
   Ta sama specyficznosc (0,2,0) co zbiorczy stan ukryty wyzej — wygrywa
   WYLACZNIE kolejnoscia w zrodle, wiec nie przenos jej ponad tamta. */
.cta-banner--dark .cta-banner__list-item {
  transform: translateX(calc(-1 * var(--ctad-shift)));
}

/* Poziomy wjazd przycina sama lista, a nie sekcja: bez tego punkty wyjezdzalyby
   spod krawedzi CALEJ sekcji (`.cta-banner` ma `overflow: hidden`), czyli zza
   krawedzi ekranu — dluzsza droga, inny ruch niz we wzorcu.

   **Linia ciecia jest odsunieta o DOKLADNIE dystans ruchu** (`padding-left` =
   `--ctad-shift`, cofniete tym samym ujemnym `margin-left`, wiec uklad w spoczynku
   sie nie zmienia). Wczesniej klip lezal na krawedzi tekstu i punkt byl w trakcie
   wjazdu ROZKROJONY w powietrzu — zmierzone: do 30px lewej strony pozycji zniknietej,
   czyli praktycznie cala plakietka z ptaszkiem (22px na desktopie, 19px na mobile);
   klient zglosil to jako „animacje sie ucinaja". Teraz punkt w chwili startu stoi
   rowno na krawedzi klipu, wiec przez caly ruch widac go w calosci.
   Ujemny margines NIE robi poziomego przewijania: tresc wychodzaca poza LEWA krawedz
   bloku poczatkowego nie tworzy obszaru przewijania w ukladzie LTR (a tu i tak
   dodatkowo obcina ja `overflow: hidden` sekcji). Ten sam zabieg co w
   process.css `.process-stack__values`. */
.cta-banner--dark .cta-banner__list {
  overflow: hidden;
  padding-left: var(--ctad-shift);
  margin-left: calc(-1 * var(--ctad-shift));
}

.cta-banner--dark .trust-badge.is-shown,
.cta-banner--dark .cta-banner__heading.is-shown,
.cta-banner--dark .cta-banner__media.is-shown,
.cta-banner--dark .cta-banner__list-item.is-shown,
.cta-banner--dark .cta-banner__actions.is-shown,
.cta-banner--dark .cta-banner__badge.is-shown {
  opacity: 1;
  /* `none`, a nie `translateY(0)` — elementy jada z dwoch roznych kierunkow
     (notatka z lewej, reszta z dolu), wiec stan koncowy musi kasowac caly
     transform, nie tylko os Y. */
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .cta-banner--dark .trust-badge,
  .cta-banner--dark .cta-banner__heading,
  .cta-banner--dark .cta-banner__media,
  /* Punkty listy maja regule kierunkowa o tej samej specyficznosci (0,2,0),
     wiec licza sie tu wylacznie na tym, ze ten blok jest PÓZNIEJ w zrodle. */
  .cta-banner--dark .cta-banner__list-item,
  .cta-banner--dark .cta-banner__actions,
  .cta-banner--dark .cta-banner__badge {
    transform: none;
  }
}

/* =============================================
   REVEAL — wejscie banera koncowego (`--image`)
   Sekcja „Skontaktuj sie / Pozwol Nam Sobie Pomoc" — konczy KAZDA z 23 stron.
   Kolejnosc: label → naglowek → przycisk CTA.
   Kierunki: label i naglowek Z LEWEJ (`translateX(-30px)`), przycisk Z PRAWEJ
   (`+30px`) — kazde z blizszej krawedzi, bo na desktopie tresc to rzad
   wyrownany do dolu (tekst po lewej, przycisk po prawej). Zaden element nie
   startuje z ujemnym translateY (zasada klienta: nic z gory), a poziomy ruch
   nie robi przewijania w bok, bo `.cta-banner--image` ma `overflow: hidden`.

   Ruch 1:1 z wejscia hero (0.7s ease-out, 30px) — te same tokeny co why-us
   v63, services v37, showroom v64, realizacje v30, faq v31.

   KAZDY element odslania sie POZYCJA, nie czasem — czeka, az sam wjedzie
   w kadr (`.is-revealed` z modulu „CTA Banner Image Reveal" w common.js).
   W CSS NIE MA tu zadnego `transition-delay`: klient odrzucil kaskady czasowe
   wprost („kazdy element musi byc zalezny od scrolla"), patrz why-us.css v66.
   `.section-fade` tez by tu nie zadzialalo: tresc jest wyrownana do DOLU
   700-pikselowego banera, wiec prog liczony od gory sekcji odpalilby ja, gdy
   label jest jeszcze kilkaset px pod krawedzia okna.

   Stan ukryty jest zagniezdzony pod `.cta-banner--img-reveal` (klasa dodawana
   przez ten sam modul). Bramka bezpieczenstwa: nowy CSS ze starym,
   zbuforowanym common.js zostawilby cala tresc banera na `opacity: 0`
   NA ZAWSZE (zdarzylo sie realnie — patrz partners.css v32).
   ============================================= */
.cta-banner--image {
  --ctai-reveal-dur: 0.7s;
  --ctai-ease: ease-out;
  --ctai-shift: 30px;
}

.cta-banner--img-reveal .cta-banner__image-label,
.cta-banner--img-reveal .cta-banner__image-heading {
  opacity: 0;
  transform: translateX(calc(-1 * var(--ctai-shift)));
  transition: opacity var(--ctai-reveal-dur) var(--ctai-ease),
              transform var(--ctai-reveal-dur) var(--ctai-ease);
}

/* `background-position` MUSI zostac w tej liscie — `.ihb` ma na niej przejazd
   gradientu przy hoverze (navbar.css), a wlasny `transition` na przycisku
   skasowalby go. W tym wariancie tlo jest nadpisane na biale, wiec przejazd
   jest dzis niewidoczny, ale wroci, gdy tamto nadpisanie zniknie. */
.cta-banner--img-reveal .ihb--cta {
  opacity: 0;
  transform: translateX(var(--ctai-shift));
  transition: background-position 0.5s cubic-bezier(0.22, 1, 0.36, 1),
              opacity var(--ctai-reveal-dur) var(--ctai-ease),
              transform var(--ctai-reveal-dur) var(--ctai-ease);
}

/* Trigger pozycyjny nie jest tu nadmiarowa ostroznoscia takze z drugiego,
   czysto ukladowego powodu: ponizej 1024px `.cta-banner__image-content`
   przechodzi w kolumne z `justify-content: space-between` i przycisk laduje
   przy DOLNEJ krawedzi banera — przy kaskadzie czasowej odslanial sie, gdy byl
   190px (@390) i 207px (@1024) PONIZEJ krawedzi okna (zmierzone). */
.cta-banner--img-reveal .cta-banner__image-label.is-revealed,
.cta-banner--img-reveal .cta-banner__image-heading.is-revealed,
.cta-banner--img-reveal .ihb--cta.is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .cta-banner--img-reveal .cta-banner__image-label,
  .cta-banner--img-reveal .cta-banner__image-heading,
  .cta-banner--img-reveal .ihb--cta {
    transform: none;
    /* Modul moze wpisac jednorazowy stagger inline, gdy kilka elementow wpadnie
       w jednej klatce IO — przy reduced motion nie chcemy zadnego czekania. */
    transition-delay: 0s;
  }
}

/* ---- TELEFON: etykieta + naglowek wjezdzaja Z LEWEJ ----
   Wspolna regula calego serwisu (2026-08-03, base.css v142): na telefonie
   NAGLOWEK sekcji wchodzi Z BOKU, a TRESC (karty, zdjecia, listy) Z DOLU.
   Mieszanka jest zamierzona — klient poprosil, zeby ruch byl „i lewo/prawo,
   i z dolu", zamiast dotychczasowego przypadkowego rozklad kierunkow.
   Selektor jest KOPIA stanu ukrytego wyzej, wiec ma te sama specyficznosc i
   wygrywa kolejnoscia w zrodle; reguly odslaniajace maja o klase wiecej
   (`.visible` / `.is-revealed`), wiec dalej przebijaja obie.
   Warunek `no-preference` jest KONIECZNY: blok `prefers-reduced-motion` w tym
   pliku lezy WYZEJ, wiec bez niego ta regula by go przebila.
   Dystans idzie z tokenu sekcji (na telefonie 44px — base.css v142), a sekcja
   przycina poziomo, wiec wjazd nie robi przewijania w bok. */
@media (max-width: 768px) and (prefers-reduced-motion: no-preference) {
  .cta-banner--dark .cta-banner__heading {
    transform: translateX(calc(-1 * var(--ctad-shift)));
  }
}
