/* ==========================================================================
   ViewChat — Live Community Network: сеть портретов, нитей и частиц
   Изолированный слой стилей: всё внутри .vc-network, глобальные стили не трогаем.

   Секция заменила шаблонный блок STATISTIC («1M+ worldwide users», «99% User
   Satisfaction», «4.93 App Rating») на его же позиции: выше GLOBAL COMMUNITY,
   ниже CONNECTION STORY. Разметка обеих соседних секций не тронута.

   Опорное решение файла — то же, что у Connection Story: CSS отдаёт ФИНАЛЬНОЕ
   состояние, а JS его РАЗБИРАЕТ перед входом. Значения по умолчанию — конец
   истории: портреты видны, нити нарисованы, число на месте. Отсюда бесплатно:
   без JS, при prefers-reduced-motion и при любой опечатке в имени класса видно
   законченную сцену, а не пустой тёмный прямоугольник.

   Состояния, которые ставит js/viewchat-live-network.js:
     .is-armed   — сцена взведена в предвходовое состояние (ставится ТОЛЬКО
                   если секция ещё ниже вьюпорта, см. шапку JS);
     .is-entered — вход проигран: портреты проявлены, нити дорисованы;
     .is-playing — секция в кадре, частицы и дыхание идут;
     .is-static  — анимации отключены (reduce motion / Save-Data / слабое
                   устройство): статичная сеть с финальным числом.

   SVG для сцены — сознательное отступление от практики соседних секций (там
   сцены на CSS-градиентах и на canvas). Нити обязаны быть кривыми переменной
   длины с проявлением по stroke-dashoffset; ни градиентом, ни canvas это не
   выражается, а бриф прямо запрещает canvas. Частицы при этом НЕ внутри SVG:
   это обычные div, которые двигаются одним transform на композиторе.
   ========================================================================== */

.vc-network {
  /* --- Палитра ---------------------------------------------------------- */
  /* Фон подобран так, чтобы оба шва были невидимы, а тело секции было чуть
     светлее глобуса: сверху точное значение нижнего края глобуса
     (--vcg-space-3: #090d12), снизу — фон Connection Story (--vcs-ink: #07080b). */
  --vcn-top: #090d12;
  --vcn-mid: #0d1119;
  --vcn-bottom: #07080b;

  /* Акценты общие для трёх тёмных секций страницы: коралл ViewChat и тёплый
     огонь городов — тот же, что --vcg-lights и --vcs-lights у соседей. */
  --vcn-coral: #f2565c;
  --vcn-coral-hi: #ff6d73;
  --vcn-lights: #ffe6bc;
  --vcn-ivory: #eaf2f8;
  --vcn-ivory-soft: rgba(206, 224, 238, 0.72);
  --vcn-rim: rgba(152, 177, 194, 0.28);
  --vcn-rim-hi: rgba(198, 220, 236, 0.62);
  --vcn-thread: rgba(186, 208, 226, 0.5);

  /* --- Геометрия -------------------------------------------------------- */
  --vcn-max: 1440px;

  /* Кинематографическая сцена: высота фиксирована диапазоном, чтобы крупная
     композиция оставалась цельной и на обычном, и на сверхшироком экране. */
  --vcn-stage-h: clamp(860px, calc(100svh - 84px), 960px);

  /* Базовый размер портрета; конкретный множится на --vcn-scale из конфига. */
  --vcn-unit: clamp(84px, 7.4vw, 110px);
  --vcn-count-size: clamp(96px, 11vw, 176px);

  /* Ступень адаптива: 0 — десктопная раскладка, 1 — та же сеть, собранная
     плотнее. Всё, что отличает широкую сцену от узкой, помножено на это число,
     поэтому при t = 0 все формулы вырождаются в прежние значения. Его же
     читает js/viewchat-live-network.js, чтобы концы нитей шли за узлами. */
  --vcn-t: 0;
  /* Поправка диаметров для самых узких экранов. */
  --vcn-mk: 1;

  /* Подъём нижнего яруса. Ноль везде, кроме узкого экрана, и именно поэтому
     отдельный токен, а не правка координат проекции: те — второй конец
     интерполяции, и сдвинь мы их, уехали бы и промежуточные ступени 768..1199,
     которые обязаны остаться прежними. То же число читает JS (readLift),
     чтобы концы нитей поднялись вместе с кругами. */
  --vcn-lift: 0px;

  position: relative;
  z-index: 10;
  isolation: isolate;
  /* Сетка безопасности от горизонтального переполнения. Стоит на секции, а не
     на сцене: внутри сцены ничего резать не нужно, всё и так в долях. */
  overflow: hidden;
  contain: paint;
  color: var(--vcn-ivory);
  background-color: #06090e;
  background:
    radial-gradient(48% 54% at 50% 45%, rgba(33, 44, 62, 0.62) 0%, rgba(9, 13, 18, 0) 72%),
    linear-gradient(180deg, var(--vcn-top) 0%, var(--vcn-mid) 46%, var(--vcn-bottom) 100%);
}

