/* ==========================================================================
   NordTrip — Arctic Visual System
   Премиальный арктический гид: midnight navy + Баренцево море + aurora +
   киты + арктическая кухня. Ограниченная палитра дизайн-токенов — никаких
   случайных hueFor()-цветов (см. спринт Arctic Visual Redesign Implementation).
   ========================================================================== */

:root {
  --bg-main: #060d16;
  --bg-main-2: #0c1a2b;
  --bg-card: #101d2e;
  --bg-card-glass: rgba(255, 255, 255, .045);
  --bg-card-glass-strong: rgba(255, 255, 255, .075);

  /* Текстовые токены — фиксированные значения, НЕ завязанные на
     var(--tg-theme-text-color/--tg-theme-hint-color). Раньше эти токены
     были определены как var(--tg-theme-*, fallback) — Telegram-клиент
     подставляет туда цвет ИЗ ТЕКУЩЕЙ ТЕМЫ ПОЛЬЗОВАТЕЛЯ (светлой или тёмной),
     и на светлой теме Telegram это давало почти чёрный текст поверх нашего
     фиксированного тёмного арктического фона — источник бага "чёрный текст
     на тёмном фоне" (см. спринт Arctic Visual Redesign Fix). Тема приложения
     сознательно не следует теме Telegram, поэтому текстовые токены тоже
     должны быть фиксированными. */
  --text-main: #eef4fa;       /* заголовки, названия, основной текст */
  --text-soft: #b9cbdd;       /* второстепенный "телесный" текст: описания, подзаголовки */
  --text-muted: #8ea4ba;      /* метки, подписи, второстепенные метаданные */
  --text-dim: #5b7086;        /* disabled/locked — приглушённый, но не чёрный */
  --text-on-accent: #04141a;  /* тёмный текст поверх ярких аврора/море/янтарь кнопок */

  --accent-aurora: #34e0a1;
  --accent-sea: #2f8fd1;
  --accent-ice: #8fd8f5;
  --accent-amber: #f2b64d;   /* тёплый ориентир: XP, рейтинг ★, награда, точка маршрута — точечно */
  --danger: #ff6b6b;         /* Coral — СЕМАНТИКА ошибки/опасного, НЕ бренд-акцент (Visual System V1 §4) */
  /* Типографика V1 (§5): Lora — display/заголовки, Inter — UI/тело. Самохостинг ниже. */
  --font-display: "Lora", Georgia, "Times New Roman", serif;
  --font-ui: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Шкала радиусов (Спринт 1) */
  --r-sm: 10px; --r-md: 14px; --r-lg: 18px; --r-xl: 22px;
  --border-soft: rgba(255, 255, 255, .09);
  --shadow-soft: 0 10px 28px rgba(2, 6, 14, .45);
  --radius-card: 20px;
  --radius-pill: 999px;

  /* Motion tokens (спринт UI/Motion/Gamification audit, skill
     motion-design-for-mini-apps): единая система длительностей/easing.
     Новые анимации строить ТОЛЬКО на этих токенах; миграция старых
     транзишенов — отдельным спринтом (см. tmp/ui-motion-gamification-audit). */
  --motion-fast: 120ms;   /* мгновенный tap-фидбек */
  --motion-ui: 200ms;     /* малые UI-переходы */
  --motion-sheet: 280ms;  /* sheets/карточки */
  --motion-stamp: 900ms;  /* «дорогой» момент печати — единственный длинный */
  --ease-out-premium: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-in-exit: cubic-bezier(0.4, 0, 1, 1);
  /* Канон-именованные алиасы (docs/motion/MOTION_LANGUAGE_V1.md §2) — те же кривые,
     что --ease-out-premium/--ease-in-exit выше (значения идентичны, только имя
     из канона); --ease-water/--ease-stamp — новые токены, раньше не было. Introduced
     для Place Card Motion V1 (docs/motion/PLACE_CARD_MOTION_SPEC_V1.md), переиспользуемы
     любым будущим motion-спринтом. Существующие переходы на старых именах не трогаем. */
  --ease-enter: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --ease-water: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-stamp: cubic-bezier(0.2, 0.8, 0.2, 1);
}

* { box-sizing: border-box; }

/* Базовый сброс — браузер по умолчанию красит <button>/<input>/<a> в свои
   системные цвета (часто чёрный/тёмно-серый) независимо от color на body.
   Без явного сброса это может "просвечивать" сквозь тёмную тему на любом
   интерактивном элементе, у которого нет собственного color. */
button, input, textarea, select {
  color: inherit;
  font-family: inherit;
}
a { color: inherit; text-decoration: none; }

html, body {
  /* Не даём обычному скроллу внутри страницы "перетекать" выше/ниже видимой
     области и провоцировать нативный жест Telegram WebView "потянуть вниз —
     свернуть Mini App" (см. §6 задания Neon Arctic Map Upgrade: свайп по
     контенту под картой не должен схлопывать приложение). */
  overscroll-behavior-y: contain;
}
/* Причина белого фона на full-page screenshot (welcome-экран, карточка
   Кировска и всё ниже одного viewport): у body стоит background-attachment:
   fixed, а у html фон не был задан. По CSS2.1 §14.2, если фон html
   "transparent", браузер ПРОДВИГАЕТ фон body на уровень canvas — а canvas с
   attachment:fixed ограничен размером viewport, поэтому весь фон ниже первого
   экрана просто не рисуется (виден дефолтный белый холст). Как только у html
   есть собственный фон, продвижение не происходит, и body красит уже свой
   полный box (всю высоту документа), а не только viewport — белых участков
   не остаётся при любой высоте страницы. min-height подстраховывает короткий
   контент (в т.ч. динамическую мобильную высоту, dvh, где поддерживается). */
html {
  background: var(--bg-main-2);
  min-height: 100%;
}
/* Polish 09l: убираем системный серый flash iOS на каждый тап — у интерактивных
   элементов есть свои pressed-состояния (ниже), двойной отклик выглядит дёшево. */
* { -webkit-tap-highlight-color: transparent; }
/* Системный pressed-отклик (аудит: :active был лишь у 3 элементов — кнопки
   «мёртвые» на тап): лёгкое сжатие на токене --motion-fast. transform не
   конфликтует с layout; отдельные :active (icon-copy-btn цвет) сохраняются. */
.map-link-btn:active, .book-btn:active, .visit-btn:active:not([disabled]),
.reward-btn-primary:active, .map-sheet-cta:active, .map-sheet-poi-btn:active,
.copy-num-btn:active, .emptystate-btn:active {
  transform: scale(.97);
  transition: transform var(--motion-fast) var(--ease-in-exit);
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  font-family: var(--font-ui);
  background:
    /* мягкий переход от белого системного хедера Telegram (вне нашего DOM,
       не меняем) к тёмному фону — без этого слоя тёмный фон начинается
       сразу за хедером резким "обрывом" (см. §2 задания). */
    linear-gradient(180deg, rgba(159, 191, 219, .12) 0%, transparent 130px),
    radial-gradient(ellipse 900px 500px at 15% -10%, rgba(52, 224, 161, .10), transparent 60%),
    radial-gradient(ellipse 700px 400px at 100% 0%, rgba(47, 143, 209, .12), transparent 55%),
    linear-gradient(180deg, var(--bg-main) 0%, var(--bg-main-2) 100%);
  background-attachment: fixed;
  color: var(--text-main);
  -webkit-font-smoothing: antialiased;
}

/* Роль-система (2026-07-14): логотип бренда (Variant 4) + бейдж роли в шапке.
   display:flex/justify-content — лого+заголовок слева, бейджи справа.
   Registration hotfix (2026-07-14, поколение b): раньше h1 обрезался
   многоточием ("Регистрац…"/"Север бли…") на 375px, потому что оба бейджа
   (.role-badge + .beta-badge) были `flex: none` фиксированной ширины в одном
   ряду с заголовком, и на длинные роли ("Житель Севера") места не хватало.
   Основной заголовок обрезаться не должен ни при каких обстоятельствах —
   вместо этого при нехватке ширины на первой строке остаётся только
   бренд + role-badge, а второстепенный .beta-badge переносится на вторую
   строку (flex-wrap на контейнере + flex-basis:100% на самом бейдже, см.
   ниже — надёжно на 375/390/430px, не зависит от точной длины текста). */
/* ── Final Header Shell (принятый пакет Final_Header_Shell_Revision_Accepted_
   Assets, 2026-07-19): ЕДИНАЯ геометрия Direction Hub / City Shell.
   Токены приёмки: HEADER 84 / CONTROL 40 / MARK 36-40 / MIN GAP 6 / STROKE 1 /
   ROLE TEXT REQUIRED. Заголовок не сокращается и не переносится; на 375px
   знак = 36px (медиа-правило ниже), «Север ближе» помещается целиком. ── */
.app-header {
  height: 84px;
  padding: 0 10px;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px; /* гарантированный минимальный зазор 6px (токен MIN GAP 6) */
}
.hs-heading { display: flex; flex-direction: column; justify-content: center;
  min-width: 0; overflow: hidden; flex: 0 1 auto; }
.app-header h1 {
  font-size: 17px;
  margin: 0;
  font-weight: 700;
  letter-spacing: .1px;
  white-space: nowrap; /* принятое правило: заголовок не переносится и не режется */
}
/* 375px: приёмочная ширина заголовка «Север ближе» — 111px (handoff «111/111»);
   вместе с сезонным контролом, кнопкой роли и профилем в 84px-шапке. */
@media (max-width: 389px) {
  .app-header { padding: 0 8px; }
  .app-header h1 { font-size: 16px; letter-spacing: 0; }
}
/* Строка погоды + атрибуция Open-Meteo — контракт идентичности City Shell
   (видимы только в контуре города, см. updateHeaderShell). Те же классы
   использует .gs-topbar Главной — стили общие, второй набор не заводится. */
.hs-weather { font-size: 11.5px; color: var(--text-soft); white-space: nowrap; margin-top: 1px; }
.hs-weather .warn { color: var(--accent-amber); }
.hs-weather .err { color: var(--danger); }
.hs-weather .skel-line { display: inline-block; width: 72px; height: 8px; border-radius: 8px; background: #23384b; }
.hs-weather[hidden], .hs-attrib[hidden] { display: none; }
.hs-attrib { font-size: 8px; color: rgba(230,238,247,.42); white-space: nowrap; margin-top: 1px; }
@media (max-width: 389px) { .hs-attrib { font-size: 7.5px; } }
.hs-controls { display: flex; align-items: center; gap: 5px; margin-left: auto; flex: none; }
/* Управляющий glass-элемент шапки: 40px, контур 1px (токены CONTROL 40 / STROKE 1). */
.hs-ctl {
  width: 40px; height: 40px; flex: none; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(7, 16, 27, .68);
  backdrop-filter: blur(12px) saturate(115%);
  -webkit-backdrop-filter: blur(12px) saturate(115%);
  border: 1px solid rgba(170, 216, 235, .3);
  border-radius: 50%;
  color: rgba(205, 239, 250, .96);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08), 0 4px 14px rgba(0, 0, 0, .16);
}
.hs-ctl[hidden] { display: none; }
.hs-ctl .gs-ic, .hs-ctl .icon { width: 18px; height: 18px; margin: 0; }
.hs-ctl:active { transform: scale(.94); }
.hs-ctl:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; }
/* Компактный сезонный контрол Direction Hub: высота 40px, контур 1px;
   выбранный сезон — сегмент с текстом, альтернативный — только иконка. */
.hs-season {
  display: inline-flex; align-items: center; height: 40px; padding: 2px; gap: 2px;
  background: rgba(7, 16, 27, .68);
  backdrop-filter: blur(12px) saturate(115%);
  -webkit-backdrop-filter: blur(12px) saturate(115%);
  border: 1px solid rgba(170, 216, 235, .3);
  border-radius: 999px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08), 0 4px 14px rgba(0, 0, 0, .16);
}
.hs-season[hidden] { display: none; }
.hs-season-seg {
  height: 32px; border: 1px solid transparent; border-radius: 999px;
  display: inline-flex; align-items: center; gap: 3px; padding: 0 3px;
  background: none; color: var(--text-soft); cursor: pointer;
  font-size: 11.5px; font-weight: 650; font-family: var(--font-ui);
}
.hs-season-seg .gs-ic { width: 13px; height: 13px; }
.hs-season-seg.on {
  background: rgba(143, 216, 245, .13);
  border-color: rgba(143, 216, 245, .3);
  color: var(--text-main);
  padding: 0 6px;
}
.hs-season-seg:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .hs-ctl:active, .hs-season-seg:active { transform: none; }
}
/* На 375px знак Header Shell переключается на принятую версию 36px
   (правило пакета; srcset при этом отдаёт файл нужной плотности).
   Специфичность поднята (.app-header/.gs-topbar) — базовые 40px-правила
   объявлены ниже по файлу и иначе перебивали бы это медиа-правило. */
@media (max-width: 389px) {
  .app-header .brand-mark, .gs-topbar .gs-citymark { width: 36px; height: 36px; }
}
/* Круглая обрезка чисто через CSS — исходный PNG уже с тёмно-синим квадратным
   фоном (близким к палитре приложения), ровно так же его показывает сам
   Telegram в аватарах (docs/TELEGRAM_BRAND_ROLLOUT.md). Не пере-кроплен заранее.
   Registration hotfix: 28px был почти не виден рядом с заголовком/бейджами —
   увеличен до 38px (+~36%), без пере-кропа (тот же object-fit: cover, исходник
   128×128 — запас разрешения на увеличение без размытия). */
/* Realistic Arctic Relief V1 (2026-07-19): знак — самодостаточный круглый SVG
   (собственный clip-circle, без внешнего чёрного поля), поэтому object-fit:
   contain (не cover — иначе срез собственного круга/овал) и без доп. фона.
   Геометрия шапки (38px, круг) не менялась. src ставится контекстно в JS
   (updateHeaderMark): app-mark вне города / teriberka-mark в контуре города. */
/* Final Header Shell: принятый фотографический знак, рендер 40px (36px на
   375px — см. медиа-правило выше), прозрачный круг из пакета — без второго
   фона/рамки. */
.brand-mark {
  width: 40px; height: 40px; border-radius: 50%; overflow: hidden;
  object-fit: contain; flex: none; display: block;
}
/* Бейдж роли — тот же Polar Glass, что и .card-hero-badge-soon (единый
   контраст на тёмном и светлом фоне), но не завязан на карточку с фото —
   сидит прямо на градиентном фоне body, поэтому свой отдельный класс. */
/* Final Header Shell: текстовая кнопка роли — тот же glass, что .hs-ctl,
   но пилюля с ОБЯЗАТЕЛЬНЫМ текстом роли (токен ROLE TEXT REQUIRED, значения
   «Админ»/«Турист»/«Гид»/«Партнёр»), высота 40px / контур 1px. */
.role-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  height: 40px;
  padding: 0 8px;
  background: rgba(7, 16, 27, .68);
  backdrop-filter: blur(12px) saturate(115%);
  -webkit-backdrop-filter: blur(12px) saturate(115%);
  border: 1px solid rgba(170, 216, 235, .3);
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 650;
  font-family: var(--font-ui);
  color: rgba(205, 239, 250, .96);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08), 0 4px 14px rgba(0, 0, 0, .16);
  flex: none;
  white-space: nowrap;
}
.role-badge:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; }
.role-badge[hidden] { display: none; }
/* Кликабелен только когда реально ведёт на форму заявки (турист без заявки
   на рассмотрении) — см. updateRoleBadge() в app.20260713d.js. */
.role-badge-clickable { cursor: pointer; }

/* Кнопка "Мой кабинет" (User Cabinet & Services V1, Этап 2) — тот же Polar
   Glass, что .role-badge, но постоянная круглая иконка-кнопка (не завязана
   на роль/статус заявки, в отличие от .role-badge). */
/* Final Header Shell: кнопка профиля выровнена до 40px управляющего
   glass-элемента (токен CONTROL 40; переход в кабинет не менялся). */
.cabinet-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: none; padding: 0; cursor: pointer;
  background: rgba(7, 16, 27, .68);
  backdrop-filter: blur(12px) saturate(115%);
  -webkit-backdrop-filter: blur(12px) saturate(115%);
  border: 1px solid rgba(170, 216, 235, .3);
  border-radius: 50%;
  color: rgba(205, 239, 250, .96);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08), 0 4px 14px rgba(0, 0, 0, .16);
}
.cabinet-btn[hidden] { display: none; }
.cabinet-btn .icon { width: 18px; height: 18px; }
.cabinet-btn:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; }
.cabinet-btn:active { transform: scale(.94); }
@media (prefers-reduced-motion: reduce) { .cabinet-btn:active { transform: none; } }

/* Нижний отступ учитывает home indicator iPhone: раньше был жёсткий 36px и на
   экранах БЕЗ фикс-CTA (главная, списки, паспорт, коллекции) последняя строка
   уезжала под «чёрточку» (жалоба «часть надписи уплывает»). env() заработал
   только после viewport-fit=cover (09l), поэтому чиним системно здесь. */
#app {
  padding: 12px 16px calc(36px + env(safe-area-inset-bottom, 0px));
  /* Раньше здесь был свой плоский `background: var(--bg-main-2)` ("доп.
     страховка" от белого фона ниже viewport) — но это ЛИШНИЙ, отдельный от
     body слой сплошного цвета, а .app-header фона не задаёт вообще (виден
     градиент body сквозь него). Ровно на границе шапка/#app из-за этого был
     заметный горизонтальный шов (плавный градиент body обрывался в плоский
     цвет). html уже держит тот же var(--bg-main-2) как фон документа
     (правило выше, `html { background: ... }`) — этого достаточно для
     защиты от белого фона ниже одного экрана; #app теперь просто не красит
     поверх, и градиент body виден одним целым куском под шапкой и над
     контентом (шов исчезает, палитра та же тёмная северная).
     min-height остаётся — не про фон, а про то, чтобы короткий экран
     (напр. reg-step) не схлопывался по высоте. */
  min-height: 100vh;
  min-height: 100dvh;
}

/* Home Density & Warmth Polish V1, §5 — единый лёгкий page-transition поверх
   существующего render()-flow (см. app.*.js). Классы вешаются/снимаются
   ТОЛЬКО на #app (сиблинг #gs-nav в index.html) — нижняя панель не мигает.
   opacity+translateY only, без blur/scale/поворота, 100мс на фазу (бюджет
   160-220мс на весь переход). JS уже гейтит анимацию по
   prefers-reduced-motion; правило ниже — вторая, CSS-уровня, страховка. */
.gs-transitioning { transition: opacity 100ms ease, transform 100ms ease; }
.gs-fade-out { opacity: 0; transform: translateY(-6px); }
.gs-fade-in-start { opacity: 0; transform: translateY(6px); }
@media (prefers-reduced-motion: reduce) {
  .gs-transitioning, .gs-fade-out, .gs-fade-in-start { transition: none !important; opacity: 1 !important; transform: none !important; }
}

.muted { color: var(--text-muted); }
.lead { color: var(--text-soft); margin: 4px 0 16px; line-height: 1.5; }

/* --- иконки: inline SVG line-icons вместо emoji-как-UI (эмодзи остаются
   только как fallback в данных — content_type.icon и т.п.) --- */
/* Базовый размер 16px — контейнеры (icon-badge/chip/route-meta-item/...)
   переопределяют под себя через более специфичный селектор `.context .icon`. */
.icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 16px; height: 16px; vertical-align: -3px; margin-right: 5px; }
.icon svg { width: 100%; height: 100%; display: block; }
.icon:last-child { margin-right: 0; }
.icon-emoji { display: inline-flex; margin-right: 5px; }

.icon-badge {
  width: 44px;
  height: 44px;
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-card-glass-strong);
  border: 1px solid var(--border-soft);
  flex-shrink: 0;
}
.icon-badge .icon { width: 22px; height: 22px; margin: 0; }
.icon-badge[data-theme="amber"] { color: var(--accent-amber); background: rgba(242, 182, 77, .12); border-color: rgba(242, 182, 77, .25); }
.icon-badge[data-theme="ice"] { color: var(--accent-ice); background: rgba(143, 216, 245, .12); border-color: rgba(143, 216, 245, .25); }
.icon-badge[data-theme="aurora"] { color: var(--accent-aurora); background: rgba(52, 224, 161, .12); border-color: rgba(52, 224, 161, .25); }
.icon-badge[data-theme="sea"] { color: var(--accent-sea); background: rgba(47, 143, 209, .16); border-color: rgba(47, 143, 209, .3); }

.icon-badge.sm { width: 34px; height: 34px; border-radius: 11px; }
.icon-badge.sm .icon { width: 17px; height: 17px; }

/* ==========================================================================
   Hero (главный экран)
   ========================================================================== */
.hero {
  position: relative;
  border-radius: var(--radius-card);
  padding: 30px 22px 26px;
  margin-bottom: 14px;
  overflow: hidden;
  background:
    radial-gradient(ellipse 380px 200px at 85% -20%, rgba(52, 224, 161, .38), transparent 70%),
    radial-gradient(ellipse 300px 200px at -10% 115%, rgba(47, 143, 209, .32), transparent 65%),
    linear-gradient(160deg, #0d2136 0%, #0a1626 100%);
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-soft);
}
/* Контур берега/карты — фоновый декоративный узор travel-обложки, а не
   картинка (см. §2 задания: "лёгкая карта/контур берега как background
   pattern"). */
.hero-coast {
  position: absolute;
  inset: 0;
  opacity: .5;
  color: rgba(143, 216, 245, .5);
  pointer-events: none;
}
.hero-eyebrow {
  position: relative;
  font-size: 12px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--accent-ice);
  font-weight: 700;
  opacity: .85;
}
.hero h1 {
  position: relative;
  margin: 4px 0 6px;
  font-size: 28px;
  font-weight: 800;
  letter-spacing: .2px;
}
/* Короткий подзаголовок под названием (см. спринт Russian App Naming +
   Arctic Destination Welcome, §5) — отдельная строка между h1 и описанием. */
.hero-subtitle {
  position: relative;
  margin: 0 0 8px;
  color: var(--accent-ice);
  font-size: 14px;
  font-weight: 650;
}
.hero p {
  position: relative;
  margin: 0;
  color: var(--text-soft);
  font-size: 14px;
  line-height: 1.5;
  max-width: 88%;
}

/* декоративная "линия горизонта/сияния" — чисто CSS, без картинок */
.hero-glow {
  position: absolute;
  right: -30px;
  top: -30px;
  width: 160px;
  height: 160px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(52, 224, 161, .35), transparent 70%);
  filter: blur(2px);
  pointer-events: none;
}
/* Северное сияние (0711h): не «зелёные круги», а настоящий занавес аврора —
   ВЕРТИКАЛЬНЫЕ лучи (repeating-linear-gradient ~вертикаль) + «шторная» маска
   (ярко сверху, тает книзу) + мягкое диффузное свечение неба за лучами. Движение
   даёт rAF (лёгкое покачивание лучей + пульс), см. startAurora. */
.hero-aurora {
  position: absolute;
  inset: -40% -14% auto -14%;
  height: 210px;
  pointer-events: none;
  overflow: visible;
}
.hero-aurora .aurora-ribbon {
  position: absolute;
  left: -18%;
  right: -18%;
  top: -6%;
  height: 132%;
  pointer-events: none;
  will-change: transform, opacity;
  /* «занавес»: лучи яркие сверху, растворяются книзу */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 14%, #000 40%, transparent 86%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 14%, #000 40%, transparent 86%);
}
/* Занавес 1 — основные зелёные лучи */
.hero-aurora .aurora-ribbon:nth-child(1) {
  background: repeating-linear-gradient(93deg,
    transparent 0 7px,
    rgba(52, 224, 161, .14) 11px,
    rgba(104, 244, 194, .38) 15px,
    rgba(52, 224, 161, .12) 20px,
    transparent 28px);
  filter: blur(3px);
  transform: rotate(-3deg);
  opacity: .8;
}
/* Диффузное свечение неба за лучами (мягкое, без «кругов»-резкости) */
.hero-aurora .aurora-ribbon:nth-child(2) {
  background:
    radial-gradient(130% 70% at 62% 4%, rgba(52, 224, 161, .26), transparent 62%),
    radial-gradient(90% 60% at 28% 8%, rgba(90, 200, 255, .14), transparent 66%);
  filter: blur(16px);
  transform: none;
  opacity: .85;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 45%, transparent 90%);
  mask-image: linear-gradient(to bottom, #000 0, #000 45%, transparent 90%);
}
/* Занавес 2 — холодные бирюзово-фиолетовые лучи, смещён */
.hero-aurora .aurora-ribbon:nth-child(3) {
  left: 6%;
  background: repeating-linear-gradient(89deg,
    transparent 0 9px,
    rgba(120, 224, 255, .12) 13px,
    rgba(150, 178, 255, .26) 17px,
    rgba(120, 224, 255, .10) 22px,
    transparent 30px);
  filter: blur(3.5px);
  transform: rotate(2deg);
  opacity: .55;
}
.hero-whale {
  position: absolute;
  left: 14px;
  bottom: 10px;
  width: 46px;
  height: 46px;
  margin: 0;
  color: var(--accent-ice);
  opacity: .16;
  pointer-events: none;
}
.hero-wave {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 34px;
  opacity: .5;
  color: var(--accent-sea);
  pointer-events: none;
  margin: 0;
}

/* Pill-кнопка возврата к выбору направления — сверху home Териберки, не
   часть pageNav() (см. §3 задания Russian App Naming + Arctic Destination
   Welcome). */
.destination-switch-row { display: flex; margin-bottom: 10px; }
.destination-switch-btn { color: var(--accent-aurora); border-color: rgba(52, 224, 161, .3); }

/* --- статус пользователя: compact glass chip --- */
.status-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: var(--bg-card-glass);
  backdrop-filter: blur(14px);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 8px 16px 8px 8px;
  margin-bottom: 12px;
  color: var(--text-main);
  cursor: pointer;
  font-size: 13px;
}
.status-chip .icon-badge { width: 34px; height: 34px; border-radius: 50%; }
.status-chip .icon-badge .icon { width: 17px; height: 17px; margin: 0; }
.status-chip-metrics { display: flex; flex-wrap: wrap; gap: 2px 10px; color: var(--text-muted); }
.status-chip-metrics b { color: var(--text-main); font-weight: 600; }
.status-chip-arrow { margin-left: auto; color: var(--text-muted); width: 16px; height: 16px; flex-shrink: 0; }
.status-chip-arrow .icon { margin: 0; width: 100%; height: 100%; }

/* ==========================================================================
   Карточки разделов главного экрана + списки (Где поесть/Проживание/...)
   ========================================================================== */
.card {
  position: relative;
  display: block;
  width: 100%;
  text-align: left;
  background: var(--bg-card-glass);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-card);
  padding: 14px 16px;
  margin-bottom: 10px;
  color: var(--text-main);
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
  transition: transform .12s ease, background .12s ease, border-color .12s ease;
}
.card:active { transform: scale(.98); background: var(--bg-card-glass-strong); }
.card .card-title { font-weight: 650; font-size: 16px; letter-spacing: .1px; }
.card .card-sub { color: var(--text-soft); font-size: 13.5px; margin-top: 3px; line-height: 1.4; }

/* ==========================================================================
   Единая система premium-заглушек вместо фото: getVisualThemeForItem() +
   renderVisualPlaceholder() в app.js — один .visual-scene на 3 контекста
   (список/раздел/маршрут), а не своя случайная реализация на каждом экране
   (см. §10 задания Visual System v2).
   ========================================================================== */
