/*
 * СТРАНИЦА КЕЙСА «ЯСНЫЙ РАЗВОРОТ»
 *
 * Главы: case-intro, case-scene, case-statement, case-merge, case-path,
 * case-figure, case-numbers и новые раскладки case-quote (.case-say) и
 * case-next (.case-final). Прежние разделы кейса живут в case.css и этот
 * файл не трогает.
 *
 * Отдельный вход сборки «case» (webpack.config.js): стиль нужен только
 * странице кейса и ставится её шаблоном (pages/portfolio-case.html.twig).
 *
 * ── Сетка и ритм ─────────────────────────────────────────────────────────
 *
 * Колонка 1200 px, 12 колонок с зазором 24 px. Поля 16 px на телефоне и
 * 24 px от 768. Отступ главы 48 / 72 px, сверху у главы волосок. Каждая
 * глава на телефоне рассчитана на встроенный браузер Instagram 390×664:
 * заголовок, строка и начало кадра видны без прокрутки.
 *
 * ── Четыре глагола движения ─────────────────────────────────────────────
 *
 *   лесенка   слова заголовка выезжают из маски (.w / .w__i)      900 мс, шаг 70
 *   шторка    кадр раскрывается clip-path, картинка оседает       1100 мс
 *   прокрутка цифры едут по ленте 0…9 (.odo, макрос _odo)        700–1100 мс
 *   волосок   линия прочерчивается scaleX от левого края          500–600 мс
 *
 * Анимируются только transform, opacity и clip-path, кривая одна —
 * --ease-out (expo.out), уход — --ease-exit. Бесконечных анимаций нет.
 *
 * ── Кто прячет и кто показывает ─────────────────────────────────────────
 *
 * Базовая вёрстка — КОНЕЧНОЕ состояние. Прятать перед движением можно
 * только под флагом, которым app.css прячет [data-reveal]:
 * [data-js='on'][data-motion='full'], и дополнительно не в предпросмотре
 * админки (data-preview ставит <head>, base.html.twig) и не в секции с
 * выключенной анимацией (data-motion='off'). Показывает — класс .is-in,
 * его ставит один наблюдатель страницы (modules/case-motion.js).
 *
 * Слабое устройство (data-tier='low', ставит boot) получает вместо
 * шторок, лесенок и лент простое проявление за 400 мс — отдельный набор
 * правил в конце файла. Первый экран исключение: его лесенка стартует до
 * того, как класс устройства известен, и переключать её на ходу нельзя.
 *
 * Без скрипта, при упавшем бандле, при «уменьшении движения» и в
 * предпросмотре все главы стоят готовыми: цифры свои, волоски нарисованы,
 * шторки открыты.
 */