/* Светлые разделители шаблона у краёв тёмного блока читаются как шов — тот же
   приём, что у глобуса и у Connection Story. Верхний разделитель уже погашен
   правилом `.vc-global-globe + hr.divider`, нижний гасим здесь. Оба селектора
   без :has(), поэтому шва не будет и в Firefox младше 121. */
.vc-network + hr.divider,
hr.divider:has(+ .vc-network) {
  display: none;
}

.vc-network__inner {
  /* Формула ширины та же, что у секции глобуса. */
  width: min(var(--vcn-max), 100% - 10vw);
  margin: 0 auto;
  padding: 42px 0;
}

.vc-network__stage {
  position: relative;
  min-height: 860px;
  height: var(--vcn-stage-h);
  max-height: 960px;
  /* Инвалидация стиля не выходит за пределы сцены. paint нельзя добавлять
     отдельно от layout: свечение намеренно выходит за границы коробки. */
  contain: layout paint;
}

/* Мягкое свечение под счётчиком: фон, а не фильтр. */
.vc-network__glow {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(36% 37% at 50% 44%, rgba(255, 230, 188, 0.12) 0%, rgba(255, 230, 188, 0) 72%),
    radial-gradient(62% 26% at 50% 88%, rgba(95, 126, 154, 0.10) 0%, rgba(9, 13, 18, 0) 76%);
}

/* --- Нити ----------------------------------------------------------------- */
/* viewBox ставит JS в ПИКСЕЛЯХ измеренной сцены (`0 0 W H`), поэтому одна
   пользовательская единица равна одному CSS-пикселю: штрих равномерный,
   getTotalLength() и getPointAtLength() сразу в пикселях, а штриховка
   проявления не зависит от пропорций окна.

   preserveAspectRatio="none" в разметке оставлен СОЗНАТЕЛЬНО: в окне между
   живым ресайзом и debounce-пересчётом устаревший viewBox растягивается вместе
   с коробкой ровно так же, как процентные портреты, и концы нитей остаются
   приклеенными к кругам. При meet они бы заметно отклеились посреди
   перетаскивания окна. */

.vc-network__threads {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

/* Свечение нити сделано двумя наложенными путями с одним d, а не фильтром:
   SVG-фильтр перерастеризует поддерево при каждом изменении входа и выкидывает
   элемент из композитора. */
.vc-network__thread {
  fill: none;
  stroke: var(--vcn-thread);
  stroke-width: 1;
  stroke-linecap: round;
  opacity: 0.56;
  /* Проявление: JS ставит инлайном stroke-dasharray и stroke-dashoffset = L,
     класс .is-entered гасит offset до нуля. Это единственная анимация в секции
     вне opacity/transform — однократная, ~700 мс, на десяти волосяных линиях,
     в момент, когда секция в остальном простаивает. */
  transition: stroke-dashoffset 760ms cubic-bezier(0.33, 0.9, 0.32, 1) var(--vcn-draw-delay, 0s);
}

.vc-network__thread--halo {
  stroke-width: 4;
  opacity: 0.14;
}

/* --- Частицы -------------------------------------------------------------- */
/* Обычные div, а не SVG-круги: SVG-слой после сборки остаётся статичным и
   растеризуется один раз, а мягкий градиентный край дёшев только в CSS.
   В кадре пишется ровно один transform. */

.vc-network__sparks {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  /* Частицы появляются последними — после того, как нити дорисовались. */
  opacity: 1;
  transition: opacity 420ms linear 1200ms;
}

.vc-network__spark {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  /* На узкой сцене частицы мельче: круги там вдвое меньше, и прежняя
     точка читалась бы как ещё один узел. */
  --vcn-spark-d: calc(var(--vcn-spark-size, 5px) * (1 - .28 * var(--vcn-t, 0)));
  width: var(--vcn-spark-d);
  height: var(--vcn-spark-d);
  /* Половина размера: голый translate3d(x, y, 0) сразу центрирует точку. */
  margin: calc(var(--vcn-spark-d) / -2) 0 0 calc(var(--vcn-spark-d) / -2);
  border-radius: 50%;
  /* Свечение — радиальным градиентом, не box-shadow и не фильтром. Мягкий край
     это то, что делает движение плавным даже на 30 кадрах. */
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, rgba(226, 238, 250, 0.42) 38%, rgba(226, 238, 250, 0) 72%);
}

.vc-network__spark--coral {
  background: radial-gradient(circle, rgba(255, 138, 132, 0.95) 0%, rgba(242, 86, 92, 0.4) 38%, rgba(242, 86, 92, 0) 72%);
}

.vc-network__node-pulse {
  position: absolute;
  top: 0;
  left: 0;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border: 1px solid rgba(255, 239, 211, 0.72);
  border-radius: 50%;
  animation: vc-network-node-pulse 380ms ease-out forwards;
  pointer-events: none;
}

@keyframes vc-network-node-pulse {
  from { opacity: .85; transform: scale(.45); }
  to { opacity: 0; transform: scale(2.2); }
}

