/* ПЕРЕНОС ИЗ ЭТАЛОНА. Файл `main.css` из corona-travel, взят целиком.
 *
 * Почему целиком, а не выборкой нужных правил. Первая попытка выдёргивала
 * правила скриптом — и часть из них потеряла обёртку `@media`: десктопная
 * шапка начала применяться на всех ширинах. На широком экране значения
 * совпадают с правильными, поэтому браузерная проверка на 1440 ничего не
 * показала. Целый файл такой ошибки не допускает по построению, а лишние
 * правила для классов, которых в теме ещё нет, безвредны.
 *
 * Изменено при переносе ровно два вида вещей, оба механические:
 *
 *   1. `data-theme` -> `data-ct-theme`. Тема переименовала атрибут, чтобы
 *      не пересекаться с чужими скриптами на странице партнёра.
 *   2. Пути картинок `../img/` -> `{% static %}`-совместимые. Django
 *      отдаёт статику с другого префикса, относительный путь от CSS сюда
 *      не годится.
 *
 * Правила и комментарии эталона не трогались: в них записаны замеры
 * макета, и пересказ своими словами потерял бы основание.
 */

/* Corona Travel — mobile-first markup, BEM.
 * Base layer reproduces the 390 px frames; wider breakpoints scale the same
 * blocks up. See README.md for what is transcribed vs. approximated.
 */

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--ff-base);
  font-weight: var(--fw-medium);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img {
  display: block;
  max-width: 100%;
}

button,
input,
select {
  font: inherit;
  color: inherit;
}

a {
  color: inherit;
  text-decoration: none;
}

h1,
h2,
h3,
p,
figure {
  margin: 0;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Атрибут `hidden` обязан побеждать любой собственный display у блока.
   Браузерное правило `[hidden] { display: none }` живёт в UA-стилях и имеет
   нулевую специфичность, поэтому его перебивает даже `.field { display: flex }`.
   Из-за этого скрытые по режиму поля поиска продолжали занимать место: на
   вкладке «Отели» в полосе оставались все пять полей, и каждое сжималось до
   103 px вместо ~135. Проверка при этом проходила, потому что смотрела на
   свойство `.hidden`, а не на видимость. */
[hidden] {
  display: none !important;
}

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


/* ======================================================== контейнер
 * Одно правило вместо max-width в каждом брейкпоинте: колонка тянется
 * резиной и упирается в --container-max. Между брейкпоинтами ширина
 * растёт плавно, а не ступенькой. */
.section-head,
.search,
.results,
.chips--section,
.cards-more,
.destinations__footer,
.promo,
.telegram {
  width: min(100% - 2 * var(--gutter), var(--container-max));
  margin-inline: auto;
  padding-inline: 0;
}

/* Карточки идут в край страницы — так нарисовано в макете. */
.cards {
  grid-template-columns: repeat(var(--card-cols), 1fr);
}

/* ============================================================ page shell */

.page {
  position: relative;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
}

.page__main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Figma spaces sections with a 32 px "Margin" spacer component. */
.page__main > * + * {
  margin-top: var(--space-l);
}

/* Все секции лежат выше героя: его фоновое фото абсолютное и не должно
   перекрывать текст следующих блоков, как бы ни менялась его высота. */
.page__main > * {
  position: relative;
  z-index: 1;
}

.page__main > .hero {
  z-index: 0;
}

.section-title {
  font-size: var(--fs-title);
  font-weight: var(--fw-light);
  line-height: var(--lh-title);
}

.section-subtitle {
  margin-top: var(--space-xs);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-light);
  color: var(--text-muted);
}

/* ================================================================ header */

/* In Figma the header and the hero photo both sit at y=0, so the bar overlays
   the image rather than stacking above it. */
/* На мобильном шапка — три зоны с центрированным логотипом (курс / логотип /
   «войти»), поэтому grid с равными краями, а не space-between: у боковых зон
   разная ширина, и при space-between логотип уезжал бы от центра. */
.header {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-xs);
  height: 52px;
  padding-inline: var(--gutter);
  background: rgb(255 255 255 / 1%);
  backdrop-filter: blur(22.8px);
  -webkit-backdrop-filter: blur(22.8px);
}

/* Курс валют слева — только на мобильном: на 1024+ он живёт в topbar. */
.header__rates {
  margin: 0;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--text-on-media);
  white-space: nowrap;
}

.header__logo {
  display: inline-flex;
  align-items: center;
  color: var(--text-on-media);
}

/* Задаётся высота, а ширина считается из viewBox: у логотипа своя пропорция
   (159.546 / 33.917 = 4.704), и пара фиксированных чисел её слегка искажала —
   109x24 даёт 4.54. Высота же в макете стабильна и замеряема: 24 на 390,
   25 на 1024, 35 на 1920. */
.header__logo svg {
  width: auto;
  height: 24px;
}

.header__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2xs);
}

.header__contact {
  display: none;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-3xs);
}

.header__callback {
  padding: 0;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--text-muted);
  background: none;
  border: 0;
  cursor: pointer;
}

.header__callback:hover {
  color: var(--accent);
}

.header__login {
  padding: var(--space-xs) var(--space-s);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-on-media);
  background: rgb(255 255 255 / 8%);
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
}


/* =============================================================== topbar */
/* Верхняя служебная строка есть только в десктопных фреймах (1024+). */

/* Две линейки в шапке, и обе — от макета. Замер колонки чистого фона на
   1920 и 1024 (одинаково на обоих):

       0..47     48 px   верхняя строка
       48        линейка
       49..110   62 px   шапка
       111       линейка
       112..     контент

   Отсюда два следствия. Линейки у ВЕРХНЕГО края страницы нет — у
   .topbar__inner стоял `border-top`, он рисовал её на y=0 и в макете ничему
   не соответствовал. Линейка под шапкой, наоборот, есть, а в разметке её не
   было: контент начинался с 111 вместо 112, и вся страница ниже стояла мимо
   базовой сетки 4 px — включая заголовки секций и карточки. Это и было
   основной причиной «объекты не попадают на направляющие». */
.topbar {
  display: none;
  border-bottom: 1px solid var(--surface-border);
}

.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-l);
  width: min(100% - 2 * var(--gutter), var(--container-max));
  height: 48px;
  margin-inline: auto;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}

.topbar__theme {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0;
  font-size: var(--fs-caption);
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

.topbar__switch {
  position: relative;
  width: 40px;
  height: 20px;
  background: var(--surface-border);
  border-radius: var(--radius-pill);
  transition: background 0.15s ease;
}

.topbar__switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  background: var(--accent);
  border-radius: 50%;
  transition: transform 0.15s ease;
}

:root[data-ct-theme="dark"] .topbar__switch::after {
  transform: translateX(20px);
}

.topbar__rates,
.topbar__links {
  display: flex;
  gap: var(--space-l);
}

.topbar__links a:hover {
  color: var(--text);
}

/* Десктопная навигация и телефон — скрыты на мобильном. */
.header__nav,
.header__phone {
  display: none;
}

/* =============================================================== buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  min-height: var(--control-h);
  padding: var(--space-3xs) var(--space-s);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-medium);
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.btn__label {
  flex: 1;
  text-align: center;
}

.btn__icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
}

.btn__icon svg {
  width: 20px;
  height: 20px;
}

.btn--primary {
  color: var(--accent-text);
  background: var(--accent);
}

.btn--secondary {
  color: var(--text);
  background: none;
  box-shadow: inset 0 0 0 1px var(--surface-border);
}

.btn--block {
  display: flex;
  width: 100%;
}

/* Цена в CTA героя. По ширине меняется не оформление кнопки, а её состав:
   на 390 цены нет вовсе, с 960 она стоит после подписи через точку. Решение
   владельца 2026-08-19, OPEN-VIS-02. Скрыта по умолчанию, потому что базовой
   считается мобильная композиция.

   Разделитель — символ, а не рамка. В макете это точка; border-left давал в
   пилюле лишний вертикальный штрих, которого там нет. opacity убрана: замер
   1920 показал у подписи и у цены один цвет (#42487F против #434980 —
   разница в пределах сглаживания), то есть цена не приглушена. */
.btn__price {
  display: none;
  flex: 0 0 auto;
}

.btn--compact {
  min-height: var(--control-h);
  padding-inline: var(--space-m);
}

/* ============================================================ page dots */

.dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3xs);
}

.dots__item {
  width: 4px;
  height: 4px;
  padding: 0;
  background: var(--dot);
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: width 0.2s ease, background 0.2s ease;
}

.dots__item--active {
  width: 16px;
  background: var(--dot-active);
}

/* ================================================================== hero */

.hero {
  position: relative;
}

.hero__track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.hero__track::-webkit-scrollbar {
  display: none;
}

.hero__track:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.hero__slide {
  flex: 0 0 100%;
  min-width: 0;
  scroll-snap-align: start;
}

