/* ==========================================================================
   karlovyvary.info — лендинг
   Дизайн-система: «pop-up storybook diorama» (см. DESIGN.md)
   Механика сцены повторяет karlgonsalves.com: CSS 3D-колесо из спрайтов,
   вращаемое скроллом. Разбор оригинала — в README.md.
   ========================================================================== */

:root {
  /* Colors */
  --color-signal-yellow: #ffe600;
  --color-globe-azure: #007fff;
  --color-roof-coral: #ef3b2c;
  --color-charcoal-ink: #333333;
  --color-cloud-white: #ffffff;

  /* Typography */
  --font-display: 'Changa One', Impact, sans-serif; /* переопределяется из locale */
  --font-ui: Arial, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  --text-caption: 12px;
  --text-body: 14px;
  --text-subheading: 20px;
  --text-heading-sm: 32px;
  --text-heading: 50px;
  --text-display: 75px;

  /* Spacing */
  --spacing-10: 10px;
  --spacing-20: 20px;
  --spacing-45: 45px;
  --section-gap: 45px;

  /* Radius — единственный радиус системы */
  --radius-full: 1440px;

  /* Геометрия сцены */
  --hotel-h: min(280px, 52vw); /* размер рисунка отеля в шапке контента */
  --wheel-radius: 50vw;   /* радиус «планеты» */
  --item-size: 20vw;      /* сторона квадрата спрайта */
  --spoke-step: 15deg;    /* 24 слота × 15° = 360° */
  --scene-scale: 1;
  /* значения без JS — десктопная композиция; на узких экранах их пересчитает
     fitScene(), иначе верхушка колеса уезжает за край */
  --tilt: 20deg;
  --tilt-lamp: 15deg;

  /* Витрина (телефон с часами) над надписями сцены.

     Высота текстового слоя не задана числом, а выведена из витрины: надписи
     прижаты к его низу, поэтому только так зазор под телефоном остаётся
     одинаковым на любом экране. Задай обе величины в vh — и на ноутбуке с
     низким окном текст, чей кегль в пикселях, наехал бы на снимок.

     Нижняя граница — высота меню: оно висит поверх сцены, мерится в пикселях
     и на узком экране переносится на две-три строки, поэтому реальное значение
     --nav-h ставит JS, а число здесь — только запас до его первого замера. */
  --nav-h: 49px;
  /* строка с датой и погодой под меню: её высоту витрина обязана обойти */
  --topbar-h: 30px;

  /* Лента событий в первом экране. Переменные объявлены здесь, а не на самой
     .ribbon: от них считается положение витрины с телефоном, а витрина — не
     потомок ленты и значений с неё не видит. Ошибка тихая: витрина просто
     встаёт по центру экрана и наезжает на ленту. */
  --ribbon-w: 42vw;
  --ribbon-gap: 8px;
  --device-top: 6vh;
  --device-y: max(var(--device-top), calc(var(--nav-h) + var(--topbar-h) + 12px));
  --device-h: clamp(150px, 27vh, 300px);
  --device-gap: 34px;    /* зазор между низом витрины и первой строкой */
  --text-reserve: 124px; /* высота самого рослого из пяти экранов надписей */
  --pages-h: calc(
    var(--device-y) + var(--device-h) + var(--device-gap) + var(--text-reserve) + 2vh
  );
}

/* --------------------------------------------------------------- база --- */

*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background-color: var(--color-signal-yellow);
  color: var(--color-charcoal-ink);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  line-height: 1.5;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* Запрет бокового хода страницы.

   overflow-x должен стоять и на html, и на body: мобильные браузеры не
   переносят значение с body на вьюпорт, если у html оно visible, — страница
   продолжает ездить вбок, хотя на десктопе всё выглядит запертым.

   Именно hidden, а не clip. clip лишает html роли скролл-контейнера, значение
   не переходит на вьюпорт, и страница перестаёт прокручиваться вовсе — этой
   ошибкой я уже ронял лендинг. При hidden html остаётся скроллером, вертикаль
   работает как прежде, а липкие секции продолжают липнуть к вьюпорту.

   overscroll-behavior-x гасит «резиновый» отскок от бокового смаха. */
html, body {
  overflow-x: hidden;
  overscroll-behavior-x: none;
}

/* Полоса прокрутки не рисуется: страница листается пальцем и колесом, а на
   иллюстрации во весь экран серая планка сбоку выглядит инородно. Прокрутка
   при этом работает как обычно. Побочная польза: без полосы 100vw совпадает с
   шириной содержимого, и фиксированный слой сцены больше не торчит за правый
   край, как это было на десктопе. */
html, body { scrollbar-width: none; -ms-overflow-style: none; }
html::-webkit-scrollbar,
body::-webkit-scrollbar,
::-webkit-scrollbar { display: none; width: 0; height: 0; }

img { max-width: 100%; height: auto; }

a { color: inherit; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 2000;
  padding: var(--spacing-10) var(--spacing-20);
  background: var(--color-charcoal-ink);
  color: var(--color-cloud-white);
  text-decoration: none;
}
.skip-link:focus { left: 0; }

:focus-visible {
  outline: 3px solid var(--color-charcoal-ink);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------- нав --- */

.nav {
  position: fixed;
  top: 0; left: 0;
  z-index: 1000;
  display: flex;
  width: 100%;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  /* Плашка непрозрачная. Меню висит поверх сцены, и при прокрутке под ним
     проезжали дома: тёмные фасады под тёмными буквами — читать невозможно.
     Цвет тот же, что у неба, поэтому вверху страницы шва не видно, а всё, что
     подъезжает снизу, честно перекрывается. Отбивки по низу нет намеренно:
     линия резала жёлтое поле надвое и читалась как чужой элемент. */
  background-color: var(--color-signal-yellow);
  padding: 4px 0;
}

.nav__link {
  padding: var(--spacing-10) var(--spacing-20);
  color: var(--color-charcoal-ink);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  font-weight: 400;
  text-decoration: none;
  text-transform: uppercase;
  border-bottom: 0 solid var(--color-charcoal-ink);
}

/* активный пункт — единственный индикатор состояния: подчёркивание 20px */
.nav__link[aria-current='true'] { position: relative; }
.nav__link[aria-current='true']::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 4px;
  width: 20px; height: 2px;
  margin-left: -10px;
  background: var(--color-charcoal-ink);
}

/* Выбор языка ------------------------------------------------------------
   Тридцать шесть языков в строку меню не встают, поэтому раздел свёрнут в
   кнопку с текущим языком. Список раскрывается в несколько колонок: одним
   столбцом он был бы длиннее экрана и потребовал бы прокрутки внутри
   прокрутки. Панель непрозрачная и с чернильной рамкой — под ней проезжает
   рисованный город, на просвет читать было бы невозможно. */
.nav__langs { position: relative; }

.nav__lang-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: var(--spacing-10);
  border: 0;
  background: none;
  color: inherit;
  font-family: var(--font-ui);
  font-size: var(--text-caption);
  text-transform: uppercase;
  cursor: pointer;
}

/* Глобус нарисован бордюрами, а не эмодзи: эмодзи на каждой системе своё,
   местами цветное, и рядом с плоской графикой смотрится чужеродно. */
/* Глобус у кнопки языка убран по просьбе владельца: самоназвание языка
   («Русский», «Deutsch») говорит само за себя, а значок из двух окружностей на
   мелком кегле читался пятном. Стрелка вниз осталась — она сообщает, что это
   раскрывающийся список, а не ссылка. */

/* стрелка вниз уголком из двух бордюров */
.nav__lang-btn::after {
  content: '';
  width: 5px;
  height: 5px;
  margin-top: -3px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  rotate: 45deg;
}
.nav__lang-btn[aria-expanded='true']::after { margin-top: 2px; rotate: -135deg; }

.nav__lang-menu {
  position: absolute;
  top: calc(100% + 4px);
  inset-inline-end: 0;
  z-index: 1100;
  display: grid;
  grid-template-columns: repeat(3, minmax(120px, 1fr));
  gap: 0 var(--spacing-10);
  /* высота экрана минус меню: длинный список остаётся прокручиваемым, а не
     уезжает за нижний край */
  max-height: min(70vh, 520px);
  overflow-y: auto;
  margin: 0;
  padding: var(--spacing-10);
  list-style: none;
  background: var(--color-cloud-white);
  border: 3px solid var(--color-charcoal-ink);
}

.nav__lang-menu[hidden] { display: none; }

.nav__lang-menu a {
  display: block;
  padding: 7px var(--spacing-10);
  font-size: var(--text-caption);
  text-decoration: none;
  white-space: nowrap;
}

.nav__lang-menu a:hover { background: var(--color-signal-yellow); }

/* текущий язык — тем же подчёркиванием, что и активный пункт меню */
.nav__lang-menu a[aria-current='true'] {
  background: var(--color-signal-yellow);
  box-shadow: inset 3px 0 0 var(--color-roof-coral);
}

@media (max-width: 767px) {
  .nav__lang-menu {
    grid-template-columns: repeat(2, minmax(110px, 1fr));
    /* на телефоне панель прижата к краям экрана, а не к кнопке: у кнопки
       справа остаётся меньше места, чем нужно двум колонкам */
    position: fixed;
    top: var(--nav-h, 80px);
    inset-inline: var(--spacing-10);
  }
}

