/* ==========================================================================
   Вторая версия страницы — слои и движение.
   Подключается ПОСЛЕ tokens.css и style.css: базовые компоненты (карточки,
   темы, отзывы, блок организаторам) переиспользуются как есть, здесь только
   новое — вырезанные портреты поверх гигантской типографики, параллакс,
   бегущие полосы, прелоадер.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Вырезанный портрет
   Цвет лежит в JPEG, форма — в отдельной PNG-маске с альфа-каналом.
   Так первый экран весит 110 КБ вместо 1,2 МБ полноценного PNG.
   -------------------------------------------------------------------------- */

.cutout {
  display: block;
  width: 100%;
  height: auto;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* Путь к маске задаётся здесь, а не в разметке: url() внутри CSS-переменной
   браузер считает относительно таблицы стилей, где переменная подставляется,
   и из style-атрибута маска искалась бы в /css/. */
.cutout--hero {
  -webkit-mask-image: url(../assets/img/cutout-hero-mask.png);
  mask-image: url(../assets/img/cutout-hero-mask.png);
}
.cutout--about {
  -webkit-mask-image: url(../assets/img/cutout-about-mask.png);
  mask-image: url(../assets/img/cutout-about-mask.png);
}
.cutout--dark {
  -webkit-mask-image: url(../assets/img/cutout-dark-mask.png);
  mask-image: url(../assets/img/cutout-dark-mask.png);
}
.cutout--book {
  -webkit-mask-image: url(../assets/img/cutout-book-mask.png);
  mask-image: url(../assets/img/cutout-book-mask.png);
}

/* --------------------------------------------------------------------------
   Первый экран
   -------------------------------------------------------------------------- */

.hero2 {
  padding-top: clamp(4.5rem, 7vw, 6rem);
  padding-bottom: clamp(1.5rem, 3vw, 2.5rem);
  overflow: clip; /* слои выходят за края — подрезаем, но без прокрутки */
}

/* Надзаголовок существует в двух подачах, и показана всегда ровно одна.
   От 901px три слова висят плашками поверх портрета (см. .chip ниже) —
   именно этот приём здесь был изначально, только в плашках стояли цифры.
   Ниже 901px портрет мельчает, и плашка «Международные проекты» заняла бы
   половину его ширины, перекрыв лицо; там работает обычная строка над
   первым экраном.
   Переключение через display, а не visibility или прозрачность: скрытая
   ветка должна уйти из дерева доступности целиком, иначе скринридер
   прочитает три слова дважды. */
@media (min-width: 901px) { .hero__eyebrow { display: none; } }
@media (max-width: 900px) { .hero2__chips { display: none; } }

/* Плашки поверх портрета.
   Подложка полупрозрачная и с размытием — под плашками фотография,
   и тонкий моно-текст без подложки тонет в тёмном пиджаке. Это та же
   причина, по которой размытие стояло здесь и раньше.
   Контейнер не ловит курсор, а сами плашки ловят: они лежат слоем поверх
   всей сцены, и без этого прозрачный прямоугольник во весь первый экран
   перехватывал бы наведение на имя и портрет. */
.hero2__chips {
  position: relative;
  z-index: 3;
  align-self: stretch;
  justify-self: stretch;
  pointer-events: none;
}
.chip {
  position: absolute;
  padding: 0.7rem 1rem;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  line-height: 1.3;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  pointer-events: auto;
}
.chip--1 { left: 2%;   top: 40%; }
.chip--2 { right: 2%;  top: 27%; }
.chip--3 { right: 8%;  bottom: 14%; }

/* Сцена: имя, фигура и круглый бейдж лежат в одной ячейке грида друг на друге */
.hero2__stage {
  position: relative;
  display: grid;
  grid-template-areas: "stage";
  place-items: center;
  /* Высота подобрана так, чтобы фигура целиком помещалась в сцену
     и первый экран не выталкивал кнопки далеко под сгиб. */
  min-height: clamp(20rem, 57vh, 35rem);
  margin-top: clamp(0.75rem, 1.5vw, 1.5rem);
}
.hero2__stage > * { grid-area: stage; }

.hero2__name {
  position: relative;
  z-index: 1;
  align-self: center;
  width: 100%;
  margin: 0;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3.2rem, 15.5vw, 14rem);
  line-height: 0.78;
  letter-spacing: -0.05em;
  text-transform: uppercase;
  color: var(--ink);
  user-select: none;
}
.hero2__name span { display: block; }

/* Фигура стоит на нижней кромке сцены и перекрывает буквы.
   Размер задаётся высотой в процентах от сцены, а не шириной: тогда фигура
   физически не может вылезти за верхнюю кромку и налезть на строку
   с регалиями, какие бы пропорции ни оказались у следующего портрета. */
.hero2__figure {
  position: relative;
  z-index: 2;
  align-self: end;
  justify-self: center;
  display: flex;
  max-width: 100%;
  margin: 0;
  pointer-events: none;
}
/* Высоту ограничиваем в vh, а не процентом от сцены: у сцены задан min-height,
   то есть высота зависит от содержимого — процент от неё замкнул бы зависимость
   на себя, и картинка растянула бы сцену под свой натуральный размер. */
.hero2__figure img {
  height: 54vh;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}

/* Круглый бейдж с текстом по окружности */
.hero2__badge {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 3;
  width: clamp(7.5rem, 11.5vw, 10.5rem);
  height: clamp(7.5rem, 11.5vw, 10.5rem);
  display: grid;
  place-items: center;
}
.hero2__badge svg { width: 100%; height: 100%; animation: badge-spin 22s linear infinite; }
/* Набор подобран под длину окружности: радиус 50 даёт 314 единиц,
   строка из 55 знаков при таком кегле и разрядке занимает 313. */
.hero2__badge text {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.9px;
  fill: var(--muted);
}
.hero2__badge::after {
  content: "";
  position: absolute;
  width: 0.6rem; height: 0.6rem;
  border-radius: 50%;
  background: var(--accent-d);
}
@keyframes badge-spin { to { transform: rotate(360deg); } }

/* Две зоны: заявление и призыв, между ними волосяной разделитель.
   Форматы отсюда ушли в крутящийся круг — держать их в двух местах
   на одном экране незачем. */
.hero2__below {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: clamp(1.25rem, 3vw, 2.75rem);
  margin-top: clamp(1rem, 2vw, 1.75rem);
  padding-top: clamp(1rem, 2vw, 1.5rem);
  border-top: 1px solid var(--line);
}
.hero2__below > * + * {
  border-left: 1px solid var(--line);
  padding-left: clamp(1.25rem, 3vw, 2.75rem);
}

/* Крупная строка под именем — та же типографика, что у манифеста, но она
   стоит одна, без секции-обёртки: поэтому центрирование и снятое ограничение
   ширины задаются здесь, а не наследуются от .manifest. */
.hero2__claim {
  max-width: none;
  margin-inline: auto;
  margin-top: clamp(1rem, 2vw, 1.75rem);
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.4rem, 3.4vw, 3rem);
  line-height: 1.14;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}

.hero2__col { display: flex; flex-direction: column; gap: 0.85rem; }

.hero2__lead {
  font-size: clamp(1.1rem, 1.7vw, 1.5rem);
  line-height: 1.4;
  max-width: 52ch; /* описание стало вчетверо длиннее прежнего */
}

/* Строка текущих и прошлых ролей — моноширинная сноска под описанием */
.hero2__roles {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--muted);
}
.hero2__lead b { font-weight: 600; }
/* точка входа в абзац — тем же зелёным, что и в манифесте */
.hero2__lead em { font-style: normal; color: var(--accent-d); }

.hero2__cta { display: flex; flex-direction: column; gap: 0.85rem; align-items: flex-start; }
.hero2__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }

/* --------------------------------------------------------------------------
   Бегущие полосы
   -------------------------------------------------------------------------- */

.band {
  overflow: hidden;
  padding-block: clamp(0.5rem, 1.2vw, 1rem);
  border-block: 1px solid var(--line);
  user-select: none;
}
.band + .band { border-top: 0; }
.band__track {
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  width: max-content;
  animation: band-slide 34s linear infinite;
}
.band--reverse .band__track { animation-direction: reverse; }
.band__track span {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2rem, 6.5vw, 5.5rem);
  line-height: 1;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  /* Контурная типографика. Заливка — цветом фона, а не transparent:
     -webkit-text-stroke обводит каждый контур глифа, включая внутренние
     стыки перекрывающихся штрихов, и без заливки поверх наружу лезет
     «скелет» буквы. paint-order кладёт обводку под заливку, и стыки
     прячутся, оставляя чистый внешний контур. */
  color: var(--paper);
  -webkit-text-stroke: 1px var(--ink);
  paint-order: stroke fill;
}
.band__track span:nth-child(even) {
  color: var(--ink);
  -webkit-text-stroke: 0;
}
.band__track em { font-style: normal; color: var(--accent-d); -webkit-text-stroke: 0; }
@keyframes band-slide { to { transform: translateX(-50%); } }

/* --------------------------------------------------------------------------
   Манифест
   -------------------------------------------------------------------------- */

.manifest { position: relative; text-align: center; }

/* Крупная фраза внутри секции экспертизы.
   Раньше манифест был отдельной секцией и дышал собственным вертикальным
   паддингом с обеих сторон. Став блоком внутри «Задач», он остался вовсе
   без отступов: сверху до карточек оставался ОДИН пиксель, а снизу —
   129 от паддинга секции, и фраза читалась приплюснутой к карточкам.
   Отступ берётся тем же токеном --sec-y, которым секция задаёт свой
   паддинг: воздух сверху и снизу получается одинаковым, и фраза стоит
   в собственной полосе ровно так, как стояла отдельной секцией. */
.topics2 + .manifest { margin-top: var(--sec-y); }
.manifest__text {
  max-width: 26ch;
  margin-inline: auto;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.4rem, 3.4vw, 3rem);
  line-height: 1.14;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
.manifest__text em { font-style: normal; color: var(--accent-d); }
/* --------------------------------------------------------------------------
   Гигантское слово-подложка за секцией
   -------------------------------------------------------------------------- */

.sec { overflow: clip; } /* подложка не должна расширять страницу */

/* Знак намеренно неподвижен. Он смещался вместе с прокруткой, но на большом
   экране это читалось как дёрганье фона и отвлекало от текста, ради которого
   секция и существует — поэтому смещение убрано, а не ослаблено. */
.ghost {
  position: absolute;
  /* Привязка идёт за НИЗ подложки, а не за её верх, и это принципиально.
     Раньше стоял top: clamp(1rem, 4vw, 4rem) — отступ от верха секции.
     На широком экране подложка высокая, её низ приходился ровно на низ
     заголовка (216 против 217 при 1440), и заголовок лежал внутри слова,
     как и задумано. На узком кегль падает вчетверо, подложка съёживается,
     а верх остаётся на месте — низ уезжал на 57 при заголовке на 134–189,
     между ними открывался зазор в 78 пикселей, и слово читалось уже
     не фоном, а отдельной оборванной надписью над заголовком.
     Высота подложки — это кегль на line-height 0.8, поэтому кегль вынесен
     в переменную и вычитается здесь: низ садится на заголовок при любой
     ширине. На 1440 верх выходит 59,6 вместо прежних 58 — разница
     невидима, десктоп не меняется. */
  --ghost-fs: min(clamp(3rem, 18vw, 17rem), calc(96vw / var(--len, 6)));
  top: calc(var(--sec-y) + 5.5rem - var(--ghost-fs) * 0.8);
  left: 50%;
  z-index: 0;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  /* Размер зависит и от экрана, и от длины слова. Раньше зависел только от
     экрана — и «Организаторам» из тринадцати знаков показывалось меньше чем
     наполовину: это читалось не как приём, а как поломка вёрстки. Делитель
     подобран так, чтобы даже самое широкое по начертанию слово укладывалось
     примерно в 94% ширины экрана. Сначала было заложено 15% вылета за края —
     на телефоне любой срезанный край читается как обрезка, а не как приём,
     каким бы малым он ни был, поэтому теперь слово помещается целиком.
     Ширина буквы зависит от начертания, поэтому при одном делителе разные
     слова занимают от 74% до 94% экрана: разница в размере тут допустима,
     срезанный край — нет.
     Число знаков приходит из разметки, как координаты меток на карте;
     без него берётся среднее.
     Нижняя граница снижена с 5rem: на узких экранах она не давала слову
     уменьшаться вообще, и даже короткие «Сцены» обрезались уже на 350px. */
  font-size: var(--ghost-fs);
  line-height: 0.8;
  letter-spacing: -0.05em;
  text-transform: uppercase;
  white-space: nowrap;
  /* та же история, что и с полосами: заливка фоном + обводка снизу,
     иначе видны внутренние стыки штрихов */
  color: var(--paper);
  -webkit-text-stroke: 1px var(--line);
  paint-order: stroke fill;
  pointer-events: none;
  user-select: none;
  transform: translateX(-50%);
}
.sec--dark .ghost { color: var(--ink); -webkit-text-stroke-color: var(--ink-3); }

/* На узких и средних экранах размер считается от ШИРИНЫ слова, а не от
   числа букв, и это разные вещи. Буквы неодинаковой ширины: «Сцены»
   и «Эфиры» — оба по пять знаков, но различаются на 7%, а «О спикере»
   из девяти знаков уже «Награды» из семи, потому что пробел и «О» узкие.
   Пока делитель был числом букв, при одном значении разные слова
   занимали от 104% до 134% экрана — отсюда и обрезка, разная у каждой
   секции: от 9 до 73 пикселей с каждой стороны на 430px.

   --adv — ширина слова в кеглях, замеряется в браузере один раз:
   ширина элемента, делённая на его font-size. Величина от кегля
   не зависит, только от начертания. При смене слова её надо пересчитать,
   иначе подложка снова начнёт резаться.

   86vw, а не 96: запас в четырнадцать процентов держит слово внутри
   экрана даже когда Unbounded не успел загрузиться и подставился
   запасной Arial Black — он заметно шире.

   Верхняя граница 1320px — ширина, с которой все семь заголовков
   помещаются в одну строку; выше работает базовая формула. Вертикаль
   считает она же: берёт кегль из этой переменной. */
@media (max-width: 1320px) {
  .ghost { --ghost-fs: calc(86vw / var(--adv, 6)); }
}

/* На телефоне подложка обязана выглядывать над заголовком.
   Общая нижняя линия из правила выше работает, пока слова одного роста,
   а рост у них разный: кегль идёт от ширины слова. Короткая «Книга»
   высокая и от этой линии поднимается выше заголовка — её верх виден
   над строкой по всей ширине, и слово читается целиком. Длинные слова
   ниже и до верха заголовка не достают: за заголовком в три-четыре строки
   «Задачи» и «Бренды» прятались, и из-под коротких строк торчал только
   хвост — «АЧИ», «НДЫ», — а «Компании» за однострочным заголовком съезжали
   под него, к подписи. Обрывок слова читается как поломка, а не как фон.

   Поэтому до 640px нынешнее положение зажато с двух сторон:
   — верх слова не ниже строки сразу под номером секции: цифры номера
     кончаются около sec-y + 1rem, верх слова — на sec-y + 1.25rem;
   — низ слова не выше базовой линии первой строки заголовка, иначе
     короткое слово повиснет над ним отдельной надписью. Строка начинается
     на sec-y + 2.15rem (строка номера 1.4rem и зазор 0.75rem), базовая
     линия Unbounded при line-height 1.02 лежит на 0.885 кегля.
   Прописные подложки при line-height 0.8 занимают от 0.025 до 0.775
   кегля, отсюда множители. Крупные слова, которые и так стоят выше,
   не двигаются: «Книга» на 440px осталась ровно там, где была. «Задачи»
   и «Бренды» на той же ширине выглядывали на 5px, теперь на 18;
   «Компании» были на 11px ниже верха заголовка, теперь выглядывают на 17.

   Граница 640px — ширина, до которой номер стоит над заголовком, а не
   слева от него: только в такой шапке первая строка заголовка во всех
   секциях на одной высоте.

   Кегль заголовка здесь — переменная --fs-h2, а не токен напрямую:
   fitTitles в js/onboard2.js уменьшает её у секции, когда длинное слово
   не влезает в строку, и граница должна идти за настоящим кеглем. */
