/* ═══════════════════════════════════════════════════════════════════════════
   RU: Серверные страницы раздела города: рубрики для поисковиков (/m/<cc>/<город>/rubrics…, КМ8.6) и транспорт
       (/m/<cc>/<город>/transport…, КМ8.1). Разметку пишут src/seo/rubric-landing.ts и src/seo/transport-page.ts.

       Зачем отдельный файл. До КМ стили жили строкой в TS (PAGE_CSS, TRANSIT_CSS) с зашитыми цветами и system-ui: тёмная
       тема их не перекрашивала, шрифт был не сайта, а ворота check:theme такие строки не видят. Здесь — только токены
       kartivi.css (цвета, отступы сетки 4 px, кегли, радиусы), поэтому тон и палитра посетителя (theme-prepaint.js)
       перекрашивают страницу сами, а новый литерал цвета ловит check:theme.

       Всё под `.seo-page` (класс <body>): общие имена `.wrap`, `.meta`, `.note` не должны задеть другие страницы, если
       файл однажды подключат рядом. Табло на весь экран и виджет для чужих сайтов (/widget/…) этот файл не подключают —
       у них своё оформление внутри страницы, их встраивают другие сайты.
   EN: The city section's server pages: SEO rubrics (КМ8.6) and transport (КМ8.1). The markup comes from
       src/seo/rubric-landing.ts and src/seo/transport-page.ts.

       Why a separate file. Before КМ the styles lived as a TS string with hardcoded colours and system-ui: the dark theme
       did not repaint them, the font was not the site's, and the check:theme gate cannot see such strings. Here there are
       only kartivi.css tokens (colours, 4 px grid spacing, type steps, radii), so the visitor's tone and palette repaint the
       page by themselves, and a new colour literal is caught by check:theme.

       Everything sits under `.seo-page` (the <body> class): common names like `.wrap`, `.meta`, `.note` must not touch
       other pages should the file ever be linked next to them. The full-screen board and the widget for other sites
       (/widget/…) do not link this file — they carry their own look inside the page, other sites embed them.
   ═══════════════════════════════════════════════════════════════════════════ */

/* RU: Основа: фон поверхности и колонка чтения 860 px — та же ширина, что была у лендингов (длинный список читается
       колонкой, а не строкой во весь монитор). EN: The base: the surface ground and an 860 px reading column — the width the
       landings had (a long list reads as a column, not a monitor-wide line). */
.seo-page {
  background: var(--k-surface);
}
.seo-page .wrap {
  max-width: 860px;
  margin: 0 auto;
  padding: var(--k-s-4) var(--k-s-4) var(--k-s-8);
}
/* RU: Цвет ссылки — только у простых ссылок содержимого (без класса): `.seo-page a` перебивал `.k-site-link` шапки
       (0,1,1 против 0,1,0) — разделы общей шапки становились зелёными, — а заодно и .k-btn, .k-chip и значки маршрутов. У
       ссылок с классом свой цвет в их правиле. EN: The link colour goes only to plain content links (no class): `.seo-page a`
       beat the header's `.k-site-link` (0,1,1 vs 0,1,0) — the shared header's sections turned green — and also .k-btn, .k-chip
       and route badges. Classed links take their colour from their own rule. */
.seo-page :where(main, .live-top) a:not([class]) {
  color: var(--k-brand-ink);
  text-decoration: none;
}
.seo-page :where(main, .live-top) a:not([class]):hover {
  text-decoration: underline;
}
/* RU: Ссылки-кнопки, чипы, значки и имена мест — без подчёркивания браузера; имя места подчёркивается под курсором, как
       ссылка. EN: Button, chip, badge and place-name links lose the browser underline; a place name underlines on hover. */
.seo-page :where(main, .live-top) a[class] {
  text-decoration: none;
}
.seo-page :where(main) a.firm-name:hover {
  text-decoration: underline;
}
.seo-page .crumbs {
  margin: 0 0 var(--k-s-2);
  font: var(--k-t-sm);
  color: var(--k-text-muted);
}
.seo-page h1 {
  margin: var(--k-s-1) 0;
  font: 600 var(--k-t-head);
}
.seo-page h2 {
  margin: var(--k-s-6) 0 var(--k-s-2);
  font: 600 var(--k-t-title-s);
}
/* RU: Заголовок-ссылка хаба рубрик — цветом текста; селектор сильнее правила простых ссылок выше (0,2,2 против 0,2,1).
   EN: The rubric hub's heading link is text-coloured; the selector beats the plain-link rule above (0,2,2 vs 0,2,1). */
