/* ==========================================================================
   sekcja "courses_slider" — cross-selling „Jeszcze N kursów”.
   Wdrożenie paczki projektowej (cross-sell.css): mobile = wariant 1C,
   od 1024px = wariant 2A. Wszystko scope'owane do `.xsell`.

   Kontrakt DOM: sections/courses_slider.php. Klasy zostały przy nazwach
   z paczki (`.xsell*`), żeby kolejną jej wersję dało się porównać plik
   w plik — nazwy plików idą za konwencją repo.

   TOKENY: paczka czytała zmienne `--color-*`/`--font-*` z twardym fallbackiem
   na hexy. W tym repo tokeny nazywają się `--ds-*`, więc każde odwołanie jest
   tu przepisane — inaczej cały plik działałby na fallbackach, a te są
   z poprzedniej wersji palety (m.in. `ink-muted` #6C7688, wycofany jako kolor
   tekstu w v2.1). Żaden hex nie zostaje, poza jednym udokumentowanym niżej.

   ODSTĘPSTWA OD PACZKI — decyzja: „dociągnąć projekt do reguł”
   (CLAUDE.md → „Twarde reguły wyglądu”). Każde ma komentarz przy deklaracji:
   promienie 24/14/12 (reguła 17), waga maks. 600 (8), nic w treści poniżej
   13px (10), litery w wariantach `-ink` (12), tekst na żółtym od 16px (3),
   uniesienie 1px i cienie CTA ~30% (15, 16), `prefers-reduced-motion`
   i pierścień focusu z globalnych reguł design-system.css (2).

   KONTRAST: audyt APCA przechodzi na 360 i 1440px dla każdej litery w sekcji
   (najciaśniej: etykiety na tintach i przypis, Lc 80 i 90,6 przy progach
   80 i 90). Dwa elementy NIEtekstowe zostają poniżej progu 60 i jest to
   świadome, systemowe odstępstwo, nie przeoczenie tej sekcji:
     - pasek 4px w żywym kolorze przedmiotu (mat-pod Lc 38, informatyka 47,8,
       biologia 54,1, wos 56,8, geografia 57,6) — żywa paleta przedmiotów jest
       taka w całym systemie (DESIGN-SYSTEM.md → card-course), a przedmiot
       nazywa dodatkowo tytuł karty i etykieta „komplet materiałów”,
     - gwiazdki w `cta-solar` na bieli (Lc 26,6) — atom `.stars`, ten sam
       w hero i w opiniach; wartość oceny niesie `aria-label`.
   Zmiana którejkolwiek z tych dwóch wartości to zmiana tokena, czyli osobna
   decyzja i osobny commit (CLAUDE.md → „Czego nie robić”).

   Mobile-first: baza 360px, tablet od 768px, desktop od 1024px.
   ========================================================================== */