.vc-network.is-playing .vc-network__spark {
  will-change: transform;
}

/* --- Портреты ------------------------------------------------------------- */
/* Три вложенных элемента, потому что позиция, дыхание и вход/hover хотят
   transform на одной коробке:
     __portrait      — только позиция (translate(-50%, -50%));
     __portrait-float — только дыхание (@keyframes);
     __portrait-card  — вход и hover (transition).
   Смешать их на одном узле нельзя: последний transform затирает предыдущие. */

.vc-network__people {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

.vc-network__portrait {
  position: absolute;
  /* Десктопная доля и доля узкой сцены — два конца одной интерполяции;
     где мы сейчас, решает --vcn-t. При t = 0 обе строки дают ровно
     var(--vcn-x) / var(--vcn-y) — прежнюю десктопную раскладку. */
  left: calc(var(--vcn-x) + (var(--vcn-mx, var(--vcn-x)) - var(--vcn-x)) * var(--vcn-t, 0));
  top: calc(
    var(--vcn-y) + (var(--vcn-my, var(--vcn-y)) - var(--vcn-y)) * var(--vcn-t, 0)
    - var(--vcn-lift, 0px) * var(--vcn-lower, 0));
  /* То же с диаметром: слева десктопный (--vcn-unit × scale), справа —
     пиксельный из проекции. Бриф задаёт размеры узлов на узком экране в
     пикселях, а не в долях экрана: на 390 и на 430 они обязаны совпадать. */
  --vcn-d0: calc(var(--vcn-unit) * var(--vcn-scale, 1));
  --vcn-d1: calc(var(--vcn-md, 40px) * var(--vcn-mk, 1));
  width: calc(var(--vcn-d0) + (var(--vcn-d1) - var(--vcn-d0)) * var(--vcn-t, 0));
  height: calc(var(--vcn-d0) + (var(--vcn-d1) - var(--vcn-d0)) * var(--vcn-t, 0));
  transform: translate(-50%, -50%);
  perspective: 700px;
}

@keyframes vc-network-breathe {
  /* Первый и последний кадры идентичны: цикл замкнут, поэтому при повторе
     карточки не перескакивают в начальную точку. Промежуточные точки задают
     очень маленькую эллиптическую траекторию, а не вертикальное подпрыгивание. */
  0%, 100% { transform: translate3d(0, calc(var(--vcn-amp, 3px) * -.72), 0); }
  25% { transform: translate3d(calc(var(--vcn-amp, 3px) * .72), calc(var(--vcn-amp, 3px) * -.34), 0); }
  50% { transform: translate3d(calc(var(--vcn-amp, 3px) * .48), calc(var(--vcn-amp, 3px) * .78), 0); }
  75% { transform: translate3d(calc(var(--vcn-amp, 3px) * -.68), calc(var(--vcn-amp, 3px) * .42), 0); }
}

.vc-network__portrait-float {
  width: 100%;
  height: 100%;
  /* --vcn-amp — ПОЛОВИНА размаха: 2.0–3.0px дают требуемые брифом 4–6px.
     Расфазировка — отрицательный --vcn-delay: портрет стартует уже в середине
     цикла, поэтому никто никого не ждёт и ни одна пара не синхронна. */
  animation: vc-network-breathe var(--vcn-dur, 4.4s) cubic-bezier(.42, 0, .58, 1) var(--vcn-delay, 0s) infinite;
}

/* Пауза вне кадра — один класс на секции, ноль работы в кадре. Не
   `animation: none`: при возврате портреты прыгнули бы в нулевую фазу. */
.vc-network:not(.is-playing) .vc-network__portrait-float {
  animation-play-state: paused;
}

/* will-change только пока играем и только там, где он оправдан. Постоянно на
   двенадцати кругах это ~590 КБ текстуры каждый при DPR 3 — 7 МБ поверх живого
   WebGL-контекста глобуса; за бюджетом слоёв Chrome молча перестаёт продвигать,
   и will-change начинает только стоить. */
.vc-network.is-playing .vc-network__portrait-float {
  will-change: transform;
}

.vc-network__portrait-card {
  position: relative;
  width: 100%;
  height: 100%;
  /* Значения по умолчанию — КОНЕЦ входа. Предвходовое состояние ставит .is-armed. */
  opacity: 1;
  transform: rotateX(var(--vcn-rx, 0deg)) rotateY(var(--vcn-ry, 0deg)) translateZ(var(--vcn-z, 0px));
  transition:
    opacity 620ms ease-out var(--vcn-in-delay, 0s),
    transform 620ms cubic-bezier(0.22, 0.9, 0.3, 1) var(--vcn-in-delay, 0s);
  /* Родительские слои событий не ловят — карточке возвращаем ради hover. */
  pointer-events: auto;
  box-shadow: 0 16px 34px rgba(0, 0, 0, 0.30);
}

.vc-network__face {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Круглая маска на самом изображении, без обёртки с overflow: hidden:
     круглый клип обёртки вокруг анимируемого потомка — давний баг WebKit,
     клип не переприменяется каждый кадр и углы мигают квадратом. */
  border-radius: 50%;
  /* Светлый студийный фон портретов на почти чёрной сцене приглушается
     ПОСТОЯННЫМ фильтром: значение не анимируется, слой растеризуется один раз. */
  filter: grayscale(0.30) brightness(0.84) contrast(1.08);
  background: rgba(16, 22, 30, 0.9);
  opacity: var(--vcn-opacity, 1);
}

/* План глубины. Обе ступени помножены на --vcn-t, поэтому на десктопе
   (t = 0) правила ниже дают в точности значения выше: grayscale(.30)
   brightness(.84) и var(--vcn-opacity). Разводить планы яркостью нужно именно
   на узкой сцене, где круги мельче и стоят теснее. Гасим умеренно:
   бриф требует, чтобы не менее двенадцати узлов оставались различимы. */
.vc-network__portrait[data-vc-depth="mid"] .vc-network__face {
  filter:
    grayscale(calc(.30 + .12 * var(--vcn-t, 0)))
    brightness(calc(.84 - .10 * var(--vcn-t, 0)))
    contrast(1.08);
  opacity: calc(var(--vcn-opacity, 1) * (1 - .12 * var(--vcn-t, 0)));
}

.vc-network__portrait[data-vc-depth="bg"] .vc-network__face {
  opacity: calc(var(--vcn-opacity, 1) * (1 - .22 * var(--vcn-t, 0)));
}

/* Плейсхолдер вместо фотографии: рисуется целиком в CSS, ни одного запроса и
   ни одного файла. Коробка, круг, дыхание и hover — те же самые, поэтому
   подстановка готового портрета меняет только конфиг в JS (kind + src) и не
   трогает ни раскладку, ни нити, ни анимацию. */
.vc-network__face--placeholder {
  position: relative;
  /* clip-path вместо overflow: hidden — по той же причине, что и выше. */
  clip-path: circle(50% at 50% 50%);
  filter: none;
  background:
    radial-gradient(72% 72% at 50% 38%, rgba(30, 40, 54, 0.95) 0%, rgba(12, 17, 24, 0.98) 78%);
}

/* Голова и плечи: обобщённый силуэт, который читается как узел сети, а не как
   сломанная картинка. Псевдоэлементы не анимируются — дыхание живёт выше. */
.vc-network__face--placeholder::before,
.vc-network__face--placeholder::after {
  content: "";
  position: absolute;
  background: rgba(140, 168, 196, 0.16);
}

.vc-network__face--placeholder::before {
  left: 50%;
  top: 22%;
  width: 32%;
  height: 32%;
  margin-left: -16%;
  border-radius: 50%;
}

.vc-network__face--placeholder::after {
  left: 50%;
  top: 60%;
  width: 66%;
  height: 46%;
  margin-left: -33%;
  border-radius: 50% 50% 0 0;
}

/* Тонкая рамка отдельным слоем: на hover меняется только её opacity, поэтому
   ховер целиком остаётся внутри правила «анимируем только opacity/transform». */
.vc-network__portrait-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid var(--vcn-rim-hi);
  border-radius: 50%;
  opacity: 0.34;
  transition: opacity 220ms ease-out;
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .vc-network__portrait-card:hover {
    transform: translate3d(0, -4px, 0) rotateX(var(--vcn-rx, 0deg)) rotateY(var(--vcn-ry, 0deg)) translateZ(var(--vcn-z, 0px)) scale(1.02);
    /* На наведении задержка входа не нужна — иначе первый ховер ждал бы её. */
    transition-delay: 0s;
  }

  .vc-network__portrait-card:hover::after {
    opacity: 0.78;
  }
}

