/* =========================================================================
   landing.css — флагманский лендинг: семь типов секций, появление блоков,
   закреплённая сцена, параллакс, видео.

   Подключается только страницей Landing.cshtml, то есть моделью с флагом
   Template=Landing и заведёнными секциями. Остальным страницам сайта эти
   правила не нужны и не грузятся.

   ЧТО ТУТ ВАЖНО ПОНЯТЬ ПРО АНИМАЦИИ (объяснение для не-фронтендера).

   Браузер рисует страницу в три этапа: считает раскладку (кто где стоит и
   какого размера), рисует пиксели, склеивает слои. Первый этап — самый
   дорогой: меняешь ширину блока — браузер пересчитывает положение всего,
   что ниже.

   Поэтому анимировать можно далеко не всё. Дёшево меняются ровно два
   свойства: transform (сдвиг, поворот, масштаб) и opacity (прозрачность) —
   их браузер отдаёт видеокарте и не трогает раскладку вообще. Дорого — всё
   остальное: width, height, top, left, margin, padding.

   Ниже все анимации сделаны на transform и opacity. Ни одна не двигает
   элемент через left/top, и это не стилистика, а единственный способ
   получить 60 кадров в секунду на телефоне за 1 500 000 сум.

   КЛАССЫ РЕЖИМА. landing.js вешает на <html> один из трёх классов:

     motion-full — всё включено;
     motion-lite — слабое устройство или экономия трафика: появление блоков
                   есть, параллакса и закреплённых сцен нет, видео не
                   стартует само;
     motion-off  — система просит «меньше движения»: ничего не двигается.

   Правила ниже опираются на эти классы, а не на медиазапросы, потому что
   про экономию трафика и число ядер процессора CSS ничего не знает.
   ========================================================================= */

/* ---------- 1. Общее для всех секций ------------------------------------- */

.landing {
    /*
       Секции идут вплотную друг к другу, без зазора: у лендингов Huawei экраны
       склеены в непрерывную ленту, и полоска фона между ними разрушила бы
       ощущение цельной истории.
    */
    display: block;
}

/*
   Базовая коробка секции. Фон приходит инлайновой переменной --section-bg
   из базы, значение по умолчанию — прозрачный, то есть белый фон страницы.

   isolation: isolate создаёт секции собственный контекст наложения. Без него
   абсолютные слои соседних секций могут накладываться друг на друга при
   отрицательных отступах.
*/
.ls {
    position: relative;
    isolation: isolate;
    background: var(--section-bg, transparent);
}

/* Отступы одинаковы у всех текстовых секций — герой и сцена задают свои. */
.ls--divider,
.ls--textimage,
.ls--carousel,
.ls--video,
.ls--footnotes {
    padding-block: 48px;
}

@media (min-width: 768px) {
    .ls--divider,
    .ls--textimage,
    .ls--carousel,
    .ls--video,
    .ls--footnotes {
        padding-block: 80px;
    }
}

/* Тон текста поверх картинки — светлый или тёмный, как у баннеров. */
.ls.tone-dark { color: var(--c-heading); }

.ls.tone-light {
    color: var(--c-inverse);
}

/* Под светлым текстом мягкая тень: на пёстром кадре даже белый теряется. */
.ls.tone-light .ls-hero__inner,
.ls.tone-light .ls-scene__inner {
    text-shadow: 0 1px 14px rgba(0, 0, 0, 0.4);
}

/*
   Светлый тон сцены рассчитан на подписи ПОВЕРХ кадра — это закреплённый
   режим (is-pinned). Без закрепления (телефон, «меньше движения», слабое
   устройство, нет JS) подписи стоят под картинкой на фоне страницы,
   и белый текст на белом не виден вовсе. Поэтому вне закрепления текст
   тёмный — если только у секции нет своего фона ([style*="background"]),
   на котором светлый текст и задуман. Найдено владельцем 07.10 на nova 14 Pro.
*/
.ls--scene.tone-light:not(.is-pinned):not([style*="background"]) {
    color: var(--c-heading);
}

.ls--scene.tone-light:not(.is-pinned):not([style*="background"]) .ls-scene__inner {
    text-shadow: none;
}

/* Надзаголовок — мелкая строка над крупным текстом: «КАМЕРА», «БАТАРЕЯ». */
.ls-hero__subtitle,
.ls-divider__kicker,
.ls-ti__kicker,
.ls-car__kicker,
.ls-video__kicker,
.ls-scene__kicker {
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.75;
}

/* Подзаголовок героя поверх картинки: полупрозрачный белый на светлом
   кадре почти исчезал (Pura 80 Pro, проверка 07.10). Поверх картинки он
   непрозрачный — тень под текстом уже есть у всего блока. */
.ls.tone-light .ls-hero__subtitle {
    opacity: 1;
}

.landing__notice {
    padding-block: 12px;
    color: var(--c-muted);
}

.landing__specs {
    padding-block: 24px;
    text-align: center;
}

/* ---------- 2. Появление блока при въезде в вьюпорт ---------------------- */

/*
   ПРИЁМ 1 из analysis/05-flagship.md — самый частый и самый дешёвый.

   Как работает. Элемент лежит прозрачным и сдвинутым на 24 пикселя вниз.
   Скрипт reveal.js через IntersectionObserver узнаёт от браузера, что секция
   доехала до экрана, и вешает на неё класс is-visible. CSS доводит
   прозрачность до 1, а сдвиг до нуля — за 0.6 секунды по фирменной кривой
   Huawei.

   IntersectionObserver, а не обработчик прокрутки: обработчик вызывался бы
   сотни раз в секунду и каждый раз спрашивал бы у браузера координаты
   элемента (а это пересчёт раскладки). Наблюдатель же сам сообщает о факте
   «элемент вошёл в кадр» и не стоит почти ничего.

   Важнейшая деталь — селектор начинается с .js-reveal. Этот класс вешает
   на <html> сам reveal.js. Пока скрипт не отработал, ни одного правила
   ниже не существует, и текст просто виден. Не загрузился JS, отключён
   JS, упала сеть на середине — страница читается. У эталона наоборот:
   там начальное состояние задано в CSS, и без скрипта половина лендинга
   остаётся невидимой навсегда (analysis/07-gaps.md).

   Почему [data-reveal="1"], а не просто [data-reveal]. Razor выводит атрибут
   с пустым значением (data-reveal=""), а не опускает его, когда выражение
   в значении равно null. Селектор по факту наличия атрибута ловил бы и такие
   секции — в частности первый экран, которому анимация появления не нужна:
   он виден сразу при открытии страницы, и прятать его на 0.6 секунды значит
   задерживать самый крупный текст страницы. Найдено измерением в браузере.
*/
.js-reveal [data-reveal="1"] [data-reveal-group] > * {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}

.js-reveal [data-reveal="1"].is-visible [data-reveal-group] > * {
    opacity: 1;
    transform: none;
}

