/* ═══════════════════════════════════════════════════════════════════════
   Стили продукта для сервера. СОБРАНО, РУКАМИ НЕ ПРАВИТЬ.

   Источник — дизайн-система, в том же порядке, в котором её собирает
   design/system/build.py:

       design/system/tokens/tokens.css
       design/system/components.css
       design/system/pages/pages.css

   Пересобрать:  node server/scripts/make-styles.mjs
   Почему так:   server/scripts/make-styles.mjs, шапка файла

   Правка здесь уедет при следующей сборке. Править надо в дизайн-системе —
   там же, где экраны утверждались глазами.
   ═══════════════════════════════════════════════════════════════════════ */


/* ━━━ design/system/tokens/tokens.css ━━━ */

/* ═══════════════════════════════════════════════════════════════════════
   «Покажи мне Питер» — токены. Этап 0, шаг 2.

   Выведены из трёх утверждённых страниц шага 1, а не придуманы вперёд:
   каждое значение здесь однажды понадобилось при рисовании. Паспорт
   с объяснениями, контрастами и правилами использования — tokens.md.
   Те же токены объектами для React Native — tokens.ts.

   Пометка (↑) — взято из design/design-direction.md как есть.
   Пометка (→) — выведено рисованием страниц.
   Пометка (✎) — правка по итогам приёмки страниц.

   Файл подставляется в страницы и карточки первым: design/system/build.py.
   ═══════════════════════════════════════════════════════════════════════ */

:root{
  /* ── Цвет, светлая тема ─────────────────────────────────────────── */
  --bg:#FFFFFF;              /* ↑ один фон на весь продукт, без вариаций */
  --text:#1A1A1A;            /* ↑ 17,40:1 к фону */
  --surface:#F4F4F5;         /* ↑ врезка-факт, панель поверх карты */
  --rule:#E4E4E6;            /* ↑ линейки и рамки */

  /* ✎ Правка 07.10.2026 по аудиту WCAG (design/system/WCAG.md).
     Один токен --rule делал две разные работы: разделял строки
     и очерчивал элементы управления. Для первого 1,27 к фону — верное
     значение, выведенное рисованием: линейка не должна спорить с текстом.
     Для второго это провал нормы WCAG 2.1 SC 1.4.11, которая требует
     3,0 от всего, по чему элемент управления опознаётся. Невыбранный
     флажок был прямоугольником, обведённым почти невидимой линией:
     в помещении видно, на солнце нет.

     Поднимать --rule было нельзя — серый с 3:1 между строками списка
     превращается в решётку. Поэтому работы разведены по двум токенам.
     --edge ставится ТОЛЬКО на границу элемента управления: флажок, тег,
     тихая кнопка, заблокированный маркер, контур заливки схемы.
     На разделители его ставить нельзя — вернётся та самая решётка.

     Значение выбрано не по минимуму: 3:1 нужно и к фону, и к поверхности,
     а поверхность светлее фона и потому требовательнее. Первый кандидат
     давал 3,03 к фону и 2,75 к поверхности — сторож поймал это сразу,
     как только обе пары попали в check-contrast.py. */
  --edge:#8B8B8E;            /* 3,40 к фону, 3,09 к поверхности, 5,12 к тексту */
  --muted:#4A4A4D;           /* ↑ подписи, метаданные, 8,83:1 */
  --accent:#DC1668;          /* ↑ 4,82:1 к фону — годится и текстом, и заливкой */
  --accent-press:#C01058;    /* ↑ только заливка */
  --accent-ink:#DC1668;      /* ↑ акцентный текст; на --surface не ставить (4,38) */
  --on-accent:#FFFFFF;       /* ↑ подпись на заливке белая */

  /* ✎ Схема получила собственную заливку. Поверхность врезки (1,10 к фону)
     на схеме не работает: вода пропадала на телефоне даже в тени. */
  --scheme-fill:#E0E0E3;

  /* ✎ Лицо маркера на карте и цвет цифры на нём. В светлой теме это фон
     и акцент; в тёмной — светлое пятно, см. ниже. */
  --mk-face:#FFFFFF;
  --mk-ink:#DC1668;          /* 4,82 на белом */

  /* ✎ Своя позиция, правка 07.10.2026. Раньше метка строилась из --text
     и --bg, то есть переворачивалась вместе с темой: в тёмной получалось
     светлое ядро в тёмном кольце на тёмной карте — кольцо пропадало,
     и от метки оставалось светлое пятно без формы.
     Теперь строение одно в обеих темах: светлое кольцо, ядро чернилами.
     Это то же, что делают карты, которыми люди пользуются каждый день,
     и узнаётся без обучения. */
  --me-face:#FFFFFF;         /* кольцо и обводка клюва */
  --me-dot:#1A1A1A;          /* ядро и клюв */
  --line-case-op:.92;        /* обводка линии маршрута, см. тёмную тему */

  /* ── Типографика ─────────────────────────────────────────────────── */
  --sans:'Onest',system-ui,sans-serif;        /* ↑ весь интерфейс */
  --serif:'Literata',Georgia,serif;           /* ↑ только тело рассказа */
  --measure:62ch;                             /* → середина вилки 54–64 (↑) */

  /* ── Шаг сетки: 4 px ─────────────────────────────────────────────── */
  /* → Четвёрка, а не восьмёрка: интерлиньяж тела 19×1.52 = 28.88,
     и половинные шаги нужны, чтобы отбивки ложились на ритм строки. */
  --s1:4px;   --s2:8px;   --s3:12px;  --s4:16px;  --s5:21px;
  --s6:24px;  --s7:32px;  --s8:40px;  --s9:56px;  --s10:72px;

  --pad:21px;                /* → боковое поле на телефоне */

  /* ✎ 07.10.2026, этап 0.5. Высота нижнего меню. Единственный токен,
     который добавил этап: новых цветов, кеглей и радиусов он не вводит.

     Токен, а не число в компоненте, потому что величина работает в двух
     местах и обязана совпадать: сама полоса меню и отступ снизу у каждого
     экрана-раздела, чтобы меню не накрывало последнюю строку содержимого.
     Записанное дважды числом, оно разойдётся при первой же правке высоты —
     и разойдётся молча, обрезав ровно одну строку на одном экране.

     Безопасная зона телефона СЮДА НЕ ВХОДИТ: env(safe-area-inset-bottom)
     прибавляется в месте применения. Иначе токен стал бы вычислением,
     а в React Native env() нет вовсе — там это
     react-native-safe-area-context, см. tokens.md. */
  --tabbar:56px;

  /* ── Радиусы ─────────────────────────────────────────────────────── */
  /* → Скупая шкала. Радиус — не выразительное средство продукта:
     узнаваемость держит арка, а не скругления. */
  --r-s:6px;  --r-m:10px;  --r-l:16px;  --r-xl:22px;  --r-pill:999px;

  /* ── Арка ────────────────────────────────────────────────────────── */
  /* → Пропорция 4:5. У знака в design/icon арка 88.8×90.7, то есть 1:1.02 —
     почти полукруг без стойки. На фотографии такая форма читается кругом
     и на 32 px теряет арочность. При 1:1.25 стойка занимает половину
     высоты, и арка остаётся аркой до самых мелких миниатюр.
     Радиус задан заведомо больше половины ширины: CSS уменьшает все радиусы
     на один общий множитель, и верхние становятся ровно W/2 по обеим осям —
     настоящий полукруг при любой высоте. */
  --arch-ratio:4 / 5;
  --arch-radius:999px 999px 0 0;

  /* ── Фильтр превью ───────────────────────────────────────────────── */
  /* ↑ Только превью: обложка, миниатюры, каталог. Внутри текста — никогда.
     ✎ Десатурация отменена. Задача фильтра не «привести к одному тону»,
     а снять разнобой экспозиции между соседними кадрами.
     ✎ Контраст снижен с 1.05 до 1.015. Именно он и давал «шум» на миниатюрах:
     подъём контраста вытягивает зерно и артефакты сжатия, а на обложке
     96 px шириной они заметнее, чем на большом кадре. Понижать качество
     исходника бессмысленно — от этого артефактов станет только больше. */
  --preview-filter:saturate(.95) contrast(1.015) brightness(1.01);

  /* ── Иконки ──────────────────────────────────────────────────────── */
  /* ✎ Правка 07.10.2026, вторая. Толщин стало две.

     До первой правки иконки жили семью толщинами от 1,5 до 2,4 — их свели
     к одной, 1,75. После перехода на Ionicons выяснилось, что одна толщина
     тоже не работает, но по другой причине: 1,75 на библиотечной геометрии
     выглядит жирно рядом с границей элемента управления в один пиксель.

     Теперь так:
       --ic-stroke       1,2   обычные иконки
       --ic-stroke-bold  1,75  шеврон, галочка, крестик, ручка шторки

     Выбор — СВОЙСТВО ИКОНКИ, а не места применения, и живёт в наборе
     пометкой data-stroke="bold". Причина в форме: у этих четырёх нет
     замкнутого контура, одна-две линии, и на 1,2 они теряют уверенность —
     галочка перестаёт выглядеть поставленной, шеврон становится волоском.
     У воронки, замка, карты контур замкнут, и форму держит он.

     Обе величины в ПИКСЕЛЯХ ЭКРАНА, а не в единицах холста: иконки
     рисуются на холсте 512, а показываются от 14 до 28. Работает это
     через vector-effect:non-scaling-stroke — то же правило, по которому
     живёт линия маршрута на карте. */
  --ic-stroke:1.2px;
  --ic-stroke-bold:1.75px;

  /* ✎ --sign-stroke убран 07.10.2026. Он задавал знакам пустых состояний
     свою толщину обводки — то есть делал из иконки знак наполовину:
     толщина другая, а природа та же. Знаки стали отдельной категорией
     набора и рисуются заливкой, обводки у них нет вовсе. См. --sign-ink. */

  /* ✎ 07.10.2026. Знаки — отдельная категория графики: сплошной силуэт
     на приглушённом фоне. Замочек в заблокированном маркере, картинка
     в центре пустого экрана.

     Цвет чуть темнее поверхности, на которой знак лежит, и только.
     Это намеренно тихо: знак не сообщение, а фон настроения — читает
     человек заголовок и кнопку, а знак говорит «тут ничего» боковым
     зрением. 1,61 к поверхности в светлой теме, 1,98 в тёмной.

     ✎ В тёмной теме поднят на шаг (было 1,80): на тёмном кружке знак
     терялся. В светлой оставлен как есть — там «светлее» означало бы
     «бледнее», а замок на карте и так тихий.

     Норму SC 1.4.11 это не нарушает: заблокированный маркер — выключенный
     элемент, а выключенные стандарт из требования исключает прямо;
     пустое состояние несёт смысл текстом, а знак при нём украшение. */
  --sign-ink:#C2C2C7;

  /* ── Движение ────────────────────────────────────────────────────── */
  --ease:cubic-bezier(.2,.7,.2,1);
  --fast:160ms;  --slow:260ms;

  /* → Насколько гаснет пройденный участок линии. */
  --line-done-fade:.36;

  --pop-shadow:0 10px 30px rgba(26,26,26,.16), 0 2px 6px rgba(26,26,26,.10);
}

/* ── Тёмная тема ───────────────────────────────────────────────────────
   Вторая, не основная: гуляют днём (↑).

   ✎ Фон со сдвигом в фиолетовый: #141216 вместо нейтрального #141416.
   Решение автора. Сдвиг крошечный (зелёный −2, синий +2), и в одиночку
   его не видно — но на больших полях он читается и уводит тёмную тему
   от «просто выключенного света» в сторону акцента, который лежит
   на 335°. Светлая тема остаётся чистым белым: там фон отдаёт всё
   внимание фотографии, и любой сдвиг с ней спорит.

   Весь ряд пересчитан с тем же сдвигом, иначе поверхности и линейки
   начинают выглядеть грязными рядом с фоном. Шаги сохранены:
   фон→поверхность ΔL* 3.9, приглушённый к фону 8,75 против 8,83
   в светлой, текст к фону 15,58. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#141216;
    --text:#ECEAEE;          /* 15,58:1 */
    --surface:#1C1A1E;       /* ΔL* 3.9 от фона, как в светлой */
    --rule:#2C2A2E;
    --edge:#6A6870;          /* ✎ 3,39 к фону, 3,15 к поверхности, 4,59 к тексту */
    --sign-ink:#4C4A52;      /* ✎ 1,98 к поверхности — светлее на шаг */
    --muted:#B3B1B5;         /* 8,75:1 */
    --accent-ink:#F04A80;    /* ↑ основной даёт 3,86, этот 5,32 */
    --scheme-fill:#2E2C32;
    --me-face:#ECEAEE;       /* ✎ кольцо своей позиции: светлое в обеих темах */
    --me-dot:#141216;        /* ✎ ядро чернилами — цветом фона тёмной темы */
    --mk-face:#ECEAEE;       /* ✎ лицо маркера светлое: кольцо цветом фона
                                тонуло на тёмной карте */
    --mk-ink:#C01058;        /* 5,08 на этом лице; основной дал бы 4,03 */
    --line-case-op:0;        /* ✎ обводка линии в тёмной теме выключена:
                                тёмная подложка под тёмной картой читалась
                                грязным ореолом, а не отбивкой */
    --preview-filter:saturate(.95) contrast(1.0) brightness(.98);
    --line-done-fade:.55;
    --pop-shadow:0 10px 30px rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.4);
  }
}
:root[data-theme="dark"]{
  --bg:#141216; --text:#ECEAEE; --surface:#1C1A1E; --rule:#2C2A2E;
  --muted:#B3B1B5; --accent-ink:#F04A80; --scheme-fill:#2E2C32;
  --mk-face:#ECEAEE; --mk-ink:#C01058; --line-case-op:0;
  --me-face:#ECEAEE; --me-dot:#141216; --edge:#6A6870; --sign-ink:#4C4A52;
  --preview-filter:saturate(.95) contrast(1.0) brightness(.98);
  --line-done-fade:.55;
  --pop-shadow:0 10px 30px rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.4);
}

/* ━━━ design/system/components.css ━━━ */

/* ═══════════════════════════════════════════════════════════════════════
   «Покажи мне Питер» — компоненты. Этап 0, шаг 3.

   Всё, что переиспользуется: основа документа, типографические роли,
   блоки точки, элементы каталога, язык карты и схем, экраны-состояния.
   Величины только через токены (tokens/tokens.css) — числа в этом файле
   допустимы лишь там, где они часть формы самого компонента (толщина
   обводки маркера, пунктир утраченного контура) и объяснены рядом.

   Композиция страниц — pages/pages.css. Витрина — design/system/cards/.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══ Основа ══════════════════════════════════════════════════════════ */
*,*::before,*::after{box-sizing:border-box;}
html{-webkit-text-size-adjust:100%;}
body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:var(--sans);
  font-synthesis-weight:none;   /* синтетический жирный запрещён */
  overflow-x:hidden;
}
img{max-width:100%;}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:2px;}
@media (prefers-reduced-motion:reduce){
  *{transition-duration:1ms !important;animation-duration:1ms !important;}
}

/* ═══ Полоса набора ════════════════════════════════════════════════════ */
/* Мера 54–64 знака (↑). Стоит до блоков точки сознательно: блок со своим
   сокращённым margin (например «Обратите внимание») обязан перебивать
   margin-inline этой полосы, а не наоборот — иначе врезка уезжает
   в середину широкого экрана, где её никто не рисовал. */
.col{max-width:var(--measure);margin-inline:auto;}

/* ═══ Типографические роли ════════════════════════════════════════════ */
.t-eyebrow{
  font-family:var(--sans);font-weight:700;font-size:12.5px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--accent-ink);
}
.t-caps{
  font-family:var(--sans);font-weight:700;font-size:11.5px;
  letter-spacing:.1em;text-transform:uppercase;
}
.t-point{
  font-family:var(--sans);font-weight:800;
  font-size:clamp(34px,8.6vw,40px);line-height:1.04;letter-spacing:-.025em;
  text-wrap:balance;
}
.t-show{
  font-family:var(--sans);font-weight:800;
  font-size:clamp(32px,8.4vw,42px);line-height:.96;letter-spacing:-.035em;
  text-wrap:balance;
}
.t-meta{
  font-family:var(--sans);font-weight:500;font-size:12.5px;line-height:1.45;
  color:var(--muted);
}
/* Заголовок раздела внутри экрана — «Что нового», «Маршруты»,
   «Где проходит», «Восемь точек».
   ✘ 08.10.2026, по замечанию автора: заголовок крупнее. Было —
   .t-caps, разряженная капитель в 11,5 цветом подписи; такой
   заголовок не открывает раздел, а подписывает его сбоку.

   ✘ Живёт в компонентах, а не в композиции Главной, где начинался
   как .home-h2. Причина простая: 08.10.2026 такой же заголовок
   потребовался странице маршрута — перед картой, лентой точек
   и откликами, — и второй класс с теми же семью свойствами —
   это два заголовка, которые разойдутся на первой же правке. */
.sec-h2{
  margin:0;font-family:var(--sans);font-weight:800;
  font-size:clamp(23px,6vw,28px);line-height:1.1;letter-spacing:-.03em;
}

/* ═══ Иконки ═════════════════════════════════════════════════════════ */
/* ✎ Правка 07.10.2026. Все иконки интерфейса приходят из одного файла —
   design/system/icons/set.svg, подставляется сборщиком по имени: ICON:close.
   Паспорт набора и таблица имён — design/system/ICONS.md.

   Толщина обводки задаётся ТОЛЬКО здесь и только токеном. В разметке
   и в наборе ни одного stroke-width: иначе иконка однажды разъедется
   с остальными, и заметит это не глаз, а поиск по файлам.

   non-scaling-stroke — главное правило набора. Иконка нарисована на холсте
   24, а показывается от 12 до 44 пикселей. Без него обводка масштабируется
   вместе с холстом: одна и та же галочка весила бы 1,75 при показе в 24
   и 0,88 при показе в 12. С ним холст тянется, а обводка остаётся
   в пикселях экрана — «тот же значок крупнее» перестаёт быть
   «другой иконкой».

   Поворотом, а не второй иконкой: шеврон в наборе один и смотрит вправо. */
.ic{display:block;flex:0 0 auto;fill:none;stroke:currentColor;
    stroke-width:var(--ic-stroke);stroke-linecap:round;stroke-linejoin:round;}
/* ✎ Вторая толщина. Класс проставляет сборщик по пометке в наборе —
   руками его не пишут, иначе одна иконка окажется разной толщины
   в разных местах, с чего всё и начиналось. */