/* В макете фото — отдельный блок сверху, а текст лежит ПОД ним на светлом
   фоне со скруглёнными верхними углами. Раньше текст был поверх фото. */
.hero__media {
  height: 400px;
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-m);
  min-width: 0;
  margin-top: -24px;
  padding: var(--space-m) var(--gutter) 0;
  text-align: center;
  background: var(--bg);
  border-radius: var(--radius-m) var(--radius-m) 0 0;
}

.hero__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.hero__title {
  margin: 0;
  min-width: 0;
  /* В R9.2 Figma-title занимает до трёх строк. Фиксированная область не
     даёт CTA и поиску прыгать между короткими слайдами; полные данные
     остаются в DOM даже когда длинный заголовок визуально обрезан. */
  min-block-size: calc(3 * var(--lh-display));
  max-block-size: calc(3 * var(--lh-display));
  overflow: hidden;
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  font-weight: var(--fw-medium);
  color: var(--text);
}

/* Две смысловые части остаются в одном heading для assistive technology,
   но каждая начинает отдельную строку. Содержимое экранируется Django;
   HTML или классы в данных не становятся разметкой. */
.hero__title-part {
  display: block;
  overflow-wrap: anywhere;
}

.hero__title-accent {
  color: var(--hero-title-accent);
}

.hero__subtitle {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-light);
  color: var(--text-muted);
}

.hero__cta {
  width: 100%;
}

.hero .dots {
  margin-top: var(--space-s);
}

/* ================================================================ search */

/* Декоративная волна под полосой поиска. Растр перекрашивается маской, чтобы
   один файл работал в обеих темах. */
.search__waves {
  display: block;
  width: 100%;
  /* 28, а не 26: волна стоит в потоке и её высота входит в высоту .search,
     поэтому обязана быть кратной базе. Рисунок масштабируется маской по
     `contain`, пропорция сохраняется. */
  height: 28px;
  margin-top: var(--space-s);
  background: var(--wave-color);
  -webkit-mask: url("/static/partners__corona/img/waves-line.png") center / contain no-repeat;
  mask: url("/static/partners__corona/img/waves-line.png") center / contain no-repeat;
}


.search {
  /* горизонтальные поля задаёт общее правило контейнера */
  padding-block: var(--space-m);
}

.search__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  min-width: 0;
}

/* Figma fills this panel with a 40 % lavender gradient (#ced6fe -> #edf0ff). */
.search__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  min-width: 0;
  padding: var(--space-s);
  background: var(--panel-bg);
  border-radius: var(--radius-m);
}

.search__heading {
  font-size: var(--fs-subtitle);
  line-height: var(--lh-subtitle);
  font-weight: var(--fw-light);
}

/* Search page: the panel leads the page, so it also carries a hairline.
   Тенью, а не рамкой: рамка добавляла по 1 px сверху и снизу, полоса на
   search.html выходила 82 вместо 80, и волна с секцией под ней вставали мимо
   базовой сетки. Вид не меняется — inset-тень рисуется там же, где рамка. */
.search__form--expanded .search__panel {
  box-shadow: inset 0 0 0 1px var(--surface-border);
  backdrop-filter: blur(22.8px);
  -webkit-backdrop-filter: blur(22.8px);
}

.field {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--control-h);
  padding: var(--space-2xs) var(--space-s);
  /* в макете поля контурные, а не залитые */
  background: none;
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-pill);
}

.field__input {
  flex: 1;
  min-width: 0;
  padding: 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text);
  background: none;
  border: 0;
  appearance: none;
}

.field__input::placeholder {
  color: var(--text);
  opacity: 1;
}

.field__input:focus {
  outline: none;
}

.field:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.field:has([aria-invalid="true"]) {
  border-style: dashed;
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Числовое поле «Дней от/до»: значение вводится с клавиатуры, а не выбирается
   в оверлее, поэтому у него нет шеврона и нет readonly. */
.field--number .field__input {
  text-align: inherit;
}

/* Выключенный шаг каскада P. Пока не выбран аэропорт, «Тур» пуст и НЕ должен
   открываться: в нём нечего показывать. Гасится и поле, и его пикер —
   pointer-events снимает клик, aria-disabled сообщает то же скринридеру
   (сам input не disabled, иначе он выпал бы из порядка обхода и пользователь
   не понял бы, что поле вообще существует). */
.search__panel .field[data-disabled] {
  opacity: 0.45;
  pointer-events: none;
}

/* Видимый сброс каскада: смена «Откуда» стирает тур и обе даты. Плашка
   отличается от ошибки — это не ошибка пользователя, а следствие его выбора,
   поэтому нейтральный тон и своя полоса. */
.search__reset {
  flex: 1 0 100%;
  padding: var(--space-xs) var(--space-s);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--text-muted);
  background: var(--surface);
  border-inline-start: 4px solid var(--text-muted);
  border-radius: var(--radius-s);
}

.search__error {
  flex: 1 0 100%;
  padding: var(--space-xs) var(--space-s);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--text);
  background: var(--surface);
  border-inline-start: 4px solid var(--accent);
  border-radius: var(--radius-s);
}

/* Полоса переносится, когда в ней появляется сообщение во всю ширину. Плашка
   сброса каскада — второй такой случай после ошибки: без переноса её основа
   в 100 % сплющивала поля до одних шевронов. */
.search__panel:has(.search__error:not([hidden])),
.search__panel:has(.search__reset:not([hidden])) {
  flex-wrap: wrap;
}

[data-search-submit][data-search-loading] .btn__icon:first-child {
  width: 18px;
  height: 18px;
  border: 2px solid currentColor;
  border-inline-end-color: transparent;
  border-radius: 50%;
  animation: search-submit-spin 0.8s linear infinite;
}

[data-search-submit][data-search-loading] .btn__icon:first-child svg {
  display: none;
}

@keyframes search-submit-spin {
  to { transform: rotate(1turn); }
}

.field__icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  color: var(--text);
}

/* Ряд «Туры / Отели / Экскурсии / Отели + авиа» — отдельная строка НАД формой.
   Внутри формы он попадал бы в горизонтальный ряд полей на десктопе.

   В макете ряд отцентрован по форме, а не прижат влево. `safe center` — не
   украшательство: у .chips есть overflow-x, и обычный `center` при нехватке
   ширины срезал бы первую вкладку за левым краем без возможности доскроллить.
   `safe` в этом случае откатывается к выравниванию по началу. */
/* Ряд вкладок переменной длины: от одной до восьми, состав и порядок с
   сервера. При нехватке ширины он ПРОКРУЧИВАЕТСЯ, а не переносится и не жмёт
   подписи: перенос на второй ряд сдвинул бы полосу поиска вниз на величину,
   зависящую от числа вкладок, а сжатие подписей превратило бы «Экскурсионные
   туры» в нечитаемый огрызок. `safe center` центрирует ряд, пока он влезает,
   и прижимает к началу, когда не влезает, — без него первая вкладка уезжала
   бы за левый край и до неё нельзя было доскроллить. */
.search__tabs {
  justify-content: safe center;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  margin-bottom: var(--space-s);
}

.search__tabs::-webkit-scrollbar {
  display: none;
}

.search__tabs .chips__item {
  flex: 0 0 auto;
}

/* ================================================================= chips */

.chips {
  display: flex;
  gap: var(--space-m);
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.chips::-webkit-scrollbar {
  display: none;
}

.chips__item {
  flex: 0 0 auto;
  padding: var(--space-xs) 0;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-medium);
  /* в макете подборки набраны прописными с разрядкой */
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--tab-text);
  background: none;
  border: 0;
  white-space: nowrap;
  cursor: pointer;
}

/* Активное состояние — подчёркивание акцентом, а не залитая пилюля:
   так нарисовано и на мобильном, и на десктопе. */
/* Подчёркивание активной вкладки нарисовано тенью, а не рамкой. Рамка входила
   в высоту (12 + 20 + 12 + 2 = 46 при базе 4), и весь ряд вкладок вставал мимо
   сетки, а за ним и всё, что ниже по потоку. Тень не участвует в раскладке:
   ряд стал ровно 44, вид не изменился. Прозрачная рамка у неактивных вкладок
   больше не нужна — она резервировала место под то же подчёркивание. */
.chips__item--active {
  box-shadow: inset 0 -2px 0 var(--accent);
}

/* Chip row used directly under a section heading. */
.chips--section {
  margin-block: var(--space-s) var(--space-xs);
}

/* ================================================================= cards */

.cards {
  display: grid;
  transform: translateX(0);
  transition: transform 280ms ease;
  will-change: transform;
}

.cards--leave-left { transform: translateX(-100%); }
.cards--leave-right { transform: translateX(100%); }
.cards--enter-left,
.cards--enter-right { transition: none; }
.cards--enter-left { transform: translateX(100%); }
.cards--enter-right { transform: translateX(-100%); }
.cards--enter-active { transform: translateX(0); transition: transform 280ms ease; }