/* --- Подписи городов ------------------------------------------------------ */
/* Тихий слой у краёв сцены. Ни выносных линий, ни привязки к портретам: ни один
   конец нити не должен приходить на подпись. Слой aria-hidden — мы иллюстрируем
   идею, а не заявляем пользователей в Лондоне. */

.vc-network__cities {
  position: absolute;
  inset: 0;
  z-index: 7;
  pointer-events: none;
}

.vc-network__city {
  position: absolute;
  /* Та же интерполяция, что и у узлов: подпись переезжает вместе со своей
     частью сцены, а не прыгает на брейкпоинте. */
  left: calc(var(--vcn-city-x) + (var(--vcn-city-mx, var(--vcn-city-x)) - var(--vcn-city-x)) * var(--vcn-t, 0));
  top: calc(
    var(--vcn-city-y) + (var(--vcn-city-my, var(--vcn-city-y)) - var(--vcn-city-y)) * var(--vcn-t, 0)
    - var(--vcn-lift, 0px) * var(--vcn-lower, 0));
  transform: translate(-50%, -50%);
  color: var(--vcn-ivory-soft);
  font-size: clamp(0.62rem, 0.76vw, 0.74rem);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0.88;
}

/* Вторая пара долей — место подписи на узкой сцене: в том же углу сети,
   но в свободном зазоре между кругами, нитями и запретными зонами. */