.xsell {
  /* ── geometria karuzeli (mobile = wariant 1C) ─────────────────────────── */
  --xs-card-w: 296px;
  /* Odstęp między kartami — na życzenie podniesiony z space-3 (12px) o jeden
     krok skali, ten sam, który już obowiązywał od 768px (linia niżej), więc
     mobile dostaje więcej powietrza bez wprowadzania nowej wartości. */
  --xs-gap: var(--ds-space-4);
  /* Padding toru jest tu tylko po to, żeby cień pierwszej i ostatniej karty
     nie ucinał się na krawędzi pola przewijanego — równoważy go ujemny
     margines tej samej wielkości, więc lewa krawędź karty stoi w kolumnie
     nagłówka (i sąsiednich sekcji). Kadr następnej karty wystaje wtedy poza
     kontener, dokładnie jak w projekcie. */
  --xs-pad: var(--ds-space-3);

  /* ── etykiety kart ─────────────────────────────────────────────────────
     Cztery etykiety mają zmieścić się na telefonie w dwóch rzędach (życzenie
     z 20.08.2026). Przy 13px drugi rząd („płatność po lekcji” + „komplet
     materiałów”) potrzebuje ~309px, a karta daje 264px — nie mieści się
     nawet po wyzerowaniu paddingu, więc stopień musi zejść.

     Zejście zatrzymało się na 12px, nie 11px, bo drugi czynnik dał więcej niż
     stopień pisma: kolejność etykiet. `flex-wrap` wypełnia rzędy zachłannie,
     więc pierwotna kolejność zestawiała dwie najwęższe etykiety w rzędzie 1
     (214px z 264 dostępnych) i dwie najszersze w rzędzie 2 (269px — o 5px za
     dużo, stąd trzeci rząd). Po przełożeniu „płatność po lekcji” przed
     „30 × 120 min” (sections/courses_slider.php, tablica `$cslTags`) rzędy
     wychodzą 254 i 260px, więc mieszczą się z zapasem także w 12px.

     ODSTĘPSTWO od progu 13px (CLAUDE.md reguła 10) na warunkach reguły 11:
     przy 12px/600 (efektywnie 14px, próg 85) policzone Lc to `live` 80,1
     (niedobór 4,9), `time` 82,7 (2,3), `pay` 80,3 (4,7), `subj` 80,0–86,1
     (najgorszy niedobór 5,0) — wszystkie grubo w limicie 10 punktów zapasu.
     Etykiety nie leżą na ścieżce zakupu (cena, termin i CTA zostają
     w skali), a układ sprawdzony na 360px, nie w kalkulatorze.
     Od 768px etykiety wracają na 13px. */
  --xs-tag-font: 12px;
  --xs-tag-pad: 5px 8px;
  --xs-tag-icon: 12px;
  --xs-tag-gap: 5px;

  display: block;
  /* Tło całej sekcji: ciepły krem od krawędzi do krawędzi. Paczka miała tu
     biel i wpuszczoną w nią jasną płytę (`.xsell__slab`) — płyta została
     zdjęta na życzenie, więc kolor niesie sama sekcja, a treść zostaje
     w `.container-ms`. Pion sekcji zbiera się teraz w jednym miejscu
     (wcześniej dodawał się padding sekcji i padding płyty) i jest zgrany
     z rytmem sąsiednich sekcji landingu: course_faq ma 40/48 i 64/80. */
  background-color: var(--ds-color-canvas-warm);
  padding: 40px 0 48px;
  font-family: var(--ds-font-body);
  color: var(--ds-color-ink);
  /* Tor wychodzi poza kontener (patrz `--xs-pad`), a samo `overflow-x: auto`
     na polu przewijanym nie powstrzymuje przeglądarki od doliczenia jego
     zawartości do szerokości przewijania dokumentu — zmierzone: 6747px przy
     360px ekranu, czyli poziomy pasek na całej stronie. Wcześniej domykała to
     płyta (`overflow: hidden`), więc po jej zdjęciu clip przechodzi na sekcję.
     `clip` zamiast `hidden`, bo nie robi z sekcji kontenera przewijania. */
  overflow-x: clip;
}

/* Reset w `:where()` — zerowa specyficzność, więc klasy komponentów zawsze
   wygrywają. Bez tego `.xsell li` (0,1,1) zabijało padding `.xsell-tag` (0,1,0).
   Potrzebny, bo Bootstrap dokłada marginesy nagłówkom, akapitom i listom. */
.xsell *, .xsell *::before, .xsell *::after { box-sizing: border-box; }
.xsell :where(p, h2, h3, ul, li, s, figure) { margin: 0; padding: 0; }
.xsell :where(ul) { list-style: none; }
.xsell :where(img) { display: block; max-width: 100%; }
.xsell :where(svg) { display: block; }
/* `:not(.btn)` jest tu konieczne, nie ozdobne: `:where()` samo nie ma wagi,
   ale `.xsell` przed nim daje (0,1,0) — dokładnie tyle, ile `.btn-blue`
   w design-system.css, a ten plik ładuje się później. Bez wyłączenia
   przycisków `color: inherit` wygrywał z kolorem atomu i etykieta CTA
   wychodziła ciemna na błękicie (Lc 26,2) zamiast białej (81,4). */