/*
   Лесенка: заголовок, подзаголовок, текст выезжают друг за другом с шагом
   80 мс. Одновременное появление читается как «моргнуло», последовательное —
   как движение.
*/
.js-reveal [data-reveal="1"].is-visible [data-reveal-group] > *:nth-child(2) { transition-delay: 0.08s; }
.js-reveal [data-reveal="1"].is-visible [data-reveal-group] > *:nth-child(3) { transition-delay: 0.16s; }
.js-reveal [data-reveal="1"].is-visible [data-reveal-group] > *:nth-child(4) { transition-delay: 0.24s; }

/* ---------- 3. Герой ------------------------------------------------------ */

/*
   Первый экран лендинга: медиа во всю площадь, текст поверх. Устройство
   такое же, как у баннера главной, но выше — это единственный экран, а не
   один из девяти.

   Высота переменной, а не числом: она разная на трёх брейкпоинтах, и
   переменную видно в одном месте.
*/
/* Не выше окна: над героем стоят шапка, крошки и панель модели (около
   110px). При 780px на ноутбуке 1366×768 заголовок h1 — название модели —
   оказывался ниже края экрана (проверка этапа 8). */
.ls--hero {
    height: min(var(--hero-h, 560px), max(460px, calc(100svh - var(--header-h) - 110px)));
    overflow: hidden;
    background: var(--section-bg, var(--c-surface));
}

@media (min-width: 768px) {
    .ls--hero { --hero-h: 680px; }
}

@media (min-width: 1080px) {
    .ls--hero { --hero-h: 780px; }
}

/*
   ГЕРОЙ В ПРОПОРЦИЯХ КАДРА (09.10) — то же правило, что у баннеров главной
   (home.css). Вьюха передаёт пропорции кадра на каждом диапазоне ширины,
   коробка берёт их через aspect-ratio и зажимается только высотой окна.
   Было: фиксированные 780px — на мониторе 2560 кадр 2:1 терял по 19%
   сверху и снизу, у Band 11 Pro срезался браслет; на вертикальном
   планшете кадр nova 14 Pro терял 29% сверху (детектор обрезки, 09.10).
   Нижний предел 460px — чтобы на узком окне текст поверх кадра
   помещался. Ролик-герой переменных не получает и живёт по старому
   правилу высоты.
*/
.ls--hero[style*="--ar-d"] {
    --ar: var(--ar-d);
    --hero-max: max(460px, calc(100svh - var(--header-h) - 110px));
    height: auto;
    /* Ширина — строго по родителю. Без этого min-height пересчитывается
       через aspect-ratio в минимальную ширину: баннер 10:3 с нижним
       пределом 720px требовал 2400px и давал горизонтальную прокрутку. */
    width: 100%;
    min-width: 0;
    max-width: 100%;
    aspect-ratio: var(--ar);
    min-height: min(460px, var(--hero-max));
    max-height: var(--hero-max);
}

@media (max-width: 1079.98px) {
    .ls--hero[style*="--ar-d"] { --ar: var(--ar-t); }
}

@media (max-width: 1079.98px) and (orientation: portrait) {
    .ls--hero[style*="--ar-d"] { --ar: var(--ar-tp); }
}

@media (max-width: 767.98px) {
    .ls--hero[style*="--ar-d"] { --ar: var(--ar-m); }
}

.ls-hero__media {
    position: absolute;
    inset: 0;
}

/*
   object-fit: cover — медиа заполняет коробку целиком и обрезается по краям.
   Для полноэкранного кадра это единственный правильный режим: пропорции
   экрана посетителя мы не знаем, а растянутый по одной оси кадр выглядит
   сломанным.
*/
.ls-hero__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/*
   КАДР ГЕРОЯ ЦЕЛИКОМ (09.10).

   Коробка героя — в пропорциях кадра (см. выше), но не выше окна. Если
   окно ниже кадра, обложка срезала товар: у Pura 90s Pro на мониторе
   владельца (1692×830) телефон терял верх камеры и низ корпуса — 29% кадра.
   Теперь кадр вписывается целиком (contain), а поля по бокам закрашивает
   landing.js: он берёт цвета крайних столбцов самого кадра (сверху вниз)
   и продолжает их градиентом — розовый фон Pura 90s Pro, бархат Pura 80
   Pro, серо-белый фон WATCH 5 тянутся дальше без шва. Пробовали размытую
   копию кадра — в поля затекали тёмные пятна от самого товара у края.
   Цвет секции тоже не годится: фон снимка бывает двухцветным.
   Если пропорции окна и кадра совпадают, полей нет вовсе.
   Без скрипта поля — цвет секции или светло-серый.
*/
.ls--hero[style*="--ar-d"] .ls-hero__img,
.ls--hero[style*="--ar-d"] .lvideo__el--cover {
    object-fit: contain;
}

/*
   ПОЛЯ ГЕРОЯ БЕЗ ШВА (10.10). Классы ставит landing.js по краям кадра:
   - hero-anchor-r / -l — товар уходит за один край файла (ремешок WATCH 5
     за правый): кадр прижат к этому краю окна, и товар уходит за край
     экрана; поле — с другой, ровной стороны;
   - hero-feather — оба края пёстрые, это фотография (Pura 90s Pro,
     MatePad): края кадра мягко растворяются в поля, иначе по границе
     кадра шла вертикальная линия и предметы у края обрывались по ней.
     Ширину поля (--hf-l) и растворения (--hf-w) скрипт пересчитывает
     при смене размера окна.
*/
.ls-hero__media.hero-anchor-r .ls-hero__img,
.ls-hero__media.hero-anchor-r .lvideo__el--cover { object-position: 100% 50%; }

.ls-hero__media.hero-anchor-l .ls-hero__img,
.ls-hero__media.hero-anchor-l .lvideo__el--cover { object-position: 0% 50%; }

.ls-hero__media.hero-feather.has-fields .ls-hero__img,
.ls-hero__media.hero-feather.has-fields .lvideo__el--cover {
    -webkit-mask-image: linear-gradient(to right,
        transparent var(--hf-l),
        #000 calc(var(--hf-l) + var(--hf-w)),
        #000 calc(100% - var(--hf-l) - var(--hf-w)),
        transparent calc(100% - var(--hf-l)));
    mask-image: linear-gradient(to right,
        transparent var(--hf-l),
        #000 calc(var(--hf-l) + var(--hf-w)),
        #000 calc(100% - var(--hf-l) - var(--hf-w)),
        transparent calc(100% - var(--hf-l)));
}

.ls-hero__content {
    position: absolute;
    inset: 0;
    display: flex;
    /* Клики проходят сквозь текстовый слой на медиа: у героя нет кнопок,
       а невидимый слой поверх видео мешал бы нажать на плеер. */
    pointer-events: none;
}

.ls-hero__inner {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
    width: 100%;
    padding-block: 48px;
}