/* ================================================================ СЦЕНА === */
/*
   Иерархия (1:1 с механикой оригинала):
   .scene   — фиксированный слой на весь экран
    └ .camera        perspective
       ├ .axis--world           синий шар, радиус 50vw, центр ниже экрана
       ├ .ring (наклон кольца)  rotateX/Y/Z — «ось планеты»
       │  └ .roll   (скролл)    rotateX(--roll × -360deg)
       │     └ .spoke           rotateX(--i × 15deg) — спица
       │        └ .item         translateY(-50vw) — спрайт стоит на поверхности
*/

.scene {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  /* Главное здесь — clip-path, а не overflow.

     Колесо живёт внутри transform-style: preserve-3d, а трёхмерных потомков
     overflow: hidden не обрезает: спица шириной в три тысячи пикселей выходит
     за слой, расширяет страницу, и её начинает возить вбок. На десктопе это
     маскировалось, на телефоне вылезало. clip-path режет по-настоящему, в том
     числе трёхмерное содержимое. */
  clip-path: inset(0);
}

.camera {
  position: relative;
  width: 100vw;
  height: 100vh;
  perspective: var(--persp, 75vw);
  transform: scale(var(--scene-scale));
  transform-style: preserve-3d;
}

.axis {
  position: absolute;
  top: var(--horizon, 50vh);
  left: 0;
  width: 100vw;
  height: 100vw;
}

/* Река Тепла.
   Два кольца объектов наклонены зеркально (rotateY ±20°, rotateZ ±20°), поэтому
   их верхушки расходятся и между ними образуется долина. Дно этой долины —
   синий круг: это и есть река, по обе стороны которой стоит курортная улица. */
.axis--water {
  border-radius: var(--radius-full);
  background-color: var(--color-globe-azure);
}

/* Каменная набережная: белое кольцо ровно по краю воды, на уровне radius 50vw —
   там же, где стоят основания домов. Внутри border-box, поэтому лежит на воде. */
.axis--quay {
  border: 1.6vw solid var(--color-cloud-white);
  border-radius: var(--radius-full);
  background: transparent;
}

/* Блик на воде — второй, меньший круг. Плоская заливка, без градиента. */
.axis--water::after {
  content: '';
  position: absolute;
  left: 12%;
  top: 26%;
  width: 46%;
  height: 46%;
  border-radius: var(--radius-full);
  background-color: #1a8cff;
}

.ring {
  transform-style: preserve-3d;
}

/* Наклон колец разводит их верхушки в стороны — между ними и получается
   долина с рекой. Величина наклона больше не константа: при большом масштабе
   те же 20° уносят верхушку колеса далеко за левый край экрана, что и
   происходило на телефоне. Угол считает JS так, чтобы разнос верхушек всегда
   составлял ту же долю ширины экрана, что и на десктопе. */
.ring--left        { transform: rotateX(20deg) rotateY(calc(var(--tilt) * -1)) rotateZ(calc(var(--tilt) * -1)); }
.ring--right       { transform: rotateX(20deg) rotateY(var(--tilt))            rotateZ(var(--tilt)); }
.ring--lamps-left  { transform: rotateX(12deg) rotateY(calc(var(--tilt-lamp) * -1)) rotateZ(calc(var(--tilt-lamp) * -1)); }
.ring--lamps-right { transform: rotateX(12deg) rotateY(var(--tilt-lamp))            rotateZ(var(--tilt-lamp)); }
.ring--clouds      { transform: rotateX(-45deg) rotateY(0deg)   rotateZ(-90deg); }
.ring--pages       { transform: none; }

.roll {
  position: absolute;
  inset: 0;
  transform: rotateX(calc(var(--roll, 0) * -360deg));
  transform-style: preserve-3d;
  will-change: transform;
}

/* облака крутятся медленнее — параллакс */
.ring--clouds .roll { transform: rotateX(calc(var(--roll, 0) * -140deg)); }

.spoke {
  position: absolute;
  inset: 0;
  transform: rotateX(calc(var(--i) * var(--spoke-step)));
  transform-style: preserve-3d;
}

/* Множитель подскока объекта на гребне. Регистрируем как настоящее число,
   иначе браузер не умеет его интерполировать и вместо движения будет рывок. */
@property --pop {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}

/* спрайт: квадрат 20vw, стоит основанием на окружности радиуса 50vw */
.item {
  position: absolute;
  top: 0;
  left: calc(var(--item-size) / -2);
  width: var(--item-size);
  height: var(--item-size);
  margin-left: 50vw;
  background-image: var(--art);
  background-position: bottom center;
  background-repeat: no-repeat;
  background-size: contain;
  transform: translateY(calc(var(--item-size) * -1)) rotateY(60deg) scale(var(--pop, 1));
  /* рост от основания. Для rotateY это безразлично — ось вертикальная и
     проходит через ту же точку, — а масштаб теперь не отрывает дом от земли. */
  transform-origin: 50% 100%;
  transform-style: preserve-3d;
  /* Спрайты повёрнуты на 60° и в какие-то моменты проходят почти рёбром к
     камере. Без этого браузер перерисовывает их обратную сторону и картинка
     мигает — особенно заметно на телефоне при прокрутке. */
  backface-visibility: hidden;
}

.item--right {
  transform: translateY(calc(var(--item-size) * -1)) rotateY(-60deg) scale(var(--pop, 1));
}

/* Объект, доехавший до верхней точки колеса, коротко подпрыгивает — здания
   как будто здороваются, когда выходят на гребень. Признак ставит JS на ту
   спицу, что сейчас наверху, и снимает, когда её сменяет следующая. */
@keyframes item-pop {
  0%   { --pop: 1; }
  30%  { --pop: 1.3; }
  62%  { --pop: 0.93; }
  82%  { --pop: 1.08; }
  100% { --pop: 1; }
}

.spoke[data-crest] .item {
  animation: item-pop 0.72s cubic-bezier(0.2, 0.85, 0.3, 1);
}

@media (prefers-reduced-motion: reduce) {
  .spoke[data-crest] .item { animation: none; }
}

.item--lamp { --item-size: 14vw; }

.item--cloud {
  --item-size: 16vw;
  left: calc(var(--item-size) / -2 + var(--x, 0vw));
  transform: translateY(-50vw) rotateX(90deg) rotateY(-45deg) rotateZ(90deg);
}

/* Телефон и часы — витрина приложения в верхней трети первого экрана.

   Слой намеренно вынесен из .page: наплыв прокрутки (--s) масштабирует только
   надписи, а снимки стоят неподвижно, иначе на каждом шаге прокрутки телефон
   раздувался бы и вылезал за края кадра.

   Пропорции считаются от одной величины --device-h: телефон занимает её всю,
   часы — 62 % и заходят на его край, чтобы два снимка читались одним предметом,
   а не двумя картинками рядом. Тот же приём, что на стартовом экране
   приложения. */
.scene__device {
  /* −1..1 от курсора и −1..1 «вдоха» на каждый экран прокрутки; пишет scene.js */
  --mx: 0;
  --my: 0;
  --drift: 0;
  position: absolute;
  left: 50%;
  top: var(--device-y);
  translate: -50%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: var(--device-h);
  pointer-events: none;
}

.scene__device img {
  width: auto;
  display: block;
}

/* Амплитуды намеренно разные у телефона и часов: одинаковый сдвиг читался бы
   как съезжающая картинка, разный — как глубина. Единицы измерения — пиксели, а
   не доли: движение должно остаться незаметным на любом размере экрана. */
.scene__device .scene__phone {
  height: 100%;
  translate: calc(var(--mx) * 9px) calc(var(--my) * 6px + var(--drift) * 5px);
  rotate: calc(var(--mx) * 1.2deg);
  will-change: translate, rotate;
  filter: drop-shadow(0 calc(var(--device-h) / 24) calc(var(--device-h) / 12) rgba(51, 51, 51, 0.28));
}

/* Похарек — между телефоном и часами и по глубине, и по амплитуде движения:
   средний план ходит меньше переднего и больше заднего, иначе глубина
   рассыпается. Нахлёст влево — чашка накрывает правую часть телефона, как в
   финальном макете владельца. */
.scene__device .scene__cup {
  height: 74%;
  margin-inline-start: calc(var(--device-h) * -0.26);
  translate: calc(var(--mx) * 13px) calc(var(--my) * 8px + var(--drift) * -2px);
  rotate: calc(var(--mx) * -1deg);
  will-change: translate, rotate;
  filter: drop-shadow(0 calc(var(--device-h) / 26) calc(var(--device-h) / 13) rgba(51, 51, 51, 0.3));
}

/* отрицательный отступ — тот самый нахлёст; нижний приподнимает часы над
   основанием телефона, иначе ремешок читается его тенью.
   Нахлёст увеличен: часы лежат на чашке, а не рядом с ней. */
.scene__device .scene__watch {
  height: 62%;
  margin-inline-start: calc(var(--device-h) * -0.38);
  margin-bottom: calc(var(--device-h) * 0.04);
  translate: calc(var(--mx) * 17px) calc(var(--my) * 11px + var(--drift) * -6px);
  rotate: calc(var(--mx) * -2deg);
  will-change: translate, rotate;
  filter: drop-shadow(0 calc(var(--device-h) / 28) calc(var(--device-h) / 14) rgba(51, 51, 51, 0.32));
}

