/* ============================================================
   LUVI — ТОКЕНЫ И UI-КИТ
   Версия 1.0 · 18.09.2026

   Значения вынуты из боевого приложения (luvi-live/css/app.css),
   они совпадают с ним до символа. Компоненты названы с приставкой
   `lv-`, чтобы кит можно было положить в чужую вёрстку и ничего
   там не задеть.

   Порядок в файле: токены, база, типографика, компоненты,
   цветные поверхности, раскладка, телефон.
   ============================================================ */

/* ------------------------------------------------------------
   1. ТОКЕНЫ
   ------------------------------------------------------------ */
:root {
  /* цвет */
  --lv-bg: #0C0B10;
  --lv-surface-1: #15141B;
  --lv-surface-2: #1C1B23;
  --lv-surface-3: #24232C;
  --lv-surface-hover: #2A2933;

  --lv-text-1: #F7F5F1;
  --lv-text-2: #CFCCD6;
  --lv-text-3: #A09CAA;
  --lv-text-ph: #6E6A79;   /* подсказка в поле: темнее обычного текста */

  --lv-border-1: rgba(255, 255, 255, .10);   /* заметная рамка */
  --lv-border-2: rgba(255, 255, 255, .06);   /* тихая рамка */

  --lv-accent: #FD429C;
  --lv-accent-hover: #E0388C;
  --lv-accent-soft: rgba(253, 66, 156, .14);
  --lv-grad: linear-gradient(115deg, #FF8A18 0%, #FB4595 62%, #E83B8A 100%);

  --lv-success: #2FBE7A;
  --lv-warning: #F5A65C;
  --lv-error: #FF6B5E;

  /* отступы раскладки. Ступени 5 нет намеренно: её отсутствие
     не даёт подбирать промежуточные значения на глаз */
  --lv-s1: 4px;  --lv-s2: 8px;   --lv-s3: 12px; --lv-s4: 16px;
  --lv-s6: 24px; --lv-s8: 32px;  --lv-s12: 48px; --lv-s16: 64px;
  /* Ступень только для сайта. В приложении её нет и быть не должно:
     секции лендинга дышат шире экранов сервиса, 64 там мало. */
  --lv-s24: 96px;

  /* радиусы */
  --lv-r-sm: 8px; --lv-r-md: 12px; --lv-r-lg: 16px; --lv-r-full: 999px;

  /* тень одна на систему и только у того, что всплывает над страницей */
  --lv-shadow-overlay: 0 16px 40px rgba(0, 0, 0, .45);
  /* выделение — одно мягкое кольцо, двойного контура быть не должно */
  --lv-ring: 0 0 0 3px rgba(253, 66, 156, .22);

  --lv-font-ui: "Mulish", -apple-system, "Segoe UI", sans-serif;
  --lv-font-head: "Onest", "Mulish", sans-serif;

  --lv-ease-out: cubic-bezier(.23, 1, .32, 1);
  --lv-ease-drawer: cubic-bezier(.32, .72, 0, 1);
  --lv-t-press: 120ms; --lv-t-hover: 160ms; --lv-t-pop: 160ms; --lv-t-drawer: 240ms;

  --lv-z-dropdown: 40; --lv-z-sticky: 50; --lv-z-player: 60; --lv-z-drawer: 80;
  --lv-z-backdrop: 90; --lv-z-sheet: 91; --lv-z-modal: 100;
  --lv-z-toast: 110; --lv-z-tooltip: 120;
}

/* ------------------------------------------------------------
   2. БАЗА
   ------------------------------------------------------------ */
.lv-root {
  background: var(--lv-bg);
  color: var(--lv-text-1);
  font: 400 14px/1.5 var(--lv-font-ui);
  -webkit-font-smoothing: antialiased;
}
.lv-root *, .lv-root *::before, .lv-root *::after { box-sizing: border-box; }
/* Сброс ссылки завёрнут в :where, чтобы весить ноль. Без этого
   селектор `.lv-root a` (класс плюс тег) перебивал любой `.lv-btn--*`
   с его одним классом, и кнопка-ссылка всегда получала наследуемый
   цвет вместо своего. Держалось незаметно только потому, что
   наследуемый цвет совпадал с нужным у основной и у тихой кнопки.
   На первой же кнопке с другим цветом это вылезло. */
.lv-root :where(a) { color: inherit; text-decoration: none; }
.lv-root button, .lv-root input, .lv-root select, .lv-root textarea { font-family: inherit; }
.lv-root button { cursor: pointer; }
.lv-root img { max-width: 100%; }
.lv-root :focus-visible { outline: none; box-shadow: var(--lv-ring); }
.lv-tabular { font-variant-numeric: tabular-nums; }
.lv-ico { flex: none; }

/* ------------------------------------------------------------
   3. ТИПОГРАФИКА
   ------------------------------------------------------------ */
.lv-h1 { font: 800 28px/1.15 var(--lv-font-head); letter-spacing: -.02em; text-wrap: balance; margin: 0; }
.lv-h2 { font: 700 18px/1.25 var(--lv-font-head); letter-spacing: -.01em; margin: 0; }
.lv-title { font: 700 16px/1.3 var(--lv-font-ui); margin: 0; }
.lv-body { font: 400 14px/1.5 var(--lv-font-ui); color: var(--lv-text-2); text-wrap: pretty; margin: 0; }
.lv-label { font: 600 12px/1.35 var(--lv-font-ui); color: var(--lv-text-3); }
.lv-num { font: 800 28px/1.1 var(--lv-font-head); font-variant-numeric: tabular-nums; }
.lv-kicker {
  display: block;
  font: 700 11px/1 var(--lv-font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--lv-text-3);
}
/* крупный заголовок посадочной страницы */
.lv-display { font: 800 clamp(32px, 6vw, 64px)/1.05 var(--lv-font-head); letter-spacing: -.03em; margin: 0; }
.lv-lead { font: 400 clamp(15px, 2vw, 19px)/1.5 var(--lv-font-ui); color: var(--lv-text-2); margin: 0; }

/* ------------------------------------------------------------
   4. КНОПКА
   Главная кнопка экрана одна и заливается градиентом. Градиент —
   метка действия, а не украшение: второй такой на экране отнимает
   у первого весь смысл.
   ------------------------------------------------------------ */
.lv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--lv-s2);
  height: 44px;
  padding: 0 20px;
  border: 1px solid transparent;
  border-radius: var(--lv-r-full);
  background: none;
  color: var(--lv-text-1);
  font: 600 14px/1 var(--lv-font-ui);
  white-space: nowrap;
  cursor: pointer;
  transition: transform var(--lv-t-press) var(--lv-ease-out),
              background var(--lv-t-hover), border-color var(--lv-t-hover), color var(--lv-t-hover);
}
.lv-btn:active { transform: scale(.97); }
/* Градиент растянут на рамку (border-box): у кнопки прозрачная рамка
   в 1 px, и без этого фон повторялся под ней, давая розовую полоску
   слева и оранжевую справа. */
.lv-btn--primary { background: var(--lv-grad) border-box; color: #fff; }
.lv-btn--secondary { background: var(--lv-surface-2); border-color: var(--lv-border-1); }
.lv-btn--ghost { color: var(--lv-text-2); }
/* ТЕКСТОВАЯ КНОПКА АКЦЕНТОМ.
   Ни заливки, ни рамки, только цвет. Нужна там, где два действия
   стоят рядом и одно из них легче по весу, но должно быть заметно:
   вход рядом с регистрацией. От ghost отличается тем, что тот тихий
   и серый, а этот заметный. Цель под палец остаётся 44 px, потому
   что высота берётся от самой .lv-btn. */
.lv-btn--text { background: none; border-color: transparent; color: var(--lv-accent); padding: 0 12px; }
@media (hover: hover) and (pointer: fine) { .lv-btn--text:hover { color: var(--lv-accent-hover); } }
.lv-btn--danger { color: var(--lv-error); }
.lv-btn--sm { height: 36px; padding: 0 14px; font-size: 13px; }
.lv-btn--icon {
  width: 36px; height: 36px; padding: 0;
  border-radius: var(--lv-r-full);
  color: var(--lv-text-2);
}
.lv-btn--icon:active { transform: scale(.94); }
.lv-btn--block { width: 100%; }
@media (hover: hover) and (pointer: fine) {
  .lv-btn--primary:hover { filter: brightness(1.06); }
  .lv-btn--secondary:hover { background: var(--lv-surface-3); }
  .lv-btn--ghost:hover { background: var(--lv-surface-2); color: var(--lv-text-1); }
  .lv-btn--danger:hover { background: rgba(255, 107, 94, .12); }
  .lv-btn--icon:hover { background: var(--lv-surface-2); color: var(--lv-text-1); }
}
.lv-btn:disabled, .lv-btn[aria-disabled="true"] {
  background: var(--lv-surface-2);
  background-image: none;
  border-color: var(--lv-border-1);
  color: var(--lv-text-3);
  cursor: not-allowed;
  transform: none;
  filter: none;
}
/* Строка под выключенной кнопкой: немая серая кнопка без объяснения —
   это тупик, человек не знает, что ему сделать */
.lv-btn-why {
  display: block;
  margin-top: 6px;
  font: 400 12px/1.35 var(--lv-font-ui);
  color: var(--lv-text-3);
  text-align: center;
}
.lv-btn[data-loading="1"] { pointer-events: none; color: transparent; position: relative; }
.lv-btn[data-loading="1"]::after {
  content: "";
  position: absolute;
  width: 18px; height: 18px;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: lv-spin .7s linear infinite;
}
@keyframes lv-spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------
   5. ПОЛЕ ВВОДА
   ------------------------------------------------------------ */
.lv-field { display: block; }
.lv-field__label {
  display: block;
  margin-bottom: 6px;
  font: 600 12px/1.35 var(--lv-font-ui);
  color: var(--lv-text-3);
}
.lv-field__input, .lv-field textarea {
  width: 100%;
  height: 44px;
  padding: 0 14px;
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-2);
  border: 1px solid var(--lv-border-1);
  color: var(--lv-text-1);
  font: 400 15px/1.4 var(--lv-font-ui);
  transition: border-color var(--lv-t-hover);
}
.lv-field textarea { height: auto; min-height: 160px; padding: 12px 14px; resize: vertical; }
.lv-field__input::placeholder, .lv-field textarea::placeholder {
  color: var(--lv-text-ph);
  font-size: 14px;   /* подсказка мельче вводимого: их нельзя путать */
}
.lv-field__input:focus, .lv-field textarea:focus {
  border-color: var(--lv-accent);
  outline: none;
  box-shadow: var(--lv-ring);
}
.lv-field__help, .lv-field__error {
  display: block;
  margin-top: 6px;
  font: 400 12px/1.4 var(--lv-font-ui);
  color: var(--lv-text-3);
}
.lv-field__error { color: var(--lv-error); }
/* подвал поля: подсказка слева, счётчик знаков справа */
.lv-field__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--lv-s3);
  margin-top: 6px;
}
.lv-field__foot .lv-field__help,
.lv-field__foot .lv-field__error { margin-top: 0; }
.lv-field__count {
  flex: none;
  font: 400 12px/1.4 var(--lv-font-ui);
  color: var(--lv-text-3);
  font-variant-numeric: tabular-nums;
}
.lv-field__count.is-over { color: var(--lv-error); }
/* приставка и окончание внутри поля: валюта, домен, единица */
.lv-field__row { position: relative; display: block; }
.lv-field__pre, .lv-field__post {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  color: var(--lv-text-3);
  font: 400 15px/1 var(--lv-font-ui);
  pointer-events: none;
}
.lv-field__pre { left: 14px; }
.lv-field__post { right: 14px; }
/* Ошибка прячет обычную подсказку: две строки под полем спорят друг с другом */
.lv-field[data-invalid="1"] .lv-field__input,
.lv-field[data-invalid="1"] textarea { border-color: var(--lv-error); }
.lv-field[data-invalid="1"] .lv-field__help { display: none; }
/* поле без своей коробки — для панели, которая рисует рамку за него */
.lv-field__input.lv-bare, .lv-field textarea.lv-bare {
  background: none;
  border-color: transparent;
  padding-left: 0;
  padding-right: 0;
}
.lv-field__input.lv-bare:focus, .lv-field textarea.lv-bare:focus { box-shadow: none; border-color: transparent; }

/* РАСТУЩЕЕ ПОЛЕ (добавлено 22.09.2026, приложение: стиль и «исключить»).
   Выглядит строкой, но это textarea: со второй строки растёт само и
   останавливается на своём пределе — у стиля три строки, у остальных
   две. Дальше включается ручка в углу, и человек тянет поле сам.

   Ручку показываем только когда текст уже не помещается: на пустом
   поле она намекает, что тянуть надо всегда, и сбивает с толку. Высоту
   считает скрипт (`рост()` в views-studio.js), предел задаётся самим
   полем через `data-rows` — не общей цифрой на всех.

   Здесь был ряд тегов: строка резалась по запятым и каждая примета
   показывалась облачком под полем. Владелец снял это 22.09.2026 —
   облачка дублировали то, что человек только что напечатал. */
.lv-field textarea.lv-bare.lv-grow {
  padding-bottom: 0;
  overflow: hidden;
  resize: none;
  min-height: 0;
}
.lv-field textarea.lv-bare.lv-grow.is-full { overflow: auto; resize: vertical; }

/* ------------------------------------------------------------
   6. ВЫБОР: ЧЕКБОКС, РАДИО, ПЕРЕКЛЮЧАТЕЛЬ, ПОЛЗУНОК
   Системные элементы выбора рисуются светлыми и чужими поверх
   тёмного интерфейса — ровно та же беда, из-за которой запрещён
   системный <select>. Поэтому родной input прячем, а вид рисуем
   сами. Прячем прозрачностью и размером, а НЕ `display: none`:
   скрытый через display элемент выпадает из обхода с клавиатуры
   и перестаёт переключаться пробелом.
   ------------------------------------------------------------ */