.xsell :where(a:not(.btn)) { text-decoration: none; color: inherit; }
.xsell :where(button) { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
/* Pierścień focusu niesie globalna reguła z design-system.css — reguła
   z paczki (outline-offset 3px) była jej duplikatem o innym offsecie. */

.xsell__sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.xsell-u-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── nagłówek ────────────────────────────────────────────────────────────
   Bez płyty nagłówek nie potrzebuje własnego paddingu poziomego: 16px daje
   `.container-ms`, więc H2 stoi dokładnie w kolumnie nagłówków sąsiednich
   sekcji. `--xs-pad` został przy karuzeli, gdzie robi miejsce na cień kart. */

/* 13px (text-eyebrow), nie 12px z paczki — nic w treści poniżej 13px. */
.xsell__eyebrow {
  font-size: var(--ds-text-eyebrow);
  font-weight: var(--ds-weight-semi);
  line-height: 1.2;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ds-color-cta-solar-ink);
}

.xsell__title {
  margin: var(--ds-space-2) 0;
  font-family: var(--ds-font-display);
  /* text-h1 (23/31px) zamiast 24/34px z paczki — stopień ze skali systemu. */
  font-size: var(--ds-text-h1);
  font-weight: var(--ds-weight-semi);
  line-height: 1.18;
  letter-spacing: -0.02em;
  text-wrap: balance;
  color: var(--ds-color-ink);
}

/* Jedno wyróżnienie w nagłówku: niebieski marker (żółty znika na kremie).
   Sam gradient markera niesie atom `.hl-marker-blue` z design-system.css —
   tu zostaje tylko zdjęcie kursywy z <em>. */
.xsell__title em { font-style: normal; }

.xsell__lead {
  /* Poniżej 1024px `.xsell__viewport-wrap` nie ma jeszcze własnego
     margin-top (patrz niżej) — bez tego odstępu lead stykał się z karuzelą,
     gdy `.xsell__promo` (jedyny element z odstępem górnym) nie występuje. */
  margin-bottom: var(--ds-space-5);
  font-size: var(--ds-text-body-lg);
  line-height: 1.55;
  color: var(--ds-color-ink-secondary);
  text-wrap: pretty;
}

/* ── promocja ────────────────────────────────────────────────────────────
   Paczka miała tu pełny żółty pasek z cieniem i licznikiem. Żółte wróciło do
   jedynej roli, jaką ma w systemie (główne CTA), licznik został zdjęty
   (20.08.2026), więc promocja jest jednym zdaniem w kolorze treści. Zniknął
   przy tym wymóg 16px — dotyczył ciemnego tekstu na pełnym solarze
   (reguła 3) — więc wiersz idzie na `text-body` (15/16px). */
.xsell__promo {
  margin: var(--ds-space-4) 0 0;
  font-size: var(--ds-text-body);
  font-weight: var(--ds-weight-semi);
  line-height: 1.45;
  color: var(--ds-color-ink);
}

/* Przypis jest dziś wyłączony (parametr `note` bez wartości) — styl zostaje,
   bo podanie parametru ma go od razu ustawić na miejscu. */
.xsell__note {
  margin-top: 14px;
  font-size: var(--ds-text-caption);
  line-height: 1.45;
  color: var(--ds-color-ink-muted);
}

/* ── karuzela ────────────────────────────────────────────────────────────
   Tor jest klonowany przez JS (bezszwowa pętla), więc szerokość liczy się
   z zawartości. `overscroll-behavior-x: contain` pilnuje, żeby dojechanie do
   końca nie porwało całej strony ani gestu „wstecz”. */
