/* ──────────────────────────────────────────────────────────────────────────
   sekcja "course_reviews" — opinie kursantów na landingu kursu.
   Zależności: assets/css/design-system.css (--ds-*, .container-ms, .text-*,
   .badge/.subj-badge, .stars, mechanizm .subj-*) — kontrakt DOM
   w sections/course_reviews.php.

   TYPOGRAFIA: rozmiary niosą klasy design systemu nadane w markupie
   (.text-heading-1, .text-body-lg, .text-body-sm, .text-caption, .text-label,
   .text-numeral-xl).
   Font-size ustawiamy tu tylko tam, gdzie projekt sekcji świadomie schodzi
   z siatki DS (inicjały w awatarze) — z komentarzem przy każdym miejscu.

   KOLOR PRZEDMIOTU: klasa .subj-* siedzi na karcie (pierwszy przedmiot opinii)
   i ustawia --subj-tint/--subj-deep (design-system.css). Awatar czyta te zmienne
   z fallbackiem na kolor marki, więc opinia bez przypisanego przedmiotu też
   wygląda kompletnie. Kolor przedmiotu jest tu wyłącznie akcentem na awatarze —
   nigdy na przycisku ani na ocenie.

   Mobile-first: baza 360px, desktop od 1024px.
   ────────────────────────────────────────────────────────────────────────── */

/* Tło pełnej szerokości — sekcja nie stoi na płycie .slab, więc ciepły kanwas
   idzie na sam <section>, a treść trzyma domyślny kontener strony .container-ms.
   Włosowe krawędzie u góry i u dołu odcinają ciepłe tło od białych sąsiadów,
   bez promienia i bez cienia (płyta miała je od .slab). */
.crv {
  background-color: var(--ds-color-canvas-warm);
  border-block: 1px solid var(--ds-color-hairline);
  padding: 34px 0 30px;
}

/* ── Nagłówek ──────────────────────────────────────────────────────────── */
/* Płaska lista elementów w DOM (H2 → blok oceny → lead) — na mobile to jest
   docelowa kolejność, na desktopie ten sam DOM układa się w dwie kolumny
   przez grid-template-areas. Bez wrappera, bez duplikowania treści.
   Na mobile cały nagłówek jest wyśrodkowany (tytuł, blok oceny, lead); od 1024px
   wraca do wyrównania do lewej, bo tam nagłówek dzieli wiersz z blokiem oceny. */
.crv-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  text-align: center;
}

/* Sztywne złamanie H2 na wszystkich szerokościach: nagłówek ma stać w dwóch
   linijkach — „Zobacz, co kursanci mówią” / „o naszych kursach” — i to złamanie
   niesie <br>, nie przypadkowa szerokość kolumny. */
.crv-title-break { display: inline; }

.crv-title {
  margin: 0 0 var(--ds-space-3);
  color: var(--ds-color-ink);
  text-wrap: pretty;
}

/* Jedyne wyróżnienie w tym nagłówku. DS ma .hl-gradient/.hl-marker/.hl-color,
   ale żadne z nich nie daje płaskiego błękitu marki, którego chce projekt
   sekcji — a nagłówek sąsiaduje z blokiem oceny i wersja gradientowa robiła
   w tym kadrze drugi punkt zaczepienia dla oka. */
.crv-mark { color: var(--ds-color-primary); }

.crv-lead {
  margin: 0;
  color: var(--ds-color-ink-secondary);
  text-wrap: pretty;
}

/* ── Blok oceny (puchar + średnia) ─────────────────────────────────────── */
/* Na mobile leży poziomo między H2 a leadem: puchar po lewej, liczba i dopisek
   w kolumnie obok. Bez chromu karty — sama liczba jest już mocnym akcentem,
   a ramka robiłaby z niej drugą kartę w kadrze pełnym kart. */
.crv-score {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
  text-align: left;
}

.crv-score-ic {
  flex: none;
  width: 46px;
  height: 46px;
  color: var(--ds-color-cta-solar);
}