/* Положение текста — из поля TextPosition, три варианта. */
.ls--hero.pos-top .ls-hero__content { align-items: flex-start; }
.ls--hero.pos-center .ls-hero__content { align-items: center; }
.ls--hero.pos-bottom .ls-hero__content { align-items: flex-end; }

/* «Справа» — как у баннера главной: на узком экране внизу, с 1080px —
   правая половина кадра по центру. */
.ls--hero.pos-right .ls-hero__content,
.ls--hero.pos-left .ls-hero__content { align-items: flex-end; }

/* Текстовая колонка «справа» начинается с 62% ширины окна, а не с середины:
   кадр героя растянут на всю ширину (cover), и товар на ключевых кадрах
   Huawei заходит за середину. У nova 14 Pro рамка корпуса пересекала строку
   подзаголовка. Край товара у nova 14 Pro — до ~60,6% ширины окна на любом
   экране от 1080 до 2560 (кадр 2:1 вписывается обложкой то по ширине, то по
   высоте), отсюда 62% (проверка этапа 8 на мониторах 1692 и 2545). */
@media (min-width: 1080px) {
    .ls--hero.pos-right .ls-hero__content { align-items: center; }
    /* Процент — от ширины окна, а колонка текста — контейнер не шире 1440px
       по центру. На мониторе 2560 «56%» давали отступ 1425px внутри
       контейнера 1440: подзаголовок стоял по слову в строке и вылезал за
       правый край (этап 8, второй монитор владельца). Вычитаем поле слева
       от контейнера — проценты в calc считаются от той же ширины окна. */
    .ls--hero.pos-right .ls-hero__inner { padding-left: calc(62% - max(0px, (100% - 1440px) / 2)); }
    .ls--hero.pos-right .ls-hero__title { font-size: 52px; }

    /* «Слева» (10.10) — зеркало «Справа»: товар у правого края кадра
       (WATCH 5, ремешок уходит за край экрана), текст — в левой трети. */
    .ls--hero.pos-left .ls-hero__content { align-items: center; }
    .ls--hero.pos-left .ls-hero__inner { padding-right: calc(60% - max(0px, (100% - 1440px) / 2)); }
    .ls--hero.pos-left .ls-hero__title { font-size: 52px; }
}

@media (min-width: 1440px) {
    .ls--hero.pos-right .ls-hero__title,
    .ls--hero.pos-left .ls-hero__title { font-size: 64px; }
}

.ls-hero__title {
    font-size: 34px;
    font-weight: 700;
    line-height: 1.1;
}

.ls-hero__text {
    font-size: 16px;
    max-width: 40ch;   /* длинная строка поверх картинки читается плохо */
}

@media (min-width: 768px) {
    .ls-hero__title { font-size: 52px; }
    .ls-hero__text { font-size: 18px; }
}

@media (min-width: 1080px) {
    .ls-hero__title { font-size: 64px; }
}

/* ---------- 4. Разделитель ------------------------------------------------ */

/*
   Ширина блока — в пикселях, а абзаца — в знаках. 46ch считается от шрифта
   самого элемента (16px), это около 400px: заголовок в 44px в такую колонку
   ломался на четыре строки по слову («Идеальные / снимки, / которые /
   вдохновляют», проверка 07.10). Заголовку — 860px, абзацу — 46 знаков:
   длиннее строка читается хуже.
*/
.ls-divider__inner {
    max-width: 860px;
    margin-inline: auto;
    text-align: center;
}

.ls-divider__text {
    max-width: 46ch;
    margin-inline: auto;
}

.ls-divider__title {
    margin-top: 8px;
    font-size: 28px;
    font-weight: 700;
    line-height: 1.15;
}

.ls-divider__text {
    margin-top: 16px;
    font-size: 16px;
    opacity: 0.8;
}

@media (min-width: 768px) {
    .ls-divider__title { font-size: 44px; }
    .ls-divider__text { font-size: 18px; }
}

/* ---------- 5. Текст и картинка ------------------------------------------ */

.ls-ti__inner {
    display: grid;
    gap: 24px;
    align-items: center;
}

@media (min-width: 1080px) {
    .ls-ti__inner {
        grid-template-columns: 1fr 1fr;
        gap: 56px;
    }

    /*
       pos-bottom means «сначала картинка». Меняем местами не разметку,
       а порядок отрисовки: order переставляет колонки визуально, оставляя
       текст первым в коде — для скринридера и поисковика.
    */
    .ls--textimage.pos-bottom .ls-ti__text { order: 2; }
    .ls--textimage.pos-bottom .ls-ti__media { order: 1; }
}

.ls-ti__title {
    margin-top: 8px;
    font-size: 26px;
    font-weight: 700;
    line-height: 1.2;
}

.ls-ti__body {
    margin-top: 12px;
    font-size: 16px;
    opacity: 0.85;
}

@media (min-width: 768px) {
    .ls-ti__title { font-size: 36px; }
    .ls-ti__body { font-size: 18px; }
}

/*
   Коробка картинки: скруглённые углы и подложка цвета секции. overflow:
   hidden здесь только ради скругления — сама картинка в коробку входит
   целиком и ничем не обрезается.
*/
.ls-ti__media {
    margin: 0;
    overflow: hidden;
    border-radius: 16px;
    /* Подложка — цвет секции: на чёрных секциях Pura 80 Pro светлая
       подложка просвечивала по скруглённым углам кадра (09.10). */
    background: var(--section-bg, var(--c-surface));
}

.ls-ti__parallax {
    display: block;
}

.ls-ti__img {
    width: 100%;
    height: auto;
}

/*
   Вертикальный кадр не выше окна (09.10). Экран и батарея FIT 5 Pro
   (1108×1834) в колонке 672px выходили выше 1000px: ни в одном положении
   прокрутки часы не были видны целиком. Высота кадра — не больше окна под
   шапкой и панелью модели, ширина подстраивается сама (width: auto), а
   коробка с подложкой обтягивает кадр (justify-self: center) — без пустой
   серой рамки сбоку.
*/
@media (min-width: 1080px) {
    .ls-ti__media {
        justify-self: center;
    }

    .ls-ti__img {
        width: auto;
        max-height: calc(100svh - var(--header-h) - 57px - 96px);
    }
}