.xsell__viewport-wrap { position: relative; }

.xsell__viewport {
  display: block;
  overflow-x: auto;
  overflow-y: hidden;
  margin-inline: calc(-1 * var(--xs-pad));
  padding: var(--ds-space-1) var(--xs-pad) var(--ds-space-2);
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  /* Pasek przewijania zabrany celowo: tor ma strzałki i podgląd następnej
     karty za krawędzią, a systemowy pasek dokładał drugą linię pod kartami. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.xsell__viewport::-webkit-scrollbar { display: none; }

/* Przeciąganie myszką (obsługa w .js). Łapka pokazuje się tylko tam, gdzie
   jest kursor — na dotyku i bez JS-a nic się nie zmienia, bo palcem tor
   przewija się natywnie. `is-dragging` gasi zaznaczanie tekstu w trakcie
   ciągnięcia; kursor przełącza się na zamkniętą dłoń. */
@media (hover: hover) {
  .xsell[data-js] .xsell__viewport { cursor: grab; }
  .xsell[data-js] .xsell__viewport.is-dragging { cursor: grabbing; user-select: none; }
}

.xsell__track { display: flex; gap: var(--xs-gap); width: max-content; }
.xsell__track > * { flex: 0 0 var(--xs-card-w); width: var(--xs-card-w); }

/* Strzałki tylko tam, gdzie jest kursor — i tylko wtedy, gdy JS wystartował. */
.xsell__nav { display: none; }

/* ── karta kursu ─────────────────────────────────────────────────────── */
.xsell-card {
  /* ── kolor przedmiotu ──────────────────────────────────────────────────
     Paczka niosła własną paletę dwunastu przedmiotów. Tu kolor wchodzi
     wspólnym mechanizmem `.subj-*` z design-system.css (CLAUDE.md → „Klasy
     CSS”: nigdy z ręki w markupie). Żywy ton i tint są w obu miejscach
     identyczne; różniły się tylko tony `-deep`, których wersja z paczki
     (np. #E6124E na tincie #FDEAF0) nie dobijała progu APCA dla liter.

     Aliasy MUSZĄ stać tutaj, nie na `.xsell`: `var(--subj)` rozwiązuje się
     w miejscu deklaracji, a `--subj` wnosi dopiero klasa `.subj-*` na karcie.
     Policzone na roocie sekcji wszystkie karty dostawały kolor marki. */
  --xs-subj:      var(--subj, var(--ds-color-primary));
  --xs-subj-deep: var(--subj-deep, var(--ds-color-primary-ink));
  --xs-subj-tint: var(--subj-tint, var(--ds-color-primary-tint));

  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: var(--ds-color-surface);
  color: var(--ds-color-ink);
  /* 14px (radius-lg), nie 28px z paczki — promień karty z systemu. */
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-md);
  transition:
    transform var(--ds-duration-base) var(--ds-ease-out-soft),
    box-shadow var(--ds-duration-base) var(--ds-ease-out-soft);
}

/* Uniesienie 1px z systemu, nie 3px z paczki: przy 3px siatka kart faluje pod
   kursorem, a na dotyku i tak nikt tego nie zobaczy. */
@media (hover: hover) {
  .xsell-card:hover { transform: var(--ds-hover-lift); box-shadow: var(--ds-shadow-lg); }
}

.xsell-card__bar { display: block; height: 4px; background-color: var(--xs-subj); }

.xsell-card__thumb {
  display: block;
  position: relative;
  aspect-ratio: 16 / 9;
  background-color: var(--ds-color-canvas-soft);
}

.xsell-card__img { width: 100%; height: 100%; object-fit: cover; }

/* Szkicowy placeholder niesie atom `.ph` z design-system.css (ten sam raster
   i ten sam stopień, co w pozostałych sekcjach) — tu zostaje samo ułożenie
   w kadrze. Do podmiany na <img class="xsell-card__img">. */