.visual-scene {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.visual-scene .icon {
  width: 34px;
  height: 34px;
  margin: 0;
  opacity: .95;
  filter: drop-shadow(0 0 10px currentColor);
  position: relative;
  z-index: 1;
}
/* Большая полупрозрачная "водяная" версия той же иконки на фоне — вместо
   плоского прямоугольника. */
.visual-scene .icon.icon-ghost {
  position: absolute;
  right: -10px;
  bottom: -14px;
  width: 84px;
  height: 84px;
  opacity: .14;
  filter: none;
}
.visual-scene::before {
  content: "";
  position: absolute; inset: 0;
  background-image: repeating-linear-gradient(135deg, rgba(255,255,255,.05) 0 2px, transparent 2px 14px);
  mix-blend-mode: overlay;
}
.visual-scene::after {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(circle at 30% 20%, rgba(255,255,255,.14), transparent 55%);
}
.visual-scene[data-theme="amber"] { background: linear-gradient(155deg, #4a3413, #241a0c); color: var(--accent-amber); border-color: rgba(242, 182, 77, .18); }
.visual-scene[data-theme="ice"] { background: linear-gradient(155deg, #163244, #0c1c28); color: var(--accent-ice); border-color: rgba(143, 216, 245, .18); }
.visual-scene[data-theme="aurora"] { background: linear-gradient(155deg, #12362c, #0b1e19); color: var(--accent-aurora); border-color: rgba(52, 224, 161, .18); }
.visual-scene[data-theme="sea"] { background: linear-gradient(155deg, #123249, #0a1c2a); color: var(--accent-sea); border-color: rgba(47, 143, 209, .2); }

/* Заглушка большой карточки списка (POI без фото) — заполняет .card-hero. */
.card-visual-fallback { position: absolute; inset: 0; }

/* Заглушка маршрута — пунктирная "тропа" вместо диагональной текстуры. */
.route-visual::before {
  background-image: radial-gradient(circle, rgba(255,255,255,.3) 1.4px, transparent 1.6px);
  background-size: 11px 11px;
  mix-blend-mode: normal;
}

/* ==========================================================================
   Сцены заглушек (см. §5 задания Scenario-first Travel UX) — вторая,
   независимая от --theme (цвет) ось: разный CSS-паттерн текстуры на месте
   фото, чтобы заглушки не выглядели однообразно. Тема (4 токена палитры)
   не меняется — сцена трогает только background-image/mix-blend-mode.
   ========================================================================== */
/* море/океан — спокойные горизонтальные полосы, как штиль */
.visual-scene[data-scene="sea"]::before {
  background-image: repeating-linear-gradient(180deg, rgba(255,255,255,.08) 0 2px, transparent 2px 15px);
  mix-blend-mode: overlay;
}
/* скалы/побережье — резкие диагональные грани */
.visual-scene[data-scene="cliffs"]::before {
  background-image: repeating-linear-gradient(112deg, rgba(255,255,255,.1) 0 3px, transparent 3px 19px);
  mix-blend-mode: overlay;
}
/* северное сияние — цветные ленты вместо нейтральной текстуры */
.visual-scene[data-scene="aurora"]::before {
  background-image:
    linear-gradient(100deg, transparent 8%, rgba(52, 224, 161, .3) 32%, transparent 54%),
    linear-gradient(100deg, transparent 45%, rgba(143, 216, 245, .24) 66%, transparent 86%);
  mix-blend-mode: normal;
}
/* дом/ночлег — мягкая точечная "вязаная" текстура, по-домашнему */
.visual-scene[data-scene="stay"]::before {
  background-image: radial-gradient(circle, rgba(255,255,255,.1) 1.6px, transparent 1.8px);
  background-size: 15px 15px;
  mix-blend-mode: overlay;
}
/* баня/тепло — волнистые линии пара */
.visual-scene[data-scene="sauna"]::before {
  background-image: repeating-linear-gradient(195deg, rgba(255,255,255,.09) 0 2px, transparent 2px 13px);
  mix-blend-mode: overlay;
}
/* заброшенные места/история — грубая "выветренная" перекрёстная штриховка */
.visual-scene[data-scene="history"]::before {
  background-image:
    repeating-linear-gradient(68deg, rgba(255,255,255,.06) 0 1px, transparent 1px 9px),
    repeating-linear-gradient(152deg, rgba(255,255,255,.05) 0 1px, transparent 1px 12px);
  mix-blend-mode: overlay;
}
/* ягоды/северная природа — рассыпанные крупные точки */
.visual-scene[data-scene="berries"]::before {
  background-image: radial-gradient(circle, rgba(255,255,255,.16) 2px, transparent 2.2px);
  background-size: 13px 13px;
  mix-blend-mode: overlay;
}
/* кит/еда используют базовую диагональную текстуру .visual-scene::before —
   их идентичность уже несёт иконка (кит/вилка), различать паттерном не нужно. */

/* птицы — лёгкий шеврон, как V-клин перелётной стаи */
.visual-scene[data-scene="bird"]::before {
  background-image:
    repeating-linear-gradient(45deg, rgba(255,255,255,.09) 0 2px, transparent 2px 16px),
    repeating-linear-gradient(135deg, rgba(255,255,255,.09) 0 2px, transparent 2px 16px);
  mix-blend-mode: overlay;
}
/* краб/панцирь — плотная перекрёстная сетка */
.visual-scene[data-scene="crab"]::before {
  background-image:
    repeating-linear-gradient(60deg, rgba(255,255,255,.08) 0 2px, transparent 2px 11px),
    repeating-linear-gradient(-60deg, rgba(255,255,255,.08) 0 2px, transparent 2px 11px);
  mix-blend-mode: overlay;
}
/* тундровые растения — тонкие вертикальные "травинки" */
.visual-scene[data-scene="plant"]::before {
  background-image: repeating-linear-gradient(90deg, rgba(255,255,255,.09) 0 1.5px, transparent 1.5px 9px);
  mix-blend-mode: overlay;
}
/* особые места — редкие точки-метки, как отметки на карте */
.visual-scene[data-scene="place"]::before {
  background-image: radial-gradient(circle, rgba(255,255,255,.14) 1.6px, transparent 1.8px);
  background-size: 20px 20px;
  mix-blend-mode: overlay;
}

/* Мини-визуальная зона раздела на главном экране — вместо голой icon-badge
   на пустом фоне (см. §2 задания: "убрать ощущение пустых больших плиток"). */
.section-visual {
  height: 74px;
  border-radius: 14px;
  margin-bottom: 10px;
  border: 1px solid rgba(255, 255, 255, .07);
}
.section-visual .icon-badge { position: relative; z-index: 1; }
.section-visual .icon.icon-ghost { width: 64px; height: 64px; right: -8px; bottom: -18px; }

/* "Что хочешь сделать?" — сценарные карточки в горизонтальный скролл (см.
   §1 задания Scenario-first Travel UX): турист выбирает намерение, а не
   раздел каталога. Крупнее и заметнее домашней сетки разделов ниже. */
.scenario-scroll {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding-bottom: 4px;
  margin-bottom: 18px;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}
.scenario-scroll::-webkit-scrollbar { display: none; }
.scenario-card {
  flex: 0 0 152px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  text-align: left;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-card);
  padding: 12px;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
}
.scenario-card:active { transform: scale(.98); background: var(--bg-card-glass-strong); }
.scenario-card .section-visual { height: 82px; margin-bottom: 10px; }
.scenario-card .card-title { font-size: 14px; font-weight: 650; letter-spacing: .1px; }
.scenario-card .card-sub { font-size: 11.5px; margin-top: 3px; color: var(--text-muted); line-height: 1.35; }

/* Skeleton-состояние homeView (см. спринт Loading Fix) — силуэты карточек вместо
   пустого текста "Загрузка…", пока идёт первая волна запросов. Мягкий pulse,
   без резких миганий; формы повторяют реальный layout (hero/chip/scenario-ряд/
   сетка разделов), чтобы переход к реальному контенту не "прыгал". */
@keyframes skeletonPulse { 0%, 100% { opacity: .55; } 50% { opacity: .9; } }
.skeleton-hero, .skeleton-chip, .skeleton-tile, .skeleton-card, .skeleton-map, .skeleton-pill {
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-card);
  animation: skeletonPulse 1.4s ease-in-out infinite;
}
.skeleton-hero { height: 160px; margin-bottom: 14px; }
.skeleton-chip { height: 46px; border-radius: var(--radius-pill); margin-bottom: 14px; }
.skeleton-row { display: flex; gap: 10px; margin-bottom: 14px; }
.skeleton-row .skeleton-tile { flex: 1; height: 96px; }
.skeleton-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.skeleton-card { height: 84px; }
/* Скелет карты (см. mapView): высокий блок карты + ряд пилюль-сценариев сверху. */
.skeleton-pill-row { flex-wrap: nowrap; overflow: hidden; }
.skeleton-pill { flex: 0 0 auto; width: 108px; height: 38px; border-radius: var(--radius-pill); }
.skeleton-map { height: 62vh; min-height: 320px; border-radius: var(--radius-lg, 18px); }
.loading-progress-text {
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
  margin-top: 18px;
  min-height: 1em;
}
.loading-progress-actions { display: flex; justify-content: center; margin-top: 10px; }
.loading-progress-actions button {
  background: var(--bg-card-glass-strong);
  color: var(--text-main);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 10px 22px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

/* Домашняя сетка разделов. */
.home-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
.card-section { margin-bottom: 0; padding: 14px 14px 16px; overflow: hidden; }
.card-section .card-title { font-size: 15px; }
.card-section .card-sub { font-size: 12.5px; }

/* "Коллекции Севера" — широкая эмоциональная карточка на всю ширину сетки
   (не квадратная плитка, см. §2 задания). */
.card-emotive {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 14px;
}
.card-emotive .section-visual {
  width: 74px;
  height: 74px;
  flex-shrink: 0;
  margin-bottom: 0;
}
.card-emotive .card-title { font-size: 16px; }

.card-full { grid-column: 1 / -1; }

/* "Паспорт полярника" — full-width profile card. */
.card-profile {
  display: flex;
  align-items: center;
  gap: 14px;
  background:
    radial-gradient(ellipse 220px 120px at 100% 0%, rgba(242, 182, 77, .18), transparent 65%),
    linear-gradient(150deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .02));
}
.card-profile .card-title { font-size: 16px; }

/* ==========================================================================
   Story cards — фото-first карточки списков (Где поесть/Проживание/Чем
   заняться): большая визуальная зона сверху с overlay вместо текста под
   плоской иконкой (см. §3 задания Visual System v2).
   ========================================================================== */
.card-story { padding: 0; overflow: hidden; }
.card-hero {
  position: relative;
  width: 100%;
  height: 156px;
  overflow: hidden;
  border-radius: var(--radius-card) var(--radius-card) 0 0;
}
.card-hero img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.card-hero-overlay {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  padding: 10px 14px 12px;
  background: linear-gradient(180deg, transparent, rgba(3, 8, 14, .85) 78%);
}
.card-hero-title { font-size: 16.5px; font-weight: 700; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.5); }
.card-hero-badges {
  position: absolute;
  top: 10px; right: 10px;
  left: 10px;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
}
.card-hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: rgba(8, 16, 26, .78);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: var(--radius-pill);
  padding: 4px 9px;
  font-size: 12px;
  font-weight: 650;
  color: #fff;
}
.card-hero-badge .icon { width: 12px; height: 12px; color: var(--accent-amber); }
.card-hero-badge-closed { color: #ff9d8a; }
.card-hero-badge-closed .icon { color: #ff9d8a; }
/* "Что посмотреть" — сценарная подпись (см. §5.1 задания UX-патч), отдельный
   ледяной тон, чтобы не путать со звездой рейтинга/статусом "Закрыто". */
.card-hero-badge-scenario { color: var(--accent-ice); border-color: rgba(143, 216, 245, .3); }
.card-body { padding: 12px 16px 14px; }

/* Рейтинг/теги/CTA-стрелка на карточке списка. */
.card-meta-row { display: flex; align-items: center; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
.card-rating { display: inline-flex; align-items: center; gap: 4px; color: var(--accent-amber); font-size: 13px; font-weight: 600; }
.card-rating .icon { width: 13px; height: 13px; }
.card-tag-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
  font-size: 11.5px;
  color: var(--text-muted);
}
.card-arrow {
  position: absolute;
  right: 14px;
  bottom: 14px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--bg-card-glass-strong);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-story .card-arrow { bottom: 12px; }
.card-arrow .icon { width: 13px; height: 13px; margin: 0; }
.card-status-closed {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: #ff9d8a;
  font-size: 12px;
  font-weight: 600;
}
.card-status-closed .icon { width: 12px; height: 12px; }

/* ==========================================================================
   Экран выбора направления (см. спринт Russian App Naming + Arctic
   Destination Welcome) — карточки направлений travel-choice, поверх той же
   .card-story/.card-hero базы, что и остальные story-карточки приложения.
   ========================================================================== */
/* Статичный фото-hero экрана выбора города (см. задание Welcome Photo Hero,
   2026-07-14) — тот же принцип статичности, что и .gs-hero (D-0014/D-0016):
   .ph неподвижен, никакого Ken Burns/aurora-rAF, весь эффект даёт только
   фото + затемняющий градиент под текстом. Не путать с .hero (карточки-плашки
   без фото, используется отдельно в Place Card, .hero не трогаем). */
.welcome-hero { position: relative; margin: 4px 0 14px; height: 220px; border-radius: var(--radius-card);
  overflow: hidden; border: 1px solid var(--border-soft);
  /* isolation: изолирует mix-blend-mode Aurora Light Drift ниже — screen
     должен смешиваться только с самим фото hero, а не «протекать» на
     соседние карточки/фон страницы под этой карточкой. */
  isolation: isolate; }
.welcome-hero .ph { position: absolute; inset: 0; background-size: cover; background-position: center 55%; }
/* Aurora Light Drift (Welcome Motion + Micro-Polish, 2026-07-14) — единственный
   motion-слой этой итерации; Layer B ("Aurora Breathing Glow") не добавлен —
   визуально не потребовался, Layer A сам по себе не выглядит механическим (см.
   motion-qa-notes.md). Реализовано ЧИСТО через ::before самого .ph — тот же
   photo-layer переиспользуется через `background-image: inherit` (наследует
   inline background-image, заданный в JS), поэтому JS/разметка hero не
   менялись. Стек внутри .ph: 1) фон .ph (статичное фото) → 2) ::before (этот
   слой, движется) — оба ПОД .welcome-hero::after (затемнение) и .body (текст,
   z-index:2), т.к. ::before генерируется до собственного контента .ph, а
   :after контейнера генерируется после .ph в document order (z-index:auto
   красится по порядку появления). Двигается только transform: translateX —
   background-position/size НЕ анимируются, поэтому это не Ken Burns и не
   влияет на макет (layout shift = 0). Небольшой постоянный горизонтальный
   overscan (-3%/-3%) — не zoom, только чтобы blur-край слоя при сдвиге ±0.8%
   не попадал в видимую (обрезанную overflow:hidden) область. Маска ограничивает
   слой верхней третью кадра (небо/сияние) и гасит его ДО деревьев/берега/воды —
   двоения наземных объектов нет, т.к. там слоя просто нет (opacity 0 по маске),
   а не потому что он визуально совпадает с неподвижным фото. */
@keyframes wh-aurora-drift { from { transform: translateX(-0.8%); } to { transform: translateX(0.8%); } }
.welcome-hero .ph::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: -3%; right: -3%;
  background-image: inherit;
  background-position: inherit;
  background-size: cover;
  opacity: .1;
  filter: blur(7px);
  mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 26%, transparent 48%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 26%, transparent 48%);
  animation: wh-aurora-drift 22s ease-in-out infinite alternate;
  will-change: transform;
  pointer-events: none;
}
/* Reduced motion (§17 задания): анимация полностью отключена, слой остаётся
   статичным на своём начальном transform — яркость/opacity/маска не меняются,
   композиция визуально идентична (просто не движется). */
@media (prefers-reduced-motion: reduce) {
  .welcome-hero .ph::before { animation: none; }
}
/* Polish iteration 2: затемнение усилено слева+снизу, сияние справа+сверху
   по-прежнему хорошо видно (горизонтальный слой гаснет к 55%, вертикальный
   вверху почти прозрачный) — фото и текст остаются статичными (без Ken
   Burns/aurora-overlay/canvas/rAF, только два CSS-градиента). */
.welcome-hero::after { content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(6,13,22,.22) 0%, transparent 32%, rgba(6,13,22,.58) 66%, rgba(6,13,22,.95) 100%),
    linear-gradient(100deg, rgba(6,13,22,.4) 0%, transparent 55%); }
.welcome-hero .body { position: absolute; left: 18px; right: 18px; bottom: 14px; z-index: 2; }
.welcome-hero h1 { margin: 10px 0 6px; font-size: 26px; font-weight: 800; line-height: 1.08; }
.welcome-hero p { margin: 0; color: var(--text-soft); font-size: 13.5px; line-height: 1.45; max-width: 92%; }

/* Final Header Shell (2026-07-19): большой сезонный переключатель welcome-
   экрана удалён вместе с пустым отступом — сезон живёт компактным контролом
   в шапке Direction Hub (.hs-season, см. блок Header Shell выше). CSS
   .gs-season-pill/.gs-season-opt удалён как мёртвый (потребителей нет). */

.destination-list { margin-top: 4px; }
/* Full-bleed destination cards (Welcome Visual Polish Iteration 2, §2): фото
   занимает всю карточку, единый overlay-градиент, текст поверх фото — без
   отдельной непрозрачной .card-body-панели снизу. Scoped на .destination-card
   — другие card-story (рестораны/отели/активности) используют прежнюю
   разметку/CSS .card-hero(top)+.card-body(bottom) и не затронуты. */
.destination-card { padding: 0; overflow: hidden; min-height: 220px; }
.destination-card .card-hero { position: absolute; inset: 0; height: auto; border-radius: inherit; }
/* Общий градиент для всех трёх карточек (не индивидуальный по фото) — сильнее
   слева+снизу, слабее справа+сверху; тёмный навигационный тон (не чистый
   чёрный), без агрессивного glow. Второй слой (radial) добавляет чуть
   уверенности в нижнем левом углу, где садится текст — важно для светлого
   кадра Кировска, чтобы белый текст оставался читаемым. */
.destination-card .card-hero-overlay {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 16px 18px 14px;
  background:
    radial-gradient(ellipse 220px 160px at 0% 100%, rgba(4, 9, 16, .5), transparent 65%),
    linear-gradient(180deg, transparent 0%, transparent 32%, rgba(4, 9, 16, .58) 64%, rgba(4, 9, 16, .92) 100%),
    linear-gradient(100deg, rgba(4, 9, 16, .48) 0%, rgba(4, 9, 16, .14) 40%, transparent 64%);
}
.destination-card .card-hero-title { font-size: 19px; }
.destination-card .card-sub { margin-top: 6px; max-width: 94%; }
.destination-card .destination-cta { margin-top: 10px; }
.destination-cta {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--accent-aurora);
  font-size: 13.5px;
  font-weight: 650;
}
.destination-cta .icon { width: 14px; height: 14px; margin: 0; }
.card-hero-badge-open { color: var(--accent-aurora); }
.card-hero-badge-open .icon { color: var(--accent-aurora); }
/* "Скоро" — ледяной, а не серый тон: читается как "будущее направление",
   а не как ошибка/выключенный элемент (см. спринт Arctic Welcome Polish, §2).
   Polar Glass contrast fix (Welcome Motion + Micro-Polish, 2026-07-14): прежний
   светлый полупрозрачный фон (rgba(143,216,245,.14)) сливался со светлым небом
   на фото Кировска — недостаточный контраст. Тёмная подложка + холодная
   обводка держат контраст одинаково и на тёмном (Мурманск), и на светлом
   (Кировск) фото; размер/паддинги/радиус наследуются от .card-hero-badge
   без изменений. .card-hero-badge-open (Териберка, "✓ Открыто") — отдельный
   класс, этим правилом не затронут. */
.card-hero-badge-soon {
  color: rgba(205, 239, 250, .96);
  background: rgba(7, 16, 27, .68);
  border-color: rgba(170, 216, 235, .3);
  backdrop-filter: blur(12px) saturate(115%);
  -webkit-backdrop-filter: blur(12px) saturate(115%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08), 0 4px 14px rgba(0, 0, 0, .16);
}
/* "Скоро" — явно неактивная карточка, но красивая "будущая витрина", а не
   мёртвая/сломанная (см. §2 задания Arctic Welcome Polish): лёгкое
   приглушение вместо тяжёлого, аврора/лёд-тема сцены остаётся видна. Курсор
   остаётся pointer — тап всё равно даёт обратную связь (toast), а не тишину. */
.destination-card-soon { opacity: .94; }
.destination-card-soon .card-hero { filter: grayscale(.1) brightness(1.02); }
.destination-card-soon .card-hero-title { color: rgba(255, 255, 255, .94); }
.card-closed { opacity: .58; }

/* Форма заявки на роль (roleApplicationView, 2026-07-14) — простые текстовые
   поля на токенах приложения (не hard-coded hex, как в отдельной HTML-админке
   app/delivery/admin/). */
.form-field { margin-top: 14px; }
.form-field label { display: block; margin-bottom: 6px; font-size: 13px; font-weight: 550; color: var(--text-muted); }
.form-input {
  width: 100%; box-sizing: border-box; padding: 11px 13px; font-size: 14px;
  font-family: inherit; color: var(--text-main); background: var(--bg-card-glass);
  border: 1px solid var(--border-soft); border-radius: var(--r-sm);
  transition: border-color var(--motion-fast, 150ms) ease, background var(--motion-fast, 150ms) ease;
}
.form-input:focus {
  outline: none; border-color: var(--accent-ice);
  background: rgba(143, 216, 245, .06);
}
textarea.form-input { min-height: 72px; resize: vertical; }
.form-hint { margin-top: 4px; font-size: 12px; color: var(--text-muted); }
.form-error {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--r-sm);
  background: rgba(255, 107, 107, .12); border: 1px solid rgba(255, 107, 107, .3);
  color: #ff9d8a; font-size: 13px; display: flex; align-items: flex-start; gap: 7px;
  animation: formErrorIn var(--motion-ui) var(--ease-out-premium) both;
}
.form-error .icon { width: 15px; height: 15px; flex: none; margin-top: 1px; color: #ff9d8a; }
/* [hidden] обычно "просто работает" через UA-стили (display:none), но здесь
   у .form-error свой display:flex той же специфичности — а автор-правило,
   идущее позже UA-таблицы, побеждает и рисует пустую красную рамку даже при
   hidden (обнаружено визуально на экране "Личные данные", Этап 2.1 Final
   Polish; тот же паттерн `class="form-error" hidden` уже использовался и
   раньше в проекте — правило ниже чинит все места разом). */
.form-error[hidden] { display: none; }
@keyframes formErrorIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .form-input { transition: none; }
  .form-error { animation: none; }
}

.actions { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }
.actions button {
  background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora));
  color: var(--text-on-accent);
  font-weight: 700;
  border: none;
  border-radius: var(--radius-pill);
  padding: 14px;
  font-size: 15px;
  cursor: pointer;
}

/* ==========================================================================
   Detail screen (POI)
   ========================================================================== */
.detail h2 { margin: 4px 0; font-size: 21px; font-weight: 750; }
.detail .type { color: var(--text-muted); font-style: normal; font-size: 13px; margin-bottom: 12px; letter-spacing: .2px; text-transform: uppercase; }
.detail p { line-height: 1.55; }
.kv { margin: 2px 0; }
.tags { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 6px; }

/* "Коротко о месте" — первый абзац в отдельном заметном блоке, а не первой
   строкой той же простыни текста, что и длинное описание (см. §4 задания:
   убрать ощущение текстовой простыни). */
.detail-lead {
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--accent-ice);
  border-radius: 14px;
  padding: 12px 14px;
  margin: 12px 0;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--text-soft);
}

.poi-hero {
  position: relative;
  width: 100%;
  height: 230px;
  border-radius: var(--radius-card);
  margin-bottom: 12px;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  border: 1px solid var(--border-soft);
}
.poi-hero img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.poi-hero-overlay {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 14px;
  background: linear-gradient(180deg, transparent, rgba(3, 8, 14, .82) 75%);
}
.poi-hero-overlay .poi-hero-title { font-size: 20px; font-weight: 750; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.5); }
.poi-hero-overlay .poi-hero-type { font-size: 12px; color: rgba(255,255,255,.75); text-transform: uppercase; letter-spacing: .3px; margin-top: 2px; }
.poi-hero.no-photo { background: linear-gradient(155deg, #123249, #0a1c2a); }
.poi-hero.no-photo[data-theme="amber"] { background: linear-gradient(155deg, #4a3413, #241a0c); }
.poi-hero.no-photo[data-theme="ice"] { background: linear-gradient(155deg, #163244, #0c1c28); }
.poi-hero.no-photo[data-theme="aurora"] { background: linear-gradient(155deg, #12362c, #0b1e19); }
.poi-hero.no-photo[data-theme="sea"] { background: linear-gradient(155deg, #123249, #0a1c2a); }
.poi-hero.no-photo .poi-hero-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: .28;
}
.poi-hero.no-photo .poi-hero-icon .icon { width: 56px; height: 56px; margin: 0; }
/* Рейтинг/статус-бейджи поверх hero (переиспользуют .card-hero-badge). */
.poi-hero-badges { position: absolute; top: 12px; right: 12px; z-index: 1; display: flex; gap: 6px; }

/* --- Venue card (business_info.card): сочная карточка заведения, пилот «Терь».
   Гармонизирована с текущей тёмной арктической темой (токены aurora/sea),
   акцент — тёмно-бирюзовый/арктический зелёный. Аддитивно, старые POI без
   business_info.card не затрагиваются. --- */
.poi-hero-venue { height: 250px; }
.poi-hero-venue .poi-hero-overlay {
  padding: 16px 16px 18px;
  background: linear-gradient(180deg, transparent, rgba(3, 8, 14, .55) 45%, rgba(3, 8, 14, .9) 100%);
}
.poi-hero-eyebrow {
  display: inline-block;
  margin-bottom: 8px;
  padding: 4px 11px;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--text-on-accent);
  background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora));
  border-radius: var(--radius-pill);
  box-shadow: 0 4px 14px rgba(47, 143, 209, .35);
}
.poi-hero-venue .poi-hero-title { font-size: 23px; line-height: 1.15; }
.poi-hero-headline {
  margin-top: 5px;
  font-size: 13.5px;
  line-height: 1.4;
  font-weight: 500;
  color: rgba(255, 255, 255, .9);
  text-shadow: 0 1px 5px rgba(0, 0, 0, .5);
}

.venue-intro {
  margin: 14px 0 4px;
  color: var(--text-soft);
  font-size: 14.5px;
  line-height: 1.55;
}

.venue-reasons, .venue-highlights, .venue-gallery { margin-top: 18px; }
.venue-reasons-list {
  list-style: none;
  counter-reset: venue-reason;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.venue-reasons-list li {
  counter-increment: venue-reason;
  position: relative;
  padding: 12px 14px 12px 46px;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  color: var(--text-main);
  font-size: 14px;
  line-height: 1.4;
}
.venue-reasons-list li::before {
  content: counter(venue-reason);
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-on-accent);
  background: linear-gradient(135deg, var(--accent-aurora), var(--accent-sea));
  border-radius: var(--radius-pill);
}

.venue-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.venue-chip {
  padding: 7px 13px;
  font-size: 13px;
  font-weight: 550;
  color: var(--accent-ice);
  background: rgba(47, 143, 209, .12);
  border: 1px solid rgba(47, 143, 209, .32);
  border-radius: var(--radius-pill);
}
/* Чипы внутри info-блока («Что есть», спринт Gold Standard): заголовок блока
   уже даёт отступ сверху — большой margin-top не нужен; чипы чуть компактнее. */
.venue-chips-flat { margin-top: 4px; }
.venue-chips-flat .venue-chip { padding: 5px 11px; font-size: 12.5px; }

.venue-gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 10px;
}
.venue-gallery-item {
  position: relative;
  margin: 0;
  aspect-ratio: 1 / 1;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--border-soft);
  background: linear-gradient(155deg, #123249, #0a1c2a);
}
.venue-gallery-item img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Подпись под фото (только venue-карточки, только если caption задан): одна
   строка снизу плитки поверх мягкого затемнения, длинная — с многоточием. */
.venue-gallery-cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  padding: 12px 7px 5px;
  font-size: 10.5px;
  line-height: 1.15;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background: linear-gradient(180deg, transparent, rgba(3, 8, 14, .82));
}
/* fallback: битое/отсутствующее фото → фирменный холодный северный градиент
   (глубокая бирюза → морской синий → ночь), а не «сломанная картинка» и не
   коричневый amber. Одинаково для ресторана и гостиницы, независимо от типа. */
.venue-gallery-item.no-photo,
.venue-gallery-item.no-photo[data-theme] {
  background: linear-gradient(150deg, #0f3a3a 0%, #10314a 55%, #0a1b2b 100%);
}
/* aurora-glow поверх плитки — намеренный фирменный визуал, не пустота */
.venue-gallery-item.no-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 30% 28%, rgba(52, 224, 161, .18), transparent 58%),
    radial-gradient(circle at 72% 68%, rgba(143, 216, 245, .16), transparent 60%);
}
/* Venue-hero без фото: тот же холодный северный градиент + aurora-glow
   (перекрывает type-тему amber у ресторана — специфичность выше и правило
   ниже по файлу). Читается как фирменная заставка, а не пустой прямоугольник. */
.poi-hero.poi-hero-venue.no-photo,
.poi-hero-venue.no-photo[data-theme] {
  background: linear-gradient(150deg, #0f3a3a 0%, #10314a 55%, #0a1b2b 100%);
}
.poi-hero-venue.no-photo::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(circle at 26% 24%, rgba(52, 224, 161, .20), transparent 55%),
    radial-gradient(circle at 78% 62%, rgba(143, 216, 245, .16), transparent 58%);
}

/* C: иконка типа места. По умолчанию скрыта; показывается ТОЛЬКО когда hero
   без фото (класс .no-photo — в т.ч. когда <img> упал по onerror). Так на
   пустом hero виден ненавязчивый фирменный знак (ресторан → вилка/нож,
   гостиница → дом), а при успешной загрузке фото иконки нет. */
.poi-hero-icon { display: none; }
.poi-hero.no-photo .poi-hero-icon { display: flex; }
.poi-hero-venue.no-photo .poi-hero-icon { opacity: .34; color: var(--accent-ice); z-index: 0; }
.poi-hero-venue.no-photo .poi-hero-icon .icon { width: 64px; height: 64px; }

/* Venue CTA: обычный блок в конце карточки (не fixed), чтобы не перекрывать
   смысловые блоки. Только для venue-карточек; старые POI — прежний .detail-cta-bar. */
.detail-cta-inline { margin-top: 22px; }
.detail-cta-inline .cta-row { margin-top: 0; }
.detail-cta-inline .visit-btn { margin-top: 8px; }

/* Галерея «Фото скоро добавим»: один широкий фирменный плейсхолдер вместо
   нескольких пустых плиток, пока нет реальных фото (card.galleryComingSoon). */
.venue-gallery-coming {
  position: relative;
  margin-top: 10px;
  min-height: 128px;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--border-soft);
  background: linear-gradient(150deg, #0f3a3a 0%, #10314a 55%, #0a1b2b 100%);
  display: flex;
  align-items: center;
  justify-content: center;
}
.venue-gallery-coming::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 28% 26%, rgba(52, 224, 161, .18), transparent 58%),
    radial-gradient(circle at 74% 70%, rgba(143, 216, 245, .15), transparent 60%);
}
.venue-gallery-coming-label {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 15px;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-ice);
  background: rgba(6, 13, 22, .38);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  backdrop-filter: blur(2px);
}
.venue-gallery-coming-label .icon { width: 16px; height: 16px; }
/* Вторая строка плейсхолдера (спринт Attraction Card Render): честное
   объяснение про проверку прав. Колонка + отступ — правим контейнер мягко. */
.venue-gallery-coming { flex-direction: column; gap: 8px; padding: 18px 14px; }
.venue-gallery-coming-sub {
  position: relative; z-index: 1;
  font-size: 12px; color: var(--text-muted); text-align: center; max-width: 260px;
}

/* --- Attraction-блоки (docs/ATTRACTION_CARDS.md): «Важно знать» /
   «Как добраться» / «Безопасность» / атрибуция фото / точка уточняется --- */
