/* =========================================================
   ValueVerse — дизайн-система публичного сайта

   Принцип «Дуга»: строго нейтральная база (графит / почти белый),
   иерархия строится типографикой и воздухом, а не цветом.
   Единственный хроматический акцент — холодный «дуговой» синий —
   отмечает интерактивность и инженерные схемы, и больше ничего.

   Правила, которых держимся:
     · градиенты только по светлоте, никогда по цветовому тону;
     · тени и свечения всегда нейтральные, без подцветки;
     · главная кнопка не цветная — она максимально контрастная;
     · акцент означает «это можно нажать» или «это схема».

   Тема переключается атрибутом data-theme на <html>:
     dark | light | auto (по настройке системы посетителя).
   Атрибут проставляет сервер, поэтому при загрузке нет мигания темой.
   ========================================================= */

/* ---------------------------------------------------------
   Тёмная палитра
   --------------------------------------------------------- */
:root {
  color-scheme: dark;

  --bg:          #050607;
  --bg-elev:     #0c0e11;
  --bg-card:     rgba(255, 255, 255, .045);
  --bg-card-2:   rgba(255, 255, 255, .075);
  --line:        rgba(255, 255, 255, .12);
  --line-soft:   rgba(255, 255, 255, .07);

  --text:        #f5f6f8;
  --muted:       #a3a9b4;
  --muted-2:     #7e8590;
  --prose-text:  #d6dae1;
  --placeholder: #5f646d;

  --accent:       #7fa8f5;                    /* графика и схемы */
  --accent-text:  #8ab4ff;                    /* текстовые ссылки */
  --accent-deep:  #4e7ed0;
  --accent-soft:  rgba(138, 180, 255, .10);
  --accent-line:  rgba(138, 180, 255, .28);

  --header-bg:      rgba(5, 6, 7, .55);
  --header-bg-fix:  rgba(5, 6, 7, .84);
  --overlay-bg:     #050607;

  --media-bg:    linear-gradient(160deg, #101317, #06070a);
  --metric-bg:   #08090b;
  --panel-bg:    radial-gradient(120% 100% at 80% 0%, rgba(176, 198, 236, .06), transparent 58%),
                 linear-gradient(180deg, #0d0f12, #06070a);
  --cta-bg:      linear-gradient(180deg, #0c0e11, #06070a);
  --hero-glow:   radial-gradient(circle at 50% 42%,
                   rgba(176, 198, 236, .09) 0%, rgba(176, 198, 236, .035) 34%, transparent 62%);
  --page-glow:   radial-gradient(ellipse at 50% 0%, rgba(176, 198, 236, .07), transparent 65%);

  --shadow-card:      none;
  --shadow-card-over: none;
  --img-shadow:        drop-shadow(0 40px 90px rgba(0, 0, 0, .75));
  --img-shadow-soft:   drop-shadow(0 30px 80px rgba(0, 0, 0, .7));
  --domain-img-shadow: drop-shadow(0 30px 70px rgba(0, 0, 0, .7));

  /* Главная кнопка: белая пилюля с почти чёрным текстом */
  /* Кнопки — стекло. Материал собран из четырёх слоёв: размытие фона,
     подложка, кромка и блик. Кромка и есть главное: настоящее стекло ловит
     свет верхним ребром и тенью по нижнему, без этого получается просто
     полупрозрачный прямоугольник.

     Главная кнопка намеренно плотная. Она лежит в том числе на фотографии
     баннера, и сквозь неё не должно просвечивать настолько, чтобы подпись
     потеряла контраст: у неё в дизайн-системе роль самого контрастного
     элемента страницы, а не самого красивого. */
  --glass-blur:      16px;
  --glass-sat:       170%;
  --glass-rim:       rgba(255, 255, 255, .5);    /* блик верхнего ребра */
  --glass-rim-low:   rgba(255, 255, 255, .08);   /* отражение нижнего */
  --glass-sheen:     rgba(255, 255, 255, .18);   /* скользящий отсвет */

  --btn-grad:        linear-gradient(180deg, rgba(255, 255, 255, .95), rgba(230, 233, 238, .88));
  --btn-text:        #08090b;
  --btn-rim:         rgba(255, 255, 255, .85);
  --btn-rim-low:     rgba(9, 11, 14, .12);
  --btn-shadow:      0 8px 30px -12px rgba(0, 0, 0, .8);
  --btn-shadow-over: 0 14px 44px -14px rgba(0, 0, 0, .9);
  --glass-ghost:     rgba(255, 255, 255, .07);
  --glass-ghost-over: rgba(255, 255, 255, .13);

  /* Градиент заголовка — по светлоте, без смены тона */
  --head-grad: linear-gradient(100deg, #ffffff 8%, #dde1e8 52%, #b9bfc9 96%);

  --input-bg:       rgba(255, 255, 255, .04);
  --input-bg-focus: rgba(255, 255, 255, .06);

  --ok-bg:   rgba(102, 209, 137, .1);
  --ok-line: rgba(102, 209, 137, .35);
  --ok-text: #a9e8c1;
  --err-bg:   rgba(217, 86, 79, .1);
  --err-line: rgba(217, 86, 79, .35);
  --err-text: #ffb1ab;
  --err-field: #d9564f;

  --radius:      20px;
  --radius-lg:   28px;
  --pill:        980px;

  --wrap:        1220px;
  --gutter:      clamp(20px, 4vw, 40px);
  --section:     clamp(88px, 11vw, 168px);

  --ease:        cubic-bezier(.22, .68, .16, 1);
  /* К базовому стеку добавлены системные шрифты с арабицей и иероглификой:
     без них браузер подставляет случайный запасной, и текст выпадает из
     типографики сайта. Свои файлы шрифтов не грузим — системные на этих
     языках выглядят привычнее и не стоят ни байта трафика. */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Inter",
          "Helvetica Neue", Helvetica, Arial,
          "Noto Sans Arabic", "Geeza Pro", Tahoma,
          "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC",
          sans-serif;
}

/* ---------------------------------------------------------
   Светлая палитра.
   Блок повторяется дважды намеренно: один раз для явно выбранной темы,
   второй — для системной настройки. Так тема работает без JavaScript.
   При правке синхронизировать оба блока.
   --------------------------------------------------------- */
:root[data-theme="light"] {
  color-scheme: light;

  --bg:          #f7f8f9;
  --bg-elev:     #ffffff;
  --bg-card:     #ffffff;
  --bg-card-2:   #f2f4f7;
  --line:        rgba(16, 18, 21, .14);
  --line-soft:   rgba(16, 18, 21, .08);

  --text:        #101215;
  --muted:       #565c66;
  --muted-2:     #686e78;
  --prose-text:  #2e333a;
  --placeholder: #8c929b;

  --accent:       #1a5fd8;
  --accent-text:  #1550c8;
  --accent-deep:  #103f9e;
  --accent-soft:  rgba(26, 95, 216, .08);
  --accent-line:  rgba(26, 95, 216, .26);

  --header-bg:      rgba(247, 248, 249, .72);
  --header-bg-fix:  rgba(247, 248, 249, .93);
  --overlay-bg:     #f7f8f9;

  --media-bg:    linear-gradient(160deg, #f2f4f7, #e7eaef);
  --metric-bg:   #fafbfc;
  --panel-bg:    linear-gradient(180deg, #ffffff, #f1f3f6);
  --cta-bg:      linear-gradient(180deg, #ffffff, #f3f5f8);
  --hero-glow:   radial-gradient(circle at 50% 42%, rgba(26, 95, 216, .05) 0%, transparent 60%);
  --page-glow:   radial-gradient(ellipse at 50% 0%, rgba(26, 95, 216, .04), transparent 66%);

  --shadow-card:      0 1px 2px rgba(16, 18, 21, .05), 0 10px 30px -16px rgba(16, 18, 21, .16);
  --shadow-card-over: 0 2px 4px rgba(16, 18, 21, .06), 0 22px 48px -20px rgba(16, 18, 21, .24);
  --img-shadow:        drop-shadow(0 26px 54px rgba(16, 18, 21, .15));
  --img-shadow-soft:   drop-shadow(0 22px 46px rgba(16, 18, 21, .13));
  --domain-img-shadow: drop-shadow(0 22px 44px rgba(16, 18, 21, .14));

  /* На светлом фоне блик белым не виден — кромку держит тень, а верхнее
     ребро подсвечивается изнутри самой кнопки. */
  --glass-rim:       rgba(255, 255, 255, .8);
  --glass-rim-low:   rgba(16, 18, 21, .06);
  --glass-sheen:     rgba(255, 255, 255, .5);

  --btn-grad:        linear-gradient(180deg, rgba(23, 26, 31, .93), rgba(11, 13, 16, .88));
  --btn-text:        #ffffff;
  --btn-rim:         rgba(255, 255, 255, .22);
  --btn-rim-low:     rgba(0, 0, 0, .28);
  --btn-shadow:      0 6px 20px -8px rgba(16, 18, 21, .35);
  --btn-shadow-over: 0 12px 30px -10px rgba(16, 18, 21, .45);
  --glass-ghost:     rgba(255, 255, 255, .6);
  --glass-ghost-over: rgba(255, 255, 255, .85);

  --head-grad: linear-gradient(100deg, #101215 8%, #2b3038 54%, #4a5058 96%);

  --input-bg:       #ffffff;
  --input-bg-focus: #ffffff;

  --ok-bg:   rgba(22, 107, 67, .09);
  --ok-line: rgba(22, 107, 67, .32);
  --ok-text: #166b43;
  --err-bg:   rgba(184, 60, 52, .08);
  --err-line: rgba(184, 60, 52, .3);
  --err-text: #9d251d;
  --err-field: #c1483f;
}

@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] {
    color-scheme: light;

    --bg:          #f7f8f9;
    --bg-elev:     #ffffff;
    --bg-card:     #ffffff;
    --bg-card-2:   #f2f4f7;
    --line:        rgba(16, 18, 21, .14);
    --line-soft:   rgba(16, 18, 21, .08);

    --text:        #101215;
    --muted:       #565c66;
    --muted-2:     #686e78;
    --prose-text:  #2e333a;
    --placeholder: #8c929b;

    --accent:       #1a5fd8;
    --accent-text:  #1550c8;
    --accent-deep:  #103f9e;
    --accent-soft:  rgba(26, 95, 216, .08);
    --accent-line:  rgba(26, 95, 216, .26);

    --header-bg:      rgba(247, 248, 249, .72);
    --header-bg-fix:  rgba(247, 248, 249, .93);
    --overlay-bg:     #f7f8f9;

    --media-bg:    linear-gradient(160deg, #f2f4f7, #e7eaef);
    --metric-bg:   #fafbfc;
    --panel-bg:    linear-gradient(180deg, #ffffff, #f1f3f6);
    --cta-bg:      linear-gradient(180deg, #ffffff, #f3f5f8);
    --hero-glow:   radial-gradient(circle at 50% 42%, rgba(26, 95, 216, .05) 0%, transparent 60%);
    --page-glow:   radial-gradient(ellipse at 50% 0%, rgba(26, 95, 216, .04), transparent 66%);

    --shadow-card:      0 1px 2px rgba(16, 18, 21, .05), 0 10px 30px -16px rgba(16, 18, 21, .16);
    --shadow-card-over: 0 2px 4px rgba(16, 18, 21, .06), 0 22px 48px -20px rgba(16, 18, 21, .24);
    --img-shadow:        drop-shadow(0 26px 54px rgba(16, 18, 21, .15));
    --img-shadow-soft:   drop-shadow(0 22px 46px rgba(16, 18, 21, .13));
    --domain-img-shadow: drop-shadow(0 22px 44px rgba(16, 18, 21, .14));

    --glass-rim:       rgba(255, 255, 255, .8);
    --glass-rim-low:   rgba(16, 18, 21, .06);
    --glass-sheen:     rgba(255, 255, 255, .5);

    --btn-grad:        linear-gradient(180deg, rgba(23, 26, 31, .93), rgba(11, 13, 16, .88));
    --btn-text:        #ffffff;
    --btn-rim:         rgba(255, 255, 255, .22);
    --btn-rim-low:     rgba(0, 0, 0, .28);
    --btn-shadow:      0 6px 20px -8px rgba(16, 18, 21, .35);
    --btn-shadow-over: 0 12px 30px -10px rgba(16, 18, 21, .45);
    --glass-ghost:     rgba(255, 255, 255, .6);
    --glass-ghost-over: rgba(255, 255, 255, .85);

    --head-grad: linear-gradient(100deg, #101215 8%, #2b3038 54%, #4a5058 96%);

    --input-bg:       #ffffff;
    --input-bg-focus: #ffffff;

    --ok-bg:   rgba(22, 107, 67, .09);
    --ok-line: rgba(22, 107, 67, .32);
    --ok-text: #166b43;
    --err-bg:   rgba(184, 60, 52, .08);
    --err-line: rgba(184, 60, 52, .3);
    --err-text: #9d251d;
    --err-field: #c1483f;
  }
}

/* =========================================================
   Слой движения — единая система, а не набор эффектов

   Три шкалы времени:
     · взаимодействие  — отклик на палец и курсор, ~120–300 мс;
     · появление       — блок занимает своё место, ~620–900 мс;
     · крупный вход    — первый экран и большие панели, ~1.0–1.2 с.
   Дольше 1.2 с не длится ничего.

   Кривые только затухающие: движение выходит на место и
   останавливается. Пружин, отскоков и вращений в системе нет.

   Двигаем исключительно transform и opacity — ни одно правило
   слоя не трогает размеры, отступы и координаты.

   Два выключателя гасят весь слой сразу:
     --m-on   1 → движение включено, 0 → всё сразу на своих местах;
     --m-hide 0 → непоказанный элемент прозрачен, 1 → виден.
   Их переключают: <noscript> в head, класс .motion-off (сбой
   скрипта) и prefers-reduced-motion. Правило одно — эффект общий.
   ========================================================= */
:root {
  /* Взаимодействие */
  --mo-press:   130ms;   /* нажатие: обратная связь под пальцем */
  --mo-fast:    180ms;   /* цвет, граница, мелкое состояние */
  --mo-quick:   300ms;   /* наведение, сдвиг стрелки */
  --mo-theme:   320ms;   /* смена темы */
  --mo-ui:      420ms;   /* шапка, меню, крупные состояния */

  /* Появление */
  --mo-in-sm:   620ms;   /* мелкий элемент */
  --mo-in:      820ms;   /* обычный блок */
  --mo-in-lg:  1100ms;   /* крупный кинематографичный вход, потолок системы */

  /* Шаг очереди внутри группы */
  --mo-step:     95ms;
  --mo-step-sm:  62ms;

  /* Кривые */
  --ease-out:  cubic-bezier(.16, .84, .30, 1);   /* вход: резкий старт, мягкая посадка */
  --ease-soft: cubic-bezier(.38, .02, .22, 1);   /* уход и возврат */

  --m-on: 1;
  --m-hide: 0;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 76px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.55;
  letter-spacing: -.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  transition: background .4s var(--ease), color .4s var(--ease);
}

body.is-locked { overflow: hidden; }

img, svg, video { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }

::selection { background: rgba(138, 180, 255, .28); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------------------------------------------------------
   Заставка загрузки

   Устройство осторожное: заставка лежит ПОВЕРХ уже отрисованной страницы
   и никогда не управляет её видимостью. Убирается тремя способами:
     1) скрипт снимает её по событию load — обычный путь;
     2) если скрипт не сработал, её убирает анимация vv-pre-out;
     3) при отключённом JavaScript она не показывается вовсе (правило в head).
   Поэтому страница не может остаться закрытой заставкой.
   --------------------------------------------------------- */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 26px;
  background: var(--bg);
  color: var(--text);
  animation: vv-pre-out .5s var(--ease) 2.6s forwards;   /* страховка на случай молчания скрипта */
}
.preloader.is-done { animation: vv-pre-out .5s var(--ease) forwards; }

.preloader__mark { line-height: 0; }

/* Контур обводится одной линией — он для того и собран непрерывным. */
.preloader__frame {
  stroke-dasharray: 1;
  animation: vv-pre-draw 1s cubic-bezier(.5, 0, .2, 1) backwards;
}
/* Буква проявляется следом, когда ядро уже очерчено. */
.preloader__letter {
  stroke-dasharray: 1;
  animation: vv-pre-draw .55s cubic-bezier(.4, 0, .3, 1) .55s backwards;
}

.preloader__bar {
  width: 132px;
  height: 2px;
  border-radius: 2px;
  background: var(--line-soft);
  overflow: hidden;
}
.preloader__bar span {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transform-origin: 0 50%;
  animation: vv-pre-bar 1.5s cubic-bezier(.35, 0, .2, 1) backwards;
}

@keyframes vv-pre-draw { from { stroke-dashoffset: 1 } to { stroke-dashoffset: 0 } }
@keyframes vv-pre-bar  { from { transform: scaleX(0) } to { transform: scaleX(1) } }
@keyframes vv-pre-out  { to { opacity: 0; visibility: hidden; pointer-events: none } }

/* «Меньше движения» — показываем страницу сразу, без представления. */
@media (prefers-reduced-motion: reduce) {
  .preloader { display: none; }
}

/* ---------------------------------------------------------
   Каркас
   --------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: 820px; }

.section { padding-block: var(--section); position: relative; }
.section--tight { padding-block: clamp(56px, 7vw, 104px); }
.section + .section { padding-top: 0; }

.divider { height: 1px; background: var(--line-soft); border: 0; margin: 0; }

/* ---------------------------------------------------------
   Типографика
   --------------------------------------------------------- */
/* Надзаголовок читается за счёт разрядки и капитализации, а не цвета. */
.kicker {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-bottom: 20px;
}

/* Нижняя граница по vw, а не фиксированная: длинные слова русского языка
   («спроектированная») не должны вылезать за край узкого экрана. */
.display {
  font-size: clamp(30px, 7.4vw, 92px);
  line-height: 1.02;
  letter-spacing: -.038em;
  font-weight: 600;
  margin: 0;
  overflow-wrap: break-word;
}

.h2 {
  font-size: clamp(27px, 4.6vw, 60px);
  line-height: 1.06;
  letter-spacing: -.032em;
  font-weight: 600;
  margin: 0;
  overflow-wrap: break-word;
}

.h3 {
  font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1.15;
  letter-spacing: -.024em;
  font-weight: 600;
  margin: 0;
}

.lead {
  font-size: clamp(17px, 1.5vw, 22px);
  line-height: 1.5;
  color: var(--muted);
  letter-spacing: -.015em;
  margin: 24px 0 0;
}

/* Выделенная часть заголовка: переход по светлоте, без смены тона. */
.accent-word {
  background: var(--head-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.section-head { max-width: 760px; }
.section-head--center { margin-inline: auto; text-align: center; }

/* ---------------------------------------------------------
   Кнопки
   --------------------------------------------------------- */
/* Кнопки — стекло.

   Материал держится не на прозрачности, а на кромке. Настоящее стекло
   ловит свет верхним ребром и отражает его нижним; без этих двух линий
   получается просто мутный прямоугольник. Поэтому кромка задана внутренними
   тенями, а размытие фона идёт довеском.

   Блик проходит по кнопке при наведении. Он намеренно слабый: подпись
   лежит выше него, и яркий отсвет читался бы как засветка текста.

   Ничего в анимации, кроме transform и opacity: backdrop-filter и без того
   недёшев, добавлять к нему пересчёт каждого кадра нельзя. */
.btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;                    /* блик не должен вылезать за пилюлю */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 28px;
  border-radius: var(--pill);
  border: 1px solid transparent;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -.01em;
  cursor: pointer;
  white-space: nowrap;
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  transition: transform var(--mo-quick) var(--ease-out),
              background var(--mo-quick) var(--ease),
              border-color var(--mo-quick) var(--ease),
              box-shadow var(--mo-quick) var(--ease-out),
              opacity var(--mo-fast) linear;
}

/* Скользящий отсвет. Лежит поверх подложки, поэтому и держится на низкой
   прозрачности: сквозь него читается подпись. */
.btn::after {
  content: "";
  position: absolute;
  inset: -50% -60%;
  background: linear-gradient(102deg,
              transparent 40%, var(--glass-sheen) 50%, transparent 60%);
  transform: translate3d(-42%, 0, 0);
  opacity: 0;
  pointer-events: none;
  transition: transform 700ms var(--ease-out), opacity var(--mo-fast) linear;
}
.btn:hover::after { transform: translate3d(42%, 0, 0); opacity: 1; }

.btn:hover { transform: translate3d(0, -2px, 0); }
/* Нажатие: короткое проседание без пружины — палец «продавливает» кнопку. */
.btn:active {
  transform: translate3d(0, 0, 0) scale(.978);
  transition-duration: var(--mo-press);
}

.btn--primary {
  background: var(--btn-grad);
  color: var(--btn-text);
  font-weight: 600;
  box-shadow: inset 0 1px 0 var(--btn-rim),
              inset 0 -1px 0 var(--btn-rim-low),
              var(--btn-shadow);
}
.btn--primary:hover {
  box-shadow: inset 0 1px 0 var(--btn-rim),
              inset 0 -1px 0 var(--btn-rim-low),
              var(--btn-shadow-over);
}

.btn--ghost {
  background: var(--glass-ghost);
  border-color: var(--line);
  color: var(--text);
  box-shadow: inset 0 1px 0 var(--glass-rim),
              inset 0 -1px 0 var(--glass-rim-low);
}
.btn--ghost:hover { border-color: var(--line); background: var(--glass-ghost-over); }

/* Где размытия нет — стекло превращается в обычную плотную кнопку.
   Так надёжнее, чем оставлять полупрозрачную подложку без размытия:
   без него сквозь неё просвечивает всё подряд и подпись теряется. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .btn--ghost { background: var(--bg-card); }
}

/* Блик — украшение, а не сообщение: при настройке «меньше движения» он
   просто не появляется. */
@media (prefers-reduced-motion: reduce) {
  .btn::after { display: none; }
}

.btn--sm { padding: 9px 20px; font-size: 13.5px; }
.btn--block { width: 100%; }
.btn[disabled] { opacity: .55; pointer-events: none; }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--accent-text);
  font-size: 15px;
  font-weight: 500;
}
.link-arrow { transition: color var(--mo-fast) var(--ease); }
.link-arrow .ico { transition: transform var(--mo-quick) var(--ease-out); }
.link-arrow:hover .ico { transform: translate3d(5px, 0, 0); }
.link-arrow:active .ico { transform: translate3d(2px, 0, 0); transition-duration: var(--mo-press); }

/* ---------------------------------------------------------
   Шапка
   --------------------------------------------------------- */
.header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  height: 60px;
  display: flex;
  align-items: center;
  background: var(--header-bg);
  backdrop-filter: saturate(180%) blur(22px);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  border-bottom: 1px solid transparent;
  /* Размытие не анимируем: перерисовка стекла в каждом кадре — самая
     дорогая операция на странице, а выигрыш глазом не читается. */
  transition: background var(--mo-ui) var(--ease),
              border-color var(--mo-ui) var(--ease),
              transform var(--mo-ui) var(--ease-soft);
}
.header.is-scrolled {
  background: var(--header-bg-fix);
  border-bottom-color: var(--line-soft);
}
/* Движение вниз освобождает экран, движение вверх немедленно возвращает шапку.
   Только transform — высота и положение в потоке не меняются. */
.header.is-hidden { transform: translate3d(0, -100%, 0); }
.header__inner {
  display: flex;
  align-items: center;
  gap: 28px;
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.logo { display: flex; align-items: center; gap: 11px; flex-shrink: 0; }
.logo__mark { color: var(--text); display: block; }
.logo__text {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.logo__img { height: 26px; width: auto; }

.nav { display: flex; align-items: center; gap: 30px; margin-inline-start: auto; }
.nav__link {
  position: relative;
  font-size: 13.5px;
  color: var(--muted);
  transition: color var(--mo-fast) var(--ease), transform var(--mo-press) var(--ease-out);
  white-space: nowrap;
}
.nav__link:hover { color: var(--text); }
.nav__link.is-active { color: var(--text); }

/* Ссылка для клавиатуры: спрятана, пока её не сфокусируют. */
.skip-link {
  position: fixed;
  top: 10px;
  inset-inline-start: 10px;
  z-index: 200;
  padding: 11px 18px;
  border-radius: var(--pill);
  background: var(--btn-grad);
  color: var(--btn-text);
  font-size: 14px;
  font-weight: 600;
  transform: translateY(-160%);
  transition: transform var(--mo-quick) var(--ease-out);
}
.skip-link:focus-visible { transform: none; }

.mobile-nav__foot {
  margin-top: auto;
  padding-top: 28px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 20px;
}
.mobile-nav__contact {
  font-size: 16px !important;
  padding: 0 !important;
  border: 0 !important;
  color: var(--muted);
}
/* Подчёркивание раскрывается от начала слова — не появляется целиком. */
.nav__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -7px;
  height: 1px;
  background: currentColor;
  opacity: .45;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--mo-quick) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .nav__link:hover::after { transform: scaleX(1); }
}
.nav__link:active { transform: translate3d(0, 1px, 0); }

.header__actions { display: flex; align-items: center; gap: 12px; }

.lang {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 5px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
}
.lang a {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .08em;
  padding: 7px 11px;                 /* палец должен попадать: цель не меньше 44 px по высоте вместе с рамкой */
  border-radius: var(--pill);
  color: var(--muted-2);
  transition: color var(--mo-fast) var(--ease),
              background var(--mo-fast) var(--ease),
              transform var(--mo-fast) var(--ease-out);
}
.lang a.is-active { background: var(--bg-card-2); color: var(--text); }
.lang a:hover { color: var(--text); }
.lang a:active { transform: scale(.93); transition-duration: var(--mo-press); }

/* Переключатель языка. Семь пунктов в строку не помещаются, поэтому
   раскрывающийся список на <details> — он работает и без JavaScript. */
.lang {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--pill);
}
.lang > summary {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 12px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--muted-2);
  cursor: pointer;
  list-style: none;
  transition: color var(--mo-fast) var(--ease);
}
.lang > summary::-webkit-details-marker { display: none; }
.lang > summary:hover { color: var(--text); }
.lang[open] > summary { color: var(--text); }
.lang[open] > summary .ico { transform: rotate(180deg); }
.lang > summary .ico { transition: transform var(--mo-quick) var(--ease-out); }

.lang__list {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + 8px);
  z-index: 110;
  min-width: 168px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 18px 44px -18px rgba(0, 0, 0, .6);
}
.lang__list a {
  display: block;
  padding: 9px 12px;
  border-radius: 9px;
  font-size: 14px;
  color: var(--muted);
  white-space: nowrap;
  transition: background var(--mo-fast) var(--ease), color var(--mo-fast) var(--ease);
}
.lang__list a:hover { background: var(--bg-card-2); color: var(--text); }
.lang__list a.is-active { color: var(--text); background: var(--bg-card); }

.mobile-nav__langs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.mobile-nav__langs a {
  font-size: 12px !important;
  font-weight: 600;
  letter-spacing: .08em;
  padding: 8px 12px !important;
  border: 1px solid var(--line) !important;
  border-radius: var(--pill);
  color: var(--muted-2);
}
.mobile-nav__langs a.is-active { color: var(--text); background: var(--bg-card-2); }

/* Переключатель темы */
.theme-toggle {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  flex-shrink: 0;
  transition: color var(--mo-fast) var(--ease),
              border-color var(--mo-fast) var(--ease),
              background var(--mo-fast) var(--ease),
              transform var(--mo-fast) var(--ease-out);
}
.theme-toggle:hover { color: var(--text); background: var(--bg-card); }
.theme-toggle:active { transform: scale(.9); transition-duration: var(--mo-press); }
.theme-toggle .ico { transition: opacity var(--mo-fast) var(--ease); }
.theme-toggle .ico--sun { display: none; }
.theme-toggle .ico--moon { display: block; }
:root[data-theme="light"] .theme-toggle .ico--sun { display: block; }
:root[data-theme="light"] .theme-toggle .ico--moon { display: none; }
@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] .theme-toggle .ico--sun { display: block; }
  :root[data-theme="auto"] .theme-toggle .ico--moon { display: none; }
}