/*
   ПРИЁМ 3 — параллакс.

   Кадр вместе со своей коробкой плывёт по вертикали в пределах ±24
   пикселей по ходу прокрутки — медленнее, чем текст рядом, и это даёт
   ощущение глубины.

   Раньше двигалась картинка ВНУТРИ коробки, а чтобы сдвиг не открыл
   пустую полосу, она была увеличена на 12% — и коробка срезала по 6%
   с каждой стороны. У снимков товара, нарезанных Huawei впритык,
   пропадали ремешок часов, угол планшета, край корпуса (замечание
   владельца 09.10: «обрезанные картинки, заметно по самому продукту»).
   Теперь кадр виден целиком всегда.

   Значение --p (0 в начале секции, 1 в конце) выставляет landing.js.
   Дальше вся математика — на CSS: calc умножает число на пиксели.
   Скрипт не трогает ни один пиксель раскладки, он только меняет число.

   will-change: transform — подсказка браузеру «этот элемент будет
   двигаться, вынеси его на отдельный слой заранее». Без неё первый кадр
   анимации дёргается. Ставим её ровно на те элементы, которые реально
   двигаются: слой в памяти видеокарты стоит денег, и раздавать подсказку
   всем подряд — известный способ замедлить страницу вместо ускорения.

   Только в режиме motion-full: на слабом устройстве параллакс — первое,
   от чего надо отказаться. Он даёт меньше всего смысла на единицу работы.
*/
.motion-full .ls--textimage[data-parallax] .ls-ti__media {
    will-change: transform;
    transform: translate3d(0, calc(24px - var(--p, 0.5) * 48px), 0);
}

/*
   ТОВАР УХОДИТ ЗА КРАЙ КАДРА — СЕКЦИЯ-ПОЛОСА (10.10).

   Huawei режет снимки товара впритык: ремешок часов уходит за верх и низ
   файла, кабель — за низ, корпус телефона — за бок. В скруглённой плашке
   с полями такой ремешок обрывался ровной линией посреди страницы —
   это и было «обрезанное по самому продукту» из замечания владельца
   (FIT 5 Pro, FIT 5, Band 11 Pro, «Экран» Pura 80 Pro, «Батарея» nova).

   Какой край задет, сервер узнаёт по пикселям самого файла
   (ImageEdgeLayout → классы edge-d-* для компьютера и edge-m-* для
   телефона и планшета; t, r, b, l — верх, право, низ, лево) и ставит
   is-band. Тогда:
   - секция — сплошная полоса цвета фона кадра (его вьюха кладёт в
     --section-bg), плашки и скругления у кадра нет: край файла
     растворяется в полосе;
   - задетые верх и низ прижимаются к краю полосы — ремешок «проходит»
     через неё, как на лендингах Huawei;
   - где прижать нельзя (бок кадра посреди страницы, верх кадра под текстом
     на телефоне), край мягко растворяется маской: товар не обрывается,
     а уходит в фон. Маска — это не обрезка: прозрачность растёт плавно
     на последних 16–18% кадра, и фон под ним того же цвета.
   Работает для любой картинки, которую загрузит сотрудник, без настроек.
*/
.ls--textimage.is-band .ls-ti__media {
    overflow: visible;
    border-radius: 0;
    background: transparent;
}