/* Текстовые экраны: плоский слой поверх сцены, вне 3D.
   Внутри preserve-3d текст растрируется в текстуру и мылится — здесь он
   рисуется обычным образом и остаётся чётким. Сменой управляет JS. */
.pages {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  display: grid;
  /* выше линии крыш: там текст пересекается с фасадами меньше всего.
     Слой опущен ниже, чем был, — над надписями теперь стоят телефон с
     часами, и им нужна верхняя треть экрана */
  height: var(--pages-h);
  padding: 0 var(--spacing-20) 2vh;
  align-items: end;
  justify-items: center;
  pointer-events: none;
}

/* Наплыв: текст растёт вместе с прокруткой и уходит за край — тот же эффект
   приближения, что давало 3D-кольцо, но обычным 2D-масштабом, поэтому буквы
   остаются чёткими. Значения --s и --o каждый кадр пишет JS, поэтому никаких
   transition: движение идёт ровно за пальцем. */
.page {
  grid-area: 1 / 1;
  opacity: var(--o, 0);
  transform: scale(var(--s, 1));
  transform-origin: 50% 60%;
  text-align: center;
  will-change: transform, opacity;
}

.page:not([data-active='true']) {
  visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .page { transform: none; }
}

.page__eyebrow {
  display: block;
  margin-bottom: 0.9em;
  color: var(--color-charcoal-ink);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  text-transform: uppercase;
}

/* Текст едет над сценой и попадает то на жёлтое небо, то на светлые фасады,
   то на синюю воду — одной заливкой все три фона не перекрыть. Решение то же,
   каким нарисована вся графика: плоская заливка плюс тонкий чернильный контур.
   paint-order обязателен — без него обводка рисуется по центру глифа и съедает
   букву; со stroke first она уходит назад, и форма буквы сохраняется. */
.display {
  margin: 0;
  color: var(--color-cloud-white);
  -webkit-text-stroke-color: var(--color-charcoal-ink);
  paint-order: stroke fill;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  text-wrap: balance;
}

/* Толщина задаётся на самих строках, а не на .display: em считается от
   собственного кегля элемента, а у .display он наследуется от body (14px),
   и обводка выходила в 1 пиксель вместо четырёх. */
.page .display__big { -webkit-text-stroke-width: 0.08em; }
.page .display__small { -webkit-text-stroke-width: 0.09em; }

.page__eyebrow {
  -webkit-text-stroke: 0.1em var(--color-cloud-white);
  paint-order: stroke fill;
}

.display__big {
  display: block;
  font-size: var(--text-display);
  line-height: 0.72;
}

.display__small {
  display: block;
  font-size: var(--text-heading-sm);
  line-height: 1.15;
}

/* CTA внутри сцены — единственный кликабельный элемент слоя */
.scene__cta {
  position: fixed;
  left: 50%;
  bottom: 12vh;
  z-index: 600;
  transform: translateX(-50%);
  pointer-events: auto;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s linear;
}

.scene__cta[data-visible='true'] { opacity: 1; visibility: visible; }

/* цепочка шагов Longevity */
.chain {
  margin: 0 0 var(--spacing-45);
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.chain__item {
  position: relative;
  padding: 0 0 var(--spacing-45) 68px;
  counter-increment: step;
}

.chain__item::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -4px;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  background: var(--color-globe-azure);
  color: var(--color-cloud-white);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  line-height: 48px;
  text-align: center;
}

/* линия, связывающая шаги в один маршрут */
.chain__item:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 23px;
  top: 52px;
  bottom: 12px;
  width: 3px;
  background: var(--color-globe-azure);
}

.chain__item h3 {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading-sm);
  font-weight: 400;
  line-height: 1;
}

.chain__item p { margin: 0; max-width: 58ch; }

/* Кнопка после цепочки шагов. Отбита от списка и от заключительной строки:
   она — точка решения, и слипшись с текстом читалась бы его частью. */
.chain__cta {
  margin: var(--section-gap) 0 var(--spacing-45);
  text-align: center;
}

.chain__cta .btn__note {
  display: block;
  margin-top: var(--spacing-10);
}

.chain__note {
  max-width: 52ch;
  margin: 0 auto;
  padding-top: var(--spacing-20);
  border-top: 4px solid var(--color-charcoal-ink);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  line-height: 1.15;
  text-align: center;
}

.scroll-hint {
  position: fixed;
  left: 50%;
  bottom: var(--spacing-20);
  z-index: 600;
  transform: translateX(-50%);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  pointer-events: none;
  transition: opacity 0.2s linear;
}

/* Подпись объекта, доехавшего до верхней точки колеса.
   Сидит над водой, ниже линии крыш и выше подсказки прокрутки — единственная
   полоса кадра, где ничего не нарисовано. Нажатие уводит к карточке места,
   поэтому это кнопка, а не подпись: цель 44px и видимый фокус. */
/* Плашка-кнопка: слева кружок с рисунком объекта, справа название и подсказка.
   Жёсткая тень без размытия — та же «наклейка», что и у прочих плоских фигур
   системы; она же отыгрывает нажатие: плашка проседает на высоту тени. */
.crest-label {
  /* Центрируется полями, а не сдвигом: при left:50% доступная ширина плашке
     считается от середины экрана, и длинное название ломается на строки
     задолго до настоящего края. */
  position: fixed;
  left: 0;
  right: 0;
  bottom: 26vh;
  z-index: 590;
  transform: translateY(6px);
  display: flex;
  align-items: center;
  gap: var(--spacing-10);
  width: max-content;
  max-width: min(88vw, 460px);
  margin: 0 auto;
  min-height: 44px;
  padding: 6px 20px 6px 6px;
  border: 3px solid var(--color-charcoal-ink);
  border-radius: var(--radius-full);
  background-color: var(--color-cloud-white);
  box-shadow: 0 4px 0 var(--color-charcoal-ink);
  color: var(--color-charcoal-ink);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  pointer-events: auto;
  opacity: 0;
  transition: opacity 0.25s linear, transform 0.25s cubic-bezier(0.2, 0.85, 0.3, 1),
    background-color 0.15s linear, box-shadow 0.15s linear;
}

.crest-label[data-visible='true'] {
  opacity: 1;
  transform: translateY(0);
}

/* рисунок объекта в круге — тот же приём, что у метки на фотографии карточки */
.crest-label__chip {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  background-color: var(--color-globe-azure);
  background-image: var(--art);
  background-repeat: no-repeat;
  background-position: center 78%;
  background-size: 74%;
}

.crest-label__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.crest-label__name {
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  font-weight: 400;
  line-height: 1.05;
}

/* стрелка вниз — обещание того, что произойдёт по нажатию */
.crest-label__hint {
  font-family: var(--font-ui);
  font-size: var(--text-caption);
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-roof-coral);
}

.crest-label__hint::after {
  content: ' ↓';
  display: inline-block;
}

.crest-label:hover { background-color: var(--color-signal-yellow); }

/* нажатие: плашка садится на тень, как настоящая клавиша */
.crest-label:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--color-charcoal-ink);
}

.crest-label:focus-visible { outline: 3px solid var(--color-roof-coral); outline-offset: 3px; }

/* смена объекта на гребне: имя не подменяется рывком, а всплывает снизу */
.crest-label[data-swap='true'] .crest-label__chip,
.crest-label[data-swap='true'] .crest-label__name {
  animation: crest-swap 0.3s cubic-bezier(0.2, 0.85, 0.3, 1) both;
}

@keyframes crest-swap {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .crest-label[data-swap='true'] .crest-label__chip,
  .crest-label[data-swap='true'] .crest-label__name { animation: none; }
}

/* «драйвер» скролла: пустая высота, которая крутит колесо */
/* Длина прокрутки задана числом экранов, а не константой: каждому тексту свои
   100vh хода. Иначе добавленный экран не удлинял бы привод, а делил тот же
   путь на большее число долей — вся цепочка начинала пролетать быстрее.
   Число подставляет сборка (--screens в <style> страницы). */
.scroll-driver { height: calc(var(--screens, 5) * 100vh); }

/* --------------------------------------------------------------- кнопки --- */

.btn {
  display: inline-block;
  padding: 14px 28px;
  border: 3px solid var(--color-charcoal-ink);
  border-radius: 0;
  background: var(--color-cloud-white);
  color: var(--color-charcoal-ink);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  line-height: 1;
  text-decoration: none;
  text-transform: uppercase;
}

.btn:hover { background: var(--color-signal-yellow); }

.btn--coral {
  background: var(--color-roof-coral);
  color: var(--color-cloud-white);
}
.btn--coral:hover { background: var(--color-charcoal-ink); }

.btn__note {
  display: block;
  margin-top: var(--spacing-10);
  font-family: var(--font-ui);
  font-size: var(--text-caption);
  text-transform: none;
  text-align: center;
}

/* ============================================================= КОНТЕНТ === */

.content {
  position: relative;
  z-index: 10;
  background: none; /* фон живёт на самих блоках — они наезжают друг на друга */
  counter-reset: sect;
}