.seo-page h2 > a:not([class]) {
  color: var(--k-text);
}
.seo-page .sub {
  margin: 0 0 var(--k-s-3);
  font: var(--k-t-base);
  color: var(--k-text-muted);
}
.seo-page .map-link {
  margin: var(--k-s-2) var(--k-s-2) var(--k-s-2) 0;
}
.seo-page .map-link:hover {
  text-decoration: none;
}
.seo-page .chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--k-s-2);
  margin: var(--k-s-3) 0 var(--k-s-4);
}
.seo-page .chips .k-chip:hover {
  text-decoration: none;
  background: var(--k-brand-soft);
}
.seo-page footer {
  margin-top: var(--k-s-7);
  font: var(--k-t-cap);
  color: var(--k-text-subtle);
}

/* RU: Список организаций — как строка выдачи на карте (КМ5): имя, затем «статус · часы», затем «адрес · телефон». Статус
       окрашен тем же смыслом, что в выдаче: открыто — зелёный, закрыто — красный. EN: The firm list — like a search result
       row on the map (КМ5): the name, then "status · hours", then "address · phone". The status is coloured with the same
       meaning as in the results: open is green, closed is red. */
.seo-page ul.firms {
  margin: 0;
  padding: 0;
  list-style: none;
}
.seo-page ul.firms li {
  padding: var(--k-s-3) 0;
  border-bottom: 1px solid var(--k-border-soft);
}
.seo-page .firm-name,
.seo-page ul.firms li > a:first-child {
  font: 600 var(--k-t-title-s);
  color: var(--k-text);
}
.seo-page .meta {
  margin-top: 2px;
  font: var(--k-t-sm);
  color: var(--k-text-muted);
}
.seo-page .st-open {
  color: var(--k-ok);
  font-weight: 600;
}
.seo-page .st-soon {
  color: var(--k-warn);
  font-weight: 600;
}
.seo-page .st-closed {
  color: var(--k-danger);
  font-weight: 600;
}
/* RU: Телефон — ссылка tel: (на телефоне звонок одним касанием), но цветом текста строки: строка не должна пестрить.
   EN: The phone is a tel: link (one tap to call on a phone) but in the row's text colour: the row must not look busy. */
.seo-page .meta a.tel {
  color: inherit;
  white-space: nowrap;
}
.seo-page nav.pager {
  display: flex;
  gap: var(--k-s-3);
  margin-top: var(--k-s-5);
  font: 600 var(--k-t-base);
}
.seo-page nav.pager a {
  display: inline-flex;
  align-items: center;
  gap: var(--k-s-1);
  min-height: var(--k-tap-min);
}

/* RU: Транспорт: значок маршрута — цвет вида транспорта задаёт разметка (как на карте), буквы — белые токеном. EN: Transport:
       the route badge — the mode colour comes from the markup (as on the map), the letters are white by token. */
.seo-page .rt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-width: 36px;
  height: 28px;
  padding: 0 var(--k-s-2);
  border-radius: var(--k-r-s);
  color: var(--k-on-cat);
  font: 600 var(--k-t-base);
  text-decoration: none;
}
.seo-page .rt:hover {
  text-decoration: none;
}
.seo-page .routes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--k-s-2);
  margin: var(--k-s-2) 0 var(--k-s-4);
}
.seo-page .note {
  margin: var(--k-s-3) 0;
  padding: var(--k-s-3);
  border-radius: var(--k-r-m);
  background: var(--k-surface-2);
  font: var(--k-t-base);
  color: var(--k-text-muted);
}
.seo-page .note.warn {
  background: var(--k-warn-soft);
  color: var(--k-warn);
}
.seo-page .kv {
  font: var(--k-t-base);
  color: var(--k-text-muted);
}
.seo-page .upd {
  font: var(--k-t-cap);
  color: var(--k-text-subtle);
}

/* RU: Табло. Колонка времени без общего nowrap: 30.09 на 320 px «4 мин по расписанию» с «интервал ~N мин» раздвигали
       таблицу до 390 px (прокрутка вбок). Неразрывны только минуты и значок живого прогноза, подписи — шире 400 px; узкая —
       колонка значка маршрута. EN: The board. No blanket nowrap on the time column: at 320 px it widened the table to
       390 px (sideways scroll) on 09-30. Only the minutes and the live mark stay whole, captions above 400 px; the narrow
       column is the route badge. */