.vc-network__city--seoul { --vcn-city-x: 5.5%; --vcn-city-y: 7.5%; --vcn-city-mx: 17%; --vcn-city-my: 5.5%; }
.vc-network__city--tokyo { --vcn-city-x: 34.5%; --vcn-city-y: 5.5%; --vcn-city-mx: 65.5%; --vcn-city-my: 17.8%; }
.vc-network__city--paris { --vcn-city-x: 94%; --vcn-city-y: 6.5%; --vcn-city-mx: 91%; --vcn-city-my: 33.5%; }
/* Подпись нижнего яруса: едет вверх вместе со своими узлами. Множитель
   чуть больше единицы: у текста должен остаться воздух и сверху (круги),
   и снизу (панель), а полоса между ними после подъёма уже. */
.vc-network__city--london { --vcn-city-x: 2.5%; --vcn-city-y: 65%; --vcn-city-mx: 34%; --vcn-city-my: 82%; --vcn-lower: 1.25; }
.vc-network__city--new-york { --vcn-city-x: 5.5%; --vcn-city-y: 41%; }
.vc-network__city--singapore { --vcn-city-x: 75%; --vcn-city-y: 80.5%; }
.vc-network__city--dubai { --vcn-city-x: 96%; --vcn-city-y: 58%; }
.vc-network__city--astana { --vcn-city-x: 55%; --vcn-city-y: 81%; }

/* Четыре подписи без узких координат гаснут по мере сжатия сцены и на
   узком экране выключены вовсе (см. медиазапрос ниже): класть их некуда,
   не задев ни полосу счётчика, ни панель метрик. Скрывается ПОДПИСЬ, а не узел. */
.vc-network__city--new-york,
.vc-network__city--singapore,
.vc-network__city--dubai,
.vc-network__city--astana {
  opacity: calc(.88 * max(0, 1 - var(--vcn-t, 0) * 2.2));
}

/* --- Счётчик -------------------------------------------------------------- */

.vc-network__headline {
  position: absolute;
  left: 50%;
  /* На десктопе блок стоит по центру сцены, на узкой — прижат к её верху
     на 182px. Между ними та же интерполяция по --vcn-t, что и у узлов: иначе
     на промежуточных ступенях типографика села бы на нижнюю дугу сети.

     Именно 182, а не «сразу под верхней дугой»: между дугой и счётчиком
     проходит самая длинная нить сцены (0-6), которая обходит типографику
     сверху, и её плечи у краёв сцены опускаются ниже её вершины. */
  top: calc(45% - (45% - 182px) * var(--vcn-t, 0));
  z-index: 5;
  width: min(760px, 64%);
  margin: 0;
  transform: translate(-50%, calc(-50% * (1 - var(--vcn-t, 0))));
  text-align: center;
  pointer-events: auto;
}

.vc-network__headline h2 { margin: 14px 0 0; }

.vc-network__badge {
  display: inline-flex;
  padding: 8px 13px;
  border: 1px solid rgba(214, 229, 240, .20);
  border-radius: 999px;
  background: rgba(10, 15, 23, .58);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
  color: rgba(234, 242, 248, .80);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .20em;
}

.vc-network__count {
  display: block;
  color: #fff;
  font-size: var(--vcn-count-size);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  /* Без табличных цифр строка дёргается на каждом кадре счёта: у
     пропорциональных цифр разная ширина. Обе гарнитуры сайта несут tnum. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  /* Значения по умолчанию — конец входа. */
  opacity: 1;
  transform: none;
  transition: opacity 520ms ease-out, transform 520ms cubic-bezier(0.22, 0.9, 0.3, 1);
}

.vc-network__label {
  display: block;
  margin-top: 12px;
  color: var(--vcn-ivory-soft);
  font-size: clamp(0.72rem, 0.88vw, 0.9rem);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  opacity: 1;
  transition: opacity 520ms ease-out 120ms;
}

.vc-network__subline {
  margin: 17px 0 0;
  color: rgba(226, 237, 246, .78);
  font-size: clamp(.95rem, 1.25vw, 1.1rem);
  line-height: 1.35;
  transition: opacity 520ms ease-out 180ms, transform 520ms cubic-bezier(0.22, 0.9, 0.3, 1) 180ms;
}

.vc-network__cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 24px;
  padding: 14px 21px;
  border-radius: 999px;
  background: #fff;
  color: #111821;
  font-size: .86rem;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  box-shadow: 0 12px 28px rgba(0, 0, 0, .22);
  transition: opacity 520ms ease-out 240ms, transform 520ms cubic-bezier(0.22, 0.9, 0.3, 1) 240ms, background-color 180ms ease;
}