.lv-check, .lv-radio, .lv-switch {
  position: relative;
  display: inline-flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
  font: 400 14px/1.45 var(--lv-font-ui);
  color: var(--lv-text-1);
}
.lv-switch { align-items: center; }
.lv-check > input, .lv-radio > input, .lv-switch > input {
  position: absolute;
  width: 1px; height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.lv-check__box, .lv-radio__box {
  flex: none;
  position: relative;
  width: 20px; height: 20px;
  margin-top: 1px;                      /* сесть на первую строку подписи */
  border: 1px solid var(--lv-border-1);
  background: var(--lv-surface-2);
  transition: background var(--lv-t-hover), border-color var(--lv-t-hover);
}
.lv-check__box { border-radius: 6px; }
.lv-radio__box { border-radius: var(--lv-r-full); }

/* галочка нарисована двумя гранями рамки: своего знака здесь не нужно */
.lv-check__box::after {
  content: "";
  position: absolute;
  left: 6.5px; top: 3px;
  width: 5px; height: 9px;
  border: 2px solid #fff;
  border-top: 0; border-left: 0;
  border-radius: 1px;
  opacity: 0;
  transform: rotate(45deg) scale(.5);
  transition: opacity var(--lv-t-press), transform var(--lv-t-press);
}
.lv-check > input:checked ~ .lv-check__box,
.lv-check > input:indeterminate ~ .lv-check__box,
.lv-radio > input:checked ~ .lv-radio__box {
  background: var(--lv-accent);
  border-color: var(--lv-accent);
}
.lv-check > input:checked ~ .lv-check__box::after { opacity: 1; transform: rotate(45deg) scale(1); }
/* «выбрать всё», когда выбрано не всё: чёрточка вместо галочки */
.lv-check > input:indeterminate ~ .lv-check__box::after {
  opacity: 1;
  left: 4px; top: 8px;
  width: 10px; height: 0;
  border: 0; border-top: 2px solid #fff;
  transform: none;
}
.lv-radio__box::after {
  content: "";
  position: absolute; inset: 0; margin: auto;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #fff;
  opacity: 0;
  transform: scale(.4);
  transition: opacity var(--lv-t-press), transform var(--lv-t-press);
}
.lv-radio > input:checked ~ .lv-radio__box::after { opacity: 1; transform: scale(1); }

.lv-switch__track {
  flex: none;
  position: relative;
  width: 40px; height: 24px;
  border-radius: var(--lv-r-full);
  background: var(--lv-surface-3);
  border: 1px solid var(--lv-border-1);
  transition: background var(--lv-t-hover), border-color var(--lv-t-hover);
}
.lv-switch__track::after {
  content: "";
  position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--lv-text-3);
  transition: transform var(--lv-t-hover), background var(--lv-t-hover);
}
.lv-switch > input:checked ~ .lv-switch__track { background: var(--lv-accent); border-color: var(--lv-accent); }
.lv-switch > input:checked ~ .lv-switch__track::after { transform: translateX(16px); background: #fff; }

/* Выделение — одно кольцо, как у всего остального. Рамка при этом
   не меняется: двойного контура в системе нет. */
.lv-check > input:focus-visible ~ .lv-check__box,
.lv-radio > input:focus-visible ~ .lv-radio__box,
.lv-switch > input:focus-visible ~ .lv-switch__track { box-shadow: var(--lv-ring); }
@media (hover: hover) and (pointer: fine) {
  .lv-check:hover .lv-check__box,
  .lv-radio:hover .lv-radio__box,
  .lv-switch:hover .lv-switch__track { border-color: rgba(255, 255, 255, .24); }
}
.lv-check:has(> input:disabled),
.lv-radio:has(> input:disabled),
.lv-switch:has(> input:disabled) { cursor: not-allowed; opacity: .45; }

/* подпись под строкой выбора: зачем это включать */
.lv-check__note, .lv-radio__note, .lv-switch__note {
  display: block;
  margin-top: 2px;
  font: 400 12px/1.4 var(--lv-font-ui);
  color: var(--lv-text-3);
}
/* группа: строки выбора идут лесенкой, а не в строку */
.lv-choices { display: grid; gap: var(--lv-s3); }

/* Ползунок. Заливка слева считается через --lv-range-p (0…100):
   у вебкита нет своей «пройденной» части дорожки, её приходится
   рисовать градиентом и обновлять из скрипта. */
.lv-range {
  width: 100%;
  height: 24px;
  margin: 0;
  background: none;
  appearance: none; -webkit-appearance: none;
  cursor: pointer;
}
.lv-range::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg,
    var(--lv-accent) 0 calc(var(--lv-range-p, 50) * 1%),
    var(--lv-surface-3) calc(var(--lv-range-p, 50) * 1%) 100%);
}
.lv-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 18px; height: 18px;
  margin-top: -7px;
  border: 0;
  border-radius: 50%;
  background: #fff;
}
.lv-range::-moz-range-track { height: 4px; border-radius: 2px; background: var(--lv-surface-3); }
.lv-range::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--lv-accent); }
.lv-range::-moz-range-thumb { width: 18px; height: 18px; border: 0; border-radius: 50%; background: #fff; }
.lv-range:focus-visible { outline: none; }
.lv-range:focus-visible::-webkit-slider-thumb { box-shadow: var(--lv-ring); }
.lv-range:focus-visible::-moz-range-thumb { box-shadow: var(--lv-ring); }
.lv-range:disabled { opacity: .45; cursor: not-allowed; }
/* строка «подпись — значение» над ползунком */
.lv-range-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--lv-s3); margin-bottom: 6px; }
.lv-range-row b { font: 600 12px/1.35 var(--lv-font-ui); color: var(--lv-text-3); font-weight: 600; }
.lv-range-row span { font: 600 13px/1.2 var(--lv-font-ui); color: var(--lv-text-1); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------
   7. СПИСОК, ПОИСК, ЗАГРУЗКА ФАЙЛА
   ------------------------------------------------------------ */
/* Свой выпадающий список взамен системного <select>. Скрипт обязан
   дать клавиатуру: стрелки, Enter, Esc и набор первых букв. Без
   этого список хуже системного, а не лучше. */
.lv-select { position: relative; }
.lv-select__btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--lv-s2);
  width: 100%;
  height: 44px;
  padding: 0 10px 0 14px;
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-2);
  border: 1px solid var(--lv-border-1);
  color: var(--lv-text-1);
  font: 400 15px/1.4 var(--lv-font-ui);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--lv-t-hover);
}
.lv-select__btn > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* значок ставится в разметку первым, а стоять должен последним */
.lv-select__btn .lv-ico { order: 1; flex: none; color: var(--lv-text-3); transition: transform var(--lv-t-hover); }
.lv-select__btn:focus-visible,
.lv-select__btn[aria-expanded="true"] { outline: none; border-color: var(--lv-accent); box-shadow: var(--lv-ring); }
.lv-select__btn[aria-expanded="true"] .lv-ico { transform: rotate(180deg); }
.lv-select[data-empty="1"] .lv-select__btn > span { color: var(--lv-text-ph); }
.lv-select__list {
  position: absolute;
  z-index: var(--lv-z-dropdown);
  top: calc(100% + 6px); left: 0; right: 0;
  max-height: 264px;
  overflow: auto;
  padding: 6px;
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-3);
  border: 1px solid var(--lv-border-1);
  box-shadow: var(--lv-shadow-overlay);
  animation: lv-pop var(--lv-t-pop) var(--lv-ease-out);
}
.lv-select__list[hidden] { display: none; }
.lv-select__opt {
  display: flex;
  align-items: center;
  gap: var(--lv-s2);
  width: 100%;
  height: 40px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--lv-r-sm);
  background: none;
  color: var(--lv-text-1);
  font: 400 14px/1 var(--lv-font-ui);
  text-align: left;
  cursor: pointer;
}
.lv-select__opt > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lv-select__opt .lv-ico { margin-left: auto; flex: none; color: var(--lv-accent); opacity: 0; }
.lv-select__opt[aria-selected="true"] .lv-ico { opacity: 1; }
/* курсор клавиатуры и наведение мышью — одна и та же подсветка:
   иначе на экране оказывается два «выбранных» пункта сразу */
.lv-select__opt.is-cursor { background: var(--lv-surface-hover); }
@media (hover: hover) and (pointer: fine) { .lv-select__opt:hover { background: var(--lv-surface-hover); } }

/* Поиск. Значок слева не нажимается, крестик справа нажимается. */
.lv-search { position: relative; display: block; }
.lv-search__ico {
  position: absolute; left: 13px; top: 50%;
  transform: translateY(-50%);
  display: flex;
  color: var(--lv-text-3);
  pointer-events: none;
}
.lv-search .lv-field__input { padding-left: 40px; padding-right: 40px; }
.lv-search__clear {
  position: absolute; right: 6px; top: 50%;
  transform: translateY(-50%);
  display: grid; place-items: center;
  width: 32px; height: 32px;
  border: 0; border-radius: var(--lv-r-sm);
  background: none;
  color: var(--lv-text-3);
  cursor: pointer;
}
.lv-search__clear[hidden] { display: none; }
@media (hover: hover) and (pointer: fine) { .lv-search__clear:hover { color: var(--lv-text-1); background: var(--lv-surface-3); } }
.lv-search__clear:focus-visible { outline: none; box-shadow: var(--lv-ring); }

/* Загрузка файла. Пунктир говорит «сюда можно бросить» — сплошная
   рамка такого не обещает. */
.lv-drop {
  display: grid;
  justify-items: center;
  gap: var(--lv-s2);
  padding: var(--lv-s8) var(--lv-s4);
  border: 1px dashed rgba(255, 255, 255, .18);
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-2);
  color: var(--lv-text-2);
  font: 400 14px/1.5 var(--lv-font-ui);
  text-align: center;
  transition: border-color var(--lv-t-hover), background var(--lv-t-hover);
}
.lv-drop > .lv-ico { color: var(--lv-text-3); }
.lv-drop__hint { font: 400 12px/1.4 var(--lv-font-ui); color: var(--lv-text-3); }
.lv-drop.is-over { border-color: var(--lv-accent); background: var(--lv-accent-soft); border-style: solid; }
.lv-drop[data-invalid="1"] { border-color: var(--lv-error); }
.lv-drop[data-invalid="1"] .lv-drop__hint { color: var(--lv-error); }
/* принятый файл: строка с именем, весом и крестиком */
.lv-file {
  display: flex;
  align-items: center;
  gap: var(--lv-s3);
  padding: 10px 10px 10px var(--lv-s3);
  border: 1px solid var(--lv-border-2);
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-2);
}
.lv-file__main { min-width: 0; flex: 1; }
.lv-file__name { display: block; font: 600 14px/1.3 var(--lv-font-ui); color: var(--lv-text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lv-file__size { display: block; margin-top: 2px; font: 400 12px/1.2 var(--lv-font-ui); color: var(--lv-text-3); font-variant-numeric: tabular-nums; }
.lv-file > .lv-ico { flex: none; color: var(--lv-text-3); }

/* ------------------------------------------------------------
   8. ПОЛОСА-СООБЩЕНИЕ
   Это не тост. Тост сообщает, что действие уже случилось, и уходит
   сам. Полоса остаётся на экране и объясняет, что не так прямо
   здесь: ошибка всей формы, предупреждение, подтверждение.
   ------------------------------------------------------------ */
.lv-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--lv-s3);
  padding: 12px 14px;
  border: 1px solid var(--lv-border-2);
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-2);
  color: var(--lv-text-2);
  font: 400 14px/1.5 var(--lv-font-ui);
}
.lv-alert > .lv-ico { flex: none; margin-top: 2px; color: var(--lv-text-3); }
.lv-alert__main { min-width: 0; }
.lv-alert b { display: block; margin-bottom: 2px; font: 700 14px/1.4 var(--lv-font-ui); color: var(--lv-text-1); }
.lv-alert--error   { border-color: rgba(255, 107, 94, .35); background: rgba(255, 107, 94, .10); }
.lv-alert--warning { border-color: rgba(245, 166, 92, .35); background: rgba(245, 166, 92, .10); }
.lv-alert--success { border-color: rgba(47, 190, 122, .35); background: rgba(47, 190, 122, .10); }
.lv-alert--error > .lv-ico   { color: var(--lv-error); }
.lv-alert--warning > .lv-ico { color: var(--lv-warning); }
.lv-alert--success > .lv-ico { color: var(--lv-success); }

/* ------------------------------------------------------------
   9. ЧИП, ТЕГ, СЕГМЕНТ
   ------------------------------------------------------------ */
.lv-chips { display: flex; gap: var(--lv-s2); flex-wrap: wrap; }
.lv-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border-radius: var(--lv-r-full);
  background: var(--lv-surface-2);
  border: 1px solid var(--lv-border-2);
  color: var(--lv-text-2);
  font: 600 13px/1 var(--lv-font-ui);
  cursor: pointer;
  transition: background var(--lv-t-hover), border-color var(--lv-t-hover),
              color var(--lv-t-hover), transform var(--lv-t-press);
}
@media (hover: hover) and (pointer: fine) {
  .lv-chip:hover { border-color: var(--lv-border-1); color: var(--lv-text-1); }
}
.lv-chip:active { transform: scale(.97); }
.lv-chip[aria-pressed="true"] {
  background: var(--lv-accent-soft);
  border-color: var(--lv-accent);
  color: var(--lv-accent);
}
.lv-chip__n { color: var(--lv-text-3); font-variant-numeric: tabular-nums; }
.lv-chip[aria-pressed="true"] .lv-chip__n { color: var(--lv-accent); }
.lv-chip--add { background: none; border-style: dashed; color: var(--lv-text-3); }
.lv-chip:disabled { opacity: .5; cursor: not-allowed; }

.lv-tag {
  display: inline-block;
  height: 24px;
  padding: 0 9px;
  border-radius: var(--lv-r-full);
  background: var(--lv-surface-2);
  color: var(--lv-text-2);
  font: 600 12px/24px var(--lv-font-ui);
}
.lv-tag--genre { background: var(--lv-accent-soft); color: var(--lv-accent); }

.lv-seg {
  display: inline-flex;
  padding: 4px;
  border-radius: var(--lv-r-full);
  background: var(--lv-surface-2);
  border: 1px solid var(--lv-border-2);
}
.lv-seg__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* центрируем флексом, а не line-height, равным высоте: иначе любое
     изменение высоты уводит подпись вниз */
  height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--lv-r-full);
  background: none;
  color: var(--lv-text-2);
  font: 600 14px/1 var(--lv-font-ui);
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--lv-t-hover), background var(--lv-t-hover);
}
.lv-seg__item[aria-selected="true"] { background: var(--lv-surface-3); color: var(--lv-text-1); }

/* ------------------------------------------------------------
   10. ПАНЕЛЬ ФОРМЫ
   Главный приём системы: рамку рисует панель, а поля внутри своей
   рамки не имеют. Так уходит «поле в поле» — болезнь сложных форм.
   ------------------------------------------------------------ */
