/* Мобильный клиент Hodim. Один экран за раз, крупные цели нажатия,
   безопасные отступы под вырезы и жестовую панель Android. */

/* Manrope — голос интерфейса. Лежит у нас, а не у Google: иначе приложение
   перестало бы открываться без сети и в политике приватности появилась бы
   третья сторона, видящая адрес каждого пользователя. Переменный, все
   начертания от 400 до 800 в одном файле на подмножество.
   Лицензия OFL 1.1, текст рядом в fonts/OFL.txt. */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/manrope-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/manrope-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/manrope-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;
}

:root {
  /* Направление E, светлая тема по умолчанию не ставится: значения ниже —
     тёмные, светлые лежат в @media prefers-color-scheme ниже. Так было и
     раньше, менять устройство ради смены палитры незачем.

     Глубина вместо рамок: слои различаются светлотой, а не линиями
     в один пиксель. */
  /* Палитра эталона: почти чёрный фон с синевой, карточки на два тона
     светлее и живой салатовый акцент. Прежние серые (#16191d / #5ec98d)
     владелец продукта назвал «полностью серым, некрасивым»: фон и карточки
     различались на полтона, а зелёный был приглушён до оливкового. */
  --bg: #0d1116;
  --surface: #161c23;
  --surface-2: #1e252e;
  --surface-3: #253039;
  --fg: #f3f6f9;
  --muted: #98a5b4;
  --line: #2a343f;
  /* Зелёный светлее, чем в светлой теме: #1f6f4a на тёмном не читается. */
  --accent: #4ade80;
  --accent-2: #34d399;
  --accent-soft: rgba(74, 222, 128, .16);
  --accent-fg: #06210f;
  /* Главная кнопка — это роль «максимальный контраст», а не «чёрная».
     В тёмной теме она светлая, иначе была бы чёрной на чёрном. */
  --primary: #e9ebee;
  --primary-fg: #0e1013;
  /* Рейка маршрута: линия между точками и обод непройденной точки. */
  --rail: #2a343f;
  --rail-dot: #3c4a58;
  --ok: #4ade80;
  --warn: #f0b429;
  /* «Убрать точку» — единственное действие, которое нельзя отменить одним
     нажатием. Приглушённый красный, а не сигнальный: кнопка не кричит,
     пока её не тронули. */
  --danger: #e08d8d;
  --ad: #c78a4a;
  /* Скругления крупнее: 18 у карточек, пилюля у чипов. Мелкий радиус —
     примета интерфейсов, нарисованных по умолчанию. */
  --radius: 18px;
  --radius-sm: 12px;
  --pill: 999px;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .3);
  --shadow-2: 0 8px 24px rgba(0, 0, 0, .35);
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}

/* Тема: по телефону, пока человек не выбрал своё в настройках. Выбор
   ставит атрибут data-theme на <html> и перебивает системный запрос. */
html[data-theme="light"], html[data-theme="light"] body { color-scheme: light; }
html[data-theme="dark"], html[data-theme="dark"] body { color-scheme: dark; }

@media (prefers-color-scheme: light) {
  html:not([data-theme="dark"]), html:not([data-theme="dark"]) body {
    color-scheme: light;
  }
  :root:not([data-theme="dark"]) {
    --bg: #fbfaf8; --surface: #ffffff; --surface-2: #f4f3f0; --surface-3: #f0efec;
    --fg: #16191a; --muted: #6d7472; --line: #eceae5;
    --accent: #1f6f4a; --accent-2: #1f6f4a; --accent-soft: #e6f0ea; --accent-fg: #ffffff;
    --primary: #16191a; --primary-fg: #ffffff;
    --rail: #e2e1dd; --rail-dot: #c9c8c3;
    --ok: #1f6f4a; --warn: #b45309; --danger: #b4443c; --ad: #92400e;
    --shadow-1: 0 1px 6px rgba(22, 25, 26, .06);
    --shadow-2: 0 20px 50px rgba(22, 25, 26, .13);
    --attrib-invert: 0;
  }
}

:root[data-theme="light"] {
    --bg: #fbfaf8; --surface: #ffffff; --surface-2: #f4f3f0; --surface-3: #f0efec;
    --fg: #16191a; --muted: #6d7472; --line: #eceae5;
    --accent: #1f6f4a; --accent-2: #1f6f4a; --accent-soft: #e6f0ea; --accent-fg: #ffffff;
    --primary: #16191a; --primary-fg: #ffffff;
    --rail: #e2e1dd; --rail-dot: #c9c8c3;
    --ok: #1f6f4a; --warn: #b45309; --danger: #b4443c; --ad: #92400e;
    --shadow-1: 0 1px 6px rgba(22, 25, 26, .06);
    --shadow-2: 0 20px 50px rgba(22, 25, 26, .13);
    --attrib-invert: 0;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* Атрибут hidden обязан побеждать любой display из правил ниже:
   без этого .loader { display: flex } делает лоадер вечно видимым. */
[hidden] { display: none !important; }

html, body {
  margin: 0; height: 100%; overflow: hidden;
  /* Системные элементы — галочки, ползунок бюджета, поле времени — рисует
     браузер. Без этой строки он рисует их светлыми поверх тёмной темы:
     белые квадраты галочек на тёмной карточке видно за версту. */
  color-scheme: dark;
  background: var(--bg); color: var(--fg);
  font: 16px/1.45 Manrope, -apple-system, "Segoe UI", Roboto, system-ui, sans-serif;
  /* Цифры одинаковой ширины по всему приложению: время, суммы и метры
     стоят в колонку и не прыгают при пересчёте маршрута. */
  font-variant-numeric: tabular-nums;
  /* Интерфейс не выделяется пальцем. Владелец продукта: «если мисскликаешь
     по названию, открывается гугл поиск, это бесит». Так и есть: промах
     по названию выделял слово, Android показывал поверх приложения панель
     «нажмите, чтобы увидеть результаты поиска», и человек оказывался
     в браузере вместо карточки места. Выделение остаётся там, где текст
     читают и копируют, — оно перечислено ниже. */
  -webkit-user-select: none; user-select: none;
  /* И меню по долгому нажатию (iOS): «скопировать», «поделиться» поверх
     кнопки — то же самое недоразумение. */
  -webkit-touch-callout: none;
  overscroll-behavior: none;
}

/* Всё приложение живёт внутри #app: он же система координат для карты,
   панели и оверлеев, он же «корпус телефона» на широком экране. */
#app { position: relative; width: 100%; height: 100%; overflow: hidden; background: var(--bg); }

.screen { display: none; height: 100%; flex-direction: column; }
.screen.active { display: flex; }

/* На десктопе показываем вертикальный экран телефона, а не растянутую страницу:
   бету обкатывают в том формате, в котором её увидит пользователь. */
@media (min-width: 700px) and (min-height: 600px) {
  body {
    display: grid; place-items: center;
    background: #0a0b0e;
    background-image: radial-gradient(circle at 50% 0%, #171a21 0%, #0a0b0e 60%);
  }
  #app {
    width: 402px;
    height: min(872px, calc(100vh - 40px));
    border-radius: 40px;
    border: 10px solid #05060a;
    box-shadow: 0 24px 70px rgba(0, 0, 0, .55), 0 0 0 1px #2a2f3a;
  }
  /* Вырез под фронтальную камеру — чтобы safe-area было видно как на телефоне */
  #app::before {
    content: ""; position: absolute; z-index: 10; top: 8px; left: 50%;
    transform: translateX(-50%);
    width: 108px; height: 26px; border-radius: 14px; background: #05060a;
  }
  :root { --safe-top: 30px; --safe-bottom: 10px; }
}

/* ------------------------------- анкета ------------------------------- */

/* Шапка. Заголовок слева, круглая кнопка настроек в правом верхнем углу
   поверх него: язык человек выбирает один раз при первом запуске, а шестерёнка
   нужна и позже. */
.app-bar { position: relative; padding: calc(var(--safe-top) + 18px) 20px 8px; }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface); color: var(--fg);
  cursor: pointer;
}
.icon-btn svg { width: 20px; height: 20px; }
.icon-btn:active { transform: scale(.94); }
.icon-btn.corner { position: absolute; top: calc(var(--safe-top) + 14px); right: 20px; }
/* Заголовок не заезжает под кнопку. */
.app-bar h1, .app-bar .sub { padding-right: 48px; }
.app-bar h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; }
.app-bar .sub { margin: 4px 0 0; color: var(--muted); font-size: 14px; }

.fields {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 12px 20px 20px;
}
.field { display: block; margin-bottom: 22px; }
.label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 8px; }
.big { display: block; font-size: 32px; font-weight: 700; letter-spacing: -0.02em; }
.hint { display: block; margin-top: 6px; font-size: 12px; color: var(--muted); }

input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 34px; background: transparent; margin: 6px 0 0;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 6px; border-radius: 3px; background: var(--surface-2);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 28px; height: 28px; margin-top: -11px; border-radius: 50%;
  background: var(--accent); border: none;
}
input[type="range"]::-moz-range-track { height: 6px; border-radius: 3px; background: var(--surface-2); }
input[type="range"]::-moz-range-thumb {
  width: 28px; height: 28px; border: none; border-radius: 50%; background: var(--accent);
}

.chips { display: flex; gap: 8px; }
.chips.wrap { flex-wrap: wrap; }
.chips.wrap button { flex: 0 0 auto; }

/* Группа настроек: колонки равной ширины вместо ряда по содержимому.
   Английские подписи длиннее украинских, и в ряд они не помещались —
   «Metres and kilometres» вылезало за кнопку. Простой перенос давал рваную
   лесенку «две кнопки и одна», сетка держит их ровными. */
.chips.grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 8px;
}
.chips.grid button {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  white-space: normal; line-height: 1.25; text-align: center;
  padding: 8px 10px; min-height: 46px;
}
/* Флажок языка внутри кнопки: та же ширина, что была в прежнем меню. */
.chips.grid button svg {
  width: 18px; height: 12px; flex: none; border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .25) inset;
}

/* Язык — строками, а не колонками: «Українська» с флажком в треть ширины
   экрана не помещается и обрезается. Строка на язык читается как выбор
   в системных настройках и влезает на любом языке. */
.chips.rows { grid-template-columns: 1fr; }
.chips.rows button { justify-content: flex-start; padding-left: 14px; }
.chips button {
  /* Ряды с фиксированным числом кнопок («скільки вас», «скільки часу»)
     делят ширину поровну; переносимые группы ниже — по содержимому,
     иначе текст обрезается. */
  flex: 1 1 auto; min-width: 0; min-height: 46px; padding: 0 16px;
  white-space: nowrap;
  /* Пилюля и заливка вместо рамки: обведённые прямоугольники читаются
     как поля формы, а нажимать хочется то, что похоже на кнопку. */
  border: 1px solid transparent; border-radius: var(--pill);
  /* На ступень светлее карточки, в которой лежат: с одинаковым фоном чипы
     сливались с ней и выглядели просто строчками текста. */
  background: var(--surface-2); color: var(--fg);
  font: inherit; font-size: 15px; cursor: pointer;
}
.chips button.on {
  background: var(--accent);
  color: var(--accent-fg); border-color: transparent; font-weight: 600;
  box-shadow: var(--shadow-1);
}
.chips button:active:not([disabled]) { transform: scale(.96); }

.switch { display: inline-flex; align-items: center; gap: 8px; margin-top: 12px; font-size: 15px; }
.switch input { width: 22px; height: 22px; accent-color: var(--accent); }

.bottom-bar {
  padding: 12px 20px calc(var(--safe-bottom) + 16px);
  border-top: 1px solid var(--line); background: var(--surface);
}
/* ─────────────────────────── Главная ───────────────────────────
   То, ради чего приложение открывают: одна кнопка, начатый маршрут
   и три коротких ленты. Ленты крутятся вбок — вертикаль на главной
   дороже: до кнопки «скласти маршрут» человек должен дотянуться
   не листая. */
/* Шапка стоит над прокруткой, поэтому у неё свой фон: без него заголовок
   ленты подъезжал под приветствие и читался наполовину. */
.home-head {
  position: relative; z-index: 2; background: var(--bg);
  padding: calc(var(--safe-top) + 18px) 20px 4px;
}
.home-head h1 { margin: 0; font-size: 26px; letter-spacing: -0.02em; }
.home-head .hint { margin: 4px 0 0; }
/* Запас снизу под нижнюю панель: она лежит поверх экрана, и без него
   последний блок уходил под неё. Владелец продукта: «достижения вообще
   за экран вниз уехали и не скроллятся». */
.home-body {
  flex: 1; overflow-y: auto;
  padding: 12px 20px calc(var(--safe-bottom) + 84px);
}
#home-build { width: 100%; margin-bottom: 16px; }

.home-block { margin-top: 22px; }
.home-block-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
/* Заголовок в одну строку: «Сьогодні в місті» переносилось на две,
   и «Усі» повисало сбоку. */
.home-block-head h2 {
  flex: 1 1 auto; min-width: 0;
  margin: 0; font-size: 16px; letter-spacing: -0.01em; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* Базовый `.linkish` — кнопка во всю ширину (так она стоит под анкетой).
   В шапке блока это забирало всю строку, и заголовок сжимался в ноль:
   владелец продукта увидел ленты без названий — «косая вёрстка». */
.home-block-head .linkish {
  flex: none; width: auto; margin: 0; padding: 4px 0; font-size: 14px;
}
/* Лента вбок: карточки одной ширины, край последней виден — так понятно,
   что список продолжается. */
.home-rail {
  display: flex; gap: 10px; margin: 10px -20px 0; padding: 0 20px 4px;
  overflow-x: auto; scroll-snap-type: x proximity;
  /* Привязка считает «начало» от края прокрутки, а не от внутреннего
     отступа: лента открывалась уже сдвинутой на 20 px, и первая карточка
     упиралась в край экрана. Владелец продукта: «чипы кривые, упираются
     в край экрана». Отступ привязки равен отступу ленты. */
  scroll-padding-inline: 20px;
  scrollbar-width: none;
}
.home-rail::-webkit-scrollbar { display: none; }
/* Перетаскивание мышью: на компьютере горизонтальной прокрутки у мыши нет.
   Владелец продукта: «на компе не могу вращать эти чипы вбок». */
@media (hover: hover) and (pointer: fine) {
  .home-rail { cursor: grab; }
  .home-rail.dragging { cursor: grabbing; scroll-snap-type: none; }
  .home-rail.dragging * { pointer-events: none; }
}
/* Две карточки целиком и краешек третьей: при жёстких 180 px вторая
   обрезалась ровно посередине — «кривые чипы». */
.home-card {
  flex: none; width: 46%; min-width: 148px; max-width: 210px;
  scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--fg); font: inherit; text-align: left;
  cursor: pointer;
}
.home-card:active { transform: scale(.98); }
/* Название — не больше трёх строк. Владелец продукта: «почему длина текста
   порвала к хренам длину чипа и всё изуродовало». Театрализованная
   экскурсия с интерактивно-анимационной постановкой растянула карточку
   на двенадцать строк, а с ней и всю ленту: соседние карточки в ряду
   тянутся за самой высокой. */
.home-card b {
  font-size: 14px; line-height: 1.25;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}
/* Подпись под названием — две строки: площадка и цена целиком не нужны,
   полное название человек увидит, открыв событие. */