.xsell-card__ph { position: absolute; inset: 0; }

.xsell-card__body {
  display: flex;
  flex-direction: column;
  gap: 7px;
  flex: 1;
  padding: var(--ds-space-4);
}

/* Kolor i odstęp gwiazdek niesie atom `.stars`; projekt chce ich o 1px
   większych niż domyślne 15px z systemu. */
.xsell-card__star { width: 16px; height: 16px; }

.xsell-card__title {
  font-family: var(--ds-font-display);
  /* text-h3 (17/18px) — stopień z paczki na mobile, ze skali na desktopie. */
  font-size: var(--ds-text-h3);
  font-weight: var(--ds-weight-semi);
  line-height: 1.4;
  letter-spacing: -0.005em;
  text-wrap: balance;
}

.xsell-card__title a { transition: color var(--ds-duration-base) var(--ds-ease-out-soft); }
.xsell-card__title a:hover { color: var(--ds-color-primary-ink); }

/* ── etykiety ────────────────────────────────────────────────────────────
   Jeden kolor dla wszystkich: beż `canvas-soft` z tekstem `ink-secondary`
   (Lc 89 przy progu 85 dla 12px/600 i 80 dla 13px/600). Paczka miała tu pięć
   przydziałów semantycznych — czerwony `live`, niebieski `time`, bursztynowy
   `start`, zielony `pay` i kolor przedmiotu `subj` — zdjęte na życzenie
   (20.08.2026). Klasy `.xsell-tag--*` lecą dalej z PHP jako zaczep, więc
   przywrócenie kolorów to dopisanie czterech reguł:
     --live  { background: error-tint;   color: error-ink }
     --time  { background: primary-tint; color: primary-ink }
     --pay   { background: success-tint; color: success-ink }
     --subj  { background: var(--xs-subj-tint); color: var(--xs-subj-deep) }
   Zdaniowe, nie wersaliki — dlatego świadomie NIE korzystają z `.badge-*`
   z design-system.css, które nadaje `text-transform: uppercase`. */
.xsell-card__tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0; }

.xsell-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--xs-tag-gap);
  padding: var(--xs-tag-pad);
  border-radius: var(--ds-radius-full);
  background-color: var(--ds-color-canvas-soft);
  color: var(--ds-color-ink-secondary);
  font-size: var(--xs-tag-font);
  font-weight: var(--ds-weight-semi);
  line-height: 1.2;
  white-space: nowrap;
}

.xsell-tag__icon { width: var(--xs-tag-icon); height: var(--xs-tag-icon); flex: none; }

/* Kropka „na żywo” zostaje czerwona: po zrównaniu etykiet do beżu to jedyny
   sygnał w wierszu, a kropka nie jest literą, więc bierze żywy kolor
   (CLAUDE.md, reguła 12). Lc 64,6 na beżu przy progu 60 dla elementów
   nietekstowych. Puls animuje wyłącznie transform i opacity. */
.xsell-tag__dot {
  position: relative;
  width: 6px; height: 6px; flex: none;
  border-radius: var(--ds-radius-full);
  background-color: var(--ds-color-error);
}
.xsell-tag__dot::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  background-color: inherit;
  animation: xsell-pulse 1.8s var(--ds-ease-out-soft) infinite;
}
@keyframes xsell-pulse {
  0%   { transform: scale(1);   opacity: 1; }
  70%  { transform: scale(1.9); opacity: 0; }
  100% { opacity: 0; }
}

/* Opis karty to tekst wspierający — 14px (body-sm), nie 13px z paczki. */
.xsell-card__desc {
  font-size: var(--ds-text-body-sm);
  line-height: 1.45;
  color: var(--ds-color-ink);
  text-wrap: pretty;
}

.xsell-card__teacher { display: flex; align-items: center; gap: 9px; margin-top: 2px; }