/* Кнопка меню. Размер фиксирован и не сжимается: при узком экране флексбокс
   отбирал место именно у неё, и на 375 px иконка схлопывалась в нулевую
   ширину — кнопка оставалась нажимаемой, но невидимой. */
.burger {
  display: none;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  place-items: center;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text);
  transition: transform var(--mo-fast) var(--ease-out), opacity var(--mo-fast) var(--ease);
}
.burger svg { flex-shrink: 0; }
.burger:active { transform: scale(.88); transition-duration: var(--mo-press); }

/* Мобильное меню */
.mobile-nav {
  position: fixed;
  inset: 60px 0 0;
  z-index: 99;
  background: var(--overlay-bg);        /* сплошной фон: полупрозрачный оверлей местами просвечивал */
  padding: 34px var(--gutter) 48px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  transform: translate3d(0, calc(-12px * var(--m-on, 1)), 0);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--mo-quick) var(--ease), transform var(--mo-ui) var(--ease-out), visibility var(--mo-ui);
  overflow-y: auto;
}
.mobile-nav.is-open { opacity: 1; transform: none; visibility: visible; }
.mobile-nav a {
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -.02em;
  padding: 15px 0;
  border-bottom: 1px solid var(--line-soft);
}
/* Пункты выходят очередью: индекс --i проставляет скрипт. */
.mobile-nav a:not(.btn) {
  opacity: var(--m-hide, 0);
  transform: translate3d(0, calc(12px * var(--m-on, 1)), 0);
  transition: opacity var(--mo-quick) var(--ease-out), transform var(--mo-quick) var(--ease-out);
}
.mobile-nav.is-open a:not(.btn) {
  opacity: 1;
  transform: none;
  transition-delay: calc(90ms + var(--i, 0) * var(--mo-step-sm));
}
.mobile-nav.is-open a:not(.btn):active {
  transform: translate3d(6px, 0, 0);
  transition-delay: 0ms;
  transition-duration: var(--mo-press);
}
.mobile-nav .btn { margin-top: 28px; }