/* Production R11: tabpanels живут в одном viewport. Во время переключения
   обе панели абсолютны, а JS временно фиксирует высоту родителя — ряд не
   прыгает, даже если в подборках разное число карточек. */
.ct-card-panels {
  position: relative;
  overflow: hidden;
}

.ct-card-panel--animating {
  position: absolute;
  inset: 0;
  width: 100%;
}

.ct-collection-label {
  width: min(100% - 2 * var(--gutter), var(--container-max));
  margin: var(--space-s) auto var(--space-xs);
  color: var(--text-muted);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

/* Содержимое прижато к низу, а не разнесено по краям. Здесь стояло
   `space-between`, и бэйдж как первый ребёнок уезжал в верхний угол — в тот
   самый, где в макете водяной знак. Замер 1920 (карточка 320): знак 35..77 /
   1035..1063, бэйдж 1222..1245 вплотную над подписью, то есть оба у низа.
   `flex-end`, а не `margin-top: auto` у бэйджа: auto-отступ разрешается в
   произвольное число (75 и 95 на 390) и уводит вертикальный ритм с базы —
   это поймал qa:rhythm. Так же устроена .dest-card. */
.card {
  --card-padding: var(--space-xs);
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--space-s);
  aspect-ratio: 1;
  padding: var(--card-padding);
  overflow: hidden;
  color: var(--text-on-media);
  isolation: isolate;
}

/* В production карточка — одна ссылка целиком. Caption не имеет внешних
   отступов: стекло прижато к левому, правому и нижнему краям карточки,
   внутренний отступ остаётся системным --space-m. */
.ct-card__surface {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--card-padding);
  overflow: hidden;
  color: inherit;
  isolation: isolate;
}

a.ct-card__surface:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
}

.ct-card__surface .card__media {
  z-index: 0;
}

.ct-card__surface .card__mark,
.ct-card__surface .card__body {
  z-index: 1;
}

.ct-card__surface .card__body {
  align-self: stretch;
  margin: 0 calc(-1 * var(--card-padding)) calc(-1 * var(--card-padding));
  padding: var(--space-m);
  background: rgb(0 0 0 / 22%);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.ct-card__surface .ct-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs);
  margin-bottom: var(--space-2xs);
}

.ct-card__surface .card__title,
.ct-card__surface .card__meta {
  display: block;
}

.ct-card__surface .card__meta {
  display: flex;
}

.ct-card__surface .card__media img {
  transform: scale(1);
  transform-origin: center;
  transition: transform 280ms ease;
}

.ct-card__surface .card__media::after {
  transition: background 220ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .card:hover .card__media img {
    transform: scale(1.04);
  }

  .card:hover .card__media::after {
    background: linear-gradient(180deg, rgb(0 0 0 / 10%) 28%, rgb(0 0 0 / 70%) 100%);
  }
}

.card:focus-within .card__media img {
  transform: scale(1.04);
}

.card:focus-within .card__media::after {
  background: linear-gradient(180deg, rgb(0 0 0 / 10%) 28%, rgb(0 0 0 / 70%) 100%);
}

@supports not ((backdrop-filter: blur(1px))) {
  .ct-card__surface .card__body {
    background: rgb(0 0 0 / 72%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cards,
  .cards--enter-active,
  .ct-card__surface .card__media img,
  .ct-card__surface .card__media::after {
    transition-duration: 0.01ms;
  }

  .ct-card__surface .card__media img {
    transform: none !important;
  }
}

.card__media {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgb(0 0 0 / 5%) 40%, rgb(0 0 0 / 55%) 100%);
}

/* Бэйдж стоит НАД заголовком у нижнего края, а не в верхнем углу карточки.
   Замер макета на 1920 (карточка 320, первая в ряду): знак 35..77 по вертикали
   1035..1063 — то есть в верхнем углу, а бэйдж 1222..1245, вплотную над
   подписью. У карточки `justify-content: space-between`, и бэйдж как первый
   ребёнок уезжал наверх — в тот самый угол, где в макете водяной знак. Пока
   знака не было, это не бросалось в глаза; после его появления они наложились
   друг на друга. `margin-top: auto` отдаёт весь свободный ход бэйджу, и он
   вместе с .card__body прижимается вниз. */
.card__tag {
  align-self: flex-start;
  padding: var(--space-3xs) var(--space-xs);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--ct-white);
  background: rgb(255 255 255 / 20%);
  border-radius: var(--radius-pill);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* Цвет бэйджа кодирует тип подборки — так в макете. */
.card__tag--pair {
  color: var(--ct-black);
  background: var(--ct-gold-light);
}

.card__tag--group {
  background: var(--ct-violet);
}

.card__tag--paradise {
  background: var(--ct-teal);
  color: var(--ct-black);
}

.card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  min-width: 0;
}

.card__title {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-light);
}

.card__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2xs);
}

.card__price {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-medium);
}

.card__duration {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-light);
  color: var(--text-on-media-muted);
}

.cards-more {
  display: flex;
  justify-content: center;
  margin-top: var(--space-s);
}

/* ========================================================== destinations */

.destinations__grid {
  display: flex;
  gap: var(--space-2xs);
  margin-top: var(--space-s);
  padding-inline: var(--gutter);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  min-width: 0;
  -webkit-overflow-scrolling: touch;
}

.destinations__grid::-webkit-scrollbar {
  display: none;
}

.destinations__footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-s);
  margin-top: var(--space-s);
}

.dest-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--space-3xs);
  flex: 0 0 179px;
  height: 229px;
  padding: var(--space-s);
  overflow: hidden;
  color: var(--text-on-media);
  scroll-snap-align: start;
  border-radius: var(--radius-s);
  isolation: isolate;
}

.dest-card__media {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.dest-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dest-card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgb(0 0 0 / 5%) 30%, rgb(0 0 0 / 60%) 100%);
}

.dest-card__title {
  font-size: var(--fs-subtitle);
  line-height: var(--lh-subtitle);
  font-weight: var(--fw-light);
}

.dest-card__count {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--text-on-media-muted);
}

/* ================================================================= promo */

.promo {
  position: relative;
  width: min(100% - 2 * var(--gutter), 1144px);
  overflow: hidden;
  border-radius: var(--radius-m);
  margin-inline: auto;
}

.ct-promo__track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.ct-promo__track::-webkit-scrollbar { display: none; }

.ct-promo__banner {
  display: flex;
  flex: 0 0 100%;
  flex-direction: column;
  gap: var(--space-m);
  color: inherit;
  scroll-snap-align: start;
  text-decoration: none;
}

.ct-promo__dots {
  justify-content: center;
  padding: var(--space-xs);
}

.promo__media img {
  width: 100%;
  height: 260px;
  object-fit: cover;
}

.promo__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  padding: 0 var(--space-s) var(--space-s);
  text-align: center;
}

.promo__title {
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  font-weight: var(--fw-medium);
}

.promo__text {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-light);
  color: var(--text-muted);
}

/* Декор промо-блока из макета: золотая кавычка между заголовком и текстом,
   волна между текстом и кнопкой. Замер 1920: кавычка по центру колонки, волна
   ~198 px при ширине колонки ~405, высота ~25.
   Оба элемента декоративные и пустые — знак рисует CSS, поэтому он не попадает
   ни в выделение, ни в скринридер. Отрицательные margin убирают половину
   родительского `gap: --space-m`: в макете кавычка прижата к заголовку теснее,
   чем блоки друг к другу. */
.promo__quote {
  align-self: center;
  /* Кегль подобран по замеру самого знака, а не по кеглю текста: в макете
     кавычка ~25x20, при 40px глиф выходил 15 и читался мелкой. */
  font-size: 56px;
  line-height: 32px;
  font-weight: var(--fw-medium);
  color: var(--accent);
}

.promo__quote::before {
  content: "\201C";
}

/* Та же маска и тот же токен цвета, что у волны под поиском: файл один,
   растровый, и перекрашивается под тему.
   Но размер маски другой — `auto 100%`, а не `contain`. Файл длинный и узкий;
   при `contain` он вписывается по ШИРИНЕ, высота схлопывается, и вместо волны
   получается мелкая рябь. `auto 100%` масштабирует по высоте, сохраняя
   пропорцию волны, а лишнее обрезается по ширине — в макете у промо как раз
   меньше периодов, чем под поиском. */
.promo__waves {
  align-self: center;
  width: min(200px, 60%);
  height: 24px;
  background: var(--wave-color);
  -webkit-mask: url("/static/partners__corona/img/waves-line.png") center / auto 100% no-repeat;
  mask: url("/static/partners__corona/img/waves-line.png") center / auto 100% no-repeat;
}