.home-card .hint {
  font-size: 12px;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
/* Одинаковая высота во всей ленте: карточки стоят в ряд, и разнобой
   читается как поломка. */
.home-rail .home-card { min-height: 132px; }
.home-card-when { font-size: 12px; font-weight: 600; color: var(--accent); }

/* Карточка маршрута города в ленте: схема пути сверху, под ней точки. */
/* Карточка маршрута на главной — снимок во всю карточку, поверх него
   название, цифры и два чипа. Так она устроена на рендере главной. */
.city-card {
  position: relative; overflow: hidden;
  width: 78%; min-width: 250px; max-width: 300px; min-height: 190px;
  padding: 0; border-radius: 18px;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.city-card-bg { position: absolute; inset: 0; z-index: 0; }
.city-card-bg .history-map { width: 100%; height: 100%; display: block; }
.city-card::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(0, 0, 0, .12) 0%, rgba(0, 0, 0, .55) 52%, rgba(0, 0, 0, .92) 100%);
}
.city-card-body {
  position: relative; z-index: 2; display: grid; gap: 5px;
  padding: 10px 12px 12px; text-align: left;
}
.city-card-body b {
  font-size: 15px; font-weight: 700; color: #fff; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.city-card-nums { font-size: 12.5px; color: rgba(255, 255, 255, .85); }
.city-card-chips { display: flex; flex-wrap: wrap; gap: 5px; }
/* Отступы тесные, потому что чипов четыре, а не два: замер на телефоне
   показал нехватку двадцати точек, и «55 ккал» уезжал на вторую строку.
   Перенос сам по себе не ломается, но ряд из трёх и одного выглядит
   случайным — будто последний чип забыли. */
.city-chip {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 6px; border-radius: var(--pill);
  background: rgba(8, 12, 17, .72); color: #fff;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
.city-chip-easy { color: #4ade80; }
.city-chip-medium { color: #facc15; }
.city-chip-hard { color: #fb923c; }
.city-chip-extreme { color: #f87171; }
.city-chip-kcal { color: #ffb277; }
/* Цвета те же, что у плиток на экране маршрута: шаги зелёные (это
   расстояние), время сиреневое. Одна шкала цветов на всё приложение —
   человек узнаёт цифру по цвету раньше, чем прочтёт подпись. */
/* Список точек в догоняющем вопросе: их обычно одна-две, изредка три.
   Кружок вместо маркера — тот же знак, что стоит у точки на карте. */
.passed-list {
  margin: 0 0 4px; padding: 0; list-style: none; display: grid; gap: 6px;
  text-align: left; max-height: 40vh; overflow-y: auto;
}
.passed-list li {
  display: flex; align-items: center; gap: 8px;
  font-size: 15px; color: var(--fg);
}
.passed-list li::before {
  content: ''; flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
}
.city-chip-steps { color: #6fe0a0; }
.city-chip-time { color: #c9a6ff; }
/* Меню карточки маршрута: три точки и список под ними. Раньше нажатие
   вело прямо к вопросу об удалении — единственное действие пряталось
   за значком «ещё». */
.history-menu-wrap { position: relative; }
.history-menu {
  position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 20;
  min-width: 168px; padding: 6px;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
}
/* Открывается вверх: карточки лежат стопкой, и список под нижней уехал бы
   за край экрана. */
.history-menu button {
  display: block; width: 100%; padding: 10px 12px;
  border: 0; border-radius: 8px; background: none; color: var(--fg);
  font: inherit; font-size: 14.5px; text-align: left; cursor: pointer;
}
.history-menu button:hover, .history-menu button:focus-visible {
  background: var(--surface-2);
}
.history-menu button.danger { color: var(--danger, #f87171); }

/* «Топ» у маршрутов, которые прошли и высоко оценили. */
.city-card-top {
  position: absolute; z-index: 3; top: 10px; left: 10px;
  padding: 3px 10px; border-radius: var(--pill);
  background: var(--accent); color: var(--accent-fg);
  font-size: 11.5px; font-weight: 700;
}
/* ─────── Схема маршрута: линия поверх подложки с домами ───────
   Владелец продукта: «помимо линии — фон в виде реальных застроек, как
   полноценная схема, а надписи с характеристиками под ней». Подложка
   приезжает картинкой (см. app.js, схемаМаршрута) и появляется плавно:
   без этого карточка дёргается, когда снимок доезжает. */
/* Пока снимка нет, подложка держит цвет карты, а не тёмной панели:
   карточка истории на полсекунды становилась чёрным прямоугольником,
   пока строился снимок. Владелец продукта: «сначала чёрная, длится это
   примерно 0.5-1 сек». Оттенок — светлая земля OpenFreeMap, тот же,
   что у полей на готовом снимке, поэтому подмена не бросается в глаза. */
.route-thumb {
  position: relative; display: block; overflow: hidden;
  width: 100%; height: 100%;
  border-radius: var(--radius-sm);
  background: #e9e5dd;
}
.route-thumb svg {
  position: relative; z-index: 1;
  width: 100%; height: 100%; display: block;
}
.route-thumb-bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 220ms ease;
}
.route-thumb.has-bg .route-thumb-bg { opacity: 1; }
/* Линия читается и на светлых кварталах: под ней лёгкая подложка. */
.route-thumb.has-bg svg { filter: drop-shadow(0 0 2px var(--bg)); }
/* Скрытая карта, на которой рисуются все подложки. Из потока убрана
   полностью, но не `display:none` — иначе WebGL не отрисует кадр. */
.thumb-stage {
  position: fixed; left: -9999px; top: 0;
  pointer-events: none; opacity: 0;
}

/* Схема во всю ширину карточки, подписи под ней. */
.city-card-map { display: block; height: 96px; margin-bottom: 6px; }

/* «Назад» и подвкладки — одной строкой. Владелец продукта о макете:
   «кнопка назад лежит в едином ряде с маршрутами, красиво выглядит». */
.routes-bar {
  display: flex; align-items: center; gap: 10px;
  padding: calc(var(--safe-top) + 10px) 16px 0;
}
.routes-bar > .ghost { flex: none; }
/* Подвкладки делят остаток строки поровну: их две, а общая сетка вкладок
   рассчитана на три, и обе прижимались влево. Владелец продукта: «лучшие
   маршруты слеплен, а должен был равно поделить место с соседом». */
#routes-subtabs {
  flex: 1 1 auto; min-width: 0; margin: 0;
  grid-template-columns: repeat(2, 1fr);
}
/* Ряд действий тот же, что в своих маршрутах: кнопка слева, значки
   справа. Владелец продукта: «кнопку пройти маршрут убери такую огромную»,
   «должна быть точно того же стиля», «кнопки поделиться и удалить добавь,
   как в моих маршрутах, в то же самое место». */
/* «Пройти маршрут» — той же ширины и формы, что «Поділитися з містом»
   в своих маршрутах: обе вкладки должны читаться как одна карточка. */
.city-route-actions .ghost {
  flex: none; width: auto; margin-top: 0; margin-right: 0;
  min-height: 38px; border-radius: 12px;
  background: var(--surface-2); border: 1px solid var(--line);
}
.city-route-actions .city-forget { color: var(--muted); }

/* Карточка подборки — та же карточка маршрута: размеры, поля и отступы
   берутся у `.history-card`, чтобы обе вкладки выглядели одинаково.
   Владелец продукта: «сделай размер чипов полностью одинаковый как
   в моих маршрутах». */
/* Карточка подборки живёт по тем же правилам, что «Мои маршруты»:
   подложка во всю ширину, текст и цифры поверх неё. */
.city-photo {
  display: block; width: 100%; height: 100%; object-fit: cover;
}

.city-route-text { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.city-route-text b { font-size: 15px; }

/* Карточка нажимается целиком — значит и выглядеть должна нажимаемой. */
.city-route[data-open] { cursor: pointer; }
.city-route[data-open]:active { background: var(--surface-2); }
.city-route[data-open]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Нижняя строка карточки — как на эталоне: широкая кнопка «Поділитися
   з містом» со значком закладки и два круга справа. Все три одной семьёй
   с плитками: фон карточки, тонкий контур, ничего яркого.

   Владелец продукта: «нет подложки под нижнюю панель» — раньше кнопки
   лежали прямо на светлой карте, от контура оставался кусок, а надпись
   сливалась с домами. Теперь под ними ровный фон карточки. */
.history-actions .history-to-city svg { flex: none; opacity: .9; }
.history-actions .icon-btn {
  flex: none; border-radius: 50%;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--fg);
}

/* ─────── Карточка места в ленте «Поруч»: снимок подложкой ───────
   Владелец продукта: «подложкой чипов мест рядом должны быть фотки,
   только сделать, чтобы всё сочеталось — фото и текст, возможно чуть
   стилизовать». Текст лежит на снимке, поэтому под ним затемнение:
   снимки приходят любые, и на светлом небе белые буквы исчезали. */
.place-card { position: relative; overflow: hidden; padding: 0; min-height: 132px; }
.place-card-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
}
.place-card-text {
  position: relative; z-index: 1; margin-top: auto;
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px 14px; text-align: left;
}
/* Название слева, расстояние в правом нижнем углу карточки.

   Сначала они стояли в одной сетке и выравнивались по общей строке:
   у названия в две строки расстояние съезжало вниз, у короткого —
   оставалось вверху. Владелец продукта: «почему оно скачет». Теперь
   расстояние прижато к углу и не зависит от длины названия, а текст
   оставляет ему место справа. */
#home-places .place-card-text {
  display: block;
  padding-right: 64px;
}
#home-places .place-card-text b {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
#home-places .place-card .hint {
  position: absolute; right: 12px; bottom: 12px; z-index: 2;
  white-space: nowrap;
}
/* Затемнение только под текстом: снимок сверху остаётся снимком. */
.place-card.has-photo .place-card-text {
  background: linear-gradient(to top,
    rgba(0, 0, 0, .78) 0%, rgba(0, 0, 0, .55) 55%, rgba(0, 0, 0, 0) 100%);
  padding-top: 28px;
}
.place-card.has-photo .place-card-text b { color: #fff; }
.place-card.has-photo .place-card-text .hint { color: rgba(255, 255, 255, .82); }
/* Без снимка карточка прежняя: текст на поверхности, без градиента. */
.place-card:not(.has-photo) { justify-content: flex-start; }
.place-card:not(.has-photo) .place-card-text { padding-top: 12px; }

/* Достижения: значок, «продолжай идти», название, полоса и цифры —
   как на рендере главной. */
.home-ach {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) 20px;
  align-items: center; gap: 12px;
  width: 100%; margin-top: 10px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); color: var(--fg); font: inherit; text-align: left;
  cursor: pointer;
}
.home-ach-icon { font-size: 30px; line-height: 1; text-align: center; }
.home-ach-text { display: grid; gap: 3px; min-width: 0; }
.home-ach-text b { display: block; font-size: 16px; font-weight: 700; }
.home-ach-text .hint { font-size: 11.5px; }
.home-ach-bar {
  height: 5px; border-radius: 3px; background: var(--surface-2);
  overflow: hidden;
}
.home-ach-bar i { display: block; height: 100%; background: var(--accent); }

/* Карточка последнего маршрута на главной: своих отступов формы у неё
   больше нет. */
#home-screen .last-plan { margin: 0 0 4px; }

/* ─────────────────── Нижняя панель навигации ───────────────────────
   Владелец продукта: «перенесём верхнее меню вниз, сделаем общую панель
   навигации… слева в углу главная, справа в углу профиль». Значки
   в верхних углах телефона большим пальцем не достаются.

   Панель одна на всё приложение и лежит поверх экранов; экраны получают
   отступ снизу через `body.with-tabbar`, чтобы последняя строка списка
   не пряталась под ней. */
.tabbar {
  /* `absolute`, а не `fixed`: панель — часть «корпуса телефона» (#app),
     и на широком экране она обязана кончаться там же, где приложение.
     С `fixed` она растягивалась во всю ширину окна браузера. */
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 60;
  display: grid; grid-template-columns: repeat(5, 1fr);
  padding: 8px 4px calc(var(--safe-bottom) + 8px);
  /* На эталоне панель того же тона, что фон экрана, и без светлой
     полосы сверху: она не должна спорить с карточками. */
  background: var(--bg); border-top: 0;
}
.tabbar .tab {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 4px 2px; border: 0; background: none; cursor: pointer;
  color: var(--muted); font: inherit; font-size: 11px; line-height: 1.2;
}
.tabbar .tab svg { width: 24px; height: 24px; }
.tabbar .tab.on { color: var(--accent); }
.tabbar .tab:active { transform: scale(.94); }

/* Место под панель. Списки и кнопка «Далі» иначе уходят под неё. */
body.with-tabbar .bottom-bar { padding-bottom: calc(var(--safe-bottom) + 74px); }
body.with-tabbar .fields,
body.with-tabbar #history-list,
body.with-tabbar #events-list,
body.with-tabbar .screen > .list { padding-bottom: 78px; }
/* На карте мест панель лежит поверх: карта занимает весь экран, и отступ
   ей не нужен — нужен запас нижним кнопкам. */
body.with-tabbar #explore-chip { bottom: calc(var(--safe-bottom) + 76px); }
/* Панель прицела — над нижним меню, как и полоска места: без этого её
   нижняя половина уходила под вкладки, и кнопки «Зберегти» было не достать.
   Владелец продукта: «панельку при создании точки поднять выше, она сейчас
   прячется под менюшку». */
body.with-tabbar #aim-bar { bottom: calc(var(--safe-bottom) + 76px); }

.geo { margin: 0 0 10px; font-size: 13px; color: var(--muted); text-align: center; }
.geo.err { color: var(--warn); }

/* Главная кнопка — роль «максимальный контраст», а не «зелёная». В светлой
   теме она тёмная, в тёмной светлая. Акцент отдан тому, что помечает:
   бесплатным точкам, выбранным ответам, метке старта на карте. Если красить
   акцентом и кнопку, самое яркое пятно экрана перестаёт что-либо значить. */
button.primary, a.secondary {
  display: block; width: 100%; min-height: 56px; border: none;
  border-radius: var(--pill);
  background: var(--primary);
  color: var(--primary-fg);
  font: inherit; font-size: 17px; font-weight: 700; letter-spacing: -0.01em;
  cursor: pointer; text-align: center; line-height: 56px; text-decoration: none;
  box-shadow: var(--shadow-2);
  transition: transform .12s ease, box-shadow .12s ease, opacity .12s ease;
}
button.primary:active:not([disabled]) { transform: scale(.98); box-shadow: var(--shadow-1); }
button.primary[disabled] { opacity: .45; }
a.secondary { background: var(--surface-2); color: var(--fg); margin-top: 14px;
              box-shadow: none; }
button.ghost { background: none; border: 1px solid var(--line); color: var(--fg);
               border-radius: 10px; font: inherit; cursor: pointer; }
button.ghost.small { min-height: 34px; padding: 0 12px; font-size: 14px; }

/* ------------------------------ маршрут ------------------------------- */

/* Карта и лист делят экран поровну. Владелец продукта об эталоне: «нужно
   больше места карте уделить: если на рендере 30/70 в пользу ui, у нас
   должно быть примерно 50/50». */
#map { position: absolute; inset: 0 0 50% 0; background: var(--surface-2); z-index: 0;
        transition: inset .22s ease; }

/* Карта на весь экран: панель уезжает вниз, остаётся кнопка возврата
   и компактная карточка выбранной точки. */
.map-full #map { inset: 0; }
.map-full .sheet { top: 100%; }

.map-fab {
  position: absolute; z-index: 5; top: calc(var(--safe-top) + 12px); right: 12px;
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 38px; padding: 0 16px; border-radius: 19px; border: none;
  background: var(--surface); color: var(--fg); font: inherit; font-size: 14px;
  font-weight: 600; cursor: pointer; box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
}

/* Контролы карты справа сверху идут под кнопкой «Карта/Список»: она
   лежит поверх карты и заняла бы то же место. */
#map .maplibregl-ctrl-top-right,
#explore-map .maplibregl-ctrl-top-right,
#pick-map .maplibregl-ctrl-top-right {
  top: calc(var(--safe-top) + 58px); right: 4px;
}

/* Нижние контролы карты (масштаб, атрибуция) поднимаются над панелью
   навигации: иначе плюс и минус наполовину уходят под неё. */
body.with-tabbar #explore-map .maplibregl-ctrl-bottom-right,
body.with-tabbar #explore-map .maplibregl-ctrl-bottom-left {
  bottom: calc(var(--safe-bottom) + 62px);
}

/* Карточка точки, выбранной на карте. Пока карта занимает верхние 55 %
   экрана, карточка держится над панелью маршрута: с прежним `bottom` она
   ложилась поверх панели, и владелец продукта увидел «патч», который нечем
   закрыть — тап по карте до него уже не доставал. */
.map-card {
  position: absolute; left: 12px; right: 12px; bottom: 16px; z-index: 7;
  display: grid; gap: 10px;
  padding: 12px; max-height: 46vh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-2);
}
/* Карточка точки на карте маршрута — та же, что на экране мест: снимок,
   категория, адрес. Владелец продукта: «нужно всё свести к единому».
   Своё у неё только время прихода. */
.map-card .when { font-size: 13px; color: var(--accent); font-weight: 600; }
.map-card .chip-close {
  position: absolute; top: 8px; right: 8px;
  width: 28px; height: 28px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface-2);
  color: var(--muted); font-size: 18px; line-height: 1; cursor: pointer;
}
/* «Як дістатися» рядом с «Подробиці», а не отдельной строкой. */
.map-card .chip-nav {
  flex: 1 1 auto; min-height: 40px; border-radius: var(--pill);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--accent-fg);
  font-weight: 600; font-size: 15px; text-decoration: none;
}

.map-full .map-card { bottom: calc(var(--safe-bottom) + 14px); }
.map-pin { cursor: pointer; }
.map-pin.origin { background: var(--ok) !important; width: 14px !important; height: 14px !important;
                  box-shadow: 0 0 0 4px rgba(127, 209, 139, .25) !important; }

.sheet {
  z-index: 2;
  position: absolute; left: 0; right: 0; bottom: 0; top: 50%;
  background: var(--surface); border-radius: 20px 20px 0 0;
  border-top: 1px solid var(--line);
  padding: 0 16px calc(var(--safe-bottom) + 20px);
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  transition: top .22s ease;
}
.sheet.expanded { top: 8%; }
/* Полоска захвата. Сама полоска тонкая, но область жеста — вся её строка:
   пять пикселей пальцем не поймать. */
.grip {
  position: relative; width: 100%; height: 26px; margin: 2px 0 6px;
  cursor: grab; touch-action: none;
}
.grip::after {
  content: ''; position: absolute; left: 50%; top: 11px;
  width: 44px; height: 5px; margin-left: -22px;
  border-radius: 3px; background: var(--line);
}
.grip:active { cursor: grabbing; }