.ic-bold{stroke-width:var(--ic-stroke-bold);}
.ic *{vector-effect:non-scaling-stroke;}
.ic .ic-fill{fill:currentColor;stroke:none;}
.ic-left{transform:rotate(180deg);}
.ic-up{transform:rotate(-90deg);}
.ic-down{transform:rotate(90deg);}

/* ✎ 07.10.2026. Знак — вторая категория набора, и рисуется он иначе:
   сплошной заливкой, без обводки. Класс .sg, имена символов с s-.

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

   Старый .sign (обводка 2,4) убран: он делал из иконки знак наполовину —
   толщина другая, а природа та же. */
.sg{display:block;flex:0 0 auto;fill:var(--sign-ink);stroke:none;}

/* ✎ 07.10.2026, этап 0.5. ЗАЛИВКА КАК ИСПОЛНЕНИЕ ИКОНКИ, А НЕ КАК ЗНАК.

   Третий случай, которого в наборе не было. Знак (.sg) — тихий силуэт
   на приглушённой поверхности: он работает фоном настроения, показывается
   от 28 и красится в --sign-ink. Активный пункт меню делает ровно
   обратное — это самое громкое пятно экрана, он стоит в 24 и красится
   акцентом.

   Общее у них только одно: обе формы без обводки. Поэтому заливка —
   ИСПОЛНЕНИЕ, а не категория, и у неё свой класс. Если бы активный пункт
   нарисовали классом .sg с переопределённым fill, правило «знаки от 28»
   пришлось бы объявить неверным — а оно верное для знаков. */
.ic-solid{display:block;flex:0 0 auto;fill:currentColor;stroke:none;}
/* ✎ Тихий знак. Три места живут по одному правилу: пустое состояние,
   экран-замок и арка без кадра. Во всех трёх знак лежит на приглушённой
   поверхности, занимает много места и не является сообщением — сообщение
   рядом словами. Поэтому он гасится до цвета линеек и ставится по центру.

   Блочным элементам text-align:center не помогает, нужен margin-inline —
   на этом уже спотыкались дважды, в пустых состояниях и в экране-замке. */
.sg-quiet{fill:var(--rule);margin-inline:auto;}

/* ═══ Арочная маска ═══════════════════════════════════════════════════ */
.arch{
  aspect-ratio:var(--arch-ratio);
  border-radius:var(--arch-radius);
  overflow:hidden;background:var(--surface);display:block;
}
.arch>img{width:100%;height:100%;object-fit:cover;display:block;filter:var(--preview-filter);}

/* ✎ Правка 07.10.2026: вместо слов «нет кадра» — иконка.
   Слова в арке 26 px (ряд миниатюр в карточке маршрута) не помещались
   вовсе и гасились через font-size:0, то есть в одном месте плейсхолдер
   был текстом, а в другом пустотой. Иконка работает в обоих размерах
   и не требует перевода. Значок приглушён: это отсутствие кадра,
   а не сообщение, и внимания он забирать не должен. */
.arch-empty{
  aspect-ratio:var(--arch-ratio);
  background:var(--surface);border:1px solid var(--rule);
  border-radius:var(--arch-radius);
  display:flex;align-items:center;justify-content:center;
}
/* Знак занимает долю ширины арки, а не фиксированный размер: один и тот же
   плейсхолдер стоит и в 26 px, и в 96. Ступени размера тут не работают —
   это не иконка в строке, а заполнение площади.
   ✎ 07.10.2026: обводка заменена на заливку, как в пустых состояниях
   и экране-замке. Правило одно: знак на приглушённой поверхности —
   силуэт, а не контур. */
/* ✎ 08.10.2026, замечание автора по профилю: «незаполненный аватар
   слишком бледный и в тёмной, и в светлой теме, почти неотличим
   от фона».

   Знак здесь стоял классом .sg-quiet, то есть цветом линейки
   (--rule, 1,2 к фону). Это цвет для знака-настроения в пустом
   состоянии, где смысл несут заголовок и кнопка, а знак работает
   боковым зрением. В арке роль другая: знак ЗАМЕЩАЕТ СОДЕРЖИМОЕ,
   он сам и есть сообщение «кадра нет», и быть тише линейки ему
   не за что.

   Теперь --edge: 3,0 к фону и 3,15 к поверхности в обеих темах.
   Это тот же тон, которым нарисованы границы управления, то есть
   самая тихая величина в системе, которая ещё гарантированно видна.

   ПРАВИЛО ОДНО НА ВСЕ ПУСТЫЕ АРКИ, а не только на аватар: заметнее
   станут и семь пустых арок в ленте точек страницы маршрута,
   и миниатюры в каталоге. Разводить два цвета для одной роли —
   ровно та болезнь, от которой лечится этот этап. */
.arch-empty .sg{width:46%;height:auto;fill:var(--edge);}

/* ═══ Кнопки ══════════════════════════════════════════════════════════ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);
  min-height:48px;padding:0 var(--s6);               /* 48 — палец на ходу */
  border:0;border-radius:var(--r-pill);cursor:pointer;text-decoration:none;
  font-family:var(--sans);font-weight:700;font-size:15px;letter-spacing:-.005em;
  background:var(--accent);color:var(--on-accent);
  transition:background var(--fast) var(--ease),transform var(--fast) var(--ease);
}
.btn:active{background:var(--accent-press);transform:scale(.985);}
/* ✎ Граница тихой кнопки — --edge, а не --rule: это единственное,
   по чему кнопка опознаётся кнопкой. WCAG 2.1 SC 1.4.11, см. WCAG.md.

   ✎ 07.10.2026: вес подписи понижен с 700 до 600. На заливке 700 уместен —
   там подпись лежит на цвете и должна его перебить. На тихой кнопке
   рядом с границей в один пиксель тот же вес выглядит тяжелее самой
   кнопки: граница тонкая, а содержимое жирное, и элемент разваливается
   на две разные плотности. Утолщать границу нельзя — тихая кнопка
   перестанет быть тихой, — значит облегчать надо содержимое. */
.btn-quiet{background:transparent;color:var(--text);border:1px solid var(--edge);
           font-weight:600;}
.btn-quiet:active{background:var(--surface);transform:scale(.985);}
.btn-sm{min-height:40px;padding:0 var(--s5);font-size:13px;}
.btn-wide{width:100%;}

.icon-btn{
  flex:0 0 auto;width:36px;height:36px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  border:0;background:transparent;color:var(--text);cursor:pointer;
  transition:background var(--fast) var(--ease);
}
.icon-btn:active{background:var(--surface);}
/* ✎ 08.10.2026, замечание автора: «иконку настроек хотелось бы видеть
   с обводкой — чтобы была заметнее».

   Обводка та же, что у тихой кнопки: 1 пиксель по --edge. Это
   единственный способ сделать значок без подписи заметным, не трогая
   сам рисунок: толщину иконок задаёт токен, и утолщать её ради
   одного места нельзя — с этого в проекте когда-то и началось
   семь разных толщин. */
.icon-btn-edge{border:1px solid var(--edge);}

/* ═══ Бейджи цены ═════════════════════════════════════════════════════
   → «Бесплатно» — заливкой: это самый сильный довод первого маршрута
   и вход из поиска. Цена — чернилами: она не должна кричать громче
   бесплатного и не должна выглядеть кнопкой. */
/* ✎ Правка 07.10.2026: подпись стояла на 0,31 px выше оптического центра.
   Причина не в вёрстке, а в шрифте: align-items:center центрирует строчный
   бокс, а он резервирует место под выносные элементы вниз (у Onest подъём
   10,625 против спуска 3,375 при кегле 11). У прописных выносных нет,
   поэтому краска оказывается в верхней половине бокса.

   Поправка вдвое больше смещения: при фиксированной высоте и
   box-sizing:border-box отступ сверху ужимает содержимое с двух сторон,
   и строка едет вниз на половину отступа. Измерено, а не подобрано:
   0,62 → смещение 0,31. */
.badge-free{
  display:inline-flex;align-items:center;height:24px;padding:.62px 9px 0;
  border-radius:var(--r-pill);background:var(--accent);color:var(--on-accent);
  font-family:var(--sans);font-weight:700;font-size:11px;
  letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;
}
.badge-price{
  font-family:var(--sans);font-weight:700;font-size:16px;letter-spacing:-.01em;
  color:var(--text);white-space:nowrap;
}

/* ═══ Верхняя панель чтения ══════════════════════════════════════════ */
.topbar{position:sticky;top:0;z-index:20;background:var(--bg);border-bottom:1px solid var(--rule);}
.topbar-track{height:3px;background:var(--rule);}
.topbar-fill{height:100%;width:0;background:var(--accent);}
.topbar-row{
  display:flex;align-items:center;gap:var(--s3);
  max-width:720px;margin:0 auto;padding:6px var(--pad);
}
.topbar-name{
  flex:1 1 auto;min-width:0;
  font-family:var(--sans);font-weight:600;font-size:13px;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  background:none;border:0;padding:10px 0;text-align:left;cursor:pointer;
}
.topbar .icon-btn{margin-right:-8px;}

/* ═══ Шапка точки ════════════════════════════════════════════════════ */
.point-head{padding-top:var(--s8);}
.point-head .t-eyebrow{display:block;margin-bottom:var(--s3);}
.point-head .t-point{margin:0;}

/* ═══ Блоки точки ════════════════════════════════════════════════════ */
/* Блок 3 «Обратите внимание».
   → ОТКЛОНЕНИЕ ОТ ШКАЛЫ: 17 px основным цветом вместо 15 px приглушённым.
   Это первая строка, которую человек ищет глазами на улице. */
.look{margin:var(--s6) 0 var(--s7);padding-left:var(--s4);border-left:2px solid var(--accent);}
.look p{
  margin:0;
  font-family:var(--serif);font-style:italic;font-weight:400;
  font-size:17px;line-height:1.45;color:var(--text);
}

.body{position:relative;}      /* держит всплывашку пояснения */
.body p{font-family:var(--serif);font-weight:400;font-size:19px;line-height:1.52;margin:0 0 var(--s6);}
.body p:last-child{margin-bottom:0;}
.body strong{font-weight:700;}      /* настоящая Literata 700, не синтетика */

.body ul{margin:0 0 var(--s6);padding:0;list-style:none;}
.body ul li{
  font-family:var(--serif);font-size:19px;line-height:1.5;
  padding-left:var(--s5);position:relative;margin-bottom:10px;
}
.body ul li::before{
  content:"";position:absolute;left:0;top:.62em;
  width:7px;height:7px;border-radius:50%;background:var(--accent);
}

/* ── Фото во всю ширину (блок 5). Без фильтра и без кадрирования (↑) ──── */
.fig{margin:var(--s8) 0;}
.fig img{display:block;width:100%;height:auto;}
/* ✎ ПОДПИСЬ ПО ЦЕНТРУ — решение автора 13.10.2026. Кадр идёт во всю
   ширину полосы, а подпись короче строки текста: прижатая влево,
   она читалась обрывком абзаца, а не частью кадра. */
.fig figcaption{
  font-family:var(--sans);font-weight:500;font-size:12.5px;line-height:1.45;
  color:var(--muted);margin-top:10px;max-width:var(--measure);margin-inline:auto;
  text-align:center;
}

/* ── Схема (блок 6) ─────────────────────────────────────────────────────
   Схема рисуется в системе координат, равной пикселям телефона: viewBox
   шириной 390. Иначе подпись в 13 единиц холста 640 превращается
   на экране в 8 пикселей, и схема не читается даже в тени. */
.scheme{display:block;width:100%;height:auto;}
/* ✎ Контур по краю заливки. Сама заливка даёт к фону 1,32 — норма
   SC 1.4.11 требует 3,0, и затемнять её нельзя: схема станет грязной
   и перестанет быть чертежом. Контур решает то же другим способом —
   граница воды и двора читается, а поле остаётся светлым. */
.scheme .s-fill{fill:var(--scheme-fill);stroke:var(--edge);stroke-width:1;
                vector-effect:non-scaling-stroke;}                   /* вода, двор, газон */
.scheme .s-mass{fill:var(--bg);stroke:var(--text);stroke-width:2;}   /* здание, объём */
.scheme .s-lost{stroke:var(--text);stroke-width:1.5;stroke-dasharray:7 6;fill:none;opacity:.5;}
.scheme .s-lead{stroke:var(--text);stroke-width:1;opacity:.4;}
/* Черта правила: предел по закону, граница зоны. Отличается от утраченного
   и цветом, и частотой пунктира — это не «здесь что-то было», а «выше
   нельзя». Акцент тут уместен: черта и есть предмет рассказа. */
.scheme .s-limit{stroke:var(--accent);stroke-width:1.5;stroke-dasharray:3 5;fill:none;}
/* ✎ Метка места. Тонкие кольца терялись среди линий чертежа, поэтому
   метка стала плотной: ореол, белое кольцо и залитая сердцевина — тот же
   приём, что у маркера на карте. На схеме это единственное цветное пятно,
   и оно обязано находиться взглядом сразу. */
.scheme .s-pin-halo{fill:var(--accent);opacity:.18;}
.scheme .s-pin-face{fill:var(--bg);}
.scheme .s-pin{fill:var(--accent);}
.scheme .s-pin-ring{fill:none;stroke:var(--accent);stroke-width:2;}
.scheme .s-lbl{font-family:var(--sans);font-weight:500;font-size:13px;fill:var(--muted);}
.scheme .s-lbl-key{font-family:var(--sans);font-weight:700;font-size:13px;fill:var(--accent-ink);}

/* ── Пояснение термина ──────────────────────────────────────────────────
   Подчёркивания нет — слово остаётся обычным текстом, опознаёт его только
   значок. Объяснение всплывает поверх текста ниже, а не раздвигает абзац:
   строка, которую читаешь, не должна уезжать из-под пальца. */
.term{font:inherit;color:inherit;background:none;border:0;padding:0;margin:0;
      cursor:pointer;text-decoration:none;}
/* ✎ Поправка центрирования. Отступ у самого кружка не годится: внутри него
   живут два разных знака, «?» и «×», и краска у них лежит по-разному.
   Вопросительный знак — это чаша и оторванная точка, его краска кончается
   на базовой линии. Крестик симметричен относительно середины строчных
   и уже стоит по центру. Общий отступ двигает оба и ломает тот, который
   был верным, — поэтому правится только «?», и правится он сам.

   Значение выбрано лесенкой: 0 · 0,8 · 1,6 · 2,4 · 3,2 отрисованы рядом
   при девятикратном увеличении с чертой по центру кружка. Ноль даёт знак
   заметно выше черты, 1,6 — ниже; верное между ними. */
.term-mark{
  display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;margin-left:3px;border-radius:50%;
  background:var(--accent);color:var(--on-accent);
  vertical-align:.26em;
  font-family:var(--sans);font-weight:700;font-size:12px;line-height:1;
  transition:background var(--fast) var(--ease);
}
.term-mark .m-open{position:relative;top:.6px;}   /* ✎ только «?», см. выше */
.term-mark .m-close{display:none;font-size:14px;line-height:1;}  /* ✎ крестик крупнее */
.term[aria-expanded="true"] .term-mark{background:var(--accent-press);}
.term[aria-expanded="true"] .term-mark .m-open{display:none;}
.term[aria-expanded="true"] .term-mark .m-close{display:block;}

.term-pop{
  position:absolute;left:0;right:0;z-index:5;
  padding:var(--s4) var(--s5);
  background:var(--surface);border:1px solid var(--rule);
  border-left:2px solid var(--accent);border-radius:var(--r-m);
  box-shadow:var(--pop-shadow);
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.5;color:var(--text);
}
.term-pop[hidden]{display:none;}

/* ── Врезка-факт (блок 9). Одна на точку ────────────────────────────── */
.inset{margin:var(--s8) 0;padding:var(--s6) var(--s5);background:var(--surface);border-radius:var(--r-s);}
/* ✎ Добавлен h3. На странице маршрута тот же блок лежит
   на уровень глубже: там h1 — название маршрута, h2 — заголовок
   точки, и врезка обязана быть h3. Порядок заголовков —
   навигация для скринридера, и пропуск уровня ей мешает
   сильнее, чем два селектора мешают нам. */
.inset h2,.inset h3{margin:0 0 var(--s3);color:var(--text);}
.inset p{
  font-family:var(--serif);font-weight:400;font-size:16.5px;line-height:1.5;
  margin:0 0 var(--s3);max-width:var(--measure);
}
.inset p:last-child{margin-bottom:0;}

/* ── «Что здесь сегодня» (блок 8 «Сейчас здесь» в формате) ───────────────
   Типографика та же, что у ядра — это тот же рассказ. Отличает его подпись:
   правило двух времён и есть структурное преимущество продукта над
   аудиогидом, и человеку стоит видеть, что настоящее здесь описано.
   ✎ Название на экране отличается от названия блока в формате. В формате
   он «Сейчас здесь» — это имя для автора; читателю такая формулировка
   звучит обрубком. Дата проверки с экрана убрана, она нужна в админке. */
.now{margin:var(--s8) auto 0;}
.now-head{padding-bottom:var(--s3);margin-bottom:var(--s5);border-bottom:1px solid var(--rule);}
.now-label{color:var(--text);}

/* ── «Подробнее» (блок 11) ──────────────────────────────────────────────
   Рамки нет: внутри блока такой же рассказ, как снаружи, и боковые отступы
   рамки ужимали строку — мера съезжала с 54–64 знаков. */
.more{margin:var(--s8) auto;max-width:var(--measure);
      border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);}
.more[open]{border-bottom:0;}
.more>summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:center;gap:var(--s3);
  min-height:56px;padding:var(--s3) 0;
  font-family:var(--sans);font-weight:700;font-size:15px;color:var(--accent-ink);
}
.more>summary::-webkit-details-marker{display:none;}
.more-chev{flex:0 0 auto;color:var(--accent);transition:transform var(--fast) var(--ease);}
.more[open] .more-chev{transform:rotate(90deg);}
.more-body{padding:0 0 var(--s6);}
.more-body p{font-family:var(--serif);font-size:19px;line-height:1.52;margin:0 0 var(--s6);}
.more-body p:last-child{margin-bottom:0;}
/* Боковые поля не трогаем: раз у «Подробнее» нет рамки, фото внутри него
   ведёт себя ровно так же, как фото в основном потоке. */
.more-body .fig{margin-top:var(--s7);margin-bottom:var(--s7);}

/* ── Конец точки ────────────────────────────────────────────────────────
   Блок «Далее» убран целиком, вместе со строкой формата: между точками
   ведёт карта, и указатель следующей точки был её дубликатом. */