.crv-score-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* .text-numeral-xl (32/44px) wnosi stopień, display font i tabularne cyfry. */
.crv-score-val { color: var(--ds-color-ink); }
.crv-score-note { color: var(--ds-color-ink-secondary); }

/* ── Siatka kart ───────────────────────────────────────────────────────── */
.crv-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  margin-top: var(--ds-space-4);
}

.crv-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--ds-space-4);
  background-color: var(--ds-color-canvas);
  border: 1px solid var(--ds-color-hairline);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-xs);
}

/* Karty nie są klikalne — świadomie bez hovera (hover zostaje dla elementów,
   które faktycznie coś robią po kliknięciu). Focus tylko programistyczny:
   po rozwinięciu listy JS przenosi tu focus, żeby nie zgubił się razem
   ze znikającym przyciskiem. */
.crv-card:focus { outline: none; }
.crv-card:focus-visible { outline: 2px solid var(--ds-color-primary); outline-offset: 3px; }

/* Limit widocznych kart. Ukrywa CSS, nie JS — skrypt tylko oznacza root
   atrybutem `data-js`. Bez JS (albo gdy skrypt padnie) widać wszystkie opinie
   i nie ma martwego przycisku. */
.crv[data-js]:not(.is-expanded) .crv-card[data-crv-hide-m] { display: none; }

/* Awatar po lewej, kolumna z imieniem i plakietką obok niego, ocena 5/5 przy
   prawej krawędzi karty. Wyrównanie do góry, nie do środka: awatar ma stać
   naprzeciw obu linii kolumny (imię + plakietka), a nie zjeżdżać między nie. */
.crv-who {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.crv-avatar {
  flex: none;
  /* Wysokość awatara ≈ wysokość kolumny obok (imię 18px + 5px + plakietka 22px),
     żeby wiersz miał jeden blok po lewej, a nie kółko wielkości jednej linii. */
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--ds-radius-full);
  /* Kolor przedmiotu z .subj-* na karcie; brak przedmiotu → neutralny błękit marki. */
  background-color: var(--subj-tint, var(--ds-color-primary-tint));
  color: var(--subj-deep, var(--ds-color-primary-ink));
  /* Wewnętrzny pierścień — tinty przedmiotów są bardzo jasne i bez niego
     awatar rozpływa się na białej karcie. */
  box-shadow: inset 0 0 0 1px rgba(19, 26, 43, 0.07);
  font-family: var(--ds-font-display);
  font-weight: 600;
  /* Poza skalą DS: inicjały mają wypełnić 36px koło, nie być czytane jak tekst. */
  font-size: 15px;
  line-height: 1;
}

.crv-who-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.crv-name {
  font-weight: 600;
  line-height: 1.3;
  color: var(--ds-color-ink);
}

/* ── Plakietka weryfikacji ─────────────────────────────────────────────── */
.crv-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-top: 5px;
}

/* Kolory z .badge-success (success-ink na success-tint). Lokalnie kształt
   pigułki, zdjęte wersaliki z trackingiem 0.09em (wersja zdaniowa jest o ~30%
   węższa i nie wypycha oceny 5/5) oraz stopień 12px.
   ODSTĘPSTWO (decyzja Tomka): 12px schodzi pod 13px z CLAUDE.md, reguła 10.
   Przy 12px/600 próg to Lc ~93, a para success-ink/success-tint daje Lc 80,3 —
   plakietka jest więc świadomie poniżej progu. Zapas jest tylko w tym, że nie
   niesie informacji potrzebnej do zakupu (nie ma jej na ścieżce: cena, termin,
   formularz, CTA) i powtarza to, co mówi lead sekcji. */
.crv-tag {
  flex: none;
  padding: 5px 10px 5px 9px;
  border-radius: var(--ds-radius-full);
  font-family: var(--ds-font-body);
  font-weight: 600;
  /* Poza skalą DS (najniższy stopień to --ds-text-label 13px) — patrz komentarz
     o odstępstwie wyżej. */
  font-size: 12px;
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
}