.attraction-note-list { margin: 4px 0 0; padding-left: 18px; }
.attraction-note-list li { margin: 4px 0; line-height: 1.45; }
.attraction-route-note { margin-top: 4px; line-height: 1.45; }
.attraction-route-note + .attraction-route-note { margin-top: 6px; }
/* Безопасность: спокойный акцент (аврора-бирюза слева), не «красная тревога». */
.attraction-safety .info-row {
  border-left: 3px solid var(--accent-aurora, #34e0a1);
  border-radius: 14px;
}
/* Атрибуция CC/Wikimedia-фото: мелко, muted, внизу карточки; ссылки — обычные. */
.photo-attribution {
  margin-top: 14px; font-size: 11.5px; line-height: 1.5; color: var(--text-muted);
}
.photo-attribution a { color: var(--text-muted); text-decoration: underline; text-underline-offset: 2px; }
/* «Точка на карте уточняется» — мягкая замена карт-CTA, не ошибка. */
.location-pending-note {
  display: flex; align-items: flex-start; gap: 10px;
  margin-top: 14px; padding: 12px 14px;
  background: var(--bg-card-glass); border: 1px dashed var(--border-soft);
  border-radius: 14px;
}
.location-pending-note .icon { width: 18px; height: 18px; margin-top: 1px; color: var(--text-muted); }
.location-pending-title { font-size: 14px; font-weight: 600; color: var(--text-soft, #c9d6e2); }
.location-pending-sub { font-size: 12.5px; color: var(--text-muted); margin-top: 2px; }

/* --- Hero-карусель (card.heroGallery): свайп + доты ---
   Спринт Hero Carousel Fix: touch-action pan-y — вертикальный scroll остаётся
   браузеру, горизонтальный жест обрабатывает JS (initHeroCarousel) на всём
   контейнере, потому что трек (z-index:0) перекрыт overlay (z-index:1). */
.poi-hero-carousel { padding: 0; touch-action: pan-y; }
.poi-hero-carousel .poi-hero-track {
  position: absolute; inset: 0; z-index: 0;
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.poi-hero-carousel .poi-hero-track::-webkit-scrollbar { display: none; }
.poi-hero-slide { position: relative; flex: 0 0 100%; scroll-snap-align: center; }
.poi-hero-slide img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.poi-hero-slide.no-photo { background: linear-gradient(150deg, #0f3a3a 0%, #10314a 55%, #0a1b2b 100%); }
.poi-hero-dots {
  position: absolute; top: 12px; left: 0; right: 0; z-index: 2;
  display: flex; justify-content: center; gap: 6px;
}
/* Доты — настоящие кнопки (было: декоративные span + pointer-events:none).
   Прозрачный border + background-clip:padding-box = тап-зона ~18–30px при
   видимой точке 6px (иначе в 6px пальцем не попасть). */
.poi-hero-dot {
  appearance: none; -webkit-appearance: none; padding: 0; cursor: pointer;
  width: 6px; height: 6px; border-radius: 50%;
  border: 6px solid transparent; box-sizing: content-box;
  background: rgba(255,255,255,.5); background-clip: padding-box;
  transition: width .2s, background .2s;
}
.poi-hero-dot.active { background: #fff; background-clip: padding-box; width: 18px; border-radius: 9px; }

/* --- Галерея: компактная горизонтальная лента превью --- */
.venue-gallery-strip {
  display: flex; gap: 8px; overflow-x: auto; margin-top: 10px; padding-bottom: 2px;
  scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.venue-gallery-strip::-webkit-scrollbar { display: none; }
.venue-gallery-thumb {
  position: relative; flex: 0 0 auto; width: 132px; height: 96px; padding: 0;
  border: 1px solid var(--border-soft); border-radius: 12px; overflow: hidden;
  cursor: pointer; scroll-snap-align: start; background: linear-gradient(155deg, #123249, #0a1c2a);
}
.venue-gallery-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.venue-gallery-thumb.no-photo { background: linear-gradient(150deg,#0f3a3a,#10314a 55%,#0a1b2b); }
.venue-gallery-thumb-cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 4px 7px;
  font-size: 10.5px; line-height: 1.15; color: #fff; text-align: left;
  background: linear-gradient(180deg, transparent, rgba(3,8,14,.85));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.venue-gallery-hint { margin-top: 6px; font-size: 11px; color: var(--text-muted); }

/* --- Lightbox: крупное фото + свайп/стрелки + подпись + «N из M» --- */
.lightbox {
  position: fixed; inset: 0; z-index: 3000; display: none;
  background: rgba(2,6,12,.94); backdrop-filter: blur(4px);
  flex-direction: column; align-items: center; justify-content: center;
}
.lightbox.open { display: flex; }
.lightbox-stage { flex: 1; width: 100%; display: flex; align-items: center; justify-content: center; padding: 48px 12px 8px; }
.lightbox-img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 10px; }
.lightbox-close, .lightbox-nav {
  position: absolute; z-index: 2; border: none; border-radius: 50%;
  background: rgba(255,255,255,.14); color: #fff; line-height: 1; cursor: pointer;
}
.lightbox-close { top: 10px; right: 12px; width: 40px; height: 40px; font-size: 26px; }
.lightbox-nav { top: 50%; transform: translateY(-50%); width: 42px; height: 42px; font-size: 26px; }
.lightbox-prev { left: 10px; } .lightbox-next { right: 10px; }
.lightbox-bar {
  width: 100%; padding: 10px 16px calc(14px + env(safe-area-inset-bottom,0px));
  display: flex; align-items: center; justify-content: space-between; gap: 12px; color: #fff;
}
.lightbox-caption { font-size: 14px; font-weight: 550; }
.lightbox-counter { font-size: 12px; color: rgba(255,255,255,.7); white-space: nowrap; }

/* --- Chooser выбора номера (два телефона) --- */
.call-chooser {
  position: fixed; inset: 0; z-index: 3100; display: flex; align-items: flex-end;
  background: rgba(2,6,12,.55); backdrop-filter: blur(2px);
}
.call-chooser-sheet {
  width: 100%; background: var(--bg-card); border-radius: 20px 20px 0 0;
  border-top: 1px solid var(--border-soft);
  padding: 14px 14px calc(14px + env(safe-area-inset-bottom,0px));
  display: flex; flex-direction: column; gap: 8px;
}
.call-chooser-title { font-size: 13px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .4px; margin: 2px 2px 4px; }
/* .call-btn и .call-chooser-btn теперь <a href="tel:"> — убираем подчёркивание ссылки */
.call-btn { text-decoration: none; }
.call-chooser-btn {
  display: flex; align-items: center; gap: 10px; padding: 13px 14px; text-decoration: none;
  border: 1px solid var(--border-soft); border-radius: 14px; background: var(--bg-card-glass);
  color: var(--text-main); font-size: 15px; font-weight: 600; cursor: pointer; text-align: left;
}
.call-chooser-btn .call-chooser-num { margin-left: auto; color: var(--text-muted); font-weight: 500; font-size: 13px; }
.call-chooser-cancel {
  margin-top: 4px; padding: 12px; border: none; border-radius: 14px;
  background: var(--bg-card-glass-strong); color: var(--text-soft); font-size: 15px; font-weight: 600; cursor: pointer;
}

/* Телефон в блоке «Контакты» — кликабелен (звонит через dialPhone) */
.contact-call {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--accent-ice); font: inherit; font-weight: 600;
  text-decoration: underline; text-underline-offset: 2px;
}
/* Контакт — двухстрочная grid-ячейка (спринт Contacts Layout Fix): label
   сверху мелким, значение под ним, copy-иконка справа по центру ячейки.
   Было: flex-wrap строка «label: значение [иконка]» — на 375px переносилась
   произвольно, иконки «плыли», длинный email/сайт упирался в кнопку.
   minmax(0,1fr) + overflow-wrap:anywhere не дают значению залезть под иконку. */
.contact-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  column-gap: 12px; row-gap: 2px;
  align-items: center;
  padding: 8px 0;
}
.contact-line + .contact-line { border-top: 1px solid var(--border-soft); }
.contact-line .contact-label {
  grid-column: 1; grid-row: 1;
  font-size: 12px; color: var(--text-muted); letter-spacing: .2px;
}
.contact-line .contact-num,
.contact-line .contact-call {
  grid-column: 1; grid-row: 2;
  min-width: 0; overflow-wrap: anywhere; justify-self: start; text-align: left;
}
.contact-line .contact-num { color: var(--text-main); font-weight: 600; letter-spacing: .3px; }
.contact-line .icon-copy-btn { grid-column: 2; grid-row: 1 / span 2; align-self: center; margin-left: 0; }
/* Маленькая иконка копирования (контакты + booking sheet) */
.icon-copy-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; flex: 0 0 auto; cursor: pointer;
  color: var(--text-muted); background: var(--bg-card-glass);
  border: 1px solid var(--border-soft); border-radius: 8px;
}
.icon-copy-btn .icon { width: 15px; height: 15px; margin: 0; }
.icon-copy-btn:active { color: var(--accent-ice); }

/* CTA брони — СПОКОЙНЫЕ кнопки (не золотые плашки), в стиле map-link-btn */
.book-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.book-btn {
  flex: 1 1 calc(50% - 4px);
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px; border-radius: var(--radius-pill); cursor: pointer;
  background: var(--bg-card-glass-strong); border: 1px solid var(--border-soft);
  color: var(--accent-ice); font-size: 14px; font-weight: 650;
}
/* Нижний блок «Гостиница: … [Скопировать номер …]» УДАЛЁН (спринт Ter
   Contacts Cleanup): контакты и копирование живут только в верхнем блоке
   «Контакты» (.contact-line + .icon-copy-btn) — без дублей. */

/* Scroll-lock фона под открытым lightbox (iOS-паттерн: фиксируем body с
   сохранением scrollY в JS; см. lockBodyScroll/unlockBodyScroll). */
body.lb-lock { position: fixed; width: 100%; overflow: hidden; }

/* Bottom sheet бронирования: номер (читаемый/выделяемый), «Позвонить» и копия */
.book-sheet {
  position: fixed; inset: 0; z-index: 3100; display: flex; align-items: flex-end;
  background: rgba(2, 6, 12, .55); backdrop-filter: blur(2px);
}
.book-sheet-panel {
  width: 100%; background: var(--bg-card); border-radius: 20px 20px 0 0;
  border-top: 1px solid var(--border-soft);
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; gap: 10px;
}
.book-sheet-title { font-size: 17px; font-weight: 700; color: var(--text-main); }
.book-sheet-label { font-size: 12px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .3px; }
.book-sheet-num-row { display: flex; align-items: center; gap: 10px; }
.book-sheet-num {
  font-size: 22px; font-weight: 700; color: var(--text-main); letter-spacing: .3px;
  user-select: all; -webkit-user-select: all;
}
.book-sheet-num-row .icon-copy-btn { margin-left: auto; width: 34px; height: 34px; }
.book-sheet-call {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px; border-radius: 14px; text-decoration: none; font-size: 15px; font-weight: 700;
  background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora)); color: var(--text-on-accent);
}
.book-sheet-site { text-align: center; padding: 8px; color: var(--accent-ice); text-decoration: none; font-weight: 600; font-size: 14px; }
.book-sheet-done {
  margin-top: 2px; padding: 12px; border: none; border-radius: 14px; cursor: pointer;
  background: var(--bg-card-glass); color: var(--text-soft); font-size: 15px; font-weight: 600;
}

@media (max-width: 360px) {
  .venue-gallery-thumb { width: 118px; height: 88px; }
  .poi-hero-venue { height: 220px; }
}

/* Маршрут как timeline: тонкая вертикальная линия связывает пронумерованные
   icon-badge точек (см. §9 задания: "точки маршрута — как steps/timeline"). */
.route-timeline { position: relative; }
.route-timeline::before {
  content: "";
  position: absolute;
  left: 17px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background: var(--border-soft);
}
.route-point {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border-soft);
  padding: 11px 0;
  color: var(--text-main);
  cursor: pointer;
  font: inherit;
}
.route-point b { display: inline-block; font-weight: 600; }
.route-point-body { flex: 1; min-width: 0; }

/* --- информационные ряды (кухня/часы/цена/адрес/контакты) --- */
/* Лёгкая общая глубина стеклянных поверхностей (не только .card) — тонкий
   внутренний блик сверху, без изменения структуры Паспорта/инфо-рядов
   (см. §8 задания: "не превращать Паспорт обратно в список карточек"). */
.info-row, .organizer, .stamp, .passport-strip, .achievement, .collection, .polar-item {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
}

.info-block { margin: 10px 0; }
.info-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  padding: 10px 12px;
  margin-bottom: 8px;
}
.info-row .icon-badge { width: 30px; height: 30px; border-radius: 10px; }
.info-row .icon-badge .icon { width: 15px; height: 15px; margin: 0; }
.info-row-body { flex: 1; min-width: 0; }
.info-title { color: var(--text-muted); font-size: 12px; margin-bottom: 2px; text-transform: uppercase; letter-spacing: .2px; }
.small { font-size: 12px; }
.info-block a, .info-row a { color: var(--accent-ice); }

.banner-closed {
  background: rgba(255, 107, 107, .1);
  border: 1px solid rgba(255, 107, 107, .35);
  color: #ff9d8a;
  border-radius: 14px;
  padding: 10px 14px;
  margin: 10px 0;
  font-weight: 600;
}
.banner-closed .small { color: var(--text-muted); font-weight: 400; margin-top: 4px; }

.error { color: #ff9d8a; }

/* Premium CTA — не стандартная Telegram-синяя кнопка. */
.visit-btn, .map-link-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  margin-top: 12px;
  border: none;
  border-radius: var(--radius-pill);
  padding: 13px;
  font-size: 15px;
  font-weight: 650;
  cursor: pointer;
}
.visit-btn {
  background: linear-gradient(135deg, var(--accent-aurora), var(--accent-sea));
  color: var(--text-on-accent);
}
.visit-btn.visited { background: rgba(52, 224, 161, .16); color: var(--accent-aurora); border: 1px solid rgba(52, 224, 161, .35); }
.visit-btn:disabled { opacity: .6; }
/* Компактный хинт-отмена внутри «Посещено» — делает откат обнаружимым, но тише
   основного статуса; высоту кнопки/бара не меняет (см. #4 safe-area). */
.visit-undo-hint {
  margin-left: 9px;
  padding-left: 9px;
  border-left: 1px solid rgba(52, 224, 161, .3);
  font-size: 12px;
  font-weight: 550;
  color: var(--text-dim);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.map-link-btn {
  background: var(--bg-card-glass-strong);
  color: var(--accent-ice);
  border: 1px solid rgba(143, 216, 245, .3);
  margin: 10px 0 4px;
}
.cta-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
/* flex-wrap + basis ~50%: при 4 действиях (карточки «Терь» с телефоном) кнопки
   аккуратно ложатся 2×2 вместо ухода 4-й кнопки за край; при 2–3 кнопках —
   как раньше. Ничего не скрываем через overflow. */
.cta-row .map-link-btn, .cta-row .call-btn { margin: 0; flex: 1 1 calc(50% - 4px); }
.call-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--bg-card-glass-strong);
  color: var(--accent-amber);
  border: 1px solid rgba(242, 182, 77, .3);
  border-radius: var(--radius-pill);
  padding: 13px;
  font-size: 15px;
  font-weight: 650;
  text-decoration: none;
  cursor: pointer;
}
.visit-btn .icon, .map-link-btn .icon, .call-btn .icon { width: 16px; height: 16px; }

/* Sticky bottom CTA — карта/звонок/посещение всегда под рукой при скролле
   длинного описания места (см. §4 задания: "sticky bottom CTA оставить"). */
/* Ряд CTA может переноситься в 2 строки (до 4 действий: На карте/Позвонить/
   Яндекс/2ГИС) → фиксированная нижняя панель становится выше. Спейсер рассчитан
   на 2 ряда, чтобы адрес/теги не прятались за панелью. На карточках с 1–2
   кнопками это лишь чуть больше пустоты у самого низа — контент не ломается. */
/* Hotfix 09k: 150px не хватало — cta-row в фикс-панели переносится на две
   строки на 375px (3 кнопки) + visit-btn, панель выше спейсера → перекрывала
   теги/текст в конце карточки (реальный iPhone-репорт). 190px + safe-area. */
.detail-cta-spacer { height: calc(190px + env(safe-area-inset-bottom, 0px)); }
.detail-cta-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 900;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(180deg, transparent, rgba(3, 8, 14, .88) 30%, rgba(3, 8, 14, .96));
  backdrop-filter: blur(10px);
}
.detail-cta-bar .cta-row { margin-top: 0; }
.detail-cta-bar .visit-btn { margin-top: 8px; }

.section-title {
  margin: 18px 0 8px;
  font-size: 13px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .4px;
  font-weight: 650;
}

.organizer { background: var(--bg-card-glass); border: 1px solid var(--border-soft); border-radius: 14px; padding: 10px 14px; margin-bottom: 8px; }
.organizer-title { font-weight: 650; }

/* ==========================================================================
   Фильтры / переключатели — dark glass pills
   ========================================================================== */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 7px 13px;
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
}
.chip .icon { width: 14px; height: 14px; }
.chip.active {
  background: linear-gradient(135deg, rgba(52, 224, 161, .9), rgba(47, 143, 209, .9));
  color: var(--text-on-accent);
  border-color: transparent;
  font-weight: 650;
}

.toggle { display: flex; gap: 6px; margin-bottom: 12px; background: var(--bg-card-glass); border: 1px solid var(--border-soft); border-radius: var(--radius-pill); padding: 4px; }
.toggle button {
  flex: 1;
  background: none;
  border: none;
  border-radius: var(--radius-pill);
  padding: 9px;
  color: var(--text-muted);
  font-size: 13.5px;
  cursor: pointer;
}
.toggle button.active { background: var(--bg-card-glass-strong); color: var(--text-main); font-weight: 650; }

.empty-state { text-align: center; padding: 40px 16px; color: var(--text-muted); }
.empty-state .icon { width: 40px; height: 40px; margin: 0 auto 12px; opacity: .6; }

/* Навигационная строка "Назад / Главная" на внутренних экранах (см. §3
   задания Reward Animation + Home Navigation). */
.page-nav-row { display: flex; gap: 8px; margin-bottom: 12px; }
.page-nav-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  font-size: 13px;
  color: var(--text-soft);
  cursor: pointer;
}
.page-nav-btn .icon { width: 14px; height: 14px; margin: 0; }
.page-nav-back .icon { transform: scaleX(-1); } /* переиспользуем "arrow" (вправо) — разворачиваем влево */
.page-nav-home { margin-left: auto; color: var(--accent-ice); border-color: rgba(143, 216, 245, .3); }

/* ==========================================================================
   Паспорт полярника + Штампы
   ========================================================================== */
/* Страница паспорта: тёплая бумага, на которую поставлены ink-печати (см.
   docs/PASSPORT_STAMP_SYSTEM.md). Тёмно-синие оттиски читаются на светлом, а не
   на тёмном фоне приложения — поэтому именно бумажная страница (просил пользователь). */
/* Страница паспорта — ТЁМНАЯ (0711c, без бумаги): панель с лёгким градиентом
   в тон приложения; светлые ink-печати читаются на тёмном. */
.passport-page {
  position: relative;
  background:
    radial-gradient(120% 90% at 50% -10%, rgba(47, 143, 209, .10), transparent 60%),
    linear-gradient(180deg, rgba(255, 255, 255, .035), rgba(255, 255, 255, .012));
  border: 1px solid rgba(255, 255, 255, .06);
  border-radius: 18px;
  padding: 14px 12px 16px;
  margin-bottom: 16px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 8px 26px rgba(0, 0, 0, .3);
  overflow: hidden;
}
.passport-page-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 12px; padding: 0 2px;
}
.passport-page-title {
  font: 700 11px/1 Georgia, "Times New Roman", serif;
  letter-spacing: 1.4px; text-transform: uppercase; color: var(--accent-ice);
}
.passport-page-count { font-size: 11px; color: var(--text-dim); font-weight: 600; }
.stamps-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px 8px; margin: 0; }
.stamp {
  background: none; border: none; aspect-ratio: auto;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  cursor: pointer; text-align: center; padding: 0; color: inherit;
}
/* Каждая печать на тёмной glass-карточке (см. §3 задания). */
.stamp-frame {
  width: 100%; aspect-ratio: 1; border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .025);
  border: 1px solid rgba(255, 255, 255, .05);
  transform: rotate(var(--rot, 0deg));
  transition: transform var(--motion-ui) var(--ease-out-premium), box-shadow var(--motion-ui) ease;
}
.stamp:active .stamp-frame { transform: rotate(var(--rot, 0deg)) scale(.93); }
.stamp:not(.stamp-locked):active .stamp-frame,
.stamp:not(.stamp-locked):hover .stamp-frame {
  box-shadow: 0 0 0 1px rgba(127, 182, 255, .25), 0 0 18px rgba(127, 182, 255, .18);
}
.stamp-img { width: 88%; height: auto; display: block; filter: drop-shadow(0 0 6px rgba(127, 182, 255, .22)); }
.stamp-name {
  font-size: 10.5px; font-weight: 600; line-height: 1.15; color: var(--text-soft);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.stamp .stamp-date { font-size: 9.5px; color: var(--text-dim); letter-spacing: .3px; }
/* «Не открыто» — пунктирный контур + минимальная иконка, не доминирует. */
.stamp-locked { cursor: default; }
.stamp-locked-ph {
  border: 1.5px dashed rgba(143, 180, 220, .3);
  background: rgba(255, 255, 255, .012);
}
.stamp-locked-ph .icon { width: 30px; height: 30px; color: rgba(143, 180, 220, .45); margin: 0; }
.stamp-locked .stamp-name { color: var(--text-dim); }
.stamp-locked .stamp-date { color: rgba(143, 180, 220, .4); }

.passport-strip {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 11px 16px;
  margin-bottom: 12px;
  color: var(--text-main);
  font-size: 13.5px;
  cursor: pointer;
}
.passport-strip .icon { width: 16px; height: 16px; color: var(--accent-aurora); flex-shrink: 0; }

.achievement {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  padding: 12px 14px;
  margin-bottom: 10px;
}
.achievement.unlocked { border-color: rgba(242, 182, 77, .3); background: linear-gradient(120deg, rgba(242, 182, 77, .08), transparent); }
.achievement.locked { background: rgba(255, 255, 255, .02); }
.achievement.locked .ach-title, .achievement.locked .ach-desc { color: var(--text-dim); }
.ach-title { font-weight: 650; }
.ach-desc { color: var(--text-soft); font-size: 13px; margin-top: 2px; }
.ach-xp { color: var(--accent-amber); font-size: 12px; margin-top: 4px; font-weight: 650; }

/* ==========================================================================
   Админ-панель (MVP, см. спринт Admin Panel MVP) — простые карточки-счётчики,
   без графиков (сознательно, см. §4 задания: "без графиков на первом MVP").
   ========================================================================== */
.admin-title { display: flex; align-items: center; gap: 8px; font-size: 19px; margin: 4px 0 12px; }
.admin-title .icon { width: 20px; height: 20px; color: var(--accent-amber); }
.admin-refresh-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 14px;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  color: var(--text-soft);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.admin-refresh-btn .icon { width: 14px; height: 14px; }
.admin-card {
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-card);
  padding: 14px 16px;
  margin-bottom: 12px;
}
.admin-card-title { font-weight: 700; font-size: 14.5px; margin-bottom: 8px; color: var(--accent-ice); }
.admin-stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 5px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 13.5px;
  color: var(--text-soft);
}
.admin-stat-row:last-child { border-bottom: none; }
.admin-stat-row b { color: var(--text-main); font-weight: 700; }
.admin-top-list { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border-soft); }
.admin-top-list-title { font-size: 12px; text-transform: uppercase; letter-spacing: .3px; color: var(--text-muted); margin-bottom: 6px; }
.admin-top-row { display: flex; justify-content: space-between; font-size: 13px; color: var(--text-soft); padding: 3px 0; }
.admin-top-row b { color: var(--text-main); }

/* Кнопка входа в админку рядом с "Все направления" — не должна выглядеть
   заметнее основной pill-кнопки, просто соседний ярлык (см. §4 задания: "не
   показывать обычным пользователям" — тем, кому не is_admin, эта кнопка вообще
   не рендерится, см. destinationSwitchPill). */
.admin-entry-btn { color: var(--accent-amber); }

.collection {
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  padding: 12px 14px;
  margin-bottom: 10px;
  opacity: .75;
}
.collection.completed { opacity: 1; border-color: rgba(52, 224, 161, .3); }
.col-head { display: flex; gap: 12px; align-items: flex-start; }
.col-title { font-weight: 650; }
.col-desc { color: var(--text-soft); font-size: 13px; margin-top: 2px; }
.progress-bar { height: 6px; border-radius: 3px; background: rgba(255, 255, 255, .1); margin-top: 10px; overflow: hidden; }
.progress-fill { height: 100%; background: linear-gradient(90deg, var(--accent-sea), var(--accent-aurora)); border-radius: 3px; }
.col-meta { color: var(--text-muted); font-size: 12px; margin-top: 6px; }

/* ==========================================================================
   Коллекции Севера — field journal mood
   ========================================================================== */
.polar-item {
  aspect-ratio: 1;
  border-radius: 16px;
  background: rgba(255, 255, 255, .035);
  border: 1px solid var(--border-soft);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
  text-align: center;
  padding: 6px;
  color: var(--text-main);
}
.polar-item .icon { width: 24px; height: 24px; color: var(--text-muted); margin: 0; }
/* common — нейтральная; дальше теплее/ярче по мере редкости (см. §7 задания) */
.polar-item[data-rarity="uncommon"] { border-color: rgba(52, 224, 161, .3); }
.polar-item[data-rarity="uncommon"] .icon { color: var(--accent-aurora); }
.polar-item[data-rarity="rare"] { border-color: rgba(143, 216, 245, .35); }
.polar-item[data-rarity="rare"] .icon { color: var(--accent-ice); }
.polar-item[data-rarity="legendary"] { border-color: rgba(242, 182, 77, .5); box-shadow: 0 0 14px rgba(242, 182, 77, .16), inset 0 0 20px rgba(242, 182, 77, .05); }
.polar-item[data-rarity="legendary"] .icon { color: var(--accent-amber); }
/* Заблокированный элемент остаётся загадочным (приглушённые цвета), но не
   "растворяется" целиком через opacity на всём блоке — так подпись и рамка
   карточки остаются читаемыми (см. §7/§11 задания Arctic Visual Redesign Fix).
   Эмодзи вида/ягоды при этом обесцвечен как силуэт — не серый текст, а
   "контур находки", которую предстоит открыть (см. §7 задания Visual System v2). */
.polar-item.locked { background: rgba(255, 255, 255, .02); }
.polar-item.locked .icon { color: var(--text-dim); opacity: .8; }
.polar-item.locked .polar-item-title { color: var(--text-dim); }
/* Смягчено (см. спринт Arctic Welcome Polish, §3): объект на дефолтном фото
   должен угадываться даже закрытым, а не тонуть в почти чёрном силуэте —
   раньше brightness(.55) в паре с opacity(.8) давало слишком тёмный композит. */
.polar-item.locked .polar-item-visual { filter: grayscale(1) brightness(.72) contrast(.95); opacity: .88; }
.polar-item.unlocked { opacity: 1; background: var(--bg-card-glass-strong); }
.polar-item-visual { display: flex; align-items: center; justify-content: center; }
.polar-item-visual img { width: 36px; height: 36px; object-fit: cover; border-radius: 10px; }
/* Открытая находка — фото заметно живее закрытой (см. §3 задания): небольшой
   буст без "кислотных" цветов, только на реальных фото (эмодзи-фолбэк этот
   класс не задевает, т.к. правило целится в img внутри .polar-item-visual). */
.polar-item.unlocked .polar-item-visual img { filter: brightness(1.14) contrast(1.08) saturate(1.06); }
.polar-item-title { font-size: 10px; color: var(--text-muted); line-height: 1.2; }

.polar-rarity { font-weight: 650; display: flex; align-items: center; gap: 6px; }
.polar-rarity .icon { width: 15px; height: 15px; }
.polar-rarity[data-rarity="common"] { color: var(--text-muted); }
.polar-rarity[data-rarity="uncommon"] { color: var(--accent-aurora); }
.polar-rarity[data-rarity="rare"] { color: var(--accent-ice); }
.polar-rarity[data-rarity="legendary"] { color: var(--accent-amber); }
.polar-hint { color: var(--text-soft); font-style: italic; }

.polar-detail-visual {
  width: 76px;
  height: 76px;
  border-radius: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  margin-bottom: 10px;
}
.polar-detail-visual .icon { width: 38px; height: 38px; color: var(--text-muted); margin: 0; }
.polar-detail-visual[data-rarity="uncommon"] { background: rgba(52, 224, 161, .1); border-color: rgba(52, 224, 161, .3); }
.polar-detail-visual[data-rarity="uncommon"] .icon { color: var(--accent-aurora); }
.polar-detail-visual[data-rarity="rare"] { background: rgba(143, 216, 245, .1); border-color: rgba(143, 216, 245, .3); }
.polar-detail-visual[data-rarity="rare"] .icon { color: var(--accent-ice); }
.polar-detail-visual[data-rarity="legendary"] { background: rgba(242, 182, 77, .14); border-color: rgba(242, 182, 77, .4); box-shadow: 0 0 18px rgba(242, 182, 77, .18); }
.polar-detail-visual[data-rarity="legendary"] .icon { color: var(--accent-amber); }

/* Группы "Коллекций Севера" — свой акцент на каждую (POLAR_GROUP_THEME в
   app.js), чтобы 5 групп визуально отличались друг от друга в рамках тех же
   4 токенов палитры, не как одинаковые серые блоки (см. §7 задания). */