/* СТОПКА БЛОКОВ. Каждый блок закрепляется, следующий наезжает на него сверху,
   как новая страница поверх старой. Непрозрачный фон обязателен: без него
   закреплённый блок просвечивал бы сквозь наезжающий. */
.content > .section,
.content > .cta-band {
  position: sticky;
  top: var(--sticky-top, 0px);
  background-color: var(--color-signal-yellow);
}

/* Шапка — исключение: её верх до середины отеля прозрачен, сквозь него видна
   сцена с городом и водой, поэтому здание стоит не на глухой плашке, а среди
   панорамы. Жёсткая остановка цвета, не растяжка. */
.content > .hero {
  background-color: transparent;
  background-image: linear-gradient(
    to bottom,
    transparent calc(var(--hotel-h) / 2),
    var(--color-signal-yellow) calc(var(--hotel-h) / 2)
  );
  background-repeat: no-repeat;
}

.section {
  padding: var(--section-gap) var(--spacing-20);
}

.wrap {
  max-width: 1100px;
  margin: 0 auto;
}

/* Заголовок секции: горизонт синего шара, поднимающийся из-под текста.
   Радиус 1440px в этой системе существует, чтобы ВЫРЕЗАТЬ круг из жёлтого
   холста, а не чтобы скруглять карточку: на широком невысоком блоке он давал
   «пилюлю» — форму, которой в системе нет. Круг шире вьюпорта и прижат низом,
   поэтому в кадре видна только его верхняя дуга. */
.section__opener {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  min-height: 44vw;
  margin: 0 calc(var(--spacing-20) * -1) var(--section-gap);
  padding: 9vw var(--spacing-20) 4vw;
  overflow: hidden;
  border-radius: 0;
  background: none;
  counter-increment: sect;
  text-align: center;
}

.section__opener::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 150vw;
  height: 150vw;
  margin-left: -75vw;
  border-radius: var(--radius-full);
  background: var(--color-globe-azure);
}

/* номер секции жёлтым по синему — холст возвращается как акцент */
.section__opener::after {
  content: counter(sect, decimal-leading-zero);
  position: relative;
  order: -1;
  margin-bottom: var(--spacing-20);
  color: var(--color-signal-yellow);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  letter-spacing: 0.28em;
  text-indent: 0.28em;
}

.section__opener h2 {
  position: relative; /* иначе ::before, как позиционированный, закроет текст */
  max-width: 15ch;
  margin: 0;
  color: var(--color-cloud-white);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading);
  font-weight: 400;
  line-height: 0.82;
  letter-spacing: -0.01em;
  text-wrap: balance; /* лечит рваный перенос в 3 строки */
}

.lead {
  max-width: 62ch;
  margin: 0 auto var(--spacing-45);
  font-size: var(--text-subheading);
  line-height: 1.45;
}

/* Шапка держится в пределах экрана и ЗАКРЕПЛЯЕТСЯ: следующий блок наезжает
   на неё сверху, как новая страница поверх старой. Липкий блок выше экрана
   обрезал бы собственный низ — часть текста стала бы недоступна, поэтому
   закрепление включается только там, где шапка гарантированно помещается. */
.hero {
  --hotel-top: -5vh;
  position: relative;
  padding-top: 0;
  padding-bottom: var(--spacing-20);
  text-align: center;
}


/* Отель на стыке сцены и контента — без подложки: рисунок лежит прямо на
   жёлтом холсте, как и вся графика системы. */
.hero__hotel {
  position: relative;
  display: block;
  width: var(--hotel-h);
  height: var(--hotel-h);
  margin: var(--hotel-top) auto var(--spacing-45);
}

.hero__art {
  position: absolute;
  inset: 0;
  background-image: var(--art);
  background-position: bottom center;
  background-repeat: no-repeat;
  background-size: contain;
  /* растём от основания: здание не должно отрываться от земли */
  transform-origin: 50% 100%;
  will-change: transform;
}

/* Лучи за зданием. Жёсткие остановки цвета в конической заливке дают плоские
   клинья, а не растяжку, — приём остаётся в системе. Лежат отдельным слоем
   под рисунком, потому что у элемента с фоном нет способа положить что-то
   позади собственного фона, не создав путаницы со стековым контекстом. */
.hero__rays {
  position: absolute;
  left: 50%;
  top: 62%;
  width: 230%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: var(--radius-full);
  background: repeating-conic-gradient(
    var(--color-roof-coral) 0deg 5deg,
    transparent 5deg 22deg
  );
  opacity: 0;
  pointer-events: none;
}

/* Выход отеля: выпрыгивает снизу, сильно перелетает нужный размер и качается
   до места — движение книжки-раскладушки. Признак вешает JS, когда здание
   поднялось на треть экрана: сработай оно раньше, приём проходил бы, пока
   рисунок ещё пролетает у нижнего края и на него никто не смотрит. */
@keyframes hotel-pop {
  0%   { transform: translateY(38%) scale(0.5) rotate(-5deg); opacity: 0; }
  38%  { transform: translateY(-5%) scale(1.22) rotate(2deg); opacity: 1; }
  58%  { transform: translateY(2%)  scale(0.88) rotate(-1.5deg); }
  76%  { transform: translateY(0)   scale(1.1)  rotate(0.6deg); }
  90%  { transform: scale(0.96); }
  100% { transform: none; }
}

@keyframes hotel-rays {
  0%   { opacity: 0; scale: 0.15; rotate: -18deg; }
  30%  { opacity: 0.9; }
  100% { opacity: 0; scale: 1.5; rotate: 10deg; }
}

.hero__hotel[data-seen='true'] .hero__art {
  animation: hotel-pop 1.4s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}

.hero__hotel[data-seen='true'] .hero__rays {
  animation: hotel-rays 1.1s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  .hero__hotel[data-seen='true'] .hero__art,
  .hero__hotel[data-seen='true'] .hero__rays { animation: none; }
  .hero__rays { display: none; }
}

.hero__eyebrow {
  margin: 0 0 var(--spacing-20);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  text-transform: uppercase;
}

.hero__logo {
  display: block;
  width: 272px;
  max-width: 72vw;
  height: auto;
  margin: 0 auto var(--spacing-20);
}

/* Заголовок мельче: бренд уже держат логотип и надпись над ним, а строка
   длинная — на крупном кегле она разваливалась на три ряда и забивала блок. */
.hero h1 {
  max-width: 28ch;
  margin: 0 auto var(--spacing-20);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading-sm);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* три программы в строку, разделены коралловой точкой */
.hero__programmes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-10) var(--spacing-20);
  margin: 0 0 var(--spacing-45);
  padding: 0;
  justify-content: center;
  list-style: none;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  line-height: 1.1;
}

.hero__programmes li { position: relative; }

.hero__programmes li:not(:last-child)::after {
  content: '';
  position: absolute;
  right: calc(var(--spacing-20) / -2 - 4px);
  top: 50%;
  width: 8px;
  height: 8px;
  margin-top: -4px;
  border-radius: var(--radius-full);
  background: var(--color-roof-coral);
}

.hero__claims {
  max-width: 56ch;
  margin: 0 auto var(--spacing-45);
}

.hero__claims p {
  margin: 0 0 var(--spacing-10);
  padding-top: var(--spacing-10);
  border-top: 2px solid var(--color-charcoal-ink);
  font-size: var(--text-subheading);
  line-height: 1.3;
}

.hero__claims p:last-child { margin-bottom: 0; }

/* -------------------------------------------------------------- таблица --- */

.table-wrap { overflow-x: auto; margin-bottom: var(--spacing-45); }

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-body);
}

caption {
  padding-bottom: var(--spacing-10);
  font-size: var(--text-caption);
  text-align: start;
}

th, td {
  padding: var(--spacing-10);
  border-bottom: 2px solid var(--color-charcoal-ink);
  text-align: start;
  vertical-align: top;
}

thead th {
  border-bottom-width: 4px;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  font-weight: 400;
}

td.num {
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  white-space: nowrap;
}

/* ---------------------------------------------------------------- список --- */

.objects { display: grid; gap: var(--spacing-45); }

@media (min-width: 760px) {
  .objects { grid-template-columns: repeat(2, 1fr); gap: var(--spacing-45); }
}

.object { scroll-margin-top: 80px; }

/* Карточка, к которой привела подпись со сцены: короткая вспышка рамкой.
   Программный фокус нужен для клавиатуры, но обводку браузера здесь рисовать
   не за чем — её роль играет та же вспышка. */
.object:focus { outline: none; }

.object[data-flash='true'] {
  animation: card-flash 1.4s ease-out both;
}

@keyframes card-flash {
  0%   { outline: 3px solid var(--color-roof-coral); outline-offset: 10px; }
  70%  { outline: 3px solid var(--color-roof-coral); outline-offset: 10px; }
  100% { outline: 3px solid transparent; outline-offset: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .object[data-flash='true'] { animation: none; }
}

.object__fig {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: 180px;
  margin: 0 0 var(--spacing-10);
  background: var(--color-globe-azure);
}

.object__fig img {
  max-height: 160px;
  width: auto;
}

/* Реальная фотография в круглом кадре. Радиус 1440px — та же фигура, что у
   шара сцены и у заголовков секций, поэтому снимок читается как часть
   системы, а не как вставка из другого проекта. */
.object__photo {
  position: relative;
  width: 100%;
  max-width: 320px;
  margin: 0 auto var(--spacing-20);
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  background: var(--color-globe-azure);
}

.object__photo img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-full);
  object-fit: cover;
}