.lv-pan {
  border: 1px solid transparent;
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-2);
  overflow: hidden;
}
/* 14 px — единственное намеренное исключение из шкалы: 12 ещё тесно,
   16 уже разваливает форму */
.lv-pan + .lv-pan { margin-top: 14px; }
/* пока пишут в поле, у панели проявляется контур: без этого не видно,
   где курсор, ведь у полей своей рамки нет */
.lv-pan:focus-within { border-color: rgba(255, 255, 255, .18); }
/* Всё, что лежит ВНУТРИ панели, поднимается на ступень: сама панель
   цвета `--lv-surface-2`, и элементы того же цвета на ней растворяются. */
.lv-pan .lv-chip,
.lv-pan .lv-seg,
.lv-pan .lv-btn--secondary,
.lv-pan .lv-select__btn,
.lv-pan .lv-field__input,
.lv-pan .lv-check__box,
.lv-pan .lv-radio__box,
.lv-pan .lv-drop { background: var(--lv-surface-3); }
.lv-pan .lv-switch__track { background: var(--lv-surface-hover); }
.lv-pan > input:checked ~ .lv-switch__track { background: var(--lv-accent); }
.lv-pan .lv-seg__item[aria-selected="true"] { background: var(--lv-surface-hover); }
.lv-pan__head { display: flex; align-items: center; gap: var(--lv-s2); padding-right: var(--lv-s2); }
.lv-pan__toggle {
  display: flex;
  align-items: center;
  gap: var(--lv-s2);
  flex: 1;
  min-width: 0;
  height: 44px;
  padding: 0 var(--lv-s3);
  border: 0;
  background: none;
  color: var(--lv-text-2);
  font: 600 14px/1 var(--lv-font-ui);
  text-align: left;
}
.lv-pan__toggle .lv-ico { transition: transform var(--lv-t-hover); }
.lv-pan__toggle[aria-expanded="false"] .lv-ico { transform: rotate(-90deg); }
.lv-pan__tools { display: flex; align-items: center; gap: 2px; flex: none; }
.lv-pan__tool {
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  border: 0;
  border-radius: var(--lv-r-sm);
  background: none;
  color: var(--lv-text-3);
}
.lv-pan__tool:hover { background: var(--lv-surface-2); color: var(--lv-text-1); }
.lv-pan__body { padding: 0 var(--lv-s3) var(--lv-s3); }

/* ------------------------------------------------------------
   11. КАРТОЧКА И СТРОКА
   ------------------------------------------------------------ */
.lv-card {
  padding: var(--lv-s4);
  border: 1px solid var(--lv-border-2);
  border-radius: var(--lv-r-lg);
  background: var(--lv-surface-1);
}
/* Вложенных карточек не делаем: карточка внутри карточки лишает обе
   смысла, глаз перестаёт понимать, что здесь главное */
.lv-card .lv-card { border: 0; background: none; padding: 0; }

.lv-row {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--lv-s3);
  height: 72px;
  padding: 0 var(--lv-s4);
  border-radius: var(--lv-r-md);
  transition: background var(--lv-t-hover);
}
@media (hover: hover) and (pointer: fine) { .lv-row:hover { background: var(--lv-surface-2); } }
.lv-row[data-playing="1"] { background: var(--lv-surface-2); }
.lv-row[data-playing="1"] .lv-row__title { color: var(--lv-accent); }
.lv-row__cover {
  position: relative;
  width: 56px; height: 56px;
  border: 0; padding: 0;
  border-radius: var(--lv-r-sm);
  overflow: hidden;
  background: var(--lv-grad);
}
.lv-row__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lv-row__main { min-width: 0; }
.lv-row__title {
  font: 700 15px/1.3 var(--lv-font-ui);
  color: var(--lv-text-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lv-row__sub {
  font: 400 13px/1.35 var(--lv-font-ui);
  color: var(--lv-text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lv-row__time { font: 600 13px/1 var(--lv-font-ui); color: var(--lv-text-3); font-variant-numeric: tabular-nums; }
.lv-row__acts { display: flex; gap: var(--lv-s1); opacity: 0; transition: opacity var(--lv-t-hover); }
.lv-row:hover .lv-row__acts, .lv-row:focus-within .lv-row__acts { opacity: 1; }
@media (hover: none) { .lv-row__acts { opacity: 1; } }

/* плитка сводки: короткий ответ крупной цифрой */
.lv-stat {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--lv-s1);
  min-height: 84px;
  padding: var(--lv-s3);
  border: 1px solid var(--lv-border-2);
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-1);
}
.lv-stat__n { font: 800 24px/1 var(--lv-font-head); color: var(--lv-text-1); }
.lv-stat__c { font: 400 12px/1.2 var(--lv-font-ui); color: var(--lv-text-3); }

/* ------------------------------------------------------------
   12. МЕНЮ, ШТОРКА, ОКНО, ТОСТ
   ------------------------------------------------------------ */
.lv-menu {
  min-width: 220px;
  max-width: 300px;   /* без потолка длинная сноска растягивает меню на пол-экрана */
  padding: 6px;
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-3);
  border: 1px solid var(--lv-border-1);
  box-shadow: var(--lv-shadow-overlay);
  animation: lv-pop var(--lv-t-pop) var(--lv-ease-out);
}
@keyframes lv-pop { from { opacity: 0; transform: scale(.96); } }
.lv-menu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 40px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--lv-r-sm);
  background: none;
  color: var(--lv-text-2);
  font: 600 14px/1 var(--lv-font-ui);
  text-align: left;
}
.lv-menu__item:hover { background: var(--lv-surface-hover); color: var(--lv-text-1); }
.lv-menu__item--danger { color: var(--lv-error); }
.lv-menu hr { border: 0; border-top: 1px solid var(--lv-border-1); margin: 6px 4px; }
.lv-menu__note { margin: 6px 10px; font: 400 12px/1.4 var(--lv-font-ui); color: var(--lv-text-3); }

.lv-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--lv-z-backdrop);
  background: rgba(8, 7, 12, .48);
  animation: lv-fade var(--lv-t-drawer);
}
@keyframes lv-fade { from { opacity: 0; } }
/* Шторка ВЫШЕ своей подложки. Если поставить её ниже, подложка накроет
   шторку целиком: она будет выглядеть погашенной и не нажмётся нигде.
   Мы на этом обожглись и полгода не замечали */
.lv-sheet {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(480px, 100%);
  z-index: var(--lv-z-sheet);
  background: var(--lv-surface-1);
  border-left: 1px solid var(--lv-border-1);
  display: flex;
  flex-direction: column;
  animation: lv-slide-in var(--lv-t-drawer) var(--lv-ease-drawer);
}
@keyframes lv-slide-in { from { transform: translateX(100%); } }
.lv-sheet__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px var(--lv-s6);
  border-bottom: 1px solid var(--lv-border-2);
}
.lv-sheet__body { flex: 1; overflow: auto; padding: var(--lv-s6); }
.lv-sheet__foot { padding: var(--lv-s4) var(--lv-s6); border-top: 1px solid var(--lv-border-2); display: flex; gap: var(--lv-s2); justify-content: flex-end; }

.lv-modal {
  position: fixed;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: var(--lv-z-modal);
  width: min(720px, calc(100vw - 48px));
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  border-radius: var(--lv-r-lg);
  /* Окно всплывает над страницей, поэтому берёт самую светлую поверхность
     и рамку чуть заметнее обычной: на surface-1 оно сливалось с подложкой. */
  background: var(--lv-surface-3);
  border: 1px solid rgba(255, 255, 255, .14);
  box-shadow: var(--lv-shadow-overlay);
}
.lv-modal__head { padding: var(--lv-s6) var(--lv-s6) var(--lv-s4); }
.lv-modal__body { padding: 0 var(--lv-s6); overflow: auto; }
.lv-modal__foot { padding: var(--lv-s4) var(--lv-s6) var(--lv-s6); display: flex; gap: var(--lv-s2); justify-content: flex-end; }

.lv-toast {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 12px var(--lv-s4);
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-3);
  border: 1px solid var(--lv-border-1);
  box-shadow: var(--lv-shadow-overlay);
  color: var(--lv-text-1);
  font: 600 13px/1.3 var(--lv-font-ui);
}
.lv-toast__action { color: var(--lv-accent); background: none; border: 0; font: inherit; }

/* ------------------------------------------------------------
   13. ПУСТОЕ СОСТОЯНИЕ, СКЕЛЕТОН, ПОДСКАЗКА
   ------------------------------------------------------------ */
.lv-empty {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--lv-s3);
  padding: var(--lv-s16) var(--lv-s6);
  text-align: center;
}
.lv-empty h3 { font: 700 18px/1.25 var(--lv-font-head); margin: 0; }
/* не шире 46 знаков: длинная строка по центру не читается */
.lv-empty p { max-width: 46ch; margin: 0; color: var(--lv-text-2); }

.lv-skeleton { background: var(--lv-surface-2); border-radius: var(--lv-r-sm); position: relative; overflow: hidden; }
.lv-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .05), transparent);
  animation: lv-shimmer 1.2s linear infinite;
}
@keyframes lv-shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* Своя подсказка, а не системная: системная приходит через полторы
   секунды и рисуется светлой плашкой поверх тёмного интерфейса,
   поэтому люди решают, что подсказки нет вовсе */
.lv-tip {
  position: fixed;
  z-index: var(--lv-z-tooltip);
  max-width: 260px;
  padding: 8px 10px;
  border-radius: var(--lv-r-sm);
  background: var(--lv-surface-3);
  border: 1px solid var(--lv-border-1);
  box-shadow: var(--lv-shadow-overlay);
  color: var(--lv-text-1);
  font: 600 12px/1.4 var(--lv-font-ui);
  pointer-events: none;
}

/* ------------------------------------------------------------
   14. ЖИВЫЕ ЭЛЕМЕНТЫ: ЭКВАЛАЙЗЕР
   ------------------------------------------------------------ */
.lv-eq { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; }
.lv-eq i {
  width: 2px;
  background: var(--lv-accent);
  border-radius: 1px;
  animation: lv-eq .9s ease-in-out infinite;
}
.lv-eq i:nth-child(1) { height: 6px; }
.lv-eq i:nth-child(2) { height: 12px; animation-delay: .16s; }
.lv-eq i:nth-child(3) { height: 8px; animation-delay: .32s; }
.lv-eq i:nth-child(4) { height: 11px; animation-delay: .48s; }
.lv-eq i:nth-child(5) { height: 5px; animation-delay: .64s; }
@keyframes lv-eq { 0%, 100% { transform: scaleY(.45); } 50% { transform: scaleY(1); } }
.lv-eq--lg { height: 22px; gap: 3px; }
.lv-eq--lg i { width: 3px; }

/* ------------------------------------------------------------
   15. ЦВЕТНЫЕ ПОВЕРХНОСТИ
   Яркие блоки собраны из размытых пятен под тёмной подложкой,
   без единой картинки: они ничего не весят, тянутся под любой
   размер и не устаревают вместе с фотографией.
   Это и есть тот единственный градиентный элемент на экране.

   Пятен может быть два или три. Два дают диагональ, три — свет,
   который живёт сам по себе. Третье ставится элементом
   `.lv-glow__c`, потому что оба псевдоэлемента уже заняты.
   ------------------------------------------------------------ */
.lv-glow {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  border-radius: var(--lv-r-lg);
  background: #120F1A;
  --lv-glow-a: #FF8A18;
  --lv-glow-b: #FB4595;
  --lv-glow-c: #7B5CFF;
}
.lv-glow::before, .lv-glow::after {
  content: "";
  position: absolute;
  width: 420px; height: 420px;
  border-radius: 50%;
  filter: blur(72px);
  opacity: .8;
  pointer-events: none;
  z-index: -1;
}
.lv-glow::before {
  left: -80px; top: -180px;
  background: radial-gradient(circle, var(--lv-glow-a) 0%, transparent 70%);
  animation: lv-glow-a 28s ease-in-out infinite alternate;
}
.lv-glow::after {
  right: -60px; bottom: -220px;
  background: radial-gradient(circle, var(--lv-glow-b) 0%, transparent 70%);
  animation: lv-glow-b 34s ease-in-out infinite alternate;
}
/* третье пятно. Ставится элементом: оба псевдоэлемента уже заняты
   первыми двумя, а трогать разметку ради света не хочется нигде,
   кроме главного блока. */
.lv-glow__c {
  position: absolute;
  left: 42%; top: -140px;
  width: 360px; height: 360px;
  border-radius: 50%;
  filter: blur(76px);
  opacity: .55;
  pointer-events: none;
  z-index: -1;
  background: radial-gradient(circle, var(--lv-glow-c) 0%, transparent 70%);
  animation: lv-glow-c 40s ease-in-out infinite alternate;
}
@keyframes lv-glow-a { to { transform: translate3d(90px, 60px, 0) scale(1.15); } }
@keyframes lv-glow-b { to { transform: translate3d(-70px, -50px, 0) scale(1.1); } }
@keyframes lv-glow-c { to { transform: translate3d(-120px, 90px, 0) scale(1.2); } }

/* ПОКА БЛОК ИГРАЕТ, СВЕТ ДЫШИТ.
   Раньше здесь был такт на 120 ударов в минуту — полсекунды на удар.
   Выглядело дёрганьем: блок фоновый, а мельтешил сильнее всего на
   экране. Теперь это дыхание — цикл 6 секунд, размах 3 %. Движение
   заметно, только если смотреть на него нарочно, а это ему и нужно.

   Дыхание живёт отдельным свойством `scale`, а не внутри `transform`:
   иначе оно затирало бы медленный дрейф пятен. Складываются они
   только когда записаны порознь. */
.lv-glow.is-on::before,
.lv-glow.is-on::after,
.lv-glow.is-on .lv-glow__c { opacity: 1; }
.lv-glow.is-on::before { animation: lv-glow-a 28s ease-in-out infinite alternate, lv-glow-breathe 6s ease-in-out infinite; }
.lv-glow.is-on::after  { animation: lv-glow-b 34s ease-in-out infinite alternate, lv-glow-breathe 6s ease-in-out infinite -2s; }
.lv-glow.is-on .lv-glow__c { animation: lv-glow-c 40s ease-in-out infinite alternate, lv-glow-breathe 7.5s ease-in-out infinite -4s; }
@keyframes lv-glow-breathe {
  0%, 100% { scale: 1; }
  50% { scale: 1.03; }
}
/* кнопка отзывается тем же дыханием, но совсем слабо: она главная,
   ей не к лицу прыгать */