.endbar{max-width:var(--measure);margin:var(--s9) auto 0;padding-top:var(--s7);border-top:1px solid var(--rule);}
/* Подводка к действию в конце страницы маршрута: одна фраза
   о том, что дальше. На карточке точки её нет: там человек
   уже идёт по маршруту и уговаривать его не надо. */
.endbar-lead{
  margin:0 0 var(--s5);
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.55;
  color:var(--muted);
}

/* ── Источники (блок 12) ────────────────────────────────────────────────
   Атрибуция текстом: офлайн ссылка некликабельна (↑). */
.sources{max-width:var(--measure);margin:var(--s8) auto 0;padding-top:var(--s6);border-top:1px solid var(--rule);}
.sources h2{color:var(--muted);margin:0 0 var(--s3);}
.sources h2:not(:first-child){margin-top:var(--s6);}
.sources ul{margin:0;padding:0;list-style:none;}
.sources li{
  font-family:var(--sans);font-weight:500;font-size:12.5px;line-height:1.55;
  color:var(--muted);margin-bottom:7px;padding-left:var(--s3);position:relative;
}
.sources li::before{content:"—";position:absolute;left:0;}

/* Сообщить о неточности. Стоит последним и держится тихо: пользуются им
   редко, но именно он делает обещание актуальности выполнимым. */
.report{max-width:var(--measure);margin:var(--s7) auto 0;display:flex;justify-content:center;}

/* ═══ Было/стало ══════════════════════════════════════════════════════ */
/* Блок 7 формата. Слайдер на два кадра одного места.

   Год обязателен у обоих, включая современный: без года «стало» через
   пять лет само превращается в «было», и пара начинает врать.

   Управление — обычный ползунок, растянутый поверх кадра и сделанный
   невидимым. Так оно работает пальцем, мышью и с клавиатуры разом,
   и не приходится писать своё перетаскивание. В React Native такого
   элемента нет — там PanResponder поверх двух картинок.

   Шторка белая (цветом фона), а не акцентная: акцент на маршруте
   и на маркерах, и на фотографии он читался бы как часть снимка. */
.ba{position:relative;margin:var(--s8) 0;overflow:hidden;--ba:50%;}
.ba-pair{position:relative;display:block;}
.ba-pair img{display:block;width:100%;height:auto;}

/* «Стало» лежит поверх «было» и обрезается слева: ползунок вправо —
   больше прошлого, влево — больше настоящего. */
.ba-after{position:absolute;inset:0;clip-path:inset(0 0 0 var(--ba));}
.ba-after img{width:100%;height:100%;object-fit:cover;}

.ba-line{
  position:absolute;top:0;bottom:0;left:var(--ba);width:2px;
  margin-left:-1px;background:var(--bg);pointer-events:none;
}
.ba-grip{
  position:absolute;top:50%;left:var(--ba);
  width:40px;height:40px;margin:-20px 0 0 -20px;border-radius:50%;
  background:var(--bg);color:var(--text);box-shadow:var(--pop-shadow);
  display:flex;align-items:center;justify-content:center;pointer-events:none;
}
/* Ползунок невидим, но занимает весь кадр: цель для пальца — вся
   фотография, а не сорокапиксельный кружок. */
.ba-range{
  position:absolute;inset:0;width:100%;height:100%;margin:0;
  opacity:0;cursor:ew-resize;
}
.ba-year{
  position:absolute;bottom:var(--s3);padding:3px 9px;
  border-radius:var(--r-pill);background:var(--bg);opacity:.92;
  font-family:var(--sans);font-weight:700;font-size:12px;color:var(--text);
}
.ba-year.is-was{left:var(--s3);}
.ba-year.is-now{right:var(--s3);}

/* ═══ Переключатель «списком / на карте» ═════════════════════════════ */
/* ✎ Переключатель «списком / на карте» — только иконки, как в рефе.
   Подписи словами занимали треть строки заголовка и ничего не добавляли.
   ✎ Это одна кнопка, а не две: состояний всего два, и переключать их
   нажатием в любое место привычнее, чем целиться в неактивную половину. */
/* ✎ 09.10.2026, приёмка с телефона: «свитч список-карта — подложку
   поярче». На улице его почти не было видно, и причина оказалась
   не в оттенке, а в том, что у переключателя НЕ БЫЛО ГРАНИЦЫ ВООБЩЕ.
   Подложка --surface отстоит от фона на 1,08 — это отбивка для крупного
   поля вроде футера, а не для кнопки в 46 пикселей.

   Поднимать саму заливку некуда: следующая ступень, --rule, даёт 1,2
   и на солнце тоже пропадёт, а более тёмная превратила бы дорожку
   в пятно рядом с активной половиной, которая и так набрана чернилами.

   Поэтому граница, а не заливка: 1 пиксель по --edge, 3,0 к фону
   в обеих темах. Это не выдумка под один случай, а правило системы,
   которое сюда просто забыли применить: по нему же нарисованы рамка
   флажка и тихая кнопка (WCAG 1.4.11, см. WCAG.md). Переключатель —
   такой же элемент управления, и граница ему положена по тому же
   пункту.

   Высота выросла с 44 до 46 на толщину границы. Цель для пальца
   от этого только лучше. */
.viewswitch{
  display:inline-flex;align-items:center;gap:2px;
  padding:3px;border:1px solid var(--edge);cursor:pointer;
  background:var(--surface);border-radius:var(--r-pill);flex:0 0 auto;
}
.viewswitch span{
  width:38px;height:38px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--muted);
  transition:background var(--fast) var(--ease),color var(--fast) var(--ease);
}
.viewswitch[aria-pressed="false"] .vs-list,
.viewswitch[aria-pressed="true"]  .vs-map{background:var(--text);color:var(--bg);}

/* ═══ Инструменты выдачи: сортировка и фильтры ═══════════════════════ */
.tool{
  display:inline-flex;align-items:center;gap:8px;
  min-height:44px;padding:0 2px;border:0;background:none;cursor:pointer;
  font-family:var(--sans);font-weight:600;font-size:14px;color:var(--text);
}
.tool svg{color:var(--accent);flex:0 0 auto;}
.tool-count{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:20px;height:20px;padding:0 6px;border-radius:var(--r-pill);
  background:var(--accent);color:var(--on-accent);font-size:11.5px;font-weight:700;
}
.tool-count[hidden]{display:none;}

/* ═══ Меню сортировки ════════════════════════════════════════════════ */
/* ✎ Сортировка раскрывается от самого поля, как обычный селект, а не
   всплывает снизу экрана: вариантов мало, и уводить ради них весь экран
   незачем. Фильтров много — они уходят на отдельную страницу. */
.sortwrap{position:relative;}
.sortmenu{
  position:absolute;top:calc(100% + 6px);left:0;z-index:30;min-width:210px;
  padding:var(--s2);border-radius:var(--r-m);
  background:var(--bg);border:1px solid var(--rule);box-shadow:var(--pop-shadow);
}
.sortmenu[hidden]{display:none;}
.sortmenu button{
  display:flex;align-items:center;gap:var(--s3);width:100%;
  min-height:44px;padding:0 var(--s3);border:0;border-radius:var(--r-s);
  background:none;cursor:pointer;text-align:left;
  font-family:var(--sans);font-weight:500;font-size:14px;color:var(--text);
}
/* ✎ Выбранный вариант виден не галочкой, а целиком: подложка, насыщенность
   и цвет. Галочка одна вытягивала на себя слишком мало. */
.sortmenu button[aria-pressed="true"]{
  font-weight:700;color:var(--accent-ink);background:var(--surface);
}
.sortmenu button .tick{margin-left:auto;color:var(--accent);opacity:0;}
.sortmenu button[aria-pressed="true"] .tick{opacity:1;}

/* ═══ Тег фильтра ════════════════════════════════════════════════════ */
.tag{
  flex:0 0 auto;
  display:inline-flex;align-items:center;gap:7px;
  min-height:42px;padding:0 var(--s5);
  border:1px solid var(--edge);border-radius:var(--r-pill);   /* ✎ WCAG 1.4.11 */
  background:transparent;color:var(--text);cursor:pointer;
  /* ✎ 600 → 500 по той же причине, что у тихой кнопки: граница в пиксель
     и жирная подпись живут в разных плотностях. Выбранный чип остаётся
     плотнее — у него заливка, и вес там работает на неё, см. ниже. */
  font-family:var(--sans);font-weight:500;font-size:14px;white-space:nowrap;
  transition:background var(--fast) var(--ease),border-color var(--fast) var(--ease),
             color var(--fast) var(--ease);
}
.tag[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);
                           color:var(--on-accent);font-weight:600;}

/* ═══ Поле ввода ═════════════════════════════════════════════════════ */
/* ✎ Добавлено на этапе 3. До него в продукте не было ни одного поля
   ввода: человек читает, а не пишет. Первое появилось на странице
   «Сообщить о неточности» — единственном месте, где человек говорит
   с автором. Этап 4 возьмёт этот же компонент для своего сообщения
   с точки, этап 5 — для почты при оплате.

   Границей по --edge, а не заливкой: правило системы для элементов
   управления (WCAG 1.4.11), то же, по которому нарисованы тихая
   кнопка, рамка флажка и переключатель вида.

   Кегль 16, а не 14: в поле ввода на телефоне меньшее значит
   принудительное увеличение страницы при касании — браузеры мобильных
   делают это сами, и вернуть масштаб человек уже не может.

   Шрифт интерфейсный: это не рассказ, это разговор. */
.field{display:block;}
.field>.t-caps{display:block;margin-bottom:var(--s3);color:var(--muted);}
.field textarea,
.field input[type="text"],
.field input[type="email"]{
  display:block;width:100%;box-sizing:border-box;
  min-height:48px;padding:var(--s3) var(--s4);
  border:1px solid var(--edge);border-radius:var(--r-m);
  background:var(--bg);color:var(--text);
  font-family:var(--sans);font-weight:500;font-size:16px;line-height:1.5;
  resize:vertical;
}
.field textarea::placeholder,
.field input::placeholder{color:var(--muted);}
.field textarea:focus-visible,
.field input:focus-visible{border-color:var(--accent);}
.field+.btn{margin-top:var(--s5);}

/* ═══ Фильтры и сортировка формой ════════════════════════════════════ */
/* ✎ Добавлено на этапе 3, вместе с публичными страницами.

   В макете каталога флажки, теги и сортировка — кнопки с aria-pressed,
   и состояние им ставил скрипт страницы: макет ничего не фильтровал,
   он показывал вид.

   НА САЙТЕ ФИЛЬТР ОБЯЗАН РАБОТАТЬ БЕЗ СКРИПТА. Две причины, и первая
   важнее: страницу читает поисковик, а это главный канал притока
   продукта. Вторая — человек с одной полоской сети на улице: разметка
   доезжает, скрипт может и не доехать.

   Поэтому на сайте те же .check и .tag стоят МЕТКАМИ с настоящим
   флажком внутри, выдача приходит с сервера, а сортировка — обычные
   ссылки. Вид от этого не меняется: правила ниже повторяют ровно те,
   что выше стоят у aria-pressed, и ни одной своей величины не вводят.

   Скрипт на странице всё равно есть — он открывает лист фильтров
   и считает выбранное, — но без него страница остаётся рабочей:
   лист фильтров тогда просто виден как часть страницы. */

/* Сам флажок не виден, но существует: его читает экранный диктор,
   он ловит фокус и он же несёт значение в адрес страницы.
   display:none убрал бы и то, и другое. */
.check>input,
.tag>input{
  position:absolute;width:1px;height:1px;opacity:0;
  margin:0;pointer-events:none;
}
/* Метка ведёт себя как кнопка: вся строка — цель для пальца. */
label.check,label.tag{position:relative;}
/* Обводка фокуса приезжает на метку, а не на невидимый флажок. */
.check:has(>input:focus-visible),
.tag:has(>input:focus-visible){
  outline:2px solid var(--accent);outline-offset:3px;border-radius:2px;
}

.check:has(>input:checked){font-weight:700;}
.check:has(>input:checked) .check-box{
  background:var(--accent);border-color:var(--accent);color:var(--on-accent);
}
.check:has(>input:checked) .check-box.radio{
  background:transparent;border-color:var(--accent);border-width:7px;color:transparent;
}
.tag:has(>input:checked){
  background:var(--accent);border-color:var(--accent);
  color:var(--on-accent);font-weight:600;
}

/* Сортировка ссылками: вариантов четыре, каждый — свой адрес выдачи.
   Правила те же, что у кнопок меню, просто приписаны и ссылке. */
.sortmenu a{
  display:flex;align-items:center;gap:var(--s3);width:100%;
  min-height:44px;padding:0 var(--s3);border:0;border-radius:var(--r-s);
  background:none;cursor:pointer;text-align:left;text-decoration:none;
  font-family:var(--sans);font-weight:500;font-size:14px;color:var(--text);
}
.sortmenu a[aria-current="true"]{
  font-weight:700;color:var(--accent-ink);background:var(--surface);
}
.sortmenu a .tick{margin-left:auto;color:var(--accent);opacity:0;}
.sortmenu a[aria-current="true"] .tick{opacity:1;}

/* Быстрый фильтр и переключатель «списком / на карте» на сайте —
   ссылки, а не кнопки: это разные адреса выдачи, и человек должен
   попадать на них из поиска и из закладки. Подчёркивание снимается:
   выглядят они кнопками, и ссылочная черта под словом ломала бы
   и форму, и ритм строки. */
a.tag{text-decoration:none;}
a.viewswitch{text-decoration:none;}
/* Имя маршрута в панели чтения на сайте — ссылка на маршрут (в макете
   это была кнопка «к началу точки»: там точка открывалась поверх карты,
   и уходить из неё было некуда). Вид остаётся прежним: подчёркивание
   в липкой панели читается как опечатка, а не как ссылка. */
a.topbar-name{text-decoration:none;}

/* ═══ Карточка маршрута ══════════════════════════════════════════════ */

/* ── Карточка маршрута ──────────────────────────────────────────────────
   Строкой, а не плиткой: названия маршрутов длинные, в двухколоночной
   сетке «Канал, которого не должно было быть» разбивается на пять строк.
   Вся карточка — одна ссылка.
   ✎ Характеристики разведены точками и не переносятся: район ушёл
   из этой строки вниз, к цене, и получил свой значок. Правый нижний
   угол карточки перестал пустовать. */
.rcard{
  display:flex;gap:var(--s4);align-items:flex-start;
  padding:var(--s5) 0;border-bottom:1px solid var(--rule);
  color:inherit;text-decoration:none;
}
.rcard:first-of-type{border-top:1px solid var(--rule);}
.rcard-cover{flex:0 0 auto;width:96px;}
.rcard-cover .arch{width:100%;}
.rcard-body{flex:1 1 auto;min-width:0;padding-top:2px;
            display:flex;flex-direction:column;gap:var(--s2);}
.rcard-title{
  font-family:var(--sans);font-weight:800;font-size:19px;letter-spacing:-.022em;
  line-height:1.15;margin:0;text-wrap:balance;
}
/* ✎ 07.10.2026, этап 0.5. К селектору добавлен .rhead-meta.
   Строка метаданных точками-разделителями — роль, а не частность
   карточки каталога: та же строка стоит в шапке страницы
   маршрута. Человек видел её в списке, и узнавание экономит
   ему чтение — значит правило одно, а не два похожих. */
.rcard-meta,.rhead-meta{
  display:flex;align-items:center;flex-wrap:wrap;
  font-family:var(--sans);font-weight:500;font-size:12.5px;color:var(--muted);
}
.rcard-meta span+span::before,.rhead-meta span+span::before{
  content:"·";display:inline-block;width:14px;text-align:center;
  color:var(--muted);opacity:.5;font-weight:700;
}
.rcard-foot{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
            margin-top:auto;padding-top:var(--s1);}
.rcard-place{
  display:inline-flex;align-items:center;gap:5px;min-width:0;
  font-family:var(--sans);font-weight:600;font-size:12.5px;color:var(--muted);
}
.rcard-place svg{flex:0 0 auto;opacity:.75;}
.rcard-place span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

/* ═══ Миниатюры точек в карточке маршрута ═════════════════════════════ */
/* Вариант карточки: под характеристиками ряд арок-миниатюр точек.
   Отвечает на вопрос, который цифра «8 точек» не закрывает, — а что
   именно я увижу. Арка в 26 пикселей — предел, при котором она ещё
   читается аркой, а не кружком; ниже начинает врать форма.

   Миниатюры проходят фильтр превью, значит годятся только кадры
   с пометкой «обработка разрешена». У точки без такого кадра показывается
   пустая арка — ряд не должен рассыпаться из-за одной дырки. */
.rcard-points{display:flex;align-items:center;gap:6px;margin-top:2px;}
.rcard-points .arch,
.rcard-points .arch-empty{flex:0 0 auto;width:26px;}
.rcard-points-more{
  font-family:var(--sans);font-weight:600;font-size:11.5px;color:var(--muted);
  white-space:nowrap;
}

/* ═══ Пустое состояние ═══════════════════════════════════════════════ */
/* ── Пустое состояние каталога ───────────────────────────────────────── */
.empty{margin:var(--s7) 0 0;padding:var(--s8) var(--s6);
       background:var(--surface);border-radius:var(--r-m);text-align:center;}
/* ✎ 07.10.2026. Три правки разом.
   1. По центру. Знак — блочный элемент, а text-align:center блочные
      не двигает: знак стоял слева, и это было видно.
   2. Крупнее: 56 → 88. Знак в пустом состоянии не значок, а фон
      настроения — он должен читаться боковым зрением, пока глаз
      ищет заголовок и кнопку.
   3. Тише. Цвет --sign-ink рассчитан на серый кружок маркера; на поле
      пустого состояния того же тона знак в 88 пикселей становится
      пятном и спорит с заголовком. Здесь он приглушается до --rule. */
.empty-mark{margin:0 auto var(--s5);line-height:0;}
.empty-title{font-family:var(--sans);font-weight:800;font-size:21px;letter-spacing:-.02em;margin:0 0 var(--s3);}
.empty-text{
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.55;
  color:var(--muted);margin:0 auto;max-width:42ch;
}
/* ✎ 07.10.2026, этап 0.5. Отступ кнопки под текстом переехал сюда
   из внутреннего стиля разметки. Значение то же, вид не меняется;
   но теперь пустых состояний с кнопкой стало четыре, и число,
   записанное в разметке четыре раза, расходится с пятого. */
.empty .btn{margin-top:var(--s5);}