/* Шапка листа: название маршрута и из чего он состоит, «поділитися» справа. */
.sheet-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
/* Сердце у заголовка: контур, без плашки — как на эталоне. */
.heart-btn {
  flex: none; width: 36px; height: 36px; padding: 0; border: 0;
  background: none; color: var(--fg); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.heart-btn.on { color: var(--accent); }
.heart-btn.on svg { fill: currentColor; }
.heart-btn:active { transform: scale(.94); }
.sheet-title { min-width: 0; }
/* Заголовок в одну строку и мельче: вместе с подзаголовком он занимал
   восьмую часть листа, а лист делит экран с картой пополам. Владелец
   продукта: «„Маршрут по Києву“ и строчка ниже мешают и занимают слишком
   много места». Из чего сложена прогулка, видно по самим точкам. */
.sheet-title h2 {
  margin: 0; font-size: 17px; line-height: 1.2; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Плитки маршрута крупнее, чем в карточках истории: здесь они главные
   цифры экрана, а не подпись к картинке. */
#route-stats { margin: 8px 0 2px; gap: 8px; }
/* Четыре плитки в ряд на телефоне — это 80 точек на каждую: значку
   отдаём двадцать, остальное числу и подписи. С прежними двадцатью
   шестью подпись «бюджет» обрывалась на «бюд…». */
#route-stats .ach { padding: 7px 8px; gap: 5px;
  grid-template-columns: 20px minmax(0, 1fr); }
#route-stats .ach-icon { width: 20px; height: 20px; }
#route-stats .ach-icon svg { width: 18px; height: 18px; }
#route-stats .ach b { font-size: clamp(12px, 3.8vw, 16px); }
#route-stats .ach-long b { font-size: clamp(10px, 3.2vw, 13px); }
#route-stats .ach-label { font-size: 9.5px; letter-spacing: -0.01em; }

/* Вкладки маршрута: активная обведена зелёным, как на эталоне. */
/* Четыре подписи обязаны поместиться в ширину телефона: «Як дістатися»
   обрывалось на «Як добраты». Владелец продукта: «часть слов не
   поместились на экран просто». */
.route-tabs {
  display: flex; gap: 4px; margin: 14px 0 10px;
  overflow-x: auto; scrollbar-width: none;
}
.route-tabs::-webkit-scrollbar { display: none; }
/* Неактивные вкладки — только текст, без плашки: на эталоне подсвечена
   ровно одна. Кегль такой, чтобы «Як дістатися» помещалось целиком. */
.route-tab {
  flex: none; min-height: 32px; padding: 0 9px; border-radius: var(--pill);
  border: 1px solid transparent; background: transparent; color: var(--muted);
  font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  white-space: nowrap;
}
.route-tab.on { border-color: var(--accent); color: var(--accent); }

/* Снимки маршрута сеткой: во вкладке «Фото» они и есть содержание. */
.route-photos { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.route-photos img {
  width: 100%; height: 120px; object-fit: cover; display: block;
  border-radius: var(--radius-sm); background: var(--surface-2); cursor: pointer;
}
.route-about h4 { margin: 14px 0 4px; font-size: 15px; }
.route-about p { margin: 0; font-size: 14px; color: var(--muted); line-height: 1.45; }
.route-nav { display: grid; gap: 10px; }
.route-nav .nav-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
}
.route-nav .nav-row a {
  flex: none; min-height: 32px; padding: 0 12px; border-radius: var(--pill);
  display: inline-flex; align-items: center; background: var(--accent);
  color: var(--accent-fg); font-size: 13px; font-weight: 600; text-decoration: none;
}
.sheet-actions { display: flex; gap: 8px; align-items: center; margin-top: 10px; }
/* Главное действие — во всю оставшуюся ширину, как на эталоне. */
.sheet-actions .go.wide { flex: 1 1 auto; }
.sheet-actions .icon-btn { flex: none; width: 52px; height: 52px; }
/* Второстепенные действия открываются по «ще»: на экране их место занимает
   карта. */
.sheet-extra { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
/* Главное действие экрана маршрута. Зелёная и заметная: остальные кнопки
   в этой строке — про «потом», а эта про «сейчас иду». */
.sheet-actions .go {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 52px; padding: 0 22px; border: none;
  border-radius: var(--pill);
  background: var(--accent); color: var(--accent-fg);
  font: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
}
.sheet-actions .go:active:not([disabled]) { transform: scale(.98); }
.sheet-actions .go.done { background: var(--surface-2); color: var(--muted); }
/* Пока маршрут идёт, кнопка остаётся живой: на ней время и «завершити». */
.sheet-actions .go.walking { background: var(--warn); color: #1b1206; }
/* Пока идём, рядом с «Паузою» стоит отмена — маленькая и красная. Кнопка
   паузы от этого уже примерно на пятую часть: место занимает отмена. */
.icon-btn.danger { border-color: rgba(229, 72, 77, .5); color: #e5484d; }
.icon-btn.danger:active { transform: scale(.94); }
/* Вес правила выше, чем у общего `.finish-actions .go`: иначе кнопка
   отмены выходила зелёной, а зелёный на таком вопросе читается как «ок». */
.finish-actions .go.danger-fill { background: #e5484d; color: #fff; }
.sheet-actions .share {
  width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--fg); border: 1px solid var(--line);
}
.spent { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; }
/* «из 1200 ₴» переносится целиком: разорванное надвое, оно читается
   как две разные суммы. */
.of { color: var(--muted); font-size: 14px; margin-left: 6px; white-space: nowrap; }
.summary { margin: 6px 0 16px; color: var(--muted); font-size: 14px; }

/* Точка маршрута: слева рейка со временем, справа карточка.
   Снимок — значок 62×62 сбоку, а не полоса во всю ширину. Причина
   не в красоте: фотография есть у 22–42 % культурных точек, и карточка,
   построенная вокруг снимка, в райцентре превращалась в серый прямоугольник.
   Здесь точка без фото просто становится компактнее. */
/* Карточка занимает всю ширину листа, номер лежит поверх её угла —
   как на макете. Прежняя колонка в 38 точек под номер и время съедала
   десятую часть ширины у каждого снимка. Владелец продукта: «фото мест
   должны быть подложкой так же на всю ширину». */
.point { position: relative; display: block; padding: 0 0 11px; }
.point .rail {
  position: absolute; top: 10px; left: 10px; z-index: 3;
  display: flex; flex-direction: column; align-items: center;
}
/* Линия соединяет чекпоинты, как в списке этапов: идёт от номера вниз
   поверх снимка и до следующей карточки. Пройденный участок зелёный —
   по нему видно, докуда дошли. Владелец продукта: «нет вертикальной линии
   между чекпоинтами, она должна быть интерактивная». */
.point .rail-line { display: none; }
/* Линия идёт ровно через центр кружка с номером: кружок стоит на 10 точках
   от края и сам шириной 26, значит центр — на 23. Раньше линия висела
   на 11 и проходила мимо цифр. Владелец продукта: «поправь вертикальную
   линию слева, чтобы она чётко проходила через круглые цифры». */
.point::before {
  content: ''; position: absolute; z-index: 2;
  left: calc(10px + 13px - 1.5px); top: 40px; bottom: 0;
  width: 3px; border-radius: 2px;
  background: rgba(255, 255, 255, .35);
}
/* Последняя точка: линия доходит до низа карточки, и метка стоит там же.

   Раньше линия у неё просто обрывалась, а кружок висел сверху: маршрут
   выглядел незаконченным — будто дальше есть что-то, чего не видно.
   Владелец продукта нарисовал, как надо: «у последнего пункта в маршруте
   протяни линию и перенеси зелёную галочку в нижнюю часть карточки». */
.point:last-child::before { top: 0; bottom: 26px; }
.point:last-child .rail { top: auto; bottom: 11px; }
.point.done::before { background: var(--accent); }
.point .time { font-weight: 600; font-size: 13px; color: var(--accent); }
/* Пройденные и будущие точки различаются заливкой кружка, а не цветом текста:
   на маленьком экране заливка заметнее. */
/* Кружок с номером этапа: он же стоит на метке карты. Размер под цифру —
   двенадцати пикселей прежнего кружка на неё не хватало. */
.point .dot {
  width: 26px; height: 26px; border-radius: 50%; margin-top: 0;
  border: 2px solid rgba(255, 255, 255, .85);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
  background: var(--accent); color: var(--accent-fg); flex-shrink: 0;
  display: grid; place-items: center;
  font-size: 12px; font-weight: 700; line-height: 1;
}
.point ~ .point .time { color: var(--fg); }
.point ~ .point .dot {
  background: var(--bg); border: 2px solid var(--rail-dot); color: var(--fg);
}
.point .rail-line { width: 2px; flex-grow: 1; background: var(--rail); margin: 4px 0; min-height: 12px; }
/* Текст и снимок стоят рядом, ряд действий — под ними во всю ширину.

   Раньше действия лежали внутри текстовой колонки, а её сужает снимок:
   на телефоне под кнопки оставалось 185 пикселей из 285, и они переносились
   по одной на строку. Теперь им достаётся вся ширина карточки. */
/* Счёт прогулки и пройденные точки — «игра» из просьбы владельца:
   видно, сколько закрыто и сколько осталось. */
.walk-progress {
  margin: 2px 0 8px; font-size: 13px; font-weight: 600; color: var(--accent);
  text-align: center;
}
.walk-progress.done { color: var(--ok); }
/* Пройденная точка отличается только галочкой в кружке. Приглушать всю
   карточку не нужно: владелец продукта — «убрать затемнение фото на
   пройденных точках, достаточно уже зелёных выполненных галочек». Снимок
   под затемнением читался как незагрузившийся. */
.point.done .time::after { content: ' ✓'; }
.point.done .dot {
  background: var(--ok); border-color: var(--ok); color: var(--accent-fg);
  font-size: 0;
}
/* Номер сменился галочкой — ровно как на метке карты. */
.point.done .dot::after { content: '✓'; font-size: 13px; }
/* Метка на карте: номер сменился галочкой. */
.map-pin.done { background: var(--ok) !important; }

/* Снимок — подложка всей карточки, текст лежит поверх него. Владелец
   продукта: «фотография должна быть подложкой под весь блок точки,
   с небольшим затемнением по низу». Прежний снимок 88×88 сбоку сужал
   текстовую колонку, и длинное название ломалось на три строки. */
.point .card {
  position: relative; overflow: hidden;
  background: var(--surface); border-radius: var(--radius-sm); padding: 0;
  box-shadow: var(--shadow-1);
  display: flex; flex-direction: column; justify-content: flex-end;
}
.point .card.with-photo { min-height: 176px; }
/* Затемнение только по низу: под текстом, а не поверх всей картинки. */
.point .card.with-photo::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(0, 0, 0, 0) 8%, rgba(0, 0, 0, .58) 42%, rgba(0, 0, 0, .92) 100%);
}
/* Текст отступает от линии чекпоинтов, которая идёт по левому краю
   карточки: без отступа названия наезжали прямо на неё. Владелец
   продукта: «названия всё ещё наезжают на полоску». */
.point .body { min-width: 0; padding: 11px 12px 0 32px; position: relative; z-index: 2; }
.point .card:not(.with-photo) .body { padding-top: 42px; }
.point .card.with-photo .body { padding-top: 8px; }
/* В карточке маршрута — только название, чипы и переход, как на макете.
   Адрес, описание и примечания к цене читаются во вкладке «Опис»
   и в карточке места: над чипами они повторяли их же словами. */
.point .addr, .point .desc, .point .meta { display: none; }
.point .card.with-photo h3 { color: #fff; text-shadow: 0 1px 4px rgba(0, 0, 0, .8); }
.point .card.with-photo .addr,
.point .card.with-photo .desc,
.point .card.with-photo .meta { color: rgba(255, 255, 255, .88);
  text-shadow: 0 1px 4px rgba(0, 0, 0, .8); }
/* Чипы на снимке — тёмным стеклом, иначе светлые серые тонут в кадре. */
/* На снимке остаются только название и чипы, как на эталоне: описание
   и адрес там превращались в кашу поверх кадра. Полный текст — во вкладке
   «Опис» и в карточке места. */
.point .card.with-photo .desc,
.point .card.with-photo .addr,
.point .card.with-photo .meta { display: none; }
.point .card.with-photo .badge {
  background: rgba(8, 12, 17, .72); color: #fff; border-color: transparent;
}
.point .card.with-photo .badge.free { background: var(--accent); color: var(--accent-fg); }
/* Дорога до этой точки — в нижней строке карточки, слева от значков:
   там она стояла всегда, менялся только счёт. */
.leg-in {
  display: flex; align-items: center; gap: 6px; margin: 0 auto 0 0;
  font-size: 13px; font-weight: 600; white-space: nowrap;
}
.point .card.with-photo .leg-in { color: #fff; text-shadow: 0 1px 4px rgba(0, 0, 0, .8); }
.point .card .leg-in { color: var(--muted); }
.leg-in svg { flex: none; opacity: .9; }
.point h3 { margin: 0 0 3px; font-size: 17px; font-weight: 700; letter-spacing: -0.01em; }
.point .meta { color: var(--muted); font-size: 13px; margin: 0 0 4px; line-height: 1.4; }
/* Описание — три строки. Вступление статьи Википедии бывает на абзац,
   и в карточке маршрута оно вытесняет остальные точки за край экрана:
   человек листает не маршрут, а чтение. Полный текст открывается по тапу
   на названии. */
.point .desc {
  font-size: 13px; color: var(--muted); margin: 0 0 4px; line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.point .badges {
  display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin: 6px 0 0;
}
/* Название и чипы стоят плотно, как на макете: сначала имя, сразу под ним
   ряд «тип · цена · сколько там пробыть». */
.point h3 { margin: 0; }
/* Чипы как на эталоне: обычный регистр, плотный фон. Капс с разрядкой
   делал из «Пам'ятник · Безкоштовно · 15 хв» три вывески. Заглавными
   осталось только «безкоштовно» — главное слово чипа. */
.point .badge {
  font-size: 11.5px; font-weight: 600; letter-spacing: 0; text-transform: none;
  background: var(--surface-2); color: var(--fg); border-radius: 9px; padding: 4px 9px;
}
.point .badge.free {
  background: var(--accent); color: var(--accent-fg);
  text-transform: uppercase; letter-spacing: .04em; font-weight: 700;
}
/* Что это за место. Стоит первым в ряду бейджей: карточка «Залізний кінь»
   без него не говорила, памятник это, кафе или набережная. */
.point .badge.kind { color: var(--fg); }
/* Снимок места — крупнее, чем был. Владелец продукта: «вижу, что можно
   добавить фотки чуть крупнее, если поработать качественно с местом,
   оптимизируя его». Место нашлось за счёт колонки таймлайна: 46 пикселей
   под время и кружок сузились до 38, а карточка получила их себе.
   Держим квадрат: снимки приходят и вертикальные, и горизонтальные,
   и общая сетка ломалась бы от каждого. */
.point img {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  display: block; border-radius: 0; background: var(--surface-2);
}
/* Лицензия снимка — под значком, а не строкой. §10 требует показать её
   «по тапу», и владелец продукта прямо попросил убрать «CC BY 3.0 · Norbert
   Aepli, Switzerland (User:Noebu)» из карточки: имя чужого фотографа занимало
   больше места, чем описание самого места. */
/* Знак лицензии стоит в ряду бейджей, а не под ними: отдельной строкой
   он занимал место наравне с содержательными пометками. */
/* Поле с кнопкой в одну строку: имя и «Зберегти» рядом.

   Кнопке нужен свой размер: `button.primary` в этом проекте — главная
   кнопка экрана, она блочная и во всю ширину. В ряду с полем такая
   раздавливала ввод имени до квадратика (05.09.2026). */
/* Строка «аватар — имя — сохранить». По центру, а не враспор: у поля,
   кнопки и кружка разная высота, и враспор они вставали на трёх разных
   уровнях. Владелец продукта: «окошко авы выровни, оно криво врезано». */
.field-row { display: flex; gap: 8px; align-items: center; }
/* Аватар: кружок ростом с поле имени. Пустой — силуэт, чтобы было видно,
   что сюда можно нажать. `align-self` обязателен: строка растягивает
   ячейки по высоте, и кружок выходил овалом, вжатым в верх строки. */
.avatar-pick {
  flex: none; align-self: center;
  width: 48px; height: 48px; min-width: 48px; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface-2); color: var(--muted);
  display: grid; place-items: center; overflow: hidden; cursor: pointer;
  transition: transform .12s ease, opacity .15s ease;
}
.avatar-pick:active { transform: scale(.94); }
.avatar-pick.busy { opacity: .5; pointer-events: none; }
.avatar-pick svg { width: 26px; height: 26px; }
.avatar-pick img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Квадратная кнопка со значком: подпись словом забирала треть строки. */
.icon-btn {
  flex: none; width: 48px; height: 48px; min-height: 48px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.icon-btn svg { width: 20px; height: 20px; }
/* Верхний отступ поле получает от общего правила `.select` — под подписью
   он нужен, а в строке сдвигал имя на три точки ниже кружка и кнопки. */
.field-row .select { flex: 1 1 auto; min-width: 0; height: 48px; margin: 0; }
.field-row button.primary {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: auto; min-height: 48px; padding: 0 18px;
  font-size: 15px; line-height: 1;
}
.field-row button:disabled { opacity: .45; cursor: default; }

.credit-mark {
  width: 20px; height: 20px; padding: 0; align-self: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface-2); color: var(--muted);
  font: inherit; font-size: 11px; line-height: 18px; cursor: pointer;
}
.credit-mark:active { transform: scale(.92); }
/* Ряд действий карточки: главное словами, остальное значками.

   Четыре полноразмерные кнопки переносились на три строки, и в маршруте
   из пяти точек подписи занимали пол-экрана. Владелец продукта: «кнопки
   на маршрутах большие и размазаны, что занимает много места на карточке».
   Теперь ряд помещается в одну строку: «як дістатись» тянется, значки
   стоят справа фиксированной ширины. */
/* Нижняя строка карточки: слева время до следующей точки, справа значки.
   На макете они стоят одной строкой, а не двумя. */
.point .actions {
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: 6px; flex-wrap: nowrap;
  margin-top: 6px; padding: 0 12px 11px 32px;
}
/* Стрелка «подробнее» у правого края, как на эталоне. */
.point .actions .point-open { margin-left: auto; }
.point .card.with-photo .actions a,
.point .card.with-photo .actions button {
  background: rgba(16, 20, 26, .62); border-color: transparent; color: #fff;
}
.point .actions a, .point .actions button {
  min-height: 34px; padding: 0 12px; border-radius: var(--pill);
  border: 1px solid var(--line); background: var(--surface-2); color: var(--fg);
  font: inherit; font-size: 13.5px; font-weight: 600; line-height: 32px;
  text-decoration: none; cursor: pointer;
  transition: transform .12s ease, background .12s ease, color .12s ease;
}
.point .actions a:active, .point .actions button:active { transform: scale(.97); }
/* Значки одинаковой круглой формы: ряд читается как один набор. */
.point .actions .icon-act, .point .actions .icon-btn.shoot {
  width: 36px; height: 36px; min-height: 36px; padding: 0; line-height: 0;
  border-radius: 50%; display: inline-flex; align-items: center;
  justify-content: center; color: var(--muted);
}
.point .actions .icon-act:hover, .point .actions .icon-btn.shoot:hover {
  color: var(--fg); background: var(--surface-3);
}
/* Значки — отдельной группой у правого края: слова не отрываются друг от
   друга, когда у события появляются билеты. */
.point .actions .icon-row {
  display: inline-flex; align-items: center; gap: 5px; margin-left: auto;
}
/* Удаление краснеет под пальцем: соседние значки безобидны, а этот убирает
   точку из маршрута. */
.point .actions .icon-act.danger:hover,
.point .actions .icon-act.danger:active { color: var(--danger); border-color: var(--danger); }
/* Кнопка навигации — сплошным акцентом, без градиента: градиент в списке
   из восьми карточек читается как шум, а не как выделение. */
.point .actions a.nav {
  background: var(--accent); color: var(--accent-fg);
  border-color: transparent; font-weight: 700;
}

/* Отступ слева обязателен: метка стоит вплотную к названию внутри
   заголовка, и «Жовтневий Палац» с меткой читались как «Палацподія». */
.tag { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 20px;
       font-size: 11px; border: 1px solid currentColor; margin-left: 6px; vertical-align: 1px; }
.tag.free { color: var(--ok); }
.tag.ad { color: var(--ad); }
.tag.event { color: var(--accent); }

.leg {
  display: flex; align-items: center; gap: 7px;
  padding: 0 0 11px 58px; color: var(--muted); font-size: 12px;
}
.leg a { color: var(--accent); text-decoration: none; border: 1px solid var(--line);
         border-radius: 8px; padding: 4px 10px; }

.feedback { margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--line); }
.feedback p { margin: 0 0 10px; font-size: 15px; }
.attribution { margin: 20px 0 0; font-size: 11px; color: var(--muted); text-align: center; }

/* Полосы прокрутки прячем: внутри «телефона» их не бывает, а на десктопе
   они ломали ощущение реального экрана. Прокрутка при этом работает. */
#app ::-webkit-scrollbar { width: 0; height: 0; }
#app * { scrollbar-width: none; }

/* ------------------------ афіша, картка, установка --------------------- */

.linkish {
  display: block; width: 100%; margin-top: 10px; padding: 8px;
  background: none; border: none; color: var(--accent);
  font: inherit; font-size: 14px; cursor: pointer;
}
.row-bar { display: flex; align-items: center; gap: 12px; }
.row-bar h1 { font-size: 20px; }

/* Разделы афиши. Сетка равных колонок, а не свободный перенос: при переносе
   разделы вставали рваной лесенкой — четыре в первой строке и одинокие
   «Музеї» во второй. Прокрутку вбок пробовали раньше: «Сцена» уезжала
   за край, а мышью на сайте такую полосу не подвинуть вовсе. */
/* Колонок ровно столько, сколько вкладок. Жёсткие три делили полосу
   натрое и при двух вкладках оставляли треть пустой: «Найкращі маршрути»
   вылезали за свою колонку, и подчёркивания стояли разной ширины.
   Владелец продукта: «выровняй строки „Мои маршруты“ и „Популярные
   маршруты“, они не совпадают по ширине вкладок». */
.tabs {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: 2px 4px;
  margin: 0 0 6px; padding: 0 0 2px;
  border-bottom: 1px solid var(--line);
}
.tabs button {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); font: inherit; font-size: 12px; font-weight: 600;
  letter-spacing: -.2px;
  padding: 8px 2px 6px; cursor: pointer; white-space: nowrap;
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.tabs button.on { color: var(--fg); border-bottom-color: var(--accent); }
.tabs .count {
  display: inline-block; padding: 1px 5px;
  border-radius: 9px; background: var(--surface-2); color: var(--muted);
  font-size: 11px; font-weight: 600;
}
.tabs button.on .count { background: var(--accent); color: var(--accent-fg); }

/* Кнопки в карточке афиши: билет и «у маршрут». Раньше карточка была просто
   ссылкой — человек видел концерт и не мог ничего с ним сделать, кроме как
   уйти из приложения. */
.event-actions { display: flex; gap: 8px; margin-top: 10px; }
.event-actions a, .event-actions button {
  /* Равные половины, а не по содержимому: «Билеты» короче, чем «В маршрут»,
     и кнопки выходили разной ширины.

     Текст центрируется флексом, а не подобранной высотой строки: «Билеты» —
     ссылка, «В маршрут» — кнопка, и у браузера для них разные умолчания.
     На высоте строки надпись в ссылке съезжала вниз относительно соседней. */
  display: inline-flex; align-items: center; justify-content: center;
  flex: 1 1 0; min-width: 0; min-height: 38px; line-height: 1.2; text-align: center;
  border-radius: 10px; border: 1px solid var(--line); background: var(--surface-2);
  color: var(--fg); font: inherit; font-size: 14px; font-weight: 600;
  text-decoration: none; cursor: pointer; padding: 0 12px;
}
.event-actions .ticket {
  background: var(--accent); color: var(--accent-fg); border-color: transparent;
}

/* Плашка о событии, выбранном на экране афиши: в списке шага его может
   не быть — окно прогулки его не захватывает, — но выбор сделан. */
/* Закреплённое место — маленьким чипом под строкой шага: булавка,
   название, крестик. По ширине содержимого, а не во всю строку: место
   в маршруте — это пометка, а не поле ввода. */
/* В нижней панели свои отступы: она уже с полями, лишние края не нужны. */
.pinned-row:not(:empty) { margin-bottom: 8px; }
/* Ряд чипов: мест бывает несколько, и на узком экране они переносятся
   на вторую строку, а не выдавливают друг друга. */
.picked-note {
  display: flex; flex-wrap: wrap; gap: 6px;
  max-width: 100%; margin: 0 0 2px;
}
.picked-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 5px 5px 5px 10px;
  border: 1px solid var(--accent); border-radius: 999px;
  background: var(--accent-soft); color: var(--fg);
  font-size: 13px; line-height: 1.2;
}
.picked-pin { width: 15px; height: 15px; flex: 0 0 auto; color: var(--accent); }
.picked-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-weight: 600;
}
.picked-drop {
  flex: 0 0 auto; width: 22px; height: 22px; padding: 0;
  display: grid; place-items: center; border: 0; border-radius: 50%;
  background: rgba(255, 255, 255, .12); color: var(--fg); cursor: pointer;
}
.picked-drop svg { width: 12px; height: 12px; display: block; }
.picked-drop:active { transform: scale(.92); }

/* Строка поиска над афишей: список из двадцати шести событий глазами
   не перебирается, а название человек уже знает. */
/* Карта всех мест: витрина каталога. Карта занимает экран целиком, шапка
   и фильтры лежат поверх — иначе на телефоне карте остаётся полоска. */
.explore-map { position: absolute; inset: 0; }
/* Кнопки в строку, счётчик под ними: на телефоне «Показано місць: 240»
   между «Назад» и «Фільтри» не помещалось и наезжало на обе кнопки. */
.explore-bar {
  position: absolute; left: 0; right: 0; top: 0; z-index: 5;
  display: grid; grid-template-columns: auto 1fr auto;
  grid-template-areas: 'back search filters' 'count count count' 'go go go' 'add add add';
  align-items: center; gap: 8px;
  justify-items: start;
  padding: calc(var(--safe-top) + 10px) 16px 12px;
  /* Без затемняющей полосы сверху. Владелец продукта: «в верхней части экрана
     убери затемнение плавное, оно мешает» — оно съедало треть карты, а нужно
     было только ради читаемости счётчика. У кнопок фон свой, счётчику дали
     собственную плашку: так карта видна целиком. */
  pointer-events: none;
}
/* Кнопки на карте: непрозрачные. Класс `ghost` рисует их без фона —
   на тёмном экране это уместно, а поверх светлой карты от кнопки остаётся
   только рамка, и надписи не видно. Раньше их вытягивала затемняющая полоса
   сверху; полосу убрали по просьбе владельца — значит фон нужен самим
   кнопкам. Владелец продукта: «сверху рамки багнутые и нет кнопки вернуться
   назад» — кнопка была, но прочитать её было нельзя. */
.explore-bar button {
  pointer-events: auto;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  color: var(--fg); border-color: transparent;
  /* «Более читаемые кнопки с мягкими тенями и прозрачностью» — подпись
     к эталону. Под кнопкой стекло: карта просвечивает, а надпись читается. */
  box-shadow: 0 2px 6px rgba(0, 0, 0, .28), 0 8px 20px rgba(0, 0, 0, .3);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: transform .12s ease, background .15s ease;
}
.explore-bar button:active { transform: scale(.96); }
#explore-back { grid-area: back; }
#explore-search-toggle { grid-area: search; justify-self: stretch; }
#explore-filters-toggle { grid-area: filters; }
#explore-add { grid-area: add; }
/* Переход к анкете: зелёная пилюля со счётчиком отобранных мест. Она
   заметнее прочих кнопок панели — это следующий шаг человека, а не одна
   из равных возможностей. */
/* Пилюля на два действия. Отступ у крестика был привязан к ширине текста
   и в русском наехал на кнопку — теперь его место определяет сама вёрстка. */
.explore-go-wrap {
  grid-area: go; justify-self: start;
  display: inline-flex; align-items: stretch;
  border-radius: var(--pill); background: var(--accent);
  overflow: hidden;
}
#explore-go { border-radius: 0; }
/* Тонкая черта отделяет крестик от кнопки: без неё нажатие «мимо» стоило бы
   человеку всей корзины. */
#explore-clear {
  display: grid; place-items: center; width: 38px;
  border: 0; border-left: 1px solid rgba(0, 0, 0, .18);
  background: none; color: var(--accent-fg); cursor: pointer;
}
#explore-clear:hover, #explore-clear:focus-visible { background: rgba(0, 0, 0, .12); }
#explore-clear:active { background: rgba(0, 0, 0, .2); }
.explore-bar button.explore-go {
  background: var(--accent); color: var(--accent-fg); font-weight: 700;
  gap: 8px;
}
.explore-go .picked-count {
  display: inline-grid; place-items: center; min-width: 20px; height: 20px;
  padding: 0 5px; border-radius: 999px; font-style: normal; font-size: 12px;
  background: rgba(0, 0, 0, .22); color: inherit;
}
/* Кнопки панели — пилюли с тенью, как на эталоне: значок и подпись рядом. */
.explore-bar button {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 38px; padding: 0 14px; border-radius: var(--pill);
}
.explore-bar button svg { flex: none; opacity: .9; }
/* Зелёная точка у «Фільтрів»: список мест сужен. */
.explore-bar .dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--surface) 80%, transparent);
}
.explore-bar .plus { color: var(--accent); font-style: normal; font-size: 17px; }
/* Открытый поиск подсвечен: видно, что список сужен строкой. */
#explore-search-toggle.on {
  background: var(--accent); color: var(--accent-fg);
}
.explore-count {
  grid-area: count; justify-self: center; min-width: 0;
  padding: 4px 12px; border-radius: var(--pill);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  font-size: 13px; color: var(--fg); text-align: center;
}
/* Снимки, присланные людьми: полоса с горизонтальной прокруткой. У кафе
   это единственный способ увидеть зал и меню — в свободных источниках
   таких фотографий нет вовсе. */