.lv-glow.is-on .lv-hero__play { animation: lv-hero-breathe 6s ease-in-out infinite; }
@keyframes lv-hero-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, .18); }
  50% { box-shadow: 0 0 0 12px rgba(255, 255, 255, 0); }
}

/* цветовые версии: одна и та же форма, разный характер */
.lv-glow--fire   { --lv-glow-a: #FF8A18; --lv-glow-b: #FB4595; --lv-glow-c: #7B5CFF; }  /* фирменный */
.lv-glow--violet { --lv-glow-a: #A855F7; --lv-glow-b: #FB4595; --lv-glow-c: #2B8BFF; }  /* ночной */
.lv-glow--ocean  { --lv-glow-a: #2B8BFF; --lv-glow-b: #7B5CFF; --lv-glow-c: #2FBE7A; }  /* спокойный */
.lv-glow--mint   { --lv-glow-a: #2FBE7A; --lv-glow-b: #2B8BFF; --lv-glow-c: #F5A65C; }  /* свежий */
.lv-glow--amber  { --lv-glow-a: #F5A65C; --lv-glow-b: #FF6B5E; --lv-glow-c: #FB4595; }  /* тёплый */
.lv-glow--berry  { --lv-glow-a: #FB4595; --lv-glow-b: #7B2CBF; --lv-glow-c: #FF8A18; }  /* густой */
.lv-glow--ash    { --lv-glow-a: #6E6A79; --lv-glow-b: #2A2933; --lv-glow-c: #4A4754; }  /* тихий */

@media (prefers-reduced-motion: reduce) {
  .lv-glow::before, .lv-glow::after, .lv-glow__c,
  .lv-glow.is-on::before, .lv-glow.is-on::after,
  .lv-glow.is-on .lv-glow__c, .lv-glow.is-on .lv-hero__play { animation: none; }
  .lv-eq i, .lv-skeleton::after { animation: none; }
}

/* блок-герой: крупный заголовок, одна кнопка, под ней строка с песней */
.lv-hero { padding: var(--lv-s6); }
.lv-hero__in {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--lv-s4);
  padding: var(--lv-s4) 0 var(--lv-s2);
}
.lv-hero__title {
  margin: 0;
  font: 800 40px/1.05 var(--lv-font-head);
  letter-spacing: -.03em;
  color: #fff;
  text-align: center;
}
.lv-hero__play {
  display: grid;
  place-items: center;
  width: 96px; height: 96px;
  border: 0;
  border-radius: var(--lv-r-full);
  background: #fff;
  color: #17141F;
  transition: transform var(--lv-t-hover);
}
@media (hover: hover) and (pointer: fine) { .lv-hero__play:hover { transform: scale(1.05); } }
.lv-hero__play:active { transform: scale(.96); }
.lv-hero__now {
  display: flex;
  align-items: center;
  gap: var(--lv-s2);
  max-width: 100%;
  min-width: 0;
  padding: 5px 5px 5px var(--lv-s3);
  border-radius: var(--lv-r-full);
  background: rgba(10, 8, 14, .42);
}
.lv-hero__sub {
  min-width: 0;
  font: 400 13px/1.4 var(--lv-font-ui);
  color: rgba(255, 255, 255, .7);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Название песни весомое, исполнитель тихий. Вся строка при этом
   уступает кнопке: она подпись к действию, а не действие. */
.lv-hero__sub b { font-weight: 700; color: #fff; }
.lv-hero__sub b::after { content: " · "; font-weight: 400; color: rgba(255, 255, 255, .4); }
.lv-hero__mark {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border-radius: var(--lv-r-full);
  /* Тёмная, а не светлая: белые 11 px на светлой плашке поверх яркой
     заливки не добирают контраст. Проверено замером. */
  background: rgba(10, 8, 14, .42);
  color: #fff;
  font: 600 11px/1 var(--lv-font-ui);
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* ПЛИТКА.
   Один компонент на все случаи: раздел, жанр, подборка. Разница
   между ними только в пропорции и в том, есть картинка или нет,
   а это модификаторы, а не отдельные компоненты. Одно время обложка
   подборки жила здесь же вторым именем `.lv-cover` — сведено обратно
   20.09.2026, потому что делало ровно то же самое. */
.lv-tile {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: var(--lv-r-md);
  background: var(--lv-surface-2);
}
.lv-tile--sq { aspect-ratio: 1; }
.lv-tile--45 { aspect-ratio: 4 / 5; border-radius: var(--lv-r-lg); }   /* обложка подборки */
.lv-tile img, .lv-tile__img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--lv-t-drawer) var(--lv-ease-out);
}
.lv-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 40%, rgba(8, 7, 12, .78) 100%);
}
/* Знак вместо картинки. Держим его в верхних двух третях: нижнюю
   займёт название, и там знак уйдёт под затемнение. */
.lv-tile__ico {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 68%;
  display: grid;
  place-items: center;
  color: #fff;
  opacity: .92;
}
.lv-tile__cap {
  position: absolute;
  left: var(--lv-s3); right: var(--lv-s3); bottom: var(--lv-s3);
  z-index: 1;
}
.lv-tile__name { display: block; font: 700 14px/1.2 var(--lv-font-ui); color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lv-tile--45 .lv-tile__name { font: 700 16px/1.2 var(--lv-font-head); letter-spacing: -.01em; }
.lv-tile__n { display: block; margin-top: 2px; font: 400 12px/1.2 var(--lv-font-ui); color: rgba(255, 255, 255, .72); }
@media (hover: hover) and (pointer: fine) {
  .lv-tile:hover .lv-tile__img { transform: scale(1.04); }
  .lv-tile:hover .lv-tile__name { color: var(--lv-accent); }
}
.lv-tile:focus-visible { outline: none; box-shadow: var(--lv-ring); }
/* плитка без картинки: цвет берётся из набора lv-glow */
.lv-tile--glow { background: #120F1A; }
.lv-tile--glow::before {
  content: "";
  position: absolute;
  inset: -30%;
  background:
    radial-gradient(circle at 30% 25%, var(--lv-glow-a, #FF8A18) 0%, transparent 55%),
    radial-gradient(circle at 75% 80%, var(--lv-glow-b, #FB4595) 0%, transparent 55%);
  filter: blur(28px);
  opacity: .85;
}

/* ------------------------------------------------------------
   16. БЛОКИ САЙТА
   Всё, из чего собирается посадочная. В приложении эти классы не
   нужны и не используются: у сервиса свой скелет.

   Главное отличие от экранов — воздух. Секция сайта дышит
   ступенью `--lv-s24` (96), которой в приложении нет. Всё
   остальное то же: одна тень только у всплывающего, один градиент
   на экран, сетки через minmax(0, 1fr).
   ------------------------------------------------------------ */

/* ---------- шапка сайта ---------- */
/* Высота шапки живёт переменной: по ней же считается, на сколько
   отступать при переходе по якорю. Шапка прилипшая, и без отступа
   заголовок секции уезжает под неё — человек нажимает «Вопросы» и
   видит не заголовок, а середину первого ответа. */
:root { --lv-shapka: 64px; }
/* Порог тот же, на котором шапка и так перестраивается (прячется
   меню и сжимаются поля), иначе высота и её содержимое разъедутся. */
@media (max-width: 900px) { :root { --lv-shapka: 56px; } }
:target,
.lv-section[id] { scroll-margin-top: calc(var(--lv-shapka) + var(--lv-s4)); }

.lv-topbar {
  position: sticky; top: 0;
  z-index: var(--lv-z-sticky);
  display: flex;
  align-items: center;
  gap: var(--lv-s6);
  height: var(--lv-shapka);
  padding: 0 var(--lv-s6);
  border-bottom: 1px solid var(--lv-border-2);
  /* Полупрозрачная с размытием: содержимое должно просвечивать,
     иначе шапка отрезает первый экран плотной полосой. */
  background: rgba(12, 11, 16, .72);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.lv-topbar__logo { height: 26px; flex: none; display: block; }
.lv-topbar__nav { display: flex; align-items: center; gap: var(--lv-s5, var(--lv-s4)); }
/* Цель у ссылки шапки была ростом со сами буквы: 14 px по вертикали.
   Добираем её полями до 44, сама строка при этом не двигается, потому
   что ряд выравнивает содержимое по центру. */
.lv-topbar__nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 2px;
  font: 600 14px/1 var(--lv-font-ui);
  color: var(--lv-text-2);
}
@media (hover: hover) and (pointer: fine) { .lv-topbar__nav a:hover { color: var(--lv-text-1); } }
.lv-topbar__acts { margin-left: auto; display: flex; align-items: center; gap: var(--lv-s2); }

/* ---------- первый экран ----------
   Заголовок и подзаголовок берут готовые `.lv-display` и `.lv-lead`:
   заводить им вторые имена незачем. Здесь только раскладка. */
/* Высоту первого экрана задаёт не только текст: под ним живёт волна,
   и на мониторе ей нужен разбег. При одних полях экран выходил 560 px
   и свет упирался в стык со следующей секцией. Минимум в 76vh даёт
   волне место, но не заставляет человека листать вслепую: следующий
   заголовок всё равно заглядывает снизу. */
.lv-first {
  position: relative;
  overflow: hidden;
  /* Колонка, а не сетка. В сетке обёртка с автополями по бокам
     перестаёт растягиваться на всю ширину и садится по содержимому:
     текст первого экрана съезжал к середине и переставал стоять на
     одной вертикали с заголовками остальных секций. Замер показывал
     919 px вместо 1200 и левый край 284 вместо 144. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--lv-s24) 0;
}
/* Обёртка внутри обязательно во всю ширину. У `.lv-wrap` поля по бокам
   стоят `auto` — так она и встаёт по центру. Но в колонке (и в сетке
   тоже) автополе по поперечной оси отменяет растягивание, и обёртка
   садится по содержимому: замер давал 919 px вместо 1200 и левый край
   заголовка 284 вместо 144. С явной шириной автополя снова просто
   центруют. */
.lv-first > .lv-wrap { width: 100%; }
/* Только на большом экране: на телефоне высота и так набирается
   текстом, а лишние поля отодвигали кнопку за нижний край. */
@media (min-width: 901px) {
  .lv-first { min-height: 76vh; }
}
.lv-first__in {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--lv-s12);
  align-items: center;
}
.lv-first__acts { display: flex; flex-wrap: wrap; gap: var(--lv-s3); margin-top: var(--lv-s8); }
.lv-first__art { border-radius: var(--lv-r-lg); overflow: hidden; aspect-ratio: 4 / 3; }
.lv-first__art img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ФОН ПЕРВОГО ЭКРАНА: ЖИВОЙ СВЕТ «МОЕЙ ВОЛНЫ».

   Это тот самый холст, что стоит в ленте приложения: шесть широких
   лент света текут поперёк экрана и просвечивают друг через друга.
   Модуль взят из приложения без единой правки и лежит в
   `assets/wave-art.js`, источник — `luvi-live/js/wave-art.js`.
   Файл не переименован и класс холста оставлен родным (`wave__art`,
   а не `lv-*`), чтобы его можно было пересинхронить копированием,
   когда модуль в приложении поменяется.

   Размытые пятна `.lv-glow` тут стояли и были сняты: они дают мягкое
   свечение, но это не та анимация, что в ленте.
   ------------------------------------------------------------ */
.lv-first__bg {
  position: absolute; inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--lv-bg);
}
/* СЛОЙ РАЗМЫТИЯ.
   Отдельный элемент между фоном и холстом, и вся его работа это
   фильтр. Раньше и насыщенность, и размытие стояли прямо на холсте,
   а на холсте же висит маска (`mask-image`, чтобы низ растворялся).
   WebKit на сочетание «маска и фильтр на одном элементе» отвечает
   тем, что фильтр просто не применяет: на айфоне волна выходила
   резкими лентами, хотя в Chrome была мягкой. Маска осталась на
   холсте, фильтр переехал сюда, и они больше не встречаются.

   Радиус складывается из двух частей. Базовую считает сам скрипт от
   размера холста и кладёт в переменную `--wave-blur` прямо на холст
   (раньше он размывал себя через `ctx.filter`, но этого не умеют
   Safari до 18-й версии и часть мобильных браузеров). Скрипт ставит
   переменную на холсте, а фильтр нам нужен на слое, поэтому значение
   поднимает наверх скрипт страницы — в `--lv-volna-baza`. Вторая
   часть, `--lv-volna-dobavka`, это наша добавка на узких экранах.

   ВАЖНО: базовый радиус не ставить обратно на сам холст. Там маска,
   и WebKit при маске фильтр не применяет. */
.lv-first__volna {
  position: absolute;
  inset: 0;
  pointer-events: none;
  filter: saturate(1.15)
          blur(calc(var(--lv-volna-baza, 0px) + var(--lv-volna-dobavka, 0px)));
}
.lv-first__bg { --lv-volna-dobavka: 0px; }
.lv-first__bg .wave__art {
  position: absolute;
  left: 0; right: 0; top: 0;
  width: 100%; height: 100%;
  display: block;
  pointer-events: none;
  /* Фильтра тут нет намеренно: на холсте маска, а маска и фильтр на
     одном элементе в WebKit не уживаются. Размывает слой-родитель. */
  /* низ растворяем, иначе по краю холста видна прямая линия среза
     и первый экран снова читается как карточка */
  -webkit-mask-image: linear-gradient(0deg, transparent 0%, #000 22%, #000 100%);
  mask-image: linear-gradient(0deg, transparent 0%, #000 22%, #000 100%);
}
/* Текст лежит прямо на свете, и держаться он должен сам. Тень нужна
   не заголовку, а подводке: у заголовка кегль под шестьдесят и он
   читается на чём угодно, а подводка идёт 17 px по самым ярким лентам.
   Замер контраста на телефоне давал 2,9 при норме 4,5. Поэтому ей тень
   плотнее и вес выше обычного. */
.lv-first__h1,
.lv-first .lv-display { text-shadow: 0 2px 20px rgba(8, 6, 14, .55); }
.lv-first__in .lv-lead {
  font-weight: 500;
  color: var(--lv-text-1);
  text-shadow: 0 1px 2px rgba(8, 6, 14, .9), 0 2px 14px rgba(8, 6, 14, .85);
}
/* Настоящий H1 страницы. По размеру это прежний надзаголовок, но в
   двух строках и с поисковым текстом: «Твоя песня готова за минуту»
   осталась крупной фразой, а заголовком первого уровня стало то, что
   человек ищет. Регистр поднимает стиль, в разметке слова обычные. */
.lv-first__h1 {
  margin: 0;
  max-width: 34ch;
  /* У надзаголовка межстрочное равно кеглю: он всегда был в одну
     строку. Здесь строки две, и их надо развести. Разрядку чуть
     убавляем: с обычной десятой доли em вторая строка не влезала в
     телефон и ломалась на третью. */
  line-height: 1.5;
  letter-spacing: .06em;
}

/* На телефоне текст занимает всю ширину, и ленты проходят прямо под
   абзацем. Контраст держится, но читать по светящемуся ядру неудобно,
   поэтому свет опускаем ниже текста. */
/* Базовый радиус скрипт берёт от размера холста в точках (wave-art.js,
   `blur` от меньшей стороны). На мониторе холст крупный и выходит
   мягкое свечение; на телефоне он втрое меньше и ужимается на экран,
   поэтому того же радиуса не хватает — ленты читаются как нарисованные
   мазки. Скрипт общий с приложением, трогать его ради лендинга нельзя,
   поэтому недостающую мягкость добавляем поверх. */
/* ВОЛНА НА ТЕЛЕФОНЕ БЕРЁТ РАЗБЕГ ШИРЕ ЭКРАНА.
   Размах лент модуль считает от меньшей стороны холста. На мониторе
   это 748 точек и ленты расходятся решёткой, каждая видна отдельно;
   на телефоне холст узкий, размах падает до 530, шесть лент налезают
   друг на друга и сливаются в одно пятно. Добавка размытия поверх
   делала из пятна кисель.

   Лечим не размытием, а шириной: холст берём вдвое шире экрана и
   сдвигаем влево на половину лишнего. Модуль упирается в свой предел
   в 1100 точек, то есть считает ровно тот же размах, что и на
   мониторе, а мы видим середину этой картины. Ленты снова читаются
   по отдельности, и добавка размытия больше не нужна. */
@media (max-width: 900px) {
  /* Ширину даём слою, а не холсту. Модуль замеряет именно тот блок,
     в который его положили, и от этого замера считает и размер
     холста, и размах лент. Растянуть сам холст стилем значит
     растянуть прежнюю картинку: лент столько же, они только толще
     и мутнее. Проверено замером: у холста оставался прежний радиус
     5,83 вместо 8,23. */
  .lv-first__volna { inset: 0 auto 0 -50%; width: 200%; }
  .lv-first__bg .wave__art { top: 18%; height: 82%; }
  .lv-first__bg { --lv-volna-dobavka: 0px; }
}

/* Тонкая завеса по самому низу: стык со следующей секцией. */
.lv-first__veil {
  position: absolute; inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(0deg, var(--lv-bg) 0%, rgba(12, 11, 16, 0) 18%);
}

/* СЛАЙДЕР НА ТЕЛЕФОНЕ.
   Ниже 900 сетка карточек становится горизонтальной лентой с
   прилипанием. Девять карточек в столбик съедали два экрана прокрутки;
   лентой они занимают один, а листать её привычнее, чем мотать
   страницу. Лента выходит в края экрана: упираться она должна в
   границу телефона, а не в отступ страницы, иначе не читается как
   лента. Следующая карточка нарочно видна краем, это единственный
   намёк, что тут можно листать.
   ------------------------------------------------------------ */
.lv-slider { position: relative; }
.lv-dots { display: none; }

@media (max-width: 900px) {
  .lv-slider > .lv-grid {
    display: flex;
    gap: var(--lv-s3);
    overflow-x: auto;
    /* Обязательно явно. По умолчанию, когда одна ось получает auto,
       вторая тоже становится прокручиваемой, и лента начинала ездить
       вверх-вниз внутри себя. */
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0 calc(var(--lv-s4) * -1);
    padding: 2px var(--lv-s4);
  }
  .lv-slider > .lv-grid::-webkit-scrollbar { display: none; }
  .lv-slider > .lv-grid > * { flex: 0 0 78%; scroll-snap-align: start; }
  /* Возможности листаются карточками вдвое уже остальных: их
     девять, и в узкой колонке на экран попадает сразу две с
     половиной, а не одна.

     44 %, а не 39: на 39 в колонке оставалось 128 точек, и подпись
     шла по два-три слова в строку, а заголовок в четыре строки.
     Пять лишних процентов дают 147 точек, строки становятся длиннее,
     а карточка от этого ниже, а не выше. Второй карточки на экране
     всё так же видно край, значит понятно, что лента листается. */
  .lv-slider--2 > .lv-grid > * { flex: 0 0 44%; }

  /* Лента в два ряда. Девять карточек одной строкой требуют девяти
     свайпов; два ряда сокращают их до пяти, а высота блока при этом
     не меняется, потому что карточки вдвое ниже ленты не бывают.
     Сетка вместо ряда: только она умеет раскладывать поперёк. */
  .lv-slider--2 > .lv-grid {
    display: grid;
    /* Колонки базовой сетки обязательно сбросить: пока они заданы,
       раскладка поперёк укладывает карточки в них, и ширина выходит
       26 px вместо 78 %. Поймано замером. */
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-template-rows: repeat(2, auto);
    grid-auto-columns: 44%;
    align-items: stretch;
  }
  .lv-slider--2 > .lv-grid > * { flex: none; }

  .lv-dots { display: flex; justify-content: center; gap: 7px; margin-top: var(--lv-s4); }
  .lv-dots i {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--lv-text-3);
    opacity: .34;
    transition: opacity var(--lv-t-hover), background var(--lv-t-hover);
  }
  .lv-dots i.is-on { opacity: 1; background: var(--lv-accent); }
}

/* КАРТОЧКА ВОЗМОЖНОСТИ.
   Порядок как у Suno: сверху заголовок и довод, картинка внизу и
   крупная. Значок сверху читался служебной пометкой, а картинка снизу
   работает наградой: сначала прочитал, потом увидел.
   Высоту держим одинаковой, иначе в сетке из двух столбцов картинки
   встают на разной высоте и ряд рассыпается. */
/* Порядок: сначала кадр, потом заголовок, потом текст.
   Сперва было наоборот, по образцу Suno. Но у них карточка объясняет
   функцию продукта и картинка там доказательство, а у нас она
   украшение. Когда картинка не доказательство, она должна идти
   первой: глаз цепляется за неё и дальше читает. Так же устроены
   наши плитки поводов, и к ним вопросов не было.

   Кадр вертикальный, три к четырём. Горизонтальный в узкой карточке
   занимал четверть высоты и терялся, а основная аудитория мобильная,
   и формат идёт от неё. */
.lv-fcard {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}
.lv-fcard__in { padding: var(--lv-s6); }
.lv-fcard__t { margin: 0; font: 700 23px/1.2 var(--lv-font-head); color: var(--lv-text-1); letter-spacing: -.02em; }
.lv-fcard__d { margin: var(--lv-s3) 0 0; font: 400 15px/1.6 var(--lv-font-ui); color: var(--lv-text-2); }
/* Кадр квадратный на любой ширине, и ровно в этом формате картинки
   нарисованы, поэтому ничего не обрезается; object-fit оставлен
   страховкой. Формат выбран владельцем из трёх: вертикальный 3:4
   растил карточку до 560 px, горизонтальный 3:2 сплющивал предметы
   вроде микрофона и палочки. Квадрат держит и то и другое.
   object-fit оставлен только как страховка. */
.lv-fcard__art {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}
.lv-card__art--tall { aspect-ratio: 3 / 4; }

/* НА ТЕЛЕФОНЕ КАРТОЧКА ВДВОЕ УЖЕ.
   Кадр по-прежнему идёт от края до края карточки, но сама карточка
   занимает не 78 % ширины экрана, а 39 % (см. .lv-slider ниже), и
   квадратный кадр вслед за ней ужимается с 279 точек до 140. Раньше
   он забирал больше половины карточки: было видно картинку и начало
   заголовка, а чтобы дочитать довод, приходилось листать.

   Кегли идут за шириной. В колонке 140 точек заголовок в 21 px
   ложится по два слова в строку, поэтому здесь 17, а текст 13. */
@media (max-width: 900px) {
  .lv-fcard__in { padding: var(--lv-s3); }
  .lv-fcard__t { font-size: 17px; line-height: 1.2; }
  /* Кегль подписи ниже обычного намеренно: колонка у карточки
     128 точек, и на 13 px в строку влезало по два-три слова.
     На 12 px с поджатым межбуквенным строка забирает на треть
     больше слов, и карточка становится ниже. Это подпись под
     заголовком в 17 px, а не основной текст страницы. */
  .lv-fcard__d { margin-top: var(--lv-s2); font-size: 12px; line-height: 1.45; letter-spacing: -.005em; }
}

/* КАРТОЧКА ТРЕКА НА ПОЛКЕ.
   Перенесена из ленты приложения (.shcard в luvi-live/css/app.css),
   чтобы на сайте и в сервисе трек выглядел и вёл себя одинаково.
   Вся карточка это кнопка: тык по центру обложки запускает песню,
   тык по играющей ставит паузу.

   Кружок с треугольником тёмный, а не фирменный розовый: розовый на
   цветной обложке спорит с ней и теряется. Это решение из приложения,
   не менял. Аватарки автора здесь нет намеренно: на 22 px лица
   превращаются в серые пятна.
   ------------------------------------------------------------ */
.lv-shcard {
  display: grid;
  gap: 2px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
  scroll-snap-align: start;
}
.lv-shcard__t {
  font: 700 15px/1.3 var(--lv-font-head);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color var(--lv-t-hover);
}
.lv-shcard__by {
  font: 400 13px/1.3 var(--lv-font-ui);
  color: var(--lv-text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lv-shcard__cov {
  position: relative;
  display: block;
  aspect-ratio: 1;
  margin-bottom: var(--lv-s2);
  border-radius: var(--lv-r-md);
  overflow: hidden;
  background: var(--lv-surface-2);
}
.lv-shcard__cov img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lv-shcard__cov::after {
  content: "";
  position: absolute; inset: 0;
  background: rgba(6, 6, 12, .52);
  opacity: 0;
  transition: opacity var(--lv-t-hover);
  pointer-events: none;
}
.lv-shcard__play {
  position: absolute;
  left: 50%; top: 50%;
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border-radius: var(--lv-r-full);
  background: rgba(8, 8, 14, .72);
  border: 1px solid rgba(255, 255, 255, .16);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  opacity: 0;
  transform: translate(-50%, -50%) scale(.88);
  transition: opacity var(--lv-t-hover), transform var(--lv-t-hover);
}
.lv-shcard__play svg { margin-left: 2px; }   /* треугольник оптически по центру */
.lv-shcard__pause { display: none; }
.lv-shcard__eq { display: none; align-items: flex-end; gap: 3px; height: 16px; }
.lv-shcard__eq i {
  width: 3px; height: 100%;
  border-radius: 2px;
  background: #fff;
  transform-origin: bottom;
  animation: lv-eq .9s ease-in-out infinite;
}
.lv-shcard__eq i:nth-child(2) { animation-delay: .16s; }
.lv-shcard__eq i:nth-child(3) { animation-delay: .32s; }

/* играет: обложка притушена, кнопка видна, в ней пляшет эквалайзер */
.lv-shcard[data-on="1"] .lv-shcard__cov::after { opacity: 1; }
.lv-shcard[data-on="1"] .lv-shcard__play { opacity: 1; transform: translate(-50%, -50%); }
.lv-shcard[data-on="1"] .lv-shcard__play > svg { display: none; }
.lv-shcard[data-on="1"] .lv-shcard__eq { display: inline-flex; }

@media (hover: hover) and (pointer: fine) {
  .lv-shcard:hover .lv-shcard__cov::after { opacity: 1; }
  .lv-shcard:hover .lv-shcard__play { opacity: 1; transform: translate(-50%, -50%); }
  .lv-shcard:hover .lv-shcard__t { color: var(--lv-accent); }
  /* навёл на играющую: вместо эквалайзера пауза, видно, что тык остановит */
  .lv-shcard[data-on="1"]:hover .lv-shcard__eq { display: none; }
  .lv-shcard[data-on="1"]:hover .lv-shcard__pause { display: block; }
}
.lv-shcard:focus-visible { outline: none; box-shadow: var(--lv-ring); border-radius: var(--lv-r-md); }
@media (prefers-reduced-motion: reduce) { .lv-shcard__eq i { animation: none; } }

/* на телефоне курсора нет, поэтому кнопка видна всегда */
@media (hover: none) {
  .lv-shcard__play { opacity: 1; transform: translate(-50%, -50%); }
  .lv-shcard__cov::after { opacity: .28; }
}

/* СЕКЦИЯ НА ПОЛОСЕ.
   Тот же серый, что у поверхностей интерфейса, плюс тонкие линии по
   краям. Длинная страница из одиннадцати секций на одном фоне читается
   сплошным полотном; полоса разбивает её на главы.

   Ставить только туда, где нет карточек: у карточки фон тот же
   surface-1, и на полосе она растворяется. Годятся цифры, вопросы,
   шаги, бегущая строка. И не больше двух полос на страницу, иначе
   вместо глав получается тельняшка.
   ------------------------------------------------------------ */
.lv-section--band {
  background: var(--lv-surface-1);
  border-top: 1px solid var(--lv-border-2);
  border-bottom: 1px solid var(--lv-border-2);
}
/* Две полосы подряд читаются одной главой, поэтому шов между ними
   убираем. И линию подвала тоже: иначе на стыке выходит двойная
   черта в один пиксель друг от друга. */
.lv-section--band + .lv-section--band { border-top: 0; }
.lv-section--band + .lv-footer { border-top: 0; }

/* ЛИПКИЙ ПРИЗЫВ.
   Появляется, когда первый экран с его кнопкой уже уехал наверх, и
   прячется, когда до последнего призыва доехали: две кнопки об одном
   и том же на одном экране мешают друг другу.

   На телефоне полоса во всю ширину внизу, в зоне большого пальца.
   На большом экране это плавающая пилюля по центру: полоса во всю
   ширину монитора выглядит баннером, а её и так хватает.
   Отступ снизу учитывает вырез у айфона через safe-area.
   ------------------------------------------------------------ */
.lv-sticky {
  position: fixed;
  left: 0; right: 0;
  bottom: 0;
  z-index: var(--lv-z-sticky);
  display: flex;
  justify-content: center;
  padding: var(--lv-s3) var(--lv-s4);
  padding-bottom: calc(var(--lv-s3) + env(safe-area-inset-bottom, 0px));
  pointer-events: none;
  opacity: 0;
  translate: 0 120%;
  transition: opacity .28s ease, translate .34s var(--lv-ease-out);
}
.lv-sticky.is-on { opacity: 1; translate: 0 0; }
.lv-sticky__in {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--lv-s4);
  max-width: 100%;
  padding: 10px 10px 10px var(--lv-s6);
  border: 1px solid var(--lv-border-1);
  border-radius: var(--lv-r-full);
  background: rgba(21, 20, 27, .82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: var(--lv-shadow-overlay);
}
.lv-sticky__t { font: 600 15px/1.2 var(--lv-font-ui); color: var(--lv-text-1); white-space: nowrap; }

/* НА БОЛЬШОМ ЭКРАНЕ ЭТО ПИЛЮЛЯ ПО НИЗУ ОКНА.
   Карточка у правого края тут стояла и снята по решению владельца:
   она перекрывала правую колонку блоков и тянула взгляд вбок посреди
   чтения. Низ окна спокойнее — там её видно, но она не спорит с
   содержимым, и путь до неё одинаковый на любом экране.

   Полосу во всю ширину монитора не делаем: она читается баннером
   согласия на куки. Здесь пилюля по центру, шириной по содержимому.
   Слева живой знак, чтобы пилюля не выглядела картинкой. */
@media (min-width: 701px) {
  .lv-sticky {
    padding: var(--lv-s4) var(--lv-s6) var(--lv-s6);
    padding-bottom: calc(var(--lv-s6) + env(safe-area-inset-bottom, 0px));
  }
  .lv-sticky__in { gap: var(--lv-s4); padding: 10px 10px 10px var(--lv-s4); }
}

/* ФИРМЕННОЕ СЕРДЦЕ-ЭКВАЛАЙЗЕР.
   Тот же знак, что в логотипе и на аватарке: одиннадцать полос, торцы
   которых обводят сердце. Геометрия не придумана и не посчитана по
   формуле — она снята попиксельно с логотипа (Luvi/СОЦ СЕТИ/аватарка/
   сгенерировать.py), поэтому знак совпадает с фирменным один в один.
   Система координат знака 222 x 216, её и держит viewBox.

   Полосы дышат вокруг своей середины, а не растут снизу: так силуэт
   сердца остаётся читаемым на любом кадре. Размах намеренно
   небольшой — при большом знак рассыпается в случайный частокол.
   Пары полос слева и справа дышат в такт: сердце симметрично, и
   вразнобой оно перестаёт им быть.

   Сверху удар сердца: знак целиком коротко подскакивает дважды, как
   настоящий пульс, и потом держит паузу. Масштаб живёт на обёртке, а
   не на самом svg: два преобразования на одном элементе пришлось бы
   писать одной анимацией.
   ------------------------------------------------------------ */
.lv-serdce {
  flex: none;
  display: block;
  width: var(--lv-serdce-r, 40px);
  animation: lv-serdce-udar 2.6s ease-in-out infinite;
}
.lv-serdce__z { display: block; width: 100%; height: auto; overflow: visible; }
.lv-serdce__z rect {
  fill: url(#lv-serdce-grad);
  transform-box: fill-box;
  transform-origin: center;
  animation: lv-serdce-dyshit 1.05s ease-in-out infinite alternate;
}
/* Счёт по типу, а не по порядку среди соседей: первым ребёнком в svg
   стоит defs с градиентом, и обычный nth-child сдвинул бы всю пару. */
.lv-serdce__z rect:nth-of-type(1),
.lv-serdce__z rect:nth-of-type(11) { animation-delay: -.86s; }
.lv-serdce__z rect:nth-of-type(2),
.lv-serdce__z rect:nth-of-type(10) { animation-delay: -.52s; }
.lv-serdce__z rect:nth-of-type(3),
.lv-serdce__z rect:nth-of-type(9)  { animation-delay: -.18s; }
.lv-serdce__z rect:nth-of-type(4),
.lv-serdce__z rect:nth-of-type(8)  { animation-delay: -.68s; }
.lv-serdce__z rect:nth-of-type(5),
.lv-serdce__z rect:nth-of-type(7)  { animation-delay: -.34s; }
@keyframes lv-serdce-dyshit {
  from { transform: scaleY(.78); }
  to   { transform: scaleY(1); }
}
@keyframes lv-serdce-udar {
  0%, 62%, 100% { transform: scale(1); }
  8%            { transform: scale(1.1); }
  16%           { transform: scale(1); }
  24%           { transform: scale(1.06); }
  32%           { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .lv-serdce,
  .lv-serdce__z rect { animation: none; }
}

@media (max-width: 700px) {
  /* На телефоне уходит серая коробка с рамкой вокруг кнопки: она
     читалась как ещё одна панель поверх страницы и отбирала у кнопки
     половину веса. Вместо неё тёмная полоса в цвет страницы, а на ней
     обычная наша кнопка-пилюля с полями по краям. Кнопка во всю
     кромку без полей тут уже стояла и выглядела не кнопкой, а плитой,
     приклеенной к низу экрана. Сверху полоса растворяется, чтобы
     содержимое уезжало под неё, а не обрывалось по линии. */
  .lv-sticky {
    padding: var(--lv-s8) var(--lv-s6) var(--lv-s3);
    padding-bottom: calc(var(--lv-s3) + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(0deg,
      var(--lv-bg) 0%, var(--lv-bg) 64%, rgba(12, 11, 16, 0) 100%);
  }
  .lv-sticky__in {
    width: 100%;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }
  /* Знак и подпись на телефоне не показываем: в полосе живёт одна
     кнопка во всю её ширину, и слева от неё места нет. */
  .lv-sticky__t,
  .lv-sticky .lv-serdce { display: none; }
  /* Кнопка во всю ширину полосы, но с обычным скруглением кита: от
     края экрана её отделяют поля самой полосы. Вырез айфона учтён
     полосой, а не кнопкой, иначе подпись внутри уезжала вниз. */
  .lv-sticky .lv-btn {
    width: 100%;
    height: auto;
    min-height: 52px;
    font-size: 16px;
    padding: var(--lv-s3) var(--lv-s4);
  }
}
@media (prefers-reduced-motion: reduce) {
  .lv-sticky { transition: opacity .28s ease; translate: 0 0; }
}

/* СВЕТ ПОД КУРСОРОМ.
   Мягкое пятно фирменного цвета идёт за мышью внутри карточки. Приём
   общеизвестный: радиальный градиент в позиции курсора, живёт в вёрстке
   лет десять. Написан здесь свой, чужой код не взят — у React Bits,
   откуда пришла идея посмотреть в эту сторону, лицензия MIT с Commons
   Clause, и распространять их компоненты, в том числе портированные,
   нельзя. Наш пакет публикуется и уходит наружу, поэтому в нём должно
   лежать только наше.

   Позицию пишет JS в --mx и --my. Пока мыши не было, пятно спрятано:
   иначе оно висело бы в углу карточки при первой отрисовке.
   Включается только там, где есть настоящая мышь: на телефоне курсора
   нет, а лишний слой был бы.
   ------------------------------------------------------------ */
.lv-spot { position: relative; }
.lv-spot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .34s ease;
  background: radial-gradient(260px circle at var(--mx, 50%) var(--my, -20%),
              rgba(253, 66, 156, .15), rgba(253, 66, 156, .05) 38%, transparent 66%);
}
@media (hover: hover) and (pointer: fine) {
  .lv-spot:hover::after { opacity: 1; }
}

/* ПОЯВЛЕНИЕ ПО ПРОКРУТКЕ.
   Блок въезжает снизу, когда дошёл до экрана. Класс навешивает JS, а не
   вёрстка: если скрипт не сработает, всё просто видно сразу, а не
   остаётся прозрачным навсегда. Так уже появляются цифры, здесь то же
   правило доведено до остальной страницы.
   ------------------------------------------------------------ */
.lv-rise {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .6s var(--lv-ease-out), transform .6s var(--lv-ease-out);
}
.lv-rise.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .lv-rise { opacity: 1; transform: none; transition: none; }
}

/* КОЛЛАЖ-ПОЛОСА.
   Три горизонтальных кадра в ряд, читаются как одна широкая панель.
   Скругление и обрезка живут на контейнере, а не на каждом кадре:
   так у полосы круглые внешние углы и прямые внутренние стыки,
   и она не рассыпается на три отдельные карточки. */
.lv-collage {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  border-radius: var(--lv-r-lg);
  overflow: hidden;
}
.lv-collage figure { margin: 0; aspect-ratio: 4 / 3; }
.lv-collage img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- секция ---------- */
.lv-section { padding: var(--lv-s24) 0; }
.lv-section--tight { padding: var(--lv-s16) 0; }
.lv-section__head { max-width: 62ch; margin: 0 0 var(--lv-s12); }
.lv-section--center .lv-section__head { margin-left: auto; margin-right: auto; text-align: center; }
/* Нижняя граница поднята с 26 до 31 px: на телефоне заголовок секции
   отличался от обычного текста только начертанием, и страница читалась
   сплошняком без глав. */
.lv-section__title {
  margin: 6px 0 0;
  font: 800 clamp(31px, 3.4vw, 40px)/1.1 var(--lv-font-head);
  letter-spacing: -.025em;
  text-wrap: balance;
}
.lv-section__lead { margin: var(--lv-s3) 0 0; max-width: 68ch; font: 400 16px/1.6 var(--lv-font-ui); color: var(--lv-text-2); }

/* ---------- шаги «как это работает» ---------- */
.lv-steps {
  display: grid;
  gap: var(--lv-s8);
  grid-template-columns: repeat(var(--lv-cols, 3), minmax(0, 1fr));
  counter-reset: lv-step;
}
.lv-step { counter-increment: lv-step; }
.lv-step__n {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  border-radius: var(--lv-r-full);
  background: var(--lv-surface-2);
  border: 1px solid var(--lv-border-2);
  font: 700 16px/1 var(--lv-font-head);
  color: var(--lv-text-1);
  font-variant-numeric: tabular-nums;
}
.lv-step__n::before { content: counter(lv-step); }
.lv-step__t { margin: var(--lv-s4) 0 0; font: 700 18px/1.25 var(--lv-font-head); }
.lv-step__d { margin: 6px 0 0; font: 400 14px/1.6 var(--lv-font-ui); color: var(--lv-text-2); max-width: 40ch; }

/* ---------- тариф ---------- */
.lv-price {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--lv-s6);
  border: 1px solid var(--lv-border-2);
  border-radius: var(--lv-r-lg);
  background: var(--lv-surface-1);
}
.lv-price--hot { border-color: var(--lv-accent); }
/* Градиентная плашка — одно из трёх принятых исключений из правила
   «один градиент на экран». Второй градиент на странице тарифов это
   кнопка выбранного тарифа, и больше ничего. */
.lv-price__badge {
  position: absolute;
  top: -11px; left: var(--lv-s6);
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border-radius: var(--lv-r-full);
  background: var(--lv-grad);
  color: #fff;
  font: 700 11px/1 var(--lv-font-ui);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.lv-price__name { font: 700 16px/1.2 var(--lv-font-head); color: var(--lv-text-2); }
.lv-price__sum {
  margin: var(--lv-s3) 0 0;
  font: 800 36px/1 var(--lv-font-head);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.lv-price__sum span { font: 400 14px/1 var(--lv-font-ui); color: var(--lv-text-3); }
.lv-price__note { margin: 6px 0 0; font: 400 13px/1.4 var(--lv-font-ui); color: var(--lv-text-3); }
.lv-price__feats { margin: var(--lv-s6) 0; padding: 0; display: grid; gap: var(--lv-s3); list-style: none; }
.lv-price__feats li { display: flex; gap: var(--lv-s2); font: 400 14px/1.5 var(--lv-font-ui); color: var(--lv-text-2); }
.lv-price__feats .lv-ico { flex: none; margin-top: 2px; color: var(--lv-success); }
.lv-price__feats li.is-off { color: var(--lv-text-3); }
.lv-price__feats li.is-off .lv-ico { color: var(--lv-text-3); }
.lv-price > .lv-btn { margin-top: auto; }

/* ---------- частые вопросы ---------- */
.lv-acc { border-top: 1px solid var(--lv-border-2); }
.lv-acc__item { border-bottom: 1px solid var(--lv-border-2); }
.lv-acc__btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--lv-s4);
  width: 100%;
  padding: var(--lv-s4) 0;
  border: 0;
  background: none;
  color: var(--lv-text-1);
  font: 700 16px/1.4 var(--lv-font-head);
  text-align: left;
  cursor: pointer;
}
.lv-acc__btn .lv-ico { flex: none; color: var(--lv-text-3); transition: transform var(--lv-t-hover); }
.lv-acc__btn[aria-expanded="true"] .lv-ico { transform: rotate(180deg); }
.lv-acc__btn:focus-visible { outline: none; box-shadow: var(--lv-ring); border-radius: var(--lv-r-sm); }
.lv-acc__body {
  padding: 0 0 var(--lv-s4);
  max-width: 68ch;
  font: 400 15px/1.6 var(--lv-font-ui);
  color: var(--lv-text-2);
}
.lv-acc__body[hidden] { display: none; }

/* ---------- отзыв ---------- */
.lv-quote {
  padding: var(--lv-s6);
  border: 1px solid var(--lv-border-2);
  border-radius: var(--lv-r-lg);
  background: var(--lv-surface-1);
}
.lv-quote__text { margin: 0; font: 400 16px/1.6 var(--lv-font-ui); color: var(--lv-text-1); }
.lv-quote__who { display: flex; align-items: center; gap: var(--lv-s3); margin-top: var(--lv-s4); }
.lv-quote__ava {
  flex: none;
  display: grid;
  place-items: center;
  width: 36px; height: 36px;
  border-radius: var(--lv-r-full);
  background: var(--lv-surface-3);
  object-fit: cover;
  font: 700 14px/1 var(--lv-font-head);
  color: var(--lv-text-2);
}
.lv-quote__name { display: block; font: 600 14px/1.3 var(--lv-font-ui); }
.lv-quote__sub { display: block; font: 400 12px/1.3 var(--lv-font-ui); color: var(--lv-text-3); }

/* ---------- полоса призыва ---------- */
.lv-cta {
  position: relative;
  overflow: hidden;
  padding: var(--lv-s16) var(--lv-s8);
  border-radius: var(--lv-r-lg);
  text-align: center;
}
.lv-cta__title {
  margin: 0;
  font: 800 clamp(31px, 3vw, 40px)/1.15 var(--lv-font-head);
  letter-spacing: -.02em;
  color: #fff;
  text-wrap: balance;
}
.lv-cta__lead {
  margin: var(--lv-s3) auto 0;
  max-width: 52ch;
  font: 400 16px/1.6 var(--lv-font-ui);
  color: rgba(255, 255, 255, .78);
}
.lv-cta__acts { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--lv-s3); margin-top: var(--lv-s8); }

/* ПОЛОСА СВЕТА ВО ВСЮ ШИРИНУ.
   Тот же свет, что в карточке, но подложка — вся секция: углов нет,
   края уходят за экран. Пятна при этом надо раздувать. Кружки по
   420 px сидели в середине широкого монитора, а по бокам оставалась
   ровная тёмная плита, и блок читался как карточка, которой забыли
   скруглить углы. Здесь они считаются от ширины экрана. */
.lv-glow--full { border-radius: 0; }
.lv-glow--full::before {
  width: 62vw; height: 62vw; max-width: 900px; max-height: 900px;
  left: -10vw; top: -34%;
}
.lv-glow--full::after {
  width: 64vw; height: 64vw; max-width: 940px; max-height: 940px;
  right: -12vw; bottom: -38%;
}
.lv-glow--full .lv-glow__c {
  width: 50vw; height: 50vw; max-width: 720px; max-height: 720px;
  left: 38%; top: -26%;
}
/* Секция-призыв. Поля меньше обычной секции: внутри всего три строки,
   и общие 96 px сверху и снизу оставляли посреди света пустоту. */
.lv-cta-sec { padding: var(--lv-s16) 0; }
/* На телефоне секция низкая и узкая, и пятна в 62vw висели по углам
   почти целиком за краем: оставалась тёмная плита с цветными уголками.
   Раздуваем их до ширины экрана и подтягиваем к середине. */
@media (max-width: 900px) {
  .lv-cta-sec { padding: var(--lv-s12) 0; }
  .lv-glow--full::before { width: 100vw; height: 100vw; left: -34vw; top: -40%; }
  .lv-glow--full::after { width: 104vw; height: 104vw; right: -38vw; bottom: -44%; }
  .lv-glow--full .lv-glow__c { width: 82vw; height: 82vw; left: 22%; top: -30%; }
}

/* ЭКРАН ПРИЛОЖЕНИЯ С РАЗЛЕТАЮЩИМИСЯ МЕТКАМИ.
   Снимок «Ленты» из рабочего приложения в рамке телефона, а вокруг
   него отлетают в стороны метки: что происходит с треком после того,
   как его выложили. Метки не считают слушателей и ничего не обещают,
   это названия самих действий в ленте.

   Рамка рисуется не картинкой, а рамкой и скруглением: картинка
   телефона на тёмном фоне всегда выдаёт чужой рендер, а так экран
   остаётся нашим. Низ снимка растворяется маской, иначе видно, что
   он обрезан.
   ------------------------------------------------------------ */
.lv-vitrina {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--lv-s16);
  align-items: center;
}
.lv-vitrina__ekran {
  position: relative;
  justify-self: center;
  width: min(300px, 78vw);
}
/* На большом экране телефон прижат к левому краю своей колонки.
   По центру колонки он упирался в липкую карточку у правого края
   окна, и та закрывала метку «Моя волна», а на 1280 наезжала и на
   сам экран. */
@media (min-width: 901px) {
  .lv-vitrina__ekran { justify-self: start; margin-left: var(--lv-s8); }
}
.lv-vitrina__ramka {
  position: relative;
  z-index: 1;
  padding: 10px;
  border: 1px solid var(--lv-border-1);
  border-radius: 38px;
  background: var(--lv-surface-1);
  box-shadow: var(--lv-shadow-overlay);
}
/* Низ обрезается рамкой, без растворения. Маска тут стояла и снята:
   она гасила снимок в цвет рамки, и казалось, что картинка
   выцветает. Телефон и должен обрезать содержимое по краю экрана. */
.lv-vitrina__ramka img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 28px;
}
/* Метки лежат поверх рамки и уходят за её края. */
.lv-metka {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: var(--lv-s2);
  padding: 8px 14px;
  border: 1px solid var(--lv-border-1);
  border-radius: var(--lv-r-full);
  background: rgba(21, 20, 27, .92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--lv-shadow-overlay);
  font: 600 13px/1.2 var(--lv-font-ui);
  color: var(--lv-text-1);
  white-space: nowrap;
  /* Плавают по кругу вокруг своей точки: каждая со своей задержкой,
     иначе шесть меток качаются в такт и выглядят одной деталью. */
  animation: lv-metka-plyvet 7s ease-in-out infinite alternate;
  animation-delay: var(--zaderzhka, 0s);
}
.lv-metka svg { color: var(--lv-accent); flex: none; }
@keyframes lv-metka-plyvet {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(var(--dx, 6px), var(--dy, -10px), 0); }
}
/* Появление: метки выплывают из-за экрана по очереди, когда блок
   попал в поле зрения. До этого их не видно совсем. */
.lv-metka { opacity: 0; }
.lv-vitrina__ekran.is-in .lv-metka {
  opacity: 1;
  transition: opacity .6s var(--lv-ease-out) var(--pauza, 0s);
}
@media (prefers-reduced-motion: reduce) {
  .lv-metka { animation: none; }
}

/* Четыре точки вокруг экрана. Отступы в процентах от ширины самого
   экрана, а не в точках: экран ужимается вместе с окном, и метки
   должны уезжать за его край ровно настолько же. Сдвиги и задержки
   у всех разные, иначе четыре метки качаются в такт. */
.lv-metka--a { top: -3%; left: -13%;  --dx: -7px;  --dy: -11px; --zaderzhka: 0s;    --pauza: .05s; }
.lv-metka--b { top: 29%; right: -11%; --dx: 9px;   --dy: -8px;  --zaderzhka: -1.6s; --pauza: .2s; }
.lv-metka--v { top: 55%; left: -15%;  --dx: -10px; --dy: 7px;   --zaderzhka: -3.1s; --pauza: .35s; }
.lv-metka--g { top: 77%; right: -9%;  --dx: 8px;   --dy: 10px;  --zaderzhka: -4.4s; --pauza: .5s; }

@media (max-width: 900px) {
  .lv-vitrina { grid-template-columns: minmax(0, 1fr); gap: var(--lv-s12); }
  .lv-vitrina__ekran { width: min(268px, 66vw); }
  .lv-metka { font-size: 12px; padding: 7px 12px; }
  /* На телефоне по бокам от экрана остаётся точек сорок, и прежние
     отступы выносили метки за край страницы: появлялась прокрутка
     вбок. Здесь они почти не вылезают. */
  .lv-metka--a { left: -7%; }
  .lv-metka--b { right: -6%; }
  .lv-metka--v { left: -8%; }
  .lv-metka--g { right: -5%; }
}

/* МУЛЬТИТРЕК: ПЕСНЯ, РАЗОБРАННАЯ НА ДОРОЖКИ.
   Повторяет экран «Сведение» из приложения: имя дорожки, её волна,
   громкость. Волна нарисована столбиками, а не картинкой, потому что
   дорожек шесть и каждая своего цвета — картинкой это полтора
   мегабайта, а так пара килобайт разметки.

   Движение одно и честное: по дорожкам идёт игла, и позади неё волна
   загорается в полный цвет. Столбики нарочно не прыгают: у волны
   форма постоянная, прыгают полосы эквалайзера, а это другое.
   Поэтому анимаций всего семь на весь блок, а не по одной на каждый
   из двухсот столбиков.
   ------------------------------------------------------------ */
.lv-mult {
  position: relative;
  border: 1px solid var(--lv-border-1);
  border-radius: var(--lv-r-lg);
  background: var(--lv-surface-1);
  padding: var(--lv-s6);
  overflow: hidden;
}
.lv-mult__shapka {
  display: flex;
  align-items: baseline;
  gap: var(--lv-s3);
  margin-bottom: var(--lv-s6);
}
.lv-mult__nazv { font: 700 17px/1.2 var(--lv-font-head); color: var(--lv-text-1); }
.lv-mult__meta { font: 400 13px/1.2 var(--lv-font-ui); color: var(--lv-text-3); }

.lv-mult__lenty { display: grid; gap: var(--lv-s2); position: relative; }

.lv-mult__d {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) 44px;
  align-items: center;
  gap: var(--lv-s3);
  padding: var(--lv-s2) var(--lv-s3);
  border-radius: var(--lv-r-md);
  background: color-mix(in srgb, var(--cvet) 9%, transparent);
}
.lv-mult__imya { font: 600 13px/1.2 var(--lv-font-ui); color: var(--cvet); }
.lv-mult__gr { font: 400 12px/1.2 var(--lv-font-ui); color: var(--lv-text-3); text-align: right; }

.lv-mult__volna { position: relative; height: 34px; }
.lv-mult__stolb {
  position: absolute; inset: 0;
  display: flex;
  align-items: center;
  gap: 2px;
}
.lv-mult__stolb i {
  flex: 1 1 0;
  min-width: 0;
  height: var(--v, 40%);
  border-radius: 1px;
  background: var(--cvet);
  opacity: .28;
}
/* Копия поверх, обрезанная слева направо: это и есть сыгранная часть. */
.lv-mult__stolb--yark i { opacity: 1; }
.lv-mult__stolb--yark {
  clip-path: inset(0 100% 0 0);
  animation: lv-mult-igra 9s linear infinite;
}
@keyframes lv-mult-igra {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
/* Игла идёт по всем дорожкам сразу, поэтому лежит на их общей коробке. */
.lv-mult__igla {
  position: absolute;
  top: 0; bottom: 0;
  left: 0;
  width: 2px;
  border-radius: var(--lv-r-full);
  background: var(--lv-text-1);
  box-shadow: 0 0 12px rgba(255, 255, 255, .55);
  animation: lv-mult-igla 9s linear infinite;
  pointer-events: none;
}
@keyframes lv-mult-igla {
  from { left: 96px; }
  to   { left: calc(100% - 44px); }
}
@media (prefers-reduced-motion: reduce) {
  .lv-mult__stolb--yark { animation: none; clip-path: inset(0 34% 0 0); }
  .lv-mult__igla { animation: none; left: 66%; }
}

.lv-mult__niz {
  margin-top: var(--lv-s6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--lv-s3);
  align-items: center;
}
.lv-mult__kn {
  display: inline-flex;
  align-items: center;
  gap: var(--lv-s2);
  height: 38px;
  padding: 0 16px;
  border: 1px solid var(--lv-border-1);
  border-radius: var(--lv-r-full);
  font: 600 13px/1 var(--lv-font-ui);
  color: var(--lv-text-2);
}

@media (max-width: 700px) {
  .lv-mult { padding: var(--lv-s4); }
  /* Имя дорожки уезжает над волной: в колонке 96 px на телефоне
     не остаётся места самой волне. */
  .lv-mult__d { grid-template-columns: 62px minmax(0, 1fr); }
  .lv-mult__gr { display: none; }
  .lv-mult__imya { font-size: 11px; }
  .lv-mult__volna { height: 26px; }
  @keyframes lv-mult-igla {
    from { left: 62px; }
    to   { left: 100%; }
  }
}

/* ---------- ряд площадок ---------- */
.lv-logos { display: flex; flex-wrap: wrap; align-items: center; gap: var(--lv-s6) var(--lv-s8); }
.lv-logos > span {
  display: inline-flex;
  align-items: center;
  gap: var(--lv-s2);
  font: 600 14px/1 var(--lv-font-ui);
  color: var(--lv-text-3);
}
.lv-logos .lv-ico { color: var(--lv-text-2); }

/* ---------- подвал ---------- */
.lv-footer { padding: var(--lv-s16) 0 var(--lv-s8); border-top: 1px solid var(--lv-border-2); }
.lv-footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: var(--lv-s8);
}
.lv-footer__col h4 {
  margin: 0 0 var(--lv-s3);
  font: 700 11px/1 var(--lv-font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--lv-text-3);
}
/* Поля вместо высоты строки: цель вырастает до 44, а список остаётся
   плотным, потому что поля наложены друг на друга отрицательным
   отступом. На телефоне ниже тот же приём. */
.lv-footer__col a {
  display: flex;
  align-items: center;
  min-height: 44px;
  margin: -9px 0;
  padding: 9px 0;
  font: 400 14px/1.4 var(--lv-font-ui);
  color: var(--lv-text-2);
}
@media (hover: hover) and (pointer: fine) { .lv-footer__col a:hover { color: var(--lv-text-1); } }
.lv-footer__note { margin: var(--lv-s3) 0 0; font: 400 13px/1.6 var(--lv-font-ui); color: var(--lv-text-3); max-width: 38ch; }
.lv-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--lv-s4);
  margin-top: var(--lv-s12);
  padding-top: var(--lv-s6);
  border-top: 1px solid var(--lv-border-2);
  font: 400 13px/1.4 var(--lv-font-ui);
  color: var(--lv-text-3);
}

/* ---------- бегущая строка ----------
   Два ряда навстречу друг другу. Содержимое внутри ряда продублировано
   вручную, и лента едет ровно на половину: только так шов не виден. */
.lv-ticker {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.lv-ticker__row {
  display: flex;
  gap: var(--lv-s2);
  width: max-content;
  animation: lv-ticker 46s linear infinite;
}
.lv-ticker__row + .lv-ticker__row { margin-top: var(--lv-s2); animation-duration: 58s; animation-direction: reverse; }
.lv-ticker__row > span {
  flex: none;
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 20px;
  border-radius: var(--lv-r-full);
  background: var(--lv-surface-2);
  border: 1px solid var(--lv-border-2);
  color: var(--lv-text-2);
  font: 600 15px/1 var(--lv-font-ui);
  white-space: nowrap;
}
@keyframes lv-ticker { to { transform: translateX(-50%); } }

/* ---------- карточка с картинкой ---------- */
.lv-card--art { padding: 0; overflow: hidden; }
/* height: auto обязателен. Атрибуты width и height на картинке
   браузер превращает в настоящие правила CSS, и заданная высота
   отменяет aspect-ratio: карточка вытягивалась с 222 до 625 px.
   Атрибуты при этом нужны, иначе место под картинку не зарезервировано
   и вёрстка прыгает при загрузке. */
.lv-card__art { display: block; width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: cover; }
.lv-card__in { padding: var(--lv-s6); }
.lv-card__eyebrow {
  display: block;
  margin-bottom: 8px;
  font: 700 11px/1 var(--lv-font-ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--lv-accent);
}

/* ---------- полка с прокруткой вбок ----------
   Для рядов, которые длиннее экрана: треки, обложки, площадки.
   Полоса прокрутки убрана, но сама прокрутка на месте. */
.lv-rail {
  display: flex;
  gap: var(--lv-s4);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  padding-bottom: 4px;
}
.lv-rail::-webkit-scrollbar { display: none; }
/* Ширина карточки задана, а не растянута по месту: иначе восемь
   обложек делят ряд и съезжают до 130 px, где название обрезается на
   втором слове. В сервисе полка 148 px, здесь чуть шире: на
   посадочной обложка это доказательство, а не строка каталога.
   Ряд при этом перестаёт влезать целиком, и это правильно: видно,
   что треков больше. */
.lv-rail > * { scroll-snap-align: start; flex: 0 0 180px; }
@media (max-width: 900px) { .lv-rail > * { flex-basis: 150px; } }

/* ---------- карточка трека ---------- */
/* КАРТОЧКА ТРЕКА НА ПОЛКЕ.
   Анатомия взята с Suno: высокая обложка два к трём, треугольник в
   левом верхнем углу без кружка, одна плашка с прослушиваниями снизу,
   под обложкой название, ниже аватарка и имя автора.

   Кнопкой сделана вся карточка, а треугольник только метка. Так тык
   по центру обложки запускает песню (цель заведомо больше 44 px), а
   на вид карточка остаётся такой же чистой, как у них.

   Обложки у всех авторов разные, поэтому плашки полупрозрачные с
   размытием: сплошная заливка на светлой обложке выглядит заплаткой.
   ------------------------------------------------------------ */
.lv-trackcard {
  flex: none;
  width: 260px;
  display: block;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.lv-trackcard__cov {
  position: relative;
  display: block;
  aspect-ratio: 2 / 3;
  border-radius: var(--lv-r-md);
  overflow: hidden;
  background: var(--lv-surface-2);
}
.lv-trackcard__cov img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* затемнение при наведении и во время игры: без него белый
   треугольник теряется на светлой обложке */
.lv-trackcard__cov::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(6, 6, 12, .34) 0%, rgba(6, 6, 12, 0) 34%);
  pointer-events: none;
}
.lv-trackcard__play {
  position: absolute; left: 12px; top: 12px;
  display: grid; place-items: center;
  width: 26px; height: 26px;
  color: #fff;
  filter: drop-shadow(0 1px 4px rgba(0, 0, 0, .6));
  pointer-events: none;
}
.lv-trackcard__eq { display: none; align-items: flex-end; gap: 3px; height: 16px; }
.lv-trackcard__eq i {
  width: 3px; height: 100%;
  border-radius: 2px;
  background: #fff;
  transform-origin: bottom;
  animation: lv-eq .9s ease-in-out infinite;
}
.lv-trackcard__eq i:nth-child(2) { animation-delay: .16s; }
.lv-trackcard__eq i:nth-child(3) { animation-delay: .32s; }
/* играет: обложка притушена, вместо треугольника пляшет эквалайзер */
.lv-trackcard[data-on="1"] .lv-trackcard__cov::after {
  background: linear-gradient(180deg, rgba(6, 6, 12, .42) 0%, rgba(6, 6, 12, .28) 100%);
}
.lv-trackcard[data-on="1"] .lv-trackcard__play > svg { display: none; }
.lv-trackcard[data-on="1"] .lv-trackcard__eq { display: inline-flex; }

.lv-trackcard__stats { position: absolute; left: 12px; bottom: 12px; display: flex; gap: 6px; }
.lv-trackcard__stats span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 28px;
  padding: 0 10px;
  border-radius: var(--lv-r-full);
  background: rgba(10, 8, 14, .58);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  font: 600 13px/1 var(--lv-font-ui);
  font-variant-numeric: tabular-nums;
}
.lv-trackcard__t {
  display: block;
  margin: var(--lv-s3) 0 0;
  font: 700 17px/1.3 var(--lv-font-head);
  color: var(--lv-text-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color var(--lv-t-hover);
}
.lv-trackcard__by { display: flex; align-items: center; gap: 8px; margin-top: 8px; min-width: 0; }
.lv-trackcard__by img { flex: none; width: 24px; height: 24px; border-radius: var(--lv-r-full); object-fit: cover; }
.lv-trackcard__by span { font: 400 14px/1.3 var(--lv-font-ui); color: var(--lv-text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (hover: hover) and (pointer: fine) {
  .lv-trackcard:hover .lv-trackcard__t { color: var(--lv-accent); }
  .lv-trackcard:hover .lv-trackcard__cov::after {
    background: linear-gradient(180deg, rgba(6, 6, 12, .46) 0%, rgba(6, 6, 12, .18) 100%);
  }
}
.lv-trackcard:focus-visible { outline: none; }
.lv-trackcard:focus-visible .lv-trackcard__cov { box-shadow: var(--lv-ring); }
@media (prefers-reduced-motion: reduce) { .lv-trackcard__eq i { animation: none; } }

/* ---------- баннер с кадром ---------- */
.lv-banner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .85fr);
  align-items: stretch;
  overflow: hidden;
  border: 1px solid var(--lv-border-2);
  border-radius: var(--lv-r-lg);
  background: var(--lv-surface-1);
}
.lv-banner__in { padding: var(--lv-s12); align-self: center; }
.lv-banner__art { position: relative; min-height: 300px; }
.lv-banner__art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }
/* Кадр уходит в фон плавно, иначе на стыке получается шов. */
.lv-banner__art::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--lv-surface-1) 0%, rgba(21, 20, 27, 0) 45%);
}

/* ---------- крупные числа ---------- */
.lv-stats { display: grid; grid-template-columns: repeat(var(--lv-cols, 3), minmax(0, 1fr)); gap: var(--lv-s8); text-align: center; }
.lv-stats__n {
  display: block;
  font: 800 clamp(38px, 6vw, 68px)/1 var(--lv-font-head);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--lv-text-1);
}
/* Цифры набираются фирменным градиентом. Единственное место, где
   градиент попал в буквы: в своей секции он больше ни с чем не спорит,
   а цифры и есть тот самый акцент, ради которого секция стоит.
   Под @supports, потому что без поддержки обрезки фона прозрачная
   заливка оставила бы пустое место вместо чисел. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .lv-stats__n {
    background: var(--lv-grad);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
  }
}
.lv-stats__c { display: block; margin-top: 8px; font: 400 14px/1.4 var(--lv-font-ui); color: var(--lv-text-3); }
/* Число доезжает до значения, когда его увидели. Ширину держит
   `tabular-nums` на самом числе: без неё строка дёргается на каждом
   кадре, потому что цифры разной ширины. */
.lv-stats__n { font-variant-numeric: tabular-nums; }
.lv-stats__i { opacity: 0; transform: translateY(10px); transition: opacity .5s var(--lv-ease-out), transform .5s var(--lv-ease-out); }
.lv-stats__i.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .lv-stats__i { opacity: 1; transform: none; transition: none; }
}