.crv-tag-ver {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.crv-tag-ver svg { width: 12px; height: 12px; flex: none; }

/* Ocena karty — dosunięta do prawej krawędzi, wyrównana do linii imienia.
   Stopień z .text-heading-1 (23/31px), kolor to pełny Solar.
   ODSTĘPSTWO (decyzja Tomka): #FFC42E na białej karcie daje Lc 26,6, czyli
   znacznie poniżej progu ~60 dla tego stopnia (CLAUDE.md, reguła 11 dopuszcza
   maks. 10 punktów zapasu). Liczba jest ozdobnym powtórzeniem oceny, którą
   niosą gwiazdki niżej (mają aria-label) i średnia 5,0 w nagłówku sekcji, więc
   jej nieprzeczytanie nie gubi treści — ale przy silnym słońcu na telefonie
   realnie nie będzie czytelna. Czytelny wariant tego złota: `#A87700`
   (Lc 66,7). */
.crv-rate {
  margin-left: auto;
  padding-left: 6px;
  flex: none;
  color: var(--ds-color-cta-solar);
  font-variant-numeric: tabular-nums lining-nums;
  /* Krótki cień w tonie Solar — ten sam zabieg co pod gwiazdkami; nie jest
     poświatą (krycie 0.42, rozmycie 3px), a odcina glif od białego tła. */
  filter: drop-shadow(0 1px 2px rgba(224, 162, 0, 0.42));
}

/* Gwiazdki — .stars z DS wnosi układ, rozmiar i kolor Solar. Tu dochodzi
   ciepły cień i wejście „pop”. */
.crv-stars {
  margin-top: 14px;
  /* Cień w tonie Solar (ten sam ton co --ds-shadow-cta-solar), ale jako
     drop-shadow po kształcie glifu — box-shadow obrysowałby prostokąt. */
  filter: drop-shadow(0 2px 3px rgba(224, 162, 0, 0.42));
}

/* Animacja startuje dopiero, gdy karta wejdzie w kadr (JS dokłada .is-seen).
   Stan wyjściowy chowamy tylko przy .is-anim, czyli gdy JS naprawdę pilnuje
   ujawnienia — bez tego gwiazdki zostałyby niewidzialne przy padniętym
   skrypcie albo braku IntersectionObserver. */
.crv.is-anim .crv-card:not(.is-seen) .crv-stars svg { opacity: 0; }

.crv.is-anim .crv-card.is-seen .crv-stars svg {
  animation: crv-star-pop var(--ds-duration-slow) var(--ds-ease-out-soft) both;
}
.crv.is-anim .crv-card.is-seen .crv-stars svg:nth-child(1) { animation-delay:  60ms; }
.crv.is-anim .crv-card.is-seen .crv-stars svg:nth-child(2) { animation-delay: 130ms; }
.crv.is-anim .crv-card.is-seen .crv-stars svg:nth-child(3) { animation-delay: 200ms; }
.crv.is-anim .crv-card.is-seen .crv-stars svg:nth-child(4) { animation-delay: 270ms; }
.crv.is-anim .crv-card.is-seen .crv-stars svg:nth-child(5) { animation-delay: 340ms; }

/* Tylko transform + opacity — zero layoutu w animacji. */
@keyframes crv-star-pop {
  from { opacity: 0; transform: scale(0.55) rotate(-14deg); }
  to   { opacity: 1; transform: none; }
}

.crv-quote {
  margin: 10px 0 0;
  color: var(--ds-color-ink-secondary);
  text-wrap: pretty;
}

/* ── Przycisk „pokaż pozostałe” ────────────────────────────────────────── */
/* Bez JS przycisk nie ma czego odsłaniać (wszystkie opinie są już widoczne),
   więc pokazuje się dopiero z `data-js`. */
.crv-morewrap { display: none; }
.crv[data-js] .crv-morewrap { display: block; margin-top: var(--ds-space-4); }

.crv-more { width: 100%; }
.crv-more[hidden] { display: none; }
.crv-more:hover { transform: var(--ds-hover-lift); box-shadow: var(--ds-shadow-md); }
.crv-more:active { transform: scale(var(--ds-press-scale)); box-shadow: var(--ds-shadow-xs); }
.crv-more:focus-visible { outline: 2px solid var(--ds-color-primary); outline-offset: 3px; }

/* ── Desktop 1024px+ : nagłówek dwukolumnowy, siatka 3 × 3 ─────────────── */
@media (min-width: 1024px) {
  .crv { padding: 56px 0 52px; }

  .crv-header {
    text-align: left;
    grid-template-columns: minmax(0, 620px) auto;
    grid-template-areas:
      "title score"
      "lead  score";
    justify-content: space-between;
    column-gap: var(--ds-space-10);
  }
  .crv-title { grid-area: title; margin: 0 0 10px; }
  .crv-lead  { grid-area: lead; }

  /* Blok oceny staje się pionowy i dosunięty do dołu kolumny tekstu — linia
     bazowa leadu i dopisku pod średnią jest tym, co spina obie kolumny.
     Wyrównanie do prawej krawędzi kontenera, nie do środka: kolumna ma być
     drugim akcentem kadru, nie jego osią. */
  .crv-score {
    grid-area: score;
    align-self: end;
    justify-self: end;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    margin: 0;
    text-align: right;
  }
  .crv-score-ic { width: 82px; height: 82px; stroke-width: 1.4; }
  .crv-score-text { align-items: flex-end; }
  .crv-score-val { line-height: 0.95; }

  .crv-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--ds-space-5);
    margin-top: var(--ds-space-8);
  }

  /* Kolejność desktopowa: od najdłuższej wypowiedzi do najkrótszej, żeby wiersze
     miały zbliżoną wysokość (karta rozciąga się do najwyższej w wierszu).
     Ranking liczy PHP i wpisuje klasą crv-o-*, więc DOM (i kolejność czytana
     przez czytnik ekranu) zostaje ta sama na obu breakpointach — bez re-sortu
     w JS, czyli bez przeskoku layoutu przy zmianie szerokości okna.
     Karta bez rangi (gdyby opinii było więcej niż rang poniżej) ląduje na końcu. */
  .crv-card { order: 99; padding: var(--ds-space-5); }
  .crv-o-1  { order:  1; } .crv-o-2  { order:  2; } .crv-o-3  { order:  3; }
  .crv-o-4  { order:  4; } .crv-o-5  { order:  5; } .crv-o-6  { order:  6; }
  .crv-o-7  { order:  7; } .crv-o-8  { order:  8; } .crv-o-9  { order:  9; }
  .crv-o-10 { order: 10; } .crv-o-11 { order: 11; } .crv-o-12 { order: 12; }
  .crv-o-13 { order: 13; } .crv-o-14 { order: 14; } .crv-o-15 { order: 15; }
  .crv-o-16 { order: 16; } .crv-o-17 { order: 17; } .crv-o-18 { order: 18; }
  .crv-o-19 { order: 19; } .crv-o-20 { order: 20; } .crv-o-21 { order: 21; }
  .crv-o-22 { order: 22; } .crv-o-23 { order: 23; } .crv-o-24 { order: 24; }

  /* Inny limit widocznych kart niż na mobile: liczy się ranga, nie pozycja
     w źródle, więc reguła mobilna musi zostać tu cofnięta. */
  .crv[data-js]:not(.is-expanded) .crv-card[data-crv-hide-m] { display: flex; }
  .crv[data-js]:not(.is-expanded) .crv-card[data-crv-hide-d] { display: none; }

  .crv-avatar { width: 46px; height: 46px; font-size: 15.5px; }
  .crv-stars { margin-top: 14px; }

  .crv[data-js] .crv-morewrap { display: flex; justify-content: center; margin-top: 28px; }
  .crv-more { width: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .crv.is-anim .crv-card .crv-stars svg { opacity: 1; animation: none; }
  .crv-more,
  .crv-more:hover,
  .crv-more:active { transition: none; transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   BLOK WIDEO — opinie absolwentów nad kartami tekstowymi.
   Wdrożenie paczki „design_handoff_opinie_wideo" w tej sekcji (jeden nagłówek
   dla całości, patrz komentarz w sections/course_reviews.php).

   TYPOGRAFIA: stopnie niosą klasy DS z markupu (.text-heading-3 na imieniu,
   .text-body-sm na kursie, .text-caption na edycji i liczniku). Font-size
   ustawiamy tu w jednym miejscu (etykieta HUD) — z uzasadnieniem przy niej.

   Mobile-first: baza 360px (karuzela z pętlą), desktop od 1024px
   (trzy kolumny na pełną szerokość .container-ms).
   ══════════════════════════════════════════════════════════════════════════ */

.crv-vid {
  margin-top: var(--ds-space-6);
  padding-bottom: var(--ds-space-6);
  /* Włosowa linia oddziela dowód wideo od dowodu tekstowego — w jednej sekcji
     nie ma drugiego tła ani drugiego nagłówka, więc granicę niesie ta krawędź. */
  border-bottom: 1px solid var(--ds-color-hairline);
}

/* ── Tor karuzeli (mobile) ────────────────────────────────────────────────
   Natywny scroll + scroll-snap; pętlę robi JS klonami i przeskokiem scrollLeft
   (patrz assets/js/course_reviews.js) — bez animowania czegokolwiek w JS.
   Tor wychodzi na pełną szerokość okna (ujemny margines znosi padding
   .container-ms), a padding 64px pozwala pierwszemu i ostatniemu kadrowi
   zatrzymać się na środku. Przy 390px widać ~52px następnego telefonu. */
.crv-vid-rail {
  display: flex;
  gap: var(--ds-space-3);
  margin-inline: calc(var(--ds-space-4) * -1);
  padding: 26px 64px 8px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.crv-vid-rail::-webkit-scrollbar { display: none; }

/* Przeciąganie myszą (JS: pointerdown/move na torze). Łapka pokazuje się tylko
   tam, gdzie kursor istnieje I gdzie jest co ciągnąć — na dotyku tor przewija
   się palcem natywnie, a od 1024px nie ma karuzeli, tylko siatka trzech kolumn.
   `is-dragging` dokłada JS na czas gestu: blokuje zaznaczanie tekstu
   i podmienia kursor na zaciśniętą łapkę. */
@media (hover: hover) and (pointer: fine) and (max-width: 1023.98px) {
  .crv-vid-rail { cursor: grab; }
  .crv-vid-rail.is-dragging { cursor: grabbing; }
}
.crv-vid-rail.is-dragging { user-select: none; }
/* Poster w <video> jest obrazem — bez tego mysz „odrywa" go od kadru
   i zamiast przewijania rusza duch obrazka. */
.crv-video { -webkit-user-drag: none; user-select: none; }

/* Szerokość kadru płynna, nie sztywne 262px z paczki: na 360px zostaje wtedy
   widoczny podgląd następnego telefonu, a na 430px kadr rośnie razem z ekranem. */
.crv-vid-item {
  flex: none;
  width: min(262px, 72vw);
  margin: 0;
  scroll-snap-align: center;
}

/* ── Mockup telefonu ──────────────────────────────────────────────────────
   Świadomie nieforealistyczny: obudowa, głośniczek, nic więcej. Promienie
   40px (obudowa) i 32px (ekran) są powyżej sufitu skali DS (24px) — to kształt
   przedmiotu, nie chrom komponentu, więc wartość jest lokalna i celowa.
   Wypukłość niesie wewnętrzne światło z systemu, nie kolorowa poświata. */
.crv-phone {
  position: relative;
  padding: 8px;
  border-radius: 40px;
  background-color: var(--ds-color-ink);
  box-shadow: var(--ds-shadow-lg), inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}
.crv-phone-speaker {
  position: absolute;
  left: 50%;
  top: 15px;
  z-index: 2;
  transform: translateX(-50%);
  width: 54px;
  height: 4px;
  border-radius: var(--ds-radius-full);
  background-color: rgba(255, 255, 255, 0.22);
}

/* Kadr 9:16 — proporcja materiału (1080×1920). aspect-ratio zamiast wysokości
   w px: brak przeskoku layoutu przy wczytywaniu postera (CLS). */
.crv-screen {
  position: relative;
  aspect-ratio: 9 / 16;
  border-radius: 32px;
  overflow: hidden;
  background-color: var(--ds-color-primary-deep);
  cursor: pointer;
}
.crv-video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Scrim: przygaszenie góry i dołu kadru, żeby biały krążek i HUD miały na czym
   stać niezależnie od tego, co jest na nagraniu. Ink w rgba, bo to przezrocze
   nad wideo — nie ma tokenu na półprzezroczysty ink. */
.crv-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(19, 26, 43, 0.30) 0%,
    rgba(19, 26, 43, 0) 28%,
    rgba(19, 26, 43, 0) 62%,
    rgba(19, 26, 43, 0.62) 100%);
  transition: opacity var(--ds-duration-base) var(--ds-ease-out-soft);
}