/* Объект без снимка (источники): рисунок не отдельный кадр, а иконка в начале
   заголовка. Пропорции спрайта квадратные, поэтому и коробка квадратная;
   contain оставляет весь силуэт, нижний край сажает рисунок на строку. */
.object__icon {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  background-image: var(--art);
  background-repeat: no-repeat;
  background-position: center bottom;
  background-size: contain;
}

/* рисованный спрайт того же объекта — меткой на краю кадра */
.object__chip {
  position: absolute;
  left: 2%;
  bottom: 2%;
  width: 32%;
  height: 32%;
  border: 3px solid var(--color-charcoal-ink);
  border-radius: var(--radius-full);
  background-color: var(--color-signal-yellow);
  background-image: var(--art);
  background-position: center 76%;
  background-repeat: no-repeat;
  background-size: 66%;
}

.object__credit {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1.9em;
  font-size: var(--text-caption);
  text-align: center;
}

.object__credit a { color: inherit; }

.objects--photo .object { padding-bottom: var(--spacing-10); }

.footer__photo-note { margin-top: var(--spacing-10); }

.object h3 {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading-sm);
  font-weight: 400;
  line-height: 1;
}

/* заголовок с иконкой: рисунок и текст стоят на одной линии, длинное название
   переносится внутри своего блока, а не отрывает иконку */
.object--icon h3 {
  display: flex;
  align-items: flex-end;
  gap: var(--spacing-10);
}

.object__meta {
  margin: 0 0 var(--spacing-10);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.object p { margin: 0; }

/* ---------------------------------------------------------------- шаги --- */

.steps { display: grid; gap: var(--spacing-45); }

@media (min-width: 760px) {
  .steps { grid-template-columns: repeat(3, 1fr); }
}

.step h3 {
  margin: 0 0 var(--spacing-10);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading-sm);
  font-weight: 400;
  line-height: 1;
}

.step p { margin: 0; }

/* ------------------------------------------------------ карты маршрутов --- */

/* Группа = категория каталога (Маршрут I / II / III): у неё свой заголовок и
   объяснение рельефа, дальше — карточки самих троп. */
.routes__group { margin-top: var(--spacing-45); }

.routes__cat {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading-sm);
  font-weight: 400;
  line-height: 1;
}

.routes__cat-text { max-width: 70ch; margin: 0 0 var(--spacing-20); }

.routes {
  display: grid;
  gap: var(--spacing-45) var(--spacing-20);
  grid-template-columns: 1fr;
}