.collection.polar-group { border-left: 3px solid transparent; }
.collection.polar-group[data-theme="sea"] { border-left-color: rgba(47, 143, 209, .55); }
.collection.polar-group[data-theme="ice"] { border-left-color: rgba(143, 216, 245, .55); }
.collection.polar-group[data-theme="aurora"] { border-left-color: rgba(52, 224, 161, .55); }
.collection.polar-group[data-theme="amber"] { border-left-color: rgba(242, 182, 77, .55); }

/* Личное фото — как вклеенная в дневник фотография-воспоминание (лёгкий
   поворот + "булавка" сверху), не служебный аплоад-превью (см. §7 задания). */
.polar-photo-frame {
  position: relative;
  background: linear-gradient(155deg, rgba(255,255,255,.08), rgba(255,255,255,.02));
  border: 1px solid var(--border-soft);
  border-radius: 4px 4px 16px 16px;
  padding: 10px 10px 22px;
  margin: 18px 0 8px;
  transform: rotate(-1.4deg);
  box-shadow: 0 10px 22px rgba(2, 6, 14, .5);
}
.polar-photo-frame::before {
  content: "";
  position: absolute;
  top: -7px; left: 50%;
  width: 14px; height: 14px;
  margin-left: -7px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--accent-amber), #8a5f1c 75%);
  box-shadow: 0 2px 4px rgba(0,0,0,.4);
}
.polar-photo { width: 100%; max-height: 240px; object-fit: cover; border-radius: 2px 2px 10px 10px; display: block; }

/* ==========================================================================
   Карта — Arctic Map Experience: гибрид рабочего Leaflet + брендовой
   "оболочки" travel-гида (см. спринт Arctic Map Experience Redesign).
   ========================================================================== */

/* Быстрые сценарии над картой (см. §2 задания Human Tourist Map Experience
   v1) — ОДИН компактный горизонтальный ряд вместо: переключателя Места/
   Маршруты + отдельного переключателя Схема/Точная карта + ряда "быстрых
   действий" + огромной панели чипов типов контента. Карта — экран выбора,
   а не панель настройки. */
.map-scenario-row { display: flex; gap: 8px; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 8px; padding-bottom: 2px; scrollbar-width: none; }
.map-scenario-row::-webkit-scrollbar { display: none; }
.map-scenario-pill {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 9px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-soft);
  white-space: nowrap;
  cursor: pointer;
}
.map-scenario-pill .icon { width: 15px; height: 15px; margin: 0; }
.map-scenario-pill.active { background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora)); color: var(--text-on-accent); border-color: transparent; }

/* Схема/Точная карта — компактный тумблер (см. §4 задания Human Tourist Map
   Experience v1: "сжать до минимума", "не делать большую панель"), теперь
   плавающей "стеклянной" пилюлей ПОВЕРХ карты (см. §3 задания Premium
   Illustrated Map: "не большая кнопка", "не показывать много элементов
   одновременно сверху") — тот же язык, что у .map-locate-btn (обе кнопки
   лежат в .map-wrap), а не отдельная строка над картой, отъедающая высоту.
   Leaflet zoom-контролы стоят в topleft — переключатель сознательно справа,
   чтобы не перекрываться. Показывает, во ЧТО можно переключиться (не текущий
   режим) — как обычная toggle-кнопка. */
.map-style-toggle {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 500;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--bg-card-glass-strong);
  border: 1px solid rgba(143, 216, 245, .3);
  border-radius: var(--radius-pill);
  padding: 6px 11px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-soft);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow-soft);
  cursor: pointer;
}
.map-style-toggle .icon { width: 13px; height: 13px; margin: 0; }

/* Горизонтальные пилюли выбора маршрута в режиме "Маршруты" — тот же язык,
   что у .chips, но со скроллом в один ряд (маршрутов может быть много).
   Виден только в режиме "Маршруты" (см. mapRouteRow в JS). */
.route-picker { display: flex; gap: 8px; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 12px; padding-bottom: 2px; scrollbar-width: none; }
.route-picker::-webkit-scrollbar { display: none; }
.route-pill {
  flex-shrink: 0;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  font-size: 13px;
  color: var(--text-soft);
  white-space: nowrap;
  cursor: pointer;
}
.route-pill.active { background: linear-gradient(135deg, rgba(52, 224, 161, .9), rgba(47, 143, 209, .9)); color: var(--text-on-accent); font-weight: 650; border-color: transparent; }

/* Горизонтальные пилюли выбора маршрута в режиме "Маршруты" — тот же язык,
   что у .chips, но со скроллом в один ряд (маршрутов может быть много). */
.route-picker { display: flex; gap: 8px; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 12px; padding-bottom: 2px; scrollbar-width: none; }
.route-picker::-webkit-scrollbar { display: none; }
.route-pill {
  flex-shrink: 0;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  font-size: 13px;
  color: var(--text-soft);
  white-space: nowrap;
  cursor: pointer;
}
.route-pill.active { background: linear-gradient(135deg, rgba(52, 224, 161, .9), rgba(47, 143, 209, .9)); color: var(--text-on-accent); font-weight: 650; border-color: transparent; }

#map {
  /* Уменьшено с 57vh до ~46vh (см. §3 задания Human Tourist Map Experience
     v1: "около 45-50vh, не монолит на 65vh+") — измерено вживую на 375×812:
     при 57vh кнопка "Показать, где я" в idle-состоянии шита ниже видимой
     области (812.5px из 812px viewport, см. отчёт спринта) — при 46vh она
     полностью видна без скролла вместе с заголовком и статистикой листа. */
  height: 46vh;
  border-radius: var(--radius-card);
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-soft);
}
/* Совсем невысокие экраны (iPhone SE и т.п., см. §3 задания: "можно ещё
   компактнее") — доля фиксированной "шапки" карты (сценарии/тумблер) в vh
   растёт на маленьких экранах, поэтому там нужен более агрессивный запас. */
@media (max-height: 700px) {
  #map { height: 40vh; }
}
/* Более чистая/холодная подложка: CARTO Voyager (муted, светлые тона —
   "море мягкое, суша приглушённая") + лёгкий CSS-фильтр под палитру, вместо
   тяжёлого затемнения сырых OSM-тайлов (см. §1 задания). Тайл-провайдер
   безопасно заменим — Voyager это бесплатные публичные базовые карты CARTO,
   не требующие API-ключа при разумном трафике, только атрибуция. */
#map .leaflet-tile-pane { filter: saturate(.82) brightness(.97) hue-rotate(-4deg) contrast(1.02); }
/* "Схема" (см. спринт Neon Arctic Map Upgrade, §1, донастроено в спринте
   Brand Palette + Real Routes §1) — та же CARTO-подложка, светлые тайлы
   превращены в глубокую полуночную навигационную тему чистым CSS-фильтром
   (классический invert()-трюк "светлая карта → тёмная карта", без единого
   стороннего скриншота/тайла — см. §9: "не копировать 2ГИС/Яндекс/Google").
   invert(1) переворачивает тона, hue-rotate(190deg) — подобран так, чтобы
   вода читалась холодным сине-бирюзовым (ближе к --accent-sea/--accent-ice),
   а не инвертированным оранжевым; saturate/contrast/brightness настраивают
   глубину "deep navy" и уверенность акцентов поверх тёмного ядра, не давая
   карте стать кислотной.
   Donастроено в спринте Final Map Usability Polish (§1 задания: "дороги,
   берег и структура посёлка плохо читаются") — прежние brightness(.76)/
   contrast(1.2)/saturate(2.3) после invert() гасили дороги и сушу почти до
   чёрного, из-за чего вода/земля/дороги/берег сливались в одно пятно.
   Приподнятый brightness + сниженные contrast/saturate возвращают дорогам
   и береговой линии читаемую светлую линию, оставляя воду холодной
   сине-бирюзовой, а сушу — тёмной, но не чёрной (видна структура посёлка). */
#map.map-style-scheme .leaflet-tile-pane { filter: invert(1) hue-rotate(190deg) brightness(1.05) contrast(1) saturate(1.7); }
/* Холодный "вуаль"-слой поверх тайлов, ниже маркеров/линий/контролов —
   единит светлую карту с тёмным UI бренда, не мешая интерактивности
   (pointer-events:none, z-index между tilePane(200) и overlayPane(400)). */
.map-veil {
  position: absolute;
  inset: 0;
  z-index: 350;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 50% at 50% 0%, rgba(6, 13, 22, .16), transparent 65%),
    linear-gradient(165deg, rgba(20, 42, 62, .16), rgba(8, 20, 32, .07));
  mix-blend-mode: multiply;
}
/* Едва заметная "техническая" сетка (см. §1 задания: "опционально —
   технологичный grid-паттерн") — намёк на travel-tech продукт, а не игровую
   фэнтези-карту. Очень низкая непрозрачность и крупный шаг, чтобы не спорить
   с пинами/подписями за внимание; видна только в "Схеме". */
.map-scheme-grid {
  position: absolute;
  inset: 0;
  z-index: 355;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s ease;
  background-image:
    linear-gradient(rgba(143, 216, 245, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(143, 216, 245, .05) 1px, transparent 1px);
  background-size: 44px 44px;
}
#map.map-style-scheme .map-scheme-grid { opacity: 1; }
/* Более выраженный арктический тон в "Схеме" — аврора/лёд-градиент + мягкая
   "аврора"-вуаль поверх тёмного ядра (см. §1 задания: "soft premium outer
   glow/bloom", "optional aurora veil"), плавно появляется/исчезает при
   переключении режима. */
.map-scheme-veil {
  position: absolute;
  inset: 0;
  z-index: 360;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s ease;
  /* Сине-бирюзовый (sea/ice) акцент усилен относительно аврора-зелёного — вода
     должна читаться уверенным холодным cold-blue/cyan (см. §1 задания Brand
     Palette + Real Routes), аврора остаётся мягким фоновым сопровождением. */
  background:
    /* Широкая мягкая лента вдоль самого верха карты — визуальный отголосок
       северного сияния из референса (см. спринт Premium Illustrated Map,
       §1: "лёгкое северное сияние в верхней части карты"), не буквальная
       иллюстрация неба, просто акцентный цветовой градиент поверх тайлов. */
    radial-gradient(ellipse 90% 30% at 50% -8%, rgba(52, 224, 161, .22), transparent 68%),
    radial-gradient(ellipse 65% 42% at 14% 6%, rgba(52, 224, 161, .13), transparent 62%),
    radial-gradient(ellipse 62% 50% at 90% 92%, rgba(47, 143, 209, .26), transparent 68%),
    radial-gradient(ellipse 52% 38% at 60% -6%, rgba(143, 216, 245, .17), transparent 60%),
    linear-gradient(160deg, rgba(6, 13, 22, .12), rgba(8, 20, 32, .03));
}
#map.map-style-scheme .map-scheme-veil { opacity: 1; }
/* Едва заметная звёздная россыпь в верхней части "Схемы" (см. §1 задания:
   "subtle... texture, чтобы не было плоской пустоты") — читается вместе с
   аврора-лентой выше как ночное северное небо, без единого файла-ассета:
   несколько крошечных радиальных градиентов вместо картинки/текстуры. */
.map-scheme-stars {
  position: absolute;
  inset: 0;
  z-index: 356;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s ease;
  background-image:
    radial-gradient(1.5px 1.5px at 20% 15%, rgba(255,255,255,.5), transparent),
    radial-gradient(1px 1px at 65% 8%, rgba(255,255,255,.4), transparent),
    radial-gradient(1.5px 1.5px at 82% 22%, rgba(255,255,255,.35), transparent),
    radial-gradient(1px 1px at 40% 5%, rgba(255,255,255,.4), transparent),
    radial-gradient(1px 1px at 90% 14%, rgba(255,255,255,.3), transparent),
    radial-gradient(1.5px 1.5px at 8% 28%, rgba(255,255,255,.22), transparent),
    radial-gradient(1px 1px at 52% 24%, rgba(255,255,255,.25), transparent);
  background-repeat: no-repeat;
  background-size: 100% 46%;
}
#map.map-style-scheme .map-scheme-stars { opacity: 1; }
#map .leaflet-control-zoom a {
  background: var(--bg-card-glass-strong) !important;
  color: var(--text-main) !important;
  border-color: var(--border-soft) !important;
  backdrop-filter: blur(6px);
}
.map-wrap { position: relative; }

/* "Где я" (см. §1 задания Map Usability) — плавающая кнопка поверх карты,
   привычное место (снизу справа, над bottom sheet), как в стандартных
   картах. Pending-состояние — лёгкий пульс, пока идёт getCurrentPosition. */
.map-locate-btn {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 500;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--bg-card-glass-strong);
  border: 1px solid var(--border-soft);
  color: var(--text-main);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.map-locate-btn .icon { width: 19px; height: 19px; margin: 0; }
@keyframes mapLocatePending { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
.map-locate-btn-pending .icon { animation: mapLocatePending 1s ease-in-out infinite; color: var(--accent-aurora); }

/* Маркер пользователя "Вы здесь" — явно другой язык, чем POI-пины (сплошной
   аврора-круг с пульсирующим ореолом), чтобы не путать со списком мест. */
.map-user-marker { width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; }
.map-user-marker-dot {
  position: relative;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent-aurora);
  border: 2px solid rgba(6, 13, 22, .9);
  box-shadow: 0 0 0 3px rgba(52, 224, 161, .35);
}
.map-user-marker-dot::after {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  background: rgba(52, 224, 161, .28);
  animation: mapUserPulse 1.8s ease-out infinite;
}
@keyframes mapUserPulse {
  0% { transform: scale(.4); opacity: .8; }
  100% { transform: scale(1.6); opacity: 0; }
}

/* Брендовая система маркеров: тёмное ядро + акцентный контур + иконка —
   не просто иконки поверх Leaflet, а часть визуального языка NordTrip
   (см. §3 задания). */
.map-pin-wrap { background: transparent; border: none; }
.map-pin {
  background: rgba(8, 16, 26, .93);
  border: 2.5px solid var(--pin-color, var(--accent-sea));
  border-radius: 50%;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--pin-color, var(--accent-sea));
  box-shadow: 0 3px 10px rgba(0,0,0,.4), inset 0 0 0 1px rgba(255,255,255,.05);
  transition: transform .15s ease, opacity .2s ease, filter .2s ease;
}
.map-pin .icon { width: 15px; height: 15px; margin: 0; }
.map-pin-numbered { font-weight: 700; font-size: 13px; }
.map-pin-closed { opacity: .55; border-color: #ff6b6b; border-style: dashed; color: #ff6b6b; }
/* Разрежение "кластера" по zoom без библиотеки кластеризации — на
   отдалённом zoom одиночные маркеры чуть мельче (см. §3 задания). Настоящее
   decluttering скученных точек делает JS-бакетинг (renderPlacesLayer) —
   схлопывает близкие маркеры в count bubble. */
#map.map-zoomed-out .map-pin { transform: scale(.82); }
/* marker.getElement() у L.divIcon возвращает внешний .map-pin-wrap, а не
   внутренний .map-pin — классы "выбран"/"приглушён" вешаем на wrap. z-index
   у Leaflet выставлен инлайново (по широте маркера) — перебиваем !important. */
.map-pin-wrap.map-pin-selected { z-index: 1000 !important; }
.map-pin-wrap.map-pin-selected .map-pin {
  transform: scale(1.28);
  border-color: var(--accent-aurora);
  box-shadow: 0 0 0 5px rgba(52, 224, 161, .22), 0 0 20px rgba(52, 224, 161, .38), 0 4px 14px rgba(0,0,0,.5);
}
#map.map-zoomed-out .map-pin-wrap.map-pin-selected .map-pin { transform: scale(1.28); }
.map-pin-wrap-dimmed .map-pin { opacity: .4; filter: grayscale(.4); }

/* Count bubble — простое ручное decluttering: маркеры, попавшие в одну
   экранную ячейку на текущем zoom, схлопываются в один "+N" (см. §3
   задания: "без тяжёлых внешних библиотек кластеризации"). */
.map-cluster-bubble {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(145deg, rgba(47, 143, 209, .95), rgba(52, 224, 161, .88));
  color: var(--text-on-accent);
  font-weight: 750;
  font-size: 13px;
  border: 2px solid rgba(255, 255, 255, .32);
  box-shadow: 0 4px 14px rgba(0,0,0,.4), 0 0 0 4px rgba(52, 224, 161, .12);
  cursor: pointer;
}
/* "Схема" (см. §6 задания Illustrated Tourist Map v1, доработано в Neon
   Arctic Map Upgrade §4) — кластер называется по-человечески ("12 мест"/
   "Еда"/"Природа"), а не техническим "+12", и оформлен как "стеклянная"
   неоновая пилюля (backdrop-blur + мягкое свечение), а не плоский кружок —
   на тёмной карте это читается как premium travel-tech UI, не аркадный
   бабл. Режим "Карта" (без этого класса) не меняется вовсе. */
.map-cluster-bubble-scheme {
  width: auto;
  min-width: 64px;
  height: 36px;
  border-radius: var(--radius-pill);
  padding: 0 14px;
  font-size: 12.5px;
  white-space: nowrap;
  background: linear-gradient(145deg, rgba(47, 143, 209, .82), rgba(52, 224, 161, .76));
  backdrop-filter: blur(7px);
  border: 1px solid rgba(255, 255, 255, .4);
  box-shadow: 0 4px 16px rgba(0,0,0,.45), 0 0 0 1px rgba(52, 224, 161, .18), 0 0 18px rgba(47, 143, 209, .35);
}

/* Крупный дружелюбный пин "Схемы" — узнаваемый значок-метка вместо тихой
   навигационной точки (см. §5 задания Illustrated Tourist Map v1), крупнее и
   с фирменным неоновым свечением (см. §1/§4 Neon Arctic Map Upgrade: "яркие
   неоновые пины", "premium bloom, не дешёвый неон"). drop-shadow вместо
   box-shadow — мягкое цветное свечение вокруг круглой формы, не квадратный
   ореол. Режим "Карта" использует прежний .map-pin без изменений. */
.map-pin-scheme {
  position: relative;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: rgba(6, 12, 20, .96);
  border: 3.5px solid var(--pin-color, var(--accent-sea));
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--pin-color, var(--accent-sea));
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08), inset 0 0 10px rgba(255,255,255,.04);
  filter: drop-shadow(0 4px 9px rgba(0,0,0,.5)) drop-shadow(0 0 8px var(--pin-color, var(--accent-sea)));
  transition: transform .15s ease, opacity .2s ease, filter .2s ease;
}
.map-pin-scheme::after {
  content: "";
  position: absolute;
  bottom: -7px;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 8px solid var(--pin-color, var(--accent-sea));
}
.map-pin-scheme .icon { width: 24px; height: 24px; margin: 0; }
.map-pin-scheme.map-pin-closed { opacity: .55; border-color: #ff6b6b; border-style: dashed; color: #ff6b6b; }
.map-pin-scheme.map-pin-closed::after { border-top-color: #ff6b6b; }
/* Выбранная точка — заметно сильнее свечение (см. §4 задания: "у выбранного
   маркера — сильный, очевидный glow; у остальных — спокойнее"), у остальных
   пинов свечение остаётся скромным ambient-эффектом из .map-pin-scheme выше. */
.map-pin-wrap.map-pin-selected .map-pin-scheme {
  transform: scale(1.22);
  border-color: var(--accent-aurora);
  filter:
    drop-shadow(0 4px 10px rgba(0,0,0,.55))
    drop-shadow(0 0 8px var(--accent-aurora))
    drop-shadow(0 0 20px var(--accent-aurora));
}
.map-pin-wrap-dimmed .map-pin-scheme { opacity: .45; filter: grayscale(.35); }

/* Подписи зон (см. §3-4 задания Illustrated Tourist Map v1: "Териберка"/
   "Лодейное"/"Берег") — просто ориентировочный текст, привязанный к реальным
   координатам через Leaflet (двигается вместе с картой корректно, без
   отдельного синхронизируемого SVG-слоя), не кликабелен. Лёгкое ледяное
   свечение (см. §4 Neon Arctic Map Upgrade: "подписи территорий видны, но не
   кричат") вместо плоской белой обводки. */
.map-zone-label-wrap { background: transparent; border: none; }
.map-zone-label {
  display: inline-block;
  font-size: 12px;
  font-weight: 750;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: rgba(210, 238, 250, .92);
  text-shadow: 0 1px 4px rgba(0,0,0,.7), 0 0 12px rgba(143, 216, 245, .35);
  white-space: nowrap;
  pointer-events: none;
}

/* Постоянные подписи важных точек в "Схеме" (см. §5 задания Illustrated
   Tourist Map v1, доработано в §4 Neon Arctic Map Upgrade) — используем
   Leaflet tooltip вместо ручного HTML-слоя, перекрашиваем под неоновую тему
   (по умолчанию у Leaflet — белый фон + чёрная стрелка) с тонким ледяным
   контуром вместо нейтральной серой рамки. */
.leaflet-tooltip.map-poi-label {
  background: rgba(6, 12, 20, .88);
  border: 1px solid rgba(143, 216, 245, .3);
  color: var(--text-main);
  font-size: 11px;
  font-weight: 650;
  padding: 3px 8px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,.4), 0 0 10px rgba(143, 216, 245, .18);
}
.leaflet-tooltip.map-poi-label::before { display: none; }

/* ==========================================================================
   Bottom sheet — единая "нижняя карточка" travel-гида вместо технической
   легенды: idle (обзор области) / POI (выбранное место) / route (маршрут)
   переключаются подменой содержимого одного и того же докед-контейнера
   (см. §2 задания — не отдельные попапы, а один продуктовый компонент).
   ========================================================================== */
.map-sheet {
  position: relative;
  margin-top: 10px;
  background:
    radial-gradient(ellipse 220px 90px at 12% -25%, rgba(52, 224, 161, .09), transparent 60%),
    rgba(10, 20, 32, .96);
  /* Polish 09l: blur(12px) при фоне .94 почти не виден, но заставляет WebView
     пересчитывать размытие при КАЖДОМ пане/зуме карты под sheet'ом — убран,
     непрозрачность чуть поднята (.96). */
  border: 1px solid var(--border-soft);
  border-radius: 18px;
  padding: 14px;
  box-shadow: var(--shadow-soft), inset 0 1px 0 rgba(255, 255, 255, .05);
  /* Явно разрешаем вертикальный скролл жестом (см. §6 задания) — карточка
     не должна конкурировать с Leaflet-жестами карты выше неё. */
  touch-action: pan-y;
}
.map-sheet-title { font-weight: 700; font-size: 15.5px; }
.map-sheet-copy { color: var(--text-soft); font-size: 13px; margin-top: 4px; line-height: 1.45; }
.map-sheet-close {
  position: absolute; top: 10px; right: 10px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--bg-card-glass-strong); color: var(--text-muted);
  display: flex; align-items: center; justify-content: center;
  border: none; cursor: pointer; font-size: 15px; line-height: 1;
}

/* --- idle: обзор области --- */
.map-sheet-idle-eyebrow { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: var(--accent-ice); font-weight: 700; opacity: .85; }
.map-sheet-stats { display: flex; align-items: center; gap: 14px; margin-top: 8px; color: var(--text-muted); font-size: 12.5px; }
.map-sheet-stats .icon { width: 13px; height: 13px; color: var(--accent-ice); }
.map-sheet-cta {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%;
  margin-top: 12px;
  background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora));
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--radius-pill);
  padding: 12px;
  font-size: 14px;
  font-weight: 650;
  cursor: pointer;
}
.map-sheet-cta .icon { width: 15px; height: 15px; }
/* CTA "Показать меня" (см. §4/§7 задания Illustrated Tourist Map v1) —
   вторичный по значимости к "Посмотреть маршруты", поэтому контурный, а не
   сплошной градиент, чтобы оба CTA не спорили за внимание, если видны разом. */
.map-sheet-locate-cta {
  background: var(--bg-card-glass-strong);
  color: var(--accent-ice);
  border: 1px solid rgba(143, 216, 245, .3);
}
/* Яндекс/2ГИС под "Открыть маршрут" (см. §5 задания Final Map Usability
   Polish: "кнопки слишком крупные") — раньше это были ещё две полноразмерные
   .map-sheet-cta ПОД ОСНОВНОЙ, каждая на всю ширину карточки; теперь общий
   .map-sheet-cta задаёт только форму/отступ, а -ghost сжимает их вдвое (общая
   строка, компактный паддинг/шрифт) и убирает сплошной градиент — основной
   CTA остаётся один явный крупный акцент, эти двое — вспомогательные. */
.map-sheet-route-nav-row { display: flex; gap: 8px; margin-top: 8px; }
.map-sheet-cta-ghost {
  width: auto;
  flex: 1 1 0;
  margin-top: 0;
  padding: 9px 8px;
  font-size: 12.5px;
  background: var(--bg-card-glass-strong);
  color: var(--accent-ice);
  border: 1px solid rgba(143, 216, 245, .3);
}
.map-sheet-cta-ghost .icon { width: 13px; height: 13px; }
/* Отдельная карточка «Посмотреть маршруты» в idle-плашке карты (см. §6 задания
   0711c): раньше переиспользовала -ghost (flex-child, margin-top:0) и без отступа
   налезала на список мест. Теперь — самостоятельная secondary-карточка с иконкой
   маршрута, чётким верхним отступом и высотой ~50px (не перекрывает список). */
.map-sheet-routes-cta {
  width: 100%;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
  padding: 14px 16px;
  min-height: 50px;
  background: var(--bg-card-glass-strong);
  color: var(--accent-ice);
  border: 1px solid rgba(143, 216, 245, .3);
  font-size: 14px;
}
.map-sheet-routes-cta > span { flex: 1; text-align: left; }
.map-sheet-routes-cta .icon { width: 16px; height: 16px; flex-shrink: 0; }

/* "Рядом с вами" (см. §2 задания Map Usability) — переиспользует
   .map-sheet-cluster-row/-list; отдельный заголовок блока не нужен — теперь
   это сам заголовок sheet'а (см. §7 задания Illustrated Tourist Map v1). */
.map-sheet-nearby { margin-top: 14px; }

/* --- POI: выбранное место --- */
.map-sheet-poi { display: flex; gap: 12px; align-items: flex-start; padding-right: 20px; }
/* Тонкое цветное кольцо по теме категории (та же переменная --pin-color, что
   и у пинов на карте, см. §6 задания Premium Illustrated Map) — визуал в
   карточке читается как продолжение цвета пина, а не нейтральный квадрат. */
.map-sheet-poi-visual {
  position: relative;
  width: 56px;
  height: 56px;
  border-radius: 13px;
  flex-shrink: 0;
  overflow: hidden;
  border: 2px solid var(--pin-color, var(--border-soft));
  box-shadow: 0 3px 10px rgba(0,0,0,.35);
}
.map-sheet-poi-visual img { width: 100%; height: 100%; object-fit: cover; display: block; }
.map-sheet-poi-visual .visual-scene .icon { width: 22px; height: 22px; }
.map-sheet-poi-visual .visual-scene .icon.icon-ghost { display: none; }
.map-sheet-poi-body { flex: 1; min-width: 0; }
.map-sheet-poi-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.map-sheet-poi-type { color: var(--text-muted); font-size: 11px; text-transform: uppercase; letter-spacing: .2px; margin-top: 1px; }
.map-sheet-poi-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
/* Вынесены на всю ширину карточки, за пределы узкой текстовой колонки (см.
   §5 задания Final Map Usability Polish) — раньше "Открыть"/"Маршрут в
   Яндекс"/"Открыть в 2ГИС" жили в ~225px колонке рядом с превью-картинкой и
   складывались в три отдельные строки почти на всю высоту карточки. */
.map-sheet-poi-actions { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.map-sheet-poi-nav-row { display: flex; gap: 6px; }
.map-sheet-poi-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  background: var(--bg-card-glass-strong);
  color: var(--accent-ice);
  border: 1px solid rgba(143, 216, 245, .3);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  font-size: 12.5px;
  font-weight: 650;
  cursor: pointer;
}
.map-sheet-poi-btn .icon { width: 13px; height: 13px; }
/* Яндекс/2ГИС рядом друг с другом в общей строке ниже "Открыть" — вдвое
   компактнее, чем раньше (по отдельной строке на каждую кнопку). */
.map-sheet-poi-nav-row .map-sheet-poi-btn-ghost { flex: 1 1 0; }

/* --- cluster: "В этой зоне: N мест" вместо тихого зума по bubble (см. §4
   задания Scenario-first Travel UX) --- */
.map-sheet-cluster-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  max-height: 260px;
  overflow-y: auto;
  /* Внутренний скролл списка не должен "протекать" в жест сворачивания
     Mini App на iOS (см. §6 задания) — тот же приём, что и в discovery-modal. */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  touch-action: pan-y;
}
.map-sheet-cluster-row {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  text-align: left;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 8px 10px;
  cursor: pointer;
}
.map-sheet-cluster-row .icon-badge { width: 32px; height: 32px; border-radius: 10px; }
.map-sheet-cluster-row .icon-badge .icon { width: 15px; height: 15px; }
.map-sheet-cluster-row-body { flex: 1; min-width: 0; }
.map-sheet-cluster-row-title { font-size: 13.5px; font-weight: 600; }
.map-sheet-cluster-row-type { color: var(--text-muted); font-size: 11px; text-transform: uppercase; letter-spacing: .2px; margin-top: 1px; }

/* --- route: карточка выбранного маршрута --- */
.map-sheet-route-header { display: flex; gap: 12px; align-items: center; }
.map-sheet-route-meta { color: var(--text-muted); font-size: 12px; margin-top: 2px; }
/* Честная оговорка (см. §5 задания Neon Arctic Map Upgrade: линия маршрута —
   декоративный порядок точек, не буквальный путь по земле/воде). */
.map-sheet-route-disclaimer {
  display: flex;
  gap: 6px;
  align-items: flex-start;
  margin-top: 10px;
  padding: 8px 10px;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  color: var(--text-muted);
  font-size: 11.5px;
  line-height: 1.4;
}
.map-sheet-route-disclaimer .icon { width: 13px; height: 13px; flex-shrink: 0; margin-top: 1px; color: var(--accent-ice); }