@media (max-width: 640px) {
  .ghost {
    top: clamp(
      calc(var(--sec-y) + 2.15rem + var(--fs-h2) * 0.885 - var(--ghost-fs) * 0.775),
      calc(var(--sec-y) + 5.5rem - var(--ghost-fs) * 0.8),
      calc(var(--sec-y) + 1.25rem - var(--ghost-fs) * 0.025)
    );
  }
}

.sec > .wrap { position: relative; z-index: 1; }

/* Портрет слоем внутри секции «О спикере».
   align-items: start обязателен: при end текст прижимался к нижней кромке
   портрета, и над ним зияла дыра во всю разницу высот — под 400 пикселей. */
.about2 {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
  /* без отступа портрет упирается в карточки ролей вплотную */
  margin-bottom: clamp(2rem, 4vw, 3rem);
}
/* Левая колонка. Раньше под текстом стояла карточка Cleeq и добирала высоту
   до крупного портрета — комментарий на этом месте прямо предупреждал, что
   без неё под текстом зазияет дыра. Так и вышло: при смене позиционирования
   карточка уехала в «Компании и роли», образование — в «Публичную
   деятельность», и под справкой осталось 407 пустых пикселей. Высоту теперь
   добирает сам набор — см. блок #bio .about__text в конце файла. */
.about2__col {
  display: flex;
  flex-direction: column;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}

.about2__figure {
  margin: 0;
  align-self: end;
  display: flex;
  flex-direction: column;
}
/* Портрет занимает всю ширину своей колонки, и высота выводится из пропорции
   760/1340: на 1440 это 504 -> 889. max-height 60rem (960px) — мёртвый
   предохранитель, он не срабатывает никогда; это НЕ ручка размера.
   Если однажды понадобится ужать портрет по высоте, знать нужно три вещи:
   (1) в @media (max-width: 1080px) потолок обязательно сбрасывать, иначе
       на десктопе портрет станет уже, чем на телефоне;
   (2) при потолке картинка повиснет по центру бокса, и справа ляжет полоса
       пустоты — нужен object-position;
   (3) mask-position придётся сдвинуть тем же значением: маска и JPEG
       вписываются одинаковым contain, и сдвиг только одного из них
       срежет портрет маской.
   mask-size: contain и object-fit: contain вписывают картинку и маску
   одинаково, поэтому сами по себе они не разъезжаются. */
.about2__figure img {
  width: 100%;
  height: auto;
  max-height: 60rem;
  object-fit: contain;
}

/* Портрет в тёмной секции */
.dark-figure {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.dark-figure figure { margin: 0; display: flex; justify-content: center; }
.dark-figure figure img {
  height: clamp(18rem, 52vh, 30rem);
  width: auto;
  max-width: 100%;
  object-fit: contain;
}
.dark-figure__text h2,
.dark-figure__text h3 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.7rem, 4vw, 3.2rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  margin-bottom: 1.25rem;
}
.dark-figure__text p { color: var(--muted); font-size: var(--fs-lead); line-height: 1.5; max-width: 46ch; }
.dark-figure__text .book__rank {
  margin-top: 1.25rem;
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--accent);
}
.dark-figure__text .book__meta {
  margin-top: 0.5rem;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-d);
}
.dark-figure__text .cta__actions { margin-top: clamp(1.5rem, 3vw, 2rem); }

/* --------------------------------------------------------------------------
   Имя во всю ширину подвала
   -------------------------------------------------------------------------- */

/* Имя всегда в две строки, как на первом экране, и целиком — без обрезки.
   Раньше оно шло одной строкой, а нижнюю кромку букв срезал отрицательный
   отступ. overflow: clip не создаёт своего контекста форматирования, и этот
   отступ проваливался сквозь блок: подвал наезжал на буквы, его черта шла
   прямо по ним, ножки «Д» срезались. На экранах меньше 2300px имя и так
   переносилось, но при line-height 0.75 ножки «Д» ложились на вторую строку. */
.bigname {
  padding-top: clamp(2rem, 5vw, 4rem);
  text-align: center;
  user-select: none;
}
/* Колонка та же, что у подвала, и она же контейнер: кегль считается от её
   ширины, а не от экрана. 100vw включает полосу прокрутки, и на Windows
   строка вылезала бы за колонку на её ширину. */
.bigname__inner { container-type: inline-size; }
.bigname span {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  /* 4.747 — ширина «Ракеты» по краске в кеглях при этом трекинге, замерена
     в браузере один раз. Строка встаёт в колонку от края до края: от начала
     копирайта до конца «Наверх». При смене трекинга число пересчитать. */
  font-size: calc(100cqi / 4.747);
  /* Заглавные Unbounded стоят ровно посередине строки: высота 0,75 кегля
     при выносах 0,995 и 0,245. Ножки «Д» уходят под строку на 0,15, и при
     line-height 1 до верха «Ракеты» остаётся ещё 0,1 кегля. */
  line-height: 1;
  letter-spacing: -0.055em;
  text-transform: uppercase;
}
/* Центруем краску, а не ширину набора. Трекинг браузер ставит и после
   последней буквы, а у «Р» слева в самом знаке заметный просвет — без
   сдвига обе строки уезжали вправо: «Ракета» на 0,06 кегля (15px
   на десктопе), «Дима» на 0,04. */
.bigname span:first-child { transform: translateX(-0.038em); }
.bigname span:last-child {
  transform: translateX(-0.0605em);
  /* От линии шрифта до черты подвала столько же, сколько от черты до его
     текста. В строке под линией шрифта уже лежит (1 − 0,75) / 2 = 0,125
     кегля — их вычитаем. */
  padding-bottom: calc(clamp(2rem, 4vw, 3rem) - 0.125em);
}

/* --------------------------------------------------------------------------
   Прелоадер (разметку добавляет скрипт, поэтому без JS его просто нет)
   -------------------------------------------------------------------------- */

.preloader {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  background: var(--ink);
  color: var(--paper);
  transition: transform 0.7s var(--ease);
}
.preloader.is-done { transform: translateY(-100%); }
.preloader__name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.5rem, 5vw, 3.5rem);
  letter-spacing: -0.03em;
  text-transform: uppercase;
  overflow: hidden;
}
.preloader__name span {
  display: block;
  animation: preload-rise 0.6s var(--ease) both;
}
@keyframes preload-rise { from { transform: translateY(110%); } }

/* ==========================================================================
   ДВИЖЕНИЕ
   Всё ниже — прогрессивное улучшение. В браузерах без scroll-driven animations
   (сейчас это Firefox) страница остаётся статичной и полностью читаемой,
   поэтому полифилл не подключаем — так советует и гайд по этой технике.
   Проверка на animation-range обязательна: она отсеивает частичную поддержку.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {

    /* --- Параллакс первого экрана.
       Считаем от прокрутки страницы, а не от видимости блока: первый экран
       и так уже виден при загрузке, поэтому диапазоны entry/exit тут не годятся. */
    @keyframes layer-up   { to { transform: translateY(-9vh); } }
    @keyframes layer-up-2 { to { transform: translateY(-3.5vh); } }
    @keyframes layer-up-3 { to { transform: translateY(-15vh); } }
    @keyframes layer-fade { to { opacity: 0.15; } }

    .hero2__name {
      animation: layer-up-2 linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 90vh;
    }
    .hero2__figure {
      animation: layer-up linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 90vh;
    }
    .hero2__chips, .hero2__badge {
      animation: layer-up-3 linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 90vh;
    }

    /* --- Появление блоков.
       Стаггер задаём вручную по nth-child: sibling-index() пока слишком новый. */
    @keyframes rise-in { from { opacity: 0; transform: translateY(28px); } }
    .fx {
      animation: rise-in linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 45%;
    }
    .fx-stagger > *              { animation: rise-in linear both; animation-timeline: view(); }
    .fx-stagger > *:nth-child(1) { animation-range: entry 4%  entry 40%; }
    .fx-stagger > *:nth-child(2) { animation-range: entry 8%  entry 46%; }
    .fx-stagger > *:nth-child(3) { animation-range: entry 12% entry 52%; }
    .fx-stagger > *:nth-child(4) { animation-range: entry 16% entry 58%; }
    .fx-stagger > *:nth-child(5) { animation-range: entry 20% entry 64%; }
    .fx-stagger > *:nth-child(6) { animation-range: entry 24% entry 70%; }
  }
}

/* --------------------------------------------------------------------------
   Адаптив
   -------------------------------------------------------------------------- */

@media (max-width: 1080px) {
  .about2, .dark-figure { grid-template-columns: 1fr; }
  .dark-figure figure { max-width: 22rem; }
  .about2__figure { max-width: 22rem; justify-self: center; }
}

@media (max-width: 900px) {
  .hero2__stage { min-height: clamp(24rem, 58vh, 34rem); }
  .hero2__figure img { height: 44vh; }
  .hero2__below { grid-template-columns: 1fr; align-items: start; }
  .hero2__badge { display: none; } /* места мало — бейдж только на десктопе */
}

@media (max-width: 640px) {
  .hero2__stage { min-height: 22rem; }
  .hero2__name { font-size: clamp(2.6rem, 17vw, 4.5rem); }
  .manifest__text { max-width: none; }

  /* Высота окна на телефоне не постоянна. При прокрутке панели браузера
     сворачиваются, а во встроенных браузерах — Telegram и других — окно
     вместе с ними вытягивается, и vh растёт. Портрет в 44vh увеличивался
     прямо под пальцем: на iPhone шириной 390 в Telegram с 283 до 320px,
     а на телефоне покрупнее ещё и раздвигал сцену, сталкивая текст ниже.
     Ширина при прокрутке не меняется, поэтому здесь размер считается от неё.
     Потолок равен высоте сцены: фигура всегда в неё помещается. Заодно
     портрет и имя, заданное в vw, держат одну пропорцию на любом телефоне. */
  .hero2__figure img { height: min(80vw, 22rem); }
  /* Обложка книги — та же история: clamp(18rem, 52vh, 30rem) дышал вместе
     с окном, 334 → 378px. */
  .dark-figure figure img { height: min(90vw, 24rem); }
}

@media (prefers-reduced-motion: reduce) {
  .band__track,
  .hero2__badge svg { animation: none; }
  .preloader { display: none; }
  /* Взвесь в лучах здесь не упомянута намеренно: её анимация объявлена
     внутри @media (prefers-reduced-motion: no-preference) и при reduce
     не создаётся вовсе. Сам свет не гасим — освещение не движение. */
}

/* Аварийный выключатель: скрипт ставит этот класс, если появление блоков
   не сработало (таймлайн не прицепился). Контент важнее эффекта. */
.fx-off .fx,
.fx-off .fx-stagger > *,
.fx-off .hero2__name,
.fx-off .hero2__figure,
.fx-off .hero2__chips,
.fx-off .hero2__badge {
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
}

/* fx-off значит «анимация появления не прицепилась, покажите контент».
   Свет зала гасить по этому поводу НЕЛЬЗЯ: он чистый CSS, от скролл-таймлайнов
   не зависит, и без него награды окажутся в чёрной пустоте — то есть в момент,
   когда секция должна стать надёжнее, она стала бы хуже.
   Отдельно: класс приходит не только на <html>. Съёмщик tools/shot.js ставит
   его прямо на элементы .fx, а .trophies — как раз такой элемент; правило
   вида .fx-off .hall-rig гасило установку на каждом снимке.
   Останавливаем только то, что действительно является эффектом, — движение. */
.fx-off .dust { animation: none; }

/* --------------------------------------------------------------------------
   Выезд первого экрана при загрузке
   Начальное состояние живёт под классом .intro, который ставит инлайн-скрипт
   в <head>; снимается добавлением .intro-done, когда уходит шторка.
   Анимации висят на ВНУТРЕННИХ элементах: на внешних уже сидит параллакс,
   и вторая анимация на том же свойстве просто вытеснила бы его.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .intro:not(.intro-done) .hero2__name span,
  .intro:not(.intro-done) .hero2__figure img,
  .intro:not(.intro-done) .chip,
  .intro:not(.intro-done) .hero2__badge,
  .intro:not(.intro-done) .hero2__below {
    opacity: 0;
  }

  @keyframes intro-up     { from { opacity: 0; transform: translateY(30px); } }
  @keyframes intro-figure { from { opacity: 0; transform: translateY(42px) scale(1.03); } }
  @keyframes intro-pop    { from { opacity: 0; transform: translateY(14px) scale(0.94); } }

  .intro-done .hero2__name span:nth-child(1) { animation: intro-up 0.85s var(--ease) 0.08s both; }
  .intro-done .hero2__name span:nth-child(2) { animation: intro-up 0.85s var(--ease) 0.16s both; }
  .intro-done .hero2__figure img             { animation: intro-figure 0.95s var(--ease) 0.22s both; }
  .intro-done .hero2__below                  { animation: intro-up 0.8s var(--ease) 0.34s both; }
  .intro-done .chip--1                       { animation: intro-pop 0.6s var(--ease) 0.50s both; }
  .intro-done .chip--2                       { animation: intro-pop 0.6s var(--ease) 0.58s both; }
  .intro-done .chip--3                       { animation: intro-pop 0.6s var(--ease) 0.66s both; }

  /* У бейджа transform занят параллаксом, поэтому только прозрачность
     и через переход, а не анимацию — иначе параллакс будет вытеснен. */
  .hero2__badge { transition: opacity 0.8s var(--ease) 0.6s; }
}

/* --------------------------------------------------------------------------
   Нижний блок первого экрана: как три зоны схлопываются на узких экранах
   -------------------------------------------------------------------------- */

/* Зон теперь две, и до 900px они спокойно стоят рядом. Раньше здесь был
   перенос кнопок на свою строку — он нужен был при трёх колонках, а сейчас
   только зря добавлял высоту на невысоких экранах вроде 1024x768. */
@media (max-width: 900px) {
  .hero2__below { grid-template-columns: 1fr; }
  .hero2__cta {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    gap: 1rem;
    border-left: 0;
    padding-left: 0;
    padding-top: clamp(0.9rem, 2vw, 1.25rem);
    border-top: 1px solid var(--line);
  }
}

@media (max-width: 560px) {
  .hero2__cta { flex-direction: column; align-items: flex-start; }
}


/* --------------------------------------------------------------------------
   Роли — карточки в языке карточки Cleeq
   Классы новые (roles2/role), потому что .roles описан в style.css,
   который принадлежит первой версии и правке не подлежит.
   -------------------------------------------------------------------------- */

.roles2 {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap);
  margin-bottom: var(--gap);
}