@media (max-width: 900px) {
  .lv-ticker__row > span { height: 40px; padding: 0 16px; font-size: 14px; }
  .lv-trackcard { width: 200px; }
  /* пальцем от 44, как и везде ниже 900 */
  .lv-trackcard__play { left: 10px; top: 10px; }
  .lv-trackcard__stats { left: 10px; bottom: 10px; }
  .lv-trackcard__t { font-size: 16px; }
  /* длинное название лучше перенести, чем обрезать многоточием */
  .lv-trackcard__t { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  .lv-banner { grid-template-columns: minmax(0, 1fr); }
  .lv-banner__in { padding: var(--lv-s6); order: 2; }
  .lv-banner__art { min-height: 260px; order: 1; }
  .lv-banner__art::after { background: linear-gradient(0deg, var(--lv-surface-1) 0%, rgba(21, 20, 27, 0) 55%); }
  .lv-stats { --lv-cols: 1; gap: var(--lv-s6); }
}
@media (prefers-reduced-motion: reduce) { .lv-ticker__row { animation: none; } }

@media (max-width: 900px) {
  .lv-topbar { padding: 0 var(--lv-s4); gap: var(--lv-s3); }
  .lv-topbar__nav { display: none; }
  .lv-first, .lv-section { padding: var(--lv-s16) 0; }
  .lv-first__in { grid-template-columns: minmax(0, 1fr); gap: var(--lv-s8); }
  .lv-section__head { margin-bottom: var(--lv-s8); }
  .lv-steps { grid-template-columns: minmax(0, 1fr); gap: var(--lv-s6); }
  /* Кружок 44 px над каждым заголовком добавлял на телефоне почти
     двести пикселей пустоты на три шага. Номер уходит в строку
     с заголовком и становится ростом со строку. */
  .lv-step {
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr);
    column-gap: var(--lv-s3);
    row-gap: 6px;
    align-items: center;
  }
  .lv-step__n { width: 28px; height: 28px; font-size: 13px; }
  .lv-step__t { grid-column: 2; margin: 0; font-size: 17px; }
  .lv-step__d { grid-column: 2; margin: 0; }
  /* Полоса остаётся полосой и на телефоне: три кадра в ряд. Чтобы они
     не выродились в тонкую ленту, на узком экране берём квадрат
     вместо горизонтали. */
  .lv-collage { gap: 4px; }
  .lv-collage figure { aspect-ratio: 1; }
  .lv-cta { padding: var(--lv-s12) var(--lv-s4); }
  /* ПОДВАЛ НА ТЕЛЕФОНЕ.
     Ссылке нужна цель под палец, но правило min-height: 44px растащило
     список так, что между строками оказывалось по сорок пустых
     пикселей, и подвал занимал целый экран. Цель добираем невидимым
     полем через отрицательный отступ: палец попадает в 44 px, а на
     глаз строки идут плотно. */
  .lv-footer { padding: var(--lv-s12) 0 var(--lv-s6); }
  .lv-footer__top { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--lv-s8) var(--lv-s6); }
  .lv-footer__col:first-child { grid-column: 1 / -1; }
  .lv-footer__col a {
    display: flex;
    align-items: center;
    min-height: 44px;
    margin: -10px 0;
    padding: 10px 0;
    font-size: 15px;
  }
  .lv-footer__bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--lv-s2);
    margin-top: var(--lv-s8);
    padding-top: var(--lv-s4);
  }
  .lv-acc__btn { padding: var(--lv-s4) 0; }
}