/* ---------------------------------------------------------
   Первый экран
   --------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: 130px;
  padding-bottom: 60px;
  overflow: hidden;
  text-align: center;
}
.hero__glow {
  position: absolute;
  top: -18%;
  left: 50%;
  transform: translateX(-50%);
  width: min(1300px, 150vw);
  aspect-ratio: 1;
  background: var(--hero-glow);
  pointer-events: none;
}
.hero__inner { position: relative; z-index: 1; }
.hero .display { max-width: 15ch; margin-inline: auto; }
.hero .lead { max-width: 62ch; margin-inline: auto; }

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
  margin-top: 40px;
}

.hero__media {
  position: relative;
  margin: clamp(40px, 6vw, 72px) auto 0;
  max-width: 1000px;
  z-index: 1;
}
.hero__media img {
  width: 100%;
  height: auto;
  filter: var(--img-shadow);
}

/* Полоса метрик */
.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--line-soft);
  border-block: 1px solid var(--line-soft);
  margin-top: clamp(48px, 7vw, 90px);
}
.stat {
  background: var(--bg);
  padding: clamp(26px, 3vw, 40px) clamp(16px, 2vw, 28px);
  text-align: start;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.stat__icon { color: var(--muted-2); }
.stat__value {
  font-size: clamp(28px, 3.2vw, 44px);
  font-weight: 600;
  letter-spacing: -.035em;
  line-height: 1;
  font-variant-numeric: tabular-nums;   /* ширина цифры постоянна — счётчик не дёргает вёрстку */
}
.stat__label { font-size: 14px; color: var(--muted-2); line-height: 1.4; max-width: 24ch; }

/* ---------------------------------------------------------
   Манифест
   --------------------------------------------------------- */
.manifesto { text-align: center; }
.manifesto__text {
  font-size: clamp(24px, 3.4vw, 44px);
  line-height: 1.22;
  letter-spacing: -.03em;
  font-weight: 500;
  margin: 0 auto;
  max-width: 22ch;
  text-wrap: balance;
}
.manifesto__sign {
  margin-top: 32px;
  font-size: 13px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--muted-2);
}

/* ---------------------------------------------------------
   Направления (чередующиеся блоки)
   --------------------------------------------------------- */
.domain {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(40px, 6vw, 96px);
  align-items: center;
  padding-block: clamp(56px, 7vw, 96px);
  border-top: 1px solid var(--line-soft);
}
.domain:nth-child(even) .domain__media { order: -1; }

/* Знак направления — единственный цветной элемент секции. */
.domain__badge {
  width: 54px;
  height: 54px;
  border-radius: 16px;
  display: grid;
  place-items: center;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  margin-bottom: 24px;
}