@media (min-width: 620px) {
  .routes { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1000px) {
  .routes { grid-template-columns: repeat(3, 1fr); }
}

/* Контур маршрута в круглом медальоне — та же фигура, что у фотографий
   объектов и у шара сцены. Белая заливка, а не жёлтая: линия воды и точки
   источников на белом читаются, на жёлтом сливаются с полем страницы. */
.route__map {
  width: 100%;
  max-width: 260px;
  margin: 0 auto var(--spacing-10);
  aspect-ratio: 1;
  border: 3px solid var(--color-charcoal-ink);
  border-radius: var(--radius-full);
  background-color: var(--color-cloud-white);
  overflow: hidden;
}

.route__map svg { display: block; width: 100%; height: 100%; }

.route__line {
  fill: none;
  stroke: var(--color-globe-azure);
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* источники на пути — жёлтые точки в обводке, как таблички на колоннадах */
.route__spring {
  fill: var(--color-signal-yellow);
  stroke: var(--color-charcoal-ink);
  stroke-width: 1.2;
}

/* старт и финиш: у кольцевых это одна и та же точка */
.route__start {
  fill: var(--color-roof-coral);
  stroke: var(--color-cloud-white);
  stroke-width: 2;
}

.route h3 {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  font-weight: 400;
  line-height: 1;
}

.route__meta {
  margin: 0 0 var(--spacing-10);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.route p { margin: 0; }

.route__cta { margin-top: var(--spacing-10) !important; }

/* Кнопка в карточке — не третий большой прямоугольник рядом с двумя другими:
   в ряду их девятнадцать. Достаточно ссылки-строки с той же стрелкой, что и
   у подписи на сцене. */
.route__cta a {
  font-weight: 700;
  text-transform: uppercase;
  font-size: var(--text-caption);
  letter-spacing: 0.08em;
  color: var(--color-roof-coral);
  text-decoration: none;
  border-bottom: 2px solid var(--color-roof-coral);
}

.route__cta a::after { content: ' →'; }
.route__cta a:hover { color: var(--color-charcoal-ink); border-bottom-color: var(--color-charcoal-ink); }

.routes__note {
  max-width: 70ch;
  margin: var(--spacing-45) auto var(--spacing-20);
}

/* ----------------------------------------------------------------- FAQ --- */

.faq { max-width: 70ch; margin: 0 auto; }

.faq__item { border-top: 2px solid var(--color-charcoal-ink); }
.faq__item:last-child { border-bottom: 2px solid var(--color-charcoal-ink); }

.faq__item summary {
  padding: var(--spacing-20) 0;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  cursor: pointer;
  list-style: none;
}

.faq__item summary::-webkit-details-marker { display: none; }

.faq__item summary::after {
  content: '+';
  float: inline-end;
  font-size: var(--text-heading-sm);
  line-height: 0.8;
}

.faq__item[open] summary::after { content: '–'; }

.faq__item p { margin: 0 0 var(--spacing-20); }

/* --------------------------------------------------------------- футер --- */

/* Финальный блок — тот же приём, но дуга уходит вверх: круг прижат верхом,
   и полоса читается как обратный горизонт, закрывающий страницу. */
.cta-band {
  position: relative;
  padding: 12vw var(--spacing-20) 9vw;
  overflow: hidden;
  background: none;
  text-align: center;
}

.cta-band::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  width: 150vw;
  height: 150vw;
  margin-left: -75vw;
  border-radius: var(--radius-full);
  background: var(--color-globe-azure);
}

.cta-band > * { position: relative; }

.cta-band h2 {
  margin: 0 auto var(--spacing-20);
  max-width: 16ch;
  color: var(--color-cloud-white);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading);
  font-weight: 400;
  line-height: 0.82;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.cta-band p {
  max-width: 50ch;
  margin: 0 auto var(--spacing-20);
  color: var(--color-cloud-white);
  font-size: var(--text-subheading);
}

/* --------------------------------------------- приложение внутри страницы --- */

/* Живое app.karlovyvary.info в конце лендинга. Рамка нарисована теми же
   средствами, что и вся система: чернильный контур в четыре пикселя, никаких
   теней и скруглений.

   Раздел ровно в высоту экрана, а рамка забирает всё, что остаётся от
   заголовка. Это и есть остановка: дойдя сюда, гость видит приложение во весь
   кадр, курсор оказывается внутри рамки — и колесо крутит уже приложение, а не
   страницу. Лендинг стоит на месте сам собой, никакой блокировки прокрутки для
   этого не нужно.

   svh, а не vh: на телефоне vh считается по экрану с убранной адресной
   строкой, и рамка вылезала бы за нижний край ровно на её высоту. svh — это
   всегда видимая часть, поэтому приложение целиком помещается и в тот момент,
   когда строка на месте. */
.app-embed {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
  /* отбивки уже, чем у обычной секции: каждый снятый пиксель уходит рамке */
  padding-top: var(--spacing-20);
  padding-bottom: var(--spacing-20);
  /* Фон и слой заданы здесь, а не только правилом стопки .content > .section:
     последняя рамка стоит ПОСЛЕ подвала, вне .content, и без этого сквозь неё
     просвечивала бы закреплённая сцена с городом. */
  position: relative;
  z-index: 10;
  background-color: var(--color-signal-yellow);
}

/* .wrap обязан РАСТИ, а не просто быть колонкой: иначе он остаётся по высоте
   содержимого, свободная высота секции достаётся ему же в виде пустоты,
   и рамка внизу занимает пятую часть экрана вместо всего кадра. */
.app-embed > .wrap {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 0; /* без этого flex-потомок не даёт колонке сжиматься */
}

.app-embed__frame {
  flex: 1 1 auto;
  display: flex;
  min-height: 220px; /* на низком окне заголовок не должен съесть рамку целиком */
  overflow: hidden;
  border: 4px solid var(--color-charcoal-ink);
  background: var(--color-cloud-white);
}

.app-embed__frame iframe {
  flex: 1 1 auto;
  display: block;
  width: 100%;
  border: 0;
}

/* Полоса по краям раздела — единственное место, за которое можно взяться,
   чтобы пролистать лендинг дальше: внутри рамки и колесо, и палец достаются
   приложению. Поэтому боковые поля здесь не убираем даже на телефоне. */
.app-embed__out {
  margin: var(--spacing-10) 0 0;
  font-size: var(--text-caption);
}

/* Подвал лежит вне .content, поэтому свой слой и глухой фон ему нужны отдельно:
   без них сквозь адрес и список языков просвечивала закреплённая сцена — город
   с водой проступал прямо сквозь текст. */
.footer {
  position: relative;
  z-index: 10;
  background-color: var(--color-signal-yellow);
  padding: var(--section-gap) var(--spacing-20);
  border-top: 4px solid var(--color-charcoal-ink);
  font-size: var(--text-caption);
}

.footer__grid { display: grid; gap: var(--spacing-20); }

@media (min-width: 760px) {
  .footer__grid { grid-template-columns: repeat(3, 1fr); }
}

.footer h2 {
  margin: 0 0 var(--spacing-10);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  font-weight: 400;
}

.footer ul { margin: 0; padding: 0; list-style: none; }

/* Список языков в подвале.

   Тридцать шесть кодов в столбик вытягивали подвал на восемьсот с лишним
   пикселей — больше, чем весь остальной подвал вместе взятый, и колонка «Язык»
   уезжала далеко ниже двух соседних. Коды короткие, в две-три буквы, поэтому
   кладём их строкой с переносом: тот же список умещается в четыре ряда.

   Подчёркивание снято: у тридцати шести ссылок подряд оно превращается в сетку
   из чёрточек и читать коды мешает. Возвращается при наведении и по фокусу,
   так что признак ссылки никуда не делся. */
.footer__langs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px var(--spacing-10);
}

.footer__langs a {
  display: block;
  padding: 2px 0;
  text-decoration: none;
}

.footer__langs a:hover,
.footer__langs a:focus-visible { text-decoration: underline; }

/* текущий язык — единственная жирная метка в списке */
.footer__langs a[aria-current] { font-weight: bold; text-decoration: underline; }

/* ------------------------------------------------------------ адаптив --- */

@media (max-width: 991px) {
  :root { --scene-scale: 1.25; --text-display: 60px; --text-heading: 40px; }
  .display__big { line-height: 0.75; }
}

@media (max-width: 767px) {
  /* Витринный кегль привязан к ширине экрана, а не задан числом.
     «Нейропомощник» — одно слово в тринадцать знаков, перенести его негде: при
     фиксированных 50px оно занимало 478px на экране в 412 и обрезалось с обеих
     сторон, теряя первую и последнюю букву. Доля 7.2vw подобрана по самому
     длинному слову из всех экранов, с запасом на внутренние поля. */
  :root { --scene-scale: 1.375; --text-display: min(50px, 7.2vw); --text-heading: 32px; --text-heading-sm: 26px; }
  .section__opener { min-height: 60vw; }

  /* На телефоне меню занимает две строки, поэтому витрина начинается ниже, а
     размер её меньше: линия земли здесь на 68vh, и опускать надписи дальше
     некуда — они упёрлись бы в крыши. */
  :root {
    --nav-h: 80px; /* меню здесь в две строки */
    --device-top: 10vh;
    --device-h: clamp(140px, 22vh, 240px);
    --text-reserve: 104px;
  }

  /* Пять экранов прокрутки — это семь-восемь смахиваний до первого текста.
     На телефоне вступление короче: колесо успевает показать себя, но не
     превращается в препятствие перед контентом. */
  /* на телефоне шаг короче: пять экранов — это семь-восемь смахиваний до
     первого текста, и вступление не должно превращаться в препятствие */
  .scroll-driver { height: calc(var(--screens, 5) * 60vh); }

  .crest-label { bottom: 22vh; font-size: var(--text-caption); padding: 10px 18px; }

  /* Плотность ленты домов.

     Оригинал держит в кадре под семь десятков спрайтов внахлёст, поэтому шара
     за ними почти не видно. У нас объектов всего 26 — это содержание, его не
     придумать, — и на узком экране между ними зияло пустое синее. Раз число
     домов фиксировано, расширяем каждый: лента смыкается и закрывает шар.

     Фонари при этом убираем. На телефоне они мельче спички, разрывают ленту
     светлыми промежутками и стоят четверти всей отрисовки сцены — на плавность
     прокрутки это влияет заметнее, чем на красоту. */
  /* Промежутки между домами.

     На телефоне оба берега попадают в кадр разом: двадцать восемь зданий на
     четыреста пикселей вставали в восемь слоёв и сливались в мешанину. Уменьшать
     спрайт бесполезно — при 13vw слоёв всё ещё четыре с половиной, а дома
     становятся неразличимы. Убирать одно кольцо нельзя: кольца это два берега
     реки, без правого весь город съезжает влево.

     Прореживание пробовали в трёх вариантах — через одно, каждое третье,
     каждое четвёртое — и все читались одинаково плохо: у горизонта дома всё
     равно спрессованы, а на переднем плане остаются по два-три с широкими
     просветами между ними. Дыры в первом ряду и есть то, что бросается в глаза.

     Поэтому дома оставляем все, а тесноту снимаем размером: 26vw вместо
     исходных 34vw. Здания на четверть у́же, шаг между ними падает с 15 до 11
     пикселей, ряд смыкается — и при этом не сливается в кашу, как было при
     крупных спрайтах.

     Фонари убираем совсем: мелкие, дробят ряд, а стоят четверти отрисовки. */
  :root { --item-size: 32vw; }
  .ring--lamps-left, .ring--lamps-right { display: none; }

  /* Линия земли ниже.

     На десктопе шар начинается на середине экрана — там кадр широкий, и дуга
     читается пологой. На телефоне тот же горизонт задирался под середину: было
     видно слишком большой кусок окружности, дома по краям заваливались набок, а
     под ними оставалась половина экрана пустой воды.

     Но и перебарщивать нельзя: при 66vh линия земли уходила на 72%, и между
     нижним краем текста и крышами оставалась пустая жёлтая полоса в пятую часть
     экрана — кадр становился лысым.

     68vh в паре с уменьшенным шаром: земля на 70%, а дуга уходит за боковой
     край экрана на 84% — дома продолжают спускаться к нижним углам, а не
     обрываются на середине, оставляя воду. Пустоту под текстом закрывают сами
     дома: при 32vw они поднимаются почти до заголовка. */
  .axis { --horizon: 68vh; }
}

@media (max-width: 479px) {
  :root { --text-display: min(40px, 7.2vw); --text-heading: 28px; --text-subheading: 17px; }
  :root { --text-reserve: 88px; } /* кегль витрины здесь мельче — строки ниже */
  .nav__link { padding: 8px 10px; font-size: var(--text-caption); }
}

/* ==========================================================================
   КАЛЕНДАРЬ СОБЫТИЙ
   Лента в первом экране, раздел на главной, каталог и страница события.

   Форма та же, что у всей системы: плоские плашки, контур 3px цвета чернил,
   нулевой радиус. Круглых карточек здесь нет намеренно — радиус 1440px в этой
   системе вырезает круг из жёлтого холста, а не скругляет прямоугольник.
   ========================================================================== */

/* ------------------------------------------------- дата и погода --- */

/* Полоска под меню. Висит поверх сцены, как и меню, поэтому фон непрозрачный:
   под ней проезжают дома. Высота учтена в --device-y — иначе витрина с
   телефоном начиналась бы под этой строкой. */
.topbar {
  position: fixed;
  top: var(--nav-h);
  left: 0;
  z-index: 999;
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: var(--spacing-10);
  width: 100%;
  padding: 2px var(--spacing-20) 6px;
  background-color: var(--color-signal-yellow);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.topbar__date { opacity: 0.7; }

/* Часы курорта. Моноширинного шрифта в системе нет, поэтому цифры фиксируются
   табличными глифами — иначе строка дёргается каждую минуту, когда единица
   сменяется восьмёркой. */
.topbar__clock {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

.topbar__clock[hidden] { display: none; }

.topbar__weather {
  font-size: var(--text-body);
  letter-spacing: 0;
}

.topbar__weather[hidden] { display: none; }

/* На страницах календаря меню стоит в потоке и липнет к верху. Строка с датой
   идёт под ним обычным блоком: сделай её тоже липкой — при прокрутке обе
   прилипли бы к нулю и наехали друг на друга. Прилипать должно меню, дата
   нужна один раз при заходе. */
body.subpage .topbar {
  position: static;
}

/* ----------------------------------------------------- лента в шапке --- */

/* Лента ближайших событий справа от витрины — и на десктопе, и на телефоне.
   Живёт внутри .scene, у которой pointer-events: none, — значит по ней нельзя
   щёлкнуть, пока не вернуть события указателя явно.

   Ширина ленты вынесена в переменную, потому что от неё считается положение
   витрины: телефон с часами стоит по центру того, что осталось слева. Задавать
   оба числа руками — верный способ однажды получить или наезд, или дыру между
   ними. */
.ribbon {
  position: absolute;
  top: var(--device-y);
  inset-inline-end: var(--ribbon-gap);
  z-index: 3;
  display: block;
  width: var(--ribbon-w);
  pointer-events: auto;
}

/* Витрина стоит по центру страницы — так решил владелец, и на широком экране
   это правильно: телефон с часами остаётся главным предметом первого экрана, а
   лента живёт справа своей жизнью.

   Но по центру она может стоять только пока хватает места. На экране около
   тысячи пикселей центр страницы и левый край ленты — это одна и та же точка,
   и витрина уезжает под карточки. Поэтому центр ограничен сверху: правый край
   витрины не заходит за ленту. Половина её ширины — примерно 0.55 высоты
   (телефон плюс часы внахлёст), считать точнее незачем, это отступ. */
.scene__device {
  left: min(
    50%,
    calc(100% - var(--ribbon-w) - var(--ribbon-gap) * 2 - var(--device-h) * 0.55)
  );
}

@media (min-width: 900px) {
  :root {
    --ribbon-w: clamp(240px, 24vw, 320px);
    --ribbon-gap: clamp(16px, 3vw, 48px);
  }

  .ribbon__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--spacing-10);
    margin: 0 0 10px;
    font-family: var(--font-display);
    font-weight: var(--font-display-weight, 400);
    font-size: var(--text-subheading);
    line-height: 1;
    text-transform: uppercase;
  }

  .ribbon__all {
    font-family: var(--font-ui);
    font-size: var(--text-caption);
    text-transform: uppercase;
    text-decoration: none;
    border-bottom: 2px solid var(--color-roof-coral);
  }

  /* Вертикальная стопка со своей прокруткой: высота ленты привязана к витрине,
     и без ограничения восемь карточек уехали бы под колесо. */
  .ribbon__track {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: min(52vh, calc(var(--device-h) + 190px));
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .ribbon__item {
    display: grid;
    grid-template-columns: 56px 1fr;
    grid-template-rows: auto auto;
    column-gap: 10px;
    align-items: center;
    padding: 8px;
    border: 3px solid var(--color-charcoal-ink);
    background: var(--color-cloud-white);
    text-decoration: none;
  }

  .ribbon__item:hover { background: var(--color-signal-yellow); }

  .ribbon__cover {
    grid-row: 1 / 3;
    width: 56px;
    height: 46px;
    background-image: var(--cover);
    background-size: cover;
    background-position: center;
    border: 2px solid var(--color-charcoal-ink);
  }

  .ribbon__when {
    font-size: var(--text-caption);
    text-transform: uppercase;
    color: var(--color-roof-coral);
  }

  /* Название в две строки максимум: третья строка ломает высоту карточки, и
     стопка перестаёт читаться ровным списком. */
  .ribbon__title {
    font-size: var(--text-body);
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

}

/* Телефон: витрина слева, лента событий справа от неё — требование владельца,
   чтобы «что сегодня в городе» было видно с первого экрана и на телефоне тоже.
   Ради этого лента здесь сжата до предела: обложки нет (в 40 % ширины экрана
   картинка 56px превращается в цветное пятно), названию хватает двух строк
   мелким кеглем, а сама стопка ниже — под ней ещё колесо и надписи. */
@media (max-width: 899px) {
  /* Витрина на телефоне мельче, чем была: рядом с ней теперь стоит лента, и в
     прежнем размере пара «телефон + лента» упиралась в надписи сцены. Заодно
     это отвечает на просьбу владельца сделать смартфон с часами компактнее. */
  :root { --device-h: clamp(110px, 16vh, 150px); }

  /* Стопка кончается там же, где витрина: всё, что ниже, наезжало бы на
     надпись «Longevity» под ними. Остальные события — прокруткой. */
  .ribbon__track { max-height: var(--device-h); }

  /* Заголовок в столбик: «Ближайшие события» и «Все события» в сорока
     процентах ширины экрана рядом не помещаются и налезают друг на друга. */
  .ribbon__head {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    margin: 0 0 6px;
    font-size: 11px;
    line-height: 1.1;
    text-transform: uppercase;
  }

  .ribbon__all {
    font-size: 10px;
    text-decoration: none;
    text-transform: uppercase;
    border-bottom: 2px solid var(--color-roof-coral);
    white-space: nowrap;
  }

  .ribbon__track {
    display: flex;
    flex-direction: column;
    gap: 5px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .ribbon__item {
    display: block;
    padding: 5px 7px;
    border: 2px solid var(--color-charcoal-ink);
    background: var(--color-cloud-white);
    text-decoration: none;
  }

  .ribbon__cover { display: none; }

  .ribbon__when {
    display: block;
    font-size: 10px;
    line-height: 1.2;
    text-transform: uppercase;
    color: var(--color-roof-coral);
  }

  .ribbon__title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 11px;
    line-height: 1.2;
  }
}

/* ------------------------------------------------------------ фильтры --- */

.ev-filters { margin: var(--spacing-20) 0; }

.ev-filters__row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
  padding-bottom: 2px;
}

.ev-filters__row--sort { align-items: center; }

.ev-filters__label {
  flex: none;
  align-self: center;
  font-size: var(--text-caption);
  text-transform: uppercase;
  opacity: 0.7;
}

.chip {
  flex: none;
  /* 44px по высоте — это не круглое число, а минимальная цель для пальца;
     ниже начинаются промахи. */
  min-height: 44px;
  padding: 10px 16px;
  border: 3px solid var(--color-charcoal-ink);
  border-radius: 0;
  background: var(--color-cloud-white);
  color: var(--color-charcoal-ink);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}

/* Телефон: кнопки мельче и переносятся на следующую строку.
   Раньше ряд прокручивался вбок, и кнопки уходили за правый край — владелец
   справедливо счёл это поломкой: спрятанное за краем нажимают только те, кто
   догадался, что там что-то есть. Перенос честнее, а мельче они ровно
   настолько, чтобы палец всё ещё попадал: 38px — нижняя граница, при которой
   промахи ещё редки. */
@media (max-width: 767px) {
  .chip {
    min-height: 38px;
    padding: 7px 10px;
    border-width: 2px;
    font-size: var(--text-caption);
  }

  .ev-filters__row { gap: 6px; margin-bottom: 6px; }
  .ev-filters__label { width: 100%; }
}

.chip:hover { background: var(--color-signal-yellow); }

.chip.is-on {
  background: var(--color-charcoal-ink);
  color: var(--color-cloud-white);
}

.chip--sort.is-on { background: var(--color-roof-coral); }

.ev-filters__count {
  margin: 4px 0 0;
  font-size: var(--text-caption);
  text-transform: uppercase;
  opacity: 0.7;
}

/* Не «пусто», а объяснение над подставленным списком ближайших событий:
   тупиковый экран заставлял гостя угадывать, какой фильтр снять. */
.ev-empty {
  margin: 0 0 var(--spacing-20);
  padding: var(--spacing-10) var(--spacing-20);
  border-inline-start: 6px solid var(--color-roof-coral);
  background: var(--color-cloud-white);
  font-size: var(--text-body);
}

/* ------------------------------------------------------------ карточки --- */

.ev-grid {
  display: grid;
  gap: var(--spacing-20);
  grid-template-columns: 1fr;
}

@media (min-width: 620px) {
  .ev-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1000px) {
  .ev-grid { grid-template-columns: repeat(3, 1fr); }
}

.ev {
  display: flex;
  flex-direction: column;
  border: 3px solid var(--color-charcoal-ink);
  background: var(--color-cloud-white);
}

.ev[hidden] { display: none; }

/* Событие, все сеансы которого уже прошли, скрипт помечает data-past. Прячем
   его правилом, а не через hidden: так оно исчезает и там, где нет панели
   отбора — в блоке «похожие события» и в ленте первого экрана. */
.ev[data-past],
.ribbon__item[data-past] { display: none; }

.ev__media {
  position: relative;
  border-bottom: 3px solid var(--color-charcoal-ink);
}

.ev__cover {
  display: block;
  line-height: 0;
}

/* Цена плашкой поверх обложки. Требование владельца — видеть её сразу, не
   открывая событие: «когда» и «почём» — два первых вопроса, и ради второго
   гость не должен проваливаться в карточку. Портал цену указывает не всегда,
   поэтому у молчания своя подпись, а не пустое место. */
.ev__pricetag {
  position: absolute;
  inset-inline-end: 10px;
  bottom: 10px;
  margin: 0;
  padding: 6px 12px;
  border: 3px solid var(--color-charcoal-ink);
  background: var(--color-cloud-white);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-body);
  line-height: 1.1;
  text-transform: uppercase;
  max-width: calc(100% - 20px);
}

.ev__pricetag--free {
  background: var(--color-roof-coral);
  color: var(--color-cloud-white);
}

/* Цены нет — плашка тише остальных: это не предложение, а признание, что
   организатор её не назвал. */
.ev__pricetag--unknown {
  border-width: 2px;
  font-family: var(--font-ui);
  font-size: var(--text-caption);
  opacity: 0.85;
}

.ev__cover img {
  width: 100%;
  height: auto;
  aspect-ratio: 5 / 3;
  object-fit: cover;
  display: block;
}

/* Вертикальную афишу кадрируем от верха. По центру, как все остальные кадры,
   от плаката 960×1360 остаётся середина: название и дата уезжают за границу
   кадра, и карточка теряет то единственное, ради чего плакат рисовали. */
.ev__cover--portrait img { object-position: center top; }

.ev__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--spacing-20);
  flex: 1;
}