/* Водяной знак на фото промо — в макете он в левом верхнем углу кадра, как и
   на карточках. Доли те же, что у .card__mark. */
.promo__mark {
  position: absolute;
  top: 10%;
  left: 10%;
  width: 64px;
  aspect-ratio: 1.663;
  background: url("/static/partners__corona/img/corona-mark.svg") center / contain no-repeat;
  opacity: 0.85;
  pointer-events: none;
}

.promo__media {
  position: relative;
}

/* «Хочу сюда» в макете тёмно-синяя в светлой теме и золотая в тёмной — та же
   роль, что у «Найти» в полосе поиска, поэтому и токен общий (--btn-strong-*),
   а не золотой --accent, как было. */
.promo__cta {
  align-self: center;
  color: var(--btn-strong-text);
  background: var(--btn-strong-bg);
}

/* ============================================================== telegram */

.telegram {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  padding-bottom: var(--space-m);
  overflow: hidden;
  text-align: center;
  border-radius: var(--radius-m);
  margin-inline: var(--gutter);
  background: var(--surface);
}

.telegram__media {
  display: grid;
  place-items: center;
  width: 100%;
  padding: var(--space-m) 0;
}

.telegram__media img {
  width: 316px;
  max-width: 100%;
  height: auto;
}

.telegram__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  padding-inline: var(--space-s);
}

.telegram__title {
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  font-weight: var(--fw-medium);
}

.telegram__text {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-light);
  color: var(--text-muted);
}

/* ================================================================ footer */

.footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  padding: var(--space-l) 0;
  color: var(--text-on-media-muted);
  background: var(--footer-bg);
  border-radius: var(--radius-m) var(--radius-m) 0 0;
}

.footer__top {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
  padding-inline: var(--gutter);
}

.footer__contacts {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.footer__address {
  font-size: var(--fs-subtitle);
  line-height: var(--lh-subtitle);
}

.footer__hours {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: rgb(255 255 255 / 40%);
}

.footer__phones {
  display: flex;
  gap: var(--space-m);
  margin-top: var(--space-l);
}

.footer__phone {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.footer__phone-number {
  font-size: var(--fs-subtitle);
  line-height: var(--lh-subtitle);
}

.footer__phone-region {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: rgb(255 255 255 / 40%);
}

.footer__email {
  margin-top: var(--space-l);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.footer__social {
  display: flex;
  gap: var(--space-l);
}

.footer__social a {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  color: var(--text-on-media-muted);
}

.footer__social svg {
  width: 24px;
  height: 24px;
}

.footer__legal {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-m) var(--gutter) 0;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: rgb(255 255 255 / 40%);
}

/* =============================================================== tab bar */

.tabbar {
  position: fixed;
  bottom: 0;
  left: 50%;
  z-index: 30;
  width: 100%;
  max-width: var(--page-max);
  transform: translateX(-50%);
  padding: var(--space-m) var(--space-m)
    calc(var(--space-xs) + env(safe-area-inset-bottom, 0px));
  background: var(--tabbar-bg);
}

.tabbar__list {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
}

.tabbar__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3xs);
  /* ширину задаёт подпись: при кегле 13 фиксированные 36 px её обрезали */
  min-width: 44px;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: rgb(255 255 255 / 60%);
}

/* Партнёрским пунктам иконки не придуманы, но подписи стоят на одной
   линии с пунктами, у которых иконка есть. */
.tabbar__link--text::before {
  content: "";
  display: block;
  width: 24px;
  height: 24px;
}

/* В панели это кнопка открытия dialog, а не ссылка. Сбрасываем UA-рамку
   и фон, сохраняя те же размеры и цвет подписи, что у соседних ссылок. */
button.tabbar__link {
  appearance: none;
  border: 0;
  padding: 0;
  background: none;
  font-family: inherit;
  text-align: center;
  cursor: pointer;
}

.tabbar__link--active {
  color: var(--ct-white);
}

.tabbar__link--active .tabbar__icon {
  color: var(--ct-gold-light);
}

.tabbar__icon {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
}

.tabbar__icon svg {
  width: 24px;
  height: 24px;
}

/* «меню» — просто полоски, отдельная иконка тут не нужна */
.tabbar__icon--bars {
  position: relative;
  width: 28px;
}

.tabbar__icon--bars::before,
.tabbar__icon--bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 28px;
  height: 1.3px;
  background: currentColor;
}

.tabbar__icon--bars::before {
  top: 8.35px;
}

.tabbar__icon--bars::after {
  top: 16.35px;
}

/* ========================================================== breakpoints */

@media (min-width: 600px) {
  :root {
    --gutter: var(--space-m);
    --cols: 6;
    --card-cols: 3;
  }

  .hero__cta {
    min-width: 280px;
  }

  /* 3 колонки, а не 4: при ширине оболочки 720 четыре карточки дают 180 px —
     меньше, чем 195 px на телефоне, то есть карточка мельчала с ростом экрана. */
  .footer__top {
    flex-direction: row;
    justify-content: space-between;
  }
}

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

   Границы диапазона обязаны быть обе. Правила ниже задают ширины, а
   десктопная композиция с 960 переопределяет не все из них: `max-width` у
   .search__form не перекрывается ничем, и открытый `min-width: 600px`
   протаскивал колонку 560 на все ширины вплоть до 2560. Это поймал
   check-layout.mjs. */
@media (min-width: 600px) and (max-width: 959.98px) {
  :root {
    /* Центральная колонка планшета: герой и поиск держат мобильную композицию
       в столбик, и без предела она растягивалась бы на все 959. Карточек это
       не касается — они в макете идут в край. */
    --tablet-col: 560px;
  }

  /* Здесь стоял `padding-top: 340px` у .hero__body — единственное правило
     героя в брейкпоинте 600, без комментария и с самого первого коммита
     разметки. Оно осталось от исходной раскладки, где фото лежало ФОНОМ под
     текстом и отступ сдвигал текст ниже кадра. Позже герой перестроили по
     макету — фото стало отдельным блоком СВЕРХУ, а текст лёг под ним, — и
     отступ превратился в пустое поле: на 768 между низом фото (400) и
     заголовком зияло 340 px, а поиск уезжал за первый экран. На мобильном
     правило не действовало, на десктопе перекрывалось раскладкой в две
     колонки, поэтому дефект жил ровно в этом диапазоне. */

  /* Фото растёт вместе с окном, иначе к 959 кадр 400 px выглядит приплюснутой
     полосой над пустой страницей. Прямая через 600 -> 400 и 959 -> 486
     стыкуется с --hero-media-h, который с 960 даёт ~594.
     Округление до базы обязательно: `24vw` даёт дробную высоту (440.32 на
     768), и дробь протекает вниз по всему потоку. Первая строка — запасная
     для браузеров без `round()`. */
  .hero__media {
    height: clamp(400px, calc(256px + 24vw), 520px);
    height: round(nearest, clamp(400px, calc(256px + 24vw), 520px), var(--baseline));
  }

  /* Ширина строки, а не блока: подложка героя со скруглёнными углами остаётся
     во всю колонку, как на мобильном, но центрированный текст на 900 px не
     растягивается на всю её ширину. 560 — та же мера, что и у полосы поиска
     ниже: на планшете обе колонки контента совпадают. */
  .hero__text,
  .hero__cta {
    max-width: var(--tablet-col);
  }

  .hero__cta {
    width: 100%;
  }

  /* Поиск — та же мобильная композиция (заголовок, вкладки, поля в столбик),
     но в центральной колонке. Без этого три поля во всю ширину 959 читаются
     как таблица, а не как форма. */
  .search__form,
  .search__tabs,
  .search__heading,
  .search__waves {
    width: 100%;
    max-width: var(--tablet-col);
    margin-inline: auto;
  }
}

/* ======================================================= 1024: десктоп */
/* Отдельная композиция из фреймов «1024», а не масштабированный мобильный:
   двухуровневая шапка, герой в две колонки, сетка 4x256, без таб-бара. */