/* ═══ Карточка маршрута в ленте на карте ═════════════════════════════ */
.cat-map-card{
  /* min-width:0 обязателен: без него автоминимум флекс-элемента поднимает
     карточку до ширины содержимого, и лента перестаёт быть лентой.
     ✎ Карточка подросла, название переносится на две строки, цена ушла
     под него. Район с карточки убран: мы и так на карте. */
  flex:0 0 92%;min-width:0;scroll-snap-align:center;
  display:flex;gap:var(--s3);align-items:stretch;
  padding:var(--s3);border-radius:var(--r-l);
  background:var(--bg);border:1px solid var(--rule);box-shadow:var(--pop-shadow);
  color:inherit;text-decoration:none;
}
.cat-map-card .arch{flex:0 0 auto;width:58px;align-self:center;}
.cat-map-card-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:5px;}
.cat-map-card-foot{display:flex;align-items:center;justify-content:space-between;
                   gap:var(--s3);margin-top:auto;}
.cat-map-card-foot .t-meta{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.cat-map-title{font-family:var(--sans);font-weight:700;font-size:15px;
               letter-spacing:-.015em;line-height:1.18;
               display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
               overflow:hidden;}

/* ═══ Маршрут на обзорной карте ══════════════════════════════════════ */
/* ✎ У маршрута на обзорной карте появились точки — простые кружки.
   Без них линия читается как «куда-то ведёт», но не как «столько остановок». */
.route-dot{fill:var(--mk-face);stroke:var(--accent);stroke-width:2;
           vector-effect:non-scaling-stroke;}

/* ✎ Правка 07.10.2026. Решение автора: когда маршрут выбран, на карте
   виден только он. Приглушение отменено.

   Было: невыбранные оставались на карте с прозрачностью 0,28. Довод был
   «пусть видно, что ещё рядом». На деле четыре полупрозрачные линии вокруг
   выбранной превращают карту в паутину, в которой выбранный перестаёт
   быть выбранным, — а вопрос «что ещё рядом» карта и так закрывает,
   пока ни один маршрут не выбран. */
.route{transition:opacity var(--slow) var(--ease);}
.route[data-active="false"]{opacity:0;pointer-events:none;}

/* ✎ Камера карты. Переезд сделан переходом CSS у группы, а не анимацией
   viewBox через requestAnimationFrame: кадры не выдаются, когда элемент
   вне экрана или вкладка притушена, и карта тогда молча оставалась
   на прежнем маршруте. Переход CSS доводит состояние до конца всегда. */
.camera{transform-box:view-box;transform-origin:0 0;
        transition:transform var(--slow) var(--ease);}
@media (prefers-reduced-motion:reduce){.camera{transition:none;}}

/* ═══ Экран-замок ═════════════════════════════════════════════════════ */
/* «Купить, чтобы читать дальше». Текст первой точки обрывается, и дальше
   стоит эта панель.

   Затухания текста градиентом нет сознательно: приём показывает размытые
   строки, которые человек всё равно пытается прочесть, и выглядит
   как подначка. Обрыв честнее — видно, где кончилось бесплатное.

   Акцентного текста на панели нет: она лежит на поверхности, а мелкий
   акцент на поверхности запрещён по контрасту. Акцент здесь один — кнопка. */
.lock{
  margin:var(--s8) 0 0;padding:var(--s8) var(--s5);
  background:var(--surface);border-radius:var(--r-m);text-align:center;
}
/* ✎ 07.10.2026: знак заливкой и по центру, как в пустых состояниях.
   Было обводкой и слева — слева потому, что знак блочный, а text-align
   блочные не двигает; та же ошибка, что нашлась в пустых состояниях.
   Обводкой — потому что экран-замок собирали до того, как у знаков
   появилась своя категория. */
.lock-ico{margin:0 auto var(--s5);line-height:0;}
.lock-title{
  margin:0 0 var(--s3);
  font-family:var(--sans);font-weight:800;font-size:21px;letter-spacing:-.02em;
}
.lock-text{
  margin:0 auto var(--s6);max-width:42ch;
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.55;
  color:var(--muted);
}
.lock-note{
  margin:var(--s4) 0 0;
  font-family:var(--sans);font-weight:500;font-size:12.5px;color:var(--muted);
}

/* ═══ Маршрут пройден ═════════════════════════════════════════════════ */
/* Экран в конце прогулки. Ни бейджей, ни очков, ни «поделиться» —
   геймификации в продукте нет. Единственное, что здесь есть, —
   сколько человек прошёл и куда идти дальше. */
.done{text-align:center;padding:var(--s9) 0 var(--s8);}
.done .arch{width:120px;margin:0 auto var(--s6);}
.done-title{
  margin:0 0 var(--s3);
  font-family:var(--sans);font-weight:800;
  font-size:clamp(28px,7.4vw,34px);line-height:1.04;letter-spacing:-.03em;
  text-wrap:balance;
}
.done-text{
  margin:0 auto;max-width:44ch;
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.55;
  color:var(--muted);
}
.done-stats{
  display:flex;justify-content:center;gap:var(--s8);
  margin:var(--s7) 0;padding:var(--s5) 0;
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
}
.done-n{
  display:block;
  font-family:var(--sans);font-weight:800;font-size:21px;letter-spacing:-.02em;
}
.done-lbl{
  display:block;margin-top:3px;
  font-family:var(--sans);font-weight:500;font-size:12.5px;color:var(--muted);
}

/* ═══ Экран фильтров ═════════════════════════════════════════════════ */
/* ── Экран-лист: `.sheet` ───────────────────────────────────────────────
   ✎ Было нижнее окно, стало на всю страницу. Групп четыре, значений
   полтора десятка — в нижнем окне это превращается в колодец, по которому
   скроллишь вслепую. На всю страницу видно сразу, где находишься.

   ✎ ЭТАП 3: КЛАСС ПЕРЕИМЕНОВАН ИЗ `.filters`. Он оказался не фильтрами,
   а экраном-листом: носителей три — фильтры каталога, настройки и экран
   удаления аккаунта, — и имя по первому из них сбивало с толку каждого,
   кто открывал второй. Долг записан в этапе 0.5, отложен дважды
   и закрыт здесь, где экраны собираются заново.

   ЛИСТ МОЖЕТ ЛЕЖАТЬ ПОВЕРХ ЛИСТА: настройки открываются с профиля,
   экран удаления — из настроек. Кто выше, говорит `z-index`, а не
   порядок в разметке, и Escape закрывает только верхний. В React Native
   это стек модальных экранов, а не два булевых флага. */
.sheet{
  position:fixed;inset:0;z-index:40;display:flex;flex-direction:column;
  background:var(--bg);
}
.sheet[hidden]{display:none;}
.sheet-head{
  display:flex;align-items:center;gap:var(--s3);
  padding:var(--s3) var(--pad);border-bottom:1px solid var(--rule);
}
.sheet-head h2{flex:1 1 auto;margin:0;font-family:var(--sans);font-weight:800;
                 font-size:21px;letter-spacing:-.025em;}
/* Сбросить — без обводки, акцентом, в правом верхнем углу */
.filters-reset{
  flex:0 0 auto;border:0;background:none;cursor:pointer;padding:10px 0 10px var(--s3);
  font-family:var(--sans);font-weight:700;font-size:14px;color:var(--accent-ink);
}
.sheet-body{flex:1 1 auto;overflow-y:auto;padding:var(--s6) var(--pad) var(--s8);}
.fgroup+.fgroup{margin-top:var(--s8);}
.fgroup h3{margin:0 0 var(--s3);color:var(--muted);}

/* ── Группа кружков без линеек ───────────────────────────────────────
   ✎ 08.10.2026, замечание автора по настройкам: «виды темы,
   разделённые дивайдером, выглядят как разные настройки. Подумать
   об ином способе разделения. Дивайдером разделять разные виды
   настроек».

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

   Поэтому здесь линеек нет, а вместо них поверхность: три кружка
   лежат на одной плитке, то есть видимо принадлежат друг другу.
   Разделяет группы по-прежнему линейка — но между группами,
   а не внутри них.

   Геометрия строки не трогается: те же 52 пикселя цели, что
   у флажка фильтра. Меняется только то, чем строки отделены
   друг от друга, — и внутри плитки они не отделены ничем. */
.fgroup-boxed{
  background:var(--surface);border-radius:var(--r-m);
  padding:0 var(--s4);
}
.fgroup-boxed .check{border-bottom:0;}

/* ТОТ ЖЕ ВОПРОС СТОИТ В ФИЛЬТРАХ КАТАЛОГА: группа «Сколько времени»
   тоже кружки, тоже один выбор, и линейки в ней тоже лишние. Правка
   туда НЕ ВНЕСЕНА молча — каталог принят в этапе 0, и менять его вид
   без показа нельзя. Переводится на .fgroup-boxed на ближайшей
   приёмке каталога, одной строкой в разметке. */

/* Район и тема — флажки: выбирают несколько */
/* ✎ 07.10.2026, этап 0.5. К селектору добавлен .row. Геометрия строки
   в 52 пикселя с линейкой снизу оказалась ролью, а не частностью
   фильтров: по ней же живут строки профиля и настроек. Правило одно
   и приписано обоим именам, а не скопировано во второе место.
   Расходятся они только состояниями: у флажка есть выбор
   (aria-pressed ниже), у строки настроек его нет. */
.check,.row{
  display:flex;align-items:center;gap:var(--s4);width:100%;
  min-height:52px;padding:0;border:0;background:none;cursor:pointer;
  font-family:var(--sans);font-weight:500;font-size:16px;color:var(--text);
  text-align:left;border-bottom:1px solid var(--rule);
}
.fgroup .check:last-child,.sgroup .row:last-child{border-bottom:0;}
/* Флажок — там, где выбирают несколько; кружок — там, где один.
   ✎ «Сколько времени» переведено на кружки: вилки взаимоисключающие. */
/* ✎ Галочка одна на всю систему: флажок фильтра, выбранная сортировка
   и пройденная точка на карте. Иконки из шрифта в проекте запрещены —
   знак «✓» шрифтом на её месте выглядел волоском и жил по правилам
   текста, а не графики.
   ✎ Правка 07.10.2026: была обводка 2.4 при размере 13. Теперь форма
   приходит из набора (ICON:check) на общей толщине, а размер поднят
   до 16 — на холсте 24 та же галочка при 16 читается не хуже прежней,
   и исключение из общей толщины больше не нужно. Внутри флажка 24×24
   с рамкой 1.5 остаётся 21 — помещается свободно. */
.ico-check{display:block;}

.check-box{
  flex:0 0 auto;width:24px;height:24px;border-radius:7px;
  border:1.5px solid var(--edge);              /* ✎ WCAG 1.4.11, см. WCAG.md */
  display:inline-flex;align-items:center;justify-content:center;color:transparent;
  transition:background var(--fast) var(--ease),border-color var(--fast) var(--ease);
}
.check[aria-pressed="true"]{font-weight:700;}
.check[aria-pressed="true"] .check-box{background:var(--accent);border-color:var(--accent);color:var(--on-accent);}
.check-box.radio{border-radius:50%;}
.check[aria-pressed="true"] .check-box.radio{
  background:transparent;border-color:var(--accent);border-width:7px;color:transparent;
}
.check-text{flex:1 1 auto;}
.check-n{flex:0 0 auto;font-size:13px;color:var(--muted);font-weight:500;}

/* Цена — тегами, выбрать можно один */
.ftags{display:flex;flex-wrap:wrap;gap:var(--s2);}

.sheet-foot{
  flex:0 0 auto;padding:var(--s4) var(--pad) calc(var(--s5) + env(safe-area-inset-bottom));
  border-top:1px solid var(--rule);
}

/* ═══ Футер сайта ════════════════════════════════════════════════════ */
/* ── Общий футер сайта ──────────────────────────────────────────────────
   ✎ Добавлен. Политика и согласие на обработку персональных данных —
   не вежливость, а условие: без них не регистрируются Яндекс ID и VK ID
   и не принимает RuStore. Ссылки должны откуда-то вести с самого начала. */
/* ✎ 07.10.2026, этап 0.5. Нижнее поле выросло на высоту меню.

   Решение автора: футер и нижнее меню стоят на ВСЕХ страницах, кроме
   карты прохождения. Меню лежит position:fixed, то есть вынуто из потока
   и накрывает низ страницы, — и место под него добавляет именно футер,
   а не поле самой страницы.

   ✎ 09.10.2026, приёмка с телефона: «подложка футера на десктопе
   на всю ширину окна».

   ФУТЕР ВЫШЕЛ ИЗ <main>. До правки он лежал внутри полосы содержимого
   и вырывался из её боковых полей отрицательными margin. На телефоне
   это работало: полоса там и есть экран. На широком экране полоса
   шириной 720 по центру, и отрицательные поля отменяли только
   собственный --pad — подложка обрывалась по краям колонки, и футер
   читался не подвалом страницы, а ещё одним блоком в ней.

   Отрицательными полями это не чинится в принципе: ширина окна
   элементу внутри центрированной колонки неизвестна. Приёмы с 100vw
   врут на величину полосы прокрутки. Поэтому футер стоит соседом
   <main>, а не его последним ребёнком, — и занимает всю ширину
   без единого трюка.

   Внутренняя колонка — .site-footer-in, те же 720 по центру: подложка
   во всю ширину, содержимое по полосе набора.

   Отступ сверху теперь даёт нижнее поле самой страницы (--s10
   у каждого контейнера), а не margin футера: два поля подряд дали бы
   полторы сотни пикселей пустоты. Место под нижнее меню по-прежнему
   добавляет футер своим нижним padding — он последний в потоке,
   и больше это сделать некому. */
.site-footer{
  padding:var(--s8) var(--pad)
          calc(var(--s9) + var(--tabbar) + env(safe-area-inset-bottom));
  background:var(--surface);
}
.site-footer-in{max-width:720px;margin:0 auto;}
/* ✎ В интерфейсе бренд — название набором, а не знак. Знак рассчитан
   на 24 px и маску магазина, а рядом с текстом работает хуже, чем само
   название.

   ✎ Правка 07.10.2026, вторая. Сначала подпись собиралась живым текстом
   в две строки, и пропорция вышла не та: надстрочник был вдвое с лишним
   мельче главного слова, а должен быть вчетверо. Теперь знак берётся
   из готового файла автора — design/system/assets/wordmark.svg,
   буквы обведены в контуры.

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

   Цвет приходит из токенов. В исходнике стояли #6B6B6B и #E8196B;
   второй — не акцент продукта (#DC1668), и оставить его значило бы
   завести второй розовый. */
.footer-mark{margin-bottom:var(--s5);}
.wordmark{display:block;width:150px;height:auto;}
.wordmark-top{fill:var(--muted);}
.wordmark-main{fill:var(--accent-ink);}

.footer-cols{display:flex;flex-wrap:wrap;gap:var(--s7) var(--s9);}
.footer-col h3{margin:0 0 var(--s3);color:var(--muted);}
.footer-col ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:var(--s3);}
.footer-col a{
  font-family:var(--sans);font-weight:500;font-size:14px;color:var(--text);
  text-decoration:none;border-bottom:1px solid var(--rule);padding-bottom:1px;
}
.footer-legal{
  margin-top:var(--s8);padding-top:var(--s5);border-top:1px solid var(--rule);
  font-family:var(--sans);font-weight:500;font-size:12.5px;line-height:1.6;color:var(--muted);
}
.footer-legal p{margin:0 0 6px;}

/* ═══ Подложка карты ═════════════════════════════════════════════════ */
/* Карта обесцвечена в самом растре — это решение дизайна, а не настройка
   темы (↑). Под тему доводится только светлота. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .map-img{filter:invert(.9) contrast(.86) brightness(1.02);}
}
:root[data-theme="dark"] .map-img{filter:invert(.9) contrast(.86) brightness(1.02);}
:root[data-theme="light"] .map-img{filter:none;}

/* ═══ Линия маршрута ═════════════════════════════════════════════════ */
/* Линия маршрута. Толщина в пикселях экрана, а не в единицах холста:
   линия не должна толстеть и худеть вместе с масштабом карты.
   ✎ Обводка остаётся только в светлой теме. На тёмной карте подложка
   цветом фона читалась не отбивкой, а грязным ореолом вокруг линии. */
.line path{fill:none;stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke;}
.line-case{stroke:var(--bg);stroke-width:9;opacity:var(--line-case-op);}
.line-done{stroke:var(--accent);stroke-width:4;opacity:var(--line-done-fade);}
.line-next{stroke:var(--accent);stroke-width:4;}

/* ═══ Маркеры и своя позиция ═════════════════════════════════════════ */
/* Маркеры. Состояния различаются размером и формой, а не только цветом:
   на солнце и издали цвет — слабый признак.
   Прочитанная точка — заливка с галочкой, непрочитанная — светлое лицо
   с акцентным кольцом и номером. Порядок не навязывается: номер показывает
   место точки в маршруте, а не очередь.
   ✎ В тёмной теме лицо непрочитанного маркера светлое, а не цвета фона:
   тёмный кружок на тёмной карте не читался. */
.mk-ring{fill:var(--mk-face);stroke:var(--accent);stroke-width:2.5;vector-effect:non-scaling-stroke;}
.mk-full{fill:var(--accent);}
/* ✎ 07.10.2026: обводка убрана совсем. Решение автора, и довод верный:
   серый кружок сам по себе читается как «сюда пока нельзя» — это
   та же механика, по которой выключенная кнопка опознаётся без рамки.
   Обводку сюда поставил аудит WCAG, но норма SC 1.4.11 требует контраста
   от того, по чему элемент опознаётся, а здесь опознаётся заливка,
   а не граница: поверхность к карте различима, и знак внутри стал
   плотнее — см. ниже. */
.mk-lock{fill:var(--surface);}
/* Замочек внутри заблокированной точки. Приглушённый, без акцента:
   это не действие и не предупреждение, а «сюда пока нельзя». Цифры
   на таком маркере нет — номер точки человеку, который маршрут
   не купил, ничего не говорит.

   ✎ 07.10.2026: знак рисуется заливкой, а не обводкой, и крупнее.
   На сером кружке тонкая обводка давала вторую плотность поверх первой
   и читалась хуже всего там, где нужнее всего, — на солнце. Заливка
   чуть темнее фона кружка решает то же одной плотностью.
   Геометрия — из набора знаков, s-lock. */
.mk-lock-ico{fill:var(--sign-ink);stroke:none;}
.mk-check{stroke:var(--on-accent);}
.mk-halo{fill:var(--accent);opacity:.16;}
.mk-num{font-family:var(--sans);font-weight:700;font-size:23px;text-anchor:middle;}
.mk-num-out{fill:var(--mk-ink);}

/* Своя позиция — чернилами, а не акцентом: акцент занят маршрутом,
   и смешивать «где я» и «куда идти» нельзя.
   ✎ Форма из рефа: плотный круг в белом кольце и маленький треугольник
   на кромке — он и есть направление. Нет азимута — треугольник просто
   не рисуется, круг остаётся тем же. Так состояние «направление неизвестно»
   не выглядит поломкой: это та же метка, только без клюва.
   Азимут: Android — watchHeadingAsync, браузер — deviceorientation. */