/* ── Play ─────────────────────────────────────────────────────────────────
   Sam glif, bez białego krążka pod spodem: krążek 76px zasłaniał twarz
   prowadzącego, a to ona jest tu treścią. Pole dotykowe zostaje 76px
   (CLAUDE.md, reguła 15) — jest po prostu przezroczyste.
   Trójkąt jest biały i przygaszony do 0.72, żeby leżał NA kadrze, a nie
   przed nim. Czytelność na jasnej klatce (biała ściana za Emilią, okno
   za Pawłem) niesie krótki cień pod glifem — to nie poświata z reguły 16,
   tylko krawędź: 2px rozmycia, 45% krycia, bez koloru marki.
   Glif stoi na 64% wysokości kadru, nie w geometrycznym środku: wszystkie
   trzy nagrania mają twarz w górnej połowie (tego wymaga brief kadru), więc
   środek to dokładnie usta i broda. Na 64% glif siedzi na torsie i wchodzi
   w górę przyciemnienia scrima, co dodatkowo mu pomaga. */
.crv-play {
  position: absolute;
  left: 50%;
  top: 64%;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 76px;
  height: 76px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ds-color-on-primary);
  opacity: 0.72;
  filter: drop-shadow(0 1px 2px rgba(19, 26, 43, 0.45));
  cursor: pointer;
  /* Tylko transform i opacity — bez animowania wymiarów
     (CLAUDE.md → Stabilność, pkt 2). */
  transition: transform var(--ds-duration-base) var(--ds-ease-out-soft),
              opacity var(--ds-duration-base) var(--ds-ease-out-soft);
}
/* Bez krążka trójkąt musi być większy, żeby czytał się jak kontrolka, a nie
   jak przypadkowy artefakt na klatce — 30px z paczki działało tylko dlatego,
   że siedziało w białym kole. Margines to optyczne wyśrodkowanie: geometryczny
   środek trójkąta wygląda za lewo. */