.ls--textimage.is-band .ls-ti__img {
    display: block;
    --ft: 0%;
    --fr: 0%;
    --fb: 0%;
    --fl: 0%;
    -webkit-mask-image:
        linear-gradient(to bottom, transparent, #000 var(--ft), #000 calc(100% - var(--fb)), transparent),
        linear-gradient(to right, transparent, #000 var(--fl), #000 calc(100% - var(--fr)), transparent);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to bottom, transparent, #000 var(--ft), #000 calc(100% - var(--fb)), transparent),
        linear-gradient(to right, transparent, #000 var(--fl), #000 calc(100% - var(--fr)), transparent);
    mask-composite: intersect;
}

/* Целый товар, нарезанный впритык (is-pad): планшет MatePad упирался
   рамкой в верх плашки и выглядел срезанным. Плашка — цвета фона самого
   кадра (у PNG без фона — прежний светло-серый), с полями внутри: товар
   получает воздух, а шва между кадром и плашкой нет. */
.ls--textimage.is-pad .ls-ti__media {
    padding: clamp(20px, 5%, 40px);
    background: var(--ti-pad-bg, var(--c-surface));
}

/* Телефон и планшет — одна колонка: текст, под ним кадр. */
@media (max-width: 1079.98px) {
    /* Низ задет — кадр последним блоком стоит вплотную к низу полосы. */
    .ls--textimage.is-band.edge-m-b {
        padding-bottom: 0;
    }

    /* Верх задет — над кадром текст, прижать некуда: растворяем. */
    .ls--textimage.is-band.edge-m-t .ls-ti__img {
        --ft: 18%;
    }

    /* Бок задет — кадр во всю ширину экрана, от края до края: на телефоне
       это естественно, и боковой край файла совпадает с краем экрана. */
    .ls--textimage.is-band.edge-m-l .ls-ti__media,
    .ls--textimage.is-band.edge-m-r .ls-ti__media {
        margin-inline: calc(var(--gutter) * -1);
    }

    .ls--textimage.is-band.edge-m-l .ls-ti__img,
    .ls--textimage.is-band.edge-m-r .ls-ti__img {
        width: 100%;
        max-width: none;
    }
}

/* Компьютер — две колонки. */
@media (min-width: 1080px) {
    .ls--textimage.is-band.edge-d-t { padding-top: 0; }
    .ls--textimage.is-band.edge-d-b { padding-bottom: 0; }

    .ls--textimage.is-band.edge-d-t .ls-ti__media { align-self: start; }
    .ls--textimage.is-band.edge-d-b .ls-ti__media { align-self: end; }
    .ls--textimage.is-band.edge-d-t.edge-d-b .ls-ti__media { align-self: center; }

    /* Поля полосы ушли — воздух вокруг текста держит сама колонка текста. */
    .ls--textimage.is-band.edge-d-t .ls-ti__text,
    .ls--textimage.is-band.edge-d-b .ls-ti__text {
        padding-block: 48px;
    }

    /* Бок кадра стоит посреди страницы — растворяем его. */
    .ls--textimage.is-band.edge-d-l .ls-ti__img { --fl: 16%; }
    .ls--textimage.is-band.edge-d-r .ls-ti__img { --fr: 16%; }

    /* Секция со своим фоном (is-fade) — соседи обычно того же цвета, край
       полосы не виден, поэтому верх и низ тоже растворяем. */
    .ls--textimage.is-band.is-fade.edge-d-t .ls-ti__img,
    .ls--textimage.is-band.same-prev.edge-d-t .ls-ti__img { --ft: 18%; }
    .ls--textimage.is-band.is-fade.edge-d-b .ls-ti__img,
    .ls--textimage.is-band.same-next.edge-d-b .ls-ti__img { --fb: 18%; }
}

/* Сосед того же цвета (same-prev / same-next ставит landing.js) — край
   полосы не виден, растворяем и прижатый край. */
@media (max-width: 1079.98px) {
    .ls--textimage.is-band.is-fade.edge-m-b .ls-ti__img,
    .ls--textimage.is-band.same-next.edge-m-b .ls-ti__img { --fb: 18%; }
}

/* Картинка во всю ширину с текстом поверх — третий вариант раскладки.
   Только на широком экране: на телефоне текст поверх картинки нечитаем. */
@media (min-width: 768px) {
    .ls--textimage.pos-center .ls-ti__inner {
        display: block;
        position: relative;
    }

    .ls--textimage.pos-center .ls-ti__text {
        position: absolute;
        left: 0;
        bottom: 32px;
        max-width: 42ch;
        padding-inline: 32px;
    }
}

/* ---------- 6. Карусель --------------------------------------------------- */

.ls-car__head {
    margin-bottom: 24px;
}

.ls-car__title {
    margin-top: 8px;
    font-size: 26px;
    font-weight: 700;
}

@media (min-width: 768px) {
    .ls-car__title { font-size: 36px; }
}

/*
   ЛЕНТА СЛАЙДОВ БЕЗ БИБЛИОТЕКИ.

   scroll-snap-type: x mandatory — «при остановке прокрутки довести ближайший
   слайд до точки привязки». Эти две строки заменяют весь механизм
   перелистывания Swiper: браузер сам ловит инерцию пальца и доводит слайд.

   scroll-padding-inline держит привязку с учётом полей контейнера, иначе
   первый слайд прилипал бы к самому краю экрана.

   Полосу прокрутки прячем: она здесь визуальный мусор, а прокрутка и так
   очевидна по обрезанному следующему слайду справа.
*/
.ls-car__track {
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Поле слева совпадает с краем контейнера сайта на любой ширине.
       Контейнер с 1440px перестаёт расти и встаёт по центру; лента же
       тянется на всё окно, и с простым var(--gutter) на мониторе 1920
       первый слайд начинался на 240px левее заголовка над ним
       (проверка 07.10). max() берёт большее из двух: обычное поле или
       половину лишней ширины окна плюс поле. */
    --car-edge: max(var(--gutter), (100% - 1440px) / 2 + var(--gutter));
    scroll-padding-inline: var(--car-edge);
    padding-inline: var(--car-edge);
    padding-block: 4px;
    scrollbar-width: none;
    /* Инерционная прокрутка на iOS. */
    -webkit-overflow-scrolling: touch;
}

.ls-car__track::-webkit-scrollbar {
    display: none;
}

/*
   СЛАЙД В ПРОПОРЦИЯХ СВОЕГО КАДРА.

   Раньше у всех слайдов была одна рамка 4:5 и кадр вписывался в неё
   обложкой. Снимки Huawei в каруселях бывают и 16:9, и квадратные, и
   вертикальные — у широких от рамки оставалась середина: у камеры
   Pura 80 Pro пропадало до 57% кадра, у квадратных часов WATCH 5 — по 10%
   с боков (замечание владельца 09.10, замер детектором обрезки).

   Теперь кадр не режется никогда:
   - телефон — слайд шириной 82% экрана (следующий выглядывает справа,
     видно, что ленту можно листать), высота — по пропорции кадра;
   - с 768px — у всех слайдов одна ВЫСОТА (--car-h), а ширина своя:
     широкий кадр даёт широкий слайд, вертикальный — узкий. Лента ровная
     по высоте, и каждый снимок целиком. Высота — не больше, чем помещается
     в окне под шапкой, панелью модели, заголовком и подписью.
   align-items: flex-start — слайды разной высоты на телефоне не
   растягиваются до самого высокого.
*/
.ls-car__track {
    align-items: flex-start;
}

.ls-car__slide {
    flex: 0 0 82%;
    scroll-snap-align: center;
}

.ls-car__figure {
    margin: 0;
    overflow: hidden;
    border-radius: 16px;
    background: var(--c-surface);
}

.ls-car__img {
    width: 100%;
    height: auto;
}

.ls-car__caption {
    display: block;
    padding: 16px;
}

@media (min-width: 768px) {
    .ls-car__track {
        --car-h: clamp(280px, calc(100svh - var(--header-h) - 57px - 300px), 480px);
    }

    /* Ширина слайда = ширина кадра при высоте --car-h. Потолок 80% ленты:
       очень широкий кадр на планшете не займёт весь экран. */
    .ls-car__slide {
        flex: 0 0 auto;
        max-width: 80%;
    }

    .ls-car__img {
        width: auto;
        max-width: 100%;
        height: var(--car-h);
        /* Если кадр упёрся в потолок ширины — вписывается целиком,
           а не сплющивается. */
        object-fit: contain;
    }

    /* Подпись не раздвигает слайд: её собственная ширина 0, а растягивается
       она до ширины кадра (min-width: 100%). Иначе длинная подпись под
       узким вертикальным кадром делала слайд шире картинки. */
    .ls-car__caption {
        width: 0;
        min-width: 100%;
    }
}

@media (min-width: 1080px) {
    .ls-car__track {
        --car-h: clamp(300px, calc(100svh - var(--header-h) - 57px - 300px), 540px);
    }
}

.ls-car__slide-title {
    display: block;
    font-size: 17px;
    font-weight: 700;
}

.ls-car__slide-text {
    display: block;
    margin-top: 6px;
    font-size: 14px;
    opacity: 0.8;
}

/*
   ПРИЁМ 2 — параллакс подписей.

   У эталона подпись слайда сдвинута на 150px и прозрачна, пока слайд не
   стал активным (data-swiper-parallax-x="-150"). Мы делаем то же самое, но
   переключателем служит не прогресс жеста, а факт «слайд виден в ленте целиком» —
   его сообщает IntersectionObserver.

   Правило живёт только в режиме motion-full и только после того, как скрипт
   отработал: без JS все подписи видны обычным образом.
*/
.motion-full .ls-car__caption {
    opacity: 0.25;
    transform: translateX(40px);
    transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}

.motion-full .ls-car__slide.is-current .ls-car__caption {
    opacity: 1;
    transform: none;
}

.ls-car__nav {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    margin-top: 16px;
}

.ls-car__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--c-border);
    border-radius: 50%;
    font-size: 18px;
    transition: background-color 0.2s var(--ease);
}

.ls-car__btn:hover {
    background: var(--c-surface);
}

.ls-car__btn[disabled] {
    opacity: 0.35;
    cursor: default;
}

/* ---------- 7. Закреплённая сцена ---------------------------------------- */

/*
   СОСТОЯНИЕ ПО УМОЛЧАНИЮ — обычная вертикальная последовательность.

   Ничего не прилипает, состояния идут одно под другим, полоска прогресса
   спрятана. Так сцена выглядит без JS, на телефоне и на слабом устройстве.
   Это не «запасной вариант на всякий случай», а основной режим вёрстки:
   закрепление включается поверх него классом is-pinned.
*/
.ls--scene {
    padding-block: 48px;
}

.ls-scene__bg {
    overflow: hidden;
    border-radius: 16px;
}

.ls-scene__img {
    width: 100%;
    height: auto;
}

.ls-scene__inner {
    margin-top: 24px;
}

.ls-scene__title {
    font-size: 26px;
    font-weight: 700;
    line-height: 1.2;
}

.ls-scene__states {
    display: grid;
    gap: 28px;
    margin-top: 20px;
}

.ls-scene__state-title {
    font-size: 19px;
    font-weight: 700;
}

.ls-scene__state-text {
    margin-top: 8px;
    font-size: 16px;
    opacity: 0.85;
    max-width: 46ch;
}

.ls-scene__state-img {
    width: 100%;
    height: auto;
    margin-bottom: 12px;
    border-radius: 12px;
}

.ls-scene__progress {
    display: none;
}

@media (min-width: 768px) {
    .ls-scene__title { font-size: 40px; }
}

/*
   Товар уходит за нижний край кадра (is-bleed-b ставит landing.js), а
   сцена идёт одной колонкой (телефон, вертикальный планшет): текст
   сверху, кадр под ним вплотную к низу секции. В обычном порядке «кадр,
   потом текст» три Pura 80 Pro обрывались ровной линией прямо над
   заголовком «Цвета» (10.10). Скругление только сверху — низ кадра и есть
   край секции.
*/
@media (max-width: 767.98px), (max-width: 1079.98px) and (orientation: portrait) {
    .ls--scene.is-bleed-b:not(.is-pinned) {
        padding-bottom: 0;
    }

    .ls--scene.is-bleed-b:not(.is-pinned) .ls-scene__pin {
        display: flex;
        flex-direction: column;
    }

    .ls--scene.is-bleed-b:not(.is-pinned) .ls-scene__bg {
        order: 2;
        margin-top: 24px;
        border-radius: 16px 16px 0 0;
    }

    .ls--scene.is-bleed-b:not(.is-pinned) .ls-scene__img {
        display: block;
    }

    .ls--scene.is-bleed-b:not(.is-pinned) .ls-scene__inner {
        margin-top: 0;
    }
}

/*
   «Справа» БЕЗ закрепления на широком экране: кадр в левой колонке и не
   выше окна, подписи — в правой, все состояния подряд.

   Без закрепления сцену видят не только телефоны. Компьютер переходит в
   облегчённый режим, если браузер оценил связь как «3g» (обычное дело для
   Wi-Fi в Узбекистане), если стоит экономия трафика или «меньше движения».
   В этом режиме вертикальный кадр «Дизайн» 610×1316 вставал по центру
   во всю свою высоту — полтора экрана картинки, а заголовок уезжал под неё
   (проверка 07.10 на мониторе владельца). Колонки повторяют раскладку
   закреплённой сцены, только ничего не прилипает.
*/
@media (min-width: 1080px), (min-width: 768px) and (orientation: landscape) {
    .ls--scene.pos-right:not(.is-pinned) .ls-scene__pin {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        align-items: center;
        gap: 32px;
        max-width: 1440px;
        margin-inline: auto;
        padding-inline: var(--gutter);
    }

    .ls--scene.pos-right:not(.is-pinned) .ls-scene__bg {
        border-radius: 0;
    }

    /* Высота — окно за вычетом шапки, панели модели (57px) и полей секции. */
    .ls--scene.pos-right:not(.is-pinned) .ls-scene__img {
        width: auto;
        max-height: calc(100svh - var(--header-h) - 57px - 96px);
    }

    /* Внутренний контейнер уже в колонке: свои поля и отступ сверху ему
       не нужны. */
    .ls--scene.pos-right:not(.is-pinned) .ls-scene__inner {
        margin-top: 0;
        padding-inline: 0;
    }
}

/*
   ЗАКРЕПЛЁННЫЙ РЕЖИМ. Включается классом is-pinned, который вешает
   landing.js — и только на широком экране, при полном режиме движения.

   Механика в двух правилах:

   1) Внешняя секция становится высотой в несколько экранов
      (--scene-steps задаёт вьюха по числу состояний).
   2) Внутренний блок получает position: sticky и высоту ровно в один экран.

   sticky означает «веди себя как обычный блок, пока верх не дошёл до
   указанной границы; после этого стой на месте, пока родитель не кончится».
   Граница — top: var(--header-h), то есть блок прилипает не к верху окна,
   а под шапку сайта.

   В итоге пользователь прокручивает высоту второго экрана, а картинка стоит.
   Именно это ощущение и снято с эталона: там секция 1801px при окне 901px.
*/
.ls--scene.is-pinned {
    min-height: calc(var(--scene-steps, 2) * 100vh);
    padding-block: 0;
}

/* Прилипает под шапку И под панель модели (57px: строка 40px + поля +
   рамка). Раньше — только под шапку, и верхние 57px сцены всё время были
   закрыты панелью: у кадра «Дизайн» под ней пряталась камера (проверка
   этапа 8). Панель модели есть на каждом лендинге. */
.is-pinned .ls-scene__pin {
    --pnav-h: 57px;
    position: sticky;
    top: calc(var(--header-h) + var(--pnav-h));
    height: calc(100vh - var(--header-h) - var(--pnav-h));
    overflow: hidden;
}

.is-pinned .ls-scene__bg {
    position: absolute;
    inset: 0;
    border-radius: 0;
}

/*
   Фон сцены тоже двигается — медленнее содержимого, на 6% высоты за всю
   сцену. Это и создаёт ощущение глубины. Масштаб 1.08 даёт запас, чтобы
   сдвиг не открыл край.
*/
.is-pinned .ls-scene__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    will-change: transform;
    transform: translate3d(0, calc(var(--p, 0) * -6%), 0) scale(1.08);
}