.ev__cat {
  align-self: flex-start;
  padding: 4px 10px;
  border: 2px solid var(--color-charcoal-ink);
  background: var(--color-signal-yellow);
  font-size: var(--text-caption);
  text-transform: uppercase;
  line-height: 1.2;
}

/* Рубрики различаются цветом плашки, а не только словом: в списке из трёх
   колонок глаз ловит цвет раньше, чем читает подпись. Цвета — те же, что у
   обложек, чтобы карточка не спорила сама с собой. */
.ev__cat--concert, .ev__cat--theatre, .ev__cat--festival {
  background: var(--color-roof-coral);
  color: var(--color-cloud-white);
}
.ev__cat--tour, .ev__cat--exhibition, .ev__cat--unesco {
  background: var(--color-globe-azure);
  color: var(--color-cloud-white);
}

.ev__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-subheading);
  line-height: 1.15;
}

.ev__title a { text-decoration: none; }
.ev__title a:hover { text-decoration: underline; }

/* Строка фактов: когда, где, почём, сколько идти. Точка между полями рисуется
   псевдоэлементом, а не вписана в текст, — иначе она осталась бы висеть после
   поля, которого у события нет (цена бывает пустой сплошь и рядом). */
.ev__meta {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: var(--text-caption);
  text-transform: uppercase;
}