.crv-play svg { width: 42px; height: 42px; margin-left: 4px; }
.crv-play:active { transform: translate(-50%, -50%) scale(0.97); opacity: 0.9; }

/* ── HUD odtwarzania ──────────────────────────────────────────────────────
   Pas przy dolnej krawędzi kadru: etykieta stanu, ikona pauzy i postęp.
   pointer-events: none — klik przechodzi do kadru, który przełącza play/pause. */
.crv-hud {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--ds-duration-base) var(--ds-ease-out-soft);
}
.crv-hud-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--ds-space-4) var(--ds-space-3);
}
/* 13px, nie 12px z paczki: w treści nie ma nic poniżej 13px (CLAUDE.md,
   reguła 10). Biel na przygaszonym dole kadru — nie na żywym kolorze. */
.crv-hud-label {
  font-family: var(--ds-font-body);
  font-weight: var(--ds-weight-semi);
  font-size: var(--ds-text-caption);
  line-height: 1.2;
  color: var(--ds-color-on-primary);
}
.crv-hud-pause {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--ds-radius-full);
  background-color: rgba(255, 255, 255, 0.92);
  color: var(--ds-color-ink);
}
.crv-track { height: 3px; background-color: rgba(255, 255, 255, 0.28); }
.crv-bar {
  display: block;
  width: 100%;
  height: 100%;
  /* Postęp jedzie na scaleX, nie na width: `timeupdate` leci ~4 razy na sekundę,
     a zmiana szerokości za każdym razem przelicza layout (CLAUDE.md →
     Stabilność, pkt 2). Bez transition — pasek ma iść z nagraniem, nie za nim. */
  transform: scaleX(0);
  transform-origin: left center;
  /* Solar występuje w tej sekcji wyłącznie jako pasek postępu i nigdy jako
     przycisk — akcja zakupowa zostaje w sticky barze (CLAUDE.md, reguła 2). */
  background-color: var(--ds-color-cta-solar);
}