@media (min-width: 960px) {
  :root {
    --page-max: 1440px;
    --container-max: 1144px;
    --gutter: 32px;
    --cols: 12;
    --card-cols: 4;

    /* Высота шапки из макета: она занимает 49..110, между двумя линейками. */
    --header-h: 62px;

    /* Осознанное исключение из «всё по --container-max»: полоса поиска в
       макете заметно уже контентной колонки и центрирована отдельно.
       Замеры фреймов (оторочка: левый край .. правый край, ширина):
         1024   107..915    808
         1440   150..1290  1140
         1920   391..1529  1138
       Фрейм «1440» экспортирован в 1.5x (файл 2160 px): левый край фото в нём
       лежит ровно на 1080, то есть на середине кадра, — по этому и проверен
       масштаб. Прежняя запись «111..970, 859» получена без деления на 1.5 и
       была неверной; из неё же выросла формула `max(812px, 60vw)`.
       Что показывают замеры: с 1440 полоса перестаёт расти. Её ширина,
       белая пилюля (920) и кнопка (148) на 1440 и на 1920 совпадают до
       пикселя — дизайнер переиспользовал один и тот же компонент. Растёт
       только поле вокруг него.
       Отсюда `min(1144px, max(812px, 80vw - 8px))`: прямая проходит ровно
       через 1024 -> 812 и 1440 -> 1144, дальше упор. 1144 — это не подгонка,
       а --container-max брейкпоинта 1024: шире контентной колонки того
       фрейма полоса не бывает нигде. */
    --search-max: min(1144px, max(812px, calc(80vw - 8px)));

    /* Отступ слева до колонки поиска. В макете текст героя начинается ровно
       там же, где полоса поиска, а не по краю страницы: замеры левого края
       заголовка — 107 на 1024 и 391 на 1920, при левом крае формы 107 и 394.
       Поэтому герой и поиск делят одну вертикаль.
       `100vw`, а не `100%`: значение потребляется внутри колонки шириной
       475 px, и процент там считался бы от неё. Именно на этом ломалось
       прежнее `max(--gutter, (100% - --container-max) / 2)` — оно всегда
       уходило в отрицательное и молча падало в gutter.
       Голый `100vw` тоже неверен: .page упирается в --page-max и центрируется,
       и выше 1920 полоса вставала по середине страницы, а текст героя — по
       середине окна (на 2560 форма 708, заголовок 1028). Считаем от ширины
       страницы, а не окна. */
    --hero-inset: max(
      var(--gutter),
      calc((min(100vw, var(--page-max)) - var(--search-max)) / 2)
    );

    /* Высота фото героя. Раньше её ничто не задавало: у .hero__media стояло
       `height: auto`, и высоту диктовала пропорция портретного файла
       939x1200 — то есть 1.5 ширины колонки. На 1920 это давало 1440 px
       против 754 в макете, и слайдер вместе с точками уезжал далеко за первый
       экран.
       `440px + 16vw` — прямая по двум замеренным фреймам: 604 на 1024
       (макет 609) и 747 на 1920 (макет 754).
       Второе слагаемое min() — страховка по высоте окна: 112 px занимают
       topbar и шапка вместе с обеими линейками, --space-m остаётся на просвет
       под точками. На высоком окне выигрывает макетная высота, на низком —
       окно, и герой всё равно помещается целиком. Правило действует только с
       960 px, где 100vh стабилен.

       Обе половины округляются до сетки 4 px. Без этого `16vw` давал дробную
       высоту (670.4 на 1440), и дробь протекала вниз по всему потоку: секции
       ниже вставали на 845.39, 1419.39 и так далее — мимо направляющих на
       любом масштабе. Первая строка оставлена как запасная для браузеров без
       `round()`: они её и применят, вторую пропустят как непонятную. */
    --hero-media-h: min(
      calc(440px + 16vw),
      calc(100vh - 112px - var(--space-m))
    );
    --hero-media-h: min(
      round(nearest, calc(440px + 16vw), var(--baseline)),
      round(nearest, calc(100vh - 112px - var(--space-m)), var(--baseline))
    );

    /* Насколько полоса поиска заходит на низ героя. 248 = 166 (низ формы от
       верха .search) + ~82 просвета до нижнего края фото; в макете под формой
       остаётся ~77 px кадра. Кратно базовой сетке 4. Тем же значением
       .hero__body резервирует место снизу, чтобы текст не попал под полосу. */
    --search-overlap: 248px;

    /* Роли те же пять — меняется только их размер на широком экране. */
    --fs-display: 40px;
    --lh-display: 44px;
    --fs-title: 28px;
    --lh-title: 36px;
    --fs-subtitle: 20px;
    --lh-subtitle: 28px;
  }

  .page {
    padding-bottom: 0;
  }

  .tabbar {
    display: none;
  }

  /* --- шапка --- */
  .topbar {
    display: block;
  }

  /* Линейка под шапкой (y=111 в макете). Нарисована здесь, а не как
     `border-bottom` у .header: в макете она идёт во всю ширину страницы —
     проверено замером колонки x=8, далеко за пределами контейнера, — а
     .header на десктопе центрирован и уже страницы. .page__main начинается
     ровно на этой границе и занимает всю ширину, поэтому линейка его.
     Она же возвращает контент на 112 и ставит всю страницу на сетку 4 px. */
  .page__main {
    border-top: 1px solid var(--surface-border);
  }

  .header {
    /* В потоке, но также containing block для выпадающих панелей меню. */
    position: relative;
    /* на десктопе зон четыре и логотип не центрируется — возвращаем поток */
    display: flex;
    justify-content: flex-start;
    gap: var(--space-m);
    /* 62 — замер макета (шапка занимает 49..110). Вынесено в токен, потому что
       на эту же высоту опирается разделитель у «Войти». */
    height: var(--header-h);
    /* шапке нужно ~960 px — на узком десктопе она шире контентной колонки,
       поэтому берёт всю ширину за вычетом полей */
    width: min(100% - 2 * var(--gutter), max(var(--container-max), 960px));
    margin-inline: auto;
    padding-inline: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .header__rates {
    display: none;
  }

  .header__logo {
    color: var(--text);
  }

  /* Между 1024 и 1920 логотип в макете растёт: замер экспортов даёт 114x25 и
     160x35. Прежние 130x29 не совпадали ни с одним из двух фреймов — они были
     серединой между ними. Линейная интерполяция попадает в оба: 25 при 1024,
     35 при 1920, дальше упор. Тот же приём, что у --search-max. */
  .header__logo svg {
    height: clamp(25px, 1.116vw + 13.57px, 35px);
  }

  /* Вертикальные разделители шапки — из макета. Замер 1920: тонкие линии на
     x=240 (после логотипа), 1643 (перед телефоном) и 1798 (перед «Войти»),
     каждая во всю высоту шапки, 49..110. Это те самые «разделители desktop
     header», которые числились недостающими в OPEN-VIS-03.

     Рисуются рамкой у самих зон, а не отдельными элементами: разметку менять не
     нужно, и линия не может разъехаться с границей зоны. `align-self: stretch`
     обязателен — у .header стоит `align-items: center`, и без него рамка была бы
     ростом с содержимое, а не во всю высоту шапки. Внутреннее выравнивание
     возвращается своим `align-items: center`. */
  .header__nav,
  .header__contact {
    align-self: stretch;
    border-left: 1px solid var(--surface-border);
  }

  .header__nav {
    display: flex;
    align-items: center;
    flex: 1;
    padding-left: var(--space-m);
  }

  .header__nav ul {
    display: flex;
    gap: var(--space-s);
    font-size: var(--fs-body);
    white-space: nowrap;
  }

  .header__nav a:hover {
    color: var(--accent);
  }

  /* Активный пункт подчёркнут золотом — так в макете под «Турами». Тем же
     приёмом, что и активная вкладка поиска: тень не входит в высоту и не
     сдвигает строку с базовой сетки. */
  .header__nav a[aria-current="page"] {
    box-shadow: inset 0 -2px 0 var(--accent);
  }

  .header__contact {
    display: flex;
    align-items: center;
    padding-left: var(--space-m);
  }

  .header__phone {
    display: inline-block;
    font-size: var(--fs-subtitle);
    line-height: var(--lh-subtitle);
    color: var(--text);
    white-space: nowrap;
  }

  /* На десктопе «Войти» — подчёркнутая надпись, а не пилюля: так в макете
     (замер 1920, справа от разделителя на 1798). Пилюля остаётся мобильной
     композицией, там она поверх фото и залита полупрозрачным белым.
     Подчёркивание тенью, а не рамкой: не входит в высоту и не сдвигает строку
     с базовой сетки — тем же приёмом, что у активного пункта меню и вкладок
     поиска.
     Разделитель слева нарисован псевдоэлементом: он должен идти во всю высоту
     шапки, а надпись ниже. */
  .header__login {
    position: relative;
    margin-left: var(--space-m);
    padding: 0;
    color: var(--text);
    background: none;
    border-radius: 0;
    box-shadow: inset 0 -1px 0 currentColor;
  }

  .header__login::before {
    content: "";
    position: absolute;
    top: 50%;
    left: calc(-1 * var(--space-m));
    width: 1px;
    height: var(--header-h);
    background: var(--surface-border);
    transform: translateY(-50%);
  }

  /* --- герой: текст слева, фото справа ---
     Каждый слайд сам по себе двухколоночный: слева текстовая колонка на
     светлом фоне, справа фото, уходящее за правый край страницы. */
  .hero__slide {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
    min-height: 520px;
  }

  .hero__media {
    order: 2;
    height: var(--hero-media-h);
    min-height: 0;
  }

  .hero__body {
    order: 1;
    /* Прижат к верху, а не центрирован. При центрировании на невысоком герое
       (1024, короткое окно) текст сползал в полосу наложенного поиска, и поле
       «Страна» перекрывало CTA. Резервировать место снизу нельзя: колонка от
       этого растёт и герой перестаёт помещаться в экран. */
    justify-content: flex-start;
    align-items: flex-start;
    gap: var(--space-m);
    margin-top: 0;
    /* R9.2: справа padding не нужен — ширину ограничивает .hero__text.
       Прежние 40 px переносили «Полное погружение» на третью accent-строку
       и визуально выталкивали обычную часть из трёхстрочного окна. */
    padding: var(--space-l) 0 var(--space-xl) var(--hero-inset);
    text-align: left;
    background: none;
    border-radius: 0;
  }

  .hero__text {
    /* R9.2/Figma: левая колонка 1920 идёт от x=388 до seam x=960.
       Системная мера 560 px оставляет accent в двух строках, а обычную
       часть — видимой третьей строкой; прежние 475 px съедали её clamp'ом. */
    max-width: 560px;
  }

  /* CTA героя с 960 — контурная пилюля с ценой и без иконки; до 960 она
     залитая, с иконкой и без цены. Это не смена стиля по ширине, а смена
     состава кнопки, и владелец подтвердил её 2026-08-19 (OPEN-VIS-02).
     Замер экспортов: пилюля 242x48 и в светлой, и в тёмной теме, обводка
     1 px, подпись и цена одного цвета.

     min-width здесь — не подгонка под фрейм, а мера против скачков в карусели.
     В макете кнопка 209x40 на 1024 и 242x48 на 1920, то есть у неё своя шкала
     кегля; у нас кегль один на всю страницу (принято в OPEN-DESIGN-01), и без
     порога ширина считалась бы от текста, а цены у слайдов разные — замер дал
     233, 233, 241 и 234 на 1024. Кнопка меняла бы размер при перелистывании.
     Порог 240 делает все четыре одинаковыми; расхождение с фреймом 1024 по
     ширине — та же принятая цена гибрида, что и в OPEN-LAYOUT-01.

     justify-content и flex у подписи переопределяются, потому что базовая
     .btn растягивает подпись (flex: 1) и разносит содержимое по краям — это
     верно для кнопки с иконкой, но здесь иконки нет, и «Забронировать · цена»
     в макете стоит одним блоком по центру пилюли: замер 1920 даёт центр
     текста 509 при центре пилюли 509. gap снимается, чтобы точку окружали
     одинаковые отступы с обеих сторон, а не gap слева и padding справа. */
  .hero__cta {
    width: auto;
    min-width: 240px;
    justify-content: center;
    gap: 0;
    color: var(--text);
    background: none;
    box-shadow: inset 0 0 0 1px var(--btn-outline-border);
  }

  .hero__cta .btn__icon {
    display: none;
  }

  .hero__cta .btn__label {
    flex: 0 0 auto;
    text-align: left;
  }

  .hero__cta .btn__price {
    display: inline;
  }

  .hero__cta .btn__price::before {
    content: "·";
    padding-inline: var(--space-3xs);
  }

  /* Точки берут тот же отступ, что и текст. Раньше здесь стояло то же
     выражение с `100%`, но считалось оно от .hero (полная ширина), а у
     .hero__body — от колонки 475 px. Поэтому на 1920 точки стояли на 160, а
     заголовок на 48: два разных значения из одной формулы. */
  .hero .dots {
    position: absolute;
    bottom: var(--space-m);
    left: var(--hero-inset);
    margin-top: 0;
  }

  /* --- поиск: горизонтальная строка, наложенная на низ героя --- */
  /* В макете полоса поиска лежит ПОВЕРХ фото у его нижнего края, а не идёт
     отдельной секцией ниже: на 1920 фото 0..753, форма 600..676, то есть под
     формой остаётся ещё ~77 px фото. Отрицательный margin поднимает блок на
     эту величину; 248 = 166 (низ формы от верха .search) + ~82 просвета, и
     кратно базовой сетке 4. Только с 960 px: на мобильном макет показывает
     поиск отдельным блоком под героем. */
  /* Селектор с .page__main обязателен: ниже в этом же брейкпоинте ещё раз
     объявлен `.page__main > * + *` с margin-top: --space-l, и при равной
     специфичности он бы перебил простой .search. */
  .page__main > .search {
    position: relative;
    z-index: 2;
    margin-top: calc(-1 * var(--search-overlap));
    /* Отрицательный margin поднимает не только полосу, но и весь поток за ней:
       секция «Рекомендуемые туры» уезжала вверх на те же 248 и её заголовок
       залезал под нижний край фото (это и поймал qa:responsive). Возвращаем
       следующему блоку нормальный просвет под фотографией. */
    margin-bottom: var(--space-l);
  }

  /* В макете форма — одна полоса 812x76: поля и кнопка в один ряд, кнопка
     СПРАВА (так во всех эталонных экспортах: 1024 и 1920 «Туры», 1440 «Отели»).
     Здесь стоял `row-reverse` с комментарием «на главной кнопка идёт первой в
     DOM». Это неверно: в обоих файлах первым идёт .search__panel, кнопка
     вторая, поэтому row-reverse уносил её влево — зеркально макету. На
     search.html ошибку гасил `.search__form--expanded { flex-direction: row }`,
     из-за чего расхождение видно было только на главной. */
  /* Полоса — ОДНА белая пилюля, внутри которой поля разделены тонкими
     линейками, а кнопка «Найти» лежит там же у правого края. Так во всех
     эталонных экспортах, включая тёмную тему: замерено rgb(255,255,255) у
     подложки и тёмный текст поверх неё на 1440 dark.
     Отдельные контурные поля — это МОБИЛЬНЫЙ вид макета; на десктопе их
     контуры и радиусы снимаются ниже. */
  /* Внешний слой — полупрозрачная оторочка. Внутри неё белая полоса с полями
     и кнопка submit. Замеры макета на 1920: оторочка 391..1529, белая полоса
     414..1334, кнопка 1358..1506; по вертикали оторочка 584..664, белая
     полоса 600..648. Отсюда padding 16/24 и высота поля 48. */
  .search__form {
    flex-direction: row;
    align-items: center;
    gap: var(--space-m);
    padding: var(--space-s) var(--space-m);
    color: var(--search-bar-text);
    background: var(--search-ring-bg);
    border-radius: var(--radius-pill);
  }

  /* Ширину ограничивает --search-max у обеих полос — и компактной, и
     раскрытой. Исключение для раскрытой стояло на утверждении «для неё нет
     эталонного экспорта»; экспорты есть: 1024-search-*, 1440-search-*,
     1920-search-* — это тот же герой с той же полосой, замеры совпадают с
     главной до пикселя (1920: оторочка 391..1529, пилюля 414..1334, кнопка
     1358..1505). Без ограничения search.html растягивал полосу на всю
     контентную колонку: 1360 на 1440 и 1600 на 1920 при макетных 1140/1138,
     причём она начиналась от поля страницы, а не от вертикали текста
     слайдера. */
  .search__form {
    width: min(100%, var(--search-max));
    margin-inline: auto;
  }

  /* «Дополнительно» на десктопе — под полосой, а не внутри неё.
     В эталонных экспортах его нет вовсе, и раньше он был скрыт на десктопе.
     Решение владельца 2026-08-09: контрол нужен и здесь, потому что на
     coronatours.ru у вкладки «Отели» есть «Additional filters».
     Внутрь пилюли он не помещается — на 1024 съедал бы 217 px из 620 и
     обрезал плейсхолдеры соседей. В проде он тоже стоит ПОД строкой поиска
     (`#hotel-search-form__additional_title` над контейнером
     `#hotel-search-form-top-panel-additional`), так что место совпадает.
     Абсолютное позиционирование, чтобы не влиять на раскладку полей. */
  .search__form {
    position: relative;
  }

  /* Компактный контрол внутри пилюли, перед «Найти». Отдельной плашкой под
     полосой он читался как посторонний элемент, а подпись целиком (217 px)
     внутрь не помещается — поэтому остаётся иконка, а название уходит в
     доступное имя. На мобильном кнопка остаётся полной, с подписью. */
  .search__form [data-extras] {
    flex: 0 0 auto;
    width: auto;
    min-width: 0;
    padding-inline: var(--space-s);
    color: var(--search-bar-text);
    background: none;
    border-radius: 0;
    box-shadow: none;
    border-left: 1px solid var(--search-bar-divider);
  }

  /* Подпись прячется ВИЗУАЛЬНО, а не через display: none — иначе у кнопки не
     остаётся доступного имени, ведь обе иконки aria-hidden. */
  .search__form [data-extras] .btn__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* второй шеврон избыточен рядом с иконкой фильтров */
  .search__form [data-extras] .btn__icon + .btn__label + .btn__icon {
    display: none;
  }


  /* Белая полоса с полями. stretch — чтобы тонкие разделители между полями
     шли на всю её высоту, как в макете. */
  .search__panel {
    flex: 1;
    flex-direction: row;
    align-items: stretch;
    gap: 0;
    min-width: 0;
    min-height: 48px;
    padding: 0;
    background: var(--search-panel-bg);
    border-radius: var(--radius-pill);
  }

  /* Ряд вкладок лежит поверх фото и пересекает его левую границу: часть
     подписей оказывается на светлом фоне страницы, часть — на кадре.
     Матовая подложка по ширине содержимого решает это одним цветом.
     Только с 960 px: на мобильном ряд не над фото и умеет скроллиться, а
     `width: max-content` сломал бы прокрутку. */
  /* Ряд вкладок лежит прямо на фоне, без подложки — так в макете.
     Ряд центрирован, а фото героя начинается ровно на середине страницы,
     поэтому левая половина подписей всегда на светлом фоне, правая — на
     кадре. В макете они там просто перекрашены в белый вручную.
     Ряд центрирован, значит граница кадра приходится ровно на его середину,
     и то же самое выражается градиентом с жёсткой остановкой на 50 %,
     обрезанным по тексту. Получается ровно поведение макета и без подложки,
     на которую жаловались. Подпись, попавшая на саму границу, окажется
     двухцветной — так же, как было бы и в макете.
     В тёмной теме подписи белые с обеих сторон, градиент не нужен. */
  .search__tabs {
    width: max-content;
    max-width: 100%;
    margin-inline: auto;
  }

  /* Градиент привязан к вьюпорту (`fixed`), а не к самому чипу. Иначе переход
     тёмное/белое повторялся бы внутри каждой подписи, и «ТУРЫ» выходило бы
     двухцветным. С привязкой к вьюпорту жёсткая остановка на 50 % попадает
     ровно на левый край фото героя, и подписи красятся так же, как в макете,
     на любой ширине. Положить градиент на весь ряд нельзя: <button> не
     участвует в text-clip родителя. */
  .search__tabs .chips__item {
    background: var(--search-tabs-ink) fixed;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }

  /* Поля без собственных контуров: границей служит линейка слева у каждого,
     кроме первого видимого. `:first-of-type` не годится — состав меняется по
     режиму, и первым видимым может оказаться любое поле. */
  .search__panel .field {
    flex: 1 1 0;
    min-width: 0;
    padding-inline: var(--space-m);
    border: 0;
    border-radius: 0;
  }

  /* У «Туров с перелётом» шесть полей плюс «Дополнительно» против четырёх у
     «Отелей». При тех же 24 px по бокам на подпись остаётся ~69 px, и
     «Откуда» превращалось в «Откуд», а «Туристы» в «Турис». Отступы ужимаются
     только в этой композиции: у остальных вкладок полей меньше и ужимать
     нечего. */
  [data-search-mode-active="package-tours"] .search__panel .field {
    padding-inline: var(--space-xs);
  }

  .search__panel .field ~ .field:not([hidden]) {
    border-left: 1px solid var(--search-bar-divider);
  }

  .search__panel .field__input,
  .search__panel .field__icon {
    color: var(--search-bar-text);
  }

  .search__panel .field__input::placeholder {
    color: var(--search-bar-text-muted);
  }

  .search__panel .btn {
    flex: 0 0 auto;
    width: auto;
  }

  /* Кнопка внутри оторочки, справа от белой полосы. Цвет свой, не --accent:
     в светлой теме макета она тёмно-синяя, в тёмной золотая. */
  .search__form > .btn {
    flex: 0 0 auto;
    width: auto;
    /* Ширина по макету: 122 на 1024 и 148 на 1920. Прежние 180/242 остались
       от раскладки, где кнопка стояла снаружи полосы; внутри оторочки они
       съедали белую полосу — на 1920 она выходила 838 вместо 920.
       Верхний предел 148: с 1440 полоса замирает, и кнопка внутри неё тоже —
       в макете она 148 и на 1440, и на 1920. Без предела 2.5vw уводили её на
       164 к 2560, съедая белую полосу. */
    min-width: min(148px, calc(100px + 2.5vw));
    min-height: 48px;
    color: var(--search-submit-text);
    background: var(--search-submit-bg);
  }

  /* Декоративная волна под полосой поиска.
     Раньше у неё не было ни одного правила: картинка вставала сразу за формой
     (зазор 0) и по левому краю .search, а не под самой полосой.
     В макете на 1920 форма кончается на 676, волна идёт 694..724 и по ширине
     примерно две трети полосы, примерно по её центру.
     Замер по горизонтали на 1920: волна 546..1295, то есть 750 при полосе
     1138 — ровно две трети.
     Цвет в макете зависит от темы: в светлой волна индиговая, в тёмной
     золотая. Файл один и растровый, поэтому перекрашиваем маской — форма
     сохраняется, цвет берётся из токена.
     Две трети считаются от полосы, а не от секции: `66%` брались от .search
     во всю контентную колонку и после того, как полоса перестала расти,
     давали 1056 при полосе 1144 — почти во всю её ширину. */
  .search__waves {
    width: min(100%, calc(0.66 * var(--search-max)));
    margin-inline: auto;
  }

  .search__heading,
  .search__panel .chips {
    display: none;
  }

  /* --- секции --- */
  .chips--section {
    flex-wrap: wrap;
    overflow: visible;
  }


  /* В десктопных фреймах заголовок секции — центрированный блок уже
     контейнера. В макете это 596 из 812 и 838 из 1144 — в обоих случаях
     примерно 73 %, поэтому доля, а не фиксированные значения. */
  .section-head {
    max-width: 73%;
    text-align: center;
  }

  .chips--section,
  .cards-more {
    justify-content: center;
  }

  .page__main > * + * {
    margin-top: var(--space-l);
  }

  /* --- карточки: 4 в ряд по 256 --- */
  .card {
    --card-padding: var(--space-m);
  }

  .card__title {
    font-size: var(--fs-body);
    line-height: var(--lh-body);
  }

  /* --- промо: изображение и текст в строку --- */
  /* В макете текст слева, фото справа. В разметке .promo__media идёт первым —
     так надо мобильному, где фото сверху. `row-reverse` разворачивает только
     визуальный порядок и не трогает DOM: на мобильном порядок остаётся
     «фото, текст», на десктопе становится «текст, фото». */
  .promo {
    width: min(100% - 2 * var(--gutter), 1144px);
  }

  .ct-promo__banner {
    flex-direction: row-reverse;
    align-items: center;
    gap: var(--space-l);
  }

  .ct-promo__banner .promo__media,
  .ct-promo__banner .promo__body {
    flex: 1 1 50%;
  }

  .promo__media img {
    height: 324px;
  }

  /* Колонка центрирована и на десктопе — так в макете. Здесь стояло
     `text-align: left`, из-за чего заголовок и кнопка выключались влево, а
     декор оставался по центру. */
  .promo__body {
    padding: 0;
  }

  /* --- направления --- */
  .destinations__scroller {
    width: min(100% - 2 * var(--gutter), var(--container-max));
    margin-inline: auto;
    padding-inline: 0;
    gap: var(--space-xs);
  }

  .dest-card {
    flex: 1 1 0;
    height: 340px;
  }

  /* --- telegram --- */
  .telegram {
    flex-direction: row;
    align-items: center;
    gap: var(--space-l);
    padding: var(--space-m) var(--space-l);
    text-align: left;
  }

  .telegram__media {
    flex: 0 0 260px;
    padding: 0;
  }

  .telegram__body {
    padding-inline: 0;
  }

  /* --- футер --- */
  .footer {
    padding-inline: max(var(--gutter), calc((100% - var(--container-max)) / 2));
  }

  .footer__top {
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-start;
    padding-inline: 0;
  }

  .footer__legal {
    flex-direction: row;
    justify-content: space-between;
    padding-inline: 0;
  }
}

@media (min-width: 1400px) {
  :root {
    --page-max: 1680px;
    --container-max: 1360px;
    --gutter: var(--space-l);
    --fs-display: 56px;
    --lh-display: 64px;
    --fs-title: 32px;
    --lh-title: 40px;
    --control-h: 48px;
  }

  .hero__slide {
    min-height: 620px;
  }

  /* Ширину держит .hero__text, а не сама колонка: с border-box фиксированная
     ширина съедалась бы отступом --hero-inset.
     Верхний отступ здесь раньше был 128 px — под герой, высота которого ничем
     не ограничивалась. После того как фото получило макетную высоту и
     страховку по окну, эти 128 px опускали текст прямо в полосу поиска: на
     1440x780 CTA пересекался и с формой, и с рядом вкладок. Отступ задаётся
     один раз в брейкпоинте 960 и равен --space-l. */

  .hero__cta {
    min-width: 242px;
  }

  .promo,
  .telegram {
    gap: 56px; /* в макете 53 — не кратно базе */
  }

  .promo__media img {
    height: 418px;
  }

  .dest-card {
    height: 420px;
  }

  .footer {
    padding-block: 64px;
  }
}

/* ======================================================== 1920: широкий */
/* Во фрейме «1920» колонка контента остаётся 1144 — растут только поля.
   Реально меняются две вещи: карточек в ряду 5 (384 px) и крупнее заголовки. */

@media (min-width: 1800px) {
  :root {
    --page-max: 1920px;
    --container-max: 1600px;
    --gutter: 48px;
    --card-cols: 6;
  }

  .destinations__scroller {
    max-width: 1440px;
  }

  .footer {
    padding-block: 80px;
  }
}

/* ====================================================== переключение темы */
/* Токены не анимируются сами по себе: custom property меняет значение одним
   кадром, и переключатель перекрашивал страницу вспышкой во весь экран —
   особенно резко при уходе в тёмную, где меняется и фон, и весь текст.

   Два пути, ровно один из них работает за переключение (выбор в theme.js):
   круговая развёртка там, где есть View Transitions, и кросс-фейд по
   свойствам там, где их нет. Оба привязаны к состоянию, которое ставит JS
   на время переключения, а не висят на элементах постоянно. Это не
   осторожность, а необходимость: глобальный `transition: background-color`
   отработал бы и на первой отрисовке, и страница въезжала бы в свою тему из
   цвета по умолчанию при каждой загрузке. */

/* --- путь 1: круговая развёртка --- */
/* Снимок старой темы остаётся на месте, новая раскрывается кругом из самой
   кнопки. Кросс-фейдится готовая картинка целиком, поэтому плавно проходят и
   градиенты (--panel-bg, --search-tabs-ink), и фотографии — свойствами этого
   не добиться: background-image между двумя градиентами браузеры
   интерполируют не всегда, а кадры героя не участвуют вовсе. */
::view-transition-old(root),
::view-transition-new(root) {
  /* Штатный кросс-фейд выключен: обе половины стоят неподвижно, всё движение
     даёт clip-path у верхней. Без mix-blend-mode: normal слои смешиваются и
     на кромке круга проступает третий цвет. */
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-new(root) {
  z-index: 1;
}

::view-transition-old(root) {
  z-index: 0;
}

/* --- путь 2: кросс-фейд по свойствам --- */
:root {
  /* Длительность живёт здесь, рядом с правилом. theme.js читает её, чтобы
     снять атрибут ровно по окончании, а не по своей второй константе,
     которая рано или поздно разошлась бы с этой. */
  --theme-fade: 280ms;
}

/* `!important` здесь осознан: правило обязано перебить собственные переходы
   элементов (`transition: transform .15s` и подобные), иначе они выиграли бы
   по каскаду и часть страницы перекрашивалась бы рывком. Действует только
   пока JS держит атрибут — то есть эти самые 280 мс. */
:root[data-ct-theme-anim] *,
:root[data-ct-theme-anim] *::before,
:root[data-ct-theme-anim] *::after {
  transition:
    background-color var(--theme-fade) ease,
    border-color var(--theme-fade) ease,
    color var(--theme-fade) ease,
    fill var(--theme-fade) ease,
    box-shadow var(--theme-fade) ease !important;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Псевдоэлементы вида ::view-transition-* не подпадают под `*` выше, и
     погасить развёртку отсюда нельзя. theme.js проверяет reduced motion сам и
     в этом режиме меняет тему без анимации вовсе; правило ниже — страховка на
     случай, если развёртку запустит что-то помимо него. */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* «Без перелёта» — отдельный ряд НАД полосой, как в боевом шаблоне: там это
   свой `.row`, скрытый целиком, пока у партнёра нет туров без перелёта.
   Внутрь пилюли его класть нельзя: седьмой контрол в ряду резал подписи
   соседей до «Откуд» и «Турист», а полоса рассчитана на четыре поля. */
/* Ряд НАД полосой, а не элемент внутри неё: у формы на десктопе flex-строка,
   и без явной основы в 100 % флаг вставал слева от пилюли. */
.search__flag {
  flex: 1 0 100%;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-bottom: var(--space-2xs);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--text-on-media);
  cursor: pointer;
}

.search__flag-box {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--accent);
}

@media (min-width: 960px) {
  .search__flag {
    color: var(--text);
    margin-bottom: 0;
  }

  /* Форма переносится, только когда флаг показан: у «Отелей» и TR его нет,
     и лишний перенос сдвигал бы полосу вниз на пустом месте. */
.search__form:has(.search__flag:not([hidden])) {
    flex-wrap: wrap;
    row-gap: var(--space-2xs);
  }
}

/* ================================================================ новости R9 */

.news {
  width: 100%;
  margin-inline: auto;
}

.news__panel {
  padding: var(--space-m) var(--space-s);
  background: var(--news-panel-bg);
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-m);
}