.ev__meta > span + span::before {
  content: '·';
  margin-inline-end: 10px;
  opacity: 0.5;
}

.ev__when { color: var(--color-roof-coral); }
.ev__walk { opacity: 0.7; }

.ev__lead {
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ev__more {
  margin: auto 0 0;
  padding-top: 8px;
  font-size: var(--text-caption);
  text-transform: uppercase;
}

.ev__more a {
  text-decoration: none;
  border-bottom: 2px solid var(--color-roof-coral);
}

.ev--compact .ev__cover img { aspect-ratio: 2 / 1; }
.ev--compact .ev__body { padding: var(--spacing-10) var(--spacing-20); }

.events__all { margin-top: var(--spacing-45); text-align: center; }

/* Ссылка на первоисточник обязательна: факты о событиях собраны с городского
   портала, и умалчивать об этом нечестно. */
.events__source {
  margin-top: var(--spacing-20);
  font-size: var(--text-caption);
  opacity: 0.7;
}

/* -------------------------------------------------------------- реклама --- */

/* Реклама помечена словом, а не только классом: и гость, и поисковик должны
   видеть, что это оплаченное место, а не редакционная карточка. */
.ad {
  display: block;
  margin: var(--spacing-20) 0;
  border: 3px solid var(--color-charcoal-ink);
  background: var(--color-cloud-white);
  text-align: center;
}

.ad__label {
  display: block;
  padding: 2px 8px;
  border-bottom: 2px solid var(--color-charcoal-ink);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.6;
}

.ad img {
  display: block;
  width: 100%;
  height: auto;
}

.ad--events-inline { grid-column: 1 / -1; }

@media (min-width: 1000px) {
  /* Внутри списка баннер занимает одну ячейку сетки, а не всю строку: полоса
     во всю ширину разрывает каталог надвое. */
  .ad--events-inline { grid-column: auto; }
  .ad--events-inline img { height: 100%; object-fit: cover; }
}

/* ------------------------------------------------- страницы календаря --- */

/* На страницах календаря нет закреплённой сцены, поэтому фон держит body.

   Меню здесь липкое, а не фиксированное. Фиксированное меню на главной висит
   поверх сцены и не занимает места, а высоту под него держит --nav-h, которую
   ставит scene.js. На страницах календаря сцены нет, скрипт не грузится, и
   --nav-h остаётся значением по умолчанию — а меню на узком экране переносится
   на две-три строки и накрывает хлебные крошки. Липкое меню занимает своё
   место в потоке, и вопрос высоты снимается совсем. */
body.subpage { background-color: var(--color-signal-yellow); }

body.subpage .nav {
  position: sticky;
  top: 0;
}

.content--page {
  position: relative;
  z-index: 10;
  padding: var(--spacing-45) var(--spacing-20) var(--section-gap);
}

.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--spacing-20);
  font-size: var(--text-caption);
  text-transform: uppercase;
}

.crumbs a { text-decoration: none; border-bottom: 2px solid var(--color-charcoal-ink); }
.crumbs__sep { opacity: 0.4; }

.page-head { margin-bottom: var(--spacing-45); }

.page-head h1 {
  margin: 0 0 var(--spacing-10);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading);
  line-height: 1;
  text-transform: uppercase;
}

/* --------------------------------------------------- страница события --- */

.ev-page {
  max-width: 760px;
  margin: 0 auto;
  border: 3px solid var(--color-charcoal-ink);
  background: var(--color-cloud-white);
  padding: var(--spacing-20);
}

.ev-page__cover {
  margin: calc(-1 * var(--spacing-20)) calc(-1 * var(--spacing-20)) var(--spacing-20);
  border-bottom: 3px solid var(--color-charcoal-ink);
  line-height: 0;
}

/* На странице события снимок показывается целиком, без кадрирования: здесь у
   него есть место, и обрезать вертикальную афишу незачем. Потолок по высоте
   нужен, чтобы плакат не занял весь первый экран и не отодвинул факты. */
.ev-page__cover img {
  width: 100%;
  height: auto;
  max-height: 70vh;
  object-fit: contain;
  display: block;
  background: var(--color-signal-yellow);
}

.ev-page__credit {
  padding: 6px var(--spacing-20);
  border-top: 2px solid var(--color-charcoal-ink);
  font-size: var(--text-caption);
  line-height: 1.3;
  opacity: 0.75;
}

.ev-page__head h1 {
  margin: 8px 0 var(--spacing-10);
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading-sm);
  line-height: 1.05;
  text-transform: uppercase;
}

/* Факты таблицей «подпись — значение»: гость ищет глазами «когда» и «сколько
   идти», а не читает абзац. */
.facts {
  display: grid;
  gap: var(--spacing-10);
  grid-template-columns: 1fr;
  margin: var(--spacing-20) 0;
  padding: var(--spacing-20);
  border: 3px solid var(--color-charcoal-ink);
  background: var(--color-signal-yellow);
}

@media (min-width: 620px) {
  .facts { grid-template-columns: repeat(2, 1fr); }
}

.facts__item dt {
  font-size: var(--text-caption);
  text-transform: uppercase;
  opacity: 0.7;
}

.facts__item dd {
  margin: 2px 0 0;
  font-size: var(--text-subheading);
  line-height: 1.15;
}

/* Плашка про Region Card — теперь ссылка в магазин карты, поэтому она обязана
   выглядеть нажимаемой: сама плашка кликабельна целиком, а не подчёркнутое
   слово внутри неё. */
.ev-page__badge {
  display: inline-block;
  margin: 0 0 var(--spacing-20);
  border: 3px solid var(--color-charcoal-ink);
  background: var(--color-globe-azure);
  font-size: var(--text-caption);
  text-transform: uppercase;
}

.ev-page__badge a,
.ev-page__badge {
  color: var(--color-cloud-white);
  text-decoration: none;
}

.ev-page__badge a {
  display: block;
  padding: 8px 14px;
}

.ev-page__badge a::after {
  content: ' →';
}

.ev-page__badge:hover {
  background: var(--color-roof-coral);
}

.ev-page__text p { margin: 0 0 var(--spacing-10); }

.ev-page__tip {
  margin: var(--spacing-20) 0;
  padding: var(--spacing-10) var(--spacing-20);
  border-inline-start: 6px solid var(--color-roof-coral);
  background: var(--color-signal-yellow);
}

.ev-page__dates h2,
.ev-related h2 {
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 400);
  font-size: var(--text-heading-sm);
  text-transform: uppercase;
  margin: var(--spacing-45) 0 var(--spacing-10);
}

.dates {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 6px;
  grid-template-columns: 1fr;
}

@media (min-width: 620px) {
  .dates { grid-template-columns: repeat(2, 1fr); }
}

.dates li {
  padding: 8px var(--spacing-10);
  border: 2px solid var(--color-charcoal-ink);
  font-size: var(--text-body);
}

.ev-page__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-10);
  margin: var(--spacing-45) 0 var(--spacing-10);
}

.ev-page__map,
.ev-page__source,
.ev-page__back {
  font-size: var(--text-caption);
  opacity: 0.75;
}

.ev-page__back { margin-top: var(--spacing-45); text-align: center; }

.ev-related { max-width: 1100px; margin: var(--spacing-45) auto 0; }

/* На печать событие уходит без меню и без рамки приложения — остаются факты,
   расписание и адрес: ровно то, что гость может унести с собой на прогулку. */
@media print {
  .ev-filters, .ad, .app-embed, .ev-page__actions { display: none !important; }
  .ev-page { border: none; padding: 0; }
}

/* --------------------------------------------- уважение к prefers-motion --- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .scroll-driver { height: 100vh; }

  /* колесо замирает в стартовом положении, страница остаётся читаемой */
  .roll { transform: none !important; }

  /* витрина не ходит ни за курсором, ни за прокруткой */
  .scene__device .scene__phone,
  .scene__device .scene__cup,
  .scene__device .scene__watch { translate: none; rotate: none; }
  .scene__cta { opacity: 1; visibility: visible; }
}

/* печать: сцена не нужна, контент — нужен */
@media print {
  .scene, .scroll-driver, .nav, .scroll-hint { display: none !important; }
  body { background: #fff; }
}