/* Оттенки направлений: маркировка, а не украшение.
   Сети идут фирменным акцентом, остальные — своим тоном.
   В светлой теме тона глубже, чтобы держать контраст на белом. */
.domain[data-domain="transformers"] { --accent: #e0a288; }
.domain[data-domain="green"]        { --accent: #6fd39f; }
.domain[data-domain="ai"]           { --accent: #63d6d0; }
.domain[data-domain="nuclear"]      { --accent: #b7a0f7; }

:root[data-theme="light"] .domain[data-domain="transformers"] { --accent: #8f4a2c; }
:root[data-theme="light"] .domain[data-domain="green"]        { --accent: #166b43; }
:root[data-theme="light"] .domain[data-domain="ai"]           { --accent: #0c6873; }
:root[data-theme="light"] .domain[data-domain="nuclear"]      { --accent: #5f3cb4; }

@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] .domain[data-domain="transformers"] { --accent: #8f4a2c; }
  :root[data-theme="auto"] .domain[data-domain="green"]        { --accent: #166b43; }
  :root[data-theme="auto"] .domain[data-domain="ai"]           { --accent: #0c6873; }
  :root[data-theme="auto"] .domain[data-domain="nuclear"]      { --accent: #5f3cb4; }
}

.domain .kicker { margin-bottom: 12px; }
.domain__text { color: var(--muted); margin: 20px 0 0; font-size: 17px; }

.bullets { list-style: none; margin: 28px 0 0; padding: 0; display: grid; gap: 12px; }
.bullets li {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  font-size: 15.5px;
  color: var(--muted);
}
.bullets .ico { color: var(--muted-2); flex-shrink: 0; margin-top: 2px; }

.domain__media { position: relative; }
.domain__media img {
  width: 100%;
  border-radius: var(--radius-lg);
  filter: var(--domain-img-shadow);
}

/* ---------------------------------------------------------
   Чертёж становится объектом

   Схема лежит в потоке и видна всегда — это базовое состояние блока.
   Снимок построенного объекта вынут из потока и лежит слоем поверх;
   он появляется только по намерению. Нет скрипта, нет снимка, не
   загрузился файл — в кадре остаётся чертёж, и высота блока не
   меняется ни на пиксель: слой снимка на неё не влияет.

   Всё состояние держит один выключатель --viz-shown (0 или 1).
   От него зависят прозрачность, сдвиг полосы и сами длительности:
   вход длиннее выхода, потому что показ объекта — событие, а
   возврат к чертежу — просто уход курсора.

   Двигаются только opacity и трансформации.
   --------------------------------------------------------- */
.viz {
  --viz-shown: 0;
  --viz-dir:   1;                          /* направление развёртки; в RTL -1 */
  --viz-beam:  rgba(255, 255, 255, .34);   /* свечение нейтральное, как везде в системе */
  --viz-mark:  rgba(255, 255, 255, .72);   /* визирные уголки лежат на снимке, не на фоне */
  /* Наклон открытки. Снимок ровно того же размера, что и чертёж, и лежит
     поверх — но повёрнут вокруг центра. При повороте прямоугольника углы
     выходят за исходную рамку, а у чертежа его собственные углы, наоборот,
     открываются: видно, что подложек две. Величина маленькая намеренно —
     полтора градуса читаются как «положили от руки», а три уже как брак
     вёрстки. Два с небольшим градуса на кадре в полтысячи пикселей
     обнажают угол чертежа примерно на десять точек — этого хватает,
     чтобы подложка прочиталась, и мало, чтобы блок выглядел кривым. */
  --viz-tilt: -2.2deg;
}
/* На закатном рендере светлой темы полоса тонет — делаем её плотнее. */
:root[data-theme="light"] .viz { --viz-beam: rgba(255, 255, 255, .5); }
@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] .viz { --viz-beam: rgba(255, 255, 255, .5); }
}

/* Показ включают два независимых источника: кнопка (класс is-shown,
   работает везде) и наведение — но только точным указателем и только
   когда файл уже загружен: пустой кадр показывать нечем.
   is-held ставит скрипт, когда посетитель сам вернул чертёж: наведение
   не спорит с явным решением, пока курсор не уйдёт из кадра. */
.viz.is-shown,
.viz.is-auto { --viz-shown: 1; }        /* is-auto — фоновая смена, её ведёт скрипт */
@media (hover: hover) and (pointer: fine) {
  .viz.is-ready:hover:not(.is-held) { --viz-shown: 1; }
}

.viz__stage { position: relative; }
/* Строчная картинка оставляет под собой зазор в выносной элемент —
   слой снимка не совпал бы со схемой по нижнему краю. */
.viz__stage img { display: block; }
/* В режиме «как в системе» схема приходит внутри <picture>:
   display: contents убирает лишнюю обёртку из потока. */
.viz__stage picture { display: contents; }

/* Снимок лежит на чертеже как открытка, положенная сверху: он меньше
   кадра и сдвинут, поэтому по краям остаётся видна схема, а собственная
   тень отрывает его от неё. Так сразу читается, что это два разных
   изображения, а не одно, подменённое другим.

   Сдвиг задан переменными: --viz-lift двигает открытку вверх и влево,
   а поля снизу и справа остаются больше — свет на сайте падает сверху,
   и тень должна ложиться в ту же сторону.

   Тень — box-shadow, а не filter: drop-shadow пересчитывался бы на
   каждом кадре масштабирования, а box-shadow композитится вместе со
   слоем. Скругление меньше, чем у кадра: у вложенного прямоугольника
   радиус всегда меньше внешнего, иначе углы выглядят надутыми. */
.viz__card {
  position: absolute;
  inset: 0;                       /* ровно размер чертежа, ни больше ни меньше */
  border-radius: var(--radius-lg);
  overflow: hidden;
  pointer-events: none;
  box-shadow: 0 18px 44px -16px rgba(0, 0, 0, .62),
              0 2px 10px -3px rgba(0, 0, 0, .45);
  opacity: var(--viz-shown);
  /* Открытку кладут, а не проявляют: приходит чуть крупнее и сильнее
     повёрнутой, потом садится на место. rotate и scale — отдельные
     свойства, поэтому не спорят с transform, если он кому-то понадобится. */
  rotate: calc(var(--viz-tilt) * (1.45 - .45 * var(--viz-shown)));
  scale: calc(1 + .018 * (1 - var(--viz-shown)));
  transition-property: opacity, scale, rotate;
  transition-duration: calc(260ms + var(--viz-shown) * 160ms);   /* 420 мс показ, 260 мс возврат */
  transition-timing-function: var(--ease-out);
  transition-delay: calc(var(--viz-shown) * 90ms);               /* проступает следом за полосой, уходит сразу */
}

/* Специфичность выше, чем у .domain__media img: оттуда иначе приходят
   ширина 100% и наружная тень, рассчитанные на картинку во весь кадр. */
.viz .viz__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;              /* пропорции снимка не влияют на форму открытки */
  border-radius: inherit;
  filter: none;
}

/* В светлой теме тень мягче: на белом листе густая тень выглядит грязью. */
:root[data-theme="light"] .viz__card {
  box-shadow: 0 16px 38px -18px rgba(16, 18, 21, .34),
              0 2px 6px -2px rgba(16, 18, 21, .18);
}
@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] .viz__card {
    box-shadow: 0 16px 38px -18px rgba(16, 18, 21, .34),
                0 2px 6px -2px rgba(16, 18, 21, .18);
  }
}

/* На узком экране открытка почти во весь кадр: поля в 5,5 % от маленькой
   ширины съедают слишком много самого снимка. */
@media (max-width: 640px) {
  .viz__frame { inset: 14px; }
}

/* Полоса развёртки: узкая световая полоса проходит кадр насквозь.
   В покое она за пределами кадра, поэтому её прозрачность не нужно
   анимировать вовсе — прячет обрезка. Обрезаем собственную рамку
   полосы, а не подложку: у схемы наружная тень, overflow на кадре
   срезал бы её. */
.viz__scan {
  position: absolute;
  inset: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  pointer-events: none;
}
.viz__scan::before {
  content: "";
  position: absolute;
  inset: -2% 0;
  background: linear-gradient(90deg, transparent 36%, var(--viz-beam) 50%, transparent 64%);
  transform: translate3d(calc((var(--viz-shown) * 208% - 104%) * var(--viz-dir)), 0, 0);
  transition: transform calc(320ms + var(--viz-shown) * 300ms) var(--ease-soft);
}

/* Визирные уголки — след прибора, который «поймал» объект. Два уголка
   по диагонали: рамка целиком читалась бы как реклама. Садятся
   последними, когда полоса уже прошла. Тень статична и нужна, чтобы
   уголок был виден и на ночном небе, и на закатном. */
.viz__frame {
  position: absolute;
  inset: 22px;
  pointer-events: none;
  filter: drop-shadow(0 0 3px rgba(0, 0, 0, .45));
  opacity: calc(var(--viz-shown) * .85);
  /* Тот же наклон, что у открытки: уголки помечают снимок и обязаны
     лежать по его краям, а не по краям чертежа. */
  rotate: var(--viz-tilt);
  transform: scale(calc(1 + .02 * (1 - var(--viz-shown))));
  transition-property: opacity, transform;
  transition-duration: calc(220ms + var(--viz-shown) * 300ms);
  transition-timing-function: var(--ease-out);
  transition-delay: calc(var(--viz-shown) * 180ms);
}
.viz__frame::before,
.viz__frame::after {
  content: "";
  position: absolute;
  width: 22px;
  height: 22px;
  border: 0 solid var(--viz-mark);
}
/* Диагональ идёт через угол, свободный от кнопки: она живёт в нижнем
   углу по направлению письма, и уголок под ней был бы не виден. */
.viz__frame::before {
  inset-block-start: 0;
  inset-inline-end: 0;
  border-top-width: 1px;
  border-inline-end-width: 1px;
}
.viz__frame::after {
  inset-block-end: 0;
  inset-inline-start: 0;
  border-bottom-width: 1px;
  border-inline-start-width: 1px;
}

/* Смену кадра ведёт наведение и фоновый показ. Кнопки нет намеренно:
   на снимке она перекрывала сюжет, а без неё блок остаётся картинкой,
   а не элементом управления. На телефоне, где наведения не существует,
   снимок всё равно показывается — сам, фоновой сменой. */

/* Печать и экспорт в PDF: остаётся чертёж — он и есть документ. */
@media print {
  .viz__card, .viz__scan, .viz__frame { display: none; }
}

/* ---------------------------------------------------------
   Баннеры первого экрана

   Управляемый кадр под кнопками: фотография, поверх неё —
   надзаголовок, заголовок, предложение и кнопка. Баннеров может
   быть несколько; переключаются сами и точками под кадром.

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

     1) Пропорции кадра заданы, снимок обрезается по ним. Значит
        безопасная зона — свойство КАДРА, а не картинки: «нижняя
        половина» одинакова для любого файла и любых его пропорций.
     2) Затемнение привязано к нижнему краю кадра, а не к тексту.
        Вертикальный слой не зависит от направления письма; боковой,
        который добивает угол со стороны текста, — один и зеркалится.
     3) Длина текста ограничена числом строк. Иначе блок вылезет выше
        безопасной зоны, и весь расчёт перестанет действовать.

   Сам расчёт: белый текст держит контраст 4.5:1, пока яркость
   подложки не выше 0.18. Худший случай — чистый белый пиксель под
   текстом; чтобы увести его под порог, нужно перекрытие чёрным
   примерно на 0.54. В зоне текста слои дают 0.56–0.79 — с запасом.
   Числа ниже расчётные, а не подобранные на глаз; правя их, считайте.

   Внутри кадра всегда «тёмная тема»: подложка — фотография, а она
   не переключается вместе с сайтом. Поэтому цвета здесь заданы
   прямо, а не токенами — токены перевернулись бы вместе со страницей.
   --------------------------------------------------------- */
.banner {
  --bnr-dir:   1;                    /* направление смены: 1 вперёд, -1 назад */
  --bnr-rtl:   1;                    /* зеркало для арабской версии */
  --bnr-shift: 16px;                 /* сдвиг слайда при смене */
  --bnr-ratio: 16 / 9;               /* пропорции кадра держат высоту блока */
  --bnr-pad:   clamp(20px, 3.2vw, 40px);

  --bnr-ink:   #ffffff;
  --bnr-ink-2: rgba(255, 255, 255, .84);
  --bnr-ink-3: rgba(255, 255, 255, .66);
  /* Обводка фокуса лежит на затемнённом снимке в обеих темах:
     светлый акцент читается там, где тёмный утонул бы. */
  --accent: #8ab4ff;

  position: relative;
  max-width: 1000px;
  margin: clamp(40px, 6vw, 72px) auto 0;
  z-index: 1;
}

/* Кадр. Скругление — как у всех изображений сайта. Наружная тень
   берётся из карточного токена: в тёмной теме её нет и не нужно,
   в светлой она отрывает снимок от белого листа. Фильтр drop-shadow
   здесь не используется намеренно — он пересчитывался бы на каждом
   кадре смены слайда (та же причина, что у .viz__photo). */
.banner__frame {
  position: relative;
  aspect-ratio: var(--bnr-ratio);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--media-bg);       /* пока файл не пришёл, кадр не пустой */
  box-shadow: var(--shadow-card);
  isolation: isolate;
}

/* Волосяная внутренняя обводка очерчивает скруглённый край и на
   почти чёрной странице, и на белой. Отдельным слоем, а не в
   box-shadow кадра: в тёмной теме --shadow-card равен none, и
   список теней с none был бы недействителен. */
.banner__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .09);
}

/* Слайд. Покой — прозрачен и сдвинут в ту сторону, откуда придёт.
   visibility выключает скрытый слайд целиком: его ссылка не попадает
   ни в обход по Tab, ни в дерево доступности. Она переключается без
   перехода, но с задержкой на время ухода — иначе слайд исчез бы
   раньше, чем погас. */
.banner__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transform: translate3d(
    calc(var(--bnr-shift) * var(--bnr-dir) * var(--bnr-rtl) * var(--m-on, 1)), 0, 0);
  transition-property: opacity, transform, visibility;
  transition-duration: var(--mo-in-sm), var(--mo-in-sm), 0s;
  transition-delay: 0s, 0s, var(--mo-in-sm);
  transition-timing-function: var(--ease-out);
}

/* Без скрипта виден первый баннер — с текстом и работающей кнопкой.
   Скрипт отменяет это правило классом is-live и дальше показывает
   ровно один слайд сам. */
.banner:not(.is-live) .banner__slide:first-child,
.banner__slide.is-current {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

/* Уходящий слайд выходит в противоположную сторону. */
.banner__slide.is-leaving {
  transform: translate3d(
    calc(var(--bnr-shift) * var(--bnr-dir) * var(--bnr-rtl) * -1 * var(--m-on, 1)), 0, 0);
}

/* В режиме «как в системе» картинка приходит внутри <picture>:
   display: contents убирает лишнюю обёртку из потока. */
.banner__slide picture { display: contents; }

.banner__slide img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;                 /* пропорции файла не влияют на высоту кадра */
  object-position: 50% 50%;
}

/* Затемнение. Два слоя, оба привязаны к краям кадра, а не к тексту:
   вертикальный — рабочая лошадка, боковой добивает угол со стороны
   текста и не трогает противоположную половину снимка. */
.banner__slide::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(to top,
      rgba(0, 0, 0, .74) 0%,
      rgba(0, 0, 0, .68) 22%,
      rgba(0, 0, 0, .58) 42%,
      rgba(0, 0, 0, .40) 58%,
      rgba(0, 0, 0, .16) 76%,
      transparent 92%),
    linear-gradient(to right,
      rgba(0, 0, 0, .34) 0%,
      rgba(0, 0, 0, .16) 34%,
      transparent 62%);
}