/* ✎ Правка 07.10.2026: строение метки одно в обеих темах — светлое кольцо,
   ядро чернилами. Было наоборот: цвета брались из --text и --bg и
   переворачивались вместе с темой, из-за чего на тёмной карте кольцо
   совпадало с подложкой и метка теряла форму.

   У клюва появилась обводка цветом кольца. Клюв торчит за кольцо
   (от 11,3 до 24,8 единиц) и оказывается прямо на подложке: тёмный
   треугольник на тёмной карте пропадал целиком. Обводка в пикселях
   экрана, как у всей графики карты. */
.me{filter:drop-shadow(0 1px 3px rgba(0,0,0,.4));}
.me-beak{fill:var(--me-dot);stroke:var(--me-face);stroke-width:1.5;
         stroke-linejoin:round;vector-effect:non-scaling-stroke;}
.me-ring{fill:var(--me-face);}
.me-dot{fill:var(--me-dot);}

/* ═══ Схлопывание близких точек ══════════════════════════════════════ */
/* У первого же маршрута две точки стоят в 67 метрах: Казанский собор
   и Казанский мост. Карта зумится везде, где она есть, и дальше считается
   так: метров в пикселе = 78477 / 2^зум на широте Петербурга.

     z17 — 0,60 м/px — 112 px между центрами, свободно
     z16 — 1,20       —  56 px, тесно, но читается
     z15 — 2,39       —  28 px, кольца соприкасаются
     z14 — 4,79       —  14 px, одно поверх другого
     z13 — 9,58       —   7 px, видно одну точку вместо двух

   ПРАВИЛО: точки схлопываются, когда между центрами меньше полутора
   диаметров кольца, то есть меньше 42 px. Правило одно на все экраны
   с картой — и каталог, и обзор маршрута, и прохождение: зум есть
   везде, значит и слипнуться точки могут везде.

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

   БЕЗ ЦИФР. Число внутри группы путает: на соседних маркерах цифра
   означает номер точки в маршруте, и «2» в группе прочитается как
   «точка 2», а не «здесь две точки». Группа показывается тремя точками
   в ряд — знак «тут несколько», который не спорит с нумерацией.
   Форма тоже другая: кольцо крупнее обычного маркера (r 15 против 13),
   чтобы группа отличалась от точки не только содержимым.

   Нажатие приближает карту до масштаба, где точки расходятся. */
.mk-cluster{fill:var(--mk-face);stroke:var(--accent);stroke-width:2.5;
            vector-effect:non-scaling-stroke;}
.mk-cluster-dot{fill:var(--mk-ink);}

/* ═══ Панель поверх карты ════════════════════════════════════════════ */
/* Панель поверх карты. Слева кнопка «Выйти» со словом, а не крестик:
   крестик на панели читался как «свернуть эту плашку».

   ✎ Правка 07.10.2026: кнопка стоит слева ВСЕГДА. В карточке 08 она
   по недосмотру уехала вправо, и один и тот же элемент управления жил
   в двух местах. Левый верхний угол — это «назад» на любом экране,
   и выход из маршрута не исключение.

   ✎ Иконка — шеврон влево, а не дверь со стрелкой. Дверь читается как
   «выйти из аккаунта»: человек на улице, нажимая её, вправе испугаться,
   что его разлогинит. Дверь остаётся в наборе и достаётся профилю,
   где она по смыслу и нужна. */