/* Декоративный коннектор маршрута в "Схеме" (см. §5 задания: точки идут по
   порядку — 1, 2, 3 — соединённые мягкой светящейся линией, которая
   сознательно НЕ похожа на настоящую тропу/дорогу, чтобы не создавать
   впечатление "иди по воде"). Лёгкая анимация пунктира читается как
   "направление следования", а не проложенный маршрут. */
.map-route-line-scheme {
  stroke-dasharray: 1 9;
  stroke-linecap: round;
  animation: mapRouteFlow 1.6s linear infinite;
  filter: drop-shadow(0 0 4px var(--accent-amber));
}
@keyframes mapRouteFlow { to { stroke-dashoffset: -20; } }

/* Реальная геометрия участка (см. §4 задания Brand Palette + Real Routes) —
   сплошная светящаяся линия по настоящим дорогам/тропам (данные из OSRM/GPX,
   визуал полностью наш — см. §4: "не копировать чужие тайлы"), явно
   отличается от пунктирного декоративного коннектора: солиднее, ровнее,
   читается как "это действительно путь", а не условная связь точек. */
.map-route-line-real {
  filter: drop-shadow(0 0 3px var(--accent-aurora)) drop-shadow(0 0 9px rgba(52, 224, 161, .45));
}

/* ==========================================================================
   Маршруты — expedition cards
   ========================================================================== */
.route-meta-row { display: flex; align-items: center; gap: 14px; margin-top: 8px; flex-wrap: wrap; }
.route-meta-item { display: flex; align-items: center; gap: 5px; color: var(--text-muted); font-size: 12.5px; }
.route-meta-item .icon { width: 14px; height: 14px; color: var(--accent-ice); }
/* тонкая декоративная "тропа" — тире + точка, компас-мотив, без грубых emoji */
.route-path-deco { display: flex; align-items: center; gap: 4px; color: var(--text-muted); opacity: .5; margin: 6px 0; }
.route-path-deco .icon { width: 14px; height: 14px; }

/* Карточка маршрута в списке — визуальная зона с пунктирной "тропой" вместо
   плоской строки таблицы, travel-сценарий, а не запись каталога (см. §3
   задания Scenario-first Travel UX). */
.route-card { padding: 0; overflow: hidden; }
.route-card-visual { height: 92px; border-radius: var(--radius-card) var(--radius-card) 0 0; }
.route-card-visual .icon { width: 30px; height: 30px; }
.route-card-visual .icon.icon-ghost { width: 68px; height: 68px; }
.route-card .card-body { padding: 12px 16px 14px; position: relative; }
/* Декоративная "линия пути" — точки-остановки поверх тонкой пунктирной
   линии, ощущение путешествия вместо строки таблицы. */
.route-line-preview { display: flex; align-items: center; margin-top: 9px; opacity: .8; }
.route-line-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-ice); flex-shrink: 0; }
.route-line-seg { flex: 1; min-width: 10px; height: 1px; margin: 0 4px; background: repeating-linear-gradient(90deg, var(--border-soft) 0 4px, transparent 4px 8px); }
.route-line-more { color: var(--text-muted); font-size: 11px; margin-left: 4px; white-space: nowrap; }
.route-key-points { color: var(--text-muted); font-size: 12px; margin-top: 5px; }
.route-card-cta { display: flex; align-items: center; gap: 6px; color: var(--accent-ice); font-size: 13px; font-weight: 650; margin-top: 11px; }
.route-card-cta .icon { width: 13px; height: 13px; margin: 0; }

/* Route hero в деталях маршрута — как у POI, только с маршрутной темой. */
.route-hero {
  position: relative;
  width: 100%;
  height: 150px;
  border-radius: var(--radius-card);
  margin-bottom: 12px;
  overflow: hidden;
  border: 1px solid var(--border-soft);
}
.route-hero img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Заметная карточка-CTA "на карту" над списком точек — не просто ссылка
   (см. §9 задания: "карта маршрута выше списка точек"). */
.route-map-cta {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  background: var(--bg-card-glass);
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  padding: 12px 14px;
  margin: 4px 0 14px;
  cursor: pointer;
  overflow: hidden;
}
.route-map-cta::before {
  content: "";
  position: absolute; inset: 0;
  background-image: radial-gradient(circle, rgba(143, 216, 245, .18) 1.4px, transparent 1.6px);
  background-size: 13px 13px;
  mask-image: linear-gradient(90deg, transparent, #000 55%);
}
.route-map-cta-title { font-weight: 650; font-size: 14.5px; position: relative; }
.route-map-cta-sub { color: var(--text-muted); font-size: 12px; margin-top: 2px; position: relative; }
.route-map-cta .icon-badge { position: relative; }
.route-map-cta .card-arrow { position: relative; right: 0; bottom: 0; margin-left: auto; }

#toasts {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 1000;
  pointer-events: none;
}
.toast {
  background: linear-gradient(135deg, rgba(52, 224, 161, .95), rgba(47, 143, 209, .95));
  color: var(--text-on-accent);
  font-weight: 600;
  padding: 11px 16px;
  border-radius: 14px;
  font-size: 14px;
  box-shadow: var(--shadow-soft);
  animation: toast-in .2s ease-out;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* ==========================================================================
   Reward overlay (см. спринт Reward Animation + Home Navigation) — момент
   "получено" для посещений/находок/ачивок/разворотов. Один персистентный
   full-screen узел, содержимое подменяется под конкретное событие.
   ========================================================================== */
.reward-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(4, 9, 16, 0);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-ui) ease, background var(--motion-ui) ease;
}
.reward-overlay.visible {
  opacity: 1;
  pointer-events: auto;
  /* Hotfix 09k: full-screen backdrop-filter на iOS Telegram «залипал» размытым
     экраном на переходах между наградами (реальный iPhone-репорт) и дорог для
     WebView (skill motion-design §constraints). Плотный фон вместо блюра. */
  background: rgba(4, 9, 16, .88);
}
.reward-card {
  position: relative;
  width: 100%;
  max-width: 320px;
  overflow: hidden;
  background:
    radial-gradient(ellipse 240px 140px at 50% -10%, rgba(52, 224, 161, .16), transparent 60%),
    rgba(10, 20, 32, .97);
  border: 1px solid var(--border-soft);
  border-radius: 24px;
  padding: 30px 22px 24px;
  text-align: center;
  box-shadow: var(--shadow-soft), 0 0 42px rgba(52, 224, 161, .1);
  transform: translateY(-20px) scale(.82) rotate(-2deg);
  opacity: 0;
  transition: transform .34s cubic-bezier(.34, 1.56, .64, 1), opacity .22s ease;
}
.reward-overlay.visible .reward-card {
  transform: translateY(0) scale(1) rotate(0deg);
  opacity: 1;
}
/* Мягкая пульсация аврора-свечения за карточкой — тонко, не отвлекает от текста. */
.reward-glow {
  position: absolute;
  inset: -30% -25% auto -25%;
  height: 60%;
  background: radial-gradient(ellipse, rgba(52, 224, 161, .32), transparent 70%);
  filter: blur(22px);
  opacity: .4;
  pointer-events: none;
  animation: reward-pulse 2.6s ease-in-out infinite;
}
@keyframes reward-pulse {
  0%, 100% { opacity: .3; }
  50% { opacity: .65; }
}
.reward-visual {
  position: relative;
  width: 84px;
  height: 84px;
  margin: 0 auto 16px;
  border-radius: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 1px var(--border-soft), var(--shadow-soft);
}
.reward-visual .icon { width: 40px; height: 40px; margin: 0; filter: drop-shadow(0 0 10px currentColor); }
.reward-visual .icon-emoji { font-size: 40px; margin: 0; line-height: 1; }
.reward-visual[data-theme="amber"] { background: linear-gradient(155deg, #4a3413, #241a0c); color: var(--accent-amber); }
.reward-visual[data-theme="ice"] { background: linear-gradient(155deg, #163244, #0c1c28); color: var(--accent-ice); }
.reward-visual[data-theme="aurora"] { background: linear-gradient(155deg, #12362c, #0b1e19); color: var(--accent-aurora); }
.reward-visual[data-theme="sea"] { background: linear-gradient(155deg, #123249, #0a1c2a); color: var(--accent-sea); }
/* Discovery-награда (см. §6 задания Discovery Modal Cards) переиспользует
   .visual-scene (силуэт + ghost-иконка на текстуре) вместо плоской иконки —
   те же паттерны сцен, что и на самой карточке открытия. Оверрайд размеров
   под компактный 84x84 бейдж награды (в .visual-scene по умолчанию иконки
   рассчитаны на более крупные карточки списков). */
.reward-visual.visual-scene { overflow: hidden; }
.reward-visual.visual-scene .icon { width: 40px; height: 40px; }

/* --- Passport stamp v1 (спринт UI/Motion/Gamification audit) ---
   Печать за визит: круглый ink-бейдж с двойным кольцом «проштамповывается»
   (scale .86→1.06→1, rotate −4°→1°→0°, чернильная opacity) — токены motion.
   transform/opacity only; will-change не постоянный (снимается с концом
   анимации, т.к. animation конечная). Не казино: 4 микро-«инея», без конфетти. */
.reward-visual.reward-stamp {
  background: none;
  border-radius: 50%;
  overflow: visible;
}
.reward-stamp-ring {
  width: 84px; height: 84px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 3px solid currentColor;
  position: relative;
  background:
    radial-gradient(circle at 32% 28%, rgba(255,255,255,.10), transparent 55%),
    radial-gradient(circle at 68% 74%, rgba(0,0,0,.28), transparent 60%);
  box-shadow: 0 4px 18px rgba(2, 6, 14, .5), inset 0 0 14px rgba(0,0,0,.35);
  /* «Прижим» печати (2026-07-11, апгрейд эмоции): раньше печать МЯГКО вырастала
     (ease-out 900ms) — ощущалась «появлением картинки», не ударом. Теперь она
     заносится над бумагой (scale 1.7), УСКОРЯЯСЬ падает (per-keyframe ease-in) и
     БЬЁТ с overshoot ниже 1 в ~55% таймлайна, затем осадка с мелким отскоком.
     Задержка 120ms — карточка успевает встать (антиципация), а момент касания
     синхронен с тяжёлым haptic (STAMP_IMPACT_MS = 120 + 0.55*620 в app.js). */
  animation: reward-stamp-press 620ms both;
  animation-delay: 120ms;
  will-change: transform;
}
/* Внутреннее пунктирное кольцо — «зубчики» печати. */
.reward-stamp-ring::after {
  content: "";
  position: absolute; inset: 5px;
  border-radius: 50%;
  border: 1.5px dashed currentColor;
  opacity: .65;
}
/* Ударная волна «чернил»: появляется ТОЛЬКО в момент касания и расходится
   кольцом наружу — читаемый акцент «печать легла», без конфетти. */
.reward-stamp-ring::before {
  content: "";
  position: absolute; inset: -7px;
  border-radius: 50%;
  border: 2px solid currentColor;
  opacity: 0;
  pointer-events: none;
  animation: reward-stamp-shock 620ms ease-out both;
  animation-delay: 120ms;
}
.reward-stamp-ring .icon { width: 36px; height: 36px; margin: 0; filter: drop-shadow(0 0 8px currentColor); }
@keyframes reward-stamp-press {
  0%   { transform: scale(1.7) rotate(-9deg);  opacity: 0; animation-timing-function: cubic-bezier(.5, 0, .85, .4); }
  30%  { opacity: 1; }
  55%  { transform: scale(.9) rotate(2deg);    opacity: 1; animation-timing-function: cubic-bezier(.2, .75, .3, 1); }
  72%  { transform: scale(1.05) rotate(-1deg); }
  87%  { transform: scale(.985) rotate(0deg); }
  100% { transform: scale(1) rotate(0deg);     opacity: 1; }
}
@keyframes reward-stamp-shock {
  0%, 48% { transform: scale(.82); opacity: 0; }
  56%     { transform: scale(.94); opacity: .5; }
  100%    { transform: scale(1.55); opacity: 0; }
}
/* Frost specks: 4 CSS-точки, вылетают в МОМЕНТ удара печати (≈460ms). */
.reward-speck {
  position: absolute;
  width: 4px; height: 4px; border-radius: 50%;
  background: currentColor;
  opacity: 0;
  animation: reward-speck-drift 620ms var(--ease-out-premium) both;
  animation-delay: 440ms;
  pointer-events: none;
}
.reward-speck:nth-child(2) { top: 6px; left: 12px; }
.reward-speck:nth-child(3) { top: 12px; right: 8px; animation-delay: 500ms; }
.reward-speck:nth-child(4) { bottom: 10px; left: 6px; animation-delay: 470ms; }
.reward-speck:nth-child(5) { bottom: 4px; right: 14px; animation-delay: 520ms; }
@keyframes reward-speck-drift {
  0%   { transform: translate(0, 0) scale(1); opacity: 0; }
  25%  { opacity: .8; }
  100% { transform: translate(var(--speck-dx, 8px), -14px) scale(.4); opacity: 0; }
}
/* Медаль (ачивка/коллекция): один деликатный световой блик проходит по бейджу —
   «полированная награда», отличный характер от печати (Этап 2). */
.reward-medal { overflow: hidden; }
.reward-medal-sheen {
  position: absolute; inset: 0;
  background: linear-gradient(115deg, transparent 34%, rgba(255, 255, 255, .5) 50%, transparent 66%);
  transform: translateX(-120%);
  animation: reward-medal-sweep 850ms ease-out 300ms both;
  pointer-events: none;
}
@keyframes reward-medal-sweep { to { transform: translateX(120%); } }

/* --- Passport stamp v3 (0711c): печать «прибивается» на ТЁМНОМ фоне с мягким
   свечением (без бумаги). Светлые чернила + glow; ударная волна и брызги —
   чернильно-голубые. Только transform/opacity. */
.reward-stamp-real {
  width: 152px; height: 152px; margin: 2px auto 18px;
  position: relative; background: none; box-shadow: none; border-radius: 50%;
  color: #7fb6ff; overflow: visible;
}
.reward-stamp-glow {
  position: absolute; inset: -14%; border-radius: 50%;
  background: radial-gradient(circle, rgba(127, 182, 255, .28), transparent 68%);
  filter: blur(6px); pointer-events: none;
  animation: reward-glow-in var(--motion-sheet) var(--ease-out-premium) both; animation-delay: 120ms;
}
@keyframes reward-glow-in { from { opacity: 0; transform: scale(.7); } to { opacity: 1; transform: scale(1); } }
.reward-stamp-art {
  position: absolute; inset: 6px; width: calc(100% - 12px); height: calc(100% - 12px);
  filter: drop-shadow(0 0 8px rgba(127, 182, 255, .3));
  animation: reward-stamp-press 620ms both; animation-delay: 120ms; will-change: transform;
}
.reward-shock {
  position: absolute; inset: 12px; border-radius: 50%;
  border: 2px solid rgba(127, 182, 255, .8); opacity: 0; pointer-events: none;
  animation: reward-stamp-shock 620ms ease-out both; animation-delay: 120ms;
}
.reward-date {
  position: relative; display: inline-flex; align-items: center; gap: 5px;
  margin-top: 7px; font-size: 12.5px; color: var(--text-muted);
}
.reward-date .icon { width: 13px; height: 13px; margin: 0; }
.reward-speck:nth-child(3) { --speck-dx: -10px; }
.reward-speck:nth-child(4) { --speck-dx: -8px; }
.reward-speck:nth-child(5) { --speck-dx: 12px; }
/* Подфраза под названием — тише заголовка, теплее XP. */
.reward-subline {
  margin-top: 6px;
  font-size: 13.5px;
  color: var(--text-soft);
}
.reward-visual.visual-scene .icon.icon-ghost { width: 64px; height: 64px; right: -8px; bottom: -10px; }
.reward-eyebrow { position: relative; font-size: 12px; letter-spacing: 1.4px; text-transform: uppercase; color: var(--accent-ice); font-weight: 700; opacity: .9; }
.reward-title { position: relative; font-size: 20px; font-weight: 750; margin-top: 6px; line-height: 1.25; }
.reward-xp { position: relative; color: var(--accent-amber); font-size: 14px; font-weight: 650; margin-top: 7px; }
.reward-actions { position: relative; display: flex; flex-direction: column; gap: 8px; margin-top: 22px; }
.reward-btn-primary {
  background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora));
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--radius-pill);
  padding: 13px;
  font-size: 15px;
  font-weight: 650;
  cursor: pointer;
}
.reward-btn-secondary {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 13px;
  padding: 4px;
  cursor: pointer;
}
/* Hotfix 09k: тихая отмена случайного посещения — самая незаметная из трёх
   кнопок (не конкурирует с «Отлично»/«Открыть паспорт»), но всегда доступна. */
.reward-btn-undo {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 12px;
  padding: 4px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Уважаем prefers-reduced-motion — только fade, без scale/rotate/bounce/пульса. */
@media (prefers-reduced-motion: reduce) {
  .reward-card, .reward-overlay.visible .reward-card {
    transition: opacity .18s ease;
    transform: none;
  }
  .reward-glow { animation: none; opacity: .35; }
  .discovery-modal-card, .discovery-modal.visible .discovery-modal-card {
    transition: opacity .18s ease;
    transform: none;
  }
  /* Passport stamp: без прижима/удара/волны/искр — печать просто появляется
     (fade), состояние «получена» остаётся полностью видимым. Haptic оставляем
     (это подтверждение, не вестибулярная анимация — motion sickness не создаёт). */
  .reward-stamp-ring { animation: reward-fade-in var(--motion-ui) ease both; }
  .reward-stamp-ring::before { display: none; }
  .reward-stamp-art { animation: reward-fade-in var(--motion-ui) ease both; }
  .reward-shock { display: none; }
  .reward-stamp-glow { animation: none; opacity: 1; transform: none; }
  .reward-speck { display: none; }
  .reward-medal-sheen { display: none; }
  /* Расширение покрытия (аудит 2026-07-10): декоративные пульсы и въезды —
     статичны; смысловые состояния остаются видимыми. */
  .toast { animation: none; }
  [class*="skeleton"] { animation: none; opacity: .7; }
  .hero-aurora, .hero-aurora::before, .hero-aurora::after { animation: none; }
  .map-user-marker-dot::after { animation: none; opacity: .4; }
  .map-route-line-scheme { animation: none; }
  .poi-hero-dot { transition: none; }
}
@keyframes reward-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ==========================================================================
   Discovery modal (см. спринт Discovery Modal Cards) — всплывающая карточка
   находки поверх экрана "Коллекций Севера" вместо отдельной страницы.
   Тот же двухслойный приём, что и у .reward-overlay/.reward-card: внешний
   слой отвечает за фон/blur, внутренний — за bounce-появление карточки.
   ========================================================================== */
.discovery-modal {
  position: fixed; inset: 0; z-index: 1900;
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0;
  background: rgba(4, 9, 16, 0);
  opacity: 0; pointer-events: none;
  transition: opacity var(--motion-ui) ease, background var(--motion-ui) ease;
}
.discovery-modal.visible {
  opacity: 1; pointer-events: auto;
  /* Polish 09l: full-screen backdrop-filter — та же iOS-проблема, что у
     reward-overlay (hotfix 09k): залипание/дороговизна. Плотный фон. */
  background: rgba(4, 9, 16, .88);
}
.discovery-modal-card {
  position: relative;
  width: 100%;
  max-width: 480px;
  max-height: 92vh;
  overflow: hidden;
  background: rgba(10, 20, 32, .98);
  border: 1px solid var(--border-soft);
  border-bottom: none;
  border-radius: 22px 22px 0 0;
  box-shadow: var(--shadow-soft);
  transform: translateY(28px);
  opacity: 0;
  transition: transform .32s cubic-bezier(.34, 1.1, .64, 1), opacity .22s ease;
}
.discovery-modal.visible .discovery-modal-card {
  transform: translateY(0);
  opacity: 1;
}
.discovery-modal-drag {
  position: absolute; top: 9px; left: 50%; z-index: 2;
  transform: translateX(-50%);
  width: 36px; height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .28);
}
.discovery-modal-close {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: rgba(4, 9, 16, .5);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, .15);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  cursor: pointer;
}
.discovery-modal-close .icon { width: 15px; height: 15px; margin: 0; }
.discovery-modal-scroll {
  overflow-y: auto;
  max-height: min(86vh, 720px);
  -webkit-overflow-scrolling: touch;
}
.discovery-modal-loading { padding: 60px 20px; text-align: center; }

.discovery-modal-hero {
  position: relative;
  width: 100%;
  height: 220px;
  overflow: hidden;
}
/* Сцена — всегда базовый слой на всю область hero, даже когда есть фото
   (см. Discovery Legal Photo Pack, §10 задания): если <img> не загрузится,
   onerror уберёт её, и сцена уже готова показаться вместо пустоты. */
/* z-index расставлен явно (а не полагаемся на порядок в DOM): у сцены внутри
   .visual-scene::before есть mix-blend-mode, из-за которого без явного
   z-index сцена в некоторых браузерах красится ПОВЕРХ следующего за ней
   <img>, а не под ним — фото пропадало бы за силуэтом. */
.discovery-modal-hero-scene { position: absolute; inset: 0; z-index: 0; }
.discovery-modal-hero img {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* Градиент снизу под бейджем/подписью — фото не "обрывается" в текст (см.
   §6 задания). */
.discovery-modal-hero:has(img)::after {
  content: "";
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(180deg, transparent 55%, rgba(4, 9, 16, .78) 100%);
  pointer-events: none;
}
.discovery-modal-hero-badge {
  position: absolute; left: 14px; bottom: 14px; z-index: 3;
  display: inline-flex; align-items: center; gap: 6px;
  background: rgba(4, 9, 16, .6);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, .15);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  font-size: 12px;
  color: #fff;
}
.discovery-modal-hero-badge .icon { width: 14px; height: 14px; margin: 0; }
/* Ненавязчивая строка атрибуции — маленькая, полупрозрачная, не перекрывает
   объект (см. спринт Discovery Media Quality Replacement, §UI): раньше
   плашка была слишком заметная (плотный фон, крупный паддинг) и на кадрах,
   где объект небольшой, спорила с ним за внимание. Полная атрибуция всё
   равно не теряется — данные хранятся в реестре и доступны по клику (ссылка
   на источник), лицензия — в title-подсказке. */
.discovery-modal-hero-credit {
  position: absolute; right: 10px; bottom: 10px; z-index: 3;
  max-width: 50%;
  background: rgba(4, 9, 16, .32);
  backdrop-filter: blur(3px);
  border-radius: var(--radius-pill);
  padding: 3px 8px;
  font-size: 10px;
  line-height: 1.4;
  color: rgba(255, 255, 255, .6);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: .85;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
}
.discovery-modal-hero-credit:hover,
.discovery-modal-hero-credit:focus-visible {
  opacity: 1;
  background: rgba(4, 9, 16, .6);
  color: #fff;
}
.discovery-modal-hero-scene .icon { width: 52px; height: 52px; }
.discovery-modal-hero-scene .icon.icon-ghost { width: 140px; height: 140px; right: -20px; bottom: -30px; }

.discovery-modal-body { padding: 18px 18px 22px; }
.discovery-modal-group { font-size: 12px; letter-spacing: .6px; text-transform: uppercase; color: var(--accent-ice); font-weight: 700; opacity: .9; }
.discovery-modal-body h2 { margin: 4px 0 8px; font-size: 20px; font-weight: 750; }

/* ==========================================================================
   GLOBAL APP SHELL V1 (поколение 20260712a)
   docs/GLOBAL_APP_SHELL_V1.md + docs/VISUAL_SYSTEM_V1.md + решение D-0004.
   Всё namespaced префиксом .gs-* → нулевой риск коллизий с существующими
   классами (карта/маршруты/паспорт/карточки не затрагиваются).
   ========================================================================== */

/* --- Самохостируемые шрифты (Спринт 1): woff2, font-display:swap, без CDN --- */
@font-face{font-family:'Lora';font-style:normal;font-weight:600;font-display:swap;src:url('./assets/fonts/lora-600-cyrillic.woff2') format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Lora';font-style:normal;font-weight:700;font-display:swap;src:url('./assets/fonts/lora-700-cyrillic.woff2') format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url('./assets/fonts/inter-400-cyrillic.woff2') format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url('./assets/fonts/inter-400-latin.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url('./assets/fonts/inter-500-cyrillic.woff2') format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url('./assets/fonts/inter-500-latin.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url('./assets/fonts/inter-600-cyrillic.woff2') format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url('./assets/fonts/inter-600-latin.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url('./assets/fonts/inter-700-cyrillic.woff2') format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url('./assets/fonts/inter-700-latin.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}

/* Display-сериф Lora на заголовках; тело — Inter (body уже переключён выше). */
.app-header h1, .hero h1, .section-title,
.card-title, .card-hero-title, .poi-hero-title, .poi-hero-headline,
.gs-hl .b h3, .gs-topbar .place .n,
.gs-resume .prog b, .gs-empty h4, .gs-pcard h3, .gs-rcard h3 {
  font-family: var(--font-display);
}

/* На главной прячем системный .app-header — у shell свой .gs-topbar в потоке. */
body.is-home .app-header { display: none; }
/* Нижняя навигация фиксирована — резервируем место, чтобы контент не уходил под неё. */
/* Native Arctic (Этап C) — панель теперь плавает с зазором 8px НАД safe-area
   (см. #gs-nav bottom), а не примыкает к нему вплотную — зазор добавлен явно
   в клиренс контента, чтобы плавающая панель не перекрывала последний ряд. */
body.gs-nav-on #app { padding-bottom: calc(var(--gs-navh) + env(safe-area-inset-bottom, 0px) + 8px + 12px); }

:root { --gs-navh: 62px; }

.gs-ic { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
.gs-ic-sm { width: 16px; height: 16px; }

/* ── TOP BAR (главная) — Teriberka Home Reference Lock V1, §2: слева городской
   знак (36–40px, из конфигурации направления — DESTINATIONS.iconName) +
   название города; подзаголовок «Кольский полуостров» убран (правило .r
   удалено вместе с разметкой). ── */
/* Final Header Shell: City Shell Главной — та же 84px-геометрия, что и
   .app-header (единая система: HEADER 84 / CONTROL 40 / MARK 36-40 /
   MIN GAP 6 / STROKE 1). Управляющие .gs-iconbtn уже 40px/1px. */
.gs-topbar { display: flex; align-items: center; gap: 10px; padding: 6px 4px 2px; margin: 0 0 4px; min-height: 84px; }
.gs-citymark { width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(143,216,245,.1); border: 1px solid var(--border-soft); color: var(--accent-ice); }
.gs-citymark .icon { width: 22px; height: 22px; margin: 0; }
/* Realistic Arctic Relief V1 (2026-07-19): вариант с принятым городским знаком —
   сам SVG занимает весь круг, без второго фона/рамки (знак самодостаточен). */
.gs-citymark--img { background: none; border: none; overflow: hidden; }
.gs-citymark--img .gs-mark-img { width: 100%; height: 100%; border-radius: 50%;
  object-fit: contain; display: block; }
.gs-topbar .place { line-height: 1.15; }
.gs-topbar .place .n { font-weight: 700; font-size: 19px; }
.gs-topbar .sp { margin-left: auto; }
.gs-iconbtn { width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,.06);
  border: 1px solid var(--border-soft); color: var(--text-main);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: transform var(--motion-fast) var(--ease-in-exit); }
.gs-iconbtn:active { transform: scale(.94); }
/* :focus-visible по принятому паттерну (#gs-nav/.gs-tg-btn). */
.gs-iconbtn:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; }

/* Final Structure Polish (2026-07-18): городской topbar — единственный
   sticky-элемент главной. position:sticky (НЕ fixed) — элемент остаётся в
   потоке, контент не «прыгает» при включении закрепления. Отрицательные
   маргины компенсируют паддинг #app (12px 16px), чтобы плотная glass-подложка
   доходила до краёв экрана и контент при прокрутке не выглядывал по бокам.
   Плотность фона .92 + blur: контент под шапкой не мешает чтению (требование
   владельца «спокойный, достаточно плотный glass»). safe-area-inset-top учтён
   (в Telegram WebView обычно 0 — WebView начинается под системной шапкой
   Telegram, но fullscreen-режимы отдают реальное значение). z-index 400 —
   ниже #gs-nav (500) и выше контента; с нижней навигацией не конфликтует. */
/* City Background & Header Continuity (2026-07-19): плотная непрозрачная
   подложка (.92 + blur 14) заменена деликатным прозрачным градиентом —
   фон побережья читается СКВОЗЬ шапку, шапки всех четырёх вкладок Териберки
   воспринимаются одним компонентом (у .app-header подложки нет вовсе).
   Лёгкий blur и вертикальный градиент сверху — только ради читаемости
   названия/погоды на светлых участках фото. Sticky-поведение, высота 84px,
   геометрия и состав Header Shell не менялись. */
.gs-topbar-sticky {
  position: sticky; top: 0; z-index: 400;
  margin: -12px -16px 8px; padding: calc(8px + env(safe-area-inset-top, 0px)) 16px 8px;
  background: linear-gradient(180deg, rgba(4, 12, 20, .46), rgba(4, 12, 20, .18) 66%, rgba(4, 12, 20, 0));
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
}

/* ── section header ── */
.gs-hsec { display: flex; align-items: baseline; margin: 22px 0 12px; }
.gs-hsec .t { font-weight: 700; font-size: 12px; letter-spacing: 1.4px; text-transform: uppercase; color: var(--text-muted); }
.gs-hsec .more { margin-left: auto; font-size: 12.5px; color: var(--accent-ice); font-weight: 600; display: inline-flex; align-items: center; gap: 3px; background: none; cursor: pointer; }

/* ── Seasonal Hero V1 (заменяет прежний статичный D-0014 .gs-hero — снят
   целиком). Home Density & Warmth Polish V1 — геометрия облегчена: aspect-
   ratio 16/10 вместо фиксированных 310px (Owner QA: "непривлекательные и
   массивные"). 3 полноширинных слайда (Village/Coast/Harbor), нативный
   horizontal scroll-snap, без "peek next" (в отличие от .gs-carousel). ── */
/* ── HEADER CARD — Home Density, Warmth & Guide Catalog Polish V2: топбар +
   hero-фото + Telegram-переходы объединены в одну карточку вместо трёх
   раздельных блоков (Owner: "совместим шапку и кнопки, пусть будут
   прозрачные и частью шапки"). Внутренние компоненты (.gs-topbar/.gs-shero/
   .gs-tg-row) — без изменений разметки, только вложены сюда + переопределены
   отступы ниже. ── */
/* Final Structure Polish (2026-07-18): topbar вынесен из карточки в отдельный
   sticky-контейнер (.gs-topbar-sticky выше), Telegram-ряд и dots переехали
   ВНУТРЬ фотографии (.gs-shero-foot ниже) — в карточке остаются hero и тонкая
   служебная строка атрибуции на её нижней границе. */
.gs-header-card {
  background: linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,.012));
  border: 1px solid var(--border-soft); border-radius: var(--r-xl);
  padding: 8px 8px 4px; margin: 4px 0 0;
}
.gs-header-card .gs-shero { margin: 0; }
/* Final Structure Polish (2026-07-18), §4: Telegram-переходы — ЧАСТЬ hero.
   Общий absolute-оверлей у нижнего края фотографии: dots по центру
   непосредственно над Telegram-полосой, ниже — две равные ПОЛНОСТЬЮ
   прозрачные зоны «Канал»/«Чат» (без тёмных карточек и круглых фонов),
   разделённые одним тонким вертикальным штрихом. Контраст обеспечивает
   усиленный нижний скрим слайда (::after ниже), а не собственный фон зон.
   pointer-events: none на контейнере — свайп по фото не съедается там, где
   нет кнопок; сами кнопки кликабельны (pointer-events:auto). */
.gs-shero-foot {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center;
  pointer-events: none;
}
.gs-shero-foot .gs-tg-row {
  width: 100%; margin: 0; padding: 0 10px 4px; gap: 0;
  pointer-events: auto;
}
/* Final Visual Fix (2026-07-18): обе зоны — ОДИН прозрачный слой. В resting нет
   ни фона, ни рамки, ни box-shadow, ни скругления (border-radius:0 — прежний
   12px в сочетании с border-left-разделителем закруглял линию сверху/снизу и
   читался как отдельный округлый контейнер вокруг «Чат»). Разделитель — один
   короткий центрированный вертикальный штрих через ::before второй зоны (не
   border у скруглённого элемента), гарантированно прямой и не похож на рамку
   карточки. equal-width обеспечивает flex:1 базового .gs-tg-btn. */
.gs-shero-foot .gs-tg-btn {
  position: relative; background: none; border: none; border-radius: 0; box-shadow: none;
  min-height: 44px; padding: 6px 4px 8px;
}
.gs-shero-foot .gs-tg-btn + .gs-tg-btn::before {
  content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 1px; height: 56%; background: rgba(255,255,255,.24); pointer-events: none;
}
/* Клавиатурный focus-visible сохранён (базовое правило .gs-tg-btn:focus-visible);
   после touch/click постоянного контура не остаётся — гасим не-клавиатурный :focus
   явно (страховка для WebView, где :focus залипает после тапа). */
.gs-shero-foot .gs-tg-btn:focus:not(:focus-visible) { outline: none; }
/* Белые подписи/светлые иконки поверх скрима — читаемо на всех 6 сезонных фото. */
.gs-shero-foot .gs-tg-btn .lbl { color: #fff; text-shadow: 0 1px 6px rgba(2,10,18,.55); }

.gs-shero { position: relative; margin: 4px 0 0; }
.gs-shero-rail {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; scrollbar-width: none; overscroll-behavior-x: contain;
  border-radius: var(--r-xl);
}
.gs-shero-rail::-webkit-scrollbar { display: none; }
.gs-shero-slide {
  flex: 0 0 100%; scroll-snap-align: start; position: relative;
  aspect-ratio: 16 / 10; overflow: hidden; border: 1px solid var(--border-soft); background: #132436;
}
/* object-position задаётся инлайн-стилем на <img> (см. seasonalHeroHtml) —
   у Village он различается по сезону (реальные фото разной композиции),
   единое CSS-правило на класс сцены больше не подходит. */
.gs-shero-slide img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Teriberka Home Reference Lock V1, §4: на фото больше нет текстового блока —
   тяжёлые левые скримы (::before под текст) удалены, фотография остаётся
   чистой. Final Structure Polish (2026-07-18): нижний скрим усилен — теперь
   он «сажает» прозрачные зоны Канал/Чат и dots, живущие ПОВЕРХ нижнего края
   фото (.gs-shero-foot), и обеспечивает их контраст на всех summer/winter
   фотографиях. */
.gs-shero-slide::after { content: ""; position: absolute; inset: 54% 0 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(2,11,18,.78)); }
/* Final Structure Polish (2026-07-18): точки hero — ВНУТРИ фотографии, снизу
   по центру, непосредственно над Telegram-полосой (внутри .gs-shero-foot).
   Небольшой контрастный glass-чип допускается заданием — читаемость на любом
   кадре. Absolute-оверлей не увеличивает высоту hero; внешняя строка точек
   под карточкой удалена. */
.gs-shero-dots { display: inline-flex; justify-content: center; align-items: center; gap: 7px;
  margin: 0 0 2px; padding: 4px 9px; border-radius: 99px;
  background: rgba(7, 18, 30, .38);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.gs-shero-dot { width: 5px; height: 5px; border-radius: 50%; background: rgba(214,230,242,.55); }
.gs-shero-dot.on { width: 14px; border-radius: 4px; background: var(--accent-aurora); }
/* Final Header Shell (2026-07-19): плашка погоды на фото (.gs-shero-weather)
   и служебная строка (.gs-shero-service) удалены как мёртвый CSS — погода и
   атрибуция Open-Meteo переехали в City Shell шапку (.hs-weather/.hs-attrib,
   принятые макеты пакета). Фото/автоплей/точки/Telegram-ряд hero не менялись. */
@media (prefers-reduced-motion: reduce) {
  .gs-shero-rail { scroll-behavior: auto; }
}

/* ── Final Header Shell §5: принятый вертикальный фон побережья — ТОЛЬКО на
   Териберка → Главная (body-класс ставит/снимает homeView через
   _viewTeardown, утечка на другие экраны исключена механизмом render()).
   Слои: принятый защитный overlay (SVG-градиент) поверх принятого
   mobile-WebP; static fallback подключён для prefers-reduced-motion.
   Фон фиксированный (без параллакса/анимации — «новую анимацию в этом
   раунде не создавать»), позади всего контента (z-index:-1), карточки и
   glass-подложки сохраняют собственные фоны — читаемость не страдает;
   cover без швов и резких границ. ── */
/* Background Visibility Hotfix (2026-07-19): body сам рисует непрозрачный
   градиент, а ::before с z-index:-1 в КОРНЕВОМ stacking context рисуется ДО
   фонов in-flow потомков html (CSS 2.1 App. E) — т.е. ПОД заливкой body, и
   побережье было полностью скрыто. isolation:isolate делает body собственным
   stacking context: внутри него порядок — фон body → ::before(z:-1) →
   контент → sticky/фикс-панели. Побережье встаёт НАД базовой заливкой и ПОД
   всем UI; интерактив ничем не перекрывается (z-index UI не менялись). */
body.ter-home-bg { isolation: isolate; }
body.ter-home-bg::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: url("assets/shell/teriberka-bg/teriberka-bg-overlay.svg"),
                    url("assets/shell/teriberka-bg/teriberka-bg-mobile.webp");
  background-size: cover, cover;
  background-position: center top, center top;
  background-repeat: no-repeat, no-repeat;
}
@media (prefers-reduced-motion: reduce) {
  body.ter-home-bg::before {
    background-image: url("assets/shell/teriberka-bg/teriberka-bg-overlay.svg"),
                      url("assets/shell/teriberka-bg/teriberka-bg-static-fallback.webp");
  }
}

/* ── Telegram Compact («Север в Telegram») — Home Density & Warmth Polish V1:
   3 компактные равные кнопки (иконка + короткая подпись) вместо прежних
   тяжёлых строк с описанием/чипами (Owner QA: "занимают слишком много
   места"). Section-header/описания/чипы/Expanded — убраны, смысл понятен
   из контекста (сразу под hero). ── */
.gs-tg-row { display: flex; gap: 8px; margin: 10px 0 0; }
.gs-tg-btn {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 44px; padding: 9px 4px; border-radius: 14px; border: 1px solid var(--border-soft);
  background: linear-gradient(145deg, var(--bg-card), rgba(11,23,38,.6));
  color: var(--text-main); cursor: pointer;
}
.gs-tg-btn:active { transform: scale(.97); }
/* Подписи (Reference Lock §6) — существующий самохостящийся display-font Lora
   с аккуратным курсивным морским характером. Отдельного italic-начертания в
   assets/fonts нет — браузер синтезирует наклон из Lora 600 (CDN и новые
   внешние шрифты запрещены). Размер чуть крупнее прежнего — курсивная
   кириллица Lora на 11px читалась бы хуже прямого Inter. */
.gs-tg-btn .lbl { font-family: var(--font-display); font-style: italic;
  font-size: 12.5px; font-weight: 600; letter-spacing: .3px; }
.gs-tg-btn:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; border-radius: 14px; }

/* ── Telegram Compact Northern Icons & Micro-Motion — авторские SVG-иконки
   (морошка/кит-с-сигналом/два дельфина) вместо абстрактных глифов ⚙/◉/◌.
   Переиспользуют существующий .icon/ICONS-рендер (icon()), второй механизм
   иконок не заводился — только размер/цвет переопределены под контекст
   Telegram Compact (тот же паттерн, что .icon-badge .icon выше). ── */
.gs-tg-btn .icon.gs-tg-ic { width: 20px; height: 20px; margin: 0; color: var(--accent-ice); }
.gs-tg-btn .icon.gs-tg-ic svg { overflow: visible; }
.gs-tg-btn .tg-berry, .gs-tg-btn .tg-leaf,
.gs-tg-btn .tg-whale-body, .gs-tg-btn .tg-whale-tail, .gs-tg-btn .tg-whale-wave1, .gs-tg-btn .tg-whale-wave2,
.gs-tg-btn .tg-dolph-l, .gs-tg-btn .tg-dolph-r, .gs-tg-btn .tg-dolph-talk { transform-origin: center; }
.gs-tg-btn .tg-dolph-talk { opacity: 0; } /* декоративный "знак общения" — виден только во время микроанимации */

/* Основной импульс всей иконки — единый для всех трёх (scale, не выше 1.08). */
@keyframes tgIconPulse { 0%, 100% { transform: scale(1); } 40% { transform: scale(1.07); } }
.gs-tg-btn .icon.gs-tg-ic.is-playing { animation: tgIconPulse 520ms var(--ease-water); }

/* Бот (морошка): лист едва заметно качается на своём креплении. */
.gs-tg-btn .tg-leaf { transform-origin: 11.6px 8.2px; }
@keyframes tgLeafWobble { 0%, 100% { transform: rotate(0deg); } 35% { transform: rotate(-8deg); } 70% { transform: rotate(4deg); } }
.gs-tg-btn .is-playing .tg-leaf { animation: tgLeafWobble 520ms var(--ease-water); }

/* Канал (кит + сигнал): хвост слегка "бьёт", волны сигнала проступают по очереди. */
.gs-tg-btn .tg-whale-tail { transform-origin: 14.6px 13px; }
@keyframes tgTailFlick { 0%, 100% { transform: rotate(0deg); } 30% { transform: rotate(6deg); } 65% { transform: rotate(-3deg); } }
.gs-tg-btn .is-playing .tg-whale-tail { animation: tgTailFlick 520ms var(--ease-water); }
@keyframes tgWaveReveal { 0%, 100% { opacity: 1; transform: scale(1); } 45% { opacity: .55; transform: scale(1.12); } }
.gs-tg-btn .is-playing .tg-whale-wave1 { animation: tgWaveReveal 520ms var(--ease-water); }
.gs-tg-btn .is-playing .tg-whale-wave2 { animation: tgWaveReveal 520ms var(--ease-water) 90ms; }

/* Чат (два дельфина): слегка сближаются, между ними коротко проступает знак общения. */
@keyframes tgDolphApproachL { 0%, 100% { transform: translateX(0); } 45% { transform: translateX(1.1px); } }
@keyframes tgDolphApproachR { 0%, 100% { transform: translateX(0); } 45% { transform: translateX(-1.1px); } }
@keyframes tgTalkReveal { 0%, 100% { opacity: 0; transform: scale(.4); } 55% { opacity: 1; transform: scale(1.15); } }
.gs-tg-btn .is-playing .tg-dolph-l { animation: tgDolphApproachL 520ms var(--ease-water); }
.gs-tg-btn .is-playing .tg-dolph-r { animation: tgDolphApproachR 520ms var(--ease-water); }
.gs-tg-btn .is-playing .tg-dolph-talk { animation: tgTalkReveal 520ms var(--ease-water); }

@media (prefers-reduced-motion: reduce) {
  .gs-tg-btn .icon.gs-tg-ic.is-playing,
  .gs-tg-btn .is-playing .tg-leaf, .gs-tg-btn .is-playing .tg-whale-tail,
  .gs-tg-btn .is-playing .tg-whale-wave1, .gs-tg-btn .is-playing .tg-whale-wave2,
  .gs-tg-btn .is-playing .tg-dolph-l, .gs-tg-btn .is-playing .tg-dolph-r,
  .gs-tg-btn .is-playing .tg-dolph-talk { animation: none; }
}

/* ── BUTTON SYSTEM ── */
.gs-btn { border: none; font-family: var(--font-ui); font-weight: 650; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: 999px; transition: transform var(--motion-fast) var(--ease-in-exit); }
.gs-btn:active { transform: scale(.97); }
.gs-btn .gs-ic { width: 18px; height: 18px; }
.gs-btn-primary { flex: 1; background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora));
  color: var(--text-on-accent); padding: 14px 18px; font-size: 15px; min-height: 48px; }
.gs-btn-secondary { background: rgba(6,13,22,.5); color: var(--text-main);
  border: 1px solid rgba(255,255,255,.2); padding: 14px 16px; font-size: 14px; min-height: 48px; }
.gs-btn-compact { background: rgba(255,255,255,.06); border: 1px solid var(--border-soft);
  color: var(--text-main); padding: 9px 14px; font-size: 13px; min-height: 40px; }
.gs-btn-compact.accent { background: rgba(52,224,161,.14); border-color: rgba(52,224,161,.32); color: var(--accent-aurora); }
.gs-btn-inline { background: none; color: var(--accent-ice); font-weight: 600; font-size: 13px; padding: 2px 0; gap: 4px; }
.gs-btn-danger { background: rgba(255,107,107,.12); border: 1px solid rgba(255,107,107,.3); color: var(--danger); padding: 11px 16px; font-size: 13.5px; }


/* ── Activities Carousel V1 (Этап C) — заменяет прежний одиночный .gs-hl
   (удалён как полностью неиспользуемый — единственный вызов был здесь).
   4 редакционных слайда, нативный horizontal scroll-snap с видимым краем
   следующего слайда (margin-right отрицательный на .gs-carousel + padding на
   rail — тот же приём, что уже в .pc-sea .rel-scroll этого файла), без
   автопрокрутки и без сторонних drag-библиотек.
   Home Density & Warmth Polish V1, §3 — геометрия сменена на один
   полноширинный банер БЕЗ "peek next slide" (Owner QA: "карточки слишком
   крупные"): negative-margin bleed убран, slide теперь flex:0 0 100%,
   aspect-ratio ~16/7 (заметно ниже прежних 245px). ── */
.gs-carousel { overflow: hidden; position: relative; }
.gs-carousel-rail {
  display: flex; overflow-x: auto;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; overscroll-behavior-x: contain;
}
.gs-carousel-rail::-webkit-scrollbar { display: none; }
.gs-carousel-slide {
  flex: 0 0 100%; aspect-ratio: 16 / 7; border-radius: var(--r-lg);
  overflow: hidden; position: relative; scroll-snap-align: start;
  /* Home Density, Warmth & Guide Catalog Polish V2: тёплая рамка/glow вместо
     нейтральной — карусель теперь читается как промо-баннер (Owner: "гибрид",
     те же реальные туры/активности, без стороннего рекламного слоя, просто
     более "рекламная" подача). */
  border: 1px solid rgba(242,182,77,.35); box-shadow: 0 8px 22px -10px rgba(242,182,77,.4);
  text-align: left; color: var(--text-main);
  cursor: pointer; padding: 0; background: var(--bg-card); display: block;
}
.gs-carousel-slide img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* ATV VIDEO ONE-SHOT V1 (2026-07-20): видео летней ATV-карточки лежит в ТОМ ЖЕ
   media-контейнере поверх постера-<img>, та же геометрия (inset:0 / cover) —
   без искажения, без изменения border-radius, без новых рамок/теней. По DOM-
   порядку video рисуется НАД img, но ПОД защитным градиентом ::after (у него нет
   z-index и он идёт позже в порядке отрисовки) → tag/h3/dots (z-index:2) и
   градиент остаются над видео и читаемыми. pointer-events:none → tap уходит на
   <button>, свайп карусели не перехватывается. Изначально opacity:0 (виден
   постер); .is-on проявляет видео (появление/последний кадр держим на opacity:1). */
.gs-carousel-slide video.gs-slide-video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  pointer-events: none; opacity: 0; transition: opacity .28s ease; background: transparent;
}
.gs-carousel-slide video.gs-slide-video.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  /* Reduced motion: видео полностью исключено — только постер. */
  .gs-carousel-slide video.gs-slide-video { display: none; }
}
.gs-carousel-slide::after {
  content: ""; position: absolute; inset: 30% 0 0;
  background: linear-gradient(180deg, transparent, rgba(2, 7, 13, .94));
}
.gs-carousel-slide .tag {
  position: absolute; z-index: 2; top: 14px; left: 16px; display: inline-flex;
  padding: 6px 10px; border-radius: 9px;
  background: linear-gradient(135deg, var(--accent-amber), #e0973a); border: none;
  color: #1a1206; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px;
}
/* Final Structure Polish (2026-07-18): заголовок поднят выше нижнего края —
   освобождает полосу под точками-индикаторами внутри изображения. */
.gs-carousel-slide h3 {
  position: absolute; z-index: 2; left: 18px; right: 18px; bottom: 32px;
  font-weight: 700; font-size: 19px; line-height: 1.12; margin: 0;
}
/* Final Structure Polish (2026-07-18): точки — ВНУТРИ изображения баннера,
   снизу по центру, поверх защитного градиента слайда, не перекрывая
   заголовок (он поднят выше). Отдельная строка под баннером удалена —
   высота секции не растёт. */
.gs-carousel-dots { position: absolute; left: 50%; transform: translateX(-50%); bottom: 9px; z-index: 2;
  display: inline-flex; justify-content: center; align-items: center; gap: 7px;
  padding: 4px 9px; border-radius: 99px; background: rgba(7, 18, 30, .38);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); pointer-events: none; }