.is-pinned .ls-scene__layer {
    position: absolute;
    inset: 0;
    display: flex;
}

.ls--scene.is-pinned.pos-top .ls-scene__layer { align-items: flex-start; }
.ls--scene.is-pinned.pos-center .ls-scene__layer { align-items: center; }
.ls--scene.is-pinned.pos-bottom .ls-scene__layer { align-items: flex-end; }
.ls--scene.is-pinned.pos-right .ls-scene__layer { align-items: center; }

/*
   «Справа» у закреплённой сцены — кадр целиком в левой половине, текст
   в правой. Для вертикальных снимков товара: растянутый на всю ширину кадр
   610×1316 («Дизайн» у nova 14 Pro) увеличивался в 2,4 раза и превращался
   в мыльную текстуру, а белый текст поверх неё не читался (проверка 07.10).
   contain показывает снимок целиком и не крупнее, чем он есть.

   Горизонтальный планшет (1024×768) тоже получает эту раскладку: там
   приходит тот же десктопный вертикальный кадр, и обложкой на всю ширину
   от телефона оставалась увеличенная в 1,8 раза текстура (проверка этапа 8).
   Вертикальный планшет берёт мобильный кадр — ему обложка подходит.
*/
@media (min-width: 1080px), (min-width: 768px) and (orientation: landscape) {
    /* Та же поправка на поле контейнера, что у героя «справа». */
    .ls--scene.is-pinned.pos-right .ls-scene__inner { padding-left: calc(50% - max(0px, (100% - 1440px) / 2)); }

    .ls--scene.is-pinned.pos-right .ls-scene__bg {
        inset: 0 50% 0 0;
    }

    /* Без параллакса: сдвиг и увеличение 1.08 придуманы для фона на всю
       ширину, где запас прячется за краем. Снимку товара целиком запаса нет —
       к концу сцены у телефона срезалось до 37px сверху (замер этапа 8). */
    .ls--scene.is-pinned.pos-right .ls-scene__img {
        object-fit: contain;
        padding-block: 32px;
        transform: none;
        will-change: auto;
    }

    /* Товар уходит за нижний край кадра (класс ставит landing.js): кадр
       прижат к низу сцены, и телефоны «вырастают» из её края, а не
       обрываются над полосой фона (10.10). */
    .ls--scene.is-pinned.pos-right.is-bleed-b .ls-scene__img {
        object-position: 50% 100%;
        padding-bottom: 0;
    }

    /* То же без закрепления: колонка с кадром встаёт к низу секции,
       нижнее поле секции убрано. */
    .ls--scene.pos-right.is-bleed-b:not(.is-pinned) {
        padding-bottom: 0;
    }

    .ls--scene.pos-right.is-bleed-b:not(.is-pinned) .ls-scene__bg {
        align-self: end;
    }

    .ls--scene.pos-right.is-bleed-b:not(.is-pinned) .ls-scene__img {
        display: block;
    }
}