.seo-page table.board {
  width: 100%;
  border-collapse: collapse;
  font: var(--k-t-read);
}
.seo-page table.board td {
  padding: var(--k-s-2) var(--k-s-1);
  border-bottom: 1px solid var(--k-border-soft);
  vertical-align: middle;
}
.seo-page table.board td:first-child {
  width: 1%;
}
.seo-page table.board td.when {
  text-align: right;
}
.seo-page table.board td.when b,
.seo-page table.board td.when .gps {
  white-space: nowrap;
}
@media (min-width: 400px) {
  .seo-page table.board td.when small {
    white-space: nowrap;
  }
}
.seo-page table.board td.when b {
  font: 600 var(--k-t-title-s);
  color: var(--k-ok);
}
.seo-page table.board td.when b.plan {
  color: var(--k-text);
}
.seo-page table.board td.when b.stale {
  color: var(--k-warn);
}
.seo-page table.board small {
  font: var(--k-t-cap);
  color: var(--k-text-muted);
}
/* RU: Значок живого прогноза (бывшая надпись «GPS», КМ8.1): форма, а не только цвет, отличает его от расписания; смысл —
       в подсказке и aria-label. EN: The live forecast mark (formerly the "GPS" text, КМ8.1): a shape, not just a colour, sets
       it apart from the timetable; the meaning is in the tooltip and aria-label. */
.seo-page .gps {
  display: inline-flex;
  align-items: center;
  margin-left: var(--k-s-1);
  color: var(--k-ok);
  vertical-align: -2px;
}
.seo-page .gps.stale {
  color: var(--k-warn);
}
.seo-page ul.veh {
  margin: var(--k-s-2) 0 var(--k-s-4);
  padding: 0;
  list-style: none;
}
.seo-page ul.veh li {
  padding: var(--k-s-2) 0;
  border-bottom: 1px solid var(--k-border-soft);
  font: var(--k-t-base);
}
.seo-page table.tt {
  margin: var(--k-s-1) 0 var(--k-s-4);
  border-collapse: collapse;
  font: var(--k-t-base);
}
.seo-page table.tt th {
  padding: var(--k-s-1) var(--k-s-3) var(--k-s-1) 0;
  color: var(--k-text-muted);
  font-weight: 600;
  text-align: right;
  vertical-align: top;
}
.seo-page table.tt td {
  padding: var(--k-s-1) 0;
  word-spacing: 6px;
  font-variant-numeric: tabular-nums;
}
.seo-page h3.tt-head {
  margin: var(--k-s-4) 0 var(--k-s-1);
  font: 600 var(--k-t-read);
}
.seo-page ol.stops {
  margin: var(--k-s-2) 0 var(--k-s-4);
  padding-left: var(--k-s-6);
}
.seo-page ol.stops li {
  padding: var(--k-s-1) 0;
}
.seo-page .dir {
  margin: var(--k-s-3) 0;
  padding: var(--k-s-3) var(--k-s-4);
  border: 1px solid var(--k-border);
  border-radius: var(--k-r-m);
}
.seo-page .dir h3 {
  margin: 0 0 var(--k-s-1);
  font: 600 var(--k-t-title-s);
}
.seo-page svg.line {
  width: 100%;
  height: auto;
  max-height: 260px;
  margin: var(--k-s-2) 0;
  border-radius: var(--k-r-m);
  background: var(--k-surface-2);
}

/* RU: Поиск остановки и форма «Как добраться»: поле высотой 48 px (токен поля), кнопка — общая .k-btn. EN: The stop search
       and the trip form: a 48 px field (the field token), the button is the shared .k-btn. */
.seo-page form.q {
  display: flex;
  gap: var(--k-s-2);
  margin: var(--k-s-2) 0 var(--k-s-3);
}
.seo-page .field {
  flex: 1;
  min-width: 0;
  min-height: var(--k-h-field);
  box-sizing: border-box;
  padding: 0 var(--k-s-3);
  border: 1px solid var(--k-border);
  border-radius: var(--k-r-m);
  background: var(--k-surface);
  color: var(--k-text);
  font: var(--k-t-read);
}
.seo-page .field::placeholder {
  color: var(--k-text-subtle);
}
.seo-page .field:focus-visible {
  outline: none;
  box-shadow: var(--k-focus);
}
.seo-page form .k-btn {
  min-height: var(--k-h-field);
}
/* RU: «Встроить на свой сайт» — свёрнуто: код iframe нужен владельцам чужих сайтов, а не пассажиру у остановки. EN: "Встроить
       на свой сайт" is collapsed: the iframe code is for other sites' owners, not for a passenger at the stop. */
.seo-page details.embed {
  margin: var(--k-s-2) 0 var(--k-s-4);
  font: var(--k-t-base);
  color: var(--k-text-muted);
}
/* RU: Пункт списка, а не flex: так у «Встроить на свой сайт» остаётся треугольник раскрытия браузера — без него свёрнутый
       блок не отличить от подписи. 12 + 20 + 12 = 44 px — высота касания. EN: A list item rather than flex: that keeps the
       browser's disclosure triangle — without it the folded block looks like a caption. 12 + 20 + 12 = 44 px tap height. */