/* Текстовый блок: низ кадра, по началу строки. Центрировать его
   было бы повтором первого экрана — и блок сел бы ровно на сюжет
   снимка. У края он читается как подпись к изображению, то есть
   подчинён заголовку страницы, а не спорит с ним. */
.banner__body {
  position: absolute;
  z-index: 2;
  inset-block-end: var(--bnr-pad);
  inset-inline-start: var(--bnr-pad);
  inset-inline-end: var(--bnr-pad);
  max-width: 46ch;
  text-align: start;
  color: var(--bnr-ink);
}

.banner .kicker { margin-bottom: 12px; color: var(--bnr-ink-3); }

/* Ограничитель строк — часть расчёта контраста, а не косметика:
   без него длинный заголовок из админки поднимет блок выше
   безопасной зоны. */
.banner__title {
  margin: 0;
  font-size: clamp(21px, 2.5vw, 34px);
  /* 1.15, а не 1.1: строку обрезает overflow, и при плотном интерлиньяже
     он срезает нижние выносные кириллицы — «р», «у», «ц». */
  line-height: 1.15;
  letter-spacing: -.028em;
  font-weight: 600;
  text-wrap: balance;
  overflow-wrap: break-word;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Три строки, а не две: осмысленное описание энергообъекта в два
   предложения не укладывается, а обрезанная на полуслове фраза хуже,
   чем её отсутствие. Блок при этом остаётся в нижней половине кадра,
   на которую и рассчитано затемнение. */
.banner__text {
  margin: 12px 0 0;
  max-width: 52ch;
  font-size: clamp(14.5px, 1.15vw, 16.5px);
  line-height: 1.5;
  color: var(--bnr-ink-2);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

/* Кнопка лежит на фотографии, а фотография не переключается вместе
   с сайтом — поэтому значения главной кнопки заданы здесь, а не
   взяты из темы. Она всегда белая пилюля: непрозрачная, и попадает
   в самую тёмную часть затемнения, так что работает на любом снимке. */
.banner__cta {
  margin-top: 22px;
  --btn-grad:        linear-gradient(180deg, #ffffff, #e6e9ee);
  --btn-text:        #08090b;
  --btn-shadow:      0 8px 30px -12px rgba(0, 0, 0, .8);
  --btn-shadow-over: 0 14px 44px -14px rgba(0, 0, 0, .9);
}

/* Точки. Без скрипта не показываются: переключать ими было бы нечего.
   Внутри кадра им понадобилось бы собственное затемнение и они
   спорили бы с текстом — снаружи они читаются всегда и живут по
   токенам страницы. */
.banner__dots {
  display: none;
  justify-content: center;
  gap: 4px;
  margin-top: 6px;                   /* остальной отступ дают сами кнопки */
}
.banner.is-live .banner__dots { display: flex; }

/* Настоящая кнопка с внятной подписью, а не div: цель для пальца —
   вся высота ряда, видимая метка — полоска внутри. */
.banner__dot {
  appearance: none;
  border: 0;
  background: none;
  padding: 0 5px;
  min-height: 44px;
  display: grid;
  place-items: center;
  cursor: pointer;
  border-radius: var(--pill);
}
.banner__dot::before {
  content: "";
  display: block;
  width: 28px;
  height: 3px;
  border-radius: var(--pill);
  background: var(--line);
  /* Длина меняется масштабом, а не шириной: слой движения не трогает
     размеры, и шаг между точками остаётся постоянным. */
  transform: scaleX(calc(1 - .38 * var(--m-on, 1)));
  transition: transform var(--mo-quick) var(--ease-out),
              background var(--mo-fast) var(--ease);
}
.banner__dot:hover::before { background: var(--muted-2); }
.banner__dot[aria-current="true"]::before { background: var(--text); transform: none; }
.banner__dot:active::before { transition-duration: var(--mo-press); }

/* Узкий экран: описание уходит — заголовок и кнопка делают дело,
   а описание поясняет. Кадр при этом становится выше, чтобы текст
   не лёг на снимок сплошняком; широкий рендер подрезается по бокам,
   сюжет у таких кадров в центре. */
@media (max-width: 900px) {
  .banner { --bnr-ratio: 3 / 2; }
}
@media (max-width: 640px) {
  .banner { --bnr-ratio: 4 / 3; }
  .banner__text { display: none; }
  .banner__cta { margin-top: 16px; }
}

/* Печать: остаётся текущий кадр, точки не нужны. */
@media print {
  .banner__dots { display: none; }
}

/* ---------------------------------------------------------
   Карточки вызовов
   --------------------------------------------------------- */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  margin-top: 56px;
}
.card {
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 30px 26px 32px;
  box-shadow: var(--shadow-card);
  transition: transform var(--mo-ui) var(--ease-out), border-color var(--mo-ui) var(--ease),
              background var(--mo-ui) var(--ease), box-shadow var(--mo-ui) var(--ease);
}
.card:hover {
  transform: translate3d(0, -4px, 0);
  border-color: var(--line);
  background: var(--bg-card-2);
  box-shadow: var(--shadow-card-over);
}
.card:active { transform: translate3d(0, -1px, 0) scale(.994); transition-duration: var(--mo-press); }
.card__icon { color: var(--text); margin-bottom: 22px; }
.card__title { font-size: 17px; font-weight: 600; letter-spacing: -.015em; margin: 0 0 10px; }
.card__text { font-size: 15px; color: var(--muted-2); margin: 0; line-height: 1.5; }

.note {
  margin-top: 44px;
  font-size: clamp(16px, 1.6vw, 20px);
  color: var(--muted);
  text-align: center;
}

/* ---------------------------------------------------------
   Компетенции
   --------------------------------------------------------- */
.solutions {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: clamp(40px, 6vw, 90px);
  align-items: center;
  margin-top: 60px;
}
.solutions__media img { width: 100%; filter: var(--img-shadow-soft); }

.slist { display: grid; }
.slist__item {
  display: flex;
  gap: 22px;
  padding: 22px 4px;
  border-top: 1px solid var(--line-soft);
  transition: background var(--mo-quick) var(--ease);
}
.slist__item:last-child { border-bottom: 1px solid var(--line-soft); }
.slist__item:hover { background: var(--bg-card); }
.slist__item:hover .slist__icon { transform: translate3d(3px, 0, 0); border-color: var(--line); }
.slist__icon {
  transition: transform var(--mo-quick) var(--ease-out), border-color var(--mo-quick) var(--ease);
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 13px;
  display: grid;
  place-items: center;
  color: var(--text);
  background: var(--bg-card-2);
  border: 1px solid var(--line);
}
.slist__title { font-size: 17px; font-weight: 600; margin: 4px 0 6px; letter-spacing: -.015em; }
.slist__text { font-size: 15px; color: var(--muted-2); margin: 0; }

/* ---------------------------------------------------------
   Технологии
   --------------------------------------------------------- */
.tech {
  position: relative;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-soft);
  background: var(--panel-bg);
  box-shadow: var(--shadow-card);
  padding: clamp(40px, 5vw, 76px);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(36px, 5vw, 72px);
  align-items: center;
  overflow: hidden;
}
.tech__media img { width: 100%; }

/* ---------------------------------------------------------
   Процесс
   --------------------------------------------------------- */
.process { margin-top: 64px; position: relative; }
/* Колонки делят доступную ширину поровну — сколько бы этапов ни завели в админке. */
.process__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 12px;
  scrollbar-width: none;
}
.process__track::-webkit-scrollbar { display: none; }