.photo-strip {
  display: flex; gap: 8px; margin: 10px 0 0;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.photo-strip img {
  width: 108px; height: 108px; flex: none;
  border-radius: var(--radius-sm); object-fit: cover;
}

/* Карточка места поверх карты: полоска снизу, а не отдельный экран.
   Владелец продукта: «нужен всплывающий чип с информацией, не закрывая
   полностью карту» — иначе теряется место, на которое смотришь. */
/* Карточка места над картой: сведения сверху, действия одной строкой снизу.
   Раньше кнопки шли столбиком внутри текста и занимали больше места, чем
   само заведение, — владелец продукта назвал это «размазано». */
/* Полоска места узкая и лежит над кнопками карты: снимок, название,
   «Кафе · 4.8 ★» и стрелка в карточку. Прежняя карточка занимала до
   половины экрана и закрывала как раз то место, на которое смотришь. */
/* Карточка не доходит до правого края: там колонка кнопок карты —
   масштаб и «моє місце». Владелец продукта: «плашка объекта закрывает
   боковое управление… возможно, лучше кнопки не поднимать, а сузить чип».
   Шестьдесят восемь точек — сама кнопка (44) и отступы по краям. */
.explore-chip {
  position: absolute; left: 12px; right: 68px;
  bottom: calc(var(--safe-bottom) + 74px); z-index: 7;
  display: grid; gap: 8px;
  padding: 8px 10px; max-height: 42vh; overflow-y: auto;
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  border: 1px solid var(--line);
  border-radius: 18px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .3), 0 16px 34px rgba(0, 0, 0, .5);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  animation: chip-up .18s ease-out;
}
/* Полоска не выпрыгивает, а поднимается: резкое появление читается
   как подмена содержимого экрана. */
@keyframes chip-up {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
.chip-head { display: flex; gap: 10px; align-items: center; padding-right: 26px; }
.chip-photo {
  width: 46px; height: 46px; flex: none;
  border-radius: 12px; object-fit: cover;
}
.chip-go { flex: none; color: var(--muted); display: grid; place-items: center; }
.chip-rating { color: var(--warn); font-weight: 600; }
.chip-body { display: grid; gap: 3px; min-width: 0; flex: 1 1 auto; }
.chip-body b {
  font-size: 15px; font-weight: 700; line-height: 1.2;
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chip-meta {
  font-size: 12.5px; color: var(--muted); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Одна надпись и значки рядом: «Подробиці» тянется, съёмка и правка —
   фиксированные квадраты, так строка не разъезжается на узком экране. */
/* Снимок и название в чипе места нажимаются — открывают карточку. */
.chip-head.tappable { cursor: pointer; }
.chip-head.tappable:active { opacity: .8; }

/* Ряд действий сжался до «У маршрут» и значков: остальное — в карточке. */
.chip-actions { display: flex; gap: 8px; align-items: center; }
.chip-actions:empty { display: none; }
.chip-main {
  flex: 1 1 auto; min-height: 40px; border: none; border-radius: var(--pill);
  background: var(--surface-2); color: var(--fg);
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
}
/* «У маршрут» в чипе места — акцентом: это главное, зачем открывали карту. */
.chip-pin {
  flex: 1 1 auto; min-height: 40px; border: 1px solid var(--accent);
  border-radius: var(--pill); background: transparent; color: var(--accent);
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
}
.chip-pin:active { transform: scale(.97); }
.chip-actions .icon-btn {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--fg); border: 1px solid var(--line);
}
.chip-close {
  position: absolute; top: 6px; right: 8px;
  width: 28px; height: 28px; border: none; border-radius: 50%;
  background: transparent; color: var(--muted); font-size: 20px; cursor: pointer;
}
.chip-loading { margin: 0; color: var(--muted); font-size: 14px; }

/* Кнопки карты: масштаб одной пилюлей, «моє місце» кругом под ней —
   как на эталоне. Стандартные кнопки MapLibre выглядели чужими: белый
   квадрат с тонкой рамкой поверх тёмного интерфейса. */
.map-tools {
  position: absolute; z-index: 8; right: 12px;
  bottom: calc(var(--safe-bottom) + 74px);
  display: grid; gap: 10px; justify-items: center;
}
/* Кнопки карты с глубиной и откликом на нажатие — так они описаны
   на эталоне («современный стиль кнопок с глубиной и анимацией
   при нажатии»). Тень двойная: близкая даёт край, дальняя — объём. */
.map-zoom {
  display: grid; border-radius: 22px; overflow: hidden;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .3), 0 10px 24px rgba(0, 0, 0, .4);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.map-zoom button {
  width: 44px; height: 44px; border: 0; background: none; color: var(--fg);
  font: inherit; font-size: 21px; line-height: 1; cursor: pointer;
  transition: background .15s ease, transform .12s ease;
}
.map-zoom button:active { background: var(--surface-2); transform: scale(.9); }
.map-zoom-line { height: 1px; background: var(--line); margin: 0 10px; }
.map-here {
  width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; color: var(--fg);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .3), 0 10px 24px rgba(0, 0, 0, .4);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: transform .12s ease, color .15s ease;
}
.map-here:active { transform: scale(.94); }
.map-here.on { color: var(--accent); }
/* Стандартные кнопки MapLibre на этом экране больше не нужны. */
#explore-map .maplibregl-ctrl-bottom-right { display: none; }

/* Поиск на карте: свёрнут в кнопку, разворачивается полосой под шапкой. */
.explore-search {
  position: absolute; z-index: 6; left: 12px; right: 12px;
  top: calc(var(--safe-top) + 76px); margin: 0;
}

/* Панель фильтров — карточка по содержимому, а не полоса во всю высоту:
   на эталоне она висит под кнопкой «Фільтри» и кончается там же, где
   список. Длинный список прокручивается внутри, но не до самого низа
   экрана — карта остаётся видна. */
.explore-filters {
  position: absolute; z-index: 6; right: 12px;
  top: calc(var(--safe-top) + 56px);
  width: min(248px, 66%); max-height: min(62vh, 520px);
  overflow-y: auto; padding: 14px;
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  border: 1px solid var(--line);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .3), 0 16px 36px rgba(0, 0, 0, .5);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.explore-filters-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px; font-size: 15px;
}
.explore-filters-head b { font-weight: 700; }
/* Панель фильтров на эталоне — карточка с мягкой тенью, а не полоса. */
.explore-filters { border-radius: 18px; box-shadow: 0 12px 30px rgba(0, 0, 0, .5); }
/* Строка фильтра: галочка, значок группы, название, число мест справа —
   как на эталоне карты. */