.news__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2xs) var(--space-m);
}

.news__title {
  margin: 0 0 var(--space-m);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
}

.news__all {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--accent);
  white-space: nowrap;
}

.news__grid {
  display: grid;
  gap: var(--space-s);
  margin: 0;
  padding: 0;
  list-style: none;
}

.news__item {
  min-width: 0;
}

/* Вся карточка — одна ссылка. Квадрат фото определяет высоту ряда. */
.news-card {
  --news-media: 176px;
  display: grid;
  grid-template-columns: var(--news-media) minmax(0, 1fr);
  height: 100%;
  overflow: hidden;
  color: var(--text);
  text-decoration: none;
  border: 1px solid var(--surface-border);
  border-radius: calc(1.5 * var(--radius-s));
  transition: border-color 0.2s ease;
}

.news-card__media {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--panel-bg);
}

.news-card__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

.news-card__media[data-empty]::before {
  content: "";
  position: absolute;
  inset: 30%;
  background: currentColor;
  opacity: 0.2;
  -webkit-mask: url("../img/corona-mark.svg") center / contain no-repeat;
  mask: url("../img/corona-mark.svg") center / contain no-repeat;
}

.news-card__tags {
  position: absolute;
  top: var(--space-xs);
  right: var(--space-xs);
  left: var(--space-xs);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs);
}