.is-pinned .ls-scene__inner {
    width: 100%;
    margin-top: 0;
    padding-block: 48px;
}

/*
   Состояния ложатся друг на друга: контейнер position: relative, состояния
   absolute. Видно то, которому скрипт поставил is-active.

   pointer-events: none у невидимых — иначе прозрачный текст верхнего
   состояния перехватывал бы клики по нижнему.
*/
.is-pinned .ls-scene__states {
    position: relative;
    display: block;
    margin-top: 20px;
    min-height: 180px;
}

.is-pinned .ls-scene__state {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
    pointer-events: none;
}

.is-pinned .ls-scene__state.is-active {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}

/*
   Полоска прогресса. В закреплённой сцене прокрутка «не работает» — страница
   стоит на месте, — и без индикатора это читается как зависание.
   Ширина считается тем же числом --p, что и параллакс.
*/
.is-pinned .ls-scene__progress {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background: rgba(127, 127, 127, 0.25);
    /* Гаснет на последних 2% прокрутки: заполненная до конца полоска,
       уезжая вверх вместе со сценой, читалась как жирная линия-разделитель
       поперёк страницы (проверка этапа 8). */
    opacity: clamp(0, (1 - var(--p, 0)) * 50, 1);
}

.is-pinned .ls-scene__bar {
    display: block;
    height: 100%;
    width: calc(var(--p, 0) * 100%);
    background: currentColor;
    opacity: 0.7;
}

/* ---------- 8. Видео ------------------------------------------------------ */

/* Колонка заголовка узкая и стоит по центру над роликом, поэтому и текст
   в ней по центру — как у разделителя. С выравниванием влево блок висел
   посередине экрана, а строки начинались с произвольной точки, не совпадая
   ни с краем сайта, ни с роликом (проверка этапа 8). */
.ls-video__head {
    margin-bottom: 24px;
    max-width: 60ch;
    text-align: center;
}

.ls-video__title {
    margin-top: 8px;
    font-size: 26px;
    font-weight: 700;
}

.ls-video__text {
    margin-top: 12px;
    font-size: 16px;
    opacity: 0.85;
}

@media (min-width: 768px) {
    .ls-video__title { font-size: 36px; }
}

/* Обнуляем только вертикальные поля figure. «margin: 0» целиком снимал
   и margin-inline: auto контейнера: на экранах шире 1440 колонка ролика
   прижималась к левому краю, а узкий ролик с жестом стоял не по центру
   (проверка этапа 8 на мониторе 2560). */
.lvideo {
    margin-block: 0;
}

.lvideo__el {
    width: 100%;
    height: auto;
    border-radius: 16px;
    /* Чёрная подложка: пока постер не загрузился, белый прямоугольник
       на месте ролика выглядит как дыра в вёрстке. */
    background: #000;

    /*
       object-fit относится и к кадрам ролика, и к постеру. Коробку задаёт
       сам ролик (атрибуты width/height теперь читаются из mp4), а постер —
       отдельный файл, и его пропорции могут не совпасть. cover обрежет
       постер по краям вместо того, чтобы вписать его с чёрными полосами.
    */
    object-fit: cover;
}

/* Ролик фоном секции-героя: заполняет всю площадь и обрезается по краям. */
.lvideo__el--cover {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 0;
}

/* Кнопка «Пауза / Смотреть» под роликом (её добавляет landing.js, когда
   запускает ролик сам). Цвет — от тона секции: на тёмной белая, на светлой
   тёмная. Значок нарисован рамками: две полосы паузы или треугольник. */
.lvideo__toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    width: fit-content;
    min-height: 36px;
    margin: 12px auto 0;
    padding: 6px 14px;
    border: 1px solid currentColor;
    border-radius: 18px;
    font-size: 14px;
    line-height: 1.2;
}

.lvideo__icon {
    width: 10px;
    height: 12px;
    border-left: 3px solid currentColor;
    border-right: 3px solid currentColor;
}

.lvideo__toggle.is-paused .lvideo__icon {
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 6px 0 6px 10px;
    border-color: transparent transparent transparent currentColor;
}

/* ---------- 9. Сноски ----------------------------------------------------- */

.ls--footnotes {
    border-top: 1px solid var(--c-border);
    color: var(--c-muted);
}

.ls-fn__title {
    font-size: 14px;
    font-weight: 700;
}

.ls-fn__list {
    margin-top: 12px;
    padding-left: 20px;
    list-style: decimal;
}

.ls-fn__item {
    font-size: 12px;
    line-height: 1.6;
}

/* ---------- 10. Второстепенные пункты липкой навигации -------------------- */

/* Якоря секций лендинга в панели: они второстепеннее «Обзора»
   и «Характеристик», поэтому чуть бледнее. */
.product-nav__link--section {
    font-weight: 500;
}

/* ---------- 11. Выключение движения -------------------------------------- */

/*
   Системная настройка «меньше движения» — не пожелание, а требование
   доступности: у части людей движущиеся интерфейсы вызывают тошноту и
   головокружение. base.css уже гасит длительности всех переходов; здесь
   снимаем оставшиеся смещения и масштабы, чтобы не осталось «застывшего
   сдвинутым» текста.

   Правила продублированы для класса motion-off: скрипт ставит его по той же
   системной настройке, и так гарантированно выключается то, что зависит
   от классов, а не от медиазапроса.
*/
@media (prefers-reduced-motion: reduce) {
    .js-reveal [data-reveal="1"] [data-reveal-group] > * {
        opacity: 1;
        transform: none;
    }

    .ls-ti__media,
    .ls-scene__img,
    .ls-car__caption {
        transform: none !important;
        will-change: auto !important;
    }
}