.step { position: relative; text-align: center; padding-top: 8px; }
.step::before {                       /* соединительная линия */
  content: '';
  position: absolute;
  top: 38px;
  inset-inline-start: -50%;
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line), transparent);
  /* Линия прорисовывается слева направо: масштаб по X, ширина не трогается. */
  transform-origin: left center;
  transition: transform 640ms var(--ease-out)
              calc(var(--rd, 0ms) + 200ms + var(--i, 0) * 95ms);
}
.step:first-child::before { display: none; }

.step__dot {
  width: 62px;
  height: 62px;
  margin: 0 auto 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  color: var(--muted);
  position: relative;
  z-index: 1;
  transition: color var(--mo-quick) var(--ease),
              border-color var(--mo-quick) var(--ease),
              background var(--mo-quick) var(--ease),
              opacity var(--mo-in-sm) var(--ease-out) calc(var(--rd, 0ms) + 260ms + var(--i, 0) * 95ms),
              transform var(--mo-in-sm) var(--ease-out) calc(var(--rd, 0ms) + 260ms + var(--i, 0) * 95ms);
}
.step__num, .step__title {
  transition: opacity var(--mo-in-sm) var(--ease-out) calc(var(--rd, 0ms) + 340ms + var(--i, 0) * 95ms),
              transform var(--mo-in-sm) var(--ease-out) calc(var(--rd, 0ms) + 340ms + var(--i, 0) * 95ms);
}
.step:hover .step__dot { color: var(--text); border-color: var(--line); background: var(--bg-card-2); }
/* Ключевой этап выделен контрастом, а не свечением. */
.step--accent .step__dot {
  border-color: var(--text);
  color: var(--text);
  background: var(--bg-card-2);
}
.step__num {
  font-size: 12px;
  color: var(--muted-2);
  letter-spacing: .1em;
  display: block;
  margin-bottom: 6px;
}
.step__title { font-size: 14px; line-height: 1.35; margin: 0; padding-inline: 4px; }

/* ---------------------------------------------------------
   Кейсы
   --------------------------------------------------------- */
.grid-cases {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 24px;
  margin-top: 56px;
}
.case {
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  transition: transform var(--mo-ui) var(--ease-out), border-color var(--mo-ui) var(--ease),
              box-shadow var(--mo-ui) var(--ease);
}
.case:hover {
  transform: translate3d(0, -6px, 0);
  border-color: var(--line);
  box-shadow: var(--shadow-card-over);
}
.case:active { transform: translate3d(0, -2px, 0) scale(.995); transition-duration: var(--mo-press); }
.case__media {
  aspect-ratio: 16 / 10;
  background: var(--media-bg);
  overflow: hidden;
}
.case__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 900ms var(--ease-out); }
.case:hover .case__media img { transform: scale(1.04); }
.tag { transition: border-color var(--mo-fast) var(--ease), color var(--mo-fast) var(--ease); }
.case:hover .tag { border-color: var(--line); color: var(--text); }
.case__body { padding: 26px 26px 28px; display: flex; flex-direction: column; flex: 1; }
.tag {
  display: inline-block;
  font-size: 11.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--pill);
  padding: 4px 11px;
  margin-bottom: 16px;
  align-self: flex-start;
}
.case__title { font-size: 20px; font-weight: 600; letter-spacing: -.02em; margin: 0 0 10px; }
.case__summary { font-size: 14.5px; color: var(--muted-2); margin: 0 0 22px; }

.metrics { display: grid; gap: 1px; background: var(--line-soft); margin-top: auto; }
.metric {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  background: var(--metric-bg);
  padding: 11px 2px;
  font-size: 13.5px;
}
.metric__label { color: var(--muted-2); }
/* Главный показатель выделен тоном и весом, остальные приглушены.
   Табличные цифры и выравнивание вправо: пока счётчик отсчитывает,
   значение не «прыгает» по ширине и остаётся прижатым к своему краю. */
.metric__value {
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  text-align: end;
}
.metric__value.is-accent { color: var(--text); }

.case__more { margin-top: 22px; }

/* ---------------------------------------------------------
   Финальный призыв
   --------------------------------------------------------- */
.cta {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line-soft);
  background: var(--cta-bg);
  box-shadow: var(--shadow-card);
  padding: clamp(48px, 6vw, 92px);
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: 40px;
  align-items: center;
}
.cta__media { position: relative; }
.cta__media img { width: 100%; filter: var(--img-shadow-soft); }
.cta__actions { margin-top: 34px; }

/* ---------------------------------------------------------
   Форма
   --------------------------------------------------------- */
.contact {
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
  gap: clamp(40px, 6vw, 88px);
  align-items: start;
}
.contact__list { list-style: none; margin: 40px 0 0; padding: 0; display: grid; gap: 22px; }
.contact__list li { display: flex; gap: 14px; align-items: flex-start; }
.contact__list .ico { color: var(--muted-2); flex-shrink: 0; margin-top: 3px; }
.contact__label { font-size: 12px; text-transform: uppercase; letter-spacing: .14em; color: var(--muted-2); }
.contact__value { font-size: 16.5px; transition: color .25s var(--ease); }
.contact__value:hover { color: var(--accent-text); }

.form {
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: clamp(26px, 3.4vw, 44px);
}
.form__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.field { display: flex; flex-direction: column; gap: 8px; }
.field--full { grid-column: 1 / -1; }
.field label { font-size: 13px; color: var(--muted); letter-spacing: -.005em; }
.field .req { color: var(--muted-2); }
.field__opt { color: var(--muted-2); font-size: 12px; }
.field__hint { font-size: 12.5px; color: var(--muted-2); line-height: 1.4; }
.form__hint { font-size: 12.5px; color: var(--muted-2); margin: 0 0 18px; }
.form__consent a { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }

/* Экран после успешной отправки: поля прячутся, чтобы заявку не отправили дважды. */
.form__sent { display: grid; gap: 18px; }
.form__sent p { margin: 0; color: var(--muted); font-size: 15px; line-height: 1.55; }
.form__sent .form__sent-title { color: var(--text); font-size: 19px; font-weight: 600; letter-spacing: -.02em; }
.form__sent-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 4px; }
.form.is-sent .form__grid,
.form.is-sent .form__foot,
.form.is-sent .form__hint { display: none; }

.field input,
.field textarea {
  background: var(--input-bg);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 13px 15px;
  font-size: 15.5px;
  color: var(--text);
  transition: border-color .25s var(--ease), background .25s var(--ease), box-shadow .25s var(--ease);
  width: 100%;
}
.field textarea { resize: vertical; min-height: 124px; }
.field input::placeholder, .field textarea::placeholder { color: var(--placeholder); }
/* Фокус — законная зона акцента: это состояние взаимодействия. */
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--input-bg-focus);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.field.has-error input, .field.has-error textarea { border-color: var(--err-field); }
.field__error { font-size: 12.5px; color: var(--err-text); min-height: 1em; }

.form__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px;
  margin-top: 26px;
}
.form__consent { font-size: 12.5px; color: var(--muted-2); flex: 1; min-width: 200px; line-height: 1.45; }

.hp { position: absolute; left: -9999px; opacity: 0; height: 0; width: 0; overflow: hidden; }

.alert {
  border-radius: 14px;
  padding: 15px 18px;
  font-size: 15px;
  margin-bottom: 22px;
  display: flex;
  gap: 11px;
  align-items: flex-start;
}
.alert .ico { flex-shrink: 0; margin-top: 2px; }
.alert--ok { background: var(--ok-bg); border: 1px solid var(--ok-line); color: var(--ok-text); }
.alert--err { background: var(--err-bg); border: 1px solid var(--err-line); color: var(--err-text); }

/* ---------------------------------------------------------
   Подвал
   --------------------------------------------------------- */
.footer {
  border-top: 1px solid var(--line-soft);
  padding-block: 64px 40px;
  margin-top: var(--section);
}
.footer__top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 44px;
  padding-bottom: 44px;
}
.footer__text { color: var(--muted-2); font-size: 15px; margin: 20px 0 0; max-width: 38ch; }
.footer__col h4 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--muted-2);
  margin: 0 0 18px;
  font-weight: 600;
}
.footer__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.footer__col a { font-size: 15px; color: var(--muted); transition: color .25s; }
.footer__col a:hover { color: var(--text); }
.footer__bottom {
  border-top: 1px solid var(--line-soft);
  padding-top: 26px;
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: space-between;
  font-size: 13px;
  color: var(--muted-2);
}

/* ---------------------------------------------------------
   Внутренние страницы
   --------------------------------------------------------- */
.page-hero {
  padding-top: calc(60px + clamp(64px, 9vw, 128px));
  padding-bottom: clamp(40px, 5vw, 64px);
  position: relative;
}
.page-hero::after {
  content: '';
  position: absolute;
  inset: 0 auto auto 50%;
  transform: translateX(-50%);
  width: 900px;
  height: 500px;
  background: var(--page-glow);
  pointer-events: none;
  z-index: -1;
}

.article { max-width: 760px; margin-inline: auto; }
.article__cover {
  width: 100%;
  border-radius: var(--radius-lg);
  margin-bottom: clamp(32px, 4vw, 56px);
  border: 1px solid var(--line-soft);
}
.prose { font-size: 17.5px; line-height: 1.68; color: var(--prose-text); }
.prose h3 {
  font-size: clamp(22px, 2.4vw, 30px);
  letter-spacing: -.025em;
  color: var(--text);
  margin: 48px 0 16px;
  font-weight: 600;
}
.prose h4 { font-size: 20px; margin: 36px 0 12px; color: var(--text); }
.prose p { margin: 0 0 20px; }
.prose strong { color: var(--text); font-weight: 600; }
.prose ul, .prose ol { margin: 0 0 22px; padding-inline-start: 22px; }
.prose li { margin-bottom: 10px; }
.prose a { color: var(--accent-text); text-decoration: underline; text-underline-offset: 3px; }