.gs-carousel-dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(214,230,242,.55); }
.gs-carousel-dot.on { width: 18px; border-radius: 4px; background: var(--accent-aurora); }
.gs-carousel-slide:focus-visible {
  outline: 2px solid var(--accent-ice); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .gs-carousel-rail { scroll-behavior: auto; }
}

/* ── SECTION ROWS (компактные строки вместо стены плиток) ── */
.gs-rows { display: flex; flex-direction: column; gap: 8px; }
.gs-row { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--bg-card); border: 1px solid var(--border-soft); width: 100%; text-align: left;
  color: var(--text-main); cursor: pointer; transition: transform var(--motion-fast) var(--ease-in-exit); }
.gs-row:active { transform: scale(.99); }
.gs-row .b { width: 42px; height: 42px; border-radius: 12px; display: flex; align-items: center; justify-content: center;
  flex-shrink: 0; background: var(--gs-tint, rgba(143,216,245,.12)); color: var(--gs-acc, var(--accent-ice)); }
.gs-row .b .gs-ic { width: 23px; height: 23px; }
.gs-row .tx { min-width: 0; }
.gs-row .tx .t { font-weight: 650; font-size: 15px; }
.gs-row .tx .s { font-size: 12px; color: var(--text-muted); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gs-row .cnt { margin-left: auto; display: flex; align-items: center; gap: 8px; color: var(--text-muted); font-size: 13px; flex-shrink: 0; }
.gs-row.feat { background: linear-gradient(120deg, rgba(242,182,77,.10), rgba(16,29,46,.6)); border-color: rgba(242,182,77,.28); }

/* ── CATEGORY GRID (Где поесть / Проживание) — Home Density, Warmth & Guide
   Catalog Polish V2: после переезда "Маршрутов" и "Коллекций Севера" в
   Паспорт/архив в каталоге остаётся 1 featured-строка + 2 плитки — вместо
   стены из 5 тонких строк 2-колоночная сетка использует освободившееся место
   премиальнее (референс — категорийные плитки travel-приложений: Airbnb/
   2GIS), а не оставляет пустоту. ── */
/* Final Structure Polish (2026-07-18): «Что вас ждёт?» — строгая сетка 2×2 из
   четырёх ОДИНАКОВЫХ прямоугольных фотоплиток (Owner QA): равные ширина/
   высота (grid 1fr 1fr + единый aspect-ratio), один радиус, одинаковые
   внутренние отступы и положение заголовка, object-fit:cover, лёгкий нижний
   защитный градиент и белый заголовок поверх фотографии. Старые цветные
   пиктограммы/иконочные блоки (.b/--gs-tint) удалены вместе с разметкой. */
.gs-cat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 8px; }
.gs-cat-tile {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden;
  border-radius: var(--r-md); border: 1px solid var(--border-soft);
  padding: 0; text-align: left; cursor: pointer; background: #132436;
  transition: transform var(--motion-fast) var(--ease-in-exit);
}
.gs-cat-tile:active { transform: scale(.97); }
.gs-cat-tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.gs-cat-tile::after { content: ""; position: absolute; inset: 45% 0 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(2, 10, 18, .82)); }
.gs-cat-tile .t {
  position: absolute; left: 12px; right: 12px; bottom: 10px; z-index: 2;
  color: #fff; font-weight: 650; font-size: 13px; line-height: 1.18;
  text-shadow: 0 1px 5px rgba(2, 10, 18, .5);
  /* Final Visual Fix (2026-07-18): перенос с дефисом ПОЛНОСТЬЮ запрещён
     (Product Director: «Достопримечатель-ности» — некрасиво). Три короткие
     подписи помещаются в одну строку 13px без переноса. */
  -webkit-hyphens: none; hyphens: none; overflow-wrap: normal; white-space: nowrap;
}
/* Отдельный адаптивный размер ТОЛЬКО для длинной подписи «Достопримечательности»
   (21 символ, одно слово): уменьшенный кегль, помещается в одну строку без
   дефиса и обрезки на 375 и 390px. Остальные три подписи не уменьшаются. */
.gs-cat-tile .t.t-long { font-size: 10.5px; letter-spacing: -.1px; }
.gs-cat-tile:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; }


/* ── BOTTOM NAV (постоянная) ── */
/* Native Arctic Variant A (Этап C, точный перенос принятого handoff,
   activities-carousel-seasonal/design/index.html .nav/.tab) — заменяет Premium
   Bottom Nav V1 (D-0015): единая плавающая стеклянная панель с закруглёнными
   углами со всех сторон (не edge-to-edge с border-top, как раньше) — структура/
   роутинг/набор иконок/3 таба (D-0007) не менялись, только визуал контейнера.
   safe-area учтён в позиции панели (bottom), а не во внутреннем padding —
   панель теперь плавает НАД home-indicator, а не примыкает к нему. */
/* Home Density & Warmth Polish V1, §4 — панель облегчена (Owner QA: "стала
   лучше, но остаётся массивной"): padding/фон/тень уменьшены, ближе к
   привычному iOS/Telegram tab bar. Три пункта/safe-area/структура — без
   изменений. */
#gs-nav { position: fixed; left: 12px; right: 12px;
  bottom: calc(8px + env(safe-area-inset-bottom, 0px)); z-index: 500; display: none;
  justify-content: space-around; align-items: stretch;
  background: rgba(9, 16, 25, .6); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(143, 216, 245, .18); border-radius: 22px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .28), inset 0 1px 0 rgba(255, 255, 255, .06);
  padding: 5px 6px; }
body.gs-nav-on #gs-nav { display: flex; }
/* Bottom Navigation Frame Hotfix (2026-07-16) — <button> по умолчанию рисует
   системную рамку/push-button chrome (WebKit: appearance:auto + border:outset),
   а ни один из глобальных сбросов (button{color/font-family}, *{tap-highlight})
   не трогает border/outline/appearance — рамка была видна вокруг каждой
   вкладки всегда, независимо от фокуса (подтверждено скриншотом до правки).
   iOS дополнительно "залипает" :focus после тапа — второй источник рамки
   вокруг именно нажатой вкладки. Оба источника закрываются одним сбросом +
   явным :focus-visible по уже принятому в проекте паттерну (кабинет/
   регистрация: --accent-ice, 2px, offset 2px) — доступность для клавиатуры
   не теряется. */
#gs-nav button { position: relative; display: flex; flex-direction: column; align-items: center; gap: 2px;
  color: #a9bccf; font-size: 10px; font-weight: 600; padding: 6px 12px 5px; background: none; cursor: pointer;
  min-height: 44px; transition: transform var(--motion-fast) var(--ease-in-exit);
  border: 0; outline: none; -webkit-appearance: none; appearance: none; }
#gs-nav button:focus { outline: none; }
#gs-nav button:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; border-radius: 10px; }
#gs-nav button .gs-ic { width: 21px; height: 21px; }
#gs-nav button:active { transform: scale(.96); }
/* Home Density & Warmth Polish V1, §4 — Owner QA: убрать отдельную
   декоративную звёздочку (::after), северный акцент встроен ВНУТРЬ самой
   активной иконки (мягкое aurora-свечение через drop-shadow), а не рядом с
   ней отдельным элементом. Бейдж-подложка под иконкой (.gs-ic-wrap::before)
   — сохранена, это основной различитель активного таба. */
#gs-nav button.on { color: var(--text-main); }
.gs-ic-wrap { position: relative; display: inline-flex; align-items: center; justify-content: center; }
#gs-nav button.on .gs-ic-wrap::before {
  content: ""; position: absolute; inset: -8px -16px; border-radius: 14px; z-index: -1;
  background: rgba(52, 224, 161, .12);
}
#gs-nav button.on .gs-ic-wrap .gs-ic {
  filter: drop-shadow(0 0 3px rgba(52, 224, 161, .65));
}
#gs-nav button .bdg { position: absolute; top: -1px; right: 8px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent-amber); border: 2px solid #0a1220; }
/* модалки (reward z2000 / lightbox z3000) перекрывают nav сами; в lightbox
   дополнительно гасим на всякий случай */
body.lb-lock #gs-nav { display: none; }

/* ── empty state (shell) ── */
.gs-empty { text-align: center; padding: 28px 16px; background: rgba(255,255,255,.02);
  border: 1px dashed var(--border-soft); border-radius: var(--r-md); }
.gs-empty .ill { color: var(--accent-ice); opacity: .8; margin-bottom: 10px; }
.gs-empty .ill .gs-ic { width: 44px; height: 44px; }
.gs-empty h4 { font-weight: 700; font-size: 16px; margin: 0 0 4px; }
.gs-empty p { font-size: 13px; color: var(--text-muted); margin: 0 0 14px; line-height: 1.5; }

/* ── reduced-motion: Seasonal Hero — свайп остаётся ручным/нативным (браузер
   не завязывает scroll-snap на этот media query), просто убираем JS/CSS
   smooth-scroll там, где он есть (см. .gs-shero-rail выше). Информация не
   теряется — доступность контента не ухудшается (Visual System V1 §7). ── */
@media (prefers-reduced-motion: reduce) {
  .gs-btn, .gs-row, .gs-resume, .gs-iconbtn, .gs-hl, #gs-nav button { transition: none; }
}

/* ==========================================================================
   PLACE CARD V1 — ACTIVITY / SEA EXPERIENCE («Хвосты китов»)
   docs/PLACE_CARD_DESIGN_V1.md (форма, approved) + docs/motion/
   PLACE_CARD_MOTION_SPEC_V1.md (motion, APPROVED_WITH_NOTES). Только ЭТОТ
   тип карточки (не обобщать на рестораны/жильё/маршруты/стационарную природу).

   Все правила скопированы под .pc-sea (корневой контейнер карточки), а не как
   глобальные .hero/.know/.crit — в проекте уже есть НЕСВЯЗАННЫЙ класс .hero
   (welcomeView/homeView, другая композиция: padding-блок, не full-viewport
   фото). Имена классов внутри (.hero, .hero .ph, .know-body, .kitem, .crit,
   .btn-stamp) — ровно те, что зафиксированы в SPEC §0 (карта селекторов) и
   approved-прототипе tmp/place-card-v1/index.html v3: SPEC заявляет "форма не
   меняется", поэтому имена внутри не переименованы — только защищены областью
   видимости через .pc-sea, чтобы не пересечься с чужим .hero.
   ========================================================================== */
.pc-sea {
  /* Локальные scoped-токены под точные числа SPEC (canon MOTION_LANGUAGE_V1 §2:
     --motion-fast 160-180мс, --motion-ui 200-240мс) — глобальные --motion-fast
     (120мс, тюнинг существующего tap-фидбека) и --motion-ui (200мс) НЕ трогаем
     (регресс тапов по всему приложению); внутри .pc-sea var()-каскад разрешает
     эти же имена в SPEC-точные значения, не выходя за пределы карточки. */
  --motion-fast: 170ms;
  --motion-ui: 220ms;
  position: relative;
  background: var(--bg-main);
  min-height: 100vh;
}