.panel{
  position:absolute;left:var(--s3);right:var(--s3);
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--r-l);
}
.panel-top{top:var(--s3);padding:var(--s2) var(--s3) var(--s4);}
.panel-top-row{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s3);}
.panel-top-name{
  flex:1 1 auto;min-width:0;
  font-family:var(--sans);font-weight:700;font-size:15px;letter-spacing:-.01em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.panel-leave{
  flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;
  height:32px;padding:0 10px 0 7px;border:0;border-radius:var(--r-pill);
  background:var(--bg);color:var(--muted);cursor:pointer;
  font-family:var(--sans);font-weight:600;font-size:12.5px;
}
.panel-leave:active{background:var(--rule);}

/* ═══ Прогресс точками ═══════════════════════════════════════════════ */
/* ── Прогресс точками ───────────────────────────────────────────────────
   ✎ Было: полоска и «1/8». Стало: сами точки маршрута, соединённые линией.
   Полоска показывала долю, а человеку на улице нужно другое — сколько
   остановок и где он среди них.

   ✎ Точки некликабельны. Открывать точку отсюда было бы вторым способом
   сделать то же самое: на карте для этого есть сами маркеры — они крупнее,
   стоят на своём месте и не требуют попадания в восьмую часть узкой полосы.

   Это заодно снимает ограничение по длине маршрута. Пока точки были целью
   для пальца, на каждую требовалось около двадцати двух пикселей, и ряд
   ломался после двенадцати остановок. Теперь ширину диктует только
   различимость, и два десятка точек помещаются спокойно. Для наших
   восьми-девяти (WORKFLOW.md) запас с избытком. */
.steps{display:flex;align-items:center;padding:var(--s1) var(--s1) 2px;}
.step{
  flex:0 0 auto;width:17px;height:22px;
  display:flex;align-items:center;justify-content:center;
}
.step::before{
  content:"";width:8px;height:8px;border-radius:50%;
  background:var(--mk-face);box-shadow:inset 0 0 0 2px var(--accent);
}
.step.is-read::before{background:var(--accent);box-shadow:none;}
.step.is-near::before{
  width:14px;height:14px;
  box-shadow:inset 0 0 0 2.5px var(--accent), 0 0 0 4px rgba(220,22,104,.18);
}
.step-link{flex:1 1 auto;height:2px;border-radius:1px;background:var(--rule);}
.step-link.is-read{background:var(--accent);opacity:.45;}

/* ═══ Полоска прогресса ══════════════════════════════════════════════ */
/* Запасной вид для длинных маршрутов */
.progress{display:flex;align-items:center;gap:var(--s3);padding:0 var(--s2);}
.progress-track{flex:1 1 auto;height:4px;border-radius:2px;background:var(--rule);overflow:hidden;}
.progress-fill{height:100%;background:var(--accent);border-radius:2px;}
.progress-text{flex:0 0 auto;font-family:var(--sans);font-weight:500;font-size:12.5px;color:var(--muted);}

/* ═══ Подсветка при подходе ══════════════════════════════════════════ */
/* Подсветка при подходе. Карточка сама не открывается (↑). */
.panel-bottom{bottom:var(--s3);padding:var(--s3);display:flex;align-items:center;gap:var(--s3);}
.panel-bottom .arch{flex:0 0 auto;width:52px;}
.panel-bottom-text{flex:1 1 auto;min-width:0;}
.panel-near{display:block;color:var(--accent-ink);margin-bottom:3px;}
.panel-point{font-family:var(--sans);font-weight:700;font-size:17px;letter-spacing:-.01em;line-height:1.2;}

/* ═══ Атрибуция карты ════════════════════════════════════════════════ */
.attrib{
  position:absolute;right:var(--s3);bottom:calc(var(--s3) + 96px);margin:0;
  padding:3px 7px;border-radius:var(--r-s);
  background:var(--bg);opacity:.82;
  font-family:var(--sans);font-weight:500;font-size:10.5px;color:var(--muted);
}

/* ═══════════════════════════════════════════════════════════════════════
   ЭТАП 0.5 — НАВИГАЦИЯ, ГЛАВНАЯ, СТРАНИЦА МАРШРУТА, ПРОФИЛЬ

   Структура разделов и место каждого экрана — design/system/IA.md.
   Этот файл отвечает только за форму.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══ Шапка сайта ════════════════════════════════════════════════════ */
/* ✎ Добавлена на этапе 3, когда появились настоящие публичные страницы.
   До этого она была описана словами в IA.md и не рисовалась: рисовать
   шапку сайта, пока сайта нет, значило бы выдумывать её.

   ЗАЧЕМ ОНА ЕСТЬ. На широком экране нижнее меню выглядит непривычно,
   и решение IA.md такое: «нижнее меню на узком экране, шапка
   на широком» — это единственное, чем навигация сайта отличается
   от навигации приложения. Устройство страницы при этом одно
   на оба носителя: содержимое, под ним футер.

   ВТОРАЯ ПРИЧИНА ВАЖНЕЕ ПЕРВОЙ: логотипа на Главной больше нет
   (решение 08.10.2026), значит на сайте бренд над содержимым несёт
   только шапка.

   ГЕОМЕТРИЯ ВЗЯТА У .cat-tools, как и записано в IA.md: та же высота
   и те же поля, линейка снизу. Своих величин здесь нет ни одной —
   шапка не новый язык, а применение принятого.

   ВХОДА В ШАПКЕ ПОКА НЕТ. По IA.md справа стоит вход, но аккаунт
   приезжает на этапе 5: ссылка вела бы в пустоту. Место под него
   держит .site-head-right — пустой контейнер, который на этапе 5
   заполняется, не трогая раскладку. */
.site-header{display:none;}
.site-head-in{
  max-width:720px;margin:0 auto;
  display:flex;align-items:center;gap:var(--s6);
}
.site-head-mark{display:block;line-height:0;}
.site-head-in .wordmark{width:112px;}
.site-head-nav{display:flex;align-items:center;gap:var(--s5);}
.site-head-nav a{
  font-family:var(--sans);font-weight:600;font-size:14px;
  color:var(--text);text-decoration:none;
}
/* Активный раздел — цветом и весом, тем же языком, что в нижнем меню.
   Поверхности под ним нет: мелкий акцент разрешён только на фоне. */
.site-head-nav a[aria-current="page"]{color:var(--accent-ink);font-weight:700;}
.site-head-right{margin-left:auto;}

/* Шапка появляется на широком экране. Нижнее меню там прячется —
   но правило для него стоит НИЖЕ, сразу после объявления `.tabbar`:
   при одинаковой силе побеждает то правило, которое позже в файле,
   и спрятанное отсюда меню возвращалось бы собственным `display:flex`.
   Поймано глазами на первой же собранной странице. */
@media (min-width:760px){
  .site-header{
    display:block;
    padding:var(--s2) var(--pad);
    background:var(--bg);border-bottom:1px solid var(--rule);
  }
  /* Место под меню в футере тоже лишнее: меню на этом экране нет. */
  .site-footer{padding-bottom:var(--s9);}
}

/* ═══ Нижнее меню ════════════════════════════════════════════════════ */
/* Три раздела: Главная, Маршруты, Профиль. Четвёртого нет и быть не может,
   см. IA.md — при четырёх подпись перестаёт влезать, а цель для пальца
   падает ниже четверти ширины.

   ФОН — var(--bg), НЕ ПОВЕРХНОСТЬ. Это не вкусовое решение: активный
   пункт помечается акцентным ТЕКСТОМ, а мелкий акцент на поверхности
   даёт 4,38 — ниже нормы (tokens.md, правило 1). На поверхности пришлось
   бы помечать активный пункт чем-то другим, и меню перестало бы
   пользоваться тем же языком, что остальной продукт.

   Сверху линейка --rule, а не --edge: это разделитель между меню
   и содержимым, а не граница элемента управления. --edge здесь дал бы
   ту самую решётку, из-за которой токены и разводили (см. tokens.css).

   Безопасная зона прибавляется здесь, а не в токене: в токене она
   превратила бы величину в вычисление, а в React Native env() нет вовсе. */
.tabbar{
  position:fixed;left:0;right:0;bottom:0;z-index:30;
  display:flex;align-items:stretch;
  background:var(--bg);border-top:1px solid var(--rule);
  padding-bottom:env(safe-area-inset-bottom);
}
/* Цель для пальца — вся треть ширины и вся высота полосы: 56 против
   требуемых 48. Иконка и подпись внутри только рисуют, попадать можно
   куда угодно в пределах пункта. */
.tab{
  flex:1 1 0;min-width:0;min-height:var(--tabbar);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  border:0;background:none;cursor:pointer;text-decoration:none;color:var(--muted);
}
.tab-label{font-family:var(--sans);font-weight:600;font-size:11.5px;}
/* Активный пункт меняет ФОРМУ: обводка становится заливкой. Обе иконки
   лежат в разметке, показывает нужную CSS — скрипта это не требует
   и в React Native переносится условием на состояние.

   Зачем форма, если есть цвет и вес: цвет на солнце признак слабый,
   а подпись в 11,5 боковым зрением не читается вовсе. Силуэт виден
   и тем, и другим — и это тот же довод, по которому маркеры на карте
   различаются размером и формой, а не только заливкой. */
.tab-on{display:none;}
.tab[aria-current="page"] .tab-off{display:none;}
.tab[aria-current="page"] .tab-on{display:block;}
/* Активный пункт отличается ЦВЕТОМ И ВЕСОМ, а не только цветом.
   Довод тот же, что у маркеров на карте: на солнце и боковым зрением
   цвет — слабый признак, и один он работу не делает. */
.tab[aria-current="page"]{color:var(--accent-ink);}
.tab[aria-current="page"] .tab-label{font-weight:700;}
/* Нажатие подсвечивается только у НЕактивного пункта. По активному
   нажимать некуда — человек уже здесь, — а поверхность под акцентным
   текстом вернула бы запрещённые 4,38. */
.tab:not([aria-current="page"]):active{background:var(--surface);}

/* Шапка и нижнее меню НИКОГДА не показываются вместе: это одна и та же
   навигация в двух исполнениях (`IA.md`). Рубеж 760 — тот же, на котором
   ленты и карточки переходят к широкой раскладке. */
@media (min-width:760px){
  .tabbar{display:none;}
}

/* ═══ Строка списка ══════════════════════════════════════════════════ */
/* .row — та же геометрия, что у флажка фильтра: строка в 52 пикселя
   с линейкой снизу, по которой попадают пальцем. Роль в tokens.md
   называется check и описана как «строка фильтра» — теперь у неё
   второй носитель: строки профиля и настроек.

   Имя второе, а правило одно: геометрия приписана к обоим селекторам
   выше, а не скопирована. Расходятся они только состояниями — у флажка
   есть выбор, у строки настроек его нет, — и состояния лежат отдельно
   в правилах .check. */
.row{text-decoration:none;}
/* Список обязан быть ограничен с обеих сторон, иначе группа
   из одной строки остаётся вообще без линеек: нижнюю снимает
   правило последней строки, а верхней нет, и строка висит в воздухе.
   В профиле таких групп две: «Мои маршруты» и «Восстановить покупки».

   Через соседа, а не через :first-of-type — в группе рядом стоят
   и ссылки, и кнопки, а :first-of-type считает первым каждый тип
   отдельно и поставил бы вторую линейку посередине списка. */
.sgroup .row{border-top:1px solid var(--rule);}
.sgroup .row+.row{border-top:0;}
/* Шеврон строки — всегда справа и всегда приглушён: он сообщает
   «здесь есть продолжение», а не предлагает действие. */
.row-chev{margin-left:auto;flex:0 0 auto;color:var(--muted);}
/* Приглушённая строка под группой настроек: объясняет, что значит
   выбранное. */
.row-note{
  font-family:var(--sans);font-weight:500;font-size:13px;line-height:1.5;
  color:var(--muted);margin:var(--s3) 0 0;
}
/* Опасное действие в списке настроек. Не красное: красного в палитре нет,
   а акцент занят покупкой и маршрутом. Приглушённое — и этого довольно:
   честность здесь несёт не цвет строки, а экран, который она открывает. */
.row-quiet{color:var(--muted);}

/* ═══ Слайдер ════════════════════════════════════════════════════════ */
/* Один компонент на две ленты Главной: новости и маршруты. Разное у них
   только ширина слайда и круговая прокрутка — и то, и другое включается
   снаружи, разметкой.

   АВТОМАТИЧЕСКОЙ ПРОКРУТКИ НЕТ НИ У ОДНОЙ. Решение автора, 08.10.2026,
   и оно не обсуждается ни в одном из двух мест: лента, которая уезжает
   сама, отнимает у человека чтение на полуслове и попадает пальцем
   не туда, куда он целился. Это же снимает вопрос
   prefers-reduced-motion: двигаться самостоятельно здесь нечему.

   ПРОКРУТКА, А НЕ ПЕРЕЛИСТЫВАНИЕ ПО КНОПКАМ. Палец на телефоне ведёт
   ленту напрямую, с инерцией и прилипанием; стрелки добавляются только
   на широком экране, где пальца нет (см. .slider-arrow ниже).

   Полоса прокрутки спрятана, но прокрутка остаётся настоящей: это
   обычный overflow-x, а не перетаскивание на скриптах. Значит работают
   колесо, трекпад, Shift+колесо, клавиши и чтение с экрана.

   ЗАЗОР РАВЕН БОКОВОМУ ПОЛЮ, и это не совпадение, а расчёт. Слайд
   прилипает началом, то есть встаёт на поле; значит предыдущий слайд
   кончается ровно на «поле минус зазор» от края экрана. При зазоре
   меньше поля от него остаётся полоска в несколько пикселей — у ленты
   маршрутов её нет, потому что перед первой карточкой ничего нет,
   а у круговой ленты предыдущий слайд есть всегда, и полоска читалась
   не как «лента продолжается», а как сбой отрисовки. Сравняв зазор
   с полем, мы уводим этот край ровно за экран. */
.slider{position:relative;}

/* Лента выходит за боковые поля и возвращает их внутрь отступом:
   заглядывающий край следующего слайда обязан доходить до края экрана,
   иначе он читается не как «дальше есть ещё», а как обрезанная карточка.
   scroll-padding держит прилипание на поле, а не на краю экрана. */
.slider-track{
  display:flex;gap:var(--pad);
  margin-inline:calc(-1 * var(--pad));
  padding-inline:var(--pad);
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scroll-padding-inline:var(--pad);
  scrollbar-width:none;-webkit-overflow-scrolling:touch;
}
.slider-track::-webkit-scrollbar{display:none;}
.slider-track>*{flex:0 0 auto;scroll-snap-align:start;}

/* ✎ На широком экране слайд прилипает серединой, а не началом: лента
   короче экрана не бывает, и половинки по обе стороны честно показывают,
   что она продолжается в обе стороны. На телефоне так нельзя — первый
   слайд уехал бы к середине и половина его спряталась бы за краем. */
@media (min-width:760px){
  .slider-track>*{scroll-snap-align:center;}
}

/* Стрелки — ТОЛЬКО на широком экране. На телефоне они лишние: там лента
   ведётся пальцем. На настольном браузере пальца нет, а горизонтальная
   прокрутка мышью без Shift не делается вовсе — без стрелок лента там
   просто не листается. Поэтому это не украшение, а единственный способ
   ею воспользоваться. */
.slider-arrow{display:none;}
@media (min-width:760px){
  .slider-arrow{
    display:flex;align-items:center;justify-content:center;
    position:absolute;top:calc(50% - 22px);z-index:2;
    width:44px;height:44px;border-radius:50%;border:0;cursor:pointer;
    background:var(--bg);color:var(--text);box-shadow:var(--pop-shadow);
    transition:transform var(--fast) var(--ease),opacity var(--fast) var(--ease);
  }
  .slider-arrow:active{transform:scale(.94);}
  /* Стрелка висит НАД краем ленты, а не рядом с ней: полоса и так заняла
     всю ширину полосы набора, и место сбоку пришлось бы отнять у слайдов. */
  .slider-prev{left:calc(-1 * var(--s4));}
  .slider-next{right:calc(-1 * var(--s4));}
  /* Край ленты достигнут — стрелка гаснет, но не исчезает: исчезающая
     кнопка сдвигает соседей и уводит курсор с того места, куда человек
     только что целился. У круговой ленты края нет, и гаснуть нечему. */
  .slider-arrow[disabled]{opacity:.35;cursor:default;box-shadow:none;}
}

/* ═══ Новости и анонсы ═══════════════════════════════════════════════ */
/* ЛЕНТА, И НОВОСТЬ В НЕЙ — ТОЛЬКО КАРТИНКА. Решение автора, 08.10.2026;
   оно отменяет прежнее «вертикальным списком, а не лентой» и прежнюю
   карточку с заголовком и текстом.

   Что это меняет по сути: текст новости теперь запечён в растр. Плюс
   очевиден — баннер говорит одним взглядом и сам себе картинка. Минус
   надо держать в голове, он не маленький: такую новость нельзя
   переписать за десять минут, нельзя увеличить вместе со шрифтом
   системы и нельзя прочитать с экрана. Отсюда два обязательных правила,
   а не пожелания:

     1. alt у каждого баннера повторяет ЕГО ПОДПИСЬ СЛОВАМИ, а не
        описывает кадр. Для незрячего alt — это и есть новость.
     2. Баннер рисуется под оба конца вилки: он один на светлую
        и тёмную тему и на любую ширину от 240 до 420 точек.

   «Акций» здесь по-прежнему нет: акция это скидка, а скидки исключены
   из этапа 5 PRD. Это анонсы — новый маршрут, бесплатный маршрут,
   переписанная точка, свой канал.

   Счётчика в заголовке раздела нет — правило этапа 0: либо значение
   приходит из данных, либо его нет вовсе. */

/* Ширина слайда даёт ровно один баннер и 0,4 следующего: полный кадр
   читается, а заглядывающий край сообщает, что лента листается, —
   и сообщает это без единой точки-индикатора под лентой. */
.nslide{
  width:calc((100vw - var(--pad) * 2) / 1.4);
  max-width:420px;
  border-radius:var(--r-l);overflow:hidden;
  background:var(--surface);display:block;
}
/* 4:3 задаётся здесь, а не пропорциями файла: баннер придёт из админки,
   и ошибка в пропорции не должна ломать ритм ленты. */
.nslide img{
  display:block;width:100%;aspect-ratio:4 / 3;object-fit:cover;
  filter:var(--preview-filter);
}
@media (min-width:760px){
  .nslide{width:300px;}
}

/* ═══ Карточка маршрута в ленте — вертикальная ═══════════════════════ */
/* ВТОРАЯ КАРТОЧКА МАРШРУТА, И ЭТО ОСОЗНАННАЯ ЦЕНА. В каталоге карточка
   строкой: кадр слева, текст справа. В ленте та же строка, сжатая
   до двух третей ширины телефона, разваливается — на кадр остаётся
   полоска, а название уходит в пять строк.

   Поэтому в ленте кадр сверху, текст снизу. Узнавание между Главной
   и каталогом при этом слабеет, и это записано как цена решения,
   а не как недосмотр: содержимое карточки — название, характеристики,
   район, цена — остаётся тем же и в том же порядке, меняется только
   раскладка. Если вертикальная окажется лучше и в каталоге, вторая
   уйдёт, а не останется рядом навсегда. */
.rcard-v{
  display:flex;flex-direction:column;gap:var(--s3);
  width:min(64vw,250px);
  color:inherit;text-decoration:none;
}
/* Арка здесь КВАДРАТНАЯ, а не 4:5. Проверено на экране: при 4:5 обложка
   в карточке шириной 250 вырастает до 312, и одна карточка занимает
   полэкрана телефона — лента перестаёт быть лентой, а название
   и цена уходят за нижний край. Купол при этом остаётся полукругом,
   то есть аркой; меняется только высота опор.
   Пропорция 4:5 остаётся нормой везде, где арка стоит одна: обложка
   маршрута, миниатюры точек, карточка каталога. */
.rcard-v .arch{width:100%;aspect-ratio:1;}
.rcard-v-body{display:flex;flex-direction:column;gap:var(--s2);}
/* Название мельче, чем в каталоге (19 → 17): в карточке вдвое уже
   строка, и прежний кегль давал четыре строки вместо двух.
   Обрезается третьей строкой — иначе карточки в ленте разной высоты,
   и нижний край ленты начинает гулять. */
.rcard-v-title{
  font-family:var(--sans);font-weight:800;font-size:17px;letter-spacing:-.02em;
  line-height:1.15;margin:0;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
}
.rcard-v .rcard-foot{margin-top:var(--s1);padding-top:0;}
@media (min-width:760px){
  .rcard-v{width:216px;}
}

/* ═══ Страница маршрута ══════════════════════════════════════════════ */
/* Экран, на который человек попадает из поисковой выдачи и на котором
   решает, стоит ли маршрут денег. По research/market-and-audience.md —
   главная точка входа в продукт, а не второстепенная страница.

   ✎ ПЕРЕСОБРАНА 08.10.2026 по приёмке. Двенадцать замечаний автора,
   и четыре из них меняют не вид, а устройство экрана:
     — обложка вышла из арки и стала прямоугольной во всю ширину;
     — восемь точек и отклики стали лентами, а не списками;
     — первая точка целиком со страницы убрана совсем;
     — лид сведён к двум коротким абзацам с записанным пределом длины.
   Разбор каждого — в журнале этапа. */

/* ── Обложка ─────────────────────────────────────────────────────────
   ✎ БЕЗ АРКИ. Решение автора, 08.10.2026, и оно отменяет прежнее
   «обложка маршрута — главное место применения арки».

   Довод автора: на телефоне обложка обязана идти во всю ширину экрана,
   а арка в 390 пикселей шириной — это полукруг радиусом в полэкрана,
   то есть не проём, а купол над страницей. Арка остаётся там, где она
   мелкая и работает знаком: миниатюры точек, карточка каталога, лента
   Главной, аватар профиля. На кадре в полэкрана она перестаёт быть
   знаком и становится формой самой страницы.

   ✎ ВЫСОКАЯ НА ТЕЛЕФОНЕ, ШИРОКАЯ НА ШИРОКОМ ЭКРАНЕ. Решение автора,
   08.10.2026, вторая правка того же дня: «чтоб на экране телефона
   при входе низ занимали цена, название и характеристики, а весь верх —
   обложка». То есть высота обложки задана не пропорцией кадра,
   а ОСТАТКОМ ЭКРАНА, и считается арифметикой, которую видно:

     100svh − 56 (нижнее меню) − 34 (безопасная зона) − 157 (бейдж
     с районом, заголовок в две строки и характеристики) = высота обложки

   Отсюда clamp(300px, 100svh − 247px, 640px). Нижняя граница держит
   обложку кадром, а не полоской, если экран очень низкий; верхняя —
   не даёт ей вырасти на планшете в портрете, где 100svh за тысячу.
   Кнопки при этом оказываются сразу под сгибом, и это не недосмотр:
   край акцентной кнопки, заглядывающий снизу, — лучшее приглашение
   листать, чем пустое поле.

   На широком экране svh не годится — там высота окна ничего не говорит
   о композиции, — и работает пропорция 7:5.

   ✎ КАДР ПОДГОТОВЛЕН ФАЙЛОМ, А НЕ object-position. Исходник
   вертикальный, 1280×1707, и это обычный городской снимок: четыре
   светофора, провода, толпа на переходе, дорожная разметка. Именно эту
   тесноту автор дважды назвал «шумом» — и она не в фильтре, а в кадре.

   Сначала низ срезался свойством: в коробке ШИРЕ исходника
   object-fit:cover режет по вертикали, и object-position сверху
   отсекает лишнее сам. Высокая обложка это сломала — коробка стала
   УЖЕ исходника, cover начал резать по горизонтали, и кадр показался
   во всю высоту вместе со светофорами. Одним свойством две разные
   обрезки не делаются.

   Поэтому обрезка переехала в `assets/make-thumbs.py`: он кладёт рядом
   с исходником `02-bashnya-cover.jpg` — верхние 55% кадра, без
   уменьшения и без фильтров. Это ровно то, что в продукте делает
   админка при загрузке (см. photo-registry.md, «превью отдаётся
   в размере, близком к размеру показа»), и производный файл записан
   в реестр прав отдельной строкой: CC BY 3.0 обработку разрешает.

   ✎ ФИЛЬТРА ПРЕВЬЮ ЗДЕСЬ НЕТ. Задача --preview-filter — снять разнобой
   экспозиции между СОСЕДНИМИ кадрами в ряду. У обложки соседей нет:
   она одна на экране и ни с чем не сравнивается. А подъём контраста
   на кадре во всю ширину вытягивает провода и зерно — ровно то,
   от чего избавляет кадрирование. Фильтр остаётся на миниатюрах
   и в каталоге, где ряд есть. */
.rcover{
  display:block;overflow:hidden;
  background:var(--surface);border-radius:var(--r-l);
}
/* height:auto — не украшение. У картинки в разметке стоят width
   и height атрибутами (они держат место до загрузки), а атрибут
   height — это подсказка стиля. aspect-ratio работает только когда
   одна из сторон auto; без height:auto кадр вставал во всю свою
   натуральную высоту и занимал собой весь экран. */
.rcover img{
  display:block;width:100%;height:auto;
  aspect-ratio:7 / 5;object-fit:cover;object-position:50% 0;
}

/* ── Шапка ───────────────────────────────────────────────────────────
   ЗАГОЛОВОК ПОД ФОТОГРАФИЕЙ, А НЕ ПОВЕРХ. Реф Lumi Airlines
   (design-direction, раздел 1) ставит витринный гротеск поверх кадра,
   и соблазн велик. Но текст поверх фотографии требует затемнения
   или плашки, а это спорит сразу с двумя принятыми правилами: «один фон
   на весь продукт, без вариаций» и «интерфейс уходит в тень, фотография
   занимает главное положение». Затемнив кадр, мы отнимаем у него ровно
   то, за что он здесь стоит.

   ✎ ЦЕНА ИЛИ БЕЙДЖ — СРАЗУ ПОД КАДРОМ, А КНОПКА ВО ВСЮ ШИРИНУ.
   Замечание автора: бейдж «Бесплатно» и кнопка «Пройти маршрут» плохо
   смотрелись в одной строке. Причина называется точно: это вещи разной
   природы. Бейдж — свойство товара, его читают; кнопка — действие,
   на неё нажимают. Поставленные в строку через justify-content:space-between
   они читались парой кнопок, из которых одна почему-то не нажимается.

   Теперь бейдж стоит первой строкой под кадром, рядом с районом —
   ровно тем же сочетанием, что в подвале карточки каталога, откуда
   человек сюда и пришёл. А кнопки лежат внизу шапки столбиком
   во всю ширину. */
/* ✘ ВЕРХНЕГО ОТСТУПА НЕТ. Был s5 — при арке он держал купол
   на расстоянии от края экрана. Прямоугольный кадр во всю ширину
   с полоской фона над ним читается не «во всю ширину», а «почти
   во всю» — то есть как недоделанный край, а не как решение. */
.rhead{padding-top:0;}
.rhead-text{padding-top:var(--s5);}
/* Бейдж и район в одной строке. Бейдж первый: цена — первое, что
   спрашивают у страницы маршрута, и спорить с ней за это место
   названию района незачем. */
.rhead-tag{
  display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  margin-bottom:var(--s4);
}
.rhead-text h1{margin:0;}
/* Метаданные тем же языком, что в карточке каталога: точки-разделители,
   без иконок. Человек уже видел эту строку в списке — узнавание
   экономит ему чтение. */
.rhead-meta{margin-top:var(--s4);}
/* ✎ Действия столбиком, обе кнопки во всю ширину. Вторая — «Сохранить
   офлайн» — тихая: скачивание это удобство, а не то, ради чего страницу
   открыли, и спорить за нажатие с «Пройти маршрут» оно не должно.

   ОНА ПОКАЗЫВАЕТСЯ НЕ ВСЕГДА. Условие — маршрут бесплатный или куплен;
   у неоплаченного платного маршрута скачивать нечего, и кнопка там
   не гаснет, а отсутствует. Выключенная кнопка обещает то, чего
   не будет, и заставляет догадываться, чего не хватает. */
.rhead-actions{
  display:flex;flex-direction:column;gap:var(--s3);
  margin-top:var(--s6);padding-top:var(--s6);border-top:1px solid var(--rule);
}

/* ── Лид: нить и «кому подойдёт» ─────────────────────────────────────
   ✎ ПЕРЕСОБРАН 08.10.2026. Было: отдельный блок «Одна история»
   с подписью-капителью, под ним отдельное описание в два абзаца.
   Стало: один блок без заголовка и ровно два коротких абзаца.

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

   ПРЕДЕЛ ДЛИНЫ ЗАПИСАН, А НЕ ПОДРАЗУМЕВАЕТСЯ: два абзаца, вместе
   не больше 500 знаков. Это не вкусовое ограничение. Экран продаёт
   пролистыванием: ниже лида идут карта, путь, предупреждения, лента
   точек и отклики, и каждый лишний абзац наверху отодвигает их
   за нижний край. Текст, который не помещается в предел, —
   это точка маршрута, а не его описание.

   ✎ Предел поднят с 400 до 500 знаков, 08.10.2026, по замечанию автора
   «можно немного увеличить минимальный объём, но постарайся обойтись
   двумя абзацами». 400 вмещали нить только с сокращением, и сокращать
   приходилось не лишнее, а оборот про мост, под которым покушение
   сорвалось, — то есть третий поворот истории из трёх. При 500 нить
   из route.md встаёт целиком, дословно, и резать её больше не нужно.

   Нить стоит ПЕРВЫМ абзацем и БЕЗ ЗАГОЛОВКА. Описание «что увидишь
   и для кого» есть у любого маршрута у любого конкурента; нить —
   единственное, что отличает продукт от бесплатного блога
   (design-direction, три фильтра).

   ✎ У ВТОРОГО АБЗАЦА ЗАГОЛОВОК ЕСТЬ — «Кому подойдёт». Он был снят
   вместе с «Одной историей» и возвращён в тот же день по замечанию
   автора: «зря убрал надпись кому подойдёт». Разница между двумя
   заголовками в том, кому они адресованы. «Одна история» — подпись
   для автора, читателю она ничего не сообщает. «Кому подойдёт» —
   вопрос, который человек задаёт сам, и строка отвечает на него
   до того, как он дочитает абзац.

   Набран телом рассказа: нить и есть рассказ, просто короткий.
   Поверхности под лидом нет — на поверхности он прочитался бы врезкой. */
/* ✎ ВО ВСЕХ БЛОКАХ НИЖЕ — margin-block, А НЕ СОКРАЩЁННЫЙ margin.
   Это не придирка. Все они ставятся вместе с классом .col, а тот
   даёт margin-inline:auto — полосу набора в 62 знака по центру.
   Сокращённый margin со значением 0 по бокам этот auto обнулял,
   и блок уезжал к левому краю на десяток пикселей относительно
   соседнего текста. На телефоне этого не видно вовсе, на широком
   экране видно сразу.

   Это второй случай той же ловушки: первый был в этапе 0,
   когда .col уехал в композицию и перестал перебиваться блоком
   «Обратите внимание». Тогда починили порядок склейки, сейчас —
   само свойство: margin-block трогает только верх и низ. */
.rlead{margin-block:var(--s8);}
.rlead p{
  margin:0 0 var(--s5);
  font-family:var(--serif);font-weight:400;font-size:19px;line-height:1.52;
}
.rlead p:last-child{margin-bottom:0;}
/* Заголовок второго абзаца — капитель цветом подписи, а не .sec-h2.
   Это не раздел экрана, а подпись внутри одного текста: крупный
   заголовок разорвал бы лид на два блока, которые автор только что
   свёл в один. */
.rlead .t-caps{
  display:block;color:var(--muted);
  margin:var(--s6) 0 var(--s2);
}

/* ── Раздел страницы маршрута ────────────────────────────────────────
   ✎ 08.10.2026. У карты, ленты точек и откликов появились настоящие
   заголовки — .sec-h2, те же, что открывают разделы Главной.

   Прежде разделы этой страницы подписывались капителью .t-caps цветом
   подписи. Замечание про карту звучало буквально «перед картой нужен
   заголовок, и расстояние побольше от текста» — то есть подпись сбоку
   за заголовок не считается. Это ровно тот же разбор, по которому
   в этом же этапе укрупнились заголовки Главной.

   Отступ между разделами — s9, а не s7: раздел отделяется от соседнего
   не линейкой, а воздухом, и воздуха должно хватать, чтобы заголовок
   читался началом нового, а не хвостом предыдущего. */
.rsec{margin-block:var(--s9);}
.rsec>.sec-h2{margin:0 0 var(--s5);}

/* ── Карта маршрута: превью с путём ──────────────────────────────────
   ЭТО НЕ КАРТА ПРОХОЖДЕНИЯ. Здесь только линия и точки: ни своей
   позиции, ни прогресса, ни подсветки при подходе. Решение автора,
   записанное в IA.md: карта бывает двух исполнений, и это разные
   части приложения.

   Довод: на странице маршрута карта отвечает на вопрос «куда меня
   поведут», а не «где я сейчас». Своя позиция тут только отвлекает —
   человек может читать страницу вечером в отеле, в другом районе. */
/* ✎ УГЛЫ НЕ СКРУГЛЯЮТСЯ — ни на телефоне, ни на широком экране.
   Решение автора, 08.10.2026. На телефоне скругления не было и так:
   карта выходит за боковые поля, и округлять край, который уехал
   за экран, нечего. На широком экране оно оставалось — и оказалось
   единственным местом, где скруглён ПРЯМОУГОЛЬНИК СОДЕРЖИМОГО,
   а не поверхность под ним. Карта не карточка: у неё нет полей,
   она и есть изображение во всю ширину полосы, как фотография
   внутри рассказа, — а фотографии в этом продукте не скругляются
   нигде. overflow:hidden остаётся: он держит подложку в границах
   блока, а не углы. */
.rmap{
  position:relative;overflow:hidden;
  background:var(--surface);
}
.rmap svg{display:block;width:100%;height:auto;}
/* ✎ Этап 3: на макете карта была картинкой, в продукте это живая карта
   MapLibre на наших тайлах. Пропорция задаётся здесь, а не высотой
   кадра: у живой карты кадра нет вовсе. 3:4 — та же вертикаль, в которой
   карта стояла на макете, и она же удобна пешеходному маршруту: город
   вытянут вдоль каналов.

   Пока карта не поднялась (скрипт ещё не доехал или его нет вовсе),
   на месте холста лежит поверхность — не пустой провал, а место,
   которое честно ждёт. Текста «карта не загрузилась» здесь нет:
   страница читается и без карты, а строка про сбой отвлекала бы
   от маршрута. */
.rmap-canvas{display:block;width:100%;aspect-ratio:3 / 4;background:var(--surface);}
@media (min-width:760px){
  /* На широком экране вертикаль в 3:4 уводит страницу в колодец. */
  .rmap-canvas{aspect-ratio:4 / 3;}
}
/* Атрибуция прижата к низу самой карты. Общее правило .attrib поднимает
   её на 96 пикселей — там под картой лежит лента карточек, здесь
   её нет. */
.rmap .attrib{bottom:var(--s3);}

/* ── Путь: начало и финиш ────────────────────────────────────────────
   ✎ ПЕРЕСОБРАН 08.10.2026, ВТОРОЙ РАЗ ЗА ДЕНЬ. Утром здесь были строки
   с линейками, днём — плитки на приглушённой поверхности, и плитки
   прожили один показ: «список под картой теперь по стилю спорит
   с блоком ниже».

   Спорил не стиль, а роль. Плитки и блок «Что учесть» лежали на одной
   поверхности, одинаковыми прямоугольниками, друг под другом — два
   блока, которые читаются одним, хотя говорят разное: один отвечает
   «куда идти», другой «к чему готовиться».

   ЧТО ВЫБРОШЕНО: МЕТРО. Решение автора, и у него две причины, обе
   сильные. Первая — **оно спорило с началом**: «Спас на Крови,
   наб. канала Грибоедова, 2» и «Невский проспект» стояли рядом двумя
   адресами, и человек читал их как два разных начала. Вторая — **его
   не всегда можно назвать**: у маршрута по дворам или по Васильевскому
   ближайшая станция может быть в двадцати минутах, и поле, которое
   у половины маршрутов пустое, не поле, а повод каждый раз решать
   заново. Заодно со страницы ушло единственное значение, которого
   не было в route.md.

   ✎ СЕЗОН ТОЖЕ УБРАН, и это третья правка блока за день. Сначала он
   остался отдельной строкой со значком календаря; автор убрал и его:
   «Круглый год, всё на улице» не говорит ничего, чего нельзя сказать
   в «Что учесть», а строка со значком обещала поле, которое у половины
   маршрутов будет либо пустым, либо таким же необязательным.

   ПРАВИЛО НА ВПЕРЁД: сезонность — это предупреждение, а не свойство.
   «Зимой набережная не чищена» идёт в «Что учесть» вместе с дождём
   и толпой, потому что там она отвечает на вопрос «к чему готовиться»,
   а не занимает отдельную строку ради симметрии. Значок календаря
   из набора убран следом: иконка без места применения в наборе
   не держится.

   ЧТО ОСТАЛОСЬ: начало и финиш, связанные ВИДИМОЙ ЛИНИЕЙ — это тот же
   язык, на котором говорит карта прямо над ними: кольцо, линия,
   заливка. Блок читается продолжением карты, а не списком под ней.

   КОЛЬЦО И ЗАЛИВКА, А НЕ ДВЕ ИКОНКИ. Начало — кольцо, финиш —
   закрашенный круг. Та же пара, что у маркеров на карте в этапе 0,
   и рисуется она не значком из набора, а формой: значок здесь лёг бы
   второй плотностью поверх первой, а главное — ни один значок
   не умеет быть концом линии.

   ПОВЕРХНОСТИ ПОД ЭТИМ БЛОКОМ НЕТ ВОВСЕ. В этом и ответ на замечание:
   рядом с «Что учесть» на поверхности путь без поверхности читается
   другой вещью с первого взгляда, без единого слова. */
.rpath{margin-block:var(--s7);}
.rpath-row{position:relative;display:flex;align-items:flex-start;gap:var(--s4);}
.rpath-row+.rpath-row{margin-top:var(--s6);}
/* Линия между кольцом и заливкой. Рисуется на ПЕРВОЙ строке и уходит
   вниз за её край ровно на зазор между строками: так она не зависит
   от того, в сколько строк лёг адрес. */
.rpath-row:first-child::before{
  content:"";position:absolute;left:6px;top:18px;
  bottom:calc(-1 * (var(--s6) + 4px));
  width:2px;background:var(--accent);opacity:.3;
}
.rpath-mark{
  flex:0 0 auto;width:14px;height:14px;margin-top:4px;
  border-radius:50%;border:2.5px solid var(--accent);background:var(--bg);
}
.rpath-end .rpath-mark{background:var(--accent);}
.rpath-body{min-width:0;}
.rpath-k{display:block;color:var(--muted);margin-bottom:var(--s1);}
.rpath-v{
  display:block;
  font-family:var(--sans);font-weight:600;font-size:17px;line-height:1.35;
  letter-spacing:-.01em;
}

/* ── Предупреждения ──────────────────────────────────────────────────
   Отдельно от .inset, хотя поверхность та же. У врезки-факта тело —
   Literata 16,5: это рассказ, самое сильное на точке. Предупреждение
   рассказом не является, это служебное сообщение, и набирается
   интерфейсным Onest. Один фон, две разные роли — ровно так же
   различаются панель поверх карты и врезка.

   Маркер списка — приглушённое тире, как в источниках, а не акцентная
   точка, как в списке внутри рассказа. Предупреждение не должно
   выглядеть интереснее самого маршрута.

   В тексте только ДОЛГОВЕЧНЫЕ формулировки: ни чисел, ни расписаний.
   Правило PRD, и оно про содержание, а не про вид: портится не
   предупреждение, а число в нём. */
.warn{margin-block:var(--s7);padding:var(--s5);background:var(--surface);border-radius:var(--r-s);}
.warn h2{margin:0 0 var(--s3);color:var(--text);}
.warn ul{margin:0;padding:0;list-style:none;}
.warn li{
  position:relative;padding-left:var(--s5);margin-bottom:10px;
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.5;
}
.warn li:last-child{margin-bottom:0;}
.warn li::before{content:"\2014";position:absolute;left:0;color:var(--muted);}

/* ── Точки маршрута: лента ───────────────────────────────────────────
   ✎ ЛЕНТОЙ, А НЕ СПИСКОМ. Решение автора, 08.10.2026; оно отменяет
   список строк с номерами и линейками-разделителями.

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

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

   Слайд КВАДРАТНЫЙ, а не 4:5 — то же решение и та же причина, что
   у карточки маршрута в ленте Главной: при 4:5 арка шириной 256
   вырастает до 320, и один слайд занимает полэкрана телефона.
   Пропорция 4:5 остаётся нормой везде, где арка стоит одна.

   Ширина слайда — та же формула, что у ленты новостей: ровно один
   слайд и 0,4 следующего. Заглядывающий край сообщает, что лента
   листается, без единой точки-индикатора под ней.

   Семь миниатюр из восьми — пустые арки, и это не недоделка макета.
   Миниатюра проходит фильтр превью, значит требует кадра с пометкой
   «обработка разрешена»; по первому маршруту такой ровно один.
   Остальное под CC BY-SA, которое обрабатывать нельзя. */
.pslide{
  display:flex;flex-direction:column;gap:var(--s3);
  width:min(calc((100vw - var(--pad) * 2) / 1.4),280px);
  color:inherit;text-decoration:none;
}
.pslide .arch,.pslide .arch-empty{width:100%;aspect-ratio:1;}
.pslide-row{display:flex;align-items:center;gap:var(--s2);min-width:0;}
/* Закрытая точка приглушена целиком: и замок, и имя. Это выключенный
   элемент, и норма SC 1.4.11 выключенные исключает прямо — тот же
   довод, по которому приглушён замок в маркере (см. --sign-ink).
   Замок берётся обводкой, а не знаком: знаки по паспорту ставятся
   от 28 пикселей, здесь размер 16. */
.pslide-lock{flex:0 0 auto;display:flex;color:var(--muted);}
.pslide-name{
  min-width:0;
  font-family:var(--sans);font-weight:600;font-size:16px;line-height:1.25;
  letter-spacing:-.01em;
}
.pslide[data-locked="true"] .pslide-name{color:var(--muted);}
@media (min-width:760px){
  .pslide{width:220px;}
}

/* ── Редакторская цитата ─────────────────────────────────────────────
   Социальное доказательство v1: публичных отзывов и рейтингов в продукте
   нет (решение PRD — пустой блок «0 отзывов» работает против продаж),
   цитаты ставит автор сама. Аватара нет: в модели данных у отклика
   только текст, подпись и флаг публикации.

   ✎ БЕЗ ПОЛОСЫ СЛЕВА И ПО ЦЕНТРУ. Решение автора, 08.10.2026,
   и оно снимает прежнее «полоса линейкой, а не акцентом» целиком:
   полосы больше нет никакой.

   Довод к нему виден, как только отклики стали лентой. Полоса слева —
   знак цитаты ВНУТРИ текста: она говорит «это чужие слова в чужом
   рассказе». В ленте чужого рассказа вокруг нет, слайд и так отделён
   от всего зазором и краем экрана, и полоса вместо отбивки работала
   лишней вертикалью рядом с такой же вертикалью соседнего слайда.

   ~~Выравнивание по центру — оттуда же.~~ ✎ 09.10.2026, ОТМЕНЕНО
   приёмкой с телефона: «отклики выровнять по левой стороне, или так-то
   переоформить, сейчас выглядят off».

   Довод «по центру отклик читается репликой» был верен для цитаты,
   которая лежит ОДНА и ни с чем не соседствует. В ленте всё иначе:
   два центрированных блока разной длины стоят рядом, их левые края
   не совпадают ни между собой, ни с заголовком раздела и всем
   остальным текстом страницы, — и лента выглядит съехавшей.

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

   ✎ ЛЕНТОЙ, А НЕ СТОЛБИКОМ — решение того же дня, тем же замечанием,
   что и у точек. Отклики не текст страницы, а голоса рядом с ней:
   столбиком они набирали высоту наравне с рассказом и обещали, что их
   много, хотя их два. Лента показывает ровно столько, сколько есть. */
.quote{margin-block:var(--s8);}
.quote p{
  margin:0 0 var(--s4);
  font-family:var(--serif);font-style:italic;font-weight:400;
  font-size:17px;line-height:1.45;
}
/* Подпись отбита линейкой, а не только полем: под текстом в одну строку
   и под текстом в четыре одно и то же поле читается по-разному,
   а линейка держит границу между голосом и тем, чей он, одинаково. */
.quote footer{
  padding-top:var(--s3);border-top:1px solid var(--rule);
  font-family:var(--sans);font-weight:500;font-size:12.5px;color:var(--muted);
}
/* Цитата внутри ленты: своих вертикальных полей не держит — их задаёт
   раздел, — и получает ширину слайда. Слайды в ленте равной высоты:
   flex растягивает их по самому длинному, и нижний край ленты
   не гуляет. Подпись прижата к низу слайда, а не болтается сразу
   под текстом разной длины: в ленте эти строки стоят рядом и обязаны
   лежать на одной линии. */
.qslide{
  display:flex;flex-direction:column;justify-content:space-between;
  width:min(calc((100vw - var(--pad) * 2) / 1.4),360px);
  margin-block:0;
  padding:var(--s5);background:var(--surface);border-radius:var(--r-m);
}
@media (min-width:760px){
  .qslide{width:320px;}
}

/* ═══ Профиль ════════════════════════════════════════════════════════ */
/* Шапка: аватар и шестерёнка одной строкой, имя под ними.

   ✎ 08.10.2026, замечание автора: «иконка аватара — одна на строку,
   имя крупнее и ниже аватарки». Было строкой: аватар слева, имя
   и служба входа справа — и в такой строке имя не могло вырасти,
   не разогнав шапку в полтора аватара высотой.

   Столбиком аватар перестал делить ширину с текстом, и это дало
   две вещи сразу: арка выросла с 56 до 72, имя — с 20 до 29.
   Выравнивание осталось ПО ЛЕВОМУ КРАЮ, как всё на этом экране:
   центрированная шапка над списком, набранным от левого края,
   читается чужой вставкой.

   ✎ ВТОРОЙ ПОКАЗ, 08.10.2026: шестерёнка переехала СЮДА, в строку
   аватара. Отдельная строка под неё была пустой на три четверти,
   и экран начинался с пустоты. Теперь верх держат две вещи: аватар
   слева, значок справа. Выравнивание по верху, а не по центру арки:
   значок — часть верхнего края экрана, а не спутник портрета. */
/* ✎ ТРЕТИЙ ПОКАЗ, 08.10.2026: «сверху профиля теперь много места».
   Своё поле у шапки было лишним: страница уже даёт --s5 сверху,
   и два поля подряд складывались в 36 пикселей пустоты над аватаром.
   Сверху не осталось ничего — отбивку держит поле страницы. */
.phead{padding:0 0 var(--s6);}
.phead-top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s4);}
.phead .arch,.phead .arch-empty{flex:0 0 auto;width:72px;margin-bottom:var(--s4);}
/* Значок вынесен на величину своего поля за полосу содержимого:
   у .icon-btn 36 пикселей цели при значке в 20, и без выноса
   шестерёнка стояла бы не по краю текста, а на восемь пикселей
   внутрь от него. Тот же приём и тот же довод, что у крестика
   в панели чтения (.topbar .icon-btn). */