/* ------------------------------------------------------------
   17. РАСКЛАДКА
   ------------------------------------------------------------ */
.lv-wrap { max-width: 1200px; margin: 0 auto; padding: 0 var(--lv-s6); }
.lv-stack > * + * { margin-top: var(--lv-s4); }
.lv-stack--s2 > * + * { margin-top: var(--lv-s2); }
.lv-stack--s6 > * + * { margin-top: var(--lv-s6); }
.lv-stack--s12 > * + * { margin-top: var(--lv-s12); }
.lv-line { display: flex; align-items: center; gap: var(--lv-s3); }
.lv-line--between { justify-content: space-between; }
/* У `1fr` минимум равен ширине содержимого, поэтому одна широкая
   таблица раздувает колонку и уводит вбок всю страницу.
   В системе допустим только `minmax(0, 1fr)` */
.lv-grid { display: grid; gap: var(--lv-s4); grid-template-columns: repeat(var(--lv-cols, 3), minmax(0, 1fr)); }
.lv-grid--2 { --lv-cols: 2; }
.lv-grid--4 { --lv-cols: 4; }
.lv-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--lv-s4);
  margin-bottom: var(--lv-s6);
}

/* ------------------------------------------------------------
   18. ТЕЛЕФОН
   Одна точка излома — 900 px. Всё, во что тычут пальцем, от 44 px;
   плеер и таб-бар из этого правила исключены, у них своя сетка.
   ------------------------------------------------------------ */