/* ── 1. HERO — Сценарий 1 (SPEC §1) ── */
.pc-sea .hero { position: relative; height: 100vh; min-height: 560px; overflow: hidden; }
/* .ph — фото-слой. top/bottom -4% даёт буфер под scroll-параллакс (§1.2/§1.5:
   диапазон смещения ограничён высотой hero — буфер гарантирует, что translateY
   никогда не обнажает край кадра). Позиция/opacity/scale входа управляются
   ТОЛЬКО из JS (placeCardParallax) инлайн-стилями — специально НЕ через CSS
   transition/animation на transform: JS одновременно пишет translateY
   (параллакс) в тот же transform, конфликт CSS-анимации с инлайн-перезаписью
   transform дал бы "застревание" промежуточного кадра (см. код). */
.pc-sea .hero .ph {
  position: absolute; left: 0; right: 0; top: -4%; bottom: -4%;
  background-position: center 64%; background-size: cover;
  opacity: 0; /* начальное состояние входа (SPEC §1.1); JS переключает на 1 */
}
.pc-sea .hero::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(6,13,22,.5) 0%, transparent 22%, transparent 46%, rgba(6,13,22,.55) 74%, rgba(6,13,22,.97) 100%);
}
.pc-sea .hero-ui {
  position: absolute; top: 0; left: 0; right: 0; z-index: 3; display: flex; gap: 10px;
  padding: calc(12px + env(safe-area-inset-top, 0px)) 14px 0;
}
.pc-sea .gbtn {
  width: 38px; height: 38px; min-width: 38px; min-height: 38px; border-radius: 50%;
  background: rgba(6,13,22,.34); backdrop-filter: blur(8px); border: 1px solid rgba(255,255,255,.14);
  color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.pc-sea .gbtn .gs-ic { width: 18px; height: 18px; }
.pc-sea .hero-ui .sp { margin-left: auto; }
/* .hero-body — эмоц. текст, всплывает вторым слоем (§1.1): opacity+translateY,
   чистая CSS-animation (не конфликтует с JS — JS никогда не трогает этот узел). */
.pc-sea .hero-body {
  position: absolute; left: 20px; right: 20px; bottom: 150px; z-index: 3;
  opacity: 0; transform: translateY(7px);
  animation: pc-body-in var(--motion-ui) var(--ease-enter) 70ms both;
}
@keyframes pc-body-in { to { opacity: 1; transform: translateY(0); } }
.pc-sea .eyebrow {
  font-size: 11.5px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase;
  color: var(--accent-ice); display: flex; align-items: center; gap: 8px;
}
.pc-sea .eyebrow .fluke { width: 17px; height: 17px; color: var(--accent-ice); opacity: .9; }
.pc-sea .hero-body h1 {
  font: 700 40px/1.02 var(--font-display); margin: 12px 0 10px; letter-spacing: -.5px; text-wrap: balance;
}
.pc-sea .hero-body .intro {
  font: 500 15px/1.5 var(--font-ui); color: #eef4fa; max-width: 30ch;
  text-shadow: 0 1px 16px rgba(4,10,20,.8), 0 0 2px rgba(4,10,20,.5);
}
.pc-sea .scrollhint {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 84px; z-index: 3;
  color: rgba(255,255,255,.6); display: flex; flex-direction: column; align-items: center; gap: 2px;
  font-size: 10px; letter-spacing: 1px; text-transform: uppercase; animation: pc-bob 2.4s ease-in-out infinite;
}
@keyframes pc-bob { 0%, 100% { transform: translate(-50%, 0); } 50% { transform: translate(-50%, 5px); } }

/* ── flow sections (не анимируются по SPEC — статичная композиция) ── */
.pc-sea section { padding: 0 20px; }
.pc-sea .lead-para { font: 400 16.5px/1.62 var(--font-ui); color: var(--text-soft); margin: 26px 0 0; }
.pc-sea .lead-para b { color: var(--text-main); font-weight: 600; }

.pc-sea .atmo {
  position: relative; margin: 24px 0 0; height: 210px; overflow: hidden;
  background: linear-gradient(160deg, #0f2334, #0a1a29);
}
.pc-sea .atmo .waves { position: absolute; inset: 0; width: 100%; height: 100%; color: var(--accent-ice); opacity: .45; }
.pc-sea .atmo::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(6,13,22,.15), transparent 45%, rgba(6,13,22,.85)); }
.pc-sea .atmo .cap { position: absolute; left: 20px; right: 20px; bottom: 14px; z-index: 2; }
.pc-sea .atmo .cap .k { font-size: 11px; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase; color: var(--accent-ice); }
.pc-sea .atmo .cap .c { font: italic 500 13.5px/1.4 var(--font-display); color: var(--text-soft); margin-top: 4px; }

.pc-sea .reasons { margin-top: 30px; }
.pc-sea .sec-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: 1.6px; text-transform: uppercase; color: var(--text-muted); margin-bottom: 16px; }
.pc-sea .reason { display: flex; gap: 14px; padding: 14px 0; border-top: 1px solid var(--border-soft); }
.pc-sea .reason:first-of-type { border-top: none; }
.pc-sea .reason .m { color: var(--accent-ice); flex-shrink: 0; margin-top: 1px; }
.pc-sea .reason .m .gs-ic { width: 22px; height: 22px; }
.pc-sea .reason h3 { font: 600 17px/1.25 var(--font-display); margin: 0 0 3px; }
.pc-sea .reason p { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--text-soft); }

/* ── 2. «Хорошо знать» — Сценарий 2 (SPEC §2) ── */
.pc-sea .know { margin-top: 30px; background: rgba(255,255,255,.022); border: 1px solid var(--border-soft); border-radius: var(--r-lg); overflow: hidden; }
.pc-sea .know-head { display: flex; align-items: center; gap: 12px; padding: 16px 16px; }
.pc-sea .know-head .m { width: 38px; height: 38px; border-radius: 11px; background: rgba(143,216,245,.1); color: var(--accent-ice); display: flex; align-items: center; justify-content: center; }
.pc-sea .know-head .m .gs-ic { width: 20px; height: 20px; }
.pc-sea .know-head .tt { font: 600 16px var(--font-display); }
.pc-sea .know-head .tt .s { display: block; font-family: var(--font-ui); font-size: 12px; color: var(--text-muted); font-weight: 400; margin-top: 1px; }
.pc-sea .know-head .tg {
  margin-left: auto; background: none; border: 1px solid var(--border-soft); color: var(--accent-ice);
  border-radius: 999px; padding: 8px 13px; font: 600 12.5px var(--font-ui); display: flex; align-items: center; gap: 5px; cursor: pointer;
}
/* Шеврон — поворот 180°, --motion-fast (SPEC §2.2), синхронно со стартом раскрытия. */
.pc-sea .know-head .tg .gs-ic { width: 14px; height: 14px; transition: transform var(--motion-fast) var(--ease-enter); }
.pc-sea .know.open .know-head .tg .gs-ic { transform: rotate(180deg); }

/* .know-body: НЕ анимируем max-height (SPEC §2.1 — layout-джанк); используем
   его как МГНОВЕННЫЙ (без transition) переключатель занимаемого места в
   потоке — реальная анимация целиком на .kitem (opacity/transform). Это и есть
   технический приём "clip-path ИЛИ обёртка с overflow:hidden... выбирает
   Конструктор" из §2.2: max-height здесь не участвует в transition вообще,
   поэтому джанка, который описывает §2.1, физически не возникает (нет кадров
   промежуточной высоты для лейаута — переключение происходит за 1 кадр). */
.pc-sea .know-body { max-height: 0; overflow: hidden; }
.pc-sea .know.open .know-body { max-height: 1000px; }
.pc-sea .know-body-in { padding: 2px 16px 16px; }

/* Пункты практики — стаггер 30-40мс на вход (§2.2), короче/синхронно на выход
   (§2.3). Транзишен-конфиг берётся из ЦЕЛЕВОГО состояния (см. CSS transitions
   spec): открытие читает delay/duration/easing из `.open .kitem`, закрытие —
   из базового правила ниже. */
.pc-sea .kitem {
  display: flex; gap: 13px; padding: 13px 0; border-top: 1px solid var(--border-soft);
  opacity: 0; transform: translateY(-6px); pointer-events: none;
  transition: opacity calc(var(--motion-ui) * .7) var(--ease-exit), transform calc(var(--motion-ui) * .7) var(--ease-exit);
  transition-delay: 0ms;
}
.pc-sea .kitem:first-child { border-top: none; }
.pc-sea .know.open .know-body-in .kitem {
  opacity: 1; transform: translateY(0); pointer-events: auto;
  transition: opacity var(--motion-ui) var(--ease-enter), transform var(--motion-ui) var(--ease-enter);
}
.pc-sea .know.open .know-body-in .kitem:nth-child(1) { transition-delay: 0ms; }
.pc-sea .know.open .know-body-in .kitem:nth-child(2) { transition-delay: 35ms; }
.pc-sea .know.open .know-body-in .kitem:nth-child(3) { transition-delay: 70ms; }
.pc-sea .know.open .know-body-in .kitem:nth-child(4) { transition-delay: 105ms; }
.pc-sea .kitem .m { color: var(--accent-ice); flex-shrink: 0; }
.pc-sea .kitem .m .gs-ic { width: 20px; height: 20px; }
.pc-sea .kitem .h { font-weight: 600; font-size: 14px; margin: 0 0 2px; }
.pc-sea .kitem p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--text-soft); }
.pc-sea .kitem.safe .m { color: var(--accent-amber); }

/* .crit — ИНВАРИАНТ §2.4: отдельно от .know, ВСЕГДА opacity:1, не участвует
   в раскрытии/сворачивании ни в каком виде. Янтарь = забота, не тревога
   (docs/VISUAL_SYSTEM_V1.md, "красный — только ошибка"). */
.pc-sea .crit {
  display: flex; gap: 9px; align-items: flex-start; margin-top: 14px; padding: 11px 13px;
  border-radius: var(--r-md); background: rgba(242,182,77,.09); border: 1px solid rgba(242,182,77,.28);
  opacity: 1;
}
.pc-sea .crit .gs-ic { width: 17px; height: 17px; color: var(--accent-amber); flex-shrink: 0; margin-top: 1px; }
.pc-sea .crit p { margin: 0; font-size: 12.5px; line-height: 1.45; color: #f0e2c8; }
.pc-sea .crit p b { color: #fff; }

/* ── CTA ── */
.pc-sea .cta-block { margin-top: 28px; display: flex; flex-direction: column; align-items: stretch; }
.pc-sea .secondary-row { display: flex; gap: 8px; margin-top: 10px; justify-content: center; }
.pc-sea .map-quiet {
  display: inline-flex; align-items: center; gap: 5px; margin: 12px auto 0; padding: 6px;
  background: none; border: none; color: var(--accent-ice); font: 600 13px var(--font-ui); cursor: pointer;
  align-self: center;
}
.pc-sea .map-quiet .gs-ic { width: 15px; height: 15px; }
.pc-sea .btn-compact {
  flex: 1; background: rgba(255,255,255,.05); border: 1px solid var(--border-soft); color: var(--text-main);
  padding: 12px; font-size: 13.5px; font-family: var(--font-ui); font-weight: 650; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer;
  transition: transform var(--motion-fast) var(--ease-in-exit);
}
.pc-sea .btn-compact:active { transform: scale(.97); }
.pc-sea .btn-compact .gs-ic { width: 18px; height: 18px; }
.pc-sea .btn-primary {
  width: 100%; background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora)); color: var(--text-on-accent);
  border: none; padding: 16px; font-size: 16px; font-family: var(--font-ui); font-weight: 650; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer;
  transition: transform var(--motion-fast) var(--ease-in-exit);
}
.pc-sea .btn-primary:active { transform: scale(.97); }
.pc-sea .btn-primary .gs-ic { width: 18px; height: 18px; }

/* ── related ── */
.pc-sea .related { margin-top: 34px; }
.pc-sea .rel-scroll { display: flex; gap: 12px; overflow-x: auto; margin: 0 -20px; padding: 0 20px 4px; scrollbar-width: none; }
.pc-sea .rel-scroll::-webkit-scrollbar { display: none; }
.pc-sea .relc { flex: 0 0 190px; border-radius: var(--r-lg); overflow: hidden; background: var(--bg-card); border: 1px solid var(--border-soft); }
.pc-sea .relc .im { height: 120px; position: relative; }
.pc-sea .relc .im img { width: 100%; height: 100%; object-fit: cover; }
.pc-sea .relc .im::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, transparent 50%, rgba(6,13,22,.6)); }
.pc-sea .relc .b { padding: 10px 12px 12px; }
.pc-sea .relc h4 { font: 600 15px var(--font-display); margin: 0; }
.pc-sea .relc .s { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }

/* ── тихая печать-CTA (Сценарий 3 триггер) ── */
.pc-sea .stamp-row { display: flex; justify-content: center; margin-top: 18px; }
.pc-sea .btn-stamp {
  background: none; border: 1px solid var(--border-soft); color: var(--text-soft); padding: 10px 18px;
  font-size: 13px; border-radius: 999px; display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  font-family: var(--font-ui); font-weight: 600; transition: transform var(--motion-fast) var(--ease-in-exit);
}
.pc-sea .btn-stamp:active { transform: scale(.97); }
.pc-sea .btn-stamp .gs-ic { width: 16px; height: 16px; color: var(--accent-aurora); }
/* .visited — класс, который ставит ОБЩИЙ обработчик data-visit (app.js) после
   успешной отметки (переиспользуется, а не вводится заново). */
.pc-sea .btn-stamp.visited { color: var(--accent-aurora); border-color: rgba(52,224,161,.3); }
.pc-sea .btn-stamp .visit-undo-hint { margin-left: 4px; opacity: .7; font-size: 11.5px; }

/* ── служебный низ ── */
.pc-sea .service { margin-top: 30px; padding: 16px 20px calc(4px + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--border-soft); }
.pc-sea .tags { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 12px; }
.pc-sea .tag { font-size: 11.5px; color: var(--text-muted); background: rgba(255,255,255,.035); border: 1px solid var(--border-soft); border-radius: 999px; padding: 4px 11px; }
.pc-sea .attrib { font-size: 11px; color: var(--text-dim); line-height: 1.5; }

/* ── Reduced-motion (§1.4/2.5) — hero/scrollhint статичны, kitem без сдвига ── */
@media (prefers-reduced-motion: reduce) {
  .pc-sea .hero .ph { animation: none !important; }
  .pc-sea .hero-body { animation: pc-fade-only 180ms linear both; }
  @keyframes pc-fade-only { from { opacity: 0; } to { opacity: 1; } }
  .pc-sea .scrollhint { animation: none; }
  .pc-sea .know-head .tg .gs-ic { transition: none; }
  .pc-sea .kitem, .pc-sea .know.open .know-body-in .kitem {
    transition: none !important; transform: none !important;
  }
}

/* ==========================================================================
   REWARD-СЦЕНА «Кит → печать» — Сценарий 3 (SPEC §3). Переиспользует
   tmp/visual-system-v1/whale-stamp-reward.html "как есть" (сториборд/тайминги/
   easing без изменений), встроена в существующий #rewardOverlay (очередь
   наград) — ТОЛЬКО для этой печати (whale-tails); медаль/находка/прочие печати
   продолжают использовать общий .reward-stamp-real (не трогаем). Классы с
   префиксом wreward- — нарочно НЕ переиспользуют общие reward-* имена, чтобы
   не зависеть от общей вёрстки .reward-card (у сцены свой каркас-стейдж).
   ========================================================================== */