/* Stan odtwarzania: play znika, HUD wchodzi, scrim ściemnia. */
.crv-vid-item.is-playing .crv-play { opacity: 0; pointer-events: none; }
.crv-vid-item.is-playing .crv-hud { opacity: 1; }
.crv-vid-item.is-playing .crv-scrim { opacity: 0.55; }

/* ── Podpis pod kadrem ────────────────────────────────────────────────────
   Nigdy na wideo. Wysokość toru bierze się z najwyższego podpisu i nie zmienia
   się przy przewijaniu, więc kropki pod torem nie skaczą. */
.crv-vid-cap {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-1);
  margin: var(--ds-space-4) 0 0;
  padding-inline: var(--ds-space-1);
  text-align: center;
}
.crv-vid-name { color: var(--ds-color-ink); }
.crv-vid-course { color: var(--ds-color-ink-secondary); text-wrap: pretty; }
.crv-vid-ed { color: var(--ds-color-ink-muted); }

/* Tablet 600–1023px: karuzela zostaje (trzy kolumny wchodzą dopiero z 1024px),
   ale kadr rośnie — przy 262px na 768px ekranu sekcja robiła się pusta.
   Przy 300px widać środkowy telefon i oba sąsiednie prawie w całości. */
@media (min-width: 600px) and (max-width: 1023.98px) {
  .crv-vid-item { width: 300px; }
}