.explore-filter {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0; font-size: 14px; cursor: pointer;
}
.explore-filter span {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.explore-filter b { color: var(--muted); font-weight: 600; font-size: 12px; }
.explore-filter input {
  width: 20px; height: 20px; flex: none; accent-color: var(--accent);
}
.explore-filter-icon {
  width: 22px; height: 22px; flex: none;
  display: grid; place-items: center; color: var(--muted);
}
.explore-filter-icon svg { width: 18px; height: 18px; }
.explore-filter-icon.group-food { color: #ffb277; }
.explore-filter-icon.group-culture { color: #8cc0ff; }
.explore-filter-icon.group-activity { color: #facc15; }
.explore-filter-icon.group-outdoor { color: #4ade80; }

/* Поиск и разделы стоят вне прокручиваемого списка, а горизонтальные отступы
   есть только у него: без своих полей строка поиска уходила за края экрана. */
.events-search { margin: 0; padding: 0 20px 10px; }
/* Экран — колонка на всю высоту, и всё, что не список, сжималось: ленты
   разделов и дней схлопывались до десяти точек, а содержимое вылезало
   за них. Шапке, поиску и лентам запрещаем сжиматься, прокручивается
   только тело афиши. */
.events-head, .events-search, .events-kinds, .events-days { flex: none; }
#events-body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: calc(var(--safe-bottom) + 78px);
}
/* Шапка афиши: заголовок слева, лупа справа. */
.row-bar { display: flex; align-items: center; gap: 10px; }
.row-bar h1 { flex: 1 1 auto; }
#events-search-toggle {
  flex: none; width: 40px; height: 40px; display: inline-flex;
  align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--fg); cursor: pointer;
}

/* Адрес точки: улица и дом под названием. У сети из шести кинотеатров
   название одно на всех, и без адреса непонятно, в какой филиал идти. */
.addr { margin: 2px 0 0; font-size: 13px; color: var(--muted); }

.event {
  display: block; padding: 14px 0; border-top: 1px solid var(--line);
  color: inherit; text-decoration: none;
}
.event .when { font-size: 13px; color: var(--accent); font-weight: 600; }
.event h3 { margin: 3px 0 4px; font-size: 16px; font-weight: 600; }
.event .meta { margin: 0; font-size: 13px; color: var(--muted); }

/* Список афиши: карточка со снимком слева. Строки без картинок владелец
   продукта назвал перегруженными и бедными — при том что постер у сеанса
   есть и на витрине выше показан. */
/* Заголовки блоков в афише живут в контейнере без боковых отступов —
   на главной их даёт экран. Без выравнивания «Підбірки для вас» и «Усі
   події» стояли вплотную к краю, а карточки под ними — с отступом. */
#events-body .home-block-head { padding: 0 16px; }
/* Лента начинается там же, где заголовок: отрицательный отступ главной
   тут возвращал карточку вплотную к краю экрана. */
#events-body .home-rail { margin: 10px 0 0; padding: 0 16px 4px; }

/* Выбор города: свой список вместо системного. Тот открывался заново
   сразу после закрытия и выйти из него было нельзя. */
.picker {
  position: fixed; inset: 0; z-index: 60; display: flex;
  align-items: flex-end; justify-content: center;
  background: rgba(0, 0, 0, .55);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
.picker-card {
  width: 100%; max-width: 460px; max-height: 72vh;
  display: flex; flex-direction: column;
  padding: 16px 16px calc(12px + var(--safe-bottom));
  background: var(--surface); color: var(--fg);
  border-radius: var(--radius) var(--radius) 0 0;
  animation: finish-rise .28s cubic-bezier(.2, .9, .3, 1);
}
@media (min-width: 560px) {
  .picker { align-items: center; }
  .picker-card { border-radius: var(--radius); }
}
.picker-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex: none; margin-bottom: 10px;
}
.picker-head h2 { margin: 0; font-size: 18px; letter-spacing: -0.01em; }
.picker-head .ghost { width: auto; margin: 0; flex: none; }
.picker-search { margin: 0 0 8px; flex: none; }
.picker-list { flex: 1 1 auto; overflow-y: auto; display: grid; gap: 4px; }
.picker-item {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; min-height: 48px; padding: 10px 12px;
  border: 0; border-radius: var(--radius-sm); background: none;
  color: var(--fg); font: inherit; font-size: 15px; text-align: left;
  cursor: pointer;
}
.picker-item:active { background: var(--surface-2); }
.picker-item.on { background: var(--accent-soft); color: var(--accent); font-weight: 700; }

.events-list { display: grid; gap: 12px; padding: 0 16px; }
/* Подпись дня в сплошном списке: двести карточек подряд без неё читаются
   как один бесконечный день. */
.events-date {
  margin: 10px 0 0; font-size: 13px; font-weight: 700;
  color: var(--accent); text-transform: none;
}
.events-date:first-child { margin-top: 0; }
.event-row {
  display: grid; grid-template-columns: 104px 1fr; gap: 12px;
  padding: 10px; border-radius: 18px;
  background: var(--surface); border: 1px solid var(--line);
  cursor: pointer;
}
.event-row-shot {
  position: relative; border-radius: 12px; overflow: hidden;
  background: var(--surface-2); min-height: 128px;
}
.event-row-shot img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* Метка раздела поверх постера: плашка обязательна — на светлом кадре
   белая надпись без неё пропадает. Цвет задаёт разметка, по разделу. */
.event-row-shot .kind-badge {
  position: absolute; left: 6px; bottom: 6px; top: auto; z-index: 2;
  padding: 3px 7px; border-radius: var(--pill);
  background: rgba(8, 12, 17, .82);
  font-size: 10.5px; font-weight: 700; color: #fff;
}
.event-row-body { display: grid; align-content: start; gap: 4px; min-width: 0; }
.event-row-body .when { font-size: 12.5px; color: var(--accent); font-weight: 700; }
.event-row-body b {
  font-size: 15px; font-weight: 700; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.event-row-body .meta { margin: 0; font-size: 12.5px; color: var(--muted); }
.event-row .event-actions { margin-top: 6px; gap: 8px; }
.event-row .event-actions .ticket,
.event-row .event-actions button {
  padding: 7px 12px; font-size: 12.5px; border-radius: var(--pill);
}

/* Карточка точки поверх маршрута: данные, которые не влезли в список */
.detail { position: absolute; inset: 0; z-index: 30; background: var(--bg); overflow-y: auto; }
.detail-inner { padding: 0 16px calc(var(--safe-bottom) + 24px); }
/* Снимок места: высокий кадр слегка подрезаем, остальные показываем целиком.

   Сначала здесь была лента в 240 пикселей с обрезкой по центру, и у кафе
   пропадала вывеска с названием — то, ради чего снимок и делали. Потом кадр
   показывался весь, и вертикальный занимал экран почти целиком. Владелец
   продукта: «если бы отрезало по 10-15 % сверху и снизу, было бы нормально
   или мне фотать горизонтально лучше?» — снимать он должен как удобно,
   подстраиваться тут интерфейсу.

   Потолок высоты привязан к ширине карточки. Полторы ширины оказалось много:
   вертикальный кадр занимал семь десятых экрана и вытеснял вниз название
   и часы работы. Владелец продукта: «фотки в чип отлично попали, ровно так
   как нужно, но когда нажимаешь подробнее, там полная вертикальная фотка,
   которая уже не так хорошо смотрится». В чипе снимок квадратный — потому
   и смотрится; здесь берём чуть вытянутый баннер той же природы.

   Обрезка смещена к верху кадра (`object-position`): у заведения важное —
   вывеска и фасад, они в верхней трети, а нижняя треть обычно тротуар.
   По центру срезало бы как раз вывеску, ради которой снимок и делали.

   Горизонтальный и квадратный кадры ниже потолка и остаются целыми.
   Второй потолок, `55vh`, — для широкого экрана: там ширина ничего
   не ограничивает.

   Селектор именной: общий `.detail img` перебивал и миниатюры в полосе
   снимков, потому что стоял в файле ниже. */
.detail .detail-photo {
  display: block; width: calc(100% + 32px); height: auto;
  max-height: min(105vw, 55vh); object-fit: cover; object-position: center 28%;
  background: var(--surface-2);
  border-radius: 0 0 14px 14px; margin: 0 -16px 14px;
}
.detail h2 { margin: 0 0 4px; font-size: 22px; letter-spacing: -0.02em; }
.detail .kicker { color: var(--muted); font-size: 13px; margin: 0 0 14px; }
.detail section { padding: 14px 0; border-top: 1px solid var(--line); }
.detail section h4 { margin: 0 0 8px; font-size: 13px; color: var(--muted); font-weight: 600; }
.detail .anchor { display: flex; justify-content: space-between; padding: 5px 0; font-size: 15px; }
/* Лицензия снимка — подпись под ним, а не абзац наравне с описанием (§10:
   показать обязаны, но она не текст о месте). */
.detail .credit-mark { margin: -6px 0 12px; }
/* Из трёх ссылок карточки главная одна — «как добраться». Остальные равны
   между собой, и если все три одинаковые, выбирать не из чего. */
.detail .actions a.nav { background: var(--accent); color: var(--accent-fg); }
.detail .close {
  position: sticky; top: 0; z-index: 2; display: block; margin: 0 0 0 auto;
  padding: calc(var(--safe-top) + 10px) 4px 10px;
  background: none; border: none; color: var(--fg); font: inherit; font-size: 15px; cursor: pointer;
}

/* Плашки живут в потоке над кнопкой, а не поверх неё: как оверлей они
   перекрывали «Скласти маршрут», и до маршрута было не добраться. */
.install-bar {
  margin: 0 20px 12px; padding: 12px 14px; border-radius: 14px;
  display: flex; flex-direction: column; gap: 10px;
  background: var(--surface-2); border: 1px solid var(--line); font-size: 14px;
}
.install-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.install-actions button { flex: 1 1 auto; }
.install-bar button {
  min-height: 34px; padding: 0 12px; border-radius: 9px; border: none;
  background: var(--accent); color: var(--accent-fg); font: inherit; font-weight: 600; cursor: pointer;
}
.install-bar button.ghost { background: none; border: 1px solid var(--line); color: var(--fg); font-weight: 400; }

/* Карточка последнего маршрута на первом экране.

   Раньше здесь была полоска «Як пройшов маршрут? Чек співпав?» с четырьмя
   кнопками в ряд — владелец продукта назвал её огрызком: вопрос без
   контекста, о каком маршруте речь, не видно. Теперь это карточка со схемой
   пути, и она же спрашивает об оценке, если человек не оценил сам. */
/* Крестик поверх карточки: убрать её с глаз на время анкеты. */
.last-plan { position: relative; }
.last-plan-close {
  position: absolute; top: 6px; right: 8px; z-index: 1;
  width: 26px; height: 26px; padding: 0; border: none; border-radius: 50%;
  background: var(--surface-2); color: var(--muted);
  font: inherit; font-size: 17px; line-height: 24px; cursor: pointer;
}
.last-plan-close:hover { color: var(--fg); background: var(--surface-3); }
.last-plan {
  /* Экран — колонка флексбокса, и карточка сжималась при нехватке места,
     а `overflow: hidden` резал ей низ: строка «Як пройшло?» со звёздами
     оказывалась наполовину за краем. Сжиматься карточке нечем — у неё
     схема фиксированного размера и две строки текста. */
  flex: none;
  margin: 0 20px 12px;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.last-plan-open {
  display: flex; gap: 12px; align-items: center; width: 100%;
  padding: 12px; border: none; background: none; color: var(--fg);
  font: inherit; text-align: left; cursor: pointer;
  transition: background .12s ease;
}
.last-plan-open:hover { background: var(--surface-2); }
.last-plan-open:active { transform: scale(.995); }
/* Схема пути: квадрат со своим фоном, чтобы линия читалась и на тёмной,
   и на светлой теме. Не сжимается — иначе длинное название маршрута
   сплющило бы картинку в полоску. */
.last-plan-map {
  flex: none; width: 84px; height: 84px; border-radius: var(--radius-sm);
  background: var(--surface-2); overflow: hidden;
}
.last-plan-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.last-plan-head { display: flex; align-items: baseline; gap: 8px; }
.last-plan-head b { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }
/* Названия точек — одной строкой: их бывает восемь, и полный список
   вытеснил бы всё остальное. */
.last-plan-text .hint {
  font-size: 13px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.last-plan-nums { font-size: 12.5px; color: var(--accent); font-weight: 600; }
/* Строка вопроса отделена линией: это уже не про сам маршрут. */
.last-plan-ask {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; border-top: 1px solid var(--line);
  font-size: 14px; color: var(--muted);
}
/* Звёзды здесь заметнее, чем в истории: там их ищут глазами сами, а тут
   это вопрос, на который ждут ответа. Цвет --line сливался с карточкой
   почти полностью — строка выглядела пустой полосой. */
.last-plan-ask .stars { display: inline-flex; gap: 2px; margin: 0; }
.last-plan-ask .star { color: var(--rail-dot); font-size: 26px; padding: 0 3px; }
.last-plan-ask .star:hover { color: var(--warn); }
.last-plan-ask .star:active { transform: scale(1.15); }
.last-plan-check-actions { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.last-plan-check-actions button {
  min-height: 32px; padding: 0 12px; border-radius: var(--pill);
  border: 1px solid var(--line); background: var(--surface-2); color: var(--fg);
  font: inherit; font-size: 13px; cursor: pointer;
}
.last-plan-check-actions button:hover { background: var(--surface-3); }

/* ------------------------------ служебное ----------------------------- */

/* Экран сборки маршрута. Раньше это была полупрозрачная накладка: сквозь
   неё просвечивала анкета, и надпись «Обираємо, що варте вечора» ложилась
   поверх кнопок выбора кухни — владелец продукта справедливо назвал это
   небрежным. Сборка — отдельный шаг со своим экраном, а не туман поверх
   предыдущего, тем более что на нём показывается реклама (§9.1). */
.loader {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 18px;
  padding: 32px 24px calc(var(--safe-bottom) + 24px);
  background: var(--bg); z-index: 50; text-align: center;
}
#loader-step {
  margin: 0; max-width: 22ch; font-size: 17px; line-height: 1.35;
  color: var(--fg);
}
.spinner {
  width: 38px; height: 38px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--accent);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Сообщение внизу экрана, а не внутри ближайшего блока с позицией.
   Было `absolute`: на экране маршрута ближайшим оказывался лист с точками,
   и «Закладка снята» всплывало посреди карточки места, наезжая на неё.
   Владелец продукта: «при клике на кнопку пишет непонятные алерты» —
   непонятны они были местом, а не словами. */
.toast {
  position: fixed; left: 16px; right: 16px; bottom: calc(var(--safe-bottom) + 20px);
  max-width: 460px; margin: 0 auto;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 12px; padding: 12px 16px; font-size: 14px; z-index: 90;
  /* Тень и слой выше карточек: сообщение читается как всплывшее поверх,
     а не как строка того блока, на который оно легло. Без неё «Закладка
     снята» выглядела частью карточки места. */
  box-shadow: 0 12px 32px rgba(0, 0, 0, .55);
  text-align: center;
}

.empty { padding: 24px 4px; color: var(--muted); font-size: 15px; }

/* Строка геолокации кликабельна: отказ определиться больше не навсегда. */
#geo-status { cursor: pointer; }
#geo-status.err { text-decoration: underline dotted; text-underline-offset: 3px; }

/* Стрелка выхода с маршрута — над картой, в левом верхнем углу. */
#route-back { position: absolute; z-index: 7; top: calc(var(--safe-top) + 14px); left: 20px; }

/* Атрибуция карты живёт в левом нижнем углу свёрнутой в значок «i»
   (см. app.js, collapseAttribution). Раньше она стояла справа и раскрытой:
   строка лицензии лезла под кнопку «Карта», обрезалась на «OpenStr…»
   и выглядела поломкой. §10 требует, чтобы лицензия была доступна, —
   значок раскрывается нажатием, как на любой карте.

   Развёрнутая занимает не больше трёх четвертей ширины и переносится
   по словам: читаться она обязана целиком. */
.maplibregl-ctrl-attrib {
  max-width: 75%;
}
.maplibregl-ctrl-attrib.maplibregl-compact-show {
  white-space: normal;
  line-height: 1.35;
}
/* Свёрнутая — круглый значок «i». Фон оставляем: без него значок теряется
   на светлой карте, а нажать по нему нужно уверенно. */
.maplibregl-ctrl-attrib.maplibregl-compact:not(.maplibregl-compact-show) {
  padding: 0; border-radius: 12px;
}
/* Плашка атрибуции идёт со своими белыми стилями: на тёмной карте это
   единственное светлое пятно в углу. Красим по теме, читаемость лицензии
   при этом не страдает (§10). */
.maplibregl-ctrl-attrib,
.maplibregl-ctrl-attrib.maplibregl-compact-show {
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  color: var(--muted);
}
.maplibregl-ctrl-attrib a { color: var(--muted); }
.maplibregl-ctrl-attrib-button { filter: invert(var(--attrib-invert, 1)); }

/* Выпадающий список и поле времени в анкете: те же поля, что и остальные,
   но нативные — свои стили браузера иначе спорят с тёмной темой. */
.select {
  width: 100%; margin-top: 6px; padding: 14px 16px;
  background: var(--surface-2); color: var(--fg);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font: inherit; appearance: none;
}
.select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Стрелка у списка: без неё поле выглядит полем ввода, и человек ждёт
   клавиатуру вместо выбора. Рисуем сами — системную мы сняли appearance. */
select.select {
  padding-right: 40px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 20px) calc(50% + 2px), calc(100% - 14px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

/* Повод, несовместимый с числом людей: «свидание» на одного — обещание,
   которого мы не выполним. Гасим, а не прячем: так видно, что вариант есть
   и от чего он зависит. */
.chips button.off { opacity: .35; cursor: not-allowed; }

/* Подсказка об установке на iPhone. Внизу экрана, над кнопкой «Поделиться»
   в Safari: стрелка человека уходит туда же, куда смотрит текст. */
.ios-install {
  position: fixed; left: 12px; right: 12px;
  bottom: calc(12px + var(--safe-bottom));
  z-index: 60; padding: 14px 40px 16px 16px; border-radius: 16px;
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
}
.ios-install-title { margin: 0 0 4px; font-weight: 600; font-size: 15px; }
.ios-install-text { margin: 0; font-size: 13px; line-height: 1.45; color: var(--muted); }
.ios-install-x {
  position: absolute; top: 6px; right: 8px; width: 32px; height: 32px;
  background: none; border: 0; color: var(--muted); font-size: 22px;
  line-height: 1; cursor: pointer;
}

/* Анонимный номер устройства: его переписывают в письмо, поэтому моноширинный
   шрифт и разрешённый перенос — на узком экране uuid не должен уезжать. */
/* Числа — системным моноширинным: на iPhone это SF Mono, на Android
   Roboto Mono, на Windows Consolas. Свой шрифт ради цифр возить незачем —
   это лишние сто килобайт к каждому запуску. */
.num { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
       font-variant-numeric: tabular-nums; }

.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
        font-size: 13px; word-break: break-all; user-select: all; }
/* Ссылки на экране источников: не кнопки, а обычные ссылки в столбик. */
.link { display: block; color: var(--accent); text-decoration: none;
        padding: 6px 0; font-size: 15px; }
.link:active { opacity: .6; }

/* Подсказка про вечер: предложение, а не команда. Появляется только когда
   прогулка действительно вечерняя. */

/* ---------------------------- вид анкеты ----------------------------- */

/* Поля собраны в карточки. Плоский список из восьми групп читался как одна
   длинная простыня: взгляду не за что зацепиться, и человек не понимает,
   сколько ещё осталось. Карточка отделяет вопрос от вопроса. */
.fields .field {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 16px 18px;
  margin-bottom: 12px;
  box-shadow: var(--shadow-1);
}
.fields .field > .label { margin-bottom: 10px; font-size: 12px;
                          text-transform: uppercase; letter-spacing: .06em; }
.fields .field > .hint:last-child { margin-top: 10px; }

/* Заголовок экрана крупнее и с воздухом: это первое, что видит человек. */
.app-bar h1 { font-size: 30px; line-height: 1.15; }
.app-bar .sub { margin-top: 6px; font-size: 15px; }

/* Чипы: выбранный отличается не только цветом, но и весом — на солнце
   разница в оттенке пропадает, разница в толщине букв остаётся. */
.chips button { transition: background .12s ease, color .12s ease; }
.chips.multi button.on::before { content: '✓'; margin-right: 6px; font-weight: 700; }

/* Множественный выбор занимает много места, поэтому чипы там компактнее. */
.chips.multi button { min-height: 42px; font-size: 15px; }

/* Ссылка-кнопка под основной: «Пропустить» на знакомстве и «Что сегодня»
   на анкете. Раньше «Пропустить» показывалась системной кнопкой браузера. */
.bottom-bar .linkish { display: block; width: 100%; margin-top: 10px; text-align: center; }

/* ------------------- выбор точки старта на карте (§3) ------------------- */

.pick-map { position: absolute; inset: 0; }

/* Перекрестие висит по центру экрана и не перехватывает касания: карту
   двигают под ним. Тень нужна, чтобы метка читалась и на светлой площади,
   и на тёмном парке. */
.crosshair {
  position: absolute; left: 50%; top: 50%;
  width: 28px; height: 28px; margin: -34px 0 0 -14px;
  pointer-events: none;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  background: var(--accent);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .45);
}
.crosshair::after {
  content: ''; position: absolute; left: 9px; top: 9px;
  width: 10px; height: 10px; border-radius: 50%; background: var(--bg);
}
#pick-screen .bottom-bar { position: absolute; left: 0; right: 0; bottom: 0; }

/* --------------------------- отделка вида ------------------------------ */

/* Заголовок экрана: крупный кегль и плотный трекинг — так набирают
   заголовки, а не подписи к полям. */
.app-bar h1 { font-size: 32px; font-weight: 700; letter-spacing: -0.03em; }

/* Нижняя панель отделяется тенью, а не линией: линия дробит экран, тень
   показывает, что панель лежит поверх списка. */
.bottom-bar {
  border-top: none;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, .28);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .bottom-bar { box-shadow: 0 -10px 30px rgba(16, 18, 27, .08); }
}
:root[data-theme="light"] .bottom-bar { box-shadow: 0 -10px 30px rgba(16, 18, 27, .08); }

/* Метка «бесплатно» и подобные: заливка вместо обводки. */
.tag { border: none; padding: 2px 10px; background: color-mix(in srgb, currentColor 16%, transparent); }

/* Крупный корпус телефона на десктопе — с тенью и скруглением поглубже. */
@media (min-width: 700px) and (min-height: 600px) {
  #app { border-radius: 28px; box-shadow: 0 30px 80px rgba(0, 0, 0, .5); }
}

/* Чипы вне карточки (экран маршрута) — на ступень светлее фона страницы. */
.screen > .chips button, .bottom-bar .chips button { background: var(--surface); }

/* Шапка с кнопкой «Назад» — вторичный экран: заголовок там подпись,
   а не витрина, и в 32 пункта он вытесняет саму кнопку. */
.app-bar.row-bar h1 { font-size: 22px; letter-spacing: -0.02em; }

/* Список афиши: те же карточки, что и в анкете, — иначе экран выглядит
   черновиком рядом с остальными. */
#events-list .event {
  display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 16px; margin-bottom: 10px;
  box-shadow: var(--shadow-1); text-decoration: none; color: inherit;
}
#events-list .event:active { transform: scale(.99); }
#events-list .when { color: var(--accent); font-size: 13px; font-weight: 600; }
#events-list h3 { margin: 4px 0 4px; font-size: 17px; font-weight: 650;
                  letter-spacing: -0.01em; }
#events-list .meta { margin: 0; color: var(--muted); font-size: 13px; }

/* Две кнопки режима под полем города: «моё место» и «на карте». Ряд узкий,
   поэтому чипы здесь компактнее обычных. */
.origin-modes { margin-top: 10px; }
.origin-modes button { flex: 0 0 auto; min-height: 38px; font-size: 14px; }


/* ───────────────────────────── Налаштування ───────────────────────────── */

/* Подпись настройки внутри карточки: не такая крупная, как заголовок
   раздела, но отделяет один переключатель от другого. */
.setting-name {
  display: block; margin: 14px 0 8px; font-size: 14px; font-weight: 600;
}
.setting-name:first-of-type { margin-top: 6px; }

/* Строка-переход: название слева, шеврон справа. Ведёт на другой экран,
   поэтому выглядит как строка списка, а не как кнопка действия. */
.row-link {
  display: flex; width: 100%; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 6px; padding: 12px 2px;
  background: none; border: none; border-bottom: 1px solid var(--line);
  color: var(--fg); font: inherit; font-size: 15px; text-align: left;
  text-decoration: none; cursor: pointer;
}
.row-link:last-of-type { border-bottom: none; }
.row-link .chev {
  width: 8px; height: 8px; border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted); transform: rotate(-45deg);
  flex: none;
}

/* Переключатель «да/нет». Настоящий checkbox под ним: доступность
   и клавиатура достаются даром, рисуем только вид. */
.switch-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; padding: 10px 0; font-size: 15px; cursor: pointer;
}
.toggle {
  appearance: none; flex: none; width: 46px; height: 28px; margin: 0;
  border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--line); position: relative; cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.toggle::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--muted); transition: transform .15s ease, background .15s ease;
}
.toggle:checked { background: var(--accent-soft); border-color: var(--accent); }
.toggle:checked::after { transform: translateX(18px); background: var(--accent); }
.toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Кнопка на всю ширину карточки: «Стерти збережене» и подобные. */
.ghost.wide { display: block; width: 100%; }

/* «Тут зачинено назавжди?» — жалоба на точку из карточки. Стоит внизу
   и выглядит скромно: это не действие, которого от человека ждут, а способ
   помочь, когда он пришёл к запертой двери. */
.detail-inner .report {
  display: block; width: 100%; margin-top: 14px;
  color: var(--muted); background: none; border: 1px solid var(--line);
}
.detail-inner .report:disabled { opacity: .5; }

/* --------------------------- анкета шагами ---------------------------- */

/* Один вопрос на экран. Восемь полей подряд человек читал как анкету
   в поликлинике: прокрутка, мелкие подписи, непонятно, где конец. */
/* Шапка мастера: слева «назад», справа шестерёнка, между ними полоса
   заполнения. Первая версия оставляла обе кнопки на абсолютных координатах,
   и «назад» ложилась поверх счётчика «2 из 8», а полоса уходила под неё. */
.wizard-bar {
  display: grid;
  grid-template-columns: 40px 1fr 40px;
  /* Кнопки стоят выше полосы, а не на её линии: владелец продукта просил
     поднять шестерёнку, и в один ряд с полосой она смотрелась приклеенной
     к ней сбоку. Счётчик занимает всю нижнюю строку по центру — прижатый
     к левому краю, он читался как случайно оброненная подпись. */
  grid-template-areas: 'back . gear' 'progress progress progress' 'count count count';
  align-items: center;
  column-gap: 12px;
  row-gap: 10px;
  padding: calc(var(--safe-top) + 10px) 20px 8px;
}
.wizard-bar .icon-btn.corner { position: static; }
.wizard-bar #wizard-back { grid-area: back; }
.wizard-bar #settings-open { grid-area: gear; }
.wizard-bar .progress { grid-area: progress; margin: 0; }
/* Список времени отделён от кнопок «зараз / оберу час»: вплотную они
   читались как одна слипшаяся панель. */
.step #start-at-field { margin-top: 14px; }
.wizard-bar .step-count { grid-area: count; margin-top: 0; text-align: center; }
/* Кнопки «назад» нет на первом шаге — колонка остаётся пустой, и полоса
   не прыгает вправо-влево при переходах. */
.wizard-bar #wizard-back[hidden] { display: block; visibility: hidden; }

/* Полоса заполнения вместо счётчика точками: восемь точек в строку мелкие,
   а полоса читается боковым зрением и не требует считать. */
.progress {
  height: 3px; margin: 6px 0 4px; border-radius: 2px;
  background: var(--surface-2); overflow: hidden;
}
.progress-line {
  height: 100%; width: 12%; border-radius: 2px;
  background: var(--accent); transition: width .25s ease;
}
.step-count { margin: 0; font-size: 12px; color: var(--muted); }

.steps {
  flex: 1 1 auto; overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding: 4px 20px 20px;
}
.step { padding: 4px 0; }
.step h2 {
  margin: 10px 0 6px; font-size: 26px; line-height: 1.2; font-weight: 700;
}
/* Пояснение под вопросом: зачем спрашиваем и что изменится в маршруте.
   Без него анкета выглядит допросом, а половина ответов — угадыванием. */
.q-hint {
  margin: 0 0 18px; font-size: 14px; line-height: 1.45; color: var(--muted);
}
.step .field { margin-top: 0; }

/* Крупные варианты: на шаге всего один вопрос, места хватает, а попасть
   пальцем в мелкий чип на ходу трудно. */
.big-chips button {
  min-height: 52px; padding: 0 18px; font-size: 16px;
}
.big-chips.wrap { gap: 10px; }

/* Кнопки шага — сеткой равных колонок, а не рядом по содержимому.
   Владелец продукта о ряде «прогулянка · побачення · друзі · сімʼя»:
   «кривая вёрстка, кнопки выровнять, сделать более равномерные».
   При переносе по содержимому каждая кнопка своей ширины, ряды рваные,
   и выбор читается как список слов, а не как набор равнозначных вариантов. */
.step .chips {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(102px, 1fr));
}
.step .chips button {
  display: flex; align-items: center; justify-content: center;
  width: 100%; padding: 10px 12px;
  /* Длинные подписи («європейська», «кава й десерти») в одну строку
     не помещаются и обрезаются — разрешаем перенос внутри кнопки. */
  white-space: normal; line-height: 1.25; text-align: center;
}

/* Афиша на шаге анкеты: карточки событий тем же строем, что плитки выбора,
   только выбор не обязателен — можно просто пойти дальше. */
.event-picks { display: grid; gap: 10px; }
/* Разделы над списком: владелец продукта просил, чтобы афиша на шаге была
   «изначально вся по категориям», без отсылки на отдельный экран. */
.step .tabs { margin: 0 0 12px; }
.event-pick {
  display: grid; gap: 3px; justify-items: start; text-align: left;
  width: 100%; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--fg);
  font: inherit; cursor: pointer;
}
.event-pick b { font-size: 15px; font-weight: 600; line-height: 1.25; }
.event-pick small { font-size: 12.5px; line-height: 1.35; color: var(--muted); }
.event-pick.on {
  background: var(--accent); border-color: transparent; color: var(--accent-fg);
}
.event-pick.on small { color: var(--accent-fg); opacity: .8; }
.event-pick[disabled] { opacity: .5; cursor: default; }
.event-picks .empty { padding: 10px 2px; }
/* Выход в полную афишу под верхушкой списка: строкой, а не кнопкой —
   это не ответ на вопрос шага, а способ посмотреть остальное. */
.all-events {
  justify-self: center; margin-top: 2px; padding: 8px 12px;
  color: var(--accent); font-size: 14px;
}

/* Плитки с пояснением: там, где от ответа зависит весь маршрут, одного
   слова мало. Строка под названием говорит, что человек получит. */
.step .chips.tiles { grid-template-columns: 1fr; }
.step .chips.tiles button {
  display: grid; gap: 4px; justify-items: start; text-align: left;
  min-height: 0; padding: 14px 16px; border-radius: var(--radius-sm);
  font-weight: 400;
}
/* Название — крупнее и плотнее описания: в плитке два уровня чтения, и без
   разницы в кегле они сливаются в один абзац. Полужирный, а не жирный:
   заголовок шага рядом набран 700, и второй такой же спорит с ним. */
.step .chips.tiles b {
  font-size: 17px; font-weight: 600; line-height: 1.25;
  letter-spacing: -.01em;
}
.step .chips.tiles small {
  font-size: 13.5px; line-height: 1.4; color: var(--muted);
}
/* На выбранной плитке фон зелёный: серая подпись на нём не читается. */
.step .chips.tiles button.on { font-weight: 400; }
.step .chips.tiles button.on small { color: var(--accent-fg); opacity: .8; }

