/* РАСКЛАДКИ СЕКЦИИ ОТЗЫВОВ - ЗЕРКАЛО, А НЕ СВОЯ ВЕРСИЯ.

   Источник: design/v4/site-testimonials/kit/ (3 файла). Собирает
   tools/adopt-testimonials.php; правка руками теряется при следующем срезе и теряется молча.

   Отступления от побайтовой копии объявлены В КОДЕ ИНСТРУМЕНТА и печатаются отчётом
   прогона. Их 17. */

/* ===== testi.css ===== */
/* СЕКЦИЯ ОТЗЫВОВ - оболочка, роли, шкала, органы карусели. Слой САЙТА (sc-base): ни одного
   --sc-*. Наборы семьи A лежат в testi-a.css.

   ГЛАВНОЕ ОТЛИЧИЕ ЭТОГО ПАКЕТА. Здесь впервые главное - движение, и механику мы НЕ пишем:
   карусели на swiper.js. Этот файл описывает то, что swiper показывает, и то, чем управляет
   человек. Ни одного правила прокрутки, ни одного scroll-snap, ни одного своего цикла.

   ЧТО ЗДЕСЬ НЕ ПРОЕКТИРУЕТСЯ: сама прокрутка, жесты, бесконечность. Замкнутый цикл берётся
   параметром loop, и данные для него НЕ дублируются: в разведке чужой набор печатает девять
   цитат при трёх отзывах, размножая их втрое - у swiper для этого есть клоны, и PHP лишних
   узлов не печатает.

   ДВА ПОРОГА:
     700  - узкий: одна карточка;
     1040 - средний.
   Число тот же, что в пакете показателей: две разные величины порога в одной теме - это два
   разных сайта. Порог swiper берётся от КОНТЕЙНЕРА (breakpointsBase: 'container'), иначе на
   кнопке «телефон» в редакторе карусель покажет десктопное число карточек - swiper по
   умолчанию меряет окно, а бумага 390 стоит внутри окна 1440.

   ШКАЛА КЕГЛЕЙ - те же десять ступеней с отношением 1,25 и монотонным интерлиньяжем,
   проверенным на КИРИЛЛИЦЕ (минимум 1,109 из-за диакритики прописных Ё и Й):
     12,8 - 1,4  | 16 - 1,5  | 20 - 1,4  | 25 - 1,3  | 31,25 - 1,25
     39,06 - 1,2 | 48,83 - 1,18 | 61,04 - 1,15 | 76,29 - 1,14 | 95,37 - 1,12
   Цитате в семье A доступны две ступени: 48,83 и 61,04. */

/* --- СИСТЕМНЫЕ ЗНАЧЕНИЯ СЛОЯ САЙТА. Те же, что в трёх прошлых пакетах: файл автономен,
   потому что подключается один, но числа обязаны совпадать - расхождение означало бы две
   шкалы органов на одном сайте. Роли палитры приходят из данных клиента инлайном.

   ТИПОГРАФИЧЕСКИЕ РОЛИ - ТЕКУЧИЕ, а не ступенчатые. Кегли не переключаются на пороге -
   они текут вместе с шириной контейнера через clamp() с единицей cqi.
     Почему cqi, а не vw: cqi - процент ширины КОНТЕЙНЕРА, vw - окна. В редакторе сайт стоит на
     бумаге 390 точек внутри окна 1440, и vw там врёт вчетверо.
     Раскладка НЕ ЗАДАЁТ кегль, она выбирает РОЛЬ: в наборе стоит --ls-role-display-sm, а не
     61px. Числа - свойство темы и меняются вместе с ней.
     Крупный кегль сжимается сильнее мелкого: 61 -> 39,8 (в 1,53 раза), 16 -> 13,3 (в 1,2). На
     телефоне заголовку надо упасть сильно, а тексту почти не меняться.
   Интерлиньяж - МНОЖИТЕЛЬ, а не число, и проверен на КИРИЛЛИЦЕ (минимум 1,109 из-за
   диакритики прописных Ё и Й).

   Что НЕ текучее и почему: число строк обрезки (--ls-testi-lines) - это не кегль, а
   решение, сколько читать до «читать целиком», и целое число текучим быть не может. --- */
.ls-w,.ls-testi-sec{
  /* текучие роли: обе границы и множитель интерлиньяжа рядом */
  --ls-role-display-sm:clamp(2.4883rem,1.9693rem + 2.3067cqi,3.8147rem);--ls-lh-display-sm:1.15;
  --ls-role-heading-lg:clamp(2.0736rem,1.6908rem + 1.7011cqi,3.0518rem);--ls-lh-heading-lg:1.18;
  --ls-role-heading-md:clamp(1.7280rem,1.4508rem + 1.2320cqi,2.4414rem);--ls-lh-heading-md:1.2;
  --ls-role-title-md:clamp(1.3333rem,1.1852rem + 0.6583cqi,1.5625rem);--ls-lh-title-md:1.3;
  --ls-role-body-md:clamp(1.0000rem,0.9022rem + 0.4348cqi,1.2500rem);--ls-lh-body-md:1.4;
  --ls-role-body-sm:clamp(0.8333rem,0.7681rem + 0.2899cqi,1.0000rem);--ls-lh-body-sm:1.5;
  --ls-role-label-sm:clamp(0.6944rem,0.6531rem + 0.1836cqi,0.8000rem);--ls-lh-label-sm:1.4;
  /* фотография тоже текучая: 96 на широком, 56 на 390 - фиксированные 96x96 на телефоне
     съедают четверть ширины карточки */
  --ls-photo-lg:clamp(3.5rem,2.4643rem + 4.6032cqi,6rem);
  --ls-photo-md:clamp(3rem,2.3786rem + 2.7619cqi,4.5rem);
  --ls-photo-sm:clamp(2.5rem,2.0857rem + 1.8413cqi,3.5rem);
  --ls-body:20px;--ls-eb:12.8px;--ls-btn:16px;--ls-sm:14px;
  --ls-dur-fast:140ms;--ls-dur-base:220ms;--ls-dur-screen:760ms;
  --ls-ease:cubic-bezier(.2,.6,.3,1);--ls-ease-enter:cubic-bezier(.16,.84,.44,1);
  --ls-stagger:26ms;--ls-focus-w:2px;--ls-tap:44px;
  --ls-pad-compact:56px;--ls-pad-cosy:96px;--ls-pad-spacious:144px;--ls-h2:61.04px}
.ls-w{container-type:inline-size;container-name:ls}
.ls-testi-sec,.ls-testi-sec *,.ls-testi-sec *::before,.ls-testi-sec *::after{box-sizing:border-box}

/* --- СЕКЦИЯ. Вертикальные поля текучие: 120 на широком, 64 на 390 - секция поджимается
   по высоте. Боковые НЕ меняются (24): на телефоне секция не прилипает к краям. --- */