.motion-off .js-reveal [data-reveal="1"] [data-reveal-group] > * {
    opacity: 1;
    transform: none;
    /* Не «доехать плавно», а сразу оказаться на месте: в режиме «меньше
       движения» плавность и есть то, от чего отказались. */
    transition: none;
}

.motion-off .ls-ti__media,
.motion-off .ls-scene__img,
.motion-off .ls-car__caption {
    transform: none;
    will-change: auto;
}

/*
   НЕ РАСТЯГИВАТЬ КАДР ШИРЕ ИСХОДНИКА.

   Тег-хелпер и видео кладут собственную ширину файла в переменную
   --natural-w. Здесь она становится потолком: картинка или ролик занимает
   ширину колонки, но не больше своего размера, и встаёт по центру.

   Зачем. Материалы Huawei часто нарезаны под колонку, а не под весь экран:
   кадр «Дизайн» у nova 14 Pro — 610 px, ролик с жестом — 636 px. Правило
   «width: 100%» растягивало их до ~1800 px: втрое, с мылом и обрезанным
   телефоном (найдено владельцем 07.10). Это то же правило «никогда не
   увеличиваем», что и при загрузке картинок в админке.

   Исключения — там, где кадр намеренно заполняет весь экран: закреплённая
   сцена (is-pinned) и полноэкранное видео (--cover). Первый экран (hero)
   и баннеры этим правилом не затронуты вовсе.
*/
.ls-ti__img,
.ls-scene__img,
.ls-scene__state-img,
.lvideo__el {
    max-width: min(100%, var(--natural-w, 100%));
    margin-inline: auto;
}

/*
   Обложка (poster) показывается ЦЕЛИКОМ, на чёрной подложке ролика.
   Обложка той же формы, что ролик, заполняет рамку полностью — разницы с
   cover нет. Обложка другой формы (у ролика «Дизайн» 16:9 — вертикальный
   кадр телефона 610×1316) при cover увеличивалась втрое и показывала кусок
   камеры во весь экран (замечание владельца 07.10). Правильная обложка —
   кадр из самого ролика: 10.10 обложки nova 14 Pro заменены кадрами
   роликов (ffmpeg), правило осталось страховкой для загрузок из админки.
*/
.lvideo__el {
    object-fit: contain;
}

/*
   РОЛИК ЦЕЛИКОМ В ОКНЕ. Ширина не больше, чем (высота окна − шапка −
   панель модели 57px − 64px воздуха) × пропорции ролика (--ratio из
   _LandingVideoTag). На низком экране ноутбука ролик 16:9 во всю колонку
   был выше окна, и кнопки плеера уходили за нижний край (проверка 07.10,
   Chrome владельца: окно 1397×641). Без --ratio правило не действует.
*/
.lvideo__el:not(.lvideo__el--cover) {
    max-width: min(100%, var(--natural-w, 100%),
                   calc((100svh - var(--header-h) - 57px - 64px) * var(--ratio, 100)));
}

.is-pinned .ls-scene__img,
.lvideo__el--cover {
    max-width: none;
}

/*
   ОДНОТОННЫЙ РОЛИК В ЦВЕТ СЕКЦИИ (vplain, 15.10). Кадр сливается с фоном,
   товар краёв не касается — но чёрная подложка (см. .lvideo__el выше)
   просвечивала по краю кадра тонкой тёмной рамкой, когда телефон
   уменьшает ролик (динамик FreeClip на #F2F2F2). Подложка снята.
*/
.lvideo.vplain .lvideo__el {
    background: transparent;
}

/*
   РОЛИК С ТОВАРОМ ЗА КРАЕМ КАДРА (vfade, 10.10). Рука с часами в «Жестах»
   WATCH 5 уходила за левый край ролика, и на чёрной секции край ролика
   резал её ровной вертикальной линией. Сервер разбирает обложку (кадр
   самого ролика) так же, как картинки «Текст + картинка», и называет
   задетые края: vf-d-* — компьютер и планшет, vf-m-* — телефон
   (мобильная обложка). Такой край растворяется маской на последних 14%:
   товар уходит в фон, а не обрывается. Скругление и чёрная подложка
   ролика сняты — у однотонного кадра рамки не должно быть видно.
*/
.lvideo.vfade .lvideo__el {
    --ft: 0%;
    --fr: 0%;
    --fb: 0%;
    --fl: 0%;
    border-radius: 0;
    background: transparent;
    -webkit-mask-image:
        linear-gradient(to bottom, transparent, #000 var(--ft), #000 calc(100% - var(--fb)), transparent),
        linear-gradient(to right, transparent, #000 var(--fl), #000 calc(100% - var(--fr)), transparent);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to bottom, transparent, #000 var(--ft), #000 calc(100% - var(--fb)), transparent),
        linear-gradient(to right, transparent, #000 var(--fl), #000 calc(100% - var(--fr)), transparent);
    mask-composite: intersect;
}

@media (min-width: 768px) {
    .lvideo.vf-d-t .lvideo__el { --ft: 14%; }
    .lvideo.vf-d-r .lvideo__el { --fr: 14%; }
    .lvideo.vf-d-b .lvideo__el { --fb: 14%; }
    .lvideo.vf-d-l .lvideo__el { --fl: 14%; }
}

@media (max-width: 767.98px) {
    .lvideo.vf-m-t .lvideo__el { --ft: 14%; }
    .lvideo.vf-m-r .lvideo__el { --fr: 14%; }
    .lvideo.vf-m-b .lvideo__el { --fb: 14%; }
    .lvideo.vf-m-l .lvideo__el { --fl: 14%; }
}

/* Ролик-герой: коробка героя в пропорциях ролика, кадр целиком, поля
   закрашивает landing.js цветами краёв постера (см. «Кадр героя целиком»
   в начале файла, 09.10) — своя подложка ролика их бы перекрыла. */
.ls--hero[style*="--ar-d"] .lvideo__el--cover {
    background: transparent;
}

/* Ролик-герой заполняет первый экран целиком, как картинка героя.
   Правило «обложка целиком» (contain) выше по файлу его перебивало: на
   телефоне заставка WATCH FIT 5 стояла с чёрными полями по бокам (09.10).
   Подложка — фон секции: чёрная полоса до первого кадра выглядела бы дырой. */
.lvideo__el--cover {
    object-fit: cover;
    background: var(--section-bg, transparent);
}

/* На телефоне мобильная заставка Huawei вертикальнее рамки героя: товар
   у неё сверху, внизу пустое поле под текст. По центру обложка срезала
   верх часов — прижимаем кадр к верху. */
@media (max-width: 767.98px) {
    .ls--hero .lvideo__el--cover { object-position: 50% 0; }

    /* Поле под текст у заставки начинается примерно с 53% её высоты.
       При обычной высоте героя (~560px) заголовок ложился на границу
       серого и белого — герой с роликом на телефоне чуть выше. Правило
       «не выше окна» остаётся: на коротком экране заголовок важнее шва. */
    .ls--hero:has(.lvideo__el--cover) { --hero-h: 640px; }
}