/* Счёт шагов под сводкой маршрута: цифра важная, но не главная — главной
   остаётся цена, и перебивать её кеглем нельзя. */
.walk-stats {
  margin: 6px 0 0; font-size: 13px; line-height: 1.4; color: var(--accent);
}

/* Пометка «пасує вашому часу» на плитке: подсказка, а не отдельный выбор. */
.step .chips.tiles .tile-fit {
  margin-top: 2px; padding: 2px 8px; border-radius: var(--pill);
  background: var(--accent-soft); color: var(--accent);
  font-size: 12px; font-style: normal; font-weight: 600;
}
.step .chips.tiles button.on .tile-fit {
  background: rgba(255, 255, 255, .22); color: var(--accent-fg);
}

/* Галочки столбиком: в строку «с детьми» и «с животным» слипались
   в одну фразу, и было непонятно, где кончается одна и начинается другая. */
.step .switch {
  display: flex; align-items: center; gap: 12px;
  margin-top: 16px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2); font-size: 16px;
}
.step .switch + .switch { margin-top: 10px; }
.step .switch input { width: 24px; height: 24px; flex: none; }

/* Полоса ожидания на экране сборки: заполняется за то же время, что держится
   экран. Без неё человек не знает, ждать ему секунду или десять. */
.loader-bar {
  width: min(220px, 60%); height: 3px; margin: 0 auto;
  border-radius: 2px; background: var(--surface-2); overflow: hidden;
}
.loader-line { height: 100%; width: 0; border-radius: 2px; background: var(--accent); }

/* Место под баннер на экране сборки (§9.1). Пока рекламная сеть
   не подключена, блок пуст и высоты не занимает: обещать место под рекламу
   и показывать пустоту хуже, чем не показывать ничего. */
.loader-ad:not(:empty) {
  width: min(320px, 90%); min-height: 100px; margin-top: 22px;
  border-radius: var(--radius-sm); background: var(--surface-2);
}

/* ─────────── Правка каталога с телефона (§11) ───────────
   Владелец продукта заполняет места сам и с телефона: «нужна полноценная
   админ-панель внутри телефонного приложения». Поля крупные и в один
   столбец — их заполняют стоя у входа в заведение, а не за столом. */
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.check {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 16px; font-size: 15px; color: var(--fg);
}
.check input { width: 22px; height: 22px; accent-color: var(--accent); }
button.danger { color: var(--warn); border-color: currentColor; }
.mod-card {
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; margin-bottom: 16px; background: var(--surface);
}
/* Модерация: кадр целиком. По обрезанной середине не понять, годится ли
   снимок — а решение принимают именно здесь. Вертикальные с телефона теряли
   и вывеску сверху, и то, что попало в нижнюю треть. */
.mod-card img {
  width: 100%; height: auto; max-height: 420px; object-fit: contain;
  display: block; background: var(--surface-2);
}
.mod-card .mod-body { padding: 12px 14px; display: grid; gap: 6px; }
.mod-card .mod-actions { display: flex; gap: 10px; padding: 0 14px 14px; }
.mod-card .mod-actions button { flex: 1 1 0; }
/* Карточка комментария: снимка нет, читается сам текст. */
.mod-text .mod-body p { margin: 0; font-size: 15px; }
.mod-avatar img { max-height: 220px; object-fit: contain; }
.editor-login { display: grid; gap: 10px; }
.todo-row span { display: grid; gap: 2px; text-align: left; }
.todo-row .hint { margin: 0; }

/* Прицел на карте. Точку ставят, наводя карту, а не тыкая пальцем: палец
   закрывает как раз то место, куда целишься. */
.map-aim {
  position: absolute; left: 50%; top: 50%; width: 44px; height: 44px;
  margin: -22px 0 0 -22px; pointer-events: none; z-index: 6;
}
.map-aim::before, .map-aim::after {
  content: ''; position: absolute; background: var(--accent);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35);
}
.map-aim::before { left: 50%; top: 0; width: 2px; height: 100%; margin-left: -1px; }
.map-aim::after { top: 50%; left: 0; height: 2px; width: 100%; margin-top: -1px; }
.aim-bar {
  position: absolute; left: 12px; right: 12px; bottom: calc(var(--safe-bottom) + 16px);
  z-index: 7; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 12px 14px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow-2); font-size: 14px;
}
.aim-actions { display: flex; gap: 8px; }

/* Кнопки в форме правки. Главная кнопка приложения (56 пикселей, во всю
   ширину) здесь смотрится тяжело: это рабочий экран, а не финальный шаг
   анкеты, и «Зберегти» с «Видалити місце» слипались в один блок. */
#editor-screen button.primary.wide {
  min-height: 46px; line-height: 46px; font-size: 16px;
  max-width: 420px; margin: 22px auto 0;
}
#editor-screen button.ghost.wide {
  min-height: 42px; max-width: 420px; margin: 18px auto 0;
}
/* «Видалити місце» — не сосед «Зберегти», а отдельное решение: между ними
   нужен воздух, иначе палец промахивается по главной кнопке в чужую. */
#editor-screen #ed-delete { margin-top: 26px; }
#editor-screen #ed-status { text-align: center; margin-top: 10px; }

/* Комментарии к месту: короткая лента подсказок от тех, кто там был. */
.comments { margin-top: 24px; }
.comment-list { display: grid; gap: 10px; margin-bottom: 14px; }
.comment {
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
}
/* Шапка отзыва: кружок автора и подпись. Без картинки подпись читалась
   как строка служебного текста, а не как сказанное человеком. */
.comment-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.comment-avatar {
  flex: none; width: 28px; height: 28px; border-radius: 50%;
  object-fit: cover; display: block; background: var(--surface);
}
.comment-avatar-letter {
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent);
  font-size: 13px; font-weight: 700;
}
.comment-head .hint { margin: 0; }
.comment p { margin: 0 0 4px; font-size: 15px; }
.comment .hint { margin: 0; }
/* Свой текст, ждущий проверки: виден автору, но отличается от принятых —
   пунктиром и приглушённым фоном, чтобы не выглядел опубликованным. */
.comment.pending {
  background: transparent; border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
}
.comment.pending p { opacity: .85; }
#comment-text { min-height: 64px; resize: vertical; }

/* История маршрутов: карточка на прогулку, звёзды и два действия. */
.history-card {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: 0; margin-bottom: 14px;
  overflow: hidden;
}
/* Поля держит `.history-body`: карта под ней идёт в самый край. */
.history-card { position: relative; }
/* Список маршрутов прижат к краям экрана сильнее общих полей: каждый
   лишний пиксель уходит в плитки. Владелец продукта: «шаги не влезают,
   уходят в …, можешь чип сделать чуточку больше и за счёт этого чуточку
   увеличить и раздвинуть значки». */
#history-list, #city-routes-list { margin-left: -8px; margin-right: -8px; }
.history-head { display: flex; justify-content: space-between; align-items: baseline;
                gap: 12px; }
.history-route { margin: 6px 0 0; }
/* Оценка — в нижнем ряду карточки, своей строки у неё больше нет. */
.stars { display: flex; gap: 4px; margin: 6px 0; }
/* Звёзды на эталоне крупные и на фоне карточки, тень им больше не нужна. */
/* Владелец продукта: «звёзды рейтинга чуть крупнее». */
.history-card .star { font-size: 19px; padding: 0 1px; }
.star {
  background: none; border: none; padding: 1px 3px; cursor: pointer;
  font-size: 19px; line-height: 1; color: var(--line);
}
.star.on { color: var(--warn); }
/* Звёзды подборки только показывают: нажимать нечего, маршрут чужой. */
.stars-static .star { cursor: default; padding: 1px 3px; }
/* Верх карточки истории: схема пути слева, текст справа — как у карточки
   последнего маршрута на первом экране, чтобы они читались как одно
   семейство. Владелец продукта: «на этих чипах так же должен быть визуал
   маршрута». */

/* Подложка карточки из двух половин: слева схема пути на карте, справа
   снимок места из самого маршрута. Владелец продукта прислал макет
   и попросил «1в1»: по карте узнаёшь, где шёл, по снимку — куда пришёл.
   Снимка нет — карта занимает всю ширину, пустоты не остаётся. */
/* Картинка — только верх карточки, и снизу она растворяется в её фоне.
   Владелец продукта прислал эталон: «у тебя чёткая граница между плитками,
   а на макете полное поглощение». Поглощение выходит само собой, когда
   плитки, звёзды и кнопки лежат на ровном тёмном фоне карточки, а не
   поверх светлой карты: контуры перестают читаться. */
.history-bg {
  position: absolute; left: 0; right: 0; top: 0; height: 190px;
  z-index: 0;
  /* Растворение — только в нижней четверти: раньше картинка начинала
     гаснуть с середины, и снимок справа выглядел обрезанным по высоте.
     Владелец продукта: «фото не во всю высоту». */
  mask-image: linear-gradient(to bottom, #000 74%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 74%, transparent 100%);
}
/* Карта — во всю ширину подложки, снимок лежит поверх её правой части
   и растворяется к левому краю. Раньше половины стояли рядом, и прозрачный
   край снимка открывал не карту, а фон карточки: на стыке получалась
   чёрная полоса. Владелец продукта: «всё ещё видно чёткое разделение
   артов с жёсткой чёрной границей». */
.history-map {
  display: block; position: absolute; inset: 0;
  width: 100%; height: 100%;
}
.history-map .route-thumb { border-radius: 0; }
.history-map .route-thumb-bg { object-fit: cover; }
/* Снимок обрезается по краям (`cover`), а не растягивается: памятник
   с растянутым конём — это и есть сплющивание, о котором говорил
   владелец продукта. */
.history-photo {
  /* Высота задана числом: у картинки своя пропорция, и при `height: auto`
     она перебивает `bottom` — снимок вылезал за подложку на треть. */
  position: absolute; top: 0; right: 0; width: 56%; height: 100%;
  object-fit: cover; object-position: center; display: block;
  mask-image: linear-gradient(to right, transparent 0%,
    rgba(0, 0, 0, .5) 18%, #000 46%);
  -webkit-mask-image: linear-gradient(to right, transparent 0%,
    rgba(0, 0, 0, .5) 18%, #000 46%);
}

/* На светлой теме белый текст поверх светлой карты не читался вовсе.
   Владелец продукта: «в светлой теме тоже поправь блоки, чтобы это
   выглядело лучше». Текст там тёмный, тень под ним светлая, а картинка
   растворяется раньше — город и список точек ложатся уже на фон карточки.
   Плитки, кнопки и звёзды берут цвета темы и правки не требуют. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .history-bg {
    mask-image: linear-gradient(to bottom, #000 56%, transparent 96%);
    -webkit-mask-image: linear-gradient(to bottom, #000 56%, transparent 96%);
  }
  :root:not([data-theme="dark"]) .history-hero-text b,
  :root:not([data-theme="dark"]) .history-away {
    color: var(--fg); text-shadow: 0 1px 3px rgba(255, 255, 255, .95);
  }
  :root:not([data-theme="dark"]) .history-hero-text .hint {
    color: var(--muted); text-shadow: 0 1px 3px rgba(255, 255, 255, .95);
  }
}
:root[data-theme="light"] .history-bg {
  mask-image: linear-gradient(to bottom, #000 56%, transparent 96%);
  -webkit-mask-image: linear-gradient(to bottom, #000 56%, transparent 96%);
}
:root[data-theme="light"] .history-hero-text b,
:root[data-theme="light"] .history-away {
  color: var(--fg); text-shadow: 0 1px 3px rgba(255, 255, 255, .95);
}
:root[data-theme="light"] .history-hero-text .hint {
  color: var(--muted); text-shadow: 0 1px 3px rgba(255, 255, 255, .95);
}

/* Общего затемнения нет. Владелец продукта: «у тебя какое-то затемнение
   идёт, а на макете плавное перетекание, без приглушения светом».
   Картинка остаётся картинкой; читаемость держат тень у текста и свои
   фоны у плиток и кнопок. */
.history-body {
  position: relative; z-index: 1;
  /* Город и список точек попадают на нижний край картинки, всё
     остальное — уже на фоне карточки. */
  padding: 142px 12px 12px;
}
.history-hero-text { display: flex; flex-direction: column; gap: 1px; }
/* Тень вместо подложки: текст читается и на светлой карте, и на снимке,
   а картинка под ним не глохнет. */
.history-hero-text b {
  font-size: 15px; font-weight: 700; color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .9), 0 0 12px rgba(0, 0, 0, .7);
}
.history-hero-text .hint {
  color: rgba(255, 255, 255, .92); font-size: 12px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .9), 0 0 10px rgba(0, 0, 0, .65);
}
/* Список точек — две строки: пять названий закрывали карту до верха. */
.history-hero-text .history-route {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; margin: 0;
}
/* Слева куда идти, справа далеко ли до начала — как на макете. */
.history-route-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: end; gap: 10px;
}
.history-away {
  display: inline-flex; align-items: center; gap: 5px;
  color: #fff; font-size: 13px; font-weight: 600; white-space: nowrap;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .9);
}
.history-away svg { opacity: .9; }
/* Число точек и цена — пилюлей слева, парой к дате справа. */
.history-count {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  padding: 3px 9px; border-radius: var(--pill);
  background: rgba(0, 0, 0, .6); color: rgba(255, 255, 255, .9);
  font-size: 11px; font-weight: 600; white-space: nowrap;
}
/* Дата пилюлей в углу карты. */
.history-date {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  padding: 3px 9px; border-radius: var(--pill);
  background: rgba(0, 0, 0, .6); color: #fff;
  font-size: 11px; font-weight: 600; white-space: nowrap;
}

/* Цифры прогулки — достижениями, а не строкой через точку.

   Владелец продукта: «пройденное расстояние, сожжённые ккал и шаги должны
   быть красиво расписаны, как достижения». Крупное число сверху, подпись
   под ним; плашки одной ширины, чтобы ряд читался как таблица результатов,
   а не как случайный набор. */
/* Сетка значков на экране достижений. Полученный — цветной с обводкой,
   остальные приглушены и без цвета: видно, сколько ещё впереди. */
.badges {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 12px;
}
.badge {
  padding: 12px 8px; border-radius: var(--radius-sm); border: 1px solid transparent;
  background: var(--surface-2); color: var(--fg); font: inherit; text-align: center;
  opacity: .55; cursor: default;
}
.badge.on { opacity: 1; background: var(--surface); border-color: var(--accent); cursor: pointer; }
.badge.on:active { transform: scale(.97); }
.badge .badge-icon { display: block; font-size: 30px; line-height: 1; filter: grayscale(1); }
.badge.on .badge-icon { filter: none; }
.badge b { display: block; margin-top: 8px; font-size: 12px; line-height: 1.25; }
.badge span:not(.badge-icon) {
  display: block; margin-top: 4px; font-size: 11px; color: var(--muted);
}
.badge-pop-icon { font-size: 64px; line-height: 1; margin-bottom: 8px; }
/* Кнопка «Поділитися» в окне нового значка. Класс `.go` описан только
   внутри `.sheet-actions`, и здесь она выходила серой — на вид неживой.
   Владелец продукта: «баг UI — серая кнопка». */
.finish-actions .go {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 52px; padding: 0 22px; border: none;
  border-radius: var(--pill);
  background: var(--accent); color: var(--accent-fg);
  font: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
}
.finish-actions .go:active:not([disabled]) { transform: scale(.98); }

/* ───────────── Экран достижений (макет владельца продукта) ─────────────
   Сорок восемь значков сеткой из плиток превращались в простыню: с одного
   взгляда не понять ни сколько взято, ни что делать дальше. Поэтому сверху
   кольцо «18 из 48», под ним фильтры и разделы, а сами значки — строками
   с задачей и полосой прогресса. */
.ach-hero {
  display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 14px;
  align-items: center; padding: 14px; margin-top: 4px;
  border-radius: 18px; background: var(--surface-2); border: 1px solid var(--line);
}
.ach-ring { position: relative; width: 92px; height: 92px; }
.ach-ring svg { width: 92px; height: 92px; transform: rotate(-90deg); }
.ach-ring circle { fill: none; stroke-width: 7; }
.ach-ring-bg { stroke: rgba(255, 255, 255, .1); }
.ach-ring-fill { stroke: var(--accent); stroke-linecap: round; transition: stroke-dasharray .4s; }
.ach-ring-num {
  position: absolute; inset: 0; display: grid; place-content: center;
  text-align: center; line-height: 1.1;
}
.ach-ring-num b { font-size: 24px; }
.ach-ring-num i { display: block; font-size: 11px; font-style: normal; color: var(--muted); }
.ach-hero-body { min-width: 0; }
.ach-hero-body b { font-size: 15px; line-height: 1.3; }
.ach-hero-body p { margin: 4px 0 8px; font-size: 12px; color: var(--muted); line-height: 1.35; }
.ach-hero-line { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; }
.ach-hero-line span { font-size: 11px; color: var(--muted); }
.ach-hero-bar {
  height: 7px; border-radius: 999px; background: rgba(255, 255, 255, .1); overflow: hidden;
}
.ach-hero-bar i { display: block; height: 100%; background: var(--accent); transition: width .4s; }

/* Три фильтра — сколько всего, сколько в работе, сколько взято. */
.ach-filters { display: flex; gap: 8px; margin-top: 12px; }
.ach-filter {
  flex: 1; padding: 9px 6px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--fg); font: inherit; font-size: 12px;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ach-filter i { font-style: normal; color: var(--muted); margin-left: 2px; }
.ach-filter.on { background: var(--accent); border-color: var(--accent); color: #04150c; }
.ach-filter.on i { color: rgba(4, 21, 12, .65); }

/* Разделы: значок и подпись. Лента прокручивается вбок — шесть кнопок
   в ряд на узком телефоне не помещаются. */
.ach-tabs {
  display: flex; gap: 8px; margin-top: 10px; overflow-x: auto;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.ach-tabs::-webkit-scrollbar { display: none; }
.ach-tab {
  flex: 0 0 auto; width: 62px; padding: 8px 4px; border-radius: 14px;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--fg);
  font: inherit; cursor: pointer; text-align: center;
}
.ach-tab span { display: block; font-size: 18px; line-height: 1; filter: grayscale(1); }
.ach-tab i {
  display: block; margin-top: 4px; font-size: 10px; font-style: normal;
  color: var(--muted); line-height: 1.15;
}
.ach-tab.on { background: var(--accent); border-color: var(--accent); }
.ach-tab.on span { filter: none; }
.ach-tab.on i { color: #04150c; }

.ach-section { margin-top: 16px; }
.ach-section-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-bottom: 8px;
}
.ach-section-head b { font-size: 15px; }
.ach-section-head span { font-size: 12px; color: var(--accent); }
.ach-list { display: grid; gap: 8px; }

/* Строка значка: слева картинка, посередине задача с полосой, справа
   счёт. Взятый — цветной с обводкой и галочкой, остальные приглушены. */
.ach-item {
  display: grid; grid-template-columns: 46px minmax(0, 1fr) auto;
  align-items: center; gap: 10px; width: 100%;
  padding: 10px; border-radius: 14px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--fg); font: inherit;
  text-align: left; cursor: default;
}
.ach-item.on { border-color: var(--accent); background: var(--surface); cursor: pointer; }
.ach-item.on:active { transform: scale(.99); }
.ach-item-icon {
  width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center;
  font-size: 24px; background: rgba(255, 255, 255, .05); filter: grayscale(1);
}
.ach-item.on .ach-item-icon { filter: none; background: rgba(61, 220, 132, .12); }
.ach-item-body { min-width: 0; }
.ach-item-body b { display: block; font-size: 14px; line-height: 1.25; }
.ach-item-task {
  display: block; margin: 2px 0 6px; font-size: 11px; color: var(--muted);
  line-height: 1.3;
}
.ach-item-bar {
  display: block; height: 5px; border-radius: 999px;
  background: rgba(255, 255, 255, .1); overflow: hidden;
}
.ach-item-bar i { display: block; height: 100%; background: var(--accent); }
.ach-item-side { text-align: right; display: grid; gap: 4px; justify-items: end; }
.ach-item-mark {
  width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
  font-size: 13px; background: rgba(255, 255, 255, .06); filter: grayscale(1);
}
.ach-item.on .ach-item-mark { background: var(--accent); color: #04150c; filter: none; }
.ach-item-num { font-size: 10px; color: var(--muted); white-space: nowrap; }

/* Лестница: активная ступень видна всегда, остальные — по нажатию.
   Кнопка со счётом «1/6» стоит справа от строки и не мешает поделиться
   полученным значком. */
.ach-ladder { display: grid; gap: 6px; }
.ach-ladder-head { display: grid; grid-template-columns: minmax(0, 1fr) 26px; gap: 4px; }
.ach-ladder-toggle {
  display: grid; place-content: center; padding: 0;
  border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--muted); font: inherit; cursor: pointer;
}
.ach-ladder-toggle span { font-size: 13px; line-height: 1; }
/* Счёт ступеней у названия: «Двадцять п’ять 1/7». */
.ach-item-steps {
  font-style: normal; font-size: 10px; font-weight: 600; color: var(--muted);
  margin-left: 6px; padding: 1px 5px; border-radius: 999px;
  background: rgba(255, 255, 255, .07); white-space: nowrap;
}
.ach-item.on .ach-item-steps { color: var(--accent); background: rgba(61, 220, 132, .12); }
.ach-ladder.open .ach-ladder-toggle { border-color: var(--accent); color: var(--accent); }
.ach-ladder-rest {
  display: grid; gap: 6px; margin-left: 12px;
  padding-left: 10px; border-left: 2px solid var(--line);
}
/* Ступени внутри — компактнее активной: они справочные. */
.ach-item-sub { padding: 8px; }
.ach-item-sub .ach-item-icon { width: 36px; height: 36px; font-size: 19px; border-radius: 10px; }
.ach-item-sub .ach-item-body b { font-size: 13px; }
/* Пять плашек сводки — в одну строку: пятая одна на второй строке
   выглядела оторванной. */