.ls-testi-sec{position:relative;
  padding-block:var(--ls-testi-pad,clamp(4rem,2.6607rem + 5.9524cqi,7.5rem));
  padding-inline:var(--ls-testi-pin,24px);
  background:var(--ls-paper,#fff);color:var(--ls-ink,#000);
  font-family:var(--ls-text-face,system-ui);
  font-size:var(--ls-role-body-md,20px);line-height:var(--ls-lh-body-md,1.4)}
.ls-testi-sec__in{inline-size:100%;max-inline-size:var(--ls-testi-max,var(--ls-max,1536px));margin-inline:auto}

/* --- ШАПКА. Наборы A1, A3 и A4 её не показывают: heading обязателен и никуда не делся, но
   у посетителя его нет. Прячет НАБОР, а не человек - см. 04-DATA-CONTRACT. --- */
.ls-testi__head{display:grid;gap:16px;max-inline-size:var(--ls-testi-headw,640px);
  margin-block-end:var(--ls-testi-gapy,56px);text-wrap:pretty}
.ls-testi__eb{display:flex;align-items:center;gap:14px;margin:0;
  font-size:var(--ls-role-label-sm,12.8px);line-height:var(--ls-lh-label-sm,1.4);
  font-weight:500;letter-spacing:.025em;color:var(--ls-muted,#666)}
.ls-testi__eb::before{content:"";inline-size:40px;block-size:1px;flex:none;
  background:currentColor;opacity:.6}
.ls-testi__h{margin:0;font-family:var(--ls-head-face,inherit);
  font-size:var(--ls-role-display-sm,61.04px);line-height:var(--ls-lh-display-sm,1.15);
  font-weight:var(--ls-testi-hw,600);letter-spacing:-0.015em}
.ls-testi__p{margin:0;font-size:var(--ls-role-body-md,20px);
  line-height:var(--ls-lh-body-md,1.4);max-inline-size:60ch;color:var(--ls-muted,#666)}

/* --- ТРЕК. Разметка swiper: .swiper > .swiper-wrapper > .swiper-slide. Здесь только вид;
   ни одного правила прокрутки. --- */
.ls-testi{position:relative}
.ls-testi .swiper{inline-size:100%}
/* ОТСТУПЛЕНИЕ: дорожка B1 и B2 уходит за край контейнера, а не обрезается по кромке
   колонки. Секция режет вылет `clip`-ом: `hidden` создал бы прокрутчик и сломал
   `position:sticky` у соседей. Разбор - в tools/adopt-testimonials.php. */
.ls-te-b1 .ls-testi-sec__in,.ls-te-b2 .ls-testi-sec__in{overflow-x:clip}
.ls-te-b1 .ls-testi .swiper,.ls-te-b2 .ls-testi .swiper{inline-size:100cqw;
  margin-inline:calc(50% - 50cqw);
  padding-inline:calc(50cqw - 50%)}
/* ДО ИНИЦИАЛИЗАЦИИ (п. 8.1 рамки): сервер напечатал все отзывы, swiper ещё не встал. Секция
   обязана быть читаемой - обычный список в потоке, а не пустая полоса и не один слайд из
   трёх. Класс is-ready ставит скрипт после init, и только тогда включается трек. */
.ls-testi:not(.is-ready) .swiper-wrapper{display:grid;gap:var(--ls-testi-gap,24px);
  grid-template-columns:var(--ls-testi-cols,1fr)}
.ls-testi:not(.is-ready) .ls-testi__ctrl{display:none}
/* Разметка слайда живёт и вне трека: у слайда нет ни absolute, ни transform от нас. */
.ls-testi .swiper-slide{block-size:auto;display:flex}
/* ОТСТУПЛЕНИЕ: карточка занимает слайд целиком. У кита она идёт по содержимому и
   жмётся к началу - замерено на его же холсте: 653,3 в слайде 976. Разбор - в
   tools/adopt-testimonials.php. */
.ls-testi .swiper-slide > .ls-testi__slide{flex:1 1 auto}
/* САМ СЛАЙД - тоже flex, и это обязательно здесь, а не в наборах: наборы задают ему
   flex-direction, justify-content и gap, а на display:block все три инертны - зазор между
   цитатой и автором оказывается нулём, и имя упирается в последнюю строку цитаты
   (замер: во всех шести карусельных наборах gap 0 при заявленных 32-44). Набор объявляет
   ПОВЕДЕНИЕ слайда, а то, что он flex, - свойство самого слайда. */
.ls-testi__slide{display:flex;min-inline-size:0}

/* --- ЦИТАТА. Единственный длинный текст, и её длину раскладка не контролирует: человек
   напишет и 40 знаков, и 400. Предел задан В СТРОКАХ, а не в кегле: падение кегля на разных
   парах гарнитур даёт разные пределы и ломает ось наборов (A1 и A4 набраны на 61,04, A2 и A3
   на 48,83 - если кегль плавает, ось перестаёт читаться).
   Обрезка по строкам плюс подпись «читать целиком»: 400 знаков на 61 пункте в колонке 896 -
   это семь строк и 700 точек, карточка вырастает вдвое и уезжает из-под глаз читающего
   (правило 01). --- */
.ls-testi__q{margin:0;
  font-size:var(--ls-testi-q,var(--ls-role-display-sm,61.04px));
  line-height:var(--ls-testi-q-lh,var(--ls-lh-display-sm,1.15));
  font-weight:var(--ls-testi-q-w,600);font-style:var(--ls-testi-q-i,normal);
  font-family:var(--ls-testi-q-face,var(--ls-head-face,inherit));
  letter-spacing:var(--ls-testi-q-ls,-0.015em);
  max-inline-size:var(--ls-testi-q-max,none);text-wrap:pretty;
  display:-webkit-box;-webkit-box-orient:vertical;
  -webkit-line-clamp:var(--ls-testi-lines,4);overflow:hidden}
/* Кнопка разворота стоит ВСЕГДА, когда цитата длиннее предела набора: обрезанный текст без
   видимого выхода - самый частый дефект таких секций. Разворот - у слайда, а не у текста:
   высота трека уже зарезервирована по самому высокому, и разворот внутри слайда её не меняет. */
.ls-testi__more{display:inline-flex;align-items:center;gap:6px;min-block-size:var(--ls-tap,44px);
  padding:0;border:0;background:none;color:inherit;font:inherit;
  font-size:var(--ls-sm,14px);text-decoration:underline;text-underline-offset:3px;
  cursor:pointer;opacity:.72}
.ls-testi__more:hover{opacity:1}
.ls-testi__slide[data-open] .ls-testi__q{-webkit-line-clamp:unset;display:block}

/* --- КАВЫЧКА-ГЛИФ. Не глиф гарнитуры: в десяти парах он разной формы и в некоторых
   отсутствует. Берётся знаком из content по языку и в RTL встаёт с другой стороны сама -
   позиция логическая. --- */
.ls-testi__q--mark{position:relative;padding-inline-start:var(--ls-testi-mark-in,0)}
.ls-testi__q--mark::before{content:"\\00AB";position:absolute;
  inset-inline-start:calc(var(--ls-testi-mark-in,0) * -1);inset-block-start:-0.06em;
  font-size:1.15em;line-height:1;color:var(--ls-acc,currentColor);opacity:.55}
[lang|="en"] .ls-testi__q--mark::before{content:"\\201C"}
[lang|="he"] .ls-testi__q--mark::before,[lang|="ar"] .ls-testi__q--mark::before{content:"\\201D"}

/* Цитата и кнопка разворота - одна группа: кнопка принадлежит цитате, а не слайду, и отбив
   до автора задаёт слайд своим gap, а не поля узлов. */
.ls-testi__body{display:grid;gap:4px;min-inline-size:0}

/* Кавычка-глиф стоит СТРОКОЙ НАД цитатой, а не абсолютно сбоку: крупный знак в поле строки
   налезает на текст при любом центрировании (проверено на A1), а в RTL ещё и меняет сторону -
   две беды разом. Строкой выше она центруется вместе с текстом и в RTL едет сама. */
.ls-testi__mark{display:block;font-size:var(--ls-testi-mark,80px);line-height:.62;
  color:var(--ls-acc,currentColor);opacity:.5;
  margin-block-end:var(--ls-testi-mark-gap,6px);user-select:none}
.ls-testi__mark::before{content:"\00AB"}
[lang|="en"] .ls-testi__mark::before{content:"\201C"}
[lang|="he"] .ls-testi__mark::before,[lang|="ar"] .ls-testi__mark::before{content:"\201D"}

/* --- ЗВЁЗДЫ. Новое поле rating, которого в разведке нет. Умолчание ПУСТО, а не пять звёзд:
   подставленный рейтинг это придуманное число. Отзыв без рейтинга показывается без звёзд, и
   ряд не едет - строка звёзд просто отсутствует, а не оставляет пустое место.
   Цвет - роль акцента, а не жёлтый: жёлтого в наших ролях нет, и во всех 24 палитрах он
   спорил бы с плоскостью. Погасшая звезда - волосяная линия. --- */
.ls-testi__rate{display:flex;align-items:center;gap:3px;line-height:1}
.ls-testi__rate .sc-icon{font-size:var(--ls-testi-star,18px);color:var(--ls-acc,currentColor);
  font-variation-settings:"FILL" 1,"wght" 400}
.ls-testi__rate .sc-icon[data-off]{color:var(--ls-line,rgba(0,0,0,.2));
  font-variation-settings:"FILL" 0,"wght" 400}

/* --- АВТОР. title необязателен - карточка не имеет права поехать; name бывает в 40 знаков,
   title в 60, и в узкой карточке это три строки вместо одной. --- */
.ls-testi__who{display:flex;align-items:center;gap:var(--ls-testi-who-gap,16px);
  min-inline-size:0}
.ls-testi__ph{flex:none;inline-size:var(--ls-testi-ph,var(--ls-photo-lg,96px));
  block-size:var(--ls-testi-ph,var(--ls-photo-lg,96px));
  border-radius:var(--ls-r-pill,50%);object-fit:cover;
  background:var(--ls-band,rgba(0,0,0,.06))}
.ls-testi__meta{min-inline-size:0;display:grid;gap:2px}
.ls-testi__nm{margin:0;font-size:var(--ls-testi-nm,var(--ls-role-body-md,20px));
  line-height:var(--ls-lh-body-md,1.4);font-weight:var(--ls-testi-nm-w,600);text-wrap:pretty}
.ls-testi__ti{margin:0;font-size:var(--ls-testi-ti,var(--ls-role-body-sm,16px));
  line-height:var(--ls-lh-body-sm,1.5);color:var(--ls-muted,#666);text-wrap:pretty}

/* --- КАРТОЧКА НА ТЕЛЕФОНЕ. На широком наборы B1 и B5 рисуют текст без обвязки: ритм
   держит линия и пустота. На телефоне карточки листаются по одной, и пустота разделять
   перестаёт - нужна граница. Приём верный, но выражен ОДНОЙ переменной (п. 5.2 рамки):
   --ls-testi-card переключает весь набор свойств сразу, а не три правила порознь. --- */
.ls-testi__card--flat{background:none;box-shadow:none;border-radius:0;padding:0}
@container ls (max-width:700px){
  .ls-testi__card--flat{background:var(--ls-band,rgba(0,0,0,.04));
    box-shadow:inset 0 0 0 1px var(--ls-line,rgba(0,0,0,.10));
    border-radius:var(--ls-radius,16px);padding:clamp(1.25rem,0.5rem + 3cqi,2rem)}
}

/* --- ОРГАНЫ. Настоящие кнопки с именами. Появляются от ДВУХ отзывов: при одном swiper
   выключен (enabled:false), органы не печатаются вовсе - не скрываются, а не печатаются, -
   перетаскивание отключено, автопрокрутка не запускается. Карусель из одного слайда это не
   карусель.
   Правило 05: кольцо фокуса обнимает всю кнопку. Правило 04: наведение меняет плоскость на
   ступень, ни вспышки, ни сдвига. --- */
.ls-testi__ctrl{display:flex;align-items:center;gap:var(--ls-testi-ctrl-gap,12px);
  margin-block-start:var(--ls-testi-ctrl-top,32px)}
.ls-testi__ctrl--center{justify-content:center}
.ls-testi__ctrl--end{justify-content:flex-end}
/* Кнопка: РИСУНОК может быть мельче цели нажатия, но цель всегда 44. В разведке кнопки
   24x24 - это меньше минимальной цели по нашим правилам доступности, и так нельзя. Рисунок
   задаётся --ls-testi-btn, поле нажатия добирается до 44 через min-size и отрицательные
   поля, чтобы рисунок остался на месте. */
.ls-testi__btn{position:relative;display:inline-flex;align-items:center;justify-content:center;
  flex:none;inline-size:var(--ls-testi-btn,44px);block-size:var(--ls-testi-btn,44px);
  /* ОТСТУПЛЕНИЕ: минимум снят, цель добирается полем ниже. Разбор - в
     tools/adopt-testimonials.php. */
  border:0;border-radius:var(--ls-testi-btn-r,50%);background:var(--ls-testi-btn-bg,transparent);
  box-shadow:inset 0 0 0 1px var(--ls-line,rgba(0,0,0,.16));
  color:inherit;font:inherit;cursor:pointer;
  transition:background-color var(--ls-dur-fast,140ms) var(--ls-ease,ease),
    box-shadow var(--ls-dur-fast,140ms) var(--ls-ease,ease)}
.ls-testi__btn .sc-icon{font-size:var(--ls-testi-btn-ic,22px)}
.ls-testi__btn:hover{background:color-mix(in oklab,currentColor 9%,transparent)}
.ls-testi__btn:focus-visible{outline:var(--ls-focus-w,2px) solid currentColor;outline-offset:2px}
/* Упор в края: swiper ставит swiper-button-disabled сам. Кнопка остаётся в обходе с
   клавиатуры - человек должен узнать, что дальше некуда, а не потерять кнопку. */
.ls-testi__btn.swiper-button-disabled{opacity:.4;cursor:default}
/* Рисунок мельче цели: кнопка сжимается визуально, поле нажатия остаётся 44. */
.ls-testi__btn--sm{--ls-testi-btn:32px;--ls-testi-btn-ic:18px}
.ls-testi__btn::after{content:"";position:absolute;
  inset:min(0px,calc((var(--ls-tap,44px) - var(--ls-testi-btn,44px)) / -2))}
.ls-testi__btn--lg{--ls-testi-btn:60px;--ls-testi-btn-ic:26px}

/* Точки. Самые мелкие органы библиотеки: рисунок 10, цель нажатия 44 через поле. Активная
   расширяется в пилюлю - но пропорция НАША: 28 к 10, а не 48 к 12 (см. 03-CAVEATS). */
.ls-testi__dots{display:flex;align-items:center;gap:var(--ls-testi-dot-gap,8px)}
.ls-testi__dot{position:relative;flex:none;inline-size:var(--ls-testi-dot,10px);
  block-size:var(--ls-testi-dot,10px);min-block-size:var(--ls-tap,44px);
  border:0;border-radius:999px /* kit-free: круг, а не угол: стороны равны */;background:none;padding:0;cursor:pointer;
  display:inline-flex;align-items:center}
.ls-testi__dot::before{content:"";inline-size:var(--ls-testi-dot,10px);
  block-size:var(--ls-testi-dot,10px);border-radius:999px /* kit-free: круг, а не угол: стороны равны */;
  background:color-mix(in oklab,currentColor 28%,transparent);
  transition:inline-size var(--ls-dur-base,220ms) var(--ls-ease,ease),
    background-color var(--ls-dur-base,220ms) var(--ls-ease,ease)}
.ls-testi__dot::after{content:"";position:absolute;inset-block:0;
  inset-inline:calc(var(--ls-testi-dot-gap,8px) / -2)}
@media (pointer:coarse){.ls-testi__dots{--ls-testi-dot-gap:calc(var(--ls-tap,44px) - var(--ls-testi-dot,10px))}}
.ls-testi__dot:hover::before{background:color-mix(in oklab,currentColor 45%,transparent)}
.ls-testi__dot:focus-visible{outline:var(--ls-focus-w,2px) solid currentColor;outline-offset:3px;
  border-radius:4px /* kit-free: кольцо фокуса, форма не предмет вкуса */}
/* Активная точка помечается ДВУМЯ признаками: классом is-on её ставит swiper, aria-current читает
   диктор. Одного признака мало: без класса вид не меняется при живом swiper, без aria диктор
   не знает, где мы. */
.ls-testi__dot[aria-current="true"]::before,.ls-testi__dot.is-on::before{
  inline-size:var(--ls-testi-dot-on,28px);background:var(--ls-acc,currentColor)}
/* Двенадцать точек в ряд не помещаются: swiper с dynamicBullets держит окно из пяти, а
   счётчик рядом говорит, где мы. Точки не сжимаются - сжатая точка нечитаема. */
.ls-testi__count{font-size:var(--ls-role-body-sm,14px);color:var(--ls-muted,#666);
  font-variant-numeric:tabular-nums;white-space:nowrap}

/* На ТЕЛЕФОНЕ СТРЕЛОК НЕТ. Палец листает свайпом, а стрелка - цель, в которую попадать
   нечем и незачем. Остаются точки: они показывают, ГДЕ мы, а не служат органом листания. */
@container ls (max-width:700px){.ls-testi__ctrl > .ls-testi__btn{display:none}}

/* --- РЕЦЕПТ ВУАЛИ поверх фотографии. Тот же, что в пакете первого экрана: один на весь
   продукт. Два слоя - ровная вуаль от роли плоскости и градиент к низу, где стоят органы.
   Порог для цитаты на 61,04 берётся ТОТ ЖЕ, что для заголовка первого экрана: кегль там и
   тут одной ступени, и второй порог был бы вторым рецептом. --- */
.ls-testi__bg{position:absolute;inset:0;overflow:hidden}
.ls-testi__bg img{inline-size:100%;block-size:100%;object-fit:cover;object-position:50% 50%}
.ls-testi__bg::after{content:"";position:absolute;inset:0;
  background:
    linear-gradient(to top,color-mix(in oklab,var(--ls-veil,#000) 78%,transparent) 0%,
      color-mix(in oklab,var(--ls-veil,#000) 34%,transparent) 46%,transparent 78%),
    color-mix(in oklab,var(--ls-veil,#000) var(--ls-testi-veil,32%),transparent)}
.ls-testi__over{position:relative;z-index:1}

/* --- ССЫЛКА-КАРТОЧКА. Показатель отзыва сегодня не кликается; если карточка станет ссылкой,
   кольцо фокуса обнимает её целиком (правило 05). --- */
a.ls-testi__card{text-decoration:none;color:inherit}
.ls-testi__card:focus-visible{outline:var(--ls-focus-w,2px) solid currentColor;outline-offset:3px}
.ls-testi-sec :focus-visible:not(.sc-se__edit){outline:var(--ls-focus-w,2px) solid currentColor;outline-offset:3px}

/* --- ЗНАЧКИ. Гарнитура объявляется В СЛОЕ САЙТА: base.css кабинета сюда не подключается, а
   без правила лигатура печатается СЛОВОМ - буквально ARROW_FORWARD посреди страницы, и
   глазами в редакторе это не ловится, потому что там подключён шрифт кабинета.
   Поимённо в этом пакете: arrow_back, arrow_forward, format_quote, pause, play_arrow. --- */
.sc-icon-KIT-TESTI{font-family:"Material Symbols Rounded";font-weight:300;font-style:normal;
  line-height:1;letter-spacing:normal;white-space:nowrap;font-feature-settings:"liga";
  font-variation-settings:"FILL" 0,"wght" 300;user-select:none}

/* Строка, видимая ТОЛЬКО в редакторе. У посетителя её нет. */
.ls-only-edit{display:none}
.ls-w[data-edit] .ls-only-edit,.ls-page[data-edit] .ls-only-edit{display:flex;align-items:flex-start;gap:8px;background:color-mix(in oklab,#fff 88%,transparent);color:#3f4650;
  margin-block-start:20px;padding-block:8px;padding-inline:12px;
  border-inline-start:2px solid var(--ls-line,rgba(0,0,0,.14));
  font-size:var(--ls-sm,14px);color:var(--ls-muted,#666);text-wrap:pretty}
.ls-only-edit .sc-icon{flex:none;font-size:18px;opacity:.7;margin-block-start:1px}

/* Приподнятая плоскость: в разведке задана как белый с прозрачностью 7,5%, и на светлой
   палитре такая карточка почти неотличима от фона. У нас это РОЛЬ band, а не прозрачность,
   и на светлой палитре карточка держится волосяной линией. */
.ls-testi__card{background:var(--ls-band,rgba(0,0,0,.04));
  box-shadow:inset 0 0 0 1px var(--ls-line,rgba(0,0,0,.10))}
.ls-panel-ink{--ls-muted:color-mix(in oklab,var(--ls-ink,#000) 78%,var(--ls-band,#eee))}

/* Правило 07: reduce - не «покороче», а подмена без перехода. Автопрокрутка выключается
   совсем (параметром autoplay:false в скрипте, здесь только переходы). */
@media (prefers-reduced-motion:reduce){
  .ls-testi-sec *{transition-duration:1ms !important;animation-duration:1ms !important;
    animation-delay:0s !important}
  .ls-testi .swiper-wrapper{transition-duration:0ms !important}
}

/* ===== testi-a.css ===== */
/* СЕМЬЯ A - одна цитата за раз. Четыре набора. Подключается после testi.css.

   ПЕРЕПИСАНО ПОД ТЕКУЧИЕ РОЛИ. Прежние ступенчатые переопределения кегля по порогам
   (61,04 -> 48,83 -> 31,25 тремя правилами) сняты: кегль теперь одна роль с clamp на cqi, и
   переопределять его на пороге не нужно. Осталось ступенчатым только число строк обрезки -
   это целое число, а не кегль.

   ОСЬ СЕМЬИ - какой РОЛЬЮ набрана цитата:
     A1  display-sm, заголовочная, прямое   - слайд во всю ширину, шапки нет
     A2  heading-lg, текстовая, курсив      - цитата в строку, органы у начала строки
     A3  heading-lg, текстовая, курсив      - всё по центру, колонка уже
     A4  display-sm, заголовочная, прямое   - цитата на фотографии

   A2 и A3 совпадают ролью и начертанием, различают их положение органов и ширина колонки:
   26ch против 20ch. В плитке каталога 320 точек при масштабе 0,29 это 93 против 69 точек -
   различие в четверть ширины плитки, видно. A1 и A4 различаются фоном, то есть тоном плитки
   целиком. */

/* ======================= A1. Слайд во всю ширину ======================= */
/* Одна карточка на всю ширину содержимого. ШАПКИ НЕТ - секция открывается прямо цитатой.
   heading у клиента заполнен и сохранён, но посетитель его не видит. */
.ls-te-a1{--ls-testi-pad:clamp(3rem,1.9821rem + 4.5238cqi,5.5rem)}
.ls-te-a1 .ls-testi__card{border-radius:calc(20px * var(--ls-r-k,1));
  padding-block:clamp(2rem,0.9107rem + 4.8413cqi,6.5rem);
  padding-inline:clamp(1.5rem,0.6964rem + 3.5714cqi,5rem)}
/* Поля 104/80 на широком вместо разведанных 132/96: при 132 карточка на среднем пороге не
   влезает в экран ноутбука целиком, и первый слайд читается без второго. */
.ls-te-a1 .ls-testi__slide{flex-direction:column;align-items:center;gap:clamp(1.5rem,0.75rem + 3cqi,2.75rem);text-align:center}
.ls-te-a1 .ls-testi__body{justify-items:center}
.ls-te-a1{--ls-testi-mark:clamp(2.75rem,1.6607rem + 4.8413cqi,5rem);--ls-testi-mark-gap:6px}
/* Цитата колонкой по центру, а не во всю карточку: строка в 1241 знак нечитаема - глаз теряет
   начало следующей строки. */
.ls-te-a1 .ls-testi__q{--ls-testi-q:var(--ls-role-display-sm);
  --ls-testi-q-lh:var(--ls-lh-display-sm);--ls-testi-q-face:var(--ls-head-face,inherit);
  --ls-testi-q-max:22ch;--ls-testi-lines:6;text-align:center}
@container ls (max-width:700px){.ls-te-a1 .ls-testi__q{--ls-testi-q-max:none;--ls-testi-lines:9}}
.ls-te-a1 .ls-testi__who{flex-direction:column;text-align:center;gap:14px}
.ls-te-a1 .ls-testi__meta{justify-items:center}
.ls-te-a1{--ls-testi-ph:var(--ls-photo-lg)}
.ls-te-a1 .ls-testi__ctrl{--ls-testi-ctrl-top:40px;justify-content:center}
/* Указатель: активный шире неактивного в 2,8 раза (28 к 10), а не в 4 (48 к 12). Пропорция
   намеренно другая - чужая пилюля 48x12 подпись их набора, см. 03-CAVEATS. */
.ls-te-a1{--ls-testi-dot:10px;--ls-testi-dot-on:28px;--ls-testi-dot-gap:10px}
@container ls (max-width:700px){.ls-te-a1{--ls-testi-dot:8px;--ls-testi-dot-on:24px}}

/* ======================= A2. Цитата в строку, органы у начала ======================= */
.ls-te-a2 .ls-testi__slide{flex-direction:column;gap:clamp(1.25rem,0.5rem + 3cqi,2.25rem);align-items:flex-start}
/* Курсив: в некоторых из десяти пар курсивного начертания нет вовсе, и браузер подделает
   наклон синтетически. font-synthesis-style:none - лучше прямое начертание, чем скошенное
   машиной; ось семьи держится ролью, а не наклоном. */
.ls-te-a2 .ls-testi__q{--ls-testi-q:var(--ls-role-heading-lg);
  --ls-testi-q-lh:var(--ls-lh-heading-lg);--ls-testi-q-i:italic;
  --ls-testi-q-face:var(--ls-text-face,inherit);--ls-testi-q-w:600;
  --ls-testi-q-max:26ch;--ls-testi-lines:5;font-synthesis-style:none}
@container ls (max-width:700px){.ls-te-a2 .ls-testi__q{--ls-testi-q-max:none;--ls-testi-lines:9}}
.ls-te-a2{--ls-testi-ph:var(--ls-photo-md);--ls-testi-nm:var(--ls-role-body-sm);
  --ls-testi-who-gap:14px}
.ls-te-a2 .ls-testi__ctrl{--ls-testi-ctrl-top:44px;justify-content:flex-start}
/* Рисунок кнопки 32, поле нажатия 44: в разведке кнопки 24x24, а это меньше минимальной цели
   по нашим правилам доступности. */
.ls-te-a2 .ls-testi__btn{--ls-testi-btn:32px;--ls-testi-btn-ic:18px}

/* ======================= A3. Цитата по центру ======================= */
/* Фотографии нет вовсе, крупного заголовка секции нет. Ни одной настройки - вкладка
   Customize у этого набора исчезает целиком. */
.ls-te-a3{--ls-testi-pad:clamp(4rem,2.4107rem + 7.0635cqi,9rem)}
.ls-te-a3 .ls-testi__slide{flex-direction:column;align-items:center;gap:clamp(1.25rem,0.5rem + 3cqi,2rem);text-align:center}
.ls-te-a3 .ls-testi__body{justify-items:center}
/* Колонка уже, чем у A2: 20ch против 26ch - геометрическое различие пары. */
.ls-te-a3 .ls-testi__q{--ls-testi-q:var(--ls-role-heading-lg);
  --ls-testi-q-lh:var(--ls-lh-heading-lg);--ls-testi-q-i:italic;
  --ls-testi-q-face:var(--ls-text-face,inherit);--ls-testi-q-w:600;
  --ls-testi-q-max:20ch;--ls-testi-lines:5;text-align:center;font-synthesis-style:none}
@container ls (max-width:700px){.ls-te-a3 .ls-testi__q{--ls-testi-q-max:none;--ls-testi-lines:9}}
.ls-te-a3 .ls-testi__who{flex-direction:column;gap:4px;text-align:center}
.ls-te-a3 .ls-testi__meta{justify-items:center}
.ls-te-a3 .ls-testi__ctrl{--ls-testi-ctrl-top:36px;justify-content:center;gap:16px}
.ls-te-a3 .ls-testi__btn{--ls-testi-btn:40px;--ls-testi-btn-ic:20px}
/* Точки 10, а не разведанные 8: восьмипиксельная точка на тёмной палитре при волосяной линии
   теряется. Цель нажатия 44 у обеих. */
.ls-te-a3{--ls-testi-dot:10px;--ls-testi-dot-on:24px;--ls-testi-dot-gap:8px}

/* ======================= A4. Цитата на фотографии ======================= */
/* Умолчание «во весь экран» ВЫКЛЮЧЕНО: отзывы стоят посреди страницы, и секция в полный
   экран разрывает чтение. Включается тумблером. */
.ls-te-a4{position:relative;isolation:isolate;color:var(--ls-ink-on-media,#fff)}
.ls-te-a4[data-fill]{min-block-size:100svh;display:flex;align-items:center}
.ls-te-a4 .ls-testi__slide{flex-direction:column;gap:clamp(1.5rem,0.75rem + 3cqi,2.5rem);align-items:flex-start}
.ls-te-a4 .ls-testi__q{--ls-testi-q:var(--ls-role-display-sm);
  --ls-testi-q-lh:var(--ls-lh-display-sm);--ls-testi-q-face:var(--ls-head-face,inherit);
  --ls-testi-q-max:24ch;--ls-testi-lines:5}
@container ls (max-width:700px){.ls-te-a4 .ls-testi__q{--ls-testi-q-max:none;--ls-testi-lines:8}}
.ls-te-a4 .ls-testi__ti{color:color-mix(in oklab,var(--ls-ink-on-media,#fff) 78%,transparent)}
/* Органы у конца строки: inset-inline-end, не right - в RTL уезжают сами. */
.ls-te-a4 .ls-testi__ctrl{--ls-testi-ctrl-top:44px;justify-content:flex-end}
.ls-te-a4 .ls-testi__btn{--ls-testi-btn:60px;--ls-testi-btn-ic:26px;--ls-testi-btn-r:calc(14px * var(--ls-r-k,1));
  box-shadow:inset 0 0 0 1px color-mix(in oklab,var(--ls-ink-on-media,#fff) 38%,transparent)}
.ls-te-a4 .ls-testi__btn:hover{background:color-mix(in oklab,var(--ls-ink-on-media,#fff) 16%,transparent)}
@container ls (max-width:700px){.ls-te-a4 .ls-testi__btn{--ls-testi-btn:44px;--ls-testi-btn-ic:22px}}
/* Фотографии нет вовсе - секция встаёт на плоскость палитры, вуали нет, чернила обычные. */
.ls-te-a4[data-nobg]{color:var(--ls-ink,#000);background:var(--ls-band,rgba(0,0,0,.04))}
.ls-te-a4[data-nobg] .ls-testi__ti{color:var(--ls-muted,#666)}
.ls-te-a4[data-nobg] .ls-testi__btn{box-shadow:inset 0 0 0 1px var(--ls-line,rgba(0,0,0,.16))}
.ls-te-a4[data-nobg] .ls-testi__btn:hover{background:color-mix(in oklab,currentColor 9%,transparent)}

/* ===== testi-b.css ===== */
/* СЕМЬЯ B - несколько карточек в виду. Пять наборов. Подключается после testi.css.

   ЧТО МЕНЯЕТСЯ ПО СРАВНЕНИЮ С СЕМЬЁЙ A. В виду несколько отзывов сразу, поэтому цитата
   набирается мельче - роли body-md и title-md вместо display-sm и heading-lg, - и главным
   становится не типографика цитаты, а РИТМ карточек: сколько их видно, чем они разделены,
   есть ли у них плоскость.

   ВТОРАЯ ОСЬ СЕМЬИ - механика:
     B1  карусель, ~2,4 карточки в виду   стрелки + точки у конца строки внизу
     B2  карусель, ~1,8 карточки          две крупные кнопки в строке шапки
     B3  статичная сетка                  органов нет
     B4  статичная сетка поверх фото      органов нет
     B5  две дорожки с автопрокруткой     органов нет, есть настройка скорости

   ВТОРОЙ ПОРОГ 1040 нужен ровно здесь: без него не выражается ни B1, ни B3. На 800 точках три
   карточки дают 250 на карточку, а цитата в 400 знаков на body-md требует минимум 300. Схема:
   до 700 одна, 700-1040 две, шире 1040 три. */

/* ======================= B1. Карусель карточек без обвязки ======================= */
/* Шапка рядом: заголовок у начала строки, абзац у конца. Ниже дорожка карточек без рамки и
   без плоскости - только текст.

   ЧЕМ ОТДЕЛЯЕТСЯ КАРТОЧКА ОТ СОСЕДНЕЙ, когда цитаты разной длины. Не пустотой: при разной
   длине цитат пустота между карточками читается как случайная, и ряд рассыпается. Ритм
   держит ВОЛОСЯНАЯ ЛИНИЯ у начала строки каждой карточки - она одной высоты у всех, потому
   что высота трека зарезервирована по самому высокому слайду (правило 06), и ряд остаётся
   рядом при любой длине цитат.
   Линия у КАЖДОЙ карточки, включая первую: номер первой зависит от числа карточек в виду, а
   оно меняется дважды и приходит инлайном - селектор :not(:first-child) врёт на двух
   карточках и на одной. Первая линия совпадает с краем содержимого, и это читается как край
   секции, а не как разделитель. */
.ls-te-b1 .ls-testi__head{--ls-testi-headw:none;display:grid;
  grid-template-columns:var(--ls-b1-head,minmax(0,1fr) minmax(0,0.75fr));
  gap:clamp(1.5rem,0.375rem + 4.5cqi,4.5rem);align-items:end}
@container ls (max-width:1040px){.ls-te-b1 .ls-testi__head{--ls-b1-head:minmax(0,1fr)}}
.ls-te-b1 .ls-testi__slide{flex-direction:column;justify-content:space-between;
  gap:clamp(1.25rem,0.5rem + 3cqi,2rem);
  border-inline-start:1px solid var(--ls-line,rgba(0,0,0,.14));
  padding-inline-start:clamp(1rem,0.5rem + 2cqi,2rem)}
@container ls (max-width:700px){.ls-te-b1 .ls-testi__slide{border-inline-start:0;
  padding-inline-start:0}}
.ls-te-b1 .ls-testi__q{--ls-testi-q:var(--ls-role-body-md);
  --ls-testi-q-lh:var(--ls-lh-body-md);--ls-testi-q-face:var(--ls-text-face,inherit);
  --ls-testi-q-w:400;--ls-testi-q-ls:normal;--ls-testi-lines:9}
.ls-te-b1{--ls-testi-ph:var(--ls-photo-sm);--ls-testi-nm-w:400}
.ls-te-b1 .ls-testi__ctrl{--ls-testi-ctrl-top:40px;justify-content:flex-end;gap:20px}
.ls-te-b1 .ls-testi__btn{--ls-testi-btn:36px;--ls-testi-btn-ic:20px}
.ls-te-b1{--ls-testi-dot:10px;--ls-testi-dot-on:26px;--ls-testi-dot-gap:8px}

/* ======================= B2. Карусель карточек с плоскостью ======================= */
/* То же листание, но карточка настоящая: скругление, внутренние поля, приподнятая плоскость.
   Органы - две крупные кнопки В СТРОКЕ ШАПКИ: они не отнимают высоту у дорожки и сразу
   видны.
   НА УЗКОМ шапка становится одной колонкой, и органы из её строки уходят ПОД дорожку: в
   390 точках заголовок и две кнопки 66 в одну строку не встают, а сжимать заголовок ради
   кнопок - менять главное на служебное. */
.ls-te-b2 .ls-testi__head{--ls-testi-headw:none;display:grid;
  grid-template-columns:var(--ls-b2-head,minmax(0,1fr) auto);
  gap:clamp(1rem,0.25rem + 3cqi,2.5rem);align-items:start}
.ls-te-b2 .ls-testi__ctrl--head{margin-block-start:0;align-self:start;justify-content:flex-end}
@container ls (max-width:700px){
  .ls-te-b2 .ls-testi__head{--ls-b2-head:minmax(0,1fr)}
  .ls-te-b2 .ls-testi__ctrl--head{display:none}
}
/* Под дорожкой органы появляются только на узком - и это точки, не стрелки. */
.ls-te-b2 .ls-testi__ctrl--under{display:none}
@container ls (max-width:700px){.ls-te-b2 .ls-testi__ctrl--under{display:flex;
  justify-content:center;--ls-testi-ctrl-top:28px}}
.ls-te-b2 .ls-testi__card{border-radius:calc(16px * var(--ls-r-k,1));padding:clamp(1.25rem,0.4643rem + 3.4921cqi,3.75rem)}
.ls-te-b2 .ls-testi__slide{flex-direction:column;justify-content:space-between;
  gap:clamp(1.25rem,0.5rem + 3cqi,2rem)}
/* Цитата на ступень крупнее, чем в B1: карточка шире, и строка в 650 точек на body-md была бы
   слишком плотной. */
.ls-te-b2 .ls-testi__q{--ls-testi-q:var(--ls-role-title-md);
  --ls-testi-q-lh:var(--ls-lh-title-md);--ls-testi-q-face:var(--ls-text-face,inherit);
  --ls-testi-q-w:400;--ls-testi-q-ls:normal;--ls-testi-lines:7}
.ls-te-b2{--ls-testi-ph:var(--ls-photo-md);--ls-testi-nm-w:600}
.ls-te-b2 .ls-testi__btn{--ls-testi-btn:56px;--ls-testi-btn-ic:24px;--ls-testi-btn-r:calc(14px * var(--ls-r-k,1))}
@container ls (max-width:700px){.ls-te-b2{--ls-testi-dot:8px;--ls-testi-dot-on:24px}}

/* ======================= B3. Статичная сетка ======================= */
/* Ничего не движется, всё видно сразу. Механики нет вовсе - swiper здесь не подключается.

   ГЛАВНЫЙ ВОПРОС НАБОРА: при трёх отзывах разведанная сетка даёт 2 + 1, и одинокая карточка
   во втором ряду читается как ошибка. У нас сетка ПОДСТРАИВАЕТСЯ ПОД ЧИСЛО ОТЗЫВОВ - тем же
   приёмом, что в пакете показателей: PHP знает число и печатает data-n, а это одна
   переменная, ради которой ничего нового заводить не надо.
     1 отзыв   - одна карточка, но не во всю ширину: колонка 34ch, иначе строка нечитаема
     2         - две в ряд
     3         - три в ряд
     4         - две и две
     5, 6      - три в ряд, второй ряд из двух или трёх
     7 и больше- три в ряд, последний ряд неполный. Это уже не «одинокая карточка», а хвост
                 длинного списка, и он читается как хвост.
     12        - четыре ряда по три. Ничего не прячем: сетка статична, прокрутка страницы и
                 есть механизм показа. */
.ls-te-b3 .ls-testi{display:grid;grid-template-columns:var(--ls-testi-cols,repeat(3,minmax(0,1fr)));
  gap:clamp(0.75rem,0.25rem + 2cqi,1.5rem)}
.ls-te-b3 .ls-testi[data-n="1"]{--ls-testi-cols:minmax(0,34ch)}
.ls-te-b3 .ls-testi[data-n="2"],.ls-te-b3 .ls-testi[data-n="4"]{--ls-testi-cols:repeat(2,minmax(0,1fr))}
@container ls (max-width:1040px){.ls-te-b3 .ls-testi{--ls-testi-cols:repeat(2,minmax(0,1fr))}}
@container ls (max-width:700px){.ls-te-b3 .ls-testi{--ls-testi-cols:minmax(0,1fr)}
  .ls-te-b3 .ls-testi[data-n="1"]{--ls-testi-cols:minmax(0,1fr)}}
.ls-te-b3 .ls-testi__card{border-radius:calc(16px * var(--ls-r-k,1));padding:clamp(1.25rem,0.6786rem + 2.5397cqi,2.25rem);
  display:flex;flex-direction:column;justify-content:space-between;
  gap:clamp(1rem,0.5rem + 2cqi,1.75rem)}
.ls-te-b3 .ls-testi__q{--ls-testi-q:var(--ls-role-body-md);
  --ls-testi-q-lh:var(--ls-lh-body-md);--ls-testi-q-face:var(--ls-text-face,inherit);
  --ls-testi-q-w:400;--ls-testi-q-ls:normal;--ls-testi-lines:8}
.ls-te-b3{--ls-testi-ph:var(--ls-photo-sm);--ls-testi-nm-w:600}
/* Кнопки СЕКЦИИ (из Content), а не органы карусели: у статичной сетки органов нет. */
.ls-testi__acts{display:flex;flex-wrap:wrap;gap:12px;margin-block-start:clamp(1.5rem,1rem + 2cqi,2.5rem)}
.ls-testi__cta{display:inline-flex;align-items:center;justify-content:center;
  min-block-size:52px;padding-block:14px;padding-inline:28px;border:0;border-radius:calc(14px * var(--ls-r-k,1));
  background:var(--ls-acc,#333);color:var(--ls-acc-on,#fff);font:inherit;
  font-size:var(--ls-role-body-sm,16px);font-weight:500;text-decoration:none;cursor:pointer;
  transition:background-color var(--ls-dur-fast,140ms) var(--ls-ease,ease)}
.ls-testi__cta--ghost{background:transparent;color:inherit;
  box-shadow:inset 0 0 0 1px color-mix(in oklab,currentColor 42%,transparent)}

/* ======================= B4. Статичная сетка на фотографии ======================= */
/* Две карточки в ряд поверх фоновой фотографии. Механики нет.

   ДВЕ ПРОЗРАЧНОСТИ СКЛАДЫВАТЬСЯ НЕ ДОЛЖНЫ. В разведке текст лежит на полупрозрачной карточке
   поверх фото, и прозрачность карточки перемножается с вуалью фона: на светлой фотографии
   текст читается, на тёмной пропадает, и обратно. Наше решение - карточка НЕПРОЗРАЧНАЯ
   (плоскость палитры), вуаль на фото ослаблена до 22% против 32% у A4.
   Одна прозрачность вместо двух: контраст текста считается от плоскости карточки и не
   зависит от фотографии вовсе, а вуаль остаётся только чтобы карточки не спорили с фоном. */
.ls-te-b4{position:relative;isolation:isolate;--ls-testi-veil:22%}
.ls-te-b4 .ls-testi__head{color:var(--ls-ink-on-media,#fff)}
.ls-te-b4 .ls-testi{display:grid;grid-template-columns:var(--ls-testi-cols,repeat(2,minmax(0,1fr)));
  gap:clamp(0.75rem,0.25rem + 2cqi,1.5rem)}
.ls-te-b4 .ls-testi[data-n="1"]{--ls-testi-cols:minmax(0,42ch)}
@container ls (max-width:700px){.ls-te-b4 .ls-testi{--ls-testi-cols:minmax(0,1fr)}
  .ls-te-b4 .ls-testi[data-n="1"]{--ls-testi-cols:minmax(0,1fr)}}
.ls-te-b4 .ls-testi__card{border-radius:calc(16px * var(--ls-r-k,1));padding:clamp(1.25rem,0.5357rem + 3.1746cqi,3rem);
  display:flex;flex-direction:column;justify-content:space-between;
  gap:clamp(1rem,0.5rem + 2cqi,1.75rem);
  /* Непрозрачная плоскость палитры, а не белый 7,5% поверх фото. */
  background:var(--ls-paper,#fff);color:var(--ls-ink,#000);box-shadow:none}
.ls-te-b4 .ls-testi__q{--ls-testi-q:var(--ls-role-title-md);
  --ls-testi-q-lh:var(--ls-lh-title-md);--ls-testi-q-face:var(--ls-text-face,inherit);
  --ls-testi-q-w:400;--ls-testi-q-ls:normal;--ls-testi-lines:7}
/* Должность равна имени по кеглю - единственное место в библиотеке. Различает их ВЕС и цвет:
   имя 600 чернилами, должность 400 приглушёнными. Одного кегля мало, двух признаков хватает. */
.ls-te-b4{--ls-testi-ph:var(--ls-photo-md);--ls-testi-nm-w:600;
  --ls-testi-ti:var(--ls-role-body-md)}
.ls-te-b4[data-nobg]{background:var(--ls-band,rgba(0,0,0,.04))}
.ls-te-b4[data-nobg] .ls-testi__head{color:var(--ls-ink,#000)}
.ls-te-b4[data-nobg] .ls-testi__card{box-shadow:inset 0 0 0 1px var(--ls-line,rgba(0,0,0,.10))}

/* ======================= B5. Две дорожки с автопрокруткой ======================= */
/* Шапка занимает половину секции, вторую - две вертикальные дорожки карточек, которые едут
   сами. Органов нет: скорость задаётся настройкой.

   ЕДИНСТВЕННЫЙ НАБОР БЕЗ БОКОВЫХ ПОЛЕЙ у дорожек: они уходят в край, и обрез края - это и
   есть подсказка «список длиннее, чем видно».
   Дорожки едут В ОДНУ СТОРОНУ, а не навстречу: встречное движение сильнее укачивает и
   заставляет глаз выбирать, за какой дорожкой следить, - а секция существует, чтобы её
   читали (правило 01).
   Полоса растворения у краёв - маска, а не градиент поверх: градиент поверх пришлось бы
   красить в цвет плоскости, а плоскостей у нас 24. */
.ls-te-b5{--ls-testi-pin:0}
.ls-te-b5 .ls-testi-sec__in{display:grid;
  grid-template-columns:var(--ls-b5-cols,minmax(0,1fr) minmax(0,1fr));
  gap:clamp(1.5rem,0.375rem + 4.5cqi,4rem);align-items:center}
.ls-te-b5 .ls-testi__head{padding-inline-start:24px;margin-block-end:0}
@container ls (max-width:1040px){.ls-te-b5 .ls-testi-sec__in{--ls-b5-cols:minmax(0,1fr)}
  .ls-te-b5 .ls-testi__head{padding-inline:24px}}
/* Две дорожки рядом. На узком остаётся ОДНА: две дорожки по 300 в 390 точках не помещаются, а
   сжимать карточку до 180 - терять цитату. */
.ls-te-b5 .ls-testi-lanes{display:grid;grid-template-columns:var(--ls-b5-lanes,repeat(2,minmax(0,1fr)));
  gap:clamp(0.75rem,0.25rem + 2cqi,2.25rem);
  /* Высота области дорожек задана в единицах контейнера, а не точками: фиксировать высоту
     раскладке нельзя (п. 8.3 рамки), а дорожке нужен предел, иначе она едет во всю страницу. */
  block-size:clamp(24rem,18rem + 24cqi,34rem);
  mask-image:linear-gradient(to bottom,transparent 0,#000 8%,#000 92%,transparent 100%)}
@container ls (max-width:700px){.ls-te-b5 .ls-testi-lanes{--ls-b5-lanes:minmax(0,1fr);
  /* ОТСТУПЛЕНИЕ: ответ «узко» для скрипта - ниже 700 дорожка ОДНА, а не одна
     колонка из двух. Разбор - в tools/adopt-testimonials.php. */
  --ls-b5-one:1;
  block-size:clamp(22rem,20rem + 8cqi,26rem)}}
/* Дорожка - НЕ карусель и не swiper: у неё нет шага, текущего слайда и органов; человек её не
   листает. Движение - CSS-анимация трека по translate: одно свойство, композитный слой, ни
   одного пересчёта раскладки. Второй проход ленты клонирует СКРИПТ, а не сервер: в разметке
   каждый отзыв один раз. Без скрипта лента стоит столбцом и читается целиком. */
.ls-te-b5 .ls-testi-lane{overflow:hidden;position:relative}
.ls-te-b5 .ls-testi-track{display:flex;flex-direction:column;
  gap:clamp(0.75rem,0.5rem + 1cqi,1.25rem)}
.ls-te-b5 .ls-testi-lane[data-run] .ls-testi-track{
  animation:ls-lane var(--ls-testi-dur,60s) linear infinite}
.ls-te-b5 .ls-testi-lanes[data-stop] .ls-testi-track{animation-play-state:paused}
/* отступление: человек выключил автопроигрывание - дорожки стоят */
.ls-testi-sec[data-still] .ls-testi-lane[data-run] .ls-testi-track{animation:none}
/* Сдвиг ровно на высоту одного прохода плюс зазор: в конце цикла клон стоит ровно там,
   где был оригинал, и стык не виден. Высота считается от содержимого, а не задана числом -
   фиксировать высоту раскладке нельзя (п. 8.3 рамки). */
@keyframes ls-lane{from{transform:translateY(0)}to{transform:translateY(-50%)}}
.ls-te-b5 .ls-testi__card{border-radius:calc(16px * var(--ls-r-k,1));padding:clamp(1.25rem,0.9rem + 1.4cqi,1.75rem);
  display:flex;flex-direction:column;justify-content:space-between;gap:1rem}
.ls-te-b5 .ls-testi__q{--ls-testi-q:var(--ls-role-body-md);
  --ls-testi-q-lh:var(--ls-lh-body-md);--ls-testi-q-face:var(--ls-text-face,inherit);
  --ls-testi-q-w:400;--ls-testi-q-ls:normal;--ls-testi-lines:7}
.ls-te-b5{--ls-testi-ph:var(--ls-photo-md);--ls-testi-nm-w:600}
/* Остановленное состояние - animation-play-state выше. Замирает МГНОВЕННО, а не дотягивает
   до карточки: дотяжка после наведения - движение, которого человек не просил (правило 01).
   Наведение на любую дорожку останавливает ОБЕ: они читаются как одна область, и остановка
   половины выглядит поломкой. */
/* Подпись остановки видна только когда движение есть: при reduce её печатать не надо -
   человек и не ждал движения. */
.ls-te-b5 .ls-testi-lanes:not([data-stop]) ~ .ls-testi-note{display:none}
@media (prefers-reduced-motion:reduce){.ls-te-b5 .ls-testi-note{display:none}}
.ls-te-b5 .ls-testi-note{display:flex;align-items:center;gap:8px;grid-column:1/-1;margin:0;padding-inline:24px;
  font-size:var(--ls-role-body-sm,14px);color:var(--ls-muted,#666)}