.xsell-card__avatar,
.xsell-card__avatar-img { width: 34px; height: 34px; flex: none; border-radius: var(--ds-radius-full); }

.xsell-card__avatar {
  display: grid;
  place-items: center;
  background-color: var(--xs-subj-tint);
  color: var(--xs-subj-deep);
  font-family: var(--ds-font-display);
  /* 13px, nie 12px z paczki. Inicjały są wprawdzie „elementem bez słów”,
     czyli wyjątkiem od progu 13px (CLAUDE.md, reguła 10), ale przy 12px/600
     `subj-*-deep` na własnym tincie daje Lc 80 przy wymaganych 85 — o jeden
     stopień wyżej ten sam kolor wymaga już tylko 80 i przechodzi. Waga
     ścięta z 700 do 600. */
  font-size: var(--ds-text-label);
  font-weight: var(--ds-weight-semi);
  box-shadow: inset 0 0 0 1px rgba(19, 26, 43, 0.08);
}

.xsell-card__avatar-img { object-fit: cover; box-shadow: inset 0 0 0 1px rgba(19, 26, 43, 0.08); }

.xsell-card__teacher-name {
  font-size: var(--ds-text-label);
  font-weight: var(--ds-weight-semi);
  line-height: 1.45;
  color: var(--ds-color-ink-secondary);
}

.xsell-card__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ds-space-3);
  margin-top: auto;
  padding-top: var(--ds-space-3);
  border-top: 1px solid var(--ds-color-hairline);
}

.xsell-card__prices { display: flex; flex-direction: column; }

/* Przekreślona cena to treść, więc `ink-muted`, nigdy `ink-faint`
   (CLAUDE.md, reguła 13). */
.xsell-card__price-old {
  font-size: var(--ds-text-caption);
  line-height: 1.45;
  color: var(--ds-color-ink-muted);
}

.xsell-card__price {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-h2);
  font-weight: var(--ds-weight-semi);
  line-height: 1.15;
  letter-spacing: -0.02em;
  /* Ta sama korekta i ta sama wartość, co przy `.csb-price` w sticky barze
     i `.cs-promo-new` w course_summary: pełny --ds-color-success-ink (#085E41)
     na tym stopniu i wadze wychodzi na oko za ciemno, bliżej czerni niż
     zieleni. Lżejszy ton, wciąż w rodzinie „ink” (żywy --ds-color-success
     zostaje do wypełnień — reguła 12): Lc ~74 na bieli, przy progu 60 dla
     19px/600. Trzecie wystąpienie tej wartości w repo — zasługuje na token
     w osobnym commicie (CLAUDE.md → „Czego nie robić”: nie zmieniamy tokenów
     przy okazji innej pracy). */
  color: #0C8058;
  font-variant-numeric: tabular-nums lining-nums;
}

/* CTA karty niesie w całości atom `.btn.btn-blue.btn-sm` z design-system.css
   (biel na błękicie marki — Lc 81,4 przy progu 70 dla 15px/600 — 44px,
   promień 12px, cień ~30% + wewnętrzne światło, stany hover/active).
   Tu zostaje tylko zakaz kurczenia się w stopce karty. */
.xsell-card__cta { flex: none; }

/* Po terminie promocji JS dodaje `.xsell--expired`. */
.xsell--expired .xsell__promo,
.xsell--expired .xsell__note,
.xsell--expired .xsell-card__price-old { display: none; }
.xsell--expired .xsell-card__price { color: var(--ds-color-ink); }

/* ── tablet ──────────────────────────────────────────────────────────── */
@media (min-width: 768px) {
  .xsell {
    --xs-card-w: 330px;
    --xs-gap: var(--ds-space-4);
    /* Miejsca w karcie jest już dość — etykiety wracają na próg 13px. */
    --xs-tag-font: var(--ds-text-label);
    --xs-tag-pad: 5px 10px;
    --xs-tag-icon: 14px;
    --xs-tag-gap: 6px;
    padding: 56px 0 64px;
  }
}