@media (max-width: 900px) {
  .lv-h1 { font-size: 24px; }
  .lv-hero__title { font-size: 32px; }
  .lv-hero__play { width: 84px; height: 84px; }
  .lv-btn { height: 48px; }
  .lv-btn--sm { height: 40px; }
  .lv-btn--icon { width: 44px; height: 44px; }
  .lv-field__input { height: 48px; }
  .lv-chip { min-height: 44px; }
  .lv-seg { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
  .lv-seg::-webkit-scrollbar { display: none; }
  .lv-seg__item { flex: none; min-height: 44px; }
  .lv-menu__item { min-height: 44px; }
  .lv-row { grid-template-columns: 56px minmax(0, 1fr) auto; gap: var(--lv-s3); padding: 0 var(--lv-s2); }
  .lv-row__time { display: none; }
  .lv-wrap { padding: 0 var(--lv-s4); }
  .lv-grid { --lv-cols: 2; }
  .lv-grid--4 { --lv-cols: 2; }
  /* шторка снизу: сбоку на телефоне ей негде взяться */
  .lv-sheet {
    top: auto; left: 0;
    width: 100%;
    height: 92vh;
    border-left: 0;
    border-top: 1px solid var(--lv-border-1);
    border-radius: var(--lv-r-lg) var(--lv-r-lg) 0 0;
    animation-name: lv-slide-up;
  }
  @keyframes lv-slide-up { from { transform: translateY(100%); } }
  .lv-modal { width: calc(100vw - 32px); }
  /* пальцем от 44: коробка выбора не растёт, растёт площадь нажатия */
  .lv-select__btn { height: 48px; }
  .lv-check, .lv-radio, .lv-switch { padding: 12px 0; }
  .lv-choices { gap: 0; }
  .lv-search__clear { width: 44px; height: 44px; }
  .lv-search .lv-field__input { padding-left: 42px; padding-right: 48px; }
  .lv-select__opt { height: 44px; }
}

/* Второй порог для сеток. На планшете две колонки ещё держатся, на
   телефоне уже нет: замер дал карточку в 171 px, и заголовок
   «Творчество и эксперименты» не помещался в неё ни одной строкой.
   Ниже 560 любая сетка карточек становится в одну колонку. */
@media (max-width: 560px) {
  .lv-grid, .lv-grid--2, .lv-grid--4 { --lv-cols: 1; }
}