.news-card__tag {
  max-width: 100%;
  padding: 0 var(--space-2xs);
  overflow: hidden;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-medium);
  color: var(--ct-white);
  white-space: nowrap;
  text-overflow: ellipsis;
  border-radius: var(--radius-s);
}

.news-card__tag--country,
.news-card__tag--more {
  background: var(--ct-tag-purple);
}

.news-card__tag--more {
  flex-shrink: 0;
}

.news-card__tag--type {
  background: var(--ct-gold-mid);
}

.news-card__body {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-2xs);
  min-width: 0;
  padding: var(--space-s);
}

.news-card__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  min-width: 0;
}

.news-card__title,
.news-card__preview {
  display: -webkit-box;
  margin: 0;
  overflow: hidden;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
}

.news-card__title {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-medium);
  -webkit-line-clamp: 3;
  line-clamp: 3;
  transition: color 0.2s ease;
}

.news-card__preview {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-light);
  color: var(--news-preview);
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.news-card__date {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-light);
  color: var(--news-date);
}

@media (hover: hover) {
  .news-card:hover {
    border-color: var(--accent);
  }

  .news-card:hover .news-card__title {
    color: var(--accent);
  }

  .news-card:hover .news-card__media img {
    transform: scale(1.04);
  }
}

.news-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .news-card,
  .news-card__title,
  .news-card__media img {
    transition: none;
  }
}

@media (min-width: 600px) {
  .news {
    width: min(100% - 2 * var(--gutter), var(--news-max));
  }

  .news-card {
    --news-media: 186px;
  }
}

@media (min-width: 960px) {
  .news__panel {
    padding: calc(2 * var(--space-s)) var(--space-m);
  }

  .news__title {
    margin-bottom: calc(2 * var(--space-s));
    font-size: var(--fs-subtitle);
    line-height: var(--lh-subtitle);
  }

  .news__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .news-card {
    --news-media: minmax(0, 49%);
    border-radius: var(--radius-m);
  }

  .news-card__body {
    padding: var(--space-m) var(--space-s);
  }

  .news-card__title {
    font-size: var(--fs-body);
    line-height: var(--lh-body);
  }

  .news-card__tags {
    top: var(--sheet-pad);
    right: var(--sheet-pad);
    left: var(--sheet-pad);
  }
}

@media (min-width: 1400px) {
  .news-card__title {
    font-size: var(--fs-subtitle);
    line-height: var(--lh-subtitle);
  }

  .news-card__preview {
    -webkit-line-clamp: 3;
    line-clamp: 3;
  }
}