.role {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  min-height: 13rem;
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  border: 1px solid var(--line);
  background: var(--paper);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.role:hover { border-color: var(--ink); transform: translateY(-3px); }

.role__num {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  color: var(--accent-d);
}
.role__tag {
  min-height: 2.7em; /* две строки при line-height 1.35 */
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  line-height: 1.35;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Раньше название прижималось к низу карточки — так оно вставало на одну
   горизонталь во всех четырёх. С появлением описания приём перестал
   работать: ко дну прижимается уже пара «название + описание», а описания
   разной длины, и названия снова разъезжаются. Теперь всё выровнено сверху,
   а метке роли задана высота в две строки — длинные должности вроде
   «CBDO · директор по развитию бизнеса» переносятся, короткие добирают
   пустотой, и названия совпадают по горизонтали без прижатия. */
.role__name {
  padding-top: 0.75rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

/* --------------------------------------------------------------------------
   Образование — строки с годами.
   Та же конструкция, что у наград в секции 04: год слева, название и описание
   справа. В прежней вёрстке заголовок был элементом списка и получал рамку
   от соседних чипов — выглядел как пустое поле ввода.
   -------------------------------------------------------------------------- */

/* Подблок внутри секции: отделяется волосяной линейкой сверху.
   .edu2 — первый такой блок на странице, .subsec — все остальные. */
.edu2,
.subsec { padding-top: clamp(1.25rem, 2.5vw, 2rem); border-top: 1px solid var(--line); }
/* В секции брендов подблоки разделяет воздух, а не линейки. Знаки стоят
   прямо на бумаге, и разлиновка вокруг них спорила бы с ними за внимание;
   зелёной моно-метки над каждым подблоком достаточно, чтобы структура
   читалась.
   В «Публичной деятельности» то же самое по другой причине: подблоки там
   разные по форме — список площадок, карточки отзывов, телефон с очередью,
   здания университетов — и не сливаются друг с другом. Линейка ложилась
   сразу под рамки карточек и под корпус телефона и читалась лишней полосой.
   В остальных секциях она остаётся — там подблоки набраны обычным текстом
   и без неё сливались бы. */
#clients .subsec,
#public .subsec,
#public .edu2 { border-top: 0; }

/* Вводный абзац подблока стоит вне .sec__head, где его grid-column не действует */
.subsec > .sec__note { margin-top: 0.75rem; }
.subsec > .orgs { margin-top: clamp(1.25rem, 2.5vw, 2rem); }

.edu2__label {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-d);
  margin-bottom: clamp(0.75rem, 1.5vw, 1.25rem);
}

.edu2__list { border-top: 1px solid var(--line); }
.edu2__list li {
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  align-items: baseline;
  gap: 0.15rem 1rem;
  padding: 0.8rem 0;
  border-bottom: 1px solid var(--line);
}
.edu2__year {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  color: var(--accent-d);
}
.edu2__place { font-weight: 600; font-size: 1rem; line-height: 1.3; }
.edu2__prog {
  grid-column: 2;
  font-size: 0.92rem;
  line-height: 1.4;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   Образование — здания университетов.
   Университеты, где пройдены программы, стоят над строками зданиями:
   London слева, Columbia справа. Где есть мышь, здания серые, наведённое
   оживает в цвете, и поверх него проявляется плашка с программами.
   Где наведения нет, программы идут списком под подписью — иначе их
   было бы не прочитать. Отсюда и порядок ниже: база — это список,
   а серые здания и плашка включаются только там, где можно навести.
   -------------------------------------------------------------------------- */

/* Здания стоят на общей линии земли, но высота у них разная: Columbia —
   башня, London Business School — длинный низкий корпус. Сцена у обоих одной
   высоты, её задаёт Columbia, а LBS прижат к её низу.
   Одинаковой высоты самим зданиям не дать: LBS пришлось бы растянуть почти
   до 770 точек при исходнике в 486, и он поплыл бы. Поэтому колонки 1.5 : 1,
   сцена London — 1.5 × 0.913 = 1.3695, и снимок 486×270 (1.8) занимает
   в ней 76% высоты. Пропорции снимков — из вывода tools/optimize-cutouts.ps1,
   меняются вместе с --ar и --stage-ar ниже.
   Промежуток растёт так, чтобы сцена не стала выше 26rem — ряд тогда шириной
   26 × 0.913 × 2.5 = 59.35rem: на широком экране здания расходятся к краям
   колонки, а не раздуваются во всю ширину. */
.campus {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  column-gap: max(var(--gap), calc(100% - 59.35rem));
  row-gap: clamp(1.75rem, 6vw, 2.5rem);
  align-items: start;
  margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
}
.campus__item { position: relative; }
.campus__item--london   { --ar: 1.8;    --stage-ar: 1.3695; }
.campus__item--columbia { --ar: 0.913;  --stage-ar: 0.913; }

/* Кнопка нужна ради фокуса с клавиатуры, нажимать в ней нечего —
   поэтому и курсор обычный: при наведении всё раскрывается само. */
.campus__btn {
  display: block;
  width: 100%;
  padding: 0;
  text-align: left;
  cursor: default;
}

.campus__stage {
  display: flex;
  align-items: flex-end;
  aspect-ratio: var(--stage-ar);
}

/* Маска висит на обёртке, а не на <img>: тень ::after должна резаться тем же
   силуэтом, что и снимок. Градиенты гасят прямые срезы кадра — приём
   .pin__photo, слои пересекаются через mask-composite. У LBS левый край
   здания и низ правого крыла обрывает рамка кадра; у Columbia прямой срез
   один — нижний. Низ у обоих гасится одинаково: стоят на одной земле. */
.campus__shape {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: var(--ar);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.campus__item--london .campus__shape {
  -webkit-mask-image:
    url(../assets/img/campus-london-mask.png),
    linear-gradient(to bottom, #000 86%, transparent),
    linear-gradient(to right, transparent, #000 9%, #000 93%, transparent);
  mask-image:
    url(../assets/img/campus-london-mask.png),
    linear-gradient(to bottom, #000 86%, transparent),
    linear-gradient(to right, transparent, #000 9%, #000 93%, transparent);
}
.campus__item--columbia .campus__shape {
  -webkit-mask-image:
    url(../assets/img/campus-columbia-mask.png),
    linear-gradient(to bottom, #000 86%, transparent);
  mask-image:
    url(../assets/img/campus-columbia-mask.png),
    linear-gradient(to bottom, #000 86%, transparent);
}

.campus__img {
  display: block;
  width: 100%;
  height: 100%;
}

.campus__name {
  display: block;
  margin-top: 0.9rem;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.3;
}
.campus__meta {
  display: block;
  margin-top: 0.25rem;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-d);
}

/* Программы списком — в наборе строк .edu2__list, что идут ниже. */
.campus__progs {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  margin-top: 0.8rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--line);
}
.campus__progs li {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr);
  align-items: baseline;
  gap: 0 1rem;
}
.campus__year {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  color: var(--accent-d);
}
.campus__prog {
  font-size: 0.92rem;
  line-height: 1.4;
  color: var(--muted);
}

/* В одну колонку общей сцены нет: у LBS она совпадает со снимком, иначе
   над зданием висела бы пустая полоса. Columbia во всю ширину выходил
   вдвое выше LBS и занимал полэкрана — ширину ему ограничиваем, чтобы
   соотношение высот осталось как в ряду. */
@media (max-width: 640px) {
  .campus { grid-template-columns: minmax(0, 1fr); }
  .campus__item--london { --stage-ar: var(--ar); }
  .campus__item--columbia .campus__stage { width: min(100%, 15rem); }
  .campus__progs li { grid-template-columns: 3rem minmax(0, 1fr); }
}

/* Порог тот же, что у зала наград и карты, только условие вывернуто:
   там список включается при (max-width: 1000px), (hover: none),
   (pointer: coarse), здесь плашка — при обратном. */
@media (min-width: 1001px) and (hover: hover) and (pointer: fine) {
  .campus__stage { transition: transform 0.35s var(--ease); }

  .campus__img {
    filter: grayscale(1) contrast(1.04);
    transition: filter 0.5s var(--ease);
  }

  /* Тень поднимается снизу: под плашкой здание темнее, и её край
     не спорит с яркими окнами; верх остаётся светлым и цветным. */
  .campus__shape::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top,
      rgb(14 15 18 / 0.6) 10%, rgb(14 15 18 / 0.25) 55%, rgb(14 15 18 / 0) 85%);
    opacity: 0;
    transition: opacity 0.35s var(--ease);
  }
  /* LBS — кремовый фасад на бледном снимке, ярких окон под плашкой нет.
     Полная тень превращала его низ в грязно-серый и съедала то самое
     появление цвета, ради которого здание было серым. И начинается она
     не от самого низа, а выше полосы, где здание растворяется: затемнённый
     край растворения читался при наведении тёмной рамкой под зданием. */
  .campus__item--london .campus__shape::after {
    background: linear-gradient(to top,
      rgb(14 15 18 / 0) 12%, rgb(14 15 18 / 0.3) 24%,
      rgb(14 15 18 / 0.1) 55%, rgb(14 15 18 / 0) 85%);
  }

  /* Слой ровно по размеру сцены: absolute с left/right 0 берёт ширину пункта,
     aspect-ratio достраивает высоту. Плашка прижата к его низу, но выше
     полосы, где снимок растворяется в бумаге, — иначе светлый текст лёг бы
     на светлое. Процент отступа считается от ширины, поэтому делится
     на пропорцию: у обоих зданий выходит 20% высоты сцены — у низкого LBS
     это чуть выше четверти самого здания, над кустами.
     По горизонтали плашка по центру: у LBS левый край здания растворён,
     и прижатая влево плашка висела бы над пустотой. */
  .campus__over {
    position: absolute;
    inset: 0 0 auto;
    aspect-ratio: var(--stage-ar);
    min-height: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    padding: 0 clamp(0.75rem, 1.5vw, 1.25rem) calc(20% / var(--stage-ar));
    pointer-events: none;
    transition: transform 0.35s var(--ease);
  }
  /* У Columbia корпус занимает весь кадр, и короткая плашка оставляла
     по бокам полосы здания — выглядело как недотянутая. Здесь она идёт
     от стены до стены. */
  .campus__item--columbia .campus__over { align-items: stretch; }

  /* Текст на плашке, а не прямо на затемнённом снимке — по той же причине,
     что у карточек публикаций ниже: чтобы белый держал контраст на ярких
     окнах Columbia, снимок пришлось бы затемнить почти до черноты. Плашка
     матовая, здание под ней размыто, а над ней видно в цвете. */
  .campus__progs {
    gap: 0.75rem;
    max-width: 100%;
    margin: 0;
    padding: clamp(0.9rem, 1.6vw, 1.15rem) clamp(1rem, 1.8vw, 1.3rem);
    border: 1px solid rgb(255 255 255 / 0.07);
    background: rgb(14 15 18 / 0.72);
    -webkit-backdrop-filter: blur(14px) saturate(1.2);
    backdrop-filter: blur(14px) saturate(1.2);
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.4rem);
    transition: opacity 0.25s var(--ease), transform 0.25s var(--ease), visibility 0.25s;
  }
  .campus__progs li { display: flex; flex-direction: column; gap: 0.15rem; }
  /* На графите нужен яркий зелёный — как на карточке Cleeq */
  .campus__year { color: var(--accent); }
  .campus__prog {
    font-weight: 600;
    font-size: 0.95rem;
    line-height: 1.3;
    color: var(--paper);
  }

  /* Открыто при наведении и при фокусе с клавиатуры. Именно :focus-visible,
     а не :focus-within: Chrome ставит фокус на кнопку и при клике мышью,
     и плашка первого здания висела бы открытой, пока курсор идёт ко второму. */
  .campus__item:hover .campus__stage,
  .campus__item:hover .campus__over,
  .campus__item:has(.campus__btn:focus-visible) .campus__stage,
  .campus__item:has(.campus__btn:focus-visible) .campus__over {
    transform: translateY(-0.4rem);
  }
  .campus__item:hover .campus__img,
  .campus__item:has(.campus__btn:focus-visible) .campus__img {
    filter: grayscale(0) contrast(1);
  }
  .campus__item:hover .campus__shape::after,
  .campus__item:has(.campus__btn:focus-visible) .campus__shape::after { opacity: 1; }
  .campus__item:hover .campus__progs,
  .campus__item:has(.campus__btn:focus-visible) .campus__progs {
    opacity: 1;
    visibility: visible;
    transform: none;
  }

  /* Класс ставит скрипт, когда плашку закрыли клавишей Escape, а курсор
     остался на здании: без этого клавиша ничего бы не изменила. Снимается
     при первом движении мыши. */
  .campus.is-hushed .campus__item:not(:has(.campus__btn:focus-visible)):hover .campus__stage,
  .campus.is-hushed .campus__item:not(:has(.campus__btn:focus-visible)):hover .campus__over {
    transform: none;
  }
  .campus.is-hushed .campus__item:not(:has(.campus__btn:focus-visible)):hover .campus__img {
    filter: grayscale(1) contrast(1.04);
  }
  .campus.is-hushed .campus__item:not(:has(.campus__btn:focus-visible)):hover .campus__shape::after {
    opacity: 0;
  }
  .campus.is-hushed .campus__item:not(:has(.campus__btn:focus-visible)):hover .campus__progs {
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.4rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .campus__stage,
  .campus__over,
  .campus__img,
  .campus__shape::after,
  .campus__progs { transition: none; }
}

@media (max-width: 1080px) {
  .roles2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
  .roles2 { grid-template-columns: 1fr; }
  .role { min-height: 0; }
  .edu2__list li { grid-template-columns: 3rem 1fr; }
}

/* --------------------------------------------------------------------------
   Карточка Cleeq: чипы AI-систем
   Берём яркий --accent, а не --accent-d: карточка тёмная, но лежит внутри
   светлой секции, где переменные не переопределены, и тёмно-зелёный
   на графите был бы нечитаем. В style.css метка и ссылка этой карточки
   сделаны через --accent по той же причине.
   -------------------------------------------------------------------------- */

.cleeq__systems {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.35rem;
}
.cleeq__systems li {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.04em;
  line-height: 1;
  color: var(--accent);
  padding: 0.4rem 0.65rem;
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
}

/* --------------------------------------------------------------------------
   Секция 02 — мозаика тем
   Контейнер получает новый класс, потому что .topics описан в style.css,
   который принадлежит первой версии. Карточкам класс .topic оставлен —
   их внутренняя типографика переиспользуется как есть, добавляется только
   рамка: раньше линии рисовал однопиксельный зазор контейнера, а здесь
   зазор настоящий.
   -------------------------------------------------------------------------- */

.topics2 {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: var(--gap);
}
.topics2 .topic {
  border: 1px solid var(--line);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease),
              background-color 0.25s var(--ease);
}
.topics2 .topic:hover { border-color: var(--ink); transform: translateY(-3px); }

/* Крупная карточка занимает два столбца и два ряда, широкие — два столбца.
   Вместе это заполняет три ряда на четыре колонки без единой дыры. */
.topic--lg   { grid-column: span 2; grid-row: span 2; }
.topic--wide { grid-column: span 2; }

.topic--lg .topic__title { font-size: clamp(1.4rem, 2.6vw, 2.1rem); }
.topic--lg .topic__desc  { font-size: 1.05rem; max-width: 40ch; }

/* Графитовые карточки. Секция светлая, переменные в ней не переопределены,
   поэтому акцент берём яркий: тёмно-зелёный на графите нечитаем. */
.topics2 .topic--feature {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.topics2 .topic--feature:hover { border-color: var(--accent); background: var(--ink-2); }
.topic--feature .topic__num { color: var(--accent); }
.topic--feature .topic__desc { color: #B9BFC9; }
.topic--feature .topic__tag { color: var(--muted-d); }
.topic--feature .topic__tag em { color: var(--accent); }

@media (max-width: 1080px) {
  .topics2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .topic--lg { grid-column: span 2; grid-row: span 1; }
}

@media (max-width: 640px) {
  .topics2 { grid-template-columns: 1fr; }
  .topic--lg, .topic--wide { grid-column: span 1; grid-row: span 1; }
}

/* --------------------------------------------------------------------------
   Набор основного текста
   Правый край биографии рвался сильно: строки заканчивались то на тире,
   то на одиночном союзе. Переносы дают браузеру право разбивать длинные
   русские слова (словарь подхватывается по lang="ru" на <html>),
   а text-wrap: pretty убирает висячие слова в конце абзаца.
   Вторая — прогрессивное улучшение: в Firefox свойство игнорируется,
   текст просто верстается как раньше.
   -------------------------------------------------------------------------- */

.about__text p,
.sec__note {
  -webkit-hyphens: auto;
  hyphens: auto;
  hyphenate-limit-chars: 6 3 3; /* не рвать короткие слова и не оставлять огрызки */
  text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   Превью cleeq.ru — снимок сайта в рамке браузера.

   ВНИМАНИЕ: сам блок сейчас закомментирован в onboard2.html. Стили оставлены
   намеренно — превью временно скрыто, а не удалено насовсем.
   Живой iframe сюда не ставим сознательно: он подтянул бы на страницу
   аналитику Cleeq (Яндекс.Метрика, trakkr.ai), добавил ~315 КБ и в колонке
   616 пикселей показал бы мобильную раскладку. Снимок пересобирается
   командой node tools/shot-cleeq.js.
   -------------------------------------------------------------------------- */

.cleeq__frame {
  display: block;
  margin-top: 0.5rem;
  border: 1px solid var(--ink-3);
  background: var(--ink-2);
  overflow: hidden;
  transition: border-color 0.25s var(--ease);
}
.cleeq__frame:hover { border-color: var(--accent); }

.cleeq__bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0.7rem;
  border-bottom: 1px solid var(--ink-3);
}
.cleeq__dots { display: flex; gap: 0.28rem; }
.cleeq__dots i {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: #3A404A;
}
.cleeq__url {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--muted-d);
}

/* Окно повторяет пропорции снимка: при замене картинки достаточно
   поправить aspect-ratio, подгонять высоту руками не нужно. */
.cleeq__window {
  display: block;
  aspect-ratio: 800 / 500;
  overflow: clip;
}
.cleeq__window img {
  display: block;
  width: 100%;
  height: auto;
}


/* Карточка с превью выросла, и левая колонка стала выше портрета.
   Центрируем портрет: симметричный воздух сверху и снизу читается как приём,
   а прижатый к краю с дырой на одной стороне — как брак.
   Липкий портрет тут не годится: у .sec стоит overflow: clip (он держит
   слова-подложки внутри страницы), а такой предок становится скроллпортом
   и position: sticky просто не срабатывает. Проверено. */
.about2 { align-items: stretch; }
.about2__figure { align-self: center; }

/* --------------------------------------------------------------------------
   Секция 01: почему справка набрана крупно.

   Высота портрета нигде не задана — она выводится из ШИРИНЫ его колонки:
   вырезка 760x1340, отношение 0,567; колонка 504 даёт 889 при окне 1440.
   Справка кеглем 17 на меру 616 занимала 481. Под текстом оставался пустой
   прямоугольник 616x407 — ровно то, на что пожаловался заказчик.

   ПОЧЕМУ НЕ ШИРИНОЙ КОЛОНОК. Обе высоты зависят от ширины, но в разные
   стороны: сужаешь портрет — он ниже, но текст при этом шире и тоже ниже.
   Уравнение сходится только при портрете в 178px и строке текста в 942px,
   то есть около 115 знаков в строке при комфортных 60–75. Читать такое
   нельзя, поэтому балансировать шириной нельзя тоже.

   ПОЧЕМУ НЕ УМЕНЬШИТЬ ПОРТРЕТ. Он единственный крупный объект секции,
   и это вырезка с маской: потолок высоты сделал бы его 272px шириной —
   уже, чем те же 352px на телефоне. Поэтому картинка не трогается вовсе,
   а высоту добирает набор.

   ПОЧЕМУ НЕЧЕГО ПЕРЕНЕСТИ. Карточки компаний уехали отсюда в «Компании
   и роли», образование и выступления намеренно унесены в «Публичную
   деятельность» — возвращать их значит отменять саму правку. Заголовок
   секции в колонку тоже не убрать: «ПРЕДПРИНИМАТЕЛЬСКИМ» своим кеглем
   не помещается в 616 и рвётся посреди слова.

   ВСЁ ЗАПЕРТО В min-width: 1081px. Ниже сетка одноколоночная, портрет стоит
   под текстом, пустоты там нет — замеры на 1080, 980, 768 и 390 до и после
   правки совпадают до пикселя. Снаружи медиазапроса правило с id однажды
   переспорило бы мобильный брейкпоинт.
   -------------------------------------------------------------------------- */
@media (min-width: 1081px) {

  /* Кегль берётся существующим токеном, а не новым числом: --fs-lead — это
     ровно тот размер, которым набрана подпись секции прямо над текстом.
     Промежуточной ступени нет: при 18px текст дорастает только до 534,
     и дыра остаётся в 355px — полумера ничего не решает.

     Мера в em, а не в rem и не в ch: --fs-lead сам сжимается по клампу
     на средних экранах, и мера, привязанная к кеглю, сжимается вместе
     с ним — число строк держится, и колонки не разъезжаются на
     промежуточных ширинах. 25em при 21,6px — это 540px, около 50 знаков
     в строке. Правый край при этом не рвётся: у абзацев уже стоят
     hyphens: auto и text-wrap: pretty. */
  #bio .about__text p {
    font-size: var(--fs-lead);
    max-width: 25em;
  }

  /* Отбивка тоже в em: при кегле 21,6 прежние 1,15rem заметно меньше
     интерлиньяжа, и абзацы слиплись бы в сплошное полотно. */
  #bio .about__text > p + p { margin-top: 1.35em; }

  /* Колонка всегда ровно той же высоты, что и портрет (align-items: stretch
     выше), а текст внутри неё короче на 12px. Без центрирования весь этот
     остаток копился бы СТРОГО ПОД ТЕКСТОМ — то есть в той самой форме,
     на которую жаловались, только мельче. Центрирование делит любой остаток
     пополам, и он читается как симметричный воздух. Заодно это страхует
     от правки текста на абзац и от неподгрузившегося Onest: промах
     становится симметричным, а не дырой снизу. */
  #bio .about2__col { justify-content: center; }
}


/* --------------------------------------------------------------------------
   Секция 03 — карта мира с метками.
   Восемь текстовых строк географией не читались, поэтому страны выступлений
   показаны на карте. Сама карта собрана из Natural Earth скриптом
   tools/country-shapes.js --world и лежит инлайном в разметке: внешним файлом
   её не отдать, страница должна открываться и с диска.
   Метки — не части SVG, а обычные кнопки поверх него: ОАЭ и Корея на карте
   мира шириной в несколько пикселей, курсором в них не попасть. Поэтому
   цель — точка постоянного размера, а не сама страна.
   -------------------------------------------------------------------------- */

.geo__map {
  position: relative;
  aspect-ratio: 1000 / 442;
  margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
}

.geo__svg { display: block; width: 100%; height: 100%; }

/* Фоновые страны — почти без контраста: они держат форму мира,
   но не спорят с подсвеченными. */
.geo__land {
  fill: var(--paper-2);
  stroke: var(--line);
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke;
}

.geo__mark {
  fill: color-mix(in srgb, var(--accent-d) 18%, transparent);
  stroke: var(--accent-d);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  transition: fill 0.25s var(--ease);
}

/* --- метки --------------------------------------------------------------- */

/* Метка и карточка позиционируются относительно КАРТЫ, а не друг друга.
   Тогда проценты в расчётах — это ширина и высота карты, и вылет за край
   снимается одним clamp, одинаково на любой ширине экрана. Сама .pin
   коробки не имеет и служит только держателем переменных --x и --y,
   которые наследуются в потомков. */
.pin { position: static; }

.pin__dot {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 0.85rem;
  height: 0.85rem;
  margin: -0.425rem 0 0 -0.425rem;
  padding: 0;
  border: 2px solid var(--paper);
  border-radius: 50%;
  background: var(--accent-d);
  cursor: pointer;
  transition: transform 0.25s var(--ease);
}
/* Отметка страны на статичной карте: точка, название всплывает
   по наведению. Само название лежит АБСОЛЮТНО, а не в потоке: иначе
   невидимая подпись раздувала бы коробку отметки, и её прозрачный
   прямоугольник перехватывал бы курсор далеко в стороне от самой точки.
   Показываем прозрачностью, а не display: при display подпись появлялась
   бы рывком, без перехода.
   Модификатор --left уводит название влево от точки — так Ирак
   не сталкивается с подписью Залива, которая идёт вправо чуть ниже.
   Клавиатуре точки недоступны намеренно: это не ссылки и не кнопки,
   а рисунок. Те же страны названы перечнем под картой, поэтому без
   наведения — и на телефоне, где его нет вовсе — ничего не теряется. */
.geo__pin {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 0.7rem;
  height: 0.7rem;
  margin: -0.35rem 0 0 -0.35rem; /* центр точки ровно на координате */
  border: 2px solid var(--paper);
  border-radius: 50%;
  background: var(--accent-d);
}
/* Прозрачная площадка вокруг точки: она мелкая, и без запаса курсор
   срывался бы с неё при малейшем движении. */
.geo__pin::after {
  content: "";
  position: absolute;
  inset: -0.7rem;
  border-radius: 50%;
}
.geo__pin__name {
  position: absolute;
  left: calc(100% + 0.5rem);
  top: 50%;
  transform: translateY(-50%);
  opacity: 0;
  transition: opacity 0.2s var(--ease);
  pointer-events: none;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--muted);
}
.geo__pin--left .geo__pin__name { left: auto; right: calc(100% + 0.5rem); }
.geo__pin:hover .geo__pin__name { opacity: 1; }

/* Карта занимает всю ширину колонки и наследует пропорцию 1000/442
   от .geo__map — это целый мир. Нижний отступ снимаем: он был нужен
   карте с метками, под которой шёл список площадок, а здесь ниже
   сразу перечень стран со своим отступом. */
.geo__map--static { margin-bottom: 0; }


/* Прозрачная площадка вокруг точки: она мелкая, и без запаса курсор
   срывался бы с неё при малейшем движении. */
.pin__dot::after {
  content: "";
  position: absolute;
  inset: -0.75rem;
  border-radius: 50%;
}
.pin:hover .pin__dot,
.pin:focus-within .pin__dot { transform: scale(1.35); }

/* --- карточка ------------------------------------------------------------ */

.pin__card {
  --card-w: 17rem;
  position: absolute;
  z-index: 2;
  width: var(--card-w);
  /* Карточка идёт под меткой и центрируется по ней, но упирается в края
     карты и дальше не двигается. Проценты считаются от ширины карты,
     rem — от шрифта; clamp их смешивает, поэтому расчёт в скрипте не нужен. */
  left: clamp(0px, calc(var(--x) - var(--card-w) / 2), calc(100% - var(--card-w)));
  top: calc(var(--y) + 1.35rem);
  padding: 0.9rem 1rem 1rem;
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--line);
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.35rem);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
  pointer-events: none;
}
.pin:hover .pin__card,
.pin:focus-within .pin__card {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
}

/* Класс ставит скрипт, когда карточку закрыли клавишей, а курсор так
   и остался над меткой: без этого Escape ничего бы не изменил.
   Снимается при первом же движении мыши. */
.geo__map.is-hushed .pin:not(:focus-within):hover .pin__card {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Метки в нижней половине карты раскрывают карточку вверх — иначе она
   свисала бы под карту. Сторона задана в разметке: положение всех шести
   меток известно заранее, вычислять нечего. */
.pin--above .pin__card {
  top: auto;
  bottom: calc(100% - var(--y) + 1.35rem);
}

/* Раскрытая карточка поднимается над соседними: у метки коробки нет,
   поэтому z-index живёт на самой карточке. */
.pin:hover .pin__card,
.pin:focus-within .pin__card { z-index: 4; }

.pin__photo {
  display: block;
  /* Карточка не должна быть выше карты: на узком десктопе карта всего
     около 370 пикселей, и высокий кадр выталкивал карточку за её край. */
  height: clamp(5rem, 9vw, 7rem);
  width: auto;
  max-width: 100%;
  margin: 0 auto 0.7rem;
  object-fit: contain;
  /* Тот же приём, что и в рядах до переделки: маска силуэта плюс градиент,
     гасящий прямой нижний срез кадра. */
  --fade-bottom: linear-gradient(to bottom, #000 82%, transparent 100%);
  -webkit-mask-size: contain, 100% 100%;
  mask-size: contain, 100% 100%;
  -webkit-mask-position: center, center;
  mask-position: center, center;
  -webkit-mask-repeat: no-repeat, no-repeat;
  mask-repeat: no-repeat, no-repeat;
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

.pin__geo {
  margin: 0 0 0.5rem;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-d);
}
.pin__venues { display: flex; flex-direction: column; gap: 0.6rem; margin: 0; }
.pin__venues li { display: flex; flex-direction: column; gap: 0.1rem; }
.pin__venues b { font-weight: 600; font-size: 0.95rem; line-height: 1.25; }
.pin__venues span {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  line-height: 1.35;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Пункт без страны: на карту его не поставить, но и потерять нельзя. */
.geo__note {
  max-width: 46ch;
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  line-height: 1.5;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Список площадок живёт в разметке всегда: на широком экране секция —
   это карта, а на узком наведения нет, и читать нужно текстом. */
.geo__list { display: none; border-top: 1px solid var(--line); }

.cutout--stage-cut-1 {
  -webkit-mask-image: url(../assets/img/stage-cut-1-mask.png), var(--fade-bottom);
  mask-image: url(../assets/img/stage-cut-1-mask.png), var(--fade-bottom);
}
.cutout--stage-cut-2 {
  -webkit-mask-image: url(../assets/img/stage-cut-2-mask.png), var(--fade-bottom);
  mask-image: url(../assets/img/stage-cut-2-mask.png), var(--fade-bottom);
}
.cutout--stage-cut-3 {
  -webkit-mask-image: url(../assets/img/stage-cut-3-mask.png), var(--fade-bottom);
  mask-image: url(../assets/img/stage-cut-3-mask.png), var(--fade-bottom);
}
.cutout--stage-cut-4 {
  -webkit-mask-image: url(../assets/img/stage-cut-4-mask.png), var(--fade-bottom);
  mask-image: url(../assets/img/stage-cut-4-mask.png), var(--fade-bottom);
}
.cutout--stage-cut-5 {
  -webkit-mask-image: url(../assets/img/stage-cut-5-mask.png), var(--fade-bottom);
  mask-image: url(../assets/img/stage-cut-5-mask.png), var(--fade-bottom);
}

/* Порог выше обычных 900: на более узком экране карта становится настолько
   низкой, что карточка с фотографией в неё уже не помещается и свисает
   за край. Там честнее показать список. */
@media (max-width: 1000px), (hover: none), (pointer: coarse) {
  /* Наведения нет — карта становится картинкой, а содержание переезжает
     в список под ней. Метки остаются как отметки мест, но не реагируют. */
  .pin__dot { pointer-events: none; }
  .pin__card { display: none; }
  .geo__list { display: block; margin-top: clamp(1.25rem, 4vw, 2rem); }
  .geo__note { margin-bottom: 0.5rem; }
}

/* --------------------------------------------------------------------------
   Секция 04 — трофейный зал.
   Награды стоят предметами на подсвеченных пьедесталах: свет падает сверху
   конусом, под предметом лежит отражение в глянце плиты, по краям ряда
   темнота. Настоящего трёхмерного движка тут быть не может — библиотеки
   только с CDN, а страница обязана открываться с диска, — поэтому объём
   собран из перспективы, теней и градиентов.
   Предметы: статуэтки Stevie и акриловый трофей, каждый парой
   «цвет + маска», как вырезанные портреты.
   -------------------------------------------------------------------------- */

.trophies {
  position: relative;
  /* Отступ снизу держал полосу подсказки. Подсказка убрана с экрана,
     поэтому он ужат до обычного зазора перед следующим блоком: иначе
     под залом осталась бы пустая полоса в семь ростов строки. */
  padding-bottom: clamp(1.5rem, 3vw, 2.5rem);

  /* Изоляция держит слои света в своём контексте: отрицательный слой
     установки уходит за предметы, но не проваливается под фон секции.
     А вот непрозрачного фона здесь быть НЕ должно, хотя он и напрашивается
     (он нужен был режиму наложения screen). Подсветка задней стены идёт
     по всей ширине секции, а блок уже неё: закрашенный прямоугольник
     перекрывал стену внутри себя, и по краям блока проступала отчётливая
     рамка — замер показывал ступеньку ровно на границе .wrap. */
  isolation: isolate;
}


/* --------------------------------------------------------------------------
   Зал наград: комната, приборы, взвесь

   В зале стоят ДВА прибора, и они не выдуманы — они уже нарисованы внутри
   самих наград. Дуги на линзе расставлены вручную, и по ним читается
   источник: белые дуги op .92 и .42 дают сектор 10:04–11:35, то есть ключ
   с 10:35, свет идёт вниз-вправо под 43° от вертикали. Тёплые дуги
   #FFD9A8 и #FFF3E0 дают 3:24–4:04, то есть чуть НИЖЕ горизонтали справа —
   это боковая штанга на уровне пояса предметов, светит влево и вверх
   под 17,5°. Векторы заливок тел (14,6° у обелиска, 21° у паруса) — это
   диагонали габаритов фигур, а не свет, и ориентироваться на них нельзя.

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

   Отсюда же неочевидное следствие, на котором держится вся картинка:
   ПОЛ ХОЛОДНЫЙ, ЗАДНЯЯ СТЕНА ТЁПЛАЯ. Ключ падает вниз-вправо и разбивается
   о пол, подсветка идёт вверх-влево и разбивается о стену — поэтому тёплое
   пятно на стене оказывается СЛЕВА, хотя тёплый прибор стоит справа.
   -------------------------------------------------------------------------- */

#awards {
  /* Весь свет описан этими переменными и больше ничем. --lit — общий
     выключатель: им гасят печать и .fx-off, а --lit: 6 показывает
     конструкцию под лупой при проверке — при шестикратном усилении любой
     затесавшийся тёмный стоп вылезает чёрной фигурой, не заметить нельзя. */
  --lit: 1;
}

/* Задняя стена зала.

   Живёт внутри зала, а не на всю секцию, и это не мелочь. Виньетка ниже
   красит НЕПРОЗРАЧНЫМ цветом секции по краям зала: пока стена светила шире
   зала, виньетка перекрывала её внутри, а снаружи стена оставалась видна —
   и по границе блока шла отчётливая рамка. Замер ловил ступеньку ровно
   на краю .wrap, а без виньетки она исчезала. Свет и затемнение обязаны
   жить в одном боксе: тогда и внутри, и снаружи края одинаковый фон. */
.trophies__hall::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(42% 44% at 44% 40%,
      rgb(255 222 180 / calc(0.042 * var(--lit))) 0%,
      rgb(255 214 168 / calc(0.016 * var(--lit))) 52%,
      rgb(255 214 168 / 0) 82%),
    radial-gradient(30% 33% at 34% 51%,
      rgb(214 234 255 / calc(0.026 * var(--lit))) 0%,
      rgb(214 234 255 / 0) 76%);
}

/* Заголовок держим в положительном слое: если конус когда-нибудь заведут
   выше, он не должен на него залезть. */
#awards .sec__head { position: relative; z-index: 1; }

/* Установка: комната и приборы. Лежит под полкой (та на z-index: 1),
   поэтому ни один световой слой не может лечь на предмет, подиум или
   подпись. Это и снимает главный риск: правый тёплый прибор физически
   не попадёт на кристалл, где правая грань — нарисованный терминатор.
   «Прожектор светит на награду» читается иначе: ореол стоит вплотную
   ЗА кромкой силуэта, а яркий воздух за тёмным краем и есть контровой. */
.hall-rig {
  position: absolute;
  /* Отрицательный слой, а не нулевой с поднятой полкой. Разница важная:
     поднимать полку через position + z-index значило сделать её стековым
     контекстом, и описание награды запиралось внутри неё — виньетка зала
     (z-index: 3) начинала рисоваться поверх текста и подъедала первое слово.
     Здесь же .trophies__hall контекста не создаёт, поэтому слой уходит
     в контекст витрины: за предметы, но поверх её фона. */
  z-index: -1;
  inset: 0;
  pointer-events: none;
}

/* Пол. Линии посчитаны по вёрстке: подошва первого ряда на 42% высоты зала,
   второго на 91%. Пятна — плоские эллипсы: свет падает под 43°, его след
   на полу вытянут. Оба ХОЛОДНЫЕ, потому что пола касается только ключ —
   ровно то, что нарисовано внутри фигур холодным отскоком снизу.
   Пятна стоят на 33% и 67% по ширине: это посчитанные точки, куда ключ
   действительно приходит на каждом ряду, а не «под первой наградой». */
.hall-rig__floor {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(30% 5.4% at 33% 42%,
      rgb(216 236 255 / calc(0.115 * var(--lit))) 0%,
      rgb(200 226 255 / calc(0.050 * var(--lit))) 44%,
      rgb(200 226 255 / 0) 78%),
    radial-gradient(36% 4.6% at 67% 91%,
      rgb(210 232 255 / calc(0.058 * var(--lit))) 0%,
      rgb(210 232 255 / 0) 76%);
  /* Затемнения тут намеренно НЕТ. Фон секции и так почти чёрный (#0E0F12,
     яркость 15 из 255), и чёрная краска поверх него даёт не глубину,
     а видимую рамку по границе зала: замер показал 7 внутри против 15
     снаружи. Пол уходит в темноту потому, что туда не доходит свет,
     а не потому, что там нарисовано чёрное. */
}


/* ПРАВИЛО ДЛЯ ВСЕХ СВЕТОВЫХ СЛОЁВ: пятно обязано гаснуть ВНУТРИ своего
   бокса. Радиальный градиент доходит до нуля на своей последней остановке;
   если она приходится дальше границы элемента, остаток срезает сам бокс —
   и получается прямая кромка, ровно та, которой быть не должно. Проверяется
   арифметикой, а не глазом: центр ± (последняя остановка × радиус) должен
   уложиться в 0..100% по обеим осям. У конусов проще: их собственные края
   вынесены за пределы зала, а зал обрезает по себе.

   Конус луча.
   Записанный урок гласит: у луча с чёткими краями получается тёмная
   геометрическая фигура за предметом, а не свет. Раз лучи теперь нужны
   видимыми, защита сделана конструктивной, а не подбором значений:

   1. В слое нет ни одного тёмного пикселя. Все стопы — один и тот же цвет
      с убывающей альфой, крайний — он же с нулевой. Ключевое слово
      transparent ЗАПРЕЩЕНО: это rgba(0,0,0,0), то есть ЧЁРНЫЙ с нулевой
      альфой, и переход к нему даёт серую кайму. Похоже, именно она
      и была тем самым тёмным контуром в прошлой попытке.
   2. mix-blend-mode: screen. screen(a,b) = a + b − a·b ≥ b, то есть слой
      математически не способен уменьшить ни один канал.
   3. Ни одной кромки с ненулевой альфой: угловые крайние стопы прозрачны
      (боков нет), спад по дальности задаёт отдельная радиальная маска
      (торца нет).

   conic-gradient взят потому, что он считает НАСТОЯЩИЕ углы: они
   не искажаются пропорцией бокса, и 126°→148° на экране это и есть
   126°→148°. Корпуса приборов не рисуем: луч проявляется из темноты,
   и мозг достраивает прибор за кадром сам. */
.hall-rig__beam {
  position: absolute;
  pointer-events: none;
  /* mix-blend-mode здесь намеренно нет. Он требовал непрозрачного задника
     у витрины, а тот рисовал видимую рамку по краям блока. Свет обходится
     без него: в слое нет ни одного тёмного пикселя, все стопы — один цвет
     с убывающей альфой, поэтому слой и так способен только прибавлять.
     Разница между screen и обычным наложением при альфе 0,02–0,09
     на почти чёрном фоне — одна-две единицы из 255. */
}

/* Ключевой: источник за кадром слева сверху, ось 43° от вертикали
   вниз-вправо (137° по циферблату CSS), раскрытие 22°. Прочёсывает зал
   по диагонали и садится на пол у колонки 2 в ближнем ряду и у колонки 3
   в дальнем — туда же, где стоят пятна на полу. */
.hall-rig__beam--key {
  /* Бокс намеренно больше зала со всех четырёх сторон: собственные края
     элемента вынесены наружу, где их обрезает overflow зала, поэтому
     показаться они не могут. Вершина конуса при этом осталась на месте —
     проценты пересчитаны под новый бокс. */
  left: -30%;
  top: -45%;
  width: 175%;
  height: 230%;
  background: conic-gradient(from 126deg at 15.2% 14.1%,
    rgb(226 240 255 / 0)                        0deg,
    rgb(226 240 255 / calc(0.018 * var(--lit))) 4deg,
    rgb(232 244 255 / calc(0.072 * var(--lit))) 10deg,
    rgb(255 255 255 / calc(0.086 * var(--lit))) 15deg,
    rgb(226 240 255 / calc(0.028 * var(--lit))) 19deg,
    rgb(226 240 255 / 0)                        22deg);
  -webkit-mask-image: radial-gradient(62% 75% at 15.2% 14.1%,
    #000 0%, rgb(0 0 0 / 0.72) 30%, rgb(0 0 0 / 0.30) 62%, rgb(0 0 0 / 0) 96%);
          mask-image: radial-gradient(62% 75% at 15.2% 14.1%,
    #000 0%, rgb(0 0 0 / 0.72) 30%, rgb(0 0 0 / 0.30) 62%, rgb(0 0 0 / 0) 96%);
}

/* Подсветка: боковая штанга справа на уровне пояса предметов, светит
   влево и ВВЕРХ под 17,5° (287,5° по циферблату), раскрытие 14°. Она
   слабее, теплее и уже ключа — зеркальности нет нигде, и это главный
   признак настоящего зала, а не двух украшений по краям. Идя вверх,
   она до пола не доходит: поэтому отскок снизу у всех фигур и нарисован
   холодным. */
.hall-rig__beam--fill {
  /* Здесь кромки были самые заметные: верх бокса стоял ровно на верхнем краю
     зала, низ — на 56% его высоты, то есть обе границы проходили прямо
     по картинке. Теперь бокс тот же, что у ключа, и все четыре края снаружи. */
  left: -30%;
  top: -45%;
  width: 175%;
  height: 230%;
  background: conic-gradient(from 280.5deg at 77.7% 42.2%,
    rgb(255 226 186 / 0)                        0deg,
    rgb(255 226 186 / calc(0.014 * var(--lit))) 3deg,
    rgb(255 216 168 / calc(0.046 * var(--lit))) 7deg,
    rgb(255 232 198 / calc(0.052 * var(--lit))) 10deg,
    rgb(255 220 178 / calc(0.016 * var(--lit))) 12deg,
    rgb(255 220 178 / 0)                        14deg);
  -webkit-mask-image: radial-gradient(80% 45% at 77.7% 42.2%,
    #000 0%, rgb(0 0 0 / 0.62) 30%, rgb(0 0 0 / 0.18) 64%, rgb(0 0 0 / 0) 92%);
          mask-image: radial-gradient(80% 45% at 77.7% 42.2%,
    #000 0%, rgb(0 0 0 / 0.62) 30%, rgb(0 0 0 / 0.18) 64%, rgb(0 0 0 / 0) 92%);
}

/* Взвесь в луче. Ни картинки, ни шум-фильтра: страница обязана
   открываться с диска, лишних запросов быть не должно, а data-URI
   с feTurbulence весит больше всей секции. Повторяющиеся точечные
   градиенты дешевле любого набора элементов — браузер растрирует
   маленькую плитку ОДИН раз и размножает её.

   Взвесь лежит ВНУТРИ конуса, поэтому маска конуса применяется ко всему
   поддереву и частицы гаснут к его краям сами: не эффектом, а как
   следствие того, что пыль светится только там, где есть свет.

   Числа не произвольные:
   — плитки 211 / 137 / 89 попарно взаимно просты, поэтому общая решётка
     возникает только на их НОК, далеко за пределами конуса, и муара нет;
   — смещение за цикл — целое число плиток по обеим осям, иначе на стыке
     цикла поле дёргается; отсюда и углы дрейфа;
   — дрейф ВВЕРХ: у горячего прибора воздух поднимается. Дрейф вниз
     мгновенно превращает зал в снегопад, это самая частая ошибка;
   — слои идут в РАЗНЫЕ стороны с разной скоростью: только это убивает
     ощущение ползущего обойного узора;
   — 5–14 px/с. Всё быстрее 20 px/с читается как снег. */
.dust {
  position: absolute;
  inset: -24%;
  background-repeat: repeat;
  pointer-events: none;
  opacity: calc(var(--a) * var(--lit));
}

.dust--a {
  --a: 0.55;
  background-size: 211px 211px;
  background-image:
    radial-gradient(circle at 34px 51px,   rgb(255 255 255 / 0.66) 0, rgb(255 255 255 / 0.50) 0.8px, rgb(255 255 255 / 0) 2.3px),
    radial-gradient(circle at 158px 23px,  rgb(232 244 255 / 0.50) 0, rgb(232 244 255 / 0.36) 0.7px, rgb(232 244 255 / 0) 2px),
    radial-gradient(circle at 96px 137px,  rgb(255 255 255 / 0.58) 0, rgb(255 255 255 / 0.40) 0.6px, rgb(255 255 255 / 0) 1.9px),
    radial-gradient(circle at 189px 108px, rgb(214 236 255 / 0.40) 0, rgb(214 236 255 / 0) 1.6px),
    radial-gradient(circle at 61px 186px,  rgb(255 255 255 / 0.34) 0, rgb(255 255 255 / 0) 1.4px);
}
.dust--b {
  --a: 0.42;
  background-size: 137px 137px;
  background-image:
    radial-gradient(circle at 22px 96px,  rgb(255 255 255 / 0.44) 0, rgb(255 255 255 / 0) 1.6px),
    radial-gradient(circle at 104px 31px, rgb(232 244 255 / 0.38) 0, rgb(232 244 255 / 0) 1.4px),
    radial-gradient(circle at 71px 118px, rgb(255 255 255 / 0.34) 0, rgb(255 255 255 / 0) 1.3px),
    radial-gradient(circle at 128px 74px, rgb(214 236 255 / 0.30) 0, rgb(214 236 255 / 0) 1.2px),
    radial-gradient(circle at 47px 12px,  rgb(255 255 255 / 0.28) 0, rgb(255 255 255 / 0) 1.1px);
}
.dust--c {
  --a: 0.30;
  background-size: 89px 89px;
  background-image:
    radial-gradient(circle at 14px 63px, rgb(255 255 255 / 0.30) 0, rgb(255 255 255 / 0) 1.2px),
    radial-gradient(circle at 67px 21px, rgb(232 244 255 / 0.26) 0, rgb(232 244 255 / 0) 1.1px),
    radial-gradient(circle at 43px 79px, rgb(255 255 255 / 0.22) 0, rgb(255 255 255 / 0) 1px),
    radial-gradient(circle at 82px 47px, rgb(214 236 255 / 0.20) 0, rgb(214 236 255 / 0) 1px),
    radial-gradient(circle at 30px 34px, rgb(255 255 255 / 0.18) 0, rgb(255 255 255 / 0) 1px);
}

/* Взвесь тёплого прибора: он слабее и проявляет меньше пыли. */
.hall-rig__beam--fill .dust {
  background-image:
    radial-gradient(circle at 34px 51px,  rgb(255 230 200 / 0.56) 0, rgb(255 230 200 / 0.4) 0.8px, rgb(255 230 200 / 0) 2.2px),
    radial-gradient(circle at 158px 23px, rgb(255 222 186 / 0.42) 0, rgb(255 222 186 / 0) 1.9px),
    radial-gradient(circle at 96px 137px, rgb(255 236 212 / 0.46) 0, rgb(255 236 212 / 0) 1.8px),
    radial-gradient(circle at 61px 186px, rgb(255 222 186 / 0.28) 0, rgb(255 222 186 / 0) 1.4px);
}
.hall-rig__beam--fill .dust--a { --a: 0.30; }
.hall-rig__beam--fill .dust--b { --a: 0.22; }

/* Пока секция за кадром, частицы стоят: ставит и снимает класс скрипт. */
.trophies.is-idle .dust { animation-play-state: paused; }

@keyframes hall-dust-a { to { transform: translate3d(-211px, -844px, 0); } }
@keyframes hall-dust-b { to { transform: translate3d(137px, -685px, 0); } }
@keyframes hall-dust-c { to { transform: translate3d(89px, -534px, 0); } }

@media (prefers-reduced-motion: no-preference) {
  /* Гейт на no-preference, а не на reduce: если медиазапрос не поддержан,
     анимации не будет вовсе — безопасный отказ. Так же написан блок
     появления первого экрана выше. */
  .dust--a { animation: hall-dust-a 61s linear infinite; will-change: transform; }
  .dust--b { animation: hall-dust-b 78s linear infinite; will-change: transform; }
  .dust--c { animation: hall-dust-c 112s linear infinite; will-change: transform; }
  .hall-rig__beam--fill .dust--a { animation-duration: 95s; }
  .hall-rig__beam--fill .dust--b { animation-duration: 130s; }
}

/* Воздух ПЕРЕД рядом: ответ на награду, которая стоит В луче. Предмет,
   за которым виден луч, — это предмет перед картинкой луча; предмет,
   у которого луч виден и спереди, стоит внутри него. Спереди берём только
   тёплый: ключ идёт сверху-сзади и перед рядом его почти нет, а подсветка
   идёт почти горизонтально и пересекает линию взгляда.
   Потолок 0,03 сознательный: .trophy__meta это --accent на --ink, и вуаль
   в 3% поднимает обе стороны, меняя отношение в третьем знаке. */
/* Перо по краям бокса — страховка на случай, если геометрия пятна когда-нибудь
   разъедется при правке: даже вылезший за край градиент будет погашен плавно,
   а не срезан прямой линией. Стоит на слоях без собственной маски. */
.trophy__stage::before,
.hall-rig__floor,
.trophies__hall::before {
  -webkit-mask-image:
    linear-gradient(to right, rgb(0 0 0 / 0) 0%, #000 7%, #000 93%, rgb(0 0 0 / 0) 100%),
    linear-gradient(to bottom, rgb(0 0 0 / 0) 0%, #000 7%, #000 93%, rgb(0 0 0 / 0) 100%);
          mask-image:
    linear-gradient(to right, rgb(0 0 0 / 0) 0%, #000 7%, #000 93%, rgb(0 0 0 / 0) 100%),
    linear-gradient(to bottom, rgb(0 0 0 / 0) 0%, #000 7%, #000 93%, rgb(0 0 0 / 0) 100%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

.hall-rig__haze {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  top: 18%;
  height: 40%;
  pointer-events: none;
  background: linear-gradient(283deg,
    rgb(255 226 186 / calc(0.030 * var(--lit))) 0%,
    rgb(255 220 178 / calc(0.014 * var(--lit))) 45%,
    rgb(255 220 178 / 0) 74%);
  -webkit-mask-image:
    linear-gradient(to bottom, rgb(0 0 0 / 0), #000 30%, #000 62%, rgb(0 0 0 / 0)),
    linear-gradient(to right, rgb(0 0 0 / 0) 0%, #000 10%, #000 90%, rgb(0 0 0 / 0) 100%);
          mask-image:
    linear-gradient(to bottom, rgb(0 0 0 / 0), #000 30%, #000 62%, rgb(0 0 0 / 0)),
    linear-gradient(to right, rgb(0 0 0 / 0) 0%, #000 10%, #000 90%, rgb(0 0 0 / 0) 100%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

/* Без масок спад по дальности задать нечем, и у конуса появится тот самый
   прямой торец. Тогда лучей нет вовсе — комната со стеной и полом остаётся. */
@supports not (mask-image: radial-gradient(#000, rgb(0 0 0 / 0))) {
  .hall-rig__beam { display: none; }
}

/* Зал: пол уходит в темноту, по краям ряда сумрак. */
.trophies__hall {
  position: relative;
  padding: clamp(1.5rem, 3vw, 2.5rem) 0 0;
  /* Комната кончается здесь: конусы заведомо больше зала (иначе у них
     были бы видны основания), и без обрезки ключевой луч уезжал бы
     на заголовок секции, а тёплый — за правый край ряда.
     clip, а не hidden: hidden сделал бы зал прокручиваемым контейнером.
     Описание позиционировано bottom: 0 относительно зала и растёт вверх,
     поэтому обрезано быть не может. */
  overflow: clip;
}
.trophies__hall::after {
  content: "";
  position: absolute;
  z-index: 3;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(90deg,
    var(--ink) 0%, transparent 9%, transparent 91%, var(--ink) 100%);
}

.trophies__shelf {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(0.5rem, 1.2vw, 1rem) clamp(0.25rem, 0.8vw, 0.75rem);
  margin: 0;
}

/* Пункт коробки не имеет: описание позиционируется относительно всей
   витрины, а не своей ячейки, — тогда оно всегда выходит в одну полосу. */
.trophy { position: static; display: flex; }

.trophy__btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  text-align: center;
  cursor: pointer;
}

/* --- сцена: свет, предмет, отражение, пьедестал -------------------------- */

.trophy__stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  /* Высота одинаковая у всех, а предметы разной величины стоят
     на общей линии — как на настоящей полке. */
  height: clamp(13rem, 24vw, 23rem);
}

/* --- свет на предметах: две ступени ------------------------------------
   Правило, по которому зальные конусы и собственный свет награды
   не конфликтуют: это ОДИН прибор в двух ролях. Конус — луч в воздухе,
   собственная пара — след того же луча на конкретном предмете. Поэтому
   её яркость не выбирается, а ВЫВОДИТСЯ из места в зале: --key говорит,
   сколько света сюда доходит, --amb — сколько его съедает воздух
   до дальнего ряда. Складывать нечего.

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

/* Собственного пятна света над наградой здесь больше нет.

   Оно задумывалось как след зального луча на конкретном предмете, но
   висело В ВОЗДУХЕ над каждой статуэткой, ничем не перекрытое, и восемь
   одинаковых кружков, посаженных по сетке, читались как ряд фонариков,
   а не как освещение. Тугое пятно вместо широкого залива проблему только
   смягчило, но не убрало.

   Свет на предмет теперь приходит только оттуда, откуда ему и положено:
   от двух зальных конусов. А что награда стоит именно в этом месте зала,
   показывает лужа на площадке подиума — она лежит НА поверхности,
   частично закрыта самим предметом и потому фонариком не читается.
   Она же отвечает на наведение. */

/* Тёплого ореола у каждой награды здесь больше нет, и это осознанно.
   Он должен был класть тёплый воздух ЗА предметом, чтобы глаз приписал
   нарисованный на фигуре контровой ободок настоящему источнику. На деле
   его центр приходился на границу ячейки, то есть в ПУСТОЙ промежуток
   между наградами, где его ничто не перекрывало, — и вместо подсветки
   за предметом получался отдельный круглый фонарик, висящий в воздухе.
   Замер находил ровно три таких пятна: x=290, 590, 890 при y=250.
   Источник тёплого света в зале остаётся: это правый конус, а ответ
   материала на него нарисован внутри самих фигур — тёплые кромки
   у обелиска, паруса, кристалла и рефлекс у линзы. */

/* Лужа света на верхней площадке подиума. Свет боковой, поэтому
   освещённая часть площадки лежит СЛЕВА от предмета — справа ляжет тень. */
.trophy__stage::before {
  content: "";
  position: absolute;
  left: 40%;
  bottom: 13.5%;
  width: 34%;
  height: 6.5%;
  transform: translateX(-50%);
  background: radial-gradient(50% 50% at 50% 50%,
    rgb(226 242 255 / 0.20) 0%,
    rgb(210 232 255 / 0.07) 48%,
    rgb(210 232 255 / 0) 80%);
  opacity: calc((var(--key) * var(--amb) * 0.9 + var(--lift) * var(--gain) * 1.4) * var(--lit));
  pointer-events: none;
  transition: opacity 0.35s var(--ease);
}

/* Раскладка по местам. Числа не «по колонкам»: ключ идёт по диагонали
   и садится на пол у колонки 2 в ближнем ряду и у колонки 3 в дальнем,
   а подсветка идёт почти горизонтально и режет ТОЛЬКО первый ряд,
   поднимаясь влево. Поэтому таблица несимметричная и немонотонная —
   она повторяет траектории двух лучей, а не сетку вёрстки.
   --key нигде не ниже 0,30: ключ нарисован внутри всех фигур, и обнулить
   его снаружи значило бы поспорить с рисунком. */
.trophy { --key: .5; --lift: 0; --gain: .34; }
.trophy:nth-child(1) { --key: 1.00; --gain: .26; }  /* вход луча */
.trophy:nth-child(2) { --key:  .92; --gain: .26; }  /* пятно ключа на полу */
.trophy:nth-child(3) { --key:  .60; --gain: .36; }  /* пересечение конусов */
.trophy:nth-child(4) { --key:  .34; --gain: .44; }  /* у тёплого прибора */
.trophy:nth-child(5) { --key:  .30; --gain: .50; }  /* дальний тёмный угол */
.trophy:nth-child(6) { --key:  .42; --gain: .46; }
.trophy:nth-child(7) { --key:  .60; --gain: .34; }  /* дальний бросок ключа */
.trophy:nth-child(8) { --key:  .44; --gain: .38; }

/* Дальний ряд: между зрителем и предметом больше воздуха, контраст падает.
   Уменьшение идёт от нижней кромки, поэтому общая линия полки не рвётся. */
.trophy__stage { --amb: 1; }
.trophy:nth-child(n+5) .trophy__stage {
  --amb: .70;
  transform-origin: bottom center;
  transform: scale(.966);
}

.trophy__object {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  flex: 1;
  min-height: 0;
  width: 100%;
}

/* Тень под основанием: она и сажает предмет на площадку.
   Отражения тут нет намеренно — на матовом камне оно читалось как вторая
   маленькая статуэтка внутри ступеней. */
/* Тень от ключа. Пола касается ТОЛЬКО ключ: тёплый прибор идёт вверх,
   и его тень уходит на заднюю стену, а не под ноги. Поэтому тень одна,
   а не две скрещенные — «два прибора, две тени» здесь физически неверно.
   Уезжает не столько центр, сколько асимметрия спада: плотнее у ножки,
   тянется вправо — как уже запечённые тени подиума (их эллипсы стоят
   на cx 202 и 204 при оси 200). */
.trophy__object::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.1rem;
  width: 44%;
  height: 0.55rem;
  transform: translateX(-38%);
  background: radial-gradient(52% 50% at 32% 50%,
    rgb(0 0 0 / 0.5) 0%, rgb(0 0 0 / 0.22) 42%, rgb(0 0 0 / 0) 76%);
  opacity: calc(0.55 + var(--key) * 0.45);
  pointer-events: none;
}

/* Контакт: самая тёмная точка любой тени там, где предмет касается опоры.
   Без неё боковой свет заставляет предмет висеть. */
.trophy__object::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.05rem;
  width: 15%;
  height: 0.22rem;
  transform: translateX(-46%);
  background: radial-gradient(50% 50% at 50% 50%,
    rgb(0 0 0 / 0.62) 0%, rgb(0 0 0 / 0) 72%);
  pointer-events: none;
}

.trophy__item {
  display: block;
  width: auto;
  max-width: 82%;
  max-height: 100%;
  object-fit: contain;
  /* Смещение по x взято из тех же 43°, что и у зального ключа: без него
     ореол падал строго вниз и спорил с боковым светом. Количество
     drop-shadow не растёт — это самая дорогая вещь в секции. */
  filter: drop-shadow(0.5rem 0.95rem 1.4rem rgb(0 0 0 / 0.58));
  transition: transform 0.35s var(--ease);
}


/* Пьедестал: светящаяся плита с видимой верхней гранью. Верх шире низа —
   этого хватает, чтобы плита читалась объёмной, а не прямоугольником. */
.trophy__plinth {
  /* Круглый мраморный подиум в три ступени. Нарисован вектором и лежит
     в спрайте: viewBox 400x150, верхняя площадка примерно на трети высоты
     сверху — поэтому подиум поднят отрицательным отступом, иначе награда
     висела бы над ним, а не стояла на площадке. */
  display: block;
  width: 78%;
  height: auto;
  margin-top: -8.8%;
}

/* --- подписи ------------------------------------------------------------- */

.trophy__name {
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.3;
}
.trophy__name sup { font-size: 0.6em; }

.trophy__meta {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}

/* --- наведение ----------------------------------------------------------- */

.trophy:hover .trophy__item:not(.trophy__item--glass),
.trophy:focus-within .trophy__item:not(.trophy__item--glass),
.trophy:hover .trophy__glass,
.trophy:focus-within .trophy__glass { transform: translateY(-0.4rem); }

/* Наведение поднимает --lift, а не задаёт непрозрачность напрямую:
   прибавка зависит от того, насколько тёмное место у награды. */
.trophy:hover,
.trophy:focus-within { --lift: 1; }


/* --- описание ------------------------------------------------------------
   Лежит в общей полосе под залом и перекрывает подсказку: поэтому
   не нужен ни :has(), ни скрипт — достаточно фона. */
.trophy__desc {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;   /* над подсказкой, которая теперь на 3 */
  margin: 0;
  padding: clamp(1rem, 2vw, 1.4rem) 0 0;
  border-top: 1px solid var(--line);
  /* Ровно цвет секции. Пробовал подложить лёгкий градиент, чтобы плашка
     не читалась дырой в освещённой комнате, — но полоса стала отличаться
     по тону от всего вокруг и била в глаза сильнее, чем сама дыра. */
  background: var(--ink);
  font-size: clamp(1rem, 1.4vw, 1.15rem);
  line-height: 1.45;
  color: var(--paper);
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.4rem);
  transition: opacity 0.22s var(--ease), transform 0.22s var(--ease), visibility 0.22s;
}

.trophy:hover .trophy__desc,
.trophy:focus-within .trophy__desc {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.trophies__hint {
  position: absolute;
  z-index: 3;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: clamp(1rem, 2vw, 1.4rem) 0 0;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Класс ставит скрипт, когда описание закрыли клавишей, а курсор остался
   на награде: без этого Escape ничего бы не изменил. */
.trophies.is-hushed .trophy:not(:focus-within):hover .trophy__desc {
  opacity: 0;
  visibility: hidden;
}

/* Вместе с описанием гаснет и разгоревшийся свет: иначе награда осталась бы
   выхваченной прожектором без всякого объяснения. */
.trophies.is-hushed .trophy:not(:focus-within):hover { --lift: 0; }

/* --- предметы ------------------------------------------------------------ */

.cutout--stevie-gold {
  -webkit-mask-image: url(../assets/img/trophy-stevie-gold-mask.png);
  mask-image: url(../assets/img/trophy-stevie-gold-mask.png);
}
.cutout--stevie-silver {
  -webkit-mask-image: url(../assets/img/trophy-stevie-silver-mask.png);
  mask-image: url(../assets/img/trophy-stevie-silver-mask.png);
}

/* Акрил нарисован вектором и лежит спрайтом в конце страницы. */
/* Скрываем спрайт не через display: none — при нём браузер не создаёт
   градиенты из <defs>, и фигуры, ссылающиеся на них через <use>,
   отрисовываются чёрными. Убираем из потока размером. */
.cutout--award-aba-2025 {
  -webkit-mask-image: url(../assets/img/award-aba-2025-mask.png);
  mask-image: url(../assets/img/award-aba-2025-mask.png);
}
.cutout--award-gra-2024 {
  -webkit-mask-image: url(../assets/img/award-gra-2024-mask.png);
  mask-image: url(../assets/img/award-gra-2024-mask.png);
}
.cutout--award-globee {
  -webkit-mask-image: url(../assets/img/award-globee-mask.png);
  mask-image: url(../assets/img/award-globee-mask.png);
}
.cutout--award-aff-2024 {
  -webkit-mask-image: url(../assets/img/award-aff-2024-mask.png);
  mask-image: url(../assets/img/award-aff-2024-mask.png);
}
.cutout--award-ai-world {
  -webkit-mask-image: url(../assets/img/award-ai-world-mask.png);
  mask-image: url(../assets/img/award-ai-world-mask.png);
}
.cutout--award-wbo {
  -webkit-mask-image: url(../assets/img/award-wbo-mask.png);
  mask-image: url(../assets/img/award-wbo-mask.png);
}
.cutout--award-hackernoon {
  -webkit-mask-image: url(../assets/img/award-hackernoon-mask.png);
  mask-image: url(../assets/img/award-hackernoon-mask.png);
}

.trophy-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
  pointer-events: none;
}

/* Обёртка ровно по размеру стекла: проценты плашки с логотипом должны
   считаться от ширины трофея, иначе она получается втрое шире стекла
   и уезжает за кромки. */
.trophy__glass {
  position: relative;
  display: block;
  height: 100%;
  max-width: 100%;
  transition: transform 0.35s var(--ease);
}

.trophy__item--glass {
  /* block обязателен: у inline-SVG под низом остаётся зазор по базовой
     линии, и трофей висит над плитой вместо того, чтобы стоять на ней. */
  display: block;
  width: auto;
  height: 100%;
  max-width: 100%;
  /* стеклу тень не нужна — оно прозрачное, тень выдала бы плоскую вырезку */
  filter: drop-shadow(0.4rem 0.72rem 1.2rem rgb(0 0 0 / 0.42));
}

/* Матовая вставка на стекле, куда нанесён логотип. На настоящих акриловых
   наградах она такая же — и без неё тёмные знаки (у World Business Outlook
   почти чёрная надпись) на тёмном стекле были бы не видны. */
.trophy__engrave {
  /* Логотип премии подан как матовое травление на стекле: полупрозрачная
     светлая зона, а не белая наклейка. Без неё тёмные знаки (у World
     Business Outlook надпись почти чёрная) на тёмном стекле не читались бы. */
  position: absolute;
  left: 50%;
  top: 36%;
  display: grid;
  place-items: center;
  width: 58%;
  height: 12%;
  transform: translateX(-50%);
  object-fit: contain;
  pointer-events: none;
}

@media (max-width: 1000px), (hover: none), (pointer: coarse) {
  /* Наведения нет — зал превращается в список: предмет слева, текст справа,
     описание видно сразу.

     Света в списке нет вовсе, и это осознанно. Зал — это комната с двумя
     приборами; в списке комнаты не осталось, а диагональный луч через
     колонку шириной clamp(7rem, 34vw, 10rem) просто перечёркивал бы строки.
     Объём держат сами фигуры: их светотень нарисована внутри, ключ
     с 10:35 никуда не делся. Раньше здесь стояло обещание «свет и отражение
     остаются», которому код не соответствовал ни строчкой. */
  .trophies { padding-bottom: 0; }
  .trophies__hall { padding-top: 0; }
  .trophies__hall::after { display: none; }
  .trophies__shelf { grid-template-columns: 1fr; gap: 0; }
  .trophy {
    display: grid;
    grid-template-columns: clamp(7rem, 34vw, 10rem) minmax(0, 1fr);
    gap: 0 clamp(0.9rem, 3vw, 1.4rem);
    align-items: center;
    padding: clamp(0.9rem, 3vw, 1.25rem) 0;
    border-bottom: 1px solid var(--line);
  }
  .trophy__btn {
    display: contents;
    cursor: default;
  }
  .trophy__stage {
    height: clamp(9rem, 40vw, 12rem);
    grid-row: span 3;
  }
  .hall-rig,
  .hall-rig__haze { display: none; }
  .trophies__hall::before { display: none; }
  .trophy__stage::before { display: none; }
  /* Дальний ряд в списке не дальний: перспективы нет, ужимать нечего. */
  .trophy:nth-child(n+5) .trophy__stage { --amb: 1; transform: none; }
  /* Подиум здесь НИЧЕМ не переопределяется, и это важно. Раньше стояло
     height: 1.1rem — при width: 78% и viewBox 400×150 пропорция ломалась
     и плита сплющивалась в полоску, а рядом ей задавали положительный
     margin-top, чтобы отодвинуть эту полоску от предмета. Когда пропорция
     починилась, положительный отступ превратился в зазор: предмет повис
     в воздухе над подиумом.
     Верное значение уже есть в общем правиле — margin-top: -8.8%. Оно
     считается от ширины сцены, поэтому сажает предмет на площадку при
     любой ширине: на 390px сцена 132,6px, подиум 103,4, его высота 38,8,
     площадка на 30% высоты — это 11,6, а −8,8% от 132,6 равно −11,7. */
  .trophy__name { align-self: end; text-align: left; }
  .trophy__meta { text-align: left; }
  .trophy__desc {
    position: static;
    padding: 0.45rem 0 0;
    border-top: 0;
    background: none;
    font-size: 0.92rem;
    color: var(--muted);
    opacity: 1;
    visibility: visible;
    transform: none;
  }
  .trophies__hint { display: none; }
}

/* --------------------------------------------------------------------------
   Подкасты и эфиры: телефон с открытым YouTube и очередь выпусков
   Выпуски показаны там, где их и смотрят: слева телефон, в котором открыт
   ролик, справа очередь в дизайне YouTube. Нажатие на выпуск открывает его
   в телефоне, нажатие на телефон — сам ролик.
   Внутри телефона и в очереди цвета родные для YouTube, вместе с красной
   полосой плеера: это изображение приложения, как скриншот, а не часть
   интерфейса сайта. Зелёный остаётся за собственными метками сайта.
   -------------------------------------------------------------------------- */
/* Слева от заголовка номер секции, справа — факт о канале, на той же базовой
   линии: секция получает две служебные отметки по краям, заголовок остаётся
   между ними. Третья колонка нужна только здесь, поэтому правило привязано
   к секции, а не к общему .sec__head. */
.sec__head:has(> .sec__count) { grid-template-columns: auto 1fr auto; }
.sec__head > .sec__count { grid-column: 3; grid-row: 1; }
.sec__count {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Раньше факт был короткой строкой вроде «135 выпусков» и переноситься не мог.
   Статус в секции публикаций длиннее, поэтому перенос разрешён, а ширина
   ограничена — иначе третья колонка съедала бы заголовок. Ограничение
   действует только в шапке: тот же узел стоит и подписью внутри подблока,
   где делить строку не с кем. */
.sec__head > .sec__count { max-width: 18ch; }
/* Узко — колонок ровно столько же, сколько у остальных секций, а факт просто
   встаёт последним по потоку: в разметке он стоит после подписи, поэтому явные
   строки не нужны. Оба брейкпоинта повторяют штатные из style.css — правило
   с #podcasts специфичнее и без этого перебивало бы их, зажимая заголовок
   в узкую колонку и разрывая слова посреди. */
@media (max-width: 760px) {
  .sec__head:has(> .sec__count) { grid-template-columns: auto 1fr; }
  .sec__head > .sec__count { grid-column: 2; grid-row: auto; max-width: none; }
}
@media (max-width: 640px) {
  .sec__head:has(> .sec__count) { grid-template-columns: 1fr; }
  .sec__head > .sec__count { grid-column: 1; }
}

.pods {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 5rem);
  margin-top: clamp(1.25rem, 2.5vw, 2rem);
}

/* --- телефон -------------------------------------------------------------- */
.pods__phone {
  justify-self: center;
  width: 100%;
  max-width: 26rem;
}
/* Пропорция — у кадра assets/img/phone.png. --phone-screen — углы экрана
   на этом кадре в долях его сторон, по часовой от левого верхнего; по ним
   fitPhone() в main.js натягивает интерфейс. Телефон снят в перспективе,
   поэтому экран не прямоугольник: верхний край скошен круче нижнего.
   Цифры считает tools/phone-screen.js.
   Подъём при наведении через translate, а не transform: у обёртки
   transform занят анимацией появления. */
.phone {
  --phone-screen: 0.06641 0.01131, 0.77531 0.06515, 0.98192 0.97096, 0.24949 0.94825;
  position: relative;
  aspect-ratio: 862 / 1639;
  transition: translate 0.35s var(--ease);
}
/* Наведение считается по всему экрану, а не по ссылке ролика: иначе на кнопке
   «Подписаться», которая лежит над ссылкой, телефон опускался бы обратно. */
.phone:has(.yt:hover) { translate: 0 -6px; }

/* Корпус лежит поверх интерфейса: рамка и вырез под камеру закрывают его
   края, как у настоящего телефона. Экран в кадре прозрачный. */
.phone__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* --- приложение YouTube внутри -------------------------------------------- */
/* Экран рисуется плоским, в точках iPhone, и натягивается на снимок матрицей
   из fitPhone() в main.js. Поэтому здесь, в отличие от остального сайта,
   размеры в px: это холст фиксированного размера, и крупный шрифт из
   настроек браузера не увеличил бы его, а разорвал.
   Цвета — тёмная тема YouTube. */
.yt {
  --yt-bg: #0f0f0f;
  --yt-text: #f1f1f1;
  --yt-muted: #aaa;
  --yt-fill: rgba(255, 255, 255, 0.1);
  --yt-red: #ff0033;

  position: absolute;
  top: 0;
  left: 0;
  width: 390px;
  height: 832px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Размер и радиус — замер экрана из tools/phone-screen.js. Тень того же
     цвета выпускает фон на пару точек под рамку: иначе полупрозрачная
     кромка рамки просвечивала бы бумагой. */
  border-radius: 48px;
  box-shadow: 0 0 0 4px var(--yt-bg);
  background: var(--yt-bg);
  color: var(--yt-text);
  font-family: var(--font-text);
  font-size: 14px;
  line-height: 1.35;
  transform-origin: 0 0;
}
.yt svg { display: block; flex: none; fill: currentColor; }

/* Вырез под камеру занимает 126…258 точек по ширине и 29 по высоте.
   Часы и значки встают по центрам «ушей» слева и справа от него. */
.yt__status {
  position: relative;
  flex: none;
  height: 44px;
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
}
.yt__clock,
.yt__sys {
  position: absolute;
  top: 0;
  width: 108px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.yt__clock { left: 18px; letter-spacing: -0.01em; }
.yt__sys { right: 18px; gap: 5px; }
.yt__sys svg { width: auto; height: 11px; }

/* Плеер с показанными кнопками — так он узнаётся с первого взгляда.
   Вуаль под кнопками та же, что у YouTube, и заодно гасит пестроту обложек. */
.yt__player {
  position: relative;
  flex: none;
  aspect-ratio: 16 / 9;
  background: #000;
}
.yt__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 0.3s var(--ease);
}
.yt__controls { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.4); }
.yt__ctl { position: absolute; display: flex; align-items: center; }
.yt__ctl svg { width: 24px; height: 24px; }
.yt__ctl--top { top: 10px; left: 10px; right: 12px; justify-content: space-between; }
.yt__ctl-set { display: flex; gap: 22px; }
.yt__ctl--mid { top: 50%; left: 50%; gap: 36px; transform: translate(-50%, -50%); }
.yt__ctl--bottom {
  left: 12px;
  right: 10px;
  bottom: 12px;
  justify-content: space-between;
  font-size: 12px;
  font-weight: 500;
}
.yt__time span { opacity: 0.7; }
.yt__round {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
}
.yt .yt__round svg { width: 28px; height: 28px; }
.yt__round--play {
  width: 60px;
  height: 60px;
  transition: transform 0.25s var(--ease);
}
.yt .yt__round--play svg { width: 38px; height: 38px; }
.phone:has(.yt__watch:hover) .yt__round--play { transform: scale(1.08); }

/* Полоса лежит по нижнему краю кадра, отметка на ней свисает вниз —
   поэтому у плеера нет overflow: hidden. */
.yt__progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: rgba(255, 255, 255, 0.3);
}
.yt__played {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  background: var(--yt-red);
}
.yt__played::after {
  content: '';
  position: absolute;
  top: 50%;
  right: -6px;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--yt-red);
}

.yt__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 16px 12px 26px;
}
.yt__title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
}
.yt__meta { margin-top: 6px; font-size: 12px; color: var(--yt-muted); }
.yt__meta b { margin-left: 8px; font-weight: 600; color: var(--yt-text); }

/* Название канала и «Вы подписаны» стоят в одну строку, и размеры здесь
   подобраны замером: в Onest 13px название занимает 165 точек, кнопка 147,
   вместе с аватаром и зазорами — 359 из 366. Крупнее любой из них, и
   название переносится. Многоточие — только страховка на случай, если
   Onest ещё не загрузился и запасной шрифт шире. */
.yt__channel { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
.yt__avatar {
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  background: #3f3f3f;
}
.yt__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* Кнопка подписки в состоянии «уже подписан»: серая, с колокольчиком
   и стрелкой, как у YouTube после нажатия «Подписаться». */
.yt__sub {
  flex: none;
  display: flex;
  align-items: center;
  gap: 3px;
  height: 34px;
  padding: 0 8px 0 10px;
  border-radius: 17px;
  background: var(--yt-fill);
  font-size: 13px;
  font-weight: 500;
}
.yt__sub svg { width: 17px; height: 17px; }
.yt__sub svg:last-child { width: 15px; height: 15px; }

.yt__actions { display: flex; gap: 8px; margin-top: 14px; }
.yt__pill {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 14px 0 12px;
  border-radius: 18px;
  background: var(--yt-fill);
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
}
.yt__pill svg { width: 22px; height: 22px; }
.yt__pill--rate { gap: 12px; padding: 0 14px; }
.yt__pill--rate i { width: 1px; height: 22px; background: rgba(255, 255, 255, 0.2); }

.yt__comments {
  margin-top: 16px;
  padding: 10px 12px 12px;
  border-radius: 12px;
  background: var(--yt-fill);
}
.yt__comments-title { display: block; font-size: 14px; font-weight: 600; }
.yt__comments-field {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  font-size: 12px;
  color: var(--yt-muted);
}
.yt__comments-field svg { width: 24px; height: 24px; }

/* «Далее» — колонка с переносом. Строка, которой не хватило высоты, уходит
   во вторую колонку, за правый край, и там её прячет overflow. Так экран
   показывает столько роликов, сколько влезает целиком, и ни один не срезан
   посередине — какой бы длины ни было название выпуска выше. */
.yt__next {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-flow: column wrap;
  align-content: flex-start;
  gap: 12px;
  margin-top: 16px;
  overflow: hidden;
}
.yt__row { display: flex; gap: 10px; width: 100%; }
.yt__thumb {
  position: relative;
  flex: none;
  width: 176px;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 8px;
  background: #272727;
}
.yt__thumb img { width: 100%; height: 100%; object-fit: cover; }
.yt__badge {
  position: absolute;
  right: 4px;
  bottom: 4px;
  padding: 1px 4px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.8);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.35;
}
.yt__row-text { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
/* Четыре строки ровно помещаются в высоту обложки вместе с каналом —
   так даже самое длинное название не обрывается многоточием. */
.yt__row-title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.3;
}
.yt__row-meta { font-size: 11px; color: var(--yt-muted); }

.yt__home {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 8px;
  width: 134px;
  height: 5px;
  margin-left: -67px;
  border-radius: 3px;
  background: var(--yt-text);
  pointer-events: none;
}

/* Весь экран — ссылка на ролик: прозрачный слой поверх интерфейса.
   Общее правило :focus-visible скругляет рамку до 2px — здесь она должна
   идти по скруглению экрана. */
.yt__watch { position: absolute; z-index: 1; inset: 0; border-radius: inherit; }
.yt__watch:focus-visible {
  outline: 4px solid var(--accent);
  outline-offset: -10px;
  border-radius: inherit;
}

/* --- корпус анфас --------------------------------------------------------- */
/* На телефоне снимок в перспективе проигрывает: экран на нём сжат и скошен,
   и в колонке шириной в 350 точек текст внутри мельчал до нечитаемого.
   Поэтому на узких экранах корпус другой — iPhone прямо, нарисованный
   стилями в цветах снимка (Sierra Blue) и с тем же вырезом под камеру.
   Интерфейс тот же и натягивается той же матрицей, только углы экрана
   образуют прямоугольник. Снимок при этом не грузится: у скрытой ленивой
   картинки загрузка не начинается.
   Размеры — в точках корпуса 424×866 (--pt): экран 390×832, вокруг рамка 5
   и чёрная кайма 12. Скругления концентричны: 65 у корпуса, 60 у каймы,
   48 у экрана. Кнопки — по их местам на снимке.
   У выреза селектор с .yt: иначе его перебивает .yt svg { display: block }. */
.phone__body,
.yt .yt__notch { display: none; }

@media (max-width: 640px) {
  .phone {
    --pt: calc(100cqw / 424);
    --phone-screen: 0.04009 0.01963, 0.95991 0.01963, 0.95991 0.98037, 0.04009 0.98037;
    container-type: inline-size;
    aspect-ratio: 424 / 866;
  }
  .phone__frame { display: none; }

  .phone__body {
    display: block;
    position: absolute;
    inset: 0;
    border-radius: calc(var(--pt) * 65);
    background: linear-gradient(150deg, #cfe0f0, #93adc4 18%, #6f89a1 42%, #a6c0d6 60%, #58728a 84%, #8ea8bf);
    box-shadow:
      inset 0 0 0 calc(var(--pt) * 0.8) rgba(38, 60, 82, 0.6),
      0 calc(var(--pt) * 28) calc(var(--pt) * 48) calc(var(--pt) * -18) rgba(14, 15, 18, 0.45);
  }
  /* Кайма под стеклом и светлая фаска между ней и металлом — та же яркая
     линия, что идёт вдоль экрана на снимке. */
  .phone__body::before {
    content: '';
    position: absolute;
    inset: calc(var(--pt) * 5);
    border-radius: calc(var(--pt) * 60);
    background: #000;
    box-shadow: 0 0 0 calc(var(--pt) * 1.2) #c7daec;
  }
  .phone__key {
    position: absolute;
    width: calc(var(--pt) * 3.4);
    background: linear-gradient(90deg, #58728a, #b4cbe0 55%, #7f99b0);
  }
  .phone__key--mute,
  .phone__key--up,
  .phone__key--down {
    left: calc(var(--pt) * -3.4);
    border-radius: calc(var(--pt) * 1.5) 0 0 calc(var(--pt) * 1.5);
  }
  .phone__key--mute { top: calc(var(--pt) * 150); height: calc(var(--pt) * 30); }
  .phone__key--up   { top: calc(var(--pt) * 206); height: calc(var(--pt) * 58); }
  .phone__key--down { top: calc(var(--pt) * 278); height: calc(var(--pt) * 58); }
  .phone__key--power {
    right: calc(var(--pt) * -3.4);
    top: calc(var(--pt) * 232);
    height: calc(var(--pt) * 92);
    border-radius: 0 calc(var(--pt) * 1.5) calc(var(--pt) * 1.5) 0;
    background: linear-gradient(270deg, #58728a, #b4cbe0 55%, #7f99b0);
  }

  /* рамки поверх экрана больше нет — подкладывать фон под её кромку незачем */
  .yt { box-shadow: none; }
  .yt .yt__notch {
    display: block;
    position: absolute;
    z-index: 3;
    top: 0;
    left: 120px;
    width: 144px;
    height: 29px;
    pointer-events: none;
  }
}

/* --- очередь -------------------------------------------------------------- */
.pods__queue { min-width: 0; }

.ytq__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}
.ytq__chip {
  display: inline-flex;
  align-items: center;
  min-height: 2rem;
  padding: 0.3rem 0.75rem;
  border-radius: 0.5rem;
  background: color-mix(in srgb, var(--ink) 6%, var(--paper));
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1.3;
  transition: background-color 0.2s var(--ease);
}
a.ytq__chip:hover { background: color-mix(in srgb, var(--ink) 11%, var(--paper)); }
.ytq__chip.is-on { background: var(--ink); color: var(--paper); }
.ytq__chip:focus-visible { border-radius: 0.5rem; }

/* Подложка строки выходит за колонку на полрема: обложки стоят ровно
   под фильтрами, а фон выбранной строки не прилипает к обложке. */
.ytq__list {
  display: grid;
  gap: 0.25rem;
  margin-inline: -0.5rem;
}
.ytq__item {
  display: grid;
  grid-template-columns: minmax(0, 36%) minmax(0, 1fr);
  align-items: start;
  gap: 1rem;
  width: 100%;
  padding: 0.5rem;
  border-radius: 0.75rem;
  text-align: left;
  transition: background-color 0.2s var(--ease);
}
.ytq__item:hover { background: var(--paper-2); }
.ytq__item[aria-pressed="true"] { background: color-mix(in srgb, var(--ink) 7%, var(--paper)); }
.ytq__item:focus-visible { border-radius: 0.75rem; }

.ytq__thumb {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 0.5rem;
  background: var(--paper-2);
}
/* hqdefault — это 4:3 с чёрными полосами сверху и снизу; обрезка по 16:9
   снимает их ровно целиком. */
.ytq__thumb img { width: 100%; height: 100%; object-fit: cover; }
.ytq__time {
  position: absolute;
  right: 0.3rem;
  bottom: 0.3rem;
  padding: 0.05rem 0.3rem;
  border-radius: 0.25rem;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  font-size: var(--fs-micro);
  font-weight: 500;
  line-height: 1.4;
  transition: opacity 0.25s var(--ease);
}
/* Выпуск, открытый в телефоне, — плашка поверх обложки, как у YouTube
   на ролике, который уже играет. Хронометраж под ней убираем. */
.ytq__now {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  background: rgba(14, 15, 18, 0.66);
  color: #fff;
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: 0.02em;
  opacity: 0;
  transition: opacity 0.25s var(--ease);
}
.ytq__now svg { width: 1rem; height: 1rem; fill: currentColor; }
.ytq__item[aria-pressed="true"] .ytq__now { opacity: 1; }
.ytq__item[aria-pressed="true"] .ytq__time { opacity: 0; }

.ytq__text { display: flex; flex-direction: column; min-width: 0; padding-top: 0.1rem; }
/* YouTube режет названия на второй строке, но здесь очередь — главный
   текст блока, и название должно читаться целиком. Четыре строки — столько
   нынешним нужно на телефоне; предел остаётся на случай будущих, чтобы
   исключительно длинное название не растянуло строку очереди. */
.ytq__title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.35;
}
.ytq__channel { margin-top: 0.35rem; }
.ytq__channel,
.ytq__date { font-size: var(--fs-small); line-height: 1.45; color: var(--muted); }

@media (max-width: 1080px) {
  .pods { grid-template-columns: minmax(0, 4fr) minmax(0, 6fr); gap: clamp(1.5rem, 4vw, 3rem); }
  .ytq__item { grid-template-columns: minmax(0, 40%) minmax(0, 1fr); gap: 0.75rem; }
  .ytq__title { font-size: 1rem; }
}

/* Уже 900px в две колонки телефон мельчает до игрушечного, а названия
   в очереди растягиваются на четыре строки. Поэтому телефон встаёт над
   очередью; выбор выпуска тогда прокручивает страницу к нему — это делает
   reveal в podcastQueue. */
@media (max-width: 900px) {
  .pods { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .pods__phone { max-width: min(20rem, 78vw); }
  /* очередь во всю ширину — обложке хватает трети, иначе текст теряется рядом */
  .ytq__item { grid-template-columns: minmax(0, 34%) minmax(0, 1fr); }
}

/* Корпус анфас (см. «корпус анфас» выше) узкий и без перспективных полей,
   поэтому может занять почти всю колонку: экран в нём и так крупнее, чем
   на снимке той же ширины. */
@media (max-width: 640px) {
  .pods__phone { max-width: min(19rem, 82vw); }
}

@media (max-width: 480px) {
  .ytq__item { grid-template-columns: minmax(0, 40%) minmax(0, 1fr); }
  /* оба фильтра встают в одну строку */
  .ytq__chips { gap: 0.4rem; }
  .ytq__chip { padding-inline: 0.6rem; font-size: 0.8125rem; }
  .ytq__title { font-size: 0.9375rem; }
  .ytq__channel,
  .ytq__date { font-size: 0.8125rem; }
  /* На обложке шириной в треть экрана значок встаёт над подписью —
     в одну строку с ним подпись упиралась в края. */
  .ytq__now { flex-direction: column; gap: 0.15rem; }
}

/* Зона логотипа у каждой формы своя: у паруса чистое место ниже, у диска —
   в круге, у кристалла — на крупной фронтальной грани. Общая позиция
   попадала бы мимо стекла. */
.trophy--lens .trophy__engrave    { top: 37.2%; width: 52%; height: 21%; }
.trophy--sail .trophy__engrave    { top: 44%; width: 52%; height: 11%; }
.trophy--shard .trophy__engrave   { top: 42%; width: 56%; height: 11%; }
.trophy--obelisk .trophy__engrave { top: 38%; width: 62%; height: 12%; }

/* Парус, обелиск и кристалл узкие и высокие, а логотипы у них — вытянутые
   вордмарки (у World Business Outlook и Hackernoon пропорция около семи
   к одному). Поперёк стекла такой знак помещается только мелким, поэтому
   ставим его вдоль: бокс широкий, а поворот на четверть делает его
   вертикальной полосой. */
.trophy--sail .trophy__engrave,
.trophy--obelisk .trophy__engrave,
.trophy--shard .trophy__engrave {
  transform: translateX(-50%) rotate(-90deg);
  transform-origin: center;
}
.trophy--sail .trophy__engrave,
.trophy--obelisk .trophy__engrave {
  top: 32%;
  width: 150%;
  height: 13%;
}

/* У кристалла ровная передняя грань короче: сверху он сходится в остриё,
   снизу начинается огранка павильона. Полоса ставится между ними, поэтому
   она ниже и заметно короче, чем у паруса с обелиском. */
.trophy--shard .trophy__engrave {
  top: 49%;
  width: 88%;
  height: 12%;
}

/* --------------------------------------------------------------------------
   Печать. style.css делает тёмную секцию белой, а зал остался бы тёмным:
   и виньетка, и весь свет посчитаны от графита, на бумаге это дало бы
   чёрные полосы по краям ряда и серые разводы поперёк него. --lit: 0
   снимает свет разом, включая пятна на полу и стену.
   -------------------------------------------------------------------------- */

@media print {
  #awards { --lit: 0; }
  .trophies { background: none; }
  .trophies__hall::after { display: none; }
  .hall-rig,
  .hall-rig__haze { display: none; }
  .trophy__object::before,
  .trophy__object::after { display: none; }
  .trophy__item,
  .trophy__item--glass { filter: none; }
}






/* --------------------------------------------------------------------------
   Стена брендов.
   Ячейка держит фиксированную геометрию: сейчас внутри надпись, позже встанет
   логотип с тем же alt — сетка при подмене не меняется, и переверстывать блок
   не придётся. Сетка из однопиксельных зазоров на цветной подложке — тот же
   приём, что у .topics и .mosaic в style.css.
   auto-fit, а не auto-fill: при семи брендах пустая восьмая колонка иначе
   осталась бы серой ячейкой без содержимого.
   -------------------------------------------------------------------------- */

/* Знаки стоят прямо на бумаге, без рамок и разлиновки: сетка всё равно
   держит их по колонкам, а лишние линейки спорили с волосяными
   разделителями подблоков выше. Заодно исчезла морока с хвостом ряда —
   брендов семь, а в два столбца помещается восемь, и пустая ячейка
   раньше светилась рамкой без содержимого. */
.logos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: clamp(1.5rem, 3.5vw, 3rem) clamp(1.25rem, 2.5vw, 2.25rem);
  align-items: center;
  margin-top: clamp(1.25rem, 2.5vw, 2rem);
}
/* Высота задаётся самим знаком плюс запас, а не рамкой: без линеек
   прежние 5.5rem превращались в полосу пустоты вокруг логотипов. */
.logo {
  display: grid;
  place-items: center;
  min-height: 3.2rem;
  text-align: center;
}
.logo__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.15;
  letter-spacing: -0.01em;
}
/* Ограничены обе стороны, а не одна. Логотипы приходят разной формы:
   у «Сбербанка» и Mercedes-Benz это почти квадратная марка, у «Яндекса»
   и Dubai Police — длинный словесный знак. По одной высоте широкий знак
   упёрся бы в стенки ячейки, по одной ширине квадратная марка выросла бы
   вдвое выше соседей. Ограничение по обеим сторонам плюс object-fit:
   contain вписывает и то и другое в общий оптический размер.
   Приглушение до 0.78 — чтобы стена брендов не спорила по контрасту
   с именами компаний в соседних блоках: это доказательство, а не заголовок. */
.logo img {
  max-height: 2.2rem;
  max-width: 100%;
  width: auto;
  object-fit: contain;
  /* В покое все знаки приводятся к одному тону, цвет возвращается
     по наведению — тот же приём, что у ленты логотипов СМИ ниже
     по странице. brightness(0) красит в чёрный КАЖДЫЙ непрозрачный
     пиксель, поэтому файлы обязаны быть с прозрачным фоном: у логотипа
     на белой подложке зальётся весь прямоугольник. Силуэт при этом
     сохраняется — он задан альфа-каналом, а не цветом. */
  filter: grayscale(1) brightness(0);
  opacity: 0.78;
  transition: filter 0.25s var(--ease), opacity 0.25s var(--ease);
}
.logo:hover img {
  filter: none;
  opacity: 1;
}
/* Выворотка. У Ozon знак — не силуэт, а белые буквы внутри цветного круга:
   brightness(0) закрасил бы круг целиком, и от букв не осталось бы следа.
   Здесь цвет не гасится в ноль, а притемняется до веса соседних знаков,
   и разница между кругом и буквами сохраняется. Значения подобраны
   сравнением на стенде, а не на глаз. */
.logo--knockout img { filter: grayscale(1) brightness(0.78) contrast(1.2); opacity: 0.92; }

/* Знаки, близкие к квадрату: круглая марка Сбербанка, эмблема Dubai Police,
   звезда Mercedes, три бриллианта Mitsubishi. По общей высоте 2.2rem они
   выглядели бы вдвое мельче широких словесных знаков вроде DAMAC или Avito:
   у квадратного знака вся масса в высоте, у словесного — в ширине, и равнять
   их по одной стороне нельзя. Высота здесь подобрана так, чтобы площадь
   знака в ряду читалась одинаковой. */
.logo--tall img { max-height: 3rem; }
/* На телефоне наведения нет — там знаки так и остаются в одном тоне.
   Это осознанно: цвет здесь украшение, а узнаваемость несёт сам силуэт. */
@media (prefers-reduced-motion: reduce) {
  .logo img { transition: none; }
}

/* Перечень стран и карта стоят внутри подблока, где сетка .sec__head
   не действует, — отступы задаём здесь. */
.subsec > .formats { margin-top: clamp(1.25rem, 2.5vw, 2rem); }
.subsec > .geo__map--static { margin-top: clamp(1.25rem, 2.5vw, 2rem); }
.subsec > .about__text { margin-top: clamp(1.25rem, 2.5vw, 2rem); }


/* --------------------------------------------------------------------------
   Карточки публикаций: обложка статьи на фоне, текст на плашке.

   Картинка — отдельный элемент <img class="kit__bg">, а не background-image
   через переменную: url() внутри custom property браузер считает от таблицы
   стилей, где переменная ПОДСТАВЛЯЕТСЯ, а не от разметки, где задана, —
   путь из style-атрибута искался бы в /css/. Та же ловушка описана выше
   у масок вырезок. К тому же <img> умеет loading="lazy".

   Почему плашка, а не затемнение всей карточки. Сначала снимок был размыт
   целиком и закрыт общим затемнением — и читался ровным серым: чтобы белый
   заголовок держал контраст, затемнять приходилось почти до непрозрачности.
   Ослабить размытие без плашки нельзя — на снимке появляются яркие пятна,
   и контраст текста падает. Поэтому текст стоит на собственной тёмной
   плашке: его читаемость больше не зависит от кадра, а сам кадр можно
   показать почти чётким.
   Плашка матовая: backdrop-filter размывает то, что лежит под ней, —
   снимок чёткий сверху и мягко уходит в размытие под текстом.
   -------------------------------------------------------------------------- */

.kit__item:has(> .kit__bg) {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* Текст опускается вниз, сверху остаётся чистый кадр. Без добавочной
     высоты текст занимал всю карточку, и снимка было бы не видно. */
  justify-content: flex-end;
  min-height: clamp(20rem, 28vw, 23.5rem);
  padding: clamp(0.75rem, 1.4vw, 1rem);
}

.kit__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Исходники шириной 800 — вдвое шире карточки, поэтому снимок резкий
     сам. Остаточное размытие в пиксель только сглаживает артефакты
     JPEG и не мешает узнавать кадр. */
  filter: blur(1px);
  /* Даже такое размытие у краёв подмешивает прозрачность — масштаб
     выносит эту кайму за overflow. */
  transform: scale(1.04);
  transition: transform 0.6s var(--ease);
}

/* Плотность плашки подобрана замером: текст скрыт, под заголовком, датой
   и «Читать» берётся самый яркий пиксель фона. При 0.72 «Читать» на второй
   карточке давал 4.4:1 при пороге 4.5 — под ним светлый стол. При 0.78
   худшие значения: заголовок 9.6:1, дата 7.4:1, «Читать» 5.3:1. */
.kit__plate {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: clamp(0.95rem, 1.7vw, 1.2rem);
  background: rgb(14 15 18 / 0.78);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  border: 1px solid rgb(255 255 255 / 0.07);
}
/* На плашке нет свободной высоты, которую «Читать» раньше прижимал ко дну */
.kit__plate .kit__action { margin-top: 0.2rem; padding-top: 0; }

/* Заголовку отведено две строки, даже если он умещается в одну. Иначе
   плашка статьи с коротким заголовком выходила ниже соседних, и их верхние
   края стояли вразнобой. Так же выровнены метки должностей в «Компаниях». */
.kit__plate .kit__title { min-height: 2.6em; } /* две строки при line-height 1.3 */

/* Карточки с обложками стоят раздельно, с зазором: базовая сетка .kit
   собирает ячейки через однопиксельный зазор на серой подложке, и между
   снимками тянулась бы серая полоса — поэтому подложку и рамку снимаем. */
.kit--3:has(.kit__bg) {
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
  background: none;
  border: 0;
}

/* Между 641 и 1080 базовая сетка .kit даёт две колонки, и третья карточка
   оставалась одна во втором ряду с пустотой справа. Растягиваем её на обе
   колонки, а плашку держим шириной в одну колонку: снимок встаёт широкой
   полосой, а текст сохраняет размер соседних плашек. */
@media (min-width: 641px) and (max-width: 1080px) {
  .kit--3:has(.kit__bg) > .kit__item:nth-child(3) { grid-column: 1 / -1; }
  .kit--3:has(.kit__bg) > .kit__item:nth-child(3) .kit__plate {
    max-width: calc(50% - 0.5 * clamp(0.75rem, 1.5vw, 1.25rem));
  }
}

/* Дата: общий --muted подобран под бумагу и на тёмном почти пропадал. */
.kit__plate .kit__desc { color: #CDD1D7; }

/* Наведение: снимок медленно наезжает */
.kit__item:has(> .kit__bg):hover .kit__bg { transform: scale(1.1); }

@media (prefers-reduced-motion: reduce) {
  .kit__bg { transition: none; }
}