.wreward-stage {
  position: relative; width: min(360px, 84vw); aspect-ratio: 360 / 460; margin: 0 auto;
  border-radius: 26px; overflow: hidden; background: linear-gradient(180deg, #040b14, #050f1b);
}
.wreward-backdrop { position: absolute; inset: 0; background: rgba(4,9,16,0); transition: background 250ms ease; }
.wreward-stage.run .wreward-backdrop { background: rgba(4,9,16,.62); }
.wreward-sea { position: absolute; left: 0; right: 0; bottom: 0; height: 38%; opacity: 0; transform: translateY(14px); transition: opacity 250ms ease, transform 250ms ease; }
.wreward-stage.run .wreward-sea { opacity: 1; transform: none; }
.wreward-sea svg { width: 100%; height: 100%; display: block; }
.wreward-fluke {
  position: absolute; left: 50%; bottom: 30%; width: 150px; height: 150px; margin-left: -75px;
  transform-origin: 50% 100%; opacity: 0; transform: translateY(80px) scale(.9);
}
.wreward-fluke svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 6px 14px rgba(0,0,0,.5)); }
.wreward-stage.run .wreward-fluke { animation: wreward-fluke 1150ms var(--ease-water) forwards; }
@keyframes wreward-fluke {
  0%   { opacity: 0; transform: translateY(80px) scale(.9); }
  22%  { opacity: 1; transform: translateY(-6px) scale(1); }
  46%  { opacity: 1; transform: translateY(-20px) scale(1.02); }
  64%  { opacity: 1; transform: translateY(10px) scale(.99) rotate(1deg); }
  72%  { opacity: 1; transform: translateY(6px) scale(1); }
  100% { opacity: 0; transform: translateY(20px) scale(.96); }
}
.wreward-impact {
  position: absolute; left: 50%; bottom: 30%; width: 20px; height: 20px; margin-left: -10px;
  border-radius: 50%; border: 2px solid rgba(142,198,239,.85); opacity: 0; pointer-events: none;
}
.wreward-stage.run .wreward-impact { animation: wreward-ring 620ms ease-out 640ms both; }
@keyframes wreward-ring { 0% { opacity: 0; transform: scale(.4); } 30% { opacity: .85; } 100% { opacity: 0; transform: scale(6); } }
.wreward-drop { position: absolute; left: 50%; bottom: 32%; width: 4px; height: 4px; border-radius: 50%; background: var(--stamp-ink, #8ec6ef); opacity: 0; }
.wreward-stage.run .wreward-drop { animation: wreward-drop 560ms ease-out 660ms both; }
.wreward-drop:nth-of-type(1) { --dx: -34px; } .wreward-drop:nth-of-type(2) { --dx: 30px; }
.wreward-drop:nth-of-type(3) { --dx: -16px; } .wreward-drop:nth-of-type(4) { --dx: 20px; }
@keyframes wreward-drop { 0% { opacity: 0; transform: translate(0,0); } 25% { opacity: .9; } 100% { opacity: 0; transform: translate(var(--dx), -26px) scale(.4); } }
.wreward-stamp-wrap { position: absolute; left: 50%; top: 34%; width: 180px; height: 180px; margin-left: -90px; margin-top: -90px; opacity: 0; }
.wreward-stamp-wrap img { width: 100%; height: 100%; display: block; filter: drop-shadow(0 0 10px rgba(142,198,239,.34)); }
.wreward-stamp-glow { position: absolute; inset: -12%; border-radius: 50%; background: radial-gradient(circle, rgba(142,198,239,.3), transparent 66%); filter: blur(8px); opacity: 0; }
.wreward-stage.run .wreward-stamp-wrap { animation: wreward-stampin 700ms var(--ease-stamp) 820ms both; }
.wreward-stage.run .wreward-stamp-glow { animation: wreward-glowin 500ms ease 860ms both; }
@keyframes wreward-stampin {
  0%   { opacity: 0; transform: scale(1.5) rotate(-7deg); }
  40%  { opacity: 1; transform: scale(.94) rotate(2deg); }
  62%  { opacity: 1; transform: scale(1.04) rotate(-1deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}
@keyframes wreward-glowin { from { opacity: 0; transform: scale(.7); } to { opacity: .9; transform: scale(1); } }
.wreward-meta { position: absolute; left: 0; right: 0; bottom: 22px; text-align: center; opacity: 0; transform: translateY(10px); padding: 0 20px; }
.wreward-stage.run .wreward-meta { animation: wreward-metain 300ms ease 1180ms both; }
@keyframes wreward-metain { to { opacity: 1; transform: none; } }
.wreward-meta .ey { font-size: 11px; font-weight: 700; letter-spacing: 1.6px; text-transform: uppercase; color: var(--accent-ice); }
.wreward-meta .nm { font: 700 20px var(--font-display); margin-top: 5px; color: var(--text-main); }
.wreward-meta .sub { font-size: 13px; color: var(--text-soft); margin-top: 3px; }
.wreward-meta .dt { font-size: 12px; color: var(--text-muted); margin-top: 6px; }
.wreward-meta .xp { color: var(--accent-amber); font-weight: 700; font-size: 14px; margin-top: 6px; }
.wreward-meta .go {
  margin-top: 12px; display: inline-flex; align-items: center; gap: 6px;
  background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora)); color: var(--text-on-accent);
  font-weight: 700; font-size: 14px; padding: 11px 22px; border-radius: 999px; border: none; cursor: pointer;
  pointer-events: auto; position: relative; z-index: 5;
}
/* Тап-скип (§3.1): пока сцена не в состоянии .settled — весь стейдж кликабелен
   и пропускает к Settle; после .settled — клик по стейджу (кроме CTA) больше
   не перехватывается (тап по CTA должен работать штатно). */
.wreward-stage:not(.settled) { cursor: pointer; }

/* Reduced-motion (§3.4): без заноса/удара/частиц; печать — спокойный fade+glow;
   вся инфа сразу видима; haptic (JS) сохраняется. */
@media (prefers-reduced-motion: reduce) {
  .wreward-stage.run .wreward-fluke,
  .wreward-stage.run .wreward-impact,
  .wreward-stage.run .wreward-drop { animation: none !important; opacity: 0 !important; }
  .wreward-stage.run .wreward-sea { transition: opacity 250ms ease; }
  .wreward-stage.run .wreward-stamp-wrap { animation: wreward-fade 300ms ease both; }
  .wreward-stage.run .wreward-stamp-glow { animation: wreward-fade 300ms ease both; opacity: .9; }
  .wreward-stage.run .wreward-meta { animation: wreward-fade 300ms ease 200ms both; }
  @keyframes wreward-fade { from { opacity: 0; } to { opacity: 1; } }
}

/* Тап-скип (§3.1): .settled форсирует конечное визуальное состояние сцены
   независимо от того, на каком кадре был тап — без "рваного" обрыва. */
.wreward-stage.settled .wreward-fluke,
.wreward-stage.settled .wreward-impact,
.wreward-stage.settled .wreward-drop { animation: none !important; opacity: 0 !important; }
.wreward-stage.settled .wreward-sea { animation: none !important; opacity: 1 !important; transform: none !important; }
.wreward-stage.settled .wreward-stamp-wrap,
.wreward-stage.settled .wreward-stamp-glow,
.wreward-stage.settled .wreward-meta { animation: none !important; opacity: 1 !important; transform: none !important; }
.wreward-stage.settled .wreward-stamp-glow { opacity: .9 !important; }

.wreward-undo {
  margin-top: 10px; background: none; border: none; color: var(--text-muted);
  font-size: 12px; text-decoration: underline; cursor: pointer; font-family: var(--font-ui);
  pointer-events: auto; position: relative; z-index: 5;
}

/* ==========================================================================
   Регистрация + admin-реестр пользователей (closed-beta deploy, 2026-07-14)
   docs/USER_ROLES_AND_REGISTRATION.md — токены приложения, без hard-coded hex.
   ========================================================================== */

/* Статичная пометка «Закрытый тест» рядом с role-badge в шапке (§15 doc —
   "ненавязчивая пометка"). Не завязана на auth-состояние — в разметке. */
.beta-badge {
  display: flex; width: fit-content; align-items: center; white-space: nowrap;
  background: rgba(242, 182, 77, .12); border: 1px solid rgba(242, 182, 77, .3);
  color: var(--accent-amber); border-radius: var(--radius-pill);
  padding: 4px 9px; font-size: 11px; font-weight: 650; letter-spacing: .2px;
  /* Final Header Shell (2026-07-19): бейдж вынесен ИЗ 84px шапки (принятый
     состав Header Shell его не содержит) — теперь отдельная тонкая строка
     сразу под шапкой, прижата вправо, как выглядела и раньше. Продуктовая
     пометка «Закрытый тест» (docs §15) сохранена. */
  margin: 0 16px 4px auto;
}
/* На Главной шапка скрыта (body.is-home) — бейдж следует за ней, как и
   раньше, когда жил внутри .app-header. */
body.is-home .beta-badge { display: none; }

/* --- Регистрация: пошаговый визард (registrationView, app.js) ---
   Премиум-полировка 2026-07-15 (типографика/отступы/контраст/анимации/
   состояния кнопок/ошибки/индикатор шагов/safe area/динамика появления) —
   поверх уже готовой базы (hotfix 2026-07-14b: активный выбор роли, счётчик
   шагов, success-экран). Safe area уже закрыта глобально на уровне #app
   (padding-bottom: calc(36px + env(safe-area-inset-bottom))) — здесь не
   дублируется. */
.reg-step {
  padding-bottom: 8px;
  animation: regStepIn var(--motion-sheet) var(--ease-out-premium) both;
}
@keyframes regStepIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .reg-step { animation: none; }
}
/* Regisration Final Polish 2026-07-15 (§B1): раньше 12vh верхнего отступа
   толкали смысловой блок слишком низко — между шапкой и заголовком оставалась
   пустая площадка на полноразмерном экране. Подняли блок (7vh), сохранив
   премиальный воздух сверху/снизу через text-align:center на весь степ. */
.reg-step-welcome { text-align: center; padding-top: 7vh; }
/* Заголовок наследовал .section-title (мелкий muted uppercase-лейбл секции) —
   на 22px это читалось как "тяжёлый капс", а не героический заголовок.
   Здесь — фирменный serif (font-family всё ещё из .section-title), но без
   uppercase/letter-spacing и в основном цвете текста, не приглушённом. */
.reg-welcome-title {
  font-size: 26px; font-weight: 700; line-height: 1.16; margin: 0 0 10px;
  letter-spacing: -.2px; text-transform: none; color: var(--text-main);
}
.reg-step-welcome p { font-size: 14.5px; line-height: 1.55; max-width: 32ch; margin-left: auto; margin-right: auto; }
.reg-step h2.section-title { margin-bottom: 4px; }

/* Индикатор шагов — тонкая линия сегментов + подпись (не бейджи/иконки/очки,
   см. §10 UX "без игровой перегрузки" — премиум ≠ игра). */
.reg-progress { margin: 0 0 18px; }
.reg-progress-track { display: flex; gap: 5px; }
.reg-progress-seg {
  flex: 1 1 0; height: 3px; border-radius: 2px;
  background: rgba(255, 255, 255, .1);
  transition: background var(--motion-ui) var(--ease-out-premium);
}
.reg-progress-seg-done { background: linear-gradient(90deg, var(--accent-sea), var(--accent-aurora)); }
.reg-progress-label { margin: 7px 0 0; font-size: 12px; letter-spacing: .2px; }

.reg-consent-row {
  display: flex; align-items: flex-start; gap: 10px; margin-top: 16px;
  font-size: 13.5px; line-height: 1.5; color: var(--text-main);
}
.reg-consent-row input[type="checkbox"] {
  margin-top: 3px; width: 18px; height: 18px; flex: none; accent-color: var(--accent-aurora);
}

/* Этап 2.1 Final Polish — отказ от указания точного возраста в регистрации
   (взаимоисключающий с числовым полем, см. wireRegStep()). Тот же паттерн
   чекбокса, что .reg-consent-row, компактнее (без длинного текста согласия). */
.reg-age-decline-row {
  display: flex; align-items: center; gap: 8px; margin-top: 10px;
  font-size: 13.5px; color: var(--text-main); min-height: 44px;
}
.reg-age-decline-row input[type="checkbox"] {
  width: 18px; height: 18px; flex: none; accent-color: var(--accent-aurora);
}

.reg-role-options { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.reg-role-card {
  display: flex; flex-direction: column; gap: 3px; padding: 14px 16px;
  background: var(--bg-card-glass); border: 1px solid var(--border-soft);
  border-radius: var(--radius-card); cursor: pointer;
  transition: border-color var(--motion-fast, 150ms) ease, background var(--motion-fast, 150ms) ease,
    transform var(--motion-fast, 150ms) var(--ease-out-premium);
}
.reg-role-card:active { transform: scale(.98); }
.reg-role-card input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.reg-role-card:focus-within { border-color: var(--accent-ice); }
.reg-role-card-title { font-weight: 700; font-size: 15px; letter-spacing: -.1px; }
.reg-role-card-hint { font-size: 12.5px; line-height: 1.4; color: var(--text-muted); }
.reg-role-card-active {
  border-color: var(--accent-aurora);
  background: rgba(52, 224, 161, .08);
}
.reg-role-card-active .reg-role-card-title { color: var(--accent-aurora); }
@media (prefers-reduced-motion: reduce) {
  .reg-role-card, .reg-role-card:active { transition: none; transform: none; }
}

/* --- User Cabinet & Services V1, Этап 1 (docs §2.1/§2.2, 2026-07-15):
   прогрессивно раскрываемый блок соцсетей на общем для всех ролей шаге
   регистрации. Возрастные пилюли (.reg-age-pill/.reg-age-options) удалены —
   Этап 2.1 Final Polish (Задача 5) заменил их на точный возраст. --- */
.reg-social-block { margin-top: 20px; }
.reg-social-list { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.reg-social-item {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  background: var(--bg-card-glass); border: 1px solid var(--border-soft); border-radius: var(--r-sm);
  font-size: 13px;
}
.reg-social-item-platform { font-weight: 700; flex: none; }
.reg-social-item-value { color: var(--text-muted); flex: 1 1 auto; word-break: break-all; }
.reg-social-remove {
  flex: none; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 0;
}
.reg-social-remove .icon { width: 14px; height: 14px; }
.reg-social-open-btn { width: auto; }
.reg-social-form { margin-top: 10px; padding: 12px; background: var(--bg-card-glass); border: 1px solid var(--border-soft); border-radius: var(--radius-card); }

/* Registration hotfix (2026-07-14b): раньше padding-top:14vh без нижнего
   ограничителя оставлял большую пустую зону под кнопкой на высоких экранах —
   "финальный экран слишком пустой". Центрируем контент-блок flex'ом внутри
   зоны примерно на высоту видимой области под шапкой (60dvh — не прижимает
   ни к верху, ни ко низу, воздух остаётся сверху/снизу симметрично). Лёгкий
   fade-in + мягкое статичное aurora-свечение за иконкой — без confetti/canvas/
   игровой анимации (§ doc), с уважением prefers-reduced-motion. */
.reg-step-done {
  position: relative;
  text-align: center;
  min-height: 60dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  animation: regDoneFadeIn 420ms var(--ease-out-premium) both;
}
@keyframes regDoneFadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .reg-step-done { animation: none; }
}
/* Статичное (не двигается, только видно) мягкое пятно света за иконкой —
   допустимо докой явно ("очень мягкая статичная aurora-подсветка"), никакого
   transform/движения, поэтому prefers-reduced-motion не требует отдельного
   отключения этого слоя (он и так неподвижен). */
.reg-done-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 220px;
  height: 220px;
  transform: translate(-50%, -55%);
  background: radial-gradient(circle, rgba(52, 224, 161, .16), transparent 70%);
  pointer-events: none;
  z-index: 0;
}
.reg-step-done .icon, .reg-step-done h1, .reg-step-done p, .reg-step-done .actions,
.reg-step-done .reg-social-warning, .reg-step-done .reg-social-success, .reg-step-done .reg-actions {
  position: relative;
  z-index: 1;
}
.reg-step-done .icon { width: 40px; height: 40px; color: var(--accent-aurora); margin-bottom: 12px; }

/* Product Director hotfix 2026-07-15 (дефект 1) — необязательное сохранение
   соцсетей может упасть ПОСЛЕ успешной регистрации: неблокирующее
   предупреждение + кнопка повтора на success-экране, регистрация уже
   сохранена и открытие приложения остаётся доступным. */
.reg-social-warning {
  display: flex; align-items: flex-start; justify-content: center; gap: 7px; margin-top: 16px;
  padding: 10px 14px; border-radius: var(--r-sm); text-align: left;
  background: rgba(255, 107, 107, .12); border: 1px solid rgba(255, 107, 107, .3);
  color: #ff9d8a; font-size: 13px;
}
.reg-social-warning .icon { width: 15px; height: 15px; flex: none; margin-top: 1px; }
.reg-social-success {
  display: flex; align-items: center; justify-content: center; gap: 7px; margin-top: 16px;
  padding: 10px 14px; border-radius: var(--r-sm);
  background: rgba(52, 224, 161, .1); border: 1px solid rgba(52, 224, 161, .3);
  color: var(--accent-aurora); font-size: 13px;
}
.reg-social-success .icon { width: 15px; height: 15px; flex: none; }
.reg-step-done .reg-actions { margin-top: 10px; }

/* Ряд "Назад + Далее/Завершить" в регистрации — НЕ переопределяет общий
   .actions (используется по всему приложению как одна кнопка в столбец;
   менять его глобально означало бы риск регрессии на несвязанных экранах,
   см. docs §16 "не трогать unrelated product screens"), а отдельный класс. */
.reg-actions { display: flex; flex-direction: row; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
.reg-actions button, .reg-actions .btn-secondary { flex: 1 1 auto; }
.reg-actions button:not(.btn-secondary) {
  background: linear-gradient(135deg, var(--accent-sea), var(--accent-aurora));
  color: var(--text-on-accent); font-weight: 700; border: none;
  border-radius: var(--radius-pill); padding: 14px; font-size: 15px; cursor: pointer;
  transition: transform var(--motion-fast) var(--ease-in-exit), opacity var(--motion-ui) ease;
}
.reg-actions button:not(.btn-secondary):active:not(:disabled) { transform: scale(.97); }
.reg-actions button:not(.btn-secondary):disabled {
  opacity: .55; cursor: default;
  display: inline-flex; align-items: center; justify-content: center;
}
.btn-secondary {
  background: var(--bg-card-glass); color: var(--text-main); font-weight: 700;
  border: 1px solid var(--border-soft); border-radius: var(--radius-pill);
  padding: 14px; font-size: 15px; cursor: pointer;
  transition: transform var(--motion-fast) var(--ease-in-exit);
}
.btn-secondary:active { transform: scale(.97); }
.reg-step button:focus-visible, .reg-role-card input:focus-visible ~ * {
  outline: 2px solid var(--accent-ice); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .reg-actions button:not(.btn-secondary), .btn-secondary { transition: none; }
  .reg-actions button:not(.btn-secondary):active:not(:disabled), .btn-secondary:active { transform: none; }
}

/* Спиннер отправки формы (submit btn во время POST /me/registration) —
   единственная в проекте loading-анимация внутри кнопки; коммуницирует
   состояние загрузки, поэтому не отключается по prefers-reduced-motion
   (та же логика, что и у нативных спиннеров ОС). */
.btn-spinner {
  display: inline-block; width: 14px; height: 14px; margin-right: 8px; vertical-align: -2px;
  border: 2px solid rgba(255, 255, 255, .4); border-top-color: #fff;
  border-radius: 50%; animation: regSpin .7s linear infinite;
}
@keyframes regSpin { to { transform: rotate(360deg); } }

/* --- User Cabinet V1, Этап 2.1 Final Polish (2026-07-16) — концепт A
   «Капитан Севера» (design/index.html): цвета/формы переведены на CSS-
   переменные приложения (--accent-amber точно совпадает с #f2b64d мокапа,
   --accent-aurora — с #34e0a1). Финальная структура кабинета (docs "Задача
   1"): шапка (имя+бейдж роли+"Изменить"), опциональный блок заявки,
   два портальных карточки, компактный прогресс, последние посещения. --- */
.cabinet-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.cabinet-header-name { flex: 1 1 auto; min-width: 0; word-break: break-word; margin: 0; }
.cabinet-role-pill {
  display: inline-flex; align-items: center; margin-left: 8px; padding: 3px 11px;
  border: 1px solid rgba(242, 182, 77, .5); border-radius: var(--radius-pill);
  color: var(--accent-amber); background: rgba(242, 182, 77, .07);
  font-size: 12px; font-weight: 700; vertical-align: middle; white-space: nowrap;
}
.cabinet-edit-btn { flex: none; width: auto; padding: 10px 16px; min-height: 44px; }
.cabinet-city { color: var(--text-muted); font-size: 13.5px; margin: 2px 0 14px; }

.cabinet-notice {
  margin: 4px 0 16px; padding: 14px 16px; border-radius: 32px;
  border: 1px solid rgba(242, 182, 77, .38);
  background: linear-gradient(145deg, rgba(45, 37, 24, .76), rgba(16, 29, 46, .9));
}
.cabinet-notice-tag {
  display: inline-flex; padding: 3px 10px; border-radius: var(--radius-pill);
  background: rgba(242, 182, 77, .16); color: var(--accent-amber);
  font-size: 11.5px; font-weight: 700; margin-bottom: 8px;
}
.cabinet-notice h3 { margin: 0 0 6px; font-size: 15px; color: var(--text-main); }
.cabinet-notice p { margin: 0 0 12px; font-size: 13.5px; color: var(--text-muted); }
.cabinet-notice-btn {
  width: auto; padding: 10px 16px; min-height: 44px; cursor: pointer;
  border: 1px solid rgba(242, 182, 77, .48); border-radius: var(--radius-pill);
  background: rgba(242, 182, 77, .1); color: var(--accent-amber);
  font-size: 14px; font-weight: 700;
}

.cabinet-app-line {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 14px; margin: 4px 0 16px; min-height: 44px;
  background: var(--bg-card-glass); border: 1px solid var(--border-soft);
  border-radius: var(--r-sm); font-size: 13.5px; color: var(--text-muted);
}
.cabinet-app-line-rejected {
  color: var(--text-main); flex-direction: column; align-items: flex-start;
}
.cabinet-app-line-rejected button {
  flex: none; align-self: flex-start; width: auto; padding: 8px 14px; min-height: 40px;
  cursor: pointer;
}
.cabinet-apply-btn { margin: 4px 0 16px; }

.cabinet-portals { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 16px; }
.cabinet-portal {
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  padding: 16px 14px; min-height: 108px; text-align: left;
  background: var(--bg-card-glass); border: 1px solid var(--border-soft);
  border-radius: var(--radius-card); color: var(--text-main); cursor: pointer;
  transition: transform var(--motion-fast) var(--ease-in-exit);
}
.cabinet-portal:active { transform: scale(.97); }
.cabinet-portal-icon { color: var(--accent-ice); }
.cabinet-portal-icon .icon { width: 22px; height: 22px; }
.cabinet-portal h2 { margin: 0; font-size: 14px; font-weight: 700; }
.cabinet-portal-count { margin-top: auto; font-size: 13px; color: var(--text-muted); display: inline-flex; align-items: center; gap: 4px; }
.cabinet-portal-count b { color: var(--text-main); font-size: 15px; }
.cabinet-portal-count .icon { width: 13px; height: 13px; }
@media (prefers-reduced-motion: reduce) { .cabinet-portal { transition: none; } .cabinet-portal:active { transform: none; } }

.cabinet-progress {
  padding: 14px 16px; margin-bottom: 16px;
  background: var(--bg-card-glass); border: 1px solid var(--border-soft); border-radius: var(--radius-card);
}
.cabinet-progress-top { display: flex; align-items: baseline; justify-content: space-between; font-size: 13.5px; }
.cabinet-progress-level { font-weight: 700; color: var(--text-main); }
.cabinet-progress-xp { color: var(--accent-amber); font-weight: 700; }
.cabinet-progress-stats { display: flex; gap: 16px; margin-top: 10px; font-size: 12.5px; color: var(--text-muted); }
.cabinet-progress-stats b { color: var(--text-main); }

.cabinet-section-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.cabinet-link-btn {
  background: none; border: none; color: var(--accent-ice); font-size: 13px; font-weight: 600;
  cursor: pointer; padding: 8px 4px; min-height: 44px;
}
.cabinet-visit-row {
  display: flex; align-items: center; gap: 12px; width: 100%; padding: 10px 4px; min-height: 44px;
  background: none; border: none; border-top: 1px solid var(--border-soft); color: var(--text-main); cursor: pointer; text-align: left;
}
.admin-card .cabinet-visit-row:first-of-type { border-top: none; }
.cabinet-visit-thumb {
  flex: none; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); background: var(--bg-card-glass-strong); color: var(--accent-aurora);
}
.cabinet-visit-thumb .icon { width: 17px; height: 17px; }
.cabinet-visit-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.cabinet-visit-title { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cabinet-visit-date { font-size: 12px; color: var(--text-muted); }
.cabinet-visit-row .chev { flex: none; color: var(--text-muted); }
.cabinet-visit-row .chev .icon { width: 14px; height: 14px; }

@media (max-width: 340px) {
  .cabinet-portals { grid-template-columns: 1fr; }
}

/* Фон «Капитан Севера» (концепт A, design/captain-bg-1080x1920.png) — ТОЛЬКО
   на кабинете/личных данных (body.captain-bg-on, см. setCaptainBg() в JS).
   Мокап накладывал изображение отдельным слоем с opacity:.16 и
   mix-blend-mode:screen поверх тёмного фона экрана.
   [ИСПРАВЛЕНО 2026-07-16, Cabinet Captain Background Contrast Hotfix] Прежняя
   реализация применяла `background-blend-mode:screen` напрямую к полнояркому
   изображению внутри многослойного `background` — но blend-mode меняет
   ТОЛЬКО способ смешения цвета, не альфа-канал слоя, а сам WebP непрозрачен.
   `screen` в паре с непрозрачным изображением ОСВЕТЛЯЛ гравюру почти до
   полной яркости — силуэт и линии гравюры перечёркивали текст полей на
   экране «Личные данные» (Owner QA, реальный iPhone). CSS не поддерживает
   per-layer opacity для `background-image`, поэтому вместо blend-хака здесь
   отдельный тёмный scrim-слой (`rgba(6,13,22,.82)`) уложен ПРЯМО НАД
   изображением в стеке (та же техника, что и "opacity" в мокапе, только
   явным overlay, а не blend-режимом) — сам layer возвращён в `normal`.
   Подобрано визуальным сравнением со скриншотом
   `design/concept-a-1080x1920.png` (силуэт — едва заметный акцент в правом
   верхнем углу, не самостоятельный объект экрана). Карточки/текст/размеры
   кабинета не тронуты — правка только в этом одном блоке. */
body.captain-bg-on {
  background-image:
    linear-gradient(180deg, rgba(159, 191, 219, .12) 0%, transparent 130px),
    radial-gradient(ellipse 900px 500px at 15% -10%, rgba(52, 224, 161, .10), transparent 60%),
    radial-gradient(ellipse 700px 400px at 100% 0%, rgba(47, 143, 209, .12), transparent 55%),
    linear-gradient(rgba(6, 13, 22, .82), rgba(6, 13, 22, .82)),
    url('assets/shell/captain-bg-1080x1920.webp'),
    linear-gradient(180deg, var(--bg-main) 0%, var(--bg-main-2) 100%);
  background-repeat: no-repeat;
  background-position: top center;
  background-size: 100% 130px, auto, auto, cover, cover, auto;
  background-blend-mode: normal, normal, normal, normal, normal, normal;
  background-attachment: fixed;
}

/* Видимый :focus-visible для всех новых интерактивных элементов кабинета
   (клавиатурная навигация, mobile UX-требования Этапа 2.1 Final Polish). */
.cabinet-edit-btn:focus-visible, .cabinet-notice-btn:focus-visible, .cabinet-role-cta:focus-visible,
.cabinet-portal:focus-visible, .cabinet-link-btn:focus-visible, .cabinet-visit-row:focus-visible,
.cabinet-app-line-rejected button:focus-visible {
  outline: 2px solid var(--accent-ice); outline-offset: 2px;
}

/* --- Admin: реестр пользователей (adminView() — вкладки Дашборд/Пользователи) --- */
.admin-tabs { display: flex; gap: 8px; margin-bottom: 14px; }
.admin-tab-btn {
  flex: 1; padding: 10px; border-radius: var(--radius-pill); border: 1px solid var(--border-soft);
  background: var(--bg-card-glass); color: var(--text-muted); font-weight: 650; font-size: 13.5px;
  cursor: pointer;
}
.admin-tab-btn-active { background: rgba(52, 224, 161, .12); border-color: var(--accent-aurora); color: var(--accent-aurora); }

.admin-filter-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.admin-filter-row select, .admin-filter-row input {
  background: var(--bg-card-glass); border: 1px solid var(--border-soft); border-radius: var(--r-sm);
  color: var(--text-main); padding: 8px 10px; font-size: 13px; font-family: inherit;
}
.admin-filter-row input[type="search"] { flex: 1 1 160px; min-width: 0; }

.admin-users-list { display: flex; flex-direction: column; gap: 8px; }
.admin-user-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 14px; background: var(--bg-card-glass); border: 1px solid var(--border-soft);
  border-radius: var(--radius-card); text-align: left; width: 100%; cursor: pointer;
}
.admin-user-row-main { min-width: 0; }
.admin-user-row-name { font-weight: 700; font-size: 14.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-user-row-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.admin-user-row-badges { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex: none; }

.admin-status-pill {
  display: inline-flex; align-items: center; border-radius: var(--radius-pill);
  padding: 3px 8px; font-size: 11px; font-weight: 650; white-space: nowrap;
}
.admin-status-not_started { background: rgba(142, 164, 186, .14); color: var(--text-muted); }
.admin-status-in_progress { background: rgba(242, 182, 77, .14); color: var(--accent-amber); }
.admin-status-completed { background: rgba(52, 224, 161, .14); color: var(--accent-aurora); }
.admin-status-pending { background: rgba(242, 182, 77, .14); color: var(--accent-amber); }
.admin-status-needs_info { background: rgba(143, 216, 245, .14); color: var(--accent-ice); }
.admin-status-approved { background: rgba(52, 224, 161, .14); color: var(--accent-aurora); }
.admin-status-rejected { background: rgba(255, 107, 107, .14); color: #ff9d8a; }

.admin-pagination { display: flex; justify-content: space-between; align-items: center; margin-top: 14px; gap: 8px; }
.admin-pagination button {
  background: var(--bg-card-glass); border: 1px solid var(--border-soft); border-radius: var(--radius-pill);
  color: var(--text-main); padding: 8px 14px; font-size: 13px; cursor: pointer;
}
.admin-pagination button:disabled { opacity: .4; cursor: default; }

.admin-detail-section { margin-top: 16px; }
.admin-detail-row {
  display: flex; justify-content: space-between; gap: 10px; padding: 7px 0;
  border-bottom: 1px solid var(--border-soft); font-size: 13.5px;
}
.admin-detail-row:last-child { border-bottom: none; }
.admin-detail-row span:first-child { color: var(--text-muted); }
.admin-detail-row span:last-child { text-align: right; word-break: break-word; }
.admin-app-history-item {
  padding: 10px 12px; background: var(--bg-card-glass); border: 1px solid var(--border-soft);
  border-radius: var(--r-sm); margin-top: 8px; font-size: 13px;
}
.admin-app-history-item p { margin: 4px 0 0; color: var(--text-muted); }

/* --- Admin CRM-карточка пользователя (docs §5-8, задание "Регистрация —
   фундамент CRM", 2026-07-15): роль/профили/сегменты/привилегии/бизнес/
   блокировка/заметки/журнал изменений — редактируемые секции внутри
   adminUserDetailView(). --- */
.crm-section { display: flex; flex-direction: column; gap: 10px; }
.crm-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.crm-chip {
  padding: 7px 13px; border-radius: var(--radius-pill); font-size: 12.5px; font-weight: 600;
  background: var(--bg-card-glass); border: 1px solid var(--border-soft); color: var(--text-muted);
  cursor: pointer; transition: all var(--motion-fast, 150ms) ease;
}
.crm-chip-active { background: rgba(52, 224, 161, .14); border-color: var(--accent-aurora); color: var(--accent-aurora); }
.crm-reason { margin-top: 2px; }
.crm-save-btn {
  align-self: flex-start; background: var(--bg-card-glass); border: 1px solid var(--border-soft);
  color: var(--text-main); font-weight: 700; font-size: 13px; padding: 9px 16px;
  border-radius: var(--radius-pill); cursor: pointer; transition: transform var(--motion-fast) var(--ease-in-exit);
}
.crm-save-btn:active:not(:disabled) { transform: scale(.97); }
.crm-save-btn:disabled { opacity: .55; cursor: default; }
.crm-save-btn-danger { background: rgba(255, 107, 107, .12); border-color: rgba(255, 107, 107, .35); color: var(--danger); }
.crm-blocked-banner {
  display: flex; align-items: center; gap: 8px; padding: 10px 14px; margin-bottom: 12px;
  background: rgba(255, 107, 107, .12); border: 1px solid rgba(255, 107, 107, .3);
  border-radius: var(--r-sm); color: #ff9d8a; font-size: 13px; font-weight: 600;
}
.crm-blocked-banner .icon { width: 16px; height: 16px; flex: none; }
#crmNotesSection textarea.form-input { min-height: 60px; }
@media (prefers-reduced-motion: reduce) {
  .crm-chip, .crm-save-btn { transition: none; }
  .crm-save-btn:active:not(:disabled) { transform: none; }
}

/* Mini App-модерация заявок на роль (docs §2A, Этап 1) — блок действий внутри
   .admin-app-history-item, показывается только для активной (pending/needs_info)
   заявки, см. renderAdminUserDetail(). */
.role-app-actions {
  margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border-soft);
  display: flex; flex-direction: column; gap: 8px;
}
.role-app-buttons { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.role-app-buttons button { flex: 1 1 auto; }

/* --- Admin dev tools: Preview/Reset/Force Start Registration (docs §3,
   задание "Регистрация — фундамент CRM", 2026-07-15) --- */
.admin-tools-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.admin-tool-btn {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  width: 100%; text-align: left; padding: 13px 14px;
  background: var(--bg-card-glass); border: 1px solid var(--border-soft);
  border-radius: var(--radius-card); cursor: pointer;
  transition: border-color var(--motion-fast, 150ms) ease, transform var(--motion-fast) var(--ease-in-exit);
}
.admin-tool-btn:active { transform: scale(.98); }
.admin-tool-btn-title { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 14.5px; color: var(--text-main); }
.admin-tool-btn-title .icon { width: 17px; height: 17px; color: var(--accent-aurora); }
.admin-tool-btn-hint { font-size: 12.5px; line-height: 1.4; color: var(--text-muted); }
.admin-tool-btn-danger .admin-tool-btn-title .icon { color: var(--danger); }
.admin-tool-btn-danger:hover, .admin-tool-btn-danger:active { border-color: rgba(255, 107, 107, .35); }
@media (prefers-reduced-motion: reduce) {
  .admin-tool-btn { transition: none; }
  .admin-tool-btn:active { transform: none; }
}

/* Баннер режима предпросмотра поверх шагов регистрации — липкий сверху,
   всегда виден независимо от прокрутки шага, явно отличим от боевого экрана. */
.reg-preview-banner {
  position: sticky; top: 0; z-index: 5; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 9px 12px; margin: -12px -16px 14px; background: rgba(242, 182, 77, .14);
  border-bottom: 1px solid rgba(242, 182, 77, .3); font-size: 12.5px; font-weight: 600; color: var(--accent-amber);
}
.reg-preview-banner .icon { width: 14px; height: 14px; margin-right: 6px; }
.reg-preview-banner span { display: flex; align-items: center; }
.reg-preview-banner button {
  flex: none; background: rgba(242, 182, 77, .18); border: 1px solid rgba(242, 182, 77, .35);
  color: var(--accent-amber); font-weight: 700; font-size: 12px; padding: 6px 12px;
  border-radius: var(--radius-pill); cursor: pointer;
}


/* ══════════════════════════════════════════════════════════════════════════
   Коллекция Севера V1 — «Северный полевой атлас» (дизайн-бриф 2026-07-22).
   Компакт: паспорт коллекционера → лента 5 чипов → одна активная группа 3×N.
   Главный выразительный элемент — реалистичные медальоны; остальной UI тихий.
   Цвет групп — ТОЛЬКО в тонком кольце/прогрессе/активном чипе/метке.
   ══════════════════════════════════════════════════════════════════════════ */
:root {
  --cnv1-sea: #35b8c9;      /* Море: глубокий бирюзовый / ледяной голубой */
  --cnv1-hills: #a8a06b;    /* Сопки: графит, мох, приглушённая охра */
  --cnv1-birds: #a9c6de;    /* Птицы: холодное небо и серебро */
  --cnv1-flora: #cc8a4e;    /* Растения: мох, морошка, брусничный акцент */
  --cnv1-taste: #c9913d;    /* Вкус: медь и янтарь */
}
/* Паспорт коллекционера — одна горизонтальная glass-карточка 88–104px. */
.cnv1-passport {
  display: flex; align-items: center; gap: 14px;
  min-height: 92px; padding: 14px 16px; margin: 4px 0 12px;
  background: linear-gradient(160deg, rgba(10, 22, 34, .72), rgba(8, 17, 27, .55));
  border: 1px solid rgba(170, 216, 235, .18); border-radius: 18px;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.cnv1-passport-main { min-width: 0; flex: 1 1 auto; }
.cnv1-passport-title { font-weight: 700; font-size: 16.5px; letter-spacing: .2px; }
.cnv1-passport-progress {
  height: 4px; margin: 8px 0 7px; border-radius: 4px;
  background: rgba(143, 216, 245, .14); overflow: hidden;
}
.cnv1-passport-progress i {
  display: block; height: 100%; border-radius: 4px;
  background: linear-gradient(90deg, var(--accent-ice), var(--accent-aurora));
}
.cnv1-passport-last { font-size: 11.5px; color: var(--text-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cnv1-passport-last b { color: var(--text-main); font-weight: 650; }
.cnv1-passport-count { flex: none; font-size: 13px; color: var(--text-muted); }
.cnv1-passport-count b { font-size: 21px; font-weight: 700; color: var(--text-main); }

/* Лента чипов групп — горизонтальная, одна активная. */
.cnv1-chips {
  display: flex; gap: 7px; margin: 0 0 14px; padding: 2px 2px 6px;
  overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.cnv1-chips::-webkit-scrollbar { display: none; }
.cnv1-chip {
  flex: none; display: inline-flex; align-items: center;
  height: 40px; padding: 0 12px; border-radius: 999px;
  background: rgba(9, 18, 28, .55); border: 1px solid var(--border-soft);
  color: var(--text-soft); font-size: 12.5px; font-weight: 650;
  font-family: var(--font-ui); cursor: pointer; white-space: nowrap;
}
.cnv1-chip .c { color: var(--text-muted); font-weight: 600; font-size: 11.5px; }
.cnv1-chip.on { color: var(--text-main); }
.cnv1-chip.on .c { color: inherit; opacity: .8; }
.cnv1-chip[data-cnv1-theme="sea"].on   { border-color: var(--cnv1-sea);   background: rgba(53, 184, 201, .13); }
.cnv1-chip[data-cnv1-theme="hills"].on { border-color: var(--cnv1-hills); background: rgba(168, 160, 107, .13); }
.cnv1-chip[data-cnv1-theme="birds"].on { border-color: var(--cnv1-birds); background: rgba(169, 198, 222, .13); }
.cnv1-chip[data-cnv1-theme="flora"].on { border-color: var(--cnv1-flora); background: rgba(204, 138, 78, .13); }
.cnv1-chip[data-cnv1-theme="taste"].on { border-color: var(--cnv1-taste); background: rgba(201, 145, 61, .13); }
.cnv1-chip:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 2px; }

/* Смена группы — crossfade 180мс + сдвиг 4px; reduced-motion — мгновенно
   (класс не вешается, см. wireCnv1Chips). */
.cnv1-host { transition: opacity 180ms var(--ease-out-premium), transform 180ms var(--ease-out-premium); }
.cnv1-host.cnv1-leave { opacity: 0; transform: translateY(4px); }
.cnv1-host.cnv1-enter { opacity: 0; transform: translateY(4px); transition: none; }

/* Сетка 3×N; последний ряд не под нижней навигацией (штатный паддинг #app
   при body.gs-nav-on учитывает nav+safe-area). */
.cnv1-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 15px 10px; padding-bottom: 6px;
}
.cnv1-card {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: none; border: none; padding: 0; cursor: pointer;
  position: relative; min-width: 0;
}
.cnv1-card:focus-visible { outline: 2px solid var(--accent-ice); outline-offset: 3px; border-radius: 14px; }
/* Медальон 84–96px: rounded-square, фикс-геометрия для обоих состояний. */
.cnv1-medallion {
  position: relative; display: block;
  width: 100%; max-width: 92px; aspect-ratio: 1 / 1;
  border-radius: 26%; overflow: hidden;
  background: #0d1b28;
  border: 2px solid transparent;
}
.cnv1-medallion img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* Закрытая находка: изображение различимо, холодный приглушающий overlay +
   тонкое ледяное кольцо; названия видны, большого замка нет. */
.cnv1-card.locked .cnv1-medallion { border-color: rgba(190, 228, 245, .28); }
.cnv1-card.locked .cnv1-medallion img { filter: saturate(.35) brightness(.78) contrast(.96); }
.cnv1-frost {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(165deg, rgba(140, 190, 215, .22), rgba(9, 24, 38, .38));
}
/* Открытая: полная насыщенность + тонкое кольцо цвета группы. */
.cnv1-grid[data-cnv1-theme="sea"]   .cnv1-card.unlocked .cnv1-medallion { border-color: var(--cnv1-sea); }
.cnv1-grid[data-cnv1-theme="hills"] .cnv1-card.unlocked .cnv1-medallion { border-color: var(--cnv1-hills); }
.cnv1-grid[data-cnv1-theme="birds"] .cnv1-card.unlocked .cnv1-medallion { border-color: var(--cnv1-birds); }
.cnv1-grid[data-cnv1-theme="flora"] .cnv1-card.unlocked .cnv1-medallion { border-color: var(--cnv1-flora); }
.cnv1-grid[data-cnv1-theme="taste"] .cnv1-card.unlocked .cnv1-medallion { border-color: var(--cnv1-taste); }
/* Метка редкости — маленькая точка на углу медальона (только закрытые). */
.cnv1-rarity {
  position: absolute; top: 4px; right: calc(50% - 46px + 6px);
  width: 8px; height: 8px; border-radius: 50%;
  border: 1px solid rgba(6, 14, 22, .8);
}
.cnv1-rarity[data-rarity="common"] { background: #8fa3b5; }
.cnv1-rarity[data-rarity="uncommon"] { background: #7fc9a8; }
.cnv1-rarity[data-rarity="rare"] { background: var(--accent-ice); }
.cnv1-rarity[data-rarity="epic"], .cnv1-rarity[data-rarity="legendary"] { background: var(--accent-amber); }
.cnv1-name {
  font-size: 11.5px; line-height: 1.25; font-weight: 600; text-align: center;
  color: var(--text-main); max-width: 100%;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.cnv1-name.t-long { font-size: 10.3px; }
.cnv1-card.locked .cnv1-name { color: var(--text-soft); }
.cnv1-date { font-size: 9px; color: var(--text-muted); }

/* Гагарка: code-native placeholder — нейтральный тёмный медальон + тонкий
   контур птицы (существующая SVG-система), без эмодзи и raster-asset. */
.cnv1-medallion.cnv1-pending {
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(160deg, #10202f, #0a1622);
  border-color: rgba(170, 200, 220, .2); border-style: dashed;
  color: rgba(169, 198, 222, .75);
}
.cnv1-medallion.cnv1-pending .icon { width: 34px; height: 34px; margin: 0; }
.cnv1-hero-pending {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: linear-gradient(160deg, #10202f, #0a1622);
  color: rgba(169, 198, 222, .8);
}
.cnv1-hero-pending .icon { width: 52px; height: 52px; margin: 0; }
.cnv1-pending-note { font-size: 11.5px; color: var(--text-muted); }

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