/* Итоги идут без значка, одними числами, а сетка плитки держит первую
   колонку в 24 пикселя под значок: «12 400» в неё не влезало и наезжало
   на подпись. Здесь колонка одна, число над подписью. */
#ach-totals { grid-template-columns: repeat(5, 1fr); gap: 6px; }
#ach-totals .ach {
  display: block; padding: 7px 3px 6px; text-align: center;
}
/* Значок над числом, своим цветом у каждой меры: пять плиток в ряд
   различаются с одного взгляда, а не читаются подписью в девять точек. */
.ach-total-icon { display: block; margin: 0 auto 2px; width: 17px; height: 17px; color: var(--accent); }
.ach-total-icon svg { width: 17px; height: 17px; display: block; }
.ach-total-steps .ach-total-icon { color: #7cc4ff; }
.ach-total-kcal .ach-total-icon { color: #ff9f6b; }
.ach-total-walks .ach-total-icon { color: #c9a7ff; }
.ach-total-places .ach-total-icon { color: #ffd166; }
#ach-totals .ach b { display: block; font-size: 14px; line-height: 1.15; }
#ach-totals .ach span {
  display: block; margin-top: 2px; font-size: 9px; line-height: 1.2;
  color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Плитка: значок кружком слева, число и подпись справа — как на макете
   владельца. Значок сверху по центру делал плитку вдвое выше, и четыре
   плитки занимали столько же места, сколько сама карта. */
.achievements {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 5px; margin: 8px 0 2px;
}
/* Содержимое по центру плитки. У коротких значений («3.0», «145», «699»)
   пара «значок — число» прижималась влево, и справа зияла пустота, тогда
   как у шагов плитка была заполнена целиком: четыре плитки в ряд выглядели
   как случайный набор. Владелец продукта: «в поле км, ккал, минуты значения
   центрируй внутри поля». */
/* Значок — своей колонкой, число и подпись — по центру оставшейся.
   В общей строке значок толкал число вправо на одиннадцать точек, а подпись
   стояла по центру: плитка выглядела косой. Владелец продукта: «косое,
   всё в разные стороны смотрит». */
.ach {
  display: grid; grid-template-columns: 24px minmax(0, 1fr);
  align-items: center; gap: 6px;
  /* Плитка низкая: владелец продукта — «значки км, шаги чуть меньше
     по высоте». Ряд стал на восемь точек тоньше, и они ушли картинке. */
  padding: 3px 8px; border-radius: 14px;
  background: var(--surface-2); border: 1px solid var(--line);
  text-align: left; min-width: 0;
}
.ach-body { min-width: 0; }
/* Значок без оправы: кружок съедал восемь пикселей ширины, а они нужны
   числу. Сам значок при этом крупнее прежнего. */
/* Значок без оправы: на эталоне он просто цветной, а квадрат под ним
   рисовал вокруг числа вторую рамку внутри рамки плитки. Цвет закреплён
   за мерой, и четыре плитки различаются с одного взгляда. */
.ach-icon {
  width: 24px; height: 24px;
  display: grid; place-items: center; color: var(--accent);
}
.ach-icon svg { width: 20px; height: 20px; display: block; }
/* Сложность прогулки: горят первые столбики, остальные приглушены,
   и с ростом сложности меняется цвет — от зелёного к красному. */
.ach-level .ach-icon rect { fill: rgba(255, 255, 255, .22); }
.ach-level-easy .ach-icon .b1,
.ach-level-medium .ach-icon .b1, .ach-level-medium .ach-icon .b2,
.ach-level-hard .ach-icon .b1, .ach-level-hard .ach-icon .b2,
.ach-level-hard .ach-icon .b3,
.ach-level-extreme .ach-icon rect { fill: currentColor; }
.ach-level-easy .ach-icon, .ach-level-easy b { color: #4ade80; }
.ach-level-medium .ach-icon, .ach-level-medium b { color: #facc15; }
.ach-level-hard .ach-icon, .ach-level-hard b { color: #fb923c; }
.ach-level-extreme .ach-icon, .ach-level-extreme b { color: #f87171; }
/* Одна палитра на всё приложение: расстояние зелёное, время сиреневое,
   калории оранжевые. Шаги были голубыми и выбивались — а это то же
   расстояние, только в шагах. Владелец продукта: «значок шагов должен
   быть вроде бы зелёный, а время сиреневый, в общем держи один стиль».
   Те же цвета носят чипы карточек на главной — см. .city-chip-*. */
.ach-km .ach-icon { color: #6fe0a0; }
.ach-steps .ach-icon { color: #6fe0a0; }
.ach-kcal .ach-icon { color: #ffb277; }
.ach-time .ach-icon { color: #c9a6ff; }

/* Число не обрезаем многоточием: «41…» вместо «4100» — это не подпись,
   а поломка. Вместо этого шрифт ужимается по ширине экрана. */
.ach b {
  display: block; font-size: clamp(11px, 3.9vw, 16px); font-weight: 700;
  line-height: 1.1; letter-spacing: -0.03em; color: var(--accent);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Пятизначные шаги и калории — мельче: иначе число упирается в край. */
.ach-long b { font-size: clamp(10px, 3.2vw, 13px); letter-spacing: -0.04em; }
.ach-steps b { color: #6fe0a0; }
.ach-kcal b { color: #ffb277; }
.ach-time b { color: #c9a6ff; }
.ach .ach-label {
  display: block; font-size: 9.5px; color: var(--muted);
  text-transform: lowercase; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}


/* Действия карточки истории — значками справа. Владелец продукта:
   «кнопка поделиться должна быть значком». Системный лист сам показывает
   все установленные соцсети, подпись рядом ничего не добавляла. */
/* Одна строка на всё: оценка слева, действия справа. Владелец продукта:
   «нижнюю панель сделаем более компактную, чтобы выиграть больше места
   для арта; рейтинг сдвигаем в нижний левый угол, кнопку шейра со всеми
   рядом». Освободившиеся точки ушли картинке. */
/* Всё в одну строку: оценка, кнопка и значки. Владелец продукта просил
   низ компактнее ради картинки, но с подписью у кнопки: «не хватает кнопки
   снизу». Поэтому строка одна, а звёзды и кнопки — мельче обычного. */
.history-actions { display: flex; gap: 6px; margin-top: 8px; align-items: center; }
.history-actions .stars { margin: 0 auto 0 0; }
.history-actions .icon-btn { width: 34px; height: 34px; flex: none; }
.history-actions .icon-btn svg { width: 17px; height: 17px; }
.history-actions .history-to-city {
  flex: 0 1 auto; min-width: 0;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 34px; padding: 0 11px; border-radius: 12px;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--fg);
  font-size: 12.5px; white-space: nowrap;
}
.history-actions .history-to-city span {
  overflow: hidden; text-overflow: ellipsis;
}
.history-actions .history-forget { color: var(--muted); }

/* Своя точка на карте маршрута. Кнопку геолокации MapLibre приводим к виду
   остальных круглых кнопок, а саму метку делаем крупнее стандартной:
   на ходу в неё смотрят мельком и издалека. */
#map .maplibregl-ctrl-group {
  border-radius: 50%; overflow: hidden; box-shadow: var(--shadow-2);
}
/* Своя метка «я здесь»: контроль геолокации рисует свою только когда сам
   следит за положением, а следит он, центрируя карту (см. возобновитьМетку). */
.me-dot {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 3px var(--surface), 0 0 10px rgba(0,0,0,.35);
}
/* На карте мест точка «ви тут» синяя: зелёный там занят самими местами
   и выбранной точкой, и своё положение в этой зелени терялось. */
.me-dot-explore {
  background: #3b82f6;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .95), 0 2px 10px rgba(0, 0, 0, .45);
}

#map .maplibregl-user-location-dot,
#map .maplibregl-user-location-dot::before {
  background-color: var(--accent);
}
#map .maplibregl-user-location-dot::after { border-color: var(--accent); }

/* Кнопка снимка: одна на два пути. Нажатие открывает короткое меню —
   снять сейчас или взять из галереи. Раньше в карточке стояли две кнопки,
   а в чипе на карте была только съёмка, и готовый снимок оттуда добавить
   было нельзя вовсе. */
.icon-btn.shoot {
  width: 36px; height: 36px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--fg); border: 1px solid var(--line);
}
.photo-pick { display: inline-flex; }
/* Лист снизу и затемнение под ним. Выпадающим списком внутри карточки места
   меню быть не может: карточка прокручивается, и всё, что торчит за её край,
   срезается — проверено на телефоне 04.09.2026. Лист лежит в body, поэтому
   не зависит от того, откуда его позвали. */
.sheet-veil {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(0, 0, 0, .45);
}
.photo-source {
  position: fixed; z-index: 61;
  left: 12px; right: 12px; bottom: calc(12px + var(--safe-bottom, 0px));
  padding: 8px; display: grid; gap: 2px;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 -8px 32px rgba(0, 0, 0, .45);
}
.photo-source-title {
  margin: 4px 8px 8px; font-size: 13px; color: var(--muted);
}
.photo-source-item {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 14px 12px; min-height: 52px;
  border: none; border-radius: var(--radius-sm);
  background: transparent; color: inherit;
  font: inherit; font-size: 16px; text-align: left; cursor: pointer;
  /* Названия короткие, но на узком экране перенос выглядел бы неряшливо. */
  white-space: nowrap;
}
.photo-source-item svg { flex: none; color: var(--muted); }
.photo-source-item:active { background: var(--surface-2); }
@media (hover: hover) {
  .photo-source-item:hover { background: var(--surface-2); }
}
/* Экран пошире телефонного: лист не растягиваем во всю ширину. */
@media (min-width: 560px) {
  .photo-source { left: auto; right: 24px; width: 320px; }
}

/* Снимки места в форме правки: миниатюра и решение под ней. */
.ed-photos { display: grid; gap: 12px; }
.ed-photo {
  margin: 0; border: 1px solid var(--line); border-radius: var(--radius-sm);
  overflow: hidden; background: var(--surface-2);
}
.ed-photo img {
  width: 100%; height: auto; max-height: 300px; object-fit: contain;
  display: block; background: var(--surface-2);
}
.ed-photo figcaption {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 8px 10px; flex-wrap: wrap;
}
.ed-photo-actions { display: flex; gap: 8px; }

/* Значок профиля стоит слева от шестерёнки: настройки — про приложение,
   профиль — про человека, и путать их не нужно. */
.icon-btn.corner-2 { right: calc(20px + 44px); }
.my-photo {
  display: flex; gap: 12px; align-items: center;
  padding: 10px; margin-bottom: 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.my-photo img {
  width: 64px; height: 64px; flex: none;
  border-radius: var(--radius-sm); object-fit: cover;
}
.my-photo-body { display: grid; gap: 2px; min-width: 0; }
.my-photo-body .hint { margin: 0; }

/* Фильтры очереди заполнения: одна задача за прогулку. */
.todo-filters { padding: 0 20px 12px; gap: 8px; flex-wrap: wrap; }

/* ------------------------- конец маршрута (§12) --------------------------- */

/* Владелец продукта: «когда полностью выполняешь маршрут, он должен эпично
   закрываться, где предлагает оценить в звёздах 1-5, поделиться в соцсетях».

   Всё, что движется, движется один раз и не дольше двух секунд: это конец
   прогулки, телефон в этот момент чаще всего почти разряжен, и крутить
   бесконечную анимацию было бы издевательством. Конфетти — двадцать кусочков
   на CSS, без библиотек: сторонние скрипты у нас запрещены политикой
   безопасности, да и грузить их ради двух секунд незачем. */
.finish {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(0, 0, 0, .55);
  animation: finish-veil .25s ease-out;
}
.finish-card {
  position: relative; width: 100%; max-width: 460px;
  padding: 28px 20px calc(20px + var(--safe-bottom));
  background: var(--surface); color: var(--fg);
  border-radius: var(--radius) var(--radius) 0 0;
  text-align: center; overflow: hidden;
  animation: finish-rise .32s cubic-bezier(.2, .9, .3, 1);
}
@media (min-width: 560px) {
  .finish { align-items: center; }
  .finish-card { border-radius: var(--radius); }
}
.finish-check { color: var(--accent); margin-bottom: 6px; }
/* Кружок обводится, галочка дорисовывается следом — рисунок «ставит печать». */
.finish-ring {
  stroke-dasharray: 145; stroke-dashoffset: 145;
  animation: finish-draw .5s ease-out forwards;
}
.finish-tick {
  stroke-dasharray: 40; stroke-dashoffset: 40;
  animation: finish-draw .35s .45s ease-out forwards;
}
.finish-card h2 { margin: 0 0 6px; font-size: 24px; letter-spacing: -0.02em; }
.finish-sub { margin: 0 0 14px; color: var(--muted); font-size: 15px; }
.finish-steps, .finish-spent { margin: 0 0 4px; font-size: 15px; }
.finish-spent { color: var(--muted); margin-bottom: 18px; }
.finish-rate-label { margin: 0 0 8px; font-size: 15px; color: var(--muted); }
.finish-stars { display: flex; justify-content: center; gap: 6px; margin-bottom: 16px; }
.finish-stars .star {
  width: 44px; height: 44px; border: none; background: transparent;
  color: var(--line); font-size: 30px; line-height: 1; cursor: pointer;
  transition: transform .12s ease, color .12s ease;
}
.finish-stars .star.on { color: #f5c451; }
.finish-stars .star:active { transform: scale(1.2); }
.finish-thanks { margin: 0 0 14px; color: var(--accent); font-size: 14px; }
.finish-actions { display: grid; gap: 10px; }
/* Вид задаём здесь, а не классом `go`: тот описан только внутри панели
   маршрута и снаружи не работает — кнопка выходила серой. */
.finish-actions button {
  min-height: 48px; border-radius: var(--pill);
  font: inherit; font-size: 16px; font-weight: 600; cursor: pointer;
}
.finish-share {
  border: none; background: var(--accent); color: var(--accent-fg);
  font-weight: 700 !important;
}
.finish-close {
  border: 1px solid var(--line); background: transparent; color: var(--fg);
}

/* Конфетти: кусочки раскладывает JS, здесь только само падение. */
.finish-burst { position: absolute; inset: 0 0 auto; height: 0; pointer-events: none; }
.finish-burst i {
  position: absolute; top: 0; width: 8px; height: 12px; border-radius: 2px;
  opacity: 0; animation: finish-fall 1.9s ease-in forwards;
}
@keyframes finish-veil { from { opacity: 0; } }
@keyframes finish-rise { from { transform: translateY(28px); opacity: 0; } }
@keyframes finish-draw { to { stroke-dashoffset: 0; } }
@keyframes finish-fall {
  0%   { opacity: 1; transform: translateY(-10px) rotate(0deg); }
  100% { opacity: 0; transform: translateY(320px) rotate(540deg); }
}
/* Уважаем настройку «меньше движения»: она стоит у людей, которым от
   анимации плохо, — им конец маршрута радости не прибавит. */
@media (prefers-reduced-motion: reduce) {
  .finish, .finish-card, .finish-ring, .finish-tick, .finish-burst i {
    animation: none;
  }
  .finish-ring, .finish-tick { stroke-dashoffset: 0; }
  .finish-burst { display: none; }
}

/* Числовое поле в настройках (вес). Стрелки убираем: на телефоне их всё
   равно не нажать, а ширину они съедают. */
.num-input {
  width: 88px; min-height: 40px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--fg);
  font: inherit; font-size: 16px; text-align: center;
  -moz-appearance: textfield;
}
.num-input::-webkit-outer-spin-button,
.num-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Оценка места звёздами в карточке. Владелец продукта: «так же местам
   добавить рейтинг в карточках 1-5 звёзд». Стоит сразу под адресом: это
   то же самое «что за место», только словами других людей. */
.poi-rating {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 14px; flex-wrap: wrap;
}
.poi-stars { display: flex; gap: 2px; }
.poi-stars .star {
  width: 34px; height: 34px; border: none; background: transparent;
  color: var(--line); font-size: 24px; line-height: 1; cursor: pointer;
  transition: transform .12s ease, color .12s ease;
}
.poi-stars .star.on { color: #f5c451; }
.poi-stars .star:active { transform: scale(1.2); }
.poi-rating-note { font-size: 13px; color: var(--muted); }

/* Карточка маршрута в истории нажимается целиком. Владелец продукта:
   «маршруты должны быть кликабельными по всему контуру». */
.history-card { cursor: pointer; }
.history-card:active { background: var(--surface-2); }
.history-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Кнопки внутри живут своей жизнью: нажатие на них до карточки не доходит. */
.history-card button { cursor: pointer; }
.history-stats { color: var(--muted); }


/* Где текст всё-таки выделяют: описания мест, комментарии, адреса и всё,
   что вводят руками. Запрещать копирование содержательного текста — значит
   мешать человеку поделиться адресом или названием. */
.desc, .detail p, .detail h2, .addr, .comment-text, .history-route,
.chip-meta, .anchor, .mono, input, textarea, [contenteditable] {
  -webkit-user-select: text; user-select: text;
  -webkit-touch-callout: default;
}

/* ------------------------- барабан выбора (§3) ---------------------------- */

/* Владелец продукта: «сделай вращающуюся установку веса и времени, как
   в айфонах, объёмную, красивую».

   Объём даёт перспектива на контейнере плюс поворот каждой строки — угол
   считает `wheel.js` по расстоянию до середины. Привязку к делениям и
   инерцию берём у браузера: свой «физический движок» отличался бы от
   системного, и палец это чувствует сразу. */
.wheel {
  --wheel-row: 40px;
  position: relative;
  /* Не во всю ширину карточки: барабан на полэкрана выглядит громоздко,
     а крутят его большим пальцем — ему хватает узкой полосы. Владелец
     продукта: «барабан слишком широкий». */
  width: 160px; max-width: 100%; margin: 0 auto;
  height: calc(var(--wheel-row) * 5);
  overflow-y: scroll;
  /* Мягкая привязка: жёсткая (`mandatory`) на мыши тащит обратно после
     каждого щелчка колеса, и попасть в нужное значение невозможно.
     Пальцу жёсткая нужна — он останавливает барабан где угодно; для него
     она включается ниже, по типу указателя. */
  scroll-snap-type: y proximity;
  /* `scroll-behavior: smooth` здесь не ставим. С ним прокрутка становится
     анимацией, а анимацию окружение может не выполнять вовсе — при системной
     настройке «меньше движения» или в строгих режимах браузера. Барабан
     тогда не двигается ни на щелчок колеса, ни со стрелок: проверено,
     `scrollTop` оставался прежним. Пальцу плавность даёт сам браузер. */
  /* Перспектива подальше: при близкой строки распухают у краёв и текст
     расползается — владелец продукта назвал это «шакальным качеством».
     С восьмьюстами наклон читается, а буквы остаются буквами. */
  perspective: 800px;
  perspective-origin: center;
  /* Полоса прокрутки барабану не нужна: он и так понятен. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  overscroll-behavior: contain;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  text-align: center;
  cursor: grab;
}
.wheel::-webkit-scrollbar { display: none; }
.wheel:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wheel-pad { height: calc(var(--wheel-row) * 2); }
.wheel-item {
  height: var(--wheel-row); line-height: var(--wheel-row);
  scroll-snap-align: center;
  font-size: 20px; color: var(--fg);
  /* Чёткость наклонённого текста. `will-change` здесь только вредил:
     браузер поднимал каждую строку в отдельный слой и растрировал её
     заранее — в наклоне такой слой растягивается, и цифры мылятся.
     Владелец продукта: «цифры всё ещё мутные». */
  transform-style: flat;
  backface-visibility: hidden;
  -webkit-font-smoothing: antialiased;
  transition: color .12s ease;
}
.wheel-item.on { font-weight: 700; }
/* Окошко выбранного значения: две линии поперёк барабана, как в часах. */
.wheel-frame { position: relative; }
.wheel-frame::before, .wheel-frame::after {
  content: ""; position: absolute; left: 8px; right: 8px; height: 1px;
  background: var(--line); pointer-events: none; z-index: 2;
}
.wheel-frame::before { top: calc(50% - 20px); }
.wheel-frame::after { top: calc(50% + 20px); }
/* Линии окошка — по ширине самого барабана, а не всей карточки. */
.wheel-frame::before, .wheel-frame::after {
  left: 50%; right: auto; width: 176px; transform: translateX(-50%);
}
/* Два барабана рядом — часы и минуты. */
.wheel-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* Пальцем барабан останавливают где попало — ему нужна жёсткая привязка. */
@media (pointer: coarse) {
  .wheel { scroll-snap-type: y mandatory; }
}
@media (prefers-reduced-motion: reduce) {
  .wheel-item { transform: none !important; opacity: 1 !important; }
}

/* Часы работы в форме правки: два барабана рядом и подпись «з» / «до». */
.wheel-pair .wheel-frame { display: grid; justify-items: center; gap: 4px; }
.wheel-pair .wheel { width: 120px; }
.wheel-pair .wheel-frame::before,
.wheel-pair .wheel-frame::after { width: 132px; }
#ed-hours-preview { display: block; margin-top: 8px; color: var(--muted); }

/* ───────────────── Главная по рендеру владельца продукта ─────────────────
   Приветствие с колоколом, строка поиска, зелёная плашка «Скласти маршрут»
   и четыре двери в каталог мест. Раньше здесь была одна кнопка во всю
   ширину: с неё нельзя было попасть ни в поиск, ни в каталог, не пройдя
   анкету целиком. */
.home-top {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
/* Погода на остаток дня: четыре отсечки в ряд, каждая — час, небо,
   градусы. Полоска узкая: это подсказка, а не отдельный экран погоды. */
.weather-day {
  display: flex; gap: 6px; margin-top: 10px;
  padding: 8px; border-radius: 16px;
  background: var(--surface-2); border: 1px solid var(--line);
}
.weather-hour {
  flex: 1 1 0; display: grid; justify-items: center; gap: 2px;
  font-size: 12px; color: var(--muted);
}
.weather-hour b { font-size: 11.5px; font-weight: 700; color: var(--muted); }
.weather-hour span { font-size: 13px; font-weight: 700; color: var(--fg); }
.weather-hour svg { width: 22px; height: 22px; }

/* Карточка идущего маршрута: зелёный край, чтобы её нельзя было принять
   за вчерашнюю прогулку. */
.last-plan.walking { border-color: var(--accent); }
.last-plan.walking .last-plan-nums { color: var(--accent); font-weight: 700; }

/* Градусник рядом с колоколом: значок неба и температура. */
.home-weather {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  padding: 4px 12px 4px 8px; border-radius: var(--pill);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--fg); font: inherit; cursor: pointer;
  transition: transform .12s ease;
}
.home-weather:active { transform: scale(.94); }
.home-weather i { display: grid; place-items: center; }
.home-weather b { font-size: 16px; font-weight: 700; }
.home-bell {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface); color: var(--fg);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.home-bell:active { transform: scale(.94); }
/* Счётчик на колоколе: сколько непрочитанного. Ставится поверх значка
   справа сверху, как в почте. Раньше здесь была точка — она говорила
   «что-то есть», но не сколько; владелец продукта попросил цифру.

   Обводка цветом фона отделяет кружок от значка под ним, иначе цифра
   сливается с дужкой колокола. */
.home-bell { position: relative; }
.home-bell-dot {
  position: absolute; top: 2px; right: 2px;
  display: grid; place-items: center;
  min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: 999px; background: var(--accent);
  color: #0b1220; font-size: 10px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--bg);
}
/* «9+» шире одной цифры — кружок вытягивается в пилюлю. */
.home-bell-dot.wide { min-width: 20px; }

/* Виды уведомлений: ряд пилюль под шапкой, открывается кнопкой в углу.
   Спрятан по умолчанию — лента важнее фильтра, и на трёх записях он лишний. */
/* Крестик на записи: убрать её из ленты. Стоит в правом верхнем углу
   карточки, чтобы не спорить со временем справа внизу. */
.notes-list { position: relative; }
.note { position: relative; }
.note-drop {
  position: absolute; top: 6px; right: 6px; z-index: 2;
  display: grid; place-items: center; width: 26px; height: 26px;
  border: 0; border-radius: 50%; background: none;
  color: var(--muted); cursor: pointer; opacity: .6;
}
.note-drop:hover, .note-drop:focus-visible {
  background: var(--surface-2); color: var(--fg); opacity: 1;
}
/* Место под крестик: без него время записи уезжало бы под него. */
.note .note-when { padding-right: 22px; }
#notes-clear { margin: 4px 20px 12px; }

.notes-kinds {
  display: flex; flex-wrap: wrap; gap: 8px; padding: 0 20px 10px;
}
.notes-kind {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: var(--pill);
  border: 1px solid var(--line); background: var(--surface-2);
  color: var(--muted); font-size: 13px; font-weight: 600; cursor: pointer;
}
.notes-kind.on {
  background: var(--accent); border-color: var(--accent); color: var(--accent-fg);
}
/* Сколько непрочитанного в этом виде — тем же кружком, что на колоколе. */
.notes-kind-n {
  display: inline-grid; place-items: center; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 999px; font-style: normal; font-size: 10px;
  font-weight: 800; background: var(--accent); color: #0b1220;
}
.notes-kind.on .notes-kind-n { background: rgba(0, 0, 0, .25); color: inherit; }

/* Лента уведомлений: значок, две строки текста, время справа. */
/* Пройдені / Плановані: две пилюли под вкладками маршрутов. Не третий
   ряд вкладок — они бы спорили с верхними за внимание. */
.mine-tabs { display: flex; gap: 8px; padding: 8px 20px 0; }
.mine-tab {
  padding: 7px 14px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--muted); font: inherit;
  font-size: 13px; font-weight: 600; cursor: pointer;
}
.mine-tab.on { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }

.notes-list { display: grid; gap: 8px; margin-top: 4px; }
.note {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto;
  align-items: center; gap: 10px; width: 100%;
  padding: 10px; border-radius: 14px; border: 1px solid var(--line);
  background: var(--surface-2); color: var(--fg); font: inherit;
  text-align: left; cursor: pointer;
}
.note:active { transform: scale(.99); }
.note.new { border-color: var(--accent); background: var(--surface); }
.note-icon {
  width: 40px; height: 40px; border-radius: 12px; display: grid;
  place-items: center; font-size: 20px; background: rgba(255, 255, 255, .05);
}
.note.new .note-icon { background: rgba(61, 220, 132, .12); }
.note-body { min-width: 0; }
.note-body b { display: block; font-size: 13px; }
.note-text {
  display: block; margin-top: 2px; font-size: 12px; color: var(--muted);
  line-height: 1.3;
}
.note-sub { display: block; margin-top: 2px; font-size: 11px; color: var(--muted); }
.note-when { font-size: 10px; color: var(--muted); white-space: nowrap; align-self: start; }

/* Поиск на главной: поле, а не кнопка-переход. Список находок выпадает
   под ним и накрывает то, что ниже, — человек читает выдачу, а не ищет
   её среди карточек. */
/* Выше карточек: у чипа «Топ» и у меток афиши свой `z-index: 3`, и при
   равном весе побеждал тот, что ниже в разметке, — зелёные плашки
   проступали сквозь список находок. Владелец продукта: «вот словил баг,
   через поиск пробивается топ и т. д.». Ниже нижней панели (60): она
   должна остаться поверх всего. */
.home-search-wrap { position: relative; margin-bottom: 12px; z-index: 30; }
.home-search {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 48px; padding: 0 16px;
  border-radius: var(--pill); border: 1px solid var(--line);
  background: var(--surface); color: var(--muted);
  font: inherit; font-size: 14.5px; text-align: left; cursor: text;
}
.home-search svg { flex: none; }
.home-search:focus-within { border-color: var(--accent); }
.home-search input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  color: var(--fg); font: inherit; font-size: 14.5px;
}
.home-search input::placeholder { color: var(--muted); }
/* Крестик у поля типа search в вебките лишний: он и без того есть
   в нашей вёрстке, а два подряд выглядят поломкой. */
.home-search input::-webkit-search-cancel-button { display: none; }
/* Свой крестик: браузерный есть не во всех и выглядит по-разному. Область
   нажатия шире значка — пальцем в шестнадцать точек не попасть. */
.home-search-clear {
  flex: none; display: grid; place-items: center;
  width: 28px; height: 28px; margin-right: -6px;
  border: 0; border-radius: 50%; background: none;
  color: var(--muted); cursor: pointer;
}
.home-search-clear:hover, .home-search-clear:focus-visible {
  background: var(--surface-2); color: var(--fg);
}

.home-found {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 1;
  max-height: 58vh; overflow-y: auto;
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
  padding: 6px;
}
.home-found-item {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  width: 100%; padding: 9px 10px; border: 0; border-radius: 12px;
  background: none; color: var(--fg); font: inherit; text-align: left;
  cursor: pointer;
}
.home-found-item:hover, .home-found-item:focus-visible { background: var(--surface-2); }
.home-found-name { font-size: 14.5px; font-weight: 600; }
/* Расстояние прижато вправо и не переносится: по нему глаз и ведёт
   сравнение строк между собой. */
.home-found-far {
  grid-column: 2; grid-row: 1 / span 2; align-self: center;
  font-size: 12.5px; color: var(--accent); font-weight: 700;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.home-found-addr {
  grid-column: 1; font-size: 12px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.home-found-empty { padding: 12px 10px; font-size: 13px; color: var(--muted); }

.home-build {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 14px 16px; margin-bottom: 14px;
  border: 0; border-radius: 18px; cursor: pointer;
  background: var(--accent); color: var(--accent-fg); text-align: left;
}
.home-build:active { transform: scale(.99); }
.home-build-icon {
  flex: none; width: 40px; height: 40px; border-radius: 12px;
  display: grid; place-items: center;
  background: rgba(0, 0, 0, .14); color: inherit;
}
.home-build-text { flex: 1 1 auto; min-width: 0; }
.home-build-text b { display: block; font-size: 16px; font-weight: 700; }
.home-build-text span { display: block; font-size: 12.5px; opacity: .78; }
.home-build-go { flex: none; opacity: .8; }

/* Четыре двери в каталог: значок в цветном квадрате и подпись под ним. */
.home-cats {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
  margin-bottom: 18px;
}
.home-cat {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 12px 4px; border-radius: 16px; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--fg);
  font: inherit; font-size: 12px; font-weight: 600;
}
.home-cat:active { transform: scale(.97); }
.home-cat-icon {
  width: 44px; height: 44px; border-radius: 14px;
  display: grid; place-items: center;
}
.home-cat-icon svg { width: 26px; height: 26px; }
.home-cat-food .home-cat-icon { background: rgba(255, 169, 107, .16); color: #ffb277; }
.home-cat-culture .home-cat-icon { background: rgba(127, 180, 255, .16); color: #8cc0ff; }
.home-cat-activity .home-cat-icon { background: rgba(250, 204, 21, .16); color: #facc15; }
.home-cat-outdoor .home-cat-icon { background: rgba(74, 222, 128, .16); color: #4ade80; }

/* Подпись над названием значка и стрелка справа — по рендеру главной. */
.home-ach-kicker { font-size: 12px; color: var(--muted); }
.home-ach-go { color: var(--muted); }

/* ─────── Главная: доводка под рендер ───────
   Приветствие одной строкой (в две оно занимало пятую часть первого
   экрана), карточка маршрута почти во всю ширину, время события —
   пилюлей поверх постера сверху. */
.home-top h1 { font-size: 21px; line-height: 1.2; margin: 0; }
.city-card { width: 88%; min-width: 268px; max-width: 330px; min-height: 210px; }
.city-card-body b { font-size: 16.5px; }
.city-chip { display: inline-flex; align-items: center; gap: 5px; }
/* Время сеанса — плотной пилюлей в углу постера. Полупрозрачная тонула
   на светлых кадрах: владелец продукта — «на светлых подложках не видно
   время начала в афише». */
.place-card .home-card-when {
  position: absolute; z-index: 3; top: 10px; left: 10px;
  padding: 4px 10px; border-radius: var(--pill);
  background: var(--accent); color: var(--accent-fg);
  font-size: 11px; font-weight: 700; white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
}
/* Названию нужно место под пилюлей: без отступа они наезжали друг
   на друга. Владелец продукта: «чипы с временем налазят на названия». */
#home-events .place-card-text { padding-top: 46px; }
.place-card { position: relative; }
.event-venue { display: inline-flex; align-items: center; gap: 5px; }
.event-price { color: var(--fg); font-weight: 600; }
/* Точки-пагинация под лентой маршрутов — как на рендере. */
.home-dots { display: flex; justify-content: center; gap: 6px; margin-top: 10px; }
.home-dots i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--line);
  transition: width .2s ease, background .2s ease;
}
.home-dots i.on { width: 18px; background: var(--accent); }

/* ─────────────────────── Афиша по эталону ───────────────────────
   Заголовок с городом, поиск строкой, разделы значками, выбор дня,
   главное событие карточкой во всю ширину, лента ближайших и подборки.
   Прежний список одинаковых строк с двумя кнопками владелец продукта
   назвал бедным: «визуальные карточки с изображениями» — первое, что
   он выписал в улучшения. */
.events-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; padding: calc(var(--safe-top) + 12px) 16px 10px;
}
.events-title h1 { margin: 0; font-size: 24px; line-height: 1.15; }
.events-title .hint { margin: 4px 0 0; font-size: 13px; }
.events-city {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  min-height: 38px; padding: 0 12px; border-radius: var(--pill);
  border: 1px solid var(--line); background: var(--surface); color: var(--fg);
  font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
}
.events-city svg:first-child { color: var(--accent); }
.events-city:active { transform: scale(.97); }

.events-search {
  display: flex; align-items: center; gap: 10px;
  margin: 0 16px 12px; padding: 0 14px;
  border-radius: var(--pill); border: 1px solid var(--line);
  background: var(--surface); color: var(--muted);
}
.events-search svg { flex: none; }
.events-search input { border: 0; background: none; padding: 12px 0; min-height: 0; }

/* Разделы: квадратная карточка со значком и подписью. */
.events-kinds {
  display: flex; gap: 8px; padding: 0 16px 10px;
  overflow-x: auto; scrollbar-width: none;
}
.events-kinds::-webkit-scrollbar { display: none; }
.events-kind {
  flex: none; width: 74px; padding: 10px 6px; border-radius: 16px;
  border: 1px solid var(--line); background: var(--surface); color: var(--fg);
  font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer;
  display: grid; justify-items: center; gap: 6px;
  transition: transform .12s ease, background .15s ease;
}
.events-kind svg { width: 22px; height: 22px; }
/* Значок раздела цветной: форма у шести значков разная, но с одного
   взгляда это не читается — цвет разделяет быстрее. На выбранной плитке
   значок гасим до цвета подписи, иначе он спорит с зелёной заливкой. */
.events-kind-icon { display: grid; place-items: center; }
.events-kind.on .events-kind-icon { color: inherit !important; }
.events-kind:active { transform: scale(.96); }
.events-kind.on { background: var(--accent); color: var(--accent-fg); border-color: transparent; }
.events-kind .count {
  font-size: 10.5px; font-weight: 700; opacity: .7;
}

/* Дни: пилюли с числом и днём недели. */
.events-days {
  display: flex; gap: 8px; padding: 0 16px 14px;
  overflow-x: auto; scrollbar-width: none;
}
.events-days::-webkit-scrollbar { display: none; }
/* Пять кнопок на ширину телефона: при отступах в четырнадцать точек
   последняя не помещалась и лента открывалась подрезанной. */
.events-day {
  flex: none; min-height: 44px; padding: 6px 10px; border-radius: 14px;
  border: 1px solid var(--line); background: var(--surface); color: var(--fg);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  display: grid; justify-items: center; gap: 2px; line-height: 1.15;
}
.events-day span { font-size: 11px; color: var(--muted); font-weight: 500; }
.events-day.on { background: var(--accent); color: var(--accent-fg); border-color: transparent; }
.events-day.on span { color: inherit; opacity: .75; }
/* «Все» в ленте дней: пятая кнопка вместо календаря. */
.events-day-all { font-weight: 700; }

/* Главное событие — карточка во всю ширину со снимком. */
.events-hero { margin: 0 16px 18px; }
.hero-card {
  position: relative; display: block; width: 100%; padding: 0;
  border: 0; border-radius: 20px; overflow: hidden; cursor: pointer;
  min-height: 210px; background: var(--surface-2); text-align: left;
  color: #fff;
}
.hero-card img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
}
.hero-card::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to bottom,
    rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, .55) 55%, rgba(0, 0, 0, .92) 100%);
}
.hero-body {
  position: relative; z-index: 2; display: grid; gap: 6px;
  padding: 120px 14px 14px;
}
.hero-badge {
  position: absolute; z-index: 3; top: 12px; left: 12px;
  padding: 5px 11px; border-radius: var(--pill);
  background: var(--accent); color: var(--accent-fg);
  font-size: 11.5px; font-weight: 700;
}
.hero-body b { font-size: 19px; font-weight: 700; line-height: 1.2; }
.hero-line {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: rgba(255, 255, 255, .9);
}
.hero-line svg { flex: none; opacity: .85; }
.hero-price {
  justify-self: start; margin-top: 6px;
  padding: 9px 16px; border-radius: var(--pill);
  background: var(--accent); color: var(--accent-fg);
  font-size: 14px; font-weight: 700;
}

/* Карточка события в ленте: снимок, раздел, время, место, цена. */
.event-card {
  position: relative; width: 74%; min-width: 218px; max-width: 250px;
  padding: 0; border-radius: 18px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  color: var(--fg); text-align: left; cursor: pointer;
  display: grid; grid-template-rows: 128px auto;
}
.event-card img { width: 100%; height: 128px; object-fit: cover; display: block; }
.event-card .kind-badge {
  position: absolute; top: 100px; left: 10px; z-index: 2;
  padding: 3px 9px; border-radius: var(--pill);
  background: rgba(8, 12, 17, .78); color: #fff;
  font-size: 11px; font-weight: 700;
}
.event-card-body { display: grid; gap: 4px; padding: 12px; }
.event-card-body b {
  font-size: 14.5px; font-weight: 700; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.event-card-body .line {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--muted);
}
.event-card-body .line svg { flex: none; }
.event-card .price {
  justify-self: start; margin-top: 4px;
  padding: 6px 12px; border-radius: var(--pill);
  background: var(--accent-soft); color: var(--accent);
  font-size: 12px; font-weight: 700;
}

/* Подборки: плитка со снимком, названием и числом событий. */
.pick-card {
  position: relative; width: 60%; min-width: 176px; max-width: 210px;
  min-height: 116px; padding: 0; border: 0; border-radius: 16px;
  overflow: hidden; cursor: pointer; color: #fff; text-align: left;
  background: var(--surface-2);
}
.pick-card img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
}
.pick-card::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top, rgba(0, 0, 0, .85), rgba(0, 0, 0, .25));
}
.pick-body { position: relative; z-index: 2; display: grid; gap: 2px; padding: 12px; }
.pick-body b { font-size: 14px; font-weight: 700; line-height: 1.2; }
.pick-body span { font-size: 11.5px; opacity: .82; }
.pick-go {
  position: absolute; z-index: 2; right: 10px; bottom: 10px;
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .16); color: #fff;
}

/* Список городов афиши: сам элемент невидим, открывается родным выбором
   телефона по нажатию на пилюлю с названием города. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  border: 0; opacity: 0; pointer-events: none;
}

/* «Назад» в афише прижат к заголовку и появляется только при переходе
   из анкеты. */
.events-back { align-self: center; margin-right: 4px; }