.vc-network__cta:hover { color: #111821; background: #f5f7f8; transform: translateY(-2px); }

.vc-network__metrics {
  position: absolute;
  z-index: 6;
  left: 50%;
  bottom: 30px;
  width: min(76vw, 1120px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin: 0 auto;
  overflow: hidden;
  border: 1px solid rgba(209, 229, 241, .17);
  border-radius: 18px;
  background: linear-gradient(135deg, rgba(28, 39, 53, .72), rgba(12, 18, 28, .64));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 18px 50px rgba(0, 0, 0, .20);
  backdrop-filter: blur(12px);
  transform: translateX(-50%);
  transition: opacity 520ms ease-out 320ms, transform 520ms cubic-bezier(0.22, 0.9, 0.3, 1) 320ms;
}

.vc-network__metric { min-width: 0; padding: 19px 20px 18px; text-align: center; }
.vc-network__metric + .vc-network__metric { border-left: 1px solid rgba(209, 229, 241, .14); }
.vc-network__metric strong { display: block; color: #fff; font-size: clamp(1.3rem, 2.1vw, 1.85rem); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
/* Прямой потомок, а не любой вложенный span: внутри <strong> теперь живёт
   разметка одометра, и descendant-селектор красил бы её подписью — цифры
   уезжали в 8px, ломали display и добавляли себе margin-top на каждый разряд. */
.vc-network__metric > span { display: block; margin-top: 8px; color: rgba(218, 232, 241, .65); font-size: .69rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }

/* --- Одометр -------------------------------------------------------------- */
/* Разряды чисел секции меняются прокруткой барабана, а не подменой текста:
   старая цифра уходит вверх на свою высоту, новая приходит снизу. Двигаются
   ТОЛЬКО изменившиеся разряды — запятая и плюс лежат отдельными ячейками и не
   анимируются вовсе.

   Барабан из двух ячеек, а не из десяти: длинная лента 0–9 обязывает крутить
   мимо всех промежуточных цифр, а бриф просит короткий переход без «прокрутки
   счётчика такси». Покой барабана — translateY(0) с текущей цифрой сверху;
   после перехода он стоит на -1em с новой цифрой снизу. Перед следующим
   переходом JS кладёт наверх ТУ ЖЕ цифру, что видна сейчас, и снимает
   transform без перехода — кадр сброса визуально неотличим от предыдущего,
   поэтому мигания нет и без третьей ячейки.

   Ни scale, ни blur, ни свечения, ни отскока: единственное анимируемое
   свойство во всём слое — transform барабана. */

/* Одометр наследует типографику своего хозяина и ничего не берёт от соседних
   правил секции: он стоит внутри h2 и внутри <strong>, у которых разные кегли,
   и любое descendant-правило вокруг не должно уметь его перекрасить. */
.vc-network__odo,
.vc-network__odo span,
.vc-network__odo i {
  margin: 0;
  color: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: none;
}

.vc-network__odo {
  display: inline-flex;
  align-items: flex-start;
  line-height: 1;
}

.vc-network__odo-d,
.vc-network__odo-s {
  display: block;
  height: 1em;
  line-height: 1em;
}

/* Окно шириной в одну цифру. Ширину не задаём: у обеих гарнитур сайта включены
   табличные цифры (font-variant-numeric на .vc-network__count и на strong
   метрик), поэтому ячейка и так получает одинаковый шаг и строка не дёргается. */
.vc-network__odo-d {
  overflow: hidden;
}

.vc-network__odo-reel {
  display: block;
  transition: transform 250ms cubic-bezier(.22, .8, .28, 1);
}

.vc-network__odo-reel > i {
  display: block;
  height: 1em;
  line-height: 1em;
  font-style: normal;
}

/* --- Предвходовое состояние ----------------------------------------------- */
/* Единственный класс, который разбирает финальную сцену. JS ставит его ТОЛЬКО
   если секция ещё ниже вьюпорта: при перезагрузке посреди страницы взводить
   нечего — законченный кадр просто есть. */

.vc-network.is-armed .vc-network__portrait-card {
  opacity: 0;
  transform: scale(0.92);
}

.vc-network.is-armed .vc-network__count {
  opacity: 0;
  transform: translate3d(0, 10px, 0);
}

.vc-network.is-armed .vc-network__label {
  opacity: 0;
}

.vc-network.is-armed .vc-network__badge,
.vc-network.is-armed .vc-network__subline,
.vc-network.is-armed .vc-network__cta,
.vc-network.is-armed .vc-network__metrics { opacity: 0; transform: translate3d(-50%, 10px, 0); }

.vc-network.is-armed .vc-network__sparks {
  opacity: 0;
}

/* --- Статичный режим ------------------------------------------------------ */
/* prefers-reduced-motion, Save-Data, слабое устройство. В отличие от глобуса,
   сцена здесь ВСЁ РАВНО строится: бриф требует видимой статичной сети.
   Частицы при этом не создаются вовсе — их не нужно ни прятать, ни считать. */

.vc-network.is-static .vc-network__portrait-float,
.vc-network.is-static .vc-network__portrait-card,
.vc-network.is-static .vc-network__count,
.vc-network.is-static .vc-network__label,
.vc-network.is-static .vc-network__badge,
.vc-network.is-static .vc-network__subline,
.vc-network.is-static .vc-network__cta,
.vc-network.is-static .vc-network__metrics,
.vc-network.is-static .vc-network__odo-reel,
.vc-network.is-static .vc-network__thread {
  animation: none;
  transition: none;
  will-change: auto;
}

.vc-network.is-static .vc-network__sparks {
  display: none;
}

/* --- Ступени адаптива ------------------------------------------------------ */

/* Единственный переключатель композиции — --vcn-t. Ноль означает десктопную
   раскладку ДОСЛОВНО (все формулы ниже при t = 0 вырождаются в прежние
   значения), единица — ту же сеть, собранную плотнее под узкий экран.

   Ступени, а не один порог: между 1200 и 768 сцена проходит через .22, .45 и
   .68, поэтому сужение окна читается как масштабирование и перераскладка одной
   сети, а не как подмена композиции на 992px. Тот же t читает JS
   (readTier) — второго списка брейкпоинтов в проекте нет.

     >= 1200px   t = 0     десктопный Atlas без изменений;
     1024..1199  t = .22   он же, чуть плотнее;
     900..1023   t = .45   compact;
     768..899    t = .68   compact, ближе к узкой раскладке;
     <= 767px    t = 1     та же сеть, собранная в вертикальную сцену. */

@media (max-width: 1199.98px) { .vc-network { --vcn-t: .22; } }
@media (max-width: 1023.98px) { .vc-network { --vcn-t: .45; } }
@media (max-width: 899.98px) { .vc-network { --vcn-t: .68; } }

/* --- Узкая сцена ----------------------------------------------------------- */

/* Ниже 768px сцена не строится заново — она собирается ПЛОТНЕЕ. Те же
   шестнадцать узлов, те же двадцать нитей, та же топология; меняются диаметры,
   плотность координат, яркость дальних плейсхолдеров и число подписей городов.

   Вертикаль сцены (доли считаны от --vcn-stage-h):

     A. COUNTER ZONE   162px .. ~378px  — eyebrow, число, подпись, CTA;
        по её БОКАМ стоят два узла — потому проекция и разводит их от центра;
     B. NETWORK        сверху (.085..175), с боков (.260..375) и снизу
        (.520..795) от счётчика — узлы окружают его, а не выстраиваются
        колонкой;
     C. STATS PANEL    нижние 82px с отступом 30px.

   Высота сцены растёт вместе с шириной и упирается в 860px — ту же высоту,
   что и у compact-ступени. Поэтому на 767 и на 768 сцена одинаковой высоты и
   переход через порог не виден. */

@media (max-width: 767.98px) {
  .vc-network {
    --vcn-t: 1;
    /* 780..860 по брифу; на 412px даёт 811px. */
    --vcn-stage-h: clamp(780px, 416px + 96vw, 860px);
    --vcn-count-size: clamp(40px, 12vw, 52px);
    /* Нижняя дуга сети уезжает вверх на 56px вместе с нитями, частицами
       и подписью: низ сцены нужен панели метрик, которую внизу экрана
       перекрывает чужой фиксированный виджет. Верхняя дуга, боковые узлы и
       полоса счётчика не двигаются вовсе. */
    --vcn-lift: 56px;
  }

  /* Верхняя граница секции. Соседняя секция сверху тоже тёмная и заканчивается
     собственным текстом; без этой линии её последняя строка читается как начало
     нашей сцены. Линия принадлежит #vc-live-network и соседа не трогает. */
  .vc-network::before {
    content: "";
    position: absolute;
    z-index: 8;
    top: 0;
    right: 0;
    left: 0;
    height: 1px;
    background: rgba(196, 218, 236, .13);
    pointer-events: none;
  }

  .vc-network__inner {
    /* Во всю ширину: панель метрик и сеть меряются от края экрана, а не от
       колонки контента. Нулевой padding убирает собственную пустую полосу
       секции над сценой. */
    width: 100%;
    padding: 0;
  }

  .vc-network__stage {
    min-height: 0;
    max-height: none;
  }

  /* Свечение переразмечено под вертикальную сцену: тёплое пятно под числом и
     холодное под нижней дугой сети. Панель метрик остаётся на ровном фоне. */
  .vc-network__glow {
    background:
      radial-gradient(62% 15% at 50% 33%, rgba(255, 230, 188, .13) 0%, rgba(255, 230, 188, 0) 74%),
      radial-gradient(76% 14% at 50% 70%, rgba(95, 126, 154, .16) 0%, rgba(9, 13, 18, 0) 78%);
  }

  /* --- A. COUNTER ZONE ---------------------------------------------------- */
  /* Ширина ограничена НАМЕРЕННО и жёстче, чем просят буквы: по бокам полосы
     стоят два узла сети, и запас между коробкой текста и кругом — это и есть
     чистая запретная зона. */
  .vc-network__headline { width: min(252px, 64%); }

  .vc-network__badge { padding: 6px 10px; font-size: 8px; }

  .vc-network__count {
    letter-spacing: -0.03em;
    /* Число обязано быть в одну строку: перенос сломал бы всю вертикаль яруса
       и утащил CTA в нижнюю дугу сети. */
    white-space: nowrap;
  }

  .vc-network__label {
    margin-top: 10px;
    font-size: .68rem;
    letter-spacing: 0.16em;
  }

  .vc-network__subline { margin-top: 9px; font-size: .82rem; }
  .vc-network__cta { margin-top: 17px; padding: 11px 16px; font-size: .74rem; }

  /* --- B. СЕТЬ ------------------------------------------------------------ */
  /* Круги мельче ещё на восьмую на самых узких экранах: на 320px узел в 56px
     упёрся бы в край сцены. Множитель один на все узлы — пропорции набора не
     меняются. */
  .vc-network { --vcn-mk: 1; }

  /* --- C. STATS PANEL ------------------------------------------------------ */
  /* 102px от низа сцены, а не 30: внизу экрана висит фиксированный AI Chat,
     и правая метрика уходила под него. Виджет не трогаем вовсе — уходим сами.
     Ширина и высота прежние. */
  .vc-network__metrics {
    bottom: 102px;
    width: calc(100% - 32px);
    min-height: 82px;
    border-radius: 16px;
  }

  /* Центрируем содержимое колонки, а не саму колонку: align-items на гриде
     ужал бы ячейки и разорвал вертикальные разделители. */
  .vc-network__metric {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 12px 6px;
  }

  .vc-network__metric strong { font-size: clamp(1.15rem, 5.4vw, 1.42rem); }

  /* Уменьшена ТОЛЬКО подпись: «CONVERSATIONS TODAY» обязано лечь в две строки,
     а не в три, и при этом не тянуть за собой размер главного значения. */
  .vc-network__metric > span {
    margin-top: 7px;
    font-size: .52rem;
    letter-spacing: .05em;
    line-height: 1.3;
  }

  /* Подписи городов: на узкой сцене их четыре. Скрывается ПОДПИСЬ, а не узел —
     остальные четыре города просто нечем подписать, не задев ни полосу
     счётчика, ни панель. */
  .vc-network__city {
    font-size: .58rem;
    letter-spacing: 0.14em;
    opacity: 0.78;
  }

  .vc-network__city--astana,
  .vc-network__city--new-york,
  .vc-network__city--singapore,
  .vc-network__city--dubai {
    display: none;
  }

  /* --- Дальние узлы ------------------------------------------------- */
  /* Восемь CSS-плейсхолдеров на широком экране читаются за счёт размера:
     55..83px хватает, чтобы разглядеть силуэт даже при слабом контрасте. На
     412px те же узлы — 24..32px, и прежние значения (силуэт .16 на почти чёрном
     круге, общая прозрачность .56) давали тёмную дырку, а не участника сети.

     Лечим тремя вещами и только здесь, ниже 768px: круг становится холодным
     slate заметно светлее фона сцены (#0d1119), силуэт — спокойным сине-серым,
     ободок — тонкой светлой линией. Ни свечения, ни анимации: меняются
     только цвета уже существующих слоёв, ни одного нового элемента и ни одного
     файла. Тише настоящих портретов они остаются и так: вдвое меньше, без
     цвета и без лица. */
  .vc-network__face--placeholder {
    background:
      radial-gradient(78% 78% at 50% 32%, #33445e 0%, #1e2937 68%, #18202c 100%);
  }

  .vc-network__face--placeholder::before,
  .vc-network__face--placeholder::after {
    background: rgba(163, 189, 214, .72);
  }

  /* Общая прозрачность дальнего плана перестаёт гасить узел до
     неразличимости: планы разводят размер и цвет, а не исчезновение. */
  .vc-network__portrait[data-vc-depth="bg"] .vc-network__face {
    opacity: .92;
  }

  /* Тот самый ободок. Отдельный слой уже есть у каждого узла — ему достаётся
     больше контраста, новых элементов не заводим. */
  .vc-network__portrait[data-vc-depth="bg"] .vc-network__portrait-card::after {
    border-color: rgba(198, 220, 236, .62);
    opacity: .58;
  }
}

@media (max-width: 399.98px) {
  .vc-network { --vcn-mk: .88; }
}

/* --- Доступность ----------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .vc-network__portrait-float {
    animation: none;
  }

  .vc-network__portrait-card,
  .vc-network__count,
  .vc-network__label,
  .vc-network__badge,
  .vc-network__subline,
  .vc-network__cta,
  .vc-network__metrics,
  .vc-network__thread,
  .vc-network__sparks,
  .vc-network__odo-reel {
    transition: none;
  }

  .vc-network__sparks {
    display: none;
  }
}

/* Почти чёрная композиция не должна схлопываться при запросе контраста. */
@media (prefers-contrast: more) {
  .vc-network {
    --vcn-ivory-soft: rgba(232, 242, 250, 0.92);
    --vcn-thread: rgba(214, 230, 244, 0.7);
    --vcn-rim-hi: rgba(226, 240, 250, 0.8);
  }

  .vc-network__city {
    opacity: 0.8;
  }
}