/* Таблицы в текстах: перечни cookie и сроков хранения. Обёртка прокручивается
   сама, поэтому широкая таблица не растягивает страницу по горизонтали. */
.prose .table-scroll { overflow-x: auto; margin: 0 0 24px; }
.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15.5px;
  line-height: 1.5;
}
.prose th, .prose td {
  text-align: start;
  vertical-align: top;
  padding: 12px 16px 12px 0;
  border-bottom: 1px solid var(--line-soft);
}
.prose th {
  color: var(--text);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.prose td:first-child { white-space: nowrap; }
.prose tbody tr:last-child td { border-bottom: 0; }
.prose code {
  background: var(--bg-card-2);
  border: 1px solid var(--line-soft);
  padding: 2px 7px;
  border-radius: 6px;
  font-size: .9em;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  font-size: 13.5px;
  color: var(--muted-2);
  margin-top: 22px;
}

.back-link { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--muted); margin-bottom: 26px; }
.back-link:hover { color: var(--accent-text); }

.post-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 24px;
}
.post {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-card);
  box-shadow: var(--shadow-card);
  transition: transform var(--mo-ui) var(--ease-out), border-color var(--mo-ui) var(--ease),
              box-shadow var(--mo-ui) var(--ease);
}
.post:hover {
  transform: translate3d(0, -6px, 0);
  border-color: var(--line);
  box-shadow: var(--shadow-card-over);
}
.post:active { transform: translate3d(0, -2px, 0) scale(.995); transition-duration: var(--mo-press); }
.post__media { aspect-ratio: 16 / 9; overflow: hidden; background: var(--media-bg); }
.post__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 900ms var(--ease-out);
}
.post:hover .post__media img { transform: scale(1.04); }
.post__body { padding: 24px 24px 28px; display: flex; flex-direction: column; flex: 1; }
.post__title { font-size: 19px; font-weight: 600; letter-spacing: -.02em; margin: 0 0 10px; line-height: 1.3; }
.post__excerpt { font-size: 14.5px; color: var(--muted-2); margin: 0 0 20px; flex: 1; }
.post__date { font-size: 12.5px; color: var(--muted-2); letter-spacing: .04em; }

.metrics-wide {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  margin: 40px 0;
}
.metrics-wide .metric {
  flex-direction: column;
  gap: 8px;
  padding: 24px 22px;
  background: var(--metric-bg);
}
.metrics-wide .metric__value { font-size: 28px; letter-spacing: -.03em; text-align: start; }

.empty { text-align: center; color: var(--muted-2); padding: 80px 0; }

/* ---------------------------------------------------------
   Появление при скролле

   Скрытое состояние живёт на [data-reveal]:not(.is-in) — после
   появления с элемента не остаётся ни одного правила слоя движения,
   поэтому его собственные состояния (наведение, нажатие) работают
   в полную силу, а will-change не висит в памяти вечно.

   Амплитуда зависит от веса элемента: крупная панель поднимается
   заметно и медленно, подпись — почти незаметно и быстро.
   --rd — задержка элемента в очереди группы, её ставит скрипт.
   --------------------------------------------------------- */
[data-reveal] {
  transition: opacity var(--rv-dur, var(--mo-in)) var(--ease-out) var(--rd, 0ms),
              transform var(--rv-dur, var(--mo-in)) var(--ease-out) var(--rd, 0ms);
}
[data-reveal]:not(.is-in) {
  opacity: var(--m-hide, 0);
  transform: translate3d(0, calc(var(--rv-y, 24px) * var(--m-on, 1)), 0)
             scale(calc(1 - var(--rv-shrink, 0) * var(--m-on, 1)));
  will-change: opacity, transform;
}

/* Крупный кинематографичный вход: первый экран и большие панели. */
.banner[data-reveal],
.hero__media[data-reveal],
.tech[data-reveal],
.cta[data-reveal] {
  --rv-y: 44px;
  --rv-shrink: .014;
  --rv-dur: var(--mo-in-lg);
}
.hero .display[data-reveal] { --rv-y: 20px; --rv-dur: 1000ms; }

/* Иллюстрации и карточки: средний вес. */
.domain__media[data-reveal],
.solutions__media[data-reveal],
.case[data-reveal],
.post[data-reveal],
.article__cover[data-reveal],
.metrics-wide[data-reveal] {
  --rv-y: 32px;
  --rv-shrink: .008;
  --rv-dur: 920ms;
}

/* Мелкие элементы: короткий путь, короткое время. */
.kicker[data-reveal],
.note[data-reveal],
.meta[data-reveal],
.slist__item[data-reveal],
.manifesto__sign[data-reveal] {
  --rv-y: 12px;
  --rv-dur: var(--mo-in-sm);
}

/* Блоки с собственной внутренней очередью двигаются только прозрачностью:
   иначе движение читалось бы дважды — и у контейнера, и у детей. */
.stats[data-reveal],
.process[data-reveal] { --rv-y: 0px; --rv-dur: var(--mo-in-sm); }

/* ---------------------------------------------------------
   Ступенчатость внутри группы: --sd проставляет скрипт
   --------------------------------------------------------- */
.stats .stat,
.bullets li,
.contact__list li,
.metrics .metric,
.metrics-wide .metric {
  transition: opacity var(--mo-in-sm) var(--ease-out) var(--sd, 0ms),
              transform var(--mo-in-sm) var(--ease-out) var(--sd, 0ms);
}
[data-reveal]:not(.is-in) .stat,
[data-reveal]:not(.is-in) .bullets li,
[data-reveal]:not(.is-in) .contact__list li,
[data-reveal]:not(.is-in) .metric,
[data-reveal]:not(.is-in) .step__dot,
[data-reveal]:not(.is-in) .step__num,
[data-reveal]:not(.is-in) .step__title {
  opacity: var(--m-hide, 0);
  transform: translate3d(0, calc(var(--sub-y, 14px) * var(--m-on, 1)), 0);
}
/* Узел шкалы дополнительно подбирается по размеру — «загорается». */
[data-reveal]:not(.is-in) .step__dot {
  transform: translate3d(0, calc(8px * var(--m-on, 1)), 0)
             scale(calc(1 - .07 * var(--m-on, 1)));
}
/* Линия связи между этапами: прорисовка по X. */
[data-reveal]:not(.is-in) .step::before { transform: scaleX(calc(1 - var(--m-on, 1))); }

/* ---------------------------------------------------------
   Страховка от невидимого контента

   Три независимых рубежа:
     1) <noscript> в head переводит --m-on/--m-hide — тема без JS;
     2) .motion-off ставит скрипт при любой своей ошибке;
     3) правило ниже — на случай, если site.js вовсе не загрузился:
        через 2.2 с содержимое проявляется само, без участия JS.
   Успешно стартовавший скрипт отменяет третий рубеж классом
   .motion-ready, поэтому в нормальной работе он не срабатывает.
   --------------------------------------------------------- */
@keyframes vv-failsafe   { to { opacity: 1; transform: none; } }
@keyframes vv-failsafe-x { to { transform: scaleX(1); } }

[data-reveal]:not(.is-in),
[data-reveal]:not(.is-in) .stat,
[data-reveal]:not(.is-in) .bullets li,
[data-reveal]:not(.is-in) .contact__list li,
[data-reveal]:not(.is-in) .metric,
[data-reveal]:not(.is-in) .step__dot,
[data-reveal]:not(.is-in) .step__num,
[data-reveal]:not(.is-in) .step__title {
  animation: vv-failsafe 1ms linear 2200ms forwards;
}
[data-reveal]:not(.is-in) .step::before {
  animation: vv-failsafe-x 1ms linear 2200ms forwards;
}

:root.motion-ready [data-reveal]:not(.is-in),
:root.motion-ready [data-reveal]:not(.is-in) .stat,
:root.motion-ready [data-reveal]:not(.is-in) .bullets li,
:root.motion-ready [data-reveal]:not(.is-in) .contact__list li,
:root.motion-ready [data-reveal]:not(.is-in) .metric,
:root.motion-ready [data-reveal]:not(.is-in) .step__dot,
:root.motion-ready [data-reveal]:not(.is-in) .step__num,
:root.motion-ready [data-reveal]:not(.is-in) .step__title,
:root.motion-ready [data-reveal]:not(.is-in) .step::before {
  animation: none;
}

/* Скрипт упал — движения нет, всё содержимое на месте. */
:root.motion-off { --m-on: 0; --m-hide: 1; }
:root.motion-off [data-reveal],
:root.motion-off [data-reveal] * { animation: none !important; }

/* ---------------------------------------------------------
   Смена темы

   Основной путь — штатный кроссфейд браузера (View Transitions):
   он снимает старый кадр целиком, поэтому картинки не мигают.
   Запасной путь для браузеров без него — общий переход по цвету.
   --------------------------------------------------------- */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--mo-theme);
  animation-timing-function: var(--ease);
}

:root.theme-anim,
:root.theme-anim *,
:root.theme-anim *::before,
:root.theme-anim *::after {
  transition-property: background-color, background-image, border-color, color,
                       fill, stroke, box-shadow, filter, opacity;
  transition-duration: var(--mo-theme);
  transition-timing-function: var(--ease);
  transition-delay: 0s;
}
/* Ещё не показанные блоки продолжают жить по своим правилам появления. */
:root.theme-anim [data-reveal]:not(.is-in) {
  transition-property: opacity, transform;
  transition-duration: var(--rv-dur, var(--mo-in));
  transition-delay: var(--rd, 0ms);
}

/* ---------------------------------------------------------
   Адаптив
   --------------------------------------------------------- */
@media (max-width: 1024px) {
  .nav { display: none; }
  .burger { display: grid; }
  .header__actions { margin-inline-start: auto; }
  .header__actions .btn { display: none; }
  .stats { grid-template-columns: repeat(2, 1fr); }
  .domain, .solutions, .tech, .cta, .contact { grid-template-columns: 1fr; }
  .domain:nth-child(even) .domain__media { order: 0; }
  .domain__media, .solutions__media, .tech__media, .cta__media { max-width: 560px; }
  .footer__top { grid-template-columns: 1fr 1fr; }
}

/* Узкий телефон: в шапке остаётся только знак, кнопка меню и тема.
   Переключатель языка переезжает в мобильное меню — там он заметнее,
   а в шапке освобождает место, которого не хватало кнопке меню. */
@media (max-width: 460px) {
  .header__inner { gap: 12px; }
  .logo__text { display: none; }
  .header__actions .lang { display: none; }
}