.phead-top .icon-btn{margin-right:-8px;}
/* Имя — h1 этого экрана, и набрано оно крупнее заголовков разделов
   (.sec-h2, 23–28): имя человека и есть содержимое профиля.

   На Главной такой же h1 нарочно сделан ТИШЕ разделов, и это
   не противоречие, а то же правило, применённое к другому случаю:
   «Доброе утро, София» о содержимом экрана не сообщает ничего,
   а здесь сообщает всё. */
.phead-name{
  margin:0 0 var(--s1);
  font-family:var(--sans);font-weight:800;
  font-size:clamp(26px,7.4vw,30px);line-height:1.05;letter-spacing:-.03em;
}
.phead .t-meta{margin:0;}
/* Пустое состояние профиля стоит сразу под шапкой, и своего верхнего
   поля ему не нужно: отбивку уже дала шапка. */
.profile-sec>.empty{margin:0;}

/* ── Статистика ──────────────────────────────────────────────────────
   ✎ ВТОРОЙ ПОКАЗ, 08.10.2026: «статистику хотелось бы не по центру,
   а как-то иначе разместить, но не списком».

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

   Стало — ТРИ ОТДЕЛЬНЫЕ ПЛИТКИ в ряд, числа от левого края своей
   плитки. ✎ ТРЕТИЙ ПОКАЗ, 08.10.2026: «статистика — почти. Давай
   на отдельные блоки разобьём?»

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

   Поверхность и скругление те же, что у пустого состояния на этом же
   месте (.empty): два состояния одного блока одной породы, и переход
   между ними — замена содержимого, а не замена блока.

   Скругление мельче (--r-s против --r-m у .empty): плитка в треть
   ширины с большим радиусом превращается в таблетку.

   От .done-stats осталась пара «число + подпись» (.done-n, .done-lbl):
   это типографика, она общая. Контейнер свой, и это честнее, чем
   перекрывать половину чужих объявлений ради другого места. */
.pstats{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s2);}
.pstats>div{
  min-width:0;padding:var(--s4) var(--s3);
  background:var(--surface);border-radius:var(--r-s);
}

/* ── Своя карточка маршрута в ленте профиля ──────────────────────────
   ✎ ВТОРОЙ ПОКАЗ, 08.10.2026: «карточки маршрутов не одной высоты —
   надо поправить; можно уменьшить картинки или сделать горизонтальные
   карточки; зависит от того, как выделишь состояния».

   Вертикальные карточки были разной высоты не случайно: высоту им
   задавало название в одну, две или три строки. Подрезать его
   до ровного числа строк значило бы прятать слова ради ровного края.
   У горизонтальной карточки высоту задаёт кадр, один на всех, —
   и ровный край получается сам, без подрезки.

   Это .rcard, ПРИНЯТАЯ КАРТОЧКА КАТАЛОГА, а не третья форма карточки
   в продукте. Довод: в профиле лежит список СВОИХ маршрутов, и он
   ближе к каталогу, чем к витрине Главной. Снято здесь только то,
   что принадлежит списку, а не карточке: линейки сверху и снизу
   и поля между строками списка.

   Ширина даёт один полный кадр и заглядывающий край следующего —
   та же мера, что у ленты новостей на Главной. */
.rcard-mine{
  width:min(86vw,340px);
  padding:0;border-bottom:0;
}
.rcard-mine:first-of-type{border-top:0;}
.rcard-mine .rcard-cover{width:88px;}
/* Название мельче, чем в каталоге (19 → 17): в ленте строка вдвое
   короче. Обрезается второй строкой — высоту карточки держит кадр,
   и название не должно её перерастать. */