/* ── desktop = wariant 2A ────────────────────────────────────────────── */
@media (min-width: 1024px) {
  .xsell {
    --xs-card-w: 360px;
    --xs-gap: var(--ds-space-5);
    padding: 64px 0 80px;
  }

  .xsell__head {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 1200px;
    margin: 0 auto;
  }

  .xsell__title { margin: 10px 0; max-width: 720px; }
  /* margin-bottom wraca do 0: od tej szerokości odstęp przed karuzelą już
     daje `.xsell__viewport-wrap` (niżej) — dublowałby się z nim. */
  .xsell__lead { margin-bottom: 0; max-width: 640px; line-height: 1.6; }
  .xsell__promo { margin: 22px 0 0; }

  .xsell__viewport-wrap { margin-top: var(--ds-space-8); }
  .xsell__viewport { padding-bottom: 10px; }

  .xsell-card__body { gap: var(--ds-space-2); padding: 18px; }
  .xsell-card__title { line-height: 1.32; letter-spacing: -0.01em; }
  /* Opis rośnie ze skalą: body (15/16px) zamiast 15px wpisanego w paczce. */
  .xsell-card__desc { font-size: var(--ds-text-body); line-height: 1.55; }
  .xsell-card__avatar, .xsell-card__avatar-img { width: 36px; height: 36px; }
  /* Projekt chce CTA 48px od 1024px — `.btn-sm` daje 44px, więc podnosimy je
     do wartości bazowej przycisku z systemu. */
  .xsell-card__cta { min-height: 48px; padding: 12px 22px; font-size: var(--ds-text-btn); }

  .xsell__note { text-align: center; }

  /* Strzałki: pokazujemy je dopiero, gdy skrypt wystartował — bez JS nic by
     nie robiły. Promień 12px z systemu, nie 12px z paczki (tu się zgadzały). */
  .xsell[data-js] .xsell__nav {
    position: absolute;
    /* Wyśrodkowane na miniaturze, nie na wysokości karty. W paczce strzałki
       stały w 40-pikselowej rynnie płyty i nie zachodziły na nic; po zdjęciu
       płyty karty zaczynają się przy krawędzi kontenera, więc `top: 44%`
       wypadało na tytule i etykietach. Kadr 16:9 ma znaną wysokość
       (szerokość karty × 9/16), więc jego środek liczy się bez JS-a:
       4px paddingu pola + 4px paska przedmiotu + połowa kadru − połowa
       przycisku. Zachodzenie na sam kadr jest w karuzelach normą. */
    top: calc(8px + var(--xs-card-w) * 9 / 32 - 22px);
    z-index: 2;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: var(--ds-radius-md);
    background-color: var(--ds-color-surface);
    color: var(--ds-color-primary-ink);
    box-shadow: var(--ds-shadow-md);
    transition:
      transform var(--ds-duration-base) var(--ds-ease-out-soft),
      box-shadow var(--ds-duration-base) var(--ds-ease-out-soft);
  }

  .xsell__nav svg { width: 22px; height: 22px; }
  .xsell__nav:hover { transform: var(--ds-hover-lift); box-shadow: var(--ds-shadow-lg); }
  .xsell__nav:active { transform: scale(var(--ds-press-scale)); }

  /* Chevron ze sprite'u jest skierowany w lewo — „dalej” obracamy o 180°. */
  .xsell__nav--prev { left: 4px; }
  .xsell__nav--next { right: 4px; }
  .xsell__nav--next svg { transform: rotate(180deg); }
}

/* `prefers-reduced-motion` jest obsłużone globalnie w design-system.css
   (ścina animacje, przejścia i scroll-behavior) — blok z paczki byłby
   duplikatem. Autoprzewijanie wyłącza w tym trybie sam skrypt. */