@layer components {
  /* ═══ ОСНОВА ГЛАВЫ ═════════════════════════════════════════════════ */

  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .case-ch {
    --case-gutter: 16px;
    --case-gap: 24px;
    /* Низ пилюли шапки (замер в constructor.css: 77 px) и немного воздуха. */
    --case-top: 5rem;

    position: relative;
  }

  .case-ch__in {
    position: relative;
    max-width: 1200px;
    margin-inline: auto;
    padding: 48px var(--case-gutter);
  }

  .case-ch__in::before {
    content: '';
    position: absolute;
    top: 0;
    left: var(--case-gutter);
    right: var(--case-gutter);
    border-top: 0.5px solid var(--color-border-soft);
  }

  /* Тёмная глава отделяется цветом сама, волосок у неё и у соседки снизу лишний. */
  .case-ch[data-surface='dark'] > .case-ch__in::before,
  .case-ch[data-surface='dark'] + .case-ch > .case-ch__in::before,
  .case-intro--first > .case-ch__in::before {
    display: none;
  }

  /*
   * От 768 «огромное число» заканчивается своим волоском во всю колонку:
   * второй волосок у следующей главы встал бы двойной линией.
   */
  @media (min-width: 768px) {
    .case-numbers--giant + .case-ch > .case-ch__in::before {
      display: none;
    }
  }

  /* Первый экран: отступ под шапку уже дал общий «первой секции» (constructor.css). */
  .case-intro--first > .case-ch__in {
    padding-top: 8px;
  }

  @media (min-width: 768px) {
    .case-ch {
      --case-gutter: 24px;
    }

    .case-ch__in {
      padding-block: 72px;
    }

    .case-intro--first > .case-ch__in {
      padding-top: 16px;
    }
  }

  /* ═══ ТИПОГРАФИКА ═════════════════════════════════════════════════ */

  /*
   * Заголовок главы: Geist Mono 700, 36 px на телефоне — 80 px на
   * компьютере (шкала --text-display-l, но со своим низом: 48 px на 390
   * выталкивали кадр главы за первый экран Instagram).
   */
  .case-h {
    font-family: var(--font-display);
    font-size: clamp(36px, 5.2vw + 16px, 80px);
    font-weight: 700;
    line-height: 1.04;
    letter-spacing: -0.04em;
    /* Пробел моноширинного шрифта — в целую букву: в крупном кегле слова разваливаются. */
    word-spacing: -0.2em;
    color: var(--color-fg);
    text-wrap: balance;
  }

  /* Первый экран и глава без кадра: --text-display-xl, 44 px на телефоне. */
  .case-h--xl {
    font-size: clamp(44px, 7.4vw + 15px, 111px);
    font-weight: 800;
    line-height: 1;
  }

  /* Итог главы решения и подобные короткие фразы. */
  .case-h--s {
    font-size: clamp(22px, 1.2vw + 17px, 32px);
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: -0.02em;
  }

  .case-lines {
    /* Строка «одна мысль» на компьютере встаёт целиком: 48ch рвали её на хвост в одно-два слова. */
    max-width: 56ch;
    margin-top: 20px;
    font-size: var(--text-body-lead);
    font-weight: 380;
    line-height: 1.6;
    color: var(--color-fg-secondary);
    text-wrap: pretty;
  }

  .case-kicker {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 20px;
    font-size: 12px;
    line-height: 1.5;
    letter-spacing: 0.02em;
    color: var(--color-fg-muted);
  }

  .case-live {
    display: inline-block;
    padding-block: 10px;
    margin-block: -10px;
    color: var(--color-fg);
    text-decoration: underline;
    text-decoration-thickness: 0.5px;
    text-underline-offset: 4px;
    transition: text-decoration-color 0.25s var(--ease-out);
  }

  .case-live__arrow {
    display: inline-block;
    text-decoration: none;
  }

  @media (hover: hover) and (pointer: fine) {
    .case-live:hover {
      text-decoration-color: var(--color-accent);
    }
  }

  .case-cap {
    margin-top: 10px;
    font-size: var(--text-caption);
    line-height: 1.5;
    color: var(--color-fg-muted);
  }

  @media (min-width: 768px) {
    .case-kicker {
      font-size: 13px;
    }
  }

  /* ═══ ЛЕСЕНКА: маски слов ═════════════════════════════════════════ */

  /*
   * vertical-align: top, а не базовая линия: у строчного блока с обрезкой
   * базовая линия уезжает на нижний край, и слова прыгали бы по вертикали.
   * Поля по .1em — чтобы маска не срезала «у», «д», «р» и акценты.
   */
  .w {
    display: inline-block;
    overflow: clip;
    padding-block: 0.1em;
    margin-block: -0.1em;
    vertical-align: top;
  }

  .w__i {
    display: inline-block;
  }

  /* Предлог в одну-две буквы и следующее слово: перенос между ними запрещён (макрос words). */
  .w-g {
    white-space: nowrap;
  }

  /* ═══ ПЛАСТИНА: плоский экран без рамки устройства ═════════════════ */

  .case-plate {
    position: relative;
    display: block;
    overflow: clip;
    aspect-ratio: var(--ar-m, var(--ar, 3 / 2));
    border-radius: 3px;
    background-color: rgb(23 23 23 / 5%);
    /* Конечное значение шторки задано явно: от inset() к none браузер не ведёт переход. */
    clip-path: inset(0 round 3px);
  }

  /* Волосок поверх кадра: светлые края скриншота не растворяются в странице. */
  .case-plate::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: inset 0 0 0 0.5px var(--color-border-strong);
    pointer-events: none;
  }

  .case-plate picture {
    display: block;
    width: 100%;
    height: 100%;
  }

  .case-plate__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  @media (min-width: 768px) {
    .case-plate {
      aspect-ratio: var(--ar, 3 / 2);
    }
  }

  /* ═══ ЛЕНТЫ ЦИФР ══════════════════════════════════════════════════ */

  .odo {
    display: inline-flex;
    vertical-align: top;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  .odo__t {
    display: inline-flex;
  }

  .odo__c {
    display: inline-block;
    width: 1ch;
    height: 1em;
    overflow: clip;
  }

  .odo__r {
    display: block;
    white-space: pre;
    text-align: center;
    transform: translateY(calc(var(--d, 0) * -1em));
  }

  /* Узкий пробел между разрядами: у моноширинного шрифта свой — в целую цифру. */
  .odo__s {
    display: inline-block;
    width: 0.28ch;
  }

  .odo__c.is-off,
  .odo__s.is-off {
    opacity: 0;
  }

  /* Число у левого края: погашенные старшие разряды не оставляют дыры слева. */
  .odo--shift .odo__t {
    transform: translateX(calc(var(--h, 0) * -1ch - var(--hg, 0) * 0.28ch));
  }

  /* ═══ ГЛАВА 0 · ПЕРВЫЙ ЭКРАН ══════════════════════════════════════ */

  .case-intro__title {
    margin-top: 20px;
  }

  .case-intro__demo {
    display: block;
    margin-top: 14px;
  }

  .case-intro__lines {
    margin-top: 16px;
  }

  .case-intro__fig {
    margin: 24px 0 0;
  }

  @media (min-width: 768px) {
    .case-intro__title {
      margin-top: 28px;
    }

    .case-intro__lines {
      margin-top: 24px;
    }

    .case-intro__fig {
      margin-top: 40px;
    }
  }

  /* ═══ ГЛАВА «СЦЕНА» ═══════════════════════════════════════════════ */

  .case-scene__grid {
    display: grid;
    gap: 24px;
  }

  .case-scene__fig {
    margin: 0;
  }

  .case-scene__mark {
    display: block;
    margin-top: 10px;
    text-align: right;
  }

  /* Глава без кадра держит свой крупный кегль (case-h--xl). */
  .case-scene .case-h:not(.case-h--xl) {
    font-size: clamp(40px, 5vw + 20px, 80px);
  }

  @media (min-width: 768px) {
    .case-scene--right .case-scene__grid,
    .case-scene--left .case-scene__grid {
      grid-template-columns: repeat(12, minmax(0, 1fr));
      column-gap: var(--case-gap);
      align-items: center;
    }

    .case-scene--right .case-scene__copy {
      grid-column: 1 / 6;
    }

    .case-scene--right .case-scene__fig {
      grid-column: 6 / 13;
    }

    .case-scene--left .case-scene__copy {
      grid-area: 1 / 8 / 2 / 13;
    }

    .case-scene--left .case-scene__fig {
      grid-area: 1 / 1 / 2 / 8;
    }

    /* Пять колонок из двенадцати: кегль ниже, иначе «Каталог жил» не встаёт в строку. */
    .case-scene--right .case-h:not(.case-h--xl),
    .case-scene--left .case-h:not(.case-h--xl) {
      font-size: clamp(40px, 4vw + 8px, 64px);
    }
  }

  /* ═══ ГЛАВА «РЕШЕНИЕ» ═════════════════════════════════════════════ */

  .case-statement__head .case-h {
    font-size: clamp(40px, 5vw + 20px, 80px);
  }

  .case-statement__body {
    margin-top: 36px;
  }

  .case-strike {
    display: grid;
    justify-items: start;
    gap: 4px;
  }

  .case-strike__word {
    position: relative;
    display: inline-block;
    font-family: var(--font-display);
    font-size: clamp(28px, 1.8vw + 21px, 48px);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.03em;
    text-decoration: none;
    color: var(--color-fg);
  }

  /* Слово после вычёркивания тускнеет до уровня fg-muted (0,58 на тёмном). */
  .case-strike__text {
    opacity: 0.58;
  }

  .case-strike__line {
    position: absolute;
    top: 54%;
    left: -0.08em;
    right: -0.3em;
    height: max(1px, 0.035em);
    background-color: var(--color-accent);
    transform-origin: left center;
  }

  .case-statement__keep {
    margin-top: 32px;
  }

  .case-statement__crop {
    max-width: 560px;
    margin-top: 20px;
  }

  @media (min-width: 768px) {
    .case-statement__body {
      margin-top: 56px;
      padding-left: calc((100% - 11 * var(--case-gap)) / 6 + 2 * var(--case-gap));
    }

    .case-strike {
      gap: 8px;
    }

    .case-statement__keep {
      margin-top: 44px;
    }
  }

  /* ═══ СЦЕНА 1 · СВЕДЕНИЕ ══════════════════════════════════════════ */

  .case-merge__stage {
    position: relative;
    margin-top: 32px;
  }

  .case-merge__sources {
    display: grid;
    gap: 14px;
  }

  .case-merge__name {
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: 0.02em;
    color: var(--color-fg-muted);
  }

  .case-merge__rows {
    display: grid;
    gap: 2px;
    margin-top: 4px;
  }

  .case-merge__row {
    font-size: 15px;
    line-height: 1.55;
    color: var(--color-fg-secondary);
  }

  .case-merge__cell {
    position: relative;
    display: inline-block;
  }

  .case-merge__cell del {
    text-decoration: none;
  }

  /* Повтор одной записи: вычеркнут и приглушён — это конечная схема. */
  .case-merge__row[data-dup] {
    opacity: 0.4;
  }

  .case-merge__strike {
    position: absolute;
    top: 52%;
    left: -2px;
    right: -2px;
    height: 1px;
    background-color: var(--color-fg);
    transform-origin: left center;
  }

  .case-merge__mark {
    display: block;
    margin-top: 12px;
  }

  .case-merge__result {
    position: relative;
    margin-top: 20px;
  }

  .case-merge__rule {
    display: block;
    height: 1px;
    background-color: var(--color-fg);
    transform-origin: left center;
  }

  .case-merge__part {
    white-space: nowrap;
  }

  .case-merge__clean {
    margin-top: 14px;
    font-family: var(--font-display);
    font-size: clamp(17px, 0.8vw + 14px, 26px);
    font-weight: 600;
    line-height: 1.35;
    letter-spacing: -0.02em;
    color: var(--color-fg);
    clip-path: inset(-10% 0 -10% 0);
  }

  .case-merge__total {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    margin-top: 20px;
  }

  /* line-height: 1 у всех держателей лент: иначе строка берёт межстрочный страницы, и под числом дыра в полкегля. */
  .case-merge__num {
    line-height: 1;
    font-family: var(--font-display);
    font-size: clamp(72px, 8.6vw + 38px, 168px);
    font-weight: 800;
    letter-spacing: -0.04em;
    color: var(--color-fg);
  }

  /*
   * Подпись прилегает к числу: ≈22 px на компьютере, ≈18 на телефоне.
   * Держатель ленты — флекс, а не строка: у строки своя «распорка» высотой
   * в кегль, и никакой отрицательный отступ ленты не поднимал подпись — она
   * отставала на треть кегля, дальше от числа, чем чистая строка над ним.
   */
  .case-merge__num {
    display: flex;
  }

  .case-merge__num .odo {
    margin-block: -0.12em -0.1em;
  }

  .case-merge__label,
  .case-merge__fig dd {
    font-size: var(--text-body-lead);
    line-height: 1.5;
    color: var(--color-fg-secondary);
  }

  .case-merge__label {
    margin-top: 8px;
  }

  .case-merge__extra {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 32px;
    margin-top: 14px;
  }

  .case-merge__fig {
    display: flex;
    align-items: baseline;
    gap: 10px;
  }

  .case-merge__fig dt {
    line-height: 1;
    font-family: var(--font-display);
    font-size: clamp(26px, 1vw + 22px, 40px);
    font-weight: 700;
    letter-spacing: -0.03em;
    color: var(--color-fg);
  }

  .case-merge__fig .odo {
    vertical-align: baseline;
  }

  /* Телефон: от каждого источника одна строка — первая, так задумано в админке. */
  @media (max-width: 767px) {
    .case-merge__row:not(:first-child) {
      display: none;
    }
  }

  @media (min-width: 768px) {
    .case-merge__stage {
      margin-top: 44px;
    }

    .case-merge__sources {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 28px var(--case-gap);
    }

    .case-merge__name {
      padding-bottom: 8px;
      border-bottom: 0.5px solid var(--color-border);
    }

    .case-merge__rows {
      margin-top: 10px;
    }

    .case-merge__row {
      font-size: 16px;
    }

    .case-merge__mark {
      text-align: right;
    }

    .case-merge__result {
      margin-top: 28px;
    }
  }

  @media (min-width: 1024px) {
    .case-merge__sources {
      grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
    }
  }

  /*
   * Сцена под закреплением (чанк case-merge/scene.js). Итог стоит в той же
   * клетке, что и строки источников, сразу под подписями: строки уходят в
   * волосок, и итог проявляется на их месте. Без этого закреплённый экран
   * был наполовину пуст — итог ждал своей очереди ниже строк. Без скрипта и
   * в щадящих режимах класса is-scene нет, и схема стоит столбиком.
   */
  .case-merge__stage.is-scene {
    display: grid;
    grid-template-rows: auto auto 1fr;
    grid-template-columns: minmax(0, 1fr);
  }

  .case-merge__stage.is-scene > .case-merge__sources {
    grid-area: 1 / 1;
  }

  /* Метка «[ пример условный ]» — про строки: стоит сразу под ними, а не под итогом. */
  .case-merge__stage.is-scene > .case-merge__mark {
    grid-area: 2 / 1;
  }

  .case-merge__stage.is-scene > .case-merge__result {
    grid-area: 1 / 1 / 4 / 2;
    align-self: start;
    margin-top: 52px;
  }

  /* Во время пина столбцам нужен свой слой. */
  .case-merge__stage.is-scene.is-pinned [data-merge-col] {
    will-change: transform;
  }

  /* ═══ СЦЕНА 2 · ОТ 1 568 ДО ОДНОГО ════════════════════════════════ */

  .case-path__body {
    margin-top: 32px;
  }

  .case-path__meter {
    display: none;
  }

  .case-path__steps {
    display: grid;
    gap: 44px;
  }

  .case-path__num {
    font-family: var(--font-display);
    font-size: 64px;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--color-fg);
  }

  .case-path__label {
    margin-top: 8px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--color-fg-secondary);
  }

  .case-path__word {
    font-family: var(--font-display);
    font-size: 36px;
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.04em;
    color: var(--color-fg);
  }

  .case-path__fig {
    position: relative;
    width: 62%;
    margin: 16px 0 0;
  }

  /* Точка-акцент на кнопке последнего шага. Кольцо цвета страницы отделяет её от зелёной кнопки. */
  .case-path__spot {
    position: absolute;
    top: var(--y);
    left: var(--x);
    width: 10px;
    height: 10px;
    margin: -5px 0 0 -5px;
    border-radius: 50%;
    background-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-bg);
  }

  /* Волосок «угол L»: от точки вниз за край кадра и влево — над подписью. */
  .case-path__elbow {
    position: absolute;
    top: var(--y);
    left: 0;
    width: var(--x);
    height: calc(100% - var(--y) + 16px);
    border-right: 1px solid var(--color-fg);
    border-bottom: 1px solid var(--color-fg);
    clip-path: inset(0);
  }

  .case-path__note {
    max-width: 30ch;
    margin-top: 14px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--color-fg);
  }

  .case-path__note--pinned {
    margin-top: 28px;
  }

  .case-path__live {
    margin-top: 36px;
    font-size: 14px;
  }

  .case-path__meter-in {
    position: sticky;
    top: max(calc(var(--case-top) + 24px), calc(50vh - 9.5rem));
  }

  .case-path__meter-num {
    font-family: var(--font-display);
    font-size: var(--text-display-feature);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--color-fg);
  }

  /* Подпись счётчика прилегает к числу, как у итога сцены «сведение» (флекс — без строчной распорки). */
  .case-path__meter-num {
    display: flex;
  }

  .case-path__meter-num .odo {
    margin-block: -0.1em;
  }

  .case-path__meter-labels {
    display: grid;
    margin-top: 14px;
    font-size: var(--text-body-lead);
    line-height: 1.5;
    color: var(--color-fg-secondary);
  }

  .case-path__ml {
    grid-area: 1 / 1;
    opacity: 0;
    transform: translateY(8px);
  }

  .case-path__ml.is-on {
    opacity: 1;
    transform: none;
  }

  .case-path--words .case-path__meter-labels {
    font-family: var(--font-display);
    font-size: clamp(40px, 5vw, 80px);
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.04em;
    color: var(--color-fg);
  }

  .case-path__meter-rule {
    display: block;
    width: min(100%, 420px);
    height: 1px;
    margin-top: 22px;
    background-color: var(--color-border-strong);
  }

  .case-path__dots {
    display: flex;
    gap: 12px;
    margin-top: 16px;
  }

  .case-path__dots li {
    position: relative;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    box-shadow: inset 0 0 0 1px var(--color-fg);
  }

  .case-path__dots li::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background-color: var(--color-fg);
    transform: scale(0);
  }

  .case-path__dots li.is-done::after {
    transform: none;
  }

  @media (min-width: 768px) {
    .case-path__body {
      display: grid;
      grid-template-columns: repeat(12, minmax(0, 1fr));
      column-gap: var(--case-gap);
      margin-top: 40px;
    }

    .case-path__steps {
      grid-area: 1 / 1 / 2 / -1;
      gap: 0;
    }

    .case-path__step {
      display: grid;
      grid-template-columns: repeat(12, minmax(0, 1fr));
      column-gap: var(--case-gap);
      align-items: center;
      padding-block: 36px;
    }

    .case-path__text {
      grid-column: 1 / 8;
    }

    .case-path__fig {
      grid-column: 9 / 13;
      width: auto;
      margin: 0;
    }

    .case-path__note {
      grid-column: 9 / 13;
    }

    .case-path__num {
      font-size: var(--text-display-feature);
    }

    .case-path__word {
      font-size: clamp(40px, 5vw, 80px);
    }

    /* Со скриптом: липкий счётчик слева, числа у шагов уходят с глаз (но не у читалки). */
    .case-path.is-enhanced .case-path__meter {
      display: block;
      grid-area: 1 / 1 / 2 / 8;
      position: relative;
      z-index: 1;
      pointer-events: none;
    }

    .case-path.is-enhanced .case-path__text {
      opacity: 0;
    }

    .case-path.is-enhanced .case-path__plate {
      opacity: 0.35;
    }

    .case-path.is-enhanced .is-active .case-path__plate {
      opacity: 1;
    }

    .case-path__live {
      margin-top: 24px;
    }
  }

  /* ═══ ЭКРАН С ВЫНОСКАМИ ═══════════════════════════════════════════ */

  .case-figure__stage {
    /* Лупа: диаметр и во сколько раз картинка в ней шире самой лупы. */
    --lens: 88px;
    --z: 6;

    margin: 32px 0 0;
  }

  .case-figure__box {
    position: relative;
  }

  .case-figure__dots {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    aspect-ratio: var(--ar-m, var(--ar));
    pointer-events: none;
  }

  .case-figure__dot {
    position: absolute;
    top: var(--y);
    left: var(--x);
    width: 8px;
    height: 8px;
    margin: -4px 0 0 -4px;
    border-radius: 50%;
    background-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-surface-raised);
  }

  .case-figure__notes,
  .case-figure__list {
    display: grid;
    margin-top: 16px;
  }

  .case-figure__note,
  .case-figure__list li {
    display: grid;
    grid-template-columns: var(--lens, 88px) minmax(0, 1fr);
    align-items: center;
    gap: 16px;
    padding-block: 12px;
    border-top: 0.5px solid var(--color-border-soft);
  }

  .case-figure__list li {
    grid-template-columns: minmax(0, 1fr);
  }

  .case-figure__line {
    display: none;
  }

  /*
   * Лупа: тот же файл кадра, что и у пластины, увеличенный вокруг точки.
   * --bx/--by — доли от левого верхнего угла, --arn — ширина к высоте.
   */
  .case-figure__lens {
    width: var(--lens);
    height: var(--lens);
    border-radius: 50%;
    background-color: var(--color-surface-raised);
    background-image: var(--src);
    background-repeat: no-repeat;
    background-size: calc(var(--lens) * var(--z)) auto;
    background-position:
      calc(var(--lens) / 2 - var(--bx) * var(--lens) * var(--z))
      calc(var(--lens) / 2 - var(--by) * var(--lens) * var(--z) / var(--arn));
    box-shadow: inset 0 0 0 0.5px var(--color-border-strong);
    clip-path: circle(50% at 50% 50%);
  }

  /* Лупы берут файл, когда глава дошла до экрана: к этому времени его уже скачала пластина. */
  [data-js='on'] .case-figure__stage:not(.is-in) .case-figure__lens {
    background-image: none;
  }

  .case-figure__k {
    display: block;
    font-size: 13px;
    line-height: 1.4;
    color: var(--color-fg-muted);
  }

  .case-figure__v {
    display: block;
    margin-top: 2px;
    font-family: var(--font-display);
    font-size: 18px;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: -0.01em;
    color: var(--color-fg);
  }

  /*
   * Кадр уменьшен (на 390 px — вдвое): точка в 8 px ложилась на ту самую
   * строку, на которую указывает («210–•40 г»). 6 px и на пиксель ниже
   * координаты — ровно в просвет под строкой, куда её и ставили; ниже уже
   * задевала бы следующую строку (замер на 390 px по кадру карточки).
   */
  @media (max-width: 1023px) {
    .case-figure__dot {
      width: 6px;
      height: 6px;
      margin: -2px 0 0 -3px;
      box-shadow: 0 0 0 1px var(--color-surface-raised);
    }
  }

  /*
   * Отдельный кадр для телефона: точки и лупы поставлены по основному.
   * На другом кадре точки не показываем, а лупы не выводим вовсе — они
   * увеличивали бы не ту картинку, что на экране, и тянули бы второй файл.
   * Выноски остаются списком.
   */
  @media (max-width: 767px) {
    .case-figure--alt-m .case-figure__dots,
    .case-figure--alt-m .case-figure__lens {
      display: none;
      background-image: none;
    }

    .case-figure--alt-m .case-figure__note {
      grid-template-columns: minmax(0, 1fr);
    }
  }

  @media (min-width: 768px) {
    .case-figure__stage {
      margin-top: 44px;
    }

    .case-figure__dots {
      aspect-ratio: var(--ar);
    }

    .case-figure__box {
      width: calc((100% - 11 * var(--case-gap)) * 8 / 12 + 7 * var(--case-gap));
    }
  }

  /*
   * Планшет: кадр на 7/12, выноски с лупами — колонкой рядом, а не списком
   * под кадром. Иначе справа от кадра стояла треть пустой ширины, а список
   * уходил на второй экран. Точки остаются на кадре: их слой встаёт в ту же
   * клетку сетки, что и пластина.
   */
  @media (min-width: 768px) and (max-width: 1023px) {
    .case-figure__stage.has-notes .case-figure__box {
      display: grid;
      grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
      column-gap: var(--case-gap);
      align-items: start;
      width: 100%;
    }

    .case-figure__stage.has-notes .case-figure__plate,
    .case-figure__stage.has-notes .case-figure__dots {
      grid-area: 1 / 1;
    }

    .case-figure__stage.has-notes .case-figure__dots {
      position: relative;
    }

    .case-figure__stage.has-notes .case-figure__notes {
      grid-area: 1 / 2;
      margin-top: 0;
    }

    .case-figure__stage.has-notes .case-figure__note:first-child {
      padding-top: 0;
      border-top: 0;
    }
  }

  /* Крупный экран: подписи в колонке справа, на высоте своих точек. */
  @media (min-width: 1024px) {
    .case-figure__stage.has-notes .case-figure__box {
      width: calc((100% - 11 * var(--case-gap)) * 7 / 12 + 6 * var(--case-gap));
    }

    .case-figure__notes {
      position: absolute;
      top: 0;
      left: 0;
      display: block;
      width: 100%;
      aspect-ratio: var(--ar);
      margin: 0;
    }

    /*
     * Выноска раскладывается от самой пластины: пункт списка теряет место,
     * а волосок и подпись встают относительно слоя выносок (он ровно по
     * кадру). Над кадром волосок пунктирный — сплошная линия поперёк
     * текста скриншота читалась бы как зачёркивание; за краем кадра он
     * сплошной и доходит до подписи.
     */
    .case-figure__note {
      position: static;
      display: block;
      padding: 0;
      border: 0;
    }

    .case-figure__lens {
      display: none;
    }

    .case-figure__line {
      position: absolute;
      top: var(--y);
      left: var(--x);
      display: block;
      width: calc(100% - var(--x) + 36px);
      height: 1px;
      background:
        linear-gradient(to right, transparent calc(100% - 36px), var(--color-fg) 0),
        repeating-linear-gradient(to right, color-mix(in srgb, var(--color-fg) 70%, transparent) 0 2px, transparent 2px 5px);
      transform-origin: left center;
    }

    /* Подпись сдвинута от соседки (case-figure.js): волосок доходит до неё уступом. */
    .case-figure__note[data-bend] .case-figure__line::after {
      content: '';
      position: absolute;
      top: 0;
      left: calc(100% - 1px);
      width: 12px;
      height: var(--nya);
      border-left: 1px solid var(--color-fg);
      border-bottom: 1px solid var(--color-fg);
    }

    .case-figure__label {
      position: absolute;
      top: calc(var(--y) - 9px);
      left: calc(100% + 48px);
      width: 300px;
      transform: translateY(var(--ny, 0px));
    }
  }

  /* ═══ ЧИСЛА ПРОЕКТА ═══════════════════════════════════════════════ */

  .case-numbers {
    /* Кегль огромной цифры — местная переменная главы, как «0 BYN» у листа обязательств. */
    /* И по высоте окна: на ноутбуке 1366×657 цифра в 480 px занимала весь экран. */
    --case-giant: clamp(200px, min(36vw, 62vh), 480px);
  }

  .case-numbers__grid {
    display: grid;
    grid-template-areas: 'num' 'rule' 'copy';
    gap: 20px;
  }

  .case-numbers__giant {
    grid-area: num;
    display: flex;
    align-items: flex-start;
    gap: 14px;
  }

  .case-numbers__value {
    line-height: 1;
    font-family: var(--font-display);
    font-size: var(--case-giant);
    font-weight: 800;
    letter-spacing: -0.06em;
    color: var(--color-fg);
  }

  /*
   * Держатель — флекс, а не строка: строчная «распорка» высотой в кегль
   * (480 px) держала под цифрой ещё 130 px пустоты, и волосок главы
   * отрывался от «2».
   */
  .case-numbers__value {
    display: flex;
  }

  /* У огромной цифры свой левый отступ глифа (~0.07em): без поправки «2» стоит правее края колонки. */
  .case-numbers__value .odo {
    margin-block: -0.14em -0.13em;
    margin-inline-start: -0.065em;
  }

  .case-numbers__unit {
    margin-top: 0.15em;
    font-family: var(--font-display);
    font-size: clamp(28px, 2vw + 20px, 48px);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--color-fg);
  }

  .case-numbers__rule {
    grid-area: rule;
    display: block;
    height: 1px;
    background-color: var(--color-fg);
    transform-origin: left center;
  }

  .case-numbers__copy {
    grid-area: copy;
  }

  .case-numbers__row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 24px var(--case-gap);
    margin-top: 32px;
  }

  .case-numbers__num {
    line-height: 1;
    font-family: var(--font-display);
    font-size: clamp(48px, 3vw + 36px, 80px);
    font-weight: 800;
    letter-spacing: -0.04em;
    color: var(--color-fg);
  }

  .case-numbers__label {
    margin-top: 8px;
    font-size: 15px;
    color: var(--color-fg-secondary);
  }

  @media (min-width: 768px) {
    .case-numbers__grid {
      grid-template-columns: 5fr 7fr;
      grid-template-areas: 'num copy' 'rule rule';
      column-gap: var(--case-gap);
      row-gap: 40px;
      align-items: center;
    }
  }

  /* ═══ ОТЗЫВ (новая раскладка case-quote) ══════════════════════════ */

  .case-say__grid {
    display: grid;
    gap: 36px;
  }

  .case-say__main {
    margin: 0;
  }

  .case-say__eyebrow {
    margin-bottom: 16px;
  }

  .case-say__mark {
    display: block;
    height: 0.56em;
    font-family: var(--font-display);
    font-size: clamp(72px, 4vw + 56px, 128px);
    font-weight: 700;
    line-height: 0.8;
    color: var(--color-accent);
    clip-path: inset(-30% -10% -30% 0);
  }

  .case-say__text {
    max-width: 28ch;
    margin-top: 16px;
    font-family: var(--font-mono);
    font-size: clamp(21px, 0.9vw + 17.5px, 28px);
    font-weight: 300;
    line-height: 1.45;
    letter-spacing: -0.01em;
    color: var(--color-fg);
    text-wrap: pretty;
  }

  .case-say__text p + p {
    margin-top: 0.6em;
  }

  .case-say__who {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 24px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--color-fg-muted);
  }

  .case-say__role {
    color: var(--color-fg-secondary);
  }

  .case-say__shot {
    max-width: 420px;
  }

  @media (min-width: 768px) {
    .case-say--shot .case-say__grid {
      grid-template-columns: repeat(12, minmax(0, 1fr));
      column-gap: var(--case-gap);
      align-items: center;
    }

    .case-say--shot .case-say__main {
      grid-column: 1 / 8;
    }

    .case-say--shot .case-say__aside {
      grid-column: 8 / 13;
      justify-self: end;
      width: 100%;
      max-width: 420px;
    }
  }

  /* ═══ ФИНАЛ (новая раскладка case-next) ═══════════════════════════ */

  .case-final__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 28px;
  }

  .btn.case-cta {
    flex: 1 1 100%;
    min-height: 56px;
    padding-inline: 28px;
    font-size: 15px;
  }

  .case-final__links {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 28px;
    margin-top: 22px;
    font-size: 14px;
  }

  .case-final__next {
    margin-top: 56px;
    padding-top: 24px;
    border-top: 0.5px solid var(--color-border);
  }

  .case-final__eyebrow {
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: 0.02em;
    color: var(--color-fg-muted);
  }

  .case-final__work {
    display: grid;
    gap: 20px;
    margin-top: 14px;
    color: inherit;
    text-decoration: none;
  }

  .case-final__copy {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .case-final__title {
    position: relative;
    font-family: var(--font-display);
    font-size: clamp(36px, 3vw + 24px, 64px);
    font-weight: 700;
    line-height: 1.04;
    letter-spacing: -0.04em;
    color: var(--color-fg);
    text-wrap: balance;
  }

  .case-final__title::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -4px;
    height: 1px;
    background-color: currentColor;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.28s var(--ease-out);
  }

  .case-final__summary {
    max-width: 40ch;
    font-size: 15px;
    line-height: 1.6;
    color: var(--color-fg-secondary);
  }

  .case-final__cover {
    --ar-m: 3 / 4;
    --ar: 3 / 2;
  }

  .case-final__all {
    margin-top: 22px;
    font-size: 14px;
  }

  @media (min-width: 768px) {
    .btn.case-cta {
      flex: 0 0 auto;
    }

    .case-final__next {
      margin-top: 72px;
    }

    .case-final__work {
      grid-template-columns: repeat(12, minmax(0, 1fr));
      column-gap: var(--case-gap);
      align-items: center;
    }

    .case-final__copy {
      grid-column: 1 / 6;
    }

    .case-final__cover {
      grid-column: 7 / 13;
    }
  }

  @media (hover: hover) and (pointer: fine) {
    .case-final__work:hover .case-final__title::after {
      transform: none;
    }

    .case-final__work .case-final__cover .case-plate__img {
      transition: transform 0.32s var(--ease-out);
    }

    .case-final__work:hover .case-final__cover .case-plate__img {
      transform: scale(1.03);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .case-final__work:hover .case-final__cover .case-plate__img {
      transform: none;
    }

    .case-final__title::after,
    .case-live {
      transition: none;
    }
  }
}