@media (max-width: 640px) {
  body { font-size: 16px; }
  /* Меньше 16px в поле ввода — iOS Safari при фокусе увеличивает страницу
     и обратно её не возвращает. */
  .field input, .field textarea { font-size: 16px; }
  /* На телефоне первый экран начинается сразу под шапкой, без вертикального центрирования. */
  .hero {
    min-height: auto;
    justify-content: flex-start;
    padding-top: 96px;
    padding-bottom: 48px;
  }
  .display { font-size: clamp(30px, 8.4vw, 44px); }
  .manifesto__text { max-width: none; }
  .form__grid { grid-template-columns: 1fr; }
  .footer__top { grid-template-columns: 1fr; gap: 34px; }
  /* Шкала процесса разворачивается в вертикальный список.
     Раньше это была горизонтальная прокрутка без видимой полосы: шесть
     этапов из восьми оставались за краем, и клавиатурой их было не достать. */
  .process__track {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    overflow: visible;
    gap: 0;
  }
  /* Кружок слева на две строки, номер и название — колонкой справа от него. */
  .step {
    display: grid;
    grid-template-columns: 62px 1fr;
    grid-template-rows: auto auto;
    column-gap: 18px;
    text-align: start;
    padding: 10px 0;
  }
  .step__dot { grid-column: 1; grid-row: 1 / span 2; align-self: center; margin: 0; }
  .step__num { grid-column: 2; grid-row: 1; align-self: end; margin: 0 0 3px; }
  .step__title { grid-column: 2; grid-row: 2; align-self: start; font-size: 15.5px; padding: 0; }
  /* Связь между этапами — вертикальная линия, кружок перекрывает её собой. */
  .step::before {
    top: 0;
    bottom: 0;
    inset-inline-start: 31px;
    width: 1px;
    height: auto;
    background: linear-gradient(180deg, transparent, var(--line) 18%, var(--line) 82%, transparent);
  }

  .metrics-wide { grid-template-columns: 1fr 1fr; }
}

/* На узком экране путь до места короче, а вход быстрее: на телефоне
   блок пролистывают, а не рассматривают. Параллакс скрипт здесь не включает. */
@media (max-width: 640px) {
  [data-reveal] { --rv-y: 16px; --rv-dur: var(--mo-in-sm); --rv-shrink: 0; }
  .banner[data-reveal],
  .hero__media[data-reveal],
  .tech[data-reveal],
  .cta[data-reveal],
  .domain__media[data-reveal],
  .solutions__media[data-reveal],
  .case[data-reveal],
  .post[data-reveal],
  .metrics-wide[data-reveal] { --rv-y: 22px; --rv-dur: var(--mo-in); }
}

/* ---------------------------------------------------------
   Уважение к настройке «меньше движения»

   Выключатели --m-on / --m-hide гасят весь слой одним правилом:
   и появление блоков, и очередь внутри групп, и прорисовку шкалы,
   и выезд мобильного меню. Скрипт дополнительно не запускает
   параллакс, счётчики и скрытие шапки. Содержимое видно сразу.
   --------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  :root { --m-on: 0; --m-hide: 1; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-delay: 0s !important;
    transition-duration: .001ms !important;
    transition-delay: 0s !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .header.is-hidden { transform: none; }
  /* Смена схемы на снимок мгновенная; светового прохода при этом быть не должно. */
  .viz__scan { display: none; }
}


/* ---------------------------------------------------------
   Согласие на cookie

   Блокирующее окно: пока выбор не сделан, страницей пользоваться
   нельзя. Заперт при этом только ввод — сам текст страницы остаётся
   в разметке и виден за подложкой, поэтому поисковый робот и режим
   чтения получают страницу целиком.

   Прокрутка запирается не скриптом, а самим слоем: overscroll-behavior
   не пускает прокрутку на страницу под ним. Без JavaScript окно всё
   равно работает — это обычная форма, каждая кнопка шлёт своё значение.
   --------------------------------------------------------- */
.consent {
  position: fixed;
  inset: 0;
  z-index: 120;                       /* выше шапки, ниже заставки */
  display: grid;
  place-items: end center;
  padding: clamp(16px, 3vw, 32px);
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  overscroll-behavior: contain;
  overflow-y: auto;
}
@media (min-width: 720px) { .consent { place-items: center; } }

.consent__box {
  width: min(560px, 100%);
  /* Раскрытый текст политики упирается в высоту экрана и дальше листается
     внутри самого окна: страница под ним заперта, и её прокрутка тут не
     поможет. Прокручивается окно целиком, а не текст внутри него —
     содержимое <details> лежит в анонимном боксе, до которого флекс-
     ограничения не доходят, и внутренняя прокрутка там расползалась:
     на узком экране текст уезжал под кнопки. */
  max-height: calc(100svh - clamp(32px, 6vw, 64px));
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Обычный поток, а не флекс: у липкой полосы с кнопками внутри
     флекс-контейнера containing block равен ей самой, и прилипать ей
     некуда — она уезжает вместе с текстом. */
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 24px 70px -24px rgba(0, 0, 0, .7);
  --consent-pad: clamp(24px, 3.5vw, 34px);
  /* Снизу отступа нет: его берёт на себя закреплённая полоса с кнопками,
     иначе текст уезжал бы в зазор под ней. */
  padding: var(--consent-pad) var(--consent-pad) 0;
  /* Вход без движения содержимого страницы: только прозрачность и сдвиг. */
  animation: vv-consent-in var(--mo-ui) var(--ease-out) both;
}

/* Окно шире, когда в нём читают документ: 560 px под юридический текст мало. */
.consent:has(.consent__doc[open]) .consent__box { width: min(760px, 100%); }

/* Раскрывающийся текст политики. details нативен и не требует скрипта —
   это важно: без скрипта окно всё равно обязано быть работоспособным. */
.consent__doc {
  margin-top: 20px;
  border-top: 1px solid var(--line-soft);
  min-height: 0;
}
.consent__doc > summary {
  cursor: pointer;
  padding: 14px 0 0;
  font-size: 13.5px;
  color: var(--muted-2);
  list-style: none;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.consent__doc > summary::-webkit-details-marker { display: none; }
.consent__doc > summary::after { content: " ↓"; text-decoration: none; }
.consent__doc[open] > summary::after { content: " ↑"; }
.consent__doc > summary:hover { color: var(--text); }

.consent__prose {
  margin-top: 14px;
  padding-inline-end: 4px;
  font-size: 14.5px;
  line-height: 1.6;
}
.consent__prose h2 { font-size: 16px; margin: 22px 0 8px; }
.consent__prose h3 { font-size: 15px; margin: 18px 0 6px; }
.consent__prose p, .consent__prose ul, .consent__prose ol { margin: 0 0 14px; }
.consent__prose table { font-size: 13.5px; }
.consent__prose th, .consent__prose td { padding: 8px 12px 8px 0; }
.consent__prose td:first-child { white-space: normal; }

/* На юридических страницах окно ничего не запирает: полоса внизу,
   поверх которой страница читается целиком. */
.consent--bar {
  place-items: end center;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  pointer-events: none;               /* фон полосы кликам не мешает */
  overflow: visible;
}
.consent--bar .consent__box {
  pointer-events: auto;
  width: min(920px, 100%);
  padding-bottom: var(--consent-pad);
  animation: none;
}
.consent--bar .consent__actions { position: static; background: none; padding-bottom: 0; }
@keyframes vv-consent-in {
  from { opacity: 0; transform: translate3d(0, calc(14px * var(--m-on, 1)), 0); }
  to   { opacity: 1; transform: none; }
}

.consent__title { font-size: 20px; line-height: 1.25; margin: 0 0 10px; font-weight: 600; }
.consent__text { color: var(--muted); font-size: 15px; line-height: 1.6; margin: 0 0 22px; }

/* Кнопки прилипают к низу окна: сколько бы текста ни было раскрыто,
   выбор остаётся на виду и до него не нужно долистывать. Отрицательные
   поля растягивают подложку на всю ширину окна, перекрывая текст,
   который уезжает под неё. */
.consent__actions {
  position: sticky;
  bottom: 0;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  /* Отрицательные поля по бокам растягивают подложку на всю ширину окна,
     чтобы текст уходил под неё, а не выглядывал по краям. */
  margin: 22px calc(var(--consent-pad) * -1) 0;
  padding: 18px var(--consent-pad) var(--consent-pad);
  background: linear-gradient(to bottom, transparent, var(--bg-elev) 26%);
}

/* Обе кнопки одного размера, формы и веса. Это не вкусовое решение:
   согласие считается свободным только тогда, когда отказаться не
   труднее, чем согласиться. Залитая белым кнопка «принять» рядом с
   бледной «отказаться» — ровно то, за что такие окна признают
   недействительными. Отличие оставлено одно и небольшое: у согласия
   акцентная рамка, чтобы кадр не выглядел незаконченным. */
.consent__btn {
  flex: 1 1 180px;
  justify-content: center;
  min-height: 46px;
}
.consent__btn--yes { border-color: var(--accent-line); }
.consent__btn--yes:hover { border-color: var(--accent); background: var(--bg-card-2); }

.consent__more {
  display: inline-block;
  margin-top: 18px;
  font-size: 13.5px;
  color: var(--muted-2);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.consent__more:hover { color: var(--text); }

/* Ссылка «Настройки cookie» в подвале: возвращает окно на любой странице. */
.footer__consent {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: start;
}

/* ---------------------------------------------------------
   Арабский: письмо справа налево

   Основная вёрстка зеркалится сама — отступы и позиции заданы
   логическими свойствами. Здесь только то, что логикой не выражается:
   направление стрелок и точка отсчёта у полосы прогресса.
   --------------------------------------------------------- */
[dir="rtl"] .link-arrow .ico,
[dir="rtl"] .back-link .ico { transform: scaleX(-1); }
[dir="rtl"] .link-arrow:hover .ico { transform: scaleX(-1) translateX(5px); }
[dir="rtl"] .preloader__bar span { transform-origin: 100% 50%; }
/* Развёртка идёт по направлению письма: в арабской версии — справа налево. */
[dir="rtl"] .viz { --viz-dir: -1; }

/* Смена слайдов идёт по направлению письма; зеркалится и боковой
   слой затемнения — вертикальный от направления не зависит. */
[dir="rtl"] .banner { --bnr-rtl: -1; }
[dir="rtl"] .banner__slide::before { transform: scaleX(-1); }
[dir="rtl"] .banner__title { line-height: 1.3; }
[dir="rtl"] .hp { left: auto; right: -9999px; }

/* Разрядка в верхнем регистре арабицу только портит: буквы соединяются,
   и разрыв между ними выглядит как ошибка набора. */
[dir="rtl"] .kicker,
[dir="rtl"] .tag,
[dir="rtl"] .contact__label,
[dir="rtl"] .manifesto__sign,
[dir="rtl"] .footer__col h4 {
  letter-spacing: 0;
  text-transform: none;
}
[dir="rtl"] .logo__text { letter-spacing: .12em; }

/* Заголовки арабицы требуют большей высоты строки: у шрифта высокие
   выносные элементы, и при плотном наборе строки наезжают друг на друга. */
[dir="rtl"] .display { line-height: 1.25; }
[dir="rtl"] .h2 { line-height: 1.3; }
[dir="rtl"] .manifesto__text { line-height: 1.45; }