.seo-page details.embed summary {
  display: list-item;
  padding: var(--k-s-3) 0;
  color: var(--k-brand-ink);
  font-weight: 600;
  cursor: pointer;
}
.seo-page details.embed textarea {
  width: 100%;
  box-sizing: border-box;
  padding: var(--k-s-2);
  border: 1px solid var(--k-border);
  border-radius: var(--k-r-s);
  background: var(--k-surface-2);
  color: var(--k-text);
  font-family: var(--k-font-mono);
  font-size: var(--k-fs-sm);
  line-height: var(--k-lh-sm);
}

/* RU: «Как добраться на транспорте»: подсказки остановок под полем и карточки вариантов. EN: The trip planner: stop
       suggestions under the field and option cards. */
.seo-page #tp-form {
  display: grid;
  gap: var(--k-s-2);
  margin: var(--k-s-3) 0;
}
.seo-page #tp-form .row {
  position: relative;
  display: flex;
  gap: var(--k-s-2);
}
.seo-page ul.sug {
  position: absolute;
  top: calc(var(--k-h-field) + var(--k-s-1));
  left: 0;
  right: 0;
  z-index: var(--k-z-popover);
  margin: 0;
  padding: var(--k-s-1);
  border: 1px solid var(--k-border);
  border-radius: var(--k-r-m);
  background: var(--k-surface);
  box-shadow: var(--k-shadow-2);
  list-style: none;
}
.seo-page ul.sug button {
  display: block;
  width: 100%;
  min-height: var(--k-tap-min);
  padding: 0 var(--k-s-2);
  border: 0;
  border-radius: var(--k-r-s);
  background: none;
  color: var(--k-text);
  font: var(--k-t-read);
  text-align: left;
  cursor: pointer;
}
.seo-page ul.sug button:hover {
  background: var(--k-surface-2);
}
.seo-page article.opt {
  margin: var(--k-s-3) 0;
  padding: var(--k-s-3) var(--k-s-4);
  border: 1px solid var(--k-border);
  border-radius: var(--k-r-m);
}
.seo-page article.opt h3 {
  margin: 0;
  font: 600 var(--k-t-title-s);
}
.seo-page article.opt ol {
  margin: var(--k-s-2) 0 0;
  padding-left: var(--k-s-5);
}
.seo-page article.opt li {
  padding: var(--k-s-1) 0;
}
.seo-page article.opt li.walk {
  color: var(--k-text-muted);
}

/* RU: «Транспорт на карте»: общая шапка сверху, под ней строка раздела, карта — на всю оставшуюся высоту. Раньше строка
       висела position:fixed поверх карты без шапки сайта. EN: "Transport on the map": the shared header on top, the section
       bar under it, the map fills the remaining height. The bar used to hang position:fixed over the map with no site
       header. */
.seo-live {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}
.seo-live .live-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--k-s-1) var(--k-s-3);
  padding: var(--k-s-2) var(--k-s-4);
  border-bottom: 1px solid var(--k-border);
  background: var(--k-surface);
  font: var(--k-t-base);
}
.seo-live .live-top h1 {
  margin: 0;
  font: 600 var(--k-t-title-s);
}
.seo-live .live-top a {
  display: inline-flex;
  align-items: center;
  gap: var(--k-s-1);
  font-weight: 600;
}
.seo-live .live-top .st {
  color: var(--k-text-muted);
}
.seo-live .live-stage {
  position: relative;
  flex: 1;
  min-height: 0;
}
.seo-live #map {
  position: absolute;
  inset: 0;
}
.seo-live .legend {
  position: absolute;
  left: var(--k-s-3);
  bottom: var(--k-s-7);
  z-index: var(--k-z-map-overlay);
  padding: var(--k-s-2) var(--k-s-3);
  border-radius: var(--k-r-m);
  background: var(--k-surface);
  box-shadow: var(--k-shadow-2);
  font: var(--k-t-cap);
  color: var(--k-text);
}
.seo-live .legend div {
  display: flex;
  align-items: center;
  gap: var(--k-s-2);
  min-height: 20px;
}
.seo-live .legend i {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: var(--k-r-pill);
}
.seo-live .hint {
  position: absolute;
  top: var(--k-s-3);
  left: 50%;
  z-index: var(--k-z-map-overlay);
  transform: translateX(-50%);
  padding: var(--k-s-2) var(--k-s-3);
  border-radius: var(--k-r-s);
  background: var(--k-text);
  color: var(--k-surface);
  font: var(--k-t-sm);
}
.seo-live .maplibregl-popup-content {
  background: var(--k-surface);
  color: var(--k-text);
  font: var(--k-t-base);
}