/* ── Desktop ≥1024px: trzy kolumny na pełną szerokość kontenera ─────────── */
@media (min-width: 1024px) {
  .crv-vid {
    margin-top: var(--ds-space-8);
    padding-bottom: var(--ds-space-10);
  }

  /* Siatka zamiast toru: wszystkie trzy nagrania widoczne naraz, bez
     przewijania. Kolumna przy kontenerze 1320px ma ~400px, czyli kadr ~380px —
     materiał 720px szerokości renderuje się bez skalowania w górę. */
  .crv-vid-rail {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--ds-space-10);
    margin-inline: 0;
    padding: var(--ds-space-10) 0 0;
    overflow: visible;
  }
  .crv-vid-item { width: auto; }
  .crv-vid-item[data-crv-vclone] { display: none; }

  .crv-phone { padding: 10px; border-radius: 46px; }
  .crv-phone-speaker { top: 18px; width: 62px; height: 5px; }
  .crv-screen { border-radius: 36px; }

  .crv-play { width: 84px; height: 84px; }
  .crv-play svg { width: 50px; height: 50px; margin-left: 5px; }
  .crv-hud-row { padding: 0 18px 14px; }
  .crv-hud-pause { width: 40px; height: 40px; }
  .crv-track { height: 4px; }

  .crv-vid-cap { margin-top: var(--ds-space-5); gap: 6px; max-width: 320px; margin-inline: auto; }
}

/* Uniesienie krążka tylko tam, gdzie kursor istnieje — na dotyku hover potrafi
   się „zaciąć" po tapnięciu. */
@media (hover: hover) {
  /* Pod kursorem glif dochodzi do pełnego krycia — sygnał, że to przycisk,
     a nie znak wodny. Uniesienie zostaje z systemu (1–3px), bez powiększania. */
  .crv-play:hover { transform: translate(-50%, -50%) translateY(-3px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .crv-vid-rail { scroll-behavior: auto; }
}