.rcard-mine .rcard-title{
  font-size:17px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
/* Района в этих карточках нет: это твои маршруты, ты уже знаешь, где
   они. Освободившуюся строку занимает состояние — ради него карточка
   и переделывалась. */
.rcard-mine .rcard-foot{justify-content:flex-start;}

/* ── Состояние своего маршрута ───────────────────────────────────────
   ✎ ВТОРОЙ ПОКАЗ, 08.10.2026: «у маршрутов нравится идея состояний —
   хотелось бы как-то выделить её». Было приглушённой капителью
   на месте цены, и читалось это подписью, а не состоянием. Стало —
   значок и слово в овале с обводкой.

   ✎ ТРЕТИЙ ПОКАЗ, 08.10.2026, и он отменяет вчерашнее «у непройденного
   состояния нет вовсе». Вопрос автора: «Васильевский — там совсем нет
   статуса? Как он тогда попал в профиль пользователя?»

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

   Значит пустого состояния не существует, а есть четыре:

     пройден      обводка акцентом, галочка, акцентные чернила
     скачан       обводка границы управления, стрелка
     куплен       обводка границы управления, одно слово
     хочу пройти  появится в этапе 6 вместе с дневником

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

   СОСТОЯНИЕ ПОКАЗЫВАЕТСЯ ОДНО, старшее. Маршрут бывает куплен,
   скачан и пройден разом, и три овала в строке превратили бы карточку
   в список отметок. Старшинство: пройден → скачан → куплен →
   хочу пройти.

   Заливки нет ни у одного, и это не осторожность. Заливкой акцентом
   в продукте набран бейдж «Бесплатно» — довод купить. Состояние
   ни к чему не зовёт, и повторять им форму довода значит путать
   два разных сообщения на соседних экранах. */
.rstate{
  display:inline-flex;align-items:center;gap:5px;
  height:26px;padding:0 10px;border-radius:var(--r-pill);
  border:1px solid var(--edge);color:var(--text);
  font-family:var(--sans);font-weight:700;font-size:11.5px;
  letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;
}
.rstate .ic{opacity:.8;}
.rstate-done{border-color:var(--accent);color:var(--accent-ink);}
.rstate-done .ic{opacity:1;}
/* «Куплен» — без значка, и поле слева ему не нужно. */
.rstate-own{color:var(--muted);}

/* ═══ Группа настроек ════════════════════════════════════════════════ */
/* ✎ 08.10.2026. Заголовок группы выбирается КЛАССОМ, а не тегом. Группы
   настроек переехали с профиля в лист: на странице они открывались h2,
   внутри листа заголовок листа сам h2 — и подписи групп обязаны стать
   h3, иначе в разметке два заголовка одного уровня, один внутри
   другого. Правило по тегу пришлось бы писать дважды, правило
   по .t-caps работает на любом уровне. */
.sgroup+.sgroup{margin-top:var(--s8);}
.sgroup>.t-caps{margin:0 0 var(--s3);color:var(--muted);}

/* ═══ Экран удаления аккаунта ════════════════════════════════════════ */
/* Требование магазина, а не функция профиля. Нарисовать экран мало:
   покупки по PRD восстанавливаются после переустановки, а после удаления
   аккаунта восстанавливать их нечем — привязка к службе входа исчезает
   вместе с аккаунтом. Дизайн обязан сказать это человеку ДО нажатия,
   а не после.

   Геометрия — экрана-листа (.sheet), та же, что у фильтров каталога
   и у настроек. ✎ Этап 3: класс переименован из .filters, а четыре
   пары кеглей сведены — оба долга закрыты там же, где появились
   первые экраны в React Native.

   АКЦЕНТНАЯ КНОПКА ЗДЕСЬ — «ОСТАВИТЬ АККАУНТ». Опасное действие не может
   быть самым ярким пятном на экране: акцент в продукте значит «дальше»,
   и ставить его на необратимое удаление значит подталкивать к нему. */
/* ✎ 08.10.2026. Экран удаления открывается ИЗ НАСТРОЕК, то есть
   ложится листом поверх листа. Какой из двух выше, сказано здесь,
   а не порядком в разметке: порядок переставляется любой правкой
   разметки и нигде не объявлен, а правило видно. */
.danger{z-index:41;}
.danger-lead{
  margin:0 0 var(--s7);
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.55;
  color:var(--muted);
}
.dgroup+.dgroup{margin-top:var(--s7);}
/* ✎ 08.10.2026: по классу, а не по тегу — тот же довод, что
   у .sgroup выше: уровень заголовка зависит от того, где стоит
   группа, а вид — нет. В витрине 15 эта же группа стоит с h4,
   и правило по тегу до сих пор до неё не доставало. */
.dgroup>.t-caps{margin:0 0 var(--s3);color:var(--muted);}
.dlist{margin:0;padding:0;list-style:none;}
.dlist li{
  position:relative;padding-left:var(--s5);margin-bottom:10px;
  font-family:var(--sans);font-weight:500;font-size:14px;line-height:1.5;
}
.dlist li:last-child{margin-bottom:0;}
.dlist li::before{content:"\2014";position:absolute;left:0;color:var(--muted);}
/* Строка про покупки — единственная полновесная в списке потерь.
   Остальное человек возместит заново, покупки нет. */
.dlist li b{font-weight:700;color:var(--text);}
/* Подтверждение вторым шагом. Флажок — принятый компонент фильтров:
   это тот же жест, и заводить второй ради одного экрана не за что. */
.danger-ack{margin-top:var(--s7);padding-top:var(--s5);border-top:1px solid var(--rule);}
.danger-ack .check{border-bottom:0;align-items:flex-start;}
.danger-ack .check-box{margin-top:2px;}
.danger-ack .check-text{line-height:1.45;}
.danger-foot .btn-quiet{margin-top:var(--s3);}
/* Выключенная кнопка. Норма контраста выключенные элементы исключает
   прямо — тот же довод, что у заблокированного маркера. */
.btn[disabled],.btn-quiet[disabled]{opacity:.45;pointer-events:none;}

/* ━━━ design/system/pages/pages.css ━━━ */

/* ═══════════════════════════════════════════════════════════════════════
   «Покажи мне Питер» — композиция трёх страниц шага 1.

   Здесь только то, что относится к странице целиком: ширина полосы,
   боковые поля, вынос фотографий за поля на телефоне, рамка телефона
   на большом экране. Всё остальное пришло компонентами.

   Токены — tokens/tokens.css. Компоненты — components.css.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════
   Карточка точки
   ═══════════════════════════════════════════════════════════════════════ */

.page{max-width:720px;margin:0 auto;padding:0 var(--pad) var(--s10);}

@media (max-width:719px){
  .fig,.inset{margin-left:calc(-1 * var(--pad));margin-right:calc(-1 * var(--pad));}
  .fig figcaption{padding:0 var(--pad);}
  .inset{border-radius:0;padding-left:var(--pad);padding-right:var(--pad);}
}

/* ═══════════════════════════════════════════════════════════════════════
   Каталог
   ═══════════════════════════════════════════════════════════════════════ */

.cat{max-width:720px;margin:0 auto;padding:var(--s7) var(--pad) var(--s10);}
.cat-city{display:block;color:var(--accent-ink);margin-bottom:var(--s2);}

/* ✎ Переключатель выровнен по центру всего блока заголовка, а не по верху
   строки «Маршруты»: раньше он вставал относительно надзаголовка случайно. */
.cat-head{display:flex;align-items:center;gap:var(--s4);margin-bottom:var(--s5);}
.cat-head-text{flex:1 1 auto;min-width:0;}
.cat-head h1{margin:0;}

/* ✎ Сортировка слева, фильтры справа — расположение из рефа. Обе кнопки
   без рамки: это не действия над содержимым, а вход в настройку выдачи. */
.cat-tools{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  padding:var(--s2) 0;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
}

/* ✎ Теги крупнее, чем в рефе: по ним попадают пальцем на ходу,
   и они же — главный быстрый фильтр. */
.cat-tags{
  display:flex;gap:var(--s2);overflow-x:auto;
  margin:var(--s4) calc(-1 * var(--pad)) var(--s4);
  padding:0 var(--pad) var(--s1);scrollbar-width:none;
}
.cat-tags::-webkit-scrollbar{display:none;}

/* ✎ На карте управление не сворачивается строкой-аккордеоном — одинокая
   строка аккордеона выглядит обрубком меню. Вместо этого сортировка
   и фильтры переезжают поверх карты плавающими кнопками: так делает любое
   картографическое приложение, и карта получает всю высоту.
   Сам элемент один и тот же, он просто переносится в другое место разметки —
   иначе пришлось бы держать два набора кнопок и синхронизировать их. */
.cat[data-view="map"] .cat-tags,
.cat[data-view="map"] .cat-found{display:none;}

.cat-map .cat-tools{
  position:absolute;top:var(--s3);left:var(--s3);right:var(--s3);z-index:5;
  border:0;padding:0;gap:var(--s2);
}
.cat-map .cat-tools .tool{
  min-height:40px;padding:0 var(--s4);border-radius:var(--r-pill);
  background:var(--bg);box-shadow:var(--pop-shadow);
}
.cat-map .sortmenu{min-width:200px;}

/* Сколько нашлось — под фильтрами, перед списком */
.cat-found{font-family:var(--sans);font-weight:500;font-size:13px;color:var(--muted);
           margin:0 0 var(--s4);}

/* ── Режим «на карте» ───────────────────────────────────────────────────
   Маршрут выбирают по месту не реже, чем по теме. Переключатель живёт
   внутри каталога: отдельный раздел под карту показывал бы то же самое
   во второй раз. */
.pane[hidden]{display:none;}
.cat-map{
  position:relative;margin:0 calc(-1 * var(--pad));
  height:72vh;min-height:440px;background:var(--surface);overflow:hidden;
}
/* Только сама карта. Без прямого потомка правило цепляло и значки
   в кнопках, которые переезжают поверх карты, и раздувало их. */
.cat-map > svg{position:absolute;inset:0;width:100%;height:100%;}
/* ✎ Этап 3: живая карта вместо картинки. Холст занимает всё поле
   режима «на карте», лента карточек лежит поверх него. */
.cat-map > .cat-map-canvas{position:absolute;inset:0;width:100%;height:100%;}

/* ✎ Под картой лента из всех маршрутов, а не карточка одного. Листаешь —
   карта переезжает на тот маршрут, который сейчас по центру. Так карта
   отвечает на вопрос «а что рядом», ради которого её и открывают. */
.cat-map-deck{
  position:absolute;left:0;right:0;bottom:var(--s3);
  display:flex;gap:var(--s3);
  padding:0 var(--s3);
  overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;
  -webkit-overflow-scrolling:touch;
}
.cat-map-deck::-webkit-scrollbar{display:none;}

/* ═══════════════════════════════════════════════════════════════════════
   Экран прохождения
   ═══════════════════════════════════════════════════════════════════════ */

.screen{position:relative;width:100%;min-height:100dvh;overflow:hidden;background:var(--surface);}
.map-svg{position:absolute;inset:0;width:100%;height:100%;display:block;}

@media (min-width:760px){
  .screen{
    max-width:402px;min-height:0;height:874px;margin:var(--s8) auto;
    border:1px solid var(--rule);border-radius:var(--r-l);
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   Этап 0.5 — композиция трёх новых страниц.

   Здесь по-прежнему только то, что относится к странице целиком:
   ширина полосы, боковые поля, вынос за поля на телефоне, место
   под нижнее меню. Всё остальное пришло компонентами.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Главная и профиль ───────────────────────────────────────────────
   Нижнее поле — обычное: место под нижнее меню добавляет футер, который
   стоит внизу каждой из этих страниц. См. components.css, .site-footer. */
.home,.profile{
  max-width:720px;margin:0 auto;
  padding:var(--s5) var(--pad) var(--s10);
}

/* ── Главная ──────────────────────────────────────────────────────────
   ✎ 08.10.2026, второе решение автора за день: ЛОГОТИПА И ФРАЗЫ
   В ШАПКЕ БОЛЬШЕ НЕТ. Вместо них — приветствие по имени.

   Логотип не переехал, а ушёл: бренд на этом экране несёт футер,
   а на широком экране сайта — шапка, описанная правилом в IA.md.
   Человек, открывший приложение, уже знает, что он открыл; человек,
   пришедший на сайт из поиска, видит логотип в шапке. Повторять его
   третий раз первой строкой содержимого было данью привычке.

   Приветствие — заголовок экрана по разметке и тихая строка по виду,
   и это расхождение нарочное. По правилу шапки из IA.md корень раздела
   начинается своим заголовком; у Главной своего заголовка не было
   вовсе, его роль играл логотип, — поэтому h1. Но громким этот
   заголовок быть не должен: «Доброе утро, София» ничего не сообщает
   о содержимом экрана, и набранное в 34 жирным оно отняло бы первое
   место у «Что нового», которое сообщает.

   ✎ 08.10.2026, по замечанию автора: было 27–34 восьмым начертанием,
   стало 16 пятым. Иерархия экрана теперь такая: громче всего —
   заголовки разделов, приветствие стоит над ними тише их.
   Для чтения с экрана и для поисковика h1 при этом на месте. */
.home-top{padding:var(--s2) 0 var(--s6);}
.home-hello{
  margin:0;
  font-family:var(--sans);font-weight:500;font-size:16px;line-height:1.4;
  letter-spacing:-.01em;
}

.home-sec+.home-sec{margin-top:var(--s9);}

/* ── Строка заголовка раздела Главной ────────────────────────
   Здесь только раскладка строки: заголовок слева, «Посмотреть все»
   справа. Сам заголовок — .sec-h2 из компонентов: 08.10.2026 такой же
   заголовок потребовался странице маршрута, а заголовок, который
   стоит на двух экранах, живёт не в композиции одного из них. */
.home-head{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--s4);margin:0 0 var(--s4);
}
/* «Посмотреть все» над лентой — ссылкой, а не кнопкой: кнопка внизу
   раздела уже есть, и две кнопки с одним адресом в одном блоке
   превращают переход в каталог в два разных действия. Здесь же
   её задача другая — сказать, что лента не всё, что есть. */
.home-more{
  display:inline-flex;align-items:center;gap:2px;flex:0 0 auto;
  font-family:var(--sans);font-weight:600;font-size:14px;letter-spacing:-.01em;
  color:var(--accent-ink);text-decoration:none;white-space:nowrap;
}
.home-more .ic{opacity:.9;}

/* Переход в каталог — кнопкой во всю ширину. Это последнее, что человек
   видит на Главной, и единственное действие, которое она предлагает
   после чтения.

   ✎ 08.10.2026. Кнопка стала акцентной. Прежнее правило звучало
   «акцент на Главной уже занят активным пунктом меню», и оно было
   сформулировано слишком широко. Активный пункт меню — не действие,
   а отметка места, где человек стоит; он не соревнуется с кнопкой
   за нажатие. Правило переписано: акцентное действие на экране
   одно — и на Главной это «Все маршруты». */
.home-all{margin-top:var(--s6);}

/* ── Страница маршрута ────────────────────────────────────────────────
   Класс .rpage, а не .route: .route уже занят группой линии маршрута
   на карте (components.css), и совпадение имён поймало бы прозрачность
   невыбранного маршрута на всей странице.

   Нижнее поле обычное: место под меню добавляет футер, как и на всех
   остальных страницах. См. components.css, .site-footer. */
.rpage{max-width:720px;margin:0 auto;padding:0 var(--pad) var(--s10);}

/* Обложка и карта выходят за боковые поля на телефоне — ровно как фото
   во всю ширину внутри рассказа. Довод тот же: это главные изображения
   экрана, и поля рядом с ними отнимают у них ширину ни за что.

   ✎ 08.10.2026. У обложки скругление на телефоне СНИМАЕТСЯ ПОЛНОСТЬЮ,
   как у карты. Раньше здесь сохранялась арка — «главный узнаваемый знак
   продукта», — и это решение отменено автором на приёмке: кадр во всю
   ширину телефона обязан быть кадром во всю ширину, а не куполом
   над страницей. Арка остаётся на миниатюрах, в каталоге, в ленте
   Главной и на аватаре — везде, где она мелкая и читается знаком. */
@media (max-width:719px){
  .rcover,.rmap{margin-left:calc(-1 * var(--pad));margin-right:calc(-1 * var(--pad));}
  .rcover,.rcover img{border-radius:0;}
  /* У карты обнулять нечего: ✎ 08.10.2026 скругление снято с неё
     совсем, в компонентах. */
  /* ✎ ВЫСОТА ОБЛОЖКИ СЧИТАЕТСЯ ОТ ЭКРАНА, А НЕ ОТ ПРОПОРЦИИ КАДРА.
     Решение автора, 08.10.2026: «чтоб на экране телефона при входе низ
     занимали цена, название и характеристики, а весь верх — обложка».

     Арифметика видна, и её можно пересчитать, если шапка изменится:

       100svh                 высота экрана без адресной строки браузера
       − 56   (--tabbar)      нижнее меню
       − 34                   безопасная зона под меню
       − 157                  бейдж с районом (40) + заголовок в две
                              строки (63) + характеристики (34) + поле
                              над ними (20)
       = высота обложки

     svh, а не vh: на телефоне vh считается от экрана БЕЗ адресной
     строки и не меняется при её сворачивании, то есть первый экран
     оказался бы выше, чем он есть, и характеристики ушли бы под сгиб.

     clamp держит края. Нижний (300) — чтобы на низком экране обложка
     осталась кадром, а не полоской. Верхний (640) — чтобы на планшете
     в портрете, где 100svh за тысячу, она не съела весь первый экран.

     Кнопки при этом оказываются сразу ПОД сгибом, и это не недосмотр:
     край акцентной кнопки, заглядывающий снизу, приглашает листать
     лучше, чем пустое поле. */
  .rcover img{height:clamp(300px,calc(100svh - 247px),640px);}
}
/* ✎ ШИРОКИЙ ЭКРАН: обложка занимает всю полосу страницы, 720 пикселей,
   со скруглением r-l. Автор посмотрел и принял с одной правкой —
   «да, норм выглядит, можно только чуть высоту увеличить», 08.10.2026.
   Пропорция поднята с 3:2 до 7:5, то есть с 480 до 514 пикселей.

   svh здесь не работает: высота окна настольного браузера ничего
   не говорит о композиции — его растягивают и сжимают как попало,
   и обложка прыгала бы вслед. На телефоне экран один и его высота
   и есть композиция.

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

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

   ✎ 08.10.2026. Тот же класс работает и внутри листа настроек: группы
   «Тема», «Покупки», «Документы», «Аккаунт» переехали туда с профиля,
   а правило у них одно — вертикальный шаг между группами. Заводить
   второе имя ради того, что группы сменили экран, не за что: лист
   настроек — часть профиля, а не отдельный раздел.

   Заголовки выбираются КЛАССОМ, а не тегом. На странице группа
   открывается h2, внутри листа — h3 (h2 там занят заголовком листа),
   и правило по тегу пришлось бы писать дважды. */
.profile-sec+.profile-sec{margin-top:var(--s8);}
.profile-sec>.t-caps{margin:0 0 var(--s3);color:var(--muted);}
/* Заголовок раздела внутри профиля — «Мои маршруты» над лентой.
   Это полноценный заголовок (.sec-h2), а не подпись группы: под ним
   лежит содержимое, а не список настроек. Приглушать его нельзя —
   он единственный на экране. */
.profile-sec>.sec-h2{margin:0 0 var(--s4);}