/*
 * ════════════════════════════════════════════════════════════════════════
 *  ДВИЖЕНИЕ
 * ════════════════════════════════════════════════════════════════════════
 *
 * Три приставки, и каждая — предохранитель:
 *
 *   первый экран  :root[data-js='on'][data-motion='full']:not([data-preview])
 *                 .case-intro--first:not([data-motion='off'])
 *   обычный       :root[data-js='on'][data-motion='full']:not([data-preview])
 *                 :not([data-tier='low']) .case-ch:not([data-motion='off'])
 *   слабый        то же, но [data-tier='low'] — только проявление
 *
 * Правила «спрятать» живут только под приставками. Правила «показать» —
 * переходы с задержками — висят на .is-in без приставки: там, где ничего не
 * прятали, переход ничего и не двигает.
 */

@layer components {
  @keyframes case-rise {
    from {
      transform: translateY(110%);
    }
  }

  @keyframes case-fade {
    from {
      opacity: 0;
    }
  }

  @keyframes case-up {
    from {
      opacity: 0;
      transform: translateY(12px);
    }
  }

  @keyframes case-settle {
    from {
      transform: scale(1.05);
    }

    to {
      transform: none;
    }
  }

  @keyframes case-ping {
    50% {
      transform: scale(1.6);
    }
  }

  /* ─── Первый экран: ключевые кадры с первой отрисовки, без JS ────── */

  :root[data-js='on'][data-motion='full']:not([data-preview]) .case-intro--first:not([data-motion='off']) .w__i {
    animation: case-rise 0.9s var(--ease-out) calc(80ms + var(--i, 0) * 70ms) backwards;
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]) .case-intro--first:not([data-motion='off']) .case-kicker {
    animation: case-fade 0.4s var(--ease-out) backwards;
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]) .case-intro--first:not([data-motion='off']) .case-intro__lines,
  :root[data-js='on'][data-motion='full']:not([data-preview]) .case-intro--first:not([data-motion='off']) .case-intro__demo {
    animation: case-up 0.7s var(--ease-out) 0.4s backwards;
  }

  /*
   * Кадр оседает 1.05 → 1, пока проходит экран. Ведёт композитор по
   * прокрутке (view()), скрипта нет. Где view() не умеют — кадр просто стоит.
   */
  @supports (animation-timeline: view()) {
    :root[data-js='on'][data-motion='full']:not([data-preview]) .case-intro--first:not([data-motion='off']) .case-intro__plate .case-plate__img {
      animation: case-settle linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 50%;
    }
  }

  /* ─── Лесенка и строки ──────────────────────────────────────────── */

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .w__i {
    transform: translateY(110%);
  }

  .is-in .w__i {
    transition: transform 0.9s var(--ease-out) calc(var(--d0, 0ms) + var(--i, 0) * 70ms);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-lines {
    opacity: 0;
    transform: translateY(14px);
  }

  .is-in .case-lines {
    transition:
      opacity 0.7s var(--ease-out) var(--ld, 250ms),
      transform 0.7s var(--ease-out) var(--ld, 250ms);
  }

  .case-lines--late {
    --ld: 520ms;
  }

  /* ─── Шторка ────────────────────────────────────────────────────── */

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-plate {
    clip-path: inset(8% round 3px);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-plate__img {
    transform: scale(1.06);
  }

  /*
   * «Было» открывается сверху вниз, кроп кнопки — из середины в стороны.
   *
   * Шторка не закрывается в ноль: от кадра остаётся волосок в 2 px. Кадр,
   * срезанный целиком, браузер считает невидимым и не грузит его ленивую
   * картинку, пока шторка не открылась, — и она открывалась бы над пустотой.
   * Наблюдатель поэтому тоже смотрит на обёртку кадра, а не на сам кадр.
   */
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-plate--drop {
    clip-path: inset(0 0 calc(100% - 2px) 0 round 3px);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-statement__crop {
    clip-path: inset(0 calc(50% - 1px) 0 calc(50% - 1px) round 3px);
  }

  .is-in .case-plate {
    transition: clip-path var(--pt, 1.1s) var(--ease-out) var(--pd, 0ms);
  }

  .is-in .case-plate__img {
    transition: transform var(--pt, 1.1s) var(--ease-out) var(--pd, 0ms);
  }

  .case-statement__crop {
    --pt: 0.9s;
    --pd: calc(var(--settle, 1100ms) + 380ms);
  }

  .case-say__shot {
    --pd: 180ms;
  }

  /* ─── Решение: волоски вычёркивания ──────────────────────────────── */

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-strike__line,
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-numbers__rule {
    transform: scaleX(0);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-strike__text {
    opacity: 1;
  }

  .is-in .case-strike__line {
    transition: transform 0.6s var(--ease-out) calc(var(--i, 0) * 180ms);
  }

  /* Слово тускнеет, когда его волосок прошёл семь десятых пути. */
  .is-in .case-strike__text {
    transition: opacity 0.32s var(--ease-out) calc(var(--i, 0) * 180ms + 420ms);
  }

  .case-statement__keep {
    --d0: var(--settle, 1100ms);
  }

  .is-in .case-numbers__rule {
    transition: transform 0.8s var(--ease-out) 0.3s;
  }

  /* ─── Ленты цифр ────────────────────────────────────────────────── */

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .odo:not([data-odo-manual]) .odo__r,
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .is-armed:not(.is-in) .odo .odo__r {
    transform: none;
  }

  /* Число до своей очереди не стоит нулём на виду: проявляется в момент прокрутки. */
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .odo:not([data-odo-manual]) {
    opacity: 0;
  }

  .is-in .odo:not([data-odo-manual]) {
    transition: opacity 0.35s var(--ease-out) var(--odo-delay, 0ms);
  }

  :root[data-motion='full'] .odo__r {
    transition: transform var(--odo-t, 0.9s) var(--ease-out) calc(var(--odo-delay, 0ms) + var(--k, 0) * var(--odo-step, 60ms));
  }

  :root[data-motion='full'] .odo__c,
  :root[data-motion='full'] .odo__s {
    transition: opacity 0.32s var(--ease-out);
  }

  :root[data-motion='full'] .odo--shift .odo__t {
    transition: transform 0.7s var(--ease-out);
  }

  /* Скрипт ставит ленту на место без перехода (lib/odometer.js, jump). */
  .odo.is-still .odo__r,
  .odo.is-still .odo__c,
  .odo.is-still .odo__s,
  .odo.is-still .odo__t {
    transition: none;
  }

  .case-numbers__value .odo {
    --odo-t: 1.1s;
    --odo-delay: 120ms;
  }

  /* ─── Сцена 1: спектакль на CSS (телефон, низкий экран, без пина) ── */

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) [data-merge-strike],
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) .case-merge__rule {
    transform: scaleX(0);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) .case-merge__row[data-dup] {
    opacity: 1;
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) .case-merge__clean {
    clip-path: inset(-10% 100% -10% 0);
  }

  /* Числа не стоят нулями, пока ждут своей очереди: проявляются в момент прокрутки. */
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) .case-merge__label,
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) .case-merge__fig dd,
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) .case-merge__num,
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-merge__stage.is-armed:not(.is-scene):not(.is-in) .case-merge__fig dt {
    opacity: 0;
  }

  .case-merge__stage.is-in:not(.is-scene) [data-merge-strike] {
    transition: transform 0.4s var(--ease-out) calc(var(--s, 0) * 150ms + var(--i, 0) * 60ms);
  }

  .case-merge__stage.is-in:not(.is-scene) .case-merge__row[data-dup] {
    transition: opacity 0.5s var(--ease-out) calc(450ms + var(--s, 0) * 150ms);
  }

  .case-merge__stage.is-in:not(.is-scene) .case-merge__rule {
    transition: transform 0.5s var(--ease-out) 0.7s;
  }

  .case-merge__stage.is-in:not(.is-scene) .case-merge__clean {
    transition: clip-path 0.6s var(--ease-out) 0.9s;
  }

  .case-merge__stage.is-in:not(.is-scene) .case-merge__label,
  .case-merge__stage.is-in:not(.is-scene) .case-merge__fig dd,
  .case-merge__stage.is-in:not(.is-scene) .case-merge__num {
    transition: opacity 0.4s var(--ease-out) 1.2s;
  }

  .case-merge__stage.is-in:not(.is-scene) .case-merge__fig dt {
    transition: opacity 0.4s var(--ease-out) calc(1320ms + var(--i, 0) * 120ms);
  }

  .case-merge__total .odo {
    --odo-t: 1.1s;
    --odo-delay: 1200ms;
  }

  .case-merge__fig .odo {
    --odo-delay: calc(1320ms + var(--i, 0) * 120ms);
  }

  /* Под пином цифры идут своим ходом сразу по сигналу сцены. */
  .case-merge__stage.is-scene .case-merge__total .odo {
    --odo-delay: 0ms;
  }

  .case-merge__stage.is-scene .case-merge__fig .odo {
    --odo-delay: calc(120ms + var(--i, 0) * 120ms);
  }

  /* ─── Сцена 2: счётчик, точки шагов, точка на «Позвонить» ─────────── */

  :root[data-motion='full'] .case-path__ml {
    transition:
      opacity 0.2s var(--ease-exit),
      transform 0s linear 0.2s;
  }

  :root[data-motion='full'] .case-path__ml.is-on {
    transition:
      opacity 0.42s var(--ease-out) 0.12s,
      transform 0.42s var(--ease-out) 0.12s;
  }

  :root[data-motion='full'] .case-path__dots li::after {
    transition: transform 0.32s var(--ease-out);
  }

  .case-path.is-enhanced .case-path__plate {
    transition:
      opacity 0.42s var(--ease-out),
      clip-path 1.1s var(--ease-out);
  }

  .case-path__meter-num .odo {
    --odo-t: 0.7s;
    --odo-step: 40ms;
  }

  .case-path__num .odo {
    --odo-t: 0.9s;
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-path.is-live:not([data-motion='off']) .case-path__step:not(.is-spot) .case-path__spot {
    transform: scale(0);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-path.is-live:not([data-motion='off']) .case-path__step:not(.is-spot) .case-path__elbow {
    clip-path: inset(0 0 100% 100%);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-path.is-live:not([data-motion='off']) .case-path__step:not(.is-spot) .case-path__note {
    opacity: 0;
    transform: translateY(8px);
  }

  .is-spot .case-path__spot {
    transition: transform 0.32s var(--ease-out) 0.5s;
  }

  .is-spot .case-path__elbow {
    transition: clip-path 0.5s var(--ease-out) 0.72s;
  }

  .is-spot .case-path__note {
    transition:
      opacity 0.42s var(--ease-out) 1s,
      transform 0.42s var(--ease-out) 1s;
  }

  /* ─── Экран с выносками ─────────────────────────────────────────── */

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-figure__dot {
    transform: scale(0);
  }

  .is-in .case-figure__dot {
    transition: transform 0.25s var(--ease-out) calc(700ms + var(--i, 0) * 120ms);
  }

  @media (min-width: 1024px) {
    :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-figure__stage:not(.is-in) .case-figure__line {
      transform: scaleX(0);
    }

    :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-figure__stage:not(.is-in) .case-figure__label {
      opacity: 0;
      transform: translate(-8px, var(--ny, 0px));
    }

    .case-figure__stage.is-in .case-figure__line {
      transition: transform 0.5s var(--ease-out) calc(850ms + var(--i, 0) * 120ms);
    }

    .case-figure__stage.is-in .case-figure__label {
      transition:
        opacity 0.6s var(--ease-out) calc(1000ms + var(--i, 0) * 120ms),
        transform 0.6s var(--ease-out) calc(1000ms + var(--i, 0) * 120ms);
    }
  }

  @media (max-width: 1023px) {
    :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-figure__note:not(.is-in) .case-figure__lens {
      clip-path: circle(0% at 50% 50%);
    }

    :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .case-figure__note:not(.is-in) .case-figure__label {
      opacity: 0;
      transform: translateX(-8px);
    }

    .case-figure__note.is-in .case-figure__lens {
      transition: clip-path 0.6s var(--ease-out);
    }

    .case-figure__note.is-in .case-figure__label {
      transition:
        opacity 0.6s var(--ease-out) 0.12s,
        transform 0.6s var(--ease-out) 0.12s;
    }

    /* Точка на кадре один раз отзывается, когда раскрылась её лупа. */
    :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-figure__dot.is-ping {
      animation: case-ping 0.42s var(--ease-out);
    }
  }

  /* ─── Отзыв ─────────────────────────────────────────────────────── */

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-say__mark {
    clip-path: inset(-30% 100% -30% 0);
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-say__text,
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-say__who {
    opacity: 0;
    transform: translateY(16px);
  }

  .is-in .case-say__mark {
    transition: clip-path 0.5s var(--ease-out);
  }

  .is-in .case-say__text {
    transition:
      opacity 0.8s var(--ease-out) 0.15s,
      transform 0.8s var(--ease-out) 0.15s;
  }

  .is-in .case-say__who {
    transition:
      opacity 0.8s var(--ease-out) 0.4s,
      transform 0.8s var(--ease-out) 0.4s;
  }

  /* ─── Финал: кнопки ─────────────────────────────────────────────── */

  /*
   * Кнопки входят анимацией, а не переходом: переход с задержкой остался
   * бы на кнопке навсегда, и отклик на наведение и нажатие опаздывал бы на
   * полсекунды. Заполнение backwards отпускает кнопку сразу после входа.
   */
  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) [data-in]:not(.is-in) .case-cta {
    opacity: 0;
  }

  :root[data-js='on'][data-motion='full']:not([data-preview]):not([data-tier='low']) .case-ch:not([data-motion='off']) .is-in .case-cta {
    animation: case-up 0.6s var(--ease-out) calc(420ms + var(--i, 0) * 90ms) backwards;
  }

  /* ─── Слабое устройство: вместо шторок, лесенок и лент — проявление ── */

  :root[data-js='on'][data-motion='full'][data-tier='low']:not([data-preview]) .case-ch:not([data-motion='off']) [data-in],
  :root[data-js='on'][data-motion='full'][data-tier='low']:not([data-preview]) .case-ch:not([data-motion='off']) .is-armed {
    transition: opacity 0.4s var(--ease-out);
  }

  :root[data-js='on'][data-motion='full'][data-tier='low']:not([data-preview]) .case-ch:not([data-motion='off']) [data-in]:not(.is-in),
  :root[data-js='on'][data-motion='full'][data-tier='low']:not([data-preview]) .case-ch:not([data-motion='off']) .is-armed:not(.is-in) {
    opacity: 0;
  }

  [data-tier='low'] .odo__r,
  [data-tier='low'] .odo__t {
    transition: none;
  }
}

